Module: MilkTea::LowererScans

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

Instance Method Summary collapse

Instance Method Details

#analysis_uses_fatal?(analysis) ⇒ Boolean

Returns:

  • (Boolean)


84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/milk_tea/core/lowering/scans.rb', line 84

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)


97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/milk_tea/core/lowering/scans.rb', line 97

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)


118
119
120
# File 'lib/milk_tea/core/lowering/scans.rb', line 118

def block_uses_expression_pattern?(statements, &predicate)
  statements.any? { |statement| statement_uses_expression_pattern?(statement, &predicate) }
end

#block_uses_fatal?(statements) ⇒ Boolean

Returns:

  • (Boolean)


110
111
112
# File 'lib/milk_tea/core/lowering/scans.rb', line 110

def block_uses_fatal?(statements)
  block_uses_expression_pattern?(statements) { |expression| fatal_expression?(expression) }
end

#block_uses_offsetof?(statements) ⇒ Boolean

Returns:

  • (Boolean)


114
115
116
# File 'lib/milk_tea/core/lowering/scans.rb', line 114

def block_uses_offsetof?(statements)
  block_uses_expression_pattern?(statements) { |expression| offsetof_expression?(expression) }
end

#build_method_definitionsObject



203
204
205
206
207
208
209
210
211
212
213
# File 'lib/milk_tea/core/lowering/scans.rb', line 203

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



48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/milk_tea/core/lowering/scans.rb', line 48

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



40
41
42
43
44
45
46
# File 'lib/milk_tea/core/lowering/scans.rb', line 40

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



13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/milk_tea/core/lowering/scans.rb', line 13

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_struct_from_decl(decl) ⇒ Object



28
29
30
# File 'lib/milk_tea/core/lowering/scans.rb', line 28

def collect_struct_from_decl(decl)
  collect_one_struct_decl(decl)
end

#collect_structsObject



9
10
11
# File 'lib/milk_tea/core/lowering/scans.rb', line 9

def collect_structs
  @ctx.ast.declarations.each { |decl| collect_one_struct_decl(decl) }
end

#expression_uses_pattern?(expression, &predicate) ⇒ Boolean

Returns:

  • (Boolean)


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
189
190
191
# File 'lib/milk_tea/core/lowering/scans.rb', line 159

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)


151
152
153
# File 'lib/milk_tea/core/lowering/scans.rb', line 151

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



32
33
34
35
36
37
38
# File 'lib/milk_tea/core/lowering/scans.rb', line 32

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



62
63
64
65
66
# File 'lib/milk_tea/core/lowering/scans.rb', line 62

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)


155
156
157
# File 'lib/milk_tea/core/lowering/scans.rb', line 155

def offsetof_expression?(expression)
  expression.is_a?(AST::OffsetofExpr)
end

#prepare_analysis(analysis, path: nil) ⇒ Object



193
194
195
196
197
# File 'lib/milk_tea/core/lowering/scans.rb', line 193

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)


72
73
74
75
76
# File 'lib/milk_tea/core/lowering/scans.rb', line 72

def program_uses_fatal?
  each_non_raw_module_analysis.any? do |analysis|
    analysis_uses_fatal?(analysis)
  end
end

#program_uses_offsetof?Boolean

Returns:

  • (Boolean)


78
79
80
81
82
# File 'lib/milk_tea/core/lowering/scans.rb', line 78

def program_uses_offsetof?
  each_non_raw_module_analysis.any? do |analysis|
    analysis_uses_offsetof?(analysis)
  end
end

#sanitize_type_name_for_tuple(type) ⇒ Object



199
200
201
# File 'lib/milk_tea/core/lowering/scans.rb', line 199

def sanitize_type_name_for_tuple(type)
  type.to_s.gsub(/[^a-zA-Z0-9]/, "_").gsub(/_+/, "_").gsub(/^_|_$/, "")
end

#standard_c_runtime_header?(header_name) ⇒ Boolean

Returns:

  • (Boolean)


68
69
70
# File 'lib/milk_tea/core/lowering/scans.rb', line 68

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)


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
147
148
149
# File 'lib/milk_tea/core/lowering/scans.rb', line 122

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