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.



6367
6368
6369
6370
# File 'lib/idlc/ast.rb', line 6367

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



6523
6524
6525
6526
6527
6528
6529
6530
6531
6532
6533
6534
# File 'lib/idlc/ast.rb', line 6523

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



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

def condition = @children[0]

#const_eval?(symtab) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#false_expressionObject



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

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



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

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



6496
6497
6498
6499
6500
6501
6502
6503
6504
6505
6506
6507
# File 'lib/idlc/ast.rb', line 6496

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



6514
6515
6516
6517
6518
6519
6520
# File 'lib/idlc/ast.rb', line 6514

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



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

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

#true_expressionObject



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

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:



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
6450
6451
6452
6453
6454
6455
6456
6457
6458
6459
6460
6461
6462
6463
6464
# File 'lib/idlc/ast.rb', line 6412

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:



6373
6374
6375
6376
6377
6378
6379
6380
6381
6382
6383
6384
6385
6386
6387
6388
6389
6390
6391
6392
6393
6394
6395
6396
6397
6398
6399
6400
6401
6402
6403
6404
6405
6406
6407
6408
6409
# File 'lib/idlc/ast.rb', line 6373

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



6467
6468
6469
# File 'lib/idlc/ast.rb', line 6467

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



6472
6473
6474
6475
6476
6477
6478
6479
# File 'lib/idlc/ast.rb', line 6472

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