Class: Parslet::Atoms::Infix
Constant Summary
Constants included from Precedence
Precedence::ALTERNATE, Precedence::BASE, Precedence::LOOKAHEAD, Precedence::OUTER, Precedence::REPETITION, Precedence::SEQUENCE
Instance Attribute Summary collapse
-
#element ⇒ Object
readonly
Returns the value of attribute element.
-
#operations ⇒ Object
readonly
Returns the value of attribute operations.
-
#reducer ⇒ Object
readonly
Returns the value of attribute reducer.
Attributes inherited from Base
Instance Method Summary collapse
-
#initialize(element, operations, &reducer) ⇒ Infix
constructor
A new instance of Infix.
- #match_operation(source, context, consume_all) ⇒ Object
-
#precedence_climb(source, context, consume_all, current_prec = 1, needs_element = false) ⇒ Object
A precedence climbing algorithm married to parslet, as described here eli.thegreenplace.net/2012/08/02/parsing-expressions-by-precedence-climbing/.
-
#produce_tree(ary) ⇒ Object
Turns an array of the form [‘1’, ‘+’, [‘2’, ‘*’, ‘3’]] into a hash that reflects the same structure.
- #to_s_inner(prec) ⇒ Object
- #try(source, context, consume_all) ⇒ Object
- #unwrap(expr) ⇒ Object
Methods inherited from Base
#accept, #apply, #cached?, #inspect, #parse, #parse_with_debug, precedence, #setup_and_apply, #to_s
Methods included from CanFlatten
#flatten, #flatten_repetition, #flatten_sequence, #foldl, #merge_fold, #warn_about_duplicate_keys
Methods included from DSL
#>>, #absent?, #as, #capture, #ignore, #maybe, #present?, #repeat, #|
Constructor Details
#initialize(element, operations, &reducer) ⇒ Infix
Returns a new instance of Infix.
4 5 6 7 8 9 10 |
# File 'lib/parslet/atoms/infix.rb', line 4 def initialize(element, operations, &reducer) super() @element = element @operations = operations @reducer = reducer || lambda { |left, op, right| {l: left, o: op, r: right} } end |
Instance Attribute Details
#element ⇒ Object (readonly)
Returns the value of attribute element.
2 3 4 |
# File 'lib/parslet/atoms/infix.rb', line 2 def element @element end |
#operations ⇒ Object (readonly)
Returns the value of attribute operations.
2 3 4 |
# File 'lib/parslet/atoms/infix.rb', line 2 def operations @operations end |
#reducer ⇒ Object (readonly)
Returns the value of attribute reducer.
2 3 4 |
# File 'lib/parslet/atoms/infix.rb', line 2 def reducer @reducer end |
Instance Method Details
#match_operation(source, context, consume_all) ⇒ Object
98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/parslet/atoms/infix.rb', line 98 def match_operation(source, context, consume_all) errors = [] @operations.each do |op_atom, prec, assoc| success, value = op_atom.apply(source, context, consume_all) return flatten(value, true), prec, assoc if success # assert: this was in fact an error, accumulate errors << value end return nil end |
#precedence_climb(source, context, consume_all, current_prec = 1, needs_element = false) ⇒ Object
Error handling in this routine is done by throwing :error and as a value the error to return to parslet. This avoids cluttering the recursion logic here with parslet error handling.
A precedence climbing algorithm married to parslet, as described here
http://eli.thegreenplace.net/2012/08/02/parsing-expressions-by-precedence-climbing/
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 |
# File 'lib/parslet/atoms/infix.rb', line 51 def precedence_climb(source, context, consume_all, current_prec=1, needs_element=false) result = [] # To even begin parsing an arithmetic expression, there needs to be # at least one @element. success, value = @element.apply(source, context, false) unless success throw :error, context.err(self, source, "#{@element.inspect} was expected", [value]) end result << flatten(value, true) # Loop until we fail on operator matching or until input runs out. loop do op_pos = source.bytepos op_match, prec, assoc = match_operation(source, context, false) # If no operator could be matched here, one of several cases # applies: # # - end of file # - end of expression # - syntax error # # We abort matching the expression here. break unless op_match if prec >= current_prec next_prec = (assoc == :left) ? prec+1 : prec result << op_match result << precedence_climb( source, context, consume_all, next_prec, true) else source.bytepos = op_pos return unwrap(result) end end return unwrap(result) end |
#produce_tree(ary) ⇒ Object
Turns an array of the form [‘1’, ‘+’, [‘2’, ‘*’, ‘3’]] into a hash that reflects the same structure.
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/parslet/atoms/infix.rb', line 23 def produce_tree(ary) return ary unless ary.kind_of? Array left = ary.shift until ary.empty? op, right = ary.shift(2) # p [left, op, right] if right.kind_of? Array # Subexpression -> Subhash left = reducer.call(left, op, produce_tree(right)) else left = reducer.call(left, op, right) end end left end |
#to_s_inner(prec) ⇒ Object
111 112 113 114 |
# File 'lib/parslet/atoms/infix.rb', line 111 def to_s_inner(prec) ops = @operations.map { |o, _, _| o.inspect }.join(', ') "infix_expression(#{@element.inspect}, [#{ops}])" end |
#try(source, context, consume_all) ⇒ Object
12 13 14 15 16 17 18 |
# File 'lib/parslet/atoms/infix.rb', line 12 def try(source, context, consume_all) return catch(:error) { return succ( produce_tree( precedence_climb(source, context, consume_all))) } end |
#unwrap(expr) ⇒ Object
94 95 96 |
# File 'lib/parslet/atoms/infix.rb', line 94 def unwrap expr expr.size == 1 ? expr.first : expr end |