Class: Idl::AstNode

Inherits:
Object
  • Object
show all
Extended by:
T::Helpers, T::Sig
Defined in:
lib/idlc/ast.rb,
lib/idlc/type.rb,
lib/idlc/ast_decl.rb,
lib/idlc/passes/prune.rb,
lib/idlc/passes/gen_adoc.rb,
lib/idlc/passes/gen_option_adoc.rb,
lib/idlc/passes/find_return_values.rb,
lib/idlc/passes/find_src_registers.rb,
lib/idlc/passes/reachable_functions.rb,
lib/idlc/passes/find_referenced_csrs.rb,
lib/idlc/passes/reachable_exceptions.rb

Overview

set up a default

Direct Known Subclasses

ArrayIncludesAst, ArrayLiteralAst, ArraySizeAst, AryElementAccessAst, AryElementAssignmentAst, AryRangeAccessAst, AryRangeAssignmentAst, BinaryExpressionAst, BitfieldDefinitionAst, BitfieldFieldDefinitionAst, BitsCastAst, BuiltinEnumDefinitionAst, BuiltinTypeNameAst, BuiltinVariableAst, CommentAst, ConcatenationExpressionAst, ConditionalReturnStatementAst, ConditionalStatementAst, ConstraintBodyAst, CsrFieldAssignmentAst, CsrFieldReadExpressionAst, CsrFunctionCallAst, CsrReadExpressionAst, CsrSoftwareWriteAst, CsrWriteAst, DontCareLvalueAst, DontCareReturnAst, ElseIfAst, EnumArrayCastAst, EnumCastAst, EnumDefinitionAst, EnumElementSizeAst, EnumRefAst, EnumSizeAst, FalseExpressionAst, FetchAst, FieldAccessExpressionAst, FieldAssignmentAst, ForLoopAst, FunctionBodyAst, FunctionCallExpressionAst, FunctionDefAst, GlobalAst, GlobalWithInitializationAst, IdAst, IfAst, IfBodyAst, ImplicationExpressionAst, ImplicationStatementAst, IncludeStatementAst, IntLiteralAst, IsaAst, MultiVariableAssignmentAst, MultiVariableDeclarationAst, NoopAst, ParenExpressionAst, ParseTimeDetectedTypeError, PcAssignmentAst, PostDecrementExpressionAst, PostIncrementExpressionAst, ReplicationExpressionAst, ReturnExpressionAst, ReturnStatementAst, SignCastAst, StatementAst, StringLiteralAst, StructDefinitionAst, TernaryOperatorExpressionAst, TrueExpressionAst, UnaryOperatorExpressionAst, UserTypeNameAst, VariableAssignmentAst, VariableDeclarationAst, VariableDeclarationWithInitializationAst, WidthRevealAst

Defined Under Namespace

Classes: InternalError, LinesDescriptor, TypeError, ValueError

Constant Summary collapse

Bits1Type =
Type.new(:bits, width: 1, qualifiers: [:known].freeze).freeze
PossiblyUnknownBits1Type =
Type.new(:bits, width: 1).freeze
Bits32Type =
Type.new(:bits, width: 32, qualifiers: [:known].freeze).freeze
PossiblyUnknownBits32Type =
Type.new(:bits, width: 32).freeze
Bits64Type =
Type.new(:bits, width: 64, qualifiers: [:known].freeze).freeze
PossiblyUnknownBits64Type =
Type.new(:bits, width: 64).freeze
ConstBoolType =
Type.new(:boolean, qualifiers: [:const]).freeze
BoolType =
Type.new(:boolean).freeze
VoidType =
Type.new(:void).freeze
StringType =
Type.new(:string).freeze
ReachableFunctionCacheType =
T.type_alias { T::Hash[T::Array[T.untyped], T::Array[FunctionDefAst]] }

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(input, interval, children) ⇒ AstNode

Returns a new instance of AstNode.



231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/idlc/ast.rb', line 231

def initialize(input, interval, children)
  @input = input
  @input_file = nil
  @starting_line = 0
  @starting_offset = 0
  @line_file_offsets = T.let(nil, T.nilable(T::Array[Integer]))
  @interval = interval
  @interval_text_value =
    unless input.nil? || interval.nil?
      input[interval]
    end
  @children = children
  @parent = nil # will be set later unless this is the root
  @children.each { |child| child.instance_variable_set(:@parent, self) }
