mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
Implemented print keyword and associated tests
This commit is contained in:
@@ -87,7 +87,7 @@ FN_CALL
|
||||
FN_RETURN
|
||||
*read a list of return values specified in C into 'R', pop S, restore (S, E, C, D) from D(0) popping it, store the contents of 'R' in E or S based on the next few parts of C
|
||||
|
||||
//bespoke utility instructions
|
||||
//various action instructions
|
||||
ASSERT
|
||||
if S(-1) is falsy, print S(0) and exit
|
||||
PRINT
|
||||
|
||||
@@ -22,7 +22,11 @@ This repository holds the reference implementation for Toy version 2.x, written
|
||||
|
||||
# Syntax
|
||||
|
||||
*Coming Soon.*
|
||||
```toy
|
||||
print 42; //print is a built-in keyword
|
||||
|
||||
//more examples to be added as the features are implemented
|
||||
```
|
||||
|
||||
# Building
|
||||
|
||||
|
||||
19
repl/main.c
19
repl/main.c
@@ -157,16 +157,21 @@ CmdLine parseCmdLine(int argc, const char* argv[]) {
|
||||
return cmd;
|
||||
}
|
||||
|
||||
//callback
|
||||
static void errorAndExit(const char* msg) {
|
||||
fprintf(stderr, "%s", msg);
|
||||
//callbacks
|
||||
static void printCallback(const char* msg) {
|
||||
fprintf(stdout, "%s\n", msg);
|
||||
}
|
||||
|
||||
static void errorAndExitCallback(const char* msg) {
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
//main file
|
||||
int main(int argc, const char* argv[]) {
|
||||
Toy_setErrorCallback(errorAndExit);
|
||||
Toy_setAssertFailureCallback(errorAndExit);
|
||||
Toy_setPrintCallback(printCallback);
|
||||
Toy_setErrorCallback(errorAndExitCallback);
|
||||
Toy_setAssertFailureCallback(errorAndExitCallback);
|
||||
|
||||
CmdLine cmd = parseCmdLine(argc, argv);
|
||||
|
||||
@@ -226,7 +231,8 @@ int main(int argc, const char* argv[]) {
|
||||
Toy_runVM(&vm);
|
||||
|
||||
//debugging result
|
||||
printf("printing the stack result\n\ntype\tvalue\n");
|
||||
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];
|
||||
|
||||
@@ -260,6 +266,7 @@ int main(int argc, const char* argv[]) {
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
//cleanup
|
||||
Toy_freeVM(&vm);
|
||||
|
||||
5
scripts/example-print.toy
Normal file
5
scripts/example-print.toy
Normal file
@@ -0,0 +1,5 @@
|
||||
//print statement
|
||||
print 42;
|
||||
|
||||
//it can handle complex expressions
|
||||
print 3 * 5;
|
||||
@@ -1 +1,2 @@
|
||||
//expression
|
||||
(1 + 2) * (3 + 4);
|
||||
@@ -72,6 +72,15 @@ void Toy_private_emitAstGroup(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||
(*astHandle) = tmp;
|
||||
}
|
||||
|
||||
void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||
|
||||
tmp->type = TOY_AST_PRINT;
|
||||
tmp->print.child = (*astHandle);
|
||||
|
||||
(*astHandle) = tmp;
|
||||
}
|
||||
|
||||
void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
||||
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ typedef enum Toy_AstType {
|
||||
TOY_AST_BINARY,
|
||||
TOY_AST_GROUP,
|
||||
|
||||
TOY_AST_PRINT,
|
||||
|
||||
TOY_AST_PASS,
|
||||
TOY_AST_ERROR,
|
||||
TOY_AST_END,
|
||||
@@ -85,6 +87,11 @@ typedef struct Toy_AstGroup {
|
||||
Toy_Ast* child;
|
||||
} Toy_AstGroup;
|
||||
|
||||
typedef struct Toy_AstPrint {
|
||||
Toy_AstType type;
|
||||
Toy_Ast* child;
|
||||
} Toy_AstPrint;
|
||||
|
||||
typedef struct Toy_AstPass {
|
||||
Toy_AstType type;
|
||||
} Toy_AstPass;
|
||||
@@ -105,6 +112,7 @@ union Toy_Ast { //32 | 64 BITNESS
|
||||
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
|
||||
@@ -118,6 +126,8 @@ void Toy_private_emitAstUnary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, To
|
||||
void Toy_private_emitAstBinary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right);
|
||||
void Toy_private_emitAstGroup(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||
|
||||
void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||
|
||||
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);
|
||||
@@ -33,6 +33,10 @@ typedef enum Toy_OpcodeType {
|
||||
//control instructions
|
||||
TOY_OPCODE_RETURN,
|
||||
|
||||
//various action instructions
|
||||
TOY_OPCODE_PRINT,
|
||||
//TODO: clear the program stack
|
||||
|
||||
//meta instructions
|
||||
TOY_OPCODE_PASS,
|
||||
TOY_OPCODE_ERROR,
|
||||
|
||||
@@ -482,20 +482,20 @@ static void makeExpr(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro
|
||||
parsePrecedence(bucketHandle, parser, rootHandle, PREC_ASSIGNMENT);
|
||||
}
|
||||
|
||||
static void makeExprStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||
//check for empty lines
|
||||
if (match(parser, TOY_TOKEN_OPERATOR_SEMICOLON)) {
|
||||
Toy_private_emitAstPass(bucketHandle, rootHandle);
|
||||
return;
|
||||
static void makePrintStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||
makeExpr(bucketHandle, parser, rootHandle);
|
||||
Toy_private_emitAstPrint(bucketHandle, rootHandle);
|
||||
|
||||
consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of print statement");
|
||||
}
|
||||
|
||||
static void makeExprStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||
makeExpr(bucketHandle, parser, rootHandle);
|
||||
consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of expression statement");
|
||||
}
|
||||
|
||||
static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||
//block
|
||||
//print
|
||||
//assert
|
||||
//if-then-else
|
||||
//while-then
|
||||
@@ -505,8 +505,22 @@ static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro
|
||||
//return
|
||||
//import
|
||||
|
||||
//check for empty lines
|
||||
if (match(parser, TOY_TOKEN_OPERATOR_SEMICOLON)) {
|
||||
Toy_private_emitAstPass(bucketHandle, rootHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
else if (match(parser, TOY_TOKEN_KEYWORD_PRINT)) {
|
||||
makePrintStmt(bucketHandle, parser, rootHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
else {
|
||||
//default
|
||||
makeExprStmt(bucketHandle, parser, rootHandle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void makeDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||
|
||||
@@ -33,6 +33,7 @@ void Toy_setPrintCallback(Toy_callbackType cb) {
|
||||
void Toy_setErrorCallback(Toy_callbackType cb) {
|
||||
errorCallback = cb;
|
||||
}
|
||||
|
||||
void Toy_setAssertFailureCallback(Toy_callbackType cb) {
|
||||
assertCallback = cb;
|
||||
}
|
||||
@@ -48,4 +49,3 @@ void Toy_resetErrorCallback() {
|
||||
void Toy_resetAssertFailureCallback() {
|
||||
assertCallback = errDefault;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,25 +63,25 @@ static void writeInstructionValue(Toy_Routine** rt, Toy_AstValue ast) {
|
||||
if (TOY_VALUE_IS_NULL(ast.value)) {
|
||||
//NOTHING - null's type data is enough
|
||||
|
||||
//BUGFIX: 4-byte alignment
|
||||
//4-byte alignment
|
||||
EMIT_BYTE(rt, 0);
|
||||
EMIT_BYTE(rt, 0);
|
||||
}
|
||||
else if (TOY_VALUE_IS_BOOLEAN(ast.value)) {
|
||||
EMIT_BYTE(rt, TOY_VALUE_AS_BOOLEAN(ast.value));
|
||||
|
||||
//BUGFIX: 4-byte alignment
|
||||
//4-byte alignment
|
||||
EMIT_BYTE(rt, 0);
|
||||
}
|
||||
else if (TOY_VALUE_IS_INTEGER(ast.value)) {
|
||||
//BUGFIX: 4-byte alignment
|
||||
//4-byte alignment
|
||||
EMIT_BYTE(rt, 0);
|
||||
EMIT_BYTE(rt, 0);
|
||||
|
||||
EMIT_INT(rt, code, TOY_VALUE_AS_INTEGER(ast.value));
|
||||
}
|
||||
else if (TOY_VALUE_IS_FLOAT(ast.value)) {
|
||||
//BUGFIX: 4-byte alignment
|
||||
//4-byte alignment
|
||||
EMIT_BYTE(rt, 0);
|
||||
EMIT_BYTE(rt, 0);
|
||||
|
||||
@@ -100,7 +100,7 @@ static void writeInstructionUnary(Toy_Routine** rt, Toy_AstUnary ast) {
|
||||
if (ast.flag == TOY_AST_FLAG_NEGATE) {
|
||||
EMIT_BYTE(rt, TOY_OPCODE_NEGATE);
|
||||
|
||||
//BUGFIX: 4-byte alignment
|
||||
//4-byte alignment
|
||||
EMIT_BYTE(rt, 0);
|
||||
EMIT_BYTE(rt, 0);
|
||||
EMIT_BYTE(rt, 0);
|
||||
@@ -197,7 +197,20 @@ static void writeInstructionBinary(Toy_Routine** rt, Toy_AstBinary ast) {
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
//BUGFIX: 4-byte alignment (covers most cases)
|
||||
//4-byte alignment (covers most cases)
|
||||
EMIT_BYTE(rt, 0);
|
||||
EMIT_BYTE(rt, 0);
|
||||
EMIT_BYTE(rt, 0);
|
||||
}
|
||||
|
||||
static void writeInstructionPrint(Toy_Routine** rt, Toy_AstPrint ast) {
|
||||
//the thing to print
|
||||
writeRoutineCode(rt, ast.child);
|
||||
|
||||
//output the print opcode
|
||||
EMIT_BYTE(rt, TOY_OPCODE_PRINT);
|
||||
|
||||
//4-byte alignment
|
||||
EMIT_BYTE(rt, 0);
|
||||
EMIT_BYTE(rt, 0);
|
||||
EMIT_BYTE(rt, 0);
|
||||
@@ -232,6 +245,10 @@ static void writeRoutineCode(Toy_Routine** rt, Toy_Ast* ast) {
|
||||
writeInstructionBinary(rt, ast->binary);
|
||||
break;
|
||||
|
||||
case TOY_AST_PRINT:
|
||||
writeInstructionPrint(rt, ast->print);
|
||||
break;
|
||||
|
||||
//other disallowed instructions
|
||||
case TOY_AST_GROUP:
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST type found: Group shouldn't be used\n" TOY_CC_RESET);
|
||||
@@ -271,7 +288,7 @@ static void* writeRoutine(Toy_Routine* rt, Toy_Ast* ast) {
|
||||
//code
|
||||
writeRoutineCode(&rt, ast);
|
||||
EMIT_BYTE(&rt, TOY_OPCODE_RETURN); //temp terminator
|
||||
EMIT_BYTE(&rt, 0); //BUGFIX: 4-byte alignment
|
||||
EMIT_BYTE(&rt, 0); //4-byte alignment
|
||||
EMIT_BYTE(&rt, 0);
|
||||
EMIT_BYTE(&rt, 0);
|
||||
//TODO: jumps
|
||||
|
||||
@@ -58,4 +58,3 @@ TOY_API bool Toy_private_isTruthy(Toy_Value value);
|
||||
TOY_API bool Toy_private_isEqual(Toy_Value left, Toy_Value right);
|
||||
|
||||
unsigned int Toy_hashValue(Toy_Value value);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "toy_vm.h"
|
||||
#include "toy_console_colors.h"
|
||||
|
||||
#include "toy_print.h"
|
||||
#include "toy_opcodes.h"
|
||||
#include "toy_value.h"
|
||||
|
||||
@@ -235,15 +236,55 @@ static void processLogical(Toy_VM* vm, Toy_OpcodeType opcode) {
|
||||
}
|
||||
}
|
||||
|
||||
static void processPrint(Toy_VM* vm) {
|
||||
//print the value on top of the stack, popping it
|
||||
Toy_Value value = Toy_popStack(&vm->stack);
|
||||
|
||||
//NOTE: don't append a newline - leave that choice to the host
|
||||
switch(value.type) {
|
||||
case TOY_VALUE_NULL:
|
||||
Toy_print("null");
|
||||
break;
|
||||
|
||||
case TOY_VALUE_BOOLEAN:
|
||||
Toy_print(TOY_VALUE_AS_BOOLEAN(value) ? "true" : "false");
|
||||
break;
|
||||
|
||||
case TOY_VALUE_INTEGER: {
|
||||
char buffer[16];
|
||||
sprintf(buffer, "%d", TOY_VALUE_AS_INTEGER(value));
|
||||
Toy_print(buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
case TOY_VALUE_FLOAT: {
|
||||
char buffer[16];
|
||||
sprintf(buffer, "%f", TOY_VALUE_AS_FLOAT(value));
|
||||
Toy_print(buffer);
|
||||
break;
|
||||
}
|
||||
|
||||
case TOY_VALUE_STRING: //TODO: decide on how long strings, etc. live for in memory
|
||||
case TOY_VALUE_ARRAY:
|
||||
case TOY_VALUE_DICTIONARY:
|
||||
case TOY_VALUE_FUNCTION:
|
||||
case TOY_VALUE_OPAQUE:
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown value type %d passed to processPrint, exiting\n" TOY_CC_RESET, value.type);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
static void process(Toy_VM* vm) {
|
||||
while(true) {
|
||||
Toy_OpcodeType opcode = READ_BYTE(vm);
|
||||
|
||||
switch(opcode) {
|
||||
//variable instructions
|
||||
case TOY_OPCODE_READ:
|
||||
processRead(vm);
|
||||
break;
|
||||
|
||||
//arithmetic instructions
|
||||
case TOY_OPCODE_ADD:
|
||||
case TOY_OPCODE_SUBTRACT:
|
||||
case TOY_OPCODE_MULTIPLY:
|
||||
@@ -252,6 +293,7 @@ static void process(Toy_VM* vm) {
|
||||
processArithmetic(vm, opcode);
|
||||
break;
|
||||
|
||||
//comparison instructions
|
||||
case TOY_OPCODE_COMPARE_EQUAL:
|
||||
case TOY_OPCODE_COMPARE_LESS:
|
||||
case TOY_OPCODE_COMPARE_LESS_EQUAL:
|
||||
@@ -260,6 +302,7 @@ static void process(Toy_VM* vm) {
|
||||
processComparison(vm, opcode);
|
||||
break;
|
||||
|
||||
//logical instructions
|
||||
case TOY_OPCODE_AND:
|
||||
case TOY_OPCODE_OR:
|
||||
case TOY_OPCODE_TRUTHY:
|
||||
@@ -267,6 +310,17 @@ static void process(Toy_VM* vm) {
|
||||
processLogical(vm, opcode);
|
||||
break;
|
||||
|
||||
//control instructions
|
||||
case TOY_OPCODE_RETURN:
|
||||
//temp terminator
|
||||
return;
|
||||
|
||||
//various action instructions
|
||||
case TOY_OPCODE_PRINT:
|
||||
processPrint(vm);
|
||||
break;
|
||||
|
||||
//not yet implemented
|
||||
case TOY_OPCODE_LOAD:
|
||||
case TOY_OPCODE_LOAD_LONG:
|
||||
case TOY_OPCODE_DECLARE:
|
||||
@@ -277,10 +331,6 @@ static void process(Toy_VM* vm) {
|
||||
case TOY_OPCODE_EOF:
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid opcode %d found, exiting\n" TOY_CC_RESET, opcode);
|
||||
exit(-1);
|
||||
|
||||
case TOY_OPCODE_RETURN: //temp terminator, temp position
|
||||
//
|
||||
return;
|
||||
}
|
||||
|
||||
//prepare for the next instruction
|
||||
|
||||
@@ -20,6 +20,7 @@ int test_sizeof_ast_64bit() {
|
||||
TEST_SIZEOF(Toy_AstUnary, 16);
|
||||
TEST_SIZEOF(Toy_AstBinary, 24);
|
||||
TEST_SIZEOF(Toy_AstGroup, 16);
|
||||
TEST_SIZEOF(Toy_AstPrint, 16);
|
||||
TEST_SIZEOF(Toy_AstPass, 4);
|
||||
TEST_SIZEOF(Toy_AstError, 4);
|
||||
TEST_SIZEOF(Toy_AstEnd, 4);
|
||||
@@ -47,6 +48,7 @@ int test_sizeof_ast_32bit() {
|
||||
TEST_SIZEOF(Toy_AstUnary, 12);
|
||||
TEST_SIZEOF(Toy_AstBinary, 16);
|
||||
TEST_SIZEOF(Toy_AstGroup, 8);
|
||||
TEST_SIZEOF(Toy_AstPrint, 8);
|
||||
TEST_SIZEOF(Toy_AstPass, 4);
|
||||
TEST_SIZEOF(Toy_AstError, 4);
|
||||
TEST_SIZEOF(Toy_AstEnd, 4);
|
||||
@@ -146,6 +148,33 @@ int test_type_emission(Toy_Bucket** bucketHandle) {
|
||||
}
|
||||
}
|
||||
|
||||
//emit print keyword
|
||||
{
|
||||
//build the AST
|
||||
Toy_Ast* ast = NULL;
|
||||
Toy_Ast* right = NULL;
|
||||
Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_TO_INTEGER(42));
|
||||
Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_TO_INTEGER(69));
|
||||
Toy_private_emitAstBinary(bucketHandle, &ast, TOY_AST_FLAG_ADD, right);
|
||||
Toy_private_emitAstPrint(bucketHandle, &ast);
|
||||
|
||||
//check if it worked
|
||||
if (
|
||||
ast == NULL ||
|
||||
ast->type != TOY_AST_PRINT ||
|
||||
ast->print.child == NULL ||
|
||||
ast->print.child->type != TOY_AST_BINARY ||
|
||||
ast->print.child->binary.flag != TOY_AST_FLAG_ADD ||
|
||||
ast->print.child->binary.left->type != TOY_AST_VALUE ||
|
||||
TOY_VALUE_AS_INTEGER(ast->print.child->binary.left->value.value) != 42 ||
|
||||
ast->print.child->binary.right->type != TOY_AST_VALUE ||
|
||||
TOY_VALUE_AS_INTEGER(ast->print.child->binary.right->value.value) != 69)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a print as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
//emit and append blocks of code
|
||||
{
|
||||
//initialize the root block
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <string.h>
|
||||
|
||||
//tests
|
||||
int test_routine_header_and_values(Toy_Bucket** bucketHandle) {
|
||||
int test_routine_expressions(Toy_Bucket** bucketHandle) {
|
||||
//simple test to ensure the header looks right with an empty ast
|
||||
{
|
||||
//setup
|
||||
@@ -248,7 +248,7 @@ int test_routine_header_and_values(Toy_Bucket** bucketHandle) {
|
||||
*((unsigned char*)(buffer + 27)) != 0 ||
|
||||
*(int*)(buffer + 28) != 42 ||
|
||||
*((unsigned char*)(buffer + 32)) != TOY_OPCODE_RETURN ||
|
||||
*((unsigned char*)(buffer + 23)) != 0 ||
|
||||
*((unsigned char*)(buffer + 33)) != 0 ||
|
||||
*((unsigned char*)(buffer + 34)) != 0 ||
|
||||
*((unsigned char*)(buffer + 35)) != 0
|
||||
)
|
||||
@@ -302,7 +302,7 @@ int test_routine_header_and_values(Toy_Bucket** bucketHandle) {
|
||||
*((unsigned char*)(buffer + 27)) != 0 ||
|
||||
*(float*)(buffer + 28) != 3.1415f ||
|
||||
*((unsigned char*)(buffer + 32)) != TOY_OPCODE_RETURN ||
|
||||
*((unsigned char*)(buffer + 23)) != 0 ||
|
||||
*((unsigned char*)(buffer + 33)) != 0 ||
|
||||
*((unsigned char*)(buffer + 34)) != 0 ||
|
||||
*((unsigned char*)(buffer + 35)) != 0
|
||||
)
|
||||
@@ -619,13 +619,75 @@ int test_routine_binary(Toy_Bucket** bucketHandle) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_routine_keywords(Toy_Bucket** bucketHandle) {
|
||||
//print
|
||||
{
|
||||
//setup
|
||||
const char* source = "print 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* ptr = (int*)buffer;
|
||||
|
||||
if ((ptr++)[0] != 40 || //total size
|
||||
(ptr++)[0] != 0 || //param count
|
||||
(ptr++)[0] != 0 || //jump count
|
||||
(ptr++)[0] != 0 || //data count
|
||||
(ptr++)[0] != 0) //subs count
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
//check code
|
||||
if (*((unsigned char*)(buffer + 24)) != TOY_OPCODE_READ ||
|
||||
*((unsigned char*)(buffer + 25)) != TOY_VALUE_INTEGER ||
|
||||
*((unsigned char*)(buffer + 26)) != 0 ||
|
||||
*((unsigned char*)(buffer + 27)) != 0 ||
|
||||
*(int*)(buffer + 28) != 42 ||
|
||||
*((unsigned char*)(buffer + 32)) != TOY_OPCODE_PRINT ||
|
||||
*((unsigned char*)(buffer + 33)) != 0 ||
|
||||
*((unsigned char*)(buffer + 34)) != 0 ||
|
||||
*((unsigned char*)(buffer + 35)) != 0 ||
|
||||
*((unsigned char*)(buffer + 36)) != TOY_OPCODE_RETURN ||
|
||||
*((unsigned char*)(buffer + 37)) != 0 ||
|
||||
*((unsigned char*)(buffer + 38)) != 0 ||
|
||||
*((unsigned char*)(buffer + 39)) != 0
|
||||
)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
//cleanup
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
//run each test set, returning the total errors given
|
||||
int total = 0, res = 0;
|
||||
|
||||
{
|
||||
Toy_Bucket* bucket = Toy_allocateBucket(sizeof(Toy_Ast) * 32);
|
||||
res = test_routine_header_and_values(&bucket);
|
||||
res = test_routine_expressions(&bucket);
|
||||
Toy_freeBucket(&bucket);
|
||||
if (res == 0) {
|
||||
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
|
||||
@@ -643,5 +705,15 @@ int main() {
|
||||
total += res;
|
||||
}
|
||||
|
||||
{
|
||||
Toy_Bucket* bucket = Toy_allocateBucket(sizeof(Toy_Ast) * 32);
|
||||
res = test_routine_keywords(&bucket);
|
||||
Toy_freeBucket(&bucket);
|
||||
if (res == 0) {
|
||||
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
|
||||
}
|
||||
total += res;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
@@ -4,8 +4,10 @@
|
||||
#include "toy_lexer.h"
|
||||
#include "toy_parser.h"
|
||||
#include "toy_bytecode.h"
|
||||
#include "toy_print.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
//utils
|
||||
@@ -161,6 +163,59 @@ int test_opcode_not_equal(Toy_Bucket** bucketHandle) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char* callbackUtilReceived = NULL;
|
||||
static void callbackUtil(const char* msg) {
|
||||
if (msg != NULL) {
|
||||
free(callbackUtilReceived);
|
||||
callbackUtilReceived = (char*)malloc(strlen(msg) + 1);
|
||||
strcpy(callbackUtilReceived, msg);
|
||||
}
|
||||
}
|
||||
|
||||
int test_keywords(Toy_Bucket** bucketHandle) {
|
||||
//test print
|
||||
{
|
||||
//setup
|
||||
Toy_setPrintCallback(callbackUtil);
|
||||
const char* source = "print 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);
|
||||
|
||||
Toy_VM vm;
|
||||
Toy_bindVM(&vm, bc.ptr);
|
||||
|
||||
//run
|
||||
Toy_runVM(&vm);
|
||||
|
||||
//check the final state of the stack
|
||||
if (callbackUtilReceived == NULL ||
|
||||
strcmp(callbackUtilReceived, "42") != 0)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected value '%s' passed to print keyword, source: %s\n" TOY_CC_RESET, callbackUtilReceived != NULL ? callbackUtilReceived : "NULL", source);
|
||||
|
||||
//cleanup and return
|
||||
Toy_resetPrintCallback();
|
||||
free(callbackUtilReceived);
|
||||
Toy_freeVM(&vm);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//teadown
|
||||
Toy_resetPrintCallback();
|
||||
free(callbackUtilReceived);
|
||||
Toy_freeVM(&vm);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main() {
|
||||
//run each test set, returning the total errors given
|
||||
int total = 0, res = 0;
|
||||
@@ -195,5 +250,15 @@ int main() {
|
||||
total += res;
|
||||
}
|
||||
|
||||
{
|
||||
Toy_Bucket* bucket = Toy_allocateBucket(sizeof(Toy_Ast) * 32);
|
||||
res = test_keywords(&bucket);
|
||||
Toy_freeBucket(&bucket);
|
||||
if (res == 0) {
|
||||
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
|
||||
}
|
||||
total += res;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user