idlc 0.1.2 → 0.1.5

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.
data/lib/idlc/ast.rb CHANGED
@@ -65,11 +65,15 @@ module Idl
65
65
 
66
66
  # @return [String] Source input file
67
67
  sig { returns(T.nilable(Pathname)) }
68
- attr_reader :input_file
68
+ def input_file
69
+ @input_file || @parent&.input_file
70
+ end
69
71
 
70
72
  # @return [Integer] Starting line in the source input file (i.e., position 0 of {#input} in the file)
71
73
  sig { returns(Integer) }
72
- attr_reader :starting_line
74
+ def starting_line
75
+ @starting_line || @parent&.starting_line || 0
76
+ end
73
77
 
74
78
  # @return [String] Source string
75
79
  sig { returns(T.nilable(String)) }
@@ -212,6 +216,14 @@ module Idl
212
216
  sig { abstract.params(symtab: SymbolTable).returns(T::Boolean) }
213
217
  def const_eval?(symtab); end
214
218
 
219
+ # @return [Boolean] whether this node includes the Executable interface
220
+ sig { overridable.returns(T::Boolean).checked(:never) }
221
+ def executable? = false
222
+
223
+ # @return [Boolean] whether this node includes the Declaration interface
224
+ sig { overridable.returns(T::Boolean).checked(:never) }
225
+ def declaration? = false
226
+
215
227
  # @param input [String] The source being compiled
216
228
  # @param interval [Range] The range in the source corresponding to this AstNode
217
229
  # @param children [Array<AstNode>] Children of this node
@@ -247,7 +259,7 @@ module Idl
247
259
  # @param [Integer] starting_line The starting line number in the input file.
248
260
  # @param [Integer] starting_offset The byte offset in the file where the IDL content starts.
249
261
  # @param [Array<Integer>, nil] line_file_offsets Per-IDL-line file byte offsets (nil = use starting_offset).
250
- sig { params(filename: T.any(Pathname, String), starting_line: Integer, starting_offset: Integer, line_file_offsets: T.nilable(T::Array[Integer])).void }
262
+ sig { params(filename: T.any(Pathname, String), starting_line: Integer, starting_offset: Integer, line_file_offsets: T.nilable(T::Array[Integer])).void.checked(:never) }
251
263
  def set_input_file_unless_already_set(filename, starting_line = 0, starting_offset = 0, line_file_offsets = nil)
252
264
  return unless @input_file.nil?
253
265
 
@@ -255,10 +267,6 @@ module Idl
255
267
  @starting_line = starting_line
256
268
  @starting_offset = starting_offset
257
269
  @line_file_offsets = line_file_offsets
258
- children.each do |child|
259
- child.set_input_file_unless_already_set(filename, starting_line, starting_offset, line_file_offsets)
260
- end
261
- raise "?" if @starting_line.nil?
262
270
  end
263
271
 
264
272
  # remember where the code comes from
@@ -267,22 +275,18 @@ module Idl
267
275
  # @param starting_line [Integer] Starting line in the file
268
276
  # @param starting_offset [Integer] Byte offset in the file where the IDL content starts
269
277
  # @param line_file_offsets [Array<Integer>, nil] Per-IDL-line file byte offsets (nil = use starting_offset).
270
- sig { params(filename: T.any(Pathname, String), starting_line: Integer, starting_offset: Integer, line_file_offsets: T.nilable(T::Array[Integer])).void }
278
+ sig { params(filename: T.any(Pathname, String), starting_line: Integer, starting_offset: Integer, line_file_offsets: T.nilable(T::Array[Integer])).void.checked(:never) }
271
279
  def set_input_file(filename, starting_line = 0, starting_offset = 0, line_file_offsets = nil)
272
280
  @input_file = Pathname.new(filename)
273
281
  @starting_line = starting_line
274
282
  @starting_offset = starting_offset
275
283
  @line_file_offsets = line_file_offsets
276
- children.each do |child|
277
- child.set_input_file(filename, starting_line, starting_offset, line_file_offsets)
278
- end
279
- raise "?" if @starting_line.nil?
280
284
  end
281
285
 
282
286
  # @return [Integer] the current line number
283
287
  sig { returns(Integer) }
284
288
  def lineno
285
- T.must(T.must(input)[0..T.must(interval).first]).count("\n") + 1 + (@starting_line.nil? ? 0 : @starting_line)
289
+ T.must(T.must(input)[0..T.must(interval).first]).count("\n") + 1 + starting_line
286
290
  end
287
291
 
288
292
  # @return [AstNode] the first ancestor that is_a?(+klass+)
@@ -378,7 +382,7 @@ module Idl
378
382
  starting_lineno = T.must(T.must(input)[0..lines_interval.min]).count("\n")
379
383
  lines = lines.lines.map do |line|
380
384
  starting_lineno += 1
