Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b0387edeb0 | |||
| 87185210f9 | |||
| d3616b07b2 | |||
| c097dd5514 | |||
| e59bcd0572 | |||
| 254aceadfc | |||
| 95362ed6fc | |||
| f11bc95833 | |||
| 813da3e1aa |
@@ -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 +0,0 @@
|
||||
No hating on other people, OK?
|
||||
@@ -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.
|
||||
|
||||
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
|
||||
|
||||
@@ -60,6 +60,13 @@ This source code is covered by the Zlib license (see [LICENSE](LICENSE) for deta
|
||||
|
||||
# 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
|
||||
@Gipson62 - v1 docs spell checking
|
||||
@8051Enthusiast - `fixAlignment()` trick
|
||||
@@ -70,6 +77,6 @@ This source code is covered by the Zlib license (see [LICENSE](LICENSE) for deta
|
||||
Various Anons - Feedback
|
||||
@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
@@ -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).
|
||||
|
||||
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
@@ -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
|
||||
|
||||
|
||||
+29
-10
@@ -195,9 +195,24 @@ int inspect_instruction(unsigned char* bytecode, unsigned int pc, unsigned int j
|
||||
return 4;
|
||||
|
||||
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;
|
||||
|
||||
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:
|
||||
printf(MARKER "ADD %s\n", MARKER_VALUE(pc, unsigned char), bytecode[pc + 1] == TOY_OPCODE_ASSIGN ? "and ASSIGN" : "");
|
||||
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),
|
||||
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" : " if false",
|
||||
bytecode[pc + 4] > 0 ? "+" : "", //show a + sign when positive
|
||||
bytecode[pc + 4],
|
||||
bytecode[pc + 4] + pc + 8
|
||||
);
|
||||
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_ESCAPE:
|
||||
printf(MARKER TOY_CC_DEBUG "ESCAPE relative %s%d (GOTO %u) and pop %d\n" TOY_CC_RESET, MARKER_VALUE(pc, unsigned char),
|
||||
bytecode[pc + 4] > 0 ? "+" : "", //show a + sign when positive
|
||||
bytecode[pc + 4],
|
||||
bytecode[pc + 4] + pc + 12,
|
||||
printf(MARKER TOY_CC_DEBUG "ESCAPE relative %s%d (GOTO %u) and pop %d scopes\n" TOY_CC_RESET, MARKER_VALUE(pc, unsigned char),
|
||||
(*(int*)(bytecode + pc + 4)) > 0 ? "+" : "", //show a + sign when positive
|
||||
(*(int*)(bytecode + pc + 4)),
|
||||
(*(int*)(bytecode + pc + 4)) + pc + 12,
|
||||
bytecode[pc + 8]
|
||||
);
|
||||
return 12;
|
||||
|
||||
+18
-9
@@ -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) {
|
||||
print x;
|
||||
}
|
||||
|
||||
fn b() {
|
||||
return 42;
|
||||
}
|
||||
|
||||
a(b(), b());
|
||||
for (var i in table) {
|
||||
print i;
|
||||
}
|
||||
@@ -147,6 +147,16 @@ void Toy_private_emitAstWhileThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle
|
||||
(*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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
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_IF_THEN_ELSE: return "IF_THEN_ELSE";
|
||||
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_CONTINUE: return "CONTINUE";
|
||||
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_INVOKE: return "FN_INVOKE";
|
||||
case TOY_AST_ATTRIBUTE: return "ATTRIBUTE";
|
||||
case TOY_AST_ITERABLE: return "ITERABLE";
|
||||
|
||||
case TOY_AST_STACK_POP: return "STACK_POP";
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ typedef enum Toy_AstType {
|
||||
TOY_AST_ASSERT,
|
||||
TOY_AST_IF_THEN_ELSE,
|
||||
TOY_AST_WHILE_THEN,
|
||||
TOY_AST_FOR_THEN,
|
||||
TOY_AST_BREAK,
|
||||
TOY_AST_CONTINUE,
|
||||
TOY_AST_RETURN,
|
||||
@@ -34,6 +35,7 @@ typedef enum Toy_AstType {
|
||||
TOY_AST_FN_DECLARE,
|
||||
TOY_AST_FN_INVOKE,
|
||||
TOY_AST_ATTRIBUTE,
|
||||
TOY_AST_ITERABLE,
|
||||
|
||||
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_AstWhileThen;
|
||||
|
||||
typedef struct Toy_AstForThen {
|
||||
Toy_AstType type;
|
||||
Toy_Ast* condBranch;
|
||||
Toy_Ast* thenBranch;
|
||||
} Toy_AstForThen;
|
||||
|
||||
typedef struct Toy_AstBreak {
|
||||
Toy_AstType type;
|
||||
} Toy_AstBreak;
|
||||
@@ -228,6 +236,12 @@ typedef struct Toy_AstAttribute {
|
||||
Toy_Ast* right;
|
||||
} Toy_AstAttribute;
|
||||
|
||||
typedef struct Toy_AstIterable {
|
||||
Toy_AstType type;
|
||||
Toy_Ast* left;
|
||||
Toy_Ast* right;
|
||||
} Toy_AstIterable;
|
||||
|
||||
typedef struct Toy_AstStackPop {
|
||||
Toy_AstType type;
|
||||
Toy_Ast* child;
|
||||
@@ -259,6 +273,7 @@ union Toy_Ast { //see 'test_ast.c' for bitness tests
|
||||
Toy_AstAssert assert;
|
||||
Toy_AstIfThenElse ifThenElse;
|
||||
Toy_AstWhileThen whileThen;
|
||||
Toy_AstForThen forThen;
|
||||
Toy_AstBreak breakPoint;
|
||||
Toy_AstContinue continuePoint;
|
||||
Toy_AstReturn fnReturn;
|
||||
@@ -269,6 +284,7 @@ union Toy_Ast { //see 'test_ast.c' for bitness tests
|
||||
Toy_AstFnDeclare fnDeclare;
|
||||
Toy_AstFnInvoke fnInvoke;
|
||||
Toy_AstAttribute attribute;
|
||||
Toy_AstIterable iterable;
|
||||
Toy_AstStackPop stackPop;
|
||||
Toy_AstPass pass;
|
||||
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_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_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_emitAstContinue(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle);
|
||||
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_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_emitAstIterable(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr);
|
||||
|
||||
void Toy_private_emitAstStackPop(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||
|
||||
|
||||
@@ -90,69 +90,6 @@ static void attr_arrayPopBack(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||
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) {
|
||||
(void)vm;
|
||||
(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);
|
||||
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")) {
|
||||
Toy_Function* fn = Toy_createFunctionFromCallback(&vm->memoryBucket, attr_arraySort);
|
||||
return TOY_VALUE_FROM_FUNCTION(fn);
|
||||
@@ -237,13 +170,6 @@ static void attr_tableRemove(Toy_VM* vm, Toy_FunctionNative* self) {
|
||||
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) {
|
||||
if (MATCH_VALUE_AND_CSTRING(attribute, "length")) {
|
||||
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);
|
||||
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 {
|
||||
char buffer[256];
|
||||
snprintf(buffer, 256, "Unknown attribute '%s' of type '%s'", TOY_VALUE_AS_STRING(attribute)->leaf.data, Toy_getValueTypeAsCString(compound.type));
|
||||
|
||||
+130
-2
@@ -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 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 writeInstructionAccess(Toy_Bytecode** mb, Toy_AstVarAccess ast);
|
||||
static unsigned int writeInstructionFnInvoke(Toy_Bytecode** mb, Toy_AstFnInvoke ast, bool chainedInvoke);
|
||||
|
||||
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
|
||||
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);
|
||||
|
||||
@@ -718,6 +719,123 @@ static unsigned int writeInstructionWhileThen(Toy_Bytecode** mb, Toy_AstWhileThe
|
||||
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) {
|
||||
//unused
|
||||
(void)ast;
|
||||
@@ -1154,7 +1272,7 @@ static unsigned int writeInstructionStackPop(Toy_Bytecode** mb, Toy_AstStackPop
|
||||
|
||||
//dead simple
|
||||
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);
|
||||
|
||||
@@ -1244,6 +1362,10 @@ static unsigned int writeBytecodeFromAst(Toy_Bytecode** mb, Toy_Ast* ast) {
|
||||
result += writeInstructionWhileThen(mb, ast->whileThen);
|
||||
break;
|
||||
|
||||
case TOY_AST_FOR_THEN:
|
||||
result += writeInstructionForThen(mb, ast->forThen);
|
||||
break;
|
||||
|
||||
case TOY_AST_BREAK:
|
||||
result += writeInstructionBreak(mb, ast->breakPoint);
|
||||
break;
|
||||
@@ -1284,6 +1406,12 @@ static unsigned int writeBytecodeFromAst(Toy_Bytecode** mb, Toy_Ast* ast) {
|
||||
result += writeInstructionAttribute(mb, ast->attribute);
|
||||
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:
|
||||
result += writeInstructionStackPop(mb, ast->stackPop);
|
||||
break;
|
||||
|
||||
@@ -14,6 +14,7 @@ typedef enum Toy_OpcodeType {
|
||||
TOY_OPCODE_ATTRIBUTE, //for accessing parts of compounds
|
||||
TOY_OPCODE_DUPLICATE, //duplicate 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
|
||||
TOY_OPCODE_ADD,
|
||||
@@ -66,5 +67,6 @@ typedef enum Toy_OpParamJumpConditional {
|
||||
TOY_OP_PARAM_JUMP_ALWAYS = 0,
|
||||
TOY_OP_PARAM_JUMP_IF_TRUE = 1,
|
||||
TOY_OP_PARAM_JUMP_IF_FALSE = 2,
|
||||
TOY_OP_PARAM_JUMP_IF_NULL = 3,
|
||||
} Toy_OpParamJumpConditional;
|
||||
|
||||
|
||||
+58
-5
@@ -771,7 +771,7 @@ static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_A
|
||||
if (prefix == NULL) {
|
||||
//make a nice error message
|
||||
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 {
|
||||
printError(parser, parser->previous, "Expected expression");
|
||||
@@ -865,7 +865,7 @@ static void makeIfThenElseStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, To
|
||||
makeExpr(bucketHandle, parser, &condBranch);
|
||||
consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' after 'if' condition");
|
||||
|
||||
// { thenBranch }
|
||||
//{ thenBranch }
|
||||
makeDeclarationStmt(bucketHandle, parser, &thenBranch, true);
|
||||
|
||||
//else { elseBranch }
|
||||
@@ -885,12 +885,61 @@ static void makeWhileStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast
|
||||
makeExpr(bucketHandle, parser, &condBranch);
|
||||
consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' after 'while' condition");
|
||||
|
||||
// { thenBranch }
|
||||
//{ thenBranch }
|
||||
makeDeclarationStmt(bucketHandle, parser, &thenBranch, true);
|
||||
|
||||
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) {
|
||||
Toy_private_emitAstBreak(bucketHandle, rootHandle);
|
||||
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;
|
||||
}
|
||||
|
||||
//URGENT: for-pre-clause-post-then
|
||||
//for
|
||||
else if (match(parser, TOY_TOKEN_KEYWORD_FOR)) {
|
||||
makeForStmt(bucketHandle, parser, rootHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
//TODO: import
|
||||
//import?
|
||||
|
||||
//print
|
||||
else if (match(parser, TOY_TOKEN_KEYWORD_PRINT)) {
|
||||
|
||||
@@ -29,7 +29,7 @@ typedef enum Toy_TokenType {
|
||||
TOY_TOKEN_KEYWORD_DO, //unused
|
||||
TOY_TOKEN_KEYWORD_ELSE,
|
||||
TOY_TOKEN_KEYWORD_EXPORT, //unused
|
||||
TOY_TOKEN_KEYWORD_FOR, //unused
|
||||
TOY_TOKEN_KEYWORD_FOR,
|
||||
TOY_TOKEN_KEYWORD_FOREACH, //unused
|
||||
TOY_TOKEN_KEYWORD_FUNCTION, //remapped 'fn'
|
||||
TOY_TOKEN_KEYWORD_IF,
|
||||
|
||||
+99
-8
@@ -33,6 +33,9 @@ static inline void fixAlignment(Toy_VM* vm) {
|
||||
vm->programCounter = (vm->programCounter + 3) & ~3;
|
||||
}
|
||||
|
||||
//forward declarations for delegations
|
||||
static void processJump(Toy_VM* vm);
|
||||
|
||||
//instruction handlers
|
||||
static void processRead(Toy_VM* 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);
|
||||
|
||||
//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);
|
||||
|
||||
break;
|
||||
@@ -439,7 +442,7 @@ static void processAttribute(Toy_VM* vm) {
|
||||
}
|
||||
else {
|
||||
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_pushStack(&vm->stack, TOY_VALUE_FROM_NULL());
|
||||
return;
|
||||
@@ -470,9 +473,82 @@ static void processDuplicate(Toy_VM* vm) {
|
||||
}
|
||||
|
||||
static void processEliminate(Toy_VM* vm) {
|
||||
//discard the stack top
|
||||
Toy_Value value = Toy_popStack(&vm->stack);
|
||||
Toy_freeValue(value);
|
||||
//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_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) {
|
||||
@@ -482,7 +558,7 @@ static void processArithmetic(Toy_VM* vm, Toy_OpcodeType opcode) {
|
||||
//check types
|
||||
if ((!TOY_VALUE_IS_INTEGER(left) && !TOY_VALUE_IS_FLOAT(left)) || (!TOY_VALUE_IS_INTEGER(right) && !TOY_VALUE_IS_FLOAT(right))) {
|
||||
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_freeValue(left);
|
||||
@@ -576,7 +652,7 @@ static void processComparison(Toy_VM* vm, Toy_OpcodeType opcode) {
|
||||
|
||||
if (Toy_checkValuesAreComparable(left, right) != true) {
|
||||
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_freeValue(left);
|
||||
@@ -682,6 +758,17 @@ static void processJump(Toy_VM* vm) {
|
||||
Toy_freeValue(value);
|
||||
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
|
||||
@@ -943,7 +1030,7 @@ static void processIndex(Toy_VM* vm) {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -997,6 +1084,10 @@ static unsigned int process(Toy_VM* vm) {
|
||||
processEliminate(vm);
|
||||
break;
|
||||
|
||||
case TOY_OPCODE_ITERATE:
|
||||
processIterate(vm);
|
||||
break;
|
||||
|
||||
//arithmetic instructions
|
||||
case TOY_OPCODE_ADD:
|
||||
case TOY_OPCODE_SUBTRACT:
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
//TODO: empty test script
|
||||
|
||||
//TODO: test iteration on arrays, tables, closures
|
||||
|
||||
//TODO: test break, continue
|
||||
@@ -139,7 +139,7 @@ int test_compiler_expressions(Toy_Bucket** bucketHandle) {
|
||||
*((unsigned char*)(buffer + 27)) != 0 ||
|
||||
|
||||
*((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 + 31)) != 0 ||
|
||||
|
||||
@@ -197,7 +197,7 @@ int test_compiler_expressions(Toy_Bucket** bucketHandle) {
|
||||
*((unsigned char*)(buffer + 27)) != 0 ||
|
||||
|
||||
*((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 + 31)) != 0 ||
|
||||
|
||||
@@ -257,7 +257,7 @@ int test_compiler_expressions(Toy_Bucket** bucketHandle) {
|
||||
*(int*)(buffer + 28) != 42 ||
|
||||
|
||||
*((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 + 35)) != 0 ||
|
||||
|
||||
@@ -317,7 +317,7 @@ int test_compiler_expressions(Toy_Bucket** bucketHandle) {
|
||||
*(float*)(buffer + 28) != 3.1415f ||
|
||||
|
||||
*((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 + 35)) != 0 ||
|
||||
|
||||
@@ -389,7 +389,7 @@ int test_compiler_expressions(Toy_Bucket** bucketHandle) {
|
||||
*(unsigned int*)(code + 4) != 0 || //the jump index
|
||||
|
||||
*((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 + 11)) != 0 ||
|
||||
|
||||
@@ -499,7 +499,7 @@ int test_compiler_binary(Toy_Bucket** bucketHandle) {
|
||||
*((unsigned char*)(buffer + 43)) != 0 ||
|
||||
|
||||
*((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 + 47)) != 0 ||
|
||||
|
||||
@@ -569,7 +569,7 @@ int test_compiler_binary(Toy_Bucket** bucketHandle) {
|
||||
*((unsigned char*)(buffer + 43)) != 0 ||
|
||||
|
||||
*((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 + 47)) != 0 ||
|
||||
|
||||
@@ -639,7 +639,7 @@ int test_compiler_binary(Toy_Bucket** bucketHandle) {
|
||||
*((unsigned char*)(buffer + 43)) != 0 ||
|
||||
|
||||
*((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 + 47)) != 0 ||
|
||||
|
||||
@@ -735,7 +735,7 @@ int test_compiler_binary(Toy_Bucket** bucketHandle) {
|
||||
*((unsigned char*)(buffer + 67)) != 0 ||
|
||||
|
||||
*((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 + 71)) != 0 ||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user