mt-lang 0.3.10 → 0.3.12
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/README.md +30 -8
- data/docs/index.html +38 -12
- data/docs/language-design.md +2 -2
- data/docs/language-manual.md +24 -2
- data/lib/milk_tea/base.rb +1 -1
- data/lib/milk_tea/core/ast.rb +6 -6
- data/lib/milk_tea/core/lexer.rb +2 -10
- data/lib/milk_tea/core/lowering/async/normalization.rb +5 -1
- data/lib/milk_tea/core/parser/declarations.rb +7 -7
- data/lib/milk_tea/core/parser/expressions.rb +9 -3
- data/lib/milk_tea/core/parser/statements.rb +19 -0
- data/lib/milk_tea/core/parser/types.rb +4 -4
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +2 -2
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +36 -5
- data/lib/milk_tea/core/token.rb +17 -0
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +64 -0
- data/lib/milk_tea/tooling/cli.rb +10 -4
- data/lib/milk_tea/tooling/linter/reserved_names.rb +1 -1
- data/lib/milk_tea/tooling/linter/visitors.rb +13 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bc80c3a3240cbcf4770dd067aa33559e74e3f139322b8fdb757316f3c7faf8af
|
|
4
|
+
data.tar.gz: a623e5ecaa9617aa161a83e96c4cb0e7a75b08855edae27fb56c8ddc47c7cd75
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c90aa51a21411fdcfe612f2ded69afdde6e055a012a4e886f5aa9ac69110d39a8bf93a36dde0543d1375a8bfa75ffd2f287eb082833a28b42efe19ab45c62ae6
|
|
7
|
+
data.tar.gz: a87ac20f1228f059e3508818be58e11e5491a90299e56d5ab569ab3ebf1ae3dc1f7ddff8761321754bd2bfc8c520dc9ac3876360714ced2baf6e06a82bbf635d
|
data/README.md
CHANGED
|
@@ -30,6 +30,7 @@ Package manifests, build workflow, and run workflow are documented separately in
|
|
|
30
30
|
- External files are the dedicated raw ABI surface, usually for generated or low-level `std.c.*` bindings.
|
|
31
31
|
- Module lookup resolves `a.b.c` to `a/b/c.mt`.
|
|
32
32
|
- Inside a package, the file path relative to `package.source_root` defines the module name; platform-specific files such as `name.linux.mt` still map to module `name`.
|
|
33
|
+
- Circular module imports are supported via forward-declaration bindings and two-pass checking. Cycle members see each other's type declarations in the first pass and full type information in the second pass.
|
|
33
34
|
- In ordinary files, `import` statements appear only at the top.
|
|
34
35
|
- In external files, leading `import` statements are allowed after `external`.
|
|
35
36
|
- Only external files accept `include`, `link`, and `compiler_flag` directives.
|
|
@@ -42,7 +43,7 @@ Blocks are indentation-based:
|
|
|
42
43
|
- Tabs are rejected.
|
|
43
44
|
- Indentation must be a multiple of 4 spaces.
|
|
44
45
|
- Indentation can increase by only one level at a time.
|
|
45
|
-
- Newlines end statements except inside `()` and `[]`, or when the previous physical line ends with a binary operator such as `+`, `and`, or
|
|
46
|
+
- Newlines end statements except inside `()` and `[]`, or when the previous physical line ends with a binary operator such as `+`, `and`, `==`, or `is`.
|
|
46
47
|
- Comma-separated lists inside `()` and `[]` accept trailing commas. Prefer them for multiline parameters, arguments, and type lists.
|
|
47
48
|
|
|
48
49
|
Long expressions should usually be wrapped with delimiters, following the same broad shape as Python's implicit line joining:
|
|
@@ -70,7 +71,15 @@ let values = 1 ..
|
|
|
70
71
|
4
|
|
71
72
|
```
|
|
72
73
|
|
|
73
|
-
|
|
74
|
+
Starting a physical line with an operator is a hard parse error (`operator '<op>' cannot start a statement`). Continue by ending the previous line with the operator, or wrap the expression in `()` if that layout reads better. The continuation operator set is closed: `+` `-` `*` `/` `%`, `|` `&` `^`, `<<` `>>`, `==` `!=` `<` `<=` `>` `>=`, `and` `or` `is`, and `..`. Member-access chains that exceed one line wrap in `()` with leading dots:
|
|
75
|
+
|
|
76
|
+
```mt
|
|
77
|
+
let total = (
|
|
78
|
+
values.iter()
|
|
79
|
+
.filter(pred)
|
|
80
|
+
.fold(0, add)
|
|
81
|
+
)
|
|
82
|
+
```
|
|
74
83
|
|
|
75
84
|
Comments:
|
|
76
85
|
|
|
@@ -314,6 +323,7 @@ Rules:
|
|
|
314
323
|
- Compile-time reflection over validated attributes uses `has_attribute`, `attribute_of`, `attribute_arg[T]`, `field_of`, and `callable_of`.
|
|
315
324
|
- `field_of(...)`, `callable_of(...)`, and `attribute_of(...)` produce compile-time handle values with source-visible handle types `field_handle`, `callable_handle`, and `attribute_handle`.
|
|
316
325
|
- The current C backend lowers `packed` / `align(...)` attributes with GNU-style `__attribute__((...))`, so these layout controls currently require a Clang/GCC-family compiler. On Windows that means Clang or GCC-family toolchains such as MinGW; `cl.exe` is not a supported backend for these attributes today. On wasm/browser targets the same feature works through Emscripten `emcc`, which is Clang-based.
|
|
326
|
+
- Keywords and reserved words are accepted as variant arm names, struct/union field names, enum/flags member names, lifetime parameters (`@return`), type parameters (`Box[return]`), named constructor arguments (`Meta(return = 1)`), and named tuple fields (`(return = 1, let = 2)`). Primitive type names (`int`, `str`, `ref`, etc.) remain reserved and cannot be used in these positions.
|
|
317
327
|
|
|
318
328
|
Enum and flags values support the full set of comparison operators (`==`, `!=`, `<`, `<=`, `>`, `>=`) against values of the same enum type and against their backing integer type. Comparisons use the underlying integer backing values. Flags also support bitwise operators (`|`, `&`, `^`, `~`).
|
|
319
329
|
|
|
@@ -867,9 +877,9 @@ Core modules in `std/`:
|
|
|
867
877
|
|
|
868
878
|
- `std.linear_algebra` — extends native vector/matrix/quaternion types with `dot`, `cross`, `length`, `normalized`, `lerp`, `identity`, `transpose`, `conjugate` (pure Mt, no C dependency beyond `std.math` for `sqrt`)
|
|
869
879
|
- `std.graph.Graph[T]` — adjacency-list graph with `add_node`, `add_edge`, `has_edge`, `remove_edge`, `neighbors`, `bfs`, `dfs`, `toposort`; directed or undirected; `compile()` converts to CSR-based `DenseGraph[T]` for O(degree) neighbor iteration
|
|
870
|
-
- `std.str` — extends `str` with `byte_at`, `equal`, `starts_with`, `ends_with`, `find_substring`, `is_valid_utf8`, `slice`, `to_cstr`, `hash`, `order`
|
|
880
|
+
- `std.str` — extends `str` with `byte_at`, `equal`, `starts_with`, `ends_with`, `find_substring`, `is_valid_utf8`, `slice`, `to_cstr`, `hash`, `order`; also provides C-string conversion helpers (`cstr_len`, `cstr_as_str`, `chars_as_str`, `nullable_cstr_as_str`)
|
|
871
881
|
- `std.hash` — extends the primitive integer types (`byte`/`ubyte`/`short`/`ushort`/`int`/`uint`/`long`/`ulong`/`ptr_int`/`ptr_uint`), `bool`, `float`, `double`, and `char` with canonical `hash`/`equal`/`order` hooks; import once to use primitives as Map/Set/BinaryHeap/OrderedMap keys (`str` keys come from `std.str`). Also provides generic `hash_struct[T]`, `equal_struct[T]`, `order_struct[T]` that dispatch each field through its own canonical hook via `field.type` (content-correct, including `str` and nested-struct fields).
|
|
872
|
-
- `std.cstring` — C
|
|
882
|
+
- `std.cstring` — low-level byte and C-string helpers (`copy_bytes`, `move_bytes`, `set_bytes`, `compare_bytes`, `find_byte`, `length`, `compare`, `compare_prefix`, `find_char`, `find_last_char`, `find_substring`)
|
|
873
883
|
- `std.math` — `sqrt`, `sin`, `cos`, `abs`, `pow`, etc. via C math
|
|
874
884
|
- `std.encoding` — UTF-8 validation (`is_valid_utf8`, `utf8_codepoint_count`, `decode_utf8_codepoint`, `utf8_overlong_check`)
|
|
875
885
|
- `std.string.String` — growable owned UTF-8 text
|
|
@@ -1111,6 +1121,7 @@ Current compiler rejects:
|
|
|
1111
1121
|
- range expressions are restricted to `for`-loop iterables and range-index assignment targets
|
|
1112
1122
|
- functions, methods, generic functions, and variant arms must be called — they are not usable as bare values
|
|
1113
1123
|
- `read(...)` of a raw pointer requires `unsafe`
|
|
1124
|
+
- a statement cannot begin with a binary operator (including `+`, `-`, `and`, `or`, `is`, `..`); continuation requires ending the previous line with the operator or wrapping in `()`
|
|
1114
1125
|
|
|
1115
1126
|
### Control flow restrictions
|
|
1116
1127
|
|
|
@@ -1161,11 +1172,12 @@ mtc build <path> # Build only (emit C, compile, link, --no-cache to
|
|
|
1161
1172
|
mtc run <path> # Build and execute (--no-cache to build and run without cache)
|
|
1162
1173
|
mtc debug <file.mt> # Print debug info (tokens, AST, facts, bindings, diagnostics)
|
|
1163
1174
|
mtc emit-c <path> # Emit generated C to stdout
|
|
1164
|
-
mtc format <path> # Format
|
|
1175
|
+
mtc format <path> # Format source to stdout (--write rewrites in place, --check verifies)
|
|
1165
1176
|
mtc lint <path> # Run linter (--fix to apply fixes, --select/--ignore to filter)
|
|
1166
1177
|
mtc test <path> # Discover and run @[test] functions (--timeout, --mem, --jobs)
|
|
1167
1178
|
mtc new <name> # Scaffold a new package (package.toml + src/main.mt)
|
|
1168
1179
|
mtc cache status # Show build cache stats
|
|
1180
|
+
mtc cache purge # Remove the entire build cache
|
|
1169
1181
|
mtc lex <file.mt> # Print lexer token stream
|
|
1170
1182
|
mtc parse <path> # Print parsed AST
|
|
1171
1183
|
mtc lower <path> # Print lowered IR
|
|
@@ -1183,10 +1195,10 @@ mtc deps publish <path> # Publish a package to the local registry
|
|
|
1183
1195
|
mtc deps fetch <path> # Materialize cache-backed sources
|
|
1184
1196
|
```
|
|
1185
1197
|
|
|
1186
|
-
|
|
1198
|
+
Resolve a module by name, build it, and run it:
|
|
1187
1199
|
|
|
1188
1200
|
```
|
|
1189
|
-
mtc run-module <module> #
|
|
1201
|
+
mtc run-module <module> # Resolve, build, and run a module by name (e.g. std.http.server)
|
|
1190
1202
|
```
|
|
1191
1203
|
|
|
1192
1204
|
Toolchain maintenance:
|
|
@@ -1197,9 +1209,19 @@ mtc toolchain doctor # Diagnose toolchain setup
|
|
|
1197
1209
|
mtc toolchain tools # List available native tools
|
|
1198
1210
|
```
|
|
1199
1211
|
|
|
1212
|
+
Tooling and editors:
|
|
1213
|
+
|
|
1214
|
+
```
|
|
1215
|
+
mtc bindgen <header.h> # Generate an external binding module from a C header
|
|
1216
|
+
mtc snapshot <path> # Render a highlighted HTML snapshot of a source file
|
|
1217
|
+
mtc lsp # Start the Language Server Protocol server
|
|
1218
|
+
mtc dap # Start the Debug Adapter Protocol server
|
|
1219
|
+
mtc docs [--open] [--port P] # Serve the local documentation site
|
|
1220
|
+
```
|
|
1221
|
+
|
|
1200
1222
|
Build and run commands support `--profile`, `--platform`, `--cc`, `--keep-c`, `--locked`, `--frozen`, and `-I` include paths. Dependency-locked flows support `--locked` (use package.lock) and `--frozen` (require current package.lock).
|
|
1201
1223
|
|
|
1202
|
-
Global options work with any command (before or after the subcommand, up to a `--` separator): `-h`/`--help` (also `mtc help <command>` for command-specific help), `-V`/`--version`, `-q`/`--quiet` (suppress informational output), `-v`/`--verbose` (per-file progress), and `--color auto|always|never`. Generate shell completions with `mtc completions bash|zsh|fish`. Timing breakdowns use `--timings` (on `format
|
|
1224
|
+
Global options work with any command (before or after the subcommand, up to a `--` separator): `-h`/`--help` (also `mtc help <command>` for command-specific help), `-V`/`--version`, `-q`/`--quiet` (suppress informational output), `-v`/`--verbose` (per-file progress), and `--color auto|always|never`. Generate shell completions with `mtc completions bash|zsh|fish`. Timing breakdowns use `--timings` (on `format` and `lint`); `lint` reports warnings with per-file progress.
|
|
1203
1225
|
|
|
1204
1226
|
Diagnostic output uses standard compiler format (file:line:column with source context, error codes, and caret highlighting):
|
|
1205
1227
|
|
data/docs/index.html
CHANGED
|
@@ -343,7 +343,7 @@ function main() -> int:
|
|
|
343
343
|
<tr><td><code>mtc check <path></code></td><td>Type-check + lint; reports all diagnostics sorted by line</td></tr>
|
|
344
344
|
<tr><td><code>mtc run <path></code></td><td>Build and execute</td></tr>
|
|
345
345
|
<tr><td><code>mtc build <path></code></td><td>Build only (emit C, compile, link)</td></tr>
|
|
346
|
-
<tr><td><code>mtc format <path></code></td><td>Format
|
|
346
|
+
<tr><td><code>mtc format <path></code></td><td>Format source to stdout (<code>--write</code> rewrites in place, <code>--check</code> verifies)</td></tr>
|
|
347
347
|
<tr><td><code>mtc lint <path></code></td><td>Run linter (<code>--fix</code> to apply fixes)</td></tr>
|
|
348
348
|
<tr><td><code>mtc test <path></code></td><td>Discover and run <code>@[test]</code> functions</td></tr>
|
|
349
349
|
<tr><td><code>mtc new <name></code></td><td>Scaffold a new package</td></tr>
|
|
@@ -403,6 +403,7 @@ void main_Vec2_scale(main_Vec2* this, float factor) {
|
|
|
403
403
|
<pre><code>## ── Imports ──────────────────────────────────────────────────────────
|
|
404
404
|
import std.stdio as io
|
|
405
405
|
import std.hash
|
|
406
|
+
import std.str
|
|
406
407
|
import std.vec
|
|
407
408
|
import std.map
|
|
408
409
|
import std.math
|
|
@@ -888,6 +889,7 @@ import mylib.utils</code></pre>
|
|
|
888
889
|
<li>Indentation must be a multiple of <strong>4 spaces</strong></li>
|
|
889
890
|
<li>Indentation increases by <strong>one level at a time</strong></li>
|
|
890
891
|
<li>Newlines end statements except inside <code>()</code> and <code>[]</code>, or when the previous line ends with a binary operator</li>
|
|
892
|
+
<li>Starting a line with an operator is a hard parse error (<code>operator '<op>' cannot start a statement</code>); continuation is signaled by ending the previous line with the operator, never by starting the next one with it</li>
|
|
891
893
|
</ul>
|
|
892
894
|
|
|
893
895
|
<div class="code-wrap">
|
|
@@ -902,7 +904,16 @@ let total = (
|
|
|
902
904
|
## Also accepted: operator-led continuation.
|
|
903
905
|
let total = subtotal +
|
|
904
906
|
tax -
|
|
905
|
-
discount
|
|
907
|
+
discount
|
|
908
|
+
|
|
909
|
+
## The continuation operator set is closed:
|
|
910
|
+
## + - * / % | & ^ << >> == != < <= > >= and or is ..
|
|
911
|
+
## Member-access chains exceeding one line wrap in () with leading dots:
|
|
912
|
+
let total = (
|
|
913
|
+
values.iter()
|
|
914
|
+
.filter(pred)
|
|
915
|
+
.fold(0, add)
|
|
916
|
+
)</code></pre>
|
|
906
917
|
</div>
|
|
907
918
|
|
|
908
919
|
<h3>Naming Conventions</h3>
|
|
@@ -1733,14 +1744,16 @@ let sized = name[32]</code></pre>
|
|
|
1733
1744
|
<tr><th>Precedence</th><th>Operators</th></tr>
|
|
1734
1745
|
<tr><td>1 (lowest)</td><td><code>or</code></td></tr>
|
|
1735
1746
|
<tr><td>2</td><td><code>and</code></td></tr>
|
|
1736
|
-
<tr><td>3</td><td><code
|
|
1737
|
-
<tr><td>4</td><td><code
|
|
1738
|
-
<tr><td>5</td><td><code
|
|
1739
|
-
<tr><td>6</td><td><code
|
|
1740
|
-
<tr><td>7</td><td><code>&
|
|
1741
|
-
<tr><td>8</td><td><code
|
|
1742
|
-
<tr><td>9</td><td><code
|
|
1743
|
-
<tr><td>10
|
|
1747
|
+
<tr><td>3</td><td><code>not</code> (unary prefix)</td></tr>
|
|
1748
|
+
<tr><td>4</td><td><code>is</code></td></tr>
|
|
1749
|
+
<tr><td>5</td><td><code>|</code></td></tr>
|
|
1750
|
+
<tr><td>6</td><td><code>^</code></td></tr>
|
|
1751
|
+
<tr><td>7</td><td><code>&</code></td></tr>
|
|
1752
|
+
<tr><td>8</td><td><code>==</code> <code>!=</code></td></tr>
|
|
1753
|
+
<tr><td>9</td><td><code><</code> <code><=</code> <code>></code> <code>>=</code></td></tr>
|
|
1754
|
+
<tr><td>10</td><td><code><<</code> <code>>></code></td></tr>
|
|
1755
|
+
<tr><td>11</td><td><code>+</code> <code>-</code></td></tr>
|
|
1756
|
+
<tr><td>12 (highest)</td><td><code>*</code> <code>/</code> <code>%</code></td></tr>
|
|
1744
1757
|
</table>
|
|
1745
1758
|
</div>
|
|
1746
1759
|
|
|
@@ -2169,7 +2182,7 @@ function attach(window: ref[Window]) -> Result[void, EventError]:
|
|
|
2169
2182
|
<tr><td><code>std.spatial</code></td><td><code>SpatialGrid[T]</code></td><td>—</td><td>Uniform spatial hash grid (returns Vec)</td></tr>
|
|
2170
2183
|
<tr><td><code>std.ring_buffer</code></td><td><code>RingBuffer[T]</code></td><td>Mutable <code>ptr[T]?</code></td><td>Fixed-capacity circular buffer</td></tr>
|
|
2171
2184
|
<tr><td><code>std.sparse_set</code></td><td><code>SparseSet[T]</code></td><td>Mutable <code>ptr[T]?</code></td><td>O(1) insert/remove; dense iteration</td></tr>
|
|
2172
|
-
<tr><td><code>std.lru_cache</code></td><td><code>LruCache[K,V]</code></td><td><code>
|
|
2185
|
+
<tr><td><code>std.lru_cache</code></td><td><code>LruCache[K,V]</code></td><td><code>iter()</code> → <code>linked_map.Entries[K, V]</code></td><td>Capacity-bounded LRU eviction map</td></tr>
|
|
2173
2186
|
</table>
|
|
2174
2187
|
</div>
|
|
2175
2188
|
|
|
@@ -2236,7 +2249,7 @@ function attach(window: ref[Window]) -> Result[void, EventError]:
|
|
|
2236
2249
|
<tr><td><code>mtc lint <path></code></td><td>Run linter (<code>--fix</code>, <code>--select</code>, <code>--ignore</code>)</td></tr>
|
|
2237
2250
|
<tr><td><code>mtc test <path></code></td><td>Discover and run <code>@[test]</code> functions</td></tr>
|
|
2238
2251
|
<tr><td><code>mtc new <name></code></td><td>Scaffold a new package (<code>package.toml</code> + <code>src/main.mt</code>)</td></tr>
|
|
2239
|
-
<tr><td><code>mtc run-module <module></code></td><td>
|
|
2252
|
+
<tr><td><code>mtc run-module <module></code></td><td>Resolve a module by name, build it, and run it</td></tr>
|
|
2240
2253
|
<tr><td><code>mtc completions <shell></code></td><td>Print a bash/zsh/fish completion script</td></tr>
|
|
2241
2254
|
</table>
|
|
2242
2255
|
</div>
|
|
@@ -2263,10 +2276,22 @@ function attach(window: ref[Window]) -> Result[void, EventError]:
|
|
|
2263
2276
|
<tr><td><code>mtc toolchain doctor</code></td><td>Diagnose toolchain setup</td></tr>
|
|
2264
2277
|
<tr><td><code>mtc toolchain tools</code></td><td>List available native tools</td></tr>
|
|
2265
2278
|
<tr><td><code>mtc cache status</code></td><td>Show build cache stats</td></tr>
|
|
2279
|
+
<tr><td><code>mtc cache purge</code></td><td>Remove the entire build cache</td></tr>
|
|
2266
2280
|
<tr><td><code>mtc docs [--open] [--port PORT]</code></td><td>Start a local HTTP server serving the language reference</td></tr>
|
|
2267
2281
|
</table>
|
|
2268
2282
|
</div>
|
|
2269
2283
|
|
|
2284
|
+
<h3>Tooling</h3>
|
|
2285
|
+
<div class="table-wrap">
|
|
2286
|
+
<table class="attr-table">
|
|
2287
|
+
<tr><th>Command</th><th>Description</th></tr>
|
|
2288
|
+
<tr><td><code>mtc bindgen <header.h></code></td><td>Generate an external binding module from a C header</td></tr>
|
|
2289
|
+
<tr><td><code>mtc snapshot <path></code></td><td>Render a highlighted HTML snapshot of a source file</td></tr>
|
|
2290
|
+
<tr><td><code>mtc lsp</code></td><td>Start the Language Server Protocol server (editor IntelliSense)</td></tr>
|
|
2291
|
+
<tr><td><code>mtc dap</code></td><td>Start the Debug Adapter Protocol server (debugging)</td></tr>
|
|
2292
|
+
</table>
|
|
2293
|
+
</div>
|
|
2294
|
+
|
|
2270
2295
|
<h3>Build Flags</h3>
|
|
2271
2296
|
<p>Build and run commands support: <code>--profile</code>, <code>--platform</code>, <code>--cc</code>, <code>--keep-c</code>, <code>--locked</code>, <code>--frozen</code>, and <code>-I</code> include paths.</p>
|
|
2272
2297
|
|
|
@@ -2385,6 +2410,7 @@ import std.math # intentionally available for downstream</code></pre>
|
|
|
2385
2410
|
<li>Enum and flags values do not implicitly coerce to backing integers</li>
|
|
2386
2411
|
<li><code>+</code> does not support <code>str</code>/<code>cstr</code> concatenation</li>
|
|
2387
2412
|
<li><code>==</code> and <code>!=</code> not supported on struct types; use <code>equal[T]</code></li>
|
|
2413
|
+
<li>A statement cannot begin with a binary operator (including <code>+</code>, <code>-</code>, <code>and</code>, <code>or</code>, <code>is</code>, <code>..</code>); continuation requires ending the previous line with the operator or wrapping in <code>()</code></li>
|
|
2388
2414
|
<li>Bare function and method names are not usable as values (cannot be assigned or passed without calling). Use <code>fn(...)</code> function pointer types or <code>proc(...)</code> closures for callable values.</li>
|
|
2389
2415
|
</ul>
|
|
2390
2416
|
|
data/docs/language-design.md
CHANGED
|
@@ -88,6 +88,7 @@ The compile-time evaluation surface is described in [Compile-Time Evaluation](co
|
|
|
88
88
|
- Newlines terminate statements.
|
|
89
89
|
- Newlines inside `()` and `[]` do not terminate statements.
|
|
90
90
|
- A physical line that ends with a binary operator also continues onto the next line.
|
|
91
|
+
- A physical line that begins with an operator is a hard parse error; continuation is signaled by ending the previous line with the operator, never by starting the next one with it.
|
|
91
92
|
- Tabs are illegal in source files; indentation is 4 spaces.
|
|
92
93
|
- Trailing commas are allowed in multiline call arguments and aggregate literals. The linter additionally flags a redundant trailing comma in a call-argument list (`trailing-list-comma` hint); aggregate and array literals keep them for diff-friendliness.
|
|
93
94
|
- Comments use `#`.
|
|
@@ -490,7 +491,7 @@ Milk Tea has first-class compiler support for multithreading. The threading mode
|
|
|
490
491
|
parallel for i in 0..entity_count:
|
|
491
492
|
positions[i] += velocities[i] * dt
|
|
492
493
|
|
|
493
|
-
# Structured fork-join: each
|
|
494
|
+
# Structured fork-join: each statement runs on a separate thread
|
|
494
495
|
parallel:
|
|
495
496
|
textures = load_textures(path)
|
|
496
497
|
sounds = load_sounds(path)
|
|
@@ -501,7 +502,6 @@ Design choices:
|
|
|
501
502
|
- **Structured, not fire-and-forget.** All `parallel for` and `parallel:` blocks are synchronous barriers — the calling thread blocks until all work completes. This guarantees that captured local variables remain alive for the duration and eliminates lifetime concerns without ownership annotations.
|
|
502
503
|
- **Compile-time safety.** `ref[T]` captures are rejected because mutable aliases across thread boundaries create data races. The `parallel:` block enforces single-writer-or-multiple-readers: if one statement writes a variable, no other block may read or write it.
|
|
503
504
|
- **Value capture, pointer-based arrays.** Scalars and spans are captured by value (the span's data pointer still references the original storage). Arrays are captured as pointers to their first element, so writes in the worker affect the original array.
|
|
504
|
-
- **`do` is a keyword.** It is recognized inside `parallel:` blocks to introduce each concurrent unit of work.
|
|
505
505
|
- **Real OS threads via libuv.** Thread dispatch uses `uv_thread_create` / `uv_thread_join`, consistent with `std.thread` and `std.sync`. CPU count is detected at runtime via `uv_cpu_info`. The first chunk runs on the calling thread to avoid unnecessary dispatch when the workload is small.
|
|
506
506
|
- **No forced dependency.** The build system automatically detects `parallel for` and `parallel:` usage via a sema-level flag and links libuv only when needed.
|
|
507
507
|
|
data/docs/language-manual.md
CHANGED
|
@@ -51,6 +51,7 @@ Rules:
|
|
|
51
51
|
- Valid platform filename suffixes are `linux`, `windows`, and `wasm`.
|
|
52
52
|
- The platform suffix is not part of the module name. `import a.b.c` stays the same on every target.
|
|
53
53
|
- Milk Tea does not have a source-level conditional compilation syntax such as `#if`, `#ifdef`, or per-declaration platform attributes.
|
|
54
|
+
- Circular module imports are supported. When two modules import each other, the compiler uses forward-declaration bindings for the first pass and full type-checked bindings for the second pass, enabling cross-module type references and constructors in cycle groups.
|
|
54
55
|
|
|
55
56
|
## 2. Lexical Rules
|
|
56
57
|
|
|
@@ -82,7 +83,17 @@ let total = subtotal +
|
|
|
82
83
|
discount
|
|
83
84
|
```
|
|
84
85
|
|
|
85
|
-
|
|
86
|
+
The continuation operator set is closed: `+` `-` `*` `/` `%`, `|` `&` `^`, `<<` `>>`, `==` `!=` `<` `<=` `>` `>=`, `and` `or` `is`, and `..`. Assignment operators and unary `not` are excluded — wrap in `()` when a break is needed there.
|
|
87
|
+
|
|
88
|
+
Starting a new physical line with an operator is a hard error (`operator '<op>' cannot start a statement`); end the previous line with the operator, or wrap the expression in `()` if that layout is clearer. Member-access chains that exceed one line wrap in `()` with leading dots:
|
|
89
|
+
|
|
90
|
+
```mt
|
|
91
|
+
let total = (
|
|
92
|
+
values.iter()
|
|
93
|
+
.filter(pred)
|
|
94
|
+
.fold(0, add)
|
|
95
|
+
)
|
|
96
|
+
```
|
|
86
97
|
|
|
87
98
|
### 2.2 Comments
|
|
88
99
|
|
|
@@ -360,6 +371,18 @@ Compile-time reflection over validated attributes uses `has_attribute`, `attribu
|
|
|
360
371
|
|
|
361
372
|
The current C backend lowers `packed` / `align(...)` attributes with GNU-style `__attribute__((...))`, so these layout controls currently require a Clang/GCC-family compiler. On Windows that means Clang or GCC-family toolchains such as MinGW; `cl.exe` is not a supported backend for these attributes today. On wasm/browser targets the same feature works through Emscripten `emcc`, which is Clang-based.
|
|
362
373
|
|
|
374
|
+
Language-level keywords and reserved words are accepted as:
|
|
375
|
+
- variant arm names (`return(val: int)`, `if(arg: bool)`)
|
|
376
|
+
- struct and union field names (`return: int`, `let: str`)
|
|
377
|
+
- enum and flags member names (`return = 1`, `if = 6`)
|
|
378
|
+
- variant arm payload field names (`execute(return: int)`)
|
|
379
|
+
- lifetime parameter names (`struct Buf[@return]`)
|
|
380
|
+
- type parameter names (`struct Box[return]`, `identity[type]`)
|
|
381
|
+
- named arguments in struct/variant literals (`Meta(return = 1)`)
|
|
382
|
+
- named tuple fields (`(return = 1, let = 2)`)
|
|
383
|
+
|
|
384
|
+
Primitive type names (`int`, `str`, `bool`, `ref`, `ptr`, `span`, `type`, etc.) remain reserved and cannot be used as names.
|
|
385
|
+
|
|
363
386
|
### 3.4a Enum and flags operators
|
|
364
387
|
|
|
365
388
|
Enum and flags values support the full set of comparison operators (`==`, `!=`, `<`, `<=`, `>`, `>=`) against values of the same enum type and against their backing integer type. Comparisons use the underlying integer backing values.
|
|
@@ -746,7 +769,6 @@ parallel:
|
|
|
746
769
|
Rules:
|
|
747
770
|
|
|
748
771
|
- A `parallel:` block must contain at least two statements.
|
|
749
|
-
- `do` is a contextual keyword — only recognized inside `parallel:` blocks, not reserved globally.
|
|
750
772
|
- Each statement must not contain `break`, `continue`, `return`, or `defer`.
|
|
751
773
|
- The compiler enforces single-writer-or-multiple-readers: if a variable is written in one statement, no other block may access it.
|
|
752
774
|
- Captured `ref[T]` values are rejected at compile time.
|
data/lib/milk_tea/base.rb
CHANGED
data/lib/milk_tea/core/ast.rb
CHANGED
|
@@ -153,17 +153,17 @@ module MilkTea
|
|
|
153
153
|
VariantArm = Data.define(:name, :fields, :line, :column) do
|
|
154
154
|
def initialize(name:, fields:, line: nil, column: nil) = super
|
|
155
155
|
end
|
|
156
|
-
MatchArm = Data.define(:pattern, :binding_name, :binding_line, :binding_column, :body) do
|
|
157
|
-
def initialize(pattern:, binding_name:, body:, binding_line: nil, binding_column: nil) = super
|
|
156
|
+
MatchArm = Data.define(:pattern, :binding_name, :binding_line, :binding_column, :body, :line, :column) do
|
|
157
|
+
def initialize(pattern:, binding_name:, body:, binding_line: nil, binding_column: nil, line: nil, column: nil) = super
|
|
158
158
|
end
|
|
159
159
|
MatchStmt = Data.define(:expression, :arms, :inline, :line, :column, :length) do
|
|
160
160
|
def initialize(expression:, arms:, inline: false, line: nil, column: nil, length: nil) = super
|
|
161
161
|
end
|
|
162
|
-
MatchExprArm = Data.define(:pattern, :binding_name, :binding_line, :binding_column, :value) do
|
|
163
|
-
def initialize(pattern:, binding_name:, value:, binding_line: nil, binding_column: nil) = super
|
|
162
|
+
MatchExprArm = Data.define(:pattern, :binding_name, :binding_line, :binding_column, :value, :line, :column) do
|
|
163
|
+
def initialize(pattern:, binding_name:, value:, binding_line: nil, binding_column: nil, line: nil, column: nil) = super
|
|
164
164
|
end
|
|
165
|
-
WhenBranch = Data.define(:pattern, :binding_name, :binding_line, :binding_column, :body) do
|
|
166
|
-
def initialize(pattern:, binding_name:, body:, binding_line: nil, binding_column: nil) = super
|
|
165
|
+
WhenBranch = Data.define(:pattern, :binding_name, :binding_line, :binding_column, :body, :line, :column) do
|
|
166
|
+
def initialize(pattern:, binding_name:, body:, binding_line: nil, binding_column: nil, line: nil, column: nil) = super
|
|
167
167
|
end
|
|
168
168
|
WhenStmt = Data.define(:discriminant, :branches, :else_body, :line, :column, :length) do
|
|
169
169
|
def initialize(discriminant:, branches:, else_body:, line: nil, column: nil, length: nil) = super
|
data/lib/milk_tea/core/lexer.rb
CHANGED
|
@@ -42,15 +42,7 @@ module MilkTea
|
|
|
42
42
|
link opaque public static_assert struct type union var variant extending event
|
|
43
43
|
].freeze
|
|
44
44
|
|
|
45
|
-
LINE_CONTINUATION_OPERATORS =
|
|
46
|
-
dot_dot
|
|
47
|
-
plus minus star slash percent
|
|
48
|
-
pipe amp caret
|
|
49
|
-
or and
|
|
50
|
-
equal_equal bang_equal
|
|
51
|
-
less less_equal greater greater_equal
|
|
52
|
-
shift_left shift_right
|
|
53
|
-
].freeze
|
|
45
|
+
LINE_CONTINUATION_OPERATORS = Token::LINE_CONTINUATION_OPERATORS
|
|
54
46
|
|
|
55
47
|
THREE_CHAR_TOKENS = {
|
|
56
48
|
"..." => :ellipsis,
|
|
@@ -358,7 +350,7 @@ module MilkTea
|
|
|
358
350
|
newline_start = line_offset + line.length
|
|
359
351
|
newline_end = has_newline ? (newline_start + 1) : newline_start
|
|
360
352
|
if @grouping_depth.zero?
|
|
361
|
-
if LINE_CONTINUATION_OPERATORS.include?(@tokens.last&.type)
|
|
353
|
+
if Token::LINE_CONTINUATION_OPERATORS.include?(@tokens.last&.type)
|
|
362
354
|
@continuation_pending = true
|
|
363
355
|
else
|
|
364
356
|
@tokens << token(:newline, "\n", nil, line_number, line.length + 1, start_offset: newline_start, end_offset: newline_end)
|
|
@@ -93,7 +93,7 @@ module MilkTea
|
|
|
93
93
|
arms = statement.arms.map do |arm|
|
|
94
94
|
arm_env = duplicate_env(env)
|
|
95
95
|
bind_async_variant_match_arm_env!(arm_env, scrutinee_type, arm)
|
|
96
|
-
AST::MatchArm.new(pattern: arm.pattern, binding_name: arm.binding_name, body: normalize_async_statements(arm.body, counter, arm_env, return_type:))
|
|
96
|
+
AST::MatchArm.new(pattern: arm.pattern, binding_name: arm.binding_name, body: normalize_async_statements(arm.body, counter, arm_env, return_type:), line: arm.line, column: arm.column)
|
|
97
97
|
end
|
|
98
98
|
expr_setup + [AST::MatchStmt.new(expression:, arms:)]
|
|
99
99
|
when AST::WhileStmt
|
|
@@ -307,6 +307,8 @@ module MilkTea
|
|
|
307
307
|
binding_line: arm.binding_line,
|
|
308
308
|
binding_column: arm.binding_column,
|
|
309
309
|
value: normalized_value,
|
|
310
|
+
line: arm.line,
|
|
311
|
+
column: arm.column,
|
|
310
312
|
)]
|
|
311
313
|
end
|
|
312
314
|
|
|
@@ -326,6 +328,8 @@ module MilkTea
|
|
|
326
328
|
binding_line: arm.binding_line,
|
|
327
329
|
binding_column: arm.binding_column,
|
|
328
330
|
body: pattern_setup + value_setup + [AST::Assignment.new(target: AST::Identifier.new(name: temp_name), operator: "=", value: arm.value)],
|
|
331
|
+
line: arm.line,
|
|
332
|
+
column: arm.column,
|
|
329
333
|
)
|
|
330
334
|
end,
|
|
331
335
|
line: expression.line,
|
|
@@ -372,10 +372,10 @@ module MilkTea
|
|
|
372
372
|
break if check(:rbracket)
|
|
373
373
|
|
|
374
374
|
if match(:at)
|
|
375
|
-
name_token =
|
|
375
|
+
name_token = consume_name_allowing_keywords("expected lifetime name after @")
|
|
376
376
|
lifetime_params << "@#{name_token.lexeme}"
|
|
377
377
|
else
|
|
378
|
-
name_token =
|
|
378
|
+
name_token = consume_name_allowing_keywords("expected type parameter name")
|
|
379
379
|
if match(:colon)
|
|
380
380
|
value_type = parse_type_ref
|
|
381
381
|
type_params << AST::ValueTypeParam.new(
|
|
@@ -419,7 +419,7 @@ module MilkTea
|
|
|
419
419
|
|
|
420
420
|
raise error(visibility_token, "public is only allowed on struct events") if visibility == :public
|
|
421
421
|
|
|
422
|
-
field_token =
|
|
422
|
+
field_token = consume_name_allowing_keywords("expected field name")
|
|
423
423
|
field_name = field_token.lexeme
|
|
424
424
|
consume(:colon, "expected ':' after field name")
|
|
425
425
|
field_type = parse_type_ref
|
|
@@ -440,7 +440,7 @@ module MilkTea
|
|
|
440
440
|
name = name_token.lexeme
|
|
441
441
|
c_name = parse_optional_explicit_c_name
|
|
442
442
|
fields = parse_named_block do
|
|
443
|
-
|
|
443
|
+
field_name = consume_name_allowing_keywords("expected field name").lexeme
|
|
444
444
|
consume(:colon, "expected ':' after field name")
|
|
445
445
|
field_type = parse_type_ref
|
|
446
446
|
consume_end_of_statement
|
|
@@ -471,7 +471,7 @@ module MilkTea
|
|
|
471
471
|
members = []
|
|
472
472
|
skip_newlines
|
|
473
473
|
until check(:dedent) || eof?
|
|
474
|
-
member_token =
|
|
474
|
+
member_token = consume_name_allowing_keywords("expected member name")
|
|
475
475
|
member_name = member_token.lexeme
|
|
476
476
|
if match(:equal)
|
|
477
477
|
value = parse_expression
|
|
@@ -493,10 +493,10 @@ module MilkTea
|
|
|
493
493
|
name = name_token.lexeme
|
|
494
494
|
type_params = parse_declaration_type_params
|
|
495
495
|
arms = parse_named_block do
|
|
496
|
-
arm_name =
|
|
496
|
+
arm_name = consume_name_allowing_keywords("expected variant arm name").lexeme
|
|
497
497
|
fields = if match(:lparen)
|
|
498
498
|
parsed = parse_comma_separated_until(:rparen) do
|
|
499
|
-
|
|
499
|
+
field_name = consume_name_allowing_keywords("expected field name").lexeme
|
|
500
500
|
consume(:colon, "expected ':' after field name")
|
|
501
501
|
field_type = parse_type_ref
|
|
502
502
|
AST::Field.new(name: field_name, type: field_type)
|
|
@@ -105,6 +105,8 @@ module MilkTea
|
|
|
105
105
|
binding_line: binding_token&.line,
|
|
106
106
|
binding_column: binding_token&.column,
|
|
107
107
|
value:,
|
|
108
|
+
line: pattern.line,
|
|
109
|
+
column: pattern.column,
|
|
108
110
|
)
|
|
109
111
|
end
|
|
110
112
|
end
|
|
@@ -153,6 +155,8 @@ module MilkTea
|
|
|
153
155
|
binding_line: nil,
|
|
154
156
|
binding_column: nil,
|
|
155
157
|
value: AST::BooleanLiteral.new(value: true),
|
|
158
|
+
line: arm_pattern.line,
|
|
159
|
+
column: arm_pattern.column,
|
|
156
160
|
),
|
|
157
161
|
AST::MatchExprArm.new(
|
|
158
162
|
pattern: AST::Identifier.new(name: "_", line:, column:),
|
|
@@ -160,6 +164,8 @@ module MilkTea
|
|
|
160
164
|
binding_line: nil,
|
|
161
165
|
binding_column: nil,
|
|
162
166
|
value: AST::BooleanLiteral.new(value: false),
|
|
167
|
+
line:,
|
|
168
|
+
column:,
|
|
163
169
|
),
|
|
164
170
|
],
|
|
165
171
|
line:,
|
|
@@ -338,7 +344,7 @@ module MilkTea
|
|
|
338
344
|
end
|
|
339
345
|
|
|
340
346
|
def parse_call_argument
|
|
341
|
-
if check_name && check_next(:equal)
|
|
347
|
+
if (check_name || keyword_token?(peek)) && check_next(:equal)
|
|
342
348
|
name = advance.lexeme
|
|
343
349
|
consume(:equal, "expected '=' after named argument name")
|
|
344
350
|
AST::Argument.new(name:, value: parse_expression)
|
|
@@ -388,7 +394,7 @@ module MilkTea
|
|
|
388
394
|
elsif match(:lparen)
|
|
389
395
|
line = previous.line
|
|
390
396
|
column = previous.column
|
|
391
|
-
first = if check_name && check_next(:equal)
|
|
397
|
+
first = if (check_name || keyword_token?(peek)) && check_next(:equal)
|
|
392
398
|
name_token = advance
|
|
393
399
|
consume(:equal, "expected '=' after named tuple field")
|
|
394
400
|
AST::Argument.new(name: name_token.lexeme, value: parse_expression)
|
|
@@ -398,7 +404,7 @@ module MilkTea
|
|
|
398
404
|
if match(:comma)
|
|
399
405
|
elements = [first]
|
|
400
406
|
loop do
|
|
401
|
-
if check_name && check_next(:equal)
|
|
407
|
+
if (check_name || keyword_token?(peek)) && check_next(:equal)
|
|
402
408
|
name_token = advance
|
|
403
409
|
consume(:equal, "expected '=' after named tuple field")
|
|
404
410
|
value = parse_expression
|
|
@@ -35,7 +35,18 @@ module MilkTea
|
|
|
35
35
|
check(:when)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
def reject_operator_statement_start!
|
|
39
|
+
return unless Token::LINE_START_OPERATOR_TYPES.include?(peek.type)
|
|
40
|
+
|
|
41
|
+
operator = peek.lexeme
|
|
42
|
+
raise error(
|
|
43
|
+
peek,
|
|
44
|
+
"operator '#{operator}' cannot start a statement; end the previous line with it or wrap the expression in ( )"
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
38
48
|
def parse_statement
|
|
49
|
+
reject_operator_statement_start!
|
|
39
50
|
if match(:let)
|
|
40
51
|
parse_local_decl(:let)
|
|
41
52
|
elsif match(:var)
|
|
@@ -351,6 +362,8 @@ module MilkTea
|
|
|
351
362
|
binding_line: binding_token&.line,
|
|
352
363
|
binding_column: binding_token&.column,
|
|
353
364
|
value:,
|
|
365
|
+
line: pattern.line,
|
|
366
|
+
column: pattern.column,
|
|
354
367
|
)
|
|
355
368
|
end
|
|
356
369
|
else
|
|
@@ -362,6 +375,8 @@ module MilkTea
|
|
|
362
375
|
binding_line: binding_token&.line,
|
|
363
376
|
binding_column: binding_token&.column,
|
|
364
377
|
body:,
|
|
378
|
+
line: pattern.line,
|
|
379
|
+
column: pattern.column,
|
|
365
380
|
)
|
|
366
381
|
end
|
|
367
382
|
end
|
|
@@ -376,6 +391,8 @@ module MilkTea
|
|
|
376
391
|
binding_line: binding_token&.line,
|
|
377
392
|
binding_column: binding_token&.column,
|
|
378
393
|
body: recovered_body,
|
|
394
|
+
line: patterns.first&.line,
|
|
395
|
+
column: patterns.first&.column,
|
|
379
396
|
)] if recovered_body
|
|
380
397
|
|
|
381
398
|
raise
|
|
@@ -615,6 +632,8 @@ module MilkTea
|
|
|
615
632
|
binding_line: binding_token.line,
|
|
616
633
|
binding_column: binding_token.column,
|
|
617
634
|
body:,
|
|
635
|
+
line: pattern.line,
|
|
636
|
+
column: pattern.column,
|
|
618
637
|
)
|
|
619
638
|
skip_newlines
|
|
620
639
|
end
|
|
@@ -92,7 +92,7 @@ module MilkTea
|
|
|
92
92
|
|
|
93
93
|
first_token = peek
|
|
94
94
|
if match(:at)
|
|
95
|
-
lt_token =
|
|
95
|
+
lt_token = consume_name_allowing_keywords("expected lifetime name after @")
|
|
96
96
|
return AST::TypeRef.new(name: AST::QualifiedName.new(parts: ["@#{lt_token.lexeme}"]), arguments: [], nullable: false, lifetime: nil, line: first_token.line, column: first_token.column, length: lt_token.lexeme.length + 1)
|
|
97
97
|
end
|
|
98
98
|
name = parse_qualified_name
|
|
@@ -101,7 +101,7 @@ module MilkTea
|
|
|
101
101
|
if match(:lbracket)
|
|
102
102
|
if check(:at) && name.to_s == "ref"
|
|
103
103
|
match(:at)
|
|
104
|
-
lt_token =
|
|
104
|
+
lt_token = consume_name_allowing_keywords("expected lifetime name after @")
|
|
105
105
|
lifetime = "@#{lt_token.lexeme}"
|
|
106
106
|
consume(:comma, "expected ',' after lifetime in type arguments")
|
|
107
107
|
end
|
|
@@ -167,7 +167,7 @@ module MilkTea
|
|
|
167
167
|
end
|
|
168
168
|
|
|
169
169
|
def parse_function_type_param
|
|
170
|
-
name_token =
|
|
170
|
+
name_token = consume_name_allowing_keywords("expected function type parameter name")
|
|
171
171
|
name = name_token.lexeme
|
|
172
172
|
consume(:colon, "expected ':' after function type parameter name")
|
|
173
173
|
type = parse_type_ref
|
|
@@ -190,7 +190,7 @@ module MilkTea
|
|
|
190
190
|
return [] unless match(:lbracket)
|
|
191
191
|
|
|
192
192
|
params = parse_comma_separated_until(:rbracket) do
|
|
193
|
-
name_token =
|
|
193
|
+
name_token = consume_name_allowing_keywords("expected type parameter name")
|
|
194
194
|
name = name_token.lexeme
|
|
195
195
|
if match(:colon)
|
|
196
196
|
value_type = parse_type_ref
|
|
@@ -208,7 +208,7 @@ module MilkTea
|
|
|
208
208
|
def resolve_type_param_constraints(type_params)
|
|
209
209
|
type_params.each_with_object({}) do |type_param, constraints|
|
|
210
210
|
if type_param.is_a?(AST::ValueTypeParam)
|
|
211
|
-
|
|
211
|
+
ensure_non_reserved_value_type_name!(
|
|
212
212
|
type_param.name,
|
|
213
213
|
kind_label: "type parameter",
|
|
214
214
|
line: type_param.line,
|
|
@@ -218,7 +218,7 @@ module MilkTea
|
|
|
218
218
|
next
|
|
219
219
|
end
|
|
220
220
|
|
|
221
|
-
|
|
221
|
+
ensure_non_reserved_value_type_name!(
|
|
222
222
|
type_param.name,
|
|
223
223
|
kind_label: "type parameter",
|
|
224
224
|
line: type_param.line,
|
|
@@ -314,6 +314,12 @@ module MilkTea
|
|
|
314
314
|
expanded_declarations.grep(AST::AttributeDecl).each do |decl|
|
|
315
315
|
with_error_node(decl) do
|
|
316
316
|
raise_sema_error("duplicate attribute #{decl.name}") if @ctx.attributes.key?(decl.name)
|
|
317
|
+
ensure_non_reserved_value_type_name!(
|
|
318
|
+
decl.name,
|
|
319
|
+
kind_label: "attribute",
|
|
320
|
+
line: decl.line,
|
|
321
|
+
column: decl.column,
|
|
322
|
+
)
|
|
317
323
|
|
|
318
324
|
params = []
|
|
319
325
|
seen = {}
|
|
@@ -463,6 +469,15 @@ module MilkTea
|
|
|
463
469
|
{}
|
|
464
470
|
end
|
|
465
471
|
decl.lifetime_params.each do |lt|
|
|
472
|
+
raw_name = lt.delete_prefix("@")
|
|
473
|
+
unless raw_module?
|
|
474
|
+
ensure_non_reserved_value_type_name!(
|
|
475
|
+
raw_name,
|
|
476
|
+
kind_label: "lifetime parameter #{decl.name}",
|
|
477
|
+
line: decl.line,
|
|
478
|
+
column: decl.column,
|
|
479
|
+
)
|
|
480
|
+
end
|
|
466
481
|
type_params[lt] = Types::LifetimeRef.new(lt)
|
|
467
482
|
end if decl.respond_to?(:lifetime_params)
|
|
468
483
|
type_param_constraints = struct_type.is_a?(Types::GenericStructDefinition) ? struct_type.type_param_constraints : {}
|
|
@@ -482,7 +497,7 @@ module MilkTea
|
|
|
482
497
|
raise_sema_error("duplicate field #{decl.name}.#{field.name}") if fields.key?(field.name)
|
|
483
498
|
raise_sema_error("duplicate member #{decl.name}.#{field.name}") if events.key?(field.name)
|
|
484
499
|
unless raw_module?
|
|
485
|
-
|
|
500
|
+
ensure_non_reserved_value_type_name!(
|
|
486
501
|
field.name,
|
|
487
502
|
kind_label: "field #{decl.name}",
|
|
488
503
|
line: field.line || decl.line,
|
|
@@ -528,7 +543,7 @@ module MilkTea
|
|
|
528
543
|
raise_sema_error("duplicate event #{decl.name}.#{event_decl.name}") if events.key?(event_decl.name)
|
|
529
544
|
raise_sema_error("duplicate member #{decl.name}.#{event_decl.name}") if fields.key?(event_decl.name)
|
|
530
545
|
unless raw_module?
|
|
531
|
-
|
|
546
|
+
ensure_non_reserved_value_type_name!(
|
|
532
547
|
event_decl.name,
|
|
533
548
|
kind_label: "event #{decl.name}",
|
|
534
549
|
line: event_decl.line,
|
|
@@ -576,7 +591,7 @@ module MilkTea
|
|
|
576
591
|
decl.members.each do |member|
|
|
577
592
|
raise_sema_error("duplicate member #{decl.name}.#{member.name}") if member_names.include?(member.name)
|
|
578
593
|
unless raw_module?
|
|
579
|
-
|
|
594
|
+
ensure_non_reserved_value_type_name!(
|
|
580
595
|
member.name,
|
|
581
596
|
kind_label: "member #{decl.name}",
|
|
582
597
|
line: member.line || decl.line,
|
|
@@ -645,7 +660,7 @@ module MilkTea
|
|
|
645
660
|
begin
|
|
646
661
|
raise_sema_error("duplicate arm #{decl.name}.#{arm.name}") if seen_arms.include?(arm.name)
|
|
647
662
|
unless raw_module?
|
|
648
|
-
|
|
663
|
+
ensure_non_reserved_value_type_name!(
|
|
649
664
|
arm.name,
|
|
650
665
|
kind_label: "arm #{decl.name}",
|
|
651
666
|
line: arm.line || decl.line,
|
|
@@ -661,7 +676,7 @@ module MilkTea
|
|
|
661
676
|
begin
|
|
662
677
|
raise_sema_error("duplicate field #{arm.name}.#{field.name}") if seen_fields.include?(field.name)
|
|
663
678
|
unless raw_module?
|
|
664
|
-
|
|
679
|
+
ensure_non_reserved_value_type_name!(
|
|
665
680
|
field.name,
|
|
666
681
|
kind_label: "field #{decl.name}.#{arm.name}",
|
|
667
682
|
line: field.line || decl.line,
|
|
@@ -811,6 +826,14 @@ module MilkTea
|
|
|
811
826
|
fields = {}
|
|
812
827
|
nested.fields.each do |field|
|
|
813
828
|
raise_sema_error("duplicate field #{qualified_name}.#{field.name}") if fields.key?(field.name)
|
|
829
|
+
unless raw_module?
|
|
830
|
+
ensure_non_reserved_value_type_name!(
|
|
831
|
+
field.name,
|
|
832
|
+
kind_label: "field #{qualified_name}",
|
|
833
|
+
line: field.line || nested.line,
|
|
834
|
+
column: field.column,
|
|
835
|
+
)
|
|
836
|
+
end
|
|
814
837
|
begin
|
|
815
838
|
field_type = resolve_type_ref(field.type, type_params:, type_param_constraints:, nested_types: nested_scope)
|
|
816
839
|
validate_stored_ref_type!(field_type, "field #{qualified_name}.#{field.name}")
|
|
@@ -824,6 +847,14 @@ module MilkTea
|
|
|
824
847
|
nested_events = {}
|
|
825
848
|
nested.events.each do |event_decl|
|
|
826
849
|
raise_sema_error("duplicate event #{qualified_name}.#{event_decl.name}") if nested_events.key?(event_decl.name)
|
|
850
|
+
unless raw_module?
|
|
851
|
+
ensure_non_reserved_value_type_name!(
|
|
852
|
+
event_decl.name,
|
|
853
|
+
kind_label: "event #{qualified_name}",
|
|
854
|
+
line: event_decl.line,
|
|
855
|
+
column: event_decl.column,
|
|
856
|
+
)
|
|
857
|
+
end
|
|
827
858
|
begin
|
|
828
859
|
nested_events[event_decl.name] = resolve_event_decl_type(event_decl, type_params:, type_param_constraints:, owner_type_name: qualified_name, nested_types: nested_scope)
|
|
829
860
|
rescue SemanticError => e
|
data/lib/milk_tea/core/token.rb
CHANGED
|
@@ -11,6 +11,23 @@ module MilkTea
|
|
|
11
11
|
amp_equal pipe_equal caret_equal shift_left_equal shift_right_equal
|
|
12
12
|
].freeze
|
|
13
13
|
|
|
14
|
+
# Operator token types that may never begin a statement. A statement
|
|
15
|
+
# that spans multiple lines must end the previous line with one of
|
|
16
|
+
# LINE_CONTINUATION_OPERATORS or wrap the expression in ( ) — it must
|
|
17
|
+
# never start the next line with an operator.
|
|
18
|
+
LINE_START_OPERATOR_TYPES = %i[
|
|
19
|
+
dot_dot plus minus star slash percent
|
|
20
|
+
pipe amp caret tilde
|
|
21
|
+
or and is
|
|
22
|
+
equal_equal bang_equal
|
|
23
|
+
less less_equal greater greater_equal
|
|
24
|
+
shift_left shift_right
|
|
25
|
+
].freeze
|
|
26
|
+
|
|
27
|
+
# Binary operators that continue a statement when they end a physical
|
|
28
|
+
# line. Unary-only `~` is excluded because it has no left operand.
|
|
29
|
+
LINE_CONTINUATION_OPERATORS = (LINE_START_OPERATOR_TYPES - [:tilde]).freeze
|
|
30
|
+
|
|
14
31
|
def assignment?
|
|
15
32
|
ASSIGNMENT_TYPES.include?(type)
|
|
16
33
|
end
|
|
@@ -275,6 +275,12 @@ module MilkTea
|
|
|
275
275
|
end
|
|
276
276
|
|
|
277
277
|
if KEYWORD_TOKEN_TYPES.include?(tok.type)
|
|
278
|
+
return [:typeParameter, []] if keyword_lifetime_or_type_param?(tokens, index)
|
|
279
|
+
return [:type, []] if keyword_type_annotation?(tokens, index)
|
|
280
|
+
return [:enumMember, [:declaration]] if variant_enum_member_declaration?(tokens, index)
|
|
281
|
+
return [:property, [:declaration]] if keyword_field_declaration_token?(tokens, index)
|
|
282
|
+
return [:property, []] if keyword_member_access?(tokens, index)
|
|
283
|
+
return [:parameter, []] if keyword_named_argument_token?(tokens, index)
|
|
278
284
|
return [:keyword, []]
|
|
279
285
|
end
|
|
280
286
|
|
|
@@ -550,6 +556,64 @@ module MilkTea
|
|
|
550
556
|
next_tok&.type == :colon
|
|
551
557
|
end
|
|
552
558
|
|
|
559
|
+
def keyword_field_declaration_token?(tokens, index)
|
|
560
|
+
return false if match_arm_binding_token?(tokens, index)
|
|
561
|
+
return false if destructure_let_binding?(tokens, index)
|
|
562
|
+
|
|
563
|
+
next_tok = next_non_trivia_token(tokens, index + 1)
|
|
564
|
+
next_tok&.type == :colon
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
def keyword_member_access?(tokens, index)
|
|
568
|
+
prev_index = previous_non_trivia_token_index(tokens, index)
|
|
569
|
+
return false unless prev_index
|
|
570
|
+
|
|
571
|
+
tokens[prev_index].type == :dot
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
def keyword_lifetime_or_type_param?(tokens, index)
|
|
575
|
+
prev_index = previous_non_trivia_token_index(tokens, index)
|
|
576
|
+
return false unless prev_index
|
|
577
|
+
|
|
578
|
+
prev_type = tokens[prev_index].type
|
|
579
|
+
return true if prev_type == :at
|
|
580
|
+
return true if prev_type == :lbracket
|
|
581
|
+
return true if prev_type == :comma && inside_type_params?(tokens, prev_index)
|
|
582
|
+
false
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
def inside_type_params?(tokens, comma_index)
|
|
586
|
+
i = comma_index - 1
|
|
587
|
+
while i >= 0
|
|
588
|
+
t = tokens[i]
|
|
589
|
+
break if t.type == :lbracket
|
|
590
|
+
return false if t.type == :newline || t.type == :rbracket || t.type == :lparen
|
|
591
|
+
i -= 1
|
|
592
|
+
end
|
|
593
|
+
return false if i < 0
|
|
594
|
+
|
|
595
|
+
prev = previous_non_trivia_token_index(tokens, i)
|
|
596
|
+
return true if prev && %i[struct variant enum flags function fn proc].include?(tokens[prev].type)
|
|
597
|
+
return true if prev && tokens[prev].type == :rbracket
|
|
598
|
+
return true if prev && tokens[prev].type == :identifier
|
|
599
|
+
|
|
600
|
+
false
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
def keyword_named_argument_token?(tokens, index)
|
|
604
|
+
next_tok = next_non_trivia_token(tokens, index + 1)
|
|
605
|
+
next_tok&.type == :equal
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
def keyword_type_annotation?(tokens, index)
|
|
609
|
+
tok = tokens[index]
|
|
610
|
+
prev_index = previous_non_trivia_token_index(tokens, index)
|
|
611
|
+
return false unless prev_index
|
|
612
|
+
return false unless %i[colon arrow].include?(tokens[prev_index].type)
|
|
613
|
+
|
|
614
|
+
tokens[prev_index].line == tok.line
|
|
615
|
+
end
|
|
616
|
+
|
|
553
617
|
def destructure_let_binding?(tokens, index)
|
|
554
618
|
tok = tokens[index]
|
|
555
619
|
return false unless tok&.type == :identifier
|
data/lib/milk_tea/tooling/cli.rb
CHANGED
|
@@ -1611,6 +1611,7 @@ module MilkTea
|
|
|
1611
1611
|
input_path = nil
|
|
1612
1612
|
theme_path = nil
|
|
1613
1613
|
output_path = nil
|
|
1614
|
+
textmate_only = false
|
|
1614
1615
|
|
|
1615
1616
|
until @argv.empty?
|
|
1616
1617
|
arg = @argv.first
|
|
@@ -1629,6 +1630,9 @@ module MilkTea
|
|
|
1629
1630
|
@err.puts("snapshot: missing value for --output")
|
|
1630
1631
|
return 1
|
|
1631
1632
|
end
|
|
1633
|
+
when "--textmate-only"
|
|
1634
|
+
@argv.shift
|
|
1635
|
+
textmate_only = true
|
|
1632
1636
|
else
|
|
1633
1637
|
if arg.start_with?("-")
|
|
1634
1638
|
@err.puts("snapshot: unknown option #{arg}")
|
|
@@ -1674,10 +1678,12 @@ module MilkTea
|
|
|
1674
1678
|
args.push("-o", output_path) if output_path
|
|
1675
1679
|
|
|
1676
1680
|
semantic_result = nil
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
+
unless textmate_only
|
|
1682
|
+
begin
|
|
1683
|
+
semantic_result = MilkTea::LSP::Server.semantic_tokens_for_path(input_path)
|
|
1684
|
+
rescue => e
|
|
1685
|
+
@err.puts("snapshot: semantic analysis skipped: #{e.message}")
|
|
1686
|
+
end
|
|
1681
1687
|
end
|
|
1682
1688
|
|
|
1683
1689
|
if semantic_result && semantic_result[:entries] && !semantic_result[:entries].empty?
|
|
@@ -150,7 +150,19 @@ module MilkTea
|
|
|
150
150
|
column: statement.column,
|
|
151
151
|
var: statement.kind == :var
|
|
152
152
|
)
|
|
153
|
-
|
|
153
|
+
if statement.else_body || statement.else_binding
|
|
154
|
+
with_scope do
|
|
155
|
+
if statement.else_binding
|
|
156
|
+
declare_local(
|
|
157
|
+
statement.else_binding.name,
|
|
158
|
+
statement.else_binding.line,
|
|
159
|
+
column: statement.else_binding.column,
|
|
160
|
+
var: false
|
|
161
|
+
)
|
|
162
|
+
end
|
|
163
|
+
visit_statement_list(statement.else_body) if statement.else_body
|
|
164
|
+
end
|
|
165
|
+
end
|
|
154
166
|
check_redundant_type_annotation(statement)
|
|
155
167
|
flag_redundant_widening_cast(statement.value) if statement.type && statement.value
|
|
156
168
|
record_ptr_candidate(statement)
|
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.12
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Long (Teefan) Tran
|
|
@@ -603,7 +603,7 @@ metadata:
|
|
|
603
603
|
homepage_uri: https://teefan.github.io/mt-lang/
|
|
604
604
|
source_code_uri: https://github.com/teefan/mt-lang
|
|
605
605
|
post_install_message: |
|
|
606
|
-
Milk Tea 0.3.
|
|
606
|
+
Milk Tea 0.3.12 installed!
|
|
607
607
|
|
|
608
608
|
System requirements:
|
|
609
609
|
- A C compiler (gcc or clang) must be available on PATH
|