Spec M3 — globals, global arrays, strings, #include, #define, arena, data sections
Prerequisite: M2 closed and reviewed. Scope: everything a real program (the M4 tokenizer and later
mc.mc) needs in terms of static data and modularization.
Parser #
- Top level:
type name = CONST;(N_GLOBAL, a=init folded to N_INT),type name;,type name[CONST];(val=nelem). A non-constant initializer is an error. #include "path": relative to the directory of the including file; once-only per resolved path (linear list, insertion order). Implement as a stack of files in the lexer (push on include, pop on EOF). Errors showfile:lineof the correct file. Max depth 16.#define NAME expr: expr parsed and folded at definition time; linear table{name, val}. When the parser reads an identifier in an expression it checks the table before anything else and emits N_INT. Redefining = error.#definemust come before its use (source order).- String literals in expressions (N_STR: bytes + len in the arena).
Codegen #
- Sections created on demand, always in this order when present:
__TEXT,__text(align 2),__TEXT,__cstring(S_CSTRING_LITERALS, align 0),__DATA,__data(align 3),__DATA,__bss(S_ZEROFILL, align 4,zsize). - Globals: symbol
_namenot external; scalars in__data(initialized, type width) or in__bss(no initializer); arrays always in__bss, sizenelem*widthrounded to 16, aligned to 16. Access:adrp xd, _name@PAGE(R_PAGE21, pcrel=1, len=2) +add xd, xd, _name@PAGEOFF(R_PAGEOFF12, pcrel=0, len=2) thenldr/strwith the type's width. A global array as a value /&global= the address after theadd. - Strings: bytes + NUL in
__cstring; local symboll_str<N>(N sequential in order of first occurrence); dedup by content via linear search. Value = address via adrp/add, type uptr. - Name resolution in an expression: define (parser) → local → global → function/extern (for calls).
--dump-syms: sections (seg,sect,flags,size) and symbols in symtab final order, one per line.
Library #
lib/sys.mc:extern i64 open(uptr path, i64 flags, i64 mode); extern i64 read(i64 fd, uptr buf, i64 n); extern i64 write(i64 fd, uptr buf, i64 n); extern i64 close(i64 fd); extern void exit(i64 code);and#define O_RDONLY 0,#define O_WRONLY 1,#define O_CREAT 0x200,#define O_TRUNC 0x400(macOS values). Alsoputs(uptr s)(strlen + write) andputnum(i64), written in.mc.
Tests #
020-globals.mc: global counter incremented inside a function, global array u64[8] → 42.021-strings.mc:#include "../lib/sys.mc";puts("hello\n")→ stdouthello\n, exit 0.022-include.mc: includestests/lib/util.mc(create it) twice (once-only) and uses one of its functions.023-define.mc:#definewith a constant expression used both in an array size and in an expression → 42.024-arena.mc:u8 heap[4096]; i64 hp = 0; uptr alloc(i64 n); allocates two blocks,st64/ld64, checks alignment to 8 → 42.025-linecount.mc: openstests/025-linecount.mc(path relative to the repo root, wheretest.shruns), reads in a loop of 4096-byte chunks into a global buffer, counts\n, prints withputnum→ stdout = the number of lines in the file itself.
Acceptance #
make stage0 && make stage0-san && make budget && make test green (all M1–M3 tests);
otool -r and nm -m of the 021-strings .o showing l_str0 in __cstring and PAGE21/
PAGEOFF12 relocs; --dump-syms deterministic. Report lines per file and the total.