idlc 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +26 -0
- data/bin/idlc +10 -0
- data/lib/idlc/ast.rb +9611 -0
- data/lib/idlc/ast_decl.rb +22 -0
- data/lib/idlc/cli.rb +232 -0
- data/lib/idlc/idl.treetop +675 -0
- data/lib/idlc/idl_parser.rb +15386 -0
- data/lib/idlc/interfaces.rb +135 -0
- data/lib/idlc/log.rb +23 -0
- data/lib/idlc/passes/find_referenced_csrs.rb +39 -0
- data/lib/idlc/passes/find_return_values.rb +76 -0
- data/lib/idlc/passes/find_src_registers.rb +125 -0
- data/lib/idlc/passes/gen_adoc.rb +355 -0
- data/lib/idlc/passes/gen_option_adoc.rb +169 -0
- data/lib/idlc/passes/prune.rb +957 -0
- data/lib/idlc/passes/reachable_exceptions.rb +206 -0
- data/lib/idlc/passes/reachable_functions.rb +221 -0
- data/lib/idlc/symbol_table.rb +549 -0
- data/lib/idlc/syntax_node.rb +64 -0
- data/lib/idlc/type.rb +992 -0
- data/lib/idlc/version.rb +10 -0
- data/lib/idlc.rb +409 -0
- metadata +394 -0
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
|
2
|
+
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
3
|
+
|
|
4
|
+
# typed: true
|
|
5
|
+
# frozen_string_literal: true
|
|
6
|
+
|
|
7
|
+
require "sorbet-runtime"
|
|
8
|
+
require "concurrent/atomic/semaphore"
|
|
9
|
+
|
|
10
|
+
require_relative "log"
|
|
11
|
+
require_relative "type"
|
|
12
|
+
require_relative "interfaces"
|
|
13
|
+
|
|
14
|
+
module Idl
|
|
15
|
+
|
|
16
|
+
# Objects to represent variables in the ISA def
|
|
17
|
+
class Var
|
|
18
|
+
extend T::Sig
|
|
19
|
+
|
|
20
|
+
attr_reader :name, :type, :value
|
|
21
|
+
|
|
22
|
+
def initialize(name, type, value = nil, decode_var: false, function_name: nil, param: false, for_loop_iter: false)
|
|
23
|
+
@name = name
|
|
24
|
+
raise ArgumentError, "Expecting a Type, got #{type.class.name}" unless type.is_a?(Type)
|
|
25
|
+
|
|
26
|
+
@type = type
|
|
27
|
+
@type.freeze
|
|
28
|
+
@value = value
|
|
29
|
+
raise "unexpected" unless decode_var.is_a?(TrueClass) || decode_var.is_a?(FalseClass)
|
|
30
|
+
|
|
31
|
+
@decode_var = decode_var
|
|
32
|
+
@function_name = function_name
|
|
33
|
+
@param = param
|
|
34
|
+
@for_loop_iter = for_loop_iter
|
|
35
|
+
|
|
36
|
+
@const_compatible = true # until otherwise known
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
sig { void }
|
|
40
|
+
def const_incompatible!
|
|
41
|
+
@const_compatible = false
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
sig { returns(T::Boolean) }
|
|
45
|
+
def const_eval?
|
|
46
|
+
if @global
|
|
47
|
+
@name[0].upcase == @name[0]
|
|
48
|
+
else
|
|
49
|
+
@const_compatible
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
sig { returns(T::Boolean) }
|
|
54
|
+
def for_loop_iter?
|
|
55
|
+
@for_loop_iter
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def hash
|
|
59
|
+
[@name, @type, @value, @decode_var, @function_name, @param].hash
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def to_s
|
|
63
|
+
"VAR: #{type} #{name} #{value.nil? ? 'NO VALUE' : value}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def clone
|
|
67
|
+
Var.new(
|
|
68
|
+
name,
|
|
69
|
+
type.clone,
|
|
70
|
+
value&.clone,
|
|
71
|
+
decode_var: @decode_var,
|
|
72
|
+
function_name: @function_name,
|
|
73
|
+
param: @param
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def const?
|
|
78
|
+
@type.const?
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def decode_var?
|
|
82
|
+
@decode_var
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def param?
|
|
86
|
+
@param
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def to_cxx
|
|
90
|
+
@name
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def value=(new_value)
|
|
94
|
+
@value = new_value
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# scoped symbol table holding known symbols at a current point in parsing
|
|
99
|
+
class SymbolTable
|
|
100
|
+
extend T::Sig
|
|
101
|
+
|
|
102
|
+
# @return [Integer] 32 or 64, the XLEN in M-mode
|
|
103
|
+
# @return [nil] if the XLEN in M-mode is unknown
|
|
104
|
+
sig { returns(T.nilable(Integer)) }
|
|
105
|
+
attr_reader :mxlen
|
|
106
|
+
|
|
107
|
+
sig { returns(String) }
|
|
108
|
+
attr_reader :name
|
|
109
|
+
|
|
110
|
+
class DuplicateSymError < StandardError
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def hash
|
|
114
|
+
return @frozen_hash unless @frozen_hash.nil?
|
|
115
|
+
|
|
116
|
+
object_id
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
class EnumDef < T::Struct
|
|
120
|
+
extend T::Sig
|
|
121
|
+
|
|
122
|
+
prop :name, String
|
|
123
|
+
prop :element_values, T::Array[Integer]
|
|
124
|
+
prop :element_names, T::Array[String]
|
|
125
|
+
|
|
126
|
+
sig { params(name: String, element_values: T::Array[Integer], element_names: T::Array[String]).void }
|
|
127
|
+
def initialize(name:, element_values:, element_names:)
|
|
128
|
+
super(name:, element_values:, element_names:)
|
|
129
|
+
raise "element_values and element_names are not the same size" unless element_values.size == element_names.size
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
PossibleXlensCallbackType = T.type_alias { T.proc.returns(T::Array[Integer]) }
|
|
134
|
+
|
|
135
|
+
sig { returns(T::Boolean) }
|
|
136
|
+
def multi_xlen? = possible_xlens.size > 1
|
|
137
|
+
|
|
138
|
+
class MemoizedState < T::Struct
|
|
139
|
+
prop :possible_xlens, T.nilable(T::Array[Integer])
|
|
140
|
+
prop :params_hash, T.nilable(T::Hash[String, RuntimeParam])
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
sig { returns(T::Array[Integer]) }
|
|
144
|
+
def possible_xlens
|
|
145
|
+
@memo.possible_xlens ||=
|
|
146
|
+
begin
|
|
147
|
+
if @possible_xlens_cb.nil?
|
|
148
|
+
Idl.logger.error "Symbol table was not initialized with a possible xlens callback, so #possible_xlens is not available"
|
|
149
|
+
raise
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
@possible_xlens_cb.call
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
ImplementedCallbackType = T.type_alias { T.proc.params(arg0: String).returns(T.nilable(T::Boolean)) }
|
|
157
|
+
|
|
158
|
+
# some ugliness to capture proc types
|
|
159
|
+
# @see https://sorbet.org/docs/procs#what-can-i-do-for-better-proc-and-lambda-types
|
|
160
|
+
sig { params(blk: ImplementedCallbackType).returns(ImplementedCallbackType) }
|
|
161
|
+
def self.make_implemented_callback(&blk) = blk
|
|
162
|
+
|
|
163
|
+
ImplementedVersionCallbackType = T.type_alias { T.proc.params(arg0: String, arg1: String).returns(T.nilable(T::Boolean)) }
|
|
164
|
+
|
|
165
|
+
# some ugliness to capture proc types
|
|
166
|
+
# @see https://sorbet.org/docs/procs#what-can-i-do-for-better-proc-and-lambda-types
|
|
167
|
+
sig { params(blk: ImplementedVersionCallbackType).returns(ImplementedVersionCallbackType) }
|
|
168
|
+
def self.make_implemented_version_callback(&blk) = blk
|
|
169
|
+
|
|
170
|
+
ImplementedCsrCallbackType = T.type_alias { T.proc.params(arg0: Integer).returns(T.nilable(T::Boolean)) }
|
|
171
|
+
|
|
172
|
+
# some ugliness to capture proc types
|
|
173
|
+
# @see https://sorbet.org/docs/procs#what-can-i-do-for-better-proc-and-lambda-types
|
|
174
|
+
sig { params(blk: ImplementedCsrCallbackType).returns(ImplementedCsrCallbackType) }
|
|
175
|
+
def self.make_implemented_csr_callback(&blk) = blk
|
|
176
|
+
|
|
177
|
+
class BuiltinFunctionCallbacks < T::Struct
|
|
178
|
+
prop :implemented, ImplementedCallbackType
|
|
179
|
+
prop :implemented_version, ImplementedVersionCallbackType
|
|
180
|
+
prop :implemented_csr, ImplementedCsrCallbackType
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
attr_reader :builtin_funcs
|
|
184
|
+
|
|
185
|
+
sig { params(csr_name: String).returns(T::Boolean) }
|
|
186
|
+
def csr?(csr_name) = csr_hash.key?(csr_name)
|
|
187
|
+
|
|
188
|
+
sig { returns(T::Hash[String, Csr]) }
|
|
189
|
+
attr_reader :csr_hash
|
|
190
|
+
|
|
191
|
+
sig { params(csr_name: String).returns(T.nilable(Csr)) }
|
|
192
|
+
def csr(csr_name) = csr_hash[csr_name]
|
|
193
|
+
|
|
194
|
+
sig { params(param_name: String).returns(T.nilable(RuntimeParam)) }
|
|
195
|
+
def param(param_name) = params_hash[param_name]
|
|
196
|
+
|
|
197
|
+
sig { returns(T::Hash[String, RuntimeParam]) }
|
|
198
|
+
def params_hash
|
|
199
|
+
@memo.params_hash ||= @params.map { |p| [p.name.freeze, p] }.to_h.freeze
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
sig {
|
|
203
|
+
params(
|
|
204
|
+
mxlen: T.nilable(Integer),
|
|
205
|
+
possible_xlens_cb: T.nilable(PossibleXlensCallbackType),
|
|
206
|
+
builtin_global_vars: T::Array[Var],
|
|
207
|
+
builtin_enums: T::Array[EnumDef],
|
|
208
|
+
builtin_funcs: T.nilable(BuiltinFunctionCallbacks),
|
|
209
|
+
csrs: T::Array[Csr],
|
|
210
|
+
params: T::Array[RuntimeParam],
|
|
211
|
+
name: String
|
|
212
|
+
).void
|
|
213
|
+
}
|
|
214
|
+
def initialize(mxlen: nil, possible_xlens_cb: nil, builtin_global_vars: [], builtin_enums: [], builtin_funcs: nil, csrs: [], params: [], name: "")
|
|
215
|
+
@mutex = Thread::Mutex.new
|
|
216
|
+
@mxlen = mxlen
|
|
217
|
+
@possible_xlens_cb = possible_xlens_cb
|
|
218
|
+
@callstack = [nil]
|
|
219
|
+
@name = name
|
|
220
|
+
@memo = MemoizedState.new
|
|
221
|
+
|
|
222
|
+
# builtin types
|
|
223
|
+
@scopes = [{
|
|
224
|
+
"X" => Var.new(
|
|
225
|
+
"X",
|
|
226
|
+
Type.new(:array, sub_type: XregType.new(@mxlen.nil? ? 64 : @mxlen), width: 32, qualifiers: [:global])
|
|
227
|
+
),
|
|
228
|
+
"XReg" => XregType.new(@mxlen.nil? ? 64 : @mxlen),
|
|
229
|
+
"Boolean" => Type.new(:boolean),
|
|
230
|
+
"true" => Var.new(
|
|
231
|
+
"true",
|
|
232
|
+
Type.new(:boolean),
|
|
233
|
+
true
|
|
234
|
+
),
|
|
235
|
+
"false" => Var.new(
|
|
236
|
+
"false",
|
|
237
|
+
Type.new(:boolean),
|
|
238
|
+
false
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
}]
|
|
242
|
+
builtin_global_vars.each do |v|
|
|
243
|
+
add!(v.name, v)
|
|
244
|
+
end
|
|
245
|
+
builtin_enums.each do |enum_def|
|
|
246
|
+
add!(enum_def.name, EnumerationType.new(enum_def.name, enum_def.element_names, enum_def.element_values))
|
|
247
|
+
end
|
|
248
|
+
@builtin_funcs = builtin_funcs
|
|
249
|
+
@csrs = csrs
|
|
250
|
+
@csr_hash = @csrs.map { |csr| [csr.name.freeze, csr].freeze }.to_h.freeze
|
|
251
|
+
@params = params
|
|
252
|
+
|
|
253
|
+
# set up the global clone that be used as a mutable table
|
|
254
|
+
@global_clone_pool = T.let([], T::Array[SymbolTable])
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# @return [String] inspection string
|
|
258
|
+
sig { returns(String) }
|
|
259
|
+
def inspect
|
|
260
|
+
"SymbolTable[#{@name}]#{frozen? ? ' (frozen)' : ''}"
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# do a deep freeze to protect the sym table and all its entries from modification
|
|
264
|
+
def deep_freeze
|
|
265
|
+
@scopes.each do |k, v|
|
|
266
|
+
k.freeze
|
|
267
|
+
v.freeze
|
|
268
|
+
end
|
|
269
|
+
@scopes.freeze
|
|
270
|
+
|
|
271
|
+
# set frozen_hash so that we can quickly compare symtabs
|
|
272
|
+
@frozen_hash = [@scopes.hash, @name.hash].hash
|
|
273
|
+
|
|
274
|
+
5.times do
|
|
275
|
+
copy = SymbolTable.allocate
|
|
276
|
+
copy.instance_variable_set(:@scopes, [@scopes[0]])
|
|
277
|
+
copy.instance_variable_set(:@callstack, [@callstack[0]])
|
|
278
|
+
copy.instance_variable_set(:@mxlen, @mxlen)
|
|
279
|
+
copy.instance_variable_set(:@mutex, @mutex)
|
|
280
|
+
copy.instance_variable_set(:@name, @name)
|
|
281
|
+
copy.instance_variable_set(:@memo, @memo.dup)
|
|
282
|
+
copy.instance_variable_set(:@possible_xlens_cb, @possible_xlens_cb)
|
|
283
|
+
copy.instance_variable_set(:@params, @params)
|
|
284
|
+
copy.instance_variable_set(:@builtin_funcs, @builtin_funcs)
|
|
285
|
+
copy.instance_variable_set(:@csrs, @csrs)
|
|
286
|
+
copy.instance_variable_set(:@csr_hash, @csr_hash)
|
|
287
|
+
copy.instance_variable_set(:@global_clone_pool, @global_clone_pool)
|
|
288
|
+
copy.instance_variable_set(:@in_use, Concurrent::Semaphore.new(1))
|
|
289
|
+
@global_clone_pool << copy
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
freeze
|
|
293
|
+
self
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# pushes a new scope
|
|
297
|
+
# @return [SymbolTable] self
|
|
298
|
+
def push(ast)
|
|
299
|
+
# puts "push #{caller[0]}"
|
|
300
|
+
# @scope_caller ||= []
|
|
301
|
+
# @scope_caller.push caller[0]
|
|
302
|
+
raise "#{@scopes.size} #{@callstack.size}" unless @scopes.size == @callstack.size
|
|
303
|
+
@scopes << {}
|
|
304
|
+
@callstack << ast
|
|
305
|
+
@frozen_hash = nil
|
|
306
|
+
self
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# pops the top of the scope stack
|
|
310
|
+
def pop
|
|
311
|
+
# puts "pop #{caller[0]}"
|
|
312
|
+
# puts " from #{@scope_caller.pop}"
|
|
313
|
+
raise "Error: popping the symbol table would remove global scope" if @scopes.size == 1
|
|
314
|
+
|
|
315
|
+
raise "?" unless @scopes.size == @callstack.size
|
|
316
|
+
@scopes.pop
|
|
317
|
+
@callstack.pop
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def callstack
|
|
321
|
+
@callstack.reverse.map { |ast| ast.nil? ? "" : "#{ast.input_file}:#{ast.lineno}" }.join("\n")
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
# @return [Boolean] whether or not any symbol 'name' is defined at any level in the symbol table
|
|
325
|
+
def key?(name)
|
|
326
|
+
@scopes.each { |s| return true if s.key?(name) }
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def keys_pretty
|
|
330
|
+
@scopes.map { |s| s.keys }
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# searches the symbol table scope-by-scope to find 'name'
|
|
334
|
+
#
|
|
335
|
+
# @return [Object] A symbol named 'name', or nil if not found
|
|
336
|
+
def get(name)
|
|
337
|
+
@scopes.reverse_each do |s|
|
|
338
|
+
result = s.fetch(name, nil)
|
|
339
|
+
return result unless result.nil?
|
|
340
|
+
end
|
|
341
|
+
nil
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
def get_from(name, level)
|
|
345
|
+
raise ArgumentError, "level must be positive" unless level.positive?
|
|
346
|
+
|
|
347
|
+
raise "There is no level #{level}" unless level < levels
|
|
348
|
+
|
|
349
|
+
@scopes[0..level - 1].reverse_each do |s|
|
|
350
|
+
return s[name] if s.key?(name)
|
|
351
|
+
end
|
|
352
|
+
nil
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# @return [Object] the symbol named 'name' from global scope, or nil if not found
|
|
356
|
+
def get_global(name)
|
|
357
|
+
get_from(name, 1)
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
# searches the symbol table scope-by-scope to find all entries for which the block returns true
|
|
361
|
+
#
|
|
362
|
+
# @param single_scope [Boolean] If true, stop searching more scope as soon as there are matches
|
|
363
|
+
# @yieldparam obj [Object] A object stored in the symbol table
|
|
364
|
+
# @yieldreturn [Boolean] Whether or not the object is the one you are looking for
|
|
365
|
+
# @return [Array<Object>] All matches
|
|
366
|
+
def find_all(single_scope: false, &block)
|
|
367
|
+
raise ArgumentError, "Block needed" unless block_given?
|
|
368
|
+
|
|
369
|
+
raise ArgumentError, "Find block takes one argument" unless block.arity == 1
|
|
370
|
+
|
|
371
|
+
matches = []
|
|
372
|
+
|
|
373
|
+
@scopes.reverse_each do |s|
|
|
374
|
+
s.each_value do |v|
|
|
375
|
+
matches << v if yield v
|
|
376
|
+
end
|
|
377
|
+
break if single_scope && !matches.empty?
|
|
378
|
+
end
|
|
379
|
+
matches
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
# add a new symbol at the outermost scope
|
|
383
|
+
#
|
|
384
|
+
# @param name [#to_s] Symbol name
|
|
385
|
+
# @param var [Object] Symbol object (usually a Var or a Type)
|
|
386
|
+
def add(name, var)
|
|
387
|
+
@scopes.last[name] = var
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
# add a new symbol at the outermost scope, unless that symbol is already defined
|
|
391
|
+
#
|
|
392
|
+
# @param name [#to_s] Symbol name
|
|
393
|
+
# @param var [Object] Symbol object (usually a Var or a Type)
|
|
394
|
+
# @raise [DuplicationSymError] if 'name' is already in the symbol table
|
|
395
|
+
def add!(name, var)
|
|
396
|
+
raise DuplicateSymError, "Symbol #{name} already defined as #{get(name)}" unless @scopes.select { |h| h.key? name }.empty?
|
|
397
|
+
|
|
398
|
+
@scopes.last[name] = var
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
# delete a new symbol at the outermost scopea
|
|
402
|
+
#
|
|
403
|
+
# @param name [#to_s] Symbol name
|
|
404
|
+
# @param var [Object] Symbol object (usually a Var or a Type)
|
|
405
|
+
def del(name)
|
|
406
|
+
raise "No symbol #{name} at outer scope" unless @scopes.last.key?(name)
|
|
407
|
+
|
|
408
|
+
@scopes.last.delete(name)
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
# add to the scope above the tail, and make sure name is unique at that scope
|
|
412
|
+
def add_above!(name, var)
|
|
413
|
+
raise "There is only one scope" if @scopes.size <= 1
|
|
414
|
+
|
|
415
|
+
raise "Symbol #{name} already defined" unless @scopes[0..-2].select { |h| h.key? name }.empty?
|
|
416
|
+
|
|
417
|
+
@scopes[-2][name] = var
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
# add to the scope at level, and make sure name is unique at that scope
|
|
421
|
+
def add_at!(level, name, var)
|
|
422
|
+
raise "Level #{level} is too large #{@scopes.size}" if level >= @scopes.size
|
|
423
|
+
|
|
424
|
+
raise "Symbol #{name} already defined" unless @scopes[0...level].select { |h| h.key? name }.empty?
|
|
425
|
+
|
|
426
|
+
@scopes[level][name] = var
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
# @return [Integer] Number of scopes on the symbol table (global at 1)
|
|
430
|
+
def levels
|
|
431
|
+
@scopes.size
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
# pretty-print the symbol table contents
|
|
435
|
+
sig { void }
|
|
436
|
+
def print
|
|
437
|
+
@scopes.each do |s|
|
|
438
|
+
s.each do |name, obj|
|
|
439
|
+
puts "#{name} #{obj}"
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
# @return [Boolean] true if the symbol table is at the global scope
|
|
445
|
+
def at_global_scope?
|
|
446
|
+
@scopes.size == 1
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
# Returns a Hash mapping each Var in non-global scopes to its current value.
|
|
450
|
+
# Only captures Vars (not Types or other objects).
|
|
451
|
+
# skips frozen vars since they can't be modified
|
|
452
|
+
def snapshot_values
|
|
453
|
+
snapshot = {}
|
|
454
|
+
@scopes[1..].each do |scope|
|
|
455
|
+
scope.each_value do |v|
|
|
456
|
+
if v.is_a?(Var) && !v.frozen?
|
|
457
|
+
snapshot[v] = v.value
|
|
458
|
+
end
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
snapshot
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
# Restores Var values from a snapshot produced by snapshot_values.
|
|
465
|
+
def restore_values(snapshot)
|
|
466
|
+
snapshot.each do |var, value|
|
|
467
|
+
var.value = value
|
|
468
|
+
end
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
# @return [SymbolTable] a mutable clone of the global scope of this SymbolTable
|
|
472
|
+
def global_clone
|
|
473
|
+
# raise "symtab isn't frozen" if @global_clone.nil?
|
|
474
|
+
# raise "global clone isn't at global scope" unless @global_clone.at_global_scope?
|
|
475
|
+
|
|
476
|
+
@global_clone_pool.each do |symtab|
|
|
477
|
+
return symtab if symtab.instance_variable_get(:@in_use).try_acquire
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
# need more!
|
|
481
|
+
Idl.logger.debug "Allocating more SymbolTables"
|
|
482
|
+
5.times do
|
|
483
|
+
copy = SymbolTable.allocate
|
|
484
|
+
copy.instance_variable_set(:@scopes, [@scopes[0]])
|
|
485
|
+
copy.instance_variable_set(:@callstack, [@callstack[0]])
|
|
486
|
+
copy.instance_variable_set(:@mxlen, @mxlen)
|
|
487
|
+
copy.instance_variable_set(:@mutex, @mutex)
|
|
488
|
+
copy.instance_variable_set(:@name, @name)
|
|
489
|
+
copy.instance_variable_set(:@memo, @memo.dup)
|
|
490
|
+
copy.instance_variable_set(:@possible_xlens_cb, @possible_xlens_cb)
|
|
491
|
+
copy.instance_variable_set(:@params, @params)
|
|
492
|
+
copy.instance_variable_set(:@builtin_funcs, @builtin_funcs)
|
|
493
|
+
copy.instance_variable_set(:@csrs, @csrs)
|
|
494
|
+
copy.instance_variable_set(:@csr_hash, @csr_hash)
|
|
495
|
+
copy.instance_variable_set(:@global_clone_pool, @global_clone_pool)
|
|
496
|
+
copy.instance_variable_set(:@in_use, Concurrent::Semaphore.new(1))
|
|
497
|
+
@global_clone_pool << copy
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
global_clone
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
def release
|
|
504
|
+
@mutex.synchronize do
|
|
505
|
+
pop while levels > 1
|
|
506
|
+
raise "Clone isn't back in global scope" unless at_global_scope?
|
|
507
|
+
raise "You are calling release on the frozen SymbolTable" if frozen?
|
|
508
|
+
raise "Double release detected" unless in_use?
|
|
509
|
+
|
|
510
|
+
@in_use.release
|
|
511
|
+
end
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
def in_use? = @in_use.available_permits.zero?
|
|
515
|
+
|
|
516
|
+
# @return [SymbolTable] a deep clone of this SymbolTable
|
|
517
|
+
def deep_clone(clone_values: false, freeze_global: true)
|
|
518
|
+
raise "don't do this" unless freeze_global
|
|
519
|
+
|
|
520
|
+
# globals are frozen, so we can just return a shallow clone
|
|
521
|
+
# if we are in global scope
|
|
522
|
+
if levels == 1
|
|
523
|
+
copy = dup
|
|
524
|
+
copy.instance_variable_set(:@scopes, copy.instance_variable_get(:@scopes).dup)
|
|
525
|
+
copy.instance_variable_set(:@callstack, copy.instance_variable_get(:@callstack).dup)
|
|
526
|
+
return copy
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
copy = dup
|
|
530
|
+
# back up the table to global scope
|
|
531
|
+
copy.instance_variable_set(:@callstack, @callstack.dup)
|
|
532
|
+
copy.instance_variable_set(:@scopes, [])
|
|
533
|
+
c_scopes = copy.instance_variable_get(:@scopes)
|
|
534
|
+
c_scopes.push(@scopes[0])
|
|
535
|
+
|
|
536
|
+
@scopes[1..].each do |scope|
|
|
537
|
+
c_scopes << {}
|
|
538
|
+
scope.each do |k, v|
|
|
539
|
+
if clone_values
|
|
540
|
+
c_scopes.last[k] = v.dup
|
|
541
|
+
else
|
|
542
|
+
c_scopes.last[k] = v
|
|
543
|
+
end
|
|
544
|
+
end
|
|
545
|
+
end
|
|
546
|
+
copy
|
|
547
|
+
end
|
|
548
|
+
end
|
|
549
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
|
|
2
|
+
# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
|
3
|
+
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
4
|
+
|
|
5
|
+
# typed: true
|
|
6
|
+
# frozen_string_literal: true
|
|
7
|
+
|
|
8
|
+
require "sorbet-runtime"
|
|
9
|
+
|
|
10
|
+
# adds a few functions to Treetop's syntax node
|
|
11
|
+
module Treetop
|
|
12
|
+
module Runtime
|
|
13
|
+
class SyntaxNode
|
|
14
|
+
extend T::Sig
|
|
15
|
+
# remember where the code comes from
|
|
16
|
+
#
|
|
17
|
+
# @param filename [String] Filename
|
|
18
|
+
# @param starting_line [Integer] Starting line in the file
|
|
19
|
+
# @param starting_offset [Integer] Starting byte offset in the file
|
|
20
|
+
# @param line_file_offsets [Array<Integer>, nil] Per-IDL-line file byte offsets
|
|
21
|
+
sig { params(filename: T.nilable(String), starting_line: Integer, starting_offset: Integer, line_file_offsets: T.nilable(T::Array[Integer])).void }
|
|
22
|
+
def set_input_file(filename, starting_line = 0, starting_offset = 0, line_file_offsets = nil)
|
|
23
|
+
@input_file = filename
|
|
24
|
+
@starting_line = starting_line
|
|
25
|
+
@starting_offset = starting_offset
|
|
26
|
+
@line_file_offsets = line_file_offsets
|
|
27
|
+
elements&.each do |child|
|
|
28
|
+
# Adjust the starting offset for each child based on its position in the input
|
|
29
|
+
child_offset = starting_offset + child.interval.first
|
|
30
|
+
child.set_input_file(filename, starting_line, child_offset, line_file_offsets)
|
|
31
|
+
end
|
|
32
|
+
raise "?" if @starting_line.nil?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
sig { returns(T::Boolean) }
|
|
36
|
+
def space? = false
|
|
37
|
+
|
|
38
|
+
# Sets the input file for this syntax node unless it has already been set.
|
|
39
|
+
#
|
|
40
|
+
# If the input file has not been set, it will be set with the given filename and starting line number.
|
|
41
|
+
#
|
|
42
|
+
# @param [String] filename The name of the input file.
|
|
43
|
+
# @param [Integer] starting_line The starting line number in the input file.
|
|
44
|
+
# @param [Integer] starting_offset The starting byte offset in the input file.
|
|
45
|
+
# @param [Array<Integer>, nil] line_file_offsets Per-IDL-line file byte offsets
|
|
46
|
+
sig { params(filename: T.nilable(String), starting_line: Integer, starting_offset: Integer, line_file_offsets: T.nilable(T::Array[Integer])).void }
|
|
47
|
+
def set_input_file_unless_already_set(filename, starting_line = 0, starting_offset = 0, line_file_offsets = nil)
|
|
48
|
+
if @input_file.nil?
|
|
49
|
+
set_input_file(filename, starting_line, starting_offset, line_file_offsets)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
module Idl
|
|
57
|
+
class SyntaxNode < Treetop::Runtime::SyntaxNode
|
|
58
|
+
extend T::Sig
|
|
59
|
+
extend T::Helpers
|
|
60
|
+
|
|
61
|
+
sig { overridable.returns(Idl::AstNode) }
|
|
62
|
+
def to_ast = raise "Must override to_ast for #{self.class.name}"
|
|
63
|
+
end
|
|
64
|
+
end
|