lib/antelope/generator/templates/ruby.ant in antelope-0.2.0 vs lib/antelope/generator/templates/ruby.ant in antelope-0.2.2

- old
+ new

@@ -1,146 +1,178 @@ - -# This file assumes that the output of the generator will be placed -# within a module or a class. However, the module/class requires a -# `type` method, which takes a terminal and gives its type, as a -# symbol. These types should line up with the terminals that were -# defined in the original grammar. - -# The actions to take during parsing. In every state, there are a -# set of acceptable peek tokens; this table tells the parser what -# to do on each acceptable peek token. The possible actions include -# `:accept`, `:reduce`, and `:state`; `:accept` means to accept the -# input and return the value of the pasing. `:reduce` means to -# reduce the top of the stack into a given nonterminal. `:state` -# means to transition to another state. -# -# @return [Array<Hash<(Symbol, Array<(Symbol, Numeric)>)>>] -ACTION_TABLE = {{= generate_action_table }}.freeze - -# A list of all of the productions. Only includes the left-hand side, -# the number of tokens on the right-hand side, and the block to call -# on reduction. -# -# @return [Array<Array<(Symbol, Numeric, Proc)>>] -PRODUCTIONS = {{= generate_productions_list }}.freeze - -# Runs the parser. -# -# @param input [Array<Object>] the input to run the parser over. -# @return [Object] the result of the accept. -def parse(input) - stack = [] - stack.push([nil, 0]) - input = input.dup - last = nil - - until stack.empty? do - last = parse_action(stack, input) - end - - last - -end - -# Actually performs the parsing action on the given stack on input. -# If you want to implement a push parser, than messing with this -# method is probably the way to go. -# -# @param stack [Array<Array<(Object, Numeric)>>] the stack of the -# parser. The actual order of the stack is important. -# @param input [Array<Object>] the input to run the parser over. -# The elements of this may be passed to the `type` method. -# @return [Object] the result of the last accepting reduction. -def parse_action(stack, input) - last = nil - peek_token = if input.empty? - :$end - else - type(input.first) - end - - action = ACTION_TABLE[stack.last.last].fetch(peek_token) do - ACTION_TABLE[stack.last.last].fetch(:$default) - end - case action.first - when :accept - production = PRODUCTIONS[action.last] - last = stack.pop(production[1]).first.first - stack.pop - when :reduce - production = PRODUCTIONS[action.last] - removing = stack.pop(production[1]) - value = instance_exec(*removing.map(&:first), &production[2]) - goto = ACTION_TABLE[stack.last.last][production[0]] - stack.push([value, goto.last]) - when :state - stack.push([input.shift, action.last]) - else - raise NotImplementedError, "Unknown action #{action.first}" - end - - last - -rescue KeyError => e - if handle_error( - { :stack => stack, - :peek => peek_token, - :remaining => input, - :error => e, - :expected => ACTION_TABLE[stack.last.last].keys - }) - retry - end -end - -{{ if define_own_handler? }} -def handle_error(data, _ = false) -{{ if panic_mode? }} - if _ || data[:peek] == :$end # we can't recover if - # we're at the end -{{ end }} - raise {{= error_class }}, - "Unexpected token #{data[:peek]}; " \ - "expected one of #{data[:expected].join(', ')}", - data[:error].backtrace -{{ if panic_mode? }} - end - - new_peek = :$error - acceptable_state = false - state = nil - - until data[:stack].empty? or acceptable_state - state = data[:stack].last.last - - if ACTION_TABLE[state].key?(new_peek) - acceptable_state = true - else - data[:stack].pop # discard - end - end - - return handle_error(data, true) unless acceptable_state - - action = ACTION_TABLE[state][new_peek] - lookaheads = nil - - until lookaheads - if action[0] == :state - lookaheads = ACTION_TABLE[action.last].keys - elsif action[0] == :reduce - rule = PRODUCTIONS[action.last] - action = ACTION_TABLE[stack[-rule[1]].last][rule[0]] - end - end - - until data[:remaining].empty? || lookaheads. - include?(data[:remaining][0].first) - data[:remaining].shift - end - - data[:remaining].unshift([new_peek, data[:error]]) - true - -{{ end }} -end -{{ end }} + +# This file assumes that the output of the generator will be placed +# within a module or a class. However, the module/class requires a +# `type` method, which takes a terminal and gives its type, as a +# symbol. These types should line up with the terminals that were +# defined in the original grammar. + +# The actions to take during parsing. In every state, there are a +# set of acceptable peek tokens; this table tells the parser what +# to do on each acceptable peek token. The possible actions include +# `:accept`, `:reduce`, and `:state`; `:accept` means to accept the +# input and return the value of the pasing. `:reduce` means to +# reduce the top of the stack into a given nonterminal. `:state` +# means to transition to another state. +# +# @return [Array<Hash<(Symbol, Array<(Symbol, Numeric)>)>>] +ACTION_TABLE = {{= generate_action_table }}.freeze + +# The default action that is taken for most reductions. +# +# @return [Proc] +DEFAULT_PROC = proc { |_| _ } +# A list of all of the productions. Only includes the left-hand side, +# the number of tokens on the right-hand side, and the block to call +# on reduction. +# +# @return [Array<Array<(Symbol, Numeric, Proc)>>] +PRODUCTIONS = {{= generate_productions_list }}.freeze + +# Runs the parser. +# +# @param input [Array] the input to run the parser over. +# @return [Object] the result of the accept. +def parse(input) + stack = [] + stack.push([nil, 0]) + last = nil + input = input.to_a.dup + + until stack.empty? do + last = parse_action(stack, input) + end + + last + +end + +# Actually performs the parsing action on the given stack on input. +# If you want to implement a push parser, than messing with this +# method is probably the way to go. +# +# @param stack [Array<Array<(Object, Numeric)>>] the stack of the +# parser. The actual order of the stack is important. +# @param input [Array<Object>] the input to run the parser over. +# The elements of this may be passed to the `type` method. +# @return [Object] the result of the last accepting reduction. +def parse_action(stack, input) + last = nil + peek_token = if input.empty? + :$end + else + type(input.first) + end + + action = ACTION_TABLE[stack.last.last].fetch(peek_token) do + ACTION_TABLE[stack.last.last].fetch(:$default) + end + + case action.first + when :accept + production = PRODUCTIONS[action.last] + last = stack.pop(production[1]).first.first + stack.pop + when :reduce + production = PRODUCTIONS[action.last] + removing = stack.pop(production[1]) + value = instance_exec(*removing.map(&:first), &production[2]) + goto = ACTION_TABLE[stack.last.last][production[0]] + stack.push([value, goto.last]) + when :state + stack.push([input.shift, action.last]) + else + raise NotImplementedError, "Unknown action #{action.first}" + end + + last + +rescue KeyError => e + peek = input.first + + if handle_error({ + :stack => stack, + :peek => peek_token, + :remaining => input, + :error => e, + :line => line_of(peek), + :column => column_of(peek), + :expected => ACTION_TABLE[stack.last.last].keys + }) + retry + else + raise + end +end + +private + +def line_of(peek) + if peek.respond_to?(:line) + peek.line + else + 0 + end +end + +def column_of(peek) + if peek.respond_to?(:column) + peek.column + else + 0 + end +end + +{{ if define_own_handler? }} +def handle_error(data, _ = false) +{{ if panic_mode? }} + if _ || data[:peek] == :$end # we can't recover if + # we're at the end +{{ end }} + raise {{= error_class }}, + "Unexpected token #{data[:peek]} on line #{data[:line]}, " \ + "column #{data[:column]}; expected one of " \ + "#{data[:expected].join(', ')}", + data[:error].backtrace +{{ if panic_mode? }} + end + + new_peek = :$error + acceptable_state = false + state = nil + + until data[:stack].empty? or acceptable_state + state = data[:stack].last.last + + if ACTION_TABLE[state].key?(new_peek) + acceptable_state = true + else + data[:stack].pop # discard + end + end + + return handle_error(data, true) unless acceptable_state + + action = ACTION_TABLE[state][new_peek] + lookaheads = nil + + until lookaheads + if action[0] == :state + lookaheads = ACTION_TABLE[action.last].keys + elsif action[0] == :reduce + rule = PRODUCTIONS[action.last] + action = ACTION_TABLE[stack[-rule[1]].last][rule[0]] + end + end + + begin + until lookaheads.include?(data[:remaining][0].peek) + data[:remaining].next + end + rescue StopIteration + end + + data[:remaining].unshift([new_peek, data[:error]]) + true + +{{ end }} +end +{{ end }}