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.
8095 8096 8097 |
# File 'lib/idlc/ast.rb', line 8095 def initialize(input, interval, stmts) super(input, interval, stmts) end |
Class Method Details
.from_h(yaml, source_mapper) ⇒ Object
8203 8204 8205 8206 8207 8208 8209 8210 8211 8212 |
# File 'lib/idlc/ast.rb', line 8203 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
8088 8089 8090 8091 8092 |
# File 'lib/idlc/ast.rb', line 8088 def const_eval?(symtab) stmts.all? do |stmt| stmt.const_eval?(symtab) end end |
#execute(symtab) ⇒ Object
8158 |
# File 'lib/idlc/ast.rb', line 8158 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
8127 8128 8129 8130 8131 8132 8133 8134 8135 8136 8137 |
# File 'lib/idlc/ast.rb', line 8127 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
8142 8143 8144 8145 8146 8147 8148 8149 8150 8151 8152 8153 8154 8155 8156 |
# File 'lib/idlc/ast.rb', line 8142 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
8161 8162 8163 8164 8165 8166 8167 8168 8169 8170 8171 8172 8173 8174 8175 8176 8177 8178 8179 8180 8181 8182 8183 8184 8185 8186 8187 8188 |
# File 'lib/idlc/ast.rb', line 8161 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
8099 |
# File 'lib/idlc/ast.rb', line 8099 def statements = @children |
#stmts ⇒ Object
8101 |
# File 'lib/idlc/ast.rb', line 8101 def stmts = @children |
#to_h ⇒ Object
8196 8197 8198 8199 8200 |
# File 'lib/idlc/ast.rb', line 8196 def to_h = { "kind" => "function_body", "stmts" => stmts.map(&:to_h), "source" => source_yaml } |
#to_idl ⇒ Object
8191 8192 8193 |
# File 'lib/idlc/ast.rb', line 8191 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
8104 8105 8106 8107 8108 8109 8110 8111 8112 8113 8114 8115 8116 8117 8118 8119 8120 8121 8122 8123 8124 8125 |
# File 'lib/idlc/ast.rb', line 8104 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 |