Skip to content

Commit

Permalink
Reworked the tests, read more
Browse files Browse the repository at this point in the history
I've brought the tests up to scratch, except for compounds im the
parser, because I'm too damn tired to do that over SSH. It looks like
collections are right-recursive, whixh was unintended but still works
just fine.

I've also added the '--verbose' flag to the repl to control the
debugging output.

Several obscure bugs have been fixed, and comments have been tweaked.

Mustfail tests are still needed, but that's a low priority. See #142.

Fixed #151
  • Loading branch information
Ratstail91 committed Nov 12, 2024
1 parent b74aa63 commit be7e4dd
Show file tree
Hide file tree
Showing 22 changed files with 364 additions and 102 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/continuous-integration-v2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,3 @@ jobs:
- name: run the tests
if: (matrix.commands.gdb == true && matrix.platforms.gdb_enabled == false) != true
run: ${{ matrix.commands.exec }}


9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ This repository holds the reference implementation for Toy version 2.x, written
//print is a built-in keyword, that can handle complex expressions
print 6 * 7;
//strings can be concatenated with the .. operator
print "Hello" .. "world!";
//strings can be concatenated with the .. operator, and substringed with the [] operator
print "Hello" .. "world!"[3, 3]; //[index, length] - this prints "low"
//variables are declared easily
var foobar = 42;
Expand All @@ -40,7 +40,10 @@ var foobar = 42;
//the types default to 'any' but can be specified if needed (same with constants)
var immutable: string const = "Foobar";
//more examples to be added as the features are implemented
//the assert keyword can check an expression, and takes an optional second parameter
assert immutable == "Fizzbuzz", "This message is sent to the terminal by default";
//NOTE: This section will be expanded as more features are implemented
```

# Building
Expand Down
8 changes: 4 additions & 4 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ repl: source
.PHONY: tests
tests: clean test-cases test-integrations

.PHONY: test-all
test-all: clean test-cases test-integrations
#.PHONY: test-all
#test-all: clean test-cases test-integrations

.PHONY: test-cases
test-cases:
Expand All @@ -39,8 +39,8 @@ test-integrations:
$(MAKE) -C $(TOY_INTEGRATIONSDIR) -k

#same as above, but with GDB
.PHONY: test-gdb
test-gdb: clean test-cases-gdb test-integrations-gdb
.PHONY: tests-gdb
tests-gdb: clean test-cases-gdb test-integrations-gdb

.PHONY: test-cases-gdb
test-cases-gdb:
Expand Down
3 changes: 1 addition & 2 deletions source/toy_array.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,10 @@ TOY_API Toy_Array* Toy_resizeArray(Toy_Array* array, unsigned int capacity);

//one line to expand the array
#ifndef TOY_ARRAY_EXPAND
#define TOY_ARRAY_EXPAND(array) (array = (array != NULL && (array)->count + 1 > (array)->capacity ? Toy_resizeArray(array, (array)-> capacity * TOY_ARRAY_EXPANSION_RATE) : array))
#define TOY_ARRAY_EXPAND(array) (array = (array != NULL && (array)->count + 1 > (array)->capacity ? Toy_resizeArray(array, (array)->capacity * TOY_ARRAY_EXPANSION_RATE) : array))
#endif

//quick push back
#ifndef TOY_ARRAY_PUSHBACK
#define TOY_ARRAY_PUSHBACK(array, value) (TOY_ARRAY_EXPAND(array),(array)->data[(array)->count++] = (value))
#endif

1 change: 0 additions & 1 deletion source/toy_bytecode.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,3 @@ typedef struct Toy_Bytecode {

TOY_API Toy_Bytecode Toy_compileBytecode(Toy_Ast* ast);
TOY_API void Toy_freeBytecode(Toy_Bytecode bc);

2 changes: 1 addition & 1 deletion source/toy_lexer.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const Toy_KeywordTypeTuple keywordTuples[] = {
{TOY_TOKEN_TYPE_STRING, "string"},
{TOY_TOKEN_TYPE_ARRAY, "array"},
{TOY_TOKEN_TYPE_TABLE, "table"},
{TOY_TOKEN_TYPE_FUNCTION, "function"}, //TODO: type??
{TOY_TOKEN_TYPE_FUNCTION, "function"},
{TOY_TOKEN_TYPE_OPAQUE, "opaque"},
{TOY_TOKEN_TYPE_ANY, "any"},

Expand Down
3 changes: 1 addition & 2 deletions source/toy_opcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,10 @@ typedef enum Toy_OpcodeType {
TOY_OPCODE_PRINT,
TOY_OPCODE_CONCAT,
TOY_OPCODE_INDEX,
//TODO: clear the program stack?
//TODO: clear the program stack - much needed

//meta instructions
TOY_OPCODE_PASS,
TOY_OPCODE_ERROR,
TOY_OPCODE_EOF = 255,
} Toy_OpcodeType;

4 changes: 2 additions & 2 deletions source/toy_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ static Toy_AstFlag compound(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_A
advance(parser);

if (parser->previous.type == TOY_TOKEN_OPERATOR_COMMA) {
parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP);
parsePrecedence(bucketHandle, parser, rootHandle, PREC_GROUP); //NOT +1, as compounds are right-recursive
return TOY_AST_FLAG_COMPOUND_COLLECTION;
}
else if (parser->previous.type == TOY_TOKEN_OPERATOR_BRACKET_LEFT) {
Expand Down Expand Up @@ -848,4 +848,4 @@ void Toy_resetParser(Toy_Parser* parser) {

void Toy_configureParser(Toy_Parser* parser, bool removeAssert) {
parser->removeAssert = removeAssert;
}
}
2 changes: 0 additions & 2 deletions source/toy_value.c
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,6 @@ void Toy_stringifyValue(Toy_Value value, Toy_callbackType callback) {

case TOY_VALUE_STRING: {
Toy_String* str = TOY_VALUE_AS_STRING(value);

//TODO: decide on how long strings, etc. live for in memory
if (str->type == TOY_STRING_NODE) {
char* buffer = Toy_getStringRawBuffer(str);
callback(buffer);
Expand Down
2 changes: 1 addition & 1 deletion source/toy_vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ static void processAccess(Toy_VM* vm) {

//find and push the value
Toy_Value value = Toy_accessScope(vm->scope, TOY_VALUE_AS_STRING(name));
Toy_pushStack(&vm->stack, value);
Toy_pushStack(&vm->stack, Toy_copyValue(value));

//cleanup
Toy_freeValue(name);
Expand Down
2 changes: 1 addition & 1 deletion source/toy_vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,4 @@ TOY_API void Toy_freeVM(Toy_VM* vm);

TOY_API void Toy_resetVM(Toy_VM* vm); //prepares for another run without deleting stack, scope and memory

//TODO: inject extra data
//TODO: inject extra data (hook system for external libraries)
36 changes: 36 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Test Instructions

To run these tests, execute the following commands from the repo's root:

`make tests`
`make test-cases`
`make test-integrations`

Alternatively, to run these tests under GDB, execute the following commands from the repo's root:

`make tests-gdb`
`make test-cases-gdb`
`make test-integrations-gdb`

Remember that `make clean` will remove the build artifacts after testing, and `make tests` and `make-tests-gdb` automatically invoke `make clean` before they begin.

## Benchmarks

For testing and comparing different potential solutions. This may be left in an incomplete state, so it might not work out of the box.

## Cases

For testing individual pieces of the source code in isolation. These are essentially the unit tests.

## Integrations

This compiles the source and repl files into a library and executable, then runs each `*.toy` file through the repl to ensure the Toy code works in practice. These are essentially integration tests.

## Mustfails

These have situations which will raise errors of some kind, to ensure that common user errors are handled gracefully. This is not yet implemented.

## Standalone

These are one-file programs that are not intended to test the source directly. Instead, these can cover a number of situations, such as the exact behavior of GitHub's workflow runners, or to generate repetitive code predictably, etc.

180 changes: 121 additions & 59 deletions tests/cases/test_ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -185,73 +185,94 @@ int test_type_emission(Toy_Bucket** bucketHandle) {
}
}

//emit print keyword
//emit compound
{
//build the AST
Toy_Ast* ast = NULL;
Toy_Ast* right = NULL;
Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42));
Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69));
Toy_private_emitAstBinary(bucketHandle, &ast, TOY_AST_FLAG_ADD, right);
Toy_private_emitAstPrint(bucketHandle, &ast);
Toy_private_emitAstCompound(bucketHandle, &ast, TOY_AST_FLAG_COMPOUND_COLLECTION, right);

//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)
ast->type != TOY_AST_COMPOUND ||

ast->compound.left == NULL ||
ast->compound.left->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->compound.left->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->compound.left->value.value) != 42 ||

ast->compound.right == NULL ||
ast->compound.right->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->compound.right->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->compound.right->value.value) != 69 ||

false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a print as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a compound as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}
}

//emit and append blocks of code
//emit keyword assert
{
//initialize the root block
Toy_Ast* block = NULL;
Toy_private_initAstBlock(bucketHandle, &block);
//build the AST
Toy_Ast* ast = NULL;
Toy_Ast* child = NULL;
Toy_Ast* msg = NULL;

//loop over the ast emissions, appending each one as you go
for (int i = 0; i < 5; i++) {
//build the AST
Toy_Ast* ast = NULL;
Toy_Ast* right = NULL;
Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42));
Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69));
Toy_private_emitAstBinary(bucketHandle, &ast, TOY_AST_FLAG_ADD, right);
Toy_private_emitAstGroup(bucketHandle, &ast);
Toy_private_emitAstValue(bucketHandle, &child, TOY_VALUE_FROM_INTEGER(42));
Toy_private_emitAstValue(bucketHandle, &msg, TOY_VALUE_FROM_INTEGER(69));

Toy_private_appendAstBlock(bucketHandle, block, ast);
}
Toy_private_emitAstAssert(bucketHandle, &ast, child, msg);

//check if it worked
Toy_Ast* iter = block;
if (
ast == NULL ||
ast->type != TOY_AST_ASSERT ||
ast->assert.child == NULL ||
ast->assert.child->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->assert.child->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->assert.child->value.value) != 42 ||

ast->assert.message == NULL ||
ast->assert.message->type != TOY_AST_VALUE ||
TOY_VALUE_IS_INTEGER(ast->assert.message->value.value) != true ||
TOY_VALUE_AS_INTEGER(ast->assert.message->value.value) != 69 ||

false)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a keyword 'assert' as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}
}

while(iter != NULL) {
if (
iter->type != TOY_AST_BLOCK ||
iter->block.child == NULL ||
iter->block.child->type != TOY_AST_GROUP ||
iter->block.child->group.child == NULL ||
iter->block.child->group.child->type != TOY_AST_BINARY ||
iter->block.child->group.child->binary.flag != TOY_AST_FLAG_ADD ||
iter->block.child->group.child->binary.left->type != TOY_AST_VALUE ||
TOY_VALUE_AS_INTEGER(iter->block.child->group.child->binary.left->value.value) != 42 ||
iter->block.child->group.child->binary.right->type != TOY_AST_VALUE ||
TOY_VALUE_AS_INTEGER(iter->block.child->group.child->binary.right->value.value) != 69)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a block as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}
//emit keyword print
{
//build the AST
Toy_Ast* ast = NULL;
Toy_Ast* right = NULL;
Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42));
Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69));
Toy_private_emitAstBinary(bucketHandle, &ast, TOY_AST_FLAG_ADD, right);
Toy_private_emitAstPrint(bucketHandle, &ast);

iter = iter->block.next;
//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 keyword 'print' as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}
}

Expand Down Expand Up @@ -331,6 +352,48 @@ int test_type_emission(Toy_Bucket** bucketHandle) {
}
}

//emit and append blocks of code (at the bottom of this test function, so everything else is checked first)
{
//initialize the root block
Toy_Ast* block = NULL;
Toy_private_initAstBlock(bucketHandle, &block);

//loop over the ast emissions, appending each one as you go
for (int i = 0; i < 5; i++) {
//build the AST
Toy_Ast* ast = NULL;
Toy_Ast* right = NULL;
Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42));
Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69));
Toy_private_emitAstBinary(bucketHandle, &ast, TOY_AST_FLAG_ADD, right);
Toy_private_emitAstGroup(bucketHandle, &ast);

Toy_private_appendAstBlock(bucketHandle, block, ast);
}

//check if it worked
Toy_Ast* iter = block;

while(iter != NULL) {
if (
iter->type != TOY_AST_BLOCK ||
iter->block.child == NULL ||
iter->block.child->type != TOY_AST_GROUP ||
iter->block.child->group.child == NULL ||
iter->block.child->group.child->type != TOY_AST_BINARY ||
iter->block.child->group.child->binary.flag != TOY_AST_FLAG_ADD ||
iter->block.child->group.child->binary.left->type != TOY_AST_VALUE ||
TOY_VALUE_AS_INTEGER(iter->block.child->group.child->binary.left->value.value) != 42 ||
iter->block.child->group.child->binary.right->type != TOY_AST_VALUE ||
TOY_VALUE_AS_INTEGER(iter->block.child->group.child->binary.right->value.value) != 69)
{
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a block as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}

iter = iter->block.next;
}
}

return 0;
}
Expand All @@ -339,25 +402,24 @@ int main() {
//run each test set, returning the total errors given
int total = 0, res = 0;


{
#if TOY_BITNESS == 64
res = test_sizeof_ast_64bit();
total += res;

if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
res = test_sizeof_ast_64bit();
#elif TOY_BITNESS == 32
res = test_sizeof_ast_32bit();
total += res;

if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
res = test_sizeof_ast_32bit();
#else
fprintf(stderr, TOY_CC_WARN "WARNING: Skipping test_sizeof_ast_*bit(); Can't determine the 'bitness' of this platform (seems to be %d)\n" TOY_CC_RESET, TOY_BITNESS);
res = -1;
fprintf(stderr, TOY_CC_WARN "WARNING: Skipping test_sizeof_ast_**bit(); Can't determine the 'bitness' of this platform (seems to be %d)\n" TOY_CC_RESET, TOY_BITNESS);
#endif

if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
else if (res > 0) {
total += res;
}
}

{
Toy_Bucket* bucketHandle = Toy_allocateBucket(TOY_BUCKET_IDEAL);
res = test_type_emission(&bucketHandle);
Expand Down
Loading

0 comments on commit be7e4dd

Please sign in to comment.