Compare commits

...

9 Commits

Author SHA1 Message Date
Kayne Ruse b0387edeb0 Marked the project as open beta 2026-05-25 12:39:30 +10:00
Kayne Ruse 87185210f9 Updated docs 2026-05-25 12:14:22 +10:00
Kayne Ruse d3616b07b2 Added table support to for loops 2026-05-25 12:05:10 +10:00
Kayne Ruse c097dd5514 Removed array.forEach, tweaked error messages 2026-05-25 11:42:43 +10:00
Kayne Ruse e59bcd0572 Fixed a bug in for-loops when compound is modified
It's a simple fix, but it's basically just shadowing the compound's
name:

  var array = [1];
  for (var i in array) array.pushBack(i); //this will fail
2026-05-24 19:45:28 +10:00
Kayne Ruse 254aceadfc Updated quickstart 2026-05-24 19:02:35 +10:00
Kayne Ruse 95362ed6fc Added break and continue support to for loops 2026-05-24 18:53:45 +10:00
Kayne Ruse f11bc95833 For loop on arrays working, untested, read more
Needed: break and continue support, extensive tests, other iterables
2026-05-24 18:13:18 +10:00
Kayne Ruse 813da3e1aa WIP Adding for-loop to parser & compiler 2026-05-21 11:26:59 +10:00
20 changed files with 457 additions and 128 deletions
+23
View File
@@ -0,0 +1,23 @@
```toy
for (var name in array) {
//
}
```
pushStack name
pushStack array
pushStack counter //tracks the index for each loop
OPCODE iterable:
pushScope
declare name = array.next() if next is not null else jump to end
(user code)
popScope
jump to beginning
popStack counter, array, name
-1
View File
@@ -1 +0,0 @@
No hating on other people, OK?
+10 -3
View File
@@ -6,7 +6,7 @@
The Toy Programming Language is an imperative, bytecode-interpreted, embeddable scripting language. Rather than functioning independently, it serves as part of another program, the "host". This design allows for straightforward customization by both the host's developers and end users, achieved by exposing program logic through external scripts. The Toy Programming Language is an imperative, bytecode-interpreted, embeddable scripting language. Rather than functioning independently, it serves as part of another program, the "host". This design allows for straightforward customization by both the host's developers and end users, achieved by exposing program logic through external scripts.
This repository holds the reference implementation for Toy version 2.x, written in C - alpha testing is currently underway. This repository holds the reference implementation for Toy version 2.x, written in C - open beta is currently underway.
# Nifty Features # Nifty Features
@@ -60,6 +60,13 @@ This source code is covered by the Zlib license (see [LICENSE](LICENSE) for deta
# Contributors and Special Thanks # Contributors and Special Thanks
<p align="center">
<image src="noai.png" alt="No AI" width="200px" />
</p>
Contributions via the [GitHub mirror](https://github.com/krgamestudios/Toy) are welcome, but absolutely no AI contributions will be accepted.
"No AI" logo by [Suyash Dwivedi, via wikimedia, CC BY-SA 4.0](https://commons.wikimedia.org/w/index.php?curid=165477595)
@NishiOwO - Unofficial NetBSD support @NishiOwO - Unofficial NetBSD support
@Gipson62 - v1 docs spell checking @Gipson62 - v1 docs spell checking
@8051Enthusiast - `fixAlignment()` trick @8051Enthusiast - `fixAlignment()` trick
@@ -70,6 +77,6 @@ This source code is covered by the Zlib license (see [LICENSE](LICENSE) for deta
Various Anons - Feedback Various Anons - Feedback
@munificent - For [writing the book](http://craftinginterpreters.com/) that sparked my interest in langdev @munificent - For [writing the book](http://craftinginterpreters.com/) that sparked my interest in langdev
# Patreon Supporters # Financial Supporters
You can show your support and be listed here by joining my [Patreon](https://patreon.com/krgamestudios). You can show your support and be listed here by joining my [Patreon](https://patreon.com/krgamestudios), or buying me a coffee at [ko-fi](https://ko-fi.com/krgamestudios).
+5 -1
View File
@@ -44,4 +44,8 @@ while (true) {
This website is a work in progress - for further info, see the official repository: [https://gitea.krgamestudios.com/krgamestudios/Toy](https://gitea.krgamestudios.com/krgamestudios/Toy), or the GitHub mirror: [https://github.com/krgamestudios/Toy](https://github.com/krgamestudios/Toy). This website is a work in progress - for further info, see the official repository: [https://gitea.krgamestudios.com/krgamestudios/Toy](https://gitea.krgamestudios.com/krgamestudios/Toy), or the GitHub mirror: [https://github.com/krgamestudios/Toy](https://github.com/krgamestudios/Toy).
An example of Toy in action: [Vampire Toyvivors](https://gitea.krgamestudios.com/krgamestudios/VampireToyvivors) (a simple "game" used for testing). An example of Toy in action: [Vampire Toyvivors](https://gitea.krgamestudios.com/krgamestudios/VampireToyvivors) (a simple "game" used for testing, not intended for commercial release).
<p align="center">
<image src="img/noai.png" alt="No AI" width="200px" />
</p>
Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

+28 -1
View File
@@ -101,7 +101,34 @@ while (true) {
} }
``` ```
*Note: The `for` loop is coming soon, and will allow for iteration over an array or table, but isn't vital right now.* Alternatively, when iterating over an array or table, you can use the `for` keyword like this. While inside a for-loop, the iterable is inaccessible to prevent run-time modification (it's an ad-hoc bugfix, and will likely be improved later).
```
//Iterate on an array with a for-loop
var array = ["foo", "bar", "buzz", "fizz"];
for (var i in array) {
if (i == "buzz") break; //supports break and continue
print i;
}
```
```
//Also works when printing the values in a table
//However, the order of the values is undefined
var table = [
"Alpha": 1,
"Bravo": 2,
"Charlie": 3,
"Delta": 4,
"Echo": 5,
];
for (var i in table) {
print i; //this will print a number
print table; //this will print null
}
```
## Arrays and Tables ## Arrays and Tables
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

+28 -9
View File
@@ -195,9 +195,24 @@ int inspect_instruction(unsigned char* bytecode, unsigned int pc, unsigned int j
return 4; return 4;
case TOY_OPCODE_ELIMINATE: case TOY_OPCODE_ELIMINATE:
printf(MARKER "ELIMINATE\n", MARKER_VALUE(pc, unsigned char)); printf(MARKER "ELIMINATE %u\n", MARKER_VALUE(pc, unsigned char), (unsigned int)bytecode[pc + 1]);
return 4; return 4;
case TOY_OPCODE_ITERATE:
printf(MARKER TOY_CC_DEBUG "ITERATE on [-2] based on type, with [-1] as counter, then delegate to: JUMP %s%s (%s%d) (GOTO %u)\n" TOY_CC_RESET, MARKER_VALUE(pc, unsigned char),
bytecode[pc + 1] == TOY_OP_PARAM_JUMP_ABSOLUTE ? "absolute" : "relative",
bytecode[pc + 2] == TOY_OP_PARAM_JUMP_ALWAYS ? "" :
bytecode[pc + 2] == TOY_OP_PARAM_JUMP_IF_TRUE ? " if true" :
bytecode[pc + 2] == TOY_OP_PARAM_JUMP_IF_FALSE ? " if false" :
bytecode[pc + 2] == TOY_OP_PARAM_JUMP_IF_NULL ? " if null" :
"if <unknown>"
,
(*(int*)(bytecode + pc + 4)) > 0 ? "+" : "", //show a + sign when positive
(*(int*)(bytecode + pc + 4)),
(*(int*)(bytecode + pc + 4)) + pc + 8
);
return 8;
case TOY_OPCODE_ADD: case TOY_OPCODE_ADD:
printf(MARKER "ADD %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] == TOY_OPCODE_ASSIGN ? "and ASSIGN" : ""); printf(MARKER "ADD %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] == TOY_OPCODE_ASSIGN ? "and ASSIGN" : "");
return 4; return 4;
@@ -262,18 +277,22 @@ int inspect_instruction(unsigned char* bytecode, unsigned int pc, unsigned int j
printf(MARKER TOY_CC_DEBUG "JUMP %s%s (%s%d) (GOTO %u)\n" TOY_CC_RESET, MARKER_VALUE(pc, unsigned char), printf(MARKER TOY_CC_DEBUG "JUMP %s%s (%s%d) (GOTO %u)\n" TOY_CC_RESET, MARKER_VALUE(pc, unsigned char),
bytecode[pc + 1] == TOY_OP_PARAM_JUMP_ABSOLUTE ? "absolute" : "relative", bytecode[pc + 1] == TOY_OP_PARAM_JUMP_ABSOLUTE ? "absolute" : "relative",
bytecode[pc + 2] == TOY_OP_PARAM_JUMP_ALWAYS ? "" : bytecode[pc + 2] == TOY_OP_PARAM_JUMP_ALWAYS ? "" :
bytecode[pc + 2] == TOY_OP_PARAM_JUMP_IF_TRUE ? " if true" : " if false", bytecode[pc + 2] == TOY_OP_PARAM_JUMP_IF_TRUE ? " if true" :
bytecode[pc + 4] > 0 ? "+" : "", //show a + sign when positive bytecode[pc + 2] == TOY_OP_PARAM_JUMP_IF_FALSE ? " if false" :
bytecode[pc + 4], bytecode[pc + 2] == TOY_OP_PARAM_JUMP_IF_NULL ? " if null" :
bytecode[pc + 4] + pc + 8 "if <unknown>"
,
(*(int*)(bytecode + pc + 4)) > 0 ? "+" : "", //show a + sign when positive
(*(int*)(bytecode + pc + 4)),
(*(int*)(bytecode + pc + 4)) + pc + 8
); );
return 8; return 8;
case TOY_OPCODE_ESCAPE: case TOY_OPCODE_ESCAPE:
printf(MARKER TOY_CC_DEBUG "ESCAPE relative %s%d (GOTO %u) and pop %d\n" TOY_CC_RESET, MARKER_VALUE(pc, unsigned char), printf(MARKER TOY_CC_DEBUG "ESCAPE relative %s%d (GOTO %u) and pop %d scopes\n" TOY_CC_RESET, MARKER_VALUE(pc, unsigned char),
bytecode[pc + 4] > 0 ? "+" : "", //show a + sign when positive (*(int*)(bytecode + pc + 4)) > 0 ? "+" : "", //show a + sign when positive
bytecode[pc + 4], (*(int*)(bytecode + pc + 4)),
bytecode[pc + 4] + pc + 12, (*(int*)(bytecode + pc + 4)) + pc + 12,
bytecode[pc + 8] bytecode[pc + 8]
); );
return 12; return 12;
+17 -8
View File
@@ -1,12 +1,21 @@
var table = [
"Alpha": 1,
"Bravo": 2,
"Charlie": 3,
"Delta": 4,
"Echo": 5,
"Foxtrot": 6,
"Golf": 7,
"Hotel": 8,
"India": 9,
"Juliett": 10,
"Kilo": 11,
"Lima": 12,
"Mike": 13,
];
fn a(x) { for (var i in table) {
print x; print i;
} }
fn b() {
return 42;
}
a(b(), b());
+22
View File
@@ -147,6 +147,16 @@ void Toy_private_emitAstWhileThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle
(*astHandle) = tmp; (*astHandle) = tmp;
} }
void Toy_private_emitAstForThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_FOR_THEN;
tmp->forThen.condBranch = condBranch;
tmp->forThen.thenBranch = thenBranch;
(*astHandle) = tmp;
}
void Toy_private_emitAstBreak(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) { void Toy_private_emitAstBreak(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
@@ -244,6 +254,16 @@ void Toy_private_emitAstAttribute(Toy_Bucket** bucketHandle, Toy_Ast** astHandle
(*astHandle) = tmp; (*astHandle) = tmp;
} }
void Toy_private_emitAstIterable(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_ITERABLE;
tmp->iterable.left = (*astHandle);
tmp->iterable.right = expr;
(*astHandle) = tmp;
}
void Toy_private_emitAstStackPop(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) { void Toy_private_emitAstStackPop(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
@@ -293,6 +313,7 @@ const char* Toy_private_getAstTypeAsCString(Toy_AstType type) {
case TOY_AST_ASSERT: return "ASSERT"; case TOY_AST_ASSERT: return "ASSERT";
case TOY_AST_IF_THEN_ELSE: return "IF_THEN_ELSE"; case TOY_AST_IF_THEN_ELSE: return "IF_THEN_ELSE";
case TOY_AST_WHILE_THEN: return "WHILE_THEN"; case TOY_AST_WHILE_THEN: return "WHILE_THEN";
case TOY_AST_FOR_THEN: return "FOR_THEN";
case TOY_AST_BREAK: return "BREAK"; case TOY_AST_BREAK: return "BREAK";
case TOY_AST_CONTINUE: return "CONTINUE"; case TOY_AST_CONTINUE: return "CONTINUE";
case TOY_AST_RETURN: return "RETURN"; case TOY_AST_RETURN: return "RETURN";
@@ -305,6 +326,7 @@ const char* Toy_private_getAstTypeAsCString(Toy_AstType type) {
case TOY_AST_FN_DECLARE: return "FN_DECLARE"; case TOY_AST_FN_DECLARE: return "FN_DECLARE";
case TOY_AST_FN_INVOKE: return "FN_INVOKE"; case TOY_AST_FN_INVOKE: return "FN_INVOKE";
case TOY_AST_ATTRIBUTE: return "ATTRIBUTE"; case TOY_AST_ATTRIBUTE: return "ATTRIBUTE";
case TOY_AST_ITERABLE: return "ITERABLE";
case TOY_AST_STACK_POP: return "STACK_POP"; case TOY_AST_STACK_POP: return "STACK_POP";
+18
View File
@@ -22,6 +22,7 @@ typedef enum Toy_AstType {
TOY_AST_ASSERT, TOY_AST_ASSERT,
TOY_AST_IF_THEN_ELSE, TOY_AST_IF_THEN_ELSE,
TOY_AST_WHILE_THEN, TOY_AST_WHILE_THEN,
TOY_AST_FOR_THEN,
TOY_AST_BREAK, TOY_AST_BREAK,
TOY_AST_CONTINUE, TOY_AST_CONTINUE,
TOY_AST_RETURN, TOY_AST_RETURN,
@@ -34,6 +35,7 @@ typedef enum Toy_AstType {
TOY_AST_FN_DECLARE, TOY_AST_FN_DECLARE,
TOY_AST_FN_INVOKE, TOY_AST_FN_INVOKE,
TOY_AST_ATTRIBUTE, TOY_AST_ATTRIBUTE,
TOY_AST_ITERABLE,
TOY_AST_STACK_POP, //BUGFIX: force a single stack pop for expression statements TOY_AST_STACK_POP, //BUGFIX: force a single stack pop for expression statements
@@ -171,6 +173,12 @@ typedef struct Toy_AstWhileThen {
Toy_Ast* thenBranch; Toy_Ast* thenBranch;
} Toy_AstWhileThen; } Toy_AstWhileThen;
typedef struct Toy_AstForThen {
Toy_AstType type;
Toy_Ast* condBranch;
Toy_Ast* thenBranch;
} Toy_AstForThen;
typedef struct Toy_AstBreak { typedef struct Toy_AstBreak {
Toy_AstType type; Toy_AstType type;
} Toy_AstBreak; } Toy_AstBreak;
@@ -228,6 +236,12 @@ typedef struct Toy_AstAttribute {
Toy_Ast* right; Toy_Ast* right;
} Toy_AstAttribute; } Toy_AstAttribute;
typedef struct Toy_AstIterable {
Toy_AstType type;
Toy_Ast* left;
Toy_Ast* right;
} Toy_AstIterable;
typedef struct Toy_AstStackPop { typedef struct Toy_AstStackPop {
Toy_AstType type; Toy_AstType type;
Toy_Ast* child; Toy_Ast* child;
@@ -259,6 +273,7 @@ union Toy_Ast { //see 'test_ast.c' for bitness tests
Toy_AstAssert assert; Toy_AstAssert assert;
Toy_AstIfThenElse ifThenElse; Toy_AstIfThenElse ifThenElse;
Toy_AstWhileThen whileThen; Toy_AstWhileThen whileThen;
Toy_AstForThen forThen;
Toy_AstBreak breakPoint; Toy_AstBreak breakPoint;
Toy_AstContinue continuePoint; Toy_AstContinue continuePoint;
Toy_AstReturn fnReturn; Toy_AstReturn fnReturn;
@@ -269,6 +284,7 @@ union Toy_Ast { //see 'test_ast.c' for bitness tests
Toy_AstFnDeclare fnDeclare; Toy_AstFnDeclare fnDeclare;
Toy_AstFnInvoke fnInvoke; Toy_AstFnInvoke fnInvoke;
Toy_AstAttribute attribute; Toy_AstAttribute attribute;
Toy_AstIterable iterable;
Toy_AstStackPop stackPop; Toy_AstStackPop stackPop;
Toy_AstPass pass; Toy_AstPass pass;
Toy_AstError error; Toy_AstError error;
@@ -290,6 +306,7 @@ void Toy_private_emitAstAggregate(Toy_Bucket** bucketHandle, Toy_Ast** astHandle
void Toy_private_emitAstAssert(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* child, Toy_Ast* msg); void Toy_private_emitAstAssert(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* child, Toy_Ast* msg);
void Toy_private_emitAstIfThenElse(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch, Toy_Ast* elseBranch); void Toy_private_emitAstIfThenElse(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch, Toy_Ast* elseBranch);
void Toy_private_emitAstWhileThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch); void Toy_private_emitAstWhileThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch);
void Toy_private_emitAstForThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch);
void Toy_private_emitAstBreak(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle); void Toy_private_emitAstBreak(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle);
void Toy_private_emitAstContinue(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle); void Toy_private_emitAstContinue(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle);
void Toy_private_emitAstReturn(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); void Toy_private_emitAstReturn(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
@@ -302,6 +319,7 @@ void Toy_private_emitAstVariableAccess(Toy_Bucket** bucketHandle, Toy_Ast** astH
void Toy_private_emitAstFunctionDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* params, Toy_Ast* body); void Toy_private_emitAstFunctionDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* params, Toy_Ast* body);
void Toy_private_emitAstFunctionInvokation(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* params); void Toy_private_emitAstFunctionInvokation(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* params);
void Toy_private_emitAstAttribute(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr); void Toy_private_emitAstAttribute(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr);
void Toy_private_emitAstIterable(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr);
void Toy_private_emitAstStackPop(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); void Toy_private_emitAstStackPop(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
-78
View File
@@ -90,69 +90,6 @@ static void attr_arrayPopBack(Toy_VM* vm, Toy_FunctionNative* self) {
Toy_pushStack(&vm->stack, element); Toy_pushStack(&vm->stack, element);
} }
static void attr_arrayForEach(Toy_VM* vm, Toy_FunctionNative* self) {
(void)self;
Toy_Value compound = Toy_popStack(&vm->stack);
Toy_Value callback = Toy_popStack(&vm->stack);
if (TOY_VALUE_IS_FUNCTION(callback) != true) {
char buffer[256];
snprintf(buffer, 256, "Expected function, found '%s'", Toy_getValueTypeAsCString(callback.type));
Toy_error(buffer);
return;
}
Toy_Array* array = TOY_VALUE_AS_ARRAY(compound);
Toy_Function* fn = TOY_VALUE_AS_FUNCTION(callback);
//this emulates 'processInvoke' a bit, but not entirely
Toy_VM subVM;
Toy_inheritVM(vm, &subVM);
switch(fn->type) {
case TOY_FUNCTION_CUSTOM: {
//push and run for each element
for (unsigned int iterator = 0; iterator < array->count; iterator++) {
//bind to the subVM (more expensive than I'd like)
Toy_bindVM(&subVM, fn->bytecode.code, fn->bytecode.parentScope);
//get parameter name as a string
unsigned int paramAddr = ((unsigned int*)(subVM.code + subVM.paramAddr))[0];
Toy_ValueType paramType = (Toy_ValueType)(((unsigned int*)(subVM.code + subVM.paramAddr))[1]);
const char* cstr = ((char*)(subVM.code + subVM.dataAddr)) + paramAddr;
Toy_String* name = Toy_toStringLength(&subVM.memoryBucket, cstr, strlen(cstr));
Toy_declareScope(subVM.scope, Toy_copyString(name), paramType, Toy_copyValue(&subVM.memoryBucket, array->data[iterator]), true);
Toy_freeString(name);
Toy_runVM(&subVM);
Toy_resetVM(&subVM, false, true);
subVM.scope = NULL; //BUGFIX: need to clear the scope when iterating
}
}
break;
case TOY_FUNCTION_NATIVE: {
//this uses a subVM for the native function, which is a slight difference than 'processInoke'
for (unsigned int iterator = 0; iterator < array->count; iterator++) {
Toy_pushStack(&subVM.stack, Toy_copyValue(&subVM.memoryBucket, array->data[iterator]));
fn->native.callback(&subVM, &fn->native); //NOTE: try not to leave anything on the stack afterwards
}
}
break;
default:
Toy_error("Can't call an unknown function type in 'forEach'");
break;
}
//cleanup
Toy_freeVM(&subVM);
}
static void attr_arraySort(Toy_VM* vm, Toy_FunctionNative* self) { static void attr_arraySort(Toy_VM* vm, Toy_FunctionNative* self) {
(void)vm; (void)vm;
(void)self; (void)self;
@@ -172,10 +109,6 @@ Toy_Value Toy_private_handleArrayAttributes(Toy_VM* vm, Toy_Value compound, Toy_
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arrayPopBack); Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arrayPopBack);
return TOY_VALUE_FROM_FUNCTION(fn); return TOY_VALUE_FROM_FUNCTION(fn);
} }
else if (MATCH_VALUE_AND_CSTRING(attribute, "forEach")) {
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arrayForEach);
return TOY_VALUE_FROM_FUNCTION(fn);
}
else if (MATCH_VALUE_AND_CSTRING(attribute, "sort")) { else if (MATCH_VALUE_AND_CSTRING(attribute, "sort")) {
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arraySort); Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arraySort);
return TOY_VALUE_FROM_FUNCTION(fn); return TOY_VALUE_FROM_FUNCTION(fn);
@@ -237,13 +170,6 @@ static void attr_tableRemove(Toy_VM* vm, Toy_FunctionNative* self) {
Toy_removeTable(&table, key); Toy_removeTable(&table, key);
} }
static void attr_tableForEach(Toy_VM* vm, Toy_FunctionNative* self) {
(void)vm;
(void)self;
//URGENT: attr_tableForEach
}
Toy_Value Toy_private_handleTableAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) { Toy_Value Toy_private_handleTableAttributes(Toy_VM* vm, Toy_Value compound, Toy_Value attribute) {
if (MATCH_VALUE_AND_CSTRING(attribute, "length")) { if (MATCH_VALUE_AND_CSTRING(attribute, "length")) {
return TOY_VALUE_FROM_INTEGER(TOY_VALUE_AS_ARRAY(compound)->count); return TOY_VALUE_FROM_INTEGER(TOY_VALUE_AS_ARRAY(compound)->count);
@@ -260,10 +186,6 @@ Toy_Value Toy_private_handleTableAttributes(Toy_VM* vm, Toy_Value compound, Toy_
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_tableRemove); Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_tableRemove);
return TOY_VALUE_FROM_FUNCTION(fn); return TOY_VALUE_FROM_FUNCTION(fn);
} }
else if (MATCH_VALUE_AND_CSTRING(attribute, "forEach")) {
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_tableForEach);
return TOY_VALUE_FROM_FUNCTION(fn);
}
else { else {
char buffer[256]; char buffer[256];
snprintf(buffer, 256, "Unknown attribute '%s' of type '%s'", TOY_VALUE_AS_STRING(attribute)->leaf.data, Toy_getValueTypeAsCString(compound.type)); snprintf(buffer, 256, "Unknown attribute '%s' of type '%s'", TOY_VALUE_AS_STRING(attribute)->leaf.data, Toy_getValueTypeAsCString(compound.type));
+130 -2
View File
@@ -226,6 +226,7 @@ static unsigned int writeBytecodeFromAst(Toy_Bytecode** mb, Toy_Ast* ast); //for
static void writeBytecodeBody(Toy_Bytecode* mb, Toy_Ast* ast); static void writeBytecodeBody(Toy_Bytecode* mb, Toy_Ast* ast);
static unsigned char* collateBytecodeBody(Toy_Bytecode* mb); static unsigned char* collateBytecodeBody(Toy_Bytecode* mb);
static unsigned int writeInstructionAssign(Toy_Bytecode** mb, Toy_AstVarAssign ast, bool chainedAssignment); //forward declare for chaining of var declarations static unsigned int writeInstructionAssign(Toy_Bytecode** mb, Toy_AstVarAssign ast, bool chainedAssignment); //forward declare for chaining of var declarations
static unsigned int writeInstructionAccess(Toy_Bytecode** mb, Toy_AstVarAccess ast);
static unsigned int writeInstructionFnInvoke(Toy_Bytecode** mb, Toy_AstFnInvoke ast, bool chainedInvoke); static unsigned int writeInstructionFnInvoke(Toy_Bytecode** mb, Toy_AstFnInvoke ast, bool chainedInvoke);
static unsigned int writeInstructionValue(Toy_Bytecode** mb, Toy_AstValue ast) { static unsigned int writeInstructionValue(Toy_Bytecode** mb, Toy_AstValue ast) {
@@ -456,7 +457,7 @@ static unsigned int writeInstructionBinaryShortCircuit(Toy_Bytecode** mb, Toy_As
//if the lhs value isn't needed, pop it //if the lhs value isn't needed, pop it
EMIT_BYTE(mb, code,TOY_OPCODE_ELIMINATE); EMIT_BYTE(mb, code,TOY_OPCODE_ELIMINATE);
EMIT_BYTE(mb, code, 0); EMIT_BYTE(mb, code, 1);
EMIT_BYTE(mb, code, 0); EMIT_BYTE(mb, code, 0);
EMIT_BYTE(mb, code, 0); EMIT_BYTE(mb, code, 0);
@@ -718,6 +719,123 @@ static unsigned int writeInstructionWhileThen(Toy_Bytecode** mb, Toy_AstWhileThe
return 0; return 0;
} }
static unsigned int writeInstructionForThen(Toy_Bytecode** mb, Toy_AstForThen ast) {
//check the operands
if (ast.condBranch->type != TOY_AST_ITERABLE || ast.condBranch->iterable.left->type != TOY_AST_VAR_DECLARE || ast.condBranch->iterable.right->type != TOY_AST_VAR_ACCESS) {
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Invalid conditional found in a 'for' loop\n" TOY_CC_RESET);
(*mb)->panic = true;
return 0;
}
//set up the iterable, and the counter
writeInstructionAccess(mb, ast.condBranch->iterable.right->varAccess);
EMIT_BYTE(mb, code, TOY_OPCODE_READ);
EMIT_BYTE(mb, code, TOY_VALUE_INTEGER);
EMIT_BYTE(mb, code, 0);
EMIT_BYTE(mb, code, 0);
EMIT_INT(mb, code, 0); //start from zero
unsigned int beginAddr = CURRENT_ADDRESS(mb, code);
//access [-1] from [-2], incrementing [-1] afterwards
//then delegate to JUMP
EMIT_BYTE(mb, code, TOY_OPCODE_ITERATE);
EMIT_BYTE(mb, code, TOY_OP_PARAM_JUMP_RELATIVE);
EMIT_BYTE(mb, code, TOY_OP_PARAM_JUMP_IF_NULL);
EMIT_BYTE(mb, code, 0);
unsigned int thenParamAddr = SKIP_INT(mb, code); //parameter to be written later
//push scope (built-into the keyword)
EMIT_BYTE(mb, code, TOY_OPCODE_SCOPE_PUSH);
EMIT_BYTE(mb, code, 0);
EMIT_BYTE(mb, code, 0);
EMIT_BYTE(mb, code, 0);
(*mb)->currentScopeDepth++;
//delcare the iterator with the given string
EMIT_BYTE(mb, code, TOY_OPCODE_DECLARE);
EMIT_BYTE(mb, code, ast.condBranch->iterable.left->varDeclare.valueType);
EMIT_BYTE(mb, code, ast.condBranch->iterable.left->varDeclare.name->info.length); //quick optimisation to skip a 'strlen()' call
EMIT_BYTE(mb, code, ast.condBranch->iterable.left->varDeclare.constant); //check for constness
emitString(mb, ast.condBranch->iterable.left->varDeclare.name);
//TODO: use a different approach
//BUGFIX: shadow the iterable's name
EMIT_BYTE(mb, code, TOY_OPCODE_READ);
EMIT_BYTE(mb, code, TOY_VALUE_NULL);
EMIT_BYTE(mb, code, 0);
EMIT_BYTE(mb, code, 0);
EMIT_BYTE(mb, code, TOY_OPCODE_DECLARE);
EMIT_BYTE(mb, code, TOY_VALUE_NULL);
EMIT_BYTE(mb, code, TOY_VALUE_AS_STRING(ast.condBranch->iterable.right->varAccess.child->value.value)->info.length);
EMIT_BYTE(mb, code, true); //check for constness
emitString(mb, TOY_VALUE_AS_STRING(ast.condBranch->iterable.right->varAccess.child->value.value));
//write the body
writeBytecodeFromAst(mb, ast.thenBranch);
//pop scope after each iteration
EMIT_BYTE(mb, code, TOY_OPCODE_SCOPE_POP);
EMIT_BYTE(mb, code, 0);
EMIT_BYTE(mb, code, 0);
EMIT_BYTE(mb, code, 0);
(*mb)->currentScopeDepth--;
//jump to begin to repeat the conditional test
EMIT_BYTE(mb, code, TOY_OPCODE_JUMP);
EMIT_BYTE(mb, code, TOY_OP_PARAM_JUMP_RELATIVE);
EMIT_BYTE(mb, code, TOY_OP_PARAM_JUMP_ALWAYS);
EMIT_BYTE(mb, code, 0);
EMIT_INT(mb, code, beginAddr - (CURRENT_ADDRESS(mb, code) + 4)); //this sets a negative value
//end of the loop, overwrite the parameter
OVERWRITE_INT(mb, code, thenParamAddr, CURRENT_ADDRESS(mb, code) - (thenParamAddr + 4));
//set the break & continue data
while ((*mb)->breakEscapes->count > 0) {
//extract
unsigned int addr = (*mb)->breakEscapes->data[(*mb)->breakEscapes->count - 1].addr;
unsigned int depth = (*mb)->breakEscapes->data[(*mb)->breakEscapes->count - 1].depth;
unsigned int diff = depth - (*mb)->currentScopeDepth;
OVERWRITE_INT(mb, code, addr, CURRENT_ADDRESS(mb, code) - (addr + 8)); //tell break to come here AFTER reading the instruction
OVERWRITE_INT(mb, code, addr, diff);
//tick down
(*mb)->breakEscapes->count--;
}
while ((*mb)->continueEscapes->count > 0) {
//extract
unsigned int addr = (*mb)->continueEscapes->data[(*mb)->continueEscapes->count - 1].addr;
unsigned int depth = (*mb)->continueEscapes->data[(*mb)->continueEscapes->count - 1].depth;
unsigned int diff = depth - (*mb)->currentScopeDepth;
OVERWRITE_INT(mb, code, addr, beginAddr - (addr + 8)); //tell continue to return to the start AFTER reading the instruction
OVERWRITE_INT(mb, code, addr, diff);
//tick down
(*mb)->continueEscapes->count--;
}
//eliminate the value & counter from within the bytecode, so they're cleaned up after breaks
EMIT_BYTE(mb, code,TOY_OPCODE_ELIMINATE);
EMIT_BYTE(mb, code, 2);
EMIT_BYTE(mb, code, 0);
EMIT_BYTE(mb, code, 0);
return 0;
}
static unsigned int writeInstructionBreak(Toy_Bytecode** mb, Toy_AstBreak ast) { static unsigned int writeInstructionBreak(Toy_Bytecode** mb, Toy_AstBreak ast) {
//unused //unused
(void)ast; (void)ast;
@@ -1154,7 +1272,7 @@ static unsigned int writeInstructionStackPop(Toy_Bytecode** mb, Toy_AstStackPop
//dead simple //dead simple
EMIT_BYTE(mb, code,TOY_OPCODE_ELIMINATE); EMIT_BYTE(mb, code,TOY_OPCODE_ELIMINATE);
EMIT_BYTE(mb, code, 0); EMIT_BYTE(mb, code, 1);
EMIT_BYTE(mb, code, 0); EMIT_BYTE(mb, code, 0);
EMIT_BYTE(mb, code, 0); EMIT_BYTE(mb, code, 0);
@@ -1244,6 +1362,10 @@ static unsigned int writeBytecodeFromAst(Toy_Bytecode** mb, Toy_Ast* ast) {
result += writeInstructionWhileThen(mb, ast->whileThen); result += writeInstructionWhileThen(mb, ast->whileThen);
break; break;
case TOY_AST_FOR_THEN:
result += writeInstructionForThen(mb, ast->forThen);
break;
case TOY_AST_BREAK: case TOY_AST_BREAK:
result += writeInstructionBreak(mb, ast->breakPoint); result += writeInstructionBreak(mb, ast->breakPoint);
break; break;
@@ -1284,6 +1406,12 @@ static unsigned int writeBytecodeFromAst(Toy_Bytecode** mb, Toy_Ast* ast) {
result += writeInstructionAttribute(mb, ast->attribute); result += writeInstructionAttribute(mb, ast->attribute);
break; break;
case TOY_AST_ITERABLE:
//the 'in' keyword is only valid within a for-loop's condition
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: the 'in' keyword is only valid within a for-loop's condition\n" TOY_CC_RESET);
(*mb)->panic = true;
break;
case TOY_AST_STACK_POP: case TOY_AST_STACK_POP:
result += writeInstructionStackPop(mb, ast->stackPop); result += writeInstructionStackPop(mb, ast->stackPop);
break; break;
+2
View File
@@ -14,6 +14,7 @@ typedef enum Toy_OpcodeType {
TOY_OPCODE_ATTRIBUTE, //for accessing parts of compounds TOY_OPCODE_ATTRIBUTE, //for accessing parts of compounds
TOY_OPCODE_DUPLICATE, //duplicate the top of the stack TOY_OPCODE_DUPLICATE, //duplicate the top of the stack
TOY_OPCODE_ELIMINATE, //remove the top of the stack TOY_OPCODE_ELIMINATE, //remove the top of the stack
TOY_OPCODE_ITERATE, //for operating on all members of a compound
//arithmetic instructions //arithmetic instructions
TOY_OPCODE_ADD, TOY_OPCODE_ADD,
@@ -66,5 +67,6 @@ typedef enum Toy_OpParamJumpConditional {
TOY_OP_PARAM_JUMP_ALWAYS = 0, TOY_OP_PARAM_JUMP_ALWAYS = 0,
TOY_OP_PARAM_JUMP_IF_TRUE = 1, TOY_OP_PARAM_JUMP_IF_TRUE = 1,
TOY_OP_PARAM_JUMP_IF_FALSE = 2, TOY_OP_PARAM_JUMP_IF_FALSE = 2,
TOY_OP_PARAM_JUMP_IF_NULL = 3,
} Toy_OpParamJumpConditional; } Toy_OpParamJumpConditional;
+56 -3
View File
@@ -771,7 +771,7 @@ static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_A
if (prefix == NULL) { if (prefix == NULL) {
//make a nice error message //make a nice error message
if (Toy_private_findKeywordByType(parser->previous.type)) { if (Toy_private_findKeywordByType(parser->previous.type)) {
printError(parser, parser->previous, "Found reserved keyword instead"); printError(parser, parser->previous, "Expected expression, found reserved keyword instead");
} }
else { else {
printError(parser, parser->previous, "Expected expression"); printError(parser, parser->previous, "Expected expression");
@@ -891,6 +891,55 @@ static void makeWhileStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast
Toy_private_emitAstWhileThen(bucketHandle, rootHandle, condBranch, thenBranch); Toy_private_emitAstWhileThen(bucketHandle, rootHandle, condBranch, thenBranch);
} }
static void makeForStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
Toy_Ast* declBranch = NULL;
Toy_Ast* condBranch = NULL;
Toy_Ast* thenBranch = NULL;
//for (condBranch)
consume(parser, TOY_TOKEN_OPERATOR_PAREN_LEFT, "Expected '(' after 'for' keyword");
consume(parser, TOY_TOKEN_KEYWORD_VAR, "Expected 'var' in 'for' conditional");
//WARN: duped from var declare
consume(parser, TOY_TOKEN_NAME, "Expected variable name after 'var' keyword");
if (parser->previous.length > 255) {
printError(parser, parser->previous, "Can't have a variable name longer than 255 characters");
Toy_private_emitAstError(bucketHandle, rootHandle);
return;
}
Toy_Token nameToken = parser->previous;
//read the type specifier if present
Toy_ValueType varType = TOY_VALUE_ANY;
bool constant = false;
if (match(parser, TOY_TOKEN_OPERATOR_COLON)) {
varType = readType(parser);
if (match(parser, TOY_TOKEN_KEYWORD_CONST)) {
constant = true;
}
}
//build the name string & emit a var declare
Toy_String* nameStr = Toy_toStringLength(bucketHandle, nameToken.lexeme, nameToken.length);
Toy_private_emitAstVariableDeclaration(bucketHandle, &declBranch, nameStr, varType, constant, NULL);
//continue to the 'in' keyword
consume(parser, TOY_TOKEN_KEYWORD_IN, "Expected 'in' inside 'for' condition");
parsePrecedence(bucketHandle, parser, &condBranch, PREC_CALL);
consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' after 'for' condition");
//{ thenBranch }
makeDeclarationStmt(bucketHandle, parser, &thenBranch, true);
//finalize everything
Toy_private_emitAstIterable(bucketHandle, &declBranch, condBranch);
Toy_private_emitAstForThen(bucketHandle, rootHandle, declBranch, thenBranch);
}
static void makeBreakStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { static void makeBreakStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
Toy_private_emitAstBreak(bucketHandle, rootHandle); Toy_private_emitAstBreak(bucketHandle, rootHandle);
consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of break statement"); consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of break statement");
@@ -1070,7 +1119,11 @@ static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro
return; return;
} }
//URGENT: for-pre-clause-post-then //for
else if (match(parser, TOY_TOKEN_KEYWORD_FOR)) {
makeForStmt(bucketHandle, parser, rootHandle);
return;
}
//break //break
else if (match(parser, TOY_TOKEN_KEYWORD_BREAK)) { else if (match(parser, TOY_TOKEN_KEYWORD_BREAK)) {
@@ -1090,7 +1143,7 @@ static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro
return; return;
} }
//TODO: import //import?
//print //print
else if (match(parser, TOY_TOKEN_KEYWORD_PRINT)) { else if (match(parser, TOY_TOKEN_KEYWORD_PRINT)) {
+1 -1
View File
@@ -29,7 +29,7 @@ typedef enum Toy_TokenType {
TOY_TOKEN_KEYWORD_DO, //unused TOY_TOKEN_KEYWORD_DO, //unused
TOY_TOKEN_KEYWORD_ELSE, TOY_TOKEN_KEYWORD_ELSE,
TOY_TOKEN_KEYWORD_EXPORT, //unused TOY_TOKEN_KEYWORD_EXPORT, //unused
TOY_TOKEN_KEYWORD_FOR, //unused TOY_TOKEN_KEYWORD_FOR,
TOY_TOKEN_KEYWORD_FOREACH, //unused TOY_TOKEN_KEYWORD_FOREACH, //unused
TOY_TOKEN_KEYWORD_FUNCTION, //remapped 'fn' TOY_TOKEN_KEYWORD_FUNCTION, //remapped 'fn'
TOY_TOKEN_KEYWORD_IF, TOY_TOKEN_KEYWORD_IF,
+97 -6
View File
@@ -33,6 +33,9 @@ static inline void fixAlignment(Toy_VM* vm) {
vm->programCounter = (vm->programCounter + 3) & ~3; vm->programCounter = (vm->programCounter + 3) & ~3;
} }
//forward declarations for delegations
static void processJump(Toy_VM* vm);
//instruction handlers //instruction handlers
static void processRead(Toy_VM* vm) { static void processRead(Toy_VM* vm) {
Toy_ValueType type = READ_BYTE(vm); Toy_ValueType type = READ_BYTE(vm);
@@ -147,7 +150,7 @@ static void processRead(Toy_VM* vm) {
unsigned int addr = (unsigned int)READ_INT(vm); unsigned int addr = (unsigned int)READ_INT(vm);
//create and push the function value //create and push the function value
Toy_Function* function = Toy_createFunctionFromBytecode(&vm->memoryBucket, vm->code + vm->subsAddr + addr, vm->scope); //BUG: functions don't have the jumps indirection? Toy_Function* function = Toy_createFunctionFromBytecode(&vm->memoryBucket, vm->code + vm->subsAddr + addr, vm->scope); //BUG: functions don't have the jumpTable indirection?
value = TOY_VALUE_FROM_FUNCTION(function); value = TOY_VALUE_FROM_FUNCTION(function);
break; break;
@@ -439,7 +442,7 @@ static void processAttribute(Toy_VM* vm) {
} }
else { else {
char buffer[256]; char buffer[256];
snprintf(buffer, 256, "Can't access an attribute of type '%s'", Toy_getValueTypeAsCString(compound.type)); snprintf(buffer, 256, "Can't access an attribute of type '%s'", Toy_getValueTypeAsCString(Toy_unwrapValue(compound).type));
Toy_error(buffer); Toy_error(buffer);
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL()); Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL());
return; return;
@@ -470,10 +473,83 @@ static void processDuplicate(Toy_VM* vm) {
} }
static void processEliminate(Toy_VM* vm) { static void processEliminate(Toy_VM* vm) {
//discard the stack top //discard the stack top, X times
unsigned int x = (unsigned int)READ_BYTE(vm);
for (unsigned int i = 0; i < x; i++) {
Toy_Value value = Toy_popStack(&vm->stack); Toy_Value value = Toy_popStack(&vm->stack);
Toy_freeValue(value); Toy_freeValue(value);
} }
}
static void processIterate(Toy_VM* vm) {
//ITERATE on [-2] based on type, with [-1] as counter
//then delegate to processJump
Toy_Value counter = Toy_popStack(&vm->stack);
Toy_Value compound = Toy_popStack(&vm->stack);
if (!TOY_VALUE_IS_INTEGER(counter)) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown counter type '%s' found in for loop, exiting\n" TOY_CC_RESET, Toy_getValueTypeAsCString(Toy_unwrapValue(counter).type));
exit(-1);
}
if (TOY_VALUE_IS_ARRAY(compound)) {
Toy_Array* array = TOY_VALUE_AS_ARRAY(compound);
unsigned int index = (unsigned int)TOY_VALUE_AS_INTEGER(counter);
//check out-of-bounds
if (index >= array->count) {
//DON'T free the iterable & counter, that's embedded in the bytecode
Toy_pushStack(&vm->stack, compound);
Toy_pushStack(&vm->stack, counter);
//force a jump then exit
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL());
processJump(vm);
return;
}
//get the desired element
Toy_Value value = Toy_copyValue(&vm->memoryBucket, array->data[index]);
//push everything back onto the stack (iterating the counter)
Toy_pushStack(&vm->stack, compound);
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_INTEGER(index + 1));
Toy_pushStack(&vm->stack, value);
}
else if (TOY_VALUE_IS_TABLE(compound)) {
Toy_Table* table = TOY_VALUE_AS_TABLE(compound);
unsigned int index = (unsigned int)TOY_VALUE_AS_INTEGER(counter);
//loop to the next element
while(index < table->capacity) {
//is this a useable element
if (!TOY_VALUE_IS_NULL(table->data[index].key)) {
break;
}
index++;
}
//return the compound & counter to the stack
Toy_pushStack(&vm->stack, compound);
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_INTEGER(index + 1));
//if something was found, push it and return
if (index < table->capacity) {
Toy_pushStack(&vm->stack, Toy_copyValue(&vm->memoryBucket, table->data[index].value));
}
else {
//otherwise force a jump then exit
Toy_pushStack(&vm->stack, TOY_VALUE_FROM_NULL());
processJump(vm);
}
}
//TODO: support closures as a parameter
else {
fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown iterable type '%s' found in for loop, exiting\n" TOY_CC_RESET, Toy_getValueTypeAsCString(Toy_unwrapValue(compound).type));
exit(-1);
}
}
static void processArithmetic(Toy_VM* vm, Toy_OpcodeType opcode) { static void processArithmetic(Toy_VM* vm, Toy_OpcodeType opcode) {
Toy_Value right = Toy_popStack(&vm->stack); Toy_Value right = Toy_popStack(&vm->stack);
@@ -482,7 +558,7 @@ static void processArithmetic(Toy_VM* vm, Toy_OpcodeType opcode) {
//check types //check types
if ((!TOY_VALUE_IS_INTEGER(left) && !TOY_VALUE_IS_FLOAT(left)) || (!TOY_VALUE_IS_INTEGER(right) && !TOY_VALUE_IS_FLOAT(right))) { if ((!TOY_VALUE_IS_INTEGER(left) && !TOY_VALUE_IS_FLOAT(left)) || (!TOY_VALUE_IS_INTEGER(right) && !TOY_VALUE_IS_FLOAT(right))) {
char buffer[256]; char buffer[256];
snprintf(buffer, 256, "Invalid types '%s' and '%s' passed in arithmetic", Toy_getValueTypeAsCString(left.type), Toy_getValueTypeAsCString(right.type)); snprintf(buffer, 256, "Invalid types '%s' and '%s' passed in arithmetic", Toy_getValueTypeAsCString(Toy_unwrapValue(left).type), Toy_getValueTypeAsCString(Toy_unwrapValue(right).type));
Toy_error(buffer); Toy_error(buffer);
Toy_freeValue(left); Toy_freeValue(left);
@@ -576,7 +652,7 @@ static void processComparison(Toy_VM* vm, Toy_OpcodeType opcode) {
if (Toy_checkValuesAreComparable(left, right) != true) { if (Toy_checkValuesAreComparable(left, right) != true) {
char buffer[256]; char buffer[256];
snprintf(buffer, 256, "Can't compare value types '%s' and '%s'", Toy_getValueTypeAsCString(left.type), Toy_getValueTypeAsCString(right.type)); snprintf(buffer, 256, "Can't compare value types '%s' and '%s'", Toy_getValueTypeAsCString(Toy_unwrapValue(left).type), Toy_getValueTypeAsCString(Toy_unwrapValue(right).type));
Toy_error(buffer); Toy_error(buffer);
Toy_freeValue(left); Toy_freeValue(left);
@@ -682,6 +758,17 @@ static void processJump(Toy_VM* vm) {
Toy_freeValue(value); Toy_freeValue(value);
return; return;
} }
case TOY_OP_PARAM_JUMP_IF_NULL: {
Toy_Value value = Toy_popStack(&vm->stack);
if (TOY_VALUE_IS_NULL(value)) {
Toy_freeValue(value);
break;
}
Toy_freeValue(value);
return;
}
} }
//do the jump //do the jump
@@ -943,7 +1030,7 @@ static void processIndex(Toy_VM* vm) {
} }
else { else {
fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown value type '%s' found in processIndex, exiting\n" TOY_CC_RESET, Toy_getValueTypeAsCString(value.type)); fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown value type '%s' found in processIndex, exiting\n" TOY_CC_RESET, Toy_getValueTypeAsCString(Toy_unwrapValue(value).type));
exit(-1); exit(-1);
} }
@@ -997,6 +1084,10 @@ static unsigned int process(Toy_VM* vm) {
processEliminate(vm); processEliminate(vm);
break; break;
case TOY_OPCODE_ITERATE:
processIterate(vm);
break;
//arithmetic instructions //arithmetic instructions
case TOY_OPCODE_ADD: case TOY_OPCODE_ADD:
case TOY_OPCODE_SUBTRACT: case TOY_OPCODE_SUBTRACT:
@@ -0,0 +1,5 @@
//TODO: empty test script
//TODO: test iteration on arrays, tables, closures
//TODO: test break, continue
+9 -9
View File
@@ -139,7 +139,7 @@ int test_compiler_expressions(Toy_Bucket** bucketHandle) {
*((unsigned char*)(buffer + 27)) != 0 || *((unsigned char*)(buffer + 27)) != 0 ||
*((unsigned char*)(buffer + 28)) != TOY_OPCODE_ELIMINATE || *((unsigned char*)(buffer + 28)) != TOY_OPCODE_ELIMINATE ||
*((unsigned char*)(buffer + 29)) != 0 || *((unsigned char*)(buffer + 29)) != 1 ||
*((unsigned char*)(buffer + 30)) != 0 || *((unsigned char*)(buffer + 30)) != 0 ||
*((unsigned char*)(buffer + 31)) != 0 || *((unsigned char*)(buffer + 31)) != 0 ||
@@ -197,7 +197,7 @@ int test_compiler_expressions(Toy_Bucket** bucketHandle) {
*((unsigned char*)(buffer + 27)) != 0 || *((unsigned char*)(buffer + 27)) != 0 ||
*((unsigned char*)(buffer + 28)) != TOY_OPCODE_ELIMINATE || *((unsigned char*)(buffer + 28)) != TOY_OPCODE_ELIMINATE ||
*((unsigned char*)(buffer + 29)) != 0 || *((unsigned char*)(buffer + 29)) != 1 ||
*((unsigned char*)(buffer + 30)) != 0 || *((unsigned char*)(buffer + 30)) != 0 ||
*((unsigned char*)(buffer + 31)) != 0 || *((unsigned char*)(buffer + 31)) != 0 ||
@@ -257,7 +257,7 @@ int test_compiler_expressions(Toy_Bucket** bucketHandle) {
*(int*)(buffer + 28) != 42 || *(int*)(buffer + 28) != 42 ||
*((unsigned char*)(buffer + 32)) != TOY_OPCODE_ELIMINATE || *((unsigned char*)(buffer + 32)) != TOY_OPCODE_ELIMINATE ||
*((unsigned char*)(buffer + 33)) != 0 || *((unsigned char*)(buffer + 33)) != 1 ||
*((unsigned char*)(buffer + 34)) != 0 || *((unsigned char*)(buffer + 34)) != 0 ||
*((unsigned char*)(buffer + 35)) != 0 || *((unsigned char*)(buffer + 35)) != 0 ||
@@ -317,7 +317,7 @@ int test_compiler_expressions(Toy_Bucket** bucketHandle) {
*(float*)(buffer + 28) != 3.1415f || *(float*)(buffer + 28) != 3.1415f ||
*((unsigned char*)(buffer + 32)) != TOY_OPCODE_ELIMINATE || *((unsigned char*)(buffer + 32)) != TOY_OPCODE_ELIMINATE ||
*((unsigned char*)(buffer + 33)) != 0 || *((unsigned char*)(buffer + 33)) != 1 ||
*((unsigned char*)(buffer + 34)) != 0 || *((unsigned char*)(buffer + 34)) != 0 ||
*((unsigned char*)(buffer + 35)) != 0 || *((unsigned char*)(buffer + 35)) != 0 ||
@@ -389,7 +389,7 @@ int test_compiler_expressions(Toy_Bucket** bucketHandle) {
*(unsigned int*)(code + 4) != 0 || //the jump index *(unsigned int*)(code + 4) != 0 || //the jump index
*((unsigned char*)(code + 8)) != TOY_OPCODE_ELIMINATE || *((unsigned char*)(code + 8)) != TOY_OPCODE_ELIMINATE ||
*((unsigned char*)(code + 9)) != 0 || *((unsigned char*)(code + 9)) != 1 ||
*((unsigned char*)(code + 10)) != 0 || *((unsigned char*)(code + 10)) != 0 ||
*((unsigned char*)(code + 11)) != 0 || *((unsigned char*)(code + 11)) != 0 ||
@@ -499,7 +499,7 @@ int test_compiler_binary(Toy_Bucket** bucketHandle) {
*((unsigned char*)(buffer + 43)) != 0 || *((unsigned char*)(buffer + 43)) != 0 ||
*((unsigned char*)(buffer + 44)) != TOY_OPCODE_ELIMINATE || *((unsigned char*)(buffer + 44)) != TOY_OPCODE_ELIMINATE ||
*((unsigned char*)(buffer + 45)) != 0 || *((unsigned char*)(buffer + 45)) != 1 ||
*((unsigned char*)(buffer + 46)) != 0 || *((unsigned char*)(buffer + 46)) != 0 ||
*((unsigned char*)(buffer + 47)) != 0 || *((unsigned char*)(buffer + 47)) != 0 ||
@@ -569,7 +569,7 @@ int test_compiler_binary(Toy_Bucket** bucketHandle) {
*((unsigned char*)(buffer + 43)) != 0 || *((unsigned char*)(buffer + 43)) != 0 ||
*((unsigned char*)(buffer + 44)) != TOY_OPCODE_ELIMINATE || *((unsigned char*)(buffer + 44)) != TOY_OPCODE_ELIMINATE ||
*((unsigned char*)(buffer + 45)) != 0 || *((unsigned char*)(buffer + 45)) != 1 ||
*((unsigned char*)(buffer + 46)) != 0 || *((unsigned char*)(buffer + 46)) != 0 ||
*((unsigned char*)(buffer + 47)) != 0 || *((unsigned char*)(buffer + 47)) != 0 ||
@@ -639,7 +639,7 @@ int test_compiler_binary(Toy_Bucket** bucketHandle) {
*((unsigned char*)(buffer + 43)) != 0 || *((unsigned char*)(buffer + 43)) != 0 ||
*((unsigned char*)(buffer + 44)) != TOY_OPCODE_ELIMINATE || *((unsigned char*)(buffer + 44)) != TOY_OPCODE_ELIMINATE ||
*((unsigned char*)(buffer + 45)) != 0 || *((unsigned char*)(buffer + 45)) != 1 ||
*((unsigned char*)(buffer + 46)) != 0 || *((unsigned char*)(buffer + 46)) != 0 ||
*((unsigned char*)(buffer + 47)) != 0 || *((unsigned char*)(buffer + 47)) != 0 ||
@@ -735,7 +735,7 @@ int test_compiler_binary(Toy_Bucket** bucketHandle) {
*((unsigned char*)(buffer + 67)) != 0 || *((unsigned char*)(buffer + 67)) != 0 ||
*((unsigned char*)(buffer + 68)) != TOY_OPCODE_ELIMINATE || *((unsigned char*)(buffer + 68)) != TOY_OPCODE_ELIMINATE ||
*((unsigned char*)(buffer + 69)) != 0 || *((unsigned char*)(buffer + 69)) != 1 ||
*((unsigned char*)(buffer + 70)) != 0 || *((unsigned char*)(buffer + 70)) != 0 ||
*((unsigned char*)(buffer + 71)) != 0 || *((unsigned char*)(buffer + 71)) != 0 ||