This commit is contained in:
Chris Wanstrath 2025-10-05 21:29:30 -07:00
parent 2f2a8fe9f2
commit 8c187a89aa
13 changed files with 333 additions and 0 deletions

91
examples/README.md Normal file
View File

@ -0,0 +1,91 @@
# Bytecode Examples
This directory contains example `.reef` bytecode files demonstrating various features of the ReefVM.
## Running Examples
You can run any example using the reef binary:
```bash
./bin/reef examples/add.reef
```
## Examples
### Basic Operations
- **add.reef** - Simple addition of two numbers
- **simple-function.reef** - Function that adds two parameters
- **arrays.reef** - Array creation and operations (MAKE_ARRAY, ARRAY_PUSH, ARRAY_GET)
- **dicts.reef** - Dictionary operations (MAKE_DICT, DICT_GET)
- **loop.reef** - Simple loop using JUMP and conditional jumps
### Advanced Function Features
- **variadic.reef** - Variadic parameters that collect remaining positional arguments (`...rest`)
- **kwargs.reef** - Named arguments that collect unmatched named args into a dict (`@kwargs`)
- **mixed-variadic-kwargs.reef** - Combining variadic positional and named arguments
- **tail-recursion.reef** - Tail-recursive factorial using TAIL_CALL
- **closure.reef** - Closures that capture variables from outer scope
### Exception Handling
- **exception-handling.reef** - Try-catch-finally blocks with PUSH_TRY, THROW, and PUSH_FINALLY
## Bytecode Syntax
The `.reef` files use the following syntax:
- Comments start with `;`
- Instructions are written as `OPCODE operand`
- Labels use `#N` for relative offsets
- Function signatures: `MAKE_FUNCTION (params) #address`
- Fixed params: `(a b c)`
- Variadic: `(a ...rest)`
- Named args: `(a @kwargs)`
- Mixed: `(a ...rest @kwargs)`
## Function Calling Convention
When calling functions, the stack must contain (bottom to top):
1. The function to call
2. Positional arguments (in order)
3. Named argument key-value pairs (key, value, key, value, ...)
4. Positional argument count
5. Named argument count
Example with positional arguments:
```
LOAD functionName ; push function onto stack
PUSH arg1 ; first positional arg
PUSH arg2 ; second positional arg
PUSH 2 ; positional count
PUSH 0 ; named count
CALL
```
Example with named arguments:
```
LOAD functionName ; push function onto stack
PUSH "key1" ; first named arg key
PUSH "value1" ; first named arg value
PUSH "key2" ; second named arg key
PUSH "value2" ; second named arg value
PUSH 0 ; positional count
PUSH 2 ; named count
CALL
```
Example with mixed arguments:
```
LOAD functionName ; push function onto stack
PUSH arg1 ; positional arg
PUSH "name" ; named arg key
PUSH "Bob" ; named arg value
PUSH 1 ; positional count
PUSH 1 ; named count
CALL
```

16
examples/arrays.reef Normal file
View File

@ -0,0 +1,16 @@
; Array operations example
; Create array, push element, get element
PUSH 10
PUSH 20
PUSH 30
MAKE_ARRAY #3
STORE arr
; Push 40 to array
LOAD arr
PUSH 40
ARRAY_PUSH
; Get element at index 2
LOAD arr
PUSH 2
ARRAY_GET
HALT

24
examples/closure.reef Normal file
View File

@ -0,0 +1,24 @@
; Closure example: counter function that captures state
; outer() returns inner() which increments and returns captured count
MAKE_FUNCTION () #8
PUSH 0
PUSH 0
CALL
STORE counter_fn
LOAD counter_fn
PUSH 0
PUSH 0
CALL
HALT
; Outer function body
PUSH 0
STORE count
MAKE_FUNCTION () #8
RETURN
; Inner function body (closure over count)
LOAD count
PUSH 1
ADD
STORE count
LOAD count
RETURN

14
examples/dicts.reef Normal file
View File

@ -0,0 +1,14 @@
; Dictionary operations example
PUSH 'name'
PUSH 'Alice'
PUSH 'age'
PUSH 30
PUSH 'city'
PUSH 'NYC'
MAKE_DICT #3
STORE person
; Get value by key
LOAD person
PUSH 'name'
DICT_GET
HALT

View File

@ -0,0 +1,19 @@
; Try-catch-finally example
PUSH_TRY #9
PUSH_FINALLY #16
PUSH 'Something went wrong!'
THROW
PUSH 999
POP_TRY
JUMP #4
HALT
; Catch block
STORE err
PUSH 'Caught: '
LOAD err
ADD
HALT
; Finally block
POP
PUSH 'Finally executed'
HALT

