lib/rley/notation/grammar_builder.rb in rley-0.8.00 vs lib/rley/notation/grammar_builder.rb in rley-0.8.01

- old
+ new

@@ -1,14 +1,18 @@ # frozen_string_literal: true require 'set' + require_relative 'parser' require_relative 'ast_visitor' require_relative '../syntax/match_closest' module Rley # This module is used as a namespace module Notation # This module is used as a namespace + # Structure used for production rules that are implicitly generated by Rley + RawRule = Struct.new(:lhs, :rhs, :tag, :simple, :constraints) + # Builder GoF pattern. Builder builds a complex object # (say, a grammar) from simpler objects (terminals and productions) # and using a step by step approach. class GrammarBuilder # @return [Hash{String, GrmSymbol}] The mapping of grammar symbol names @@ -22,29 +26,36 @@ attr_reader(:visitor2rhs) # @return [Array<Production>] The list of production rules for # the grammar to build. attr_reader(:productions) + + # @return [Hash{String, String}] The synthesized raw productions + attr_reader(:synthetized) # Creates a new grammar builder. # @param aBlock [Proc] code block used to build the grammar. # @example Building a tiny English grammar - # builder = Rley::Syntax::GrammarBuilder.new do + # builder = Rley::Notation::GrammarBuilder.new do # add_terminals('n', 'v', 'adj', 'det') - # rule 'S' => %w[NP VP] - # rule 'VP' => %w[v NP] - # rule 'NP' => %w[det n] - # rule 'NP' => %w[adj NP] + # rule 'S' => 'NP VP' + # rule 'VP' => 'v NP' + # rule 'NP' => 'det n' + # rule 'NP' => 'adj NP' # end # tiny_eng = builder.grammar def initialize(&aBlock) @symbols = {} @productions = [] @parser = Notation::Parser.new @visitor2rhs = {} + @synthetized = {} - instance_exec(&aBlock) if block_given? + if block_given? + instance_exec(&aBlock) + grammar_complete! + end end # Retrieve a grammar symbol from its name. # Raise an exception if not found. # @param aSymbolName [String] the name of a grammar symbol. @@ -58,10 +69,18 @@ # @return [void] def add_terminals(*terminalSymbols) new_symbs = build_symbols(Syntax::Terminal, terminalSymbols) symbols.merge!(new_symbs) end + + # Add the given marker symbol to the grammar of the language + # @param aMarkerSymbol [String] A mazker symbol + # @return [void] + def add_marker(aMarkerSymbol) + new_symb = build_symbol(Syntax::Marker, aMarkerSymbol) + symbols[new_symb.name] = new_symb + end # Add a production rule in the grammar given one # key-value pair of the form: String => String. # Where the key is the name of the non-terminal appearing in the # left side of the rule. @@ -202,47 +221,47 @@ mapping[aModifier] end ################################## - # AST visit notification events + # RGN's AST visit notification events # ################################ def after_symbol_node(aSymbolNode, aVisitor) symb_name = aSymbolNode.name case aSymbolNode.repetition when :zero_or_one # implicitly called: rule('symb_name_qmark' => 'symb_name_qmark').tag suffix_qmark_one # implicitly called: rule('symb_name_qmark' => '').tag suffix_qmark_none name_modified = "#{symb_name}#{suffix_qmark}" unless symbols.include? name_modified - symbols[name_modified] = Syntax::NonTerminal.new(name_modified) - rule(name_modified => "#{symb_name}" ).tag suffix_qmark_one - rule(name_modified => '').tag suffix_qmark_none + add_nonterminal(name_modified) + add_raw_rule(name_modified, "#{symb_name}", suffix_qmark_one) + add_raw_rule(name_modified, '', suffix_qmark_none) end symb_name = name_modified when :zero_or_more # implicitly called: rule('symb_name_star' => 'symb_name_star symb_name').tag suffix_star_more # implicitly called: rule('symb_name_star' => '').tag suffix_star_none name_modified = "#{symb_name}#{suffix_star}" unless symbols.include? name_modified - symbols[name_modified] = Syntax::NonTerminal.new(name_modified) - rule(name_modified => "#{name_modified} #{symb_name}").tag suffix_star_more - rule(name_modified => '').tag suffix_star_none + add_nonterminal(name_modified) + add_raw_rule(name_modified, "#{name_modified} #{symb_name}", suffix_star_more) + add_raw_rule(name_modified, [], suffix_star_none) end symb_name = name_modified when :exactly_one # Do nothing when :one_or_more name_modified = "#{symb_name}#{suffix_plus}" unless symbols.include? name_modified - symbols[name_modified] = Syntax::NonTerminal.new(name_modified) - rule(name_modified => "#{name_modified} #{symb_name}").tag suffix_plus_more - rule(name_modified => symb_name).tag suffix_plus_one + add_nonterminal(name_modified) + add_raw_rule(name_modified, "#{name_modified} #{symb_name}", suffix_plus_more) + add_raw_rule(name_modified, symb_name, suffix_plus_one) end symb_name = name_modified else raise StandardError, 'Unhandled multiplicity' end @@ -262,44 +281,43 @@ def after_grouping_node(aGroupingNode, aVisitor) after_sequence_node(aGroupingNode, aVisitor) symb_name = sequence_name(aGroupingNode) unless symbols.include?(symb_name) || aGroupingNode.repetition == :exactly_one - symbols[symb_name] = Syntax::NonTerminal.new(symb_name) - simple_rule(symb_name => serialize_sequence(aGroupingNode) ).tag 'return_children' - prod = productions.last - prod.constraints = aGroupingNode.constraints + add_nonterminal(symb_name) + rhs = serialize_sequence(aGroupingNode) + add_raw_rule(symb_name, rhs, 'return_children', true, aGroupingNode.constraints) end name_modified = "#{symb_name}#{repetition2suffix(aGroupingNode.repetition)}" case aGroupingNode.repetition when :zero_or_one # implicitly called: rule('symb_name_qmark' => 'symb_name_qmark').tag suffix_qmark_one # implicitly called: rule('symb_name_qmark' => '').tag suffix_qmark_none unless symbols.include? name_modified - symbols[name_modified] = Syntax::NonTerminal.new(name_modified) - simple_rule(name_modified => symb_name).tag suffix_qmark_one - simple_rule(name_modified => []).tag suffix_qmark_none + add_nonterminal(name_modified) + add_raw_rule(name_modified, symb_name, suffix_qmark_one, true) + add_raw_rule(name_modified, [], suffix_qmark_none, true) end when :zero_or_more # implicitly called: rule('symb_name_star' => 'symb_name_star symb_name').tag suffix_star_more # implicitly called: rule('symb_name_star' => '').tag suffix_star_none unless symbols.include? name_modified - symbols[name_modified] = Syntax::NonTerminal.new(name_modified) - rule(name_modified => "#{name_modified} #{symb_name}").tag suffix_star_more - rule(name_modified => '').tag suffix_star_none + add_nonterminal(name_modified) + add_raw_rule(name_modified, "#{name_modified} #{symb_name}", suffix_star_more) + add_raw_rule(name_modified, '', suffix_star_none) end when :exactly_one # Do nothing when :one_or_more unless symbols.include? name_modified - symbols[name_modified] = Syntax::NonTerminal.new(name_modified) - rule(name_modified => "#{name_modified} #{symb_name}").tag suffix_plus_more - rule(name_modified => symb_name).tag suffix_plus_one + add_nonterminal(name_modified) + add_raw_rule(name_modified, "#{name_modified} #{symb_name}", suffix_plus_more) + add_raw_rule(name_modified, symb_name, suffix_plus_one) end else raise StandardError, 'Unhandled multiplicity' end @@ -307,11 +325,21 @@ symb = get_grm_symbol(name_modified) visitor2rhs[aVisitor] << symb end end + # A notification to the builderobject that the programmer + # has completed the entry of terminals and production rules + def grammar_complete! + process_raw_rules() + end + private + + def add_nonterminal(aName) + symbols[aName] = Syntax::NonTerminal.new(aName) + end def simple_rule(aProductionRepr) aProductionRepr.each_pair do |(lhs_name, rhs_repr)| lhs = get_grm_symbol(lhs_name) @@ -441,9 +469,34 @@ suffix = suffix = repetition2suffix(sn.repetition) text << suffix end text.strip + end + + def add_raw_rule(aSymbol, aRHS, aTag, simplified = false, constraints = []) + raw_rule = RawRule.new(aSymbol, aRHS, aTag, simplified, constraints) + if synthetized.include?(aSymbol) + @synthetized[aSymbol] << raw_rule + else + @synthetized[aSymbol] = [raw_rule] + end + end + + def process_raw_rules + until synthetized.empty? do + raw_rules = synthetized.delete(synthetized.keys.first) + raw_rules.each do |raw| + new_prod = nil + if raw.simple + new_prod = simple_rule(raw.lhs => raw.rhs) + else + new_prod = rule(raw.lhs => raw.rhs) + end + new_prod.tag(raw.tag) + new_prod.constraints = raw.constraints + end + end end end # class end # module end # module