381
- "#{@starting_line + starting_lineno - 1}: #{line}"
385
+ "#{starting_line + starting_lineno - 1}: #{line}"
382
386
  end.join("")
383
387
 
384
388
  msg = <<~WHAT
@@ -415,6 +419,57 @@ module Idl
415
419
  attr_accessor :value_error_reason, :value_error_ast
416
420
  end
417
421
 
422
+ # Extract the base variable name from a potentially nested access chain
423
+ # @param node [RvalueAst] The node to extract from
424
+ # @return [String, nil] The base variable name, or nil if not extractable
425
+ sig { params(node: RvalueAst).returns(T.nilable(String)) }
426
+ def self.extract_base_var_name(node)
427
+ case node
428
+ when IdAst
429
+ node.name
430
+ when AryElementAccessAst, AryRangeAccessAst
431
+ extract_base_var_name(node.var)
432
+ else
433
+ nil
434
+ end
435
+ end
436
+
437
+ # Perform write-back for nested array access assignments
438
+ # Handles arbitrary nesting depth: v[a][b][c] = val recursively writes back through the chain
439
+ # @param target [RvalueAst] The target expression (possibly nested)
440
+ # @param new_value [Integer] The new value to write
441
+ # @param symtab [SymbolTable] The symbol table
442
+ # @return [void]
443
+ sig { params(target: RvalueAst, new_value: ValueRbType, symtab: SymbolTable).void }
444
+ def self.write_back_nested(target, new_value, symtab)
445
+ case target
446
+ when IdAst
447
+ # Base case: simple variable
448
+ existing_var = symtab.get(target.name)
449
+ raise InternalError, "write_back_nested: '#{target.name}' not found in symbol table" if existing_var.nil?
450
+ existing_var.value = new_value
451
+ when AryElementAccessAst
452
+ # Recursive case: v[idx] = val
453
+ # Read parent, modify element, write back parent
454
+ parent_value = target.var.value(symtab)
455
+ idx_val = target.index.value(symtab)
456
+ parent_value[idx_val] = new_value
457
+ # Recursively write back the parent (which may itself be nested)
458
+ write_back_nested(target.var, parent_value, symtab)
459
+ when AryRangeAccessAst
460
+ # Recursive case: v[msb:lsb] = new_value
461
+ # Read parent, splice new_value into [msb:lsb], write parent back
462
+ parent_value = T.cast(target.var.value(symtab), Integer)
463
+ msb_val = T.cast(target.msb.value(symtab), Integer)
464
+ lsb_val = T.cast(target.lsb.value(symtab), Integer)
465
+ mask = ((1 << (msb_val - lsb_val + 1)) - 1) << lsb_val
466
+ updated_parent = (parent_value & ~mask) | ((T.cast(new_value, Integer) << lsb_val) & mask)
467
+ write_back_nested(target.var, updated_parent, symtab)
468
+ else
469
+ raise InternalError, "Unknown target type for write-back: #{target.class.name}"
470
+ end
471
+ end
472
+
418
473
  # raise a value error, indicating that the value is not known at compile time
419
474
  #
420
475
  # @param reason [String] Error message
@@ -512,11 +567,11 @@ module Idl
512
567
  # end: <0-indexed position of the ending character in the input>
513
568
  sig { returns(T::Hash[String, T.untyped]) }
514
569
  def source_yaml
515
- base_offset = @starting_offset || 0
570
+ base_offset = source_starting_offset
516
571
  interval_begin = T.must(interval).begin
517
572
  interval_end = T.must(interval).size == 0 ? T.must(interval).begin + 1 : T.must(interval).end
518
573
 
519
- lfo = @line_file_offsets
574
+ lfo = source_line_file_offsets
520
575
  if lfo
521
576
  # Map an IDL-string position to a file byte offset using the per-line table.
522
577
  # Each entry lfo[i] is the file offset of the first character of IDL line i.
@@ -539,12 +594,23 @@ module Idl
539
594
  end
540
595
 
541
596
  {
542
- "file" => @input_file.to_s,
597
+ "file" => T.must(input_file).to_s,
543
598
  "begin" => file_begin,
544
599
  "end" => file_end
545
600
  }
546
601
  end
547
602
 
603
+ sig { returns(Integer) }
604
+ def source_starting_offset
605
+ @starting_offset || @parent&.source_starting_offset || 0
606
+ end
607
+
608
+ sig { returns(T.nilable(T::Array[Integer])) }
609
+ def source_line_file_offsets
610
+ return @line_file_offsets unless @line_file_offsets.nil?
611
+ @parent&.source_line_file_offsets
612
+ end
613
+
548
614
  private
549
615
 
550
616
  # Given an IDL-string position +pos+ and the per-line file-offset table +lfo+,
@@ -673,7 +739,10 @@ module Idl
673
739
  module Executable
674
740
  extend T::Sig
675
741
  extend T::Helpers
