Class: Idl::SymbolTable
- Inherits:
-
Object
- Object
- Idl::SymbolTable
- Extended by:
- T::Sig
- Defined in:
- lib/idlc/symbol_table.rb
Overview
scoped symbol table holding known symbols at a current point in parsing
Defined Under Namespace
Classes: BuiltinFunctionCallbacks, DuplicateSymError, EnumDef, MemoizedState
Constant Summary collapse
- PossibleXlensCallbackType =
T.type_alias { T.proc.returns(T::Array[Integer]) }
- ImplementedCallbackType =
T.type_alias { T.proc.params(arg0: String).returns(T.nilable(T::Boolean)) }
- ImplementedVersionCallbackType =
T.type_alias { T.proc.params(arg0: String, arg1: String).returns(T.nilable(T::Boolean)) }
- ImplementedCsrCallbackType =
T.type_alias { T.proc.params(arg0: Integer).returns(T.nilable(T::Boolean)) }
Instance Attribute Summary collapse
-
#builtin_funcs ⇒ Object
readonly
Returns the value of attribute builtin_funcs.
-
#csr_hash ⇒ Object
readonly
Returns the value of attribute csr_hash.
-
#mxlen ⇒ Object
readonly
Returns the value of attribute mxlen.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
Class Method Summary collapse
- .make_implemented_callback(&blk) ⇒ Object
- .make_implemented_csr_callback(&blk) ⇒ Object
- .make_implemented_version_callback(&blk) ⇒ Object
Instance Method Summary collapse
-
#add(name, var) ⇒ Object
add a new symbol at the outermost scope.
-
#add!(name, var) ⇒ Object
add a new symbol at the outermost scope, unless that symbol is already defined.
-
#add_above!(name, var) ⇒ Object
add to the scope above the tail, and make sure name is unique at that scope.
-
#add_at!(level, name, var) ⇒ Object
add to the scope at level, and make sure name is unique at that scope.
-
#at_global_scope? ⇒ Boolean
True if the symbol table is at the global scope.
- #callstack ⇒ Object
- #csr(csr_name) ⇒ Object
- #csr?(csr_name) ⇒ Boolean
-
#deep_clone(clone_values: false, freeze_global: true) ⇒ SymbolTable
A deep clone of this SymbolTable.
-
#deep_freeze ⇒ Object
do a deep freeze to protect the sym table and all its entries from modification.
-
#del(name) ⇒ Object
delete a new symbol at the outermost scopea.
-
#find_all(single_scope: false) {|obj| ... } ⇒ Array<Object>
searches the symbol table scope-by-scope to find all entries for which the block returns true.
-
#get(name) ⇒ Object
searches the symbol table scope-by-scope to find ‘name’.
- #get_from(name, level) ⇒ Object
-
#get_global(name) ⇒ Object
The symbol named ‘name’ from global scope, or nil if not found.
-
#global_clone ⇒ SymbolTable
A mutable clone of the global scope of this SymbolTable.
- #hash ⇒ Object
- #in_use? ⇒ Boolean
-
#initialize(mxlen: nil, possible_xlens_cb: nil, builtin_global_vars: [], builtin_enums: [], builtin_funcs: nil, csrs: [], params: [], name: "", register_files: [], register_file_max_widths: {}) ⇒ SymbolTable
constructor
A new instance of SymbolTable.
- #inspect ⇒ Object
-
#key?(name) ⇒ Boolean
Whether or not any symbol ‘name’ is defined at any level in the symbol table.
- #keys_pretty ⇒ Object
-
#levels ⇒ Integer
Number of scopes on the symbol table (global at 1).
- #multi_xlen? ⇒ Boolean
- #param(param_name) ⇒ Object
- #params_hash ⇒ Object
-
#pop ⇒ Object
pops the top of the scope stack.
- #possible_xlens ⇒ Object
- #print ⇒ Object
-
#push(ast) ⇒ SymbolTable
pushes a new scope.
- #release ⇒ Object
-
#restore_values(snapshot) ⇒ Object
Restores Var values from a snapshot produced by snapshot_values.
-
#snapshot_values ⇒ Object
Returns a Hash mapping each Var in non-global scopes to its current value.
Constructor Details
#initialize(mxlen: nil, possible_xlens_cb: nil, builtin_global_vars: [], builtin_enums: [], builtin_funcs: nil, csrs: [], params: [], name: "", register_files: [], register_file_max_widths: {}) ⇒ SymbolTable
Returns a new instance of SymbolTable.
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/idlc/symbol_table.rb', line 216 def initialize(mxlen: nil, possible_xlens_cb: nil, builtin_global_vars: [], builtin_enums: [], builtin_funcs: nil, csrs: [], params: [], name: "", register_files: [], register_file_max_widths: {}) @mutex = Thread::Mutex.new @mxlen = mxlen @possible_xlens_cb = possible_xlens_cb @callstack = [nil] @name = name @memo = MemoizedState.new # builtin types @scopes = [{ "Boolean" => Type.new(:boolean), "true" => Var.new( "true", Type.new(:boolean), true ), "false" => Var.new( "false", Type.new(:boolean), false ) }] # @params must be set before the register_files loop so that param schema # max lookups (used to compute int_width for dynamic-width register files) # can access @params without triggering a cfg_arch.symtab circular dependency. @params = params # Register file globals (X, F, V, etc.) from YAML-derived register file objects. # Each RF provides: .name (String), .register_length (IDL body String), .registers.count (Integer). register_files.each do |rf| int_width = if register_file_max_widths.key?(rf.name) register_file_max_widths[rf.name] else # Fallback for callers without pre-computed widths (CLI, tests with literal-width RFs). # eval_register_length_idl handles literals ("return 64;") and simple MXLEN references. w = eval_register_length_idl(rf.register_length) w.is_a?(Integer) ? w : raise("Cannot determine max register width for '#{rf.name}'. " \ "Pass register_file_max_widths: to SymbolTable.new.") end elem_type = RegFileElementType.new(rf.name, int_width, max_width: int_width) array_type = Type.new(:array, sub_type: elem_type, width: rf.registers.count, qualifiers: [:global]) @scopes[0][rf.name] = Var.new(rf.name, array_type) @scopes[0]["#{rf.name}Reg"] = elem_type end builtin_global_vars.each do |v| add!(v.name, v) end builtin_enums.each do |enum_def| add!(enum_def.name, EnumerationType.new(enum_def.name, enum_def.element_names, enum_def.element_values)) end @builtin_funcs = builtin_funcs @csrs = csrs @csr_hash = @csrs.map { |csr| [csr.name.freeze, csr].freeze }.to_h.freeze # set up the global clone that be used as a mutable table @global_clone_pool = T.let([], T::Array[SymbolTable]) end |
Instance Attribute Details
#builtin_funcs ⇒ Object (readonly)
Returns the value of attribute builtin_funcs.
183 184 185 |
# File 'lib/idlc/symbol_table.rb', line 183 def builtin_funcs @builtin_funcs end |
#csr_hash ⇒ Object (readonly)
Returns the value of attribute csr_hash.
189 190 191 |
# File 'lib/idlc/symbol_table.rb', line 189 def csr_hash @csr_hash end |
#mxlen ⇒ Object (readonly)
Returns the value of attribute mxlen.
105 106 107 |
# File 'lib/idlc/symbol_table.rb', line 105 def mxlen @mxlen end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
108 109 110 |
# File 'lib/idlc/symbol_table.rb', line 108 def name @name end |
Class Method Details
.make_implemented_callback(&blk) ⇒ Object
161 |
# File 'lib/idlc/symbol_table.rb', line 161 def self.make_implemented_callback(&blk) = blk |
.make_implemented_csr_callback(&blk) ⇒ Object
175 |
# File 'lib/idlc/symbol_table.rb', line 175 def self.make_implemented_csr_callback(&blk) = blk |
.make_implemented_version_callback(&blk) ⇒ Object
168 |
# File 'lib/idlc/symbol_table.rb', line 168 def self.make_implemented_version_callback(&blk) = blk |
Instance Method Details
#add(name, var) ⇒ Object
add a new symbol at the outermost scope
427 428 429 |
# File 'lib/idlc/symbol_table.rb', line 427 def add(name, var) @scopes.last[name] = var end |
#add!(name, var) ⇒ Object
add a new symbol at the outermost scope, unless that symbol is already defined
436 437 438 439 440 |
# File 'lib/idlc/symbol_table.rb', line 436 def add!(name, var) raise DuplicateSymError, "Symbol #{name} already defined as #{get(name)}" unless @scopes.select { |h| h.key? name }.empty? @scopes.last[name] = var end |
#add_above!(name, var) ⇒ Object
add to the scope above the tail, and make sure name is unique at that scope
453 454 455 456 457 458 459 |
# File 'lib/idlc/symbol_table.rb', line 453 def add_above!(name, var) raise "There is only one scope" if @scopes.size <= 1 raise "Symbol #{name} already defined" unless @scopes[0..-2].select { |h| h.key? name }.empty? @scopes[-2][name] = var end |
#add_at!(level, name, var) ⇒ Object
add to the scope at level, and make sure name is unique at that scope
462 463 464 465 466 467 468 |
# File 'lib/idlc/symbol_table.rb', line 462 def add_at!(level, name, var) raise "Level #{level} is too large #{@scopes.size}" if level >= @scopes.size raise "Symbol #{name} already defined" unless @scopes[0...level].select { |h| h.key? name }.empty? @scopes[level][name] = var end |
#at_global_scope? ⇒ Boolean
Returns true if the symbol table is at the global scope.
486 487 488 |
# File 'lib/idlc/symbol_table.rb', line 486 def at_global_scope? @scopes.size == 1 end |
#callstack ⇒ Object
361 362 363 |
# File 'lib/idlc/symbol_table.rb', line 361 def callstack @callstack.reverse.map { |ast| ast.nil? ? "" : "#{ast.input_file}:#{ast.lineno}" }.join("\n") end |
#csr(csr_name) ⇒ Object
192 |
# File 'lib/idlc/symbol_table.rb', line 192 def csr(csr_name) = csr_hash[csr_name] |
#csr?(csr_name) ⇒ Boolean
186 |
# File 'lib/idlc/symbol_table.rb', line 186 def csr?(csr_name) = csr_hash.key?(csr_name) |
#deep_clone(clone_values: false, freeze_global: true) ⇒ SymbolTable
Returns a deep clone of this SymbolTable.
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 |
# File 'lib/idlc/symbol_table.rb', line 558 def deep_clone(clone_values: false, freeze_global: true) raise "don't do this" unless freeze_global # globals are frozen, so we can just return a shallow clone # if we are in global scope if levels == 1 copy = dup copy.instance_variable_set(:@scopes, copy.instance_variable_get(:@scopes).dup) copy.instance_variable_set(:@callstack, copy.instance_variable_get(:@callstack).dup) return copy end copy = dup # back up the table to global scope copy.instance_variable_set(:@callstack, @callstack.dup) copy.instance_variable_set(:@scopes, []) c_scopes = copy.instance_variable_get(:@scopes) c_scopes.push(@scopes[0]) @scopes[1..].each do |scope| c_scopes << {} scope.each do |k, v| if clone_values c_scopes.last[k] = v.dup else c_scopes.last[k] = v end end end copy end |
#deep_freeze ⇒ Object
do a deep freeze to protect the sym table and all its entries from modification
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/idlc/symbol_table.rb', line 305 def deep_freeze @scopes.each do |k, v| k.freeze v.freeze end @scopes.freeze # set frozen_hash so that we can quickly compare symtabs @frozen_hash = [@scopes.hash, @name.hash].hash 5.times do copy = SymbolTable.allocate copy.instance_variable_set(:@scopes, [@scopes[0]]) copy.instance_variable_set(:@callstack, [@callstack[0]]) copy.instance_variable_set(:@mxlen, @mxlen) copy.instance_variable_set(:@mutex, @mutex) copy.instance_variable_set(:@name, @name) copy.instance_variable_set(:@memo, @memo.dup) copy.instance_variable_set(:@possible_xlens_cb, @possible_xlens_cb) copy.instance_variable_set(:@params, @params) copy.instance_variable_set(:@builtin_funcs, @builtin_funcs) copy.instance_variable_set(:@csrs, @csrs) copy.instance_variable_set(:@csr_hash, @csr_hash) copy.instance_variable_set(:@global_clone_pool, @global_clone_pool) copy.instance_variable_set(:@in_use, Concurrent::Semaphore.new(1)) @global_clone_pool << copy end freeze self end |
#del(name) ⇒ Object
delete a new symbol at the outermost scopea
446 447 448 449 450 |
# File 'lib/idlc/symbol_table.rb', line 446 def del(name) raise "No symbol #{name} at outer scope" unless @scopes.last.key?(name) @scopes.last.delete(name) end |
#find_all(single_scope: false) {|obj| ... } ⇒ Array<Object>
searches the symbol table scope-by-scope to find all entries for which the block returns true
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 |
# File 'lib/idlc/symbol_table.rb', line 407 def find_all(single_scope: false, &block) raise ArgumentError, "Block needed" unless block_given? raise ArgumentError, "Find block takes one argument" unless block.arity == 1 matches = [] @scopes.reverse_each do |s| s.each_value do |v| matches << v if yield v end break if single_scope && !matches.empty? end matches end |
#get(name) ⇒ Object
searches the symbol table scope-by-scope to find ‘name’
377 378 379 380 381 382 383 |
# File 'lib/idlc/symbol_table.rb', line 377 def get(name) @scopes.reverse_each do |s| result = s.fetch(name, nil) return result unless result.nil? end nil end |
#get_from(name, level) ⇒ Object
385 386 387 388 389 390 391 392 393 394 |
# File 'lib/idlc/symbol_table.rb', line 385 def get_from(name, level) raise ArgumentError, "level must be positive" unless level.positive? raise "There is no level #{level}" unless level < levels @scopes[0..level - 1].reverse_each do |s| return s[name] if s.key?(name) end nil end |
#get_global(name) ⇒ Object
Returns the symbol named ‘name’ from global scope, or nil if not found.
397 398 399 |
# File 'lib/idlc/symbol_table.rb', line 397 def get_global(name) get_from(name, 1) end |
#global_clone ⇒ SymbolTable
Returns a mutable clone of the global scope of this SymbolTable.
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 |
# File 'lib/idlc/symbol_table.rb', line 513 def global_clone # raise "symtab isn't frozen" if @global_clone.nil? # raise "global clone isn't at global scope" unless @global_clone.at_global_scope? @global_clone_pool.each do |symtab| return symtab if symtab.instance_variable_get(:@in_use).try_acquire end # need more! Idl.logger.debug "Allocating more SymbolTables" 5.times do copy = SymbolTable.allocate copy.instance_variable_set(:@scopes, [@scopes[0]]) copy.instance_variable_set(:@callstack, [@callstack[0]]) copy.instance_variable_set(:@mxlen, @mxlen) copy.instance_variable_set(:@mutex, @mutex) copy.instance_variable_set(:@name, @name) copy.instance_variable_set(:@memo, @memo.dup) copy.instance_variable_set(:@possible_xlens_cb, @possible_xlens_cb) copy.instance_variable_set(:@params, @params) copy.instance_variable_set(:@builtin_funcs, @builtin_funcs) copy.instance_variable_set(:@csrs, @csrs) copy.instance_variable_set(:@csr_hash, @csr_hash) copy.instance_variable_set(:@global_clone_pool, @global_clone_pool) copy.instance_variable_set(:@in_use, Concurrent::Semaphore.new(1)) @global_clone_pool << copy end global_clone end |
#hash ⇒ Object
113 114 115 116 117 |
# File 'lib/idlc/symbol_table.rb', line 113 def hash return @frozen_hash unless @frozen_hash.nil? object_id end |
#in_use? ⇒ Boolean
555 |
# File 'lib/idlc/symbol_table.rb', line 555 def in_use? = @in_use.available_permits.zero? |
#inspect ⇒ Object
300 301 302 |
# File 'lib/idlc/symbol_table.rb', line 300 def inspect "SymbolTable[#{@name}]#{frozen? ? ' (frozen)' : ''}" end |
#key?(name) ⇒ Boolean
Returns whether or not any symbol ‘name’ is defined at any level in the symbol table.
366 367 368 |
# File 'lib/idlc/symbol_table.rb', line 366 def key?(name) @scopes.each { |s| return true if s.key?(name) } end |
#keys_pretty ⇒ Object
370 371 372 |
# File 'lib/idlc/symbol_table.rb', line 370 def keys_pretty @scopes.map { |s| s.keys } end |
#levels ⇒ Integer
Returns Number of scopes on the symbol table (global at 1).
471 472 473 |
# File 'lib/idlc/symbol_table.rb', line 471 def levels @scopes.size end |
#multi_xlen? ⇒ Boolean
136 |
# File 'lib/idlc/symbol_table.rb', line 136 def multi_xlen? = possible_xlens.size > 1 |
#param(param_name) ⇒ Object
195 |
# File 'lib/idlc/symbol_table.rb', line 195 def param(param_name) = params_hash[param_name] |
#params_hash ⇒ Object
198 199 200 |
# File 'lib/idlc/symbol_table.rb', line 198 def params_hash @memo.params_hash ||= @params.map { |p| [p.name.freeze, p] }.to_h.freeze end |
#pop ⇒ Object
pops the top of the scope stack
351 352 353 354 355 356 357 358 359 |
# File 'lib/idlc/symbol_table.rb', line 351 def pop # puts "pop #{caller[0]}" # puts " from #{@scope_caller.pop}" raise "Error: popping the symbol table would remove global scope" if @scopes.size == 1 raise "?" unless @scopes.size == @callstack.size @scopes.pop @callstack.pop end |
#possible_xlens ⇒ Object
144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/idlc/symbol_table.rb', line 144 def possible_xlens @memo.possible_xlens ||= begin if @possible_xlens_cb.nil? Idl.logger.error "Symbol table was not initialized with a possible xlens callback, so #possible_xlens is not available" raise end @possible_xlens_cb.call end end |
#print ⇒ Object
477 478 479 480 481 482 483 |
# File 'lib/idlc/symbol_table.rb', line 477 def print @scopes.each do |s| s.each do |name, obj| puts "#{name} #{obj}" end end end |
#push(ast) ⇒ SymbolTable
pushes a new scope
339 340 341 342 343 344 345 346 347 348 |
# File 'lib/idlc/symbol_table.rb', line 339 def push(ast) # puts "push #{caller[0]}" # @scope_caller ||= [] # @scope_caller.push caller[0] raise "#{@scopes.size} #{@callstack.size}" unless @scopes.size == @callstack.size @scopes << {} @callstack << ast @frozen_hash = nil self end |
#release ⇒ Object
544 545 546 547 548 549 550 551 552 553 |
# File 'lib/idlc/symbol_table.rb', line 544 def release @mutex.synchronize do pop while levels > 1 raise "Clone isn't back in global scope" unless at_global_scope? raise "You are calling release on the frozen SymbolTable" if frozen? raise "Double release detected" unless in_use? @in_use.release end end |
#restore_values(snapshot) ⇒ Object
Restores Var values from a snapshot produced by snapshot_values.
506 507 508 509 510 |
# File 'lib/idlc/symbol_table.rb', line 506 def restore_values(snapshot) snapshot.each do |var, value| var.value = value end end |
#snapshot_values ⇒ Object
Returns a Hash mapping each Var in non-global scopes to its current value. Only captures Vars (not Types or other objects). skips frozen vars since they can’t be modified
493 494 495 496 497 498 499 500 501 502 503 |
# File 'lib/idlc/symbol_table.rb', line 493 def snapshot_values snapshot = {} @scopes[1..].each do |scope| scope.each_value do |v| if v.is_a?(Var) && !v.frozen? snapshot[v] = v.value end end end snapshot end |