diff --git a/lib/propane/generator.rb b/lib/propane/generator.rb index c086cf5..b89e667 100644 --- a/lib/propane/generator.rb +++ b/lib/propane/generator.rb @@ -28,6 +28,7 @@ class Propane def process_grammar! tokens_by_name = {} @grammar.tokens.each do |token| + # Check for token name conflicts. if tokens_by_name.include?(token.name) raise Error.new("Duplicate token name #{token.name.inspect}") end @@ -35,9 +36,11 @@ class Propane end rule_sets = {} @grammar.rules.each do |rule| + # Check for token/rule name conflict. if tokens_by_name.include?(rule.name) raise Error.new("Rule name collides with token name #{rule.name.inspect}") end + # Build rule sets of all rules with the same name. @_rule_set_id ||= @grammar.tokens.size unless rule_sets[rule.name] rule_sets[rule.name] = RuleSet.new(rule.name, @_rule_set_id) @@ -46,9 +49,17 @@ class Propane rule.rule_set = rule_sets[rule.name] rule_sets[rule.name] << rule end + # Check for start rule. unless rule_sets["Start"] raise Error.new("Start rule not found") end + # Generate lexer user code IDs for lexer patterns with user code blocks. + @grammar.patterns.select do |pattern| + pattern.code + end.each_with_index do |pattern, code_id| + pattern.code_id = code_id + end + # Map rule components from names to Token/RuleSet objects. @grammar.rules.each do |rule| rule.components.map! do |component| if tokens_by_name[component] @@ -61,7 +72,9 @@ class Propane end end determine_possibly_empty_rulesets!(rule_sets) + # Generate the lexer. @lexer = Lexer.new(@grammar.patterns) + # Generate the parser. @parser = Parser.new(@grammar, rule_sets, rule_sets["Start"], @log) end diff --git a/lib/propane/grammar.rb b/lib/propane/grammar.rb index acfc6b6..06e5d2b 100644 --- a/lib/propane/grammar.rb +++ b/lib/propane/grammar.rb @@ -12,7 +12,6 @@ class Propane @patterns = [] @tokens = [] @rules = [] - @code_id = 0 @line_number = 1 @next_line_number = @line_number @input = input.gsub("\r\n", "\n") @@ -78,15 +77,12 @@ class Propane end pattern ||= name consume!(/\s+/) - if code = parse_code_block! - code_id = @code_id - @code_id += 1 - else + unless code = parse_code_block! consume!(/;/, "expected pattern or `;' or code block") end token = Token.new(name: name, id: @tokens.size, line_number: @line_number) @tokens << token - pattern = Pattern.new(pattern: pattern, token: token, line_number: @line_number, code: code, code_id: code_id) + pattern = Pattern.new(pattern: pattern, token: token, line_number: @line_number, code: code) @patterns << pattern end end @@ -132,9 +128,7 @@ class Propane unless code = parse_code_block! raise Error.new("Line #{@line_number}: expected code block to follow pattern") end - code_id = @code_id - @code_id += 1 - @patterns << Pattern.new(pattern: pattern, line_number: @line_number, code: code, code_id: code_id) + @patterns << Pattern.new(pattern: pattern, line_number: @line_number, code: code) end end diff --git a/lib/propane/pattern.rb b/lib/propane/pattern.rb index a8f678b..40688b0 100644 --- a/lib/propane/pattern.rb +++ b/lib/propane/pattern.rb @@ -8,7 +8,7 @@ class Propane # @option options [Integer, nil] :code_id # Code block ID. - attr_reader :code_id + attr_accessor :code_id # @return [String, nil] # Pattern. @@ -32,8 +32,6 @@ class Propane # Optional parameters. # @option options [String, nil] :code # Code block to execute when the pattern is matched. - # @option options [Integer, nil] :code_id - # Code block ID. # @option options [Boolean] :drop # Whether this is a drop pattern. # @option options [String, nil] :pattern @@ -44,7 +42,6 @@ class Propane # Line number where the token was defined in the input grammar. def initialize(options) @code = options[:code] - @code_id = options[:code_id] @drop = options[:drop] @pattern = options[:pattern] @token = options[:token] diff --git a/spec/propane/grammar_spec.rb b/spec/propane/grammar_spec.rb index 7604e49..8f159ce 100644 --- a/spec/propane/grammar_spec.rb +++ b/spec/propane/grammar_spec.rb @@ -41,7 +41,6 @@ EOF expect(o).to_not be_nil expect(o.pattern).to eq "while" expect(o.line_number).to eq 6 - expect(o.code_id).to be_nil expect(o.code).to be_nil o = grammar.tokens.find {|token| token.name == "id"} @@ -53,7 +52,6 @@ EOF expect(o).to_not be_nil expect(o.pattern).to eq "[a-zA-Z_][a-zA-Z_0-9]*" expect(o.line_number).to eq 9 - expect(o.code_id).to be_nil expect(o.code).to be_nil o = grammar.tokens.find {|token| token.name == "token_with_code"} @@ -65,7 +63,6 @@ EOF expect(o).to_not be_nil expect(o.pattern).to eq "token_with_code" expect(o.line_number).to eq 11 - expect(o.code_id).to eq 0 expect(o.code).to eq "Code for the token\n" o = grammar.tokens.find {|token| token.name == "token_with_no_pattern"} @@ -79,7 +76,6 @@ EOF expect(o).to_not be_nil expect(o.line_number).to eq 17 expect(o.token).to be_nil - expect(o.code_id).to be_nil expect(o.code).to be_nil expect(grammar.rules.size).to eq 3 @@ -128,7 +124,6 @@ EOF o = grammar.patterns.find {|pattern| pattern.token == o} expect(o).to_not be_nil - expect(o.code_id).to eq 0 expect(o.code).to eq " a = b;\n return c;\n" o = grammar.tokens.find {|token| token.name == "code2"} @@ -138,7 +133,6 @@ EOF o = grammar.patterns.find {|pattern| pattern.token == o} expect(o).to_not be_nil - expect(o.code_id).to eq 1 expect(o.code).to eq %[ writeln("Hello there");\n] end end