update to structs

This commit is contained in:
IgorCielniak
2026-01-21 14:26:30 +01:00
parent 805f8e26a5
commit ff6af0b783
4 changed files with 15 additions and 19 deletions

View File

@@ -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
View File

@@ -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:

View File

@@ -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)

View File

@@ -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