Class: Tcepsni::Parser

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/tcepsni/parser.rb

Constant Summary collapse

FAIL =
Data.define

Instance Method Summary collapse

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_arrayObject



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_attributeObject



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_attributesObject



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_identifierObject



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_classObject



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_encodingObject



139
140
141
# File 'lib/tcepsni/parser.rb', line 139

def parse_encoding
  @scanner.skip(/#<Encoding:(?<name>[^>]+)>/) && Encoding.find(@scanner[:name])
end

#parse_expressionObject 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_falseObject



43
44
45
46
# File 'lib/tcepsni/parser.rb', line 43

def parse_false
  @scanner.skip(/false\b/) or return FAIL
  false
end

#parse_floatObject



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_hashObject



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_identifierObject



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_integerObject



48
49
50
# File 'lib/tcepsni/parser.rb', line 48

def parse_integer
  @scanner.scan(/\d+/)&.to_i
end

#parse_memory_referenceObject



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_nilObject



34
35
36
37
# File 'lib/tcepsni/parser.rb', line 34

def parse_nil
  @scanner.skip(/nil\b/) or return FAIL
  nil
end

#parse_objectObject



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_rangeObject



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_stringObject



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_symbolObject



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_timeObject



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_trueObject



39
40
41
# File 'lib/tcepsni/parser.rb', line 39

def parse_true
  @scanner.skip(/true\b/) and true
end