mirror of
https://github.com/bearlanguageorg/bear.git
synced 2026-08-26 16:07:01 +00:00
Loops
This commit is contained in:
@@ -61,45 +61,57 @@ Quick start:
|
|||||||
|
|
||||||
## The VuurRaaf language
|
## The VuurRaaf language
|
||||||
|
|
||||||
A small, V-flavored language. Everything is a 64-bit integer or a string
|
A small, V-flavored language. Values are 64-bit integers, strings, or arrays
|
||||||
(strings concatenate with `+` and compare with `==`/`!=`; `print`/`println`
|
(strings concatenate with `+` and compare with `==`/`!=`; arrays are mutable
|
||||||
accept both).
|
and compare by identity).
|
||||||
|
|
||||||
```
|
```
|
||||||
fn greet(name) {
|
fn sum(items) {
|
||||||
println("hello, " + name + "!")
|
let total = 0
|
||||||
}
|
for x in items { // iterate an array
|
||||||
|
total = total + x
|
||||||
fn fib(n) {
|
|
||||||
if n < 2 {
|
|
||||||
return n
|
|
||||||
}
|
}
|
||||||
return fib(n - 1) + fib(n - 2)
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let x = 6 * 7 // let with type inference
|
let x = 6 * 7
|
||||||
assert x == 42 // checked by the vm at runtime
|
assert x == 42
|
||||||
let big = x > 40 and x < 50 // and / or / not, short-circuiting
|
let big = x > 40 and x < 50 // and / or / not, short-circuiting
|
||||||
if big {
|
if big {
|
||||||
println("x is big")
|
println("x is big")
|
||||||
} else {
|
} else {
|
||||||
println("x is small")
|
println("x is small")
|
||||||
}
|
}
|
||||||
let i = 0
|
|
||||||
while i < 3 {
|
let a = [10, 20, 30]
|
||||||
println("counting " + i) // "counting 0", ...
|
a[1] = 99 // index assignment
|
||||||
i = i + 1
|
push(a, 40) // grow in place
|
||||||
}
|
println(a) // [10, 99, 30, 40]
|
||||||
println(fib(10)) // 55
|
println(len(a)) // 4
|
||||||
|
println(sum(a)) // 179
|
||||||
|
|
||||||
|
for i in 0..5 { ... } // 0 1 2 3 4 (exclusive ..)
|
||||||
|
for i in 1...3 { ... } // 1 2 3 (inclusive ...)
|
||||||
|
|
||||||
|
let grid = [[1, 2], [3, 4]] // nested arrays
|
||||||
|
println(grid[1][0]) // 3
|
||||||
|
|
||||||
|
let i = 100
|
||||||
|
for i in 0..3 { ... } // loop vars are scoped to the loop
|
||||||
|
println(i) // 100
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- functions: `fn name(a, b) { ... }` with `return expr`
|
- functions: `fn name(a, b) { ... }` with `return expr`
|
||||||
- variables: `let name = expr`, reassignment `name = expr`
|
- variables: `let name = expr`, reassignment `name = expr`
|
||||||
|
- arrays: `[e1, e2, ...]`, indexing `a[i]` (read and write), `len(a)`,
|
||||||
|
`push(a, v)`; array literals may nest
|
||||||
|
- for loops: `for x in arr { }` and ranges `for i in 0..10 { }` /
|
||||||
|
`for i in 0...10 { }`; loop variables are scoped to the loop body
|
||||||
- operators: `+ - * / %`, `== != < <= > >=`, `and or not`, unary `-`
|
- operators: `+ - * / %`, `== != < <= > >=`, `and or not`, unary `-`
|
||||||
- statements: `let`, assignment, `if/else`, `while`, `return`, `assert`, calls,
|
- statements: `let`, assignment, `if/else`, `while`, `for`, `return`, `assert`,
|
||||||
`print(...)` / `println(...)`
|
calls, `print(...)` / `println(...)`
|
||||||
- comments: `//`
|
- comments: `//`
|
||||||
|
|
||||||
## Assembly
|
## Assembly
|
||||||
@@ -125,7 +137,7 @@ helper:
|
|||||||
|
|
||||||
Opcodes: `halt push_int push_str load store pop dup add sub mul div mod neg
|
Opcodes: `halt push_int push_str load store pop dup add sub mul div mod neg
|
||||||
eq ne lt le gt ge and or not jmp jz jnz call ret retv print println assert
|
eq ne lt le gt ge and or not jmp jz jnz call ret retv print println assert
|
||||||
enter`.
|
enter mkarray aget aset alen apush`.
|
||||||
|
|
||||||
## Formats
|
## Formats
|
||||||
|
|
||||||
@@ -141,15 +153,17 @@ enter`.
|
|||||||
| `compiler/` | lexer, recursive-descent parser, bytecode codegen (VROBJ) |
|
| `compiler/` | lexer, recursive-descent parser, bytecode codegen (VROBJ) |
|
||||||
| `assembler/` | `.vasm` -> VROBJ |
|
| `assembler/` | `.vasm` -> VROBJ |
|
||||||
| `linker/` | resolves relocations, rebases strings, emits VRBIN |
|
| `linker/` | resolves relocations, rebases strings, emits VRBIN |
|
||||||
| `vm/` | stack VM: tagged values, call frames, string heap, tracing |
|
| `vm/` | stack VM: tagged values, call frames, string/array heaps |
|
||||||
| `obj/` | VROBJ/VRBIN binary formats |
|
| `obj/` | VROBJ/VRBIN binary formats |
|
||||||
| `bin/` | small standalone tools: `tl_alloc.v`, `tl_loader.v` |
|
| `bin/` | small standalone tools: `tl_alloc.v`, `tl_loader.v` |
|
||||||
|
|
||||||
The VM is a stack machine with 64-bit tagged values (numbers are stored
|
The VM is a stack machine with 64-bit tagged values using two tag bits:
|
||||||
shifted left so no integer collides with a string handle). Calls push a frame
|
numbers are stored shifted left by two, string handles end in `01`, array
|
||||||
(return address, base pointer, argc), copy arguments into local slots, and
|
handles in `11` — so no integer ever collides with a heap handle. Arrays live
|
||||||
reserve extra locals with `enter n`. `vr debug` prints every instruction with
|
in a growable heap (`mkarray`/`aget`/`aset`/`alen`/`apush`). Calls push a
|
||||||
the stack contents.
|
frame (return address, base pointer, argc), copy arguments into local slots,
|
||||||
|
and reserve extra locals with `enter n`. `vr debug` prints every instruction
|
||||||
|
with the stack contents (arrays rendered as `[1, 2, ...]`).
|
||||||
|
|
||||||
## Repository layout
|
## Repository layout
|
||||||
|
|
||||||
|
|||||||
+19
-2
@@ -173,6 +173,22 @@ pub fn assemble(src string) !obj.Obj {
|
|||||||
o.code << u8(31)
|
o.code << u8(31)
|
||||||
o.code << obj.encode_i64(parse_int(arg, 'enter')!)
|
o.code << obj.encode_i64(parse_int(arg, 'enter')!)
|
||||||
}
|
}
|
||||||
|
'mkarray' {
|
||||||
|
o.code << u8(32)
|
||||||
|
o.code << obj.encode_i64(parse_int(arg, 'mkarray')!)
|
||||||
|
}
|
||||||
|
'aget' {
|
||||||
|
o.code << u8(33)
|
||||||
|
}
|
||||||
|
'aset' {
|
||||||
|
o.code << u8(34)
|
||||||
|
}
|
||||||
|
'alen' {
|
||||||
|
o.code << u8(35)
|
||||||
|
}
|
||||||
|
'apush' {
|
||||||
|
o.code << u8(36)
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
return error('unknown instruction "${op}"')
|
return error('unknown instruction "${op}"')
|
||||||
}
|
}
|
||||||
@@ -238,10 +254,11 @@ fn instr_len(line string) !int {
|
|||||||
op := parts[0]
|
op := parts[0]
|
||||||
match op {
|
match op {
|
||||||
'halt', 'pop', 'dup', 'add', 'sub', 'mul', 'div', 'mod', 'neg', 'eq', 'ne', 'lt', 'le',
|
'halt', 'pop', 'dup', 'add', 'sub', 'mul', 'div', 'mod', 'neg', 'eq', 'ne', 'lt', 'le',
|
||||||
'gt', 'ge', 'and', 'or', 'not', 'ret', 'retv', 'print', 'println', 'assert' {
|
'gt', 'ge', 'and', 'or', 'not', 'ret', 'retv', 'print', 'println', 'assert', 'aget',
|
||||||
|
'aset', 'alen', 'apush' {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
'push_int', 'push_str', 'load', 'store', 'jmp', 'jz', 'jnz', 'enter' {
|
'push_int', 'push_str', 'load', 'store', 'jmp', 'jz', 'jnz', 'enter', 'mkarray' {
|
||||||
return 9
|
return 9
|
||||||
}
|
}
|
||||||
'call' {
|
'call' {
|
||||||
|
|||||||
@@ -41,6 +41,11 @@ const op_print = u8(28)
|
|||||||
const op_println = u8(29)
|
const op_println = u8(29)
|
||||||
const op_assert = u8(30)
|
const op_assert = u8(30)
|
||||||
const op_enter = u8(31)
|
const op_enter = u8(31)
|
||||||
|
const op_mkarray = u8(32)
|
||||||
|
const op_aget = u8(33)
|
||||||
|
const op_aset = u8(34)
|
||||||
|
const op_alen = u8(35)
|
||||||
|
const op_apush = u8(36)
|
||||||
|
|
||||||
// compile parses and compiles VuurRaaf source into an object file.
|
// compile parses and compiles VuurRaaf source into an object file.
|
||||||
pub fn compile(src string) !obj.Obj {
|
pub fn compile(src string) !obj.Obj {
|
||||||
@@ -151,6 +156,12 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
|||||||
g.code << op_store
|
g.code << op_store
|
||||||
g.code << obj.encode_i64(i64(idx))
|
g.code << obj.encode_i64(i64(idx))
|
||||||
}
|
}
|
||||||
|
.index_assign {
|
||||||
|
g.gen_expr(st.base)!
|
||||||
|
g.gen_expr(st.idx)!
|
||||||
|
g.gen_expr(st.expr)!
|
||||||
|
g.code << op_aset
|
||||||
|
}
|
||||||
.if_stmt {
|
.if_stmt {
|
||||||
else_l := g.new_label()
|
else_l := g.new_label()
|
||||||
end_l := g.new_label()
|
end_l := g.new_label()
|
||||||
@@ -186,6 +197,87 @@ fn (mut g Gen) gen_stmt(st Stmt) ! {
|
|||||||
g.fixups << Fixup{ name: loop_l, off: u32(g.code.len) - 8 }
|
g.fixups << Fixup{ name: loop_l, off: u32(g.code.len) - 8 }
|
||||||
g.emit_label(end_l)
|
g.emit_label(end_l)
|
||||||
}
|
}
|
||||||
|
.for_range_stmt {
|
||||||
|
// for i in a..b / for i in a...b → i := a; while i <(<=) b { body; i++ }
|
||||||
|
var_idx := g.new_local()
|
||||||
|
bound_idx := g.new_local()
|
||||||
|
loop_l := g.new_label()
|
||||||
|
end_l := g.new_label()
|
||||||
|
g.gen_expr(st.expr)!
|
||||||
|
g.gen_expr(st.cond)!
|
||||||
|
g.emit_store(bound_idx)
|
||||||
|
g.emit_store(var_idx)
|
||||||
|
g.emit_label(loop_l)
|
||||||
|
g.emit_load(var_idx)
|
||||||
|
g.emit_load(bound_idx)
|
||||||
|
g.code << if st.inclusive { op_le } else { op_lt }
|
||||||
|
g.code << op_jz
|
||||||
|
g.code << obj.encode_i64(0)
|
||||||
|
g.fixups << Fixup{ name: end_l, off: u32(g.code.len) - 8 }
|
||||||
|
prev := g.locals[st.target] or { -1 }
|
||||||
|
g.locals[st.target] = var_idx
|
||||||
|
for s in st.body {
|
||||||
|
g.gen_stmt(s)!
|
||||||
|
}
|
||||||
|
if prev >= 0 {
|
||||||
|
g.locals[st.target] = prev
|
||||||
|
} else {
|
||||||
|
g.locals.delete(st.target)
|
||||||
|
}
|
||||||
|
g.emit_load(var_idx)
|
||||||
|
g.code << op_push_i
|
||||||
|
g.code << obj.encode_i64(1)
|
||||||
|
g.code << op_add
|
||||||
|
g.emit_store(var_idx)
|
||||||
|
g.code << op_jmp
|
||||||
|
g.code << obj.encode_i64(0)
|
||||||
|
g.fixups << Fixup{ name: loop_l, off: u32(g.code.len) - 8 }
|
||||||
|
g.emit_label(end_l)
|
||||||
|
}
|
||||||
|
.for_in_stmt {
|
||||||
|
// for x in arr → idx := 0; while idx < len(arr) { x := arr[idx]; body; idx++ }
|
||||||
|
arr_idx := g.new_local()
|
||||||
|
idx_idx := g.new_local()
|
||||||
|
elem_idx := g.new_local()
|
||||||
|
loop_l := g.new_label()
|
||||||
|
end_l := g.new_label()
|
||||||
|
g.gen_expr(st.expr)!
|
||||||
|
g.emit_store(arr_idx)
|
||||||
|
g.code << op_push_i
|
||||||
|
g.code << obj.encode_i64(0)
|
||||||
|
g.emit_store(idx_idx)
|
||||||
|
g.emit_label(loop_l)
|
||||||
|
g.emit_load(idx_idx)
|
||||||
|
g.emit_load(arr_idx)
|
||||||
|
g.code << op_alen
|
||||||
|
g.code << op_lt
|
||||||
|
g.code << op_jz
|
||||||
|
g.code << obj.encode_i64(0)
|
||||||
|
g.fixups << Fixup{ name: end_l, off: u32(g.code.len) - 8 }
|
||||||
|
g.emit_load(arr_idx)
|
||||||
|
g.emit_load(idx_idx)
|
||||||
|
g.code << op_aget
|
||||||
|
g.emit_store(elem_idx)
|
||||||
|
prev := g.locals[st.target] or { -1 }
|
||||||
|
g.locals[st.target] = elem_idx
|
||||||
|
for s in st.body {
|
||||||
|
g.gen_stmt(s)!
|
||||||
|
}
|
||||||
|
if prev >= 0 {
|
||||||
|
g.locals[st.target] = prev
|
||||||
|
} else {
|
||||||
|
g.locals.delete(st.target)
|
||||||
|
}
|
||||||
|
g.emit_load(idx_idx)
|
||||||
|
g.code << op_push_i
|
||||||
|
g.code << obj.encode_i64(1)
|
||||||
|
g.code << op_add
|
||||||
|
g.emit_store(idx_idx)
|
||||||
|
g.code << op_jmp
|
||||||
|
g.code << obj.encode_i64(0)
|
||||||
|
g.fixups << Fixup{ name: loop_l, off: u32(g.code.len) - 8 }
|
||||||
|
g.emit_label(end_l)
|
||||||
|
}
|
||||||
.ret_stmt {
|
.ret_stmt {
|
||||||
if st.has_val {
|
if st.has_val {
|
||||||
g.gen_expr(st.expr)!
|
g.gen_expr(st.expr)!
|
||||||
@@ -214,6 +306,18 @@ fn (mut g Gen) gen_expr(e Expr) ! {
|
|||||||
g.code << obj.encode_i64(0)
|
g.code << obj.encode_i64(0)
|
||||||
g.relocs << obj.Reloc{ offset: u32(g.code.len) - 8, name: e.str_v, kind: 1 }
|
g.relocs << obj.Reloc{ offset: u32(g.code.len) - 8, name: e.str_v, kind: 1 }
|
||||||
}
|
}
|
||||||
|
.array_lit {
|
||||||
|
for el in e.elems {
|
||||||
|
g.gen_expr(el)!
|
||||||
|
}
|
||||||
|
g.code << op_mkarray
|
||||||
|
g.code << obj.encode_i64(i64(e.elems.len))
|
||||||
|
}
|
||||||
|
.index {
|
||||||
|
g.gen_expr(*e.left)!
|
||||||
|
g.gen_expr(*e.right)!
|
||||||
|
g.code << op_aget
|
||||||
|
}
|
||||||
.bool_lit {
|
.bool_lit {
|
||||||
g.code << op_push_i
|
g.code << op_push_i
|
||||||
g.code << obj.encode_i64(e.int_v)
|
g.code << obj.encode_i64(e.int_v)
|
||||||
@@ -251,6 +355,23 @@ fn (mut g Gen) gen_call(e Expr) ! {
|
|||||||
g.code << if e.name == 'print' { op_print } else { op_println }
|
g.code << if e.name == 'print' { op_print } else { op_println }
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if e.name == 'len' {
|
||||||
|
if e.args.len != 1 {
|
||||||
|
return error('len() takes exactly one argument (line ${e.line})')
|
||||||
|
}
|
||||||
|
g.gen_expr(e.args[0])!
|
||||||
|
g.code << op_alen
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if e.name == 'push' {
|
||||||
|
if e.args.len != 2 {
|
||||||
|
return error('push() takes exactly two arguments (line ${e.line})')
|
||||||
|
}
|
||||||
|
g.gen_expr(e.args[0])!
|
||||||
|
g.gen_expr(e.args[1])!
|
||||||
|
g.code << op_apush
|
||||||
|
return
|
||||||
|
}
|
||||||
for a in e.args {
|
for a in e.args {
|
||||||
g.gen_expr(a)!
|
g.gen_expr(a)!
|
||||||
}
|
}
|
||||||
@@ -340,6 +461,24 @@ fn (mut g Gen) intern(s string) int {
|
|||||||
return idx
|
return idx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) new_local() int {
|
||||||
|
idx := g.local_cnt
|
||||||
|
g.local_cnt++
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) emit_load(idx int) {
|
||||||
|
g.code << op_load
|
||||||
|
g.code << obj.encode_i64(i64(idx))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) emit_store(idx int) {
|
||||||
|
g.code << op_store
|
||||||
|
g.code << obj.encode_i64(i64(idx))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn (mut g Gen) new_label() string {
|
fn (mut g Gen) new_label() string {
|
||||||
g.next_lbl++
|
g.next_lbl++
|
||||||
return 'L${g.next_lbl}'
|
return 'L${g.next_lbl}'
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ pub enum TokKind {
|
|||||||
rparen
|
rparen
|
||||||
lbrace
|
lbrace
|
||||||
rbrace
|
rbrace
|
||||||
|
lbracket
|
||||||
|
rbracket
|
||||||
comma
|
comma
|
||||||
plus
|
plus
|
||||||
minus
|
minus
|
||||||
@@ -23,11 +25,15 @@ pub enum TokKind {
|
|||||||
gt
|
gt
|
||||||
ge
|
ge
|
||||||
assign
|
assign
|
||||||
|
dotdot
|
||||||
|
dotdotdot
|
||||||
kw_fn
|
kw_fn
|
||||||
kw_let
|
kw_let
|
||||||
kw_if
|
kw_if
|
||||||
kw_else
|
kw_else
|
||||||
kw_while
|
kw_while
|
||||||
|
kw_for
|
||||||
|
kw_in
|
||||||
kw_return
|
kw_return
|
||||||
kw_true
|
kw_true
|
||||||
kw_false
|
kw_false
|
||||||
@@ -127,6 +133,26 @@ fn (mut l Lexer) next() !Tok {
|
|||||||
l.advance()
|
l.advance()
|
||||||
return Tok{ kind: .rbrace, lit: '}', line: line }
|
return Tok{ kind: .rbrace, lit: '}', line: line }
|
||||||
}
|
}
|
||||||
|
`[` {
|
||||||
|
l.advance()
|
||||||
|
return Tok{ kind: .lbracket, lit: '[', line: line }
|
||||||
|
}
|
||||||
|
`]` {
|
||||||
|
l.advance()
|
||||||
|
return Tok{ kind: .rbracket, lit: ']', line: line }
|
||||||
|
}
|
||||||
|
`.` {
|
||||||
|
l.advance()
|
||||||
|
if l.peek() == `.` {
|
||||||
|
l.advance()
|
||||||
|
if l.peek() == `.` {
|
||||||
|
l.advance()
|
||||||
|
return Tok{ kind: .dotdotdot, lit: '...', line: line }
|
||||||
|
}
|
||||||
|
return Tok{ kind: .dotdot, lit: '..', line: line }
|
||||||
|
}
|
||||||
|
return error('unexpected character "." at line ${line}')
|
||||||
|
}
|
||||||
`,` {
|
`,` {
|
||||||
l.advance()
|
l.advance()
|
||||||
return Tok{ kind: .comma, lit: ',', line: line }
|
return Tok{ kind: .comma, lit: ',', line: line }
|
||||||
@@ -223,6 +249,8 @@ fn (mut l Lexer) lex_ident(line int) Tok {
|
|||||||
'if' { TokKind.kw_if }
|
'if' { TokKind.kw_if }
|
||||||
'else' { TokKind.kw_else }
|
'else' { TokKind.kw_else }
|
||||||
'while' { TokKind.kw_while }
|
'while' { TokKind.kw_while }
|
||||||
|
'for' { TokKind.kw_for }
|
||||||
|
'in' { TokKind.kw_in }
|
||||||
'return' { TokKind.kw_return }
|
'return' { TokKind.kw_return }
|
||||||
'true' { TokKind.kw_true }
|
'true' { TokKind.kw_true }
|
||||||
'false' { TokKind.kw_false }
|
'false' { TokKind.kw_false }
|
||||||
|
|||||||
+94
-9
@@ -6,8 +6,11 @@
|
|||||||
// block := '{' stmt* '}'
|
// block := '{' stmt* '}'
|
||||||
// stmt := 'let' IDENT '=' expr
|
// stmt := 'let' IDENT '=' expr
|
||||||
// | IDENT '=' expr
|
// | IDENT '=' expr
|
||||||
|
// | postfix '=' expr (a[i] = v)
|
||||||
// | 'if' cond block ['else' block]
|
// | 'if' cond block ['else' block]
|
||||||
// | 'while' cond block
|
// | 'while' cond block
|
||||||
|
// | 'for' IDENT 'in' range block (range := expr '..' expr | expr '...' expr)
|
||||||
|
// | 'for' IDENT 'in' expr block (iterate an array)
|
||||||
// | 'return' [expr]
|
// | 'return' [expr]
|
||||||
// | 'assert' expr
|
// | 'assert' expr
|
||||||
// | expr
|
// | expr
|
||||||
@@ -27,6 +30,8 @@ pub enum ExprKind {
|
|||||||
str_lit
|
str_lit
|
||||||
bool_lit
|
bool_lit
|
||||||
ident
|
ident
|
||||||
|
array_lit
|
||||||
|
index
|
||||||
unary
|
unary
|
||||||
binary
|
binary
|
||||||
call
|
call
|
||||||
@@ -41,6 +46,7 @@ pub mut:
|
|||||||
op TokKind
|
op TokKind
|
||||||
left &Expr = unsafe { nil }
|
left &Expr = unsafe { nil }
|
||||||
right &Expr = unsafe { nil }
|
right &Expr = unsafe { nil }
|
||||||
|
elems []Expr
|
||||||
args []Expr
|
args []Expr
|
||||||
line int
|
line int
|
||||||
}
|
}
|
||||||
@@ -49,22 +55,28 @@ pub enum StmtKind {
|
|||||||
expr_stmt
|
expr_stmt
|
||||||
let_stmt
|
let_stmt
|
||||||
assign_stmt
|
assign_stmt
|
||||||
|
index_assign
|
||||||
if_stmt
|
if_stmt
|
||||||
while_stmt
|
while_stmt
|
||||||
|
for_range_stmt
|
||||||
|
for_in_stmt
|
||||||
ret_stmt
|
ret_stmt
|
||||||
assert_stmt
|
assert_stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Stmt {
|
pub struct Stmt {
|
||||||
pub mut:
|
pub mut:
|
||||||
kind StmtKind
|
kind StmtKind
|
||||||
target string
|
target string
|
||||||
expr Expr
|
expr Expr
|
||||||
cond Expr
|
cond Expr
|
||||||
body []Stmt
|
base Expr // index_assign: the indexed expression
|
||||||
els []Stmt
|
idx Expr // index_assign: the index expression
|
||||||
has_val bool
|
body []Stmt
|
||||||
line int
|
els []Stmt
|
||||||
|
has_val bool
|
||||||
|
inclusive bool // for_range_stmt: `..` (false) vs `...` (true)
|
||||||
|
line int
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FnDecl {
|
pub struct FnDecl {
|
||||||
@@ -195,6 +207,21 @@ fn (mut p Parser) parse_stmt() !Stmt {
|
|||||||
body := p.parse_block()!
|
body := p.parse_block()!
|
||||||
return Stmt{ kind: .while_stmt, cond: cond, body: body, line: t.line }
|
return Stmt{ kind: .while_stmt, cond: cond, body: body, line: t.line }
|
||||||
}
|
}
|
||||||
|
.kw_for {
|
||||||
|
p.advance()
|
||||||
|
name := p.expect(.ident, 'loop variable')!
|
||||||
|
p.expect(.kw_in, "'in'")!
|
||||||
|
first := p.parse_expr()!
|
||||||
|
if p.cur().kind == .dotdot || p.cur().kind == .dotdotdot {
|
||||||
|
inclusive := p.cur().kind == .dotdotdot
|
||||||
|
p.advance()
|
||||||
|
end := p.parse_expr()!
|
||||||
|
body := p.parse_block()!
|
||||||
|
return Stmt{ kind: .for_range_stmt, target: name.lit, expr: first, cond: end, inclusive: inclusive, body: body, line: t.line }
|
||||||
|
}
|
||||||
|
body := p.parse_block()!
|
||||||
|
return Stmt{ kind: .for_in_stmt, target: name.lit, expr: first, body: body, line: t.line }
|
||||||
|
}
|
||||||
.kw_return {
|
.kw_return {
|
||||||
p.advance()
|
p.advance()
|
||||||
mut e := Expr{}
|
mut e := Expr{}
|
||||||
@@ -222,6 +249,16 @@ fn (mut p Parser) parse_stmt() !Stmt {
|
|||||||
e := p.parse_expr()!
|
e := p.parse_expr()!
|
||||||
return Stmt{ kind: .assign_stmt, target: t.lit, expr: e, line: t.line }
|
return Stmt{ kind: .assign_stmt, target: t.lit, expr: e, line: t.line }
|
||||||
}
|
}
|
||||||
|
if p.cur().kind == .lbracket {
|
||||||
|
// a[i] = v or a[i] (expression statement)
|
||||||
|
e := p.parse_index_chain(t)!
|
||||||
|
if p.cur().kind == .assign {
|
||||||
|
p.advance()
|
||||||
|
rhs := p.parse_expr()!
|
||||||
|
return Stmt{ kind: .index_assign, base: *e.left, idx: *e.right, expr: rhs, line: t.line }
|
||||||
|
}
|
||||||
|
return Stmt{ kind: .expr_stmt, expr: e, line: t.line }
|
||||||
|
}
|
||||||
e := p.parse_call_or_ident(t)!
|
e := p.parse_call_or_ident(t)!
|
||||||
return Stmt{ kind: .expr_stmt, expr: e, line: t.line }
|
return Stmt{ kind: .expr_stmt, expr: e, line: t.line }
|
||||||
}
|
}
|
||||||
@@ -250,6 +287,13 @@ fn unary_node(op TokKind, operand Expr, line int) Expr {
|
|||||||
return Expr{ kind: .unary, op: op, right: &o, line: line }
|
return Expr{ kind: .unary, op: op, right: &o, line: line }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// index_node builds `base[idx]`.
|
||||||
|
fn index_node(base Expr, idx Expr, line int) Expr {
|
||||||
|
mut b := base
|
||||||
|
mut i := idx
|
||||||
|
return Expr{ kind: .index, left: &b, right: &i, line: line }
|
||||||
|
}
|
||||||
|
|
||||||
fn (mut p Parser) parse_expr() !Expr {
|
fn (mut p Parser) parse_expr() !Expr {
|
||||||
return p.parse_or()!
|
return p.parse_or()!
|
||||||
}
|
}
|
||||||
@@ -321,7 +365,32 @@ fn (mut p Parser) parse_unary() !Expr {
|
|||||||
e := p.parse_unary()!
|
e := p.parse_unary()!
|
||||||
return unary_node(t.kind, e, t.line)
|
return unary_node(t.kind, e, t.line)
|
||||||
}
|
}
|
||||||
return p.parse_primary()!
|
return p.parse_postfix()!
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse_postfix handles indexing: `base[expr]`, possibly chained `a[i][j]`.
|
||||||
|
fn (mut p Parser) parse_postfix() !Expr {
|
||||||
|
mut e := p.parse_primary()!
|
||||||
|
for p.cur().kind == .lbracket {
|
||||||
|
p.advance()
|
||||||
|
idx := p.parse_expr()!
|
||||||
|
p.expect(.rbracket, "']'")!
|
||||||
|
e = index_node(e, idx, e.line)
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse_index_chain is like parse_postfix but starts from an already-consumed
|
||||||
|
// identifier token (used for statements like `a[i] = v`).
|
||||||
|
fn (mut p Parser) parse_index_chain(t Tok) !Expr {
|
||||||
|
mut e := Expr{ kind: .ident, name: t.lit, line: t.line }
|
||||||
|
for p.cur().kind == .lbracket {
|
||||||
|
p.advance()
|
||||||
|
idx := p.parse_expr()!
|
||||||
|
p.expect(.rbracket, "']'")!
|
||||||
|
e = index_node(e, idx, t.line)
|
||||||
|
}
|
||||||
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut p Parser) parse_primary() !Expr {
|
fn (mut p Parser) parse_primary() !Expr {
|
||||||
@@ -349,6 +418,22 @@ fn (mut p Parser) parse_primary() !Expr {
|
|||||||
p.expect(.rparen, "')'")!
|
p.expect(.rparen, "')'")!
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
.lbracket {
|
||||||
|
p.advance()
|
||||||
|
mut elems := []Expr{}
|
||||||
|
if p.cur().kind != .rbracket {
|
||||||
|
for {
|
||||||
|
elems << p.parse_expr()!
|
||||||
|
if p.cur().kind == .comma {
|
||||||
|
p.advance()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.expect(.rbracket, "']'")!
|
||||||
|
return Expr{ kind: .array_lit, elems: elems, line: t.line }
|
||||||
|
}
|
||||||
.ident {
|
.ident {
|
||||||
p.advance()
|
p.advance()
|
||||||
return p.parse_call_or_ident(t)!
|
return p.parse_call_or_ident(t)!
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
// arrays.vr — arrays and for loops.
|
||||||
|
// Run with: vr run examples/arrays.vr
|
||||||
|
|
||||||
|
fn sum(items) {
|
||||||
|
let total = 0
|
||||||
|
for x in items {
|
||||||
|
total = total + x
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// array literal
|
||||||
|
let a = [10, 20, 30]
|
||||||
|
println(a) // [10, 20, 30]
|
||||||
|
println(len(a)) // 3
|
||||||
|
|
||||||
|
// indexing: read and write
|
||||||
|
println(a[0]) // 10
|
||||||
|
a[1] = 99
|
||||||
|
println(a) // [10, 99, 30]
|
||||||
|
|
||||||
|
// growing with push (mutates in place)
|
||||||
|
push(a, 40)
|
||||||
|
println(a) // [10, 99, 30, 40]
|
||||||
|
println(len(a)) // 4
|
||||||
|
|
||||||
|
// iterate the array
|
||||||
|
let total = sum(a)
|
||||||
|
println("sum = ")
|
||||||
|
println(total) // 179
|
||||||
|
|
||||||
|
// range for loops
|
||||||
|
for i in 0..5 {
|
||||||
|
println("i = " + i) // 0 1 2 3 4
|
||||||
|
}
|
||||||
|
|
||||||
|
for n in 1...3 {
|
||||||
|
println("n = " + n) // 1 2 3 (inclusive)
|
||||||
|
}
|
||||||
|
|
||||||
|
// nested loops
|
||||||
|
let grid = [[1, 2], [3, 4]]
|
||||||
|
for row in grid {
|
||||||
|
for cell in row {
|
||||||
|
print(cell)
|
||||||
|
print(" ")
|
||||||
|
}
|
||||||
|
println("")
|
||||||
|
}
|
||||||
|
println(grid[1][0]) // 3
|
||||||
|
|
||||||
|
// build an array in a loop
|
||||||
|
let squares = []
|
||||||
|
for i in 0..5 {
|
||||||
|
push(squares, i * i)
|
||||||
|
}
|
||||||
|
println(squares) // [0, 1, 4, 9, 16]
|
||||||
|
|
||||||
|
// loop variables are scoped to the loop
|
||||||
|
let i = 100
|
||||||
|
for i in 0..3 {
|
||||||
|
println(i)
|
||||||
|
}
|
||||||
|
println("i is back to ")
|
||||||
|
println(i) // 100
|
||||||
|
|
||||||
|
assert sum([1, 2, 3, 4]) == 10
|
||||||
|
assert len(squares) == 5
|
||||||
|
assert squares[4] == 16
|
||||||
|
assert a == a // arrays compare by identity
|
||||||
|
println("all array asserts passed")
|
||||||
|
}
|
||||||
@@ -35,6 +35,49 @@ fn test_booleans() {
|
|||||||
assert not (x == 0)
|
assert not (x == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_arrays() {
|
||||||
|
let a = [1, 2, 3]
|
||||||
|
assert len(a) == 3
|
||||||
|
assert a[0] == 1
|
||||||
|
assert a[2] == 3
|
||||||
|
a[1] = 42
|
||||||
|
assert a[1] == 42
|
||||||
|
push(a, 4)
|
||||||
|
assert len(a) == 4
|
||||||
|
assert a[3] == 4
|
||||||
|
let m = [[1, 2], [3, 4]]
|
||||||
|
assert m[1][0] == 3
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_for_range() {
|
||||||
|
let total = 0
|
||||||
|
for i in 0..5 {
|
||||||
|
total = total + i
|
||||||
|
}
|
||||||
|
assert total == 10 // 0+1+2+3+4
|
||||||
|
let inc = 0
|
||||||
|
for i in 1...3 {
|
||||||
|
inc = inc + i
|
||||||
|
}
|
||||||
|
assert inc == 6 // 1+2+3 (inclusive)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_for_in() {
|
||||||
|
let a = [5, 6, 7]
|
||||||
|
let total = 0
|
||||||
|
for x in a {
|
||||||
|
total = total + x
|
||||||
|
}
|
||||||
|
assert total == 18
|
||||||
|
let squares = []
|
||||||
|
for i in 0..4 {
|
||||||
|
push(squares, i * i)
|
||||||
|
}
|
||||||
|
assert squares == squares // identity comparison
|
||||||
|
assert len(squares) == 4
|
||||||
|
assert squares[3] == 9
|
||||||
|
}
|
||||||
|
|
||||||
fn test_failing() {
|
fn test_failing() {
|
||||||
// this one is meant to fail — shows up in `vr test` output
|
// this one is meant to fail — shows up in `vr test` output
|
||||||
assert 1 == 2
|
assert 1 == 2
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
// vm.v — the VuurRaaf runtime: a small stack-based virtual machine.
|
// vm.v — the VuurRaaf runtime: a small stack-based virtual machine.
|
||||||
//
|
//
|
||||||
// Stack values are 64-bit tagged integers: odd values are string handles into
|
// Stack values are 64-bit tagged integers with two tag bits:
|
||||||
// the runtime string heap (handle = value >> 1); even values are encoded
|
// low bits 00 -> encoded number (value = raw << 2)
|
||||||
// numbers (value = raw << 1). Encoding numbers as even values means no
|
// low bits 01 -> string handle (handle = value >> 2, into v.strings)
|
||||||
// integer ever collides with a string handle.
|
// low bits 11 -> array handle (handle = value >> 2, into v.arrays)
|
||||||
|
// Encoding numbers with a constant shift means no integer ever collides with
|
||||||
|
// a string or array handle.
|
||||||
//
|
//
|
||||||
// Call convention: CALL pushes a frame (retaddr, old bp, argc) and copies the
|
// Call convention: CALL pushes a frame (retaddr, old bp, argc) and copies the
|
||||||
// arguments into the callee's local slots; the callee reserves extra locals
|
// arguments into the callee's local slots; the callee reserves extra locals
|
||||||
@@ -45,6 +47,11 @@ const op_print = u8(28)
|
|||||||
const op_println = u8(29)
|
const op_println = u8(29)
|
||||||
const op_assert = u8(30)
|
const op_assert = u8(30)
|
||||||
const op_enter = u8(31)
|
const op_enter = u8(31)
|
||||||
|
const op_mkarray = u8(32)
|
||||||
|
const op_aget = u8(33)
|
||||||
|
const op_aset = u8(34)
|
||||||
|
const op_alen = u8(35)
|
||||||
|
const op_apush = u8(36)
|
||||||
|
|
||||||
const stack_cap = 65536
|
const stack_cap = 65536
|
||||||
|
|
||||||
@@ -52,6 +59,7 @@ struct Vm {
|
|||||||
mut:
|
mut:
|
||||||
code []u8
|
code []u8
|
||||||
strings []string
|
strings []string
|
||||||
|
arrays [][]i64
|
||||||
stack []i64
|
stack []i64
|
||||||
sp int
|
sp int
|
||||||
bp int
|
bp int
|
||||||
@@ -173,6 +181,9 @@ fn (mut v Vm) exec() ! {
|
|||||||
if v.is_str(a) {
|
if v.is_str(a) {
|
||||||
return error('cannot negate a string')
|
return error('cannot negate a string')
|
||||||
}
|
}
|
||||||
|
if v.is_arr(a) {
|
||||||
|
return error('cannot negate an array')
|
||||||
|
}
|
||||||
v.push(v.enc_int(-v.dec_int(a)))!
|
v.push(v.enc_int(-v.dec_int(a)))!
|
||||||
}
|
}
|
||||||
op_eq {
|
op_eq {
|
||||||
@@ -280,6 +291,60 @@ fn (mut v Vm) exec() ! {
|
|||||||
v.push(0)!
|
v.push(0)!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
op_mkarray {
|
||||||
|
v.ip++
|
||||||
|
n := int(v.read_i64())
|
||||||
|
mut arr := []i64{len: n}
|
||||||
|
for i := n - 1; i >= 0; i-- {
|
||||||
|
arr[i] = v.pop()!
|
||||||
|
}
|
||||||
|
v.arrays << arr
|
||||||
|
v.push(v.mkarr(v.arrays.len - 1))!
|
||||||
|
}
|
||||||
|
op_aget {
|
||||||
|
v.ip++
|
||||||
|
idx := int(v.dec_int(v.pop()!))
|
||||||
|
h := v.pop()!
|
||||||
|
if !v.is_arr(h) || !v.valid_arr_handle(h) {
|
||||||
|
return error('indexing a non-array value')
|
||||||
|
}
|
||||||
|
a := v.arrays[v.hand(h)]
|
||||||
|
if idx < 0 || idx >= a.len {
|
||||||
|
return error('array index ${idx} out of bounds (len ${a.len})')
|
||||||
|
}
|
||||||
|
v.push(a[idx])!
|
||||||
|
}
|
||||||
|
op_aset {
|
||||||
|
v.ip++
|
||||||
|
val := v.pop()!
|
||||||
|
idx := int(v.dec_int(v.pop()!))
|
||||||
|
h := v.pop()!
|
||||||
|
if !v.is_arr(h) || !v.valid_arr_handle(h) {
|
||||||
|
return error('indexing a non-array value')
|
||||||
|
}
|
||||||
|
if idx < 0 || idx >= v.arrays[v.hand(h)].len {
|
||||||
|
return error('array index ${idx} out of bounds (len ${v.arrays[v.hand(h)].len})')
|
||||||
|
}
|
||||||
|
v.arrays[v.hand(h)][idx] = val
|
||||||
|
}
|
||||||
|
op_alen {
|
||||||
|
v.ip++
|
||||||
|
h := v.pop()!
|
||||||
|
if !v.is_arr(h) || !v.valid_arr_handle(h) {
|
||||||
|
return error('len() on a non-array value')
|
||||||
|
}
|
||||||
|
v.push(v.enc_int(i64(v.arrays[v.hand(h)].len)))!
|
||||||
|
}
|
||||||
|
op_apush {
|
||||||
|
v.ip++
|
||||||
|
val := v.pop()!
|
||||||
|
h := v.pop()!
|
||||||
|
if !v.is_arr(h) || !v.valid_arr_handle(h) {
|
||||||
|
return error('push() on a non-array value')
|
||||||
|
}
|
||||||
|
v.arrays[v.hand(h)] << val
|
||||||
|
v.push(h)!
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
return error('unknown opcode ${op} at ip ${v.ip}')
|
return error('unknown opcode ${op} at ip ${v.ip}')
|
||||||
}
|
}
|
||||||
@@ -349,23 +414,31 @@ fn (mut v Vm) ret(with_val bool) ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (mut v Vm) is_str(x i64) bool {
|
fn (mut v Vm) is_str(x i64) bool {
|
||||||
return x & 1 == 1
|
return x & 3 == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut v Vm) is_arr(x i64) bool {
|
||||||
|
return x & 3 == 3
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut v Vm) enc_int(x i64) i64 {
|
fn (mut v Vm) enc_int(x i64) i64 {
|
||||||
return x << 1
|
return x << 2
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut v Vm) dec_int(x i64) i64 {
|
fn (mut v Vm) dec_int(x i64) i64 {
|
||||||
return x >> 1
|
return x >> 2
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut v Vm) hand(x i64) int {
|
fn (mut v Vm) hand(x i64) int {
|
||||||
return int(x >> 1)
|
return int(x >> 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut v Vm) mkstr(idx int) i64 {
|
fn (mut v Vm) mkstr(idx int) i64 {
|
||||||
return (i64(idx) << 1) | 1
|
return (i64(idx) << 2) | 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut v Vm) mkarr(idx int) i64 {
|
||||||
|
return (i64(idx) << 2) | 3
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut v Vm) truthy(x i64) bool {
|
fn (mut v Vm) truthy(x i64) bool {
|
||||||
@@ -377,6 +450,9 @@ fn bool_i64(b bool) i64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (mut v Vm) add(a i64, b i64) !i64 {
|
fn (mut v Vm) add(a i64, b i64) !i64 {
|
||||||
|
if v.is_arr(a) || v.is_arr(b) {
|
||||||
|
return error('cannot add arrays with +')
|
||||||
|
}
|
||||||
if v.is_str(a) && v.is_str(b) {
|
if v.is_str(a) && v.is_str(b) {
|
||||||
return v.alloc_str(v.strings[v.hand(a)] + v.strings[v.hand(b)])
|
return v.alloc_str(v.strings[v.hand(a)] + v.strings[v.hand(b)])
|
||||||
}
|
}
|
||||||
@@ -402,6 +478,9 @@ fn (mut v Vm) arith(a i64, b i64, op string) !i64 {
|
|||||||
if v.is_str(a) || v.is_str(b) {
|
if v.is_str(a) || v.is_str(b) {
|
||||||
return error('cannot use strings with "${op}"')
|
return error('cannot use strings with "${op}"')
|
||||||
}
|
}
|
||||||
|
if v.is_arr(a) || v.is_arr(b) {
|
||||||
|
return error('cannot use arrays with "${op}"')
|
||||||
|
}
|
||||||
x := v.dec_int(a)
|
x := v.dec_int(a)
|
||||||
y := v.dec_int(b)
|
y := v.dec_int(b)
|
||||||
match op {
|
match op {
|
||||||
@@ -430,6 +509,13 @@ fn (mut v Vm) arith(a i64, b i64, op string) !i64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (mut v Vm) cmp(a i64, b i64, op string) !i64 {
|
fn (mut v Vm) cmp(a i64, b i64, op string) !i64 {
|
||||||
|
if v.is_arr(a) || v.is_arr(b) {
|
||||||
|
// arrays compare by identity (handle equality) with ==/!=
|
||||||
|
if op == '==' || op == '!=' {
|
||||||
|
return bool_i64(if op == '==' { a == b } else { a != b })
|
||||||
|
}
|
||||||
|
return error('cannot order arrays')
|
||||||
|
}
|
||||||
if v.is_str(a) && v.is_str(b) {
|
if v.is_str(a) && v.is_str(b) {
|
||||||
sa := v.strings[v.hand(a)]
|
sa := v.strings[v.hand(a)]
|
||||||
sb := v.strings[v.hand(b)]
|
sb := v.strings[v.hand(b)]
|
||||||
@@ -460,11 +546,34 @@ fn (mut v Vm) cmp(a i64, b i64, op string) !i64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (mut v Vm) print_val(x i64) {
|
fn (mut v Vm) print_val(x i64) {
|
||||||
if v.is_str(x) && v.valid_handle(x) {
|
print(v.val_str(x, 0))
|
||||||
print(v.strings[v.hand(x)])
|
}
|
||||||
} else {
|
|
||||||
print(v.dec_int(x))
|
// val_str renders a value: strings as-is, arrays as [a, b, ...] (with a depth
|
||||||
|
// guard so self-referential arrays cannot hang the printer), numbers as ints.
|
||||||
|
fn (mut v Vm) val_str(x i64, depth int) string {
|
||||||
|
if depth > 16 {
|
||||||
|
return '...'
|
||||||
}
|
}
|
||||||
|
if v.is_str(x) && v.valid_handle(x) {
|
||||||
|
return v.strings[v.hand(x)]
|
||||||
|
}
|
||||||
|
if v.is_arr(x) && v.valid_arr_handle(x) {
|
||||||
|
a := v.arrays[v.hand(x)]
|
||||||
|
mut s := '['
|
||||||
|
limit := if a.len > 20 { 20 } else { a.len }
|
||||||
|
for i in 0..limit {
|
||||||
|
if i > 0 {
|
||||||
|
s += ', '
|
||||||
|
}
|
||||||
|
s += v.val_str(a[i], depth + 1)
|
||||||
|
}
|
||||||
|
if a.len > limit {
|
||||||
|
s += ', ...'
|
||||||
|
}
|
||||||
|
return s + ']'
|
||||||
|
}
|
||||||
|
return v.dec_int(x).str()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut v Vm) valid_handle(x i64) bool {
|
fn (mut v Vm) valid_handle(x i64) bool {
|
||||||
@@ -472,6 +581,11 @@ fn (mut v Vm) valid_handle(x i64) bool {
|
|||||||
return h >= 0 && h < v.strings.len
|
return h >= 0 && h < v.strings.len
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut v Vm) valid_arr_handle(x i64) bool {
|
||||||
|
h := v.hand(x)
|
||||||
|
return h >= 0 && h < v.arrays.len
|
||||||
|
}
|
||||||
|
|
||||||
fn (mut v Vm) trace_op(op u8) {
|
fn (mut v Vm) trace_op(op u8) {
|
||||||
name := match op {
|
name := match op {
|
||||||
op_halt { 'halt' }
|
op_halt { 'halt' }
|
||||||
@@ -506,6 +620,11 @@ fn (mut v Vm) trace_op(op u8) {
|
|||||||
op_println { 'println' }
|
op_println { 'println' }
|
||||||
op_assert { 'assert' }
|
op_assert { 'assert' }
|
||||||
op_enter { 'enter' }
|
op_enter { 'enter' }
|
||||||
|
op_mkarray { 'mkarray' }
|
||||||
|
op_aget { 'aget' }
|
||||||
|
op_aset { 'aset' }
|
||||||
|
op_alen { 'alen' }
|
||||||
|
op_apush { 'apush' }
|
||||||
else { '??' }
|
else { '??' }
|
||||||
}
|
}
|
||||||
mut s := ''
|
mut s := ''
|
||||||
@@ -516,7 +635,7 @@ fn (mut v Vm) trace_op(op u8) {
|
|||||||
if v.is_str(v.stack[i]) && v.valid_handle(v.stack[i]) {
|
if v.is_str(v.stack[i]) && v.valid_handle(v.stack[i]) {
|
||||||
s += '"${v.strings[v.hand(v.stack[i])]}"'
|
s += '"${v.strings[v.hand(v.stack[i])]}"'
|
||||||
} else {
|
} else {
|
||||||
s += '${v.dec_int(v.stack[i])}'
|
s += v.val_str(v.stack[i], 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println(' [ip=${v.ip:4}] ${name:-9} stack: [${s}]')
|
println(' [ip=${v.ip:4}] ${name:-9} stack: [${s}]')
|
||||||
|
|||||||
Reference in New Issue
Block a user