676
- interface!
742
+ abstract!
743
+
744
+ sig { returns(T::Boolean).checked(:never) }
745
+ def executable? = true
677
746
 
678
747
  # @!macro [new] execute
679
748
  # "execute" the statement by updating the variables in the symbol table
@@ -860,7 +929,10 @@ module Idl
860
929
  module Declaration
861
930
  extend T::Sig
862
931
  extend T::Helpers
863
- interface!
932
+ abstract!
933
+
934
+ sig { returns(T::Boolean).checked(:never) }
935
+ def declaration? = true
864
936
 
865
937
  # @!macro [new] add_symbol
866
938
  # Add symbol(s) at the outermost scope of the symbol table
@@ -1339,9 +1411,13 @@ module Idl
1339
1411
 
1340
1412
  enums.each { |g| g.add_symbol(symtab); }
1341
1413
  bitfields.each { |g| g.add_symbol(symtab) }
1414
+ # Functions must be registered before globals so that global variable
1415
+ # initializers can call functions (e.g. FLEN = implemented?(...) ? 64 : 32).
1416
+ # FunctionDefAst#add_symbol only stores the FunctionType; it does not
1417
+ # evaluate the body, so adding functions before globals is safe.
1418
+ functions.each { |g| g.add_symbol(symtab) }
1342
1419
  globals.each { |g| g.add_symbol(symtab) }
1343
1420
  structs.each { |g| g.add_symbol(symtab) }
1344
- functions.each { |g| g.add_symbol(symtab) }
1345
1421
  end
1346
1422
 
1347
1423
  # replaces an include statement with the ast in that file, making
@@ -1418,7 +1494,9 @@ module Idl
1418
1494
  end
1419
1495
  end
1420
1496
 
1421
- IsaAst.new(input, interval, kids)
1497
+ node = IsaAst.new(input, interval, kids)
1498
+ node.set_input_file(yaml.fetch("source").fetch("file"))
1499
+ node
1422
1500
  end
1423
1501
  end
1424
1502
 
@@ -1493,12 +1571,6 @@ module Idl
1493
1571
  end
1494
1572
  end
1495
1573
 
1496
- class ArraySizeSyntaxNode < SyntaxNode
1497
- def to_ast
1498
- ArraySizeAst.new(input, interval, send(:expression).to_ast)
1499
- end
1500
- end
1501
-
1502
1574
  class ArraySizeAst < AstNode
1503
1575
  include Rvalue
1504
1576
 
@@ -1560,25 +1632,21 @@ module Idl
1560
1632
  end
1561
1633
 
1562
1634
 
1563
- class EnumSizeSyntaxNode < SyntaxNode
1564
- def to_ast
1565
- EnumSizeAst.new(input, interval, send(:type_name).to_ast)
1566
- end
1567
- end
1568
-
1569
1635
  # represents the builtin that returns the nymber of elements in an enum class
1570
1636
  #
1571
1637
  # $enum_size(XRegWidth) #=> 2
1572
1638
  class EnumSizeAst < AstNode
1573
1639
  include Rvalue
1574
1640
 
1575
- def enum_class = children[0]
1641
+ def enum_class = children.fetch(0)
1576
1642
 
1577
1643
  sig { override.params(symtab: SymbolTable).returns(T::Boolean) }
1578
1644
  def const_eval?(symtab) = true
1579
1645
 
1646
+ sig { params(input: T.nilable(String), interval: T.nilable(T::Range[Integer]), enum_class_name: T.any(IdAst, UserTypeNameAst)).void }
1580
1647
  def initialize(input, interval, enum_class_name)
1581
- super(input, interval, [enum_class_name])
1648
+ user_type_name = enum_class_name.is_a?(UserTypeNameAst) ? enum_class_name : UserTypeNameAst.new(enum_class_name.input, enum_class_name.interval, enum_class_name.name)
1649
+ super(input, interval, [user_type_name])
1582
1650
  end
1583
1651
 
1584
1652
  def type_check(symtab, strict:)
@@ -1615,17 +1683,11 @@ module Idl
1615
1683
  interval = interval_from_source_yaml(yaml.fetch("source"))
1616
1684
  EnumSizeAst.new(
1617
1685
  input, interval,
1618
- T.cast(AstNode.from_h(yaml.fetch("enum_class_name"), source_mapper), T.all(Rvalue, AstNode))
1686
+ T.cast(AstNode.from_h(yaml.fetch("enum_class_name"), source_mapper), UserTypeNameAst)
1619
1687
  )
1620
1688
  end
1621
1689
  end
1622
1690
 
1623
- class EnumElementSizeSyntaxNode < SyntaxNode
1624
- def to_ast
1625
- EnumElementSizeAst.new(input, interval, send(:type_name).to_ast)
1626
- end
1627
- end
1628
-
1629
1691
  # represents the builtin that returns the bitwidth of an element in an enum class
1630
1692
  #
