Class: Graphlyte::Parsing::BacktrackingParser
- Inherits:
-
Object
- Object
- Graphlyte::Parsing::BacktrackingParser
- Defined in:
- lib/graphlyte/parsing/backtracking_parser.rb
Overview
Basic back-tracking parser, with parser-combinator methods and exceptional control-flow.
This class is just scaffolding - all domain specific parsing should go in subclasses.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#max_depth ⇒ Object
Returns the value of attribute max_depth.
-
#tokens ⇒ Object
readonly
Returns the value of attribute tokens.
Instance Method Summary collapse
- #advance ⇒ Object
- #bracket(lhs, rhs, &block) ⇒ Object
- #current ⇒ Object
- #expect(type, value = nil) ⇒ Object
-
#initialize(tokens:, max_depth: nil) ⇒ BacktrackingParser
constructor
A new instance of BacktrackingParser.
- #inspect ⇒ Object
- #many(limit: nil, &block) ⇒ Object
- #name(value = nil) ⇒ Object
- #next_token ⇒ Object
- #one_of(*alternatives) ⇒ Object
- #optional(&block) ⇒ Object
- #optional_list(&block) ⇒ Object
- #peek(offset: 0) ⇒ Object
- #punctuator(token) ⇒ Object
- #some(&block) ⇒ Object
- #subfeature ⇒ Object
- #to_s ⇒ Object
- #too_deep? ⇒ Boolean
- #try_parse ⇒ Object
Constructor Details
#initialize(tokens:, max_depth: nil) ⇒ BacktrackingParser
Returns a new instance of BacktrackingParser.
16 17 18 19 20 21 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 16 def initialize(tokens:, max_depth: nil) @tokens = tokens @index = -1 @max_depth = max_depth @current_depth = 0 end |
Instance Attribute Details
#max_depth ⇒ Object
Returns the value of attribute max_depth.
14 15 16 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 14 def max_depth @max_depth end |
#tokens ⇒ Object (readonly)
Returns the value of attribute tokens.
13 14 15 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 13 def tokens @tokens end |
Instance Method Details
#advance ⇒ Object
39 40 41 42 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 39 def advance @current = nil @index += 1 end |
#bracket(lhs, rhs, &block) ⇒ Object
133 134 135 136 137 138 139 140 141 142 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 133 def bracket(lhs, rhs, &block) expect(:PUNCTUATOR, lhs) raise TooDeep, current.location if too_deep? ret = subfeature(&block) expect(:PUNCTUATOR, rhs) ret end |
#current ⇒ Object
35 36 37 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 35 def current @current ||= peek end |
#expect(type, value = nil) ⇒ Object
49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 49 def expect(type, value = nil) try_parse do t = next_token if value raise Expected.new(t, expected: value) unless t.type == type && t.value == value else raise Unexpected, t unless t.type == type end t.value end end |
#inspect ⇒ Object
23 24 25 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 23 def inspect "#<#{self.class} @index=#{@index} @current=#{current.inspect} ...>" end |
#many(limit: nil, &block) ⇒ Object
73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 73 def many(limit: nil, &block) ret = [] until ret.length == limit begin ret << try_parse(&block) rescue ParseError return ret end end ret end |
#name(value = nil) ⇒ Object
129 130 131 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 129 def name(value = nil) expect(:NAME, value) end |
#next_token ⇒ Object
44 45 46 47 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 44 def next_token advance current end |
#one_of(*alternatives) ⇒ Object
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 106 def one_of(*alternatives) err = nil all_symbols = alternatives.all? { _1.is_a?(Symbol) } alternatives.each do |alt| case alt when Symbol then return try_parse { send(alt) } when Proc then return try_parse { alt.call } else raise 'Not an alternative' end rescue ParseError => e err = e end raise ParseError, "At #{current.location}: Expected one of #{alternatives.join(', ')}" if err && all_symbols raise err if err end |
#optional(&block) ⇒ Object
63 64 65 66 67 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 63 def optional(&block) try_parse(&block) rescue ParseError, IllegalValue nil end |
#optional_list(&block) ⇒ Object
69 70 71 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 69 def optional_list(&block) optional(&block) || [] end |
#peek(offset: 0) ⇒ Object
31 32 33 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 31 def peek(offset: 0) @tokens[@index + offset] || raise("No token at #{@index + offset}") end |
#punctuator(token) ⇒ Object
125 126 127 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 125 def punctuator(token) expect(:PUNCTUATOR, token) end |
#some(&block) ⇒ Object
87 88 89 90 91 92 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 87 def some(&block) one = yield rest = many(&block) [one] + rest end |
#subfeature ⇒ Object
150 151 152 153 154 155 156 157 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 150 def subfeature d = @current_depth @current_depth += 1 yield ensure @current_depth = d end |
#to_s ⇒ Object
27 28 29 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 27 def to_s inspect end |
#too_deep? ⇒ Boolean
144 145 146 147 148 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 144 def too_deep? return false if max_depth.nil? @current_depth > max_depth end |
#try_parse ⇒ Object
94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/graphlyte/parsing/backtracking_parser.rb', line 94 def try_parse idx = @index yield rescue ParseError => e @index = idx raise e rescue IllegalValue => e t = current @index = idx raise Illegal, t, e. end |