Version: Phase 45 Complete (v1.0 Release) Target Audience: AI Compiler Engineers & Human Maintainers Prime Directive: Read this document completely before making any changes. Do not assume features exist. Do not invent syntax. Adhere strictly to the implemented state.
G Language (file extensions .g / .gufran) is a universal systems language. It compiles pure human prose (English SVO, Bangla/Japanese SOV) into deterministic, formally verified, hyper-optimized code that speaks directly to any hardware: x86-64 and ARM64 (syscall assembly), AVR and ESP32 (raw MMIO with zero headers), and quantum processors (OpenQASM 2.0) — while retaining C99 as the default export for seamless integration with existing C ecosystems. It is designed to scale from bare-metal microcontrollers (AVR, ESP32) to desktop game engines, web servers, and quantum circuits, featuring zero syntax, automatic memory safety, and compile-time metaprogramming. The compiler is currently written in Rust, and the standard project tooling is the Cardinal Package Manager (cpm).
g-language/
├── .github/workflows/ release.yml — CI/CD: push a v* tag → matrix build
│ (ubuntu/windows/macos) → zip dist/ → GitHub Release
├── .gitignore ignores /target, /dist, and root scratch outputs (/*.c, /*.s, ...)
├── Cargo.toml workspace manifest (bins: g, gufran, cpm, apm, g-lsp)
├── Cargo.lock
├── LICENSE MIT license (SK GUFRAN AHMED)
├── README.md GitHub landing page
├── CHANGELOG.md the journey, Phase 1 → v1.0 (the Cardinal Release)
├── MANUAL.md this file — implementation manual & phase ledger
├── build_release.ps1 reproduces dist/ from source + TCC (Windows)
├── build_release.sh reproduces dist/ from source + TCC from source (Linux/macOS)
├── docs/ rfc-0001.md (grammar table and language charter)
├── examples/ G source: Stage 2-5 bootstrap compilers (lexer/parser/
│ validator/codegen.g), Stage 5 stdlibs (json.g, http.g,
│ server.g, api_server.g, web_app.g), build_script.g,
│ bootstrap.g (self-hosted C99 emitter), baremetal_*.g,
│ and demos (hello, counter, cli_tool, file_scan, ...)
├── runtime/ g_rt.h (cross-platform C99 runtime) + http_shim.c/.h (libcurl FFI shim)
│ + tcp_shim.h (header-only Winsock/socket shim) + cli_shim.h (CLI args/env)
│ + fs_shim.h (directory listing / file exists / current time)
│ + math_shim.h / regex_shim.h (Phase 41) + os_shim.h (Phase 44) + ws2_32.def
├── src/ Rust compiler core (lexer, parser, HIR/MIR/SSA, optimizer, C99 backends)
│ + gufran.rs, cpm.rs, apm.rs (bins), lsp.rs (g-lsp — Language Server
│ Protocol server, Phase 40 — completion/hover/goto-definition)
├── stdlib/ Standard Library modules in pure verse (net.g — TCP network abstraction;
│ os.g — process execution; fs.g — filesystem wrapper)
├── tests/ Integration tests (compile.rs, e2e.rs) + unit tests in src/
└── dist/ Standalone distribution template (Phase 31), git-ignored: g.exe + gufran.exe
+ cpm.exe/apm.exe + g-lsp.exe (Phase 40) + bundled TCC + runtime/ + stdlib/
Standalone dist/ layout:
dist/
├── g.exe (Rust-compiled G compiler)
├── gufran.exe (identical alias binary)
├── cpm.exe / apm.exe (Cardinal Package Manager, Phase 36)
├── g-lsp.exe (Language Server Protocol server, Phase 40 — completion/hover/goto-definition)
├── runtime/ (g_rt.h, cli_shim.h, fs_shim.h, tcp_shim.h, math_shim.h,
│ regex_shim.h, os_shim.h, ws2_32.def)
├── stdlib/ (net.g, os.g, fs.g — imported via `Import "stdlib/net.g"` etc.)
└── bin/ (bundled TCC: tcc.exe + libtcc.dll + include/ + lib/)
- Zero Syntax: The surface is human prose. There are NO brackets
{}, NO semicolons;, NO colons:, and NO indentation significance. - Implicit Scopes: Blocks (
If,For each,Define routine) are closed by narrative transitions (After.,Finally.,After processing...) or a period.. The keywordEndis forbidden (G-001 rejects it). EOF always closes (Phase 38): reaching the end of the file automatically closes every open routine, loop, conditional, record, and parallel block silently — no trailingFinally.orend.is ever required, and no G-071implicit scope closure assumeddiagnostic is emitted for EOF closures (G-071 still fires when a block is implicitly closed mid-file by the next statement). - Determinism: Identical input → identical output. Bit-for-bit reproducible builds via stable hashing, canonical symbol ordering, and platform-independent iteration.
- Safety: Memory safe, thread safe, null safe, overflow safe (where profile guarantees). Memory is scope-owned and freed on every exit path.
- No Assumptions: If a feature is not listed in this manual as "Complete," it does not exist. Do not use it in tests or generated code.
- Core Pipeline:
gCLI tool (Rust core). Lexer → Semantic Graph → HIR → Type Unification → Ownership/Capability Gates → MIR → SSA → Optimizer → C99 Backend. - Backends: Desktop (arena runtime), AVR (bare-metal ISR + MMIO), ESP32 (FreeRTOS).
- Logic & Data: Routines (
Define routine/Execute), Compound Conditionals (and/or/not), Strings (followed by/Length of), Lists (List of/Add to/Count of), Loops (Repeat N times/For each X in Y), Records (Define record), ADTs (Optional/Resultwithis present/value of). - Loop Control (Phase 32):
Break.exits the innermost enclosing loop;Continue.jumps to the next iteration. Both are legal only insideWhile/Repeat/For each(andForever/In parallel-free loop bodies); anywhere else is G-010"'break' is only allowed inside a loop"/"'continue' is only allowed inside a loop". Lowering is context-aware: insideRepeat/For each(which lower to real Cforloops) they emit Cbreak;/continue;; insideWhile/Forever(label/jump lowering) they resolve at MIR build time toJumpinstructions targeting the loop's back-edge/exit labels (an unreachable exit label is synthesized forForever, which otherwise has none). - Bare Return (Phase 32): a
Return.with no value is legal in any routine withoutreturning <Type>(i.e. void routines) and returns immediately; it lowers toSsaOp::Ret→goto g_done;(never writesg_ret). The old G-070 warning'return' requires a value to returnis gone. Type-checked:Return.in a routinereturning <Type>→ G-020"routine returns <T> but 'return' provides no value (use 'return <value>')";Return <expr>.in a void routine → G-020"routine returns no value but 'return' provides <T> (use a bare 'Return.')".Return.outside a routine is still G-010. - CLI Arguments & Environment (Phase 33): three new expressions —
Get argument count(→ Number, lowers tog_argc()),Get argument <N>(Number index → Text, lowers tog_argv(g_tN)),Get env "<NAME>"(Text name → Text, lowers tog_get_env(g_tN)). Parser: threeparse_unaryblocks dispatched on the exact wordsget argument/get env. Type-checked: a non-NumberGet argumentindex → G-020"Get argument requires a Number index, found <T>"; a non-TextGet envname → G-020"Get env requires a Text variable name, found <T>". Codegen: the Desktop (host) profile emits#include "cli_shim.h"(newruntime/cli_shim.h— included unconditionally on the host profile, likeg_rt.h) and itsmainis nowint main(int argc, char **argv)which seedsg_cli_argc/g_cli_argvglobals; the shim exposesg_argc()/g_argv(double)/g_get_env(g_str)as views into process-lifetime OS storage (no arena copy; out-of-range argument or missing env var → empty""). Game (SDL) and Web (Emscripten) profiles keepint main(void)and do not include the shim (using CLI ops there fails at C compile — embedded AVR/ESP32 likewise unchanged).--runforwarding (Phase 33):g file.g --run arg1 arg2now forwards everything after--runto the child —tcc -run file.c arg1 arg2(tcc natively passes them) and the compiled-.exepath alike; thetcc-apiin-memory path forwards them too (tcc.run(&argv)). - File System Operations & Date/Time (Phase 34): three new expressions —
List directory <Text>(→List of Text, lowers tog_list_dir(&arena, g_tN)),File exists <Text>(→ Flag/Bool, lowers to(g_file_exists(g_tN) != 0)),Current time(→ Number, lowers tog_time_now(), UNIX epoch seconds). Parser: threeparse_unaryblocks onlist directory/file exists/current time. Type-checked: a non-TextList directory/File existspath → G-020"List directory requires a Text path, found <T>"/"File exists requires a Text path, found <T>". Truthy conditions (Phase 34): a condition with no comparison phrase —If File exists "x" then,If B then— is now a truthy test:parse_cond_primaryre-parses the bare expression (previouslyparse_cond_lhsdiscarded any lhs without a following comparison operator, producing spuriousunrecognized conceptcascades),Cond::Truthyrequires the expression to type-check as Flag/Bool (else G-020"condition must be a Flag (Bool), found <T>"), and it lowers to!= 0.If File exists "x" is true thenstill parses as a plainCmpagainsttrue. Codegen:#include "fs_shim.h"is emitted only if the program uses one of the three ops (prog_uses_fsscan, same shape asprog_uses_file_io);runtime/fs_shim.his header-only (inline with--run), Windows usesFindFirstFileA/FindNextFileA+GetFileAttributesA, POSIX usesopendir/readdir+stat, time istime(NULL)from<time.h>; listing entries are arena-backedg_str(same pattern asg_str_split, so the generated epilogue'sg_vec_*_freeneeds no per-entry frees),.and..are skipped, and a missing/unreadable directory yields an empty vector (never NULL —For eachover it is safe,Count ofyields 0). Note the deviation from the directive's literal signature:g_list_dirtakesg_arena *afirst (asg_str_splitdoes) so entries live in the program arena instead of leaking per-process-heap strings. g_str_splitarity fix (Phase 34, found live): codegen previously emitted the 2-argument callg_str_split(g_tN, g_tN)whileg_rt.hdefines the 3-argumentg_str_split(g_arena *a, g_str s, g_str sep)— a latent Phase 26+ bug that made every Desktop program usingSplitfail to compile (cannot cast 'struct g_str' to 'struct g_arena *'). No test caught it because none executed Split end-to-end. Now emitted asg_str_split(&arena, g_tN, g_tN); new e2e testsplit_executes_end_to_endlocks it (printsa/b/c).- Statement connector
, and(Phase 35, self-hosting enabler): a comma-and connector allows chained clauses after a terminator —set Word to Execute TrimPeriod with Value of Tokens at I , if Word is Print then ... .Theandarm inparse_statementbumpsandand parses the next statement;, and if <cond> then <body>parses a nested if inline (sharing the enclosing block's closer, subject to the same deferral rules asOtherwise ifchains, including an explicitend.). Createexpression (Phase 35, self-hosting enabler):set Tokens to Create List of Text—parse_unaryoncreateacceptsa/anthenparse_payload_typeand returnsdefault_init(Expr::ListLit/TableLit).Create <Record>still takes the record-init path; both feed the existingNewList/NewTableMIR lowering.- Codegen bug fixed while bootstrapping (Phase 35, found live):
set Tokens to Create List of Textas the RHS of a set (not a standaloneCreate ... called ...) lowers throughStmt::Assign, whose list/table-literal special-case (NewList/NewTable) existed only inStmt::Decl— the SSA temp was emitted with no declaration and TCC failed withbootstrap.c:125: error: 'g_t9' undeclared. mir.rsStmt::Assignnow special-casesListLit/TableLitexactly likeStmt::Decl. - Cardinal Package Manager —
cpm/apm(Phase 36): a standalone package-manager CLI (src/cpm.rs;src/apm.rsis a one-lineinclude!("cpm.rs")alias bin; both added as[[bin]]targets and shipped indist/bybuild_release.ps1).cpm init <name>creates<name>/with an exact-formatcardinal.toml([package]/name/version = "0.1.0"/entry = "main.g") and amain.gthat printsHello from CPM;cpm runshellsdist/g.exe main.g --run;cpm buildshellsdist/g.exe main.g --out build/main.c. The compiler is resolved as<cpm-exe-dir>/g.exe(the dist/ layout) with ag-on-PATH fallback. cpm add <path>& local dependencies (Phase 37):cpm add <folder>reads the dependency'scardinal.toml([package]name), validates it, and records it in the current package'scardinal.tomlunder a new[dependencies]section (created if absent; an existing entry for the same name is replaced). Format:[dependencies]thenmy_lib = "path/to/my_lib"(backslashes normalized to/).cpm runandcpm buildnow parse[dependencies]and, for every entry, append-I <path>to theg.execommand line before--run/--out— the compiler's import resolver checks-Idirectories (tier 2) after the importing file's own directory, soImport "my_lib.g"resolves to<dep>/my_lib.g. The compiler-side plumbing:compile_full(source, base_dir, extra_imports, target, out_c)is the new lib.rs entry;compile_at/compile_for_targetdelegate with&[];locate_import/resolve_modules/resolve_importthreadextra_importsthrough both top-level and nested import resolution;g <file.g> -I <dir>(repeatable, also-gmode) is parsed by src/main.rs intoimport_dirs. Verified live:cpm addon a mylib package, thencpm runexecutingGreet with "CPM"from the dependency, andcpm buildwritingbuild/main.ccontaining the dependency's routine.--targetrouting (Phase 36):g <file.g> --target <t>(also-gmode) —c99(default, unchangedemit/emit_desktoppath) orbaremetal-x86/baremetal-arm64/baremetal-avr/baremetal-esp32/baremetal-quantum.src/main.rsparses the flag and calls the newcompile_for_target(lib.rs), which runs the full pipeline (lex/parse/types/ownership/capabilities/MIR/SSA/optimizer) and then routes codegen tocodegen::emit_baremetalinstead ofcodegen::emit. Unknown targets → G-072.- Universal & Quantum Bare-Metal backends (Phase 36):
codegen::emit_baremetal(target, prog, profile, board, graph)dispatches to five backends that need no C compiler and no headers:- x86-64 (
baremetal-x86, export.s): GNU as, Intel syntax, Linux System V ABI.Print "Hello"emits the canonical template —.rodatamsg: .ascii "Hello\n",_startwithmov rax,1; mov rdi,1; lea rsi,[msg]; mov rdx,len; syscallthenmov rax,60; xor rdi,rdi; syscall. Multiple prints emit one sys_write each (msg,msg0,msg1, …). Since Phase 39,_start:opens a 64-byte stack frame (push rbp/mov rbp, rsp/sub rsp, 64) and every variable lives in aqword [rbp-8],[rbp-16],[rbp-24], … slot (8 bytes each, unlimited count) —Set X to 42emitsmov qword [rbp-8], 42; the frame is torn down withmov rsp, rbp/pop rbpbeforesys_exit. Lengths arebytes + 1for the appended\n. - ARM64 (
baremetal-arm64, export.s): AArch64 template —mov x8,#64; mov x0,#1; ldr x1,=msg; mov x2,len; svc #0thenmov x8,#93; mov x0,#0; svc #0. Since Phase 42,_start:opens a stack frame (stp x29, x30, [sp, -16]!/mov x29, sp/sub sp, sp, 64) and every variable lives in a[sp, 16],[sp, 24],[sp, 32], … slot (8 bytes each, unlimited count; the first 16 bytes hold the saved frame pointer x29 / link register x30) —Set A to 1emitsmov x9, #1/str x9, [sp, 16]; the frame is torn down withadd sp, sp, 64/ldp x29, x30, [sp], 16beforesys_exit. This matches the x86-64 stack-frame architecture (Phase 39), so both major ISAs now support any number of variables with no.dataspill. - Bare-metal control flow (Phase 38) & stack frames (Phase 39 x86-64 / Phase 42 ARM64):
Whileloops andIfconditionals withplus/minusarithmetic run natively on x86-64 and ARM64. x86-64 variables are stack slots —While X is less than 5 do Set X to X plus 1.emits (inside the frame).L_start_1:/cmp qword [rbp-8], 5/jge .L_end_1(inverse jump) /inc qword [rbp-8]/ body /jmp .L_start_1/.L_end_1:;If X is greater than 5 then Set X to X minus 1.emitscmp qword [rbp-8], 5/jle .L_end_1/dec qword [rbp-8]/.L_end_1:. Variable-vs-variable comparisons and copies route the lhs/source through theraxscratch register (mov rax, qword [rbp-8]/cmp rax, qword [rbp-40]— x86 has no mem-memcmp/mov/add/subencodings). ARM64 uses the same stack-frame architecture (Phase 42) —Set X to 42emitsmov x9, #42/str x9, [sp, 16];While A is less than Eloads both slots into thex9/x10scratch registers before comparing (ldr x9, [sp, 16]/ldr x10, [sp, 48]/cmp x9, x10— AArch64 has no mem-memcmp);Set A to A plus 1emitsldr x9, [sp, 16]/add x9, x9, #1/str x9, [sp, 16]; the inverse branches areb.ge/b.le/b.gt/b.lt/b.ne/b.eqand loops jump withb. The emit-time label-resolution pass (emit_bm_flow/bm_cmp_line/print_label) names the loop-back/else targets.L_end_Nat the CondJump, the then-block label from the followingSsaOp::Label(skipped when the jump already fell through), and any remaining targets.L_Nas theJumpops are emitted; spurious labels fall away in the final sweep.slot_op(arch, slot)produces the operand for a slot —qword [rbp-N]on x86,[sp, 16+8*slot]on ARM64 — sobm_mov_imm/bm_inc/bm_dec/bm_add_imm/bm_sub_imm/bm_cmp/bm_mov_reg/bm_add_reg/bm_sub_regall operate on the same operand strings. - AVR (
baremetal-avr, export.c): raw MMIO C,/* BARE-METAL MMIO for AVR */, no#include.Set LED on.emits*(volatile unsigned char*)0x24 = 0x20;(DDRB) +*(volatile unsigned char*)0x25 = 0x20;(PORTB) +while(1) {}. Trigger: a slot namedLED(or aSetPin/TogglePinop); otherwise just the spin loop. - ESP32 (
baremetal-esp32, export.c): raw MMIO C —*(volatile unsigned int*)0x60004004 = 0x04000000;(GPIO_ENABLE_W1TS) +*(volatile unsigned int*)0x60004008 = 0x04000000;(GPIO_OUT_W1TS) +while(1) {}. Same LED trigger. - Quantum (
baremetal-quantum, export.qasm): OpenQASM 2.0 —OPENQASM 2.0; qreg q[1]; creg c[1]; h q[0]; measure q[0] -> c[0];(Set Qubit to superposition.maps to a Hadamard gate). - Backends extract
Printmessages (SsaOp::Print→Def→ConstStr→ string pool) and numeric assigns (StoreofConstNum→ slot name) from the SSA program; nested op bodies (Repeat/ForEach/Comptime/Parallel) are walked recursively.While/Ifneed no special walk — they lower to flatLabel/CondJump/Jumpops in the main stream, which the emit loop handles directly.
- x86-64 (
- Bare-metal
Targetwords &Set <x> on.(Phase 36, parser):parse_targetaccepts the modifierbaremetalplus arch words —x86_64/x86/x86-64/arm64/aarch64/quantum→Profile::Kernel,avr→ Embedded/boardavr,esp32→ Embedded/boardesp32— soTarget G Baremetal avr.compiles warning-free (the pipeline runs under the mapped profile;--targetpicks the backend).parse_setaccepts a bare trailingon/off(noto):Set LED on.→Assign { LED: Bool(true) }(previously G-070). The quantum example'sSet Qubit to superposition.parses via the silent prose fallback (string literal) — the quantum backend ignores statement content by design. - Modules & I/O:
Import "file.g",Read file,Write file(ReturnsResult, G-050 if unhandled). Import resolution (Phase 31) tries, in order: (1) the path next to the importing file (<file_dir>/<path>), (2) the directory holding thegexecutable (<g_exe>/<path>— the standalonedist/layout, soImport "stdlib/net.g"findsdist/stdlib/net.gfrom any working directory), (3) the repository root (<repo>/<path>— dev layout,cargo test/target/release/g). The same three tiers apply to nested imports (relative to the importing module's directory). G-072 if no tier yields a readable file. - Concurrency:
In parallel do,Spawn Task,Channel of,Send to,Receive from. (Move semantics G-011, Data race G-060). - ESP32: FreeRTOS tasks/queues, Wi-Fi/TCP HAL.
- Game & Web: ECS (
Create Entity,Add Component), WebAssembly (Emscripten). - Metaprogramming:
At compile timeblocks,Size of,Type of. - Advanced Data: Routine Returns (
returning <Type>), List Indexing/Mutation (Value of X at Y), Tables (Table of K to V), Text Ops (Split,Substring,contains), Table Iteration (For each Key in), Safe Lookups (ReturnsOptional<T>), String Escapes (\n,\t,\r,\",\\in string literals, resolved at tokenization and re-escaped as C literals by codegen). - Recursive Data & Enums: Recursive Records (forward typedefs), Custom Enums (
Define enumwithIf is <Variant>). - FFI:
Import C function,In unsafe context do ... End unsafe.(Note:End unsafeis the ONLY exception to the "no End keyword" rule). Calls useCall c function <name> with <expr>, <expr>.(comma-separated args). The Desktop backend emits#include <header>plus anextern <ret> <name>(<params>);declaration for each declared FFI function; the ESP32 backend does the same in its own emitter. - Running Generated C:
g <file.g> --run(or the identical aliasgufran <file.g> --run) locates the C compiler in this order: (1) a bundled TCC at<g_exe>/bin/tcc[.exe](or<g_exe>/../bin/tcc[.exe]when g sits insidebin/) — resolved viastd::env::current_exe()so it works from any working directory; (2)tcc/gcc/clang/ccon PATH (probed with--versionthen-v). Include paths are absolute and layout-aware: bundle mode passes-I <bundle>/runtime -I <bundle>/bin/include; repo mode passes-I <repo>/runtime. Platform link flags (Phase 30): Windows appends-L <runtime_dir> -lws2_32(the repo shipsws2_32.definruntime/, since the bundled TCC has nows2_32.lib/lib/ws2_32.def); Linux/macOS append-lpthread. G-072 if no compiler is found. - Language Server —
g-lsp(Phase 40):dist/g-lsp.exe(built fromsrc/lsp.rs, shipped bybuild_release.ps1) is a minimal Language Server Protocol server speaking JSON-RPC 2.0 over stdio withContent-Lengthframed messages — the same wire format VS Code expects. It reads framed messages from stdin (headersContent-Length: Nuntil a blank line, then exactly N body bytes; EOF exits cleanly), handlesinitialize(replies with{"capabilities":{"textDocumentSync":1}}— full-document sync),textDocument/didOpenandtextDocument/didChange(full text fromtextDocument/text, or incrementalcontentChanges[0].text),shutdown,exit, and respondsnullto unknown requests so clients never hang. On every open/change it runs the document throughg_lang::analyze_for_tooling(source, None)— the full diagnostic pipeline (parse, import resolution, type check, ownership, capabilities, MIR, SSA, optimizer) and the retainedSymbolTable, so tooling works even on documents with fatal errors, wherecompile_atreturnsErrand would discard the symbols — and converts the G-### diagnostics into atextDocument/publishDiagnosticsnotification: GSpans (1-based line/col) map to LSP 0-based ranges (start atline-1, col-1, one character wide), severities map 1=Error / 2=Warning / 3=Info, and each diagnostic carriescode(e.g.G-020),source: "g", and the message. Since Phase 40 it also serves IDE features from the symbol table:- Autocomplete (
textDocument/completion): the identifier under the cursor (word_at— the alphanumeric/underscore run containing the position, clamped to the document) filters the symbol table; variables return{"label": X, "kind": 6, "detail": "Number"}(CompletionItemKind::Variable), routines{"kind": 3}(CompletionItemKind::Function), and the core keywordsPrint,Set,If,While,For each,Targetare always offered ({"kind": 14},CompletionItemKind::Keyword), matched by their first token so typingForsuggestsFor each. Items are sorted alphabetically; an empty word (whitespace/past-EOF position) returns everything. - Hover (
textDocument/hover): looks the word up in the symbol table and returns Markdowncontents—**Variable:** X \n**Type:** Numberfor variables (type viaKind::name()),**Routine:** Countfor routines — ornullwhen the word is not a known symbol. - Goto-definition (
textDocument/definition): returns a singleLocation({"uri": ..., "range": ...}) whose 0-basedstartis theSpanwhere the variable was first declared (Set X to ...,Create/declarations,For eachiterators,Read file ... into X, routine parameters), one column wide. The first declaration wins, so later re-Sets keep pointing at the original site. - VS Code configuration: a trivial extension (
package.jsonwith a language server activation event) starts the server via{ "command": "dist/g-lsp.exe", "args": [] }in itscontributes.languages/vscode.languageserversection; with the G file open, squiggles appear under eachG-###error/warning on every keystroke (didChange) and on open (didOpen),Ctrl+Spaceoffers variables/routines/keywords, hovering shows the type, andF12jumps to the declaration. Verified end-to-end withexamples/completion_test.g(Target G Desktop. Set X to 42. Print X.): a live stdin session returned{"label":"X","kind":6,"detail":"Number"}for completion at theXofSet X to 42(0-based line 0, character 22),{"contents":{"kind":"markdown","value":"**Variable:** X\n**Type:** Number"}}for hover, and theLocation{"range":{"start":{"line":0,"character":18},...},"uri":"file:///completion_test.g"}for definition (the span of theSet X to 42statement); the directive's literal position (line 1, column 10) is past EOF and degrades gracefully — completion returns all symbols plus the six keywords, hover returnsnull.
- Autocomplete (
- HTTP (Stage 5 stdlib):
examples/http.gdeclaresg_http_get(Text → Text) fromruntime/http_shim.h; the shim (runtime/http_shim.c) performs a libcurl GET with follow-redirects and a 10s timeout, captures the body into a process-lifetime arena (identical representation to G's arena strings), frees all libcurl memory viacurl_easy_cleanup, and returns the body as a G Text. Bodies > 64 KiB return empty. - TCP Server (Phase 30 stdlib):
examples/server.gis a native TCP web server. It imports nineg_tcp_*functions fromruntime/tcp_shim.h— a header-only shim (definitions live in the header, so--runcompiles it inline with the generated program; no separate.cto link). On Windows the shim declares the minimal Winsock2 surface itself (WSAStartup,socket,bind,listen,accept,recv,send,closesocket) because the bundled TCC'swinapi/ships nowinsock2.h, and links them against ws2_32 via the repo'sruntime/ws2_32.defimport lib (tcc resolves DLL imports through.deffiles) plus-lws2_32. All GNumbers map to Cdouble, so every wrapper speaksdoubleand reports failure as-1.0.StartServer(PortNumber)runs WSAStartup → socket → bind(0.0.0.0:Port, htons byte-swapped manually) → listen(backlog 8) → an infiniteWhile 1 is 1 do ... end.accept loop that recv's the request into a 64 KiB static buffer,Prints it, repliesHTTP/1.1 200 OKwith aContent-Length: 11bodyHello world(real CRLF via the Phase 30\rescape), and closes. Verified end-to-end:dist\g.exe examples\server.g --runthen browsinghttp://localhost:8080returnsHello worldand the server log shows the rawGET / HTTP/1.1request. Note:g_tcp_bind's two Number parameters are namedPortandPortNumberin the import because the FFI extern declaration must not collide with the shim's internal helper names; C parameter names are cosmetic anyway. - Standard Library net.g (Phase 31):
stdlib/net.gis the first pure-verse Standard Library module — it encapsulates the Phase 30 TCP FFI so user code never touchesImport C functionorIn unsafe context(Quirk 4-style shim imports live only inside the module). It re-declares the nineg_tcp_*functions fromtcp_shim.hand exports five idiomatic routines:NetStart(Port)(Winsock init → socket → bind → listen; returns the listening descriptor or-1),NetAccept(Socket)(client descriptor or-1),NetReceive(Client)(the buffered request Text),NetSend(Client, Data), andNetClose(Socket). Modules declare noTarget(a spliced module's Target would be discarded anyway — the importing program's directive wins). On Windows the generated program linksws2_32via the-lws2_32flag andruntime/ws2_32.def(Phase 30); a server running under--runexecutes inside the tcc process (tcc -runruns in-process, so the LISTENING socket's PID istcc.exe, whose parent isg.exe) — since Phase 32, killingg.exe(Ctrl+C/Stop-Process) automatically killstcc.exevia the--runkill-on-close job object, freeing the port. - High-Level HTTP Framework in net.g (Phase 37): two new pure-verse routines on top of the five TCP routines, so a web app never touches raw request strings.
HttpSendResponse with Number called Client, Number called Status, Text called ContentType, Text called Body.builds the full response —"HTTP/1.1 " followed by Text of Status followed by "\r\nContent-Type: " followed by ContentType followed by "\r\nContent-Length: " followed by Text of Length of Body followed by "\r\n\r\n" followed by Body(real\r\nvia string-literal escapes) — and sends it withExecute NetSend with Client, H..HttpGetPath with Text called Request returning Text.extracts the path:Set Parts to Split Request by " ", thenIf Count of Parts is greater than 1 then Return Value of Parts at 1.(the 0-indexed second element ofGET /api HTTP/1.1→/api),Return "/".as the malformed-request fallback. Compiler bug fixed en route (found live): mir.rsexpr_kindhad noConcatarm, soSet H to "..." followed by ...inside a routine inferred the local asKind::Num— codegen emitteddouble g_s4 = 0; /* H */and assigned ag_strinto it, and TCC died witherror: invalid aggregate type for register loadat the first--run. AddedExpr::Concat(_, _, _) => Kind::Str(mir.rs, matching the existinginit_kindarm in ownership.rs). Locked by new compile tests:http_framework_path_parsing_and_response_codegen,http_framework_string_local_infers_str_kind,extra_import_dirs_resolve_cpm_dependency_imports. - Idiomatic Web App (Phase 37):
examples/web_app.g—Target G Desktop.+Import "stdlib/net.g".+NetStart with 8080,If Server is -1→ print failure + bareReturn., elseWhile 1 is 1 do:NetAccept→if Client is not -1thenNetReceive→HttpGetPath→if Path is "/api"→HttpSendResponse with Client and 200 and "application/json" and "{\"status\":\"ok\"}",otherwise→ 404"text/plain""Not Found"→NetClose. All statements are the directive's exact verse (comma connectors,and-joined arguments, narrative closers); the only deviations are the outerDefine routine main.scope (the language rejects a top-levelReturn.— G-010'return' is only allowed inside a routine— documented rule) and the dropped top-level trailingFinally.(unneeded since Phase 38 — EOF closes the routine silently). The server starts silently (it onlyPrints on bind failure) and binds 8080. Verified live:dist\g.exe examples\web_app.g --runbinds 8080;Invoke-WebRequest http://localhost:8080/api→200body{"status":"ok"};http://localhost:8080/other→404exception; server logs zero errors and survives both requests. - Print flushing (Phase 31):
g_print_strnow callsfflush(stdout)so server request logs appear immediately even when stdout is redirected to a file (block-buffered C stdout previously hid them until buffer fill/exit). - JSON (Stage 5 stdlib):
examples/json.gimplements a recursive-descent JSON parser in pure verse.ParseJson(Text →JsonValue) dispatches throughParseValue(string/number/bool/null/object/array),ParseObjectbuilds aTable of Text to JsonValueviaAdd K to Obj with value V,ParseArraybuilds aList of JsonValue,ParseStringscans quoted text with verbatim\xescape pass-through,ParseNumberscans-digits.digitsand constructsNumberVal of FloatviaNumber of Out, andSkipWsskips\n\t. Every parser routine returns a result record (ParseResult={JsonValue Payload, Number Next}/TextResult) carrying the value AND the next position — pure-G functional state threading (see Quirk 7).Describeprints anyJsonValuethrough an exhaustive variant chain, rendering numeric payloads viaText of value of V as NumberVal(i.e.g_format_num). - Number to Text Conversion (
Text of <Number>):Text of X(X Number or Float) formats a number into text and yields a Text. Lowers to the runtime helperg_format_num(g_tN, &arena)—snprintf(buf, 64, "%.17g", val)(round-trip safe, e.g.42→"42",3.5→"3.5") returning an arena-owned, NUL-terminatedg_str. Type-checked:Text of <non-Number>is G-020. - Text to Number Conversion (
Number of <Text>):Number of X(where X is Text) is an expression that parses the leading number of the string and yields a Number. Desktop/ESP32 lower it tostrtod(X.p, NULL); AVR usesatof(X.p)— both handle fractional text like"3.5"(Phase 26 replaced the Phase 25strtoll/atoi, which truncated at the decimal point). Safe because every arena string is NUL-terminated (g_arena_strwrites a terminator and reserveslen + 1bytes). Empty or non-numeric text yields 0; trailing junk is ignored. Type-checked:Number of <non-Text>is G-020. - Cross-Platform Runtime (Phase 27):
runtime/g_rt.hcompiles on Windows (_WIN32→windows.h+Sleep(ms)), Linux/macOS (__unix__/__APPLE__→unistd.h+usleep(ms * 1000)), AVR (__AVR__→ avr-libcutil/delay.h+_delay_loop_2; no POSIX headers), and ESP32 (ESP_PLATFORM→esp_timer.h+esp_timer_get_timebusy wait). The arena allocator and all string/vector/map operations use only C99stdio.h/stdlib.h/string.hand are fully OS-agnostic.
- Stage 1: Rust compiler core (Done).
- Stage 2:
examples/lexer.g— Tokenize routine in pure verse. (Done). - Stage 3:
examples/parser.g— ConceptNode synthesis from tokens. (Done). - Stage 4:
examples/validator.g— Semantic Validator consumingList of AstNode. (Done). - Stage 5:
examples/codegen.g— C99 emitter consumingList of AstNode. (Done). - Stage 6:
examples/bootstrap.g— a C99 emitter for a subset of G, written in pure verse, that compilesexamples/test_bootstrap.gand emitsoutput.c; the emitted C is then compiled with the bundled TCC and executed to printBootstrapSuccess. (Done — Phase 35. Moved from the repo root intoexamples/during the Phase 45 v1.0 folderization.)
dist\g.exe examples\bootstrap.g --out bootstrap.c— compiles the self-hosting emitter in pure verse: 0 diagnostics, exit 0.dist\bin\tcc.exe -I dist\runtime bootstrap.c -o bootstrap.exe— builds the standalone emitter..\bootstrap.exe examples\test_bootstrap.g— printsbootstrap: input examples\test_bootstrap.gthenbootstrap: wrote output.cexactly once, exit 0.output.ccontainsmain()withdouble x = 42;,if (x == 42) {+printf("%s\n", "BootstrapSuccess");+}— correct block closure.dist\bin\tcc.exe -I dist\runtime output.c -o test_output.exethen.\test_output.exe→ printsBootstrapSuccess, exit 0.
examples/bootstrap.g supports Set, Print, If, and end tokens in its input subset and generates double variable declarations, if guards, and printf lines. It exercises the Phase 35 language surface itself: the , and if statement connector, the create expression (set Tokens to Create List of Text), for each over nested splits, while loops with narrative After. closers, Otherwise if ... Otherwise chains, Execute <routine> with <args> calls, and the deferred-closer rule below.
READ THIS CAREFULLY.
Host Toolchain Note (updated Phase 45): G is fully standalone. dist/ bundles the working TCC (tcc.exe + libtcc.dll — tcc.exe dynamically links it, a missing DLL aborts with 0xC0000135 — + include/ + lib/) plus runtime/ with g_rt.h, cli_shim.h (Phase 33 — CLI arguments & env), fs_shim.h (Phase 34 — file system & time), tcp_shim.h, math_shim.h + regex_shim.h (Phase 41), os_shim.h (Phase 44 — process execution & file size), and ws2_32.def. build_release.ps1 reproduces dist/ from cargo build --release (the cargo call runs via cmd /c so TCC's stderr progress can't trip PS 5.1's $ErrorActionPreference = "Stop"): on this machine it copies the TCC install at C:\Users\Laptop\tcc\tcc; on CI/fresh machines (no such folder) it downloads the official tcc-0.9.27-win64-bin.zip from Savannah and expands it. build_release.sh (Linux/macOS) mirrors the script and compiles TCC 0.9.27 from source (wget/curl the tarball, ./configure && make, copy tcc + include/ + lib/ into dist/bin/); on Apple Silicon the TCC build fails (0.9.27 has no darwin-arm64 target) and the script warns instead of failing - g --run then falls back to tcc/gcc/clang on PATH (macOS ships clang). Verified: with the system TCC folder renamed off PATH, dist\g.exe examples\json.g --run compiles and executes using only the bundle (G, true, 3.5) — and dist\g.exe examples\server.g --run serves http://localhost:8080 (Hello world in a browser). The tcc-api in-memory feature still fails at link time (ld: cannot find -ltcc via x86_64-w64-mingw32-gcc) and is unused; the subprocess path is used instead. The e2e --run tests run for real when a compiler is on PATH and skip otherwise. Windows/TCC has no <regex.h> (probed: tcc -E on #include <regex.h> → error: include file 'regex.h' not found) — regex_shim.h therefore ships a POSIX regcomp/regexec implementation for non-Windows and a bounded fallback on Windows (see Phase 41 ledger).
--run child cleanup (Phase 32): on Windows, --run now spawns the child (tcc.exe -run, or the compiled .exe) via Command::spawn and immediately assigns it to a kill-on-close Windows Job Object (CreateJobObjectW + JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, raw kernel32 FFI in src/main.rs jobctl module — no external crates). The job handle lives only in g.exe, so whenever g.exe dies — Ctrl+C, Stop-Process, or crash — the OS terminates every process in the job. A --run TCP server therefore can no longer orphan its tcc.exe (which holds the LISTENING socket in-process, see Phase 31 note) and leave port 8080 stuck. Verified live: start dist\g.exe examples\api_server.g --run, GET /api → 200, Stop-Process the g.exe → tcc.exe dies within 2s and Get-NetTCPConnection -LocalPort 8080 -State Listen returns 0 immediately; a fresh --run rebinds the port right away (TIME_WAIT entries from test clients remain for a few minutes but never block the rebind).
- No pointer types: G's type system (
number,float,string,bool,pin) cannot expressvoid *,CURL *, orconst char *; Text maps to theg_strstruct passed by value. - No variadics: FFI arity is exact (G-020
"FFI function '{}' takes {} argument(s), got {}"), and every called FFI function must declarereturning <Type>(G-020"FFI function '{}' does not return a value"otherwise). - Raw libcurl is not callable: declaring
curl_easy_init/curl_easy_setopt/curl_easy_perform/curl_easy_cleanupvia FFI would emitextern long long ...prototypes that conflict with the real declarations incurl/curl.h(and varargs/write-callbacks are unexpressible).examples/http.gtherefore wraps libcurl inruntime/http_shim.c(a C shim with correct prototypes), which the directive explicitly permits ("or use a wrapper"). Call C functionis an expression: it must appear as an Assign RHS (Set X to Call c function ...); a bareCall c function curl_easy_cleanup with X.statement would fail typechecking since void-returning FFI calls are rejected.- Routine calls in an Assign RHS need
Execute/Call:Set Page to Get with Url.(no keyword) silently parses to the string literal"Get with Url"via the prose fallback — always writeSet Page to Execute Get with Url.
- Every enum variant MUST declare
of <Type>:Define enum E with A of Text and B of Text.— a payload-less variant is rejected withG-070 variant 'NullVal' requires 'of <Type>'and its recovery truncates the rest of the enum declaration (later variants silently vanish → cascading G-010 unknown-variant errors).NullVal of Text(payload unused, tag checkIf V is NullValstill works) is the workaround.
If V is StringVal then ... end.on a multi-variant enum fails withG-010 non-exhaustive match on enum 'JsonValue': variant 'X' not covered. Every enum test needs a fullOtherwise If V is <next> ...chain ending in a plainOtherwise(the checker recurses only when each Otherwise holds exactly oneIfon the same variable). This forces full chains even when only one branch is meaningful.
- Routine environments are seeded with parameters + module channels only (types.rs
check_program). A module-levelPos is 0.is invisible inside a routine (G-010 undeclared identifier 'Pos'), and by-value records/tables mutated on a param do not propagate back to the caller (struct copies; heap reallocs invalidate the caller's view). Shared mutable state across recursive routines is therefore IMPOSSIBLE; examples/json.g threads state functionally: every parse routine takes(Src, At)and returns(Payload, Next)records.
parse_bodyreturnsNoneon an unrecoverable parse failure andSome(())at EOF. Since Phase 38, EOF mid-block is NOT an error: reaching the end of the file implicitly closes every open scope (finish_blockreturns silently when the cursor is at EOF, before the G-071 lookup;parse_bodyreturnsSome(())at EOF so?-carrying callers likeparse_while/parse_ifkeep the fully-parsed statement instead of dropping it). Mid-file implicit closure (a block followed by an unrelated statement, e.g.Define record User with Number called Age.followed by aPrint) still emits G-071implicit scope closure assumed at end of <block>.- Rule: a
.gfile may now simply run to the end — the finalAfter./Finally./end.is optional. Closers are still required between blocks, exactly as in Quirk 8.
- An
Otherwise if ... Otherwise ...chain is a single statement; only the chain's rootifowns the block closer. A chain defersfinish_blockso the enclosing block's closer (After./Finally./end.) closes the whole chain:For each X in Y do If A then ... Otherwise If B then ... Otherwise ... After.— theAfter.closes theFor each, and the chain root never closes itself. - Exception (the fix that made Stage 6 work): if the token after the chain is an explicit
end., the rootifmust consume it (deferral applies only to narrative transitions). Implemented asif !chained || self.at_word("end") { finish_block }inparse_if, and the same guard inparse_otherwise_body's chain-link branch and the, and ifarm. - Subtle trap (found while fixing validator.g):
finish_blockon a lowercaseendmust also consume the trailing.(self.accept_sym('.')). Otherwise a chain link that consumed itsendleaves a dangling.token, and the enclosing chain root'sat_word("end")lookahead fails — the for-each/routine then steals the wrongend., producingG-071 dangling 'end',G-070 unrecognized concept 'Finally', andG-010 'return' is only allowed inside a routineon sources with per-levelend.closers (e.g.examples/validator.g).
- Main slots are ordered by first-use, not declaration order. Tests must not assume slot indices (
g_s0,g_s1) map directly to the order ofCreatestatements in source.
g_str_split,g_str_substring,g_str_containsare Desktop-only. On AVR/ESP32, they emit compile-safe stubs. Do not use them in logic that must execute on Micro profiles.
- Routine arguments parsed via
with <expr>greedily consume trailing conjunctions likefollowed by.Execute FileSize with TccPath followed by " bytes"binds the concatenation INSIDE the argument — the generated C passesconcat(TccPath, " bytes")as the Path (found live in Phase 44:TCC exists. Size: -1). This is correct greedy natural-language parsing, not a bug: the argument is a full expression up to the statement terminator. To pass a complex concatenated expression as an argument, assign it to an intermediate variable first (e.g.Set SizeStr to Text of Size followed by " bytes". Execute Print with SizeStr.).
Task: Prepare the repository for a public v1.0 GitHub release: folderize into a clean open-source structure, add licensing, a changelog, a polished landing page, and verify the dist/ folder as a pristine standalone artifact.
- Folderization (done): root cleaned of scratch outputs (
baremetal_*.s/.c,quantum.qasm,_space.gdeleted; generatedapi_server.c/json.c/validator.c/web_app.cremoved fromexamples/);bootstrap.gmoved from the repo root intoexamples/(Stage 6 ledger updated with the new paths)..gitignoreupdated:/dist, root-scoped scratch patterns (/*.c,/*.s,/*.qasm, …) so tracked sources underruntime//examples/stay trackable. The final layout matches the directive's tree exactly (docs/rfc-0001.mdwas MISSING — the README/MANUAL referenced it but the file did not exist — and has been created from the implemented grammar table + language charter; see item 4). - Licensing (done):
LICENSE— MIT,Copyright (c) 2026 SK GUFRAN AHMED(the authoritative file created in GitHub Desktop; the interim "Gufran / Cardinal System" draft was superseded by it, and the README credit line was aligned to the real holder); README links it and states it in the header. - Changelog (done):
CHANGELOG.md—[1.0.0] - The Cardinal Release, grouped per the directive: Core Compiler (Phases 1-5), Concurrency & ECS (6-8), Metaprogramming (9-10), Data Structures & Stdlib (14-15, 24, 41, 44, 44.1), Bare-Metal Revolution (36, 38, 39, 42, 43), Tooling & IDE (22, 29, 30, 40), Self-Hosting (35), Infrastructure. - README overhaul (done): new landing page — ASCII architecture diagram (
Prose → Lexer → Semantic Graph → HIR → Type Unification → Ownership Gates → MIR → SSA → Optimizer → C99 / x86-64 / ARM64 / OpenQASM), status banner (Phase 45 Complete (v1.0 Release), 545 tests, MIT), Quick Start via thedist/folder, a 19-item feature checklist, the folderized repository tree, example programs, CLI reference, and diagnostics.docs/rfc-0001.mdcreated (grammar table + charter) to satisfy the mandated layout and the existing MANUAL/README references. - Standalone verification (done):
build_release.ps1regenerateddist/; system TCC was already absent from PATH (Get-Command tcc→ none) anddist\g.exe examples\build_script.g --runused only the bundle:Found 29 files(32 before folderization: -4 generated.cartifacts, +1bootstrap.gmoved intoexamples/) /TCC exists. Size: 23552 bytes/tcc version 0.9.27 (x86_64 Windows)/TCC exit code: 0, exit 0.dist/listing confirmed:g.exe,gufran.exe,cpm.exe,apm.exe,g-lsp.exe,bin/,runtime/,stdlib/. - Tests & docs (done): no test path changes were required (no
.rsreference tobootstrap.gor other moved files;include_str!paths are relative).cargo build --releasezero warnings; 545 tests green (337 unit + 14 g-lsp + 185 compile + 9 e2e). MANUAL version →Phase 45 Complete (v1.0 Release); repo layout sections rewritten for the folderized state. - Cross-platform build scripts (done):
build_release.sh(Linux/macOS) mirrorsbuild_release.ps1:cargo build --release, assemblesdist/(g, gufran, cpm, apm, g-lsp + runtime shims + stdlib), then downloadstcc-0.9.27.tar.bz2from Savannah and compiles it (./configure && make) intodist/bin/(tcc + include/ + lib/). On Apple Silicon the TCC build fails (0.9.27 has no darwin-arm64 target) and the script warns and continues —g --runfalls back totcc/gcc/clangon PATH (macOS ships clang).build_release.ps1gained a CI path: whenC:\Users\Laptop\tcc\tccis absent it downloads the officialtcc-0.9.27-win64-bin.zipand expands it (recursivetcc.exesearch tolerates the bundle's internal layout), so GitHub Actions windows-latest needs no pre-installed TCC. - GitHub Actions CI/CD (done):
.github/workflows/release.yml— triggers onv*tags (plusworkflow_dispatch), matrix[ubuntu-latest, windows-latest, macos-latest],dtolnay/rust-toolchain@stable, runs the platform build script, zipsdist/(Compress-Archiveon Windows,zipon Unix), and uploads the zip to the GitHub Release viasoftprops/action-gh-release@v2(permissions: contents: write;fail_on_unmatched_files: truecatches a missing zip). Pushing tagv1.0.0therefore produces three release assets:g-language-ubuntu-latest.zip,g-language-windows-latest.zip,g-language-macos-latest.zip.
Task: Expand the standard library beyond networking and math: stdlib/os.g (run shell commands, query file sizes) and stdlib/fs.g (directory listing, existence) as pure-verse routines over a new header-only C shim, verified through a build-script example that inspects the real TCC bundle.
- Shim (done):
runtime/os_shim.h— header-only, external-linkage (the generated program emitsextern double ...AFTER the include, Phase 41 contract):double g_system(g_str cmd)runs the command viasystem()and returns the exit code as a double (the FFI maps G'sNumberto Cdouble);double g_file_size(g_str path)returns the byte size viaFindFirstFileAon Windows (nFileSizeHigh/Low) andstat()on POSIX,-1.0when the file is missing. Strings are normalised to NUL-termination through a private arena (same pattern asregex_shim.h; generated literals are arena-backedg_str). Windows cmd.exe handling (Phase 44.1):/→\rewriting, and commands containing spaces get their executable path quoted — the longest prefix ending at a space that names an existing non-directory file (the whole command is also a candidate, so a spaced exe with no arguments works). Wrapping the WHOLE command in one pair of quotes does NOT work: cmd.exe /c strips the outer quotes and re-splits at the first space ("C:\Program Files\app.exe -v"fails; verified empirically throughsystem()and directcmd /c). No existing file found → the first token is quoted (cmd builtins likeecho). Commands already starting with"pass through untouched; POSIX passes through unmodified (sh tokenises differently; quoting would break~//globs). - Stdlib (done):
stdlib/os.g—Import C function g_system from "os_shim.h" with Text called Cmd returning Number.+g_file_size ... with Text called Path returning Number., wrapper routinesRunCommand with Text called Cmd returning Number.andFileSize with Text called Path returning Number.using the provenIn unsafe context do Set R to Call c function ... with Cmd. After. Return R. Finally.shape.stdlib/fs.g—ListDir with Text called Path returning List of Text.(Return List directory Path. Finally.) andExists with Text called Path returning Flag.(Return File exists Path. Finally.) — pure wrappers over the Phase 34 builtins. The directive'sAfter.(instead ofEnd unsafe.) parses cleanly as the unsafe-scope closer. - Example & verification (done):
examples/build_script.g— the directive's build-script verse: importsstdlib/os.g+stdlib/fs.g,ListDirsexamples/, checksdist/bin/tcc.exeexists, prints its real byte size, runsdist/bin/tcc.exe -v, prints the exit code (exact file content below).dist\g.exe examples\build_script.g --run(TCC-run, real execution) prints:Found 29 files(32 before the Phase 45 folderization removed 4 generated.cartifacts fromexamples/and movedbootstrap.gin) /TCC exists. Size: 23552 bytes/tcc version 0.9.27 (x86_64 Windows)/TCC exit code: 0, exit 0. One correction to the directive's verse was required (live find):Text of Execute FileSize with TccPath followed by " bytes"bindsfollowed byINSIDE thewithargument (the generated C passesconcat(TccPath, " bytes")as the Path →-1) — the concatenation must be split:Set TccSize to Execute FileSize with TccPath.thenPrint "TCC exists. Size: " followed by Text of TccSize followed by " bytes".Also, the non-TCC--runpath keeps-lm(Phase 41); no new link flags are required forstat()/system()(msvcrt). - Toolchain (done):
build_release.ps1copiesruntime/os_shim.hintodist/runtime/(the stdlib glob already shipsos.g/fs.gintodist/stdlib/).src/main.rsneeded no changes:run_c_via_subprocess/run_generated_calready pass-I <runtime>(bundle mode), and FFI shim includes follow the Phase 41 contract —#include <os_shim.h>+externare emitted whenos.gis imported, whether or not the wrapper is called. - Tests & docs (done): 4 new compile tests —
os_fs_wrappers_lower_to_shim_calls(os_shim.h include, all fourg_r_*wrappers,g_system(g_t,g_file_size(g_t,g_file_exists(g_t,g_list_dir(&arena, g_t),os_shim_emitted_on_import_even_if_os_routine_unused(documents the import-not-callsite include rule),os_routine_parameter_must_be_text(G-020argument to routine 'RunCommand' should be String, found Number),build_script_example_compiles_clean.cargo build --releasezero warnings; 544 tests green (337 unit + 14 g-lsp + 184 compile + 9 e2e — prior 540 + 4). - Phase 44.1 (blocker resolution, done): (a) FFI
#includededuplication — newemit_ffi_declarations(out, prog)helper (src/codegen.rs) tracks emitted headers in aHashSet<String>so each header appears once per compilation unit (extern declarations still per function); used by both the Desktop and ESP32 emitters; locked byos_shim_emitted_once_despite_two_ffi_imports_from_same_header(asserts exactly one#include <os_shim.h>). (b) Auto-quoting for spaced commands (above). (c) Quirk 9 documents the greedywith <expr>argument rule (found live in Phase 44:Text of Execute FileSize with TccPath followed by " bytes"passed the concatenation as the Path →-1).cargo build --releasezero warnings; 545 tests green (337 unit + 14 g-lsp + 185 compile + 9 e2e).
Task: Map G's When and Every constructs directly to bare-metal hardware interrupts — no RTOS framework on AVR (raw avr-libc ISRs), native FreeRTOS task scheduling on ESP32 — making G an IoT-first language.
- Parser & HIR (done):
src/parser.rs—Every <N> ms do <action>.(parse_every_timer, dispatched when the token aftereveryis a Number, before the generic-binding fallback) lowers to the existingStmt::Event { EventSpec::TimerMs { ms } }handler pipeline (seconds/secondunits multiply by 1000;ms/milliseconds/doare optional words), so the action flows through MIR → SSA →prog.handlersunchanged.When interrupt occurs/When the interrupt occurs(parse_event_spec) parses to the newEventSpec::Interrupt(src/hir.rs), a top-level handler, not sequential code. Parser tests:parse_every_ms_lowers_to_timer_event,parse_every_seconds_lowers_to_ms,parse_when_interrupt_occurs,parse_when_the_interrupt_occurs. - AVR timers & interrupts (done):
emit_avr_mmio(src/codegen.rs) — forEvery <N> ms do,main()configures Timer0 in CTC mode beforewhile(1):TCCR0A = (1 << WGM01);/TCCR0B = (1 << CS02) | (1 << CS00);(1024 prescaler, 16 MHz → 15625 Hz) /OCR0A = (N*15625-500)/1000;(approximate; 10 ms → 155, exactly the directive's value) /TIMSK0 = (1 << OCIE0A);, thensei();. The action body becomes a real avr-libc handlerISR(TIMER0_COMPA_vect) { ... }(emitted aftermain). ForWhen interrupt occurs:EICRA = (1 << ISC01);/EIMSK = (1 << INT0);/sei();andISR(INT0_vect) { ... }. Event programs gain#include <avr/io.h>/#include <avr/interrupt.h>(pure pin programs stay header-free, Phase 36 style) plus static slot declarations so Emitter-emitted stores compile.Set LED on/offinside a body lowers to the hardware line (PORTB |=/~(1 << PB5)— pin 13, matching the Phase 36 MMIO convention) instead of the dead led-slot store. The MIR handler terminatorSsaOp::Retis filtered (nog_donelabel on bare metal). The Embedded-Arduino profile also gainedWhen interrupt occurs→ INT0 (avr_event_vector), and Embedded-ESP32 skips it deterministically with a comment. - ESP32 FreeRTOS tasks (done):
emit_esp32_mmio—Every <N> ms doemits#include "freertos/FreeRTOS.h"+#include "freertos/task.h", the action lifted intovoid g_task_1(void* arg) { while(1) { <body> vTaskDelay(pdMS_TO_TICKS(<N>)); } }, andvoid app_main(void)(the ESP-IDF entry point) starts it withxTaskCreate(g_task_1, "g_task_1", 2048, NULL, 1, NULL);plus a keep-alivefor (;;) { vTaskDelay(pdMS_TO_TICKS(1000)); }.Set LED onin the body writes GPIO2's raw MMIO write-to-set register (0x60004008), off uses the write-to-clear (0x6000400C). Programs without timers keep the Phase 36int main(void)shape verbatim. - Examples & verification (done):
examples/baremetal_avr_isr.g(Target G Baremetal avr. Every 10 ms do Set LED on.) andexamples/baremetal_esp32_task.g(Target G Baremetal esp32. Every 10 ms do Set LED on.) — pure verse, no trailing closers.dist\g.exe examples\baremetal_avr_isr.g --target baremetal-avr --out baremetal_avr_isr.c(exit 0): the emitted C containsTCCR0A = (1 << WGM01);,TCCR0B = (1 << CS02) | (1 << CS00);,OCR0A = 155;,TIMSK0 = (1 << OCIE0A);,sei();andISR(TIMER0_COMPA_vect) {/PORTB |= (1 << PB5);(full file pasted in the Phase 43 report).dist\g.exe examples\baremetal_esp32_task.g --target baremetal-esp32 --out baremetal_esp32_task.c(exit 0): contains#include "freertos/FreeRTOS.h",void g_task_1(void* arg) {/while(1) {/vTaskDelay(pdMS_TO_TICKS(10));andxTaskCreate(g_task_1, "g_task_1", 2048, NULL, 1, NULL);inapp_main.When interrupt occursverified separately:EICRA = (1 << ISC01);/EIMSK = (1 << INT0);/sei();/ISR(INT0_vect) { ... }. - Tests & docs (done): 4 new compile tests —
baremetal_avr_every_timer_emits_ctc_isr(includes, all four config lines, OCR0A=155, sei, ISR body, nogoto g_done),baremetal_avr_when_interrupt_emits_int0_isr(EICRA/EIMSK/sei/ISR),baremetal_esp32_every_timer_emits_freertos_task(includes, task function, vTaskDelay, xTaskCreate, nogoto g_done),baremetal_esp32_without_timer_keeps_phase36_main(Phase 36 shape preserved) — plus the 4 parser unit tests.cargo build --releasezero warnings; 540 tests green (337 unit + 14 g-lsp + 180 compile + 9 e2e — prior 532 + 8).
Task: Bring the x86-64 stack-frame architecture to the ARM64 (AArch64) backend so any number of variables works on Apple Silicon, Raspberry Pi, and ARM servers — no C compiler needed.
- Stack frame (done):
emit_arm64_asm(src/codegen.rs) opens_start:with the standard AArch64 prologuestp x29, x30, [sp, -16]!(save frame pointer + link register, pre-indexed 16-byte push) /mov x29, sp/sub sp, sp, 64(64-byte locals area), and the sharedemit_bm_flowepilogue tears the frame down withadd sp, sp, 64/ldp x29, x30, [sp], 16beforemov x8, #93(sys_exit).slot_op(BmArch::Arm64, slot)maps slot N to[sp, 16 + 8*N]— the first 16 bytes below the locals area are the saved x29/x30, so variables land at[sp, 16],[sp, 24],[sp, 32], … — unlimited variables, no.dataspill.bm_mov_imm(Set A to 1→mov x9, #1/str x9, [sp, 16]),bm_inc/bm_add_imm(Set A to A plus 1→ldr x9, [sp, 16]/add x9, x9, #1/str x9, [sp, 16]), andbm_cmp(mem-mem →ldr x9, [sp, 16]/ldr x10, [sp, 48]/cmp x9, x10; mem-imm →ldr x9, [sp, 16]/cmp x9, #5) all route memory operands through the x9/x10 scratch registers, mirroring the x86rax-routing of Phase 39. - Example & verification (done):
examples/baremetal_arm_stack.g— the directive's verse verbatim, one line, no trailing closers:Target G Baremetal arm64. Set A to 1. Set B to 2. Set C to 3. Set D to 4. Set E to 5. While A is less than E do Set A to A plus 1. Print "Looping".dist\g.exe examples\baremetal_arm_stack.g --target baremetal-arm64 --out baremetal_arm_stack.swrites the prologue (stp x29, x30, [sp, -16]!/mov x29, sp/sub sp, sp, 64), the five storesmov x9, #1..#5/str x9, [sp, 16]…[sp, 48], the loop with.L_start_1:/ldr x9, [sp, 16]/ldr x10, [sp, 48]/cmp x9, x10/b.ge .L_end_1/ldr x9, [sp, 16]/add x9, x9, #1/str x9, [sp, 16]/ sys_writeLooping\n/b .L_start_1/.L_end_1:and the epilogueadd sp, sp, 64/ldp x29, x30, [sp], 16/sys_exit(full.spasted in the Phase 42 report). Exit 0, zero diagnostics. - Tests & docs (done):
baremetal_arm64_loop_emits_canonical_asmmigrated from the Phase 38 register asserts (cmp x19, #5,add x19, x19, #1) to stack asserts (ldr x9, [sp, 16]+cmp x9, #5;ldr x9, [sp, 16]/add x9, x9, #1/str x9, [sp, 16]); newbaremetal_arm64_stack_frame_five_varslocks the prologue (stp x29, x30, [sp, -16]!/mov x29, sp/sub sp, sp, 64), all five[sp, 16]–[sp, 48]stores with their constants, the mem-mem compare, the scratch-register increment, the epilogue (add sp, sp, 64/ldp x29, x30, [sp], 16), and epilogue-before-sys_exitordering. Dead code removed for the new architecture (spilled_nums— no more.dataspill — and the unusedlabel_of).cargo build --releasezero warnings; 532 tests green (333 unit + 176 compile + 9 e2e — prior 531 + 1).
Task: Grow the standard library beyond networking: stdlib/math.g (Sin, Cos, Floor, Random) and stdlib/regex.g (RegexMatch, RegexExtract) as C FFI shims, verified through a data-pipeline example.
- Shims (done):
runtime/math_shim.h— external-linkage wrappers (the generated program emitsextern double g_math_sin(double);AFTER the include, sostatic/inlinewould violate C 6.2.2):sin/cos/floorfrom<math.h>,g_math_random(void)=rand() / (RAND_MAX + 1.0)(deterministic — msvcrt's fixed seed, good for tests).runtime/regex_shim.h—int g_regex_match(g_str, g_str)andg_str g_regex_extract(g_str, g_str, double): on non-Windows,regcomp(REG_EXTENDED)/regexecwithregmatch_tcapture (group(int)index, empty result for no-match/unused-group/bad-pattern,regfreeon every path). On Windows/TCC (no<regex.h>):g_regex_match=strstrsubstring search (pattern treated literally — documented limitation), andg_regex_extract= a small anchored capture engine supporting literal runs,.,[^X]/[X]single-char classes with+/*, and(...)groups, searching from each start position (POSIX-regexec semantics); anything outside that subset returns an empty string. Strings are normalized (NUL-terminated viag_arena_str) and results live in a private 64KBg_regex_arenainside the shim. - Deviation from the directive's 4-arg prototype (documented): the directive specified
g_str g_regex_extract(g_arena *a, g_str pattern, g_str text, double index); the G FFI boundary cannot pass the arena —SsaVal::FfiCallemitsname(g_t0, g_t1, g_t2)with exactly the declared parameters and the generatedexternmirrors the G declaration (codegen.rs ~3905). A 4-arg C function would be a compile error.g_regex_extracttherefore takes exactly the three G-declared parameters and uses the internal arena. - Stdlib (done):
stdlib/math.g+stdlib/regex.gfollow the provenhttp.gpattern —Import C function <name> from "<shim>.h" with <Type> called <Param>, … returning <Type>., wrapper routines withSet R to Call c function … .insideIn unsafe context do … End unsafe.thenReturn R., closed byFinally.. Two language additions were required:FlagjoinedType::from_wordas aBoolsynonym (the directive'sreturning Flagpreviously parsed asRecord("Flag")→ G-070), andRegexMatch's import declaresreturning Flag(Cint), NOTreturning Numberas written in the directive — aNumber-typed FFI result cannot beReturn-ed from aFlag-returning routine (G-010routine returns Bool but 'return' provides Number). The routine parameter is namedTextper the directive (with Text called Text); type position is resolved by the type vocabulary before expression resolution, so the name works. - Example & verification (done):
examples/data_pipeline.g—Import "stdlib/math.g".+Import "stdlib/regex.g"., extractgufran.devfromhttps://gufran.dev/pathwithRegexExtract with "https://([^/]+)/", Raw, 1, printRandom→Sin→Floor, and aRegexMatchIf/Else. Two corrections to the directive's sample were required: (a) bare routine calls in expressions are prose —Set Domain to RegexExtract with …silently becomes the STRING"RegexExtract with …"(Quirk 4,Set Page to Get with Url.behaves identically) — every call is writtenExecute <name> with …; (b) the directive's argument order (with Raw, "https://([^/]+)/", 1) is positional, so the pattern-first callwith "https://([^/]+)/", Raw, 1is required forgufran.dev; (c)Set Random to Random.self-shadows (LHS is declared before the RHS resolves) → renamedSet Chance to Random.; (d) capitalizedEnd.is rejected in pure verse (G-001) → the If block closes withAfter., the routine withFinally.. Live run ofdist\g.exe examples\data_pipeline.g --run: printsgufran.dev, a sine value (e.g.0.00125122),0(floor), andNo Match— the last is EXPECTED on Windows/TCC: the strstr fallback treatshttps://([^/]+)/literally, it is not a substring of the URL, soRegexMatchis 0; on POSIX the same program printsMatch. - Toolchain (done):
--run's gcc/clang path now links-lm(after the source file, correct link order; TCC needs none — msvcrt hassin/cos/floor).build_release.ps1copies both shims intodist/runtime/; the existing stdlib glob shipsmath.g/regex.gintodist/stdlib/. - Tests & docs (done): 4 new unit tests in
src/lib.rs—flag_is_bool_type_synonym(regression for theFlagkeyword),stdlib_math_imports_and_emits_ffi(asserts#include <math_shim.h>,extern double g_math_sin,extern double g_math_floor,g_math_sin(in the emitted C),stdlib_regex_imports_and_emits_ffi(#include <regex_shim.h>,extern int g_regex_match— the Flag→int contract,extern g_str g_regex_extract, both calls emitted), anddata_pipeline_example_compiles(compilesexamples/data_pipeline.gend-to-end with import resolution).cargo build --releasezero warnings; 531 tests green (333 unit + 175 compile + 9 e2e — prior 527 + 4).
Task: Make G a modern IDE language — the compiler must retain a symbol table as it type-checks (variables, routine parameters, routines), and g-lsp must serve autocomplete, hover tooltips, and goto-definition from it, even on documents with errors.
- Symbol table (done): new
src/symbols.rs—SymbolTable(name →SymbolInfo { span: Span, kind: SymbolKind }), withSymbolKind::Variable(Kind)andSymbolKind::Routine; first declaration of a name wins.types::check_programdelegates tocheck_program_with_symbols(&Program, &mut SymbolTable), and the table is populated at every declaration site the checker walks:Stmt::Decl(Create X …),Stmt::Assignwhen the name is new (Set X to 42→Variable(Number)),For eachiterators (element type for Lists,Strotherwise),Read file … into X(Str), routine parameters (typedVariable; HIR params carry no per-parameter span, so they point at the routine header span), and everyRoutineDef(SymbolKind::Routine).CompileOutputgainspub symbols: SymbolTable, andcompile_at/compile_fullpopulate it. Newg_lang::analyze_for_tooling(source, base_dir) -> (Vec<Diagnostic>, SymbolTable): the full diagnostic pipeline (parse, imports, type check, ownership, capabilities, MIR, SSA, optimizer) but returns diagnostics AND symbols —compile_atreturnsErr(and discards symbols) on fatal errors, so the LSP usesanalyze_for_toolingto serve tooling on broken documents too. - LSP features (done):
lsp.rsgains anLspServer { docs: HashMap<uri, Doc{source, symbols}> }state —didOpen/didChangeanalyze the document once and cache the symbol table, and the three requests serve from the cache (no recompile per keystroke).word_at(source, line, character)extracts the identifier under the cursor (the alphanumeric/underscore run containing the 0-based position, clamped; past-EOF → empty).textDocument/completion→{"isIncomplete": false, "items": [...]}withlabel/kind/detail: variableskind6 (CompletionItemKind::Variable,detail=Kind::name(), e.g.Number), routineskind3 (Function,detail"routine"), core keywordsPrint/Set/If/While/For each/Targetkind14 (Keyword, matched by first token soFor→For each); prefix = the word under the cursor (empty word → everything), items sorted alphabetically.textDocument/hover→{"contents": {"kind": "markdown", "value": "**Variable:** X\n**Type:** Number"}}(routines:**Routine:** Count); unknown words →nullresult.textDocument/definition→ singleLocation{"uri", "range"}from the symbol's declarationSpan(1-based → 0-based, one column wide). - Example & verification (done):
examples/completion_test.g— the directive's file verbatim:Target G Desktop. Set X to 42. Print X.Live stdin session againstdist/g-lsp.exe(initialize + didOpen + completion/hover/definition at 0-based (0, 22) — theXofSet X to 42): completion →{"id":1,...,"items":[{"detail":"Number","kind":6,"label":"X"}]}; hover →{"id":2,...,"contents":{"kind":"markdown","value":"**Variable:** X\n**Type:** Number"}}; definition →{"id":3,...,"range":{"end":{"character":19,"line":0},"start":{"character":18,"line":0}},"uri":"file:///completion_test.g"}(theSet X to 42statement span). The directive's literal position (line 1, column 10) is past EOF (the file is one line): completion degrades gracefully to all symbols + the six keywords, hover returnsnull. - Tests & docs (done): 7 new LSP unit tests —
word_at_cursor_extracts_identifier,completion_returns_variable_and_keywords(X kind 6 + all six keywords kind 14; prefix filtering to exactly one item),completion_returns_routines_as_functions(Define routine Count with Number called x returning Number:→ Count kind 3, x kind 6),hover_returns_markdown_with_type,hover_unknown_word_returns_null,definition_returns_location_span(line 1),definition_of_routine_points_at_header— plus e2elsp_serves_completion_hover_and_definition_on_stdin(openscompletion_test.gcontent, fires all three requests, asserts"id":1+"label":"X"+"kind":6,"id":2+**Variable:** X+**Type:** Number,"id":3+ thefile:///completion.gLocation at line 0).cargo build --releasezero warnings; 527 tests green (343 unit + 175 compile + 9 e2e).
Task: Give developers real-time diagnostics in VS Code via a Language Server Protocol server, and expand the bare-metal x86-64 backend from three registers to a full stack frame so any number of variables works — proving G is a true systems language.
- Language Server (done): new
[[bin]]g-lsp(src/lsp.rs) — a zero-dependency LSP server (onlyserde_jsonadded to Cargo.toml) readingContent-Length-framed JSON-RPC from stdin.read_messageparses header lines until the blank line, thenread_exact's exactly N body bytes (EOF → clean exit).initializeresponds{"capabilities":{"textDocumentSync":1}};textDocument/didOpen+textDocument/didChangeextract the document text (textDocument/text, orcontentChanges[0].textfor changes), callg_lang::compile_at(text, None, None), and emit atextDocument/publishDiagnosticsnotification — GSpan(1-based line/col) → LSP 0-based range, severities 1/2/3,code= G-###,source: "g". Unknown requests getnullresponses (clients never hang);shutdown/exit/stdin-EOF terminate.build_release.ps1shipsdist/g-lsp.exe. - Bare-metal stack frames (done):
emit_x86_asmopens every_start:withpush rbp/mov rbp, rsp/sub rsp, 64 # Allocate 64 bytes for local variablesand closes withmov rsp, rbp/pop rbpbeforesys_exit(also on the empty-program path).slot_regbecameslot_op: x86 slots map toqword [rbp-8],[rbp-16],[rbp-24], … (8 bytes each — no more three-register limit, no.dataspill on x86); ARM64 keepsx19/x20/x21+.dataspill.Set X to 0→mov qword [rbp-8], 0;inc/dec/add/suboperate on the stack operand in place (inc qword [rbp-8]); memory-to-memorycmp/mov/add/sub(x86 has no such encodings) route through theraxscratch register —While A is less than Eemitsmov rax, qword [rbp-8]/cmp rax, qword [rbp-40].walk_opsalready covers the backend-relevant nesting —While/Iflower to flatLabel/CondJump/Jumpin the main op stream, soemit_bm_flow's linear pass (whichwalk_ops-driven collectors feed) handles them directly. - Stack example (done):
examples/baremetal_stack.g— the directive's verse verbatim, no trailing closers:Target G Baremetal x86_64. Set A to 1. Set B to 2. Set C to 3. Set D to 4. Set E to 5. While A is less than E do Set A to A plus 1. Print "Looping".—dist\g.exe examples\baremetal_stack.g --target baremetal-x86 --out baremetal_stack.swrites the full prologue,[rbp-8]…[rbp-40]stores, the rax-routed compare,inc qword [rbp-8], and the epilogue (pasted in the Phase 39 report). - Verify (done):
g-lsp.exefed the directive's payload (Content-Length: 149— the directive's claimed 115 is short by 34 bytes —didOpenwith"Set X to .") responds with apublishDiagnosticsnotification carrying G-071 infono Target directive found; assuming Desktop profile; a genuine error document (Target G Desktop.\nReturn 5.) produces a severity-1G-010(locked by unit tests). - Tests & docs (done): x86 bare-metal tests migrated from register asserts to stack asserts (
baremetal_x86_set_num_emits_register_move→baremetal_x86_set_num_emits_stack_store; loop/if tests now assertcmp qword [rbp-8], 5,inc/dec qword [rbp-8]); newbaremetal_x86_stack_frame_five_varslocks prologue/epilogue/offsets/epilogue-before-sys_exit; 7 LSP unit tests (framing, severity mapping, initialize capabilities, didOpen/didChange notifications, prose-fallback info) + e2elsp_serves_publish_diagnostics_on_stdin(spawnsg-lsp.exe, writes a framed payload, closes stdin, asserts the publishDiagnostics frame).cargo build --releasezero warnings; 519 tests green (336 unit + 175 compile + 8 e2e).
Task: Let .g files end naturally (no trailing closers) and give the bare-metal backends real control flow (While/If with plus/minus arithmetic on x86-64 and ARM64).
- Implicit EOF scoping (done):
parse_bodyreturnsSome(())at EOF (wasNone, whichparse_while/parse_ifpropagated through?— a file that ran out of closers silently dropped entire loops: the loop example compiled to an EMPTY body, and the SSA only emittedStore { tmp: 0, slot: 0 });finish_blockreturns silently when the cursor is at EOF, before the G-071implicit scope closure assumedlookup. Mid-file implicit closure still reports G-071 (locked by the newparse_record_midfile_implicit_end_infounit test; the oldparse_record_implicit_end_infoEOF variant becameparse_record_eof_closed_silently). Example:examples/baremetal_loop.ghas no trailing closer and compiles clean. - Bare-metal control flow (done):
emit_x86_asm/emit_arm64_asmnow callemit_bm_flow(arch, ops, prog)withslot_regmapping —rbx/rcx/rdx(x86) andx19/x20/x21(ARM64). While/If lower to flatLabel/CondJump/Jumpin the SSA stream;bm_cmp_line+bm_inverse_jccemit the condition,print_labelresolves.L_start_N/.L_end_N/.L_Nnames at emit time (loop-back and else targets named at the CondJump; the then-label taken from the followingLabelop when the jump falls through; remaining labels named by theJumpops; the final sweep drops unreferenced labels).bm_math(add/sub) foldsplus/minusintoinc/dec/add/subon the slot register. Verified x86 output matches the directive's template exactly (.L_start_1:/cmp rbx, 5/jge .L_end_1/inc rbx/jmp .L_start_1/.L_end_1:); ARM64 verified (cmp x19, #5/b.ge .L_end_1/add x19, x19, #1/b .L_start_1) after fixing a##5double-hash (bm_cmp stripped the#prefix bm_imm already produced). The if-test (Set X to 10. If X is greater than 5 then Set X to X minus 1.) emitscmp rbx, 5/jle .L_end_1/dec rbx/.L_end_1:and executes correctly. - Examples cleaned (done): every example now ends naturally at EOF — trailing closers removed from
examples/api_server.g,cli_tool.g,codegen.g,counter.g,http.g,json.g,lexer.g,server.g,validator.g,web_app.g(plus repo-rootbootstrap.g). All 19 compilable.gexamples compile exit 0 (bare-metal ones with their--target);lexer.gwas also fixed — its finalFor each Token in Tokenswas a top-level statement referencing the routine-localTokens(pre-existing G-010, present before Phase 38); it now lives inside theTokenizeroutine body and is closed by EOF.examples/test_bootstrap.gis deliberately untouched: it is the input to thebootstrap.exesubset emitter, which needs theendtoken to emit the closing}.examples/baremetal_loop.gcreated from the directive's verse:Target G Baremetal x86_64. Set X to 0. While X is less than 5 do Set X to X plus 1. Print "Looping". - Verify (done):
dist\g.exe examples\json.g --runprintsG,true,3.5;dist\g.exe examples\baremetal_loop.g --target baremetal-x86 --out baremetal_loop.swrites the canonical.s(pasted in the Phase 38 report). - Tests & docs (done): the Phase 36 test
baremetal_x86_set_num_emits_data_quadbecamebaremetal_x86_set_num_emits_register_move(variables are registers now); new compile tests lock the bare-metal x86 and ARM64 loop assembly, the bare-metal if-minus flow, EOF-closedWhile/routine/nested blocks (no G-071), and mid-file implicit closure (both statements still emitted); new unit tests cover the record EOF/mid-file G-071 split.cargo build --releasezero warnings; 510 tests green (329 unit + 174 compile + 7 e2e).