Class: Idl::FunctionDefAst

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

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 collapse

Attributes inherited from AstNode

#children, #input, #interval, #parent

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Declaration

#declaration?

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_adoc, #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, #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, name, return_types, arguments, desc, type, body) ⇒ FunctionDefAst

Returns a new instance of FunctionDefAst.

Parameters:

  • input (String)

    The source code

  • interval (Range)

    The range in the source code for this function definition

  • name (String)

    The name of the function

  • return_types (Array<AstNode>)

    Return types

  • arguments (Array<AstNode>)

    Arguments

  • desc (String)

    Description

  • type (:normal, :builtin, :generated, :external)

    Type of function

  • body (AstNode, nil)

    Body, unless the function is builtin



8315
8316
8317
8318
8319
8320
8321
8322
8323
8324
8325
8326
8327
8328
8329
8330
8331
8332
8333
# File 'lib/idlc/ast.rb', line 8315

def initialize(input, interval, name, return_types, arguments, desc, type, body)
  if body.nil?
    super(input, interval, return_types + arguments)
  else
    super(input, interval, return_types + arguments + [body])
  end

  @name = name
  @return_type_nodes = return_types
  @argument_nodes = arguments
  @desc = desc
  @body = body
  @builtin = type == :builtin
  @generated = type == :generated
  @external = type == :external

  @cached_return_type = {}
  @memo = Memo.new
end

Instance Attribute Details

#argument_nodesObject (readonly)

Returns the value of attribute argument_nodes.



8335
8336
8337
# File 'lib/idlc/ast.rb', line 8335

def argument_nodes
  @argument_nodes
end

#return_type_nodesObject (readonly)

Returns the value of attribute return_type_nodes.



8291
8292
8293
# File 'lib/idlc/ast.rb', line 8291

def return_type_nodes
  @return_type_nodes
end

Class Method Details

.from_h(yaml, source_mapper) ⇒ Object



8585
8586
8587
8588
8589
8590
8591
8592
8593
8594
8595
8596
8597
8598
8599
# File 'lib/idlc/ast.rb', line 8585

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

  input = input_from_source_yaml(yaml.fetch("source"), source_mapper)
  interval = interval_from_source_yaml(yaml.fetch("source"))
  FunctionDefAst.new(
    input, interval,
    yaml.fetch("name"),
    yaml.fetch("return_types").map { |r| AstNode.from_h(r, source_mapper) },
    yaml.fetch("arguments").map { |a| AstNode.from_h(a, source_mapper) },
    yaml.fetch("description"),
    yaml.fetch("qualifier").to_sym,
    yaml.fetch("body").nil? ? nil : AstNode.from_h(yaml.fetch("body"), source_mapper)
  )
end

Instance Method Details

#<=>(other) ⇒ Object



8297
8298
8299
8300
8301
# File 'lib/idlc/ast.rb', line 8297

def <=>(other)
  return nil unless other.is_a?(FunctionDefAst)

  @name <=> other.name
end

#add_symbol(symtab) ⇒ Object

Add symbol(s) at the outermost scope of the symbol table

Parameters:

  • symtab (SymbolTable)

    Symbol table at the scope that the symbol(s) will be inserted



8479
8480
8481
8482
8483
8484
8485
8486
8487
8488
8489
8490
8491
# File 'lib/idlc/ast.rb', line 8479

def add_symbol(symtab)
  internal_error "Functions should be declared at global scope" unless symtab.levels == 1
  type_error "Cannot use reserved word '#{name}' as function name" if ReservedWords::RESERVED.include?(name)

  # now add the function in global scope
  def_type = FunctionType.new(
    name,
    self,
    symtab
  )

  symtab.add!(name, def_type)
end

#apply_arg_syms(symtab) ⇒ Object



8455
8456
8457
8458
8459
# File 'lib/idlc/ast.rb', line 8455

def apply_arg_syms(symtab)
  arguments(symtab).each do |arg_type, arg_name|
    symtab.add(arg_name, Var.new(arg_name, arg_type))
  end
end

#arguments(symtab) ⇒ Array<Array(Type,String)>

Returns containing the argument types and names, in order.

Returns:

  • (Array<Array(Type,String)>)

    containing the argument types and names, in order



8348
8349
8350
8351
8352
8353
8354
8355
8356
8357
8358
8359
8360
8361
8362
8363
8364
8365
8366
8367
# File 'lib/idlc/ast.rb', line 8348

