Class: Rley::RGN::GrammarBuilder
- Inherits:
-
Object
- Object
- Rley::RGN::GrammarBuilder
- Defined in:
- lib/rley/rgn/grammar_builder.rb
Overview
Builder GoF pattern. Builder builds a complex object (say, a grammar) from simpler objects (terminals and productions) and using a step by step approach.
Instance Attribute Summary collapse
-
#parser ⇒ RGN::Parser
readonly
Parser for the right-side of productions.
-
#productions ⇒ Array<Production>
readonly
The list of production rules for the grammar to build.
-
#symbols ⇒ Hash{String, GrmSymbol}
readonly
The mapping of grammar symbol names to the matching grammar symbol object.
-
#synthetized ⇒ Hash{String, String}
readonly
The synthesized raw productions.
- #visitor2rhs ⇒ Hash{ASTVisitor, Array} readonly
Instance Method Summary collapse
-
#[](aSymbolName) ⇒ GrmSymbol
Retrieve a grammar symbol from its name.
-
#add_marker(aMarkerSymbol) ⇒ void
Add the given marker symbol to the grammar of the language.
-
#add_production(aProductionRepr) ⇒ Production
(also: #rule)
Add a production rule in the grammar given one key-value pair of the form: String => String.
-
#add_terminals(*terminalSymbols) ⇒ void
Add the given terminal symbols to the grammar of the language.
- #after_repetition_node(aRepNode, aVisitor) ⇒ Object
- #after_sequence_node(aSequenceNode, _visitor) ⇒ Object
-
#after_symbol_node(aSymbolNode, aVisitor) ⇒ Object
RGN's AST visit notification events ################################.
-
#grammar ⇒ Grammar
Given the grammar symbols and productions added to the builder, build the resulting grammar (if not yet done).
-
#grammar_complete! ⇒ Object
A notification to the builderobject that the programmer has completed the entry of terminals and production rules.
-
#initialize(&aBlock) ⇒ GrammarBuilder
constructor
Creates a new RGN grammar builder.
- #modifier2suffix(aModifier) ⇒ Object
- #repetition2suffix(aRepetition) ⇒ Object
-
#suffix_plus ⇒ Object
When a symbol, say symb, in a rhs is followed by a '+' modifier, then a rule will be generated with a lhs named symb + suffix_plus implicitly called: rule('digit_plus' => 'digit_plus digit').tag suffix_plus_more implicitly called: rule('digit_plus' => 'digit').tag suffix_plus_last.
- #suffix_plus_more ⇒ Object
- #suffix_plus_one ⇒ Object
-
#suffix_qmark ⇒ Object
When a symbol, say symb, in a rhs is followed by a '*' modifier, then a rule will be generated with a lhs named symb * suffix_plus implicitly called: rule('declaration_star' => 'declaration_star declaration').tag suffix_star_more implicitly called: rule('declaration_star' => '').tag suffix_star_last.
- #suffix_qmark_none ⇒ Object
- #suffix_qmark_one ⇒ Object
-
#suffix_star ⇒ Object
When a symbol, say symb, in a rhs is followed by a '*' modifier, then a rule will be generated with a lhs named symb * suffix_plus implicitly called: rule('declaration_star' => 'declaration_star declaration').tag suffix_star_more implicitly called: rule('declaration_star' => '').tag suffix_star_last.
- #suffix_star_more ⇒ Object
- #suffix_star_none ⇒ Object
Constructor Details
#initialize(&aBlock) ⇒ GrammarBuilder
Creates a new RGN grammar builder.
38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/rley/rgn/grammar_builder.rb', line 38 def initialize(&aBlock) @symbols = {} @productions = [] @parser = RGN::Parser.new @visitor2rhs = {} @synthetized = {} if block_given? instance_exec(&aBlock) grammar_complete! end end |
Instance Attribute Details
#parser ⇒ RGN::Parser (readonly)
Returns Parser for the right-side of productions.
24 25 26 |
# File 'lib/rley/rgn/grammar_builder.rb', line 24 def parser @parser end |
#productions ⇒ Array<Production> (readonly)
Returns The list of production rules for the grammar to build.
31 32 33 |
# File 'lib/rley/rgn/grammar_builder.rb', line 31 def productions @productions end |
#symbols ⇒ Hash{String, GrmSymbol} (readonly)
Returns The mapping of grammar symbol names to the matching grammar symbol object.
21 22 23 |
# File 'lib/rley/rgn/grammar_builder.rb', line 21 def symbols @symbols end |
#synthetized ⇒ Hash{String, String} (readonly)
Returns The synthesized raw productions.
34 35 36 |
# File 'lib/rley/rgn/grammar_builder.rb', line 34 def synthetized @synthetized end |
#visitor2rhs ⇒ Hash{ASTVisitor, Array} (readonly)
27 28 29 |
# File 'lib/rley/rgn/grammar_builder.rb', line 27 def visitor2rhs @visitor2rhs end |
Instance Method Details
#[](aSymbolName) ⇒ GrmSymbol
Retrieve a grammar symbol from its name. Raise an exception if not found.
55 56 57 |
# File 'lib/rley/rgn/grammar_builder.rb', line 55 def [](aSymbolName) symbols[aSymbolName] end |
#add_marker(aMarkerSymbol) ⇒ void
This method returns an undefined value.
Add the given marker symbol to the grammar of the language
70 71 72 73 |
# File 'lib/rley/rgn/grammar_builder.rb', line 70 def add_marker(aMarkerSymbol) new_symb = build_symbol(Syntax::Marker, aMarkerSymbol) symbols[new_symb.name] = new_symb end |
#add_production(aProductionRepr) ⇒ Production Also known as: rule
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. The value is a sequence of grammar symbol names (optionally quantified). The rule is created and inserted in the grammar.
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/rley/rgn/grammar_builder.rb', line 87 def add_production(aProductionRepr) aProductionRepr.each_pair do |(lhs_name, rhs_repr)| lhs = get_grm_symbol(lhs_name) rhs = rhs_repr.kind_of?(Array) && rhs_repr.empty? ? '' : rhs_repr.strip constraints = [] if rhs.empty? rhs_members = [] else ast = parser.parse(rhs) visitor = ASTVisitor.new(ast) visitor2rhs[visitor] = [] visitor.subscribe(self) visitor.start root_node = ast.root constraints = root_node.constraints unless root_node.kind_of?(SymbolNode) rhs_members = visitor2rhs.delete(visitor) end new_prod = Syntax::Production.new(lhs, rhs_members) new_prod.constraints = constraints productions << new_prod end productions.last end |
#add_terminals(*terminalSymbols) ⇒ void
This method returns an undefined value.
Add the given terminal symbols to the grammar of the language
62 63 64 65 |
# File 'lib/rley/rgn/grammar_builder.rb', line 62 def add_terminals(*terminalSymbols) new_symbs = build_symbols(Syntax::Terminal, terminalSymbols) symbols.merge!(new_symbs) end |
#after_repetition_node(aRepNode, aVisitor) ⇒ Object
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
# File 'lib/rley/rgn/grammar_builder.rb', line 230 def after_repetition_node(aRepNode, aVisitor) add_constraints(aRepNode) return if aRepNode.repetition == :exactly_one node_name = aRepNode.name child_name = aRepNode.subnodes[0].name if aRepNode.child.is_a?(SequenceNode) && !symbols.include?(child_name) && aRepNode.repetition != :zero_or_one add_nonterminal(child_name) rhs = aRepNode.child.to_text add_raw_rule(child_name, rhs, 'return_children', true) end case aRepNode.repetition when :zero_or_one # implicitly called: rule('node_name_qmark' => 'node_name_qmark').tag suffix_qmark_one # implicitly called: rule('node_name_qmark' => '').tag suffix_qmark_none unless symbols.include? node_name add_nonterminal(node_name) if aRepNode.child.is_a?(SequenceNode) && !aRepNode.child.constraints.empty? aRepNode.constraints.merge(aRepNode.child.constraints) end rhs = aRepNode.child.to_text add_raw_rule(node_name, rhs, 'return_children', false, aRepNode.constraints) add_raw_rule(node_name, [], suffix_qmark_none, true) end when :zero_or_more # implicitly called: rule('node_name_star' => 'node_name_star node_name').tag suffix_star_more # implicitly called: rule('node_name_star' => '').tag suffix_star_none unless symbols.include? node_name add_nonterminal(node_name) rhs = "#{node_name} #{child_name}" add_raw_rule(node_name, rhs, suffix_star_more) add_raw_rule(node_name, '', suffix_star_none) end when :one_or_more unless symbols.include? node_name add_nonterminal(node_name) add_raw_rule(node_name, "#{node_name} #{child_name}", suffix_plus_more) add_raw_rule(node_name, child_name, suffix_plus_one) end else raise StandardError, 'Unhandled multiplicity' end symb = get_grm_symbol(node_name) visitor2rhs[aVisitor] << symb end |
#after_sequence_node(aSequenceNode, _visitor) ⇒ Object
226 227 228 |
# File 'lib/rley/rgn/grammar_builder.rb', line 226 def after_sequence_node(aSequenceNode, _visitor) add_constraints(aSequenceNode) end |
#after_symbol_node(aSymbolNode, aVisitor) ⇒ Object
RGN's AST visit notification events
220 221 222 223 224 |
# File 'lib/rley/rgn/grammar_builder.rb', line 220 def after_symbol_node(aSymbolNode, aVisitor) symb_name = aSymbolNode.name symb = get_grm_symbol(symb_name) visitor2rhs[aVisitor] << symb end |
#grammar ⇒ Grammar
Given the grammar symbols and productions added to the builder, build the resulting grammar (if not yet done).
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/rley/rgn/grammar_builder.rb', line 116 def grammar unless @grammar raise StandardError, 'No symbol found for grammar' if symbols.empty? if productions.empty? raise StandardError, 'No production found for grammar' end # Check that each terminal appears at least in a rhs of a production all_terminals = symbols.values.select do |a_symb| a_symb.kind_of?(Syntax::Terminal) end in_use = Set.new productions.each do |prod| prod.rhs.members.each do |symb| in_use << symb if symb.kind_of?(Syntax::Terminal) end end unused = all_terminals.reject { |a_term| in_use.include?(a_term) } unless unused.empty? suffix = "#{unused.map(&:name).join(', ')}." raise StandardError, "Useless terminal symbol(s): #{suffix}" end @grammar = Syntax::Grammar.new(productions.dup) end @grammar end |
#grammar_complete! ⇒ Object
A notification to the builderobject that the programmer has completed the entry of terminals and production rules
284 285 286 |
# File 'lib/rley/rgn/grammar_builder.rb', line 284 def grammar_complete! process_raw_rules end |
#modifier2suffix(aModifier) ⇒ Object
207 208 209 210 211 212 213 214 215 |
# File 'lib/rley/rgn/grammar_builder.rb', line 207 def modifier2suffix(aModifier) mapping = { '?' => suffix_qmark, '*' => suffix_star, '+' => suffix_plus } mapping[aModifier] end |
#repetition2suffix(aRepetition) ⇒ Object
196 197 198 199 200 201 202 203 204 205 |
# File 'lib/rley/rgn/grammar_builder.rb', line 196 def repetition2suffix(aRepetition) mapping = { zero_or_one: suffix_qmark, zero_or_more: suffix_star, exactly_one: '', one_or_more: suffix_plus } mapping[aRepetition] end |
#suffix_plus ⇒ Object
When a symbol, say symb, in a rhs is followed by a '+' modifier, then a rule will be generated with a lhs named symb + suffix_plus implicitly called: rule('digit_plus' => 'digit_plus digit').tag suffix_plus_more implicitly called: rule('digit_plus' => 'digit').tag suffix_plus_last
184 185 186 |
# File 'lib/rley/rgn/grammar_builder.rb', line 184 def suffix_plus '_plus' end |
#suffix_plus_more ⇒ Object
188 189 190 |
# File 'lib/rley/rgn/grammar_builder.rb', line 188 def suffix_plus_more '_plus_more' end |
#suffix_plus_one ⇒ Object
192 193 194 |
# File 'lib/rley/rgn/grammar_builder.rb', line 192 def suffix_plus_one '_plus_one' end |
#suffix_qmark ⇒ Object
When a symbol, say symb, in a rhs is followed by a '*' modifier, then a rule will be generated with a lhs named symb * suffix_plus implicitly called: rule('declaration_star' => 'declaration_star declaration').tag suffix_star_more implicitly called: rule('declaration_star' => '').tag suffix_star_last
152 153 154 |
# File 'lib/rley/rgn/grammar_builder.rb', line 152 def suffix_qmark '_qmark' end |
#suffix_qmark_none ⇒ Object
160 161 162 |
# File 'lib/rley/rgn/grammar_builder.rb', line 160 def suffix_qmark_none '_qmark_none' end |
#suffix_qmark_one ⇒ Object
156 157 158 |
# File 'lib/rley/rgn/grammar_builder.rb', line 156 def suffix_qmark_one '_qmark_one' end |
#suffix_star ⇒ Object
When a symbol, say symb, in a rhs is followed by a '*' modifier, then a rule will be generated with a lhs named symb * suffix_plus implicitly called: rule('declaration_star' => 'declaration_star declaration').tag suffix_star_more implicitly called: rule('declaration_star' => '').tag suffix_star_last
168 169 170 |
# File 'lib/rley/rgn/grammar_builder.rb', line 168 def suffix_star '_star' end |
#suffix_star_more ⇒ Object
172 173 174 |
# File 'lib/rley/rgn/grammar_builder.rb', line 172 def suffix_star_more '_star_more' end |
#suffix_star_none ⇒ Object
176 177 178 |
# File 'lib/rley/rgn/grammar_builder.rb', line 176 def suffix_star_none '_star_none' end |