Class: Rly::Yacc
- Inherits:
-
Object
- Object
- Rly::Yacc
- Defined in:
- lib/rly/yacc.rb,
lib/rly/helpers.rb
Direct Known Subclasses
Class Attribute Summary collapse
-
.error_handler ⇒ Object
Returns the value of attribute error_handler.
-
.grammar ⇒ Object
Returns the value of attribute grammar.
-
.lexer_class ⇒ Object
Returns the value of attribute lexer_class.
-
.prec_rules ⇒ Object
Returns the value of attribute prec_rules.
-
.rules ⇒ Object
Returns the value of attribute rules.
-
.store_grammar_def ⇒ Object
Returns the value of attribute store_grammar_def.
Instance Attribute Summary collapse
-
#lex ⇒ Object
readonly
Returns the value of attribute lex.
-
#lr_table ⇒ Object
readonly
Returns the value of attribute lr_table.
Class Method Summary collapse
- .assign_rhs(idx = 1) ⇒ Object
- .collect_to_a ⇒ Object
- .error_count ⇒ Object
- .lexer(&block) ⇒ Object
- .on_error(lambda) ⇒ Object
- .parsed_rules ⇒ Object
- .precedence(*prec) ⇒ Object
- .rule(desc, &block) ⇒ Object
- .store_grammar(fn) ⇒ Object
- .with_values ⇒ Object
Instance Method Summary collapse
-
#initialize(lex = nil) ⇒ Yacc
constructor
A new instance of Yacc.
- #inspect ⇒ Object
- #parse(input = nil, trace = false) ⇒ Object
Constructor Details
#initialize(lex = nil) ⇒ Yacc
Returns a new instance of Yacc.
13 14 15 16 17 18 |
# File 'lib/rly/yacc.rb', line 13 def initialize(lex=nil) raise ArgumentError.new("No lexer available") if lex == nil && self.class.lexer_class == nil @lex = lex || self.class.lexer_class.new @grammar = grammar end |
Class Attribute Details
.error_handler ⇒ Object
Returns the value of attribute error_handler.
322 323 324 |
# File 'lib/rly/yacc.rb', line 322 def error_handler @error_handler end |
.grammar ⇒ Object
Returns the value of attribute grammar.
322 323 324 |
# File 'lib/rly/yacc.rb', line 322 def grammar @grammar end |
.lexer_class ⇒ Object
Returns the value of attribute lexer_class.
322 323 324 |
# File 'lib/rly/yacc.rb', line 322 def lexer_class @lexer_class end |
.prec_rules ⇒ Object
Returns the value of attribute prec_rules.
322 323 324 |
# File 'lib/rly/yacc.rb', line 322 def prec_rules @prec_rules end |
.rules ⇒ Object
Returns the value of attribute rules.
322 323 324 |
# File 'lib/rly/yacc.rb', line 322 def rules @rules end |
.store_grammar_def ⇒ Object
Returns the value of attribute store_grammar_def.
322 323 324 |
# File 'lib/rly/yacc.rb', line 322 def store_grammar_def @store_grammar_def end |
Instance Attribute Details
#lex ⇒ Object (readonly)
Returns the value of attribute lex.
11 12 13 |
# File 'lib/rly/yacc.rb', line 11 def lex @lex end |
#lr_table ⇒ Object (readonly)
Returns the value of attribute lr_table.
11 12 13 |
# File 'lib/rly/yacc.rb', line 11 def lr_table @lr_table end |
Class Method Details
.assign_rhs(idx = 1) ⇒ Object
32 33 34 35 36 37 38 |
# File 'lib/rly/helpers.rb', line 32 def self.assign_rhs(idx=1) ->(*args) { ret = args.shift idx -= 1 ret.value = args[idx] ? args[idx].value : nil } end |
.collect_to_a ⇒ Object
40 41 42 43 44 45 46 |
# File 'lib/rly/helpers.rb', line 40 def self.collect_to_a ->(ret, val, *args) { ret.value = [val.value] vals = args[-1] ret.value += vals.value if vals } end |
.error_count ⇒ Object
353 354 355 |
# File 'lib/rly/yacc.rb', line 353 def error_count 3 end |
.lexer(&block) ⇒ Object
333 334 335 336 |
# File 'lib/rly/yacc.rb', line 333 def lexer(&block) @lexer_class = Class.new(Lex, &block) nil end |
.on_error(lambda) ⇒ Object
357 358 359 |
# File 'lib/rly/yacc.rb', line 357 def on_error(lambda) @error_handler = lambda end |
.parsed_rules ⇒ Object
361 362 363 364 365 366 367 368 369 370 371 372 373 374 |
# File 'lib/rly/yacc.rb', line 361 def parsed_rules return @parsed_rules if @parsed_rules @parsed_rules = [] rp = RuleParser.new self.rules.each do |desc, block| rules = rp.parse(desc) raise RuntimeError.new("Failed to parse rules: #{desc}") unless rules rules.each do |(pname, p, prec)| @parsed_rules << [pname, p, prec, block] end end @parsed_rules end |
.precedence(*prec) ⇒ Object
342 343 344 345 346 347 |
# File 'lib/rly/yacc.rb', line 342 def precedence(*prec) assoc = prec.shift count = self.prec_rules.length + 1 self.prec_rules << [assoc, prec, count] nil end |
.rule(desc, &block) ⇒ Object
328 329 330 331 |
# File 'lib/rly/yacc.rb', line 328 def rule(desc, &block) self.rules << [desc, block] nil end |
.store_grammar(fn) ⇒ Object
324 325 326 |
# File 'lib/rly/yacc.rb', line 324 def store_grammar(fn) @store_grammar_def = fn end |
.with_values ⇒ Object
24 25 26 27 28 29 30 |
# File 'lib/rly/helpers.rb', line 24 def self.with_values raise ArgumentError.new("Must pass a block") unless block_given? ->(*args) { ret = args.shift ret.value = yield *args.map { |a| a.value } } end |
Instance Method Details
#inspect ⇒ Object
20 21 22 |
# File 'lib/rly/yacc.rb', line 20 def inspect "#<#{self.class} ...>" end |
#parse(input = nil, trace = false) ⇒ Object
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 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 112 113 114 115 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 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 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 281 282 283 284 285 286 |
# File 'lib/rly/yacc.rb', line 24 def parse(input=nil, trace=false) @trace = trace lookahead = nil lookaheadstack = [] actions = @lr_table.lr_action goto = @lr_table.lr_goto prod = @lr_table.lr_productions pslice = YaccProduction.new(nil) errorcount = 0 # Set up the lexer and parser objects on pslice pslice.lexer = @lex pslice.parser = self # If input was supplied, pass to lexer @lex.input(input) if input # Set up the state and symbol stacks @statestack = [] @symstack = [] pslice.stack = @symstack errtoken = nil # The start state is assumed to be (0,$end) @statestack.push(0) sym = YaccSymbol.new sym.type = :"$end" @symstack.push(sym) state = 0 while true # Get the next symbol on the input. If a lookahead symbol # is already set, we just use that. Otherwise, we'll pull # the next token off of the lookaheadstack or from the lexer puts "State : #{state}" if @trace unless lookahead if lookaheadstack.empty? lookahead = @lex.next else lookahead = lookaheadstack.pop end unless lookahead lookahead = YaccSymbol.new() lookahead.type = :"$end" end end puts "Stack : #{(@symstack[1..-1].map{|s|s.type}.join(' ') + ' ' + lookahead.inspect).lstrip}" if @trace # Check the action table ltype = lookahead.type t = actions[state][ltype] if t if t > 0 # shift a symbol on the stack @statestack.push(t) state = t puts "Action : Shift and goto state #{t}" if @trace @symstack.push(lookahead) lookahead = nil # Decrease error count on successful shift errorcount -= 1 if errorcount > 0 next end if t < 0 # reduce a symbol on the stack, emit a production p = prod[-t] pname = p.name plen = p.length # Get production function sym = YaccSymbol.new() sym.type = pname sym.value = nil if @trace if plen puts "Action : Reduce rule [#{p}] with [#{@symstack[-plen..@symstack.length].map{|s|s.inspect}.join(', ')}] and goto state #{-t}" else puts "Action : Reduce rule [#{p}] with [] and goto state #{-t}" end end if plen targ = @symstack.pop(plen) targ.insert(0, sym) # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # The code enclosed in this section is duplicated # below as a performance optimization. Make sure # changes get made in both locations. pslice.slice = targ begin # Call the grammar rule with our special slice object @statestack.pop(plen) instance_exec(*targ, &p.block) puts "Result : #{targ[0].inspect}" if @trace @symstack.push(sym) state = goto[@statestack[-1]][pname] @statestack.push(state) rescue YaccError # If an error was set. Enter error recovery state lookaheadstack.push(lookahead) @symstack.pop # FIXME: this is definitely broken @statestack.pop state = @statestack[-1] sym.type = :error lookahead = sym errorcount = self.class.error_count @errorok = false end next # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! else targ = [ sym ] # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # The code enclosed in this section is duplicated # below as a performance optimization. Make sure # changes get made in both locations. pslice.slice = targ begin # Call the grammar rule with our special slice object @statestack.pop(plen) pslice[0] = instance_exec(*pslice, &p.block) puts "Result : #{targ[0].value}" if @trace @symstack.push(sym) state = goto[@statestack[-1]][pname] @statestack.push(state) rescue # If an error was set. Enter error recovery state lookaheadstack.push(lookahead) @symstack.pop # FIXME: this is definitely broken @statestack.pop state = @statestack[-1] sym.type = :error lookahead = sym errorcount = error_count @errorok = false end next # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! end end if t == 0 n = @symstack[-1] result = n.value puts "Done : Returning #{result}" if @trace return result end end if t == nil # We have some kind of parsing error here. To handle # this, we are going to push the current token onto # the tokenstack and replace it with an 'error' token. # If there are any synchronization rules, they may # catch it. # # In addition to pushing the error token, we call call # the user defined p_error() function if this is the # first syntax error. This function is only called if # errorcount == 0. if errorcount == 0 || @errorok == true errorcount = self.class.error_count @errorok = false errtoken = lookahead errtoken = nil if errtoken.type == :"$end" if self.class.error_handler tok = self.instance_exec(errtoken, &self.class.error_handler) if @errorok # User must have done some kind of panic # mode recovery on their own. The # returned token is the next lookahead lookahead = tok errtoken = nil next end else if errtoken location_info = lookahead.location_info puts "Fail : Syntax error at #{location_info}, token='#{errtoken}'" if @trace else puts "Fail : Parse error in input. EOF" if @trace return nil end end else errorcount = self.class.error_count end # case 1: the @statestack only has 1 entry on it. If we're in this state, the # entire parse has been rolled back and we're completely hosed. The token is # discarded and we just keep going. if @statestack.length <= 1 and lookahead.type != :"$end" lookahead = nil errtoken = nil state = 0 # Nuke the pushback stack lookaheadstack = [] next end # case 2: the @statestack has a couple of entries on it, but we're # at the end of the file. nuke the top entry and generate an error token # Start nuking entries on the stack if lookahead.type == :"$end" # Whoa. We're really hosed here. Bail out return nil end if lookahead.type != :error sym = @symstack[-1] if sym.type == :error # Hmmm. Error is on top of stack, we'll just nuke input # symbol and continue lookahead = nil next end t = YaccSymbol.new t.type = :error # if hasattr(lookahead,"lineno"): # t.lineno = lookahead.lineno t.value = lookahead lookaheadstack.push(lookahead) lookahead = t else @symstack.pop @statestack.pop state = @statestack[-1] # Potential bug fix end next end # Call an error function here raise RuntimeError.new("yacc: internal parser error!!!") end end |