ststic arrays and dynamic arrays
This commit is contained in:
90
main.py
90
main.py
@@ -439,10 +439,12 @@ class Parser:
|
|||||||
if self._handle_macro_recording(token):
|
if self._handle_macro_recording(token):
|
||||||
continue
|
continue
|
||||||
lexeme = token.lexeme
|
lexeme = token.lexeme
|
||||||
if lexeme == ":":
|
if lexeme == "[":
|
||||||
raise ParseError(
|
self._handle_list_begin()
|
||||||
f"':' definitions are no longer supported; use 'word <name> ... end' at {token.line}:{token.column}"
|
continue
|
||||||
)
|
if lexeme == "]":
|
||||||
|
self._handle_list_end(token)
|
||||||
|
continue
|
||||||
if lexeme == "word":
|
if lexeme == "word":
|
||||||
self._begin_definition(token, terminator="end")
|
self._begin_definition(token, terminator="end")
|
||||||
continue
|
continue
|
||||||
@@ -495,6 +497,16 @@ class Parser:
|
|||||||
module.variables = dict(self.variable_labels)
|
module.variables = dict(self.variable_labels)
|
||||||
return module
|
return module
|
||||||
|
|
||||||
|
def _handle_list_begin(self) -> None:
|
||||||
|
label = self._new_label("list")
|
||||||
|
self._append_op(Op(op="list_begin", data=label))
|
||||||
|
self._push_control({"type": "list", "label": label})
|
||||||
|
|
||||||
|
def _handle_list_end(self, token: Token) -> None:
|
||||||
|
entry = self._pop_control(("list",))
|
||||||
|
label = entry["label"]
|
||||||
|
self._append_op(Op(op="list_end", data=label))
|
||||||
|
|
||||||
# Internal helpers ---------------------------------------------------------
|
# Internal helpers ---------------------------------------------------------
|
||||||
|
|
||||||
def _parse_extern(self, token: Token) -> None:
|
def _parse_extern(self, token: Token) -> None:
|
||||||
@@ -1703,6 +1715,72 @@ class Assembler:
|
|||||||
self._emit_for_next(data, builder)
|
self._emit_for_next(data, builder)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if kind == "list_begin":
|
||||||
|
builder.comment("list begin")
|
||||||
|
builder.emit(" mov rax, [rel list_capture_sp]")
|
||||||
|
builder.emit(" lea rdx, [rel list_capture_stack]")
|
||||||
|
builder.emit(" mov [rdx + rax*8], r12")
|
||||||
|
builder.emit(" inc rax")
|
||||||
|
builder.emit(" mov [rel list_capture_sp], rax")
|
||||||
|
return
|
||||||
|
|
||||||
|
if kind == "list_end":
|
||||||
|
base = str(data)
|
||||||
|
loop_label = f"{base}_copy_loop"
|
||||||
|
done_label = f"{base}_copy_done"
|
||||||
|
|
||||||
|
builder.comment("list end")
|
||||||
|
# pop capture start pointer
|
||||||
|
builder.emit(" mov rax, [rel list_capture_sp]")
|
||||||
|
builder.emit(" dec rax")
|
||||||
|
builder.emit(" mov [rel list_capture_sp], rax")
|
||||||
|
builder.emit(" lea r11, [rel list_capture_stack]")
|
||||||
|
builder.emit(" mov rbx, [r11 + rax*8]")
|
||||||
|
# count = (start_r12 - r12) / 8
|
||||||
|
builder.emit(" mov rcx, rbx")
|
||||||
|
builder.emit(" sub rcx, r12")
|
||||||
|
builder.emit(" shr rcx, 3")
|
||||||
|
builder.emit(" mov [rel list_capture_tmp], rcx")
|
||||||
|
|
||||||
|
# bytes = (count + 1) * 8
|
||||||
|
builder.emit(" mov rsi, rcx")
|
||||||
|
builder.emit(" inc rsi")
|
||||||
|
builder.emit(" shl rsi, 3")
|
||||||
|
|
||||||
|
# mmap(NULL, bytes, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0)
|
||||||
|
builder.emit(" xor rdi, rdi")
|
||||||
|
builder.emit(" mov rdx, 3")
|
||||||
|
builder.emit(" mov r10, 34")
|
||||||
|
builder.emit(" mov r8, -1")
|
||||||
|
builder.emit(" xor r9, r9")
|
||||||
|
builder.emit(" mov rax, 9")
|
||||||
|
builder.emit(" syscall")
|
||||||
|
|
||||||
|
# store length
|
||||||
|
builder.emit(" mov rdx, [rel list_capture_tmp]")
|
||||||
|
builder.emit(" mov [rax], rdx")
|
||||||
|
|
||||||
|
# copy elements, preserving original push order
|
||||||
|
builder.emit(" xor rcx, rcx")
|
||||||
|
builder.emit(f"{loop_label}:")
|
||||||
|
builder.emit(" cmp rcx, rdx")
|
||||||
|
builder.emit(f" je {done_label}")
|
||||||
|
builder.emit(" mov r8, rdx")
|
||||||
|
builder.emit(" dec r8")
|
||||||
|
builder.emit(" sub r8, rcx")
|
||||||
|
builder.emit(" shl r8, 3")
|
||||||
|
builder.emit(" mov r9, [r12 + r8]")
|
||||||
|
builder.emit(" mov [rax + 8 + rcx*8], r9")
|
||||||
|
builder.emit(" inc rcx")
|
||||||
|
builder.emit(f" jmp {loop_label}")
|
||||||
|
builder.emit(f"{done_label}:")
|
||||||
|
|
||||||
|
# drop captured values and push list pointer
|
||||||
|
builder.emit(" mov r12, rbx")
|
||||||
|
builder.emit(" sub r12, 8")
|
||||||
|
builder.emit(" mov [r12], rax")
|
||||||
|
return
|
||||||
|
|
||||||
raise CompileError(f"unsupported op {node!r}")
|
raise CompileError(f"unsupported op {node!r}")
|
||||||
|
|
||||||
def _emit_wordref(self, name: str, builder: FunctionEmitter) -> None:
|
def _emit_wordref(self, name: str, builder: FunctionEmitter) -> None:
|
||||||
@@ -1855,6 +1933,10 @@ class Assembler:
|
|||||||
"print_buf_end:",
|
"print_buf_end:",
|
||||||
"align 16",
|
"align 16",
|
||||||
"persistent: resb 64",
|
"persistent: resb 64",
|
||||||
|
"align 16",
|
||||||
|
"list_capture_sp: resq 1",
|
||||||
|
"list_capture_tmp: resq 1",
|
||||||
|
"list_capture_stack: resq 1024",
|
||||||
]
|
]
|
||||||
|
|
||||||
def write_asm(self, emission: Emission, path: Path) -> None:
|
def write_asm(self, emission: Emission, path: Path) -> None:
|
||||||
|
|||||||
248
stdlib/arr.sl
Normal file
248
stdlib/arr.sl
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
# Dynamic arrays (qword elements)
|
||||||
|
#
|
||||||
|
# Layout at address `arr`:
|
||||||
|
# [arr + 0] len (qword)
|
||||||
|
# [arr + 8] cap (qword)
|
||||||
|
# [arr + 16] data (qword) = arr + 24
|
||||||
|
# [arr + 24] elements (cap * 8 bytes)
|
||||||
|
#
|
||||||
|
# Allocation: mmap; free: munmap.
|
||||||
|
# Growth: allocate new block, copy elements, munmap old block.
|
||||||
|
|
||||||
|
# : arr_new ( cap -- arr )
|
||||||
|
:asm arr_new {
|
||||||
|
mov r14, [r12] ; requested cap
|
||||||
|
cmp r14, 1
|
||||||
|
jge .cap_ok
|
||||||
|
mov r14, 1
|
||||||
|
.cap_ok:
|
||||||
|
; bytes = 24 + cap*8
|
||||||
|
mov rsi, r14
|
||||||
|
shl rsi, 3
|
||||||
|
add rsi, 24
|
||||||
|
|
||||||
|
; mmap(NULL, bytes, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0)
|
||||||
|
xor rdi, rdi
|
||||||
|
mov rdx, 3
|
||||||
|
mov r10, 34
|
||||||
|
mov r8, -1
|
||||||
|
xor r9, r9
|
||||||
|
mov rax, 9
|
||||||
|
syscall
|
||||||
|
|
||||||
|
; header
|
||||||
|
mov qword [rax], 0
|
||||||
|
mov [rax + 8], r14
|
||||||
|
lea rbx, [rax + 24]
|
||||||
|
mov [rax + 16], rbx
|
||||||
|
|
||||||
|
; replace cap with arr pointer
|
||||||
|
mov [r12], rax
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
# : arr_len ( arr -- len )
|
||||||
|
:asm arr_len {
|
||||||
|
mov rax, [r12]
|
||||||
|
mov rax, [rax]
|
||||||
|
mov [r12], rax
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
# : arr_cap ( arr -- cap )
|
||||||
|
:asm arr_cap {
|
||||||
|
mov rax, [r12]
|
||||||
|
mov rax, [rax + 8]
|
||||||
|
mov [r12], rax
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
# : arr_data ( arr -- ptr )
|
||||||
|
:asm arr_data {
|
||||||
|
mov rax, [r12]
|
||||||
|
mov rax, [rax + 16]
|
||||||
|
mov [r12], rax
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
# : arr_free ( arr -- )
|
||||||
|
:asm arr_free {
|
||||||
|
mov rbx, [r12] ; base
|
||||||
|
mov rcx, [rbx + 8] ; cap
|
||||||
|
mov rsi, rcx
|
||||||
|
shl rsi, 3
|
||||||
|
add rsi, 24
|
||||||
|
mov rdi, rbx
|
||||||
|
mov rax, 11
|
||||||
|
syscall
|
||||||
|
add r12, 8 ; drop arr
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
# : arr_reserve ( cap arr -- arr )
|
||||||
|
# Ensures capacity >= cap; returns (possibly moved) arr pointer.
|
||||||
|
:asm arr_reserve {
|
||||||
|
mov rbx, [r12] ; arr
|
||||||
|
mov r14, [r12 + 8] ; requested cap
|
||||||
|
cmp r14, 1
|
||||||
|
jge .req_ok
|
||||||
|
mov r14, 1
|
||||||
|
.req_ok:
|
||||||
|
mov rdx, [rbx + 8] ; old cap
|
||||||
|
cmp rdx, r14
|
||||||
|
jae .no_change
|
||||||
|
|
||||||
|
; alloc new block: bytes = 24 + reqcap*8
|
||||||
|
mov rsi, r14
|
||||||
|
shl rsi, 3
|
||||||
|
add rsi, 24
|
||||||
|
xor rdi, rdi
|
||||||
|
mov rdx, 3
|
||||||
|
mov r10, 34
|
||||||
|
mov r8, -1
|
||||||
|
xor r9, r9
|
||||||
|
mov rax, 9
|
||||||
|
syscall
|
||||||
|
|
||||||
|
mov r10, rax ; new base
|
||||||
|
lea r9, [r10 + 24] ; new data
|
||||||
|
|
||||||
|
; header
|
||||||
|
mov r8, [rbx] ; len
|
||||||
|
mov [r10], r8
|
||||||
|
mov [r10 + 8], r14
|
||||||
|
mov [r10 + 16], r9
|
||||||
|
|
||||||
|
; copy elements from old data
|
||||||
|
mov r11, [rbx + 16] ; old data
|
||||||
|
xor rcx, rcx ; i
|
||||||
|
.copy_loop:
|
||||||
|
cmp rcx, r8
|
||||||
|
je .copy_done
|
||||||
|
mov rdx, [r11 + rcx*8]
|
||||||
|
mov [r9 + rcx*8], rdx
|
||||||
|
inc rcx
|
||||||
|
jmp .copy_loop
|
||||||
|
.copy_done:
|
||||||
|
|
||||||
|
; munmap old block
|
||||||
|
mov rsi, [rbx + 8]
|
||||||
|
shl rsi, 3
|
||||||
|
add rsi, 24
|
||||||
|
mov rdi, rbx
|
||||||
|
mov rax, 11
|
||||||
|
syscall
|
||||||
|
|
||||||
|
; return new arr only
|
||||||
|
mov [r12 + 8], r10
|
||||||
|
add r12, 8
|
||||||
|
ret
|
||||||
|
|
||||||
|
.no_change:
|
||||||
|
; drop cap, keep arr
|
||||||
|
mov [r12 + 8], rbx
|
||||||
|
add r12, 8
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
# : arr_push ( x arr -- arr )
|
||||||
|
:asm arr_push {
|
||||||
|
mov rbx, [r12] ; arr
|
||||||
|
mov rcx, [rbx] ; len
|
||||||
|
mov rdx, [rbx + 8] ; cap
|
||||||
|
cmp rcx, rdx
|
||||||
|
jb .have_space
|
||||||
|
|
||||||
|
; grow: newcap = max(1, cap) * 2
|
||||||
|
mov r14, rdx
|
||||||
|
cmp r14, 1
|
||||||
|
jae .cap_ok
|
||||||
|
mov r14, 1
|
||||||
|
.cap_ok:
|
||||||
|
shl r14, 1
|
||||||
|
|
||||||
|
; alloc new block
|
||||||
|
mov rsi, r14
|
||||||
|
shl rsi, 3
|
||||||
|
add rsi, 24
|
||||||
|
xor rdi, rdi
|
||||||
|
mov rdx, 3
|
||||||
|
mov r10, 34
|
||||||
|
mov r8, -1
|
||||||
|
xor r9, r9
|
||||||
|
mov rax, 9
|
||||||
|
syscall
|
||||||
|
|
||||||
|
mov r10, rax ; new base
|
||||||
|
lea r9, [r10 + 24] ; new data
|
||||||
|
|
||||||
|
; header
|
||||||
|
mov rcx, [rbx] ; len (reload; syscall clobbers rcx)
|
||||||
|
mov [r10], rcx
|
||||||
|
mov [r10 + 8], r14
|
||||||
|
mov [r10 + 16], r9
|
||||||
|
|
||||||
|
; copy old data
|
||||||
|
mov r11, [rbx + 16] ; old data
|
||||||
|
xor r8, r8
|
||||||
|
.push_copy_loop:
|
||||||
|
cmp r8, rcx
|
||||||
|
je .push_copy_done
|
||||||
|
mov rdx, [r11 + r8*8]
|
||||||
|
mov [r9 + r8*8], rdx
|
||||||
|
inc r8
|
||||||
|
jmp .push_copy_loop
|
||||||
|
.push_copy_done:
|
||||||
|
|
||||||
|
; munmap old block
|
||||||
|
mov rsi, [rbx + 8]
|
||||||
|
shl rsi, 3
|
||||||
|
add rsi, 24
|
||||||
|
mov rdi, rbx
|
||||||
|
mov rax, 11
|
||||||
|
syscall
|
||||||
|
|
||||||
|
; switch to new base
|
||||||
|
mov rbx, r10
|
||||||
|
|
||||||
|
.have_space:
|
||||||
|
; store element at data[len]
|
||||||
|
mov r9, [rbx + 16]
|
||||||
|
mov rax, [r12 + 8] ; x
|
||||||
|
mov rcx, [rbx] ; len
|
||||||
|
mov [r9 + rcx*8], rax
|
||||||
|
inc rcx
|
||||||
|
mov [rbx], rcx
|
||||||
|
|
||||||
|
; return arr only
|
||||||
|
mov [r12 + 8], rbx
|
||||||
|
add r12, 8
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
# : arr_pop ( arr -- x arr )
|
||||||
|
:asm arr_pop {
|
||||||
|
mov rbx, [r12] ; arr
|
||||||
|
mov rcx, [rbx] ; len
|
||||||
|
test rcx, rcx
|
||||||
|
jz .empty
|
||||||
|
dec rcx
|
||||||
|
mov [rbx], rcx
|
||||||
|
mov rdx, [rbx + 16] ; data
|
||||||
|
mov rax, [rdx + rcx*8]
|
||||||
|
jmp .push
|
||||||
|
.empty:
|
||||||
|
xor rax, rax
|
||||||
|
.push:
|
||||||
|
sub r12, 8
|
||||||
|
mov [r12], rax
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
;
|
||||||
6
tests/arr_dynamic.expected
Normal file
6
tests/arr_dynamic.expected
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
1
|
||||||
|
3
|
||||||
|
4
|
||||||
|
10
|
||||||
|
20
|
||||||
|
30
|
||||||
23
tests/arr_dynamic.sl
Normal file
23
tests/arr_dynamic.sl
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import ../stdlib/stdlib.sl
|
||||||
|
import ../stdlib/io.sl
|
||||||
|
import ../stdlib/arr.sl
|
||||||
|
|
||||||
|
word main
|
||||||
|
0 arr_new
|
||||||
|
|
||||||
|
dup arr_cap puti cr
|
||||||
|
|
||||||
|
10 swap arr_push
|
||||||
|
20 swap arr_push
|
||||||
|
30 swap arr_push
|
||||||
|
|
||||||
|
dup arr_len puti cr
|
||||||
|
dup arr_cap puti cr
|
||||||
|
|
||||||
|
# print elements via explicit offsets: data[i] = @ (arr_data + i*8)
|
||||||
|
dup arr_data 0 8 * + @ puti cr
|
||||||
|
dup arr_data 1 8 * + @ puti cr
|
||||||
|
dup arr_data 2 8 * + @ puti cr
|
||||||
|
|
||||||
|
arr_free
|
||||||
|
end
|
||||||
1
tests/arr_dynamic.test
Normal file
1
tests/arr_dynamic.test
Normal file
@@ -0,0 +1 @@
|
|||||||
|
python main.py tests/arr_dynamic.sl -o /tmp/arr_dynamic > /dev/null && /tmp/arr_dynamic
|
||||||
2
tests/list.expected
Normal file
2
tests/list.expected
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
3
|
||||||
|
2
|
||||||
9
tests/list.sl
Normal file
9
tests/list.sl
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import ../stdlib/stdlib.sl
|
||||||
|
import ../stdlib/io.sl
|
||||||
|
|
||||||
|
word main
|
||||||
|
[ 1 2 3 4 ]
|
||||||
|
# element i is at: list_ptr + 8 + i*8
|
||||||
|
dup 8 2 * 8 + + @ puti cr # index 2 = 3
|
||||||
|
dup 8 1 * 8 + + @ puti cr # index 1 = 2
|
||||||
|
end
|
||||||
1
tests/list.test
Normal file
1
tests/list.test
Normal file
@@ -0,0 +1 @@
|
|||||||
|
python main.py tests/list.sl -o /tmp/list > /dev/null && /tmp/list
|
||||||
Reference in New Issue
Block a user