added function pointers
This commit is contained in:
1
SPEC.md
1
SPEC.md
@@ -43,6 +43,7 @@ This document reflects the implementation that ships in this repository today (`
|
||||
- `while <condition> do <body> end`; the conditional block lives between `while` and `do` and re-runs every iteration.
|
||||
- `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.
|
||||
- `&name` pushes a pointer to word `name` (its callable code label). This is intended for indirect control flow; `&name jmp` performs a tail jump to that word and is compatible with `--ct-run-main`.
|
||||
- **Text macros** – `macro name [param_count] ... ;` records raw tokens until `;`. `$0`, `$1`, ... expand to positional arguments. Macro definitions cannot nest (attempting to start another `macro` while recording raises a parse error).
|
||||
- **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.
|
||||
|
||||
61
main.py
61
main.py
@@ -742,6 +742,13 @@ class Parser:
|
||||
if self._try_literal(token):
|
||||
return
|
||||
|
||||
if token.lexeme.startswith("&"):
|
||||
target_name = token.lexeme[1:]
|
||||
if not target_name:
|
||||
raise ParseError(f"missing word name after '&' at {token.line}:{token.column}")
|
||||
self._append_op(Op(op="word_ptr", data=target_name))
|
||||
return
|
||||
|
||||
word = self.dictionary.lookup(token.lexeme)
|
||||
if word and word.immediate:
|
||||
if word.macro:
|
||||
@@ -1174,6 +1181,10 @@ class _CTVMJump(Exception):
|
||||
self.target_ip = target_ip
|
||||
|
||||
|
||||
class _CTVMReturn(Exception):
|
||||
"""Raised to return from the current word frame in _execute_nodes."""
|
||||
|
||||
|
||||
class _CTVMExit(Exception):
|
||||
"""Raised by the ``exit`` intrinsic to stop compile-time execution."""
|
||||
|
||||
@@ -1708,7 +1719,7 @@ class CompileTimeVM:
|
||||
self._execute_nodes(definition.body, _defn=definition)
|
||||
except CompileTimeError:
|
||||
raise
|
||||
except (_CTVMJump, _CTVMExit):
|
||||
except (_CTVMJump, _CTVMExit, _CTVMReturn):
|
||||
raise
|
||||
except ParseError as exc:
|
||||
raise CompileTimeError(f"{exc}\ncompile-time stack: {' -> '.join(self.call_stack)}") from None
|
||||
@@ -2341,6 +2352,9 @@ class CompileTimeVM:
|
||||
self.call_stack.pop()
|
||||
ip = jmp.target_ip
|
||||
continue
|
||||
except _CTVMReturn:
|
||||
self.call_stack.pop()
|
||||
return
|
||||
finally:
|
||||
if self.call_stack and self.call_stack[-1] == word.name:
|
||||
self.call_stack.pop()
|
||||
@@ -2365,6 +2379,8 @@ class CompileTimeVM:
|
||||
except _CTVMJump as jmp:
|
||||
ip = jmp.target_ip
|
||||
continue
|
||||
except _CTVMReturn:
|
||||
return
|
||||
ip += 1
|
||||
continue
|
||||
|
||||
@@ -2406,6 +2422,19 @@ class CompileTimeVM:
|
||||
except _CTVMJump as jmp:
|
||||
ip = jmp.target_ip
|
||||
continue
|
||||
except _CTVMReturn:
|
||||
return
|
||||
ip += 1
|
||||
continue
|
||||
|
||||
if kind == "word_ptr":
|
||||
target_name = str(node.data)
|
||||
target_word = _dict_lookup(target_name)
|
||||
if target_word is None:
|
||||
raise ParseError(
|
||||
f"unknown word '{target_name}' referenced by pointer during compile-time execution"
|
||||
)
|
||||
_push(self._handles.store(target_word))
|
||||
ip += 1
|
||||
continue
|
||||
|
||||
@@ -3002,7 +3031,7 @@ class Assembler:
|
||||
refs: Set[str] = set()
|
||||
if isinstance(definition, Definition):
|
||||
for node in definition.body:
|
||||
if node.op == "word":
|
||||
if node.op in {"word", "word_ptr"}:
|
||||
refs.add(str(node.data))
|
||||
edges[definition.name] = refs
|
||||
|
||||
@@ -3261,6 +3290,10 @@ class Assembler:
|
||||
self._emit_wordref(str(data), builder)
|
||||
return
|
||||
|
||||
if kind == "word_ptr":
|
||||
self._emit_wordptr(str(data), builder)
|
||||
return
|
||||
|
||||
if kind == "branch_zero":
|
||||
self._emit_branch_zero(str(data), builder)
|
||||
return
|
||||
@@ -3438,6 +3471,16 @@ class Assembler:
|
||||
else:
|
||||
builder.emit(f" call {sanitize_label(name)}")
|
||||
|
||||
def _emit_wordptr(self, name: str, builder: FunctionEmitter) -> None:
|
||||
word = self.dictionary.lookup(name)
|
||||
if word is None:
|
||||
suffix = f" while emitting '{self._emit_stack[-1]}'" if self._emit_stack else ""
|
||||
raise CompileError(f"unknown word '{name}'{suffix}")
|
||||
if getattr(word, "is_extern", False):
|
||||
builder.push_label(name)
|
||||
return
|
||||
builder.push_label(sanitize_label(name))
|
||||
|
||||
def _emit_branch_zero(self, target: str, builder: FunctionEmitter) -> None:
|
||||
builder.pop_to("rax")
|
||||
builder.emit(" test rax, rax")
|
||||
@@ -4279,8 +4322,18 @@ def _rt_exit(vm: CompileTimeVM) -> None:
|
||||
|
||||
|
||||
def _rt_jmp(vm: CompileTimeVM) -> None:
|
||||
target_ip = vm.pop_int()
|
||||
raise _CTVMJump(target_ip)
|
||||
target = vm.pop()
|
||||
resolved = vm._resolve_handle(target)
|
||||
if isinstance(resolved, Word):
|
||||
vm._call_word(resolved)
|
||||
raise _CTVMReturn()
|
||||
if isinstance(resolved, bool):
|
||||
raise _CTVMJump(int(resolved))
|
||||
if not isinstance(resolved, int):
|
||||
raise ParseError(
|
||||
f"jmp expects an address or word pointer, got {type(resolved).__name__}: {resolved!r}"
|
||||
)
|
||||
raise _CTVMJump(resolved)
|
||||
|
||||
|
||||
def _rt_syscall(vm: CompileTimeVM) -> None:
|
||||
|
||||
1
tests/word_ptr_jmp.expected
Normal file
1
tests/word_ptr_jmp.expected
Normal file
@@ -0,0 +1 @@
|
||||
via word ptr
|
||||
11
tests/word_ptr_jmp.sl
Normal file
11
tests/word_ptr_jmp.sl
Normal file
@@ -0,0 +1,11 @@
|
||||
import stdlib/stdlib.sl
|
||||
import stdlib/io.sl
|
||||
|
||||
word target
|
||||
"via word ptr\n" puts
|
||||
end
|
||||
|
||||
word main
|
||||
&target
|
||||
jmp
|
||||
end
|
||||
Reference in New Issue
Block a user