mt-lang 0.3.23 → 0.3.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/lib/milk_tea/base.rb +1 -1
  3. data/lib/milk_tea/core/bindings/attribute_binding.rb +51 -49
  4. data/lib/milk_tea/core/bindings/function_binding.rb +3 -1
  5. data/lib/milk_tea/core/bindings/module_binding.rb +42 -40
  6. data/lib/milk_tea/core/bindings/value_binding.rb +16 -14
  7. data/lib/milk_tea/core/bindings.rb +4 -9
  8. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +11 -11
  9. data/lib/milk_tea/core/c_backend/expressions.rb +11 -11
  10. data/lib/milk_tea/core/c_backend/feature_detection.rb +15 -19
  11. data/lib/milk_tea/core/c_backend/format_helpers.rb +1 -1
  12. data/lib/milk_tea/core/c_backend/reinterpret.rb +10 -10
  13. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1 -1
  14. data/lib/milk_tea/core/c_backend/statements.rb +20 -20
  15. data/lib/milk_tea/core/c_backend/type_declaration.rb +4 -4
  16. data/lib/milk_tea/core/c_backend/type_system.rb +2 -1
  17. data/lib/milk_tea/core/c_backend.rb +3 -3
  18. data/lib/milk_tea/core/compile_time.rb +41 -70
  19. data/lib/milk_tea/core/control_flow/builder.rb +3 -3
  20. data/lib/milk_tea/core/control_flow/constant_propagation.rb +1 -1
  21. data/lib/milk_tea/core/control_flow/graph.rb +1 -1
  22. data/lib/milk_tea/core/control_flow/termination.rb +1 -1
  23. data/lib/milk_tea/core/cst.rb +16 -0
  24. data/lib/milk_tea/core/flow_scope.rb +13 -0
  25. data/lib/milk_tea/core/lexer/character_classes.rb +2 -2
  26. data/lib/milk_tea/core/lexer/format_strings.rb +5 -5
  27. data/lib/milk_tea/core/lexer/heredocs.rb +6 -6
  28. data/lib/milk_tea/core/lexer/indentation.rb +4 -4
  29. data/lib/milk_tea/core/lexer/numbers.rb +1 -1
  30. data/lib/milk_tea/core/lexer/strings.rb +4 -4
  31. data/lib/milk_tea/core/lexer/symbols.rb +3 -3
  32. data/lib/milk_tea/core/lexer/trivia.rb +4 -4
  33. data/lib/milk_tea/core/lexer.rb +17 -17
  34. data/lib/milk_tea/core/lowering/async/analysis.rb +210 -208
  35. data/lib/milk_tea/core/lowering/async/async_lowering.rb +1406 -0
  36. data/lib/milk_tea/core/lowering/async/frame_builder.rb +615 -613
  37. data/lib/milk_tea/core/lowering/async/normalization.rb +410 -408
  38. data/lib/milk_tea/core/lowering/block.rb +982 -980
  39. data/lib/milk_tea/core/lowering/calls.rb +1360 -1358
  40. data/lib/milk_tea/core/lowering/declarations.rb +175 -173
  41. data/lib/milk_tea/core/lowering/dyn.rb +171 -169
  42. data/lib/milk_tea/core/lowering/events.rb +946 -944
  43. data/lib/milk_tea/core/lowering/expressions.rb +1514 -1512
  44. data/lib/milk_tea/core/lowering/foreign_cstr.rb +180 -178
  45. data/lib/milk_tea/core/lowering/functions.rb +186 -184
  46. data/lib/milk_tea/core/lowering/loops.rb +1004 -1002
  47. data/lib/milk_tea/core/lowering/lowering_context.rb +1 -1
  48. data/lib/milk_tea/core/lowering/proc.rb +355 -353
  49. data/lib/milk_tea/core/lowering/resolve.rb +2127 -2125
  50. data/lib/milk_tea/core/lowering/scans.rb +162 -168
  51. data/lib/milk_tea/core/lowering/str_buffer.rb +104 -102
  52. data/lib/milk_tea/core/lowering/utils.rb +965 -963
  53. data/lib/milk_tea/core/lowering.rb +22 -22
  54. data/lib/milk_tea/core/module_binder.rb +1 -1
  55. data/lib/milk_tea/core/module_loader.rb +2 -2
  56. data/lib/milk_tea/core/parser/attributes.rb +1 -1
  57. data/lib/milk_tea/core/parser/blocks.rb +2 -2
  58. data/lib/milk_tea/core/parser/declarations.rb +15 -15
  59. data/lib/milk_tea/core/parser/expressions.rb +20 -20
  60. data/lib/milk_tea/core/parser/recovery.rb +1 -1
  61. data/lib/milk_tea/core/parser/statements.rb +3 -3
  62. data/lib/milk_tea/core/parser/types.rb +4 -4
  63. data/lib/milk_tea/core/parser.rb +39 -21
  64. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +6 -6
  65. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +5 -5
  66. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +2 -2
  67. data/lib/milk_tea/core/semantic_analyzer/calls.rb +1 -1
  68. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +4 -4
  69. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +37 -37
  70. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +4 -4
  71. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +4 -4
  72. data/lib/milk_tea/core/semantic_analyzer/generics.rb +4 -4
  73. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +7 -7
  74. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +59 -59
  75. data/lib/milk_tea/core/semantic_analyzer/statements.rb +3 -3
  76. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +1 -1
  77. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +12 -12
  78. data/lib/milk_tea/core/types/layout.rb +3 -1
  79. data/lib/milk_tea/core/types/visitor.rb +351 -349
  80. data/lib/milk_tea/core.rb +0 -2
  81. data/lib/milk_tea/tooling/linter/flow_rules.rb +3 -3
  82. metadata +4 -5
  83. data/lib/milk_tea/core/cst_builder.rb +0 -19
  84. data/lib/milk_tea/core/lowering/async/lowering.rb +0 -1404
  85. data/lib/milk_tea/core/token_stream.rb +0 -30
