commit
This commit is contained in:
20
SPEC.md
20
SPEC.md
@@ -25,6 +25,7 @@
|
|||||||
- `lookup`: resolves token → word entry; can be replaced to build new namespaces or module systems.
|
- `lookup`: resolves token → word entry; can be replaced to build new namespaces or module systems.
|
||||||
- **Compile vs interpret**: Each word advertises stack effect + immediacy. Immediate words execute during compilation (macro behavior). Others emit code or inline asm.
|
- **Compile vs interpret**: Each word advertises stack effect + immediacy. Immediate words execute during compilation (macro behavior). Others emit code or inline asm.
|
||||||
- **Syntax morphing**: Provide primitives `set-reader`, `with-reader`, and word-lists so layers (e.g., Lisp-like forms) can be composed.
|
- **Syntax morphing**: Provide primitives `set-reader`, `with-reader`, and word-lists so layers (e.g., Lisp-like forms) can be composed.
|
||||||
|
- **Inline Python hooks**: `:py name { ... } ;` executes the enclosed Python block immediately, then registers `name` as a word whose behavior is provided by that block. Define a `macro(ctx)` function to intercept compilation (receiving a `MacroContext` with helpers like `next_token`, `emit_literal`, `new_label`, `inject_tokens`, and direct access to the active parser), and/or an `intrinsic(builder)` function to emit custom assembly. This lets end users extend the language—parsing source, manipulating AST nodes, or writing NASM—without touching the bootstrap source. The standard library’s `extend-syntax` and `fn` forms are ordinary `:py` blocks built with these APIs, so users can clone or replace them entirely from L2 source files.
|
||||||
|
|
||||||
## 4. Core Types & Data Model
|
## 4. Core Types & Data Model
|
||||||
- **Cells**: 64-bit signed integers; all stack operations use cells.
|
- **Cells**: 64-bit signed integers; all stack operations use cells.
|
||||||
@@ -49,6 +50,24 @@ struct: Point
|
|||||||
- `<Struct>.<field>! ( value addr -- )` stores a field via `addr + offset !`.
|
- `<Struct>.<field>! ( value addr -- )` stores a field via `addr + offset !`.
|
||||||
- Because the output is plain L2 code, users can inspect or override any generated word, and additional helpers (e.g., pointer arithmetic or iterators) can be layered on top with regular macros.
|
- Because the output is plain L2 code, users can inspect or override any generated word, and additional helpers (e.g., pointer arithmetic or iterators) can be layered on top with regular macros.
|
||||||
|
|
||||||
|
### 4.2 Lightweight C-style Sugar
|
||||||
|
|
||||||
|
- `extend-syntax` is implemented as a `:py` macro that toggles a reader mode where identifiers suffixed with `()` (e.g., `foo()`) are rewritten as ordinary word calls. The call still obeys data-stack calling conventions; the parentheses are purely syntactic sugar.
|
||||||
|
- The same user-defined macro stack unlocks a compact function form:
|
||||||
|
|
||||||
|
```
|
||||||
|
fn add(int left, int right){
|
||||||
|
return (left + right) * right;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
expands into a normal colon definition which consumes two stack arguments (`left` and `right`), mirrors them onto the return stack, evaluates the infix expression, and cleans up the temporary frame before returning.
|
||||||
|
- Current limitations:
|
||||||
|
- Only `int` parameters are recognized.
|
||||||
|
- Function bodies must be a single `return <expr>;` statement. `<expr>` may contain parameter names, integer literals, parentheses, and the binary operators `+ - * / %`.
|
||||||
|
- Parameter names become available by index via `rpick`, so advanced bodies can still drop into raw L2 code if needed.
|
||||||
|
- Since the generated code uses the return stack to store arguments, it happily composes with loops/conditionals—the frame lives beneath any subsequent `for` counters and is explicitly released before the word returns. Because `fn` lives in user space, nothing stops you from swapping it out for a completely different parser (pattern matching, keyword arguments, etc.) using the same `:py` facility.
|
||||||
|
|
||||||
## 5. Stacks & Calling Convention
|
## 5. Stacks & Calling Convention
|
||||||
- **Data stack**: Unlimited (up to memory). Manipulated via standard words (`dup`, `swap`, `rot`, `over`). Compiled code keeps top-of-stack in registers when possible for performance.
|
- **Data stack**: Unlimited (up to memory). Manipulated via standard words (`dup`, `swap`, `rot`, `over`). Compiled code keeps top-of-stack in registers when possible for performance.
|
||||||
- **Return stack**: Used for control flow. Directly accessible for meta-programming; users must avoid corrupting call frames unless intentional.
|
- **Return stack**: Used for control flow. Directly accessible for meta-programming; users must avoid corrupting call frames unless intentional.
|
||||||
@@ -102,6 +121,7 @@ struct: Point
|
|||||||
|
|
||||||
## 14. Standard Library Sketch
|
## 14. Standard Library Sketch
|
||||||
- **Core words**: Arithmetic, logic, stack ops, comparison, memory access, control flow combinators.
|
- **Core words**: Arithmetic, logic, stack ops, comparison, memory access, control flow combinators.
|
||||||
|
- **Return-stack helpers**: `>r`, `r>`, `rdrop`, and `rpick` shuffle values between the data stack and the return stack. They’re used by the `fn` sugar but also available to user code for building custom control constructs.
|
||||||
- **Meta words**: Reader management, dictionary inspection, definition forms (`:`, `:noninline`, `:asm`, `immediate`).
|
- **Meta words**: Reader management, dictionary inspection, definition forms (`:`, `:noninline`, `:asm`, `immediate`).
|
||||||
- **Allocators**: Default bump allocator, arena allocator, and hook to install custom malloc/free pairs.
|
- **Allocators**: Default bump allocator, arena allocator, and hook to install custom malloc/free pairs.
|
||||||
- **FFI/syscalls**: Thin wrappers plus convenience words for POSIX-level APIs.
|
- **FFI/syscalls**: Thin wrappers plus convenience words for POSIX-level APIs.
|
||||||
|
|||||||
730
build/test.asm
Normal file
730
build/test.asm
Normal file
@@ -0,0 +1,730 @@
|
|||||||
|
section .text
|
||||||
|
%define DSTK_BYTES 65536
|
||||||
|
%define RSTK_BYTES 65536
|
||||||
|
%define PRINT_BUF_BYTES 128
|
||||||
|
global _start
|
||||||
|
_start:
|
||||||
|
; initialize data/return stack pointers
|
||||||
|
lea r12, [rel dstack_top]
|
||||||
|
mov r15, r12
|
||||||
|
lea r13, [rel rstack_top]
|
||||||
|
call word_main
|
||||||
|
mov rax, 0
|
||||||
|
cmp r12, r15
|
||||||
|
je .no_exit_value
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
.no_exit_value:
|
||||||
|
mov rdi, rax
|
||||||
|
mov rax, 60
|
||||||
|
syscall
|
||||||
|
word_puts:
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov rbx, rax
|
||||||
|
mov r8, 0
|
||||||
|
cmp rbx, 0
|
||||||
|
jge puts_abs
|
||||||
|
neg rbx
|
||||||
|
mov r8, 1
|
||||||
|
puts_abs:
|
||||||
|
lea rsi, [rel print_buf_end]
|
||||||
|
mov rcx, 0
|
||||||
|
mov r10, 10
|
||||||
|
cmp rbx, 0
|
||||||
|
jne puts_digits
|
||||||
|
dec rsi
|
||||||
|
mov byte [rsi], '0'
|
||||||
|
inc rcx
|
||||||
|
jmp puts_sign
|
||||||
|
puts_digits:
|
||||||
|
puts_loop:
|
||||||
|
xor rdx, rdx
|
||||||
|
mov rax, rbx
|
||||||
|
div r10
|
||||||
|
add dl, '0'
|
||||||
|
dec rsi
|
||||||
|
mov [rsi], dl
|
||||||
|
inc rcx
|
||||||
|
mov rbx, rax
|
||||||
|
test rbx, rbx
|
||||||
|
jne puts_loop
|
||||||
|
puts_sign:
|
||||||
|
cmp r8, 0
|
||||||
|
je puts_finish_digits
|
||||||
|
dec rsi
|
||||||
|
mov byte [rsi], '-'
|
||||||
|
inc rcx
|
||||||
|
puts_finish_digits:
|
||||||
|
mov byte [rsi + rcx], 10
|
||||||
|
inc rcx
|
||||||
|
mov rax, 1
|
||||||
|
mov rdi, 1
|
||||||
|
mov rdx, rcx
|
||||||
|
mov r9, rsi
|
||||||
|
mov rsi, r9
|
||||||
|
syscall
|
||||||
|
ret
|
||||||
|
word_dup:
|
||||||
|
mov rax, [r12]
|
||||||
|
sub r12, 8
|
||||||
|
mov [r12], rax
|
||||||
|
ret
|
||||||
|
word_drop:
|
||||||
|
add r12, 8
|
||||||
|
ret
|
||||||
|
word_swap:
|
||||||
|
mov rax, [r12]
|
||||||
|
mov rbx, [r12 + 8]
|
||||||
|
mov [r12], rbx
|
||||||
|
mov [r12 + 8], rax
|
||||||
|
ret
|
||||||
|
word__2b:
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
add qword [r12], rax
|
||||||
|
ret
|
||||||
|
word__2d:
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
sub qword [r12], rax
|
||||||
|
ret
|
||||||
|
word__2a:
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
imul qword [r12]
|
||||||
|
mov [r12], rax
|
||||||
|
ret
|
||||||
|
word__2f:
|
||||||
|
mov rbx, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov rax, [r12]
|
||||||
|
cqo
|
||||||
|
idiv rbx
|
||||||
|
mov [r12], rax
|
||||||
|
ret
|
||||||
|
word__25:
|
||||||
|
mov rbx, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov rax, [r12]
|
||||||
|
cqo
|
||||||
|
idiv rbx
|
||||||
|
mov [r12], rdx
|
||||||
|
ret
|
||||||
|
word__3d_3d:
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov rbx, [r12]
|
||||||
|
cmp rbx, rax
|
||||||
|
mov rbx, 0
|
||||||
|
sete bl
|
||||||
|
mov [r12], rbx
|
||||||
|
ret
|
||||||
|
word__21_3d:
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov rbx, [r12]
|
||||||
|
cmp rbx, rax
|
||||||
|
mov rbx, 0
|
||||||
|
setne bl
|
||||||
|
mov [r12], rbx
|
||||||
|
ret
|
||||||
|
word__3c:
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov rbx, [r12]
|
||||||
|
cmp rbx, rax
|
||||||
|
mov rbx, 0
|
||||||
|
setl bl
|
||||||
|
mov [r12], rbx
|
||||||
|
ret
|
||||||
|
word__3e:
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov rbx, [r12]
|
||||||
|
cmp rbx, rax
|
||||||
|
mov rbx, 0
|
||||||
|
setg bl
|
||||||
|
mov [r12], rbx
|
||||||
|
ret
|
||||||
|
word__3c_3d:
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov rbx, [r12]
|
||||||
|
cmp rbx, rax
|
||||||
|
mov rbx, 0
|
||||||
|
setle bl
|
||||||
|
mov [r12], rbx
|
||||||
|
ret
|
||||||
|
word__3e_3d:
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov rbx, [r12]
|
||||||
|
cmp rbx, rax
|
||||||
|
mov rbx, 0
|
||||||
|
setge bl
|
||||||
|
mov [r12], rbx
|
||||||
|
ret
|
||||||
|
word__40:
|
||||||
|
mov rax, [r12]
|
||||||
|
mov rax, [rax]
|
||||||
|
mov [r12], rax
|
||||||
|
ret
|
||||||
|
word__21:
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov rbx, [r12]
|
||||||
|
mov [rax], rbx
|
||||||
|
add r12, 8
|
||||||
|
ret
|
||||||
|
word_mmap:
|
||||||
|
mov r9, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov r8, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov r10, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov rdx, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov rsi, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov rdi, [r12]
|
||||||
|
mov rax, 9
|
||||||
|
syscall
|
||||||
|
mov [r12], rax
|
||||||
|
ret
|
||||||
|
word_munmap:
|
||||||
|
mov rsi, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov rdi, [r12]
|
||||||
|
mov rax, 11
|
||||||
|
syscall
|
||||||
|
mov [r12], rax
|
||||||
|
ret
|
||||||
|
word_exit:
|
||||||
|
mov rdi, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov rax, 60
|
||||||
|
syscall
|
||||||
|
ret
|
||||||
|
word__3er:
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
sub r13, 8
|
||||||
|
mov [r13], rax
|
||||||
|
ret
|
||||||
|
word_r_3e:
|
||||||
|
mov rax, [r13]
|
||||||
|
add r13, 8
|
||||||
|
sub r12, 8
|
||||||
|
mov [r12], rax
|
||||||
|
ret
|
||||||
|
word_rdrop:
|
||||||
|
add r13, 8
|
||||||
|
ret
|
||||||
|
word_rpick:
|
||||||
|
mov rcx, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov rax, [r13 + rcx * 8]
|
||||||
|
sub r12, 8
|
||||||
|
mov [r12], rax
|
||||||
|
ret
|
||||||
|
word_mem_2dslot:
|
||||||
|
lea rax, [rel print_buf]
|
||||||
|
sub r12, 8
|
||||||
|
mov [r12], rax
|
||||||
|
ret
|
||||||
|
word_MAGIC:
|
||||||
|
; push 99
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 99
|
||||||
|
ret
|
||||||
|
word_add13:
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
; push 8
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 8
|
||||||
|
call word__2b
|
||||||
|
ret
|
||||||
|
word_Point_2esize:
|
||||||
|
; push 16
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 16
|
||||||
|
ret
|
||||||
|
word_Point_2ex_2esize:
|
||||||
|
; push 8
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 8
|
||||||
|
ret
|
||||||
|
word_Point_2ex_2eoffset:
|
||||||
|
; push 0
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 0
|
||||||
|
ret
|
||||||
|
word_Point_2ex_40:
|
||||||
|
call word_Point_2ex_2eoffset
|
||||||
|
call word__2b
|
||||||
|
call word__40
|
||||||
|
ret
|
||||||
|
word_Point_2ex_21:
|
||||||
|
call word_Point_2ex_2eoffset
|
||||||
|
call word__2b
|
||||||
|
call word__21
|
||||||
|
ret
|
||||||
|
word_Point_2ey_2esize:
|
||||||
|
; push 8
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 8
|
||||||
|
ret
|
||||||
|
word_Point_2ey_2eoffset:
|
||||||
|
; push 8
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 8
|
||||||
|
ret
|
||||||
|
word_Point_2ey_40:
|
||||||
|
call word_Point_2ey_2eoffset
|
||||||
|
call word__2b
|
||||||
|
call word__40
|
||||||
|
ret
|
||||||
|
word_Point_2ey_21:
|
||||||
|
call word_Point_2ey_2eoffset
|
||||||
|
call word__2b
|
||||||
|
call word__21
|
||||||
|
ret
|
||||||
|
word_fancy_add:
|
||||||
|
call word__3er
|
||||||
|
call word__3er
|
||||||
|
; push 0
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 0
|
||||||
|
call word_rpick
|
||||||
|
; push 1
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 1
|
||||||
|
call word_rpick
|
||||||
|
call word__2b
|
||||||
|
; push 1
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 1
|
||||||
|
call word_rpick
|
||||||
|
call word__2a
|
||||||
|
call word_rdrop
|
||||||
|
call word_rdrop
|
||||||
|
ret
|
||||||
|
word_test_2dadd:
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
; push 7
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 7
|
||||||
|
call word__2b
|
||||||
|
call word_puts
|
||||||
|
ret
|
||||||
|
word_test_2dsub:
|
||||||
|
; push 10
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 10
|
||||||
|
; push 3
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 3
|
||||||
|
call word__2d
|
||||||
|
call word_puts
|
||||||
|
ret
|
||||||
|
word_test_2dmul:
|
||||||
|
; push 6
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 6
|
||||||
|
; push 7
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 7
|
||||||
|
call word__2a
|
||||||
|
call word_puts
|
||||||
|
ret
|
||||||
|
word_test_2ddiv:
|
||||||
|
; push 84
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 84
|
||||||
|
; push 7
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 7
|
||||||
|
call word__2f
|
||||||
|
call word_puts
|
||||||
|
ret
|
||||||
|
word_test_2dmod:
|
||||||
|
; push 85
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 85
|
||||||
|
; push 7
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 7
|
||||||
|
call word__25
|
||||||
|
call word_puts
|
||||||
|
ret
|
||||||
|
word_test_2ddrop:
|
||||||
|
; push 10
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 10
|
||||||
|
; push 20
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 20
|
||||||
|
call word_drop
|
||||||
|
call word_puts
|
||||||
|
ret
|
||||||
|
word_test_2ddup:
|
||||||
|
; push 11
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 11
|
||||||
|
call word_dup
|
||||||
|
call word__2b
|
||||||
|
call word_puts
|
||||||
|
ret
|
||||||
|
word_test_2dswap:
|
||||||
|
; push 2
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 2
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
call word_swap
|
||||||
|
call word__2d
|
||||||
|
call word_puts
|
||||||
|
ret
|
||||||
|
word_test_2dstore:
|
||||||
|
call word_mem_2dslot
|
||||||
|
call word_dup
|
||||||
|
; push 123
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 123
|
||||||
|
call word_swap
|
||||||
|
call word__21
|
||||||
|
call word__40
|
||||||
|
call word_puts
|
||||||
|
ret
|
||||||
|
word_test_2dmmap:
|
||||||
|
; push 0
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 0
|
||||||
|
; push 4096
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 4096
|
||||||
|
; push 3
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 3
|
||||||
|
; push 34
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 34
|
||||||
|
; push -1
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], -1
|
||||||
|
; push 0
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 0
|
||||||
|
call word_mmap
|
||||||
|
call word_dup
|
||||||
|
; push 1337
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 1337
|
||||||
|
call word_swap
|
||||||
|
call word__21
|
||||||
|
call word_dup
|
||||||
|
call word__40
|
||||||
|
call word_puts
|
||||||
|
; push 4096
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 4096
|
||||||
|
call word_munmap
|
||||||
|
call word_drop
|
||||||
|
ret
|
||||||
|
word_test_2dmacro:
|
||||||
|
; push 9
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 9
|
||||||
|
call word_dup
|
||||||
|
call word__2a
|
||||||
|
call word_puts
|
||||||
|
call word_MAGIC
|
||||||
|
call word_puts
|
||||||
|
call word_add13
|
||||||
|
call word_puts
|
||||||
|
ret
|
||||||
|
word_test_2dif:
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
call word__3d_3d
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
test rax, rax
|
||||||
|
jz L_if_false_0
|
||||||
|
; push 111
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 111
|
||||||
|
call word_puts
|
||||||
|
jmp L_if_end_1
|
||||||
|
L_if_false_0:
|
||||||
|
; push 222
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 222
|
||||||
|
call word_puts
|
||||||
|
L_if_end_1:
|
||||||
|
ret
|
||||||
|
word_test_2delse_2dif:
|
||||||
|
; push 2
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 2
|
||||||
|
call word_dup
|
||||||
|
; push 1
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 1
|
||||||
|
call word__3d_3d
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
test rax, rax
|
||||||
|
jz L_if_false_2
|
||||||
|
; push 50
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 50
|
||||||
|
call word_puts
|
||||||
|
jmp L_if_end_3
|
||||||
|
L_if_false_2:
|
||||||
|
call word_dup
|
||||||
|
; push 2
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 2
|
||||||
|
call word__3d_3d
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
test rax, rax
|
||||||
|
jz L_if_false_4
|
||||||
|
; push 60
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 60
|
||||||
|
call word_puts
|
||||||
|
jmp L_if_end_5
|
||||||
|
L_if_false_4:
|
||||||
|
; push 70
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 70
|
||||||
|
call word_puts
|
||||||
|
L_if_end_5:
|
||||||
|
L_if_end_3:
|
||||||
|
call word_drop
|
||||||
|
ret
|
||||||
|
word_test_2dfor:
|
||||||
|
; push 0
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 0
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
cmp rax, 0
|
||||||
|
jle L_for_end_7
|
||||||
|
sub r13, 8
|
||||||
|
mov [r13], rax
|
||||||
|
L_for_loop_6:
|
||||||
|
; push 1
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 1
|
||||||
|
call word__2b
|
||||||
|
mov rax, [r13]
|
||||||
|
dec rax
|
||||||
|
mov [r13], rax
|
||||||
|
jg L_for_loop_6
|
||||||
|
add r13, 8
|
||||||
|
L_for_end_7:
|
||||||
|
call word_puts
|
||||||
|
ret
|
||||||
|
word_test_2dfor_2dzero:
|
||||||
|
; push 123
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 123
|
||||||
|
; push 0
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 0
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
cmp rax, 0
|
||||||
|
jle L_for_end_9
|
||||||
|
sub r13, 8
|
||||||
|
mov [r13], rax
|
||||||
|
L_for_loop_8:
|
||||||
|
call word_drop
|
||||||
|
mov rax, [r13]
|
||||||
|
dec rax
|
||||||
|
mov [r13], rax
|
||||||
|
jg L_for_loop_8
|
||||||
|
add r13, 8
|
||||||
|
L_for_end_9:
|
||||||
|
call word_puts
|
||||||
|
ret
|
||||||
|
word_test_2dstruct:
|
||||||
|
call word_mem_2dslot
|
||||||
|
call word_dup
|
||||||
|
; push 111
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 111
|
||||||
|
call word_swap
|
||||||
|
call word_Point_2ex_21
|
||||||
|
call word_dup
|
||||||
|
; push 222
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 222
|
||||||
|
call word_swap
|
||||||
|
call word_Point_2ey_21
|
||||||
|
call word_dup
|
||||||
|
call word_Point_2ex_40
|
||||||
|
call word_puts
|
||||||
|
call word_Point_2ey_40
|
||||||
|
call word_puts
|
||||||
|
call word_Point_2esize
|
||||||
|
call word_puts
|
||||||
|
ret
|
||||||
|
word_test_2dcmp:
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
call word__3d_3d
|
||||||
|
call word_puts
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
; push 4
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 4
|
||||||
|
call word__3d_3d
|
||||||
|
call word_puts
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
; push 4
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 4
|
||||||
|
call word__21_3d
|
||||||
|
call word_puts
|
||||||
|
; push 4
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 4
|
||||||
|
; push 4
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 4
|
||||||
|
call word__21_3d
|
||||||
|
call word_puts
|
||||||
|
; push 3
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 3
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
call word__3c
|
||||||
|
call word_puts
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
; push 3
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 3
|
||||||
|
call word__3c
|
||||||
|
call word_puts
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
; push 3
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 3
|
||||||
|
call word__3e
|
||||||
|
call word_puts
|
||||||
|
; push 3
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 3
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
call word__3e
|
||||||
|
call word_puts
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
call word__3c_3d
|
||||||
|
call word_puts
|
||||||
|
; push 6
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 6
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
call word__3c_3d
|
||||||
|
call word_puts
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
call word__3e_3d
|
||||||
|
call word_puts
|
||||||
|
; push 4
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 4
|
||||||
|
; push 5
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 5
|
||||||
|
call word__3e_3d
|
||||||
|
call word_puts
|
||||||
|
ret
|
||||||
|
word_test_2dc_2dfn:
|
||||||
|
; push 3
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 3
|
||||||
|
; push 7
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 7
|
||||||
|
call word_fancy_add
|
||||||
|
call word_puts
|
||||||
|
ret
|
||||||
|
word_main:
|
||||||
|
call word_test_2dadd
|
||||||
|
call word_test_2dsub
|
||||||
|
call word_test_2dmul
|
||||||
|
call word_test_2ddiv
|
||||||
|
call word_test_2dmod
|
||||||
|
call word_test_2ddrop
|
||||||
|
call word_test_2ddup
|
||||||
|
call word_test_2dswap
|
||||||
|
call word_test_2dstore
|
||||||
|
call word_test_2dmmap
|
||||||
|
call word_test_2dmacro
|
||||||
|
call word_test_2dif
|
||||||
|
call word_test_2delse_2dif
|
||||||
|
call word_test_2dfor
|
||||||
|
call word_test_2dfor_2dzero
|
||||||
|
call word_test_2dcmp
|
||||||
|
call word_test_2dstruct
|
||||||
|
call word_test_2dc_2dfn
|
||||||
|
; push 0
|
||||||
|
sub r12, 8
|
||||||
|
mov qword [r12], 0
|
||||||
|
ret
|
||||||
|
section .bss
|
||||||
|
align 16
|
||||||
|
dstack: resb DSTK_BYTES
|
||||||
|
dstack_top:
|
||||||
|
align 16
|
||||||
|
rstack: resb RSTK_BYTES
|
||||||
|
rstack_top:
|
||||||
|
align 16
|
||||||
|
print_buf: resb PRINT_BUF_BYTES
|
||||||
|
print_buf_end:
|
||||||
BIN
build/test.o
Normal file
BIN
build/test.o
Normal file
Binary file not shown.
8
main.sl
8
main.sl
@@ -3,5 +3,13 @@ import stdlib.sl
|
|||||||
: main
|
: main
|
||||||
2 40 +
|
2 40 +
|
||||||
puts
|
puts
|
||||||
|
extend-syntax
|
||||||
|
1
|
||||||
|
2
|
||||||
|
foo()
|
||||||
|
puts
|
||||||
0
|
0
|
||||||
;
|
;
|
||||||
|
fn foo(int a, int b){
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
235
stdlib.sl
235
stdlib.sl
@@ -47,6 +47,211 @@ puts_finish_digits:
|
|||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
: extend-syntax
|
||||||
|
enable-call-syntax
|
||||||
|
;
|
||||||
|
immediate
|
||||||
|
compile-only
|
||||||
|
|
||||||
|
:py fn {
|
||||||
|
FN_SPLIT_CHARS = set("(),{};+-*/%,")
|
||||||
|
|
||||||
|
def split_token(token):
|
||||||
|
lex = token.lexeme
|
||||||
|
parts = []
|
||||||
|
idx = 0
|
||||||
|
while idx < len(lex):
|
||||||
|
char = lex[idx]
|
||||||
|
if char in FN_SPLIT_CHARS:
|
||||||
|
parts.append(Token(
|
||||||
|
lexeme=char,
|
||||||
|
line=token.line,
|
||||||
|
column=token.column + idx,
|
||||||
|
start=token.start + idx,
|
||||||
|
end=token.start + idx + 1,
|
||||||
|
))
|
||||||
|
idx += 1
|
||||||
|
continue
|
||||||
|
start_idx = idx
|
||||||
|
while idx < len(lex) and lex[idx] not in FN_SPLIT_CHARS:
|
||||||
|
idx += 1
|
||||||
|
segment = lex[start_idx:idx]
|
||||||
|
if segment:
|
||||||
|
parts.append(Token(
|
||||||
|
lexeme=segment,
|
||||||
|
line=token.line,
|
||||||
|
column=token.column + start_idx,
|
||||||
|
start=token.start + start_idx,
|
||||||
|
end=token.start + idx,
|
||||||
|
))
|
||||||
|
return [part for part in parts if part.lexeme]
|
||||||
|
|
||||||
|
class FnLexer:
|
||||||
|
def __init__(self, parser):
|
||||||
|
self.parser = parser
|
||||||
|
self.buffer = []
|
||||||
|
|
||||||
|
def _fill(self):
|
||||||
|
while not self.buffer:
|
||||||
|
if self.parser._eof():
|
||||||
|
raise ParseError("unexpected EOF inside fn definition")
|
||||||
|
token = self.parser.next_token()
|
||||||
|
split = split_token(token)
|
||||||
|
if not split:
|
||||||
|
continue
|
||||||
|
self.buffer.extend(split)
|
||||||
|
|
||||||
|
def peek(self):
|
||||||
|
self._fill()
|
||||||
|
return self.buffer[0]
|
||||||
|
|
||||||
|
def pop(self):
|
||||||
|
token = self.peek()
|
||||||
|
self.buffer.pop(0)
|
||||||
|
return token
|
||||||
|
|
||||||
|
def expect(self, lexeme):
|
||||||
|
token = self.pop()
|
||||||
|
if token.lexeme != lexeme:
|
||||||
|
raise ParseError(f"expected '{lexeme}' but found '{token.lexeme}'")
|
||||||
|
return token
|
||||||
|
|
||||||
|
def push_back_remaining(self):
|
||||||
|
if not self.buffer:
|
||||||
|
return
|
||||||
|
self.parser.tokens[self.parser.pos:self.parser.pos] = self.buffer
|
||||||
|
self.buffer = []
|
||||||
|
|
||||||
|
def collect_block_tokens(self):
|
||||||
|
depth = 1
|
||||||
|
collected = []
|
||||||
|
while depth > 0:
|
||||||
|
token = self.pop()
|
||||||
|
if token.lexeme == "{":
|
||||||
|
depth += 1
|
||||||
|
collected.append(token)
|
||||||
|
continue
|
||||||
|
if token.lexeme == "}":
|
||||||
|
depth -= 1
|
||||||
|
if depth == 0:
|
||||||
|
break
|
||||||
|
collected.append(token)
|
||||||
|
continue
|
||||||
|
collected.append(token)
|
||||||
|
return collected
|
||||||
|
|
||||||
|
OP_PRECEDENCE = {}
|
||||||
|
OP_PRECEDENCE["+"] = 1
|
||||||
|
OP_PRECEDENCE["-"] = 1
|
||||||
|
OP_PRECEDENCE["*"] = 2
|
||||||
|
OP_PRECEDENCE["/"] = 2
|
||||||
|
OP_PRECEDENCE["%"] = 2
|
||||||
|
|
||||||
|
def parse_fn_body(tokens):
|
||||||
|
if not tokens:
|
||||||
|
raise ParseError("empty function body")
|
||||||
|
lexemes = [tok.lexeme for tok in tokens if tok.lexeme]
|
||||||
|
if not lexemes or lexemes[0] != "return":
|
||||||
|
raise ParseError("function body must start with 'return'")
|
||||||
|
if lexemes[-1] != ";":
|
||||||
|
raise ParseError("function body must terminate with ';'")
|
||||||
|
extra = lexemes[1:-1]
|
||||||
|
if not extra:
|
||||||
|
raise ParseError("missing return expression")
|
||||||
|
return extra
|
||||||
|
|
||||||
|
def shunting_yard(tokens):
|
||||||
|
output = []
|
||||||
|
stack = []
|
||||||
|
for token in tokens:
|
||||||
|
if token == "(":
|
||||||
|
stack.append(token)
|
||||||
|
continue
|
||||||
|
if token == ")":
|
||||||
|
while stack and stack[-1] != "(":
|
||||||
|
output.append(stack.pop())
|
||||||
|
if not stack:
|
||||||
|
raise ParseError("mismatched parentheses in return expression")
|
||||||
|
stack.pop()
|
||||||
|
continue
|
||||||
|
if token in OP_PRECEDENCE:
|
||||||
|
while stack and stack[-1] in OP_PRECEDENCE and OP_PRECEDENCE[stack[-1]] >= OP_PRECEDENCE[token]:
|
||||||
|
output.append(stack.pop())
|
||||||
|
stack.append(token)
|
||||||
|
continue
|
||||||
|
output.append(token)
|
||||||
|
while stack:
|
||||||
|
top = stack.pop()
|
||||||
|
if top == "(":
|
||||||
|
raise ParseError("mismatched parentheses in return expression")
|
||||||
|
output.append(top)
|
||||||
|
return output
|
||||||
|
|
||||||
|
def is_int_literal(text):
|
||||||
|
try:
|
||||||
|
int(text, 0)
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def translate_postfix(postfix, params):
|
||||||
|
indices = {name: idx for idx, name in enumerate(params)}
|
||||||
|
translated = []
|
||||||
|
for token in postfix:
|
||||||
|
if token in indices:
|
||||||
|
translated.append(str(indices[token]))
|
||||||
|
translated.append("rpick")
|
||||||
|
continue
|
||||||
|
if is_int_literal(token):
|
||||||
|
translated.append(token)
|
||||||
|
continue
|
||||||
|
translated.append(token)
|
||||||
|
return translated
|
||||||
|
|
||||||
|
def macro(ctx):
|
||||||
|
parser = ctx.parser
|
||||||
|
if not isinstance(parser.context_stack[-1], Module):
|
||||||
|
raise ParseError("'fn' definitions must be top-level")
|
||||||
|
lexer = FnLexer(parser)
|
||||||
|
name_token = lexer.pop()
|
||||||
|
name = name_token.lexeme
|
||||||
|
if not is_identifier(name):
|
||||||
|
raise ParseError("invalid function name for 'fn'")
|
||||||
|
lexer.expect("(")
|
||||||
|
params = []
|
||||||
|
if lexer.peek().lexeme != ")":
|
||||||
|
while True:
|
||||||
|
type_token = lexer.pop()
|
||||||
|
if type_token.lexeme != "int":
|
||||||
|
raise ParseError("only 'int' parameters are supported in fn definitions")
|
||||||
|
param_token = lexer.pop()
|
||||||
|
if not is_identifier(param_token.lexeme):
|
||||||
|
raise ParseError("invalid parameter name in fn definition")
|
||||||
|
params.append(param_token.lexeme)
|
||||||
|
if lexer.peek().lexeme == ",":
|
||||||
|
lexer.pop()
|
||||||
|
continue
|
||||||
|
break
|
||||||
|
lexer.expect(")")
|
||||||
|
lexer.expect("{")
|
||||||
|
body_tokens = lexer.collect_block_tokens()
|
||||||
|
lexer.push_back_remaining()
|
||||||
|
if len(params) != len(set(params)):
|
||||||
|
raise ParseError("duplicate parameter names in fn definition")
|
||||||
|
return_tokens = parse_fn_body(body_tokens)
|
||||||
|
postfix = shunting_yard(return_tokens)
|
||||||
|
body_words = []
|
||||||
|
for _ in reversed(params):
|
||||||
|
body_words.append(">r")
|
||||||
|
body_words.extend(translate_postfix(postfix, params))
|
||||||
|
for _ in params:
|
||||||
|
body_words.append("rdrop")
|
||||||
|
generated = []
|
||||||
|
emit_definition(generated, name_token, name, body_words)
|
||||||
|
ctx.inject_token_objects(generated)
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
:asm dup {
|
:asm dup {
|
||||||
mov rax, [r12]
|
mov rax, [r12]
|
||||||
sub r12, 8
|
sub r12, 8
|
||||||
@@ -226,3 +431,33 @@ puts_finish_digits:
|
|||||||
syscall
|
syscall
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
:asm >r {
|
||||||
|
mov rax, [r12]
|
||||||
|
add r12, 8
|
||||||
|
sub r13, 8
|
||||||
|
mov [r13], rax
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
:asm r> {
|
||||||
|
mov rax, [r13]
|
||||||
|
add r13, 8
|
||||||
|
sub r12, 8
|
||||||
|
mov [r12], rax
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
:asm rdrop {
|
||||||
|
add r13, 8
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
:asm rpick {
|
||||||
|
mov rcx, [r12]
|
||||||
|
add r12, 8
|
||||||
|
mov rax, [r13 + rcx * 8]
|
||||||
|
sub r12, 8
|
||||||
|
mov [r12], rax
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|||||||
14
test.sl
14
test.sl
@@ -31,6 +31,12 @@ struct: Point
|
|||||||
field y 8
|
field y 8
|
||||||
;struct
|
;struct
|
||||||
|
|
||||||
|
extend-syntax
|
||||||
|
|
||||||
|
fn fancy_add(int left, int right){
|
||||||
|
return (left + right) * right;
|
||||||
|
}
|
||||||
|
|
||||||
: test-add
|
: test-add
|
||||||
5 7 + puts
|
5 7 + puts
|
||||||
;
|
;
|
||||||
@@ -153,6 +159,13 @@ struct: Point
|
|||||||
4 5 >= puts
|
4 5 >= puts
|
||||||
;
|
;
|
||||||
|
|
||||||
|
: test-c-fn
|
||||||
|
3
|
||||||
|
7
|
||||||
|
fancy_add()
|
||||||
|
puts
|
||||||
|
;
|
||||||
|
|
||||||
: main
|
: main
|
||||||
test-add
|
test-add
|
||||||
test-sub
|
test-sub
|
||||||
@@ -171,5 +184,6 @@ struct: Point
|
|||||||
test-for-zero
|
test-for-zero
|
||||||
test-cmp
|
test-cmp
|
||||||
test-struct
|
test-struct
|
||||||
|
test-c-fn
|
||||||
0
|
0
|
||||||
;
|
;
|
||||||
|
|||||||
Reference in New Issue
Block a user