From 30f4cfcc9923aba686c9aa0ee6a94116a56bd3c3 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Sun, 26 Jun 2022 11:06:55 -0400 Subject: [PATCH] Write parser log file Fix bug of skipping rule set IDs. Remove unneeded out_sets from ItemSet class. --- lib/propane.rb | 2 + lib/propane/generator.rb | 20 ++++---- lib/propane/parser.rb | 84 ++++++++++++++++++++++------------ lib/propane/parser/item_set.rb | 5 -- lib/propane/rule.rb | 8 ++++ lib/propane/util.rb | 17 +++++++ spec/propane_spec.rb | 2 +- 7 files changed, 95 insertions(+), 43 deletions(-) create mode 100644 lib/propane/util.rb diff --git a/lib/propane.rb b/lib/propane.rb index 46ba0bd..37236d3 100644 --- a/lib/propane.rb +++ b/lib/propane.rb @@ -1,5 +1,6 @@ require "erb" require "set" +require "stringio" require_relative "propane/cli" require_relative "propane/code_point_range" require_relative "propane/fa" @@ -18,6 +19,7 @@ require_relative "propane/regex/unit" require_relative "propane/rule_set" require_relative "propane/rule" require_relative "propane/token" +require_relative "propane/util" require_relative "propane/version" class Propane diff --git a/lib/propane/generator.rb b/lib/propane/generator.rb index ef5953b..0b3456b 100644 --- a/lib/propane/generator.rb +++ b/lib/propane/generator.rb @@ -5,7 +5,11 @@ class Propane def initialize(grammar, output_file, log_file) @grammar = grammar @output_file = output_file - @log_file = log_file + if log_file + @log = File.open(log_file, "wb") + else + @log = StringIO.new + end @classname = @grammar.classname || File.basename(output_file).sub(%r{[^a-zA-Z0-9].*}, "").capitalize process_grammar! end @@ -16,6 +20,7 @@ class Propane File.open(@output_file, "wb") do |fh| fh.write(result) end + @log.close end private @@ -34,8 +39,10 @@ class Propane raise Error.new("Rule name collides with token name #{rule.name.inspect}") end @_rule_set_id ||= @grammar.tokens.size - rule_sets[rule.name] ||= RuleSet.new(rule.name, @_rule_set_id) - @_rule_set_id += 1 + unless rule_sets[rule.name] + rule_sets[rule.name] = RuleSet.new(rule.name, @_rule_set_id) + @_rule_set_id += 1 + end rule.rule_set = rule_sets[rule.name] rule_sets[rule.name] << rule end @@ -54,13 +61,8 @@ class Propane end end determine_possibly_empty_rulesets!(rule_sets) - puts "Start token set" - rule_sets.each do |rule_set_name, rule_set| - puts "RuleSet #{rule_set_name}:" - puts " " + rule_set.start_token_set.map(&:name).join(", ") - end @lexer = Lexer.new(@grammar.tokens, @grammar.drop_tokens) - @parser = Parser.new(rule_sets["Start"]) + @parser = Parser.new(@grammar, rule_sets, rule_sets["Start"], @log) end # Determine which grammar rules could expand to empty sequences. diff --git a/lib/propane/parser.rb b/lib/propane/parser.rb index 8047e94..79a8851 100644 --- a/lib/propane/parser.rb +++ b/lib/propane/parser.rb @@ -2,12 +2,15 @@ class Propane class Parser - def initialize(start_rule_set) + def initialize(grammar, rule_sets, start_rule_set, log) + @grammar = grammar + @rule_sets = rule_sets + @log = log @eof_token = Token.new(name: "$", id: TOKEN_EOF) - start_rule = Rule.new("$$", [start_rule_set, @eof_token], nil, nil, 0) + @start_rule = Rule.new("$$", [start_rule_set, @eof_token], nil, nil, 0) @item_sets = [] @item_sets_set = {} - start_item = Item.new(start_rule, 0) + start_item = Item.new(@start_rule, 0) eval_item_sets = Set[ItemSet.new([start_item])] while eval_item_sets.size > 0 @@ -31,30 +34,7 @@ class Propane end build_reduce_actions! - - @item_sets.each do |item_set| - puts "Item set #{item_set.id}:" - ids = item_set.in_sets.map(&:id) - puts " In sets: #{ids.join(", ")}" - puts " Out sets:" - item_set.out_sets.each do |symbol, out_set| - puts " #{symbol.name} => #{out_set.id}" - end - puts item_set - item_set.following_item_set.each do |following_symbol, following_item_set| - puts " #{following_symbol.name} => #{following_item_set.id}" - end - puts " Reduce actions:" - case item_set.reduce_actions - when Rule - puts " * => #{item_set.reduce_actions.id} (#{item_set.reduce_actions.name})" - when Hash - item_set.reduce_actions.each do |token, rule| - puts " #{token.name} => #{rule.id} (#{rule.name})" - end - end - puts - end + write_log! end def build_tables @@ -107,7 +87,6 @@ class Propane following_set = @item_sets_set[item_set.build_following_item_set(following_symbol)] item_set.following_item_set[following_symbol] = following_set following_set.in_sets << item_set - item_set.out_sets[following_symbol] = following_set end end end @@ -225,6 +204,55 @@ class Propane lookahead_tokens end + def write_log! + @log.puts Util.banner("Parser Rules") + ([@start_rule] + @grammar.rules).each do |rule| + @log.puts + @log.puts "Rule #{rule.id}:" + @log.puts " #{rule}" + end + + @log.puts + @log.puts Util.banner("Parser Tokens") + @log.puts + @grammar.tokens.each do |token| + @log.puts "Token #{token.id}: #{token.name}" + end + + @log.puts + @log.puts Util.banner("Parser Rule Sets") + @rule_sets.each do |rule_set_name, rule_set| + @log.puts + @log.puts "Rule Set #{rule_set.id}: #{rule_set_name}" + @log.puts " Start token set: #{rule_set.start_token_set.map(&:name).join(", ")}" + end + + @log.puts + @log.puts Util.banner("Parser States") + @item_sets.each do |item_set| + @log.puts + @log.puts "State #{item_set.id}:" + @log.puts item_set.to_s.gsub(/^/, " ") + incoming_ids = item_set.in_sets.map(&:id) + @log.puts + @log.puts " Incoming states: #{incoming_ids.join(", ")}" + @log.puts " Outgoing states:" + item_set.following_item_set.each do |following_symbol, following_item_set| + @log.puts " #{following_symbol.name} => #{following_item_set.id}" + end + @log.puts + @log.puts " Reduce actions:" + case item_set.reduce_actions + when Rule + @log.puts " * => #{item_set.reduce_actions.id} (#{item_set.reduce_actions.name})" + when Hash + item_set.reduce_actions.each do |token, rule| + @log.puts " lookahead #{token.name} => #{rule.name} (#{rule.id}), rule set ##{rule.rule_set.id}" + end + end + end + end + end end diff --git a/lib/propane/parser/item_set.rb b/lib/propane/parser/item_set.rb index 3cb4b1f..661f58b 100644 --- a/lib/propane/parser/item_set.rb +++ b/lib/propane/parser/item_set.rb @@ -21,10 +21,6 @@ class Propane # ItemSets leading to this item set. attr_reader :in_sets - # @return [Hash] - # ItemSets reached from this item set. Key is a Token or Rule. - attr_reader :out_sets - # @return [nil, Rule, Hash] # Reduce actions, mapping lookahead tokens to rules. attr_accessor :reduce_actions @@ -37,7 +33,6 @@ class Propane @items = Set.new(items) @following_item_set = {} @in_sets = Set.new - @out_sets = {} close! end diff --git a/lib/propane/rule.rb b/lib/propane/rule.rb index c2cc759..f3e7d95 100644 --- a/lib/propane/rule.rb +++ b/lib/propane/rule.rb @@ -56,6 +56,14 @@ class Propane @components.empty? end + # Represent the Rule as a String. + # + # @return [String] + # Rule represented as a String. + def to_s + "#{@name} -> #{@components.map(&:name).join(" ")}" + end + end end diff --git a/lib/propane/util.rb b/lib/propane/util.rb new file mode 100644 index 0000000..b796104 --- /dev/null +++ b/lib/propane/util.rb @@ -0,0 +1,17 @@ +class Propane + + # Utility methods. + module Util + + class << self + + def banner(message) + s = "*" * (message.size + 4) + "#{s}\n* #{message} *\n#{s}\n" + end + + end + + end + +end diff --git a/spec/propane_spec.rb b/spec/propane_spec.rb index a7a0dcd..ededea8 100644 --- a/spec/propane_spec.rb +++ b/spec/propane_spec.rb @@ -6,7 +6,7 @@ describe Propane do end def build_parser - result = system(*%w[./propane.sh spec/run/testparser.propane spec/run/testparser.d]) + result = system(*%w[./propane.sh spec/run/testparser.propane spec/run/testparser.d --log spec/run/testparser.log]) expect(result).to be_truthy end