1631
1693
  # $enum_element_size(PrivilegeMode) #=> 3
@@ -1637,9 +1699,10 @@ module Idl
1637
1699
  sig { override.params(symtab: SymbolTable).returns(T::Boolean) }
1638
1700
  def const_eval?(symtab) = true
1639
1701
 
1640
- sig { params(input: T.nilable(String), interval: T.nilable(T::Range[Integer]), enum_class_name: UserTypeNameAst).void }
1702
+ sig { params(input: T.nilable(String), interval: T.nilable(T::Range[Integer]), enum_class_name: T.any(IdAst, UserTypeNameAst)).void }
1641
1703
  def initialize(input, interval, enum_class_name)
1642
- super(input, interval, [enum_class_name])
1704
+ user_type_name = enum_class_name.is_a?(UserTypeNameAst) ? enum_class_name : UserTypeNameAst.new(enum_class_name.input, enum_class_name.interval, enum_class_name.name)
1705
+ super(input, interval, [user_type_name])
1643
1706
  end
1644
1707
 
1645
1708
  def type_check(symtab, strict:)
@@ -1677,12 +1740,6 @@ module Idl
1677
1740
  end
1678
1741
  end
1679
1742
 
1680
- class EnumCastSyntaxNode < SyntaxNode
1681
- def to_ast
1682
- EnumCastAst.new(input, interval, send(:type_name).to_ast, send(:expression).to_ast)
1683
- end
1684
- end
1685
-
1686
1743
  class EnumCastAst < AstNode
1687
1744
  include Rvalue
1688
1745
 
@@ -1690,22 +1747,23 @@ module Idl
1690
1747
  def const_eval?(symtab) = true
1691
1748
 
1692
1749
  # @return [UserTypeAst] Enum name
1693
- def enum_name = @children[0]
1750
+ def enum_name = @children.fetch(0)
1694
1751
 
1695
1752
  # @return [Rvalue] Value expression
1696
- def expression = @children[1]
1753
+ def expression = @children.fetch(1)
1697
1754
 
1698
1755
  sig {
1699
1756
  params(
1700
1757
  input: T.nilable(String),
1701
1758
  interval: T.nilable(T::Range[Integer]),
1702
- type_name: UserTypeNameAst,
1759
+ type_name: T.any(IdAst, UserTypeNameAst),
1703
1760
  expression: RvalueAst
1704
1761
  )
1705
1762
  .void
1706
1763
  }
1707
1764
  def initialize(input, interval, type_name, expression)
1708
- super(input, interval, [type_name, expression])
1765
+ user_type_name = type_name.is_a?(UserTypeNameAst) ? type_name : UserTypeNameAst.new(type_name.input, type_name.interval, type_name.name)
1766
+ super(input, interval, [user_type_name, expression])
1709
1767
  end
1710
1768
 
1711
1769
  def type_check(symtab, strict:)
@@ -1758,26 +1816,21 @@ module Idl
1758
1816
  end
1759
1817
  end
1760
1818
 
1761
- class EnumArrayCastSyntaxNode < SyntaxNode
1762
- def to_ast
1763
- EnumArrayCastAst.new(input, interval, send(:type_name).to_ast)
1764
- end
1765
- end
1766
-
1767
1819
  # represents the builtin that returns an array with all elements of an Enum type
1768
1820
  #
1769
1821
  # $enum_to_a(PrivilegeMode) #=> [3, 1, 1, 0, 5, 4]
1770
1822
  class EnumArrayCastAst < AstNode
1771
1823
  include Rvalue
1772
1824
 
1773
- def enum_class = children[0]
1825
+ def enum_class = children.fetch(0)
1774
1826
 
1775
1827
  sig { override.params(symtab: SymbolTable).returns(T::Boolean) }
1776
1828
  def const_eval?(symtab) = true
1777
1829
 
1778
- sig { params(input: T.nilable(String), interval: T.nilable(T::Range[Integer]), enum_class_name: UserTypeNameAst).void }
1830
+ sig { params(input: T.nilable(String), interval: T.nilable(T::Range[Integer]), enum_class_name: T.any(IdAst, UserTypeNameAst)).void }
1779
1831
  def initialize(input, interval, enum_class_name)
1780
- super(input, interval, [enum_class_name])
1832
+ user_type_name = enum_class_name.is_a?(UserTypeNameAst) ? enum_class_name : UserTypeNameAst.new(enum_class_name.input, enum_class_name.interval, enum_class_name.name)
1833
+ super(input, interval, [user_type_name])
1781
1834
  end
1782
1835
 
1783
1836
  def type_check(symtab, strict:)
@@ -2479,7 +2532,12 @@ module Idl
2479
2532
 
2480
2533
  sig { override.params(symtab: SymbolTable).returns(T::Boolean) }
2481
2534
  def const_eval?(symtab)
