update to structs
This commit is contained in:
4
SPEC.md
4
SPEC.md
@@ -27,7 +27,7 @@ This document reflects the implementation that ships in this repository today (`
|
||||
- **Memory helpers** – `mem` returns the address of the `persistent` buffer (default 64 bytes). `argc`, `argv`, and `argv@` expose process arguments. `alloc`/`free` wrap `mmap`/`munmap` for general-purpose buffers, while `memcpy` performs byte-wise copies.
|
||||
- **BSS customization** – Compile-time words may call `bss-clear` followed by `bss-append`/`bss-set` to replace the default `.bss` layout (e.g., `tests/bss_override.sl` enlarges `persistent`).
|
||||
- **Strings & buffers** – IO helpers consume explicit `(addr len)` pairs only; there is no implicit NULL contract except for stored literals.
|
||||
- **Structured data** – `struct:` blocks expand into constants and accessor words (`Foo.bar@`, `Foo.bar!`). Dynamic arrays in `stdlib/arr.sl` allocate `[len, cap, data_ptr, data...]` records via `mmap` and expose `arr_new`, `arr_len`, `arr_cap`, `arr_data`, `arr_push`, `arr_pop`, `arr_reserve`, `arr_free`.
|
||||
- **Structured data** – `struct` blocks expand into constants and accessor words (`Foo.bar@`, `Foo.bar!`). Dynamic arrays in `stdlib/arr.sl` allocate `[len, cap, data_ptr, data...]` records via `mmap` and expose `arr_new`, `arr_len`, `arr_cap`, `arr_data`, `arr_push`, `arr_pop`, `arr_reserve`, `arr_free`.
|
||||
|
||||
## 5. Definitions, Control Flow, and Syntax Sugar
|
||||
- **Word definitions** – Always `word name ... end`. Redefinitions overwrite the previous entry (a warning prints to stderr). `inline word name ... end` marks the definition for inline expansion; recursive inline calls are rejected. `immediate` and `compile-only` apply to the most recently defined word.
|
||||
@@ -37,7 +37,7 @@ This document reflects the implementation that ships in this repository today (`
|
||||
- `n for ... end`; the loop count is popped, stored on the return stack, and decremented each pass. The compile-time word `i` exposes the loop index inside macros.
|
||||
- `label name` / `goto name` perform local jumps within a definition.
|
||||
- **Text macros** – `macro name [param_count] ... ;` records raw tokens until `;`. `$1`, `$2`, ... expand to positional arguments. Macro definitions cannot nest (attempting to start another `macro` while recording raises a parse error).
|
||||
- **Struct builder** – `struct: Foo ... ;struct` emits `<Foo>.size`, `<Foo>.field.size`, `<Foo>.field.offset`, `<Foo>.field@`, and `<Foo>.field!` helpers. Layout is tightly packed with no implicit padding.
|
||||
- **Struct builder** – `struct Foo ... end` emits `<Foo>.size`, `<Foo>.field.size`, `<Foo>.field.offset`, `<Foo>.field@`, and `<Foo>.field!` helpers. Layout is tightly packed with no implicit padding.
|
||||
- **With-blocks** – `with a b in ... end` rewrites occurrences of `a`/`b` into accesses against hidden global cells (`__with_a`). On entry the block pops the named values and stores them in those cells; reads compile to `@`, writes to `!`. Because the cells live in `.data`, the slots persist across calls and are not re-entrant.
|
||||
- **List literals** – `[ values ... ]` capture the current stack slice, allocate storage (`mmap`), copy the elements, and push the pointer. The record stores `len` at offset 0 and items afterwards so user code can fetch length via `@` and iterate.
|
||||
- **Compile-time execution** – `compile-time foo` runs `foo` immediately but still emits it (if inside a definition). Immediate words always execute during parsing; ordinary words emit `word` ops for later code generation.
|
||||
|
||||
22
main.py
22
main.py
@@ -3169,19 +3169,21 @@ PY_EXEC_GLOBALS: Dict[str, Any] = {
|
||||
def macro_struct_begin(ctx: MacroContext) -> Optional[List[Op]]:
|
||||
parser = ctx.parser
|
||||
if parser._eof():
|
||||
raise ParseError("struct name missing after 'struct:'")
|
||||
raise ParseError("struct name missing after 'struct'")
|
||||
name_token = parser.next_token()
|
||||
struct_name = name_token.lexeme
|
||||
fields: List[StructField] = []
|
||||
current_offset = 0
|
||||
while True:
|
||||
if parser._eof():
|
||||
raise ParseError("unterminated struct definition (missing ';struct')")
|
||||
raise ParseError("unterminated struct definition (missing 'end')")
|
||||
token = parser.next_token()
|
||||
if token.lexeme == ";struct":
|
||||
if token.lexeme == "end":
|
||||
break
|
||||
if token.lexeme != "field":
|
||||
raise ParseError(f"expected 'field' or ';struct' in struct '{struct_name}' definition")
|
||||
raise ParseError(
|
||||
f"expected 'field' or 'end' in struct '{struct_name}' definition"
|
||||
)
|
||||
if parser._eof():
|
||||
raise ParseError("field name missing in struct definition")
|
||||
field_name_token = parser.next_token()
|
||||
@@ -3220,11 +3222,6 @@ def macro_struct_begin(ctx: MacroContext) -> Optional[List[Op]]:
|
||||
parser.tokens[parser.pos:parser.pos] = generated
|
||||
return None
|
||||
|
||||
|
||||
def macro_struct_end(ctx: MacroContext) -> Optional[List[Op]]:
|
||||
raise ParseError("';struct' must follow a 'struct:' block")
|
||||
|
||||
|
||||
def macro_here(ctx: MacroContext) -> Optional[List[Op]]:
|
||||
tok = ctx.parser._last_token
|
||||
if tok is None:
|
||||
@@ -3244,8 +3241,7 @@ def bootstrap_dictionary() -> Dictionary:
|
||||
dictionary.register(Word(name="here", immediate=True, macro=macro_here))
|
||||
dictionary.register(Word(name="with", immediate=True, macro=macro_with))
|
||||
dictionary.register(Word(name="macro", immediate=True, macro=macro_begin_text_macro))
|
||||
dictionary.register(Word(name="struct:", immediate=True, macro=macro_struct_begin))
|
||||
dictionary.register(Word(name=";struct", immediate=True, macro=macro_struct_end))
|
||||
dictionary.register(Word(name="struct", immediate=True, macro=macro_struct_begin))
|
||||
_register_compile_time_primitives(dictionary)
|
||||
return dictionary
|
||||
|
||||
@@ -3602,7 +3598,7 @@ def run_repl(
|
||||
print(" :seteditor [cmd] show/set editor command (default from $EDITOR or vim)")
|
||||
print(" :quit | :q exit the REPL")
|
||||
print("[repl] free-form input:")
|
||||
print(" definitions (word/:asm/:py/extern/macro/struct:) extend the session")
|
||||
print(" definitions (word/:asm/:py/extern/macro/struct) extend the session")
|
||||
print(" imports add to session imports")
|
||||
print(" other lines run immediately in an isolated temp program (not saved)")
|
||||
print(" multiline: end lines with \\ to continue; finish with a non-\\ line")
|
||||
@@ -3752,7 +3748,7 @@ def run_repl(
|
||||
|
||||
block_stripped = block.lstrip()
|
||||
first_tok = block_stripped.split(None, 1)[0] if block_stripped else ""
|
||||
is_definition = first_tok in {"word", ":asm", ":py", "extern", "macro", "struct:"}
|
||||
is_definition = first_tok in {"word", ":asm", ":py", "extern", "macro", "struct"}
|
||||
is_import = first_tok == "import"
|
||||
|
||||
if is_import:
|
||||
|
||||
@@ -8,10 +8,10 @@ word test-mem-alloc
|
||||
4096 free # free the memory
|
||||
end
|
||||
|
||||
struct: Point
|
||||
struct Point
|
||||
field x 8
|
||||
field y 8
|
||||
;struct
|
||||
end
|
||||
|
||||
word main
|
||||
32 alloc # allocate 32 bytes (enough for a Point struct)
|
||||
|
||||
@@ -27,10 +27,10 @@ macro defadder 3
|
||||
defconst MAGIC 99
|
||||
defadder add13 5 8
|
||||
|
||||
struct: Point
|
||||
struct Point
|
||||
field x 8
|
||||
field y 8
|
||||
;struct
|
||||
end
|
||||
|
||||
word test-add
|
||||
5 7 + puti cr
|
||||
|
||||
Reference in New Issue
Block a user