Module: MilkTea::Lowering::Scans

Included in:
MilkTea::Lowerer
Defined in:
lib/milk_tea/core/lowering/scans.rb

Instance Method Summary collapse

Instance Method Details

#analysis_uses_fatal?(analysis) ⇒ Boolean

Returns:

  • (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

Returns:

  • (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

Returns:

  • (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

Returns:

  • (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

Returns:

  • (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_definitionsObject



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_includesObject



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_structsObject



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

Returns:

  • (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

Returns:

  • (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

Returns:

  • (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

Returns:

  • (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

Returns:

  • (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

Returns:

  • (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

Returns:

  • (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.message, &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