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