Skip to content

Commit

Permalink
Implemented tests for Toy_Parser
Browse files Browse the repository at this point in the history
The parser now correctly produces a workable AST. I think I'll skip
over the optimizer, and begin on the compiler next session. The
optimizer will act directly on the AST, but it isn't totally necessary.

Other tools can also operate on the AST, such as for debugging - I'll
have to ask what kinds are out there.
  • Loading branch information
Ratstail91 committed Sep 13, 2024
1 parent b00a683 commit 898b8ef
Show file tree
Hide file tree
Showing 6 changed files with 691 additions and 71 deletions.
6 changes: 6 additions & 0 deletions source/toy_ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,9 @@ void Toy_private_emitAstError(Toy_Bucket** bucket, Toy_Ast** handle) {

(*handle)->error.type = TOY_AST_ERROR;
}

void Toy_private_emitAstEnd(Toy_Bucket** bucket, Toy_Ast** handle) {
(*handle) = (Toy_Ast*)Toy_partBucket(bucket, sizeof(Toy_Ast));

(*handle)->error.type = TOY_AST_END;
}
9 changes: 8 additions & 1 deletion source/toy_ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ typedef enum Toy_AstType {

TOY_AST_PASS,
TOY_AST_ERROR,
TOY_AST_END,
} Toy_AstType;

//flags are handled differently by different types
Expand Down Expand Up @@ -64,6 +65,7 @@ void Toy_private_emitAstGroup(Toy_Bucket** bucket, Toy_Ast** handle);

void Toy_private_emitAstPass(Toy_Bucket** bucket, Toy_Ast** handle);
void Toy_private_emitAstError(Toy_Bucket** bucket, Toy_Ast** handle);
void Toy_private_emitAstEnd(Toy_Bucket** bucket, Toy_Ast** handle);

