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
data/docs/index.html
ADDED
|
@@ -0,0 +1,2778 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" data-theme="light">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Milk Tea — Language Reference</title>
|
|
7
|
+
<meta name="description" content="Milk Tea is a statically typed, indentation-based systems language for games. It targets beautiful C.">
|
|
8
|
+
<style>
|
|
9
|
+
/* ── Reset ────────────────────────────────────────────────────────────────── */
|
|
10
|
+
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
|
11
|
+
html{scroll-padding-top:4rem}
|
|
12
|
+
body{font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,sans-serif;line-height:1.65;color:var(--fg);background:var(--bg);transition:color .2s,background .2s}
|
|
13
|
+
a{color:var(--link);text-decoration:none}
|
|
14
|
+
a:hover{text-decoration:underline}
|
|
15
|
+
img,svg{max-width:100%}
|
|
16
|
+
pre,code{font-family:"SF Mono","Cascadia Code","JetBrains Mono","Fira Code",Menlo,Consolas,monospace;font-size:.875em}
|
|
17
|
+
table{border-collapse:collapse;width:100%}
|
|
18
|
+
th,td{padding:.5rem .75rem;text-align:left;border:1px solid var(--border)}
|
|
19
|
+
th{font-weight:600;background:var(--surface)}
|
|
20
|
+
|
|
21
|
+
/* ── Theme variables ──────────────────────────────────────────────────────── */
|
|
22
|
+
:root,.light,[data-theme=light]{
|
|
23
|
+
--bg:#fefaf5; --surface:#faf3ea; --raised:#fff7ef; --border:#e8d9c5;
|
|
24
|
+
--fg:#3b2b1e; --fg-dim:#7a6a5c; --fg-faint:#b8a898;
|
|
25
|
+
--link:#b05a2a; --link-hover:#8a3d18;
|
|
26
|
+
--primary:#c4713b; --primary-dim:#e8945c; --primary-bg:#fdf0e4;
|
|
27
|
+
--accent:#4a9a6e; --accent-bg:#eaf7f0;
|
|
28
|
+
--warn:#c4a035; --warn-bg:#fef9e7;
|
|
29
|
+
--error:#c04040; --error-bg:#fdf0f0;
|
|
30
|
+
--code-bg:#fdf6ee; --code-fg:#3b2b1e;
|
|
31
|
+
--sidebar-bg:#faf3ea; --sidebar-width:17rem;
|
|
32
|
+
--shadow:0 1px 3px rgba(59,43,30,.08),0 1px 2px rgba(59,43,30,.06);
|
|
33
|
+
--shadow-lg:0 4px 12px rgba(59,43,30,.1);
|
|
34
|
+
--radius:.5rem; --radius-sm:.375rem;
|
|
35
|
+
--kw:#a05020; --type:#2a7a50; --str:#707030; --num:#b05a2a; --cmt:#a89880; --doc:#8a7a60; --op:#5a4a3a; --fn:#5a50a0; --attr:#8a5a20;
|
|
36
|
+
}
|
|
37
|
+
[data-theme=dark],.dark{
|
|
38
|
+
--bg:#1a1410; --surface:#241c15; --raised:#2a2219; --border:#3a2a1a;
|
|
39
|
+
--fg:#e8d5c4; --fg-dim:#a89880; --fg-faint:#6a5a4a;
|
|
40
|
+
--link:#e8945c; --link-hover:#f0a870;
|
|
41
|
+
--primary:#e8945c; --primary-dim:#f0a870; --primary-bg:#2a1a10;
|
|
42
|
+
--accent:#5ab882; --accent-bg:#1a2a20;
|
|
43
|
+
--warn:#d4b048; --warn-bg:#2a2410;
|
|
44
|
+
--error:#d06060; --error-bg:#2a1010;
|
|
45
|
+
--code-bg:#241c15; --code-fg:#e8d5c4;
|
|
46
|
+
--sidebar-bg:#1f1813;
|
|
47
|
+
--shadow:0 1px 3px rgba(0,0,0,.3),0 1px 2px rgba(0,0,0,.2);
|
|
48
|
+
--shadow-lg:0 4px 12px rgba(0,0,0,.4);
|
|
49
|
+
--kw:#f0a870; --type:#5ab882; --str:#c4b858; --num:#e8945c; --cmt:#6a5a4a; --doc:#7a6a5a; --op:#a89880; --fn:#9a90d8; --attr:#c49058;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* ── Layout ───────────────────────────────────────────────────────────────── */
|
|
53
|
+
body{display:flex;min-height:100vh}
|
|
54
|
+
.sidebar{position:fixed;top:0;left:0;bottom:0;width:var(--sidebar-width);background:var(--sidebar-bg);border-right:1px solid var(--border);overflow-y:auto;overflow-x:hidden;z-index:100;padding:1rem;display:flex;flex-direction:column;transition:transform .25s ease}
|
|
55
|
+
.sidebar.closed{transform:translateX(-100%)}
|
|
56
|
+
.main{margin-left:var(--sidebar-width);flex:1;min-width:0;padding:2rem 2.5rem 4rem;max-width:56rem}
|
|
57
|
+
.main.expanded{margin-left:0}
|
|
58
|
+
|
|
59
|
+
/* ── Sidebar ──────────────────────────────────────────────────────────────── */
|
|
60
|
+
.sidebar-logo{display:flex;align-items:center;gap:.5rem;padding:.5rem 0 1rem;font-size:1.15rem;font-weight:700;color:var(--fg);user-select:none}
|
|
61
|
+
.sidebar-logo .icon{font-size:1.35rem}
|
|
62
|
+
.sidebar-search{margin-bottom:.75rem}
|
|
63
|
+
.sidebar-search input{width:100%;padding:.45rem .75rem;border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--raised);color:var(--fg);font-size:.85rem;outline:none;transition:border-color .2s,box-shadow .2s}
|
|
64
|
+
.sidebar-search input:focus{border-color:var(--primary-dim);box-shadow:0 0 0 3px rgba(196,113,59,.15)}
|
|
65
|
+
.sidebar-nav{flex:1;overflow-y:auto;font-size:.88rem}
|
|
66
|
+
.sidebar-nav details{margin-bottom:.15rem}
|
|
67
|
+
.sidebar-nav summary{padding:.3rem .5rem;border-radius:var(--radius-sm);cursor:pointer;color:var(--fg-dim);font-weight:600;user-select:none;transition:background .15s}
|
|
68
|
+
.sidebar-nav summary:hover{background:var(--raised)}
|
|
69
|
+
.sidebar-nav summary::-webkit-details-marker{display:none}
|
|
70
|
+
.sidebar-nav details[open] > summary{color:var(--fg)}
|
|
71
|
+
.sidebar-nav a{display:block;padding:.25rem .5rem .25rem 1.75rem;border-radius:var(--radius-sm);color:var(--fg-dim);font-size:.83rem;transition:background .15s,color .15s}
|
|
72
|
+
.sidebar-nav a:hover{color:var(--fg);background:var(--raised);text-decoration:none}
|
|
73
|
+
.sidebar-nav a.active{color:var(--primary);background:var(--primary-bg);font-weight:500}
|
|
74
|
+
.sidebar-footer{padding-top:.75rem;border-top:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;font-size:.8rem;color:var(--fg-faint)}
|
|
75
|
+
|
|
76
|
+
/* ── Topbar (mobile) ───────────────────────────────────────────────────────── */
|
|
77
|
+
.topbar{display:none;position:sticky;top:0;z-index:99;background:var(--bg);border-bottom:1px solid var(--border);padding:.65rem 1rem;align-items:center;gap:.75rem}
|
|
78
|
+
.topbar .menu-btn{background:none;border:none;color:var(--fg);font-size:1.35rem;cursor:pointer;padding:.25rem;line-height:1}
|
|
79
|
+
.topbar .logo-text{font-weight:700;font-size:1rem}
|
|
80
|
+
.topbar .theme-btn{margin-left:auto;background:none;border:none;color:var(--fg-dim);font-size:1.1rem;cursor:pointer;padding:.25rem .35rem;border-radius:var(--radius-sm);transition:background .15s,color .15s}
|
|
81
|
+
.topbar .theme-btn:hover{background:var(--surface);color:var(--fg)}
|
|
82
|
+
.theme-btn{background:none;border:1px solid var(--border);color:var(--fg-dim);font-size:.95rem;cursor:pointer;padding:.3rem .45rem;border-radius:var(--radius-sm);transition:background .15s,border-color .15s;display:flex;align-items:center;gap:.25rem}
|
|
83
|
+
.theme-btn:hover{background:var(--surface);border-color:var(--fg-dim)}
|
|
84
|
+
|
|
85
|
+
/* ── Hero ──────────────────────────────────────────────────────────────────── */
|
|
86
|
+
.hero{padding:3rem 0 2rem;margin-bottom:1.5rem;border-bottom:1px solid var(--border)}
|
|
87
|
+
.hero h1{font-size:2.8rem;font-weight:800;letter-spacing:-.02em;line-height:1.15;margin-bottom:.5rem}
|
|
88
|
+
.hero h1 span{color:var(--primary)}
|
|
89
|
+
.hero .tagline{font-size:1.2rem;color:var(--fg-dim);margin-bottom:1.25rem;max-width:36rem}
|
|
90
|
+
.hero .badges{display:flex;flex-wrap:wrap;gap:.4rem;margin-bottom:1.5rem}
|
|
91
|
+
.hero .badge{display:inline-flex;align-items:center;gap:.3rem;padding:.25rem .65rem;border-radius:3rem;font-size:.8rem;font-weight:500;background:var(--primary-bg);color:var(--primary)}
|
|
92
|
+
.hero .badge.ac{background:var(--accent-bg);color:var(--accent)}
|
|
93
|
+
.hero code{background:var(--code-bg);padding:.1rem .35rem;border-radius:var(--radius-sm);font-size:.9em}
|
|
94
|
+
|
|
95
|
+
/* ── Section headings ──────────────────────────────────────────────────────── */
|
|
96
|
+
section{padding:2rem 0;border-bottom:1px solid var(--border)}
|
|
97
|
+
section:last-of-type{border-bottom:none}
|
|
98
|
+
h2{font-size:1.75rem;font-weight:700;letter-spacing:-.01em;margin-bottom:1rem;color:var(--fg)}
|
|
99
|
+
h3{font-size:1.25rem;font-weight:600;margin:1.5rem 0 .75rem;color:var(--fg)}
|
|
100
|
+
h4{font-size:1.05rem;font-weight:600;margin:1.25rem 0 .5rem;color:var(--fg-dim)}
|
|
101
|
+
p{margin-bottom:.75rem;max-width:44rem}
|
|
102
|
+
p + p{margin-top:-.25rem}
|
|
103
|
+
|
|
104
|
+
/* ── Code blocks ───────────────────────────────────────────────────────────── */
|
|
105
|
+
.code-wrap{position:relative;margin:1rem 0 1.5rem;border-radius:var(--radius);overflow:hidden;border:1px solid var(--border);background:var(--code-bg);box-shadow:var(--shadow)}
|
|
106
|
+
.code-wrap .code-label{position:absolute;top:0;right:0;display:flex;gap:.15rem;padding:.3rem .5rem}
|
|
107
|
+
.code-wrap .copy-btn{background:var(--raised);border:1px solid var(--border);color:var(--fg-dim);font-size:.7rem;cursor:pointer;padding:.15rem .45rem;border-radius:var(--radius-sm);opacity:0;transition:opacity .2s}
|
|
108
|
+
.code-wrap:hover .copy-btn{opacity:1}
|
|
109
|
+
.code-wrap .copy-btn.copied{color:var(--accent);border-color:var(--accent)}
|
|
110
|
+
.code-wrap pre{margin:0;padding:1.1rem 1.25rem;overflow-x:auto;line-height:1.55;color:var(--code-fg);tab-size:4;-moz-tab-size:4}
|
|
111
|
+
.code-wrap pre code{font-size:.85rem;white-space:pre}
|
|
112
|
+
.code-inline{background:var(--code-bg);padding:.12rem .4rem;border-radius:var(--radius-sm);font-size:.88em;border:1px solid var(--border)}
|
|
113
|
+
/* Syntax tokens */
|
|
114
|
+
.syn-kw{color:var(--kw);font-weight:600}
|
|
115
|
+
.syn-type{color:var(--type);font-style:italic}
|
|
116
|
+
.syn-str{color:var(--str)}
|
|
117
|
+
.syn-num{color:var(--num)}
|
|
118
|
+
.syn-cmt{color:var(--cmt);font-style:italic}
|
|
119
|
+
.syn-doc{color:var(--doc);font-style:italic}
|
|
120
|
+
.syn-op{color:var(--op)}
|
|
121
|
+
.syn-fn{color:var(--fn)}
|
|
122
|
+
.syn-attr{color:var(--attr)}
|
|
123
|
+
.syn-atom{color:var(--kw);font-weight:500}
|
|
124
|
+
|
|
125
|
+
/* ── Tables ────────────────────────────────────────────────────────────────── */
|
|
126
|
+
.table-wrap{overflow-x:auto;margin:1rem 0 1.5rem}
|
|
127
|
+
.attr-table th{font-size:.82rem;color:var(--fg-dim);white-space:nowrap}
|
|
128
|
+
.attr-table td{font-size:.85rem}
|
|
129
|
+
.attr-table td:first-child{white-space:nowrap}
|
|
130
|
+
.attr-table td:first-child code{background:var(--code-bg);padding:.08rem .35rem;border-radius:var(--radius-sm);font-size:.88em}
|
|
131
|
+
.attr-table tr:hover td{background:var(--surface)}
|
|
132
|
+
|
|
133
|
+
/* ── Callouts ──────────────────────────────────────────────────────────────── */
|
|
134
|
+
.callout{margin:1rem 0;padding:.85rem 1rem;border-radius:var(--radius);font-size:.9rem;border-left:3px solid var(--border)}
|
|
135
|
+
.callout.note{border-left-color:var(--primary-dim);background:var(--primary-bg)}
|
|
136
|
+
.callout.warn{border-left-color:var(--warn);background:var(--warn-bg)}
|
|
137
|
+
.callout.tip{border-left-color:var(--accent);background:var(--accent-bg)}
|
|
138
|
+
|
|
139
|
+
/* ── Search result highlight ───────────────────────────────────────────────── */
|
|
140
|
+
.search-highlight{background:rgba(196,161,53,.25);border-radius:2px}
|
|
141
|
+
.section-hidden{display:none!important}
|
|
142
|
+
|
|
143
|
+
/* ── Overlay (mobile sidebar) ──────────────────────────────────────────────── */
|
|
144
|
+
.overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.35);z-index:99}
|
|
145
|
+
.overlay.visible{display:block}
|
|
146
|
+
|
|
147
|
+
/* ── Progress bar ──────────────────────────────────────────────────────────── */
|
|
148
|
+
.progress{position:fixed;top:0;left:0;height:3px;background:linear-gradient(90deg,var(--primary),var(--accent));z-index:200;transition:width .1s linear}
|
|
149
|
+
|
|
150
|
+
/* ── Responsive ────────────────────────────────────────────────────────────── */
|
|
151
|
+
@media (max-width:1023px){
|
|
152
|
+
:root{--sidebar-width:16rem}
|
|
153
|
+
}
|
|
154
|
+
@media (max-width:767px){
|
|
155
|
+
.sidebar{transform:translateX(-100%)}
|
|
156
|
+
.sidebar.open{transform:translateX(0)}
|
|
157
|
+
.main{margin-left:0;padding:1.25rem 1rem 3rem}
|
|
158
|
+
.topbar{display:flex}
|
|
159
|
+
.hero h1{font-size:2rem}
|
|
160
|
+
.hero{padding:1.5rem 0 1.25rem}
|
|
161
|
+
.code-wrap pre{padding:.85rem 1rem;font-size:.82rem}
|
|
162
|
+
h2{font-size:1.4rem}
|
|
163
|
+
h3{font-size:1.1rem}
|
|
164
|
+
}
|
|
165
|
+
@media (min-width:768px){
|
|
166
|
+
.theme-btn-desktop{display:flex!important}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* ── Utility ───────────────────────────────────────────────────────────────── */
|
|
170
|
+
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
|
|
171
|
+
.flex{display:flex;gap:.5rem;flex-wrap:wrap}
|
|
172
|
+
</style>
|
|
173
|
+
</head>
|
|
174
|
+
<body>
|
|
175
|
+
|
|
176
|
+
<div class="progress" id="progress"></div>
|
|
177
|
+
<div class="overlay" id="overlay"></div>
|
|
178
|
+
|
|
179
|
+
<!-- ─── Sidebar ───────────────────────────────────────────────────────────── -->
|
|
180
|
+
<aside class="sidebar" id="sidebar">
|
|
181
|
+
<div class="sidebar-logo">
|
|
182
|
+
<a href="./" style="color:inherit;text-decoration:none;display:flex;align-items:center;gap:.5rem">
|
|
183
|
+
<span class="icon">🧋</span>
|
|
184
|
+
Milk Tea
|
|
185
|
+
</a>
|
|
186
|
+
</div>
|
|
187
|
+
<div class="sidebar-search">
|
|
188
|
+
<input type="text" id="search" placeholder="Search reference…" autocomplete="off">
|
|
189
|
+
</div>
|
|
190
|
+
<nav class="sidebar-nav" id="sidebar-nav">
|
|
191
|
+
<details open><summary>Introduction</summary>
|
|
192
|
+
<a href="#overview">Overview</a>
|
|
193
|
+
<a href="#getting-started">Getting Started</a>
|
|
194
|
+
<a href="#full-example">Full Example</a>
|
|
195
|
+
</details>
|
|
196
|
+
|
|
197
|
+
<details open><summary>Language Basics</summary>
|
|
198
|
+
<a href="#source-files">Source Files & Modules</a>
|
|
199
|
+
<a href="#lexical-rules">Lexical Rules</a>
|
|
200
|
+
<a href="#variables-guards">Variables & Guards</a>
|
|
201
|
+
<a href="#data-declarations">Data Declarations</a>
|
|
202
|
+
</details>
|
|
203
|
+
|
|
204
|
+
<details open><summary>Types & Contracts</summary>
|
|
205
|
+
<a href="#interfaces-methods">Interfaces & Methods</a>
|
|
206
|
+
<a href="#type-system">Type System</a>
|
|
207
|
+
<a href="#generics">Generics</a>
|
|
208
|
+
</details>
|
|
209
|
+
|
|
210
|
+
<details open><summary>Functions</summary>
|
|
211
|
+
<a href="#functions">Functions</a>
|
|
212
|
+
</details>
|
|
213
|
+
|
|
214
|
+
<details open><summary>Control Flow</summary>
|
|
215
|
+
<a href="#control-flow">Statements</a>
|
|
216
|
+
<a href="#match">Match Patterns</a>
|
|
217
|
+
<a href="#defer-unsafe">Defer & Unsafe</a>
|
|
218
|
+
<a href="#compile-time">Compile-Time</a>
|
|
219
|
+
</details>
|
|
220
|
+
|
|
221
|
+
<details open><summary>Expressions & Operators</summary>
|
|
222
|
+
<a href="#expressions">Expressions</a>
|
|
223
|
+
</details>
|
|
224
|
+
|
|
225
|
+
<details open><summary>Built-in Surface</summary>
|
|
226
|
+
<a href="#builtins">Built-in Callables</a>
|
|
227
|
+
<a href="#reflection">Compile-Time Reflection</a>
|
|
228
|
+
</details>
|
|
229
|
+
|
|
230
|
+
<details open><summary>Text & Strings</summary>
|
|
231
|
+
<a href="#strings">Strings & Text</a>
|
|
232
|
+
</details>
|
|
233
|
+
|
|
234
|
+
<details><summary>Advanced</summary>
|
|
235
|
+
<a href="#safety">Safety Rules</a>
|
|
236
|
+
<a href="#async">Async</a>
|
|
237
|
+
<a href="#concurrency">Concurrency</a>
|
|
238
|
+
<a href="#events">Events</a>
|
|
239
|
+
</details>
|
|
240
|
+
|
|
241
|
+
<details><summary>Reference</summary>
|
|
242
|
+
<a href="#std-library">Standard Library</a>
|
|
243
|
+
<a href="#linting">Linting</a>
|
|
244
|
+
<a href="#cli">CLI Reference</a>
|
|
245
|
+
<a href="#limitations">Language Limitations</a>
|
|
246
|
+
</details>
|
|
247
|
+
</nav>
|
|
248
|
+
<div class="sidebar-footer">
|
|
249
|
+
<button class="theme-btn theme-btn-desktop" id="theme-toggle" aria-label="Toggle theme" style="display:none">
|
|
250
|
+
<span class="icon-sun">☀</span>
|
|
251
|
+
<span class="icon-moon">🌙</span>
|
|
252
|
+
<span class="label">Theme</span>
|
|
253
|
+
</button>
|
|
254
|
+
<span style="font-size:.75rem">v1 · Language Reference</span>
|
|
255
|
+
</div>
|
|
256
|
+
</aside>
|
|
257
|
+
|
|
258
|
+
<!-- ─── Topbar (mobile) ────────────────────────────────────────────────────── -->
|
|
259
|
+
<div class="topbar">
|
|
260
|
+
<button class="menu-btn" id="menu-btn" aria-label="Toggle menu">☰</button>
|
|
261
|
+
<span class="logo-text">🍵 Milk Tea</span>
|
|
262
|
+
<button class="theme-btn" id="theme-toggle-mbl" aria-label="Toggle theme">🌙</button>
|
|
263
|
+
</div>
|
|
264
|
+
|
|
265
|
+
<!-- ─── Main Content ───────────────────────────────────────────────────────── -->
|
|
266
|
+
<main class="main" id="main">
|
|
267
|
+
|
|
268
|
+
<!-- ── Hero ────────────────────────────────────────────────────────────────── -->
|
|
269
|
+
<div class="hero">
|
|
270
|
+
<h1>Milk Tea <span>Reference</span></h1>
|
|
271
|
+
<p class="tagline">A statically typed, indentation-based systems language for games. Safe by default, explicit by design, compiling to <strong>beautiful C</strong>.</p>
|
|
272
|
+
<div class="badges">
|
|
273
|
+
<span class="badge">⚡ Statically Typed</span>
|
|
274
|
+
<span class="badge">🌎 Compiles to C</span>
|
|
275
|
+
<span class="badge ac">🔒 Safe by Default</span>
|
|
276
|
+
<span class="badge">🔧 Data-Oriented</span>
|
|
277
|
+
<span class="badge">☘ No GC</span>
|
|
278
|
+
<span class="badge ac">🔗 First-Class C FFI</span>
|
|
279
|
+
</div>
|
|
280
|
+
</div>
|
|
281
|
+
|
|
282
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
283
|
+
<section id="overview">
|
|
284
|
+
<h2>Overview</h2>
|
|
285
|
+
<p>Milk Tea code looks like code, not punctuation. It uses indentation for blocks, words over symbols, and explicit surface for allocation and unsafe operations. The compiler <code class="code-inline">mtc</code> checks, builds, and runs <code class="code-inline">.mt</code> source files, emitting readable C that a human can debug.</p>
|
|
286
|
+
|
|
287
|
+
<div class="code-wrap">
|
|
288
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
289
|
+
<pre><code>## A minimal Milk Tea program.
|
|
290
|
+
import std.stdio as io
|
|
291
|
+
|
|
292
|
+
function main() -> int:
|
|
293
|
+
io.print_line("Hello, Milk Tea!")
|
|
294
|
+
return 0</code></pre>
|
|
295
|
+
</div>
|
|
296
|
+
|
|
297
|
+
<h3>Core Design</h3>
|
|
298
|
+
<div class="table-wrap">
|
|
299
|
+
<table class="attr-table">
|
|
300
|
+
<tr><th>Principle</th><th>Description</th></tr>
|
|
301
|
+
<tr><td><strong>What you see is what runs</strong></td><td>No hidden allocation, dispatch, or heap traffic. Every cost is spelled in source.</td></tr>
|
|
302
|
+
<tr><td><strong>C is the ABI ground truth</strong></td><td>Structs, unions, pointers, and calling conventions map directly without lossy translation.</td></tr>
|
|
303
|
+
<tr><td><strong>Safe by default, unsafe by choice</strong></td><td>Pointer operations require <code>unsafe</code>. Indexing is bounds-checked. Conversions are explicit.</td></tr>
|
|
304
|
+
<tr><td><strong>Data-oriented first</strong></td><td>Plain structs, arrays, spans, pools, and arenas matter more than elaborate object systems.</td></tr>
|
|
305
|
+
<tr><td><strong>One job, one canonical surface</strong></td><td>No duplicated everyday spellings for the same concept.</td></tr>
|
|
306
|
+
</table>
|
|
307
|
+
</div>
|
|
308
|
+
</section>
|
|
309
|
+
|
|
310
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
311
|
+
<section id="getting-started">
|
|
312
|
+
<h2>Getting Started</h2>
|
|
313
|
+
|
|
314
|
+
<h3>Hello World</h3>
|
|
315
|
+
<div class="code-wrap">
|
|
316
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
317
|
+
<pre><code>function main() -> int:
|
|
318
|
+
return 0</code></pre>
|
|
319
|
+
</div>
|
|
320
|
+
<p>Save as <code class="code-inline">hello.mt</code> and run:</p>
|
|
321
|
+
<div class="code-wrap">
|
|
322
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
323
|
+
<pre><code>mtc run hello.mt</code></pre>
|
|
324
|
+
</div>
|
|
325
|
+
|
|
326
|
+
<h3>Essential CLI</h3>
|
|
327
|
+
<div class="table-wrap">
|
|
328
|
+
<table class="attr-table">
|
|
329
|
+
<tr><th>Command</th><th>Description</th></tr>
|
|
330
|
+
<tr><td><code>mtc check <path></code></td><td>Type-check + lint; reports all diagnostics sorted by line</td></tr>
|
|
331
|
+
<tr><td><code>mtc run <path></code></td><td>Build and execute</td></tr>
|
|
332
|
+
<tr><td><code>mtc build <path></code></td><td>Build only (emit C, compile, link)</td></tr>
|
|
333
|
+
<tr><td><code>mtc format <path></code></td><td>Format sources in place (<code>--check</code> for dry-run)</td></tr>
|
|
334
|
+
<tr><td><code>mtc lint <path></code></td><td>Run linter (<code>--fix</code> to apply fixes)</td></tr>
|
|
335
|
+
<tr><td><code>mtc test <path></code></td><td>Discover and run <code>@[test]</code> functions</td></tr>
|
|
336
|
+
<tr><td><code>mtc new <name></code></td><td>Scaffold a new package</td></tr>
|
|
337
|
+
</table>
|
|
338
|
+
</div>
|
|
339
|
+
<p>See <a href="#cli">CLI Reference</a> for the full command surface.</p>
|
|
340
|
+
</section>
|
|
341
|
+
|
|
342
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
343
|
+
<section id="generated-c">
|
|
344
|
+
<h2>Generated C</h2>
|
|
345
|
+
<p>Milk Tea compiles to readable C that mirrors the source structure. No hidden runtime, no hidden heap traffic, no hidden dispatch. The output is designed for a human to debug, diff, and ship.</p>
|
|
346
|
+
|
|
347
|
+
<div class="code-wrap">
|
|
348
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
349
|
+
<pre><code>## Milk Tea
|
|
350
|
+
function add(a: int, b: int) -> int:
|
|
351
|
+
return a + b
|
|
352
|
+
|
|
353
|
+
struct Vec2:
|
|
354
|
+
x: float
|
|
355
|
+
y: float
|
|
356
|
+
|
|
357
|
+
extending Vec2:
|
|
358
|
+
editable function scale(factor: float):
|
|
359
|
+
this.x *= factor
|
|
360
|
+
this.y *= factor</code></pre>
|
|
361
|
+
</div>
|
|
362
|
+
|
|
363
|
+
<div class="code-wrap">
|
|
364
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
365
|
+
<pre><code>/* Generated C */
|
|
366
|
+
int main_add(int a, int b) {
|
|
367
|
+
return a + b;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
typedef struct { float x; float y; } main_Vec2;
|
|
371
|
+
|
|
372
|
+
void main_Vec2_scale(main_Vec2* this, float factor) {
|
|
373
|
+
this->x *= factor;
|
|
374
|
+
this->y *= factor;
|
|
375
|
+
}</code></pre>
|
|
376
|
+
</div>
|
|
377
|
+
|
|
378
|
+
<div class="callout note">
|
|
379
|
+
<strong>Method lowering:</strong> <code>editable function</code> receives a writable pointer prefix. <code>function</code> (value receiver) passes the struct by value. <code>static function</code> receives no receiver. <code>defer</code> lowers to C cleanup labels. Module names prefix generated symbols to avoid collisions.
|
|
380
|
+
</div>
|
|
381
|
+
</section>
|
|
382
|
+
|
|
383
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
384
|
+
<section id="full-example">
|
|
385
|
+
<h2>Milk Tea at a Glance</h2>
|
|
386
|
+
<p>A single program demonstrating the breadth of Milk Tea: data types, interfaces, generics, control flow, error handling, closures, compile-time reflection, unsafe, events, async, and more. Section headers in <code class="code-inline">##</code> doc comments mark each feature.</p>
|
|
387
|
+
|
|
388
|
+
<div class="code-wrap">
|
|
389
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
390
|
+
<pre><code>## ── Imports ──────────────────────────────────────────────────────────
|
|
391
|
+
import std.stdio as io
|
|
392
|
+
import std.hash
|
|
393
|
+
import std.vec
|
|
394
|
+
import std.map
|
|
395
|
+
import std.math
|
|
396
|
+
import std.mem.heap as heap
|
|
397
|
+
import std.fmt
|
|
398
|
+
|
|
399
|
+
## ── Constants ────────────────────────────────────────────────────────
|
|
400
|
+
const MAX_ENTITIES: int = 256
|
|
401
|
+
const GRAVITY: float = 9.81
|
|
402
|
+
|
|
403
|
+
## Block-bodied constant (computed at compile time)
|
|
404
|
+
const BUFFER_SIZE -> int:
|
|
405
|
+
var n: int = 64
|
|
406
|
+
while n < MAX_ENTITIES:
|
|
407
|
+
n = n * 2
|
|
408
|
+
return n
|
|
409
|
+
|
|
410
|
+
## const function: evaluable at compile time, also callable at runtime
|
|
411
|
+
const function square(x: int) -> int:
|
|
412
|
+
return x * x
|
|
413
|
+
|
|
414
|
+
const TILE_AREA: int = square(16)
|
|
415
|
+
|
|
416
|
+
## ── Type Alias ───────────────────────────────────────────────────────
|
|
417
|
+
type EntityId = uint
|
|
418
|
+
|
|
419
|
+
## ── Enum (explicit backing type, exhaustive match) ───────────────────
|
|
420
|
+
enum EntityKind: ubyte
|
|
421
|
+
player = 0
|
|
422
|
+
enemy = 1
|
|
423
|
+
npc = 2
|
|
424
|
+
|
|
425
|
+
## ── Flags (named bitmask with composite alias) ──────────────────────
|
|
426
|
+
flags StatusFlags: uint
|
|
427
|
+
poisoned = 1 << 0
|
|
428
|
+
stunned = 1 << 1
|
|
429
|
+
shielded = 1 << 2
|
|
430
|
+
debuffed = StatusFlags.poisoned | StatusFlags.stunned
|
|
431
|
+
|
|
432
|
+
## ── Struct with nested struct ────────────────────────────────────────
|
|
433
|
+
struct Entity:
|
|
434
|
+
id: EntityId
|
|
435
|
+
kind: EntityKind
|
|
436
|
+
name: str
|
|
437
|
+
hp: int
|
|
438
|
+
max_hp: int
|
|
439
|
+
position: vec3
|
|
440
|
+
|
|
441
|
+
struct Stats:
|
|
442
|
+
attack: int
|
|
443
|
+
defense: int
|
|
444
|
+
speed: float
|
|
445
|
+
|
|
446
|
+
stats: Stats
|
|
447
|
+
|
|
448
|
+
## ── Variant (tagged union with payload arms) ────────────────────────
|
|
449
|
+
variant Action:
|
|
450
|
+
move(dx: float, dy: float)
|
|
451
|
+
attack(target_id: EntityId, damage: int)
|
|
452
|
+
heal(amount: int)
|
|
453
|
+
idle
|
|
454
|
+
|
|
455
|
+
## ── Interfaces (nominal contracts, no hidden dispatch) ──────────────
|
|
456
|
+
public interface Damageable:
|
|
457
|
+
editable function take_damage(amount: int) -> void
|
|
458
|
+
function is_alive() -> bool
|
|
459
|
+
|
|
460
|
+
interface Named:
|
|
461
|
+
function display_name() -> str
|
|
462
|
+
|
|
463
|
+
## ── Struct implementing multiple interfaces ─────────────────────────
|
|
464
|
+
struct Player implements Damageable, Named:
|
|
465
|
+
entity: Entity
|
|
466
|
+
score: int
|
|
467
|
+
level: int
|
|
468
|
+
|
|
469
|
+
## ── User-defined attribute ──────────────────────────────────────────
|
|
470
|
+
attribute[field] column(name: str)
|
|
471
|
+
|
|
472
|
+
struct SaveData:
|
|
473
|
+
@[column(name = "lvl")]
|
|
474
|
+
level: int
|
|
475
|
+
@[column(name = "pts")]
|
|
476
|
+
score: int
|
|
477
|
+
|
|
478
|
+
## ── Opaque type (unknown C layout) ──────────────────────────────────
|
|
479
|
+
opaque NativeHandle
|
|
480
|
+
|
|
481
|
+
## ── Methods via extending ───────────────────────────────────────────
|
|
482
|
+
## function = value receiver, editable function = mutable, static = no receiver
|
|
483
|
+
extending Entity:
|
|
484
|
+
function is_alive() -> bool:
|
|
485
|
+
return this.hp > 0
|
|
486
|
+
|
|
487
|
+
editable function apply_damage(amount: int) -> void:
|
|
488
|
+
let actual = amount - this.stats.defense
|
|
489
|
+
if actual > 0:
|
|
490
|
+
this.hp -= actual
|
|
491
|
+
if this.hp < 0:
|
|
492
|
+
this.hp = 0
|
|
493
|
+
|
|
494
|
+
static function create(id: EntityId, kind: EntityKind, name: str) -> Entity:
|
|
495
|
+
return Entity(
|
|
496
|
+
id = id,
|
|
497
|
+
kind = kind,
|
|
498
|
+
name = name,
|
|
499
|
+
hp = 100,
|
|
500
|
+
max_hp = 100,
|
|
501
|
+
position = vec3(x = 0.0, y = 0.0, z = 0.0),
|
|
502
|
+
stats = Entity.Stats(attack = 10, defense = 5, speed = 1.0),
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
extending Player:
|
|
506
|
+
editable function take_damage(amount: int) -> void:
|
|
507
|
+
this.entity.apply_damage(amount)
|
|
508
|
+
|
|
509
|
+
function is_alive() -> bool:
|
|
510
|
+
return this.entity.is_alive()
|
|
511
|
+
|
|
512
|
+
function display_name() -> str:
|
|
513
|
+
return this.entity.name
|
|
514
|
+
|
|
515
|
+
## .with() returns a copy with specified fields replaced
|
|
516
|
+
function with_score(new_score: int) -> Player:
|
|
517
|
+
return this.with(score = new_score)
|
|
518
|
+
|
|
519
|
+
## ── Hash/equal hooks (enable use as Map/Set keys) ───────────────────
|
|
520
|
+
extending Entity.Stats:
|
|
521
|
+
static function hash(value: const_ptr[Entity.Stats]) -> uint:
|
|
522
|
+
return unsafe: uint<-read(value).attack ^ uint<-read(value).defense
|
|
523
|
+
|
|
524
|
+
static function equal(a: const_ptr[Entity.Stats], b: const_ptr[Entity.Stats]) -> bool:
|
|
525
|
+
let la = unsafe: read(a)
|
|
526
|
+
let ra = unsafe: read(b)
|
|
527
|
+
return la.attack == ra.attack and la.defense == ra.defense
|
|
528
|
+
|
|
529
|
+
## ── Generic function with interface constraint ──────────────────────
|
|
530
|
+
function damage_all[T implements Damageable](targets: span[T], amount: int) -> void:
|
|
531
|
+
for i in 0..targets.len:
|
|
532
|
+
if targets[i].is_alive():
|
|
533
|
+
targets[i].take_damage(amount)
|
|
534
|
+
|
|
535
|
+
## Multiple constraints with 'and'
|
|
536
|
+
function describe[T implements Damageable and Named](target: ref[T]) -> str:
|
|
537
|
+
let alive = if target.is_alive(): "alive" else: "dead"
|
|
538
|
+
return f"#{target.display_name()} (#{alive})"
|
|
539
|
+
|
|
540
|
+
## ── Value parameter generic [N: int] ────────────────────────────────
|
|
541
|
+
function make_buffer[N: int]() -> str_buffer[N]:
|
|
542
|
+
var buf: str_buffer[N]
|
|
543
|
+
return buf
|
|
544
|
+
|
|
545
|
+
## ── Error handling with Result[T, E] ────────────────────────────────
|
|
546
|
+
enum LoadError: ubyte
|
|
547
|
+
not_found = 1
|
|
548
|
+
invalid = 2
|
|
549
|
+
|
|
550
|
+
function load_entity(id: EntityId) -> Result[Entity, LoadError]:
|
|
551
|
+
if id == 0:
|
|
552
|
+
return Result[Entity, LoadError].failure(error = LoadError.not_found)
|
|
553
|
+
return Result[Entity, LoadError].success(
|
|
554
|
+
value = Entity.create(id, EntityKind.player, "Hero"),
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
## Postfix ? propagation (enclosing must return compatible Result or Option)
|
|
558
|
+
function load_pair() -> Result[bool, LoadError]:
|
|
559
|
+
let first = load_entity(1)?
|
|
560
|
+
let second = load_entity(2)?
|
|
561
|
+
io.print_line(f"Loaded #{first.name} and #{second.name}")
|
|
562
|
+
return Result[bool, LoadError].success(value = true)
|
|
563
|
+
|
|
564
|
+
## ── Events (fixed-capacity pub/sub, zero heap during dispatch) ──────
|
|
565
|
+
struct DamageEvent:
|
|
566
|
+
target_id: EntityId
|
|
567
|
+
amount: int
|
|
568
|
+
|
|
569
|
+
event entity_damaged[8](DamageEvent)
|
|
570
|
+
|
|
571
|
+
function on_damage(evt: DamageEvent) -> void:
|
|
572
|
+
io.print_line(f"entity #{evt.target_id} took #{evt.amount} damage")
|
|
573
|
+
|
|
574
|
+
## ── Proc (closure with value captures) ──────────────────────────────
|
|
575
|
+
function make_scaler(factor: float) -> proc(x: float) -> float:
|
|
576
|
+
return proc(x: float) -> float: x * factor
|
|
577
|
+
|
|
578
|
+
## ── Async / await ───────────────────────────────────────────────────
|
|
579
|
+
async function fetch_score(id: EntityId) -> int:
|
|
580
|
+
return 42
|
|
581
|
+
|
|
582
|
+
async function load_scores() -> int:
|
|
583
|
+
let a = await fetch_score(1)
|
|
584
|
+
let b = await fetch_score(2)
|
|
585
|
+
return a + b
|
|
586
|
+
|
|
587
|
+
## ── Compile-time constants for inline if ─────────────────────────────
|
|
588
|
+
const DEBUG: bool = false
|
|
589
|
+
|
|
590
|
+
## ── Main entry point ────────────────────────────────────────────────
|
|
591
|
+
function main() -> int:
|
|
592
|
+
## Local declarations: let (immutable), var (mutable)
|
|
593
|
+
let width: int = 800
|
|
594
|
+
var score: int = 0
|
|
595
|
+
|
|
596
|
+
## str_buffer: fixed-capacity mutable UTF-8 text
|
|
597
|
+
var name_buf: str_buffer[64]
|
|
598
|
+
name_buf.assign("World")
|
|
599
|
+
name_buf.append("!")
|
|
600
|
+
io.print_line(name_buf.as_str())
|
|
601
|
+
|
|
602
|
+
## Struct construction with named fields
|
|
603
|
+
var player = Player(
|
|
604
|
+
entity = Entity.create(1, EntityKind.player, "Hero"),
|
|
605
|
+
score = 0,
|
|
606
|
+
level = 1,
|
|
607
|
+
)
|
|
608
|
+
|
|
609
|
+
## Tuple construction & destructuring
|
|
610
|
+
let pair = (42, "hello")
|
|
611
|
+
let (count, greeting) = pair
|
|
612
|
+
let named_tuple = (x = 10, y = 20)
|
|
613
|
+
let (tx, ty) = named_tuple
|
|
614
|
+
|
|
615
|
+
## Struct destructuring
|
|
616
|
+
let Entity.Stats(attack, defense, speed) = player.entity.stats
|
|
617
|
+
|
|
618
|
+
## Guard binding: let...else: (initializer must be T?, Option[T], or Result[T, E])
|
|
619
|
+
let entity = load_entity(1) else:
|
|
620
|
+
return 1
|
|
621
|
+
|
|
622
|
+
## Guard with error binding
|
|
623
|
+
let loaded = load_pair() else as error:
|
|
624
|
+
io.print_line(f"load failed: #{error}")
|
|
625
|
+
return 1
|
|
626
|
+
|
|
627
|
+
## Native vector types & arithmetic
|
|
628
|
+
var position = vec3(x = 1.0, y = 2.0, z = 3.0)
|
|
629
|
+
let direction = vec3(x = 0.0, y = 1.0, z = 0.0)
|
|
630
|
+
position = position + direction * 0.5
|
|
631
|
+
let dist = math.sqrt(double<-(position.x * position.x + position.y * position.y))
|
|
632
|
+
|
|
633
|
+
## Array, span, recoverable indexing
|
|
634
|
+
var scores = array[int, 4](10, 20, 30, 40)
|
|
635
|
+
scores[0] = 100
|
|
636
|
+
let view: span[int] = scores.as_span()
|
|
637
|
+
let maybe_val = get(scores, 99)
|
|
638
|
+
if maybe_val == null:
|
|
639
|
+
io.print_line("Index out of bounds (safe)")
|
|
640
|
+
|
|
641
|
+
## Range index assignment
|
|
642
|
+
var buf: array[float, 4]
|
|
643
|
+
buf[0..3] = (1.0, 2.0, 3.0)
|
|
644
|
+
|
|
645
|
+
## Collections: Vec and Map (import std.hash for primitive keys)
|
|
646
|
+
var entities = vec.Vec[Entity].create()
|
|
647
|
+
defer entities.release()
|
|
648
|
+
entities.push(Entity.create(1, EntityKind.player, "Hero"))
|
|
649
|
+
entities.push(Entity.create(2, EntityKind.enemy, "Goblin"))
|
|
650
|
+
|
|
651
|
+
var name_counts = map.Map[str, int].create()
|
|
652
|
+
defer name_counts.release()
|
|
653
|
+
name_counts.set("player", 1)
|
|
654
|
+
name_counts.set("enemy", 5)
|
|
655
|
+
|
|
656
|
+
## for loop: range
|
|
657
|
+
for i in 0..3:
|
|
658
|
+
io.print_line(f"index #{i}")
|
|
659
|
+
|
|
660
|
+
## for loop: array iteration with break / continue
|
|
661
|
+
let values = array[int, 5](1, 2, 3, 4, 5)
|
|
662
|
+
for v in values:
|
|
663
|
+
if v == 3:
|
|
664
|
+
continue
|
|
665
|
+
if v == 5:
|
|
666
|
+
break
|
|
667
|
+
io.print_line(f"value #{v}")
|
|
668
|
+
|
|
669
|
+
## Parallel for (arrays/spans only, lengths must match)
|
|
670
|
+
let xs = array[float, 3](1.0, 2.0, 3.0)
|
|
671
|
+
let ys = array[float, 3](4.0, 5.0, 6.0)
|
|
672
|
+
for x, y in xs, ys:
|
|
673
|
+
io.print_line(f"#{x:.1} + #{y:.1}")
|
|
674
|
+
|
|
675
|
+
## while loop
|
|
676
|
+
var n: int = 1
|
|
677
|
+
while n < 100:
|
|
678
|
+
if n > 50:
|
|
679
|
+
break
|
|
680
|
+
n = n * 2
|
|
681
|
+
|
|
682
|
+
## if / else if / else (conditions must be bool, no truthy coercion)
|
|
683
|
+
if player.level > 10:
|
|
684
|
+
io.print_line("veteran")
|
|
685
|
+
else if player.level > 5:
|
|
686
|
+
io.print_line("experienced")
|
|
687
|
+
else:
|
|
688
|
+
io.print_line("novice")
|
|
689
|
+
|
|
690
|
+
## if-expression
|
|
691
|
+
let rank = if player.level >= 10: "master" else: "apprentice"
|
|
692
|
+
|
|
693
|
+
## match: enum (must be exhaustive or have _)
|
|
694
|
+
match player.entity.kind:
|
|
695
|
+
EntityKind.player:
|
|
696
|
+
io.print_line("is player")
|
|
697
|
+
EntityKind.enemy:
|
|
698
|
+
io.print_line("is enemy")
|
|
699
|
+
_:
|
|
700
|
+
io.print_line("other")
|
|
701
|
+
|
|
702
|
+
## match: variant with struct patterns and guards
|
|
703
|
+
let action = Action.attack(target_id = 2, damage = 15)
|
|
704
|
+
match action:
|
|
705
|
+
Action.move(dx, dy):
|
|
706
|
+
io.print_line(f"move #{dx:.2}, #{dy:.2}")
|
|
707
|
+
Action.attack(target_id, damage > 10):
|
|
708
|
+
io.print_line(f"heavy attack on #{target_id}")
|
|
709
|
+
Action.attack(target_id, damage):
|
|
710
|
+
io.print_line(f"light attack on #{target_id}")
|
|
711
|
+
Action.heal(amount):
|
|
712
|
+
io.print_line(f"heal #{amount}")
|
|
713
|
+
Action.idle:
|
|
714
|
+
pass
|
|
715
|
+
|
|
716
|
+
## match expression
|
|
717
|
+
let label = match player.level:
|
|
718
|
+
1: "beginner"
|
|
719
|
+
2: "novice"
|
|
720
|
+
_: "advanced"
|
|
721
|
+
|
|
722
|
+
## Explicit cast: T<-value
|
|
723
|
+
let raw_kind = ubyte<-player.entity.kind
|
|
724
|
+
let ratio = float<-score
|
|
725
|
+
|
|
726
|
+
## Format strings: interpolation, precision, hex, owned text
|
|
727
|
+
io.print_line(f"Player: #{player.entity.name} HP=#{player.entity.hp}")
|
|
728
|
+
io.print_line(f"Position: #{position.x:.2}, #{position.y:.2}")
|
|
729
|
+
io.print_line(f"ID hex: #{player.entity.id:x}")
|
|
730
|
+
var msg = fmt.format(f"Score: #{score}")
|
|
731
|
+
defer msg.release()
|
|
732
|
+
|
|
733
|
+
## Heredoc string
|
|
734
|
+
let help = <<-HELP
|
|
735
|
+
Commands:
|
|
736
|
+
move, attack, quit
|
|
737
|
+
HELP
|
|
738
|
+
io.print_line(help)
|
|
739
|
+
|
|
740
|
+
## Defer: cleanup at scope exit (lowers to C cleanup labels)
|
|
741
|
+
let data: own[int] = heap.must_alloc[int](1)
|
|
742
|
+
defer heap.release[int](data)
|
|
743
|
+
|
|
744
|
+
## Proc / closure
|
|
745
|
+
let scale = make_scaler(2.5)
|
|
746
|
+
let scaled = scale(10.0)
|
|
747
|
+
|
|
748
|
+
## Nullability: T? for any type (pointer-like shown here)
|
|
749
|
+
var maybe_target: ptr[Entity]? = null
|
|
750
|
+
if maybe_target == null:
|
|
751
|
+
io.print_line("no target")
|
|
752
|
+
|
|
753
|
+
## unsafe: required for pointer indexing, dereference, arithmetic, casts
|
|
754
|
+
unsafe:
|
|
755
|
+
let p = ptr_of(score)
|
|
756
|
+
read(p) = 42
|
|
757
|
+
|
|
758
|
+
## ref[T]: safe non-null writable alias (implicit borrow at call site)
|
|
759
|
+
let handle = ref_of(player)
|
|
760
|
+
handle.take_damage(10)
|
|
761
|
+
io.print_line(f"HP after damage: #{player.entity.hp}")
|
|
762
|
+
|
|
763
|
+
## dyn[Interface] + adapt: runtime polymorphism (fat pointer)
|
|
764
|
+
let d: dyn[Damageable] = adapt[Damageable](ref_of(player))
|
|
765
|
+
d.take_damage(5)
|
|
766
|
+
|
|
767
|
+
## Events: subscribe, emit (emit only from declaring module)
|
|
768
|
+
let sub = entity_damaged.subscribe(on_damage) else:
|
|
769
|
+
return 1
|
|
770
|
+
entity_damaged.emit(DamageEvent(target_id = 1, amount = 25))
|
|
771
|
+
entity_damaged.unsubscribe(sub)
|
|
772
|
+
|
|
773
|
+
## .with() partial field update
|
|
774
|
+
let updated = player.with_score(999)
|
|
775
|
+
|
|
776
|
+
## Compile-time reflection: inline for over struct fields
|
|
777
|
+
inline for field in fields_of(Entity.Stats):
|
|
778
|
+
io.print_line(f"field: #{field.name}")
|
|
779
|
+
|
|
780
|
+
## inline if: dead branch elimination (dead branch may reference nonexistent symbols)
|
|
781
|
+
inline if DEBUG:
|
|
782
|
+
io.print_line("debug mode")
|
|
783
|
+
|
|
784
|
+
## ── Concurrency ─────────────────────────────────────────────────────
|
|
785
|
+
## parallel for: data-parallel loop dispatched across CPU cores
|
|
786
|
+
var positions = array[float, 4](0.0, 1.0, 2.0, 3.0)
|
|
787
|
+
var velocities = array[float, 4](0.1, 0.2, 0.3, 0.4)
|
|
788
|
+
let dt: float = 0.016
|
|
789
|
+
parallel for i in 0..4:
|
|
790
|
+
positions[i] += velocities[i] * dt
|
|
791
|
+
|
|
792
|
+
## parallel: block for structured fork-join
|
|
793
|
+
var pa: int = 0
|
|
794
|
+
var pb: int = 0
|
|
795
|
+
parallel:
|
|
796
|
+
pa = 42
|
|
797
|
+
pb = 99
|
|
798
|
+
|
|
799
|
+
## atomic[T]: lock-free concurrent access
|
|
800
|
+
var counter: atomic[int]
|
|
801
|
+
counter.store(0)
|
|
802
|
+
let prev = counter.add(1)
|
|
803
|
+
|
|
804
|
+
## detach + gather: detached concurrency with explicit join
|
|
805
|
+
let a = detach load_entity(100)
|
|
806
|
+
let b = detach load_entity(200)
|
|
807
|
+
gather a, b
|
|
808
|
+
|
|
809
|
+
## static_assert: compile-time assertions
|
|
810
|
+
static_assert(size_of(int) == 4, "expected 32-bit int")
|
|
811
|
+
static_assert(MAX_ENTITIES > 0, "need at least one entity")
|
|
812
|
+
|
|
813
|
+
io.print_line(f"All done! Score=#{score}")
|
|
814
|
+
return 0</code></pre>
|
|
815
|
+
</div>
|
|
816
|
+
|
|
817
|
+
<div class="callout tip">
|
|
818
|
+
<strong>Tip:</strong> This example covers imports, constants, <code>const function</code>, type aliases, enums, flags, structs (nested, attributed), variants, interfaces, <code>extending</code> methods, generics with constraints, value parameter generics, <code>Result</code> error handling with <code>?</code> and guard binding, events, closures (<code>proc</code>), <code>async</code>/<code>await</code>, concurrency (<code>parallel for</code>, <code>parallel:</code>, <code>detach</code> + <code>gather</code>, <code>atomic[T]</code>), native vector types, arrays, spans, collections, <code>for</code>/<code>while</code>/<code>match</code>/<code>if</code>, variant membership test (<code>is</code>), parallel <code>for</code>, <code>defer</code>, <code>unsafe</code>, <code>ref[T]</code>, <code>own[T]</code>, <code>dyn[Interface]</code>, format strings, heredocs, <code>str_buffer</code>, compile-time reflection, <code>inline for</code>/<code>inline if</code>, <code>static_assert</code>, and explicit casts. See the sections below for full details on each feature.
|
|
819
|
+
</div>
|
|
820
|
+
</section>
|
|
821
|
+
|
|
822
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
823
|
+
<section id="source-files">
|
|
824
|
+
<h2>Source Files & Modules</h2>
|
|
825
|
+
|
|
826
|
+
<p>Milk Tea source files use the <code class="code-inline">.mt</code> extension. A file is either an <strong>ordinary</strong> source file or an <strong>external</strong> file for raw ABI bindings. There are no wildcard imports — every module must be imported by name.</p>
|
|
827
|
+
|
|
828
|
+
<h3>Ordinary Files</h3>
|
|
829
|
+
<p>The normal source form. No module header; module identity is inferred from the file path relative to <code class="code-inline">package.source_root</code>. <code class="code-inline">import</code> statements appear only at the top.</p>
|
|
830
|
+
<div class="code-wrap">
|
|
831
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
832
|
+
<pre><code>import std.math as math
|
|
833
|
+
import std.raylib as rl
|
|
834
|
+
|
|
835
|
+
function main() -> int:
|
|
836
|
+
return 0</code></pre>
|
|
837
|
+
</div>
|
|
838
|
+
|
|
839
|
+
<h3>External Files</h3>
|
|
840
|
+
<p>Dedicated raw ABI surface, usually for <code class="code-inline">std.c.*</code> bindings and bindgen output. Start with <code>external</code> and accept <code>include</code>, <code>link</code>, and <code>compiler_flag</code> directives. After directives, only raw ABI declarations are allowed. The <code>mtc bindgen</code> command generates external files from C headers.</p>
|
|
841
|
+
<div class="code-wrap">
|
|
842
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
843
|
+
<pre><code>external
|
|
844
|
+
include "raylib.h"
|
|
845
|
+
link "raylib"
|
|
846
|
+
compiler_flag "-DPLATFORM_DESKTOP"
|
|
847
|
+
|
|
848
|
+
struct Color:
|
|
849
|
+
r: ubyte
|
|
850
|
+
g: ubyte
|
|
851
|
+
b: ubyte
|
|
852
|
+
a: ubyte
|
|
853
|
+
|
|
854
|
+
external function InitWindow(width: int, height: int, title: cstr) -> void</code></pre>
|
|
855
|
+
</div>
|
|
856
|
+
|
|
857
|
+
<h3>Module Resolution</h3>
|
|
858
|
+
<p>Module lookup resolves <code class="code-inline">a.b.c</code> to <code class="code-inline">a/b/c.mt</code>. For an active target platform <code>P</code>, the compiler prefers <code>a/b/c.P.mt</code> and falls back to <code>a/b/c.mt</code>. Platform-specific files still map to module <code class="code-inline">name</code>. Valid platform suffixes: <code>linux</code>, <code>windows</code>, <code>wasm</code>. There is no <code>#if</code> or <code>#ifdef</code> — use <code>when</code>, <code>inline if</code>, or platform-specific file variants instead.</p>
|
|
859
|
+
<div class="code-wrap">
|
|
860
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
861
|
+
<pre><code>import std.str
|
|
862
|
+
import std.vec
|
|
863
|
+
import mylib.utils</code></pre>
|
|
864
|
+
</div>
|
|
865
|
+
</section>
|
|
866
|
+
|
|
867
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
868
|
+
<section id="lexical-rules">
|
|
869
|
+
<h2>Lexical Rules</h2>
|
|
870
|
+
|
|
871
|
+
<h3>Indentation</h3>
|
|
872
|
+
<ul style="margin-left:1.25rem;margin-bottom:1rem">
|
|
873
|
+
<li><code>:</code> starts a block</li>
|
|
874
|
+
<li>Indentation must be <strong>spaces only</strong> (tabs are rejected)</li>
|
|
875
|
+
<li>Indentation must be a multiple of <strong>4 spaces</strong></li>
|
|
876
|
+
<li>Indentation increases by <strong>one level at a time</strong></li>
|
|
877
|
+
<li>Newlines end statements except inside <code>()</code> and <code>[]</code>, or when the previous line ends with a binary operator</li>
|
|
878
|
+
</ul>
|
|
879
|
+
|
|
880
|
+
<div class="code-wrap">
|
|
881
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
882
|
+
<pre><code>## Preferred: wrap in delimiters.
|
|
883
|
+
let total = (
|
|
884
|
+
subtotal
|
|
885
|
+
+ tax
|
|
886
|
+
- discount
|
|
887
|
+
)
|
|
888
|
+
|
|
889
|
+
## Also accepted: operator-led continuation.
|
|
890
|
+
let total = subtotal +
|
|
891
|
+
tax -
|
|
892
|
+
discount</code></pre>
|
|
893
|
+
</div>
|
|
894
|
+
|
|
895
|
+
<h3>Naming Conventions</h3>
|
|
896
|
+
<div class="table-wrap">
|
|
897
|
+
<table class="attr-table">
|
|
898
|
+
<tr><th>Category</th><th>Style</th><th>Example</th></tr>
|
|
899
|
+
<tr><td>Modules, variables, functions</td><td><code>snake_case</code></td><td><code>player_count</code>, <code>load_level</code></td></tr>
|
|
900
|
+
<tr><td>Types (struct, enum, variant, etc.)</td><td><code>PascalCase</code></td><td><code>Vec2</code>, <code>LoadError</code>, <code>EventKind</code></td></tr>
|
|
901
|
+
<tr><td>C binding modules</td><td>Preserve C names exactly</td><td><code>SDL_Window</code>, <code>c.InitWindow</code></td></tr>
|
|
902
|
+
</table>
|
|
903
|
+
</div>
|
|
904
|
+
|
|
905
|
+
<h3>Comments</h3>
|
|
906
|
+
<div class="table-wrap">
|
|
907
|
+
<table class="attr-table">
|
|
908
|
+
<tr><th>Form</th><th>Description</th></tr>
|
|
909
|
+
<tr><td><code># text</code></td><td>Line comment</td></tr>
|
|
910
|
+
<tr><td><code>## text</code></td><td>Documentation comment; attaches to the next declaration (no blank line)</td></tr>
|
|
911
|
+
</table>
|
|
912
|
+
</div>
|
|
913
|
+
|
|
914
|
+
<h3>Literals</h3>
|
|
915
|
+
<div class="table-wrap">
|
|
916
|
+
<table class="attr-table">
|
|
917
|
+
<tr><th>Kind</th><th>Examples</th><th>Type</th></tr>
|
|
918
|
+
<tr><td>Integer</td><td><code>42</code>, <code>0xff</code>, <code>0b1010</code>, <code>1_000_000</code></td><td><code>int</code> (default)</td></tr>
|
|
919
|
+
<tr><td>Float</td><td><code>3.14</code>, <code>1.2e-3</code>, <code>1.0f</code>, <code>1.0d</code></td><td><code>float</code> (default)</td></tr>
|
|
920
|
+
<tr><td>Character</td><td><code>'a'</code>, <code>'\n'</code>, <code>'\t'</code>, <code>'\0'</code>, <code>'\x41'</code></td><td><code>ubyte</code></td></tr>
|
|
921
|
+
<tr><td>Boolean</td><td><code>true</code>, <code>false</code></td><td><code>bool</code></td></tr>
|
|
922
|
+
<tr><td>String</td><td><code>"hello"</code></td><td><code>str</code></td></tr>
|
|
923
|
+
<tr><td>C String</td><td><code>c"hello"</code></td><td><code>cstr</code></td></tr>
|
|
924
|
+
<tr><td>Format String</td><td><code>f"count=#{n}"</code></td><td><code>str</code></td></tr>
|
|
925
|
+
<tr><td>Heredoc String</td><td><code><<-TAG ... TAG</code></td><td><code>str</code></td></tr>
|
|
926
|
+
<tr><td>Heredoc C String</td><td><code>c<<-TAG ... TAG</code></td><td><code>cstr</code></td></tr>
|
|
927
|
+
<tr><td>Heredoc Format</td><td><code>f<<-TAG ... TAG</code></td><td><code>str</code></td></tr>
|
|
928
|
+
<tr><td>Null</td><td><code>null</code>, <code>null[ptr[char]]</code></td><td><code>T?</code></td></tr>
|
|
929
|
+
</table>
|
|
930
|
+
</div>
|
|
931
|
+
<p>Digits may be grouped with <code class="code-inline">_</code> separators: <code>1_000_000</code>, <code>0xFF_FF</code>, <code>0b1010_0101</code>. Integer literals accept type suffixes: <code>42u</code> (<code>uint</code>), <code>0xFFub</code> (<code>ubyte</code>), <code>100z</code> (<code>ptr_uint</code>), <code>7i</code> (<code>int</code>), <code>-1l</code> (<code>long</code>). Float literals accept <code>f</code> (<code>float</code>) and <code>d</code> (<code>double</code>) suffixes: <code>1.0f</code>, <code>1.0d</code>.</p>
|
|
932
|
+
|
|
933
|
+
<h3>Operators & Punctuation</h3>
|
|
934
|
+
<div class="table-wrap">
|
|
935
|
+
<table class="attr-table">
|
|
936
|
+
<tr><th>Category</th><th>Tokens</th></tr>
|
|
937
|
+
<tr><td>Delimiters</td><td><code>( ) [ ]</code></td></tr>
|
|
938
|
+
<tr><td>Separators / Access</td><td><code>: , .</code></td></tr>
|
|
939
|
+
<tr><td>Type markers</td><td><code>-></code> <code>?</code></td></tr>
|
|
940
|
+
<tr><td>Arithmetic</td><td><code>+ - * / %</code></td></tr>
|
|
941
|
+
<tr><td>Bitwise</td><td><code>~ & | ^ << >></code></td></tr>
|
|
942
|
+
<tr><td>Comparison</td><td><code>== != < <= > >=</code></td></tr>
|
|
943
|
+
<tr><td>Assignment</td><td><code>= += -= *= /= %= &= |= ^= <<= >>=</code></td></tr>
|
|
944
|
+
<tr><td>Variadic</td><td><code>...</code></td></tr>
|
|
945
|
+
<tr><td>Word operators</td><td><code>and</code> <code>or</code> <code>not</code></td></tr>
|
|
946
|
+
<tr><td>Pattern test</td><td><code>is</code> — variant arm membership test, desugars to <code>match</code></td></tr>
|
|
947
|
+
<tr><td>Parameter modes</td><td><code>in</code> <code>out</code> <code>inout</code> (reserved for <code>foreign function</code>)</td></tr>
|
|
948
|
+
</table>
|
|
949
|
+
</div>
|
|
950
|
+
</section>
|
|
951
|
+
|
|
952
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
953
|
+
<section id="variables-guards">
|
|
954
|
+
<h2>Variables & Guards</h2>
|
|
955
|
+
|
|
956
|
+
<h3>Local Declarations</h3>
|
|
957
|
+
<div class="code-wrap">
|
|
958
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
959
|
+
<pre><code>let width: int = 1280 ## immutable
|
|
960
|
+
var score: int = 0 ## mutable
|
|
961
|
+
let dt = rl.get_frame_time() ## type inferred (float)
|
|
962
|
+
var player_count = 0 ## type inferred (int)
|
|
963
|
+
|
|
964
|
+
## Zero-initialized (explicit type required, must be zero-initializable):
|
|
965
|
+
var scratch: array[ubyte, 256]</code></pre>
|
|
966
|
+
</div>
|
|
967
|
+
|
|
968
|
+
<h3>Constants</h3>
|
|
969
|
+
<div class="code-wrap">
|
|
970
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
971
|
+
<pre><code>const GRAVITY: float = 9.81
|
|
972
|
+
|
|
973
|
+
## Block-bodied const (evaluated at compile time)
|
|
974
|
+
const NEXT_POW2 -> int:
|
|
975
|
+
var n: int = 1
|
|
976
|
+
while n < 1024:
|
|
977
|
+
n = n * 2
|
|
978
|
+
return n</code></pre>
|
|
979
|
+
</div>
|
|
980
|
+
|
|
981
|
+
<h3>Destructuring</h3>
|
|
982
|
+
<div class="code-wrap">
|
|
983
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
984
|
+
<pre><code>## Tuple destructuring
|
|
985
|
+
let (a, b) = pair()
|
|
986
|
+
let (x, y) = (1, 2)
|
|
987
|
+
|
|
988
|
+
## Struct destructuring
|
|
989
|
+
struct Vec2:
|
|
990
|
+
x: float
|
|
991
|
+
y: float
|
|
992
|
+
let p = Vec2(x = 1.0, y = 2.0)
|
|
993
|
+
let Vec2(x, y) = p</code></pre>
|
|
994
|
+
</div>
|
|
995
|
+
|
|
996
|
+
<h3>Guard Binding</h3>
|
|
997
|
+
<p>The initializer must be <code>T?</code>, <code>Option[T]</code>, or <code>Result[T, E]</code>. The <code>else</code> block must terminate control flow.</p>
|
|
998
|
+
<div class="code-wrap">
|
|
999
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1000
|
+
<pre><code>let window = maybe_window else:
|
|
1001
|
+
return 1
|
|
1002
|
+
|
|
1003
|
+
let image = load_image(path) else:
|
|
1004
|
+
return 1
|
|
1005
|
+
|
|
1006
|
+
let config = load_config() else as error:
|
|
1007
|
+
return error
|
|
1008
|
+
|
|
1009
|
+
let _ = initialize_runtime() else:
|
|
1010
|
+
return 1</code></pre>
|
|
1011
|
+
</div>
|
|
1012
|
+
|
|
1013
|
+
<h3>Postfix Result/Option Propagation (<code>?</code>)</h3>
|
|
1014
|
+
<p><code>expr?</code> unwraps <code>Option[T]</code> or <code>Result[T, E]</code> on success; on failure (None or failure(error)), returns early from the enclosing function or proc. Only allowed inside function and proc bodies.</p>
|
|
1015
|
+
<div class="code-wrap">
|
|
1016
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1017
|
+
<pre><code>let parsed = parse(input)?
|
|
1018
|
+
let lowered = lower(parsed)?
|
|
1019
|
+
return Result[Output, Error].success(value = lowered)</code></pre>
|
|
1020
|
+
</div>
|
|
1021
|
+
</section>
|
|
1022
|
+
|
|
1023
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
1024
|
+
<section id="data-declarations">
|
|
1025
|
+
<h2>Data Declarations</h2>
|
|
1026
|
+
|
|
1027
|
+
<h3>Type Alias</h3>
|
|
1028
|
+
<div class="code-wrap">
|
|
1029
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1030
|
+
<pre><code>type Seconds = float
|
|
1031
|
+
type Callback = fn(level: int, message: cstr) -> void</code></pre>
|
|
1032
|
+
</div>
|
|
1033
|
+
|
|
1034
|
+
<h3>Struct</h3>
|
|
1035
|
+
<div class="code-wrap">
|
|
1036
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1037
|
+
<pre><code>struct Vec2:
|
|
1038
|
+
x: float
|
|
1039
|
+
y: float
|
|
1040
|
+
|
|
1041
|
+
## With attributes
|
|
1042
|
+
@[packed]
|
|
1043
|
+
struct Header:
|
|
1044
|
+
tag: ubyte
|
|
1045
|
+
|
|
1046
|
+
@[align(16)]
|
|
1047
|
+
struct Mat4:
|
|
1048
|
+
data: array[float, 16]
|
|
1049
|
+
|
|
1050
|
+
## Nested structs (qualified as Parent.Nested)
|
|
1051
|
+
struct Rectangle:
|
|
1052
|
+
x: float
|
|
1053
|
+
y: float
|
|
1054
|
+
|
|
1055
|
+
struct Edge:
|
|
1056
|
+
start: float
|
|
1057
|
+
end: float
|
|
1058
|
+
|
|
1059
|
+
top_edge: Edge
|
|
1060
|
+
left_edge: Edge</code></pre>
|
|
1061
|
+
</div>
|
|
1062
|
+
|
|
1063
|
+
<h3>Union</h3>
|
|
1064
|
+
<div class="code-wrap">
|
|
1065
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1066
|
+
<pre><code>union Number:
|
|
1067
|
+
i: int
|
|
1068
|
+
f: float</code></pre>
|
|
1069
|
+
</div>
|
|
1070
|
+
|
|
1071
|
+
<h3>Enum</h3>
|
|
1072
|
+
<p>Backing type defaults to <code>int</code>; values auto-increment from <code>0</code> or the previous explicit value. Both are optional.</p>
|
|
1073
|
+
<div class="code-wrap">
|
|
1074
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1075
|
+
<pre><code>## Explicit type and values (classic form)
|
|
1076
|
+
enum State: ubyte
|
|
1077
|
+
idle = 0
|
|
1078
|
+
running = 1
|
|
1079
|
+
|
|
1080
|
+
## Backing type and values both optional
|
|
1081
|
+
enum Color:
|
|
1082
|
+
red ## 0
|
|
1083
|
+
green ## 1
|
|
1084
|
+
blue ## 2
|
|
1085
|
+
|
|
1086
|
+
## Auto-increment from explicit value
|
|
1087
|
+
enum Status:
|
|
1088
|
+
idle = 10 ## 10
|
|
1089
|
+
running ## 11
|
|
1090
|
+
stopped ## 12</code></pre>
|
|
1091
|
+
</div>
|
|
1092
|
+
|
|
1093
|
+
<h3>Flags</h3>
|
|
1094
|
+
<div class="code-wrap">
|
|
1095
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1096
|
+
<pre><code>flags Mask: uint
|
|
1097
|
+
a = 1 << 0
|
|
1098
|
+
b = 1 << 1
|
|
1099
|
+
both = Mask.a | Mask.b ## composite alias</code></pre>
|
|
1100
|
+
</div>
|
|
1101
|
+
|
|
1102
|
+
<h3>Variant (Tagged Union)</h3>
|
|
1103
|
+
<div class="code-wrap">
|
|
1104
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1105
|
+
<pre><code>variant Token:
|
|
1106
|
+
ident(text: str)
|
|
1107
|
+
number(value: int)
|
|
1108
|
+
eof
|
|
1109
|
+
|
|
1110
|
+
## Constructors
|
|
1111
|
+
let t1 = Token.ident(text = "hello") ## payload arm
|
|
1112
|
+
let t2 = Token.eof ## no-payload arm
|
|
1113
|
+
|
|
1114
|
+
## Generic variants
|
|
1115
|
+
variant Option[T]:
|
|
1116
|
+
some(value: T)
|
|
1117
|
+
none
|
|
1118
|
+
|
|
1119
|
+
variant Result[T, E]:
|
|
1120
|
+
success(value: T)
|
|
1121
|
+
failure(error: E)</code></pre>
|
|
1122
|
+
</div>
|
|
1123
|
+
|
|
1124
|
+
<div class="callout tip">
|
|
1125
|
+
<strong>Prelude types.</strong> <code>Option[T]</code> and <code>Result[T, E]</code> are auto-imported — no <code>import</code> statement is needed. They are available in every Milk Tea source file. Their extending methods (<code>is_some</code>, <code>unwrap</code>, <code>is_success</code>, <code>map_error</code>, etc.) are always accessible.
|
|
1126
|
+
</div>
|
|
1127
|
+
|
|
1128
|
+
<h3>Opaque</h3>
|
|
1129
|
+
<p>For C handles whose layout is unknown. Opaque types may implement interfaces, enabling constrained generics and <code>dyn</code> dispatch over C handles.</p>
|
|
1130
|
+
<div class="code-wrap">
|
|
1131
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1132
|
+
<pre><code>opaque SDL_Window implements Closable
|
|
1133
|
+
opaque ma_engine</code></pre>
|
|
1134
|
+
</div>
|
|
1135
|
+
|
|
1136
|
+
<h3>Attributes</h3>
|
|
1137
|
+
<p>User-defined attributes target specific declaration kinds: <code>attribute[field]</code>, <code>attribute[callable]</code>, <code>attribute[const]</code>, <code>attribute[event]</code>, <code>attribute[enum]</code>, <code>attribute[flags]</code>, <code>attribute[union]</code>, <code>attribute[variant]</code>, <code>attribute[struct]</code>. Multiple targets may be combined: <code>attribute[const, event]</code>. Built-in attributes: <code>packed</code>, <code>align(bytes)</code>, <code>deprecated(message)</code>.</p>
|
|
1138
|
+
<div class="code-wrap">
|
|
1139
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1140
|
+
<pre><code>attribute[field] rename(name: str)
|
|
1141
|
+
attribute[struct, event] trace(name: str)
|
|
1142
|
+
|
|
1143
|
+
@[trace(name = "player")]
|
|
1144
|
+
struct Player:
|
|
1145
|
+
@[rename(name = "hp")]
|
|
1146
|
+
health: int</code></pre>
|
|
1147
|
+
</div>
|
|
1148
|
+
|
|
1149
|
+
<h3>static_assert</h3>
|
|
1150
|
+
<p><code class="code-inline">static_assert(condition, message)</code> checks a compile-time boolean condition. If false, compilation fails with the given message. Valid at module level and inside function bodies, <code>inline for</code> loops, and <code>const function</code> bodies.</p>
|
|
1151
|
+
<div class="code-wrap">
|
|
1152
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1153
|
+
<pre><code>static_assert(size_of(int) == 4, "expected 32-bit int")
|
|
1154
|
+
static_assert(MAX_ENTITIES > 0, "need at least one entity")</code></pre>
|
|
1155
|
+
</div>
|
|
1156
|
+
</section>
|
|
1157
|
+
|
|
1158
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
1159
|
+
<section id="interfaces-methods">
|
|
1160
|
+
<h2>Interfaces & Methods</h2>
|
|
1161
|
+
|
|
1162
|
+
<h3>Interfaces</h3>
|
|
1163
|
+
<p>Explicit nominal method-set contracts for static polymorphism. No inheritance, no hidden dispatch.</p>
|
|
1164
|
+
<div class="code-wrap">
|
|
1165
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1166
|
+
<pre><code>public interface Damageable:
|
|
1167
|
+
editable function take_damage(amount: int) -> void
|
|
1168
|
+
function is_alive() -> bool
|
|
1169
|
+
|
|
1170
|
+
## Generic interface
|
|
1171
|
+
interface Mapper[T]:
|
|
1172
|
+
function map(x: T) -> T
|
|
1173
|
+
|
|
1174
|
+
## Conformance on nominal type
|
|
1175
|
+
struct NPC implements Damageable:
|
|
1176
|
+
hp: int
|
|
1177
|
+
name: str</code></pre>
|
|
1178
|
+
</div>
|
|
1179
|
+
|
|
1180
|
+
<h3>Methods (extending)</h3>
|
|
1181
|
+
<p>Three kinds: <code>function</code> (value receiver), <code>editable function</code> (editable receiver), <code>static function</code> (no receiver).</p>
|
|
1182
|
+
<div class="code-wrap">
|
|
1183
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1184
|
+
<pre><code>extending Counter:
|
|
1185
|
+
function read() -> int:
|
|
1186
|
+
return this.value
|
|
1187
|
+
|
|
1188
|
+
editable function bump() -> void:
|
|
1189
|
+
this.value += 1
|
|
1190
|
+
|
|
1191
|
+
static function zero() -> Counter:
|
|
1192
|
+
return Counter(value = 0)</code></pre>
|
|
1193
|
+
</div>
|
|
1194
|
+
|
|
1195
|
+
<h3>Dynamic Dispatch (<code>dyn[I]</code>)</h3>
|
|
1196
|
+
<p>Runtime interface values are fat pointers (data + vtable). Construct with <code>adapt[I](value: ref[T])</code>. For generic interfaces, the type parameter must be fully specified: <code>dyn[Mapper[int]]</code> is valid; <code>dyn[Mapper]</code> is rejected.</p>
|
|
1197
|
+
<div class="code-wrap">
|
|
1198
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1199
|
+
<pre><code>function render_all(drawables: span[dyn[Drawable]]):
|
|
1200
|
+
for d in drawables:
|
|
1201
|
+
d.draw()
|
|
1202
|
+
|
|
1203
|
+
let d: dyn[Drawable] = adapt[Drawable](ref_of(entity))</code></pre>
|
|
1204
|
+
</div>
|
|
1205
|
+
</section>
|
|
1206
|
+
|
|
1207
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
1208
|
+
<section id="type-system">
|
|
1209
|
+
<h2>Type System</h2>
|
|
1210
|
+
|
|
1211
|
+
<h3>Primitive Types</h3>
|
|
1212
|
+
<div class="table-wrap">
|
|
1213
|
+
<table class="attr-table">
|
|
1214
|
+
<tr><th>Type</th><th>Description</th></tr>
|
|
1215
|
+
<tr><td><code>bool</code></td><td>Boolean</td></tr>
|
|
1216
|
+
<tr><td><code>byte</code> <code>short</code> <code>int</code> <code>long</code></td><td>Signed integers</td></tr>
|
|
1217
|
+
<tr><td><code>ubyte</code> <code>ushort</code> <code>uint</code> <code>ulong</code></td><td>Unsigned integers</td></tr>
|
|
1218
|
+
<tr><td><code>char</code></td><td>Single-byte character (ABI-facing). Not a general arithmetic integer type — cast to an integer explicitly for arithmetic.</td></tr>
|
|
1219
|
+
<tr><td><code>ptr_int</code> <code>ptr_uint</code></td><td>Pointer-sized integers</td></tr>
|
|
1220
|
+
<tr><td><code>float</code> <code>double</code></td><td>Floating-point</td></tr>
|
|
1221
|
+
<tr><td><code>void</code></td><td>No value</td></tr>
|
|
1222
|
+
<tr><td><code>str</code></td><td>UTF-8 string view (borrowed)</td></tr>
|
|
1223
|
+
<tr><td><code>cstr</code></td><td>NUL-terminated C string</td></tr>
|
|
1224
|
+
<tr><td><code>vec2</code> <code>vec3</code> <code>vec4</code></td><td>Float vectors with <code>.x .y .z .w</code></td></tr>
|
|
1225
|
+
<tr><td><code>ivec2</code> <code>ivec3</code> <code>ivec4</code></td><td>Integer vectors</td></tr>
|
|
1226
|
+
<tr><td><code>mat3</code> <code>mat4</code></td><td>Column-major matrices</td></tr>
|
|
1227
|
+
<tr><td><code>quat</code></td><td>Quaternion (layout-compatible with <code>vec4</code>)</td></tr>
|
|
1228
|
+
</table>
|
|
1229
|
+
</div>
|
|
1230
|
+
<p>Primitive type names (<code>int</code>, <code>float</code>, <code>str</code>, <code>bool</code>, etc.) are reserved and cannot be reused for variable bindings, parameters, locals, import aliases, or type parameters.</p>
|
|
1231
|
+
|
|
1232
|
+
<h3>Native Type Construction</h3>
|
|
1233
|
+
<div class="code-wrap">
|
|
1234
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1235
|
+
<pre><code>let v = vec3(x = 1.0, y = 2.0, z = 3.0)
|
|
1236
|
+
let m = mat4(
|
|
1237
|
+
col0 = vec4(x = 1.0, y = 0.0, z = 0.0, w = 0.0),
|
|
1238
|
+
col1 = vec4(x = 0.0, y = 1.0, z = 0.0, w = 0.0),
|
|
1239
|
+
col2 = vec4(x = 0.0, y = 0.0, z = 1.0, w = 0.0),
|
|
1240
|
+
col3 = vec4(x = 0.0, y = 0.0, z = 0.0, w = 1.0),
|
|
1241
|
+
)
|
|
1242
|
+
let q = quat(x = 0.0, y = 0.0, z = 0.0, w = 1.0)</code></pre>
|
|
1243
|
+
</div>
|
|
1244
|
+
|
|
1245
|
+
<h3>Type Constructors</h3>
|
|
1246
|
+
<div class="table-wrap">
|
|
1247
|
+
<table class="attr-table">
|
|
1248
|
+
<tr><th>Constructor</th><th>Description</th></tr>
|
|
1249
|
+
<tr><td><code>ptr[T]</code></td><td>Raw writable pointer</td></tr>
|
|
1250
|
+
<tr><td><code>const_ptr[T]</code></td><td>Raw read-only pointer</td></tr>
|
|
1251
|
+
<tr><td><code>own[T]</code></td><td>Owning heap pointer — auto-dereferences like <code>ref</code> but storable, returnable, and nullable. Created via <code>heap.must_alloc[T](count)</code>. Compiles to <code>T*</code>.</td></tr>
|
|
1252
|
+
<tr><td><code>ref[T]</code></td><td>Safe non-null writable alias</td></tr>
|
|
1253
|
+
<tr><td><code>span[T]</code></td><td>Pointer + length view (<code>{ data, len }</code>)</td></tr>
|
|
1254
|
+
<tr><td><code>array[T, N]</code></td><td>Fixed-size array</td></tr>
|
|
1255
|
+
<tr><td><code>str_buffer[N]</code></td><td>Fixed-capacity mutable UTF-8 text</td></tr>
|
|
1256
|
+
<tr><td><code>Task[T]</code></td><td>Async task handle</td></tr>
|
|
1257
|
+
<tr><td><code>Option[T]</code></td><td>Optional value variant</td></tr>
|
|
1258
|
+
<tr><td><code>Result[T, E]</code></td><td>Success or failure variant</td></tr>
|
|
1259
|
+
<tr><td><code>fn(params...) -> R</code></td><td>Function pointer type</td></tr>
|
|
1260
|
+
<tr><td><code>proc(params...) -> R</code></td><td>Closure type (value captures)</td></tr>
|
|
1261
|
+
<tr><td><code>SoA[T, N]</code></td><td>Structure-of-Arrays: fields become separate arrays</td></tr>
|
|
1262
|
+
<tr><td><code>dyn[Interface]</code></td><td>Runtime interface value (fat pointer)</td></tr>
|
|
1263
|
+
<tr><td><code>atomic[T]</code></td><td>Atomic value for lock-free concurrent access</td></tr>
|
|
1264
|
+
<tr><td><code>(T, U)</code></td><td>Tuple type</td></tr>
|
|
1265
|
+
</table>
|
|
1266
|
+
</div>
|
|
1267
|
+
|
|
1268
|
+
<h3>Nullability</h3>
|
|
1269
|
+
<p><code>T?</code> is valid for <strong>any type</strong>. For pointer-like bases (<code>ptr[T]</code>, <code>const_ptr[T]</code>, <code>own[T]</code>, <code>cstr</code>, <code>fn(...)</code>, <code>proc(...)</code>, opaque), <code>T?</code> is a nullable pointer and <code>null</code> reuses the null pointer as the absent value. For non-pointer value bases (<code>int</code>, <code>bool</code>, <code>float</code>, structs), <code>T?</code> 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. Use <code>null</code> to express absence; use <code>zero[T]</code> for generic value-initialization. <code>zero[ptr[T]]</code> is rejected when the expected type is already a nullable pointer-like type — write <code>null</code> instead.</p>
|
|
1270
|
+
<p>At an FFI boundary, only pointer-like <code>T?</code> is allowed: <code>external</code> / <code>foreign function</code> parameters and returns reject a non-pointer value nullable such as <code>int?</code> — use <code>ptr[T]?</code> or pass an explicit struct.</p>
|
|
1271
|
+
<div class="code-wrap">
|
|
1272
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1273
|
+
<pre><code>let window: ptr[Window]? = null
|
|
1274
|
+
let name: cstr? = null
|
|
1275
|
+
let port: int? = null ## value-type optional, stored inline
|
|
1276
|
+
|
|
1277
|
+
## Typed null for ambiguous contexts (target must be pointer-like)
|
|
1278
|
+
let handle = null[ptr[char]]</code></pre>
|
|
1279
|
+
</div>
|
|
1280
|
+
|
|
1281
|
+
<h3>Memory Model</h3>
|
|
1282
|
+
<p>Milk Tea follows <strong>value semantics by default</strong>. Scalars, structs, and fixed arrays copy by value. There is no hidden heap boxing, no garbage collector, and no hidden reference counting. Heap allocation is always explicit and allocator-driven:</p>
|
|
1283
|
+
<div class="table-wrap">
|
|
1284
|
+
<table class="attr-table">
|
|
1285
|
+
<tr><th>Module</th><th>Use Case</th></tr>
|
|
1286
|
+
<tr><td><code>std.mem.heap</code></td><td>General-purpose allocation</td></tr>
|
|
1287
|
+
<tr><td><code>std.mem.arena</code></td><td>Frame, level, and scratch lifetimes (bulk free)</td></tr>
|
|
1288
|
+
<tr><td><code>std.mem.pool</code></td><td>Fixed-size object pools</td></tr>
|
|
1289
|
+
<tr><td><code>std.mem.stack</code></td><td>Explicit temporary allocators</td></tr>
|
|
1290
|
+
<tr><td><code>std.mem.tracking</code></td><td>Debug allocator: leak, bad-free, and double-free detection</td></tr>
|
|
1291
|
+
<tr><td><code>std.mem.endian</code></td><td>Byte-swap and network byte-order helpers (<code>hton_ushort</code>, <code>ntoh_uint</code>, etc.)</td></tr>
|
|
1292
|
+
</table>
|
|
1293
|
+
</div>
|
|
1294
|
+
|
|
1295
|
+
<div class="callout tip">
|
|
1296
|
+
<strong>ref[T] convenience:</strong> Passing a mutable addressable <code>T</code> to a <code>ref[T]</code> parameter borrows it implicitly — you don't need <code>ref_of()</code> at every call site. Member access and method calls auto-dereference <code>ref[T]</code> receivers, so <code>handle.field</code> and <code>handle.method()</code> work without <code>read(handle)</code>.
|
|
1297
|
+
</div>
|
|
1298
|
+
</section>
|
|
1299
|
+
|
|
1300
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
1301
|
+
<section id="generics">
|
|
1302
|
+
<h2>Generics</h2>
|
|
1303
|
+
<p>Generic structs, variants, functions, methods, interfaces, and foreign functions are supported. Constraints use <code>implements</code>.</p>
|
|
1304
|
+
|
|
1305
|
+
<div class="code-wrap">
|
|
1306
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1307
|
+
<pre><code>## Generic struct
|
|
1308
|
+
struct Slice[T]:
|
|
1309
|
+
data: ptr[T]
|
|
1310
|
+
len: ptr_uint
|
|
1311
|
+
|
|
1312
|
+
## Generic function with constraint
|
|
1313
|
+
function damage_one[T implements Damageable](target: ref[T], amount: int) -> void:
|
|
1314
|
+
if target.is_alive():
|
|
1315
|
+
target.take_damage(amount)
|
|
1316
|
+
|
|
1317
|
+
## Multiple constraints
|
|
1318
|
+
function update_and_draw[T implements Updatable and Drawable](value: ref[T]):
|
|
1319
|
+
value.update()
|
|
1320
|
+
value.draw()
|
|
1321
|
+
|
|
1322
|
+
## Value parameter
|
|
1323
|
+
function int_with_bits[N: int]() -> type:
|
|
1324
|
+
if N == 64:
|
|
1325
|
+
return long
|
|
1326
|
+
else:
|
|
1327
|
+
return int
|
|
1328
|
+
|
|
1329
|
+
## Specialization
|
|
1330
|
+
let big = int_with_bits[64]()
|
|
1331
|
+
let s64 = Slice[ubyte](data = ptr, len = 32)</code></pre>
|
|
1332
|
+
</div>
|
|
1333
|
+
</section>
|
|
1334
|
+
|
|
1335
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
1336
|
+
<section id="functions">
|
|
1337
|
+
<h2>Functions</h2>
|
|
1338
|
+
<p>There is no overloading. One function name maps to exactly one callable symbol. Parameters must be typed; the return type defaults to <code>void</code> if omitted.</p>
|
|
1339
|
+
|
|
1340
|
+
<h3>Ordinary Functions</h3>
|
|
1341
|
+
<div class="code-wrap">
|
|
1342
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1343
|
+
<pre><code>function add(a: int, b: int) -> int:
|
|
1344
|
+
return a + b
|
|
1345
|
+
|
|
1346
|
+
## Return type defaults to void
|
|
1347
|
+
function say_hello(name: str):
|
|
1348
|
+
io.print_line(f"Hello, #{name}!")</code></pre>
|
|
1349
|
+
</div>
|
|
1350
|
+
|
|
1351
|
+
<h3>const function</h3>
|
|
1352
|
+
<p>Evaluable at compile time. Generates both a compile-time constant-folding path and a normal runtime function. Recursive calls between <code>const</code> functions are supported. Use <code>const function</code> for reusable compile-time logic; use a block-bodied <code>const X -> T: ...</code> for one-shot computed constants.</p>
|
|
1353
|
+
<div class="code-wrap">
|
|
1354
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1355
|
+
<pre><code>const function square(x: int) -> int:
|
|
1356
|
+
return x * x
|
|
1357
|
+
|
|
1358
|
+
const RESULT: int = square(5) ## folded to 25 at compile time</code></pre>
|
|
1359
|
+
</div>
|
|
1360
|
+
|
|
1361
|
+
<h3>external function</h3>
|
|
1362
|
+
<p>No body. Raw C ABI declarations. Cannot be generic or async.</p>
|
|
1363
|
+
<div class="code-wrap">
|
|
1364
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1365
|
+
<pre><code>external function printf(format: cstr, ...) -> int</code></pre>
|
|
1366
|
+
</div>
|
|
1367
|
+
|
|
1368
|
+
<h3>foreign function</h3>
|
|
1369
|
+
<p>Projects a C function into Milk Tea types with boundary marshalling. <code>in</code>, <code>out</code>, and <code>inout</code> are declared on the parameter; callers pass ordinary expressions or lvalues. The <code>= c.Symbol(...)</code> mapping supports a fan-out form when a surface parameter must be split into multiple raw C arguments.</p>
|
|
1370
|
+
<div class="code-wrap">
|
|
1371
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1372
|
+
<pre><code>foreign function init_window(width: int, height: int, title: str as cstr) -> void = c.InitWindow
|
|
1373
|
+
foreign function load_file_data(file_name: str as cstr, out data_size: int) -> ptr[ubyte]? = c.LoadFileData
|
|
1374
|
+
foreign function close_window(consuming window: Window) -> void = c.CloseWindow</code></pre>
|
|
1375
|
+
</div>
|
|
1376
|
+
<p><strong>consuming parameters:</strong> After the call, the passed variable is bound to <code>null</code> (the foreign function takes ownership). Consuming foreign calls must appear as top-level expression statements, and the foreign function must return <code>void</code>.</p>
|
|
1377
|
+
|
|
1378
|
+
<h3>Procs (Closures)</h3>
|
|
1379
|
+
<div class="code-wrap">
|
|
1380
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1381
|
+
<pre><code>let multiplier = 2
|
|
1382
|
+
let doubler = proc(x: int) -> int:
|
|
1383
|
+
return x * multiplier
|
|
1384
|
+
|
|
1385
|
+
## Expression-bodied
|
|
1386
|
+
let tripler = proc(x: int) -> int: x * 3</code></pre>
|
|
1387
|
+
</div>
|
|
1388
|
+
</section>
|
|
1389
|
+
|
|
1390
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
1391
|
+
<section id="control-flow">
|
|
1392
|
+
<h2>Statements</h2>
|
|
1393
|
+
|
|
1394
|
+
<h3>if / else if / else</h3>
|
|
1395
|
+
<p>Condition must be <code>bool</code>. No truthy/falsy coercion.</p>
|
|
1396
|
+
<div class="code-wrap">
|
|
1397
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1398
|
+
<pre><code>if ready:
|
|
1399
|
+
start_game()
|
|
1400
|
+
else if wants_menu:
|
|
1401
|
+
open_menu()
|
|
1402
|
+
else:
|
|
1403
|
+
show_intro()
|
|
1404
|
+
|
|
1405
|
+
## Inline form: single statement on same line
|
|
1406
|
+
if ready: return 1 else: return 0
|
|
1407
|
+
if x > 10: return "big" else if x > 5: return "med" else: return "small"</code></pre>
|
|
1408
|
+
</div>
|
|
1409
|
+
|
|
1410
|
+
<h3>while</h3>
|
|
1411
|
+
<div class="code-wrap">
|
|
1412
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1413
|
+
<pre><code>while running:
|
|
1414
|
+
tick()</code></pre>
|
|
1415
|
+
</div>
|
|
1416
|
+
|
|
1417
|
+
<h3>for (Single)</h3>
|
|
1418
|
+
<p>Supports ranges, arrays, spans, and custom iterables.</p>
|
|
1419
|
+
<div class="code-wrap">
|
|
1420
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1421
|
+
<pre><code>for i in 0..count:
|
|
1422
|
+
update_enemy(i)
|
|
1423
|
+
|
|
1424
|
+
for item in items:
|
|
1425
|
+
process(item)</code></pre>
|
|
1426
|
+
</div>
|
|
1427
|
+
|
|
1428
|
+
<h3>for (Parallel)</h3>
|
|
1429
|
+
<p>Arrays and spans only. Lengths must match.</p>
|
|
1430
|
+
<div class="code-wrap">
|
|
1431
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1432
|
+
<pre><code>for entity, position, velocity in entities, positions, velocities:
|
|
1433
|
+
update(entity, position, velocity)</code></pre>
|
|
1434
|
+
</div>
|
|
1435
|
+
|
|
1436
|
+
<h3>pass</h3>
|
|
1437
|
+
<p><code class="code-inline">pass</code> is an explicit no-op statement for intentionally empty block bodies. It produces no code in the output.</p>
|
|
1438
|
+
<div class="code-wrap">
|
|
1439
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1440
|
+
<pre><code>if debug_mode:
|
|
1441
|
+
pass
|
|
1442
|
+
else:
|
|
1443
|
+
do_work()</code></pre>
|
|
1444
|
+
</div>
|
|
1445
|
+
|
|
1446
|
+
<h3>Range Index Assignment</h3>
|
|
1447
|
+
<p>Assign a tuple to a contiguous slice using an exclusive range with literal bounds. The tuple width must match the slice width exactly.</p>
|
|
1448
|
+
<div class="code-wrap">
|
|
1449
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1450
|
+
<pre><code>var buf: array[float, 4]
|
|
1451
|
+
buf[0..3] = (1.0, 2.0, 3.0) # assigns buf[0], buf[1], buf[2]</code></pre>
|
|
1452
|
+
</div>
|
|
1453
|
+
|
|
1454
|
+
<h3>Custom Iterables</h3>
|
|
1455
|
+
<p>Types can participate in <code>for</code> loops by exposing a non-editable zero-argument <code>iter()</code> method. The returned iterator must expose either:</p>
|
|
1456
|
+
<ul style="margin-left:1.25rem;margin-bottom:1rem">
|
|
1457
|
+
<li><code>next() -> ptr[T]?</code> — returns a nullable pointer to each element; iteration stops on <code>null</code>, or</li>
|
|
1458
|
+
<li><code>next() -> bool</code> together with <code>current() -> T</code> — <code>next()</code> advances and returns whether another element exists; <code>current()</code> returns the current element.</li>
|
|
1459
|
+
</ul>
|
|
1460
|
+
</section>
|
|
1461
|
+
|
|
1462
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
1463
|
+
<section id="match">
|
|
1464
|
+
<h2>Match Patterns</h2>
|
|
1465
|
+
|
|
1466
|
+
<h3>Statement Form</h3>
|
|
1467
|
+
<div class="code-wrap">
|
|
1468
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1469
|
+
<pre><code>## Enum match (must be exhaustive unless _ present)
|
|
1470
|
+
match kind:
|
|
1471
|
+
EventKind.quit:
|
|
1472
|
+
return 0
|
|
1473
|
+
_:
|
|
1474
|
+
return 1
|
|
1475
|
+
|
|
1476
|
+
## Integer match (_ required)
|
|
1477
|
+
match key_code:
|
|
1478
|
+
65:
|
|
1479
|
+
fire()
|
|
1480
|
+
27:
|
|
1481
|
+
quit()
|
|
1482
|
+
_:
|
|
1483
|
+
return
|
|
1484
|
+
|
|
1485
|
+
## Variant match with payload binding
|
|
1486
|
+
match token:
|
|
1487
|
+
Token.ident(text):
|
|
1488
|
+
use_name(text)
|
|
1489
|
+
Token.number as n:
|
|
1490
|
+
use_value(n.value)
|
|
1491
|
+
Token.eof:
|
|
1492
|
+
return</code></pre>
|
|
1493
|
+
</div>
|
|
1494
|
+
|
|
1495
|
+
<h3>Expression Form</h3>
|
|
1496
|
+
<div class="code-wrap">
|
|
1497
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1498
|
+
<pre><code>let label = match code:
|
|
1499
|
+
1: "one"
|
|
1500
|
+
2: "two"
|
|
1501
|
+
_: "other"
|
|
1502
|
+
|
|
1503
|
+
## str match (_ required)
|
|
1504
|
+
let cmd_label = match command:
|
|
1505
|
+
"lex": "lexer"
|
|
1506
|
+
"parse": "parser"
|
|
1507
|
+
_: "unknown"</code></pre>
|
|
1508
|
+
</div>
|
|
1509
|
+
|
|
1510
|
+
<h3>Variant Membership Test (<code>is</code>)</h3>
|
|
1511
|
+
<p><code>expr is Variant.Arm</code> returns <code>bool</code>. Desugars to a <code>match</code> expression at parse time — no new semantics. Use for boolean membership checks; use <code>match</code> for payload destructuring.</p>
|
|
1512
|
+
<div class="code-wrap">
|
|
1513
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1514
|
+
<pre><code>## No-payload arm
|
|
1515
|
+
if kind is TokenKind.eof:
|
|
1516
|
+
break
|
|
1517
|
+
|
|
1518
|
+
## Payload arm (any payload matches)
|
|
1519
|
+
if kind is TokenKind.identifier:
|
|
1520
|
+
process_ident()
|
|
1521
|
+
|
|
1522
|
+
## With logical operators
|
|
1523
|
+
if kind is TokenKind.eof or kind is TokenKind.indent:
|
|
1524
|
+
break
|
|
1525
|
+
|
|
1526
|
+
## In a let binding
|
|
1527
|
+
let ok = kind is TokenKind.op_plus</code></pre>
|
|
1528
|
+
</div>
|
|
1529
|
+
|
|
1530
|
+
<p>Variants also support <code>==</code> and <code>!=</code> for direct value comparison (discriminant + payload fields). The compiler generates a per-type comparison helper function in the emitted C.</p>
|
|
1531
|
+
<div class="code-wrap">
|
|
1532
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1533
|
+
<pre><code>let a = TokenKind.eof
|
|
1534
|
+
let b = TokenKind.eof
|
|
1535
|
+
if a == b:
|
|
1536
|
+
io.print_line("same arm")
|
|
1537
|
+
|
|
1538
|
+
let id1 = TokenKind.ident(name = "hello")
|
|
1539
|
+
let id2 = TokenKind.ident(name = "hello")
|
|
1540
|
+
if id1 == id2:
|
|
1541
|
+
io.print_line("same payload")
|
|
1542
|
+
|
|
1543
|
+
if id1 != TokenKind.eof:
|
|
1544
|
+
io.print_line("different arm")</code></pre>
|
|
1545
|
+
</div>
|
|
1546
|
+
|
|
1547
|
+
<h3>Struct Patterns (Variants)</h3>
|
|
1548
|
+
<p>Inline destructuring with guards, bindings, and field discards inside variant arm patterns.</p>
|
|
1549
|
+
<div class="code-wrap">
|
|
1550
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1551
|
+
<pre><code>match entity:
|
|
1552
|
+
Entity.player(hp > 0, position):
|
|
1553
|
+
render(position)
|
|
1554
|
+
Entity.player:
|
|
1555
|
+
remove()
|
|
1556
|
+
Entity.enemy:
|
|
1557
|
+
return
|
|
1558
|
+
|
|
1559
|
+
## _ discards a field without binding it
|
|
1560
|
+
match record:
|
|
1561
|
+
Record.fields(_, _, _, label):
|
|
1562
|
+
use_label(label)</code></pre>
|
|
1563
|
+
</div>
|
|
1564
|
+
</section>
|
|
1565
|
+
|
|
1566
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
1567
|
+
<section id="defer-unsafe">
|
|
1568
|
+
<h2>Defer & Unsafe</h2>
|
|
1569
|
+
|
|
1570
|
+
<h3>defer</h3>
|
|
1571
|
+
<p>Registers cleanup code at scope exit. Lowers to cleanup labels in C.</p>
|
|
1572
|
+
<div class="code-wrap">
|
|
1573
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1574
|
+
<pre><code>function main() -> int:
|
|
1575
|
+
rl.init_window(800, 600, "Game")
|
|
1576
|
+
defer rl.close_window()
|
|
1577
|
+
|
|
1578
|
+
while not rl.window_should_close():
|
|
1579
|
+
rl.begin_drawing()
|
|
1580
|
+
defer rl.end_drawing()
|
|
1581
|
+
render_frame()
|
|
1582
|
+
|
|
1583
|
+
return 0</code></pre>
|
|
1584
|
+
</div>
|
|
1585
|
+
|
|
1586
|
+
<h3>unsafe</h3>
|
|
1587
|
+
<p>Required for: pointer indexing, raw dereference, pointer arithmetic, pointer casts, <code>reinterpret[...]</code>. Calling a <code>foreign function</code> that declares <code>str as cstr</code> or <code>out</code> parameters is ordinary safe code — the foreign boundary handles marshalling. <code>unsafe</code> is for raw <code>std.c.*</code> bindings, pointer reinterpretation, and manual memory walking.</p>
|
|
1588
|
+
<div class="code-wrap">
|
|
1589
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1590
|
+
<pre><code>## Expression form
|
|
1591
|
+
let p = unsafe: pixels + offset
|
|
1592
|
+
|
|
1593
|
+
## Block form
|
|
1594
|
+
unsafe:
|
|
1595
|
+
let pixel = read(ptr[uint]<-p)
|
|
1596
|
+
pointer[0] = 1
|
|
1597
|
+
pointer[1] = 2</code></pre>
|
|
1598
|
+
</div>
|
|
1599
|
+
</section>
|
|
1600
|
+
|
|
1601
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
1602
|
+
<section id="compile-time">
|
|
1603
|
+
<h2>Compile-Time Control Flow</h2>
|
|
1604
|
+
|
|
1605
|
+
<h3>when</h3>
|
|
1606
|
+
<p>Evaluates discriminant at compile time; only the chosen branch is type-checked and emitted.</p>
|
|
1607
|
+
<div class="code-wrap">
|
|
1608
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1609
|
+
<pre><code>when TARGET_OS:
|
|
1610
|
+
TargetOs.linux:
|
|
1611
|
+
return open_linux(path)
|
|
1612
|
+
TargetOs.windows:
|
|
1613
|
+
return open_windows(path)</code></pre>
|
|
1614
|
+
</div>
|
|
1615
|
+
|
|
1616
|
+
<h3>inline for</h3>
|
|
1617
|
+
<p>Unrolls a loop over a compile-time-known array.</p>
|
|
1618
|
+
<div class="code-wrap">
|
|
1619
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1620
|
+
<pre><code>inline for field in fields_of(Particle):
|
|
1621
|
+
static_assert(field.type == float, "Particle fields must be float")</code></pre>
|
|
1622
|
+
</div>
|
|
1623
|
+
|
|
1624
|
+
<h3>inline if</h3>
|
|
1625
|
+
<p>Only the chosen branch is emitted; dead branch may reference nonexistent symbols. Supports <code>else</code> and <code>else if</code> branches with the same dead-branch elimination. A compile-time <strong>type comparison</strong> is a valid condition — <code>T == int</code> (a bare type parameter) or <code>field.type == float</code> (a reflected field type) — which enables type-based dispatch in generic bodies.</p>
|
|
1626
|
+
<div class="code-wrap">
|
|
1627
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1628
|
+
<pre><code>const DEBUG_RENDER: bool = false
|
|
1629
|
+
|
|
1630
|
+
function draw() -> void:
|
|
1631
|
+
inline if DEBUG_RENDER:
|
|
1632
|
+
debug_overlay()
|
|
1633
|
+
else:
|
|
1634
|
+
normal_draw()</code></pre>
|
|
1635
|
+
</div>
|
|
1636
|
+
|
|
1637
|
+
<h3>inline match</h3>
|
|
1638
|
+
<p>Not required to be exhaustive; only the chosen arm emits code.</p>
|
|
1639
|
+
<div class="code-wrap">
|
|
1640
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1641
|
+
<pre><code>inline match TARGET_BACKEND:
|
|
1642
|
+
Backend.gl:
|
|
1643
|
+
gl_draw(item)
|
|
1644
|
+
Backend.vulkan:
|
|
1645
|
+
vk_draw(item)</code></pre>
|
|
1646
|
+
</div>
|
|
1647
|
+
|
|
1648
|
+
<h3>inline while</h3>
|
|
1649
|
+
<p>The condition must be a compile-time constant. The loop unrolls to a fixed number of iterations, <strong>capped at 10,000</strong>. A non-terminating <code>inline while</code> (condition that never becomes false) is a compile error.</p>
|
|
1650
|
+
<div class="code-wrap">
|
|
1651
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1652
|
+
<pre><code>inline while n < 1024:
|
|
1653
|
+
n = n * 2</code></pre>
|
|
1654
|
+
</div>
|
|
1655
|
+
|
|
1656
|
+
<h3>emit (Compile-Time Code Generation)</h3>
|
|
1657
|
+
<p><code class="code-inline">emit</code> generates declarations at compile time. It is only valid inside <code>const function</code> or <code>inline</code> bodies. Currently supports emitting <code>function</code>, <code>struct</code>, and <code>const</code> declarations. Not to be confused with <code>event.emit()</code> which fires runtime events.</p>
|
|
1658
|
+
<div class="code-wrap">
|
|
1659
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1660
|
+
<pre><code>const function generate_helpers() -> void:
|
|
1661
|
+
emit function meaning_of_life() -> int:
|
|
1662
|
+
return 42
|
|
1663
|
+
emit function greet() -> str:
|
|
1664
|
+
return "hello"</code></pre>
|
|
1665
|
+
</div>
|
|
1666
|
+
</section>
|
|
1667
|
+
|
|
1668
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
1669
|
+
<section id="expressions">
|
|
1670
|
+
<h2>Expressions & Operators</h2>
|
|
1671
|
+
|
|
1672
|
+
<h3>Primary Expressions</h3>
|
|
1673
|
+
<div class="code-wrap">
|
|
1674
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1675
|
+
<pre><code>## Identifiers, literals, parenthesized expressions
|
|
1676
|
+
let x = (a + b) * c
|
|
1677
|
+
|
|
1678
|
+
## Tuple literals
|
|
1679
|
+
let pair = (42, "hello") ## positional
|
|
1680
|
+
let point = (x = 10, y = 20) ## named
|
|
1681
|
+
|
|
1682
|
+
## Type queries (compile-time)
|
|
1683
|
+
let s = size_of(int)
|
|
1684
|
+
let a = align_of(Mat4)
|
|
1685
|
+
let o = offset_of(Vec2, y)
|
|
1686
|
+
|
|
1687
|
+
## If-expressions
|
|
1688
|
+
let max = if a > b: a else: b
|
|
1689
|
+
|
|
1690
|
+
## Match-expressions
|
|
1691
|
+
let label = match code:
|
|
1692
|
+
1: "one"
|
|
1693
|
+
2: "two"
|
|
1694
|
+
_: "other"
|
|
1695
|
+
|
|
1696
|
+
## Proc expressions
|
|
1697
|
+
let add_one = proc(x: int) -> int: x + 1</code></pre>
|
|
1698
|
+
</div>
|
|
1699
|
+
|
|
1700
|
+
<h3>Postfix Expressions</h3>
|
|
1701
|
+
<div class="code-wrap">
|
|
1702
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1703
|
+
<pre><code>## Member access, indexing, calls
|
|
1704
|
+
value.field
|
|
1705
|
+
arr[i]
|
|
1706
|
+
func(arg)
|
|
1707
|
+
|
|
1708
|
+
## Partial field update (returns copy)
|
|
1709
|
+
let moved = v.with(x = 10.0)
|
|
1710
|
+
|
|
1711
|
+
## Specialization (bare or module-qualified names only)
|
|
1712
|
+
let spec = name[T]
|
|
1713
|
+
let sized = name[32]</code></pre>
|
|
1714
|
+
</div>
|
|
1715
|
+
|
|
1716
|
+
<h3>Operator Precedence (low to high)</h3>
|
|
1717
|
+
<div class="table-wrap">
|
|
1718
|
+
<table class="attr-table">
|
|
1719
|
+
<tr><th>Precedence</th><th>Operators</th></tr>
|
|
1720
|
+
<tr><td>1 (lowest)</td><td><code>or</code></td></tr>
|
|
1721
|
+
<tr><td>2</td><td><code>and</code></td></tr>
|
|
1722
|
+
<tr><td>3</td><td><code>|</code></td></tr>
|
|
1723
|
+
<tr><td>4</td><td><code>^</code></td></tr>
|
|
1724
|
+
<tr><td>5</td><td><code>&</code></td></tr>
|
|
1725
|
+
<tr><td>6</td><td><code>==</code> <code>!=</code></td></tr>
|
|
1726
|
+
<tr><td>7</td><td><code><</code> <code><=</code> <code>></code> <code>>=</code></td></tr>
|
|
1727
|
+
<tr><td>8</td><td><code><<</code> <code>>></code></td></tr>
|
|
1728
|
+
<tr><td>9</td><td><code>+</code> <code>-</code></td></tr>
|
|
1729
|
+
<tr><td>10 (highest)</td><td><code>*</code> <code>/</code> <code>%</code></td></tr>
|
|
1730
|
+
</table>
|
|
1731
|
+
</div>
|
|
1732
|
+
|
|
1733
|
+
<h3>Native Type Operators</h3>
|
|
1734
|
+
<div class="table-wrap">
|
|
1735
|
+
<table class="attr-table">
|
|
1736
|
+
<tr><th>Type</th><th>Supported Operators</th></tr>
|
|
1737
|
+
<tr><td><code>vecN</code> / <code>ivecN</code></td><td><code>+ - *</code> (component-wise same-type); <code>* /</code> (scalar); unary <code>-</code></td></tr>
|
|
1738
|
+
<tr><td><code>matN</code></td><td><code>+ -</code> (same-type); <code>* /</code> (scalar); unary <code>-</code></td></tr>
|
|
1739
|
+
<tr><td><code>quat</code></td><td><code>+ - *</code> (component-wise same-type); unary <code>-</code></td></tr>
|
|
1740
|
+
</table>
|
|
1741
|
+
</div>
|
|
1742
|
+
</section>
|
|
1743
|
+
|
|
1744
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
1745
|
+
<section id="builtins">
|
|
1746
|
+
<h2>Built-in Callables</h2>
|
|
1747
|
+
<p>Special functions recognized by the compiler that lower to specific backend operations.</p>
|
|
1748
|
+
|
|
1749
|
+
<div class="table-wrap">
|
|
1750
|
+
<table class="attr-table">
|
|
1751
|
+
<tr><th>Callable</th><th>Description</th></tr>
|
|
1752
|
+
<tr><td><code>fatal(message)</code></td><td>Terminate with message</td></tr>
|
|
1753
|
+
<tr><td><code>ref_of(x)</code></td><td>Writable safe reference to an addressable lvalue</td></tr>
|
|
1754
|
+
<tr><td><code>const_ptr_of(x)</code></td><td>Read-only raw pointer to an addressable lvalue</td></tr>
|
|
1755
|
+
<tr><td><code>ptr_of(x)</code></td><td>Writable raw pointer from a mutable addressable lvalue</td></tr>
|
|
1756
|
+
<tr><td><code>read(r)</code></td><td>Read through a <code>ref[T]</code> or <code>ptr[T]</code> (requires <code>unsafe</code> for raw)</td></tr>
|
|
1757
|
+
<tr><td><code>T<-value</code></td><td>Explicit numeric cast</td></tr>
|
|
1758
|
+
<tr><td><code>reinterpret[T](value)</code></td><td>Bit reinterpretation (requires <code>unsafe</code>)</td></tr>
|
|
1759
|
+
<tr><td><code>zero[T]</code></td><td>Zero-initialized value</td></tr>
|
|
1760
|
+
<tr><td><code>default[T]</code></td><td>Semantic default via <code>T.default()</code></td></tr>
|
|
1761
|
+
<tr><td><code>hash[T](value)</code></td><td>Canonical hash (lowers to <code>T.hash(...)</code>)</td></tr>
|
|
1762
|
+
<tr><td><code>equal[T](left, right)</code></td><td>Canonical equality (lowers to <code>T.equal(...)</code>)</td></tr>
|
|
1763
|
+
<tr><td><code>order[T](left, right)</code></td><td>Canonical ordering: returns negative when <code>left < right</code>, <code>0</code> when equal, positive when <code>left > right</code></td></tr>
|
|
1764
|
+
<tr><td><code>array[T, N](...)</code></td><td>Array literal constructor</td></tr>
|
|
1765
|
+
<tr><td><code>span[T](data, len)</code></td><td>Span construction</td></tr>
|
|
1766
|
+
<tr><td><code>get(coll, index)</code></td><td>Recoverable bounds-checked access, returns <code>ptr[T]?</code></td></tr>
|
|
1767
|
+
<tr><td><code>adapt[I](value)</code></td><td>Construct <code>dyn[I]</code> runtime interface value</td></tr>
|
|
1768
|
+
<tr><td><code>size_of(T)</code></td><td>Compile-time size in bytes</td></tr>
|
|
1769
|
+
<tr><td><code>align_of(T)</code></td><td>Compile-time alignment</td></tr>
|
|
1770
|
+
<tr><td><code>offset_of(T, field)</code></td><td>Compile-time field offset</td></tr>
|
|
1771
|
+
</table>
|
|
1772
|
+
</div>
|
|
1773
|
+
</section>
|
|
1774
|
+
|
|
1775
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
1776
|
+
<section id="reflection">
|
|
1777
|
+
<h2>Compile-Time Reflection</h2>
|
|
1778
|
+
<p>Reflection builtins return handle values representing type structure. <code>field_handle</code> exposes <code>.name</code> (<code>str</code>) and <code>.type</code> (the field's type). <code>member_handle</code> exposes <code>.name</code> (<code>str</code>) and, for enum members with explicit values, <code>.value</code> (an integer). <code>attribute_handle</code> provides access to attribute arguments via <code>attribute_arg[T]</code>.</p>
|
|
1779
|
+
|
|
1780
|
+
<div class="table-wrap">
|
|
1781
|
+
<table class="attr-table">
|
|
1782
|
+
<tr><th>Built-in</th><th>Returns</th><th>Description</th></tr>
|
|
1783
|
+
<tr><td><code>fields_of(T)</code></td><td><code>array[field_handle, N]</code></td><td>All fields of struct <code>T</code> in declaration order</td></tr>
|
|
1784
|
+
<tr><td><code>members_of(E)</code></td><td><code>array[member_handle, N]</code></td><td>All members of enum or variant <code>E</code></td></tr>
|
|
1785
|
+
<tr><td><code>attributes_of(T)</code></td><td><code>array[attribute_handle, N]</code></td><td>All attributes on <code>T</code></td></tr>
|
|
1786
|
+
<tr><td><code>attributes_of(T, name)</code></td><td><code>array[attribute_handle, N]</code></td><td>Attributes of <code>T</code> matching <code>name</code></td></tr>
|
|
1787
|
+
<tr><td><code>field_of(T, name)</code></td><td><code>field_handle</code></td><td>Named field of <code>T</code></td></tr>
|
|
1788
|
+
<tr><td><code>callable_of(T, name)</code></td><td><code>callable_handle</code></td><td>Named callable of <code>T</code></td></tr>
|
|
1789
|
+
<tr><td><code>attribute_of(T, name)</code></td><td><code>attribute_handle</code></td><td>Named attribute on <code>T</code></td></tr>
|
|
1790
|
+
<tr><td><code>has_attribute(T, name)</code></td><td><code>bool</code></td><td>Whether <code>T</code> has the named attribute</td></tr>
|
|
1791
|
+
<tr><td><code>attribute_arg[T](handle)</code></td><td><code>T</code></td><td>Typed argument of an attribute handle</td></tr>
|
|
1792
|
+
</table>
|
|
1793
|
+
</div>
|
|
1794
|
+
|
|
1795
|
+
<div class="code-wrap">
|
|
1796
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1797
|
+
<pre><code>inline for field in fields_of(Point):
|
|
1798
|
+
let sz = size_of(field.type)
|
|
1799
|
+
let off = offset_of(Point, field)
|
|
1800
|
+
static_assert(sz > 0, "field has non-zero size")</code></pre>
|
|
1801
|
+
</div>
|
|
1802
|
+
|
|
1803
|
+
<p>A <code>field_handle</code>'s <code>.type</code> is also usable directly <strong>in type position</strong> inside a compile-time context — <code>const_ptr[field.type]</code>, <code>equal[field.type](...)</code>, <code>Vec[field.type]</code> — resolving to the field's concrete type. Because the <code>inline for</code> body is type-checked once per element, per-field dispatch (including recursion into nested-struct field types) is validated at check time. This powers reflective generics such as <code>std.hash</code>'s <code>equal_struct</code> / <code>hash_struct</code> / <code>order_struct</code>, and <code>std.fmt.format_value[T]</code>, which renders any struct as <code>{ field = value, ... }</code>.</p>
|
|
1804
|
+
|
|
1805
|
+
<div class="code-wrap">
|
|
1806
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1807
|
+
<pre><code>## Per-field equality via field.type used in type position (content-correct)
|
|
1808
|
+
function field_equal[T](a: const_ptr[T], b: const_ptr[T]) -> bool:
|
|
1809
|
+
inline for field in fields_of(T):
|
|
1810
|
+
let off = offset_of(T, field)
|
|
1811
|
+
unsafe:
|
|
1812
|
+
let pa = const_ptr[field.type]<-(ptr[ubyte]<-a + off)
|
|
1813
|
+
let pb = const_ptr[field.type]<-(ptr[ubyte]<-b + off)
|
|
1814
|
+
if not equal[field.type](pa, pb):
|
|
1815
|
+
return false
|
|
1816
|
+
return true</code></pre>
|
|
1817
|
+
</div>
|
|
1818
|
+
</section>
|
|
1819
|
+
|
|
1820
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
1821
|
+
<section id="strings">
|
|
1822
|
+
<h2>Strings & Text</h2>
|
|
1823
|
+
|
|
1824
|
+
<h3>Text Types</h3>
|
|
1825
|
+
<div class="table-wrap">
|
|
1826
|
+
<table class="attr-table">
|
|
1827
|
+
<tr><th>Type</th><th>Ownership</th><th>Use Case</th></tr>
|
|
1828
|
+
<tr><td><code>str</code></td><td>Borrowed</td><td>Read-only UTF-8 view (literals, format strings, slicing)</td></tr>
|
|
1829
|
+
<tr><td><code>cstr</code></td><td>Borrowed</td><td>NUL-terminated C ABI string (<code>c"hello"</code>)</td></tr>
|
|
1830
|
+
<tr><td><code>str_buffer[N]</code></td><td>Owned (stack)</td><td>Fixed-capacity mutable UTF-8 builder</td></tr>
|
|
1831
|
+
<tr><td><code>std.string.String</code></td><td>Owned (heap)</td><td>Growable owned text via <code>fmt.format(f"...")</code></td></tr>
|
|
1832
|
+
</table>
|
|
1833
|
+
</div>
|
|
1834
|
+
|
|
1835
|
+
<h3>Format Strings</h3>
|
|
1836
|
+
<div class="code-wrap">
|
|
1837
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1838
|
+
<pre><code>let text = f"count=#{count} ok=#{ready}"
|
|
1839
|
+
|
|
1840
|
+
## Precision for floats
|
|
1841
|
+
let info = f"value=#{pi:.4}"
|
|
1842
|
+
|
|
1843
|
+
## Hex format for ints
|
|
1844
|
+
let hex = f"address=#{ptr_value:x}"
|
|
1845
|
+
|
|
1846
|
+
## Owned text (escape stack lifetime)
|
|
1847
|
+
import std.fmt
|
|
1848
|
+
let owned = fmt.format(f"count=#{count}") ## -> string.String</code></pre>
|
|
1849
|
+
</div>
|
|
1850
|
+
<p>Interpolated expressions must be <code>str</code>, <code>cstr</code>, <code>bool</code>, a numeric primitive, an integer-backed enum or flags type, or a type implementing <code>format_len()</code> and <code>append_format()</code> (custom formatting hooks). The compiler lowers <code>fmt.format(f"...")</code>, <code>str_buffer.append_format(f"...")</code>, and <code>string.String.append_format(f"...")</code> directly to the formatted output without an intermediate allocation.</p>
|
|
1851
|
+
|
|
1852
|
+
<h3>Format Specifiers</h3>
|
|
1853
|
+
<div class="table-wrap">
|
|
1854
|
+
<table class="attr-table">
|
|
1855
|
+
<tr><th>Specifier</th><th>Example</th><th>Applies To</th></tr>
|
|
1856
|
+
<tr><td><code>:.N</code></td><td><code>f"pi=#{3.14159:.2}"</code> → <code>"pi=3.14"</code></td><td><code>float</code>, <code>double</code></td></tr>
|
|
1857
|
+
<tr><td><code>:x</code> / <code>:X</code></td><td><code>f"addr=#{255:x}"</code> → <code>"addr=ff"</code></td><td>Integers, enums, flags</td></tr>
|
|
1858
|
+
<tr><td><code>:o</code> / <code>:O</code></td><td><code>f"perm=#{8:o}"</code> → <code>"perm=10"</code></td><td>Integers</td></tr>
|
|
1859
|
+
<tr><td><code>:b</code> / <code>:B</code></td><td><code>f"mask=#{5:b}"</code> → <code>"mask=101"</code></td><td>Integers</td></tr>
|
|
1860
|
+
</table>
|
|
1861
|
+
</div>
|
|
1862
|
+
|
|
1863
|
+
<h3>Custom Formatting Hooks</h3>
|
|
1864
|
+
<p>Types can participate in format strings by implementing two associated functions:</p>
|
|
1865
|
+
<div class="code-wrap">
|
|
1866
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1867
|
+
<pre><code>extending MyType:
|
|
1868
|
+
function format_len() -> ptr_uint:
|
|
1869
|
+
return ... # byte length of formatted output
|
|
1870
|
+
function append_format(output: ref[string.String]) -> void:
|
|
1871
|
+
... # write formatted bytes into output</code></pre>
|
|
1872
|
+
</div>
|
|
1873
|
+
<p>The compiler calls <code>append_format</code> directly when the sink is a <code>string.String</code> (via <code>fmt.format</code>). For <code>f"..."</code> literals and <code>str_buffer</code> sinks, the formatter must write exactly <code>format_len()</code> bytes — fewer or more raises a runtime error.</p>
|
|
1874
|
+
|
|
1875
|
+
<h3>Heredocs</h3>
|
|
1876
|
+
<p>Nonblank content lines are dedented by their shared leading spaces. The trailing newline before the terminator tag is preserved.</p>
|
|
1877
|
+
<div class="code-wrap">
|
|
1878
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1879
|
+
<pre><code>## str heredoc
|
|
1880
|
+
const help: str = <<-HELP
|
|
1881
|
+
Usage: tool [options]
|
|
1882
|
+
Options:
|
|
1883
|
+
--help Show this message
|
|
1884
|
+
HELP
|
|
1885
|
+
|
|
1886
|
+
## cstr heredoc
|
|
1887
|
+
const shader: cstr = c<<-GLSL
|
|
1888
|
+
#version 330
|
|
1889
|
+
void main() {
|
|
1890
|
+
gl_FragColor = vec4(1.0);
|
|
1891
|
+
}
|
|
1892
|
+
GLSL
|
|
1893
|
+
|
|
1894
|
+
## Format heredoc
|
|
1895
|
+
let msg = f<<-MSG
|
|
1896
|
+
Hello, #{name}!
|
|
1897
|
+
Score: #{score}
|
|
1898
|
+
MSG</code></pre>
|
|
1899
|
+
</div>
|
|
1900
|
+
|
|
1901
|
+
<h3>str_buffer[M] Methods</h3>
|
|
1902
|
+
<div class="table-wrap">
|
|
1903
|
+
<table class="attr-table">
|
|
1904
|
+
<tr><th>Method</th><th>Description</th></tr>
|
|
1905
|
+
<tr><td><code>.clear()</code></td><td>Clear contents</td></tr>
|
|
1906
|
+
<tr><td><code>.assign(str)</code></td><td>Replace contents (traps if exceeds capacity)</td></tr>
|
|
1907
|
+
<tr><td><code>.append(str)</code></td><td>Extend contents (traps if exceeds capacity)</td></tr>
|
|
1908
|
+
<tr><td><code>.assign_format(str)</code></td><td>Replace via format string</td></tr>
|
|
1909
|
+
<tr><td><code>.append_format(str)</code></td><td>Extend via format string</td></tr>
|
|
1910
|
+
<tr><td><code>.len()</code></td><td>Current text length</td></tr>
|
|
1911
|
+
<tr><td><code>.capacity()</code></td><td>Max writable bytes (excludes trailing NUL)</td></tr>
|
|
1912
|
+
<tr><td><code>.as_str()</code></td><td>Borrow as <code>str</code></td></tr>
|
|
1913
|
+
<tr><td><code>.as_cstr()</code></td><td>Borrow as <code>cstr</code></td></tr>
|
|
1914
|
+
</table>
|
|
1915
|
+
</div>
|
|
1916
|
+
|
|
1917
|
+
<h3>Adjacent String Literals</h3>
|
|
1918
|
+
<p>Ordinary <code>"..."</code> and <code>c"..."</code> literals may continue across following indented lines when each continued line starts with the same literal kind. The segments concatenate exactly with no implicit separator:</p>
|
|
1919
|
+
<div class="code-wrap">
|
|
1920
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1921
|
+
<pre><code>let text = "hello "
|
|
1922
|
+
"from multiple "
|
|
1923
|
+
"indented lines" # produces "hello from multiple indented lines"</code></pre>
|
|
1924
|
+
</div>
|
|
1925
|
+
</section>
|
|
1926
|
+
|
|
1927
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
1928
|
+
<section id="safety">
|
|
1929
|
+
<h2>Safety Rules</h2>
|
|
1930
|
+
|
|
1931
|
+
<div class="table-wrap">
|
|
1932
|
+
<table class="attr-table">
|
|
1933
|
+
<tr><th>Rule</th><th>Description</th></tr>
|
|
1934
|
+
<tr><td>Conditions must be <code>bool</code></td><td>No truthy/falsy coercion from integers or pointers</td></tr>
|
|
1935
|
+
<tr><td>Explicit casts for mixed signed/unsigned</td><td>Mixed signed/unsigned arithmetic requires <code>T<-value</code></td></tr>
|
|
1936
|
+
<tr><td>Enum/flags don't auto-coerce</td><td>Outside external-call boundaries, no implicit conversion to backing integers</td></tr>
|
|
1937
|
+
<tr><td>Safe indexing is bounds-checked</td><td><code>arr[i]</code> calls <code>fatal</code> on OOB; use <code>get(arr, i)</code> for recoverable access</td></tr>
|
|
1938
|
+
<tr><td>Pointer indexing requires <code>unsafe</code></td><td>Raw pointer dereference, arithmetic, casts, <code>reinterpret</code> all need <code>unsafe</code></td></tr>
|
|
1939
|
+
<tr><td><code>%</code> requires integer operands</td><td></td></tr>
|
|
1940
|
+
<tr><td>Bitwise ops require matching types</td><td>Integer or flags types</td></tr>
|
|
1941
|
+
<tr><td>Shift ops require integer operands</td><td></td></tr>
|
|
1942
|
+
<tr><td>No implicit struct equality</td><td>Structs cannot be compared with <code>==</code>/<code>!=</code>. Use <code>equal[T](a, b)</code>. Variants <em>do</em> support <code>==</code> and <code>!=</code> with generated per-type comparison.</td></tr>
|
|
1943
|
+
<tr><td>No <code>str</code>/<code>cstr</code> concatenation</td><td>Use format strings or <code>std.str</code> helpers</td></tr>
|
|
1944
|
+
<tr><td>Compile-time constant fit</td><td>Exact compile-time numeric constants (literals, <code>const</code> values) fit an explicit numeric target without a manual cast when representable exactly</td></tr>
|
|
1945
|
+
<tr><td>Integer-to-float at typed boundaries</td><td>A primitive integer expression may flow into an expected float type for explicit typed locals, assignments, returns, function arguments, or field initializers. Integer arithmetic stays integer arithmetic until that final boundary cast.</td></tr>
|
|
1946
|
+
</table>
|
|
1947
|
+
</div>
|
|
1948
|
+
</section>
|
|
1949
|
+
|
|
1950
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
1951
|
+
<section id="async">
|
|
1952
|
+
<h2>Async</h2>
|
|
1953
|
+
<p><code>async function</code> lifts its return type to <code>Task[T]</code>. <code>await</code> is only allowed inside async functions.</p>
|
|
1954
|
+
|
|
1955
|
+
<div class="code-wrap">
|
|
1956
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1957
|
+
<pre><code>async function child() -> int:
|
|
1958
|
+
return 41
|
|
1959
|
+
|
|
1960
|
+
async function parent() -> int:
|
|
1961
|
+
let v = await child()
|
|
1962
|
+
return v + 1
|
|
1963
|
+
|
|
1964
|
+
## async main is compiler-bootstrapped
|
|
1965
|
+
async function main() -> int:
|
|
1966
|
+
let result = await do_work()
|
|
1967
|
+
return result</code></pre>
|
|
1968
|
+
</div>
|
|
1969
|
+
|
|
1970
|
+
<h3>Task Helpers</h3>
|
|
1971
|
+
<p>Import <code>std.async</code> for runtime control:</p>
|
|
1972
|
+
<div class="code-wrap">
|
|
1973
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1974
|
+
<pre><code>import std.async as aio
|
|
1975
|
+
|
|
1976
|
+
let task = some_async_work()
|
|
1977
|
+
aio.wait(task)
|
|
1978
|
+
let r = aio.result(task)</code></pre>
|
|
1979
|
+
</div>
|
|
1980
|
+
|
|
1981
|
+
<h3>Limitations</h3>
|
|
1982
|
+
<p><code>await</code> is supported inside <code>if</code> expressions and bodies, <code>while</code> bodies and conditions, <code>for</code> bodies and iterables, <code>match</code> discriminants and arms, <code>let ... else:</code> initializers and else bodies, <code>unsafe</code> blocks, short-circuit <code>and</code>/<code>or</code> expressions, and assignment targets. <code>defer</code> is supported in async functions, including cleanup bodies that <code>await</code>. <code>async main</code> pre-lift return type must be <code>int</code> or <code>void</code>. <code>?</code> propagation inside async functions completes the task early on failure.</p>
|
|
1983
|
+
</section>
|
|
1984
|
+
|
|
1985
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
1986
|
+
<section id="concurrency">
|
|
1987
|
+
<h2>Concurrency</h2>
|
|
1988
|
+
<p>Milk Tea has first-class compiler support for multithreading using real OS threads (libuv). All concurrency is structured: spawned work completes before the parent scope continues. No hidden thread pool, no GC interaction, no fire-and-forget.</p>
|
|
1989
|
+
|
|
1990
|
+
<h3>Parallel For (data-parallel)</h3>
|
|
1991
|
+
<p>Dispatches loop iterations across CPU cores. Each iteration writes to its own index range — the bread and butter of data-oriented game programming.</p>
|
|
1992
|
+
<div class="code-wrap">
|
|
1993
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
1994
|
+
<pre><code>parallel for i in 0..entity_count:
|
|
1995
|
+
positions[i] += velocities[i] * dt</code></pre>
|
|
1996
|
+
</div>
|
|
1997
|
+
<p>Rules: range iteration only (<code>0..N</code>). No <code>break</code>, <code>continue</code>, <code>return</code>, <code>defer</code>, or nested <code>parallel for</code> in the body. <code>ref[T]</code> captures rejected.</p>
|
|
1998
|
+
|
|
1999
|
+
<h3>Parallel Blocks (structured fork-join)</h3>
|
|
2000
|
+
<p>Run heterogeneous work concurrently. Each statement runs concurrently on OS threads (one on the calling thread, the rest on worker threads). The parent blocks until all complete.</p>
|
|
2001
|
+
<div class="code-wrap">
|
|
2002
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
2003
|
+
<pre><code>parallel:
|
|
2004
|
+
load_textures(path)
|
|
2005
|
+
load_sounds(path)</code></pre>
|
|
2006
|
+
</div>
|
|
2007
|
+
<p>Rules: at least two statements. Single-writer-or-multiple-readers enforced: if one statement writes a variable, no other may access it.</p>
|
|
2008
|
+
|
|
2009
|
+
<h3>Atomic Types</h3>
|
|
2010
|
+
<p><code class="code-inline">atomic[T]</code> provides lock-free concurrent access to integer values. <code>T</code> must be a primitive integer or <code>bool</code>.</p>
|
|
2011
|
+
<div class="code-wrap">
|
|
2012
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
2013
|
+
<pre><code>var counter: atomic[int]
|
|
2014
|
+
counter.store(0)
|
|
2015
|
+
let prev = counter.add(1) # returns previous value
|
|
2016
|
+
let value = counter.load()
|
|
2017
|
+
counter.sub(1)
|
|
2018
|
+
let old = counter.exchange(42)</code></pre>
|
|
2019
|
+
</div>
|
|
2020
|
+
|
|
2021
|
+
<div class="table-wrap">
|
|
2022
|
+
<table class="attr-table">
|
|
2023
|
+
<tr><th>Method</th><th>Signature</th><th>Description</th></tr>
|
|
2024
|
+
<tr><td><code>load()</code></td><td><code>-> T</code></td><td>Atomic read</td></tr>
|
|
2025
|
+
<tr><td><code>store(value)</code></td><td><code>-> void</code></td><td>Atomic write (editable receiver)</td></tr>
|
|
2026
|
+
<tr><td><code>add(value)</code></td><td><code>-> T</code></td><td>Fetch-and-add, returns previous (editable)</td></tr>
|
|
2027
|
+
<tr><td><code>sub(value)</code></td><td><code>-> T</code></td><td>Fetch-and-sub, returns previous (editable)</td></tr>
|
|
2028
|
+
<tr><td><code>exchange(value)</code></td><td><code>-> T</code></td><td>Atomic swap, returns previous (editable)</td></tr>
|
|
2029
|
+
</table>
|
|
2030
|
+
</div>
|
|
2031
|
+
<p>All operations use sequential consistency. Lowers to C11 <code class="code-inline">_Atomic T</code> with <code class="code-inline">__atomic_*</code> builtins.</p>
|
|
2032
|
+
|
|
2033
|
+
<h3>Detach & Collect</h3>
|
|
2034
|
+
<p><code class="code-inline">detach</code> spawns work on a separate thread and returns a <code>Handle</code>. <code>gather</code> blocks until all handles complete.</p>
|
|
2035
|
+
<div class="code-wrap">
|
|
2036
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
2037
|
+
<pre><code>let a = detach load_textures(path)
|
|
2038
|
+
let b = detach load_sounds(path)
|
|
2039
|
+
process_other_stuff()
|
|
2040
|
+
gather a, b</code></pre>
|
|
2041
|
+
</div>
|
|
2042
|
+
<p>Rules: <code>detach</code> returns a <code>Handle</code> — must be bound with <code>let</code> or <code>var</code>. Currently supports global function calls with no captured local variables. <code>gather</code> joins handles in order. <code>ref[T]</code> captures rejected.</p>
|
|
2043
|
+
|
|
2044
|
+
<h3>Compile-Time Safety</h3>
|
|
2045
|
+
<ul style="margin-left:1.25rem;margin-bottom:1rem">
|
|
2046
|
+
<li><code>ref[T]</code> captures rejected at all thread boundaries</li>
|
|
2047
|
+
<li>Write conflicts detected between <code>parallel:</code> statements (single-writer-or-multiple-readers)</li>
|
|
2048
|
+
<li><code>atomic[T]</code> is always sendable across thread boundaries</li>
|
|
2049
|
+
</ul>
|
|
2050
|
+
|
|
2051
|
+
<div class="callout tip">
|
|
2052
|
+
<strong>Tip:</strong> For library-level threading, see <code>std.thread</code> (OS threads), <code>std.sync</code> (Mutex, Condition, Semaphore, AtomicUint), and <code>std.jobs</code> (thread pool). The built-in <code>parallel for</code>, <code>parallel:</code>, and <code>atomic[T]</code> are compiler-integrated and provide compile-time safety checks that library threading cannot.
|
|
2053
|
+
</div>
|
|
2054
|
+
</section>
|
|
2055
|
+
|
|
2056
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
2057
|
+
<section id="events">
|
|
2058
|
+
<h2>Events</h2>
|
|
2059
|
+
<p>Built-in typed publisher/subscriber with fixed-capacity listener storage. Zero heap allocation during dispatch — emit snapshots active listeners into a stack-allocated array, guaranteeing predictable latency critical for real-time, audio, and embedded contexts.</p>
|
|
2060
|
+
|
|
2061
|
+
<h3>Declaration</h3>
|
|
2062
|
+
<div class="code-wrap">
|
|
2063
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
2064
|
+
<pre><code>public event closed[4]
|
|
2065
|
+
public event resized[8](ResizeEvent)
|
|
2066
|
+
|
|
2067
|
+
## As struct members
|
|
2068
|
+
struct Window:
|
|
2069
|
+
public event closed[4]
|
|
2070
|
+
public event resized[8](ResizeEvent)</code></pre>
|
|
2071
|
+
</div>
|
|
2072
|
+
|
|
2073
|
+
<h3>Operations</h3>
|
|
2074
|
+
<div class="code-wrap">
|
|
2075
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
2076
|
+
<pre><code>## Subscribe
|
|
2077
|
+
let sub = window.closed.subscribe(on_close)?
|
|
2078
|
+
|
|
2079
|
+
## One-shot
|
|
2080
|
+
let once = window.resized.subscribe_once(on_resize)?
|
|
2081
|
+
|
|
2082
|
+
## Unsubscribe
|
|
2083
|
+
window.closed.unsubscribe(sub)
|
|
2084
|
+
|
|
2085
|
+
## Emit (only from declaring module)
|
|
2086
|
+
closed.emit()
|
|
2087
|
+
resized.emit(ResizeEvent(width = 800, height = 600))
|
|
2088
|
+
|
|
2089
|
+
## Async wait for next emission
|
|
2090
|
+
let payload = await event.wait()</code></pre>
|
|
2091
|
+
</div>
|
|
2092
|
+
|
|
2093
|
+
<h3>Event API Reference</h3>
|
|
2094
|
+
<div class="table-wrap">
|
|
2095
|
+
<table class="attr-table">
|
|
2096
|
+
<tr><th>Expression</th><th>Description</th></tr>
|
|
2097
|
+
<tr><td><code>event.subscribe(fn) -> Result[Subscription, EventError]</code></td><td>Register listener; fails with <code>EventError.full</code> when at capacity</td></tr>
|
|
2098
|
+
<tr><td><code>event.subscribe(state, fn)</code></td><td>Stateful subscription — state pointer passed as first arg to listener</td></tr>
|
|
2099
|
+
<tr><td><code>event.subscribe_once(fn)</code></td><td>One-shot listener; auto-unsubscribes after first emission</td></tr>
|
|
2100
|
+
<tr><td><code>event.unsubscribe(sub) -> bool</code></td><td>Returns <code>true</code> if active and removed, <code>false</code> if already stale</td></tr>
|
|
2101
|
+
<tr><td><code>event.emit(payload?)</code></td><td>Fire event (only callable from the declaring module)</td></tr>
|
|
2102
|
+
<tr><td><code>event.wait() -> Task[Result[T, EventError]]</code></td><td>Async await for next emission</td></tr>
|
|
2103
|
+
</table>
|
|
2104
|
+
</div>
|
|
2105
|
+
<p><code>Subscription</code> is an opaque handle returned by <code>subscribe</code>. <code>EventError</code> is a built-in enum with single member <code>full = 0</code>. Event methods do not support named arguments.</p>
|
|
2106
|
+
|
|
2107
|
+
<h3>Full Example</h3>
|
|
2108
|
+
<div class="code-wrap">
|
|
2109
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
2110
|
+
<pre><code>struct ResizeEvent:
|
|
2111
|
+
width: int
|
|
2112
|
+
height: int
|
|
2113
|
+
|
|
2114
|
+
struct Window:
|
|
2115
|
+
public event closed[4]
|
|
2116
|
+
public event resized[8](ResizeEvent)
|
|
2117
|
+
|
|
2118
|
+
function on_close() -> void:
|
|
2119
|
+
io.print_line("closed")
|
|
2120
|
+
|
|
2121
|
+
function attach(window: ref[Window]) -> Result[void, EventError]:
|
|
2122
|
+
let closed_sub = window.closed.subscribe(on_close)?
|
|
2123
|
+
let resized_sub = window.resized.subscribe(on_resize)?
|
|
2124
|
+
|
|
2125
|
+
defer window.closed.unsubscribe(closed_sub)
|
|
2126
|
+
defer window.resized.unsubscribe(resized_sub)
|
|
2127
|
+
|
|
2128
|
+
return Result[void, EventError].success()</code></pre>
|
|
2129
|
+
</div>
|
|
2130
|
+
</section>
|
|
2131
|
+
|
|
2132
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
2133
|
+
<section id="std-library">
|
|
2134
|
+
<h2>Standard Library</h2>
|
|
2135
|
+
|
|
2136
|
+
<h3>Collections</h3>
|
|
2137
|
+
<div class="table-wrap">
|
|
2138
|
+
<table class="attr-table">
|
|
2139
|
+
<tr><th>Module</th><th>Type</th><th>Iteration</th><th>Description</th></tr>
|
|
2140
|
+
<tr><td><code>std.vec</code></td><td><code>Vec[T]</code></td><td>Mutable <code>ptr[T]?</code></td><td>Contiguous growable array</td></tr>
|
|
2141
|
+
<tr><td><code>std.deque</code></td><td><code>Deque[T]</code></td><td>Mutable <code>ptr[T]?</code></td><td>Growable ring buffer</td></tr>
|
|
2142
|
+
<tr><td><code>std.map</code></td><td><code>Map[K, V]</code></td><td>Keys read-only; values mutable; entries <code>current()</code></td><td>Hash table (<code>hash</code>/<code>equal</code>)</td></tr>
|
|
2143
|
+
<tr><td><code>std.set</code></td><td><code>Set[T]</code></td><td>Read-only <code>ptr[T]?</code></td><td>Hash set</td></tr>
|
|
2144
|
+
<tr><td><code>std.ordered_map</code></td><td><code>OrderedMap[K,V]</code></td><td>Keys read-only; values mutable; entries <code>current()</code></td><td>AVL tree (<code>order</code>)</td></tr>
|
|
2145
|
+
<tr><td><code>std.ordered_set</code></td><td><code>OrderedSet[T]</code></td><td>Read-only <code>ptr[T]?</code></td><td>AVL sorted unique set</td></tr>
|
|
2146
|
+
<tr><td><code>std.linked_map</code></td><td><code>LinkedMap[K,V]</code></td><td>Keys read-only; values mutable; entries <code>current()</code></td><td>Insertion-ordered hash map</td></tr>
|
|
2147
|
+
<tr><td><code>std.linked_set</code></td><td><code>LinkedSet[T]</code></td><td>Read-only <code>ptr[T]?</code></td><td>Insertion-ordered hash set</td></tr>
|
|
2148
|
+
<tr><td><code>std.binary_heap</code></td><td><code>BinaryHeap[T]</code></td><td>Read-only <code>ptr[T]?</code></td><td>Max-heap (<code>order</code>)</td></tr>
|
|
2149
|
+
<tr><td><code>std.priority_queue</code></td><td><code>PriorityQueue[T]</code></td><td>Read-only <code>ptr[T]?</code></td><td>Task-oriented facade over heap</td></tr>
|
|
2150
|
+
<tr><td><code>std.counter</code></td><td><code>Counter[T]</code></td><td>Keys read-only; entries <code>current()</code></td><td>Insertion-ordered frequency table</td></tr>
|
|
2151
|
+
<tr><td><code>std.multiset</code></td><td><code>MultiSet[T]</code></td><td>Values read-only; entries <code>current()</code></td><td>Bag with per-element counts</td></tr>
|
|
2152
|
+
<tr><td><code>std.queue</code></td><td><code>Queue[T]</code></td><td>Mutable <code>ptr[T]?</code></td><td>FIFO facade over Deque</td></tr>
|
|
2153
|
+
<tr><td><code>std.stack</code></td><td><code>Stack[T]</code></td><td>Mutable <code>ptr[T]?</code></td><td>LIFO facade over Deque</td></tr>
|
|
2154
|
+
</table>
|
|
2155
|
+
</div>
|
|
2156
|
+
|
|
2157
|
+
<h3>Core Library Modules</h3>
|
|
2158
|
+
<div class="table-wrap">
|
|
2159
|
+
<table class="attr-table">
|
|
2160
|
+
<tr><th>Module</th><th>Description</th></tr>
|
|
2161
|
+
<tr><td><code>std.str</code></td><td>Borrowed string helpers: validation, equality, slicing, search</td></tr>
|
|
2162
|
+
<tr><td><code>std.string.String</code></td><td>Growable owned UTF-8 text</td></tr>
|
|
2163
|
+
<tr><td><code>std.fmt</code></td><td>Formatting engine (only canonical formatter)</td></tr>
|
|
2164
|
+
<tr><td><code>std.math</code></td><td>Math: <code>sqrt</code>, <code>sin</code>, <code>cos</code>, <code>abs</code>, <code>pow</code></td></tr>
|
|
2165
|
+
<tr><td><code>std.linear_algebra</code></td><td>Vector/matrix/quaternion extensions: <code>dot</code>, <code>cross</code>, <code>normalized</code>, <code>lerp</code>, <code>transpose</code></td></tr>
|
|
2166
|
+
<tr><td><code>std.hash</code></td><td>Canonical <code>hash</code>/<code>equal</code>/<code>order</code> for primitives and structs</td></tr>
|
|
2167
|
+
<tr><td><code>std.mem.heap</code></td><td>General heap allocator</td></tr>
|
|
2168
|
+
<tr><td><code>std.mem.arena</code></td><td>Frame/level/scratch arena allocator</td></tr>
|
|
2169
|
+
<tr><td><code>std.mem.pool</code></td><td>Fixed-size object pool</td></tr>
|
|
2170
|
+
<tr><td><code>std.mem.stack</code></td><td>Explicit temporary allocator</td></tr>
|
|
2171
|
+
<tr><td><code>std.async</code></td><td>Async runtime: <code>sleep</code>, <code>work</code>, <code>completed</code>, <code>result</code>, <code>wait</code>, <code>run</code></td></tr>
|
|
2172
|
+
<tr><td><code>std.option</code></td><td><code>Option[T]</code> — optional value (prelude type; methods always available without import). Methods: <code>is_some</code>, <code>is_none</code>, <code>unwrap</code>, <code>expect</code>, <code>unwrap_or</code>, <code>unwrap_or_else</code></td></tr>
|
|
2173
|
+
<tr><td><code>std.result</code></td><td><code>Result[T, E]</code> — fallible computation (prelude type; methods always available without import). Methods: <code>is_success</code>, <code>is_failure</code>, <code>unwrap</code>, <code>unwrap_error</code>, <code>unwrap_or</code>, <code>unwrap_or_else</code>, <code>ok</code>, <code>error</code>, <code>map_error</code></td></tr>
|
|
2174
|
+
</table>
|
|
2175
|
+
</div>
|
|
2176
|
+
|
|
2177
|
+
<h3>System & I/O</h3>
|
|
2178
|
+
<div class="table-wrap">
|
|
2179
|
+
<table class="attr-table">
|
|
2180
|
+
<tr><th>Module</th><th>Description</th></tr>
|
|
2181
|
+
<tr><td><code>std.time</code></td><td>Time primitives</td></tr>
|
|
2182
|
+
<tr><td><code>std.fs</code></td><td>File system operations</td></tr>
|
|
2183
|
+
<tr><td><code>std.path</code></td><td>Path manipulation</td></tr>
|
|
2184
|
+
<tr><td><code>std.process</code></td><td>Process management</td></tr>
|
|
2185
|
+
<tr><td><code>std.cli</code></td><td>Command line interface</td></tr>
|
|
2186
|
+
<tr><td><code>std.stdio</code></td><td>Standard I/O: <code>print_line</code>, <code>print_format</code>, <code>file_open</code>, <code>file_read_line</code></td></tr>
|
|
2187
|
+
<tr><td><code>std.terminal</code></td><td>Terminal control</td></tr>
|
|
2188
|
+
<tr><td><code>std.json</code> <code>std.toml</code></td><td>Serialization</td></tr>
|
|
2189
|
+
<tr><td><code>std.uri</code></td><td>URI parsing</td></tr>
|
|
2190
|
+
<tr><td><code>std.http</code> <code>std.tls</code> <code>std.net</code></td><td>Networking</td></tr>
|
|
2191
|
+
<tr><td><code>std.gzip</code> <code>std.tar</code></td><td>Compression</td></tr>
|
|
2192
|
+
<tr><td><code>std.sync</code> <code>std.thread</code> <code>std.jobs</code></td><td>Concurrency</td></tr>
|
|
2193
|
+
<tr><td><code>std.fsm</code> <code>std.goap</code> <code>std.behavior_tree</code></td><td>AI / State machines</td></tr>
|
|
2194
|
+
<tr><td><code>std.cell</code></td><td>Shared mutable cell allocation</td></tr>
|
|
2195
|
+
</table>
|
|
2196
|
+
</div>
|
|
2197
|
+
</section>
|
|
2198
|
+
|
|
2199
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
2200
|
+
<section id="cli">
|
|
2201
|
+
<h2>CLI Reference</h2>
|
|
2202
|
+
<p>The <code>mtc</code> CLI is the primary tool for checking, building, and running Milk Tea programs.</p>
|
|
2203
|
+
|
|
2204
|
+
<h3>Essential Commands</h3>
|
|
2205
|
+
<div class="table-wrap">
|
|
2206
|
+
<table class="attr-table">
|
|
2207
|
+
<tr><th>Command</th><th>Description</th></tr>
|
|
2208
|
+
<tr><td><code>mtc check <path></code></td><td>Type-check + lint; reports all diagnostics sorted by line</td></tr>
|
|
2209
|
+
<tr><td><code>mtc run <path></code></td><td>Build and execute</td></tr>
|
|
2210
|
+
<tr><td><code>mtc build <path></code></td><td>Build only (emit C, compile, link)</td></tr>
|
|
2211
|
+
<tr><td><code>mtc lex <file.mt></code></td><td>Print lexer token stream</td></tr>
|
|
2212
|
+
<tr><td><code>mtc parse <path></code></td><td>Print parsed AST</td></tr>
|
|
2213
|
+
<tr><td><code>mtc lower <path></code></td><td>Print lowered IR</td></tr>
|
|
2214
|
+
<tr><td><code>mtc debug <file.mt></code></td><td>Print debug info (tokens, AST, facts, bindings, diagnostics)</td></tr>
|
|
2215
|
+
<tr><td><code>mtc emit-c <path></code></td><td>Emit generated C to stdout</td></tr>
|
|
2216
|
+
<tr><td><code>mtc format <path></code></td><td>Format sources in place (<code>--check</code> for dry-run)</td></tr>
|
|
2217
|
+
<tr><td><code>mtc lint <path></code></td><td>Run linter (<code>--fix</code>, <code>--select</code>, <code>--ignore</code>)</td></tr>
|
|
2218
|
+
<tr><td><code>mtc test <path></code></td><td>Discover and run <code>@[test]</code> functions</td></tr>
|
|
2219
|
+
<tr><td><code>mtc new <name></code></td><td>Scaffold a new package (<code>package.toml</code> + <code>src/main.mt</code>)</td></tr>
|
|
2220
|
+
<tr><td><code>mtc run-module <module></code></td><td>Run a pre-built module by name</td></tr>
|
|
2221
|
+
<tr><td><code>mtc completions <shell></code></td><td>Print a bash/zsh/fish completion script</td></tr>
|
|
2222
|
+
</table>
|
|
2223
|
+
</div>
|
|
2224
|
+
|
|
2225
|
+
<h3>Package Commands</h3>
|
|
2226
|
+
<div class="table-wrap">
|
|
2227
|
+
<table class="attr-table">
|
|
2228
|
+
<tr><th>Command</th><th>Description</th></tr>
|
|
2229
|
+
<tr><td><code>mtc deps tree <path></code></td><td>Print the dependency graph</td></tr>
|
|
2230
|
+
<tr><td><code>mtc deps lock <path></code></td><td>Write/refresh <code>package.lock</code></td></tr>
|
|
2231
|
+
<tr><td><code>mtc deps add <path> <name></code></td><td>Add a dependency</td></tr>
|
|
2232
|
+
<tr><td><code>mtc deps remove <path> <name></code></td><td>Remove a dependency</td></tr>
|
|
2233
|
+
<tr><td><code>mtc deps update <path></code></td><td>Update dependencies</td></tr>
|
|
2234
|
+
<tr><td><code>mtc deps publish <path></code></td><td>Publish a package to the local registry</td></tr>
|
|
2235
|
+
<tr><td><code>mtc deps fetch <path></code></td><td>Materialize cache-backed sources</td></tr>
|
|
2236
|
+
</table>
|
|
2237
|
+
</div>
|
|
2238
|
+
|
|
2239
|
+
<h3>Toolchain</h3>
|
|
2240
|
+
<div class="table-wrap">
|
|
2241
|
+
<table class="attr-table">
|
|
2242
|
+
<tr><th>Command</th><th>Description</th></tr>
|
|
2243
|
+
<tr><td><code>mtc toolchain bootstrap</code></td><td>Bootstrap the native toolchain</td></tr>
|
|
2244
|
+
<tr><td><code>mtc toolchain doctor</code></td><td>Diagnose toolchain setup</td></tr>
|
|
2245
|
+
<tr><td><code>mtc toolchain tools</code></td><td>List available native tools</td></tr>
|
|
2246
|
+
<tr><td><code>mtc cache status</code></td><td>Show build cache stats</td></tr>
|
|
2247
|
+
<tr><td><code>mtc docs [--open] [--port PORT]</code></td><td>Start a local HTTP server serving the language reference</td></tr>
|
|
2248
|
+
</table>
|
|
2249
|
+
</div>
|
|
2250
|
+
|
|
2251
|
+
<h3>Build Flags</h3>
|
|
2252
|
+
<p>Build and run commands support: <code>--profile</code>, <code>--platform</code>, <code>--cc</code>, <code>--keep-c</code>, <code>--locked</code>, <code>--frozen</code>, and <code>-I</code> include paths.</p>
|
|
2253
|
+
|
|
2254
|
+
<h3>Global Options</h3>
|
|
2255
|
+
<p>These work with any command (before or after the subcommand, up to a <code>--</code> separator):</p>
|
|
2256
|
+
<div class="table-wrap">
|
|
2257
|
+
<table class="attr-table">
|
|
2258
|
+
<tr><th>Option</th><th>Description</th></tr>
|
|
2259
|
+
<tr><td><code>-h</code>, <code>--help</code></td><td>Show help; <code>mtc help <command></code> shows command-specific help</td></tr>
|
|
2260
|
+
<tr><td><code>-V</code>, <code>--version</code></td><td>Print the compiler version</td></tr>
|
|
2261
|
+
<tr><td><code>-q</code>, <code>--quiet</code></td><td>Suppress informational output</td></tr>
|
|
2262
|
+
<tr><td><code>-v</code>, <code>--verbose</code></td><td>Print per-file progress</td></tr>
|
|
2263
|
+
<tr><td><code>--color auto|always|never</code></td><td>Control diagnostic colorization (default: auto)</td></tr>
|
|
2264
|
+
</table>
|
|
2265
|
+
</div>
|
|
2266
|
+
</section>
|
|
2267
|
+
|
|
2268
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
2269
|
+
<section id="linting">
|
|
2270
|
+
<h2>Linting</h2>
|
|
2271
|
+
<p>Milk Tea ships with a built-in linter providing rules across correctness, style, performance, and convention. Run with <code>mtc lint</code>.</p>
|
|
2272
|
+
|
|
2273
|
+
<h3>CLI Usage</h3>
|
|
2274
|
+
<div class="code-wrap">
|
|
2275
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
2276
|
+
<pre><code>mtc lint path/to/file.mt # report all diagnostics
|
|
2277
|
+
mtc lint path/to/file.mt --fix # auto-apply safe fixes
|
|
2278
|
+
mtc lint path/to/file.mt --select unused-import # only selected rules
|
|
2279
|
+
mtc lint path/to/file.mt --ignore line-too-long # skip selected rules</code></pre>
|
|
2280
|
+
</div>
|
|
2281
|
+
|
|
2282
|
+
<h3>Inline Suppression</h3>
|
|
2283
|
+
<p>Suppress a rule for the next line with <code># lint: ignore <rule-code></code>:</p>
|
|
2284
|
+
<div class="code-wrap">
|
|
2285
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
2286
|
+
<pre><code># lint: ignore unused-import
|
|
2287
|
+
import std.math # intentionally available for downstream</code></pre>
|
|
2288
|
+
</div>
|
|
2289
|
+
|
|
2290
|
+
<h3>Configuration</h3>
|
|
2291
|
+
<p>Place a <code>.mt-lint.yml</code> file in your package root to customize rule severity:</p>
|
|
2292
|
+
<div class="code-wrap">
|
|
2293
|
+
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
|
2294
|
+
<pre><code>rules:
|
|
2295
|
+
prefer-let-else: warn
|
|
2296
|
+
unused-import: error
|
|
2297
|
+
line-too-long: off</code></pre>
|
|
2298
|
+
</div>
|
|
2299
|
+
|
|
2300
|
+
<h3>Rule Reference</h3>
|
|
2301
|
+
<div class="table-wrap">
|
|
2302
|
+
<table class="attr-table">
|
|
2303
|
+
<tr><th>Category</th><th>Code</th><th>Description</th></tr>
|
|
2304
|
+
<tr><td>Correctness</td><td><code>constant-condition</code></td><td>Always-true or always-false conditions</td></tr>
|
|
2305
|
+
<tr><td>Correctness</td><td><code>dead-assignment</code></td><td>Variable assigned but never read</td></tr>
|
|
2306
|
+
<tr><td>Correctness</td><td><code>unreachable-code</code></td><td>Code after a terminating statement</td></tr>
|
|
2307
|
+
<tr><td>Correctness</td><td><code>redundant-null-check</code></td><td>Null check on already-proven non-null</td></tr>
|
|
2308
|
+
<tr><td>Correctness</td><td><code>loop-single-iteration</code></td><td>Loop with known single iteration</td></tr>
|
|
2309
|
+
<tr><td>Style</td><td><code>prefer-let-else</code></td><td>Var followed by null check; suggest let...else</td></tr>
|
|
2310
|
+
<tr><td>Style</td><td><code>prefer-var-else</code></td><td>Same as above for mutable bindings</td></tr>
|
|
2311
|
+
<tr><td>Style</td><td><code>redundant-cast</code></td><td>Explicit cast where implicit widening already works</td></tr>
|
|
2312
|
+
<tr><td>Style</td><td><code>redundant-return</code></td><td>Unnecessary return at end of void function</td></tr>
|
|
2313
|
+
<tr><td>Style</td><td><code>trailing-list-comma</code></td><td>Multiline lists should end with trailing comma</td></tr>
|
|
2314
|
+
<tr><td>Style</td><td><code>line-too-long</code></td><td>Line exceeds recommended width</td></tr>
|
|
2315
|
+
<tr><td>Style</td><td><code>doc-tag</code></td><td>Documentation comment formatting</td></tr>
|
|
2316
|
+
<tr><td>Convention</td><td><code>unused-import</code></td><td>Imported module never referenced</td></tr>
|
|
2317
|
+
<tr><td>Convention</td><td><code>unused-var</code></td><td>Variable declared but never read</td></tr>
|
|
2318
|
+
<tr><td>Convention</td><td><code>reserved-names</code></td><td>Binding shadows a primitive type or built-in name</td></tr>
|
|
2319
|
+
<tr><td>Convention</td><td><code>event-capacity</code></td><td>Event capacity too large or unbounded</td></tr>
|
|
2320
|
+
</table>
|
|
2321
|
+
</div>
|
|
2322
|
+
<p>Severity levels: <code>error</code> (fail build), <code>warn</code> (report only), <code>off</code> (disabled). Rules default to <code>warn</code> unless noted in the manual.</p>
|
|
2323
|
+
</section>
|
|
2324
|
+
|
|
2325
|
+
<!-- ────────────────────────────────────────────────────────────────────────── -->
|
|
2326
|
+
<section id="limitations">
|
|
2327
|
+
<h2>Language Limitations</h2>
|
|
2328
|
+
<p>This is a quick-reference summary of the current compiler's rejection surface. See the <a href="https://github.com/teefan/mt-lang/blob/main/docs/language-manual.md">full manual</a> for complete rules.</p>
|
|
2329
|
+
|
|
2330
|
+
<h3>Function Restrictions</h3>
|
|
2331
|
+
<ul style="margin-left:1.25rem;margin-bottom:1rem">
|
|
2332
|
+
<li><code>external function</code> cannot be generic, async, or take/return arrays, <code>ref[...]</code>, or <code>proc(...)</code></li>
|
|
2333
|
+
<li><code>foreign function</code> cannot be async, cannot take <code>proc(...)</code></li>
|
|
2334
|
+
<li>Functions cannot return <code>ref[T]</code> or non-owning structs</li>
|
|
2335
|
+
<li>A consuming foreign function must return <code>void</code></li>
|
|
2336
|
+
<li><code>external</code> / <code>foreign function</code> cannot use a non-pointer value nullable (e.g. <code>int?</code>) as a parameter or return type; only pointer-like <code>T?</code> may cross an FFI boundary</li>
|
|
2337
|
+
</ul>
|
|
2338
|
+
|
|
2339
|
+
<h3>Storage Restrictions</h3>
|
|
2340
|
+
<ul style="margin-left:1.25rem;margin-bottom:1rem">
|
|
2341
|
+
<li><code>ref[T]</code> values rejected in constants, module variables, and nested storage</li>
|
|
2342
|
+
<li>Bare interface names are not runtime storage types; use <code>dyn[I]</code></li>
|
|
2343
|
+
<li><code>dyn[I]</code> for generic interfaces must be fully specified: <code>dyn[Mapper[int]]</code></li>
|
|
2344
|
+
<li><code>proc</code> cannot capture <code>ref[T]</code> values</li>
|
|
2345
|
+
</ul>
|
|
2346
|
+
|
|
2347
|
+
<h3>Control Flow Restrictions</h3>
|
|
2348
|
+
<ul style="margin-left:1.25rem;margin-bottom:1rem">
|
|
2349
|
+
<li><code>break</code> and <code>continue</code> must be inside loops</li>
|
|
2350
|
+
<li><code>return</code> not allowed inside <code>defer</code> blocks</li>
|
|
2351
|
+
<li><code>?</code> propagation not allowed inside <code>defer</code> blocks</li>
|
|
2352
|
+
<li><code>match</code> on enum/variant must be exhaustive unless <code>_</code> present; match on <code>int</code>/<code>str</code> requires <code>_</code></li>
|
|
2353
|
+
<li><code>let ... else:</code> else block must terminate control flow</li>
|
|
2354
|
+
</ul>
|
|
2355
|
+
|
|
2356
|
+
<h3>External File Restrictions</h3>
|
|
2357
|
+
<ul style="margin-left:1.25rem;margin-bottom:1rem">
|
|
2358
|
+
<li>Cannot contain <code>attribute</code>, <code>event</code>, <code>var</code>, <code>variant</code>, <code>interface</code>, <code>extending</code>, <code>foreign function</code>, ordinary <code>function</code>, <code>async function</code>, or <code>static_assert</code></li>
|
|
2359
|
+
<li><code>public</code> is rejected (declarations are implicitly exported)</li>
|
|
2360
|
+
</ul>
|
|
2361
|
+
|
|
2362
|
+
<h3>Expression Restrictions</h3>
|
|
2363
|
+
<ul style="margin-left:1.25rem;margin-bottom:1rem">
|
|
2364
|
+
<li>Conditions must be <code>bool</code>; no truthy/falsy coercion</li>
|
|
2365
|
+
<li>Mixed signed/unsigned arithmetic requires explicit cast; non-widening integer conversions (narrowing, signed → unsigned) need explicit <code>T<-value</code></li>
|
|
2366
|
+
<li>Enum and flags values do not implicitly coerce to backing integers</li>
|
|
2367
|
+
<li><code>+</code> does not support <code>str</code>/<code>cstr</code> concatenation</li>
|
|
2368
|
+
<li><code>==</code> and <code>!=</code> not supported on struct types; use <code>equal[T]</code></li>
|
|
2369
|
+
<li>Bare function and method names are not usable as values (cannot be assigned or passed without calling). Use <code>fn(...)</code> function pointer types or <code>proc(...)</code> closures for callable values.</li>
|
|
2370
|
+
</ul>
|
|
2371
|
+
|
|
2372
|
+
<h3>Event Restrictions</h3>
|
|
2373
|
+
<ul style="margin-left:1.25rem;margin-bottom:1rem">
|
|
2374
|
+
<li>Event payload cannot be <code>ref[T]</code></li>
|
|
2375
|
+
<li><code>emit</code> is only callable from within the declaring module</li>
|
|
2376
|
+
<li>Events not allowed in external files</li>
|
|
2377
|
+
</ul>
|
|
2378
|
+
</section>
|
|
2379
|
+
|
|
2380
|
+
</main>
|
|
2381
|
+
|
|
2382
|
+
<!-- ─── Scripts ────────────────────────────────────────────────────────────── -->
|
|
2383
|
+
<script>
|
|
2384
|
+
/* ── Theme ────────────────────────────────────────────────────────────────── */
|
|
2385
|
+
const html = document.documentElement
|
|
2386
|
+
const themeBtn = document.getElementById('theme-toggle')
|
|
2387
|
+
const themeBtnMbl = document.getElementById('theme-toggle-mbl')
|
|
2388
|
+
const STORAGE_KEY = 'mt-lang-theme'
|
|
2389
|
+
|
|
2390
|
+
function setTheme(t) {
|
|
2391
|
+
html.setAttribute('data-theme', t)
|
|
2392
|
+
try { localStorage.setItem(STORAGE_KEY, t) } catch (_) {}
|
|
2393
|
+
updateThemeIcons(t)
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2396
|
+
function updateThemeIcons(t) {
|
|
2397
|
+
const isDark = t === 'dark'
|
|
2398
|
+
document.querySelectorAll('.icon-sun').forEach(el => el.style.display = isDark ? 'inline' : 'none')
|
|
2399
|
+
document.querySelectorAll('.icon-moon').forEach(el => el.style.display = isDark ? 'none' : 'inline')
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2402
|
+
function toggleTheme() {
|
|
2403
|
+
const cur = html.getAttribute('data-theme')
|
|
2404
|
+
setTheme(cur === 'dark' ? 'light' : 'dark')
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
themeBtn.addEventListener('click', toggleTheme)
|
|
2408
|
+
themeBtnMbl.addEventListener('click', toggleTheme)
|
|
2409
|
+
|
|
2410
|
+
const saved = (() => { try { return localStorage.getItem(STORAGE_KEY) } catch (_) { return null } })()
|
|
2411
|
+
if (saved) setTheme(saved)
|
|
2412
|
+
else if (window.matchMedia('(prefers-color-scheme: dark)').matches) setTheme('dark')
|
|
2413
|
+
else updateThemeIcons('light')
|
|
2414
|
+
|
|
2415
|
+
/* ── Mobile Sidebar ────────────────────────────────────────────────────────── */
|
|
2416
|
+
const sidebar = document.getElementById('sidebar')
|
|
2417
|
+
const overlay = document.getElementById('overlay')
|
|
2418
|
+
const menuBtn = document.getElementById('menu-btn')
|
|
2419
|
+
const main = document.getElementById('main')
|
|
2420
|
+
|
|
2421
|
+
function openSidebar() { sidebar.classList.add('open'); overlay.classList.add('visible') }
|
|
2422
|
+
function closeSidebar() { sidebar.classList.remove('open'); overlay.classList.remove('visible') }
|
|
2423
|
+
|
|
2424
|
+
menuBtn.addEventListener('click', openSidebar)
|
|
2425
|
+
overlay.addEventListener('click', closeSidebar)
|
|
2426
|
+
|
|
2427
|
+
document.querySelectorAll('.sidebar-nav a').forEach(a => {
|
|
2428
|
+
a.addEventListener('click', () => {
|
|
2429
|
+
if (window.innerWidth < 768) closeSidebar()
|
|
2430
|
+
})
|
|
2431
|
+
})
|
|
2432
|
+
|
|
2433
|
+
/* ── Scroll Spy ───────────────────────────────────────────────────────────── */
|
|
2434
|
+
const sections = document.querySelectorAll('section[id]')
|
|
2435
|
+
const navLinks = document.querySelectorAll('.sidebar-nav a[href^="#"]')
|
|
2436
|
+
|
|
2437
|
+
function updateActiveLink() {
|
|
2438
|
+
let current = ''
|
|
2439
|
+
sections.forEach(s => {
|
|
2440
|
+
const top = s.getBoundingClientRect().top
|
|
2441
|
+
if (top <= 120) current = s.id
|
|
2442
|
+
})
|
|
2443
|
+
navLinks.forEach(a => {
|
|
2444
|
+
a.classList.toggle('active', a.getAttribute('href') === '#' + current)
|
|
2445
|
+
})
|
|
2446
|
+
|
|
2447
|
+
/* Ensure active link is visible in sidebar scroll */
|
|
2448
|
+
const active = document.querySelector('.sidebar-nav a.active')
|
|
2449
|
+
if (active) {
|
|
2450
|
+
active.scrollIntoView({ block: 'nearest', behavior: 'smooth' })
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
window.addEventListener('scroll', updateActiveLink, { passive: true })
|
|
2455
|
+
updateActiveLink()
|
|
2456
|
+
|
|
2457
|
+
/* ── Progress Bar ─────────────────────────────────────────────────────────── */
|
|
2458
|
+
const progress = document.getElementById('progress')
|
|
2459
|
+
function updateProgress() {
|
|
2460
|
+
const scrollTop = window.scrollY
|
|
2461
|
+
const docHeight = document.documentElement.scrollHeight - window.innerHeight
|
|
2462
|
+
const pct = docHeight > 0 ? (scrollTop / docHeight) * 100 : 0
|
|
2463
|
+
progress.style.width = pct + '%'
|
|
2464
|
+
}
|
|
2465
|
+
window.addEventListener('scroll', updateProgress, { passive: true })
|
|
2466
|
+
|
|
2467
|
+
/* ── Copy Button ───────────────────────────────────────────────────────────── */
|
|
2468
|
+
function copyCode(btn) {
|
|
2469
|
+
const pre = btn.closest('.code-wrap').querySelector('pre')
|
|
2470
|
+
const text = pre.textContent
|
|
2471
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
2472
|
+
btn.textContent = 'Copied!'
|
|
2473
|
+
btn.classList.add('copied')
|
|
2474
|
+
setTimeout(() => {
|
|
2475
|
+
btn.textContent = 'Copy'
|
|
2476
|
+
btn.classList.remove('copied')
|
|
2477
|
+
}, 1800)
|
|
2478
|
+
}).catch(() => {
|
|
2479
|
+
/* Fallback */
|
|
2480
|
+
const ta = document.createElement('textarea')
|
|
2481
|
+
ta.value = text; ta.style.position = 'fixed'; ta.style.opacity = '0'
|
|
2482
|
+
document.body.appendChild(ta); ta.select()
|
|
2483
|
+
try { document.execCommand('copy') } catch (_) {}
|
|
2484
|
+
document.body.removeChild(ta)
|
|
2485
|
+
btn.textContent = 'Copied!'
|
|
2486
|
+
btn.classList.add('copied')
|
|
2487
|
+
setTimeout(() => { btn.textContent = 'Copy'; btn.classList.remove('copied') }, 1800)
|
|
2488
|
+
})
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
/* ── Syntax Highlighting ──────────────────────────────────────────────────── */
|
|
2492
|
+
const KEYWORDS = new Set([
|
|
2493
|
+
'function','async','await','struct','enum','flags','union','variant','interface',
|
|
2494
|
+
'extending','implements','let','var','const','type','opaque','attribute',
|
|
2495
|
+
'if','else','match','for','while','return','break','continue','pass',
|
|
2496
|
+
'defer','unsafe','when','inline','external','foreign','import','public',
|
|
2497
|
+
'event','emit','not','and','or','in','out','inout','consuming','static_assert',
|
|
2498
|
+
'fn','proc','this','editable','static','as','parallel','detach','gather','is',
|
|
2499
|
+
])
|
|
2500
|
+
|
|
2501
|
+
const ATOMS = new Set(['true','false','null'])
|
|
2502
|
+
|
|
2503
|
+
const BUILTINS = new Set([
|
|
2504
|
+
'ref_of','ptr_of','const_ptr_of','read','reinterpret','zero','default',
|
|
2505
|
+
'hash','equal','order','size_of','align_of','offset_of','get','adapt','fatal',
|
|
2506
|
+
'field_of','callable_of','attribute_of','has_attribute','attribute_arg',
|
|
2507
|
+
'fields_of','members_of','attributes_of',
|
|
2508
|
+
])
|
|
2509
|
+
|
|
2510
|
+
const TYPES = new Set([
|
|
2511
|
+
'bool','byte','short','int','long','ubyte','ushort','uint','ulong',
|
|
2512
|
+
'char','ptr_int','ptr_uint','float','double','void',
|
|
2513
|
+
'str','cstr','vec2','vec3','vec4','ivec2','ivec3','ivec4',
|
|
2514
|
+
'mat3','mat4','quat','ptr','span','array','SoA',
|
|
2515
|
+
'Option','Result','Task','dyn','ref','const_ptr','own','str_buffer','proc',
|
|
2516
|
+
'field_handle','member_handle','callable_handle','attribute_handle',
|
|
2517
|
+
'EventError','Subscription','atomic',
|
|
2518
|
+
])
|
|
2519
|
+
|
|
2520
|
+
const BIFNS = new Set([
|
|
2521
|
+
'print_line','print_format','format','append_format','assign_format',
|
|
2522
|
+
'subscribe','subscribe_once','unsubscribe',
|
|
2523
|
+
'as_span','to_cstr','as_cstr','as_str',
|
|
2524
|
+
])
|
|
2525
|
+
|
|
2526
|
+
function highlight(code) {
|
|
2527
|
+
let out = ''
|
|
2528
|
+
let i = 0
|
|
2529
|
+
|
|
2530
|
+
while (i < code.length) {
|
|
2531
|
+
/* Line comments — ## doc comments first, then # regular */
|
|
2532
|
+
if (code[i] === '#' && code[i+1] === '#' && code[i+2] !== '#') {
|
|
2533
|
+
let j = i
|
|
2534
|
+
while (j < code.length && code[j] !== '\n') j++
|
|
2535
|
+
out += '<span class="syn-doc">' + esc(code.slice(i, j)) + '</span>'
|
|
2536
|
+
i = j
|
|
2537
|
+
continue
|
|
2538
|
+
}
|
|
2539
|
+
if (code[i] === '#') {
|
|
2540
|
+
let j = i
|
|
2541
|
+
while (j < code.length && code[j] !== '\n') j++
|
|
2542
|
+
out += '<span class="syn-cmt">' + esc(code.slice(i, j)) + '</span>'
|
|
2543
|
+
i = j
|
|
2544
|
+
continue
|
|
2545
|
+
}
|
|
2546
|
+
|
|
2547
|
+
/* @[...] attribute blocks */
|
|
2548
|
+
if (code[i] === '@' && code[i+1] === '[') {
|
|
2549
|
+
let j = i + 2, depth = 1
|
|
2550
|
+
while (j < code.length && depth > 0) {
|
|
2551
|
+
if (code[j] === '[') depth++
|
|
2552
|
+
else if (code[j] === ']') depth--
|
|
2553
|
+
j++
|
|
2554
|
+
}
|
|
2555
|
+
out += '<span class="syn-attr">' + esc(code.slice(i, j)) + '</span>'
|
|
2556
|
+
i = j
|
|
2557
|
+
continue
|
|
2558
|
+
}
|
|
2559
|
+
|
|
2560
|
+
/* Format string f"..." */
|
|
2561
|
+
if (code[i] === 'f' && code[i+1] === '"') {
|
|
2562
|
+
let j = i + 2
|
|
2563
|
+
while (j < code.length && code[j] !== '"') {
|
|
2564
|
+
if (code[j] === '\\') { j += 2; continue }
|
|
2565
|
+
j++
|
|
2566
|
+
}
|
|
2567
|
+
if (j < code.length) j++
|
|
2568
|
+
out += '<span class="syn-str">' + esc(code.slice(i, j)) + '</span>'
|
|
2569
|
+
i = j
|
|
2570
|
+
continue
|
|
2571
|
+
}
|
|
2572
|
+
/* C-string c"..." */
|
|
2573
|
+
if (code[i] === 'c' && code[i+1] === '"') {
|
|
2574
|
+
let j = i + 2
|
|
2575
|
+
while (j < code.length && code[j] !== '"') {
|
|
2576
|
+
if (code[j] === '\\') { j += 2; continue }
|
|
2577
|
+
j++
|
|
2578
|
+
}
|
|
2579
|
+
if (j < code.length) j++
|
|
2580
|
+
out += '<span class="syn-str">' + esc(code.slice(i, j)) + '</span>'
|
|
2581
|
+
i = j
|
|
2582
|
+
continue
|
|
2583
|
+
}
|
|
2584
|
+
/* Regular string "..." */
|
|
2585
|
+
if (code[i] === '"') {
|
|
2586
|
+
let j = i + 1
|
|
2587
|
+
while (j < code.length && code[j] !== '"') {
|
|
2588
|
+
if (code[j] === '\\') { j += 2; continue }
|
|
2589
|
+
j++
|
|
2590
|
+
}
|
|
2591
|
+
if (j < code.length) j++
|
|
2592
|
+
out += '<span class="syn-str">' + esc(code.slice(i, j)) + '</span>'
|
|
2593
|
+
i = j
|
|
2594
|
+
continue
|
|
2595
|
+
}
|
|
2596
|
+
|
|
2597
|
+
/* Heredoc (<<-TAG, c<<-TAG, f<<-TAG) */
|
|
2598
|
+
const hdMatch = code.slice(i).match(/^(c|f)?<<-/)
|
|
2599
|
+
if (hdMatch) {
|
|
2600
|
+
const tagIdx = i + hdMatch[0].length
|
|
2601
|
+
let tagEnd = tagIdx
|
|
2602
|
+
while (tagEnd < code.length && code[tagEnd] !== '\n') tagEnd++
|
|
2603
|
+
const tag = code.slice(tagIdx, tagEnd).trim()
|
|
2604
|
+
const terminator = '\n' + tag
|
|
2605
|
+
const rest = code.slice(tagEnd)
|
|
2606
|
+
const termIdx = rest.indexOf(terminator)
|
|
2607
|
+
if (termIdx !== -1) {
|
|
2608
|
+
const end = tagEnd + termIdx + terminator.length
|
|
2609
|
+
out += '<span class="syn-str">' + esc(code.slice(i, end)) + '</span>'
|
|
2610
|
+
i = end
|
|
2611
|
+
continue
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
/* Numbers */
|
|
2616
|
+
if (/\d/.test(code[i]) || (code[i] === '.' && i+1 < code.length && /\d/.test(code[i+1]))) {
|
|
2617
|
+
const numMatch = code.slice(i).match(/^(0[xX][0-9a-fA-F_]+|0[bB][01_]+|0[oO][0-7_]+|\d[\d_]*(\.[\d_]*)?([eE][+-]?\d+)?[fd]?)/)
|
|
2618
|
+
if (numMatch) {
|
|
2619
|
+
out += '<span class="syn-num">' + esc(numMatch[0]) + '</span>'
|
|
2620
|
+
i += numMatch[0].length
|
|
2621
|
+
continue
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2625
|
+
/* Words (keywords, types, builtins, identifiers) */
|
|
2626
|
+
if (/[a-zA-Z_]/.test(code[i])) {
|
|
2627
|
+
let j = i
|
|
2628
|
+
while (j < code.length && /[a-zA-Z0-9_]/.test(code[j])) j++
|
|
2629
|
+
const word = code.slice(i, j)
|
|
2630
|
+
if (ATOMS.has(word)) {
|
|
2631
|
+
out += '<span class="syn-atom">' + esc(word) + '</span>'
|
|
2632
|
+
} else if (KEYWORDS.has(word)) {
|
|
2633
|
+
out += '<span class="syn-kw">' + esc(word) + '</span>'
|
|
2634
|
+
} else if (TYPES.has(word)) {
|
|
2635
|
+
out += '<span class="syn-type">' + esc(word) + '</span>'
|
|
2636
|
+
} else if (BUILTINS.has(word)) {
|
|
2637
|
+
out += '<span class="syn-fn">' + esc(word) + '</span>'
|
|
2638
|
+
} else if (BIFNS.has(word)) {
|
|
2639
|
+
out += '<span class="syn-fn">' + esc(word) + '</span>'
|
|
2640
|
+
} else if (/^[A-Z]/.test(word) && word !== word.toUpperCase()) {
|
|
2641
|
+
out += '<span class="syn-type">' + esc(word) + '</span>'
|
|
2642
|
+
} else {
|
|
2643
|
+
out += esc(word)
|
|
2644
|
+
}
|
|
2645
|
+
i = j
|
|
2646
|
+
continue
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2649
|
+
/* Multi-char operators */
|
|
2650
|
+
const mc3 = code.slice(i, i+3)
|
|
2651
|
+
if (['<<=','>>='].includes(mc3)) {
|
|
2652
|
+
out += '<span class="syn-op">' + esc(mc3) + '</span>'
|
|
2653
|
+
i += 3; continue
|
|
2654
|
+
}
|
|
2655
|
+
const mc2 = code.slice(i, i+2)
|
|
2656
|
+
if (['<-','->','==','!=','<=','>=','<<','>>','+=','-=','*=','/=','%=','&=','|=','^=','..'].includes(mc2)) {
|
|
2657
|
+
out += '<span class="syn-op">' + esc(mc2) + '</span>'
|
|
2658
|
+
i += 2; continue
|
|
2659
|
+
}
|
|
2660
|
+
/* Single-char operators and punctuation */
|
|
2661
|
+
if ('+-*/%&|^~=<>!?:.,;@[](){}'.includes(code[i])) {
|
|
2662
|
+
out += '<span class="syn-op">' + esc(code[i]) + '</span>'
|
|
2663
|
+
i++; continue
|
|
2664
|
+
}
|
|
2665
|
+
|
|
2666
|
+
/* Whitespace / other */
|
|
2667
|
+
out += esc(code[i])
|
|
2668
|
+
i++
|
|
2669
|
+
}
|
|
2670
|
+
return out
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2673
|
+
function esc(s) {
|
|
2674
|
+
return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
/* Apply to all code blocks */
|
|
2678
|
+
document.querySelectorAll('.code-wrap pre').forEach(pre => {
|
|
2679
|
+
pre.innerHTML = '<code>' + highlight(pre.textContent) + '</code>'
|
|
2680
|
+
})
|
|
2681
|
+
|
|
2682
|
+
/* ── Search ───────────────────────────────────────────────────────────────── */
|
|
2683
|
+
const searchInput = document.getElementById('search')
|
|
2684
|
+
const allSections = document.querySelectorAll('section')
|
|
2685
|
+
|
|
2686
|
+
searchInput.addEventListener('input', function() {
|
|
2687
|
+
const q = this.value.trim().toLowerCase()
|
|
2688
|
+
|
|
2689
|
+
/* Clear previous highlights */
|
|
2690
|
+
document.querySelectorAll('.search-highlight').forEach(el => {
|
|
2691
|
+
el.replaceWith(el.textContent)
|
|
2692
|
+
})
|
|
2693
|
+
|
|
2694
|
+
allSections.forEach(s => s.classList.remove('section-hidden'))
|
|
2695
|
+
|
|
2696
|
+
if (!q) return
|
|
2697
|
+
|
|
2698
|
+
/* Simple filter: show/hide sections based on text match */
|
|
2699
|
+
allSections.forEach(s => {
|
|
2700
|
+
const text = s.textContent.toLowerCase()
|
|
2701
|
+
const h2 = s.querySelector('h2')
|
|
2702
|
+
const h3s = Array.from(s.querySelectorAll('h3'))
|
|
2703
|
+
const headingMatch = (h2 && h2.textContent.toLowerCase().includes(q)) ||
|
|
2704
|
+
h3s.some(h => h.textContent.toLowerCase().includes(q))
|
|
2705
|
+
const contentMatch = text.includes(q)
|
|
2706
|
+
|
|
2707
|
+
if (!headingMatch && !contentMatch) {
|
|
2708
|
+
s.classList.add('section-hidden')
|
|
2709
|
+
} else if (headingMatch) {
|
|
2710
|
+
/* Highlight matching text in headings */
|
|
2711
|
+
highlightText(s, q)
|
|
2712
|
+
}
|
|
2713
|
+
})
|
|
2714
|
+
|
|
2715
|
+
/* Expand all open details in sidebar */
|
|
2716
|
+
document.querySelectorAll('.sidebar-nav details').forEach(d => d.open = true)
|
|
2717
|
+
})
|
|
2718
|
+
|
|
2719
|
+
function highlightText(container, q) {
|
|
2720
|
+
const walk = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, {
|
|
2721
|
+
acceptNode: n => n.textContent.trim() ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
|
|
2722
|
+
})
|
|
2723
|
+
|
|
2724
|
+
const nodes = []
|
|
2725
|
+
while (walk.nextNode()) nodes.push(walk.currentNode)
|
|
2726
|
+
|
|
2727
|
+
nodes.forEach(node => {
|
|
2728
|
+
const parent = node.parentNode
|
|
2729
|
+
if (parent.classList.contains('syn-cmt') ||
|
|
2730
|
+
parent.classList.contains('syn-doc') ||
|
|
2731
|
+
parent.classList.contains('syn-str') ||
|
|
2732
|
+
parent.classList.contains('syn-num') ||
|
|
2733
|
+
parent.closest('pre')) return
|
|
2734
|
+
|
|
2735
|
+
const text = node.textContent
|
|
2736
|
+
const idx = text.toLowerCase().indexOf(q)
|
|
2737
|
+
if (idx === -1) return
|
|
2738
|
+
|
|
2739
|
+
const span = document.createElement('span')
|
|
2740
|
+
span.className = 'search-highlight'
|
|
2741
|
+
span.textContent = text.slice(idx, idx + q.length)
|
|
2742
|
+
|
|
2743
|
+
const frag = document.createDocumentFragment()
|
|
2744
|
+
if (idx > 0) frag.appendChild(document.createTextNode(text.slice(0, idx)))
|
|
2745
|
+
frag.appendChild(span)
|
|
2746
|
+
if (idx + q.length < text.length) frag.appendChild(document.createTextNode(text.slice(idx + q.length)))
|
|
2747
|
+
|
|
2748
|
+
parent.replaceChild(frag, node)
|
|
2749
|
+
})
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2752
|
+
/* Clear search on Escape */
|
|
2753
|
+
document.addEventListener('keydown', e => {
|
|
2754
|
+
if (e.key === 'Escape') {
|
|
2755
|
+
searchInput.value = ''
|
|
2756
|
+
searchInput.dispatchEvent(new Event('input'))
|
|
2757
|
+
searchInput.blur()
|
|
2758
|
+
}
|
|
2759
|
+
/* Focus search on / or Ctrl+K */
|
|
2760
|
+
if ((e.key === '/' || (e.key === 'k' && (e.ctrlKey || e.metaKey))) &&
|
|
2761
|
+
document.activeElement !== searchInput) {
|
|
2762
|
+
e.preventDefault()
|
|
2763
|
+
searchInput.focus()
|
|
2764
|
+
}
|
|
2765
|
+
})
|
|
2766
|
+
|
|
2767
|
+
/* ── Responsive: manage sidebar on resize ─────────────────────────────────── */
|
|
2768
|
+
let mediaQuery = window.matchMedia('(min-width: 768px)')
|
|
2769
|
+
function handleResize(e) {
|
|
2770
|
+
if (e.matches) {
|
|
2771
|
+
closeSidebar()
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
mediaQuery.addEventListener('change', handleResize)
|
|
2775
|
+
</script>
|
|
2776
|
+
|
|
2777
|
+
</body>
|
|
2778
|
+
</html>
|