Class: Idl::MultiVariableAssignmentAst

Inherits:
AstNode
  • Object
show all
Includes:
Executable
Defined in:
lib/idlc/ast.rb,
lib/idlc/passes/prune.rb,
lib/idlc/passes/prune.rb,
lib/idlc/passes/gen_adoc.rb

Overview

represents assignment of multiple variable from a function call that returns multiple values

for example:

(match_result, cfg) = pmp_match<access_size>(paddr);

Constant Summary

Constants inherited from AstNode

AstNode::Bits1Type, AstNode::Bits32Type, AstNode::Bits64Type, AstNode::BoolType, AstNode::ConstBoolType, AstNode::PossiblyUnknownBits1Type, AstNode::PossiblyUnknownBits32Type, AstNode::PossiblyUnknownBits64Type, AstNode::ReachableFunctionCacheType, AstNode::StringType, AstNode::VoidType

Instance Attribute Summary

Attributes inherited from AstNode

#children, #input, #interval, #parent

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Executable

#executable?

Methods inherited from AstNode

#always_terminates?, #declaration?, #executable?, extract_base_var_name, #find_ancestor, #find_dst_registers, #find_referenced_csrs, #find_src_registers, #freeze_tree, #gen_option_adoc, #input_file, input_from_source_yaml, #inspect, #internal_error, interval_from_source_yaml, #lineno, #lines_around, #pass_find_return_values, #path, #print_ast, #reachable_exceptions, #reachable_functions, #set_input_file, #set_input_file_unless_already_set, #source_line_file_offsets, #source_starting_offset, #source_yaml, #starting_line, #text_value, #truncation_warn, #type_error, #unindent, value_else, #value_else, value_error, #value_error, value_try, #value_try, write_back_nested

Constructor Details

#initialize(input, interval, variables, function_call) ⇒ MultiVariableAssignmentAst

Returns a new instance of MultiVariableAssignmentAst.



3449
3450
3451
# File 'lib/idlc/ast.rb', line 3449

def initialize(input, interval, variables, function_call)
  super(input, interval, variables + [function_call])
end

Class Method Details

.from_h(yaml, source_mapper) ⇒ Object



3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
# File 'lib/idlc/ast.rb', line 3530

def self.from_h(yaml, source_mapper)
  raise "Bad YAML" unless yaml.key?("kind") && yaml.fetch("kind") == "multi_var_assignment"

  input = input_from_source_yaml(yaml.fetch("source"), source_mapper)
  interval = interval_from_source_yaml(yaml.fetch("source"))
  MultiVariableAssignmentAst.new(
    input, interval,
    yaml.fetch("assignments").map { |a| AstNode.from_h(a, source_mapper) },
    T.cast(AstNode.from_h(yaml.fetch("value"), source_mapper), RvalueAst)
  )
end

Instance Method Details

#const_eval?(symtab) ⇒ Boolean

Returns:

  • (Boolean)


3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
# File 'lib/idlc/ast.rb', line 3431

def const_eval?(symtab)
  func_is_const_eval = function_call.const_eval?(symtab)
  everything_is_const_eval = func_is_const_eval

  variables.each do |variable|
    var = symtab.get(variable.name)
    type_error "#{var} was not declared" if var.nil?
    everything_is_const_eval = false unless var.const_eval?

    var.const_incompatible! unless func_is_const_eval
  end

  func_is_const_eval
end

#execute(symtab) ⇒ void

This method returns an undefined value.

“execute” the statement by updating the variables in the symbol table

Parameters:

  • symtab (SymbolTable)

    The symbol table for the context

Raises:

  • ValueError if some part of the statement cannot be executed at compile time



3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
# File 'lib/idlc/ast.rb', line 3494

def execute(symtab)
  value_result = value_try do
    values = function_call.execute(symtab)

    i = 0
    variables.each do |v|
      next if v.type(symtab).global?

      var = symtab.get(v.text_value)
      internal_error "call type check" if var.nil?

      var.value = values[i]
      i += 1
    end
  end
  value_else(value_result) do
    variables.each do |v|
      symtab.get(v.text_value).value = nil
    end
    value_error "value of right-hand side of multi-variable assignment is unknown"
  end