2482
- if var.name == "X"
2535
+ var_type = begin
2536
+ var.type(symtab)
2537
+ rescue AstNode::TypeError, AstNode::InternalError
2538
+ nil
2539
+ end
2540
+ if var_type.is_a?(Type) && var_type.kind == :array && var_type.sub_type.is_a?(RegFileElementType) && var_type.qualifiers.include?(:global)
2483
2541
  false
2484
2542
  else
2485
2543
  var.const_eval?(symtab) && index.const_eval?(symtab)
@@ -2544,7 +2602,12 @@ module Idl
2544
2602
  if var.type(symtab).integral?
2545
2603
  (var_val >> index.value(symtab)) & 1
2546
2604
  else
2547
- value_error "X registers are not compile-time-known" if var.text_value == "X"
2605
+ var_type = begin
2606
+ var.type(symtab)
2607
+ rescue AstNode::TypeError, AstNode::InternalError
2608
+ nil
2609
+ end
2610
+ value_error "Register file registers are not compile-time-known" if var_type.is_a?(Type) && var_type.kind == :array && var_type.sub_type.is_a?(RegFileElementType) && var_type.qualifiers.include?(:global)
2548
2611
 
2549
2612
  # internal_error "Not an array" unless ary.type.kind == :array
2550
2613
 
@@ -2589,7 +2652,12 @@ module Idl
2589
2652
  sig { override.params(symtab: SymbolTable).returns(T::Boolean) }
2590
2653
  def const_eval?(symtab)
2591
2654
  v = var
2592
- if v.is_a?(IdAst) && v.name == "X"
2655
+ var_type = begin
2656
+ v.type(symtab)
2657
+ rescue AstNode::TypeError, AstNode::InternalError
2658
+ nil
2659
+ end
2660
+ if var_type.is_a?(Type) && var_type.kind == :array && var_type.sub_type.is_a?(RegFileElementType) && var_type.qualifiers.include?(:global)
2593
2661
  false
2594
2662
  else
2595
2663
  v.const_eval?(symtab) && msb.const_eval?(symtab).const_eval? && lsb.const_eval?(symtab)
@@ -2853,12 +2921,6 @@ module Idl
2853
2921
  end
2854
2922
  end
2855
2923
 
2856
- class AryElementAssignmentSyntaxNode < SyntaxNode
2857
- def to_ast
2858
- AryElementAssignmentAst.new(input, interval, send(:var).to_ast, send(:idx).to_ast, send(:rval).to_ast)
2859
- end
2860
- end
2861
-
2862
2924
  # represents an array element assignment
2863
2925
  #
2864
2926
  # for example:
@@ -2873,8 +2935,10 @@ module Idl
2873
2935
  if idx.const_eval?(symtab) && rhs.const_eval?(symtab)
2874
2936
  true
2875
2937
  else
2876
- lhs_var = symtab.get(lhs.name)
2877
- type_error "array #{lhs.name} has not been declared" if lhs_var.nil?
2938
+ base_name = AstNode.extract_base_var_name(lhs)
2939
+ type_error "Cannot determine base variable for #{lhs.text_value}" if base_name.nil?
2940
+ lhs_var = symtab.get(base_name)
2941
+ type_error "array #{base_name} has not been declared" if lhs_var.nil?
2878
2942
 
2879
2943
  lhs_var.const_incompatible!
2880
2944
  false
@@ -2940,13 +3004,15 @@ module Idl
2940
3004
  value_error "right-hand side of array element assignment is unknown"
2941
3005
  end
2942
3006
  when :bits
2943
- var = symtab.get(lhs.text_value)
2944
3007
  value_result = value_try do
2945
- v = rhs.value(symtab)
2946
- var.value = (lhs.value(symtab) & ~0) | ((v & 1) << idx.value(symtab))
3008
+ new_element = (lhs.value(symtab) & ~0) | ((rhs.value(symtab) & 1) << idx.value(symtab))
3009
+ AstNode.write_back_nested(lhs, new_element, symtab)
2947
3010
  end
2948
3011
  value_else(value_result) do
2949
- var.value = nil
3012
+ base_name = T.must(AstNode.extract_base_var_name(lhs))
3013
+ v = symtab.get(base_name)
3014
+ internal_error "did not find array base '#{base_name}'" if v.nil?
3015
+ v.value = nil
2950
3016
  end
2951
3017
  else
2952
3018
  internal_error "unexpected type for array element assignment"
@@ -2983,7 +3049,27 @@ module Idl
2983
3049
 
2984
3050
  class AryRangeAssignmentSyntaxNode < SyntaxNode
2985
3051
  def to_ast
