Class: Idl::FunctionDefAst

Inherits:
AstNode
  • Object
show all
Includes:
Declaration
Defined in:
lib/idl/ast.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(input, interval, name, targs, return_types, arguments, desc, 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

  • targs (Array<AstNode>)

    Template arguments

  • arguments (Array<AstNode>)

    Arguments

  • desc (String)

    Description

  • body (AstNode, nil)

    Body, unless the function is builtin



4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
# File 'lib/idl/ast.rb', line 4847

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

  @name = name
  @targs = targs
  @return_type_nodes = return_types
  @argument_nodes = arguments
  @desc = desc
  @body = body

  @cached_return_type = {}
  @reachable_functions_cache ||= {}
end

Instance Attribute Details

#reachable_functions_cacheObject (readonly)

Returns the value of attribute reachable_functions_cache.



4865
4866
4867
# File 'lib/idl/ast.rb', line 4865

def reachable_functions_cache
  @reachable_functions_cache
end

Instance Method Details

#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



5067
5068
5069
5070
5071
5072
5073
5074
5075
5076
5077
5078
# File 'lib/idl/ast.rb', line 5067

def add_symbol(symtab)
  internal_error "Functions should be declared at global scope" unless symtab.levels == 1

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

  symtab.add!(name, def_type)
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



4895
4896
4897
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
4915
4916
4917
4918
4919
4920
4921
4922
# File 'lib/idl/ast.rb', line 4895

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

  if templated?
    template_names.each do |tname|
      internal_error "Template values missing in symtab" unless symtab.get(tname)
    end
  end

  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

    arglist << [atype, a.name]
  end

  arglist.freeze
  unless templated?
    @arglist = arglist
  end
  arglist
end

#arguments_list_strObject

returns an array of arguments, as a string function (or template instance) does not need to be resolved



4926
4927
4928
# File 'lib/idl/ast.rb', line 4926

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

#bodyObject



5118
5119
5120
5121
5122
# File 'lib/idl/ast.rb', line 5118

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

  @body
end

#builtin?Boolean

Returns:

  • (Boolean)


5124
5125
5126
# File 'lib/idl/ast.rb', line 5124

def builtin?
  @body.nil?
end

#descriptionString

Returns Asciidoc formatted function description.

Returns:

  • (String)

    Asciidoc formatted function description



4880
4881
4882
# File 'lib/idl/ast.rb', line 4880

def description
  unindent(@desc)
end

#freeze_tree(global_symtab) ⇒ Object

freeze the entire tree from further modification This is also an opportunity to pre-calculate anything that only needs global symbols

Parameters:

  • global_symtab (SymbolTable)

    Symbol table with global scope populated



4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
# File 'lib/idl/ast.rb', line 4868

def freeze_tree(global_symtab)
  return if frozen?

  unless templated?
    arguments(global_symtab)
  end

  @children.each { |child| child.freeze_tree(global_symtab) }
  freeze
end

#nameObject



5000
5001
5002
# File 'lib/idl/ast.rb', line 5000

def name
  @name
end

#num_argsInteger

Returns The number of arguments to the function.

Returns:

  • (Integer)

    The number of arguments to the function



4890
4891
4892
# File 'lib/idl/ast.rb', line 4890

def num_args
  @argument_nodes.size
end

#return_type(symtab) ⇒ Object

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



4931
4932
4933
4934
4935
4936
4937
4938
4939
4940
4941
4942
4943
4944
4945
4946
4947
4948
4949
4950
4951
4952
4953
4954
4955
4956
4957
4958
4959
4960
4961
4962
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
# File 'lib/idl/ast.rb', line 4931

def return_type(symtab)
  cached = @cached_return_type[symtab.archdef]
  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[symtab.archdef] = VoidType
    return VoidType
  end

  unless templated?
    # with no templates, the return type does not change for a given arch_def
    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.archdef] = rtype
  end

  if templated?
    template_names.each do |tname|
      internal_error "Template values missing" unless symtab.get(tname)
    end
  end



  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
end

#return_type_list_strArray<String>

function (or template instance) does not need to be resolved

Returns:

  • (Array<String>)

    return type strings



4992
4993
4994
4995
4996
4997
4998
# File 'lib/idl/ast.rb', line 4992

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