end

Class Attribute Details

.value_error_astObject

Returns the value of attribute value_error_ast.



419
420
421
# File 'lib/idlc/ast.rb', line 419

def value_error_ast
  @value_error_ast
end

.value_error_reasonObject

Returns the value of attribute value_error_reason.



419
420
421
# File 'lib/idlc/ast.rb', line 419

def value_error_reason
  @value_error_reason
end

Instance Attribute Details

#childrenObject (readonly)

Returns the value of attribute children.



99
100
101
# File 'lib/idlc/ast.rb', line 99

def children
  @children
end

#inputObject (readonly)

Returns the value of attribute input.



80
81
82
# File 'lib/idlc/ast.rb', line 80

def input
  @input
end

#intervalObject (readonly)

Returns the value of attribute interval.



84
85
86
# File 'lib/idlc/ast.rb', line 84

def interval
  @interval
end

#parentObject (readonly)

Returns the value of attribute parent.



95
96
97
# File 'lib/idlc/ast.rb', line 95

def parent
  @parent
end

Class Method Details

.extract_base_var_name(node) ⇒ Object



426
427
428
429
430
431
432
433
434
435
# File 'lib/idlc/ast.rb', line 426

def self.extract_base_var_name(node)
  case node
  when IdAst
    node.name
  when AryElementAccessAst, AryRangeAccessAst
    extract_base_var_name(node.var)
  else
    nil
  end
end

.from_h(yaml, source_mapper) ⇒ Object



655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
# File 'lib/idlc/ast.rb', line 655

