Class: Tcepsni::Parser
- Inherits:
-
Object
- Object
- Tcepsni::Parser
- Extended by:
- Forwardable
- Defined in:
- lib/tcepsni/parser.rb
Constant Summary collapse
- FAIL =
Data.define
Instance Method Summary collapse
-
#initialize(source, vendors:) ⇒ Parser
constructor
A new instance of Parser.
- #parse_array ⇒ Object
- #parse_attribute ⇒ Object
- #parse_attributes ⇒ Object
- #parse_capitalized_identifier ⇒ Object
- #parse_class ⇒ Object
- #parse_encoding ⇒ Object
-
#parse_expression ⇒ Object
(also: #parse)
If it returned nil when failed, it collides with normal nil value.
- #parse_false ⇒ Object
- #parse_float ⇒ Object
- #parse_hash ⇒ Object
- #parse_identifier ⇒ Object
- #parse_integer ⇒ Object
- #parse_memory_reference ⇒ Object
- #parse_nil ⇒ Object
- #parse_object ⇒ Object
- #parse_range ⇒ Object
- #parse_string ⇒ Object
- #parse_symbol ⇒ Object
- #parse_time ⇒ Object
- #parse_true ⇒ Object
Constructor Details
#initialize(source, vendors:) ⇒ Parser
Returns a new instance of Parser.
8 9 10 11 |
# File 'lib/tcepsni/parser.rb', line 8 def initialize(source, vendors:) @scanner = ::StringScanner.new(source) @vendors = vendors.flat_map { |vendor| [*vendor.dependencies, vendor] }.uniq end |
Instance Method Details
#parse_array ⇒ Object
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/tcepsni/parser.rb', line 80 def parse_array pos = @scanner.pos @scanner.skip('[') or return result = [] until @scanner.eos? FAIL == (expr = parse_expression) and break result << expr @scanner.skip(', ') or break end if @scanner.skip(']') result else @scanner.pos = pos nil end end |
#parse_attribute ⇒ Object
161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/tcepsni/parser.rb', line 161 def parse_attribute pos = @scanner.pos @scanner.skip('@') and key = parse_identifier and @scanner.skip('=') and if FAIL == (value = parse_expression) @scanner.pos = pos return else return key, value end end |
#parse_attributes ⇒ Object
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/tcepsni/parser.rb', line 174 def parse_attributes @scanner.match?('>') and return {} pos = @scanner.pos attributes = {} while @scanner.skip(' ') key, value = (parse_attribute or return) attributes[key] = value @scanner.skip(',') or if @scanner.match?('>') break else @scanner.pos = pos return end end attributes end |
#parse_capitalized_identifier ⇒ Object
135 136 137 |
# File 'lib/tcepsni/parser.rb', line 135 def parse_capitalized_identifier @scanner.scan(/[A-Z][A-Za-z0-9_]*/)&.intern end |
#parse_class ⇒ Object
120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/tcepsni/parser.rb', line 120 def parse_class pos = @scanner.pos namespaces = [(parse_capitalized_identifier or return)] namespaces << begin parse_capitalized_identifier or begin @scanner.pos = pos return end end while @scanner.skip('::') *namespaces, name = namespaces Tcepsni::Class.new(name:, namespaces:) end |
#parse_encoding ⇒ Object
139 140 141 |
# File 'lib/tcepsni/parser.rb', line 139 def parse_encoding @scanner.skip(/#<Encoding:(?<name>[^>]+)>/) && Encoding.find(@scanner[:name]) end |
#parse_expression ⇒ Object Also known as: parse
If it returned nil when failed, it collides with normal nil value.
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/tcepsni/parser.rb', line 14 def parse_expression FAIL == (expr = parse_nil) or return expr FAIL == (expr = parse_false) or return expr (expr = (parse_true or parse_symbol or parse_string or parse_array or parse_hash or parse_encoding or parse_object or parse_range or parse_float or parse_time or parse_integer or parse_class)) and return expr FAIL end |
#parse_false ⇒ Object
43 44 45 46 |
# File 'lib/tcepsni/parser.rb', line 43 def parse_false @scanner.skip(/false\b/) or return FAIL false end |
#parse_float ⇒ Object
208 209 210 211 212 213 214 215 216 217 |
# File 'lib/tcepsni/parser.rb', line 208 def parse_float pos = @scanner.pos int = parse_integer and if @scanner.skip('.') && (int2 = parse_integer) Float("#{int}.#{int2}") else @scanner.pos = pos nil end end |
#parse_hash ⇒ Object
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/tcepsni/parser.rb', line 97 def parse_hash pos = @scanner.pos @scanner.skip('{') or return result = {} until @scanner.eos? FAIL == (key = parse_expression) and break @scanner.skip('=>') and FAIL != (value = parse_expression) or begin @scanner.pos = pos return end result[key] = value @scanner.skip(', ') or break end if @scanner.skip('}') result else @scanner.pos = pos return end end |
#parse_identifier ⇒ Object
52 53 54 |
# File 'lib/tcepsni/parser.rb', line 52 def parse_identifier @scanner.scan(/[A-Za-z_][A-Za-z0-9_]*/)&.intern end |
#parse_integer ⇒ Object
48 49 50 |
# File 'lib/tcepsni/parser.rb', line 48 def parse_integer @scanner.scan(/\d+/)&.to_i end |
#parse_memory_reference ⇒ Object
192 193 194 195 |
# File 'lib/tcepsni/parser.rb', line 192 def parse_memory_reference @scanner.skip(/:(?<ref>0x[0-9a-f]{16})/) or return Integer(@scanner[:ref]) end |
#parse_nil ⇒ Object
34 35 36 37 |
# File 'lib/tcepsni/parser.rb', line 34 def parse_nil @scanner.skip(/nil\b/) or return FAIL nil end |
#parse_object ⇒ Object
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/tcepsni/parser.rb', line 143 def parse_object pos = @scanner.pos @scanner.skip('#<') or return @vendors.select { |vendor| vendor.object? }.each do |vendor| expr = vendor.parse(self) and return expr end klass = parse_class and memory_reference = parse_memory_reference and attributes = parse_attributes and @scanner.skip('>') and Tcepsni::Object.new(klass:, memory_reference:, attributes:) or begin @scanner.pos = pos nil end end |
#parse_range ⇒ Object
197 198 199 200 201 202 203 204 205 206 |
# File 'lib/tcepsni/parser.rb', line 197 def parse_range pos = @scanner.pos int = parse_integer and if @scanner.skip("..") && (int2 = parse_integer) int..int2 else @scanner.pos = pos nil end end |
#parse_string ⇒ Object
67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/tcepsni/parser.rb', line 67 def parse_string pos = @scanner.pos @scanner.skip('"') or return result = +'' result << if @scanner.skip('"') then return result elsif @scanner.skip('\\"') then '"' else @scanner.getch end until @scanner.eos? @scanner.pos = pos nil end |
#parse_symbol ⇒ Object
56 57 58 59 60 61 62 63 64 65 |
# File 'lib/tcepsni/parser.rb', line 56 def parse_symbol pos = @scanner.pos @scanner.skip(':') and begin (id = parse_identifier) and return id @scanner.skip('[]') and return :[] @scanner.pos = pos return end end |
#parse_time ⇒ Object
219 220 221 222 223 224 225 226 227 228 229 230 |
# File 'lib/tcepsni/parser.rb', line 219 def parse_time pos = @scanner.pos int = parse_integer and if int <= 9999 && @scanner.skip(/-(?<month>[01]\d)-(?<day>\d\d) (?<hour>[012]\d):(?<minute>[0-5]\d):(?<second>[0-5]\d) (?<zone>[+-]\d\d\d\d)/) Time.new(int, @scanner[:month], @scanner[:day], @scanner[:hour], @scanner[:minute], @scanner[:second], @scanner[:zone]) else @scanner.pos = pos nil end end |
#parse_true ⇒ Object
39 40 41 |
# File 'lib/tcepsni/parser.rb', line 39 def parse_true @scanner.skip(/true\b/) and true end |