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. - **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`). - **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. - **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 ## 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. - **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. - `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. - `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). - **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. - **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. - **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. - **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]]: def macro_struct_begin(ctx: MacroContext) -> Optional[List[Op]]:
parser = ctx.parser parser = ctx.parser
if parser._eof(): if parser._eof():
raise ParseError("struct name missing after 'struct:'") raise ParseError("struct name missing after 'struct'")
name_token = parser.next_token() name_token = parser.next_token()
struct_name = name_token.lexeme struct_name = name_token.lexeme
fields: List[StructField] = [] fields: List[StructField] = []
current_offset = 0 current_offset = 0
while True: while True:
if parser._eof(): if parser._eof():
raise ParseError("unterminated struct definition (missing ';struct')") raise ParseError("unterminated struct definition (missing 'end')")
token = parser.next_token() token = parser.next_token()
if token.lexeme == ";struct": if token.lexeme == "end":
break break
if token.lexeme != "field": 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(): if parser._eof():
raise ParseError("field name missing in struct definition") raise ParseError("field name missing in struct definition")
field_name_token = parser.next_token() 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 parser.tokens[parser.pos:parser.pos] = generated
return None 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]]: def macro_here(ctx: MacroContext) -> Optional[List[Op]]:
tok = ctx.parser._last_token tok = ctx.parser._last_token
if tok is None: 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="here", immediate=True, macro=macro_here))
dictionary.register(Word(name="with", immediate=True, macro=macro_with)) 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="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_begin))
dictionary.register(Word(name=";struct", immediate=True, macro=macro_struct_end))
_register_compile_time_primitives(dictionary) _register_compile_time_primitives(dictionary)
return dictionary return dictionary
@@ -3602,7 +3598,7 @@ def run_repl(
print(" :seteditor [cmd] show/set editor command (default from $EDITOR or vim)") print(" :seteditor [cmd] show/set editor command (default from $EDITOR or vim)")
print(" :quit | :q exit the REPL") print(" :quit | :q exit the REPL")
print("[repl] free-form input:") 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(" imports add to session imports")
print(" other lines run immediately in an isolated temp program (not saved)") print(" other lines run immediately in an isolated temp program (not saved)")
print(" multiline: end lines with \\ to continue; finish with a non-\\ line") print(" multiline: end lines with \\ to continue; finish with a non-\\ line")
@@ -3752,7 +3748,7 @@ def run_repl(
block_stripped = block.lstrip() block_stripped = block.lstrip()
first_tok = block_stripped.split(None, 1)[0] if block_stripped else "" 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" is_import = first_tok == "import"
if is_import: if is_import:

View File

@@ -8,10 +8,10 @@ word test-mem-alloc
4096 free # free the memory 4096 free # free the memory
end end
struct: Point struct Point
field x 8 field x 8
field y 8 field y 8
;struct end
word main word main
32 alloc # allocate 32 bytes (enough for a Point struct) 32 alloc # allocate 32 bytes (enough for a Point struct)

View File

@@ -27,10 +27,10 @@ macro defadder 3
defconst MAGIC 99 defconst MAGIC 99
defadder add13 5 8 defadder add13 5 8
struct: Point struct Point
field x 8 field x 8
field y 8 field y 8
;struct end
word test-add word test-add
5 7 + puti cr 5 7 + puti cr