Class: Idl::FunctionBodyAst
- Includes:
- Executable, Returns
- Defined in:
- lib/idlc/ast.rb,
lib/idlc/passes/prune.rb,
lib/idlc/passes/gen_adoc.rb,
lib/idlc/passes/gen_option_adoc.rb,
lib/idlc/passes/find_return_values.rb
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
- #const_eval?(symtab) ⇒ Boolean
- #execute(symtab) ⇒ Object
- #gen_adoc(indent = 0, indent_spaces: 2) ⇒ Object
- #gen_option_adoc ⇒ Object
-
#initialize(input, interval, stmts) ⇒ FunctionBodyAst
constructor
A new instance of FunctionBodyAst.
-
#pass_find_return_values(symtab) ⇒ Array<Ast, Array<Ast>>
List of possible return values, along with the condition it occurs under.
- #prune(symtab, forced_type: nil, args_already_applied: false) ⇒ Object
- #return_type(symtab) ⇒ Object
-
#return_value(symtab) ⇒ Integer, ...
Evaluate the compile-time return value of this node, or, if the node does not return (e.g., because it is an IfAst but there is no return on the taken path), execute the node and update the symtab.
-
#return_values(symtab) ⇒ Array<Integer>, Array<Boolean>
Evaluate all possible compile-time return values of this node, or, if the node does not return (e.g., because it is an IfAst but there is no return on a possible path), execute the node and update the symtab.
- #statements ⇒ Object
- #stmts ⇒ Object
- #to_h ⇒ Object
- #to_idl ⇒ Object
-
#type_check(symtab, strict:) ⇒ void
type check this node and all children.
Methods included from Returns
Methods included from Executable
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, #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, stmts) ⇒ FunctionBodyAst
Returns a new instance of FunctionBodyAst.
8080 8081 8082 |
# File 'lib/idlc/ast.rb', line 8080 def initialize(input, interval, stmts) super(input, interval, stmts) end |
Class Method Details
.from_h(yaml, source_mapper) ⇒ Object
8188 8189 8190 8191 8192 8193 8194 8195 8196 8197 |
# File 'lib/idlc/ast.rb', line 8188 def self.from_h(yaml, source_mapper) raise "Bad YAML" unless yaml.key?("kind") && yaml.fetch("kind") == "function_body" input = input_from_source_yaml(yaml.fetch("source"), source_mapper) interval = interval_from_source_yaml(yaml.fetch("source")) FunctionBodyAst.new( input, interval, yaml.fetch("stmts").map { |s| AstNode.from_h(s, source_mapper) } ) end |
Instance Method Details
#const_eval?(symtab) ⇒ Boolean
8073 8074 8075 8076 8077 |
# File 'lib/idlc/ast.rb', line 8073 def const_eval?(symtab) stmts.all? do |stmt| stmt.const_eval?(symtab) end end |
#execute(symtab) ⇒ Object
8143 |
# File 'lib/idlc/ast.rb', line 8143 def execute(symtab) = return_value(symtab) |
#gen_adoc(indent = 0, indent_spaces: 2) ⇒ Object
313 314 315 |
# File 'lib/idlc/passes/gen_adoc.rb', line 313 def gen_adoc(indent = 0, indent_spaces: 2) statements.map { |s| "#{' ' * indent}#{s.gen_adoc(0, indent_spaces:)}" }.join("\n") end |
#gen_option_adoc ⇒ Object
22 23 24 |
# File 'lib/idlc/passes/gen_option_adoc.rb', line 22 def gen_option_adoc statements.map(&:gen_option_adoc).join("\n") end |
#pass_find_return_values(symtab) ⇒ Array<Ast, Array<Ast>>
Returns List of possible return values, along with the condition it occurs under.
67 68 69 70 71 72 73 74 |
# File 'lib/idlc/passes/find_return_values.rb', line 67 def pass_find_return_values(symtab) values = [] current_conditions = [] statements.each do |s| s.pass_find_return_values(values, current_conditions, symtab) end values end |
#prune(symtab, forced_type: nil, args_already_applied: false) ⇒ Object
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 |
# File 'lib/idlc/passes/prune.rb', line 314 def prune(symtab, forced_type: nil, args_already_applied: false) symtab.push(self) begin func_def = find_ancestor(FunctionDefAst) unless args_already_applied || func_def.nil? # push args func_def.arguments(symtab).each do |arg_type, arg_name| symtab.add(arg_name, Var.new(arg_name, arg_type)) end end pruned_body = nil prune_stmts = -> { [].tap do |out| statements.each do |s| out << s.prune(symtab) break if out.last.always_terminates? end end } value_result = value_try do # go through the statements, and stop if we find one that returns or raises an exception statements.each_with_index do |s, idx| if s.is_a?(ReturnStatementAst) pruned_body = FunctionBodyAst.new(input, interval, statements[0..idx].map { |s| s.prune(symtab) }) return pruned_body elsif s.is_a?(ConditionalReturnStatementAst) value_try do v = s.return_value(symtab) # conditional return, condition not taken if v.nil? unless v.nil? pruned_body = FunctionBodyAst.new(input, interval, statements[0..idx].map { |s| s.prune(symtab) }) return pruned_body end end # || conditional return, condition not known; keep going elsif s.is_a?(StatementAst) && s.action.is_a?(FunctionCallExpressionAst) && s.action.name == "raise" pruned_body = FunctionBodyAst.new(input, interval, statements[0..idx].map { |s| s.prune(symtab) }) return pruned_body else s.execute(symtab) end end pruned_body = FunctionBodyAst.new(input, interval, prune_stmts.()) end value_else(value_result) do pruned_body = FunctionBodyAst.new(input, interval, prune_stmts.()) end ensure symtab.pop end pruned_body end |
#return_type(symtab) ⇒ Object
8112 8113 8114 8115 8116 8117 8118 8119 8120 8121 8122 |
# File 'lib/idlc/ast.rb', line 8112 def return_type(symtab) # go through the statements, and return the first one that has a return type stmts.each do |s| if s.is_a?(Returns) return s.return_type(symtab) elsif s.action.declaration? s.action.add_symbol(symtab) end end VoidType end |
#return_value(symtab) ⇒ Integer, ...
arguments must be put on the symtab before calling
Evaluate the compile-time return value of this node, or, if the node does not return (e.g., because it is an IfAst but there is no return on the taken path), execute the node and update the symtab
8127 8128 8129 8130 8131 8132 8133 8134 8135 8136 8137 8138 8139 8140 8141 |
# File 'lib/idlc/ast.rb', line 8127 def return_value(symtab) internal_error "Function bodies should be at global + 1 scope" unless symtab.levels == 2 # go through the statements, and return the first one that has a return value stmts.each do |s| if s.is_a?(Returns) v = s.return_value(symtab) return v unless v.nil? else s.execute(symtab) end end value_error "No function body statement returned a value" end |
#return_values(symtab) ⇒ Array<Integer>, Array<Boolean>
Evaluate all possible compile-time return values of this node, or, if the node does not return (e.g., because it is an IfAst but there is no return on a possible path), execute the node and update the symtab
8146 8147 8148 8149 8150 8151 8152 8153 8154 8155 8156 8157 8158 8159 8160 8161 8162 8163 8164 8165 8166 8167 8168 8169 8170 8171 8172 8173 |
# File 'lib/idlc/ast.rb', line 8146 def return_values(symtab) internal_error "Function bodies should be at global + 1 scope" unless symtab.levels == 2 values = T.let([], T::Array[ValueRbType]) value_result = value_try do # if there is a definite return value, then just return that return [return_value(symtab)] end value_else(value_result) do # go through the statements, and collect return values # we can stop if we encounter a statement with a known return value stmts.each do |s| if s.is_a?(Returns) value_result = value_try do v = s.return_value(symtab) return values.push(v).uniq unless v.nil? end value_else(value_result) do values += s.return_values(symtab) end else s.execute(symtab) end end end values.uniq end |
#statements ⇒ Object
8084 |
# File 'lib/idlc/ast.rb', line 8084 def statements = @children |
#stmts ⇒ Object
8086 |
# File 'lib/idlc/ast.rb', line 8086 def stmts = @children |
#to_h ⇒ Object
8181 8182 8183 8184 8185 |
# File 'lib/idlc/ast.rb', line 8181 def to_h = { "kind" => "function_body", "stmts" => stmts.map(&:to_h), "source" => source_yaml } |
#to_idl ⇒ Object
8176 8177 8178 |
# File 'lib/idlc/ast.rb', line 8176 def to_idl stmts.map(&:to_idl).join("\n") 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
8089 8090 8091 8092 8093 8094 8095 8096 8097 8098 8099 8100 8101 8102 8103 8104 8105 8106 8107 8108 8109 8110 |
# File 'lib/idlc/ast.rb', line 8089 def type_check(symtab, strict:) internal_error "Function bodies should be at global + 1 scope (at #{symtab.levels})" unless symtab.levels == 2 return_value_might_be_known = true stmts.each do |s| s.type_check(symtab, strict:) # next unless return_value_might_be_known # begin # if s.is_a?(Returns) # s.return_value(symtab) # # if we reach here, the return value is known, so we don't have to go further # break # else # s.execute(symtab) # end # rescue ValueError # return_value_might_be_known = false # end end end |