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.
|
- **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
22
main.py
@@ -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:
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user