Class: Idl::TernaryOperatorExpressionAst

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

Overview

Represents a ternary operator

for example:

condition ? a : b
(a < b) ? c : d

Defined Under Namespace

Classes: Memo

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 Rvalue

#truncate

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, #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, #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, condition, true_expression, false_expression) ⇒ TernaryOperatorExpressionAst

Returns a new instance of TernaryOperatorExpressionAst.



6352
6353
6354
6355
# File 'lib/idlc/ast.rb', line 6352

def initialize(input, interval, condition, true_expression, false_expression)
  super(input, interval, [condition, true_expression, false_expression])
  @memo = Memo.new
end

Class Method Details

.from_h(yaml, source_mapper) ⇒ Object



6508
6509
6510
6511
6512
6513
6514
6515
6516
6517
6518
6519
# File 'lib/idlc/ast.rb', line 6508

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

  input = input_from_source_yaml(yaml.fetch("source"), source_mapper)
  interval = interval_from_source_yaml(yaml.fetch("source"))
  TernaryOperatorExpressionAst.new(
    input, interval,
    T.cast(AstNode.from_h(yaml.fetch("condition"), source_mapper), RvalueAst),
    T.cast(AstNode.from_h(yaml.fetch("true_expression"), source_mapper), RvalueAst),
    T.cast(AstNode.from_h(yaml.fetch("false_expression"), source_mapper), RvalueAst)
  )
end

Instance Method Details

#conditionObject



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

def condition = @children[0]

#const_eval?(symtab) ⇒ Boolean

Returns:

  • (Boolean)


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

def const_eval?(symtab) = condition.const_eval?(symtab) && true_expression.const_eval?(symtab) && false_expression.const_eval?(symtab)

#false_expressionObject



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

def false_expression = @children[2]

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



191
192
193
# File 'lib/idlc/passes/gen_adoc.rb', line 191

def gen_adoc(indent = 0, indent_spaces: 2)
  "#{' ' * indent}#{condition.gen_adoc(0, indent_spaces:)} ? #{true_expression.gen_adoc(0, indent_spaces:)} : #{false_expression.gen_adoc(0, indent_spaces:)}"
end

#gen_option_adocObject



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/idlc/passes/gen_option_adoc.rb', line 123

