mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 23:04:08 +10:00
Implemented and tested variable declaration
Assignment, etc. is still to come, as are types.
This commit is contained in:
181
repl/main.c
181
repl/main.c
@@ -258,6 +258,130 @@ int repl(const char* filepath) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
//debugging
|
||||
static void debugStackPrint(Toy_Stack* stack) {
|
||||
//DEBUG: if there's anything on the stack, print it
|
||||
if (stack->count > 0) {
|
||||
printf("Stack Dump\n\ntype\tvalue\n");
|
||||
for (int i = 0; i < stack->count; i++) {
|
||||
Toy_Value v = ((Toy_Value*)(stack + 1))[i];
|
||||
|
||||
printf("%d\t", v.type);
|
||||
|
||||
switch(v.type) {
|
||||
case TOY_VALUE_NULL:
|
||||
printf("null");
|
||||
break;
|
||||
|
||||
case TOY_VALUE_BOOLEAN:
|
||||
printf("%s", TOY_VALUE_AS_BOOLEAN(v) ? "true" : "false");
|
||||
break;
|
||||
|
||||
case TOY_VALUE_INTEGER:
|
||||
printf("%d", TOY_VALUE_AS_INTEGER(v));
|
||||
break;
|
||||
|
||||
case TOY_VALUE_FLOAT:
|
||||
printf("%f", TOY_VALUE_AS_FLOAT(v));
|
||||
break;
|
||||
|
||||
case TOY_VALUE_STRING: {
|
||||
Toy_String* str = TOY_VALUE_AS_STRING(v);
|
||||
|
||||
//print based on type
|
||||
if (str->type == TOY_STRING_NODE) {
|
||||
char* buffer = Toy_getStringRawBuffer(str);
|
||||
printf("%s", buffer);
|
||||
free(buffer);
|
||||
}
|
||||
else if (str->type == TOY_STRING_LEAF) {
|
||||
printf("%s", str->as.leaf.data);
|
||||
}
|
||||
else if (str->type == TOY_STRING_NAME) {
|
||||
printf("%s", str->as.name.data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TOY_VALUE_ARRAY:
|
||||
case TOY_VALUE_DICTIONARY:
|
||||
case TOY_VALUE_FUNCTION:
|
||||
case TOY_VALUE_OPAQUE:
|
||||
printf("???");
|
||||
break;
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void debugScopePrint(Toy_Scope* scope, int depth) {
|
||||
//DEBUG: if there's anything in the scope, print it
|
||||
if (scope->table->count > 0) {
|
||||
printf("Scope %d Dump\n\ntype\tname\tvalue\n", depth);
|
||||
for (int i = 0; i < scope->table->capacity; i++) {
|
||||
if ( (TOY_VALUE_IS_STRING(scope->table->data[i].key) && TOY_VALUE_AS_STRING(scope->table->data[i].key)->type == TOY_STRING_NAME) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Toy_Value k = scope->table->data[i].key;
|
||||
Toy_Value v = scope->table->data[i].value;
|
||||
|
||||
printf("%d\t%s\t", v.type, TOY_VALUE_AS_STRING(k)->as.name.data);
|
||||
|
||||
switch(v.type) {
|
||||
case TOY_VALUE_NULL:
|
||||
printf("null");
|
||||
break;
|
||||
|
||||
case TOY_VALUE_BOOLEAN:
|
||||
printf("%s", TOY_VALUE_AS_BOOLEAN(v) ? "true" : "false");
|
||||
break;
|
||||
|
||||
case TOY_VALUE_INTEGER:
|
||||
printf("%d", TOY_VALUE_AS_INTEGER(v));
|
||||
break;
|
||||
|
||||
case TOY_VALUE_FLOAT:
|
||||
printf("%f", TOY_VALUE_AS_FLOAT(v));
|
||||
break;
|
||||
|
||||
case TOY_VALUE_STRING: {
|
||||
Toy_String* str = TOY_VALUE_AS_STRING(v);
|
||||
|
||||
//print based on type
|
||||
if (str->type == TOY_STRING_NODE) {
|
||||
char* buffer = Toy_getStringRawBuffer(str);
|
||||
printf("%s", buffer);
|
||||
free(buffer);
|
||||
}
|
||||
else if (str->type == TOY_STRING_LEAF) {
|
||||
printf("%s", str->as.leaf.data);
|
||||
}
|
||||
else if (str->type == TOY_STRING_NAME) {
|
||||
printf("%s\nWarning: The above value is a name string", str->as.name.data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TOY_VALUE_ARRAY:
|
||||
case TOY_VALUE_DICTIONARY:
|
||||
case TOY_VALUE_FUNCTION:
|
||||
case TOY_VALUE_OPAQUE:
|
||||
printf("???");
|
||||
break;
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (scope->next != NULL) {
|
||||
debugScopePrint(scope->next, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
//callbacks
|
||||
static void printCallback(const char* msg) {
|
||||
fprintf(stdout, "%s\n", msg);
|
||||
@@ -338,60 +462,9 @@ int main(int argc, const char* argv[]) {
|
||||
//run
|
||||
Toy_runVM(&vm);
|
||||
|
||||
//DEBUG: if there's anything left on the stack, print it
|
||||
if (vm.stack->count > 0) {
|
||||
printf("Debug output of the stack after execution\n\ntype\tvalue\n");
|
||||
for (int i = 0; i < vm.stack->count; i++) {
|
||||
Toy_Value v = ((Toy_Value*)(vm.stack + 1))[i];
|
||||
|
||||
printf(" %d\t ", v.type);
|
||||
|
||||
switch(v.type) {
|
||||
case TOY_VALUE_NULL:
|
||||
printf("null");
|
||||
break;
|
||||
|
||||
case TOY_VALUE_BOOLEAN:
|
||||
printf("%s", TOY_VALUE_AS_BOOLEAN(v) ? "true" : "false");
|
||||
break;
|
||||
|
||||
case TOY_VALUE_INTEGER:
|
||||
printf("%d", TOY_VALUE_AS_INTEGER(v));
|
||||
break;
|
||||
|
||||
case TOY_VALUE_FLOAT:
|
||||
printf("%f", TOY_VALUE_AS_FLOAT(v));
|
||||
break;
|
||||
|
||||
case TOY_VALUE_STRING: {
|
||||
Toy_String* str = TOY_VALUE_AS_STRING(v);
|
||||
|
||||
//print based on type
|
||||
if (str->type == TOY_STRING_NODE) {
|
||||
char* buffer = Toy_getStringRawBuffer(str);
|
||||
printf("%s", buffer);
|
||||
free(buffer);
|
||||
}
|
||||
else if (str->type == TOY_STRING_LEAF) {
|
||||
printf("%s", str->as.leaf.data);
|
||||
}
|
||||
else if (str->type == TOY_STRING_NAME) {
|
||||
printf("%s", str->as.name.data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TOY_VALUE_ARRAY:
|
||||
case TOY_VALUE_DICTIONARY:
|
||||
case TOY_VALUE_FUNCTION:
|
||||
case TOY_VALUE_OPAQUE:
|
||||
printf("???");
|
||||
break;
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
//print the debug info
|
||||
debugStackPrint(vm.stack);
|
||||
debugScopePrint(vm.scope, 0);
|
||||
|
||||
//cleanup
|
||||
Toy_freeVM(&vm);
|
||||
|
||||
3
scripts/example-variables.toy
Normal file
3
scripts/example-variables.toy
Normal file
@@ -0,0 +1,3 @@
|
||||
//declare a variable
|
||||
var foobar = 42;
|
||||
|
||||
@@ -81,6 +81,16 @@ void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||
(*astHandle) = tmp;
|
||||
}
|
||||
|
||||
void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* expr) {
|
||||
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||
|
||||
tmp->type = TOY_AST_VAR_DECLARE;
|
||||
tmp->varDeclare.name = name;
|
||||
tmp->varDeclare.expr = expr;
|
||||
|
||||
(*astHandle) = tmp;
|
||||
}
|
||||
|
||||
void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "toy_bucket.h"
|
||||
#include "toy_value.h"
|
||||
#include "toy_string.h"
|
||||
|
||||
//each major type
|
||||
typedef enum Toy_AstType {
|
||||
@@ -16,6 +17,8 @@ typedef enum Toy_AstType {
|
||||
|
||||
TOY_AST_PRINT,
|
||||
|
||||
TOY_AST_VAR_DECLARE,
|
||||
|
||||
TOY_AST_PASS,
|
||||
TOY_AST_ERROR,
|
||||
TOY_AST_END,
|
||||
@@ -93,6 +96,12 @@ typedef struct Toy_AstPrint {
|
||||
Toy_Ast* child;
|
||||
} Toy_AstPrint;
|
||||
|
||||
typedef struct Toy_AstVarDeclare {
|
||||
Toy_AstType type;
|
||||
Toy_String* name;
|
||||
Toy_Ast* expr;
|
||||
} Toy_AstVarDeclare;
|
||||
|
||||
typedef struct Toy_AstPass {
|
||||
Toy_AstType type;
|
||||
} Toy_AstPass;
|
||||
@@ -105,18 +114,19 @@ typedef struct Toy_AstEnd {
|
||||
Toy_AstType type;
|
||||
} Toy_AstEnd;
|
||||
|
||||
union Toy_Ast { //32 | 64 BITNESS
|
||||
Toy_AstType type; //4 | 4
|
||||
Toy_AstBlock block; //16 | 32
|
||||
Toy_AstValue value; //12 | 24
|
||||
Toy_AstUnary unary; //12 | 16
|
||||
Toy_AstBinary binary; //16 | 24
|
||||
Toy_AstGroup group; //8 | 16
|
||||
Toy_AstPrint print; //8 | 16
|
||||
Toy_AstPass pass; //4 | 4
|
||||
Toy_AstError error; //4 | 4
|
||||
Toy_AstEnd end; //4 | 4
|
||||
}; //16 | 32
|
||||
union Toy_Ast { //32 | 64 BITNESS
|
||||
Toy_AstType type; //4 | 4
|
||||
Toy_AstBlock block; //16 | 32
|
||||
Toy_AstValue value; //12 | 24
|
||||
Toy_AstUnary unary; //12 | 16
|
||||
Toy_AstBinary binary; //16 | 24
|
||||
Toy_AstGroup group; //8 | 16
|
||||
Toy_AstPrint print; //8 | 16
|
||||
Toy_AstVarDeclare varDeclare; //16 | 24
|
||||
Toy_AstPass pass; //4 | 4
|
||||
Toy_AstError error; //4 | 4
|
||||
Toy_AstEnd end; //4 | 4
|
||||
}; //16 | 32
|
||||
|
||||
void Toy_private_initAstBlock(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||
void Toy_private_appendAstBlock(Toy_Bucket** bucketHandle, Toy_Ast* block, Toy_Ast* child);
|
||||
@@ -128,6 +138,8 @@ void Toy_private_emitAstGroup(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||
|
||||
void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||
|
||||
void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* expr);
|
||||
|
||||
void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||
void Toy_private_emitAstError(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||
void Toy_private_emitAstEnd(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||
|
||||
@@ -195,7 +195,7 @@ static Toy_Token makeString(Toy_Lexer* lexer, char terminator) {
|
||||
return token;
|
||||
}
|
||||
|
||||
static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
|
||||
static Toy_Token makeKeywordOrName(Toy_Lexer* lexer) {
|
||||
advance(lexer); //first letter can only be alpha
|
||||
|
||||
while(isDigit(lexer) || isAlpha(lexer)) {
|
||||
@@ -218,10 +218,10 @@ static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
|
||||
}
|
||||
}
|
||||
|
||||
//make token (identifier)
|
||||
//make token (variable name)
|
||||
Toy_Token token;
|
||||
|
||||
token.type = TOY_TOKEN_IDENTIFIER;
|
||||
token.type = TOY_TOKEN_NAME;
|
||||
token.length = lexer->current - lexer->start;
|
||||
token.line = lexer->line;
|
||||
token.lexeme = &lexer->source[lexer->start];
|
||||
@@ -247,7 +247,7 @@ Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer) {
|
||||
if (isAtEnd(lexer)) return makeToken(lexer, TOY_TOKEN_EOF);
|
||||
|
||||
if (isDigit(lexer)) return makeIntegerOrFloat(lexer);
|
||||
if (isAlpha(lexer)) return makeKeywordOrIdentifier(lexer);
|
||||
if (isAlpha(lexer)) return makeKeywordOrName(lexer);
|
||||
|
||||
char c = advance(lexer);
|
||||
|
||||
@@ -335,7 +335,7 @@ void Toy_private_printToken(Toy_Token* token) {
|
||||
printf("\t%d\t%d\t", token->type, (int)token->line);
|
||||
|
||||
//print based on type
|
||||
if (token->type == TOY_TOKEN_IDENTIFIER || token->type == TOY_TOKEN_LITERAL_INTEGER || token->type == TOY_TOKEN_LITERAL_FLOAT || token->type == TOY_TOKEN_LITERAL_STRING) {
|
||||
if (token->type == TOY_TOKEN_NAME || token->type == TOY_TOKEN_LITERAL_INTEGER || token->type == TOY_TOKEN_LITERAL_FLOAT || token->type == TOY_TOKEN_LITERAL_STRING) {
|
||||
printf("%.*s\t", (int)token->length, token->lexeme);
|
||||
} else {
|
||||
const char* keyword = Toy_private_findKeywordByType(token->type);
|
||||
|
||||
@@ -120,7 +120,7 @@ static ParsingTuple parsingRulesetTable[] = {
|
||||
{PREC_PRIMARY,literal,NULL},// TOY_TOKEN_NULL,
|
||||
|
||||
//variable names
|
||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_IDENTIFIER,
|
||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_NAME,
|
||||
|
||||
//types
|
||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_TYPE_TYPE,
|
||||
@@ -528,6 +528,36 @@ static void makeExprStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast*
|
||||
consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of expression statement");
|
||||
}
|
||||
|
||||
static void makeVariableDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||
consume(parser, TOY_TOKEN_NAME, "Expected variable name after 'var' keyword");
|
||||
|
||||
if (parser->previous.length > 256) {
|
||||
printError(parser, parser->previous, "Can't have a variable name longer than 256 characters");
|
||||
Toy_private_emitAstError(bucketHandle, rootHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
Toy_Token nameToken = parser->previous;
|
||||
|
||||
//TODO: read the type specifier if present
|
||||
// Toy_Token typeToken = TOY_BLANK_TOKEN();
|
||||
|
||||
//build the string
|
||||
Toy_String* nameStr = Toy_createNameStringLength(bucketHandle, nameToken.lexeme, nameToken.length, TOY_VALUE_NULL);
|
||||
|
||||
//if there's an assignment, read it, or default to null
|
||||
Toy_Ast* expr = NULL;
|
||||
if (match(parser, TOY_TOKEN_OPERATOR_ASSIGN)) {
|
||||
makeExpr(bucketHandle, parser, &expr);
|
||||
}
|
||||
else {
|
||||
Toy_private_emitAstValue(bucketHandle, rootHandle, TOY_VALUE_FROM_NULL());
|
||||
}
|
||||
|
||||
//finally, emit the declaration as an Ast
|
||||
Toy_private_emitAstVariableDeclaration(bucketHandle, rootHandle, nameStr, expr);
|
||||
}
|
||||
|
||||
static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||
//block
|
||||
//assert
|
||||
@@ -558,10 +588,10 @@ static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro
|
||||
}
|
||||
|
||||
static void makeDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||
// //variable declarations
|
||||
// if (match(parser, TOY_TOKEN_KEYWORD_VAR)) {
|
||||
// makeVariableDeclarationStmt(bucketHandle, parser, rootHandle);
|
||||
// }
|
||||
//variable declarations
|
||||
if (match(parser, TOY_TOKEN_KEYWORD_VAR)) {
|
||||
makeVariableDeclarationStmt(bucketHandle, parser, rootHandle);
|
||||
}
|
||||
|
||||
// //function declarations
|
||||
// else if (match(parser, TOY_TOKEN_KEYWORD_FUNCTION)) {
|
||||
@@ -569,9 +599,9 @@ static void makeDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, T
|
||||
// }
|
||||
|
||||
//otherwise
|
||||
// else {
|
||||
else {
|
||||
makeStmt(bucketHandle, parser, rootHandle);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
static void makeBlockStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||
|
||||
@@ -266,6 +266,19 @@ static void writeInstructionPrint(Toy_Routine** rt, Toy_AstPrint ast) {
|
||||
EMIT_BYTE(rt, code,0);
|
||||
}
|
||||
|
||||
static void writeInstructionVarDeclare(Toy_Routine** rt, Toy_AstVarDeclare ast) {
|
||||
//initial value
|
||||
writeRoutineCode(rt, ast.expr);
|
||||
|
||||
//delcare with the given name string
|
||||
EMIT_BYTE(rt, code, TOY_OPCODE_DECLARE);
|
||||
EMIT_BYTE(rt, code, Toy_getNameStringType(ast.name));
|
||||
EMIT_BYTE(rt, code, ast.name->length); //quick optimisation to skip a 'strlen()' call
|
||||
EMIT_BYTE(rt, code, 0);
|
||||
|
||||
emitString(rt, ast.name);
|
||||
}
|
||||
|
||||
//routine structure
|
||||
// static void writeRoutineParam(Toy_Routine* rt) {
|
||||
// //
|
||||
@@ -303,6 +316,10 @@ static void writeRoutineCode(Toy_Routine** rt, Toy_Ast* ast) {
|
||||
writeInstructionPrint(rt, ast->print);
|
||||
break;
|
||||
|
||||
case TOY_AST_VAR_DECLARE:
|
||||
writeInstructionVarDeclare(rt, ast->varDeclare);
|
||||
break;
|
||||
|
||||
//meta instructions are disallowed
|
||||
case TOY_AST_PASS:
|
||||
//NOTE: this should be disallowed, but for now it's required for testing
|
||||
|
||||
@@ -91,8 +91,7 @@ Toy_String* Toy_createStringLength(Toy_Bucket** bucketHandle, const char* cstrin
|
||||
return result;
|
||||
}
|
||||
|
||||
TOY_API Toy_String* Toy_createNameString(Toy_Bucket** bucketHandle, const char* cname, Toy_ValueType type) {
|
||||
unsigned int length = strlen(cname);
|
||||
Toy_String* Toy_createNameStringLength(Toy_Bucket** bucketHandle, const char* cname, unsigned int length, Toy_ValueType type) {
|
||||
|
||||
//name strings can't be broken up
|
||||
if (sizeof(Toy_String) + length + 1 > (*bucketHandle)->capacity) {
|
||||
@@ -196,6 +195,15 @@ unsigned int Toy_getStringRefCount(Toy_String* str) {
|
||||
return str->refCount;
|
||||
}
|
||||
|
||||
Toy_ValueType Toy_getNameStringType(Toy_String* str) {
|
||||
if (str->type != TOY_STRING_NAME) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Can't get the variable type of a non-name string\n" TOY_CC_RESET);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return str->as.name.type;
|
||||
}
|
||||
|
||||
char* Toy_getStringRawBuffer(Toy_String* str) {
|
||||
if (str->type == TOY_STRING_NAME) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Can't get raw string buffer of a name string\n" TOY_CC_RESET);
|
||||
|
||||
@@ -38,7 +38,7 @@ typedef struct Toy_String { //32 | 64 BITNESS
|
||||
TOY_API Toy_String* Toy_createString(Toy_Bucket** bucketHandle, const char* cstring);
|
||||
TOY_API Toy_String* Toy_createStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length);
|
||||
|
||||
TOY_API Toy_String* Toy_createNameString(Toy_Bucket** bucketHandle, const char* cname, Toy_ValueType type); //for variable names
|
||||
TOY_API Toy_String* Toy_createNameStringLength(Toy_Bucket** bucketHandle, const char* cname, unsigned int length, Toy_ValueType type); //for variable names
|
||||
|
||||
TOY_API Toy_String* Toy_copyString(Toy_String* str);
|
||||
TOY_API Toy_String* Toy_deepCopyString(Toy_Bucket** bucketHandle, Toy_String* str);
|
||||
@@ -49,6 +49,7 @@ TOY_API void Toy_freeString(Toy_String* str);
|
||||
|
||||
TOY_API unsigned int Toy_getStringLength(Toy_String* str);
|
||||
TOY_API unsigned int Toy_getStringRefCount(Toy_String* str);
|
||||
TOY_API Toy_ValueType Toy_getNameStringType(Toy_String* str);
|
||||
|
||||
TOY_API char* Toy_getStringRawBuffer(Toy_String* str); //allocates the buffer on the heap, needs to be freed
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ typedef enum Toy_TokenType {
|
||||
TOY_TOKEN_NULL,
|
||||
|
||||
//variable names
|
||||
TOY_TOKEN_IDENTIFIER,
|
||||
TOY_TOKEN_NAME,
|
||||
|
||||
//types
|
||||
TOY_TOKEN_TYPE_TYPE,
|
||||
|
||||
@@ -110,6 +110,30 @@ static void processRead(Toy_VM* vm) {
|
||||
fix_alignment(vm);
|
||||
}
|
||||
|
||||
static void processDeclare(Toy_VM* vm) {
|
||||
Toy_ValueType type = READ_BYTE(vm); //variable type
|
||||
unsigned int len = READ_BYTE(vm); //name length
|
||||
fix_alignment(vm); //one spare byte
|
||||
|
||||
//grab the jump
|
||||
unsigned int jump = *(unsigned int*)(vm->routine + vm->jumpsAddr + READ_INT(vm));
|
||||
|
||||
//grab the data
|
||||
char* cstring = (char*)(vm->routine + vm->dataAddr + jump);
|
||||
|
||||
//build the name string
|
||||
Toy_String* name = Toy_createNameStringLength(&vm->stringBucket, cstring, len, type);
|
||||
|
||||
//get the value
|
||||
Toy_Value value = Toy_popStack(&vm->stack);
|
||||
|
||||
//declare it
|
||||
Toy_declareScope(vm->scope, name, value);
|
||||
|
||||
//cleanup
|
||||
Toy_freeString(name);
|
||||
}
|
||||
|
||||
static void processArithmetic(Toy_VM* vm, Toy_OpcodeType opcode) {
|
||||
Toy_Value right = Toy_popStack(&vm->stack);
|
||||
Toy_Value left = Toy_popStack(&vm->stack);
|
||||
@@ -330,6 +354,10 @@ static void process(Toy_VM* vm) {
|
||||
processRead(vm);
|
||||
break;
|
||||
|
||||
case TOY_OPCODE_DECLARE:
|
||||
processDeclare(vm);
|
||||
break;
|
||||
|
||||
//arithmetic instructions
|
||||
case TOY_OPCODE_ADD:
|
||||
case TOY_OPCODE_SUBTRACT:
|
||||
@@ -371,9 +399,11 @@ static void process(Toy_VM* vm) {
|
||||
break;
|
||||
|
||||
//not yet implemented
|
||||
case TOY_OPCODE_DECLARE:
|
||||
case TOY_OPCODE_ASSIGN:
|
||||
case TOY_OPCODE_ACCESS:
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Incomplete opcode %d found, exiting\n" TOY_CC_RESET, opcode);
|
||||
exit(-1);
|
||||
|
||||
case TOY_OPCODE_PASS:
|
||||
case TOY_OPCODE_ERROR:
|
||||
case TOY_OPCODE_EOF:
|
||||
@@ -389,9 +419,10 @@ static void process(Toy_VM* vm) {
|
||||
//exposed functions
|
||||
void Toy_initVM(Toy_VM* vm) {
|
||||
//clear the stack, scope and memory
|
||||
vm->stack = NULL;
|
||||
//TODO: clear the scope
|
||||
vm->stringBucket = NULL;
|
||||
vm->scopeBucket = NULL;
|
||||
vm->stack = NULL;
|
||||
vm->scope = NULL;
|
||||
|
||||
Toy_resetVM(vm);
|
||||
}
|
||||
@@ -453,9 +484,10 @@ void Toy_bindVMToRoutine(Toy_VM* vm, unsigned char* routine) {
|
||||
}
|
||||
|
||||
//allocate the stack, scope, and memory
|
||||
vm->stack = Toy_allocateStack();
|
||||
//TODO: scope
|
||||
vm->stringBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||
vm->scopeBucket = Toy_allocateBucket(TOY_BUCKET_SMALL);
|
||||
vm->stack = Toy_allocateStack();
|
||||
vm->scope = Toy_pushScope(&vm->scopeBucket, NULL);
|
||||
}
|
||||
|
||||
void Toy_runVM(Toy_VM* vm) {
|
||||
@@ -471,8 +503,9 @@ void Toy_runVM(Toy_VM* vm) {
|
||||
void Toy_freeVM(Toy_VM* vm) {
|
||||
//clear the stack, scope and memory
|
||||
Toy_freeStack(vm->stack);
|
||||
//TODO: clear the scope
|
||||
Toy_popScope(vm->scope);
|
||||
Toy_freeBucket(&vm->stringBucket);
|
||||
Toy_freeBucket(&vm->scopeBucket);
|
||||
|
||||
//free the bytecode
|
||||
free(vm->bc);
|
||||
@@ -499,5 +532,5 @@ void Toy_resetVM(Toy_VM* vm) {
|
||||
|
||||
vm->routineCounter = 0;
|
||||
|
||||
//NOTE: stack, scope and memory are not altered by reset
|
||||
//NOTE: stack, scope and memory are not altered during resets
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
#include "toy_common.h"
|
||||
|
||||
#include "toy_stack.h"
|
||||
#include "toy_bucket.h"
|
||||
#include "toy_stack.h"
|
||||
#include "toy_scope.h"
|
||||
|
||||
typedef struct Toy_VM {
|
||||
//hold the raw bytecode
|
||||
@@ -30,10 +31,11 @@ typedef struct Toy_VM {
|
||||
Toy_Stack* stack;
|
||||
|
||||
//scope - block-level key/value pairs
|
||||
//TODO: Toy_Scope* scope;
|
||||
Toy_Scope* scope;
|
||||
|
||||
//easy access to memory
|
||||
Toy_Bucket* stringBucket;
|
||||
Toy_Bucket* stringBucket; //stores the string literals
|
||||
Toy_Bucket* scopeBucket; //stores the scopes
|
||||
} Toy_VM;
|
||||
|
||||
TOY_API void Toy_initVM(Toy_VM* vm);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "toy_console_colors.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int test_sizeof_ast_64bit() {
|
||||
#define TEST_SIZEOF(type, size) \
|
||||
@@ -16,6 +17,7 @@ int test_sizeof_ast_64bit() {
|
||||
//run for each type
|
||||
TEST_SIZEOF(Toy_AstType, 4);
|
||||
TEST_SIZEOF(Toy_AstBlock, 32);
|
||||
TEST_SIZEOF(Toy_AstVarDeclare, 24);
|
||||
TEST_SIZEOF(Toy_AstValue, 24);
|
||||
TEST_SIZEOF(Toy_AstUnary, 16);
|
||||
TEST_SIZEOF(Toy_AstBinary, 24);
|
||||
@@ -44,6 +46,7 @@ int test_sizeof_ast_32bit() {
|
||||
//run for each type
|
||||
TEST_SIZEOF(Toy_AstType, 4);
|
||||
TEST_SIZEOF(Toy_AstBlock, 16);
|
||||
TEST_SIZEOF(Toy_AstVarDeclare, 12);
|
||||
TEST_SIZEOF(Toy_AstValue, 12);
|
||||
TEST_SIZEOF(Toy_AstUnary, 12);
|
||||
TEST_SIZEOF(Toy_AstBinary, 16);
|
||||
@@ -218,6 +221,34 @@ int test_type_emission(Toy_Bucket** bucketHandle) {
|
||||
}
|
||||
}
|
||||
|
||||
//emit var declare
|
||||
{
|
||||
//build the AST
|
||||
Toy_Ast* ast = NULL;
|
||||
Toy_String* name = Toy_createNameStringLength(bucketHandle, "foobar", 6, TOY_VALUE_NULL);
|
||||
|
||||
Toy_private_emitAstVariableDeclaration(bucketHandle, &ast, name, NULL);
|
||||
|
||||
//check if it worked
|
||||
if (
|
||||
ast == NULL ||
|
||||
ast->type != TOY_AST_VAR_DECLARE ||
|
||||
|
||||
ast->varDeclare.name == NULL ||
|
||||
ast->varDeclare.name->type != TOY_STRING_NAME ||
|
||||
strcmp(ast->varDeclare.name->as.name.data, "foobar") != 0 ||
|
||||
|
||||
ast->varDeclare.expr != NULL)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a var declare as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
|
||||
Toy_freeString(name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//cleanup
|
||||
Toy_freeString(name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -574,6 +574,16 @@ int main() {
|
||||
total += res;
|
||||
}
|
||||
|
||||
// { //TODO: test_parser.c: test_var_declare()
|
||||
// Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||
// res = test_var_declare(&bucket);
|
||||
// Toy_freeBucket(&bucket);
|
||||
// if (res == 0) {
|
||||
// printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
|
||||
// }
|
||||
// total += res;
|
||||
// }
|
||||
|
||||
{
|
||||
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||
res = test_values(&bucket);
|
||||
|
||||
@@ -777,6 +777,114 @@ int test_routine_keywords(Toy_Bucket** bucketHandle) {
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
//var declare
|
||||
{
|
||||
//setup
|
||||
const char* source = "var foobar = 42;";
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
|
||||
Toy_bindLexer(&lexer, source);
|
||||
Toy_bindParser(&parser, &lexer);
|
||||
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
|
||||
|
||||
//run
|
||||
void* buffer = Toy_compileRoutine(ast);
|
||||
int len = ((int*)buffer)[0];
|
||||
|
||||
//check header
|
||||
int* header = (int*)buffer;
|
||||
|
||||
if (header[0] != 64 || //total size
|
||||
header[1] != 0 || //param size
|
||||
header[2] != 4 || //jumps size
|
||||
header[3] != 8 || //data size
|
||||
header[4] != 0 || //subs size
|
||||
|
||||
// header[??] != ?? || //params address
|
||||
header[5] != 32 || //code address
|
||||
header[6] != 52 || //jumps address
|
||||
header[7] != 56 || //data address
|
||||
// header[??] != ?? || //subs address
|
||||
|
||||
false)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine header, source: %s\n" TOY_CC_RESET, source);
|
||||
|
||||
//cleanup and return
|
||||
free(buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void* code = buffer + 32; //8 values in the header, each 4 bytes
|
||||
|
||||
//check code
|
||||
if (
|
||||
//code start
|
||||
*((unsigned char*)(code + 0)) != TOY_OPCODE_READ ||
|
||||
*((unsigned char*)(code + 1)) != TOY_VALUE_INTEGER ||
|
||||
*((unsigned char*)(code + 2)) != 0 ||
|
||||
*((unsigned char*)(code + 3)) != 0 ||
|
||||
|
||||
*(int*)(code + 4) != 42 ||
|
||||
|
||||
*((unsigned char*)(code + 8)) != TOY_OPCODE_DECLARE ||
|
||||
*((unsigned char*)(code + 9)) != TOY_VALUE_NULL || //NOTE: will change in future
|
||||
*((unsigned char*)(code + 10)) != 6 || //strlen
|
||||
*((unsigned char*)(code + 11)) != 0 ||
|
||||
|
||||
*(unsigned int*)(code + 12) != 0 || //the jump index
|
||||
|
||||
*((unsigned char*)(code + 16)) != TOY_OPCODE_RETURN ||
|
||||
*((unsigned char*)(code + 17)) != 0 ||
|
||||
*((unsigned char*)(code + 18)) != 0 ||
|
||||
*((unsigned char*)(code + 19)) != 0 ||
|
||||
|
||||
false)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source);
|
||||
|
||||
//cleanup and return
|
||||
free(buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void* jumps = code + 20;
|
||||
|
||||
//check jumps
|
||||
if (
|
||||
//code start
|
||||
*(unsigned int*)(jumps + 0) != 0 || //the address relative to the start of the data section
|
||||
|
||||
false)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine jumps, source: %s\n" TOY_CC_RESET, source);
|
||||
|
||||
//cleanup and return
|
||||
free(buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void* data = jumps + 4;
|
||||
|
||||
//check data
|
||||
if (
|
||||
//data start (the end of the data is padded to the nearest multiple of 4)
|
||||
strcmp( ((char*)data) + ((unsigned int*)jumps)[0], "foobar" ) != 0 ||
|
||||
|
||||
false)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine data, source: %s\n" TOY_CC_RESET, source);
|
||||
|
||||
//cleanup and return
|
||||
free(buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//cleanup
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -304,8 +304,8 @@ int test_scope_elements() {
|
||||
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||
Toy_Scope* scope = Toy_pushScope(&bucket, NULL);
|
||||
|
||||
Toy_String* hello1 = Toy_createNameString(&bucket, "hello", TOY_VALUE_NULL);
|
||||
Toy_String* hello2 = Toy_createNameString(&bucket, "hello", TOY_VALUE_NULL);
|
||||
Toy_String* hello1 = Toy_createNameStringLength(&bucket, "hello", 5, TOY_VALUE_NULL);
|
||||
Toy_String* hello2 = Toy_createNameStringLength(&bucket, "hello", 5, TOY_VALUE_NULL);
|
||||
|
||||
//check nothing is here
|
||||
if (Toy_isDeclaredScope(scope, hello2)) {
|
||||
@@ -389,7 +389,7 @@ int test_scope_elements() {
|
||||
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||
Toy_Scope* scope = Toy_pushScope(&bucket, NULL);
|
||||
|
||||
Toy_String* hello = Toy_createNameString(&bucket, "hello", TOY_VALUE_NULL);
|
||||
Toy_String* hello = Toy_createNameStringLength(&bucket, "hello", 5, TOY_VALUE_NULL);
|
||||
|
||||
//declare and push
|
||||
Toy_declareScope(scope, hello, TOY_VALUE_FROM_INTEGER(42));
|
||||
|
||||
@@ -106,7 +106,7 @@ int test_string_allocation() {
|
||||
Toy_Bucket* bucket = Toy_allocateBucket(1024);
|
||||
|
||||
const char* cstring = "Hello world";
|
||||
Toy_String* str = Toy_createNameString(&bucket, cstring, TOY_VALUE_NULL);
|
||||
Toy_String* str = Toy_createNameStringLength(&bucket, cstring, strlen(cstring), TOY_VALUE_NULL);
|
||||
|
||||
//shallow and deep
|
||||
Toy_String* shallow = Toy_copyString(str);
|
||||
@@ -652,9 +652,9 @@ int test_string_equality() {
|
||||
{
|
||||
//setup
|
||||
Toy_Bucket* bucket = Toy_allocateBucket(1024);
|
||||
Toy_String* helloWorldOne = Toy_createNameString(&bucket, "Hello world", TOY_VALUE_NULL);
|
||||
Toy_String* helloWorldTwo = Toy_createNameString(&bucket, "Hello world", TOY_VALUE_NULL);
|
||||
Toy_String* helloEveryone = Toy_createNameString(&bucket, "Hello everyone", TOY_VALUE_NULL); //TODO: compare types?
|
||||
Toy_String* helloWorldOne = Toy_createNameStringLength(&bucket, "Hello world", strlen("Hello world"), TOY_VALUE_NULL);
|
||||
Toy_String* helloWorldTwo = Toy_createNameStringLength(&bucket, "Hello world", strlen("Hello world"), TOY_VALUE_NULL);
|
||||
Toy_String* helloEveryone = Toy_createNameStringLength(&bucket, "Hello everyone", strlen("Hello everyone"), TOY_VALUE_NULL); //TODO: compare types?
|
||||
|
||||
int result = 0; //for print the errors
|
||||
|
||||
|
||||
@@ -305,6 +305,57 @@ int test_keywords(Toy_Bucket** bucketHandle) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_scope(Toy_Bucket** bucketHandle) {
|
||||
//test execution
|
||||
{
|
||||
//generate bytecode for testing
|
||||
const char* source = "var foobar = 42;";
|
||||
|
||||
Toy_Lexer lexer;
|
||||
Toy_bindLexer(&lexer, source);
|
||||
|
||||
Toy_Parser parser;
|
||||
Toy_bindParser(&parser, &lexer);
|
||||
|
||||
Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser);
|
||||
|
||||
Toy_Bytecode bc = Toy_compileBytecode(ast);
|
||||
|
||||
//run the setup
|
||||
Toy_VM vm;
|
||||
Toy_initVM(&vm);
|
||||
Toy_bindVM(&vm, bc.ptr);
|
||||
|
||||
//run
|
||||
Toy_runVM(&vm);
|
||||
|
||||
//check the final state of the stack
|
||||
Toy_String* key = Toy_createNameStringLength(bucketHandle, "foobar", 6, TOY_VALUE_NULL);
|
||||
|
||||
if (vm.stack == NULL ||
|
||||
vm.stack->count != 0 ||
|
||||
|
||||
vm.scope == NULL ||
|
||||
Toy_isDeclaredScope(vm.scope, key) == false ||
|
||||
TOY_VALUE_IS_INTEGER(Toy_accessScope(vm.scope, key)) != true ||
|
||||
TOY_VALUE_AS_INTEGER(Toy_accessScope(vm.scope, key)) != 42
|
||||
|
||||
)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected result in 'Toy_VM' when testing scope, source: %s\n" TOY_CC_RESET, source);
|
||||
|
||||
//cleanup and return
|
||||
Toy_freeVM(&vm);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//teadown
|
||||
Toy_freeVM(&vm);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_vm_reuse(Toy_Bucket** bucketHandle) {
|
||||
//run code in the same vm multiple times
|
||||
{
|
||||
@@ -424,6 +475,16 @@ int main() {
|
||||
total += res;
|
||||
}
|
||||
|
||||
{
|
||||
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||
res = test_scope(&bucket);
|
||||
Toy_freeBucket(&bucket);
|
||||
if (res == 0) {
|
||||
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
|
||||
}
|
||||
total += res;
|
||||
}
|
||||
|
||||
{
|
||||
Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
|
||||
res = test_vm_reuse(&bucket);
|
||||
|
||||
Reference in New Issue
Block a user