mt-lang 0.2.0
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 +7 -0
- data/.ruby-version +1 -0
- data/AUTHORS +3 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +82 -0
- data/LICENSE +21 -0
- data/README.md +1208 -0
- data/Rakefile +332 -0
- data/bin/mtc +8 -0
- data/bin/profile-mtc-checks +133 -0
- data/bin/tracy-profiler +0 -0
- data/docs/build-guide.md +536 -0
- data/docs/index.html +2778 -0
- data/docs/language-design.md +1519 -0
- data/docs/language-manual.md +1534 -0
- data/lib/milk_tea/base.rb +49 -0
- data/lib/milk_tea/bindings/bindgen/ast_parser.rb +398 -0
- data/lib/milk_tea/bindings/bindgen/declaration.rb +319 -0
- data/lib/milk_tea/bindings/bindgen/emitter.rb +387 -0
- data/lib/milk_tea/bindings/bindgen/overrides.rb +134 -0
- data/lib/milk_tea/bindings/bindgen/type_mapper.rb +622 -0
- data/lib/milk_tea/bindings/bindgen.rb +207 -0
- data/lib/milk_tea/bindings/cli.rb +121 -0
- data/lib/milk_tea/bindings/imported_bindings/defaults.rb +210 -0
- data/lib/milk_tea/bindings/imported_bindings/generator.rb +1114 -0
- data/lib/milk_tea/bindings/imported_bindings/method_source.rb +190 -0
- data/lib/milk_tea/bindings/imported_bindings/naming.rb +286 -0
- data/lib/milk_tea/bindings/imported_bindings.rb +215 -0
- data/lib/milk_tea/bindings/opengl_registry.rb +547 -0
- data/lib/milk_tea/bindings/raw_bindings/defaults.rb +1338 -0
- data/lib/milk_tea/bindings/raw_bindings.rb +269 -0
- data/lib/milk_tea/bindings/steamworks.rb +629 -0
- data/lib/milk_tea/bindings/upstream_sources.rb +352 -0
- data/lib/milk_tea/bindings/vendored_box2d.rb +79 -0
- data/lib/milk_tea/bindings/vendored_c_library.rb +322 -0
- data/lib/milk_tea/bindings/vendored_cjson.rb +52 -0
- data/lib/milk_tea/bindings/vendored_flecs.rb +78 -0
- data/lib/milk_tea/bindings/vendored_glfw.rb +77 -0
- data/lib/milk_tea/bindings/vendored_libuv.rb +75 -0
- data/lib/milk_tea/bindings/vendored_pcre2.rb +84 -0
- data/lib/milk_tea/bindings/vendored_raylib.rb +131 -0
- data/lib/milk_tea/bindings/vendored_sdl3.rb +78 -0
- data/lib/milk_tea/bindings/vendored_steamworks.rb +71 -0
- data/lib/milk_tea/bindings/vendored_tool.rb +71 -0
- data/lib/milk_tea/bindings/vendored_tools.rb +23 -0
- data/lib/milk_tea/bindings/vendored_tracy.rb +37 -0
- data/lib/milk_tea/bindings.rb +23 -0
- data/lib/milk_tea/core/ast.rb +311 -0
- data/lib/milk_tea/core/async_runtime_installer.rb +28 -0
- data/lib/milk_tea/core/binding_types.rb +18 -0
- data/lib/milk_tea/core/bindings/attribute_binding.rb +58 -0
- data/lib/milk_tea/core/bindings/function_binding.rb +5 -0
- data/lib/milk_tea/core/bindings/module_binding.rb +58 -0
- data/lib/milk_tea/core/bindings/value_binding.rb +21 -0
- data/lib/milk_tea/core/c_backend/aggregate_utils.rb +103 -0
- data/lib/milk_tea/core/c_backend/control_flow_emission.rb +412 -0
- data/lib/milk_tea/core/c_backend/expressions.rb +468 -0
- data/lib/milk_tea/core/c_backend/feature_detection.rb +483 -0
- data/lib/milk_tea/core/c_backend/reachability.rb +398 -0
- data/lib/milk_tea/core/c_backend/reinterpret.rb +226 -0
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1080 -0
- data/lib/milk_tea/core/c_backend/statements.rb +563 -0
- data/lib/milk_tea/core/c_backend/type_collectors.rb +1545 -0
- data/lib/milk_tea/core/c_backend/type_declaration.rb +223 -0
- data/lib/milk_tea/core/c_backend/type_system.rb +345 -0
- data/lib/milk_tea/core/c_backend.rb +287 -0
- data/lib/milk_tea/core/compatibility_helpers.rb +79 -0
- data/lib/milk_tea/core/compile_time/const_eval.rb +187 -0
- data/lib/milk_tea/core/compile_time.rb +329 -0
- data/lib/milk_tea/core/control_flow/builder.rb +582 -0
- data/lib/milk_tea/core/control_flow/constant_propagation.rb +143 -0
- data/lib/milk_tea/core/control_flow/dataflow.rb +74 -0
- data/lib/milk_tea/core/control_flow/definite_assignment.rb +79 -0
- data/lib/milk_tea/core/control_flow/graph.rb +90 -0
- data/lib/milk_tea/core/control_flow/liveness.rb +24 -0
- data/lib/milk_tea/core/control_flow/nullability_flow.rb +43 -0
- data/lib/milk_tea/core/control_flow/reachability.rb +22 -0
- data/lib/milk_tea/core/control_flow/termination.rb +31 -0
- data/lib/milk_tea/core/control_flow.rb +34 -0
- data/lib/milk_tea/core/cst.rb +48 -0
- data/lib/milk_tea/core/cst_builder.rb +19 -0
- data/lib/milk_tea/core/ir.rb +85 -0
- data/lib/milk_tea/core/keywords.rb +97 -0
- data/lib/milk_tea/core/lexer/character_classes.rb +60 -0
- data/lib/milk_tea/core/lexer/format_strings.rb +225 -0
- data/lib/milk_tea/core/lexer/heredocs.rb +199 -0
- data/lib/milk_tea/core/lexer/indentation.rb +62 -0
- data/lib/milk_tea/core/lexer/numbers.rb +96 -0
- data/lib/milk_tea/core/lexer/recovery.rb +32 -0
- data/lib/milk_tea/core/lexer/strings.rb +167 -0
- data/lib/milk_tea/core/lexer/symbols.rb +71 -0
- data/lib/milk_tea/core/lexer/trivia.rb +53 -0
- data/lib/milk_tea/core/lexer.rb +430 -0
- data/lib/milk_tea/core/lowering/artifacts.rb +26 -0
- data/lib/milk_tea/core/lowering/async/analysis.rb +245 -0
- data/lib/milk_tea/core/lowering/async/lowering.rb +1399 -0
- data/lib/milk_tea/core/lowering/async/normalization.rb +459 -0
- data/lib/milk_tea/core/lowering/async.rb +714 -0
- data/lib/milk_tea/core/lowering/block.rb +1052 -0
- data/lib/milk_tea/core/lowering/calls.rb +1565 -0
- data/lib/milk_tea/core/lowering/declarations.rb +214 -0
- data/lib/milk_tea/core/lowering/dyn.rb +206 -0
- data/lib/milk_tea/core/lowering/events.rb +1054 -0
- data/lib/milk_tea/core/lowering/expressions.rb +1645 -0
- data/lib/milk_tea/core/lowering/foreign_cstr.rb +206 -0
- data/lib/milk_tea/core/lowering/functions.rb +242 -0
- data/lib/milk_tea/core/lowering/loops.rb +1087 -0
- data/lib/milk_tea/core/lowering/lowering_context.rb +80 -0
- data/lib/milk_tea/core/lowering/proc.rb +419 -0
- data/lib/milk_tea/core/lowering/resolve.rb +2516 -0
- data/lib/milk_tea/core/lowering/scans.rb +220 -0
- data/lib/milk_tea/core/lowering/str_buffer.rb +125 -0
- data/lib/milk_tea/core/lowering/utils.rb +1453 -0
- data/lib/milk_tea/core/lowering.rb +378 -0
- data/lib/milk_tea/core/module_binder.rb +181 -0
- data/lib/milk_tea/core/module_loader/errors.rb +21 -0
- data/lib/milk_tea/core/module_loader.rb +479 -0
- data/lib/milk_tea/core/module_path_resolver.rb +153 -0
- data/lib/milk_tea/core/module_roots.rb +88 -0
- data/lib/milk_tea/core/parser/attributes.rb +71 -0
- data/lib/milk_tea/core/parser/blocks.rb +119 -0
- data/lib/milk_tea/core/parser/declarations.rb +749 -0
- data/lib/milk_tea/core/parser/expressions.rb +624 -0
- data/lib/milk_tea/core/parser/recovery.rb +131 -0
- data/lib/milk_tea/core/parser/statements.rb +756 -0
- data/lib/milk_tea/core/parser/types.rb +271 -0
- data/lib/milk_tea/core/parser.rb +400 -0
- data/lib/milk_tea/core/prelude_installer.rb +29 -0
- data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +917 -0
- data/lib/milk_tea/core/pretty_printer/base_formatter.rb +87 -0
- data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +300 -0
- data/lib/milk_tea/core/pretty_printer.rb +17 -0
- data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +314 -0
- data/lib/milk_tea/core/semantic_analyzer/attributes.rb +184 -0
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +992 -0
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1735 -0
- data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +355 -0
- data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +155 -0
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +354 -0
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +383 -0
- data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +78 -0
- data/lib/milk_tea/core/semantic_analyzer/module_context.rb +35 -0
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +1438 -0
- data/lib/milk_tea/core/semantic_analyzer/nullability.rb +421 -0
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +1308 -0
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +588 -0
- data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +307 -0
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +851 -0
- data/lib/milk_tea/core/semantic_analyzer.rb +327 -0
- data/lib/milk_tea/core/token.rb +26 -0
- data/lib/milk_tea/core/token_stream.rb +30 -0
- data/lib/milk_tea/core/types/layout.rb +243 -0
- data/lib/milk_tea/core/types/predicates.rb +609 -0
- data/lib/milk_tea/core/types/registry.rb +83 -0
- data/lib/milk_tea/core/types/types.rb +1696 -0
- data/lib/milk_tea/core/types/visitor.rb +442 -0
- data/lib/milk_tea/core.rb +25 -0
- data/lib/milk_tea/dap/backends/lldb_dap.rb +158 -0
- data/lib/milk_tea/dap/protocol.rb +58 -0
- data/lib/milk_tea/dap/server/breakpoints.rb +66 -0
- data/lib/milk_tea/dap/server/debug_map.rb +291 -0
- data/lib/milk_tea/dap/server/handlers.rb +374 -0
- data/lib/milk_tea/dap/server/launch.rb +261 -0
- data/lib/milk_tea/dap/server/lldb_backend.rb +380 -0
- data/lib/milk_tea/dap/server/pause_diagnostics.rb +109 -0
- data/lib/milk_tea/dap/server/utilities.rb +160 -0
- data/lib/milk_tea/dap/server/wire.rb +55 -0
- data/lib/milk_tea/dap/server.rb +131 -0
- data/lib/milk_tea/dap/session.rb +152 -0
- data/lib/milk_tea/dap.rb +6 -0
- data/lib/milk_tea/lsp/dependency_resolution.rb +52 -0
- data/lib/milk_tea/lsp/diagnostics.rb +611 -0
- data/lib/milk_tea/lsp/protocol.rb +104 -0
- data/lib/milk_tea/lsp/server/call_hierarchy.rb +274 -0
- data/lib/milk_tea/lsp/server/code_actions.rb +446 -0
- data/lib/milk_tea/lsp/server/code_lens.rb +97 -0
- data/lib/milk_tea/lsp/server/completion.rb +1099 -0
- data/lib/milk_tea/lsp/server/configuration.rb +167 -0
- data/lib/milk_tea/lsp/server/debug_info.rb +45 -0
- data/lib/milk_tea/lsp/server/definition.rb +779 -0
- data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +239 -0
- data/lib/milk_tea/lsp/server/execute_command.rb +25 -0
- data/lib/milk_tea/lsp/server/folding_range.rb +153 -0
- data/lib/milk_tea/lsp/server/formatting.rb +575 -0
- data/lib/milk_tea/lsp/server/hover.rb +1465 -0
- data/lib/milk_tea/lsp/server/inlay_hints.rb +204 -0
- data/lib/milk_tea/lsp/server/lifecycle.rb +234 -0
- data/lib/milk_tea/lsp/server/linked_editing_range.rb +43 -0
- data/lib/milk_tea/lsp/server/on_type_formatting.rb +73 -0
- data/lib/milk_tea/lsp/server/progress.rb +47 -0
- data/lib/milk_tea/lsp/server/references.rb +433 -0
- data/lib/milk_tea/lsp/server/rename.rb +598 -0
- data/lib/milk_tea/lsp/server/selection_range.rb +130 -0
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +1745 -0
- data/lib/milk_tea/lsp/server/signature_help.rb +200 -0
- data/lib/milk_tea/lsp/server/text_documents.rb +125 -0
- data/lib/milk_tea/lsp/server/type_hierarchy.rb +167 -0
- data/lib/milk_tea/lsp/server/utilities.rb +520 -0
- data/lib/milk_tea/lsp/server.rb +415 -0
- data/lib/milk_tea/lsp/workspace/analysis.rb +209 -0
- data/lib/milk_tea/lsp/workspace/caches.rb +260 -0
- data/lib/milk_tea/lsp/workspace/collection.rb +98 -0
- data/lib/milk_tea/lsp/workspace/definition_index.rb +184 -0
- data/lib/milk_tea/lsp/workspace/dependency_graph.rb +282 -0
- data/lib/milk_tea/lsp/workspace/store.rb +154 -0
- data/lib/milk_tea/lsp/workspace/utilities.rb +490 -0
- data/lib/milk_tea/lsp/workspace.rb +127 -0
- data/lib/milk_tea/lsp.rb +13 -0
- data/lib/milk_tea/packages/atomic_write.rb +63 -0
- data/lib/milk_tea/packages/dependency_solver.rb +194 -0
- data/lib/milk_tea/packages/graph.rb +139 -0
- data/lib/milk_tea/packages/lock.rb +421 -0
- data/lib/milk_tea/packages/manager_cli.rb +485 -0
- data/lib/milk_tea/packages/manifest.rb +356 -0
- data/lib/milk_tea/packages/manifest_editor.rb +134 -0
- data/lib/milk_tea/packages/registry_metadata_provider.rb +34 -0
- data/lib/milk_tea/packages/registry_store.rb +420 -0
- data/lib/milk_tea/packages/services.rb +41 -0
- data/lib/milk_tea/packages/source_cache.rb +71 -0
- data/lib/milk_tea/packages/source_fetcher.rb +124 -0
- data/lib/milk_tea/packages/source_resolver.rb +389 -0
- data/lib/milk_tea/packages/version.rb +164 -0
- data/lib/milk_tea/packages.rb +16 -0
- data/lib/milk_tea/tooling/asset_pack.rb +141 -0
- data/lib/milk_tea/tooling/build.rb +1124 -0
- data/lib/milk_tea/tooling/build_cache.rb +240 -0
- data/lib/milk_tea/tooling/cli.rb +2688 -0
- data/lib/milk_tea/tooling/cst_formatter.rb +13 -0
- data/lib/milk_tea/tooling/debug_info_formatter.rb +456 -0
- data/lib/milk_tea/tooling/debug_map.rb +248 -0
- data/lib/milk_tea/tooling/docs_app.rb +369 -0
- data/lib/milk_tea/tooling/error_formatter.rb +86 -0
- data/lib/milk_tea/tooling/formatter.rb +787 -0
- data/lib/milk_tea/tooling/linter/doc_tags.rb +212 -0
- data/lib/milk_tea/tooling/linter/fix_engine.rb +237 -0
- data/lib/milk_tea/tooling/linter/flow_rules.rb +380 -0
- data/lib/milk_tea/tooling/linter/imports_platform.rb +841 -0
- data/lib/milk_tea/tooling/linter/release_rules.rb +744 -0
- data/lib/milk_tea/tooling/linter/reserved_names.rb +199 -0
- data/lib/milk_tea/tooling/linter/rules.rb +593 -0
- data/lib/milk_tea/tooling/linter/source_helpers.rb +407 -0
- data/lib/milk_tea/tooling/linter/trailing_comma.rb +139 -0
- data/lib/milk_tea/tooling/linter/visitors.rb +1286 -0
- data/lib/milk_tea/tooling/linter.rb +798 -0
- data/lib/milk_tea/tooling/project_scaffold.rb +82 -0
- data/lib/milk_tea/tooling/public/css/docs.css +166 -0
- data/lib/milk_tea/tooling/public/js/docs.js +94 -0
- data/lib/milk_tea/tooling/run.rb +408 -0
- data/lib/milk_tea/tooling/templates/wasm_shell.html +48 -0
- data/lib/milk_tea/tooling/toolchain_cli.rb +157 -0
- data/lib/milk_tea/tooling/views/404.erb +7 -0
- data/lib/milk_tea/tooling/views/index.erb +87 -0
- data/lib/milk_tea/tooling/views/layout.erb +69 -0
- data/lib/milk_tea/tooling/views/module.erb +97 -0
- data/lib/milk_tea/tooling/views/stdlib.erb +21 -0
- data/lib/milk_tea/tooling.rb +20 -0
- data/lib/milk_tea.rb +8 -0
- metadata +426 -0
|
@@ -0,0 +1,1534 @@
|
|
|
1
|
+
# Milk Tea Language Manual
|
|
2
|
+
|
|
3
|
+
This manual documents the Milk Tea language as implemented today in the lexer, parser, semantic checker, and compiler tests.
|
|
4
|
+
|
|
5
|
+
Package manifests and build or run workflow are documented separately in `docs/build-guide.md`.
|
|
6
|
+
|
|
7
|
+
## 1. Source Files And Modules
|
|
8
|
+
|
|
9
|
+
Milk Tea source files use the `.mt` extension.
|
|
10
|
+
|
|
11
|
+
A file can be either:
|
|
12
|
+
|
|
13
|
+
- an ordinary source file (the normal source form; no header; module name comes from the path)
|
|
14
|
+
- an external file (`external`) for raw ABI bindings
|
|
15
|
+
|
|
16
|
+
### 1.1 Ordinary file
|
|
17
|
+
|
|
18
|
+
```mt
|
|
19
|
+
function main() -> int:
|
|
20
|
+
return 0
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### 1.2 External file
|
|
24
|
+
|
|
25
|
+
```mt
|
|
26
|
+
external
|
|
27
|
+
|
|
28
|
+
include "raylib.h"
|
|
29
|
+
link "raylib"
|
|
30
|
+
|
|
31
|
+
struct Color:
|
|
32
|
+
r: ubyte
|
|
33
|
+
g: ubyte
|
|
34
|
+
b: ubyte
|
|
35
|
+
a: ubyte
|
|
36
|
+
|
|
37
|
+
external function InitWindow(width: int, height: int, title: cstr) -> void
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Rules:
|
|
41
|
+
|
|
42
|
+
- In ordinary files, `import` statements are parsed only at the top.
|
|
43
|
+
- In external files, leading `import` statements are allowed after `external`.
|
|
44
|
+
- Only external files accept `include`, `link`, and `compiler_flag` directives.
|
|
45
|
+
- External files are the dedicated raw ABI surface, usually for `std.c.*` bindings and bindgen output.
|
|
46
|
+
- After leading imports and directives, external files are intentionally narrow: they contain raw ABI declarations, not ordinary module logic.
|
|
47
|
+
- Module lookup resolves `a.b.c` to `a/b/c.mt`.
|
|
48
|
+
- Module identity is inferred from the resolved source path.
|
|
49
|
+
- Platform-specific file variants are a compiler resolution rule, not a source-language import feature.
|
|
50
|
+
- For an active target platform `P`, the compiler resolves `a.b.c` by preferring `a/b/c.P.mt` and falling back to `a/b/c.mt`.
|
|
51
|
+
- Valid platform filename suffixes are `linux`, `windows`, and `wasm`.
|
|
52
|
+
- The platform suffix is not part of the module name. `import a.b.c` stays the same on every target.
|
|
53
|
+
- Milk Tea does not have a source-level conditional compilation syntax such as `#if`, `#ifdef`, or per-declaration platform attributes.
|
|
54
|
+
|
|
55
|
+
## 2. Lexical Rules
|
|
56
|
+
|
|
57
|
+
### 2.1 Indentation and newlines
|
|
58
|
+
|
|
59
|
+
- Blocks are indentation-based.
|
|
60
|
+
- `:` starts a block.
|
|
61
|
+
- Indentation must be spaces only.
|
|
62
|
+
- Tabs are rejected.
|
|
63
|
+
- Indentation must be a multiple of 4 spaces.
|
|
64
|
+
- Indentation can increase by only one level (4 spaces) at a time.
|
|
65
|
+
- Newlines end statements, except while inside `()` or `[]`, or when the previous physical line ends with a binary operator.
|
|
66
|
+
|
|
67
|
+
For long expressions, prefer delimiter-based wrapping:
|
|
68
|
+
|
|
69
|
+
```mt
|
|
70
|
+
let total = (
|
|
71
|
+
subtotal
|
|
72
|
+
+ tax
|
|
73
|
+
- discount
|
|
74
|
+
)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Milk Tea also accepts continuation after a line-ending binary operator:
|
|
78
|
+
|
|
79
|
+
```mt
|
|
80
|
+
let total = subtotal +
|
|
81
|
+
tax -
|
|
82
|
+
discount
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Starting a new physical line with the operator is not part of the supported source contract; wrap in `()` instead if that layout is clearer.
|
|
86
|
+
|
|
87
|
+
### 2.2 Comments
|
|
88
|
+
|
|
89
|
+
- `#` starts a line comment.
|
|
90
|
+
|
|
91
|
+
Documentation comments use `##` and attach to the nearest next declaration
|
|
92
|
+
without a blank line in between.
|
|
93
|
+
|
|
94
|
+
```mt
|
|
95
|
+
## Draws a colorful triangle strip.
|
|
96
|
+
## Values are normalized to screen center.
|
|
97
|
+
function draw_strip() -> void:
|
|
98
|
+
return
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Rules for documentation comments:
|
|
102
|
+
|
|
103
|
+
- Only lines that start with `##` are documentation.
|
|
104
|
+
- Contiguous `##` lines form one markdown block.
|
|
105
|
+
- A blank line breaks attachment.
|
|
106
|
+
- Plain `#` comments are ignored by hover documentation.
|
|
107
|
+
- Documentation attaches only to declarations (`function`, `struct`, `union`, `enum`, `flags`, `variant`, `type`, `const`, `var`, `let`, `extending`, `opaque`, `interface`).
|
|
108
|
+
|
|
109
|
+
### 2.3 Literals
|
|
110
|
+
|
|
111
|
+
Supported literals:
|
|
112
|
+
|
|
113
|
+
- integer: `42`, `0xff`, `0b1010`, with `_` separators. Integer type suffixes: `42u` (`uint`), `0xFFub` (`ubyte`), `100z` (`ptr_uint`), `7i` (`int`), `-1l` (`long`), etc.
|
|
114
|
+
- float: `3.14`, `1.2e-3`, `1.1920929E-7`, `1.0f` (float suffix), `1.0d` (double suffix)
|
|
115
|
+
- character: `'a'`, `'\n'`, `'\t'`, `'\\'`, `'\''`, `'\0'`, `'\x41'`. Type is `ubyte`. Escape sequences: `\n`, `\r`, `\t`, `\\`, `\'`, `\"`, `\0` (null), `\xNN` (hex byte).
|
|
116
|
+
- string: `"hello"` (`str`). Supported string escapes are `\n`, `\r`, `\t`, `\0` (null), `\"`, `\'`, and `\\`; any other `\x` sequence is taken literally. Hex byte escapes (`\xNN`) are character-literal only, not string-literal.
|
|
117
|
+
- cstring: `c"hello"` (`cstr`)
|
|
118
|
+
- heredoc string: `<<-TAG ... TAG` (`str`)
|
|
119
|
+
- heredoc cstring: `c<<-TAG ... TAG` (`cstr`)
|
|
120
|
+
- format string: `f"count=#{count}"`
|
|
121
|
+
- booleans: `true`, `false`
|
|
122
|
+
- null: `null`, typed null `null[ptr[char]]`
|
|
123
|
+
|
|
124
|
+
### 2.4 Operators and punctuation
|
|
125
|
+
|
|
126
|
+
Symbols:
|
|
127
|
+
|
|
128
|
+
- delimiters: `(` `)` `[` `]`
|
|
129
|
+
- separators/access: `:` `,` `.`
|
|
130
|
+
- type markers: `->` `?`
|
|
131
|
+
- arithmetic: `+ - * / %`
|
|
132
|
+
- bitwise: `~ & | ^ << >>`
|
|
133
|
+
- comparison: `== != < <= > >=`
|
|
134
|
+
- assignment: `= += -= *= /= %= &= |= ^= <<= >>=`
|
|
135
|
+
- variadic marker: `...`
|
|
136
|
+
|
|
137
|
+
Word operators:
|
|
138
|
+
|
|
139
|
+
- `and`, `or`, `not`
|
|
140
|
+
- `is` — variant arm membership test (`expr is Variant.Arm` returns `bool`; desugars to a `match` expression)
|
|
141
|
+
- `in`, `out`, `inout` (reserved for `foreign function` parameter modes; legacy call-site forms are rejected semantically)
|
|
142
|
+
|
|
143
|
+
## 3. Declarations
|
|
144
|
+
|
|
145
|
+
Top-level declarations:
|
|
146
|
+
|
|
147
|
+
- `const`
|
|
148
|
+
- `const function` — compile-time-evaluable function
|
|
149
|
+
- `var`
|
|
150
|
+
- `type`
|
|
151
|
+
- `attribute`
|
|
152
|
+
- `interface`
|
|
153
|
+
- `struct`
|
|
154
|
+
- `union`
|
|
155
|
+
- `variant`
|
|
156
|
+
- `enum`
|
|
157
|
+
- `flags`
|
|
158
|
+
- `opaque`
|
|
159
|
+
- `extending`
|
|
160
|
+
- `function`
|
|
161
|
+
- `async function`
|
|
162
|
+
- `external function`
|
|
163
|
+
- `foreign function`
|
|
164
|
+
- `event`
|
|
165
|
+
- `static_assert(...)`
|
|
166
|
+
- `emit` — compile-time code generation inside `const function` or `inline` bodies
|
|
167
|
+
- `when` — compile-time conditional; may appear at module level or inside function bodies
|
|
168
|
+
|
|
169
|
+
File-kind note:
|
|
170
|
+
|
|
171
|
+
- Ordinary files may use the full declaration surface above.
|
|
172
|
+
- External files use a restricted declaration surface: `const`, `type`, `struct`, `union`, `enum`, `flags`, `opaque`, and `external function`. External files cannot declare new attributes, but supported attribute applications such as `@[packed]` and `@[align(...)]` may still appear on declarations that accept them.
|
|
173
|
+
- `event` declarations are not allowed in external files.
|
|
174
|
+
|
|
175
|
+
### 3.1 Visibility
|
|
176
|
+
|
|
177
|
+
- `public` is supported for exportable ordinary declarations.
|
|
178
|
+
- `public` is rejected on `extending` blocks.
|
|
179
|
+
- `public` is rejected on ordinary `external` declarations and `static_assert`.
|
|
180
|
+
- In external files, declarations are implicitly exported and `public` is rejected.
|
|
181
|
+
|
|
182
|
+
### 3.2 Constants and variables
|
|
183
|
+
|
|
184
|
+
```mt
|
|
185
|
+
const WIDTH: int = 1280
|
|
186
|
+
var counter: int = 0
|
|
187
|
+
var scratch: array[ubyte, 256]
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Block-bodied `const` uses `->` instead of `:`:
|
|
191
|
+
|
|
192
|
+
```mt
|
|
193
|
+
const NEXT_POW2 -> int:
|
|
194
|
+
var n: int = 1
|
|
195
|
+
while n < 1024:
|
|
196
|
+
n = n * 2
|
|
197
|
+
return n
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
The block body is evaluated at compile time. Allowed inside the block: literals, names of other `const` values, arithmetic, control flow (`if`/`else if`/`else`, `while`, `for`), `let` and `var` declarations, calls to other compile-time functions, and calls to whitelisted builtins (`size_of`, `align_of`, `offset_of`, `fields_of`, `members_of`, `attributes_of`).
|
|
201
|
+
|
|
202
|
+
Rules:
|
|
203
|
+
|
|
204
|
+
- `const` requires explicit type and initializer.
|
|
205
|
+
- A block-bodied form `const NAME -> TYPE:` followed by an indented block is also supported. The block is evaluated at compile time and must end with a `return` on every code path.
|
|
206
|
+
- Top-level `var` requires explicit type; initializer is optional.
|
|
207
|
+
- Top-level `var` initializer must be static-storage-safe.
|
|
208
|
+
- Local declarations:
|
|
209
|
+
- `let` is immutable
|
|
210
|
+
- `var` is mutable
|
|
211
|
+
- A local declaration without initializer requires explicit type and must be zero-initializable.
|
|
212
|
+
- `let` and `var` declarations may use a guard form over nullable values, `Option[T]`, and `Result[T, E]`:
|
|
213
|
+
|
|
214
|
+
```mt
|
|
215
|
+
let window = maybe_window else:
|
|
216
|
+
return 1
|
|
217
|
+
|
|
218
|
+
let image = load_image(path) else:
|
|
219
|
+
return 1
|
|
220
|
+
|
|
221
|
+
var runtime = maybe_runtime else:
|
|
222
|
+
return 1
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Rules for `let ... else:` and `var ... else:`:
|
|
226
|
+
|
|
227
|
+
- both `let` and `var` support an `else` block
|
|
228
|
+
- the initializer must have type `T?`, `Option[T]`, or `Result[T, E]`
|
|
229
|
+
- an explicit type annotation, if present, must name the success type `T`
|
|
230
|
+
- for `Option[T]`, the bound name is the `some.value`
|
|
231
|
+
- for `Result[T, E]`, the bound name is the `success.value`
|
|
232
|
+
- `let _ = expr else:` discards the success value and does not introduce a local binding
|
|
233
|
+
- for `Result[T, E]`, `else as error:` optionally binds the `failure.error` value inside the `else` block
|
|
234
|
+
- `Result[void, E]` uses the same surface via `let _ = expr else:`
|
|
235
|
+
- the `else` block must exit control flow (`return`, `break`, `continue`, or another terminating path)
|
|
236
|
+
|
|
237
|
+
Tuple and struct destructuring:
|
|
238
|
+
|
|
239
|
+
```mt
|
|
240
|
+
let (a, b) = pair()
|
|
241
|
+
let Vec2(x, y) = get_position()
|
|
242
|
+
let (_, _, title) = triple # _ discard may repeat
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Tuple destructuring binds each element by position. Struct destructuring binds each field by name against the source struct's declared field order.
|
|
246
|
+
|
|
247
|
+
Postfix Result/Option propagation:
|
|
248
|
+
|
|
249
|
+
```mt
|
|
250
|
+
let parsed = parse(input)?
|
|
251
|
+
let lowered = lower(parsed)?
|
|
252
|
+
return Result[Output, Error].success(value= lowered)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
- `expr?` requires `Option[T]` or `Result[T, E]` with a non-`void` success type. Any variant with matching arm structure (`some(value: T)`/`none` or `success(value: T)`/`failure(error: E)`) also works.
|
|
256
|
+
- on success, `expr?` evaluates to the unwrapped `T`
|
|
257
|
+
- on failure, `expr?` returns `Option[_].none` or `Result[_, E].failure(error= ...)` from the enclosing function or proc
|
|
258
|
+
- as an expression statement, `expr?` also accepts `Option[void]` or `Result[void, E]`; success continues and failure returns early
|
|
259
|
+
- `expr?` is only allowed inside function and proc bodies
|
|
260
|
+
- inside `async` functions, failure completes the task early
|
|
261
|
+
- `expr?` is not allowed inside `defer` blocks
|
|
262
|
+
- the enclosing function or proc must return a compatible type — `Option[_]` or `Result[_, E]` with the same error type `E`
|
|
263
|
+
- `let _ = expr else:` is still useful when you need an explicit `else` block or `else as error:` binding
|
|
264
|
+
|
|
265
|
+
### 3.3 Type aliases
|
|
266
|
+
|
|
267
|
+
```mt
|
|
268
|
+
type Seconds = float
|
|
269
|
+
type Callback = fn(level: int, message: cstr) -> void
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Callable and `ref[...]` rules:
|
|
273
|
+
|
|
274
|
+
- Plain stored `ref[T]` values are rejected in constants, module variables, and nested local storage such as arrays or other generic containers.
|
|
275
|
+
- In struct or union fields, bare `ref[T]` auto-generates an implicit lifetime parameter. The struct becomes non-owning and inherits ref-like restrictions: allowed as function params and local `let` variables, rejected as returns and module storage. Explicit lifetimes (`ref[@a, T]`) are still supported when the lifetime needs to be shared across fields.
|
|
276
|
+
- Ordinary local bindings may still hold a direct `ref[T]` value, for example `let handle = ref_of(counter)`.
|
|
277
|
+
- `fn(...)` and `proc(...)` parameter types may use `ref[...]` directly in parameter position.
|
|
278
|
+
- Stored callable values may use `ref[...]` only in direct callable parameter positions. This includes `fn(...)` values and `proc(...)` closure values stored in locals, struct fields, and generic containers such as `array[...]`.
|
|
279
|
+
- Stored callable values may not use `ref[...]` in return types.
|
|
280
|
+
- Stored callable values may not nest `ref[...]` anywhere except direct callable parameter positions.
|
|
281
|
+
- External functions still cannot take `ref[...]` parameters, and ordinary functions still cannot return `ref[...]`.
|
|
282
|
+
- `proc` captures are value captures. A captured local is not a mutable alias back to the outer binding. Any storable type may be captured, including scalars, arrays, structs, and other `proc` values. Captured `proc` values participate in the ref-counted lifecycle: the capturing proc retains the captured proc on creation and releases it when the env is freed.
|
|
283
|
+
- `ref[T]` values are not capturable by design since they are non-owning.
|
|
284
|
+
- Shared mutable proc state should use explicit storage such as `std.cell.alloc[T](...)` or other explicit pointer-backed state, not implicit mutable capture.
|
|
285
|
+
|
|
286
|
+
### 3.4 Struct, union, enum, flags, opaque
|
|
287
|
+
|
|
288
|
+
```mt
|
|
289
|
+
struct Vec2:
|
|
290
|
+
x: float
|
|
291
|
+
y: float
|
|
292
|
+
|
|
293
|
+
union Number:
|
|
294
|
+
i: int
|
|
295
|
+
f: float
|
|
296
|
+
|
|
297
|
+
enum State: ubyte
|
|
298
|
+
idle = 0
|
|
299
|
+
running = 1
|
|
300
|
+
|
|
301
|
+
# Backing type defaults to int; values auto-increment from 0:
|
|
302
|
+
enum Color:
|
|
303
|
+
red
|
|
304
|
+
green
|
|
305
|
+
blue
|
|
306
|
+
|
|
307
|
+
flags Mask: uint
|
|
308
|
+
a = 1 << 0
|
|
309
|
+
b = 1 << 1
|
|
310
|
+
|
|
311
|
+
opaque SDL_Window
|
|
312
|
+
|
|
313
|
+
variant Token:
|
|
314
|
+
ident(text: str)
|
|
315
|
+
number(value: int)
|
|
316
|
+
eof
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
`struct` and `opaque` declarations may add nominal interface conformance with `implements`:
|
|
320
|
+
|
|
321
|
+
```mt
|
|
322
|
+
struct NPC implements Damageable, Named:
|
|
323
|
+
hp: int
|
|
324
|
+
|
|
325
|
+
opaque SDL_Window implements Closable
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
`variant` is a tagged union. Each arm may optionally carry named payload fields. Generic variants are supported via type arguments, for example `Option[int]`:
|
|
329
|
+
|
|
330
|
+
```mt
|
|
331
|
+
variant Option[T]:
|
|
332
|
+
some(value: T)
|
|
333
|
+
none
|
|
334
|
+
|
|
335
|
+
variant Result[T, E]:
|
|
336
|
+
success(value: T)
|
|
337
|
+
failure(error: E)
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Arm constructors:
|
|
341
|
+
|
|
342
|
+
- Payload arm: `Token.ident(text = "hello")` — field names with `=`.
|
|
343
|
+
- No-payload arm: `Token.eof` — accessed as a bare member expression.
|
|
344
|
+
|
|
345
|
+
Declaration attributes use a leading `@[name(...)]` surface. User-defined attributes are declared with explicit targets such as `attribute[field] rename(name: str)` or `attribute[const, event] trace(name: str)`. Built-in `packed` and `align(bytes)` are predefined struct attributes; `deprecated(message: str)` is predefined and targets function, struct, const, enum, flags, union, variant, and event:
|
|
346
|
+
|
|
347
|
+
```mt
|
|
348
|
+
@[packed]
|
|
349
|
+
struct Header:
|
|
350
|
+
tag: ubyte
|
|
351
|
+
|
|
352
|
+
@[align(16)]
|
|
353
|
+
struct Mat4:
|
|
354
|
+
data: array[float, 16]
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
`align(...)` must be a positive power of two.
|
|
358
|
+
|
|
359
|
+
Compile-time reflection over validated attributes uses `has_attribute`, `attribute_of`, `attribute_arg[T]`, `field_of`, and `callable_of`.
|
|
360
|
+
|
|
361
|
+
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
|
+
|
|
363
|
+
### 3.4a Enum and flags operators
|
|
364
|
+
|
|
365
|
+
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.
|
|
366
|
+
|
|
367
|
+
```mt
|
|
368
|
+
const IS_RUNNING: bool = State.running == State.running
|
|
369
|
+
const IS_AFTER: bool = State.running > State.idle
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
Flags values also support bitwise operators (`|`, `&`, `^`, `~`) where both operands share the same flags type.
|
|
373
|
+
|
|
374
|
+
### 3.5 Interfaces
|
|
375
|
+
|
|
376
|
+
```mt
|
|
377
|
+
public interface Damageable:
|
|
378
|
+
editable function take_damage(amount: int) -> void
|
|
379
|
+
function is_alive() -> bool
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Rules:
|
|
383
|
+
|
|
384
|
+
- Interface bodies contain `function`, `editable function`, or `static function` signatures.
|
|
385
|
+
- Generic interfaces are supported: `interface Mapper[T]: function map(x: T) -> T`.
|
|
386
|
+
- Interface methods may not declare their own type params — the type params come from the interface.
|
|
387
|
+
- Interface methods may not be `async`.
|
|
388
|
+
- Interface methods do not have bodies.
|
|
389
|
+
- `public` is allowed on the interface declaration, not on individual interface methods.
|
|
390
|
+
- Bare interface names are not runtime storage types; they are contracts used by `implements` and constrained generics.
|
|
391
|
+
- Runtime interface values use the `dyn[InterfaceName]` type constructor. A `dyn[I]` value is a fat pointer carrying a data pointer and a vtable pointer. Construct with `adapt[I](value: ref[T])` which verifies `T implements I` at compile time.
|
|
392
|
+
- Conformance uses type substitution: `struct Foo implements Mapper[int]` checks methods against `Mapper` with `T = int`.
|
|
393
|
+
- Generic interfaces instantiated through `dyn` must be fully specified: `dyn[Mapper[int]]` is valid; `dyn[Mapper]` is rejected.
|
|
394
|
+
|
|
395
|
+
### 3.6 Methods
|
|
396
|
+
|
|
397
|
+
```mt
|
|
398
|
+
extending Counter:
|
|
399
|
+
function read() -> int:
|
|
400
|
+
return this.value
|
|
401
|
+
|
|
402
|
+
editable function bump() -> void:
|
|
403
|
+
this.value += 1
|
|
404
|
+
|
|
405
|
+
static function zero() -> Counter:
|
|
406
|
+
return Counter(value = 0)
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Kinds:
|
|
410
|
+
|
|
411
|
+
- `function` (value receiver)
|
|
412
|
+
- `editable function` (editable receiver)
|
|
413
|
+
- `static function` (no receiver)
|
|
414
|
+
|
|
415
|
+
Names such as `init` and `default` are ordinary static functions. There is no constructor keyword or hidden initializer syntax.
|
|
416
|
+
|
|
417
|
+
Method capabilities:
|
|
418
|
+
|
|
419
|
+
- async methods are supported
|
|
420
|
+
- generic methods are supported
|
|
421
|
+
|
|
422
|
+
### 3.7 Functions
|
|
423
|
+
|
|
424
|
+
```mt
|
|
425
|
+
function add(a: int, b: int) -> int:
|
|
426
|
+
return a + b
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
Rules:
|
|
430
|
+
|
|
431
|
+
- Parameters must be typed.
|
|
432
|
+
- Parameters are non-rebindable; copy into a local `var` to mutate by-value data.
|
|
433
|
+
- Mutation through referent surfaces (for example span element writes, `read(ref_value) = ...`, and pointer writes in `unsafe`) is allowed.
|
|
434
|
+
- Return type defaults to `void` if omitted.
|
|
435
|
+
- Generic functions are supported.
|
|
436
|
+
- Generic function and method type parameters may declare constraints with `implements`.
|
|
437
|
+
|
|
438
|
+
Examples:
|
|
439
|
+
|
|
440
|
+
```mt
|
|
441
|
+
function damage_one[T implements Damageable](target: ref[T], amount: int) -> void:
|
|
442
|
+
if target.is_alive():
|
|
443
|
+
target.take_damage(amount)
|
|
444
|
+
|
|
445
|
+
function tag[T implements Damageable and Named](target: ref[T]) -> str:
|
|
446
|
+
return target.name()
|
|
447
|
+
|
|
448
|
+
function make_default[T]() -> T:
|
|
449
|
+
return default[T]
|
|
450
|
+
|
|
451
|
+
function boot_screen[T implements ScreenState]() -> T:
|
|
452
|
+
return default[T]
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### 3.7a `const function`
|
|
456
|
+
|
|
457
|
+
A `const function` is evaluable at compile time and follows the same body restrictions as a block-bodied `const`. When called from a compile-time context, the call is constant-folded to its computed value:
|
|
458
|
+
|
|
459
|
+
```mt
|
|
460
|
+
const function square(x: int) -> int:
|
|
461
|
+
return x * x
|
|
462
|
+
|
|
463
|
+
const SQUARE_5: int = square(5) # folded to 25 at compile time
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
Rules:
|
|
467
|
+
|
|
468
|
+
- The body must be evaluable at compile time (literals, `const` values, arithmetic, `if`/`else`, `while`, `for`, `let`/`var`, calls to other `const` functions, and whitelisted builtins).
|
|
469
|
+
- Generates a normal runtime function as well — callable from ordinary runtime code.
|
|
470
|
+
- Called from `const` initializers, `when` discriminants, `inline for` bodies, and other compile-time contexts.
|
|
471
|
+
- Recursive calls between `const` functions are supported.
|
|
472
|
+
|
|
473
|
+
### 3.8 External functions
|
|
474
|
+
|
|
475
|
+
```mt
|
|
476
|
+
external function printf(format: cstr, ...) -> int
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
Raw `std.c.*` modules usually group many `external function` declarations inside an external file, but `external function` is also allowed in ordinary files for small manual ABI bridges.
|
|
480
|
+
|
|
481
|
+
Rules:
|
|
482
|
+
|
|
483
|
+
- no body
|
|
484
|
+
- variadic `...` supported
|
|
485
|
+
- cannot be generic
|
|
486
|
+
- cannot be async
|
|
487
|
+
- cannot take `ref` parameters
|
|
488
|
+
- cannot take `proc` parameters
|
|
489
|
+
- cannot take or return arrays
|
|
490
|
+
- cannot use a non-pointer value nullable (such as `int?`) as a parameter or return type; only pointer-like `T?` may cross the boundary
|
|
491
|
+
|
|
492
|
+
### 3.9 Foreign functions
|
|
493
|
+
|
|
494
|
+
```mt
|
|
495
|
+
foreign function init_window(width: int, height: int, title: str as cstr) -> void = c.InitWindow
|
|
496
|
+
foreign function load_file_data(file_name: str as cstr, out data_size: int) -> ptr[ubyte]? = c.LoadFileData
|
|
497
|
+
foreign function close_window(consuming window: Window) -> void = c.CloseWindow
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
Parameter modes:
|
|
501
|
+
|
|
502
|
+
- plain
|
|
503
|
+
- `in`
|
|
504
|
+
- `out`
|
|
505
|
+
- `inout`
|
|
506
|
+
- `consuming`
|
|
507
|
+
|
|
508
|
+
Boundary projections:
|
|
509
|
+
|
|
510
|
+
- `name: PublicType as BoundaryType`
|
|
511
|
+
|
|
512
|
+
Rules:
|
|
513
|
+
|
|
514
|
+
- `as` is only allowed on plain and `in` params.
|
|
515
|
+
- `in`, `out`, and `inout` are declared on the `foreign function` parameter; callers pass ordinary expressions or lvalues at those argument positions.
|
|
516
|
+
- Legacy imported-call syntax such as `load_file_data(path, out size)` or `set_shader_value(shader, loc, in value, kind)` is rejected semantically.
|
|
517
|
+
- consuming foreign calls must appear as top-level expression statements.
|
|
518
|
+
- foreign functions with consuming params must return `void`.
|
|
519
|
+
- a non-pointer value nullable (such as `int?`) cannot be a parameter or return type; only pointer-like `T?` may cross the boundary.
|
|
520
|
+
|
|
521
|
+
## 4. Statements
|
|
522
|
+
|
|
523
|
+
Supported statements:
|
|
524
|
+
|
|
525
|
+
- local declaration (`let`, `var`)
|
|
526
|
+
- assignment
|
|
527
|
+
- `if` / `else if` / `else`
|
|
528
|
+
- `match`
|
|
529
|
+
- `when` — compile-time conditional; only the chosen branch is type-checked and emitted
|
|
530
|
+
- `inline for` — loop over a compile-time-known array, unrolled at compile time
|
|
531
|
+
- `inline while` — loop with a compile-time-known condition, unrolled at compile time
|
|
532
|
+
- `inline match` — match with a compile-time-known scrutinee, unrolled at compile time
|
|
533
|
+
- `inline if` — if with a compile-time-known condition; only the chosen branch is type-checked and emitted
|
|
534
|
+
- `unsafe`
|
|
535
|
+
- `static_assert`
|
|
536
|
+
- `for`
|
|
537
|
+
- `while`
|
|
538
|
+
- `parallel for` — data-parallel loop dispatched across CPU cores
|
|
539
|
+
- `parallel:` — structured fork-join block with statements
|
|
540
|
+
- `pass`
|
|
541
|
+
- `break`
|
|
542
|
+
- `continue`
|
|
543
|
+
- `return`
|
|
544
|
+
- `defer`
|
|
545
|
+
- `emit` — compile-time code generation; only valid inside `const function` or `inline for/while/if/match` bodies
|
|
546
|
+
- expression statement
|
|
547
|
+
|
|
548
|
+
### 4.1 If
|
|
549
|
+
|
|
550
|
+
Condition must be `bool`.
|
|
551
|
+
|
|
552
|
+
`pass` is an explicit no-op statement for intentionally empty block bodies.
|
|
553
|
+
|
|
554
|
+
`if` supports an inline single-statement form where the body and `else` branch appear on the same line:
|
|
555
|
+
|
|
556
|
+
```mt
|
|
557
|
+
if ready: return 1 else: return 0
|
|
558
|
+
if x > 10: return "big" else if x > 5: return "med" else: return "small"
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
The inline form supports all statement kinds in the body (`return`, `let`, `var`, `defer`, assignment, expression statement, etc.). Blocks and inline form may be mixed: any branch may use either form independently.
|
|
562
|
+
|
|
563
|
+
### 4.2 Match
|
|
564
|
+
|
|
565
|
+
Scrutinee types supported:
|
|
566
|
+
|
|
567
|
+
- Enum: arm patterns must be members of that enum.
|
|
568
|
+
- Variant: arm patterns must be arms of that variant; a payload arm may bind its fields with `as name` or destructure them inline with struct patterns.
|
|
569
|
+
- Integer (`byte`, `short`, `int`, `long`, `ubyte`, `ushort`, `uint`, `ulong`, `ptr_int`, `ptr_uint`): arm patterns must be integer literals or char literals.
|
|
570
|
+
- `str`: arm patterns must be string literals (e.g., `"lex": ...`). Matches via full content comparison using the `equal` builtin.
|
|
571
|
+
- Tuple: arm patterns must be tuple literal patterns whose elements may be integer literals, char literals, string literals, booleans, or `_` discard.
|
|
572
|
+
|
|
573
|
+
`_` is a wildcard arm that matches any value not covered by preceding arms. It maps to a C `default:` case.
|
|
574
|
+
|
|
575
|
+
Multiple pattern values may share a single arm body using `|`:
|
|
576
|
+
|
|
577
|
+
```mt
|
|
578
|
+
match kind:
|
|
579
|
+
EventKind.quit | EventKind.cancel:
|
|
580
|
+
return 0
|
|
581
|
+
_:
|
|
582
|
+
return 1
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
Rules:
|
|
586
|
+
|
|
587
|
+
- For enum scrutinee: all members must be covered unless a `_` arm is present.
|
|
588
|
+
- For variant scrutinee: all arms must be covered unless a `_` arm is present; `as name` binds the payload struct for arms that have fields.
|
|
589
|
+
- For integer scrutinee: a `_` arm is required (integers are unbounded).
|
|
590
|
+
- For `str` scrutinee: a `_` arm is required (strings are unbounded). Arm patterns must be string literals. Matches via full content comparison.
|
|
591
|
+
- For tuple scrutinee: a `_` arm is required. Arm patterns must be tuple literal patterns. Each element may be an integer literal, char literal, string literal, boolean literal, or `_` discard. Arm arity must match the scrutinee tuple. A fully literal arm is checked for duplicates; arms containing `_` wildcard elements are not.
|
|
592
|
+
- Duplicate arm values (or duplicate `_`) are rejected.
|
|
593
|
+
- Match must be exhaustive (enum/variant without `_`) or include `_` (integer, str, or partial enum/variant).
|
|
594
|
+
|
|
595
|
+
`match` may also be used as an expression. Each arm provides a `:` value instead of an indented body, and all arm values must have compatible types:
|
|
596
|
+
|
|
597
|
+
```mt
|
|
598
|
+
let label = match code:
|
|
599
|
+
1: "one"
|
|
600
|
+
2: "two"
|
|
601
|
+
_: "other"
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
Statement form examples:
|
|
605
|
+
|
|
606
|
+
```mt
|
|
607
|
+
match kind:
|
|
608
|
+
EventKind.quit:
|
|
609
|
+
return 0
|
|
610
|
+
_:
|
|
611
|
+
return 1
|
|
612
|
+
|
|
613
|
+
match key_code:
|
|
614
|
+
65:
|
|
615
|
+
fire()
|
|
616
|
+
27:
|
|
617
|
+
quit()
|
|
618
|
+
_:
|
|
619
|
+
return
|
|
620
|
+
|
|
621
|
+
match ch:
|
|
622
|
+
'(':
|
|
623
|
+
open_paren()
|
|
624
|
+
')':
|
|
625
|
+
close_paren()
|
|
626
|
+
'+':
|
|
627
|
+
handle_plus()
|
|
628
|
+
_:
|
|
629
|
+
return
|
|
630
|
+
|
|
631
|
+
match token:
|
|
632
|
+
Token.ident as t:
|
|
633
|
+
use_name(t.text)
|
|
634
|
+
Token.number as n:
|
|
635
|
+
use_value(n.value)
|
|
636
|
+
Token.eof:
|
|
637
|
+
return
|
|
638
|
+
|
|
639
|
+
match color:
|
|
640
|
+
(255, 0, 0):
|
|
641
|
+
set_red()
|
|
642
|
+
(0, 255, 0):
|
|
643
|
+
set_green()
|
|
644
|
+
_:
|
|
645
|
+
return
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
#### Struct patterns in variant match arms
|
|
649
|
+
|
|
650
|
+
When a variant arm carries payload fields, those fields may be destructured inline. Inside `VariantName.arm_name(...)`, each argument is one of:
|
|
651
|
+
|
|
652
|
+
- **Binding:** a bare field name creates an immutable local bound to that field.
|
|
653
|
+
- **Discard:** `_` discards a field value without binding it. Use it to skip unneeded fields in multi-field payload arms.
|
|
654
|
+
- **Guard:** `field comparison_op expr` extracts the field and skips the arm if the condition is false. Supported operators: `==`, `!=`, `<`, `<=`, `>`, `>=`.
|
|
655
|
+
- **Equality pattern:** `field = expr` is a shorthand guard — the arm matches only when the field equals the expression value.
|
|
656
|
+
|
|
657
|
+
```mt
|
|
658
|
+
match entity:
|
|
659
|
+
Entity.player(hp > 0, position):
|
|
660
|
+
render(position)
|
|
661
|
+
Entity.player:
|
|
662
|
+
remove()
|
|
663
|
+
Entity.enemy:
|
|
664
|
+
return
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
Rules for struct patterns:
|
|
668
|
+
|
|
669
|
+
- Each field name must appear at most once per arm. `_` may appear any number of times as a positional discard.
|
|
670
|
+
- Guards and equality patterns are refutable: they do not count toward exhaustiveness. Exception: when equality patterns for an enum-typed field gatherively cover every member of the enum, the arm is considered exhaustive. An arm with only bindings and no guards counts as exhaustive.
|
|
671
|
+
- Struct patterns compose with `as name` bindings: `Entity.player(hp > 0) as p` binds both `hp` (guard-checked) and `p` (the full payload struct).
|
|
672
|
+
- Struct patterns do not apply to enum or integer match scrutinees.
|
|
673
|
+
- When a variant arm has exactly one payload field of struct type, and no pattern argument references that field name, the struct's own fields are transparently destructured. For example, `Entity.positioned(x, y)` where `positioned(loc: Pos)` destructures through `Pos` to bind `x` and `y`.
|
|
674
|
+
|
|
675
|
+
### 4.3 Loops
|
|
676
|
+
|
|
677
|
+
Single-form `for` supports:
|
|
678
|
+
|
|
679
|
+
- `start..stop` — exclusive integer range via range expression
|
|
680
|
+
- `array[T, N]`
|
|
681
|
+
- `span[T]`
|
|
682
|
+
- custom structural iterables with a non-editable zero-argument `iter()` method
|
|
683
|
+
|
|
684
|
+
Iterator protocol for custom structural iterables:
|
|
685
|
+
|
|
686
|
+
- the iterable value must expose `iter()` with no parameters
|
|
687
|
+
- the returned iterator must expose either `next() ->` nullable pointer-like item or `next() -> bool` together with `current()`
|
|
688
|
+
|
|
689
|
+
Parallel `for` is also supported:
|
|
690
|
+
|
|
691
|
+
```mt
|
|
692
|
+
for entity, position, velocity in entities, positions, velocities:
|
|
693
|
+
update(entity, position, velocity)
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
Rules for parallel `for`:
|
|
697
|
+
|
|
698
|
+
- each iterable must be an `array[T, N]` or `span[T]`
|
|
699
|
+
- ranges are not supported in the parallel form
|
|
700
|
+
- iterable counts must match the binding count
|
|
701
|
+
- static array lengths must match; span lengths are checked at runtime
|
|
702
|
+
|
|
703
|
+
`break` and `continue` must be inside loops.
|
|
704
|
+
|
|
705
|
+
### 4.3a Parallel For (data-parallel)
|
|
706
|
+
|
|
707
|
+
`parallel for` dispatches loop iterations across multiple CPU cores using real OS threads (libuv):
|
|
708
|
+
|
|
709
|
+
```mt
|
|
710
|
+
parallel for i in 0..entity_count:
|
|
711
|
+
positions[i] += velocities[i] * dt
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
Rules:
|
|
715
|
+
|
|
716
|
+
- Only range iteration is supported (`0..N`).
|
|
717
|
+
- The loop body must not contain `break`, `continue`, `return`, `defer`, or nested `parallel for`.
|
|
718
|
+
- Captured `ref[T]` values are rejected at compile time.
|
|
719
|
+
- Array captures are passed by pointer (writes affect the original); span and scalar captures are passed by value.
|
|
720
|
+
- The compiler automatically detects `parallel for` usage and links libuv for thread dispatch.
|
|
721
|
+
- Thread count is determined at runtime via CPU core detection; one chunk runs on the calling thread.
|
|
722
|
+
|
|
723
|
+
### 4.3b Parallel Blocks (structured fork-join)
|
|
724
|
+
|
|
725
|
+
`parallel:` blocks run each statement concurrently, blocking the caller until all complete:
|
|
726
|
+
|
|
727
|
+
```mt
|
|
728
|
+
parallel:
|
|
729
|
+
textures = load_textures(path)
|
|
730
|
+
sounds = load_sounds(path)
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
Rules:
|
|
734
|
+
|
|
735
|
+
- A `parallel:` block must contain at least two statements.
|
|
736
|
+
- `do` is a contextual keyword — only recognized inside `parallel:` blocks, not reserved globally.
|
|
737
|
+
- Each statement must not contain `break`, `continue`, `return`, or `defer`.
|
|
738
|
+
- The compiler enforces single-writer-or-multiple-readers: if a variable is written in one statement, no other block may access it.
|
|
739
|
+
- Captured `ref[T]` values are rejected at compile time.
|
|
740
|
+
- Each statement runs on its own OS thread (one on the calling thread, the rest on worker threads).
|
|
741
|
+
|
|
742
|
+
### 4.3c Detach and Collect (detached concurrency)
|
|
743
|
+
|
|
744
|
+
`detach` spawns work on a separate OS thread and returns a `Handle`. `gather` blocks until all handles complete:
|
|
745
|
+
|
|
746
|
+
```mt
|
|
747
|
+
let a = detach load_textures(path)
|
|
748
|
+
let b = detach load_sounds(path)
|
|
749
|
+
process_other_stuff()
|
|
750
|
+
gather a, b
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
Rules:
|
|
754
|
+
|
|
755
|
+
- `detach` is an expression returning a `Handle` — must be bound with `let` or `var`.
|
|
756
|
+
- By design, `detach` accepts only global function calls with no captured local variables. This is a deliberate boundary — it avoids lifetime concerns on detached work without ownership annotations. For passing data to a detached call, use explicit heap-allocated storage or shared-state primitives such as `std.sync`.
|
|
757
|
+
- `gather` takes one or more `Handle` values, joined in order.
|
|
758
|
+
- Captured `ref[T]` values are rejected at compile time.
|
|
759
|
+
- `detach` and `gather` are keywords. The compiler automatically links libuv for thread dispatch.
|
|
760
|
+
|
|
761
|
+
### 4.4 Defer
|
|
762
|
+
|
|
763
|
+
Forms:
|
|
764
|
+
|
|
765
|
+
```mt
|
|
766
|
+
defer cleanup()
|
|
767
|
+
|
|
768
|
+
# or
|
|
769
|
+
|
|
770
|
+
defer:
|
|
771
|
+
release_a()
|
|
772
|
+
release_b()
|
|
773
|
+
```
|
|
774
|
+
|
|
775
|
+
`return` is not allowed inside defer blocks.
|
|
776
|
+
|
|
777
|
+
### 4.5 Unsafe
|
|
778
|
+
|
|
779
|
+
Forms:
|
|
780
|
+
|
|
781
|
+
```mt
|
|
782
|
+
unsafe: pointer[0] = 1
|
|
783
|
+
let raw = unsafe: read(ptr)
|
|
784
|
+
|
|
785
|
+
# or
|
|
786
|
+
|
|
787
|
+
unsafe:
|
|
788
|
+
pointer[0] = 1
|
|
789
|
+
pointer[1] = 2
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
Unsafe context is required for raw-pointer-level operations such as:
|
|
793
|
+
|
|
794
|
+
- pointer indexing
|
|
795
|
+
- raw dereference
|
|
796
|
+
- pointer arithmetic
|
|
797
|
+
- pointer casts
|
|
798
|
+
- `reinterpret[...]`
|
|
799
|
+
|
|
800
|
+
### 4.6 Range index assignment
|
|
801
|
+
|
|
802
|
+
Contiguous indexed slices may be assigned from an expression list:
|
|
803
|
+
|
|
804
|
+
```mt
|
|
805
|
+
var buf: array[float, 4]
|
|
806
|
+
buf[0..3] = (1.0, 2.0, 3.0)
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
Rules:
|
|
810
|
+
|
|
811
|
+
- the target must be an addressable array-, span-, or pointer-indexable lvalue
|
|
812
|
+
- the index must be a range expression with integer literal bounds
|
|
813
|
+
- the range is start-inclusive and end-exclusive
|
|
814
|
+
- the right-hand side must be an expression list whose length exactly matches the range width
|
|
815
|
+
- each element must be assignable to the indexed element type
|
|
816
|
+
|
|
817
|
+
### 4.7 When (compile-time conditional)
|
|
818
|
+
|
|
819
|
+
```mt
|
|
820
|
+
when TARGET_OS:
|
|
821
|
+
TargetOs.linux:
|
|
822
|
+
return open_linux(path)
|
|
823
|
+
TargetOs.windows:
|
|
824
|
+
return open_windows(path)
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
Rules:
|
|
828
|
+
|
|
829
|
+
- The discriminant must be a compile-time constant.
|
|
830
|
+
- Only the chosen branch is type-checked and emitted. The other branches are not checked.
|
|
831
|
+
- `else` is required when the discriminant is not a finite type. `else` is optional only when the discriminant is an enum and every member is covered.
|
|
832
|
+
- `when` may appear at module level to conditionally include declarations, imports, or type definitions.
|
|
833
|
+
- There is no `when` expression form.
|
|
834
|
+
|
|
835
|
+
### 4.8 Inline for
|
|
836
|
+
|
|
837
|
+
```mt
|
|
838
|
+
inline for field in fields_of(Particle):
|
|
839
|
+
static_assert(field.type == float, "Particle fields must be float")
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
Rules:
|
|
843
|
+
|
|
844
|
+
- The iterable must be a compile-time-known array. The most common source is a reflection builtin (`fields_of`, `members_of`, `attributes_of`). A literal array is also accepted.
|
|
845
|
+
- The loop is unrolled once per element at compile time.
|
|
846
|
+
|
|
847
|
+
### 4.9 Inline while
|
|
848
|
+
|
|
849
|
+
```mt
|
|
850
|
+
inline while n < 1024:
|
|
851
|
+
n = n * 2
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
Rules:
|
|
855
|
+
|
|
856
|
+
- The condition must be a compile-time constant.
|
|
857
|
+
- The loop unrolls to a fixed number of iterations, capped at 10,000.
|
|
858
|
+
- A non-terminating `inline while` is a compile error.
|
|
859
|
+
|
|
860
|
+
### 4.10 Inline match
|
|
861
|
+
|
|
862
|
+
```mt
|
|
863
|
+
inline match TARGET_BACKEND:
|
|
864
|
+
Backend.gl:
|
|
865
|
+
gl_draw(item)
|
|
866
|
+
Backend.metal:
|
|
867
|
+
metal_draw(item)
|
|
868
|
+
Backend.vulkan:
|
|
869
|
+
vk_draw(item)
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
Rules:
|
|
873
|
+
|
|
874
|
+
- The scrutinee must be a compile-time constant.
|
|
875
|
+
- Only the chosen arm is type-checked and emitted.
|
|
876
|
+
- An `inline match` is not required to be exhaustive; unchosen arms are dropped.
|
|
877
|
+
|
|
878
|
+
### 4.11 Inline if
|
|
879
|
+
|
|
880
|
+
```mt
|
|
881
|
+
const DEBUG_RENDER: bool = false
|
|
882
|
+
|
|
883
|
+
function draw() -> void:
|
|
884
|
+
inline if DEBUG_RENDER:
|
|
885
|
+
debug_overlay()
|
|
886
|
+
else:
|
|
887
|
+
normal_draw()
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
Rules:
|
|
891
|
+
|
|
892
|
+
- The condition must be a compile-time constant `bool`.
|
|
893
|
+
- A compile-time **type comparison** is a valid condition: `T == int`, `field.type == float`, etc., where `T` is a generic type parameter (substituted at specialization) and `field.type` is a reflected field type. This enables type-based dispatch in generic bodies (e.g. `std.fmt.format_value[T]`).
|
|
894
|
+
- Only the chosen branch is type-checked and emitted. The dead branch may reference types and symbols that do not exist.
|
|
895
|
+
- `inline if` supports `else` and `else if` branches with the same dead-branch elimination.
|
|
896
|
+
|
|
897
|
+
## 5. Expressions
|
|
898
|
+
|
|
899
|
+
### 5.1 Primary
|
|
900
|
+
|
|
901
|
+
- identifier
|
|
902
|
+
- literals
|
|
903
|
+
- parenthesized expression
|
|
904
|
+
- tuple literal: `(a, b)` — positional; `(x = 1, y = 2)` — named
|
|
905
|
+
- `size_of(T)` — accepts a type name or a compile-time type expression
|
|
906
|
+
- `align_of(T)` — accepts a type name or a compile-time type expression
|
|
907
|
+
- `offset_of(T, field)` — the field argument may be a literal field name or a compile-time `field_handle` expression
|
|
908
|
+
- `proc(...) -> T: ...` — anonymous function expression, e.g. `let fn_ptr = proc(x: int) -> int: return x + 1`
|
|
909
|
+
- `proc(...) -> T: expr` — expression-bodied anonymous function, implicitly returning `expr`
|
|
910
|
+
- `if cond: a else: b`
|
|
911
|
+
- `match scrutinee: arm: value ...` — expression form of match; every arm provides a `:` value and all arm values must have compatible types
|
|
912
|
+
|
|
913
|
+
### 5.2 Postfix
|
|
914
|
+
|
|
915
|
+
- member access: `a.b`
|
|
916
|
+
- indexing: `a[i]`
|
|
917
|
+
- call: `f(x)`
|
|
918
|
+
- partial field update: `v.with(x = 10.0)` — returns a copy with specified fields replaced; supported on structs and native types (vector, matrix, quaternion)
|
|
919
|
+
- specialization: `name[T]`, `name[32]`, `mod.name[T]`
|
|
920
|
+
- explicit specialization is only accepted on bare or module-qualified names; `value.member[32](...)` remains indexed-call syntax, so value-member calls rely on inference instead of explicit literal specialization
|
|
921
|
+
- variant membership test: `expr is Variant.Arm` — returns `true` if the variant value matches the arm, `false` otherwise. Desugars to `match expr: Variant.Arm: true; _: false` during parsing. Works for both no-payload arms (discriminant equality) and payload arms (any payload matches). Use `match` with `as name` for payload destructuring.
|
|
922
|
+
|
|
923
|
+
### 5.3 Operator precedence (low to high)
|
|
924
|
+
|
|
925
|
+
1. `or`
|
|
926
|
+
2. `and`
|
|
927
|
+
3. `not` (unary prefix)
|
|
928
|
+
4. `is`
|
|
929
|
+
5. `|`
|
|
930
|
+
6. `^`
|
|
931
|
+
7. `&`
|
|
932
|
+
8. `==`, `!=`
|
|
933
|
+
9. `<`, `<=`, `>`, `>=`
|
|
934
|
+
10. `<<`, `>>`
|
|
935
|
+
11. `+`, `-`
|
|
936
|
+
12. `*`, `/`, `%`
|
|
937
|
+
|
|
938
|
+
### 5.4 Assignment operators
|
|
939
|
+
|
|
940
|
+
- `=`
|
|
941
|
+
- `+=` `-=` `*=` `/=`
|
|
942
|
+
- `%=`
|
|
943
|
+
- `&=` `|=` `^=`
|
|
944
|
+
- `<<=` `>>=`
|
|
945
|
+
|
|
946
|
+
## 6. Type System
|
|
947
|
+
|
|
948
|
+
### 6.1 Primitive types
|
|
949
|
+
|
|
950
|
+
- `bool`
|
|
951
|
+
- `byte` `short` `int` `long`
|
|
952
|
+
- `ubyte` `ushort` `uint` `ulong`
|
|
953
|
+
- `char`
|
|
954
|
+
- `ptr_int` `ptr_uint`
|
|
955
|
+
- `float` `double`
|
|
956
|
+
- `void`
|
|
957
|
+
- `str`
|
|
958
|
+
- `cstr`
|
|
959
|
+
- `vec2` `vec3` `vec4` — float vectors with `.x` `.y` `.z` `.w` fields; support component-wise `+` `-` `*` `/` and unary `-`
|
|
960
|
+
- `ivec2` `ivec3` `ivec4` — integer vectors with `.x` `.y` `.z` `.w` fields
|
|
961
|
+
- `mat3` `mat4` — column-major matrices; `mat3` has columns `.col0`–`.col2` (each `vec3`), `mat4` has `.col0`–`.col3` (each `vec4`)
|
|
962
|
+
- `quat` — quaternion with `.x` `.y` `.z` `.w` fields; memory-layout compatible with `vec4`
|
|
963
|
+
|
|
964
|
+
Primitive type names are reserved. They cannot be reused for value bindings, parameters, locals, import aliases, or type parameters.
|
|
965
|
+
|
|
966
|
+
Native vector, matrix, and quaternion types support aggregate construction with named fields. Omitted fields default to zero:
|
|
967
|
+
|
|
968
|
+
```mt
|
|
969
|
+
let v = vec3(x = 1.0, y = 2.0, z = 3.0)
|
|
970
|
+
let m = mat4(col0 = vec4(x = 1.0, ...), ...)
|
|
971
|
+
let q = quat(x = 0.0, y = 0.0, z = 0.0, w = 1.0)
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
### 6.2 Type constructors
|
|
975
|
+
|
|
976
|
+
- `ptr[T]`
|
|
977
|
+
- `const_ptr[T]`
|
|
978
|
+
- `own[T]`
|
|
979
|
+
- owning heap pointer: like `ptr[T]` but auto-dereferences (no `unsafe: read()` needed for field/method access)
|
|
980
|
+
- nullable: `own[T]?` lowers to a nullable `T*`; `null` is the absent value
|
|
981
|
+
- storable in struct/variant fields and returnable from functions — unlike `ref[T]`, `own[T]` has no non-owning restriction
|
|
982
|
+
- created via `heap.must_alloc[T](count)` (returns `own[T]`) or `heap.alloc[T](count)` (returns `own[T]?`); freed via `heap.release(ptr)`
|
|
983
|
+
- compiles to plain `T*` in C, with auto-deref handled at the language level
|
|
984
|
+
- `ref[T]`
|
|
985
|
+
- receiver-modifier type: passes a stored value by reference, allowing methods to mutate the underlying storage
|
|
986
|
+
- functions like `append(output: ref[string.String], text: str)` receive a safe pointer to stored data
|
|
987
|
+
- passing a mutable addressable `T` to a `ref[T]` parameter borrows it implicitly, as if the call had written `ref_of(value)` at the call site
|
|
988
|
+
- `ref` types are non-null and cannot be nullable
|
|
989
|
+
- `span[T]`
|
|
990
|
+
- `array[T, N]`
|
|
991
|
+
- `str_buffer[N]`
|
|
992
|
+
- `Task[T]`
|
|
993
|
+
- `fn(params...) -> R`
|
|
994
|
+
- `proc(params...) -> R`
|
|
995
|
+
- `Option[T]`
|
|
996
|
+
- `Result[T, E]`
|
|
997
|
+
- `SoA[T, N]` — Structure-of-Arrays: transforms `T`'s fields into separate arrays of length `N`; access as `soa[i].field`
|
|
998
|
+
- `dyn[InterfaceName]` — runtime interface value (fat pointer: data + vtable). Constructed via `adapt[Interface](value: ref[T])`. @see §3.5.
|
|
999
|
+
- `atomic[T]` — atomic value for lock-free concurrent access. `T` must be a primitive integer or `bool`. Methods: `load() -> T`, `store(value: T)`, `add(value: T) -> T`, `sub(value: T) -> T`, `exchange(value: T) -> T`. All operations use sequential consistency. Lowers to C11 `_Atomic T` with `__atomic_*` builtins. `atomic[T]` is zero-initializable and sendable.
|
|
1000
|
+
- `(T, U)` — tuple type. Positional fields auto-named `_0`, `_1`. Named fields use `(x = T, y = U)`. Copy by value, returns supported.
|
|
1001
|
+
|
|
1002
|
+
### 6.3 Nullability
|
|
1003
|
+
|
|
1004
|
+
- nullable form: `T?`, valid for any type
|
|
1005
|
+
- for pointer-like bases (`ptr[T]`, `const_ptr[T]`, `own[T]`, `cstr`, `fn(...)`, `proc(...)`, opaque), `T?` is a nullable pointer with `null` as the absent value
|
|
1006
|
+
- for non-pointer value bases (`int`, `bool`, `float`, structs, ...), `T?` is stored inline by value as a tagged optional (a presence flag plus the value); it copies by value with no hidden heap allocation or pointer aliasing
|
|
1007
|
+
- `null` expresses absence in any nullable context; the explicit typed `null[...]` form's target must be pointer-like
|
|
1008
|
+
- in nullable pointer-like contexts, use `null` instead of `zero[ptr[T]]`
|
|
1009
|
+
- at an FFI boundary (`external` / `foreign function` parameters and returns), only pointer-like `T?` is allowed; a non-pointer value nullable such as `int?` is rejected — use `ptr[T]?` or pass an explicit struct
|
|
1010
|
+
|
|
1011
|
+
### 6.4 Generics
|
|
1012
|
+
|
|
1013
|
+
Supported:
|
|
1014
|
+
|
|
1015
|
+
- generic structs
|
|
1016
|
+
- generic variants
|
|
1017
|
+
- generic interfaces
|
|
1018
|
+
- generic functions
|
|
1019
|
+
- generic methods
|
|
1020
|
+
- generic foreign functions
|
|
1021
|
+
|
|
1022
|
+
Rules:
|
|
1023
|
+
|
|
1024
|
+
- Constraints are supported on generic structs, variants, interfaces, functions, and methods.
|
|
1025
|
+
- Interface constraints use `implements`, and multiple interfaces on the same type parameter use `and`: `T implements A and B`.
|
|
1026
|
+
- There are no separate `hashes` or `equates` constraints. Generic bodies that call `hash[T](...)`, `equal[T](...)`, or `order[T](...)` rely on specialization-time checking of the canonical associated functions.
|
|
1027
|
+
- Current type parameters may be used as type expressions for associated function calls in generic bodies, such as `T.default()` or `T.tag()`.
|
|
1028
|
+
- Constraint kinds compose with `and`: `T implements ScreenState and Named` remains valid.
|
|
1029
|
+
|
|
1030
|
+
Type arguments can be:
|
|
1031
|
+
|
|
1032
|
+
- types
|
|
1033
|
+
- integer literals
|
|
1034
|
+
- named integer constants
|
|
1035
|
+
|
|
1036
|
+
Generic value parameters use the form `[N: int]` to declare a compile-time integer usable in expressions within the generic body:
|
|
1037
|
+
|
|
1038
|
+
```mt
|
|
1039
|
+
function int_with_bits[N: int]() -> type:
|
|
1040
|
+
if N == 8:
|
|
1041
|
+
return byte
|
|
1042
|
+
else if N == 16:
|
|
1043
|
+
return short
|
|
1044
|
+
else if N == 32:
|
|
1045
|
+
return int
|
|
1046
|
+
else if N == 64:
|
|
1047
|
+
return long
|
|
1048
|
+
else:
|
|
1049
|
+
static_assert(false, "unsupported bit width")
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
The call site specializes with a literal: `int_with_bits[64]`.
|
|
1053
|
+
|
|
1054
|
+
`type` is a built-in type name representing the type of types. A function may declare `-> type` as its return type to select and return a type expression at compile time. Such functions may only be called from compile-time contexts (block-bodied `const`, `when` discriminants, `inline for` bodies, generic bodies, or other `type`-returning functions). The body follows the same restrictions as a block-bodied `const`.
|
|
1055
|
+
|
|
1056
|
+
## 7. Built-In Callable Surface
|
|
1057
|
+
|
|
1058
|
+
Special recognized callables:
|
|
1059
|
+
|
|
1060
|
+
- `fatal(message)`
|
|
1061
|
+
- `ref_of(x)`
|
|
1062
|
+
- `const_ptr_of(x)`
|
|
1063
|
+
- `read(r)`
|
|
1064
|
+
- `read(p)`
|
|
1065
|
+
- `ptr_of(x)`
|
|
1066
|
+
- `T<-value`
|
|
1067
|
+
- `reinterpret[T](value)`
|
|
1068
|
+
- `zero[T]`
|
|
1069
|
+
- `default[T]`
|
|
1070
|
+
- `hash[T](value)`
|
|
1071
|
+
- `equal[T](left, right)`
|
|
1072
|
+
- `order[T](left, right)`
|
|
1073
|
+
- `array[T, N](...)`
|
|
1074
|
+
- `span[T](data = ..., len = ...)`
|
|
1075
|
+
- `get(coll, index)` — recoverable array/span indexing returning `ptr[T]?`; null on out‑of‑bounds instead of aborting
|
|
1076
|
+
- `adapt[I](value)` — constructs a `dyn[I]` runtime interface value; verifies `value`'s type implements `I` at compile time
|
|
1077
|
+
|
|
1078
|
+
`default[T]` requires an accessible zero-argument associated function `T.default()` that returns `T`.
|
|
1079
|
+
|
|
1080
|
+
`hash[T](value)` lowers to `T.hash(value: const_ptr[T]) -> uint`, `equal[T](left, right)` lowers to `T.equal(left: const_ptr[T], right: const_ptr[T]) -> bool`, and `order[T](left, right)` lowers to `T.order(left: const_ptr[T], right: const_ptr[T]) -> int`. Each argument must already be a `ref[T]`, `ptr[T]`, or `const_ptr[T]`, or be a safe stored `T` lvalue that can be borrowed implicitly.
|
|
1081
|
+
|
|
1082
|
+
`T.order(...) -> int` returns a negative value when `left < right`, `0` when the values compare equal, and a positive value when `left > right`.
|
|
1083
|
+
|
|
1084
|
+
There are no separate `hashes` or `equates` constraints; the builtins themselves force those hook requirements at specialization time.
|
|
1085
|
+
|
|
1086
|
+
There is no separate `defaults` constraint. A generic body that uses `default[T]` relies on specialization-time checking that `T.default()` exists.
|
|
1087
|
+
|
|
1088
|
+
For recoverable failures, use `Result[T, E]`. Its `.success(...)` and `.failure(...)` constructors are variant arms, not built-in callables.
|
|
1089
|
+
|
|
1090
|
+
For repeated pointer-plus-length span construction, use the built-in `span[T](data = ..., len = ...)` form directly. If the pattern repeats often in one codebase, define a small local helper in your own module instead of depending on a standard helper module.
|
|
1091
|
+
|
|
1092
|
+
When a `span[T]` is expected, an addressable `array[T, N]` value may be passed directly through the existing boundary coercion rules. Arrays also expose `.as_span()` for explicit conversion when the target type is not a call boundary.
|
|
1093
|
+
|
|
1094
|
+
### 7.0 Compile-time reflection builtins
|
|
1095
|
+
|
|
1096
|
+
Compile-time reflection builtins return handle values that represent type structure:
|
|
1097
|
+
|
|
1098
|
+
- `field_of(T, name)` — returns a `field_handle` for the named field of `T`.
|
|
1099
|
+
- `callable_of(T, name)` — returns a `callable_handle` for the named callable of `T`.
|
|
1100
|
+
- `attribute_of(T, name)` — returns an `attribute_handle` for the named attribute on `T`.
|
|
1101
|
+
- `has_attribute(T, name)` — returns `bool`; true if `T` has the named attribute applied.
|
|
1102
|
+
- `attribute_arg[T]` — returns the `T`-typed argument of a resolved attribute handle.
|
|
1103
|
+
- `fields_of(T)` — returns `array[field_handle, N]` of all fields of struct `T`, in declaration order.
|
|
1104
|
+
- `members_of(E)` — returns `array[member_handle, N]` of all members of enum or variant `E`.
|
|
1105
|
+
- `attributes_of(T)` — returns `array[attribute_handle, N]` of all attributes on `T`.
|
|
1106
|
+
- `attributes_of(T, name)` — returns `array[attribute_handle, N]` of attributes whose kind matches `name`.
|
|
1107
|
+
|
|
1108
|
+
Handle field access: `field_handle` exposes `.name` (`str`) and `.type` (the field's type). `member_handle` exposes `.name` (`str`) and, for enum members with explicit values, `.value` (an integer). `attribute_handle` provides access to attribute arguments via `attribute_arg[T]`.
|
|
1109
|
+
|
|
1110
|
+
A field handle's `.type` is also usable directly **in type position** inside a compile-time context (e.g. within `inline for field in fields_of(T)`): it resolves to the field's concrete type in type-argument and type-constructor positions such as `const_ptr[field.type]`, `equal[field.type](...)`, and `Vec[field.type]`. This powers reflective generics like `std.hash`'s `equal_struct`/`hash_struct`/`order_struct`, which dispatch each field through its canonical hook, and `std.fmt.format_value[T]`, which recursively renders a struct's fields. The `inline for` body is type-checked once **per element**, so dispatch on each field — including recursion into a nested-struct field's type — is validated at check time.
|
|
1111
|
+
|
|
1112
|
+
`read(r)` still explicitly projects a `ref[T]` to its referent value, but ordinary member access and method calls auto-dereference `ref[T]` receivers. That means `handle.field`, `handle.edit_method()`, and `handle.read()` are accepted without writing `read(handle)` first. Calls in the other direction are also lighter now: if a function expects `ref[T]`, passing a mutable addressable `T` borrows it implicitly.
|
|
1113
|
+
|
|
1114
|
+
### 7.1 Current standard collection modules
|
|
1115
|
+
|
|
1116
|
+
The current shipped collection modules in `std` are:
|
|
1117
|
+
|
|
1118
|
+
- `std.vec.Vec[T]`: contiguous growable storage with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `iter`, `as_span`, `get`, `at`, `first`, `last`, `find`, `find_index`, `reserve`, `clear`, `release`, `append_span`, `append_array`, `insert`, `push`, `pop`, `remove`, and `swap_remove`.
|
|
1119
|
+
- `std.deque.Deque[T]`: growable ring buffer with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `iter`, `get`, `at`, `first`, `last`, `reserve`, `clear`, `release`, `push_front`, `push_back`, `insert`, `pop_front`, `pop_back`, `remove`, `rotate_left`, and `rotate_right`.
|
|
1120
|
+
- `std.binary_heap.BinaryHeap[T]`: max-heap keyed by the canonical `order[T](...)` hook, with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `iter`, `peek`, `reserve`, `clear`, `release`, `push`, and `pop`.
|
|
1121
|
+
- `std.priority_queue.PriorityQueue[T]`: task-oriented facade over `BinaryHeap[T]`, with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `iter`, `peek`, `reserve`, `clear`, `release`, `enqueue`, and `dequeue`.
|
|
1122
|
+
- `std.ordered_set.OrderedSet[T]`: AVL-backed unique sorted set keyed by the canonical `order[T](...)` hook, with `create`, `len`, `is_empty`, `get`, `at`, `contains`, `iter`, `clear`, `release`, `insert`, and `remove`.
|
|
1123
|
+
- `std.ordered_map.OrderedMap[K, V]`: AVL-backed ordered map keyed by the canonical `order[K](...)` hook, with `create`, `len`, `is_empty`, `get`, `at`, `get_key`, `contains`, `keys`, `values`, `entries`, `iter`, `clear`, `release`, `set`, `get_or_insert`, `remove_entry`, and `remove`.
|
|
1124
|
+
- `std.map.Map[K, V]`: hash table keyed by the canonical `hash[K](...)` and `equal[K](...)` hooks, with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `get`, `at`, `get_key`, `contains`, `keys`, `values`, `entries`, `iter`, `clear`, `release`, `reserve`, `set`, `get_or_insert`, `remove_entry`, and `remove` (`iter()` is the same traversal as `entries()`).
|
|
1125
|
+
- `std.set.Set[T]`: hash set built on `Map[T, bool]`, with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `get`, `at`, `contains`, `iter`, `is_subset`, `union_with`, `intersection`, `difference`, `clear`, `release`, `reserve`, `insert`, and `remove`. Set union is spelled `union_with` because `union` is a reserved keyword.
|
|
1126
|
+
- `std.linked_map.LinkedMap[K, V]`: insertion-ordered hash map keyed by the canonical `hash[K](...)` and `equal[K](...)` hooks, with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `get`, `at`, `get_key`, `contains`, `keys`, `values`, `entries`, `iter`, `clear`, `release`, `reserve`, `set`, `get_or_insert`, `remove_entry`, and `remove`.
|
|
1127
|
+
- `std.linked_set.LinkedSet[T]`: insertion-ordered hash set built on `LinkedMap[T, bool]`, with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `get`, `at`, `contains`, `iter`, `is_subset`, `union_with`, `intersection`, `difference`, `clear`, `release`, `reserve`, `insert`, and `remove`.
|
|
1128
|
+
- `std.counter.Counter[T]`: insertion-ordered frequency table built on `LinkedMap[T, ptr_uint]`, with `create`, `with_capacity`, `len`, `total_count`, `capacity`, `is_empty`, `count`, `contains`, `keys`, `counts`, `entries`, `iter`, `clear`, `release`, `reserve`, `add`, `increment`, `remove_one`, and `remove`.
|
|
1129
|
+
- `std.multiset.MultiSet[T]`: insertion-ordered bag built on `Counter[T]`, with `create`, `with_capacity`, `len`, `total_count`, `distinct_len`, `capacity`, `is_empty`, `count`, `contains`, `values`, `entries`, `iter`, `is_subset`, `union_with`, `intersection`, `difference`, `symmetric_difference`, `clear`, `release`, `reserve`, `insert`, `add`, `remove_one`, and `remove_all`.
|
|
1130
|
+
- `std.queue.Queue[T]`: FIFO facade over `Deque[T]`, with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `iter`, `peek`, `clear`, `release`, `reserve`, `enqueue`, and `dequeue`.
|
|
1131
|
+
- `std.stack.Stack[T]`: LIFO facade over `Deque[T]`, with `create`, `with_capacity`, `len`, `capacity`, `is_empty`, `iter`, `peek`, `clear`, `release`, `reserve`, `push`, and `pop`.
|
|
1132
|
+
- `std.linked_map_view.SnapshotValues[K, V]`: read-only snapshot view over `LinkedMap` values in insertion order, with `create(values: linked_map.Entries[K, V])` and `iter`.
|
|
1133
|
+
- `std.linked_map_view.SnapshotEntries[K, V]`: read-only snapshot view over `LinkedMap` entries in insertion order, with `create(values: linked_map.Entries[K, V])` and `iter`.
|
|
1134
|
+
|
|
1135
|
+
Iterator notes for those collection modules:
|
|
1136
|
+
|
|
1137
|
+
- `Vec.iter()` and `Deque.iter()` use the pointer-returning iterator form.
|
|
1138
|
+
- `BinaryHeap.iter()` uses the pointer-returning iterator form with read-only element pointers, and `peek()` is also read-only because arbitrary element mutation would violate the heap invariant.
|
|
1139
|
+
- `PriorityQueue.iter()` uses the same read-only pointer-returning iterator form as `BinaryHeap.iter()`.
|
|
1140
|
+
- `OrderedSet.iter()` uses the pointer-returning iterator form with read-only element pointers so in-place mutation cannot violate the sorted uniqueness invariant.
|
|
1141
|
+
- `OrderedMap.keys()` uses the pointer-returning iterator form with read-only key pointers, `OrderedMap.values()` returns mutable value pointers in key order, and `OrderedMap.entries()` / `OrderedMap.iter()` use the `next() -> bool` plus `current()` iterator form in key order.
|
|
1142
|
+
- `Map.keys()` and `Set.iter()` use the pointer-returning iterator form.
|
|
1143
|
+
- `Map.values()` returns mutable value pointers during iteration.
|
|
1144
|
+
- `Map.entries()` and `Map.iter()` use the `next() -> bool` plus `current()` iterator form.
|
|
1145
|
+
- `LinkedMap.keys()` and `LinkedSet.iter()` use the pointer-returning iterator form with read-only key pointers in insertion order.
|
|
1146
|
+
- `LinkedMap.values()` returns mutable value pointers in insertion order.
|
|
1147
|
+
- `LinkedMap.entries()` and `LinkedMap.iter()` use the `next() -> bool` plus `current()` iterator form in insertion order.
|
|
1148
|
+
- `Counter.keys()` uses the pointer-returning iterator form with read-only key pointers in first-seen order.
|
|
1149
|
+
- `Counter.counts()` uses the `next() -> bool` plus `current()` iterator form and yields copied `ptr_uint` counts so totals cannot be mutated out of sync.
|
|
1150
|
+
- `Counter.entries()` and `Counter.iter()` use the `next() -> bool` plus `current()` iterator form and yield immutable `{ key, count }` snapshots.
|
|
1151
|
+
- `MultiSet.values()` uses the pointer-returning iterator form with read-only value pointers in first-seen order.
|
|
1152
|
+
- `MultiSet.entries()` and `MultiSet.iter()` use the `next() -> bool` plus `current()` iterator form and yield immutable `{ value, count }` snapshots.
|
|
1153
|
+
- `Queue.iter()` and `Stack.iter()` use the same mutable pointer-returning iterator form as `Deque.iter()`, and `peek()` returns a mutable element pointer because changing an element value does not violate FIFO/LIFO ordering invariants.
|
|
1154
|
+
- `SnapshotValues.iter()` and `SnapshotEntries.iter()` use the `next() -> bool` plus `current()` iterator form in insertion order.
|
|
1155
|
+
|
|
1156
|
+
## 8. Strings, C Strings, And Format Strings
|
|
1157
|
+
|
|
1158
|
+
String categories:
|
|
1159
|
+
|
|
1160
|
+
- `str` -> string view
|
|
1161
|
+
- `cstr` -> C ABI string
|
|
1162
|
+
- `str_buffer[N]` -> fixed-capacity mutable UTF-8 text buffer
|
|
1163
|
+
|
|
1164
|
+
`str_buffer[N]` methods:
|
|
1165
|
+
|
|
1166
|
+
- `clear()`
|
|
1167
|
+
- `assign(str)`
|
|
1168
|
+
- `append(str)`
|
|
1169
|
+
- `assign_format(str)`
|
|
1170
|
+
- `append_format(str)`
|
|
1171
|
+
- `len()`
|
|
1172
|
+
- `capacity()`
|
|
1173
|
+
- `as_str()`
|
|
1174
|
+
- `as_cstr()`
|
|
1175
|
+
|
|
1176
|
+
Format string syntax:
|
|
1177
|
+
|
|
1178
|
+
```mt
|
|
1179
|
+
f"count=#{count} ok=#{ready}"
|
|
1180
|
+
```
|
|
1181
|
+
|
|
1182
|
+
Heredoc syntax:
|
|
1183
|
+
|
|
1184
|
+
```mt
|
|
1185
|
+
const shader: cstr = c<<-GLSL
|
|
1186
|
+
#version 330
|
|
1187
|
+
void main()
|
|
1188
|
+
{
|
|
1189
|
+
}
|
|
1190
|
+
GLSL
|
|
1191
|
+
```
|
|
1192
|
+
|
|
1193
|
+
`<<-TAG`, `c<<-TAG`, and `f<<-TAG` read all following lines until a line containing only the terminator tag, optionally surrounded by spaces. Nonblank content lines are dedented by their shared leading spaces. The trailing newline before the terminator is preserved.
|
|
1194
|
+
|
|
1195
|
+
Ordinary `"..."` and `c"..."` literals may continue across following indented lines when each continued line starts with the same literal kind and contains nothing else. The segments concatenate exactly with no inserted separator, so any spaces or punctuation between pieces must be written explicitly.
|
|
1196
|
+
|
|
1197
|
+
In the VS Code extension, specific heredoc tags opt into embedded highlighting without changing the Milk Tea type: `GLSL`, `VERT`, `FRAG`, `COMP`, `JSON`, `JSONC`, `SQL`, `HTML`, and `MT`. `MT` recursively applies Milk Tea syntax highlighting to the heredoc content.
|
|
1198
|
+
|
|
1199
|
+
Format strings have type `str` and are valid anywhere a `str` value is accepted. Interpolated expressions must be one of: `str`, `cstr`, `bool`, a numeric primitive, an integer-backed enum or flags type, or a type implementing both `format_len() -> ptr_uint` and `append_format(output: ref[std.string.String]) -> void`. A precision specifier `:.N` is allowed on `float` and `double` interpolations. Integer-base specifiers `:x` / `:X` (hex), `:o` / `:O` (octal), and `:b` / `:B` (binary) are allowed on integer primitives and integer-backed enum/flags interpolations.
|
|
1200
|
+
|
|
1201
|
+
The following standard library functions receive special lowering for format strings — they build the formatted output directly without an intermediate allocation:
|
|
1202
|
+
|
|
1203
|
+
- `std.fmt.format` — returns `string.String`
|
|
1204
|
+
- `std.fmt.append_format` / `std.fmt.assign_format` — write directly into an existing `string.String` sink
|
|
1205
|
+
- `std.string.String.append_format` / `std.string.String.assign_format` — same direct-sink lowering on the builder methods
|
|
1206
|
+
- `str_buffer[N].append_format` / `str_buffer[N].assign_format` — same direct-sink lowering on fixed-capacity string buffers
|
|
1207
|
+
|
|
1208
|
+
Custom formatting hook notes:
|
|
1209
|
+
|
|
1210
|
+
- The hook pair is compiler-known; it is not declared through a separate interface.
|
|
1211
|
+
- `format_len()` and `append_format(...)` must both be present, non-editable, and use the exact signatures above.
|
|
1212
|
+
- Direct `string.String` sinks call the custom `append_format(...)` hook directly.
|
|
1213
|
+
- Plain `f"..."` expressions and `str_buffer` sinks still need a raw `str` result, so the compiler passes each custom part a borrowed `string.String` view onto the destination slice.
|
|
1214
|
+
- For those borrowed-slice paths, `append_format(...)` must write exactly `format_len()` bytes; writing fewer or more bytes raises a runtime error.
|
|
1215
|
+
|
|
1216
|
+
## 9. Safety And Conversion Rules
|
|
1217
|
+
|
|
1218
|
+
- conditions must be `bool`
|
|
1219
|
+
- no truthy/falsy integer or pointer coercion
|
|
1220
|
+
- mixed signed/unsigned integer arithmetic requires an explicit `T<-value` cast
|
|
1221
|
+
- non-widening integer conversions (narrowing, signed → unsigned) require an explicit `T<-value` cast; lossless widening is implicit
|
|
1222
|
+
- `%` requires integer-compatible operands
|
|
1223
|
+
- bitwise operators require matching integer/flags types
|
|
1224
|
+
- shift operators require integer operands
|
|
1225
|
+
- safe array indexing requires an addressable array value
|
|
1226
|
+
- safe indexing (`arr[i]`) is bounds-checked and calls `fatal` on out-of-bounds access
|
|
1227
|
+
- use `get(arr, i)` for recoverable indexing that returns `ptr[T]?` (null on out-of-bounds) instead of aborting
|
|
1228
|
+
- pointer indexing requires `unsafe`
|
|
1229
|
+
- `read(...)` of raw pointer requires `unsafe`
|
|
1230
|
+
- pointer casts require `unsafe`
|
|
1231
|
+
- `reinterpret[...]` requires `unsafe`, non-array concrete sized types, and equal-size source and target types.
|
|
1232
|
+
- variant types support `==` and `!=` (generates per-variant comparison helper)
|
|
1233
|
+
|
|
1234
|
+
## 10. Async Semantics
|
|
1235
|
+
|
|
1236
|
+
```mt
|
|
1237
|
+
async function child() -> int:
|
|
1238
|
+
return 41
|
|
1239
|
+
|
|
1240
|
+
async function parent() -> int:
|
|
1241
|
+
let v = await child()
|
|
1242
|
+
return v + 1
|
|
1243
|
+
```
|
|
1244
|
+
|
|
1245
|
+
Rules:
|
|
1246
|
+
|
|
1247
|
+
- async function return type is lifted to `Task[T]`
|
|
1248
|
+
- `await` is only allowed inside async functions
|
|
1249
|
+
- `async main` is compiler-bootstrapped, but async helpers remain library surface; import `std.async as aio` when you want helpers such as `sleep`, `completed`, `result`, `wait`, `run`, or runtime control
|
|
1250
|
+
- `aio.wait(...)` and `aio.run(...)` accept either a zero-arg task root or a direct task expression; the compiler defers direct task expressions automatically
|
|
1251
|
+
- `async main` pre-lift return type must be `int` or `void`
|
|
1252
|
+
|
|
1253
|
+
Current async limitations:
|
|
1254
|
+
|
|
1255
|
+
- `await` is supported inside `if` expressions, `if`/`else if`/`else` bodies and conditions, `while` bodies and conditions, single-form and parallel `for` bodies and iterables, `match` discriminants and arms, `let ... else:` initializers and else bodies, `unsafe` blocks, short-circuit `and`/`or` expressions, and assignment targets
|
|
1256
|
+
- `defer` is supported in async functions, including cleanup bodies that `await`
|
|
1257
|
+
|
|
1258
|
+
## 11. Events
|
|
1259
|
+
|
|
1260
|
+
Event declarations provide a built-in typed publisher/subscriber surface with fixed-capacity listener storage.
|
|
1261
|
+
|
|
1262
|
+
### 11.1 Declaration
|
|
1263
|
+
|
|
1264
|
+
```mt
|
|
1265
|
+
event name[capacity]
|
|
1266
|
+
event name[capacity](PayloadType)
|
|
1267
|
+
public event name[capacity]
|
|
1268
|
+
public event name[capacity](PayloadType)
|
|
1269
|
+
```
|
|
1270
|
+
|
|
1271
|
+
Examples:
|
|
1272
|
+
|
|
1273
|
+
```mt
|
|
1274
|
+
public event closed[4]
|
|
1275
|
+
public event resized[8](ResizeEvent)
|
|
1276
|
+
```
|
|
1277
|
+
|
|
1278
|
+
Rules:
|
|
1279
|
+
|
|
1280
|
+
- The capacity expression must be a compile-time positive integer literal.
|
|
1281
|
+
- An event carries either no payload or exactly one payload value.
|
|
1282
|
+
- The payload type must be a storable type; `ref[T]` payloads are rejected.
|
|
1283
|
+
- Event declarations are valid at top level and as struct members.
|
|
1284
|
+
- `emit` is only callable from within the declaring module.
|
|
1285
|
+
|
|
1286
|
+
### 11.2 Built-in operations
|
|
1287
|
+
|
|
1288
|
+
- `event.subscribe(listener) -> Result[Subscription, EventError]` — register a stateless listener
|
|
1289
|
+
- `event.subscribe_once(listener) -> Result[Subscription, EventError]` — register a one-shot listener
|
|
1290
|
+
- `event.subscribe(state: ptr[State], listener: fn(ptr[State], ...)) -> Result[Subscription, EventError]` — register a stateful listener by passing 2 positional arguments; the state pointer is stored verbatim and passed to the listener on each dispatch
|
|
1291
|
+
- `event.subscribe_once(state: ptr[State], listener: fn(ptr[State], ...)) -> Result[Subscription, EventError]` — register a stateful one-shot listener by passing 2 positional arguments
|
|
1292
|
+
- `event.unsubscribe(subscription) -> bool` — remove a listener by subscription handle; returns `true` if the listener was active and removed, `false` if the handle was stale or invalid
|
|
1293
|
+
- `event.emit()` or `event.emit(payload)` — synchronously dispatch to all listeners; only callable from the declaring module
|
|
1294
|
+
- `event.wait() -> Task[Result[T, EventError]]` — async wait for the next emission; returns the payload for payload events, or `Result[void, EventError]` for no-payload events
|
|
1295
|
+
|
|
1296
|
+
`EventError` is a built-in enum with a single member `full` (value `0`), returned when listener capacity is exhausted.
|
|
1297
|
+
|
|
1298
|
+
`Subscription` is a built-in opaque handle type returned by `subscribe` and `subscribe_once`.
|
|
1299
|
+
|
|
1300
|
+
### 11.3 Example
|
|
1301
|
+
|
|
1302
|
+
```mt
|
|
1303
|
+
struct ResizeEvent:
|
|
1304
|
+
width: int
|
|
1305
|
+
height: int
|
|
1306
|
+
|
|
1307
|
+
struct Window:
|
|
1308
|
+
public event closed[4]
|
|
1309
|
+
public event resized[8](ResizeEvent)
|
|
1310
|
+
|
|
1311
|
+
function on_close() -> void:
|
|
1312
|
+
stdio.print_line("closed")
|
|
1313
|
+
|
|
1314
|
+
function on_resize(evt: ResizeEvent) -> void:
|
|
1315
|
+
stdio.print_line(f"resize -> #{evt.width}x#{evt.height}")
|
|
1316
|
+
|
|
1317
|
+
function attach(window: ref[Window]) -> Result[void, EventError]:
|
|
1318
|
+
let closed_sub = window.closed.subscribe(on_close)?
|
|
1319
|
+
let resized_sub = window.resized.subscribe(on_resize)?
|
|
1320
|
+
|
|
1321
|
+
defer window.closed.unsubscribe(closed_sub)
|
|
1322
|
+
defer window.resized.unsubscribe(resized_sub)
|
|
1323
|
+
|
|
1324
|
+
return Result[void, EventError].success()
|
|
1325
|
+
```
|
|
1326
|
+
|
|
1327
|
+
## 12. Linting
|
|
1328
|
+
|
|
1329
|
+
The linter checks for common issues and style problems without changing program behavior.
|
|
1330
|
+
|
|
1331
|
+
### 12.1 Running the linter
|
|
1332
|
+
|
|
1333
|
+
```sh
|
|
1334
|
+
mtc lint path/to/file.mt
|
|
1335
|
+
mtc lint src/ # lint all .mt files in a directory
|
|
1336
|
+
mtc lint --fix path/to/file.mt # apply auto-fixes in place
|
|
1337
|
+
mtc lint --select prefer-let,dead-assignment file.mt
|
|
1338
|
+
mtc lint --ignore shadow file.mt
|
|
1339
|
+
```
|
|
1340
|
+
|
|
1341
|
+
### 12.2 Rules
|
|
1342
|
+
|
|
1343
|
+
The auto-fix column corresponds to `mtc lint --fix`.
|
|
1344
|
+
|
|
1345
|
+
| Code | Severity | Auto-fix | Description |
|
|
1346
|
+
|---|---|---|---|
|
|
1347
|
+
| `borrow-and-mutate` | warning | — | Local is borrowed with `ref_of` or `ptr_of` and also mutated in the same scope |
|
|
1348
|
+
| `constant-condition` | warning | — | Branch or loop condition is provably always `true` or always `false` |
|
|
1349
|
+
| `dead-assignment` | warning | — | Assigned value is overwritten before any read |
|
|
1350
|
+
| `duplicate-if-condition` | warning | — | `if`/`else if` branch repeats a previous condition and is unreachable |
|
|
1351
|
+
| `directional-ffi-arg` | hint | — | Legacy `ptr_of` / `ref_of` / `out` call-site wrapper is redundant for directional FFI parameters |
|
|
1352
|
+
| `doc-tag` | hint | — | `##` documentation comment tag (`@param`, `@return`, `@throws`, `@see`) is invalid or inconsistent with the declaration |
|
|
1353
|
+
| `event-capacity` | warning | — | Event capacity may copy too many listeners to stack on emit |
|
|
1354
|
+
| `line-too-long` | warning | — | Source line exceeds configured maximum length |
|
|
1355
|
+
| `loop-single-iteration` | warning | — | Loop body always exits on the first iteration |
|
|
1356
|
+
| `missing-return` | error | — | Function with a non-void return type lacks a guaranteed return on all paths |
|
|
1357
|
+
| `noop-compound-assignment` | hint | — | Compound assignment uses an identity value and has no effect |
|
|
1358
|
+
| `platform-api-drift` | warning | — | Public API differs across sibling platform-specific variants of the same module |
|
|
1359
|
+
| `prefer-let` | hint | yes | `var` binding is never mutated; use `let` instead |
|
|
1360
|
+
| `prefer-let-else` | hint | yes | Nullable guard can be rewritten as `let ... else:` |
|
|
1361
|
+
| `prefer-var-else` | hint | yes | Nullable guard can be rewritten as `var ... else:` |
|
|
1362
|
+
| `redundant-bool-compare` | hint | yes | Comparing a boolean expression to `true`/`false` is redundant |
|
|
1363
|
+
| `redundant-else` | warning | yes | `else` block is unnecessary because all prior branches return |
|
|
1364
|
+
| `redundant-ignored-match-binding` | hint | yes | Ignored `as _` match binding is redundant |
|
|
1365
|
+
| `redundant-null-check` | hint | — | Null check on a value already known to be non-null by flow analysis |
|
|
1366
|
+
| `redundant-return` | hint | yes | Final bare `return` in a `void` function is unnecessary |
|
|
1367
|
+
| `reserved-primitive-name` | warning | yes | Binding uses a reserved built-in type name in its active namespace |
|
|
1368
|
+
| `self-assignment` | warning | — | Variable is assigned to itself |
|
|
1369
|
+
| `self-comparison` | warning | — | Value is compared to itself, making the condition constant |
|
|
1370
|
+
| `shadow` | warning | — | Local binding shadows an outer binding with the same name |
|
|
1371
|
+
| `trailing-list-comma` | hint | yes | Trailing comma in call argument list is redundant |
|
|
1372
|
+
| `unreachable-code` | warning | — | Code after a guaranteed terminator cannot execute |
|
|
1373
|
+
| `unused-import` | warning | yes | Import alias is never referenced |
|
|
1374
|
+
| `unused-local` | warning | — | Local binding is never referenced |
|
|
1375
|
+
| `unused-param` | warning | — | Parameter is never referenced |
|
|
1376
|
+
| `useless-expression` | warning | — | Expression statement has no side effects and its result is unused |
|
|
1377
|
+
|
|
1378
|
+
### 12.3 Config file
|
|
1379
|
+
|
|
1380
|
+
Create a default config with:
|
|
1381
|
+
|
|
1382
|
+
```sh
|
|
1383
|
+
mtc lint --init
|
|
1384
|
+
```
|
|
1385
|
+
|
|
1386
|
+
Or place `.mt-lint.yml` in the project root (or any ancestor directory):
|
|
1387
|
+
|
|
1388
|
+
```yaml
|
|
1389
|
+
max_line_length: 120
|
|
1390
|
+
select:
|
|
1391
|
+
- line-too-long
|
|
1392
|
+
- prefer-let
|
|
1393
|
+
- missing-return
|
|
1394
|
+
ignore:
|
|
1395
|
+
- shadow
|
|
1396
|
+
- useless-expression
|
|
1397
|
+
```
|
|
1398
|
+
|
|
1399
|
+
When both `select` and `ignore` are present, `select` takes precedence and `ignore` is unused.
|
|
1400
|
+
|
|
1401
|
+
`max_line_length` defaults to `120` when omitted.
|
|
1402
|
+
|
|
1403
|
+
`line-too-long` code actions currently rewrite only parser-valid same-line comma-delimited `()` groups and type-position `[]` groups. Tuple literals may be rewritten without a trailing comma when the parser requires it.
|
|
1404
|
+
|
|
1405
|
+
### 12.4 Per-line suppressions
|
|
1406
|
+
|
|
1407
|
+
```mt
|
|
1408
|
+
var count = 0 # lint: ignore
|
|
1409
|
+
var total = 0 # lint: ignore(prefer-let, dead-assignment)
|
|
1410
|
+
```
|
|
1411
|
+
|
|
1412
|
+
`# lint: ignore` silences all rules on that line. `# lint: ignore(rule1, rule2)` silences only the listed rules.
|
|
1413
|
+
|
|
1414
|
+
## 13. Language Limitations
|
|
1415
|
+
|
|
1416
|
+
The compiler intentionally rejects the following patterns. These are design constraints, not missing features.
|
|
1417
|
+
|
|
1418
|
+
### 13.1 Interface restrictions
|
|
1419
|
+
|
|
1420
|
+
- interface method signatures cannot be `async` or generic
|
|
1421
|
+
- interface conformance must be declared on the nominal type, not on an `extending` block
|
|
1422
|
+
- bare interface names are not runtime storage types — `Damageable` as a field, local, parameter, or return type — use `dyn[Interface]`
|
|
1423
|
+
- `dyn[Interface]` for a generic interface must be fully specified: `dyn[Mapper[int]]`, not `dyn[Mapper]`
|
|
1424
|
+
- a type implementing an interface must match method kinds, receiver editability, parameter types, return type, and asyncness exactly
|
|
1425
|
+
|
|
1426
|
+
### 13.2 External and FFI restrictions
|
|
1427
|
+
|
|
1428
|
+
- `external function` cannot be generic, async, or array-taking / array-returning
|
|
1429
|
+
- `external function` cannot take `ref[...]` or `proc(...)` parameters
|
|
1430
|
+
- `external function` parameters cannot use `as`, `in`, `out`, or `inout`
|
|
1431
|
+
- `foreign function` cannot be async and cannot take `proc(...)` parameters
|
|
1432
|
+
- a `foreign function` with `consuming` parameter(s) must return `void`
|
|
1433
|
+
- consuming foreign calls must appear as top-level expression statements
|
|
1434
|
+
- external files have a restricted declaration surface: `const`, `type`, `struct`, `union`, `enum`, `flags`, `opaque`, and `external function` only
|
|
1435
|
+
- `public` is rejected in external files — declarations are implicitly exported
|
|
1436
|
+
- `import` statements in external files must appear before directives and declarations
|
|
1437
|
+
|
|
1438
|
+
### 13.3 Event restrictions
|
|
1439
|
+
|
|
1440
|
+
- event payload cannot be `ref[T]` in v1; the payload type must be storable
|
|
1441
|
+
- event storage types cannot be returned from functions, passed through non-pointer/non-ref parameters, or copied into locals
|
|
1442
|
+
- `emit` is only callable from within the declaring module
|
|
1443
|
+
- event methods (`subscribe`, `subscribe_once`, `unsubscribe`, `emit`, `wait`) do not support named arguments
|
|
1444
|
+
|
|
1445
|
+
### 13.4 ref[T] restrictions
|
|
1446
|
+
|
|
1447
|
+
- `ref[T]` values are rejected in constants, module variables, and nested local storage such as arrays or generic containers
|
|
1448
|
+
- functions cannot return `ref[T]` or a non-owning struct (one containing `ref` via auto-generated lifetime)
|
|
1449
|
+
- `ref[T]` values are not capturable by `proc`
|
|
1450
|
+
- `external function` cannot take `ref[...]` parameters
|
|
1451
|
+
- `ref[T]` is non-null and cannot be nullable
|
|
1452
|
+
- stored callable values (`fn(...)`, `proc(...)`) may use `ref[...]` only in direct callable parameter positions, not in return types
|
|
1453
|
+
|
|
1454
|
+
### 13.5 Pointer and safety restrictions
|
|
1455
|
+
|
|
1456
|
+
- pointer indexing, raw pointer dereference, pointer arithmetic, and pointer casts require `unsafe`
|
|
1457
|
+
- `reinterpret[T]` requires `unsafe` and non-array concrete sized types
|
|
1458
|
+
- `ptr_of`, `const_ptr_of`, and `ref_of` require an addressable source expression
|
|
1459
|
+
- safe array indexing requires an addressable array value; `get(arr, i)` provides recoverable bounds-checked access
|
|
1460
|
+
- legacy call-site markers `in`, `out`, and `inout` are rejected; parameter modes are declared on `foreign function`
|
|
1461
|
+
- pointer comparison is not treated as boolean truthiness
|
|
1462
|
+
|
|
1463
|
+
### 13.6 Type system restrictions
|
|
1464
|
+
|
|
1465
|
+
- conditions must be `bool`; integers and pointers have no implicit truthy or falsy coercion
|
|
1466
|
+
- mixed signed and unsigned integer arithmetic requires an explicit cast
|
|
1467
|
+
- enum and flags values do not implicitly coerce to their backing integer types outside external-call boundaries
|
|
1468
|
+
- `enum` backing types must be integer primitives; flags members must be compile-time integer constants
|
|
1469
|
+
- variant arm payloads cannot use `ref[T]` in v1
|
|
1470
|
+
- `null` must be used instead of `zero[ptr[T]]` in typed nullable pointer-like contexts
|
|
1471
|
+
|
|
1472
|
+
### 13.7 Operator and expression restrictions
|
|
1473
|
+
|
|
1474
|
+
- `+` does not support `str`/`cstr` concatenation
|
|
1475
|
+
- `==` and `!=` are not supported on struct types; use `equal[T]`
|
|
1476
|
+
- range expressions are restricted to `for`-loop iterables and range-index assignment targets
|
|
1477
|
+
- functions, methods, generic functions, and variant arms must be called — they are not usable as bare values
|
|
1478
|
+
- `read(...)` of a raw pointer requires `unsafe`
|
|
1479
|
+
|
|
1480
|
+
### 13.8 Control flow restrictions
|
|
1481
|
+
|
|
1482
|
+
- `break` and `continue` must be inside loops
|
|
1483
|
+
- `return` is not allowed inside `defer` blocks
|
|
1484
|
+
- `match` on enum/variant must be exhaustive unless `_` is present; `match` on integer or `str` requires `_`
|
|
1485
|
+
- `let ... else:` and `var ... else:` require `T?`, `Option[T]`, or `Result[T, E]`; the `else` block must terminate control flow
|
|
1486
|
+
- `?` propagation is only allowed inside function and proc bodies, not in `defer` blocks
|
|
1487
|
+
- `await` is only allowed inside async functions
|
|
1488
|
+
|
|
1489
|
+
### 13.9 Declaration restrictions
|
|
1490
|
+
|
|
1491
|
+
- `main` cannot be generic
|
|
1492
|
+
- `async main` pre-lift return type must be `int` or `void`
|
|
1493
|
+
- `public` is rejected on `extending` blocks, ordinary `external` declarations, `static_assert`, and in external files
|
|
1494
|
+
- `public` on struct members is only allowed on event declarations, not on fields
|
|
1495
|
+
- explicit C names are only allowed on external structs and unions
|
|
1496
|
+
- `proc(...)` values that capture local state are rejected in constants and module variables; capture-free procs (body only references module-level functions, constants, and types) are allowed in module variables
|
|
1497
|
+
- generic interfaces require type arguments when used with `dyn[Interface]`
|
|
1498
|
+
|
|
1499
|
+
### 13.10 Format string restrictions
|
|
1500
|
+
|
|
1501
|
+
- `:.N` precision is only valid on `float` and `double` interpolations
|
|
1502
|
+
- `:x`/`:X`, `:o`/`:O`, and `:b`/`:B` are only valid on integer primitives and integer-backed enums/flags
|
|
1503
|
+
|
|
1504
|
+
### 13.11 Attribute restrictions
|
|
1505
|
+
|
|
1506
|
+
- user-defined `attribute` declarations target `struct`, `field`, `callable`, `const`, `event`, `enum`, `flags`, `union`, or `variant`
|
|
1507
|
+
- `@[...]` attribute applications are only accepted on the above declaration kinds
|
|
1508
|
+
- attribute declarations are not allowed in external files
|
|
1509
|
+
- only built-in `packed` and `align(...)` struct attributes are allowed in external files
|
|
1510
|
+
|
|
1511
|
+
## 14. Example
|
|
1512
|
+
|
|
1513
|
+
```mt
|
|
1514
|
+
import std.fmt as fmt
|
|
1515
|
+
|
|
1516
|
+
struct Counter:
|
|
1517
|
+
value: int
|
|
1518
|
+
|
|
1519
|
+
extending Counter:
|
|
1520
|
+
editable function bump() -> void:
|
|
1521
|
+
this.value += 1
|
|
1522
|
+
|
|
1523
|
+
function read() -> int:
|
|
1524
|
+
return this.value
|
|
1525
|
+
|
|
1526
|
+
function main() -> int:
|
|
1527
|
+
var c = Counter(value = 0)
|
|
1528
|
+
|
|
1529
|
+
for i in 0..3:
|
|
1530
|
+
c.bump()
|
|
1531
|
+
|
|
1532
|
+
let text = f"count=#{c.read()}"
|
|
1533
|
+
return 0
|
|
1534
|
+
```
|