def gen_option_adoc
  cond = condition.is_a?(ParenExpressionAst) ? condition.expression : condition
  if cond.is_a?(BinaryExpressionAst) || cond.is_a?(UnaryOperatorExpressionAst)
    <<~ADOC
      [when,"#{cond.gen_adoc.gsub('"', "&quot;")}"]
      #{true_expression.gen_option_adoc}

      [when,"#{cond.invert(nil).gen_adoc.gsub('"', "&quot;")}"]
      #{false_expression.gen_option_adoc}

    ADOC
  else
    <<~ADOC
      [when,"#{cond.gen_adoc.gsub('"', "&quot;")}"]
      #{true_expression.gen_option_adoc}

      [when,"!(#{cond.gen_adoc.gsub('"', "&quot;")})"]
      #{false_expression.gen_option_adoc}

    ADOC
  end
end

#max_value(symtab) ⇒ Object



6467
6468
6469
6470
6471
6472
6473
6474
6475
6476
6477
6478
# File 'lib/idlc/ast.rb', line 6467

def max_value(symtab)
  value_result = value_try do
    cond = condition.value(symtab)
    return cond ? true_expression.max_value(symtab) : false_expression.max_value(symtab)
  end
  value_else(value_result) do
    t = true_expression.max_value(symtab)
    f = false_expression.max_value(symtab)
    return :unknown if t == :unknown || f == :unknown
    [t, f].max
  end
end

#min_value(symtab) ⇒ Object



6481
6482
6483
6484
6485
6486
6487
6488
6489
6490
6491
6492
# File 'lib/idlc/ast.rb', line 6481

def min_value(symtab)
  value_result = value_try do
    cond = condition.value(symtab)
    return cond ? true_expression.min_value(symtab) : false_expression.min_value(symtab)
  end
  value_else(value_result) do
    t = true_expression.min_value(symtab)
    f = false_expression.min_value(symtab)
    return :unknown if t == :unknown || f == :unknown
    [t, f].min
  end
end

#prune(symtab, forced_type: nil) ⇒ Object



706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
# File 'lib/idlc/passes/prune.rb', line 706

def prune(symtab, forced_type: nil)
  value_result = value_try do
    if condition.value(symtab)
      return true_expression.prune(symtab, forced_type: forced_type || type(symtab))
    else
      return false_expression.prune(symtab, forced_type: forced_type || type(symtab))
    end
  end
  value_else(value_result) do
    TernaryOperatorExpressionAst.new(
      input, interval,
      condition.prune(symtab),
      true_expression.prune(symtab),
      false_expression.prune(symtab)
    )
  end
end

#to_hObject



6499
6500
6501
6502
6503
6504
6505
# File 'lib/idlc/ast.rb', line 6499

def to_h = {
  "kind" => "ternary_operator_expr",
  "condition" => condition.to_h,
  "true_expression" => true_expression.to_h,
  "false_expression" => false_expression.to_h,
  "source" => source_yaml
}

#to_idlObject



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

def to_idl = "#{condition.to_idl} ? #{true_expression.to_idl} : #{false_expression.to_idl}"

#true_expressionObject



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

def true_expression = @children[1]

#type(symtab) ⇒ Type

Given a specific symbol table, return the type of this node.

Should not be called until #type_check is called with the same arguments

Parameters:

Returns:

  • (Type)

    The type of the node

Raises:



6397
6398
6399
6400
6401
6402
6403
6404
6405
6406
6407
6408
6409
6410
6411
6412
6413
6414
6415
6416
6417
6418
6419
6420
6421
6422
6423
6424
6425
6426
6427
6428
6429
6430
6431
6432
6433
6434
6435
6436
6437
6438
6439
6440
6441
6442
6443
6444
6445
6446
6447
6448
6449
# File 'lib/idlc/ast.rb', line 6397

def type(symtab)
  key = "#{symtab.name}/#{symtab.mxlen}"
  return @memo.type.fetch(key) if @memo.type.key?(key)
  t =
    if true_expression.type(symtab).kind == :bits && false_expression.type(symtab).kind == :bits
      true_type = true_expression.type(symtab)
      false_type = false_expression.type(symtab)
      true_width = true_type.width
      false_width = false_type.width
      known = true_expression.type(symtab).known? && false_expression.type(symtab).known?
      if true_width == :unknown || false_width == :unknown
        max_width =
          if true_width == :unknown && false_width == :unknown
            if true_type.max_width.nil? || false_type.max_width.nil?
              nil
            else
              [true_type.max_width, false_type.max_width].max
            end
          elsif true_width == :unknown
            if true_type.max_width.nil?
              nil
            else
              [true_type.max_width, false_width].max
            end
          elsif false_width == :unknown
            if false_type.max_width.nil?
              nil
            else
              [false_type.max_width, true_width].max
            end
          else
            raise "unreachable"
          end
        if known
          Type.new(:bits, width: :unknown, max_width:, qualifiers: [:known])
        else
          Type.new(:bits, width: :unknown, max_width:)
        end
      else
        if known
          Type.new(:bits, width: [true_width, false_width].max, qualifiers: [:known])
        else
          Type.new(:bits, width: [true_width, false_width].max)
        end
      end
    else
      true_expression.type(symtab).clone
    end
  if condition.type(symtab).const? && true_expression.type(symtab).const? && false_expression.type(symtab).const?
    t.make_const!
  end
  @memo.type[key] = t
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:



6358
6359
6360
6361
6362
6363
6364
6365
6366
6367
6368
6369
6370
6371
6372
6373
6374
6375
6376
6377
6378
6379
6380
6381
6382
6383
6384
6385
6386
6387
6388
6389
6390
6391
6392
6393
6394
# File 'lib/idlc/ast.rb', line 6358

def type_check(symtab, strict:)
  condition.type_check(symtab, strict:)
  if condition.type(symtab).kind == :bits
    type_error "ternary selector must be bool (maybe you meant '#{condition.text_value} != 0'?)"
  else
    type_error "ternary selector must be bool" unless condition.type(symtab).kind == :boolean
  end

  if strict
    value_result = value_try do
      cond = condition.value(symtab)
      # if the condition is compile-time-known, only check the used field
      cond ? true_expression.type_check(symtab, strict:) : false_expression.type_check(symtab, strict:)
    end
    value_else(value_result) do
      true_expression.type_check(symtab, strict:)
      false_expression.type_check(symtab, strict:)

      unless true_expression.type(symtab).equal_to?(false_expression.type(symtab))
        # we'll allow dissimilar if they are both bits type
        unless true_expression.type(symtab).kind == :bits && false_expression.type(symtab).kind == :bits
          type_error "True and false options must be same type (have #{true_expression.type(symtab)} and #{false_expression.type(symtab)})"
        end
      end
    end
  else
    true_expression.type_check(symtab, strict:)
    false_expression.type_check(symtab, strict:)

    unless true_expression.type(symtab).equal_to?(false_expression.type(symtab))
      # we'll allow dissimilar if they are both bits type
      unless true_expression.type(symtab).kind == :bits && false_expression.type(symtab).kind == :bits
        type_error "True and false options must be same type (have #{true_expression.type(symtab)} and #{false_expression.type(symtab)})"
      end
    end
  end
end

#value(symtab) ⇒ Object

Return the compile-time-known value of the node



6452
6453
6454
# File 'lib/idlc/ast.rb', line 6452

def value(symtab)
  condition.value(symtab) ? true_expression.value(symtab) : false_expression.value(symtab)
end

#values(symtab) ⇒ Array<Integer>, ...

Return a complete list of possible compile-time-known values of the node, or raise a ValueError if the full list cannot be determined

For most AstNodes, this will just be a single-entry array

Parameters:

  • symtab (SymbolTable)

    The context for the evaluation

Returns:

  • (Array<Integer>)

    The complete list of compile-time-known values, when they are integral

  • (Array<Boolean>)

    The complete list of compile-time-known values, when they are booleans

  • (AstNode::ValueError)

    if the list of values is not knowable at compile time



6457
6458
6459
6460
6461
6462
6463
6464
# File 'lib/idlc/ast.rb', line 6457

def values(symtab)
  value_result = value_try do
    return condition.value(symtab) ? true_expression.values(symtab) : false_expression.values(symtab)
  end
  value_else(value_result) do
    (true_expression.values(symtab) + false_expression.values(symtab)).uniq
  end
end