def arguments(symtab)
  return @memo.arguments unless @memo.arguments.nil?

  return EMPTY_ARRAY if @argument_nodes.empty?

  arglist = []

  @argument_nodes.each do |a|
    atype = a.type(symtab)
    type_error "No type for #{a.text_value}" if atype.nil?

    atype = atype.ref_type if atype.kind == :enum

    atype = atype.make_const if a.id.text_value[0].upcase == a.id.text_value[0]
    arglist << [atype, a.name]
  end

  arglist.freeze
  @memo.arguments = arglist
end

#arguments_list_strObject

returns an array of arguments, as a string function does not need to be resolved



8371
8372
8373
# File 'lib/idlc/ast.rb', line 8371

def arguments_list_str
  @argument_nodes.map(&:text_value)
end

#bodyObject



8507
8508
8509
8510
8511
# File 'lib/idlc/ast.rb', line 8507

def body
  internal_error "Function has no body" if builtin? || generated?

  @body
end

#builtin?Boolean

Returns:

  • (Boolean)


8513
8514
8515
# File 'lib/idlc/ast.rb', line 8513

def builtin?
  @builtin
end

#const_eval?(symtab) ⇒ Boolean

Returns:

  • (Boolean)


8427
8428
8429
8430
8431
8432
8433
8434
8435
8436
8437
8438
8439
8440
8441
8442
8443
8444
8445
8446
8447
8448
8449
# File 'lib/idlc/ast.rb', line 8427

def const_eval?(symtab)
  return false if builtin? || generated?

  # set up dummy const args, and see if the type comes out is const
  symtab = symtab.global_clone
  symtab.push(self)

  arguments(symtab).each do |arg_type, arg_name|
    # make the argument constant for this evaluation
    arg_type = arg_type.make_const
    symtab.add(arg_name, Var.new(arg_name, arg_type))
  end

  symtab.add(
    "__expected_return_type",
    return_type(symtab)
  )

  is_const_eval = @body.const_eval?(symtab)
  symtab.release

  is_const_eval
end

#descriptionString

Returns Asciidoc formatted function description.

Returns:

  • (String)

    Asciidoc formatted function description



8338
8339
8340
# File 'lib/idlc/ast.rb', line 8338

def description
  unindent(@desc)
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


8303
8304
8305
# File 'lib/idlc/ast.rb', line 8303

def eql?(other)
  name.eql?(other.name)
end

#external?Boolean

Returns:

  • (Boolean)


8521
8522
8523
# File 'lib/idlc/ast.rb', line 8521

def external?
  @external
end

#generated?Boolean

Returns:

  • (Boolean)


8517
8518
8519
# File 'lib/idlc/ast.rb', line 8517

def generated?
  @generated
end

#nameObject



8451
8452
8453
# File 'lib/idlc/ast.rb', line 8451

def name
  @name
end

#num_argsInteger

Returns The number of arguments to the function.

Returns:

  • (Integer)

    The number of arguments to the function



8343
8344
8345
# File 'lib/idlc/ast.rb', line 8343

def num_args
  @argument_nodes.size
end

#prune(symtab, forced_type: nil) ⇒ Object



283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'lib/idlc/passes/prune.rb', line 283

def prune(symtab, forced_type: nil)
  pruned_body =
    unless builtin? || generated?
      apply_arg_syms(symtab)
      @body.prune(symtab, args_already_applied: true)
    end

  FunctionDefAst.new(
    input, interval,
    name,
    @return_type_nodes.map(&:dup),
    @argument_nodes.map(&:dup),
    @desc,
    @type,
    pruned_body
  )
end

#qualifier_strObject



8525
8526
8527
8528
8529
8530
8531
8532
8533
8534
8535
# File 'lib/idlc/ast.rb', line 8525

def qualifier_str
  if external?
    "external"
  elsif builtin?
    "builtin"
  elsif generated?
    "generated"
  else
    ""
  end
end

#return_type(symtab) ⇒ Object

return the return type, which may be a tuple of multiple types



8376
8377
8378
8379
8380
8381
8382
8383
8384
8385
8386
8387
8388
8389
8390
8391
8392
8393
8394
8395
8396
8397
8398
8399
8400
8401
8402
8403
8404
8405
8406
8407
8408
8409
8410
8411
# File 'lib/idlc/ast.rb', line 8376

