Class: Idl::ConditionalStatementAst

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

Overview

represents a predicated simple statement

for example:

a = 2 if condition;

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 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, #nullify_assignments, #pass_find_return_values, #path, #print_ast, #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, action, condition) ⇒ ConditionalStatementAst

Returns a new instance of ConditionalStatementAst.



6649
6650
6651
# File 'lib/idlc/ast.rb', line 6649

def initialize(input, interval, action, condition)
  super(input, interval, [action, condition])
end

Class Method Details

.from_h(yaml, source_mapper) ⇒ Object



6693
6694
6695
6696
6697
6698
6699
6700
6701
6702
6703
# File 'lib/idlc/ast.rb', line 6693

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

  input = input_from_source_yaml(yaml.fetch("source"), source_mapper)
  interval = interval_from_source_yaml(yaml.fetch("source"))
  ConditionalStatementAst.new(
    input, interval,
    AstNode.from_h(yaml.fetch("expr"), source_mapper),
    AstNode.from_h(yaml.fetch("condition"), source_mapper)
  )
end

Instance Method Details

#actionObject



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

def action = @children[0]

#conditionObject



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

def condition = @children[1]

#const_eval?(symtab) ⇒ Boolean

Returns:

  • (Boolean)


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

def const_eval?(symtab) = action.const_eval?(symtab) && condition.const_eval?(symtab)

#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



6663
6664
6665
6666
6667
6668
6669
6670
6671
6672
6673
6674
6675
6676
# File 'lib/idlc/ast.rb', line 6663

def execute(symtab)
  value_result = value_try do
    cond = condition.value(symtab)

    if (cond)
      action.execute(symtab)
    end
  end
  value_else(value_result) do
    # condition is unknown; nullify any variables the action would assign
    action.nullify_assignments(symtab)
    value_error ""
  end
end

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



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

def gen_adoc(indent = 0, indent_spaces: 2)
  "#{' ' * indent}#{action.gen_adoc(0, indent_spaces:)} if (#{condition.gen_adoc(0, indent_spaces:)});"
end

#prune(symtab, forced_type: nil) ⇒ Object



611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
# File 'lib/idlc/passes/prune.rb', line 611

def prune(symtab, forced_type: nil)
  value_result = value_try do
    if condition.value(symtab)
      pruned_action = action.prune(symtab)
      pruned_action.add_symbol(symtab) if pruned_action.declaration?
      value_result = value_try do
        pruned_action.execute(symtab) if pruned_action.executable?
      end

      return StatementAst.new(input, interval, pruned_action)
    else
      return NoopAst.new
    end
  end
  value_else(value_result) do
    # condition not known
    pruned_action = action.prune(symtab)
    pruned_action.add_symbol(symtab) if pruned_action.declaration?
    value_result = value_try do
      pruned_action.execute(symtab) if pruned_action.executable?
    end
    # Condition is unknown, so the assignment may not have run; nullify to prevent leakage
    pruned_action.nullify_assignments(symtab)
    ConditionalStatementAst.new(input, interval, pruned_action, condition.prune(symtab))
  end
end

#reachable_exceptions(symtab, cache = {}) ⇒ Object



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/idlc/passes/reachable_exceptions.rb', line 159

def reachable_exceptions(symtab, cache = {})
  mask = 0
  value_result = value_try do
    mask |= condition.reachable_exceptions(symtab, cache)
    if condition.value(symtab)
      mask |= action.reachable_exceptions(symtab, cache)
      action.add_symbol(symtab) if action.declaration?
      if action.executable?
        value_result = value_try do
          action.execute(symtab)
        end
      end
    end
  end
  value_else(value_result) do
    mask = 0
    # condition not known
    mask |= condition.reachable_exceptions(symtab, cache)
    mask |= action.reachable_exceptions(symtab, cache)
    action.add_symbol(symtab) if action.declaration?
    if action.executable?
      value_result = value_try do
        action.execute(symtab)
      end
    end
  end
  mask
end

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



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/idlc/passes/reachable_functions.rb', line 181

def reachable_functions(symtab, cache = T.let({}, ReachableFunctionCacheType))

  fns = condition.is_a?(FunctionCallExpressionAst) ? condition.reachable_functions(symtab, cache) : []

  value_result = value_try do
    if condition.value(symtab)
      fns.concat action.reachable_functions(symtab, cache)
      # no need to execute action (return)
    end
  end
  value_else(value_result) do
    # condition not known
    fns = fns.concat action.reachable_functions(symtab, cache)
  end

  fns
end

#to_hObject



6685
6686
6687
6688
6689
6690
# File 'lib/idlc/ast.rb', line 6685

def to_h = {
  "kind" => "conditional_stmt",
  "condition" => condition.to_h,
  "expr" => action.to_h,
  "source" => source_yaml
}

#to_idlObject



6680
6681
6682
# File 'lib/idlc/ast.rb', line 6680

def to_idl
  "#{action.to_idl} if (#{condition.to_idl});"
end

#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:



6654
6655
6656
6657
6658
6659
6660
# File 'lib/idlc/ast.rb', line 6654

def type_check(symtab, strict:)
  action.type_check(symtab, strict:)
  type_error "Cannot declare from a conditional statement" if action.declaration?

  condition.type_check(symtab, strict:)
  type_error "condition is not boolean" unless condition.type(symtab).convertable_to?(:boolean)
end