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



8300
8301
8302
8303
8304
8305
8306
8307
8308
8309
8310
8311
8312
8313
8314
8315
8316
8317
8318
# File 'lib/idlc/ast.rb', line 8300

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.



8320
8321
8322
# File 'lib/idlc/ast.rb', line 8320

def argument_nodes
  @argument_nodes
end

#return_type_nodesObject (readonly)

Returns the value of attribute return_type_nodes.



8276
8277
8278
# File 'lib/idlc/ast.rb', line 8276

def return_type_nodes
  @return_type_nodes
end

Class Method Details

.from_h(yaml, source_mapper) ⇒ Object



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

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



8282
8283
8284
8285
8286
# File 'lib/idlc/ast.rb', line 8282

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



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

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



8440
8441
8442
8443
8444
# File 'lib/idlc/ast.rb', line 8440

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



8333
8334
8335
8336
8337
8338
8339
8340
8341
8342
8343
8344
8345
8346
8347
8348
8349
8350
8351
8352
# File 'lib/idlc/ast.rb', line 8333

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



8356
8357
8358
# File 'lib/idlc/ast.rb', line 8356

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

#bodyObject



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

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

  @body
end

#builtin?Boolean

Returns:

  • (Boolean)


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

def builtin?
  @builtin
end

#const_eval?(symtab) ⇒ Boolean

Returns:

  • (Boolean)


8412
8413
8414
8415
8416
8417
8418
8419
8420
8421
8422
8423
8424
8425
8426
8427
8428
8429
8430
8431
8432
8433
8434
# File 'lib/idlc/ast.rb', line 8412

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



8323
8324
8325
# File 'lib/idlc/ast.rb', line 8323

def description
  unindent(@desc)
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


8288
8289
8290
# File 'lib/idlc/ast.rb', line 8288

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

#external?Boolean

Returns:

  • (Boolean)


8506
8507
8508
# File 'lib/idlc/ast.rb', line 8506

def external?
  @external
end

#generated?Boolean

Returns:

  • (Boolean)


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

def generated?
  @generated
end

#nameObject



8436
8437
8438
# File 'lib/idlc/ast.rb', line 8436

def name
  @name
end

#num_argsInteger

Returns The number of arguments to the function.

Returns:

  • (Integer)

    The number of arguments to the function



8328
8329
8330
# File 'lib/idlc/ast.rb', line 8328

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



8510
8511
8512
8513
8514
8515
8516
8517
8518
8519
8520
# File 'lib/idlc/ast.rb', line 8510

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



8361
8362
8363
8364
8365
8366
8367
8368
8369
8370
8371
8372
8373
8374
8375
8376
8377
8378
8379
8380
8381
8382
8383
8384
8385
8386
8387
8388
8389
8390
8391
8392
8393
8394
8395
8396
# File 'lib/idlc/ast.rb', line 8361

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



8400
8401
8402
8403
8404
8405
8406
# File 'lib/idlc/ast.rb', line 8400

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

#to_hObject



8558
8559
8560
8561
8562
8563
8564
8565
8566
8567
# File 'lib/idlc/ast.rb', line 8558

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



8523
8524
8525
8526
8527
8528
8529
8530
8531
8532
8533
8534
8535
8536
8537
8538
8539
8540
8541
8542
8543
8544
8545
8546
8547
8548
8549
8550
8551
8552
8553
8554
8555
# File 'lib/idlc/ast.rb', line 8523

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:



8447
8448
8449
8450
8451
8452
8453
8454
8455
8456
8457
8458
8459
8460
8461
# File 'lib/idlc/ast.rb', line 8447

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



8482
8483
8484
# File 'lib/idlc/ast.rb', line 8482

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



8486
8487
8488
8489
8490
# File 'lib/idlc/ast.rb', line 8486

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

  @body.type_check(symtab, strict:)
end

#type_check_return(symtab, strict:) ⇒ Object



8478
8479
8480
# File 'lib/idlc/ast.rb', line 8478

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