2986
- AryRangeAssignmentAst.new(input, interval, send(:var).to_ast, send(:msb).to_ast, send(:lsb).to_ast, send(:rval).to_ast)
3052
+ var = send(:var).to_ast
3053
+ brackets = send(:brackets).elements
3054
+
3055
+ # Build access chain for all but the last bracket
3056
+ brackets[0..-2].each do |bracket|
3057
+ var =
3058
+ if bracket.msb.empty?
3059
+ AryElementAccessAst.new(input, interval, var, bracket.lsb.to_ast)
3060
+ else
3061
+ AryRangeAccessAst.new(input, interval, var,
3062
+ bracket.msb.expression.to_ast, bracket.lsb.to_ast)
3063
+ end
3064
+ end
3065
+
3066
+ # Use accumulated `var` for the assignment target
3067
+ last_bracket = brackets[-1]
3068
+ if last_bracket.msb.empty?
3069
+ AryElementAssignmentAst.new(input, interval, var, last_bracket.lsb.to_ast, send(:rval).to_ast)
3070
+ else
3071
+ AryRangeAssignmentAst.new(input, interval, var, last_bracket.msb.expression.to_ast, last_bracket.lsb.to_ast, send(:rval).to_ast)
3072
+ end
2987
3073
  end
2988
3074
  end
2989
3075
 
@@ -3001,8 +3087,10 @@ module Idl
3001
3087
  if lsb.const_eval?(symtab) && msb.const_eval?(symtab) && write_value.const_eval?(symtab)
3002
3088
  true
3003
3089
  else
3004
- lhs_var = symtab.get(variable.name)
3005
- type_error "array #{variable.name} has not be declared" if lhs_var.nil?
3090
+ base_name = AstNode.extract_base_var_name(variable)
3091
+ type_error "Cannot determine base variable for #{variable.text_value}" if base_name.nil?
3092
+ lhs_var = symtab.get(base_name)
3093
+ type_error "array #{base_name} has not be declared" if lhs_var.nil?
3006
3094
 
3007
3095
  lhs_var.const_incompatible!
3008
3096
  false
@@ -3060,26 +3148,29 @@ module Idl
3060
3148
  def execute(symtab)
3061
3149
  return if variable.type(symtab).global?
3062
3150
 
3063
- var = symtab.get(variable.name)
3064
- internal_error "Variable #{variable.name} not found" if var.nil?
3065
-
3066
3151
  value_result = value_try do
3067
- var_val = variable.value(symtab)
3068
-
3069
3152
  msb_val = msb.value(symtab)
3070
3153
  lsb_val = lsb.value(symtab)
3071
3154
 
3072
3155
  type_error "MSB (#{msb_val}) is <= LSB (#{lsb_val})" if msb_val <= lsb_val
3073
3156
 
3074
3157
  rval_val = write_value.value(symtab)
3075
-
3076
3158
  mask = ((1 << (msb_val - lsb_val + 1)) - 1) << lsb_val
3077
3159
 
3078
- var.value = (var_val & ~mask) | ((rval_val << lsb_val) & mask)
3160
+ # Read current value, modify bits
3161
+ var_val = variable.value(symtab)
3162
+ var_val &= ~mask
3163
+ new_val = var_val | ((rval_val << lsb_val) & mask)
3164
+
3165
+ # Write back through the access chain
3166
+ AstNode.write_back_nested(variable, new_val, symtab)
3079
3167
  :ok
3080
3168
  end
3081
3169
  value_else(value_result) do
3082
- var.value = nil
3170
+ base_name = T.must(AstNode.extract_base_var_name(variable))
3171
+ v = symtab.get(base_name)
3172
+ internal_error "did not find array base" if v.nil?
3173
+ v.value = nil
3083
3174
  value_error "Either the range or right-hand side of an array range assignment is unknown"
3084
3175
  end
3085
3176
  end
@@ -3217,7 +3308,13 @@ module Idl
3217
3308
  var.value = bitfield_val
3218
3309
  elsif var.type.kind == :struct
3219
3310
  struct_val = id.value(symtab)
3220
- struct_val[@field_name] = rhs.value(symtab)
3311
+ value_result = value_try do
3312
+ struct_val[@field_name] = rhs.value(symtab)
3313
+ end
3314
+ value_else(value_result) do
3315
+ struct_val[@field_name] = nil
3316
+ value_error ""
3317
+ end
3221
3318
  else
3222
3319
  value_error "TODO: Field assignment execution"
3223
3320
  end
@@ -5273,6 +5370,10 @@ module Idl
5273
5370
  # @!macro value
5274
5371
  def value(symtab) = expression.value(symtab)
5275
5372
 
5373
+ def max_value(symtab) = expression.max_value(symtab)
5374
+
5375
+ def min_value(symtab) = expression.min_value(symtab)
5376
+
5276
5377
  # @!macro to_idl
5277
5378
  sig { override.returns(String) }
5278
5379
  def to_idl = "(#{expression.to_idl})"
@@ -5702,6 +5803,120 @@ module Idl
5702
5803
  end
5703
5804
  end
5704
5805
 