end

#function_callObject



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

def function_call = @children.last

#gen_adoc(indent, indent_spaces: 2) ⇒ Object



76
77
78
# File 'lib/idlc/passes/gen_adoc.rb', line 76

def gen_adoc(indent, indent_spaces: 2)
  "#{' ' * indent}(#{variables.map { |v| v.gen_adoc(0, indent_spaces:) }.join(', ')} = #{function_call.gen_adoc(0, indent_spaces:)})"
end

#nullify_assignments(symtab) ⇒ Object



185
186
187
188
189
190
# File 'lib/idlc/passes/prune.rb', line 185

def nullify_assignments(symtab)
  variables.each do |v|
    sym = symtab.get(v.text_value)
    sym.value = nil unless sym.nil?
  end
end

#prune(symtab, forced_type: nil) ⇒ Object



883
884
885
886
887
888
889
890
891
892
893
894
# File 'lib/idlc/passes/prune.rb', line 883

def prune(symtab, forced_type: nil)
  new_ast = MultiVariableAssignmentAst.new(
    input, interval,
    variables.map(&:dup),
    function_call.prune(symtab)
  )
  value_try do
    new_ast.execute(symtab)
  end
  # value_else: execute already sets nil on failure, nothing more to do
  new_ast
end

#rhsObject



3458
3459
3460
# File 'lib/idlc/ast.rb', line 3458

def rhs
  function_call
end

#to_hObject



3522
3523
3524
3525
3526
3527
# File 'lib/idlc/ast.rb', line 3522

def to_h = {
  "kind" => "multi_var_assignment",
  "assignments" => variables.map(&:to_h),
  "value" => function_call.to_h,
  "source" => source_yaml
}

#to_idlObject



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

def to_idl = "(#{variables.map(&:to_idl).join(', ')}) = #{function_call.to_idl}"

#type_check(symtab, strict:) ⇒ void

This method returns an undefined value.

type check this node and all children

Calls to #type and/or #value may depend on type_check being called first with the same symtab. If not, those functions may raise an AstNode::InternalError

Parameters:

Raises:



3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
# File 'lib/idlc/ast.rb', line 3463

def type_check(symtab, strict:)
  function_call.type_check(symtab, strict:)
  variables.each { |var| var.type_check(symtab, strict:) }

  type_error "Assigning value to a constant" if variables.any? { |v| v.type(symtab).const? }

  type_error "Function '#{function_call.name}' has no return type" if function_call.type(symtab).nil?
  unless function_call.type(symtab).kind == :tuple
    type_error "Function '#{function_call.name}' only returns 1 variable"
  end

  if function_call.type(symtab).tuple_types.size != vars.size
    type_error "function '#{function_call.name}' returns #{function_call.type(symtab).tuple_types.size} arguments, but  #{variables.size} were specified"
  end

  function_call.type(symtab).tuple_types.each_index do |i|
    next if variables[i].is_a?(DontCareLvalueAst)
    raise "Implementation error" if variables[i].is_a?(DontCareReturnAst)

    var = symtab.get(variables[i].text_value)
    type_error "No symbol named '#{variables[i].text_value}'" if var.nil?

    internal_error "Cannot determine type of #{variables[i].text_value}" unless var.respond_to?(:type)

    unless var.type.convertable_to?(function_call.type(symtab).tuple_types[i])
      type_error "'#{function_call.name}' expecting a #{function_call.type(symtab).tuple_types[i]} in argument #{i}, but was given #{var.type(symtab)}"
    end
  end
end

#variablesObject



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

def variables = @children[0..-2]

#varsArray<AstNode>

Returns The variables being assigned, in order.

Returns:

  • (Array<AstNode>)

    The variables being assigned, in order



3454
3455
3456
# File 'lib/idlc/ast.rb', line 3454

def vars
  variables
end