mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 23:04:08 +10:00
Implemented tests for Toy_Bytecode and Toy_Routine
This commit is contained in:
@@ -23,6 +23,8 @@ An additional note: The contents of the build string may be anything, such as:
|
||||
* identification information, such as the developer's copyright
|
||||
* a link to Risk Astley's "Never Gonna Give You Up" on YouTube
|
||||
|
||||
Please note that in the final bytecode, if the null terminator of TOY_VERSION_BUILD is not 4-byte aligned, extra space will be allocated to round out the header's size to a multiple of 4. The contents of the extra bytes are undefined.
|
||||
|
||||
===
|
||||
|
||||
At this time, a 'module' consists of a single 'routine', which acts as its global scope.
|
||||
@@ -39,6 +41,7 @@ Additional information may be added later, or multiple 'modules' listed sequenti
|
||||
.header:
|
||||
N total size # size of this routine, including all data and subroutines
|
||||
N .param count # the number of parameter fields expected
|
||||
N .jumps count # the number of entries in the jump table (should be data count + routine count)
|
||||
N .data count # the number of data fields expected
|
||||
N .routine count # the number of routines present
|
||||
.param start # absolute addess of .param; omitted if not needed
|
||||
@@ -57,7 +60,7 @@ Additional information may be added later, or multiple 'modules' listed sequenti
|
||||
LOAD 0
|
||||
ASSERT
|
||||
|
||||
.datatable:
|
||||
.jumptable:
|
||||
# a 'symbol -> pointer' jumptable for quickly looking up values in .data and .routines
|
||||
0 -> {string, 0x00}
|
||||
1 -> {fn, 0xFF}
|
||||
|
||||
@@ -34,6 +34,11 @@ static void writeBytecodeHeader(Toy_Bytecode* bc) {
|
||||
const char* build = Toy_private_version_build();
|
||||
int len = (int)strlen(build) + 1;
|
||||
|
||||
//BUGFIX: ensure the end of the header has 4-byte alignment
|
||||
if (len % 4 != 1) { //1 to fill the 4th byte above
|
||||
len += 4 - (len % 4) +1; //ceil
|
||||
}
|
||||
|
||||
expand(bc, len);
|
||||
memcpy(bc->ptr + bc->count, build, len);
|
||||
bc->count += len;
|
||||
|
||||
@@ -60,14 +60,29 @@ static void writeInstructionValue(Toy_Routine** rt, Toy_AstValue ast) {
|
||||
//emit the raw value based on the type
|
||||
if (TOY_VALUE_IS_NULL(ast.value)) {
|
||||
//NOTHING - null's type data is enough
|
||||
|
||||
//BUGFIX: 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
|
||||
EMIT_BYTE(rt, 0);
|
||||
}
|
||||
else if (TOY_VALUE_IS_INTEGER(ast.value)) {
|
||||
//BUGFIX: 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
|
||||
EMIT_BYTE(rt, 0);
|
||||
EMIT_BYTE(rt, 0);
|
||||
|
||||
EMIT_FLOAT(rt, code, TOY_VALUE_AS_FLOAT(ast.value));
|
||||
}
|
||||
else {
|
||||
@@ -82,6 +97,11 @@ 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
|
||||
EMIT_BYTE(rt, 0);
|
||||
EMIT_BYTE(rt, 0);
|
||||
EMIT_BYTE(rt, 0);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, TOY_CC_ERROR "Invalid AST unary flag found\n" TOY_CC_RESET);
|
||||
@@ -145,7 +165,16 @@ static void writeInstructionBinary(Toy_Routine** rt, Toy_AstBinary ast) {
|
||||
}
|
||||
else if (ast.flag == TOY_AST_FLAG_COMPARE_NOT) {
|
||||
EMIT_BYTE(rt, TOY_OPCODE_COMPARE_EQUAL);
|
||||
EMIT_BYTE(rt, TOY_OPCODE_NEGATE);
|
||||
EMIT_BYTE(rt, 0);
|
||||
EMIT_BYTE(rt, 0);
|
||||
EMIT_BYTE(rt, 0);
|
||||
|
||||
EMIT_BYTE(rt, TOY_OPCODE_NEGATE); //TODO: squeeze these into one word
|
||||
EMIT_BYTE(rt, 0);
|
||||
EMIT_BYTE(rt, 0);
|
||||
EMIT_BYTE(rt, 0);
|
||||
|
||||
return;
|
||||
}
|
||||
else if (ast.flag == TOY_AST_FLAG_COMPARE_LESS) {
|
||||
EMIT_BYTE(rt, TOY_OPCODE_COMPARE_LESS);
|
||||
@@ -170,6 +199,11 @@ static void writeInstructionBinary(Toy_Routine** rt, Toy_AstBinary ast) {
|
||||
fprintf(stderr, TOY_CC_ERROR "Invalid AST binary flag found\n" TOY_CC_RESET);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
//BUGFIX: 4-byte alignment (covers most cases)
|
||||
EMIT_BYTE(rt, 0);
|
||||
EMIT_BYTE(rt, 0);
|
||||
EMIT_BYTE(rt, 0);
|
||||
}
|
||||
|
||||
//routine structure
|
||||
@@ -236,12 +270,15 @@ static void writeRoutineCode(Toy_Routine** rt, Toy_Ast* ast) {
|
||||
|
||||
static void* writeRoutine(Toy_Routine* rt, Toy_Ast* ast) {
|
||||
//build the routine's parts
|
||||
//param
|
||||
//TODO: param
|
||||
//code
|
||||
writeRoutineCode(&rt, ast);
|
||||
EMIT_BYTE(&rt, TOY_OPCODE_RETURN); //temp terminator
|
||||
//jumps
|
||||
//data
|
||||
EMIT_BYTE(&rt, 0); //BUGFIX: 4-byte alignment
|
||||
EMIT_BYTE(&rt, 0);
|
||||
EMIT_BYTE(&rt, 0);
|
||||
//TODO: jumps
|
||||
//TODO: data
|
||||
|
||||
//write the header and combine the parts
|
||||
void* buffer = TOY_ALLOCATE(unsigned char, 16);
|
||||
@@ -251,6 +288,7 @@ static void* writeRoutine(Toy_Routine* rt, Toy_Ast* ast) {
|
||||
|
||||
emitInt(&buffer, &capacity, &count, 0); //total size (overwritten later)
|
||||
emitInt(&buffer, &capacity, &count, rt->paramCount); //param count
|
||||
emitInt(&buffer, &capacity, &count, rt->jumpsCount); //jumps count
|
||||
emitInt(&buffer, &capacity, &count, rt->dataCount); //data count
|
||||
emitInt(&buffer, &capacity, &count, rt->subsCount); //routine count
|
||||
|
||||
@@ -285,7 +323,7 @@ static void* writeRoutine(Toy_Routine* rt, Toy_Ast* ast) {
|
||||
count += rt->codeCount;
|
||||
}
|
||||
|
||||
//finally, record the total size, and return the result
|
||||
//finally, record the total size within the header, and return the result
|
||||
*((int*)buffer) = count;
|
||||
|
||||
return buffer;
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
#include "toy_common.h"
|
||||
#include "toy_ast.h"
|
||||
|
||||
//routine - internal structure that holds the individual parts of a compiled routine
|
||||
//internal structure that holds the individual parts of a compiled routine
|
||||
typedef struct Toy_Routine {
|
||||
unsigned char* param; //c-string params in sequence
|
||||
unsigned char* param; //c-string params in sequence (could be moved below the jump table?)
|
||||
int paramCapacity;
|
||||
int paramCount;
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#include "toy_bytecode.h"
|
||||
#include "toy_console_colors.h"
|
||||
|
||||
#include "toy_opcodes.h"
|
||||
#include "toy_lexer.h"
|
||||
#include "toy_parser.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -30,6 +34,135 @@ int test_bytecode_header(Toy_Bucket* bucket) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bc.count % 4 != 0) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: bytecode size is not a multiple of 4, size is: %d\n" TOY_CC_RESET, bc.count);
|
||||
|
||||
//cleanup and return
|
||||
Toy_freeBytecode(bc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//cleanup
|
||||
Toy_freeBytecode(bc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_bytecode_from_source(Toy_Bucket* bucket) {
|
||||
{
|
||||
//setup
|
||||
const char* source = "(1 + 2) * (3 + 4);";
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
|
||||
Toy_bindLexer(&lexer, source);
|
||||
Toy_bindParser(&parser, &lexer);
|
||||
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
|
||||
|
||||
//run
|
||||
Toy_Bytecode bc = Toy_compileBytecode(ast);
|
||||
|
||||
//check bytecode alignment
|
||||
if (bc.count % 4 != 0) {
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: bytecode alignment is not a multiple of 4 (size is %d), source: %s\n" TOY_CC_RESET, bc.count, source);
|
||||
|
||||
//cleanup and return
|
||||
Toy_freeBytecode(bc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//check bytecode header
|
||||
if (bc.ptr[0] != TOY_VERSION_MAJOR ||
|
||||
bc.ptr[1] != TOY_VERSION_MINOR ||
|
||||
bc.ptr[2] != TOY_VERSION_PATCH ||
|
||||
strcmp((char*)(bc.ptr + 3), TOY_VERSION_BUILD) != 0)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to write the bytecode header, source: %s\n" TOY_CC_RESET, source);
|
||||
|
||||
//cleanup and return
|
||||
Toy_freeBytecode(bc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//check contents of the routine (this is copy/pasted from test_routine.c, and tweaked with the offset)
|
||||
int offset = strlen(TOY_VERSION_BUILD) + 3;
|
||||
if (offset % 4 != 0) {
|
||||
offset += 4 - (offset % 4); //ceil
|
||||
}
|
||||
|
||||
int* ptr = (int*)(bc.ptr + offset);
|
||||
|
||||
if ((ptr++)[0] != 72 || //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 within bytecode, source: %s\n" TOY_CC_RESET, source);
|
||||
|
||||
//cleanup and return
|
||||
Toy_freeBytecode(bc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//check code
|
||||
if (
|
||||
//left hand side
|
||||
*((unsigned char*)(offset + bc.ptr + 24)) != TOY_OPCODE_READ ||
|
||||
*((unsigned char*)(offset + bc.ptr + 25)) != TOY_VALUE_INTEGER ||
|
||||
*((unsigned char*)(offset + bc.ptr + 26)) != 0 ||
|
||||
*((unsigned char*)(offset + bc.ptr + 27)) != 0 ||
|
||||
*(int*)(offset + bc.ptr + 28) != 1 ||
|
||||
|
||||
*((unsigned char*)(offset + bc.ptr + 32)) != TOY_OPCODE_READ ||
|
||||
*((unsigned char*)(offset + bc.ptr + 33)) != TOY_VALUE_INTEGER ||
|
||||
*((unsigned char*)(offset + bc.ptr + 34)) != 0 ||
|
||||
*((unsigned char*)(offset + bc.ptr + 35)) != 0 ||
|
||||
*(int*)(offset + bc.ptr + 36) != 2 ||
|
||||
|
||||
*((unsigned char*)(offset + bc.ptr + 40)) != TOY_OPCODE_ADD ||
|
||||
*((unsigned char*)(offset + bc.ptr + 41)) != 0 ||
|
||||
*((unsigned char*)(offset + bc.ptr + 42)) != 0 ||
|
||||
*((unsigned char*)(offset + bc.ptr + 43)) != 0 ||
|
||||
|
||||
//right hand side
|
||||
*((unsigned char*)(offset + bc.ptr + 44)) != TOY_OPCODE_READ ||
|
||||
*((unsigned char*)(offset + bc.ptr + 45)) != TOY_VALUE_INTEGER ||
|
||||
*((unsigned char*)(offset + bc.ptr + 46)) != 0 ||
|
||||
*((unsigned char*)(offset + bc.ptr + 47)) != 0 ||
|
||||
*(int*)(offset + bc.ptr + 48) != 3 ||
|
||||
|
||||
*((unsigned char*)(offset + bc.ptr + 52)) != TOY_OPCODE_READ ||
|
||||
*((unsigned char*)(offset + bc.ptr + 53)) != TOY_VALUE_INTEGER ||
|
||||
*((unsigned char*)(offset + bc.ptr + 54)) != 0 ||
|
||||
*((unsigned char*)(offset + bc.ptr + 55)) != 0 ||
|
||||
*(int*)(offset + bc.ptr + 56) != 4 ||
|
||||
|
||||
*((unsigned char*)(offset + bc.ptr + 60)) != TOY_OPCODE_ADD ||
|
||||
*((unsigned char*)(offset + bc.ptr + 61)) != 0 ||
|
||||
*((unsigned char*)(offset + bc.ptr + 62)) != 0 ||
|
||||
*((unsigned char*)(offset + bc.ptr + 63)) != 0 ||
|
||||
|
||||
//multiply the two values
|
||||
*((unsigned char*)(offset + bc.ptr + 64)) != TOY_OPCODE_MULTIPLY ||
|
||||
*((unsigned char*)(offset + bc.ptr + 65)) != 0 ||
|
||||
*((unsigned char*)(offset + bc.ptr + 66)) != 0 ||
|
||||
*((unsigned char*)(offset + bc.ptr + 67)) != 0 ||
|
||||
|
||||
*((unsigned char*)(offset + bc.ptr + 68)) != TOY_OPCODE_RETURN ||
|
||||
*((unsigned char*)(offset + bc.ptr + 69)) != 0 ||
|
||||
*((unsigned char*)(offset + bc.ptr + 70)) != 0 ||
|
||||
*((unsigned char*)(offset + bc.ptr + 71)) != 0
|
||||
)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code within bytecode, source: %s\n" TOY_CC_RESET, source);
|
||||
|
||||
//cleanup and return
|
||||
Toy_freeBytecode(bc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//cleanup
|
||||
Toy_freeBytecode(bc);
|
||||
}
|
||||
@@ -52,5 +185,16 @@ int main() {
|
||||
total += res;
|
||||
}
|
||||
|
||||
{
|
||||
Toy_Bucket* bucket = NULL;
|
||||
TOY_BUCKET_INIT(Toy_Ast, bucket, 32);
|
||||
res = test_bytecode_from_source(bucket);
|
||||
TOY_BUCKET_FREE(bucket);
|
||||
if (res == 0) {
|
||||
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
|
||||
}
|
||||
total += res;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
#include "toy_routine.h"
|
||||
#include "toy_console_colors.h"
|
||||
|
||||
#include "toy_opcodes.h"
|
||||
#include "toy_lexer.h"
|
||||
#include "toy_parser.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
//tests
|
||||
int test_routine_header(Toy_Bucket* bucket) {
|
||||
//simple test to ensure the header looks right
|
||||
int test_routine_header_and_values(Toy_Bucket* bucket) {
|
||||
//simple test to ensure the header looks right with an empty ast
|
||||
{
|
||||
//setup
|
||||
Toy_Ast* ast = NULL;
|
||||
@@ -16,8 +20,601 @@ int test_routine_header(Toy_Bucket* bucket) {
|
||||
void* buffer = Toy_compileRoutine(ast);
|
||||
int len = ((int*)buffer)[0];
|
||||
|
||||
//check
|
||||
//TODO
|
||||
//check header
|
||||
int* ptr = (int*)buffer;
|
||||
|
||||
if ((ptr++)[0] != 28 || //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, ast: PASS\n" TOY_CC_RESET);
|
||||
|
||||
//cleanup and return
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//check code
|
||||
if (*((unsigned char*)(buffer + 24)) != TOY_OPCODE_RETURN ||
|
||||
*((unsigned char*)(buffer + 25)) != 0 ||
|
||||
*((unsigned char*)(buffer + 26)) != 0 ||
|
||||
*((unsigned char*)(buffer + 27)) != 0
|
||||
)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, ast: PASS\n" TOY_CC_RESET);
|
||||
|
||||
//cleanup and return
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//cleanup
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
}
|
||||
|
||||
//rerun the test with a more complex ast, derived from a snippet of source
|
||||
{
|
||||
//setup
|
||||
const char* source = ";"; //interestingly, different ASTs will produce the same output
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
|
||||
Toy_bindLexer(&lexer, source);
|
||||
Toy_bindParser(&parser, &lexer);
|
||||
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
|
||||
|
||||
//run
|
||||
void* buffer = Toy_compileRoutine(ast);
|
||||
int len = ((int*)buffer)[0];
|
||||
|
||||
//check header
|
||||
int* ptr = (int*)buffer;
|
||||
|
||||
if ((ptr++)[0] != 28 || //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
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//check code
|
||||
if (*((unsigned char*)(buffer + 24)) != TOY_OPCODE_RETURN ||
|
||||
*((unsigned char*)(buffer + 25)) != 0 ||
|
||||
*((unsigned char*)(buffer + 26)) != 0 ||
|
||||
*((unsigned char*)(buffer + 27)) != 0
|
||||
)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source);
|
||||
|
||||
//cleanup and return
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//cleanup
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
}
|
||||
|
||||
//produce a null value
|
||||
{
|
||||
//setup
|
||||
const char* source = "null;";
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
|
||||
Toy_bindLexer(&lexer, source);
|
||||
Toy_bindParser(&parser, &lexer);
|
||||
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
|
||||
|
||||
//run
|
||||
void* buffer = Toy_compileRoutine(ast);
|
||||
int len = ((int*)buffer)[0];
|
||||
|
||||
//check header
|
||||
int* ptr = (int*)buffer;
|
||||
|
||||
if ((ptr++)[0] != 32 || //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
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//check code
|
||||
if (*((unsigned char*)(buffer + 24)) != TOY_OPCODE_READ ||
|
||||
*((unsigned char*)(buffer + 25)) != TOY_VALUE_NULL ||
|
||||
*((unsigned char*)(buffer + 26)) != 0 ||
|
||||
*((unsigned char*)(buffer + 27)) != 0 ||
|
||||
*((unsigned char*)(buffer + 28)) != TOY_OPCODE_RETURN ||
|
||||
*((unsigned char*)(buffer + 29)) != 0 ||
|
||||
*((unsigned char*)(buffer + 30)) != 0 ||
|
||||
*((unsigned char*)(buffer + 31)) != 0
|
||||
)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source);
|
||||
|
||||
//cleanup and return
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//cleanup
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
}
|
||||
|
||||
//produce a boolean value
|
||||
{
|
||||
//setup
|
||||
const char* source = "true;";
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
|
||||
Toy_bindLexer(&lexer, source);
|
||||
Toy_bindParser(&parser, &lexer);
|
||||
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
|
||||
|
||||
//run
|
||||
void* buffer = Toy_compileRoutine(ast);
|
||||
int len = ((int*)buffer)[0];
|
||||
|
||||
//check header
|
||||
int* ptr = (int*)buffer;
|
||||
|
||||
if ((ptr++)[0] != 32 || //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
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//check code
|
||||
if (*((unsigned char*)(buffer + 24)) != TOY_OPCODE_READ ||
|
||||
*((unsigned char*)(buffer + 25)) != TOY_VALUE_BOOLEAN ||
|
||||
*((unsigned char*)(buffer + 26)) != 1 ||
|
||||
*((unsigned char*)(buffer + 27)) != 0 ||
|
||||
*((unsigned char*)(buffer + 28)) != TOY_OPCODE_RETURN ||
|
||||
*((unsigned char*)(buffer + 29)) != 0 ||
|
||||
*((unsigned char*)(buffer + 30)) != 0 ||
|
||||
*((unsigned char*)(buffer + 31)) != 0
|
||||
)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source);
|
||||
|
||||
//cleanup and return
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//cleanup
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
}
|
||||
|
||||
//produce an integer value
|
||||
{
|
||||
//setup
|
||||
const char* source = "42;";
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
|
||||
Toy_bindLexer(&lexer, source);
|
||||
Toy_bindParser(&parser, &lexer);
|
||||
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
|
||||
|
||||
//run
|
||||
void* buffer = Toy_compileRoutine(ast);
|
||||
int len = ((int*)buffer)[0];
|
||||
|
||||
//check header
|
||||
int* ptr = (int*)buffer;
|
||||
|
||||
if ((ptr++)[0] != 36 || //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
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
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_RETURN ||
|
||||
*((unsigned char*)(buffer + 23)) != 0 ||
|
||||
*((unsigned char*)(buffer + 34)) != 0 ||
|
||||
*((unsigned char*)(buffer + 35)) != 0
|
||||
)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source);
|
||||
|
||||
//cleanup and return
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//cleanup
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
}
|
||||
|
||||
//produce a float value
|
||||
{
|
||||
//setup
|
||||
const char* source = "3.1415;";
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
|
||||
Toy_bindLexer(&lexer, source);
|
||||
Toy_bindParser(&parser, &lexer);
|
||||
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
|
||||
|
||||
//run
|
||||
void* buffer = Toy_compileRoutine(ast);
|
||||
int len = ((int*)buffer)[0];
|
||||
|
||||
//check header
|
||||
int* ptr = (int*)buffer;
|
||||
|
||||
if ((ptr++)[0] != 36 || //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
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//check code
|
||||
if (*((unsigned char*)(buffer + 24)) != TOY_OPCODE_READ ||
|
||||
*((unsigned char*)(buffer + 25)) != TOY_VALUE_FLOAT ||
|
||||
*((unsigned char*)(buffer + 26)) != 0 ||
|
||||
*((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 + 34)) != 0 ||
|
||||
*((unsigned char*)(buffer + 35)) != 0
|
||||
)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source);
|
||||
|
||||
//cleanup and return
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//cleanup
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// int test_routine_unary(Toy_Bucket* bucket) {
|
||||
// //TODO: Nothing produces a unary instruction yet
|
||||
// }
|
||||
|
||||
int test_routine_binary(Toy_Bucket* bucket) {
|
||||
//produce a simple algorithm
|
||||
{
|
||||
//setup
|
||||
const char* source = "3 + 5;";
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
|
||||
Toy_bindLexer(&lexer, source);
|
||||
Toy_bindParser(&parser, &lexer);
|
||||
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
|
||||
|
||||
//run
|
||||
void* buffer = Toy_compileRoutine(ast);
|
||||
int len = ((int*)buffer)[0];
|
||||
|
||||
//check header
|
||||
int* ptr = (int*)buffer;
|
||||
|
||||
if ((ptr++)[0] != 48 || //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
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
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) != 3 ||
|
||||
|
||||
*((unsigned char*)(buffer + 32)) != TOY_OPCODE_READ ||
|
||||
*((unsigned char*)(buffer + 33)) != TOY_VALUE_INTEGER ||
|
||||
*((unsigned char*)(buffer + 34)) != 0 ||
|
||||
*((unsigned char*)(buffer + 35)) != 0 ||
|
||||
*(int*)(buffer + 36) != 5 ||
|
||||
|
||||
*((unsigned char*)(buffer + 40)) != TOY_OPCODE_ADD ||
|
||||
*((unsigned char*)(buffer + 41)) != 0 ||
|
||||
*((unsigned char*)(buffer + 42)) != 0 ||
|
||||
*((unsigned char*)(buffer + 43)) != 0 ||
|
||||
|
||||
*((unsigned char*)(buffer + 44)) != TOY_OPCODE_RETURN ||
|
||||
*((unsigned char*)(buffer + 45)) != 0 ||
|
||||
*((unsigned char*)(buffer + 46)) != 0 ||
|
||||
*((unsigned char*)(buffer + 47)) != 0
|
||||
)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source);
|
||||
|
||||
//cleanup and return
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//cleanup
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
}
|
||||
|
||||
//produce a simple comparison
|
||||
{
|
||||
//setup
|
||||
const char* source = "3 == 5;";
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
|
||||
Toy_bindLexer(&lexer, source);
|
||||
Toy_bindParser(&parser, &lexer);
|
||||
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
|
||||
|
||||
//run
|
||||
void* buffer = Toy_compileRoutine(ast);
|
||||
int len = ((int*)buffer)[0];
|
||||
|
||||
//check header
|
||||
int* ptr = (int*)buffer;
|
||||
|
||||
if ((ptr++)[0] != 48 || //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
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
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) != 3 ||
|
||||
|
||||
*((unsigned char*)(buffer + 32)) != TOY_OPCODE_READ ||
|
||||
*((unsigned char*)(buffer + 33)) != TOY_VALUE_INTEGER ||
|
||||
*((unsigned char*)(buffer + 34)) != 0 ||
|
||||
*((unsigned char*)(buffer + 35)) != 0 ||
|
||||
*(int*)(buffer + 36) != 5 ||
|
||||
|
||||
*((unsigned char*)(buffer + 40)) != TOY_OPCODE_COMPARE_EQUAL ||
|
||||
*((unsigned char*)(buffer + 41)) != 0 ||
|
||||
*((unsigned char*)(buffer + 42)) != 0 ||
|
||||
*((unsigned char*)(buffer + 43)) != 0 ||
|
||||
|
||||
*((unsigned char*)(buffer + 44)) != TOY_OPCODE_RETURN ||
|
||||
*((unsigned char*)(buffer + 45)) != 0 ||
|
||||
*((unsigned char*)(buffer + 46)) != 0 ||
|
||||
*((unsigned char*)(buffer + 47)) != 0
|
||||
)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source);
|
||||
|
||||
//cleanup and return
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//cleanup
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
}
|
||||
|
||||
//produce a simple comparison
|
||||
{
|
||||
//setup
|
||||
const char* source = "3 != 5;";
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
|
||||
Toy_bindLexer(&lexer, source);
|
||||
Toy_bindParser(&parser, &lexer);
|
||||
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
|
||||
|
||||
//run
|
||||
void* buffer = Toy_compileRoutine(ast);
|
||||
int len = ((int*)buffer)[0];
|
||||
|
||||
//check header
|
||||
int* ptr = (int*)buffer;
|
||||
|
||||
if ((ptr++)[0] != 52 || //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
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
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) != 3 ||
|
||||
|
||||
*((unsigned char*)(buffer + 32)) != TOY_OPCODE_READ ||
|
||||
*((unsigned char*)(buffer + 33)) != TOY_VALUE_INTEGER ||
|
||||
*((unsigned char*)(buffer + 34)) != 0 ||
|
||||
*((unsigned char*)(buffer + 35)) != 0 ||
|
||||
*(int*)(buffer + 36) != 5 ||
|
||||
|
||||
*((unsigned char*)(buffer + 40)) != TOY_OPCODE_COMPARE_EQUAL ||
|
||||
*((unsigned char*)(buffer + 41)) != 0 ||
|
||||
*((unsigned char*)(buffer + 42)) != 0 ||
|
||||
*((unsigned char*)(buffer + 43)) != 0 ||
|
||||
|
||||
*((unsigned char*)(buffer + 44)) != TOY_OPCODE_NEGATE ||
|
||||
*((unsigned char*)(buffer + 45)) != 0 ||
|
||||
*((unsigned char*)(buffer + 46)) != 0 ||
|
||||
*((unsigned char*)(buffer + 47)) != 0 ||
|
||||
|
||||
*((unsigned char*)(buffer + 48)) != TOY_OPCODE_RETURN ||
|
||||
*((unsigned char*)(buffer + 49)) != 0 ||
|
||||
*((unsigned char*)(buffer + 50)) != 0 ||
|
||||
*((unsigned char*)(buffer + 51)) != 0
|
||||
)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source);
|
||||
|
||||
//cleanup and return
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//cleanup
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
}
|
||||
|
||||
//produce a more complex algorithm
|
||||
{
|
||||
//setup
|
||||
const char* source = "(1 + 2) * (3 + 4);";
|
||||
Toy_Lexer lexer;
|
||||
Toy_Parser parser;
|
||||
|
||||
Toy_bindLexer(&lexer, source);
|
||||
Toy_bindParser(&parser, &lexer);
|
||||
Toy_Ast* ast = Toy_scanParser(&bucket, &parser);
|
||||
|
||||
//run
|
||||
void* buffer = Toy_compileRoutine(ast);
|
||||
int len = ((int*)buffer)[0];
|
||||
|
||||
//check header
|
||||
int* ptr = (int*)buffer;
|
||||
|
||||
if ((ptr++)[0] != 72 || //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
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//check code
|
||||
if (
|
||||
//left hand side
|
||||
*((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) != 1 ||
|
||||
|
||||
*((unsigned char*)(buffer + 32)) != TOY_OPCODE_READ ||
|
||||
*((unsigned char*)(buffer + 33)) != TOY_VALUE_INTEGER ||
|
||||
*((unsigned char*)(buffer + 34)) != 0 ||
|
||||
*((unsigned char*)(buffer + 35)) != 0 ||
|
||||
*(int*)(buffer + 36) != 2 ||
|
||||
|
||||
*((unsigned char*)(buffer + 40)) != TOY_OPCODE_ADD ||
|
||||
*((unsigned char*)(buffer + 41)) != 0 ||
|
||||
*((unsigned char*)(buffer + 42)) != 0 ||
|
||||
*((unsigned char*)(buffer + 43)) != 0 ||
|
||||
|
||||
//right hand side
|
||||
*((unsigned char*)(buffer + 44)) != TOY_OPCODE_READ ||
|
||||
*((unsigned char*)(buffer + 45)) != TOY_VALUE_INTEGER ||
|
||||
*((unsigned char*)(buffer + 46)) != 0 ||
|
||||
*((unsigned char*)(buffer + 47)) != 0 ||
|
||||
*(int*)(buffer + 48) != 3 ||
|
||||
|
||||
*((unsigned char*)(buffer + 52)) != TOY_OPCODE_READ ||
|
||||
*((unsigned char*)(buffer + 53)) != TOY_VALUE_INTEGER ||
|
||||
*((unsigned char*)(buffer + 54)) != 0 ||
|
||||
*((unsigned char*)(buffer + 55)) != 0 ||
|
||||
*(int*)(buffer + 56) != 4 ||
|
||||
|
||||
*((unsigned char*)(buffer + 60)) != TOY_OPCODE_ADD ||
|
||||
*((unsigned char*)(buffer + 61)) != 0 ||
|
||||
*((unsigned char*)(buffer + 62)) != 0 ||
|
||||
*((unsigned char*)(buffer + 63)) != 0 ||
|
||||
|
||||
//multiply the two values
|
||||
*((unsigned char*)(buffer + 64)) != TOY_OPCODE_MULTIPLY ||
|
||||
*((unsigned char*)(buffer + 65)) != 0 ||
|
||||
*((unsigned char*)(buffer + 66)) != 0 ||
|
||||
*((unsigned char*)(buffer + 67)) != 0 ||
|
||||
|
||||
*((unsigned char*)(buffer + 68)) != TOY_OPCODE_RETURN ||
|
||||
*((unsigned char*)(buffer + 69)) != 0 ||
|
||||
*((unsigned char*)(buffer + 70)) != 0 ||
|
||||
*((unsigned char*)(buffer + 71)) != 0
|
||||
)
|
||||
{
|
||||
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source);
|
||||
|
||||
//cleanup and return
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//cleanup
|
||||
TOY_FREE_ARRAY(unsigned char, buffer, len);
|
||||
@@ -27,15 +624,24 @@ int test_routine_header(Toy_Bucket* bucket) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
fprintf(stderr, TOY_CC_WARN "WARNING: Routine tests incomplete\n" TOY_CC_RESET);
|
||||
|
||||
//run each test set, returning the total errors given
|
||||
int total = 0, res = 0;
|
||||
|
||||
{
|
||||
Toy_Bucket* bucket = NULL;
|
||||
TOY_BUCKET_INIT(Toy_Ast, bucket, 32);
|
||||
res = test_routine_header(bucket);
|
||||
res = test_routine_header_and_values(bucket);
|
||||
TOY_BUCKET_FREE(bucket);
|
||||
if (res == 0) {
|
||||
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
|
||||
}
|
||||
total += res;
|
||||
}
|
||||
|
||||
{
|
||||
Toy_Bucket* bucket = NULL;
|
||||
TOY_BUCKET_INIT(Toy_Ast, bucket, 32);
|
||||
res = test_routine_binary(bucket);
|
||||
TOY_BUCKET_FREE(bucket);
|
||||
if (res == 0) {
|
||||
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
|
||||
|
||||
Reference in New Issue
Block a user