5806
+ # Generic syntax node for $name(arg, ...) — dispatches to the correct typed AstNode in to_ast.
5807
+ class DollarFunctionCallSyntaxNode < SyntaxNode
5808
+ def to_ast
5809
+ dollar_name = "$#{send(:name).text_value}"
5810
+ arg_nodes = dollar_arg_list_elements
5811
+ enum_type_name_validation = {
5812
+ 0 => {
5813
+ classes: [IdAst, UserTypeNameAst],
5814
+ description: "an identifier or user type name"
5815
+ }
5816
+ }
5817
+
5818
+ case dollar_name
5819
+ when "$width" then builtin_call_ast(dollar_name, arg_nodes, 1, WidthRevealAst)
5820
+ when "$signed" then builtin_call_ast(dollar_name, arg_nodes, 1, SignCastAst)
5821
+ when "$bits" then builtin_call_ast(dollar_name, arg_nodes, 1, BitsCastAst)
5822
+ when "$enum_size" then builtin_call_ast(dollar_name, arg_nodes, 1, EnumSizeAst, enum_type_name_validation)
5823
+ when "$enum_element_size" then builtin_call_ast(dollar_name, arg_nodes, 1, EnumElementSizeAst, enum_type_name_validation)
5824
+ when "$enum_to_a" then builtin_call_ast(dollar_name, arg_nodes, 1, EnumArrayCastAst, enum_type_name_validation)
5825
+ when "$enum" then builtin_call_ast(dollar_name, arg_nodes, 2, EnumCastAst, enum_type_name_validation)
5826
+ when "$array_size" then builtin_call_ast(dollar_name, arg_nodes, 1, ArraySizeAst)
5827
+ when "$array_includes?" then builtin_call_ast(dollar_name, arg_nodes, 2, ArrayIncludesAst)
5828
+ else
5829
+ ParseTimeDetectedTypeError.new(input, interval, "#{dollar_name} is not a builtin function")
5830
+ end
5831
+ end
5832
+
5833
+ private
5834
+
5835
+ def builtin_call_ast(dollar_name, arg_nodes, expected_arg_count, ast_class, arg_type_validations = {})
5836
+ if arg_nodes.size != expected_arg_count
5837
+ ParseTimeDetectedTypeError.new(input, interval, "#{dollar_name} expects #{expected_arg_count} argument#{expected_arg_count == 1 ? "" : "s"}; #{arg_nodes.size} given")
5838
+ else
5839
+ arg_asts = arg_nodes.first(expected_arg_count).map(&:to_ast)
5840
+
5841
+ arg_type_validations.each do |arg_index, validation|
5842
+ next if validation[:classes].any? { |klass| arg_asts[arg_index].is_a?(klass) }
5843
+
5844
+ return ParseTimeDetectedTypeError.new(
5845
+ input,
5846
+ interval,
5847
+ "#{dollar_name} expects argument #{arg_index + 1} to be #{validation[:description]}; #{builtin_arg_type_name(arg_asts[arg_index])} given"
5848
+ )
5849
+ end
5850
+
5851
+ ast_class.new(input, interval, *arg_asts)
5852
+ end
5853
+ end
5854
+
5855
+ def builtin_arg_type_name(arg_ast)
5856
+ arg_ast.class.name.split("::").last
5857
+ end
5858
+
5859
+ private
5860
+
5861
+ def dollar_arg_list_elements
5862
+ arg_list = send(:args)
5863
+ first = arg_list.first
5864
+ rest = arg_list.rest.elements.map { |e| e.expression }
5865
+ first.empty? ? rest : [first] + rest
5866
+ end
5867
+
5868
+ end
5869
+
5870
+ # Generic syntax node for $name (no parens) — creates a BuiltinVariableAst.
5871
+ class DollarVariableSyntaxNode < SyntaxNode
5872
+ def to_ast
5873
+ BuiltinVariableAst.new(input, interval, "$#{send(:name).text_value}")
5874
+ end
5875
+ end
5876
+
5877
+ # Syntax node for $name = expr — dispatches to PcAssignmentAst for $pc,
5878
+ class DollarVariableAssignmentSyntaxNode < SyntaxNode
5879
+ def to_ast
5880
+ dollar_name = "$#{send(:dollar_variable).send(:name).text_value}"
5881
+ rhs = send(:rval).to_ast
5882
+ case dollar_name
5883
+ when "$pc"
5884
+ PcAssignmentAst.new(input, interval, rhs)
5885
+ else
5886
+ ParseTimeDetectedTypeError.new(input, interval, "#{dollar_name} is not assignable")
5887
+ end
5888
+ end
5889
+ end
5890
+
5891
+ # AstNode for an error detected during to_ast. Always causes a type error
5892
+ class ParseTimeDetectedTypeError < AstNode
5893
+
5894
+ sig { override.params(symtab: SymbolTable).returns(T::Boolean) }
5895
+ def const_eval?(symtab) = true
5896
+
5897
+ def initialize(input, interval, reason)
5898
+ super(input, interval, EMPTY_ARRAY)
5899
+ @reason = reason
5900
+ end
5901
+
5902
+ sig { override.params(symtab: SymbolTable, strict: T::Boolean).void }
5903
+ def type_check(symtab, strict:)
5904
+ type_error @reason
5905
+ end
5906
+
5907
+ sig { params(symtab: SymbolTable).returns(Type) }
5908
+ def type(symtab) = type_error(@reason)
5909
+
5910
+ sig { params(symtab: SymbolTable).returns(T.untyped) }
5911
+ def value(symtab) = value_error "Can't take value of a type error"
5912
+
5913
+ sig { override.returns(String) }
5914
+ def to_idl = text_value
5915
+
5916
+ sig { override.returns(T::Hash[String, T.untyped]) }
5917
+ def to_h = { "kind" => "ParseTimeDetectedTypeError", "reason" => @reason, "source" => source_yaml }
5918
+ end
5919
+
5705
5920
  class PostIncrementExpressionSyntaxNode < SyntaxNode