#template_namesArray<String>

Returns Template arugment names, in order.

Returns:

  • (Array<String>)

    Template arugment names, in order



5081
5082
5083
# File 'lib/idl/ast.rb', line 5081

def template_names
  @targs.map(&:name)
end

#template_types(symtab) ⇒ Array<Type>

Returns Template argument types, in order.

Parameters:

Returns:

  • (Array<Type>)

    Template argument types, in order



5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097
# File 'lib/idl/ast.rb', line 5087

def template_types(symtab)
  return EMPTY_ARRAY unless templated?

  ttypes = []
  @targs.each do |a|
    ttype = a.type(symtab)
    ttype = ttype.ref_type if ttype.kind == :enum
    ttypes << ttype.clone.make_const
  end
  ttypes
end

#templated?Boolean

Returns whether or not the function is templated.

Returns:

  • (Boolean)

    whether or not the function is templated



4885
4886
4887
# File 'lib/idl/ast.rb', line 4885

def templated?
  !@targs.empty?
end

#type_check(symtab) ⇒ 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:



5040
5041
5042
5043
5044
5045
5046
5047
5048
5049
5050
5051
5052
5053
5054
5055
5056
5057
5058
5059
5060
5061
5062
5063
5064
# File 'lib/idl/ast.rb', line 5040

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

  type_check_targs(symtab)

  symtab = symtab.deep_clone
  symtab.push(self)
  template_names.each_with_index do |tname, index|
    symtab.add(tname, Var.new(tname, template_types(symtab)[index]))
  end

  type_check_return(symtab)

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


  # template functions are checked as they are called
  unless templated?
    type_check_body(symtab)
  end
  symtab.pop
end

#type_check_args(symtab) ⇒ Object



5108
5109
5110
# File 'lib/idl/ast.rb', line 5108

def type_check_args(symtab)
  @argument_nodes.each { |a| a.type_check(symtab, false) }
end

#type_check_body(symtab) ⇒ Object



5112
5113
5114
5115
5116
# File 'lib/idl/ast.rb', line 5112

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

  @body.type_check(symtab)
end

#type_check_from_call(symtab) ⇒ Object

we do lazy type checking of the function body so that we never check uncalled functions, which avoids dealing with mentions of CSRs that may not exist in a given implmentation



5023
5024
5025
5026
5027
5028
5029
5030
5031
5032
5033
5034
5035
5036
5037
# File 'lib/idl/ast.rb', line 5023

def type_check_from_call(symtab)
  internal_error "Function definitions should be at global + 1 scope" unless symtab.levels == 2

  type_check_return(symtab)
  type_check_args(symtab)
  # @argument_nodes.each do |a|
  #   value_result = value_try do
  #     symtab.add(a.name, Var.new(a.name, a.type(symtab), a.value(symtab)))
  #   end
  #   value_else(value_result) do
  #     symtab.add(a.name, Var.new(a.name, a.type(symtab)))
  #   end
  # end
  type_check_body(symtab)
end

#type_check_return(symtab) ⇒ Object



5104
5105
5106
# File 'lib/idl/ast.rb', line 5104

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

#type_check_targs(symtab) ⇒ Object



5099
5100
5101
5102
# File 'lib/idl/ast.rb', line 5099

def type_check_targs(symtab)
  @targs.each { |t| type_error "Template arguments must be uppercase" unless t.text_value[0] == t.text_value[0].upcase }
  @targs.each { |t| type_error "Template types must be integral" unless t.type(symtab).integral? }
end

#type_check_template_instance(symtab) ⇒ Object

Parameters:

  • template (Array<Integer>)

    values to apply



5005
5006
5007
5008
5009
5010
5011
5012
5013
5014
5015
5016
5017
5018
# File 'lib/idl/ast.rb', line 5005

def type_check_template_instance(symtab)
  internal_error "Function definitions should be at global + 1 scope" unless symtab.levels == 2

  internal_error "Not a template function" unless templated?

  template_names.each do |tname|
    internal_error "Template values missing" unless symtab.get(tname)
  end

  type_check_return(symtab)
  type_check_args(symtab)
  @argument_nodes.each { |a| symtab.add(a.name, Var.new(a.name, a.type(symtab))) }
  type_check_body(symtab)
end