diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..fe5a880 --- /dev/null +++ b/examples/README.md @@ -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 +``` diff --git a/examples/arrays.reef b/examples/arrays.reef new file mode 100644 index 0000000..e8b314b --- /dev/null +++ b/examples/arrays.reef @@ -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 diff --git a/examples/closure.reef b/examples/closure.reef new file mode 100644 index 0000000..16c0f45 --- /dev/null +++ b/examples/closure.reef @@ -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 diff --git a/examples/dicts.reef b/examples/dicts.reef new file mode 100644 index 0000000..5e1cc68 --- /dev/null +++ b/examples/dicts.reef @@ -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 diff --git a/examples/exception-handling.reef b/examples/exception-handling.reef new file mode 100644 index 0000000..a86bdc0 --- /dev/null +++ b/examples/exception-handling.reef @@ -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 diff --git a/examples/kwargs.reef b/examples/kwargs.reef new file mode 100644 index 0000000..f062574 --- /dev/null +++ b/examples/kwargs.reef @@ -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 diff --git a/examples/loop.reef b/examples/loop.reef new file mode 100644 index 0000000..2985f37 --- /dev/null +++ b/examples/loop.reef @@ -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 diff --git a/examples/mixed-variadic-kwargs.reef b/examples/mixed-variadic-kwargs.reef new file mode 100644 index 0000000..754ba7a --- /dev/null +++ b/examples/mixed-variadic-kwargs.reef @@ -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 diff --git a/examples/simple-function.reef b/examples/simple-function.reef new file mode 100644 index 0000000..efd2c2c --- /dev/null +++ b/examples/simple-function.reef @@ -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 diff --git a/examples/tail-recursion.reef b/examples/tail-recursion.reef new file mode 100644 index 0000000..caa7af1 --- /dev/null +++ b/examples/tail-recursion.reef @@ -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 diff --git a/examples/variadic.reef b/examples/variadic.reef new file mode 100644 index 0000000..7db8142 --- /dev/null +++ b/examples/variadic.reef @@ -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 diff --git a/src/bytecode.ts b/src/bytecode.ts index 069d527..b754af7 100644 --- a/src/bytecode.ts +++ b/src/bytecode.ts @@ -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 diff --git a/tests/bytecode.test.ts b/tests/bytecode.test.ts index b77482e..dd3ccd6 100644 --- a/tests/bytecode.test.ts +++ b/tests/bytecode.test.ts @@ -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 }) +}) +