def return_type(symtab)
  cache_key = "#{symtab.name}/#{symtab.mxlen}"
  cached = @cached_return_type[cache_key] # only cached for non-template functions
  return cached unless cached.nil?

  unless symtab.levels == 2
    internal_error "Function bodies should be at global + 1 scope (at global + #{symtab.levels - 1})"
  end

  if @return_type_nodes.empty?
    @cached_return_type[cache_key] = VoidType
    return VoidType
  end

  rtype = T.let(nil, T.nilable(Type))


  rtype =
    if @return_type_nodes.size == 1
      rtype = @return_type_nodes[0].type(symtab)
      rtype = rtype.ref_type if rtype.kind == :enum
      rtype
    else
      tuple_types = @return_type_nodes.map do |r|
        rtype = r.type(symtab)
        rtype = rtype.ref_type if rtype.kind == :enum
        rtype
      end

      Type.new(:tuple, tuple_types:)
    end

  raise "??????" if rtype.nil?

  return @cached_return_type[symtab.name] = rtype
end

#return_type_list_strArray<String>

function does not need to be resolved

Returns:

  • (Array<String>)

    return type strings



8415
8416
8417
8418
8419
8420
8421
# File 'lib/idlc/ast.rb', line 8415

def return_type_list_str
  if @return_type_nodes.empty?
    ["void"]
  else
    @return_type_nodes.map(&:text_value)
  end
end

#to_hObject



8573
8574
8575
8576
8577
8578
8579
8580
8581
8582
# File 'lib/idlc/ast.rb', line 8573

def to_h = {
  "kind" => "function_decl",
  "name" => name,
  "qualifier" => qualifier_str.empty? ? "normal" : qualifier_str,
  "return_types" => return_type_nodes.map(&:to_h),
  "arguments" => @argument_nodes.map(&:to_h),
  "description" => @desc,
  "body" => @body&.to_h,
  "source" => source_yaml
}

#to_idlObject



8538
8539
8540
8541
8542
8543
8544
8545
8546
8547
8548
8549
8550
8551
8552
8553
8554
8555
8556
8557
8558
8559
8560
8561
8562
8563
8564
8565
8566
8567
8568
8569
8570
# File 'lib/idlc/ast.rb', line 8538

def to_idl
  qualifier = qualifier_str

  returns_idl =
    if return_type_nodes.empty?
      ""
    else
      "returns #{return_type_nodes.map(&:to_idl).join(', ')}"
    end

  args_idl =
    if @argument_nodes.empty?
      ""
    else
      "arguments #{@argument_nodes.map(&:to_idl).join(", ")}"
    end

  body_idl =
    if builtin? || generated?
      ""
    else
      "body { #{@body.to_idl} }"
    end

  <<~IDL
    #{qualifier} function #{name} {
      #{returns_idl}
      #{args_idl}
      description { #{description} }
      #{body_idl}
    }
  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:



8462
8463
8464
8465
8466
8467
8468
8469
8470
8471
8472
8473
8474
8475
8476
# File 'lib/idlc/ast.rb', line 8462

def type_check(symtab, strict:)
  internal_error "Functions must be declared at global scope (at #{symtab.levels})" unless symtab.levels == 1

  symtab = symtab.deep_clone
  symtab.push(self)

  type_check_return(symtab, strict:)

  apply_arg_syms(symtab)
  type_check_args(symtab, strict:)

  type_check_body(symtab, strict:)

  symtab.pop
end

#type_check_args(symtab, strict:) ⇒ Object



8497
8498
8499
# File 'lib/idlc/ast.rb', line 8497

def type_check_args(symtab, strict:)
  @argument_nodes.each { |a| a.type_check(symtab, add_sym: false, strict:, is_function_arg: true) }
end

#type_check_body(symtab, strict:) ⇒ Object



8501
8502
8503
8504
8505
# File 'lib/idlc/ast.rb', line 8501

def type_check_body(symtab, strict:)
  return if @body.nil?

  @body.type_check(symtab, strict:)
end

#type_check_return(symtab, strict:) ⇒ Object



8493
8494
8495
# File 'lib/idlc/ast.rb', line 8493

def type_check_return(symtab, strict:)
  @return_type_nodes.each { |r| r.type_check(symtab, strict:) }
end