mt-lang 0.3.26 → 0.3.27
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/docs/self-host-contract.md +1091 -0
- data/docs/self-host-plan.md +768 -0
- data/docs/self-host-progress.md +130 -0
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/tooling/linter/visitors.rb +156 -3
- metadata +5 -2
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Self-Host Progress
|
|
2
|
+
|
|
3
|
+
Tracks implementation status against `docs/self-host-contract.md` §11.
|
|
4
|
+
|
|
5
|
+
Status: `[ ]` not started · `[~]` in progress · `[x]` done · `[-]` blocked
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Phase 0: Foundation
|
|
10
|
+
|
|
11
|
+
| Step | Status | Module | Notes |
|
|
12
|
+
|------|--------|--------|-------|
|
|
13
|
+
| 1 | [ ] | `mtc.lexer.token_kinds` | |
|
|
14
|
+
| 2 | [ ] | `mtc.lexer.token` | |
|
|
15
|
+
| 3 | [ ] | `mtc.lexer.keywords` | |
|
|
16
|
+
| 4 | [ ] | `mtc.ast` | |
|
|
17
|
+
| 5 | [ ] | `mtc.semantic.types` | |
|
|
18
|
+
| 6 | [ ] | `mtc.ir` | |
|
|
19
|
+
| 7 | [ ] | `mtc.sexpr_dumper` | |
|
|
20
|
+
|
|
21
|
+
## Phase 1: Lexer
|
|
22
|
+
|
|
23
|
+
| Step | Status | Module | Notes |
|
|
24
|
+
|------|--------|--------|-------|
|
|
25
|
+
| 8 | [ ] | `mtc.lexer.lexer` | |
|
|
26
|
+
|
|
27
|
+
## Phase 2: Parser
|
|
28
|
+
|
|
29
|
+
| Step | Status | Module | Notes |
|
|
30
|
+
|------|--------|--------|-------|
|
|
31
|
+
| 9 | [ ] | `mtc.parser.state` | |
|
|
32
|
+
| 10 | [ ] | `mtc.parser.parser` | |
|
|
33
|
+
|
|
34
|
+
## Phase 3: Path Resolver
|
|
35
|
+
|
|
36
|
+
| Step | Status | Module | Notes |
|
|
37
|
+
|------|--------|--------|-------|
|
|
38
|
+
| 11 | [ ] | `mtc.loader.path_resolver` | |
|
|
39
|
+
|
|
40
|
+
## Phase 4: Semantic Analyzer
|
|
41
|
+
|
|
42
|
+
| Step | Status | Module | Notes |
|
|
43
|
+
|------|--------|--------|-------|
|
|
44
|
+
| 12 | [ ] | `mtc.semantic.scope` | |
|
|
45
|
+
| 13 | [ ] | `mtc.semantic.intrinsics` | |
|
|
46
|
+
| 14 | [ ] | `mtc.semantic.compile_time` | |
|
|
47
|
+
| 15 | [ ] | `mtc.semantic.type_compatibility` | |
|
|
48
|
+
| 16 | [ ] | `mtc.semantic.expressions` | |
|
|
49
|
+
| 17 | [ ] | `mtc.semantic.diagnostics` | |
|
|
50
|
+
| 18 | [ ] | `mtc.semantic.analyzer` | |
|
|
51
|
+
|
|
52
|
+
## Phase 4a: Control Flow
|
|
53
|
+
|
|
54
|
+
| Step | Status | Module | Notes |
|
|
55
|
+
|------|--------|--------|-------|
|
|
56
|
+
| 19 | [ ] | `mtc.semantic.control_flow.*` | |
|
|
57
|
+
|
|
58
|
+
## Phase 4b: Module Binder
|
|
59
|
+
|
|
60
|
+
| Step | Status | Module | Notes |
|
|
61
|
+
|------|--------|--------|-------|
|
|
62
|
+
| 20 | [ ] | `mtc.loader.binder` | |
|
|
63
|
+
|
|
64
|
+
## Phase 4c: Module Loader
|
|
65
|
+
|
|
66
|
+
| Step | Status | Module | Notes |
|
|
67
|
+
|------|--------|--------|-------|
|
|
68
|
+
| 21 | [ ] | `mtc.loader.module_loader` | |
|
|
69
|
+
|
|
70
|
+
## Phase 5: Lowering
|
|
71
|
+
|
|
72
|
+
| Step | Status | Module | Notes |
|
|
73
|
+
|------|--------|--------|-------|
|
|
74
|
+
| 22 | [ ] | `mtc.lowering.utils` | |
|
|
75
|
+
| 23 | [ ] | `mtc.lowering.lowering` | |
|
|
76
|
+
| 24 | [ ] | `mtc.lowering.async` | |
|
|
77
|
+
|
|
78
|
+
## Phase 6: C Backend
|
|
79
|
+
|
|
80
|
+
| Step | Status | Module | Notes |
|
|
81
|
+
|------|--------|--------|-------|
|
|
82
|
+
| 25 | [ ] | `mtc.c_backend` | |
|
|
83
|
+
|
|
84
|
+
## Phase 6a: Pretty Printer
|
|
85
|
+
|
|
86
|
+
| Step | Status | Module | Notes |
|
|
87
|
+
|------|--------|--------|-------|
|
|
88
|
+
| 26 | [ ] | `mtc.pretty_printer` | |
|
|
89
|
+
|
|
90
|
+
## Phase 7: Build & CLI
|
|
91
|
+
|
|
92
|
+
| Step | Status | Module | Notes |
|
|
93
|
+
|------|--------|--------|-------|
|
|
94
|
+
| 27 | [ ] | `mtc.build` | |
|
|
95
|
+
| 28 | [ ] | `mtc.build_cache` | |
|
|
96
|
+
| 29 | [ ] | `mtc.main` | |
|
|
97
|
+
|
|
98
|
+
## Phase 8: Bootstrap
|
|
99
|
+
|
|
100
|
+
| Step | Status | Check | Notes |
|
|
101
|
+
|------|--------|-------|-------|
|
|
102
|
+
| 8a | [ ] | Stage2 builds | |
|
|
103
|
+
| 8b | [ ] | `diff stage2.c stage3.c` empty | |
|
|
104
|
+
| 8c | [ ] | Stage2 self-builds to working binary | |
|
|
105
|
+
| 8d | [ ] | Test suite passes under stage2 | |
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Summary
|
|
110
|
+
|
|
111
|
+
| Metric | Count |
|
|
112
|
+
|--------|------:|
|
|
113
|
+
| Total steps | 29 |
|
|
114
|
+
| Done | 0 |
|
|
115
|
+
| In progress | 0 |
|
|
116
|
+
| Blocked | 0 |
|
|
117
|
+
| Remaining | 29 |
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Post-Fixed-Point
|
|
122
|
+
|
|
123
|
+
| Subsystem | Status | Notes |
|
|
124
|
+
|-----------|--------|-------|
|
|
125
|
+
| LSP server | [ ] | Deferred |
|
|
126
|
+
| DAP server | [ ] | Deferred |
|
|
127
|
+
| Bindgen | [ ] | Deferred |
|
|
128
|
+
| Imported bindings | [ ] | Deferred |
|
|
129
|
+
| Linter | [ ] | Deferred |
|
|
130
|
+
| Packages | [ ] | Deferred |
|
data/lib/milk_tea/base.rb
CHANGED
|
@@ -164,7 +164,10 @@ module MilkTea
|
|
|
164
164
|
end
|
|
165
165
|
end
|
|
166
166
|
check_redundant_type_annotation(statement)
|
|
167
|
-
|
|
167
|
+
if statement.type && statement.value
|
|
168
|
+
flag_redundant_widening_cast(statement.value)
|
|
169
|
+
flag_redundant_literal_cast(statement.value)
|
|
170
|
+
end
|
|
168
171
|
record_ptr_candidate(statement)
|
|
169
172
|
when AST::Assignment
|
|
170
173
|
visit_expression(statement.value) # visit RHS first — reads in RHS count against dead-assignment
|
|
@@ -173,7 +176,10 @@ module MilkTea
|
|
|
173
176
|
mark_mutated(statement.target)
|
|
174
177
|
check_self_assignment(statement)
|
|
175
178
|
check_noop_compound_assignment(statement)
|
|
176
|
-
|
|
179
|
+
if statement.operator == "="
|
|
180
|
+
flag_redundant_widening_cast(statement.value)
|
|
181
|
+
flag_redundant_literal_cast(statement.value)
|
|
182
|
+
end
|
|
177
183
|
when AST::IfStmt
|
|
178
184
|
statement.branches.each do |branch|
|
|
179
185
|
visit_expression(branch.condition)
|
|
@@ -223,7 +229,10 @@ module MilkTea
|
|
|
223
229
|
@loop_depth -= 1
|
|
224
230
|
when AST::ReturnStmt
|
|
225
231
|
visit_expression(statement.value) if statement.value
|
|
226
|
-
|
|
232
|
+
if statement.value
|
|
233
|
+
flag_redundant_widening_cast(statement.value)
|
|
234
|
+
flag_redundant_literal_cast(statement.value)
|
|
235
|
+
end
|
|
227
236
|
when AST::DeferStmt
|
|
228
237
|
with_scope { visit_statement_list(statement.body) }
|
|
229
238
|
when AST::ExpressionStmt
|
|
@@ -278,6 +287,8 @@ module MilkTea
|
|
|
278
287
|
visit_expression(expression.right)
|
|
279
288
|
check_self_comparison(expression)
|
|
280
289
|
check_redundant_bool_compare(expression)
|
|
290
|
+
check_redundant_binary_operand_cast(expression)
|
|
291
|
+
check_redundant_literal_operand_cast(expression)
|
|
281
292
|
when AST::RangeExpr
|
|
282
293
|
visit_expression(expression.start_expr)
|
|
283
294
|
visit_expression(expression.end_expr)
|
|
@@ -1141,6 +1152,140 @@ module MilkTea
|
|
|
1141
1152
|
emit_redundant_cast(value, target_name, "implicit widening makes this cast redundant")
|
|
1142
1153
|
end
|
|
1143
1154
|
|
|
1155
|
+
INTEGER_TYPE_RANGES = {
|
|
1156
|
+
"byte" => (-128..127),
|
|
1157
|
+
"short" => (-32_768..32_767),
|
|
1158
|
+
"int" => (-(2**31)..2**31 - 1),
|
|
1159
|
+
"long" => (-(2**63)..2**63 - 1),
|
|
1160
|
+
"ubyte" => (0..255),
|
|
1161
|
+
"ushort" => (0..65_535),
|
|
1162
|
+
"uint" => (0..2**32 - 1),
|
|
1163
|
+
"ulong" => (0..2**64 - 1),
|
|
1164
|
+
"char" => (0..255),
|
|
1165
|
+
}.freeze
|
|
1166
|
+
|
|
1167
|
+
# Reports an integer-literal cast at a coercion slot as redundant when the
|
|
1168
|
+
# literal would implicitly coerce to the cast target: the compiler accepts
|
|
1169
|
+
# a fitting integer literal for any integer slot (`buf[0] = 1` for a
|
|
1170
|
+
# `ubyte` element), so `ubyte<-1` adds nothing. Out-of-range literals are
|
|
1171
|
+
# left alone because removing the cast would then be a type error.
|
|
1172
|
+
# Widening literal casts are already reported by flag_redundant_widening_cast.
|
|
1173
|
+
def flag_redundant_literal_cast(value)
|
|
1174
|
+
return unless value.is_a?(AST::PrefixCast)
|
|
1175
|
+
return unless @sema_facts
|
|
1176
|
+
|
|
1177
|
+
target_name = type_ref_name(value.target_type)
|
|
1178
|
+
return unless target_name
|
|
1179
|
+
range = INTEGER_TYPE_RANGES[target_name]
|
|
1180
|
+
return unless range
|
|
1181
|
+
|
|
1182
|
+
inner = value.expression
|
|
1183
|
+
return unless inner.is_a?(AST::IntegerLiteral)
|
|
1184
|
+
literal = inner.value
|
|
1185
|
+
return unless literal.is_a?(Integer)
|
|
1186
|
+
return unless range.cover?(literal)
|
|
1187
|
+
|
|
1188
|
+
inner_type = resolve_expr_type(inner)
|
|
1189
|
+
return unless inner_type.is_a?(Types::Primitive)
|
|
1190
|
+
return if inner_type.name == target_name # same-type handled elsewhere
|
|
1191
|
+
return if implicit_cast_allowed?(inner_type, target_name) # widening handled elsewhere
|
|
1192
|
+
|
|
1193
|
+
emit_redundant_cast(value, target_name, "integer literal coercion makes this cast redundant")
|
|
1194
|
+
end
|
|
1195
|
+
|
|
1196
|
+
BINARY_CAST_SAFE_OPS = %w[+ - * / % == != < <= > >=].freeze
|
|
1197
|
+
|
|
1198
|
+
# Reports a widening cast used directly as an operand of an arithmetic or
|
|
1199
|
+
# comparison operator when removing it provably leaves the operation's
|
|
1200
|
+
# result unchanged. An integer->float cast inside a float-family
|
|
1201
|
+
# expression promotes to the sibling's float type either way, so the cast
|
|
1202
|
+
# is redundant. Excluded:
|
|
1203
|
+
# * shifts (`<<`/`>>`) — the shifted operand's width is load-bearing;
|
|
1204
|
+
# * `double` in a `float` context — `float_y + double<-i` is `double`
|
|
1205
|
+
# with the cast but `float` without it;
|
|
1206
|
+
# * cast-chains such as `float<-x + float<-y` — each removal is only
|
|
1207
|
+
# safe if the sibling cast stays, so neither is independently
|
|
1208
|
+
# redundant.
|
|
1209
|
+
def check_redundant_binary_operand_cast(binary)
|
|
1210
|
+
return unless @sema_facts
|
|
1211
|
+
return unless BINARY_CAST_SAFE_OPS.include?(binary.operator)
|
|
1212
|
+
|
|
1213
|
+
check_binary_operand_cast(binary.left, binary.right)
|
|
1214
|
+
check_binary_operand_cast(binary.right, binary.left)
|
|
1215
|
+
end
|
|
1216
|
+
|
|
1217
|
+
def check_binary_operand_cast(operand, sibling)
|
|
1218
|
+
return unless operand.is_a?(AST::PrefixCast)
|
|
1219
|
+
return if sibling.is_a?(AST::PrefixCast)
|
|
1220
|
+
|
|
1221
|
+
target_type = resolve_expr_type(operand)
|
|
1222
|
+
return unless target_type.is_a?(Types::Primitive) && target_type.float?
|
|
1223
|
+
|
|
1224
|
+
inner_type = resolve_expr_type(operand.expression)
|
|
1225
|
+
return unless inner_type.is_a?(Types::Primitive) && inner_type.fixed_width_integer?
|
|
1226
|
+
|
|
1227
|
+
sibling_type = resolve_expr_type(sibling)
|
|
1228
|
+
return unless sibling_type.is_a?(Types::Primitive) && sibling_type.float?
|
|
1229
|
+
return if target_type.name == "double" && sibling_type.name == "float"
|
|
1230
|
+
|
|
1231
|
+
target_name = type_ref_name(operand.target_type)
|
|
1232
|
+
return unless target_name
|
|
1233
|
+
|
|
1234
|
+
emit_redundant_cast(operand, target_name, "implicit float widening makes this cast redundant")
|
|
1235
|
+
end
|
|
1236
|
+
|
|
1237
|
+
COMPARISON_CAST_OPS = %w[== != < <= > >=].freeze
|
|
1238
|
+
|
|
1239
|
+
# Reports an integer-literal cast used directly as a comparison operand
|
|
1240
|
+
# (e.g. `State.idle == ubyte<-0`). The literal coerces to the sibling's
|
|
1241
|
+
# type either way, so `D<-lit` adds nothing when the literal fits `D` and
|
|
1242
|
+
# the sibling is a float-family value, an integer the literal fits in, or
|
|
1243
|
+
# an enum (which compares against any integer literal). Out-of-range
|
|
1244
|
+
# literals are left alone: `ubyte<-300` wraps in `D`, which is not the
|
|
1245
|
+
# same value as the bare literal.
|
|
1246
|
+
def check_redundant_literal_operand_cast(binary)
|
|
1247
|
+
return unless @sema_facts
|
|
1248
|
+
return unless COMPARISON_CAST_OPS.include?(binary.operator)
|
|
1249
|
+
|
|
1250
|
+
check_comparison_literal_operand(binary.left, binary.right)
|
|
1251
|
+
check_comparison_literal_operand(binary.right, binary.left)
|
|
1252
|
+
end
|
|
1253
|
+
|
|
1254
|
+
def check_comparison_literal_operand(operand, sibling)
|
|
1255
|
+
return unless operand.is_a?(AST::PrefixCast)
|
|
1256
|
+
|
|
1257
|
+
target_name = type_ref_name(operand.target_type)
|
|
1258
|
+
return unless target_name
|
|
1259
|
+
target_range = INTEGER_TYPE_RANGES[target_name]
|
|
1260
|
+
return unless target_range
|
|
1261
|
+
|
|
1262
|
+
inner = operand.expression
|
|
1263
|
+
return unless inner.is_a?(AST::IntegerLiteral)
|
|
1264
|
+
literal = inner.value
|
|
1265
|
+
return unless literal.is_a?(Integer)
|
|
1266
|
+
return unless target_range.cover?(literal)
|
|
1267
|
+
|
|
1268
|
+
# Same-type casts are reported by check_redundant_cast.
|
|
1269
|
+
inner_type = resolve_expr_type(inner)
|
|
1270
|
+
return if inner_type.is_a?(Types::Primitive) && inner_type.name == target_name
|
|
1271
|
+
|
|
1272
|
+
sibling_type = resolve_expr_type(sibling)
|
|
1273
|
+
case sibling_type
|
|
1274
|
+
when Types::Primitive
|
|
1275
|
+
return unless sibling_type.float? || sibling_type.integer?
|
|
1276
|
+
if sibling_type.integer?
|
|
1277
|
+
sibling_range = INTEGER_TYPE_RANGES[sibling_type.name]
|
|
1278
|
+
return unless sibling_range && sibling_range.cover?(literal)
|
|
1279
|
+
end
|
|
1280
|
+
when Types::EnumBase
|
|
1281
|
+
nil
|
|
1282
|
+
else
|
|
1283
|
+
return
|
|
1284
|
+
end
|
|
1285
|
+
|
|
1286
|
+
emit_redundant_cast(operand, target_name, "integer literal coercion makes this cast redundant")
|
|
1287
|
+
end
|
|
1288
|
+
|
|
1144
1289
|
def same_resolved_type?(left, right)
|
|
1145
1290
|
return true if left.equal?(right)
|
|
1146
1291
|
|
|
@@ -1177,6 +1322,14 @@ module MilkTea
|
|
|
1177
1322
|
target = find_primitive_type(target_name)
|
|
1178
1323
|
return false unless target
|
|
1179
1324
|
return false unless target.is_a?(Types::Primitive)
|
|
1325
|
+
|
|
1326
|
+
# Fixed-width integers widen implicitly to float/double (matching the
|
|
1327
|
+
# compiler's promotion, e.g. `float + int` or `return int` from a
|
|
1328
|
+
# `-> float` function). `char`/`bool` are not fixed-width integers and
|
|
1329
|
+
# have no implicit conversion to the float family; float->double is not
|
|
1330
|
+
# implicit either.
|
|
1331
|
+
return true if target.float? && inner_type.fixed_width_integer?
|
|
1332
|
+
|
|
1180
1333
|
return false unless inner_type.fixed_width_integer? && target.fixed_width_integer?
|
|
1181
1334
|
|
|
1182
1335
|
if inner_type.signed_integer? == target.signed_integer?
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mt-lang
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.27
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Long (Teefan) Tran
|
|
@@ -149,6 +149,9 @@ files:
|
|
|
149
149
|
- docs/index.html
|
|
150
150
|
- docs/language-design.md
|
|
151
151
|
- docs/language-manual.md
|
|
152
|
+
- docs/self-host-contract.md
|
|
153
|
+
- docs/self-host-plan.md
|
|
154
|
+
- docs/self-host-progress.md
|
|
152
155
|
- lib/milk_tea.rb
|
|
153
156
|
- lib/milk_tea/base.rb
|
|
154
157
|
- lib/milk_tea/bindings.rb
|
|
@@ -624,7 +627,7 @@ metadata:
|
|
|
624
627
|
homepage_uri: https://teefan.github.io/mt-lang/
|
|
625
628
|
source_code_uri: https://github.com/teefan/mt-lang
|
|
626
629
|
post_install_message: |
|
|
627
|
-
Milk Tea 0.3.
|
|
630
|
+
Milk Tea 0.3.27 installed!
|
|
628
631
|
|
|
629
632
|
System requirements:
|
|
630
633
|
- A C compiler (gcc or clang) must be available on PATH
|