def self.from_h(yaml, source_mapper)
  case yaml.fetch("kind")
  when "array_access"              then AryElementAccessAst.from_h(yaml, source_mapper)
  when "array_element_assignment"  then AryElementAssignmentAst.from_h(yaml, source_mapper)
  when "array_includes_funcall"    then ArrayIncludesAst.from_h(yaml, source_mapper)
  when "array_literal"             then ArrayLiteralAst.from_h(yaml, source_mapper)
  when "array_range_access"        then AryRangeAccessAst.from_h(yaml, source_mapper)
  when "array_range_assignment"    then AryRangeAssignmentAst.from_h(yaml, source_mapper)
  when "array_size_funcall"        then ArraySizeAst.from_h(yaml, source_mapper)
  when "binary_operator_expr"      then BinaryExpressionAst.from_h(yaml, source_mapper)
  when "bitfield_decl"             then BitfieldDefinitionAst.from_h(yaml, source_mapper)
  when "bitfield_field_decl"       then BitfieldFieldDefinitionAst.from_h(yaml, source_mapper)
  when "bits_cast"                 then BitsCastAst.from_h(yaml, source_mapper)
  when "bits_literal"              then IntLiteralAst.from_h(yaml, source_mapper)
  when "bits_to_enum_cast"         then EnumCastAst.from_h(yaml, source_mapper)
  when "bits_type"                 then BuiltinTypeNameAst.from_h(yaml, source_mapper)
  when "bits_width_cast"           then WidthRevealAst.from_h(yaml, source_mapper)
  when "builtin_enum_decl"         then BuiltinEnumDefinitionAst.from_h(yaml, source_mapper)
  when "builtin_type"              then BuiltinTypeNameAst.from_h(yaml, source_mapper)
  when "builtin_var_expr"          then BuiltinVariableAst.from_h(yaml, source_mapper)
  when "comment"                   then CommentAst.from_h(yaml, source_mapper)
  when "concat_expr"               then ConcatenationExpressionAst.from_h(yaml, source_mapper)
  when "conditional_stmt"          then yaml.fetch("expr").fetch("kind") == "return_expr" ? ConditionalReturnStatementAst.from_h(yaml, source_mapper) : ConditionalStatementAst.from_h(yaml, source_mapper)
  when "constraint_body"           then ConstraintBodyAst.from_h(yaml, source_mapper)
  when "csr_access_expr"           then CsrWriteAst.from_h(yaml, source_mapper)
  when "csr_field_assignment"      then CsrFieldAssignmentAst.from_h(yaml, source_mapper)
  when "csr_field_read_expr"       then CsrFieldReadExpressionAst.from_h(yaml, source_mapper)
  when "csr_funcall_expr"          then CsrFunctionCallAst.from_h(yaml, source_mapper)
  when "csr_read_expr"             then CsrReadExpressionAst.from_h(yaml, source_mapper)
  when "csr_sw_write_expr"         then CsrSoftwareWriteAst.from_h(yaml, source_mapper)
  when "dont_care"                 then DontCareReturnAst.from_h(yaml, source_mapper)
  when "dont_care_lval"            then DontCareLvalueAst.from_h(yaml, source_mapper)
  when "else_if_stmt"              then ElseIfAst.from_h(yaml, source_mapper)
  when "enum_decl"                 then EnumDefinitionAst.from_h(yaml, source_mapper)
  when "enum_element_size_funcall" then EnumElementSizeAst.from_h(yaml, source_mapper)
  when "enum_reference_expr"       then EnumRefAst.from_h(yaml, source_mapper)
  when "enum_size_funcall"         then EnumSizeAst.from_h(yaml, source_mapper)
  when "enum_to_array_cast"        then EnumArrayCastAst.from_h(yaml, source_mapper)
  when "false"                     then FalseExpressionAst.from_h(yaml, source_mapper)
  when "field_access_expr"         then FieldAccessExpressionAst.from_h(yaml, source_mapper)
  when "field_assignment"          then FieldAssignmentAst.from_h(yaml, source_mapper)
  when "fetch_decl"                then FetchAst.from_h(yaml, source_mapper)
  when "for_loop_stmt"             then ForLoopAst.from_h(yaml, source_mapper)
  when "funcall_expr"              then FunctionCallExpressionAst.from_h(yaml, source_mapper)
  when "function_body"             then FunctionBodyAst.from_h(yaml, source_mapper)
  when "function_decl"             then FunctionDefAst.from_h(yaml, source_mapper)
  when "id"                        then IdAst.from_h(yaml, source_mapper)
  when "if_body"                   then IfBodyAst.from_h(yaml, source_mapper)
  when "if_stmt"                   then IfAst.from_h(yaml, source_mapper)
  when "implication_expr"          then ImplicationExpressionAst.from_h(yaml, source_mapper)
  when "implication_stmt"          then ImplicationStatementAst.from_h(yaml, source_mapper)
  when "isa"                       then IsaAst.from_h(yaml, source_mapper)
  when "global_var_decl_with_init" then GlobalWithInitializationAst.from_h(yaml, source_mapper)
  when "global_var_decl"           then GlobalAst.from_h(yaml, source_mapper)
  when "multi_var_assignment"      then MultiVariableAssignmentAst.from_h(yaml, source_mapper)
  when "multi_var_decl"            then MultiVariableDeclarationAst.from_h(yaml, source_mapper)
  when "noop_expr"                 then NoopAst.from_h(yaml, source_mapper)
  when "paren_expr"                then ParenExpressionAst.from_h(yaml, source_mapper)
  when "pc_assignment"             then PcAssignmentAst.from_h(yaml, source_mapper)
  when "post_decrement_expr"       then PostDecrementExpressionAst.from_h(yaml, source_mapper)
  when "post_increment_expr"       then PostIncrementExpressionAst.from_h(yaml, source_mapper)
  when "repl_expr"                 then ReplicationExpressionAst.from_h(yaml, source_mapper)
  when "return_expr"               then ReturnExpressionAst.from_h(yaml, source_mapper)
  when "sign_cast"                 then SignCastAst.from_h(yaml, source_mapper)
  when "stmt"                      then yaml.fetch("expr").fetch("kind") == "return_expr" ? ReturnStatementAst.from_h(yaml, source_mapper) : StatementAst.from_h(yaml, source_mapper)
  when "string_literal"            then StringLiteralAst.from_h(yaml, source_mapper)
  when "struct_decl"               then StructDefinitionAst.from_h(yaml, source_mapper)
  when "ternary_operator_expr"     then TernaryOperatorExpressionAst.from_h(yaml, source_mapper)
  when "true"                      then TrueExpressionAst.from_h(yaml, source_mapper)
  when "unary_operator_expr"       then UnaryOperatorExpressionAst.from_h(yaml, source_mapper)
  when "user_type_reference"       then UserTypeNameAst.from_h(yaml, source_mapper)
  when "var_assignment"            then VariableAssignmentAst.from_h(yaml, source_mapper)
  when "var_decl"                  then VariableDeclarationAst.from_h(yaml, source_mapper)
  when "var_decl_init"             then VariableDeclarationWithInitializationAst.from_h(yaml, source_mapper)
  else
    raise "Bad YAML kind: '#{yaml.fetch("kind")}'"
  end