5706
5921
  def to_ast
5707
5922
  PostIncrementExpressionAst.new(input, interval, send(:rval).to_ast)
@@ -5846,7 +6061,9 @@ module Idl
5846
6061
  range = T.cast(obj.type(symtab), BitfieldType).range(@field_name)
5847
6062
  (T.cast(obj.value(symtab), Integer) >> range.first) & ((1 << range.size) - 1)
5848
6063
  elsif kind(symtab) == :struct
5849
- T.cast(obj.value(symtab), T::Hash[String, BasicValueRbType])[@field_name]
6064
+ field_val = T.cast(obj.value(symtab), T::Hash[String, BasicValueRbType])[@field_name]
6065
+ value_error "#{@field_name} is not known at compile-time" if field_val.nil?
6066
+ field_val
5850
6067
  else
5851
6068
  type_error "#{obj.text_value} is Not a bitfield."
5852
6069
  end
@@ -6261,6 +6478,34 @@ module Idl
6261
6478
  end
6262
6479
  end
6263
6480
 
6481
+ # @!macro max_value
6482
+ def max_value(symtab)
6483
+ value_result = value_try do
6484
+ cond = condition.value(symtab)
6485
+ return cond ? true_expression.max_value(symtab) : false_expression.max_value(symtab)
6486
+ end
6487
+ value_else(value_result) do
6488
+ t = true_expression.max_value(symtab)
6489
+ f = false_expression.max_value(symtab)
6490
+ return :unknown if t == :unknown || f == :unknown
6491
+ [t, f].max
6492
+ end
6493
+ end
6494
+
6495
+ # @!macro min_value
6496
+ def min_value(symtab)
6497
+ value_result = value_try do
6498
+ cond = condition.value(symtab)
6499
+ return cond ? true_expression.min_value(symtab) : false_expression.min_value(symtab)
6500
+ end
6501
+ value_else(value_result) do
6502
+ t = true_expression.min_value(symtab)
6503
+ f = false_expression.min_value(symtab)
6504
+ return :unknown if t == :unknown || f == :unknown
6505
+ [t, f].min
6506
+ end
6507
+ end
6508
+
6264
6509
  # @!macro to_idl
6265
6510
  sig { override.returns(String) }
6266
6511
  def to_idl = "#{condition.to_idl} ? #{true_expression.to_idl} : #{false_expression.to_idl}"
@@ -6352,10 +6597,10 @@ module Idl
6352
6597
 
6353
6598
  # @!macro execute
6354
6599
  def execute(symtab)
6355
- if action.is_a?(Declaration)
6600
+ if action.declaration?
6356
6601
  action.add_symbol(symtab)
6357
6602
  end
6358
- if action.is_a?(Executable)
6603
+ if action.executable?
6359
6604
  action.execute(symtab)
6360
6605
  end
6361
6606
  end
@@ -6408,7 +6653,7 @@ module Idl
6408
6653
  # @!macro type_check
6409
6654
  def type_check(symtab, strict:)
6410
6655
  action.type_check(symtab, strict:)
6411
- type_error "Cannot declare from a conditional statement" if action.is_a?(Declaration)
6656
+ type_error "Cannot declare from a conditional statement" if action.declaration?
6412
6657
 
6413
6658
  condition.type_check(symtab, strict:)
6414
6659
  type_error "condition is not boolean" unless condition.type(symtab).convertable_to?(:boolean)
@@ -7884,7 +8129,7 @@ module Idl
7884
8129
  stmts.each do |s|
7885
8130
  if s.is_a?(Returns)
7886
8131
  return s.return_type(symtab)
7887
- elsif s.action.is_a?(Declaration)
8132
+ elsif s.action.declaration?
7888
8133
  s.action.add_symbol(symtab)
7889
8134
  end
7890
8135
  end