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)


89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/milk_tea/core/lowering/scans.rb', line 89

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)


102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/milk_tea/core/lowering/scans.rb', line 102

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)


123
124
125
# File 'lib/milk_tea/core/lowering/scans.rb', line 123

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

#block_uses_fatal?(statements) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#block_uses_offsetof?(statements) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#build_method_definitionsObject



206
207
208
209
210
211
212
213
214
215
216
# File 'lib/milk_tea/core/lowering/scans.rb', line 206

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



53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/milk_tea/core/lowering/scans.rb', line 53

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



45
46
47
48
49
50
51
# File 'lib/milk_tea/core/lowering/scans.rb', line 45

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



22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/milk_tea/core/lowering/scans.rb', line 22

def collect_struct_from_decl(decl)
  case decl
  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)
  when AST::WhenStmt
    body = lower_when_chosen_body(decl)
    body&.each { |nested| collect_struct_from_decl(nested) }
  end
end

#collect_structsObject



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/milk_tea/core/lowering/scans.rb', line 5

def collect_structs
  @ctx.ast.declarations.each do |decl|
    case decl
    when AST::WhenStmt
      body = lower_when_chosen_body(decl)
      body&.each { |nested| collect_struct_from_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
end

#expression_uses_pattern?(expression, &predicate) ⇒ Boolean

Returns:

  • (Boolean)


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
192
193
194
# File 'lib/milk_tea/core/lowering/scans.rb', line 162

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)


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

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



37
38
39
40
41
42
43
# File 'lib/milk_tea/core/lowering/scans.rb', line 37

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



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

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)


158
159
160
# File 'lib/milk_tea/core/lowering/scans.rb', line 158

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

#prepare_analysis(analysis, path: nil) ⇒ Object



196
197
198
199
200
# File 'lib/milk_tea/core/lowering/scans.rb', line 196

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)


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

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

#program_uses_offsetof?Boolean

Returns:

  • (Boolean)


83
84
85
86
87
# File 'lib/milk_tea/core/lowering/scans.rb', line 83

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



202
203
204
# File 'lib/milk_tea/core/lowering/scans.rb', line 202

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)


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

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)


127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/milk_tea/core/lowering/scans.rb', line 127

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::DeferStmt, AST::ExpressionStmt
    expression_uses_pattern?(statement.expression, &predicate)
  else
    false
  end
end