13
examples/kwargs.reef Normal file
View File

@ -0,0 +1,13 @@
; Named arguments (kwargs) example
MAKE_FUNCTION (x @kwargs) #9
PUSH 'name'
PUSH 'Alice'
PUSH 'age'
PUSH 30
PUSH 1
PUSH 2
CALL
HALT
; Return the kwargs dict
LOAD kwargs
RETURN

17
examples/loop.reef Normal file
View File

@ -0,0 +1,17 @@
; Loop example: count from 0 to 5
PUSH 0
STORE i
; Loop condition
LOAD i
PUSH 5
LT
JUMP_IF_FALSE #6
; Loop body
LOAD i
PUSH 1
ADD
STORE i
JUMP #-9
; After loop
LOAD i
HALT

View File

@ -0,0 +1,18 @@
; Mixed variadic and named arguments
; Function takes one fixed param, variadic positional, and kwargs
MAKE_FUNCTION (x ...rest @kwargs) #10
PUSH 'name'
PUSH 'Bob'
PUSH 1
PUSH 2
PUSH 3
PUSH 3
PUSH 1
CALL
HALT
; Return array with all three parts
LOAD x
LOAD rest
LOAD kwargs
MAKE_ARRAY #3
RETURN

View File

@ -0,0 +1,12 @@
; Simple function that adds two numbers
MAKE_FUNCTION (a b) #7
PUSH 10
PUSH 20
PUSH 2
PUSH 0
CALL
HALT
LOAD a
LOAD b
ADD
RETURN

View File

@ -0,0 +1,28 @@
; Tail-recursive factorial function
; factorial(n, acc) = if n <= 1 then acc else factorial(n-1, n*acc)
MAKE_FUNCTION (n acc) #21
DUP
PUSH 5
PUSH 1
PUSH 2
PUSH 0
CALL
HALT
; Function body
LOAD n
PUSH 1
LTE
JUMP_IF_FALSE #2
LOAD acc
RETURN
; Tail recursive call
DUP
LOAD n
PUSH 1
SUB
LOAD n
LOAD acc
MUL
PUSH 2
PUSH 0
TAIL_CALL

33
examples/variadic.reef Normal file
View File

@ -0,0 +1,33 @@
; Variadic function that sums all arguments
MAKE_FUNCTION (x ...rest) #19
PUSH 5
PUSH 10
PUSH 15
PUSH 20
PUSH 4
PUSH 0
CALL
HALT
; Function body: sum x and all rest elements
LOAD x
STORE sum
PUSH 0
STORE i
LOAD i
LOAD rest
ARRAY_LEN
LT
JUMP_IF_FALSE #8
LOAD sum
LOAD rest
LOAD i
ARRAY_GET
ADD
STORE sum
LOAD i
PUSH 1
ADD
STORE i
JUMP #-18
LOAD sum
RETURN

View File

@ -103,6 +103,12 @@ export function toBytecode(str: string): Bytecode /* throws */ {
}
for (let line of lines) {
// Strip semicolon comments
const commentIndex = line.indexOf(';')
if (commentIndex !== -1) {
line = line.slice(0, commentIndex)
}
const trimmed = line.trim()
if (!trimmed) continue

View File

@ -144,3 +144,45 @@ test("MAKE_FUNCTION - default with string", async () => {
expect(result).toEqual({ type: 'string', value: 'World' })
})
test("semicolon comments are ignored", () => {
const bytecode = toBytecode(`
; This is a comment
PUSH 1 ; push one
PUSH 5 ; push five
ADD ; add them
`)
expect(bytecode).toEqual({
instructions: [
{ op: OpCode.PUSH, operand: 0 },
{ op: OpCode.PUSH, operand: 1 },
{ op: OpCode.ADD },
],
constants: [
{ type: 'number', value: 1 },
{ type: 'number', value: 5 }
]
})
})
test("semicolon comments work with functions", async () => {
const bytecode = toBytecode(`
MAKE_FUNCTION (x y) #7 ; function with two params
PUSH 10 ; first arg
PUSH 20 ; second arg
PUSH 2 ; positional count
PUSH 0 ; named count
CALL ; call the function
HALT
; Function body starts here
LOAD x ; load first param
LOAD y ; load second param
ADD ; add them
RETURN ; return result
`)
const vm = new VM(bytecode)
const result = await vm.run()
expect(result).toEqual({ type: 'number', value: 30 })
})