@@ -340,7 +340,7 @@ module MilkTea
340
340
  lines
341
341
  end
342
342
 
343
- def emit_text_buffer_helpers
343
+ def emit_utf8_validation_helpers
344
344
  [
345
345
  "static bool mt_is_utf8_continuation_byte(unsigned char byte) {",
346
346
  "#{INDENT}return (byte & 0xC0u) == 0x80u;",
@@ -19,7 +19,7 @@ module MilkTea
19
19
 
20
20
  def emit_statement(statement, level, function:, used_labels:, loop_continue_label: nil, loop_break_label: nil, remaining_statements: [])
21
21
  indent = INDENT * level
22
- aliases = checked_index_aliases_for_statement(statement)
22
+ aliases = collect_checked_index_aliases_for_statement(statement)
23
23
  alias_lines = emit_checked_index_alias_declarations(aliases, indent)
24
24
  if statement.respond_to?(:line) && statement.line
25
25
  @current_line = statement.line
@@ -56,7 +56,7 @@ module MilkTea
56
56
  ["#{indent}#{emit_expression(statement.target)} #{statement.operator} #{emit_expression(statement.value)};"]
57
57
  end
58
58
  when IR::BlockStmt
59
- if block_requires_scope?(statement.body)
59
+ if statements_require_scope?(statement.body)
60
60
  lines = ["#{indent}{"]
61
61
  lines.concat(emit_statement_sequence(statement.body, level + 1, function:, used_labels:, loop_continue_label:, loop_break_label:))
62
62
  lines << "#{indent}}"
@@ -89,7 +89,7 @@ module MilkTea
89
89
  body_continue_label = loop_continue_label_name(statement.body)
90
90
  body = body_continue_label ? statement.body[0...-1] : statement.body
91
91
  body_break_label = loop_break_label_name(body, remaining_statements)
92
- @suppressed_labels << body_break_label if body_break_label && !statements_need_explicit_break_label_after_emission?(body, body_break_label, loop_break_label_active: true)
92
+ @suppressed_labels << body_break_label if body_break_label && !block_requires_break_label?(body, body_break_label, loop_break_label_active: true)
93
93
  if @debug_guards
94
94
  @loop_guard_id += 1
95
95
  guard_name = "__mt_loop_#{@loop_guard_id}"
@@ -111,12 +111,12 @@ module MilkTea
111
111
  body_continue_label = loop_continue_label_name(statement.body)
112
112
  body = body_continue_label ? statement.body[0...-1] : statement.body
113
113
  body_break_label = loop_break_label_name(body, remaining_statements)
114
- @suppressed_labels << body_break_label if body_break_label && !statements_need_explicit_break_label_after_emission?(body, body_break_label, loop_break_label_active: true)
114
+ @suppressed_labels << body_break_label if body_break_label && !block_requires_break_label?(body, body_break_label, loop_break_label_active: true)
115
115
  if @debug_guards
116
116
  @loop_guard_id += 1
117
117
  guard_name = "__mt_loop_#{@loop_guard_id}"
118
118
  lines = ["#{indent}{ uintptr_t #{guard_name} = 0;"]
119
- lines << "#{indent}for (#{emit_for_clause_statement(statement.init)}; #{emit_expression(statement.condition)}; #{emit_for_clause_statement(statement.post)}) {"
119
+ lines << "#{indent}for (#{emit_for_loop_clause(statement.init)}; #{emit_expression(statement.condition)}; #{emit_for_loop_clause(statement.post)}) {"
120
120
  guard = "#{INDENT * (level + 1)}if (++#{guard_name} > #{LOOP_GUARD_MAX_ITERATIONS}) mt_fatal(\"loop iteration limit exceeded in #{function.linkage_name}\");"
121
121
  lines << guard
122
122
  lines.concat(emit_statement_sequence(body, level + 1, function:, used_labels:, loop_continue_label: body_continue_label, loop_break_label: body_break_label))
@@ -124,7 +124,7 @@ module MilkTea
124
124
  lines << "#{indent}}"
125
125
  lines
126
126
  else
127
- lines = ["#{indent}for (#{emit_for_clause_statement(statement.init)}; #{emit_expression(statement.condition)}; #{emit_for_clause_statement(statement.post)}) {"]
127
+ lines = ["#{indent}for (#{emit_for_loop_clause(statement.init)}; #{emit_expression(statement.condition)}; #{emit_for_loop_clause(statement.post)}) {"]
128
128
  lines.concat(emit_statement_sequence(body, level + 1, function:, used_labels:, loop_continue_label: body_continue_label, loop_break_label: body_break_label))
129
129
  lines << "#{indent}}"
130
130
  lines
@@ -149,7 +149,7 @@ module MilkTea
149
149
  when IR::IfStmt
150
150
  emit_if_statement(statement, level, function:, used_labels:, loop_continue_label:, loop_break_label:)
151
151
  when IR::SwitchStmt
152
- if switch_emittable_as_if?(statement, loop_break_label:)
152
+ if can_emit_switch_as_if?(statement, loop_break_label:)
153
153
  emit_switch_as_if_statement(statement, level, function:, used_labels:, loop_continue_label:, loop_break_label:)
154
154
  else
155
155
  lines = ["#{indent}switch (#{emit_expression(statement.expression)}) {"]
@@ -183,8 +183,8 @@ module MilkTea
183
183
  alias_lines + line_directive + statement_lines
184
184
  end
185
185
 
186
- def compact_generated_statement_sequence(statements)
187
- transformed = statements.map { |statement| transform_compactable_nested_bodies(statement) }
186
+ def fold_redundant_statement_temps(statements)
187
+ transformed = statements.map { |statement| fold_nested_redundant_statement_temps(statement) }
188
188
  compacted = []
189
189
  index = 0
190
190
  reachable = true
@@ -226,26 +226,26 @@ module MilkTea
226
226
  compacted
227
227
  end
228
228
 
229
- def transform_compactable_nested_bodies(statement)
229
+ def fold_nested_redundant_statement_temps(statement)
230
230
  case statement
231
231
  when IR::BlockStmt
232
- IR::BlockStmt.new(body: compact_generated_statement_sequence(statement.body))
232
+ IR::BlockStmt.new(body: fold_redundant_statement_temps(statement.body))
233
233
  when IR::WhileStmt
234
234
  canonicalize_top_guarded_while(
235
- IR::WhileStmt.new(condition: statement.condition, body: compact_generated_statement_sequence(statement.body))
235
+ IR::WhileStmt.new(condition: statement.condition, body: fold_redundant_statement_temps(statement.body))
236
236
  )
237
237
  when IR::ForStmt
238
238
  IR::ForStmt.new(
239
239
  init: statement.init,
240
240
  condition: statement.condition,
241
241
  post: statement.post,
242
- body: compact_generated_statement_sequence(statement.body),
242
+ body: fold_redundant_statement_temps(statement.body),
243
243
  )
244
244
  when IR::IfStmt
245
245
  IR::IfStmt.new(
246
246
  condition: statement.condition,
247
- then_body: compact_generated_statement_sequence(statement.then_body),
248
- else_body: statement.else_body ? compact_generated_statement_sequence(statement.else_body) : nil,
247
+ then_body: fold_redundant_statement_temps(statement.then_body),
248
+ else_body: statement.else_body ? fold_redundant_statement_temps(statement.else_body) : nil,
249
249
  )
250
250
  when IR::SwitchStmt
251
251
  IR::SwitchStmt.new(
@@ -253,9 +253,9 @@ module MilkTea
253
253
  exhaustive: statement.exhaustive,
254
254
  cases: statement.cases.map do |switch_case|
255
255
  if switch_case.is_a?(IR::SwitchDefaultCase)
256
- IR::SwitchDefaultCase.new(body: compact_generated_statement_sequence(switch_case.body))
256
+ IR::SwitchDefaultCase.new(body: fold_redundant_statement_temps(switch_case.body))
257
257
  else
258
- IR::SwitchCase.new(value: switch_case.value, body: compact_generated_statement_sequence(switch_case.body))
258
+ IR::SwitchCase.new(value: switch_case.value, body: fold_redundant_statement_temps(switch_case.body))
259
259
  end
260
260
  end,
261
261
  )
@@ -384,7 +384,7 @@ module MilkTea
384
384
  type.is_a?(Types::Primitive) && type.name == "bool"
385
385
  end
386
386
 
387
- def checked_index_aliases_for_statement(statement)
387
+ def collect_checked_index_aliases_for_statement(statement)
388
388
  expressions = case statement
389
389
  when IR::LocalDecl
390
390
  [statement.value]
@@ -524,11 +524,11 @@ module MilkTea
524
524
  nil
525
525
  end
526
526
 
527
- def block_requires_scope?(statements)
527
+ def statements_require_scope?(statements)
528
528
  statements.any? { |statement| statement.is_a?(IR::LocalDecl) }
529
529
  end
530
530
 
531
- def emit_for_clause_statement(statement)
531
+ def emit_for_loop_clause(statement)
532
532
  case statement
533
533
  when IR::LocalDecl
534
534
  raise CBackendError.new("array for-loop init declarations are unsupported", line: 0, column: 0, path: @path) if array_type?(statement.type)
@@ -137,7 +137,7 @@ module MilkTea
137
137
  def function_params(function)
138
138
  params = []
139
139
  params << array_out_param_declaration(function.return_type, ARRAY_OUT_PARAM_NAME) if array_type?(function.return_type)
140
- params.concat(emitted_function_params(function).map { |param| c_declaration(param.pointer ? pointer_to(param.type) : param.type, param.linkage_name) })
140
+ params.concat(call_emission_params(function).map { |param| c_declaration(param.pointer ? pointer_to(param.type) : param.type, param.linkage_name) })
141
141
 
142
142
  if params.empty?
143
143
  "void"
@@ -150,7 +150,7 @@ module MilkTea
150
150
  @checked_index_alias_stack.clear
151
151
  @checked_index_alias_id = 0
152
152
  @suppressed_labels = []
153
- body = compact_generated_statement_sequence(function.body)
153
+ body = fold_redundant_statement_temps(function.body)
154
154
  lines = ["#{function_signature(function)} {"]
155
155
  used_labels = collect_used_labels(body)
156
156
  if body.empty?
@@ -194,7 +194,7 @@ module MilkTea
194
194
  def emit_simd_type(type)
195
195
  name = simd_type_name(type)
196
196
  elem_c = primitive_c_type(type.element_type.name)
197
- width = type.lane_count * Layout.primitive_layout(type.element_type).first
197
+ width = type.lane_count * Types::Layout.primitive_layout(type.element_type).first
198
198
  ["typedef #{elem_c} #{name} __attribute__((__vector_size__(#{width})));"]
199
199
  end
200
200
 
@@ -207,7 +207,7 @@ module MilkTea
207
207
  " __attribute__((#{attributes.join(', ')}))"
208
208
  end
209
209
 
210
- def emit_vector_math_types
210
+ def emit_vector_math_typedefs
211
211
  lines = []
212
212
  lines << "typedef struct mt_vec2 { float x; float y; } mt_vec2;"
213
213
  lines << "typedef struct mt_vec3 { float x; float y; float z; } mt_vec3;"
@@ -33,7 +33,7 @@ module MilkTea
33
33
  end
34
34
 
35
35
  def c_field_declaration(type, name)
36
- return "uint8_t #{name}" if void_storage_field?(type)
36
+ return "uint8_t #{name}" if void_typed_field?(type)
37
37
 
38
38
  c_declaration(type, name)
39
39
  end
@@ -224,6 +224,7 @@ module MilkTea
224
224
  end
225
225
 
226
226
  def named_type_c_name(type)
227
+ return nullable_opt_type_name(type) if type.is_a?(Types::Nullable)
227
228
  return task_type_name(type) if type.is_a?(Types::Task)
228
229
  if type.is_a?(Types::VariantArmPayload)
229
230
  return "#{named_type_c_name(type.variant_type)}_#{type.arm_name}"
@@ -127,7 +127,7 @@ module MilkTea
127
127
  lines << ""
128
128
  end
129
129
  if uses_vector_math_types?
130
- lines.concat(emit_vector_math_types)
130
+ lines.concat(emit_vector_math_typedefs)
131
131
  lines << ""
132
132
  end
133
133
  if uses_fatal_helper?
@@ -146,8 +146,8 @@ module MilkTea
146
146
  lines.concat(emit_str_equality_helper)
147
147
  lines << ""
148
148
  end
149
- if uses_text_buffer_helpers?
150
- lines.concat(emit_text_buffer_helpers)
149
+ if uses_str_buffer_helpers?
150
+ lines.concat(emit_utf8_validation_helpers)
151
151
  lines << ""
152
152
  end
153
153
  if uses_async_memory_helpers?
@@ -3,7 +3,19 @@
3
3
  require_relative "types/layout"
4
4
 
5
5
  module MilkTea
6
- module ConstEval
6
+ module CompileTime
7
+ Layout = ::MilkTea::Types::Layout
8
+
9
+ class ReturnValue < StandardError
10
+ attr_reader :value
11
+
12
+ def initialize(value)
13
+ @value = value
14
+ super("return #{value.inspect}")
15
+ end
16
+ end
17
+
18
+ class Error < StandardError; end
7
19
 
8
20
  def self.evaluate(expression, resolve_identifier:, resolve_member_access:, resolve_type_ref: nil, resolve_call: nil)
9
21
  Evaluator.new(
@@ -78,7 +90,7 @@ module MilkTea
78
90
  evaluate_binary(expression)
79
91
  when AST::IfExpr
80
92
  condition = evaluate(expression.condition)
81
- return unless ConstEval.boolean_value?(condition)
93
+ return unless CompileTime.boolean_value?(condition)
82
94
 
83
95
  evaluate(condition ? expression.then_expression : expression.else_expression)
84
96
  else
@@ -87,9 +99,24 @@ module MilkTea
87
99
  end
88
100
 
89
101
  def resolve_layout_type(type_ref)
90
- return unless @resolve_type_ref
102
+ result = begin
103
+ @resolve_type_ref&.call(type_ref)
104
+ rescue SemanticError
105
+ nil
106
+ end
107
+ return result if result
108
+
109
+ return unless type_ref.respond_to?(:name) && type_ref.name.parts.length >= 1
91
110
 
92
- @resolve_type_ref.call(type_ref)
111
+ expression = ::MilkTea::AST.build_chain_from_parts(type_ref.name.parts)
112
+ return unless expression
113
+
114
+ value = evaluate(expression)
115
+ return value if value.is_a?(Types::Struct) || value.is_a?(Types::Primitive) ||
116
+ value.is_a?(Types::Union) || value.is_a?(Types::Nullable) ||
117
+ value.is_a?(Types::StructInstance)
118
+
119
+ nil
93
120
  end
94
121
 
95
122
  def evaluate_unary(expression)
@@ -103,7 +130,7 @@ module MilkTea
103
130
  when "~"
104
131
  operand.is_a?(Integer) ? ~operand : nil
105
132
  when "not"
106
- ConstEval.boolean_value?(operand) ? !operand : nil
133
+ CompileTime.boolean_value?(operand) ? !operand : nil
107
134
  end
108
135
  end
109
136
 
@@ -112,19 +139,19 @@ module MilkTea
112
139
 
113
140
  case expression.operator
114
141
  when "and"
115
- return unless ConstEval.boolean_value?(left)
142
+ return unless CompileTime.boolean_value?(left)
116
143
  return false if left == false
117
144
 
118
145
  right = evaluate(expression.right)
119
- return right if ConstEval.boolean_value?(right)
146
+ return right if CompileTime.boolean_value?(right)
120
147
 
121
148
  return nil
122
149
  when "or"
123
- return unless ConstEval.boolean_value?(left)
150
+ return unless CompileTime.boolean_value?(left)
124
151
  return true if left == true
125
152
 
126
153
  right = evaluate(expression.right)
127
- return right if ConstEval.boolean_value?(right)
154
+ return right if CompileTime.boolean_value?(right)
128
155
 
129
156
  return nil
130
157
  end
@@ -133,9 +160,9 @@ module MilkTea
133
160
 
134
161
  case expression.operator
135
162
  when "=="
136
- ConstEval.equality_result(left, right)
163
+ CompileTime.equality_result(left, right)
137
164
  when "!="
138
- result = ConstEval.equality_result(left, right)
165
+ result = CompileTime.equality_result(left, right)
139
166
  result.nil? ? nil : !result
140
167
  when "+"
141
168
  left.is_a?(Numeric) && right.is_a?(Numeric) ? left + right : nil
@@ -145,12 +172,12 @@ module MilkTea
145
172
  left.is_a?(Numeric) && right.is_a?(Numeric) ? left * right : nil
146
173
  when "/"
147
174
  return unless left.is_a?(Numeric) && right.is_a?(Numeric)
148
- raise CompileTime::Error, "division by zero" if zero_numeric?(right)
175
+ raise Error, "division by zero" if zero_numeric?(right)
149
176
 
150
177
  left / right
151
178
  when "%"
152
179
  return unless left.is_a?(Integer) && right.is_a?(Integer)
153
- raise CompileTime::Error, "modulo by zero" if right.zero?
180
+ raise Error, "modulo by zero" if right.zero?
154
181
 
155
182
  left % right
156
183
  when "<<"
@@ -178,23 +205,6 @@ module MilkTea
178
205
  (value.is_a?(Integer) && value.zero?) || (value.is_a?(Float) && value.zero?)
179
206
  end
180
207
  end
181
- end
182
- end
183
-
184
- module MilkTea
185
- module CompileTime
186
- Layout = ::MilkTea::Layout
187
-
188
- class ReturnValue < StandardError
189
- attr_reader :value
190
-
191
- def initialize(value)
192
- @value = value
193
- super("return #{value.inspect}")
194
- end
195
- end
196
-
197
- class Error < StandardError; end
198
208
 
199
209
  class BlockContext
200
210
  attr_reader :checker
@@ -458,45 +468,6 @@ module MilkTea
458
468
  end
459
469
  end
460
470
 
461
- class Evaluator < ConstEval::Evaluator
462
-
463
- def resolve_layout_type(type_ref)
464
- result = begin
465
- super
466
- rescue SemanticError
467
- nil
468
- end
469
- return result if result
470
- return result if result
471
-
472
- return unless type_ref.respond_to?(:name) && type_ref.name.parts.length >= 1
473
-
474
- expression = ::MilkTea::AST.build_chain_from_parts(type_ref.name.parts)
475
- return unless expression
476
-
477
- value = evaluate(expression)
478
- return value if value.is_a?(Types::Struct) || value.is_a?(Types::Primitive) ||
479
- value.is_a?(Types::Union) || value.is_a?(Types::Nullable) ||
480
- value.is_a?(Types::StructInstance)
481
-
482
- nil
483
- end
484
-
485
- end
486
-
487
- def self.evaluate(expression, resolve_identifier:, resolve_member_access:, resolve_type_ref: nil, resolve_call: nil)
488
- Evaluator.new(
489
- resolve_identifier:,
490
- resolve_member_access:,
491
- resolve_type_ref:,
492
- resolve_call:,
493
- ).evaluate(expression)
494
- end
495
-
496
- def self.boolean_value?(value)
497
- ConstEval.boolean_value?(value)
498
- end
499
-
500
471
  module Reflection
501
472
  def self.core_field_handle(struct_handle, field_name)
502
473
  field_decl = struct_handle.declaration.fields.find { |f| f.name == field_name }
@@ -559,4 +530,4 @@ module MilkTea
559
530
  end
560
531
  end
561
532
  end
562
- end
533
+ end
@@ -118,7 +118,7 @@ module MilkTea
118
118
  )
119
119
  @graph.add_edge(cond_id, then_entry, label: :true_branch)
120
120
  @graph.add_edge(cond_id, fallback_entry, label: :false_branch)
121
- emit_null_test_refinements(cond_id, then_entry, fallback_entry, branch.condition)
121
+ add_null_test_refinements(cond_id, then_entry, fallback_entry, branch.condition)
122
122
  fallback_entry = cond_id
123
123
  end
124
124
  fallback_entry
@@ -177,7 +177,7 @@ module MilkTea
177
177
  body_entry = build_block(stmt.body, cond_id, break_target: next_id, continue_target: cond_id)
178
178
  @graph.add_edge(cond_id, body_entry, label: :true_branch)
179
179
  @graph.add_edge(cond_id, next_id, label: :false_branch)
180
- emit_null_test_refinements(cond_id, body_entry, next_id, stmt.condition)
180
+ add_null_test_refinements(cond_id, body_entry, next_id, stmt.condition)
181
181
  cond_id
182
182
  when AST::ForStmt
183
183
  writes = Set.new
@@ -582,7 +582,7 @@ module MilkTea
582
582
  name
583
583
  end
584
584
 
585
- def emit_null_test_refinements(cond_id, true_succ, false_succ, condition)
585
+ def add_null_test_refinements(cond_id, true_succ, false_succ, condition)
586
586
  pairs = null_check_pairs(condition, positive: true)
587
587
  return if pairs.empty?
588
588
 
@@ -105,7 +105,7 @@ module MilkTea
105
105
  def self.eval_expr_const(expr, state, binding_resolution:, strict_binding_ids:)
106
106
  return NAC if expr.nil?
107
107
 
108
- value = ConstEval.evaluate(
108
+ value = CompileTime.evaluate(
109
109
  expr,
110
110
  resolve_identifier: lambda do |identifier_expression|
111
111
  key = identifier_key(identifier_expression, binding_resolution:, strict_binding_ids:)
@@ -80,7 +80,7 @@ module MilkTea
80
80
  order
81
81
  end
82
82
 
83
- def read_bindings
83
+ def all_read_binding_keys
84
84
  keys = Set.new
85
85
  each_node { |node| keys.merge(node.reads) }
86
86
  keys
@@ -11,7 +11,7 @@ module MilkTea
11
11
  !reachability.reachable_ids.include?(graph.exit_id)
12
12
  end
13
13
 
14
- def self.loop_body_always_exits?(statements, **builder_options)
14
+ def self.loop_body_always_terminates?(statements, **builder_options)
15
15
  return false if statements.nil? || statements.empty?
16
16
 
17
17
  graph = Builder.new(**builder_options).build_loop_body(statements)
@@ -1,6 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MilkTea
4
+ class CSTBuilder
5
+ def self.build(source, path: nil)
6
+ new(source, path:).build
7
+ end
8
+
9
+ def initialize(source, path: nil)
10
+ @source = source
11
+ @path = path
12
+ end
13
+
14
+ def build
15
+ lexed = Lexer.lex_with_trivia(@source, path: @path)
16
+ CST::SourceFile.new(source: @source, tokens: lexed.tokens, trivia: lexed.trivia)
17
+ end
18
+ end
19
+
4
20
  module CST
5
21
  SourceFile = Data.define(:source, :tokens, :trivia) do
6
22
  def reconstruct
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ class FlowScope
5
+ def initialize = (@bindings = {})
6
+ def [](key) = @bindings[key]
7
+ def []=(key, val); @bindings[key] = val; end
8
+ def key?(key) = @bindings.key?(key)
9
+ def empty? = @bindings.empty?
10
+ def each(&block) = @bindings.each(&block)
11
+ def each_with_object(init, &block) = @bindings.each_with_object(init, &block)
12
+ end
13
+ end
@@ -21,7 +21,7 @@ module MilkTea
21
21
  else nil
22
22
  end
23
23
 
24
- @tokens << token(type, lexeme, literal, line_number, start + 1, start_offset: line_offset + start, end_offset: line_offset + index)
24
+ @tokens << build_token(type, lexeme, literal, line_number, start + 1, start_offset: line_offset + start, end_offset: line_offset + index)
25
25
  index
26
26
  end
27
27
 
@@ -41,7 +41,7 @@ module MilkTea
41
41
  byte ? IDENT_PART_BYTE[byte] : false
42
42
  end
43
43
 
44
- def identifier_start_token(line, start_index)
44
+ def extract_identifier_at(line, start_index)
45
45
  return "" unless start_index < line.length && identifier_start?(line[start_index])
46
46
 
47
47
  finish = start_index + 1
@@ -5,7 +5,7 @@ module MilkTea
5
5
  # Format string lexing (`f"...#{expr}..."`) and the interpolation
6
6
  # scanning / format-spec splitting shared with format heredocs.
7
7
  module FormatStrings
8
- def parse_format_heredoc_parts(content, start_line:, start_column:)
8
+ def split_format_heredoc_parts(content, start_line:, start_column:)
9
9
  parts = []
10
10
  text = +""
11
11
  index = 0
@@ -66,7 +66,7 @@ module MilkTea
66
66
  if char == '"'
67
67
  parts << { kind: :text, value: text } unless text.empty?
68
68
  lexeme = line[start..index]
69
- @tokens << token(:fstring, lexeme, parts, line_number, start + 1, start_offset: line_offset + start, end_offset: line_offset + index + 1)
69
+ @tokens << build_token(:fstring, lexeme, parts, line_number, start + 1, start_offset: line_offset + start, end_offset: line_offset + index + 1)
70
70
  return index + 1
71
71
  end
72
72
 
@@ -103,7 +103,7 @@ module MilkTea
103
103
  @recovery_errors << LexError.new("unterminated format string literal", line: line_number, column: start + 1, path: @path)
104
104
  parts << { kind: :text, value: text } unless text.empty?
105
105
  lexeme = line[start..]
106
- @tokens << token(:fstring, lexeme, parts, line_number, start + 1, start_offset: line_offset + start, end_offset: line_offset + line.length)
106
+ @tokens << build_token(:fstring, lexeme, parts, line_number, start + 1, start_offset: line_offset + start, end_offset: line_offset + line.length)
107
107
  return line.length
108
108
  end
109
109
 
@@ -117,7 +117,7 @@ module MilkTea
117
117
  char = line[index]
118
118
 
119
119
  if char == '"'
120
- index = skip_string_contents(line, index, line_number)
120
+ index = skip_nested_string_literal(line, index, line_number)
121
121
  next
122
122
  end
123
123
 
@@ -146,7 +146,7 @@ module MilkTea
146
146
  raise LexError.new("unterminated format interpolation", line: line_number, column:, path: @path)
147
147
  end
148
148
 
149
- def skip_string_contents(line, index, line_number)
149
+ def skip_nested_string_literal(line, index, line_number)
150
150
  index += 1
151
151
 
152
152
  while index < line.length
@@ -89,7 +89,7 @@ module MilkTea
89
89
  content_margin = heredoc_content_margin(content_lines)
90
90
 
91
91
  literal = if format
92
- parse_format_heredoc_parts(value, start_line: line_number + 1, start_column: content_margin + 1)
92
+ split_format_heredoc_parts(value, start_line: line_number + 1, start_column: content_margin + 1)
93
93
  else
94
94
  value
95
95
  end
@@ -102,8 +102,8 @@ module MilkTea
102
102
  :string
103
103
  end
104
104
 
105
- @tokens << token(token_type, lexeme, literal, line_number, index + 1, start_offset:, end_offset:)
106
- emit_line_newline(last_line, last_line_number, last_line_offset, last_line_has_newline)
105
+ @tokens << build_token(token_type, lexeme, literal, line_number, index + 1, start_offset:, end_offset:)
106
+ emit_newline(last_line, last_line_number, last_line_offset, last_line_has_newline)
107
107
  return resync_line_number ? (resync_line_number - line_number) : (scan_line_index - line_index)
108
108
  end
109
109
 
@@ -117,7 +117,7 @@ module MilkTea
117
117
  content_margin = heredoc_content_margin(content_lines)
118
118
 
119
119
  literal = if format
120
- parse_format_heredoc_parts(value, start_line: line_number + 1, start_column: content_margin + 1)
120
+ split_format_heredoc_parts(value, start_line: line_number + 1, start_column: content_margin + 1)
121
121
  else
122
122
  value
123
123
  end
@@ -130,8 +130,8 @@ module MilkTea
130
130
  :string
131
131
  end
132
132
 
133
- @tokens << token(token_type, lexeme, literal, line_number, index + 1, start_offset:, end_offset:)
134
- emit_line_newline(terminator_line, terminator_line_number, terminator_line_offset, terminator_has_newline)
133
+ @tokens << build_token(token_type, lexeme, literal, line_number, index + 1, start_offset:, end_offset:)
134
+ emit_newline(terminator_line, terminator_line_number, terminator_line_offset, terminator_has_newline)
135
135
 
136
136
  (scan_line_index - line_index) + 1
137
137
  end
@@ -20,13 +20,13 @@ module MilkTea
20
20
  end
21
21
 
22
22
  @indent_stack << indent
23
- @tokens << token(:indent, "", nil, line_number, 1, start_offset: line_offset, end_offset: line_offset)
23
+ @tokens << build_token(:indent, "", nil, line_number, 1, start_offset: line_offset, end_offset: line_offset)
24
24
  return
25
25
  end
26
26
 
27
27
  while @indent_stack.last > indent
28
28
  @indent_stack.pop
29
- @tokens << token(:dedent, "", nil, line_number, 1, start_offset: line_offset, end_offset: line_offset)
29
+ @tokens << build_token(:dedent, "", nil, line_number, 1, start_offset: line_offset, end_offset: line_offset)
30
30
  end
31
31
 
32
32
  return if @indent_stack.last == indent
@@ -44,13 +44,13 @@ module MilkTea
44
44
 
45
45
  if recovered_indent > current_indent
46
46
  @indent_stack << recovered_indent
47
- @tokens << token(:indent, "", nil, line_number, 1, start_offset: line_offset, end_offset: line_offset)
47
+ @tokens << build_token(:indent, "", nil, line_number, 1, start_offset: line_offset, end_offset: line_offset)
48
48
  return
49
49
  end
50
50
 
51
51
  while @indent_stack.last > recovered_indent
52
52
  @indent_stack.pop
53
- @tokens << token(:dedent, "", nil, line_number, 1, start_offset: line_offset, end_offset: line_offset)
53
+ @tokens << build_token(:dedent, "", nil, line_number, 1, start_offset: line_offset, end_offset: line_offset)
54
54
  end
55
55
 
56
56
  return if @indent_stack.last == recovered_indent
@@ -58,7 +58,7 @@ module MilkTea
58
58
  normalized = lexeme.delete("_").delete_suffix("f").delete_suffix("d")
59
59
  literal = normalized.to_f
60
60
  end
61
- @tokens << token(type, lexeme, literal, line_number, start + 1, start_offset: line_offset + start, end_offset: line_offset + index)
61
+ @tokens << build_token(type, lexeme, literal, line_number, start + 1, start_offset: line_offset + start, end_offset: line_offset + index)
62
62
  index
63
63
  end
64
64