Implemented tests for Toy_Parser

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.
This commit is contained in:
2024-09-13 18:08:52 +10:00
parent b00a6838be
commit 898b8efc04
6 changed files with 691 additions and 71 deletions

View File

@@ -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;
}

View File

@@ -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
@@ -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;
@@ -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;
@@ -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
@@ -113,4 +119,5 @@ union Toy_Ast {
Toy_AstGroup group; //8
Toy_AstPass pass; //4
Toy_AstError error; //4
Toy_AstEnd end; //4
}; //16

View File

@@ -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))

View File

@@ -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);
@@ -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);
@@ -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 {
@@ -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;
}
@@ -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;