From ca6a93a4c577691b7f79793518af19c118aaefc5 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Sun, 5 Jun 2022 14:57:58 -0400 Subject: [PATCH] Move generation logic to new Generator class --- assets/parser.d.erb | 8 +++--- lib/propane.rb | 58 +++------------------------------------- lib/propane/generator.rb | 58 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 58 deletions(-) create mode 100644 lib/propane/generator.rb diff --git a/assets/parser.d.erb b/assets/parser.d.erb index fed8a2f..9794031 100644 --- a/assets/parser.d.erb +++ b/assets/parser.d.erb @@ -1,8 +1,8 @@ -<% if @modulename %> -module <%= @modulename %>; +<% if @grammar.modulename %> +module <%= @grammar.modulename %>; <% end %> -class <%= classname %> +class <%= @classname %> { enum { @@ -117,7 +117,7 @@ class <%= classname %> uint accepts; } -<% transition_table, state_table = lexer.build_tables %> +<% transition_table, state_table = @lexer.build_tables %> private static const Transition transitions[] = [ <% transition_table.each do |transition_table_entry| %> Transition(<%= transition_table_entry[:first] %>u, <%= transition_table_entry[:last] %>u, <%= transition_table_entry[:destination] %>u), diff --git a/lib/propane.rb b/lib/propane.rb index dc488fd..46ba0bd 100644 --- a/lib/propane.rb +++ b/lib/propane.rb @@ -5,6 +5,7 @@ require_relative "propane/code_point_range" require_relative "propane/fa" require_relative "propane/fa/state" require_relative "propane/fa/state/transition" +require_relative "propane/generator" require_relative "propane/grammar" require_relative "propane/lexer" require_relative "propane/lexer/dfa" @@ -36,64 +37,13 @@ class Propane class Error < RuntimeError end - def initialize(input) - @grammar = Grammar.new(input) - @classname = @grammar.classname - @modulename = @grammar.modulename - @rule_sets = @grammar.rule_sets - end - - def generate(output_file, log_file) - expand_rules - lexer = Lexer.new(@grammar.tokens, @grammar.drop_tokens) - parser = Parser.new(@rule_sets) - classname = @classname || File.basename(output_file).sub(%r{[^a-zA-Z0-9].*}, "").capitalize - erb = ERB.new(File.read(File.join(File.dirname(File.expand_path(__FILE__)), "../assets/parser.d.erb")), trim_mode: "<>") - result = erb.result(binding.clone) - File.open(output_file, "wb") do |fh| - fh.write(result) - end - end - - private - - def expand_rules - tokens_by_name = {} - @grammar.tokens.each do |token| - if tokens_by_name.include?(token.name) - raise Error.new("Duplicate token name #{token.name.inspect}") - end - tokens_by_name[token.name] = token - end - @rule_sets.each do |rule_name, rule_set| - if tokens_by_name.include?(rule_name) - raise Error.new("Rule name collides with token name #{rule_name.inspect}") - end - end - unless @rule_sets["Start"] - raise Error.new("Start rule not found") - end - @rule_sets.each do |rule_name, rule_set| - rule_set.rules.each do |rule| - rule.components.map! do |component| - if tokens_by_name[component] - tokens_by_name[component] - elsif @rule_sets[component] - @rule_sets[component] - else - raise Error.new("Symbol #{component} not found") - end - end - end - end - end - class << self def run(input_file, output_file, log_file) begin - propane = Propane.new(File.read(input_file)) - propane.generate(output_file, log_file) + grammar = Grammar.new(File.read(input_file)) + generator = Generator.new(grammar, output_file, log_file) + generator.generate rescue Error => e $stderr.puts e.message return 2 diff --git a/lib/propane/generator.rb b/lib/propane/generator.rb new file mode 100644 index 0000000..0a7c5f2 --- /dev/null +++ b/lib/propane/generator.rb @@ -0,0 +1,58 @@ +class Propane + + class Generator + + def initialize(grammar, output_file, log_file) + @grammar = grammar + @output_file = output_file + @log_file = log_file + @classname = @grammar.classname || File.basename(output_file).sub(%r{[^a-zA-Z0-9].*}, "").capitalize + process_grammar! + @lexer = Lexer.new(@grammar.tokens, @grammar.drop_tokens) + @parser = Parser.new(@grammar.rule_sets) + end + + def generate + erb = ERB.new(File.read(File.join(File.dirname(File.expand_path(__FILE__)), "../../assets/parser.d.erb")), trim_mode: "<>") + result = erb.result(binding.clone) + File.open(@output_file, "wb") do |fh| + fh.write(result) + end + end + + private + + def process_grammar! + tokens_by_name = {} + @grammar.tokens.each do |token| + if tokens_by_name.include?(token.name) + raise Error.new("Duplicate token name #{token.name.inspect}") + end + tokens_by_name[token.name] = token + end + @grammar.rule_sets.each do |rule_name, rule_set| + if tokens_by_name.include?(rule_name) + raise Error.new("Rule name collides with token name #{rule_name.inspect}") + end + end + unless @grammar.rule_sets["Start"] + raise Error.new("Start rule not found") + end + @grammar.rule_sets.each do |rule_name, rule_set| + rule_set.rules.each do |rule| + rule.components.map! do |component| + if tokens_by_name[component] + tokens_by_name[component] + elsif @grammar.rule_sets[component] + @grammar.rule_sets[component] + else + raise Error.new("Symbol #{component} not found") + end + end + end + end + end + + end + +end