Module: MilkTea::Lowering::Scans
- Included in:
- MilkTea::Lowerer
- Defined in:
- lib/milk_tea/core/lowering/scans.rb
Instance Method Summary collapse
- #analysis_uses_fatal?(analysis) ⇒ Boolean
- #analysis_uses_offsetof?(analysis) ⇒ Boolean
- #block_uses_expression_pattern?(statements, &predicate) ⇒ Boolean
- #block_uses_fatal?(statements) ⇒ Boolean
- #block_uses_offsetof?(statements) ⇒ Boolean
- #build_method_definitions ⇒ Object
- #collect_includes ⇒ Object
- #collect_nested_structs(parent_decl, parent_name: parent_decl.name) ⇒ Object
- #collect_one_struct_decl(decl) ⇒ Object
- #collect_structs ⇒ Object
- #expression_uses_pattern?(expression, &predicate) ⇒ Boolean
- #fatal_expression?(expression) ⇒ Boolean
- #lower_when_chosen_body(decl) ⇒ Object
- #normalized_include_header(header_name) ⇒ Object
- #offsetof_expression?(expression) ⇒ Boolean
- #prepare_analysis(analysis, path: nil) ⇒ Object
- #program_uses_fatal? ⇒ Boolean
- #program_uses_offsetof? ⇒ Boolean
- #standard_c_runtime_header?(header_name) ⇒ Boolean
- #statement_uses_expression_pattern?(statement, &predicate) ⇒ Boolean
Instance Method Details
#analysis_uses_fatal?(analysis) ⇒ Boolean
81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 81 def analysis_uses_fatal?(analysis) analysis.ast.declarations.any? do |decl| case decl when AST::FunctionDef block_uses_fatal?(decl.body) when AST::ExtendingBlock decl.methods.any? { |method| block_uses_fatal?(method.body) } else false end end end |
#analysis_uses_offsetof?(analysis) ⇒ Boolean
94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 94 def analysis_uses_offsetof?(analysis) analysis.ast.declarations.any? do |decl| case decl when AST::FunctionDef block_uses_offsetof?(decl.body) when AST::ExtendingBlock decl.methods.any? { |method| block_uses_offsetof?(method.body) } else false end end end |
#block_uses_expression_pattern?(statements, &predicate) ⇒ Boolean
115 116 117 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 115 def block_uses_expression_pattern?(statements, &predicate) statements.any? { |statement| statement_uses_expression_pattern?(statement, &predicate) } end |
#block_uses_fatal?(statements) ⇒ Boolean
107 108 109 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 107 def block_uses_fatal?(statements) block_uses_expression_pattern?(statements) { |expression| fatal_expression?(expression) } end |
#block_uses_offsetof?(statements) ⇒ Boolean
111 112 113 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 111 def block_uses_offsetof?(statements) block_uses_expression_pattern?(statements) { |expression| offsetof_expression?(expression) } end |
#build_method_definitions ⇒ Object
196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 196 def build_method_definitions @program.analyses_by_path.values.each_with_object({}) do |analysis, definitions| analysis.ast.declarations.grep(AST::ExtendingBlock).each do |extending_block| receiver_type = resolve_extending_receiver_type(analysis, extending_block.type_name) extending_block.methods.each do |method| method_key = method.kind == :static ? "static:#{method.name}" : method.name definitions[[receiver_type, method_key]] = [analysis, method] end end end end |
#collect_includes ⇒ Object
45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 45 def collect_includes headers = ["<stdbool.h>", "<stdint.h>", "<string.h>"] headers << "<stddef.h>" if program_uses_offsetof? headers << "<stdio.h>" if program_uses_fatal? each_raw_module_analysis do |analysis| analysis.directives.grep(AST::IncludeDirective).each do |directive| headers << normalized_include_header(directive.value) end end headers.uniq.map { |header| IR::Include.new(header:) } end |
#collect_nested_structs(parent_decl, parent_name: parent_decl.name) ⇒ Object
37 38 39 40 41 42 43 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 37 def collect_nested_structs(parent_decl, parent_name: parent_decl.name) parent_decl.nested_types.each do |nested| qualified_name = "#{parent_name}.#{nested.name}" @ctx.struct_types[qualified_name] = @ctx.types.fetch(qualified_name) collect_nested_structs(nested, parent_name: qualified_name) end end |
#collect_one_struct_decl(decl) ⇒ Object
14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 14 def collect_one_struct_decl(decl) case decl when AST::WhenStmt body = lower_when_chosen_body(decl) body&.each { |nested| collect_one_struct_decl(nested) } when AST::OpaqueDecl @ctx.opaque_types[decl.name] = @ctx.types.fetch(decl.name) when AST::StructDecl @ctx.struct_types[decl.name] = @ctx.types.fetch(decl.name) collect_nested_structs(decl) when AST::UnionDecl @ctx.union_types[decl.name] = @ctx.types.fetch(decl.name) end end |
#collect_structs ⇒ Object
10 11 12 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 10 def collect_structs @ctx.ast.declarations.each { |decl| collect_one_struct_decl(decl) } end |
#expression_uses_pattern?(expression, &predicate) ⇒ Boolean
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 156 def expression_uses_pattern?(expression, &predicate) return false unless expression return true if predicate.call(expression) case expression when AST::AwaitExpr expression_uses_pattern?(expression.expression, &predicate) when AST::Call expression_uses_pattern?(expression.callee, &predicate) || expression.arguments.any? { |argument| expression_uses_pattern?(argument.value, &predicate) } when AST::BinaryOp expression_uses_pattern?(expression.left, &predicate) || expression_uses_pattern?(expression.right, &predicate) when AST::RangeExpr expression_uses_pattern?(expression.start_expr, &predicate) || expression_uses_pattern?(expression.end_expr, &predicate) when AST::IfExpr expression_uses_pattern?(expression.condition, &predicate) || expression_uses_pattern?(expression.then_expression, &predicate) || expression_uses_pattern?(expression.else_expression, &predicate) when AST::MatchExpr expression_uses_pattern?(expression.expression, &predicate) || expression.arms.any? { |arm| expression_uses_pattern?(arm.pattern, &predicate) || expression_uses_pattern?(arm.value, &predicate) } when AST::UnsafeExpr expression_uses_pattern?(expression.expression, &predicate) when AST::PrefixCast expression_uses_pattern?(expression.expression, &predicate) when AST::UnaryOp expression_uses_pattern?(expression.operand, &predicate) when AST::MemberAccess expression_uses_pattern?(expression.receiver, &predicate) when AST::IndexAccess expression_uses_pattern?(expression.receiver, &predicate) || expression_uses_pattern?(expression.index, &predicate) when AST::Specialization expression_uses_pattern?(expression.callee, &predicate) || expression.arguments.any? { |argument| expression_uses_pattern?(argument.value, &predicate) } else false end end |
#fatal_expression?(expression) ⇒ Boolean
148 149 150 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 148 def fatal_expression?(expression) expression.is_a?(AST::Call) && expression.callee.is_a?(AST::Identifier) && expression.callee.name == "fatal" end |
#lower_when_chosen_body(decl) ⇒ Object
29 30 31 32 33 34 35 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 29 def lower_when_chosen_body(decl) val = compile_time_const_value(decl.discriminant) return nil if val.nil? chosen = decl.branches.find { |b| val == compile_time_const_value(b.pattern) } chosen&.body || decl.else_body end |
#normalized_include_header(header_name) ⇒ Object
59 60 61 62 63 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 59 def normalized_include_header(header_name) return "<#{header_name}>" if standard_c_runtime_header?(header_name) %("#{header_name}") end |
#offsetof_expression?(expression) ⇒ Boolean
152 153 154 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 152 def offsetof_expression?(expression) expression.is_a?(AST::OffsetofExpr) end |
#prepare_analysis(analysis, path: nil) ⇒ Object
190 191 192 193 194 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 190 def prepare_analysis(analysis, path: nil) @ctx.install(analysis) @ctx.current_analysis_path = path @ctx.module_prefix = module_c_prefix(@ctx.module_name) end |
#program_uses_fatal? ⇒ Boolean
69 70 71 72 73 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 69 def program_uses_fatal? each_non_raw_module_analysis.any? do |analysis| analysis_uses_fatal?(analysis) end end |
#program_uses_offsetof? ⇒ Boolean
75 76 77 78 79 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 75 def program_uses_offsetof? each_non_raw_module_analysis.any? do |analysis| analysis_uses_offsetof?(analysis) end end |
#standard_c_runtime_header?(header_name) ⇒ Boolean
65 66 67 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 65 def standard_c_runtime_header?(header_name) %w[stdbool.h stdint.h stdlib.h string.h stddef.h stdio.h time.h].include?(header_name) end |
#statement_uses_expression_pattern?(statement, &predicate) ⇒ Boolean
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/milk_tea/core/lowering/scans.rb', line 119 def statement_uses_expression_pattern?(statement, &predicate) case statement when AST::LocalDecl expression_uses_pattern?(statement.value, &predicate) when AST::Assignment expression_uses_pattern?(statement.target, &predicate) || expression_uses_pattern?(statement.value, &predicate) when AST::IfStmt statement.branches.any? { |branch| expression_uses_pattern?(branch.condition, &predicate) || block_uses_expression_pattern?(branch.body, &predicate) } || (statement.else_body && block_uses_expression_pattern?(statement.else_body, &predicate)) when AST::MatchStmt expression_uses_pattern?(statement.expression, &predicate) || statement.arms.any? { |arm| expression_uses_pattern?(arm.pattern, &predicate) || block_uses_expression_pattern?(arm.body, &predicate) } when AST::StaticAssert expression_uses_pattern?(statement.condition, &predicate) || expression_uses_pattern?(statement., &predicate) when AST::ForStmt statement.iterables.any? { |iterable| expression_uses_pattern?(iterable, &predicate) } || block_uses_expression_pattern?(statement.body, &predicate) when AST::UnsafeStmt, AST::WhileStmt expression = statement.is_a?(AST::WhileStmt) ? statement.condition : nil (expression && expression_uses_pattern?(expression, &predicate)) || block_uses_expression_pattern?(statement.body, &predicate) when AST::ReturnStmt statement.value && expression_uses_pattern?(statement.value, &predicate) when AST::ExpressionStmt expression_uses_pattern?(statement.expression, &predicate) when AST::DeferStmt block_uses_expression_pattern?(statement.body, &predicate) else false end end |