end

.input_from_source_yaml(yaml, source_mapper) ⇒ Object



641
642
643
644
# File 'lib/idlc/ast.rb', line 641

def self.input_from_source_yaml(yaml, source_mapper)
  f = yaml.fetch("file")
  source_mapper.key?(f) ? source_mapper.fetch(f) : nil
end

.interval_from_source_yaml(yaml) ⇒ Object



647
648
649
# File 'lib/idlc/ast.rb', line 647

def self.interval_from_source_yaml(yaml)
  yaml.fetch("begin")...yaml.fetch("end")
end

.value_else(value_result, &_block) ⇒ Object



204
205
206
207
208
# File 'lib/idlc/ast.rb', line 204

def self.value_else(value_result, &_block)
  return unless value_result == :unknown_value

  yield
end

.value_error(reason, ast = nil) ⇒ Object



478
479
480
481
482
483
484
485
# File 'lib/idlc/ast.rb', line 478

def self.value_error(reason, ast = nil)
  AstNode.value_error_reason = reason
  AstNode.value_error_ast = ast
  # warn reason
  # warn "At #{ast.input_file}:#{ast.lineno}" unless ast.nil?
  throw(:value_error, :unknown_value)
  #raise AstNode::ValueError.new(lineno, input_file, reason), reason, []
end

.value_try(&block) ⇒ Object



197
198
199
# File 'lib/idlc/ast.rb', line 197

def self.value_try(&block)
  catch(:value_error, &block)
end

.write_back_nested(target, new_value, symtab) ⇒ Object



444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
# File 'lib/idlc/ast.rb', line 444

def self.write_back_nested(target, new_value, symtab)
  case target
  when IdAst
    # Base case: simple variable
    existing_var = symtab.get(target.name)
    raise InternalError, "write_back_nested: '#{target.name}' not found in symbol table" if existing_var.nil?
    existing_var.value = new_value
  when AryElementAccessAst
    # Recursive case: v[idx] = val
    # Read parent, modify element, write back parent
    parent_value = target.var.value(symtab)
    idx_val = target.index.value(symtab)
    parent_value[idx_val] = new_value
    # Recursively write back the parent (which may itself be nested)
    write_back_nested(target.var, parent_value, symtab)
  when AryRangeAccessAst
    # Recursive case: v[msb:lsb] = new_value
    # Read parent, splice new_value into [msb:lsb], write parent back
    parent_value = T.cast(target.var.value(symtab), Integer)
    msb_val = T.cast(target.msb.value(symtab), Integer)
    lsb_val = T.cast(target.lsb.value(symtab), Integer)
    mask = ((1 << (msb_val - lsb_val + 1)) - 1) << lsb_val
    updated_parent = (parent_value & ~mask) | ((T.cast(new_value, Integer) << lsb_val) & mask)
    write_back_nested(target.var, updated_parent, symtab)
  else
    raise InternalError, "Unknown target type for write-back: #{target.class.name}"
  end
end

Instance Method Details

#always_terminates?Boolean

Returns:

  • (Boolean)


100
# File 'lib/idlc/passes/prune.rb', line 100

def always_terminates? = false

#const_eval?(symtab) ⇒ Boolean

Returns:

  • (Boolean)


217
# File 'lib/idlc/ast.rb', line 217

def const_eval?(symtab); end

#declaration?Boolean

Returns:

  • (Boolean)


225
# File 'lib/idlc/ast.rb', line 225

def declaration? = false

#executable?Boolean

Returns:

  • (Boolean)


221
# File 'lib/idlc/ast.rb', line 221

def executable? = false

#find_ancestor(klass) ⇒ Object



295
296
297
298
299
300
301
302
303
# File 'lib/idlc/ast.rb', line 295

