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.
- checksums.yaml +4 -4
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/core/bindings/attribute_binding.rb +51 -49
- data/lib/milk_tea/core/bindings/function_binding.rb +3 -1
- data/lib/milk_tea/core/bindings/module_binding.rb +42 -40
- data/lib/milk_tea/core/bindings/value_binding.rb +16 -14
- data/lib/milk_tea/core/bindings.rb +4 -9
- data/lib/milk_tea/core/c_backend/control_flow_emission.rb +11 -11
- data/lib/milk_tea/core/c_backend/expressions.rb +11 -11
- data/lib/milk_tea/core/c_backend/feature_detection.rb +15 -19
- data/lib/milk_tea/core/c_backend/format_helpers.rb +1 -1
- data/lib/milk_tea/core/c_backend/reinterpret.rb +10 -10
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1 -1
- data/lib/milk_tea/core/c_backend/statements.rb +20 -20
- data/lib/milk_tea/core/c_backend/type_declaration.rb +4 -4
- data/lib/milk_tea/core/c_backend/type_system.rb +2 -1
- data/lib/milk_tea/core/c_backend.rb +3 -3
- data/lib/milk_tea/core/compile_time.rb +41 -70
- data/lib/milk_tea/core/control_flow/builder.rb +3 -3
- data/lib/milk_tea/core/control_flow/constant_propagation.rb +1 -1
- data/lib/milk_tea/core/control_flow/graph.rb +1 -1
- data/lib/milk_tea/core/control_flow/termination.rb +1 -1
- data/lib/milk_tea/core/cst.rb +16 -0
- data/lib/milk_tea/core/flow_scope.rb +13 -0
- data/lib/milk_tea/core/lexer/character_classes.rb +2 -2
- data/lib/milk_tea/core/lexer/format_strings.rb +5 -5
- data/lib/milk_tea/core/lexer/heredocs.rb +6 -6
- data/lib/milk_tea/core/lexer/indentation.rb +4 -4
- data/lib/milk_tea/core/lexer/numbers.rb +1 -1
- data/lib/milk_tea/core/lexer/strings.rb +4 -4
- data/lib/milk_tea/core/lexer/symbols.rb +3 -3
- data/lib/milk_tea/core/lexer/trivia.rb +4 -4
- data/lib/milk_tea/core/lexer.rb +17 -17
- data/lib/milk_tea/core/lowering/async/analysis.rb +210 -208
- data/lib/milk_tea/core/lowering/async/async_lowering.rb +1406 -0
- data/lib/milk_tea/core/lowering/async/frame_builder.rb +615 -613
- data/lib/milk_tea/core/lowering/async/normalization.rb +410 -408
- data/lib/milk_tea/core/lowering/block.rb +982 -980
- data/lib/milk_tea/core/lowering/calls.rb +1360 -1358
- data/lib/milk_tea/core/lowering/declarations.rb +175 -173
- data/lib/milk_tea/core/lowering/dyn.rb +171 -169
- data/lib/milk_tea/core/lowering/events.rb +946 -944
- data/lib/milk_tea/core/lowering/expressions.rb +1514 -1512
- data/lib/milk_tea/core/lowering/foreign_cstr.rb +180 -178
- data/lib/milk_tea/core/lowering/functions.rb +186 -184
- data/lib/milk_tea/core/lowering/loops.rb +1004 -1002
- data/lib/milk_tea/core/lowering/lowering_context.rb +1 -1
- data/lib/milk_tea/core/lowering/proc.rb +355 -353
- data/lib/milk_tea/core/lowering/resolve.rb +2127 -2125
- data/lib/milk_tea/core/lowering/scans.rb +162 -168
- data/lib/milk_tea/core/lowering/str_buffer.rb +104 -102
- data/lib/milk_tea/core/lowering/utils.rb +965 -963
- data/lib/milk_tea/core/lowering.rb +22 -22
- data/lib/milk_tea/core/module_binder.rb +1 -1
- data/lib/milk_tea/core/module_loader.rb +2 -2
- data/lib/milk_tea/core/parser/attributes.rb +1 -1
- data/lib/milk_tea/core/parser/blocks.rb +2 -2
- data/lib/milk_tea/core/parser/declarations.rb +15 -15
- data/lib/milk_tea/core/parser/expressions.rb +20 -20
- data/lib/milk_tea/core/parser/recovery.rb +1 -1
- data/lib/milk_tea/core/parser/statements.rb +3 -3
- data/lib/milk_tea/core/parser/types.rb +4 -4
- data/lib/milk_tea/core/parser.rb +39 -21
- data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +6 -6
- data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +5 -5
- data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +2 -2
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +1 -1
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +4 -4
- data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +37 -37
- data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +4 -4
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +4 -4
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +4 -4
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +7 -7
- data/lib/milk_tea/core/semantic_analyzer/nullability.rb +59 -59
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +3 -3
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +1 -1
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +12 -12
- data/lib/milk_tea/core/types/layout.rb +3 -1
- data/lib/milk_tea/core/types/visitor.rb +351 -349
- data/lib/milk_tea/core.rb +0 -2
- data/lib/milk_tea/tooling/linter/flow_rules.rb +3 -3
- metadata +4 -5
- data/lib/milk_tea/core/cst_builder.rb +0 -19
- data/lib/milk_tea/core/lowering/async/lowering.rb +0 -1404
- data/lib/milk_tea/core/token_stream.rb +0 -30
|
@@ -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 =
|
|
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
|
|
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 && !
|
|
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 && !
|
|
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 (#{
|
|
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 (#{
|
|
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
|
|
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
|
|
187
|
-
transformed = statements.map { |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
|
|
229
|
+
def fold_nested_redundant_statement_temps(statement)
|
|
230
230
|
case statement
|
|
231
231
|
when IR::BlockStmt
|
|
232
|
-
IR::BlockStmt.new(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:
|
|
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:
|
|
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:
|
|
248
|
-
else_body: statement.else_body ?
|
|
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:
|
|
256
|
+
IR::SwitchDefaultCase.new(body: fold_redundant_statement_temps(switch_case.body))
|
|
257
257
|
else
|
|
258
|
-
IR::SwitchCase.new(value: switch_case.value, 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
|
|
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
|
|
527
|
+
def statements_require_scope?(statements)
|
|
528
528
|
statements.any? { |statement| statement.is_a?(IR::LocalDecl) }
|
|
529
529
|
end
|
|
530
530
|
|
|
531
|
-
def
|
|
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(
|
|
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 =
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
150
|
-
lines.concat(
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
146
|
+
return right if CompileTime.boolean_value?(right)
|
|
120
147
|
|
|
121
148
|
return nil
|
|
122
149
|
when "or"
|
|
123
|
-
return unless
|
|
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
|
|
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
|
-
|
|
163
|
+
CompileTime.equality_result(left, right)
|
|
137
164
|
when "!="
|
|
138
|
-
result =
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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:)
|
|
@@ -11,7 +11,7 @@ module MilkTea
|
|
|
11
11
|
!reachability.reachable_ids.include?(graph.exit_id)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def self.
|
|
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)
|
data/lib/milk_tea/core/cst.rb
CHANGED
|
@@ -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 <<
|
|
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
|
|
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
|
|
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 <<
|
|
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 <<
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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 <<
|
|
106
|
-
|
|
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
|
-
|
|
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 <<
|
|
134
|
-
|
|
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 <<
|
|
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 <<
|
|
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 <<
|
|
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 <<
|
|
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 <<
|
|
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
|
|