typedef struct Toy_AstBlock {
Toy_AstType type;
Expand All @@ -85,8 +87,8 @@ typedef struct Toy_AstUnary {

typedef struct Toy_AstBinary {
Toy_AstType type;
Toy_Ast* left;
Toy_AstFlag flag;
Toy_Ast* left;
Toy_Ast* right;
} Toy_AstBinary;

Expand All @@ -104,6 +106,10 @@ typedef struct Toy_AstError {
//TODO: more data regarding the error
} Toy_AstError;

typedef struct Toy_AstEnd {
Toy_AstType type;
} Toy_AstEnd;

union Toy_Ast {
Toy_AstType type; //4
Toy_AstBlock block; //12
Expand All @@ -113,4 +119,5 @@ union Toy_Ast {
Toy_AstGroup group; //8
Toy_AstPass pass; //4
Toy_AstError error; //4
Toy_AstEnd end; //4
}; //16
4 changes: 2 additions & 2 deletions source/toy_memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
TOY_API void* Toy_reallocate(void* pointer, size_t oldSize, size_t newSize);

//immobile "bucket" memory structure for custom allocators
#define TOY_BUCKET_INIT(type, bucket, capacity) \
Toy_initBucket(&(bucket), sizeof(type)*(capacity))
#define TOY_BUCKET_INIT(type, bucket, count) \
Toy_initBucket(&(bucket), sizeof(type)*(count))

#define TOY_BUCKET_PART(type, bucket) \
(type*)Toy_partBucket(&(bucket), sizeof(type))
Expand Down
51 changes: 40 additions & 11 deletions source/toy_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,8 @@ static Toy_AstFlag atomic(Toy_Bucket** bucket, Toy_Parser* parser, Toy_Ast** roo
do {
buffer[i] = parser->previous.lexeme[o];
if (buffer[i] != '_') i++;
} while (parser->previous.lexeme[o++]);
} while (parser->previous.lexeme[o++] && i < parser->previous.length);
buffer[i] = '\0'; //BUGFIX

int value = 0;
sscanf(buffer, "%d", &value);
Expand All @@ -251,7 +252,8 @@ static Toy_AstFlag atomic(Toy_Bucket** bucket, Toy_Parser* parser, Toy_Ast** roo
do {
buffer[i] = parser->previous.lexeme[o];
if (buffer[i] != '_') i++;
} while (parser->previous.lexeme[o++]);
} while (parser->previous.lexeme[o++] && i < parser->previous.length);
buffer[i] = '\0'; //BUGFIX

float value = 0;
sscanf(buffer, "%f", &value);
Expand All @@ -267,12 +269,36 @@ static Toy_AstFlag atomic(Toy_Bucket** bucket, Toy_Parser* parser, Toy_Ast** roo
}

static Toy_AstFlag unary(Toy_Bucket** bucket, Toy_Parser* parser, Toy_Ast** root) {
if (parser->previous.type == TOY_TOKEN_OPERATOR_SUBTRACT || parser->previous.type == TOY_TOKEN_OPERATOR_NEGATE) {
//read what to negate
//'subtract' can only be applied to numbers and groups, while 'negate' can only be applied to booleans and groups
//this function takes the libery of peeking into the uppermost node, to see if it can apply this to it

if (parser->previous.type == TOY_TOKEN_OPERATOR_SUBTRACT) {
parsePrecedence(bucket, parser, root, PREC_UNARY);

//actually emit the negation node
Toy_private_emitAstUnary(bucket, root, TOY_AST_FLAG_NEGATE);
//negative numbers
if ((*root)->type == TOY_AST_VALUE && TOY_VALUE_IS_INTEGER((*root)->value.value)) {
(*root)->value.value = TOY_VALUE_TO_INTEGER( -TOY_VALUE_AS_INTEGER((*root)->value.value) );
}
else if ((*root)->type == TOY_AST_VALUE && TOY_VALUE_IS_FLOAT((*root)->value.value)) {
(*root)->value.value = TOY_VALUE_TO_FLOAT( -TOY_VALUE_AS_FLOAT((*root)->value.value) );
}
else {
//actually emit the negation node
Toy_private_emitAstUnary(bucket, root, TOY_AST_FLAG_NEGATE);
}
}

else if (parser->previous.type == TOY_TOKEN_OPERATOR_NEGATE) {
parsePrecedence(bucket, parser, root, PREC_UNARY);

//inverted booleans
if ((*root)->type == TOY_AST_VALUE && TOY_VALUE_IS_BOOLEAN((*root)->value.value)) {
(*root)->value.value = TOY_VALUE_TO_BOOLEAN( !TOY_VALUE_AS_BOOLEAN((*root)->value.value) );
}
else {
//actually emit the negation node
Toy_private_emitAstUnary(bucket, root, TOY_AST_FLAG_NEGATE);
}
}

else {
Expand Down Expand Up @@ -300,17 +326,17 @@ static Toy_AstFlag binary(Toy_Bucket** bucket, Toy_Parser* parser, Toy_Ast** roo
}

case TOY_TOKEN_OPERATOR_MULTIPLY: {
parsePrecedence(bucket, parser, root, PREC_TERM + 1);
parsePrecedence(bucket, parser, root, PREC_FACTOR + 1);
return TOY_AST_FLAG_MULTIPLY;
}

case TOY_TOKEN_OPERATOR_DIVIDE: {
parsePrecedence(bucket, parser, root, PREC_TERM + 1);
parsePrecedence(bucket, parser, root, PREC_FACTOR + 1);
return TOY_AST_FLAG_DIVIDE;
}

case TOY_TOKEN_OPERATOR_MODULO: {
parsePrecedence(bucket, parser, root, PREC_TERM + 1);
parsePrecedence(bucket, parser, root, PREC_FACTOR + 1);
return TOY_AST_FLAG_MODULO;
}

Expand Down Expand Up @@ -524,17 +550,20 @@ static void makeBlockStmt(Toy_Bucket** bucket, Toy_Parser* parser, Toy_Ast** roo
void Toy_bindParser(Toy_Parser* parser, Toy_Lexer* lexer) {
Toy_resetParser(parser);
parser->lexer = lexer;
advance(parser);
}

Toy_Ast* Toy_scanParser(Toy_Bucket** bucket, Toy_Parser* parser) {
Toy_Ast* root = NULL;

//check for EOF
if (match(parser, TOY_TOKEN_EOF)) {
return NULL;
Toy_private_emitAstEnd(bucket, &root);
return root;
}

//TODO: better errors, check for unbound parser, etc.

Toy_Ast* root = NULL;
makeBlockStmt(bucket, parser, &root);

return root;
Expand Down
65 changes: 10 additions & 55 deletions tests/cases/test_ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,9 @@ int test_sizeof_ast_32bit() {
return -err;
}

int test_type_emission() {
int test_type_emission(Toy_Bucket* bucket) {
//emit value
{
//bucket setup
Toy_Bucket* bucket = NULL;
TOY_BUCKET_INIT(Toy_Ast, bucket, 8);

//emit to an AST
Toy_Ast* ast = NULL;
Toy_private_emitAstValue(&bucket, &ast, TOY_VALUE_TO_INTEGER(42));
Expand All @@ -49,17 +45,10 @@ int test_type_emission() {
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a value as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}

//bucket free
TOY_BUCKET_FREE(bucket);
}

//emit unary
{
//bucket setup
Toy_Bucket* bucket = NULL;
TOY_BUCKET_INIT(Toy_Ast, bucket, 8);

//build the AST
Toy_Ast* ast = NULL;
Toy_private_emitAstValue(&bucket, &ast, TOY_VALUE_TO_INTEGER(42));
Expand All @@ -76,17 +65,10 @@ int test_type_emission() {
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a unary as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}

//bucket free
TOY_BUCKET_FREE(bucket);
}

//emit binary
{
//bucket setup
Toy_Bucket* bucket = NULL;
TOY_BUCKET_INIT(Toy_Ast, bucket, 8);

//build the AST
Toy_Ast* ast = NULL;
Toy_Ast* right = NULL;
Expand All @@ -107,17 +89,10 @@ int test_type_emission() {
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a binary as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}

//bucket free
TOY_BUCKET_FREE(bucket);
}

//emit group
{
//bucket setup
Toy_Bucket* bucket = NULL;
TOY_BUCKET_INIT(Toy_Ast, bucket, 8);

//build the AST
Toy_Ast* ast = NULL;
Toy_Ast* right = NULL;
Expand All @@ -141,17 +116,10 @@ int test_type_emission() {
fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a group as 'Toy_Ast', state unknown\n" TOY_CC_RESET);
return -1;
}

//bucket free
TOY_BUCKET_FREE(bucket);
}

//emit and append blocks of code
{
//bucket setup
Toy_Bucket* bucket = NULL;
TOY_BUCKET_INIT(Toy_Ast, bucket, 8);

//initialize the root block
Toy_Ast* block = NULL;
Toy_private_initAstBlock(&bucket, &block);
Expand Down Expand Up @@ -191,23 +159,6 @@ int test_type_emission() {

iter = iter->block.next;
}

//additional check: count the bucket's total allocations
Toy_Bucket* biter = bucket;
int total = 0;

while(biter != NULL) {
total += biter->count;
biter = biter->next;
}

if (total != 25 * (int)sizeof(Toy_Ast)) {
fprintf(stderr, TOY_CC_ERROR "ERROR: unexpected number of allocations found in a bucket, expected %d, found %d\n" TOY_CC_RESET, 25 * (int)sizeof(Toy_Ast), total);
return -1;
}

//bucket free
TOY_BUCKET_FREE(bucket);
}

return 0;
Expand All @@ -228,11 +179,15 @@ int main() {
fprintf(stderr, TOY_CC_WARN "WARNING: Skipping test_sizeof_ast_32bit(); Can't determine the 'bitness' of this platform\n" TOY_CC_RESET);
#endif

res = test_type_emission();
total += res;

if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
{
Toy_Bucket* bucket = NULL;
TOY_BUCKET_INIT(Toy_Ast, bucket, 32);
res = test_type_emission(bucket);
TOY_BUCKET_FREE(bucket);
if (res == 0) {
printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET);
}
total += res;
}

return total;
Expand Down
Loading

0 comments on commit 898b8ef

Please sign in to comment.