def find_ancestor(klass)
  if @parent.nil?
    nil
  elsif @parent.is_a?(klass)
    @parent
  else
    @parent.find_ancestor(klass)
  end
end

#find_dst_registers(symtab) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/idlc/passes/find_src_registers.rb', line 29

def find_dst_registers(symtab)
  # if executable?
  #   value_result = value_try do
  #     execute(symtab)
  #   end
  #   value_else(value_result) do
  #     execute_unknown(symtab)
  #   end
  # end
  add_symbol(symtab) if declaration?

  srcs = []
  @children.each do |child|
    srcs.concat(child.find_dst_registers(symtab))
  end
  srcs.uniq
end

#find_referenced_csrsObject



13
14
15
16
17
18
19
# File 'lib/idlc/passes/find_referenced_csrs.rb', line 13

def find_referenced_csrs
  csrs = T.let([], T::Array[String])
  @children.each do |child|
    csrs += child.find_referenced_csrs
  end
  csrs.uniq
end

#find_src_registers(symtab) ⇒ Object



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/idlc/passes/find_src_registers.rb', line 11

def find_src_registers(symtab)
  # if executable?
  #   value_result = value_try do
  #     execute(symtab)
  #   end
  #   value_else(value_result) do
  #     execute_unknown(symtab)
  #   end
  # end
  add_symbol(symtab) if declaration?

  srcs = []
  @children.each do |child|
    srcs.concat(child.find_src_registers(symtab))
  end
  srcs.uniq
end

#freeze_tree(global_symtab) ⇒ Object



520
521
522
523
524
525
# File 'lib/idlc/ast.rb', line 520

def freeze_tree(global_symtab)
  return self if frozen?

  @children.each { |child| child.freeze_tree(global_symtab) }
  freeze
end

#gen_adoc(indent = 0, indent_spaces: 2) ⇒ Object



10
11
12
# File 'lib/idlc/passes/gen_adoc.rb', line 10

def gen_adoc(indent = 0, indent_spaces: 2)
  internal_error "must implement gen_adoc for #{self.class.name}"
end

#gen_option_adocString

Generates asciidoc to document an implementation option.

The result is not IDL code, but pretty-ified Asciidoc for document layout

Returns:

  • (String)

    Asciidoc source



16
17
18
# File 'lib/idlc/passes/gen_option_adoc.rb', line 16

def gen_option_adoc
  internal_error "must implement gen_option_adoc for #{self.class.name}"
end

#input_fileObject



68
69
70
# File 'lib/idlc/ast.rb', line 68

def input_file
  @input_file || @parent&.input_file
end

#inspectObject



735
# File 'lib/idlc/ast.rb', line 735

def inspect = self.class.name.to_s

#internal_error(reason) ⇒ Object



406
407
408
409
410
411
412
413
414
# File 'lib/idlc/ast.rb', line 406

def internal_error(reason)
  msg = <<~WHAT
    In file #{input_file}
    On line #{lineno}
      An internal error occurred
      #{reason}
  WHAT
  raise AstNode::InternalError, msg
end

#linenoObject



288
289
290
# File 'lib/idlc/ast.rb', line 288

def lineno
  T.must(T.must(input)[0..T.must(interval).first]).count("\n") + 1 + starting_line
end

#lines_aroundObject



313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
# File 'lib/idlc/ast.rb', line 313

def lines_around
  cnt = 0
  interval_start = T.must(interval).min
  while cnt < 2
    cnt += 1 if T.must(input)[interval_start] == "\n"
    break if interval_start.zero?

    interval_start -= 1
  end

  cnt = 0
  interval_end = T.must(interval).max
  while cnt < 3
    cnt += 1 if T.must(input)[interval_end] == "\n"
    break if interval_end >= (T.must(input).size - 1)
    break if cnt == 3

    interval_end += 1
  end

  LinesDescriptor.new(
    lines: T.must(T.must(input)[interval_start..interval_end]),
    problem_interval: (T.must(interval).min - interval_start..T.must(interval).max - interval_start),
    lines_interval: (interval_start + 1)..interval_end
  )
end

#nullify_assignments(symtab) ⇒ Object



124
125
126
# File 'lib/idlc/passes/prune.rb', line 124

def nullify_assignments(symtab)
  children.each { |child| child.nullify_assignments(symtab) }
end

#pass_find_return_values(values, current_conditions) ⇒ Object



11
12
13
14
15
# File 'lib/idlc/passes/find_return_values.rb', line 11

def pass_find_return_values(values, current_conditions)
  children.each do |c|
    c.pass_find_return_values(values, current_conditions)
  end
end

#pathObject



529
530
531
532
533
534
535
# File 'lib/idlc/ast.rb', line 529

def path
  if @parent.nil?
    "#{self.class.name}"
  else
    "#{@parent.path}.#{self.class.name}"
  end
end


506
507
508
509
510
511
# File 'lib/idlc/ast.rb', line 506

def print_ast(indent = 0, indent_size: 2, io: $stdout)
  io.puts "#{' ' * indent}#{self.class.name}:"
  children.each do |node|
    node.print_ast(indent + indent_size, indent_size:)
  end
end

#prune(symtab, forced_type: nil) ⇒ Object

forced_type, when not nil, is the type that the pruned result must be if is used when pruning expressions to ensure that the prune doesn’t change bit width just because a value is known and would fit in something smaller



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/idlc/passes/prune.rb', line 105

def prune(symtab, forced_type: nil)
  new_children = children.map { |child| child.prune(symtab, forced_type:) }

  if executable?
    value_try do
      execute(symtab)
    end
    # value_else: execute raised ValueError; symtab state is already correct
  end
  add_symbol(symtab) if declaration?

  # avoid allocation when nothing changed
  return self if !frozen? && new_children.each_with_index.all? { |c, i| c.equal?(children[i]) }

  new_node = dup
  new_node.instance_variable_set(:@children, new_children)
  new_node
end

#reachable_exceptions(symtab, cache = {}) ⇒ Array<FunctionBodyAst>

Returns List of all functions that can be reached (via function calls) from this node.

Returns:

  • (Array<FunctionBodyAst>)

    List of all functions that can be reached (via function calls) from this node



13
14
15
16
17
18
19
20
21
# File 'lib/idlc/passes/reachable_exceptions.rb', line 13

def reachable_exceptions(symtab, cache = {})
  return 0 if @children.empty?

  mask = 0
  @children.size.times do |i|
    mask |= @children[i].reachable_exceptions(symtab, cache)
  end
  mask
end

#reachable_functions(symtab, cache = T.let({}, ReachableFunctionCacheType)) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
# File 'lib/idlc/passes/reachable_functions.rb', line 18

def reachable_functions(symtab, cache = T.let({}, ReachableFunctionCacheType))
  seen = {}
  children.each_with_object([]) do |child, result|
    child.reachable_functions(symtab, cache).each do |fn|
      unless seen.key?(fn.name)
        seen[fn.name] = true
        result << fn
      end
    end
  end
end

#set_input_file(filename, starting_line = 0, starting_offset = 0, line_file_offsets = nil) ⇒ Object



279
280
281
282
283
284
# File 'lib/idlc/ast.rb', line 279

def set_input_file(filename, starting_line = 0, starting_offset = 0, line_file_offsets = nil)
  @input_file = Pathname.new(filename)
  @starting_line = starting_line
  @starting_offset = starting_offset
  @line_file_offsets = line_file_offsets
end

#set_input_file_unless_already_set(filename, starting_line = 0, starting_offset = 0, line_file_offsets = nil) ⇒ Object



263
264
265
266
267
268
269
270
# File 'lib/idlc/ast.rb', line 263

def set_input_file_unless_already_set(filename, starting_line = 0, starting_offset = 0, line_file_offsets = nil)
  return unless @input_file.nil?

  @input_file = Pathname.new(filename)
  @starting_line = starting_line
  @starting_offset = starting_offset
  @line_file_offsets = line_file_offsets
end

#source_line_file_offsetsObject



609
610
611
612
# File 'lib/idlc/ast.rb', line 609

def source_line_file_offsets
  return @line_file_offsets unless @line_file_offsets.nil?
  @parent&.source_line_file_offsets
end

#source_starting_offsetObject



604
605
606
# File 'lib/idlc/ast.rb', line 604

def source_starting_offset
  @starting_offset || @parent&.source_starting_offset || 0
end

#source_yamlObject



569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
# File 'lib/idlc/ast.rb', line 569

def source_yaml
  base_offset = source_starting_offset
  interval_begin = T.must(interval).begin
  interval_end = T.must(interval).size == 0 ? T.must(interval).begin + 1 : T.must(interval).end

  lfo = source_line_file_offsets
  if lfo
    # Map an IDL-string position to a file byte offset using the per-line table.
    # Each entry lfo[i] is the file offset of the first character of IDL line i.
    # For position p: find the line it's on, then add the column within that line.
    file_begin = idl_pos_to_file_offset(interval_begin, lfo)
    # Find the last non-whitespace character in the interval so that end points to
    # real content and stays within file bounds.
    # For zero-size intervals, use interval_begin + 1 (matching the non-lfo path).
    inp = T.must(input)
    if T.must(interval).size == 0
      last_char = interval_begin + 1
    else
      last_char = interval_end - 1
      last_char -= 1 while last_char > interval_begin && inp[last_char] =~ /\s/
    end
    file_end = idl_pos_to_file_offset(last_char, lfo)
  else
    file_begin = base_offset + interval_begin
    file_end   = base_offset + interval_end
  end

  {
    "file" => T.must(input_file).to_s,
    "begin" => file_begin,
    "end"   => file_end
  }
end

#starting_lineObject



74
75
76
# File 'lib/idlc/ast.rb', line 74

def starting_line
  @starting_line || @parent&.starting_line || 0
end

#text_valueObject



88
89
90
# File 'lib/idlc/ast.rb', line 88

def text_value
  T.must(@interval_text_value)
end

#to_hObject



652
# File 'lib/idlc/ast.rb', line 652

def to_h; end

#to_idlObject



561
# File 'lib/idlc/ast.rb', line 561

def to_idl; end

#truncation_warn(reason) ⇒ Object



341
342
343
344
345
346
347
348
349
350
# File 'lib/idlc/ast.rb', line 341

def truncation_warn(reason)
  msg = <<~WHAT
    In file #{input_file}
    On line #{lineno}
      A value was truncated
      #{reason}.
      Perhaps you want to use a widening operator (`+, `-, `*, `<<)?
  WHAT
  warn msg
end

#type_check(symtab, strict:) ⇒ Object



551
# File 'lib/idlc/ast.rb', line 551

def type_check(symtab, strict:); end

#type_error(reason) ⇒ Object

Raises:



357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
# File 'lib/idlc/ast.rb', line 357

def type_error(reason)
  lines_desc = lines_around
  lines = lines_desc.lines
  problem_interval = lines_desc.problem_interval
  lines_interval = lines_desc.lines_interval

  lines =
    if $stdout.isatty
      [
        lines[0...problem_interval.min],
        "\u001b[31m",
        lines[problem_interval],
        "\u001b[0m",
        lines[(problem_interval.max + 1)..]
      ].join("")
    else
      [
        lines[0...problem_interval.min],
        "**HERE** >> ",
        lines[problem_interval],
        " << **HERE**",
        lines[(problem_interval.max + 1)..]
      ].join("")
    end

  starting_lineno = T.must(T.must(input)[0..lines_interval.min]).count("\n")
  lines = lines.lines.map do |line|
    starting_lineno += 1
    "#{starting_line + starting_lineno - 1}: #{line}"
  end.join("")

  msg = <<~WHAT
    In file #{input_file}
    On line #{lineno}
    In the code:

      #{lines.gsub("\n", "\n  ")}

    A type error occurred
      #{$stdout.isatty ? "\u001b[31m#{reason}\u001b[0m" : reason}
  WHAT
  raise AstNode::TypeError, msg
end

#unindent(s) ⇒ Object



496
497
498
# File 'lib/idlc/ast.rb', line 496

def unindent(s)
  s.gsub(%r{^#{s.scan(/^[ \t]+(?=\S)/).min}}, "")
end

#value_else(value_result, &block) ⇒ Object



211
# File 'lib/idlc/ast.rb', line 211

def value_else(value_result, &block) = self.class.value_else(value_result, &block)

#value_error(reason) ⇒ Object



487
# File 'lib/idlc/ast.rb', line 487

def value_error(reason) = self.class.value_error(reason, self)

#value_try(&block) ⇒ Object



201
# File 'lib/idlc/ast.rb', line 201

def value_try(&block) = self.class.value_try(&block)