Implemented and tested variable declaration

Assignment, etc. is still to come, as are types.
This commit is contained in:
2024-10-13 15:02:42 +11:00
parent 1ad6bdff70
commit 80734563b9
18 changed files with 498 additions and 99 deletions

View File

@@ -81,6 +81,16 @@ void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
(*astHandle) = tmp;
}
void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* expr) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
tmp->type = TOY_AST_VAR_DECLARE;
tmp->varDeclare.name = name;
tmp->varDeclare.expr = expr;
(*astHandle) = tmp;
}
void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

View File

@@ -4,6 +4,7 @@
#include "toy_bucket.h"
#include "toy_value.h"
#include "toy_string.h"
//each major type
typedef enum Toy_AstType {
@@ -16,6 +17,8 @@ typedef enum Toy_AstType {
TOY_AST_PRINT,
TOY_AST_VAR_DECLARE,
TOY_AST_PASS,
TOY_AST_ERROR,
TOY_AST_END,
@@ -93,6 +96,12 @@ typedef struct Toy_AstPrint {
Toy_Ast* child;
} Toy_AstPrint;
typedef struct Toy_AstVarDeclare {
Toy_AstType type;
Toy_String* name;
Toy_Ast* expr;
} Toy_AstVarDeclare;
typedef struct Toy_AstPass {
Toy_AstType type;
} Toy_AstPass;
@@ -105,18 +114,19 @@ typedef struct Toy_AstEnd {
Toy_AstType type;
} Toy_AstEnd;
union Toy_Ast { //32 | 64 BITNESS
Toy_AstType type; //4 | 4
Toy_AstBlock block; //16 | 32
Toy_AstValue value; //12 | 24
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
}; //16 | 32
union Toy_Ast { //32 | 64 BITNESS
Toy_AstType type; //4 | 4
Toy_AstBlock block; //16 | 32
Toy_AstValue value; //12 | 24
Toy_AstUnary unary; //12 | 16
Toy_AstBinary binary; //16 | 24
Toy_AstGroup group; //8 | 16
Toy_AstPrint print; //8 | 16
Toy_AstVarDeclare varDeclare; //16 | 24
Toy_AstPass pass; //4 | 4
Toy_AstError error; //4 | 4
Toy_AstEnd end; //4 | 4
}; //16 | 32
void Toy_private_initAstBlock(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_appendAstBlock(Toy_Bucket** bucketHandle, Toy_Ast* block, Toy_Ast* child);
@@ -128,6 +138,8 @@ void Toy_private_emitAstGroup(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* expr);
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);

View File

@@ -195,7 +195,7 @@ static Toy_Token makeString(Toy_Lexer* lexer, char terminator) {
return token;
}
static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
static Toy_Token makeKeywordOrName(Toy_Lexer* lexer) {
advance(lexer); //first letter can only be alpha
while(isDigit(lexer) || isAlpha(lexer)) {
@@ -218,10 +218,10 @@ static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) {
}
}
//make token (identifier)
//make token (variable name)
Toy_Token token;
token.type = TOY_TOKEN_IDENTIFIER;
token.type = TOY_TOKEN_NAME;
token.length = lexer->current - lexer->start;
token.line = lexer->line;
token.lexeme = &lexer->source[lexer->start];
@@ -247,7 +247,7 @@ Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer) {
if (isAtEnd(lexer)) return makeToken(lexer, TOY_TOKEN_EOF);
if (isDigit(lexer)) return makeIntegerOrFloat(lexer);
if (isAlpha(lexer)) return makeKeywordOrIdentifier(lexer);
if (isAlpha(lexer)) return makeKeywordOrName(lexer);
char c = advance(lexer);
@@ -335,7 +335,7 @@ void Toy_private_printToken(Toy_Token* token) {
printf("\t%d\t%d\t", token->type, (int)token->line);
//print based on type
if (token->type == TOY_TOKEN_IDENTIFIER || token->type == TOY_TOKEN_LITERAL_INTEGER || token->type == TOY_TOKEN_LITERAL_FLOAT || token->type == TOY_TOKEN_LITERAL_STRING) {
if (token->type == TOY_TOKEN_NAME || token->type == TOY_TOKEN_LITERAL_INTEGER || token->type == TOY_TOKEN_LITERAL_FLOAT || token->type == TOY_TOKEN_LITERAL_STRING) {
printf("%.*s\t", (int)token->length, token->lexeme);
} else {
const char* keyword = Toy_private_findKeywordByType(token->type);

View File

@@ -120,7 +120,7 @@ static ParsingTuple parsingRulesetTable[] = {
{PREC_PRIMARY,literal,NULL},// TOY_TOKEN_NULL,
//variable names
{PREC_NONE,NULL,NULL},// TOY_TOKEN_IDENTIFIER,
{PREC_NONE,NULL,NULL},// TOY_TOKEN_NAME,
//types
{PREC_NONE,NULL,NULL},// TOY_TOKEN_TYPE_TYPE,
@@ -528,6 +528,36 @@ static void makeExprStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast*
consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of expression statement");
}
static void makeVariableDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
consume(parser, TOY_TOKEN_NAME, "Expected variable name after 'var' keyword");
if (parser->previous.length > 256) {
printError(parser, parser->previous, "Can't have a variable name longer than 256 characters");
Toy_private_emitAstError(bucketHandle, rootHandle);
return;
}
Toy_Token nameToken = parser->previous;
//TODO: read the type specifier if present
// Toy_Token typeToken = TOY_BLANK_TOKEN();
//build the string
Toy_String* nameStr = Toy_createNameStringLength(bucketHandle, nameToken.lexeme, nameToken.length, TOY_VALUE_NULL);
//if there's an assignment, read it, or default to null
Toy_Ast* expr = NULL;
if (match(parser, TOY_TOKEN_OPERATOR_ASSIGN)) {
makeExpr(bucketHandle, parser, &expr);
}
else {
Toy_private_emitAstValue(bucketHandle, rootHandle, TOY_VALUE_FROM_NULL());
}
//finally, emit the declaration as an Ast
Toy_private_emitAstVariableDeclaration(bucketHandle, rootHandle, nameStr, expr);
}
static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
//block
//assert
@@ -558,10 +588,10 @@ static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro
}
static void makeDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
// //variable declarations
// if (match(parser, TOY_TOKEN_KEYWORD_VAR)) {
// makeVariableDeclarationStmt(bucketHandle, parser, rootHandle);
// }
//variable declarations
if (match(parser, TOY_TOKEN_KEYWORD_VAR)) {
makeVariableDeclarationStmt(bucketHandle, parser, rootHandle);
}
// //function declarations
// else if (match(parser, TOY_TOKEN_KEYWORD_FUNCTION)) {
@@ -569,9 +599,9 @@ static void makeDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, T
// }
//otherwise
// else {
else {
makeStmt(bucketHandle, parser, rootHandle);
// }
}
}
static void makeBlockStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {

View File

@@ -266,6 +266,19 @@ static void writeInstructionPrint(Toy_Routine** rt, Toy_AstPrint ast) {
EMIT_BYTE(rt, code,0);
}
static void writeInstructionVarDeclare(Toy_Routine** rt, Toy_AstVarDeclare ast) {
//initial value
writeRoutineCode(rt, ast.expr);
//delcare with the given name string
EMIT_BYTE(rt, code, TOY_OPCODE_DECLARE);
EMIT_BYTE(rt, code, Toy_getNameStringType(ast.name));
EMIT_BYTE(rt, code, ast.name->length); //quick optimisation to skip a 'strlen()' call
EMIT_BYTE(rt, code, 0);
emitString(rt, ast.name);
}
//routine structure
// static void writeRoutineParam(Toy_Routine* rt) {
// //
@@ -303,6 +316,10 @@ static void writeRoutineCode(Toy_Routine** rt, Toy_Ast* ast) {
writeInstructionPrint(rt, ast->print);
break;
case TOY_AST_VAR_DECLARE:
writeInstructionVarDeclare(rt, ast->varDeclare);
break;
//meta instructions are disallowed
case TOY_AST_PASS:
//NOTE: this should be disallowed, but for now it's required for testing

View File

@@ -91,8 +91,7 @@ Toy_String* Toy_createStringLength(Toy_Bucket** bucketHandle, const char* cstrin
return result;
}
TOY_API Toy_String* Toy_createNameString(Toy_Bucket** bucketHandle, const char* cname, Toy_ValueType type) {
unsigned int length = strlen(cname);
Toy_String* Toy_createNameStringLength(Toy_Bucket** bucketHandle, const char* cname, unsigned int length, Toy_ValueType type) {
//name strings can't be broken up
if (sizeof(Toy_String) + length + 1 > (*bucketHandle)->capacity) {
@@ -196,6 +195,15 @@ unsigned int Toy_getStringRefCount(Toy_String* str) {
return str->refCount;
}
Toy_ValueType Toy_getNameStringType(Toy_String* str) {
if (str->type != TOY_STRING_NAME) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Can't get the variable type of a non-name string\n" TOY_CC_RESET);
exit(-1);
}
return str->as.name.type;
}
char* Toy_getStringRawBuffer(Toy_String* str) {
if (str->type == TOY_STRING_NAME) {
fprintf(stderr, TOY_CC_ERROR "ERROR: Can't get raw string buffer of a name string\n" TOY_CC_RESET);

View File

@@ -38,7 +38,7 @@ typedef struct Toy_String { //32 | 64 BITNESS
TOY_API Toy_String* Toy_createString(Toy_Bucket** bucketHandle, const char* cstring);
TOY_API Toy_String* Toy_createStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length);
TOY_API Toy_String* Toy_createNameString(Toy_Bucket** bucketHandle, const char* cname, Toy_ValueType type); //for variable names
TOY_API Toy_String* Toy_createNameStringLength(Toy_Bucket** bucketHandle, const char* cname, unsigned int length, Toy_ValueType type); //for variable names
TOY_API Toy_String* Toy_copyString(Toy_String* str);
TOY_API Toy_String* Toy_deepCopyString(Toy_Bucket** bucketHandle, Toy_String* str);
@@ -49,6 +49,7 @@ TOY_API void Toy_freeString(Toy_String* str);
TOY_API unsigned int Toy_getStringLength(Toy_String* str);
TOY_API unsigned int Toy_getStringRefCount(Toy_String* str);
TOY_API Toy_ValueType Toy_getNameStringType(Toy_String* str);
TOY_API char* Toy_getStringRawBuffer(Toy_String* str); //allocates the buffer on the heap, needs to be freed

View File

@@ -6,7 +6,7 @@ typedef enum Toy_TokenType {
TOY_TOKEN_NULL,
//variable names
TOY_TOKEN_IDENTIFIER,
TOY_TOKEN_NAME,
//types
TOY_TOKEN_TYPE_TYPE,

View File

@@ -110,6 +110,30 @@ static void processRead(Toy_VM* vm) {
fix_alignment(vm);
}
static void processDeclare(Toy_VM* vm) {
Toy_ValueType type = READ_BYTE(vm); //variable type
unsigned int len = READ_BYTE(vm); //name length
fix_alignment(vm); //one spare byte
//grab the jump
unsigned int jump = *(unsigned int*)(vm->routine + vm->jumpsAddr + READ_INT(vm));
//grab the data
char* cstring = (char*)(vm->routine + vm->dataAddr + jump);
//build the name string
Toy_String* name = Toy_createNameStringLength(&vm->stringBucket, cstring, len, type);
//get the value
Toy_Value value = Toy_popStack(&vm->stack);
//declare it
Toy_declareScope(vm->scope, name, value);
//cleanup
Toy_freeString(name);
}
static void processArithmetic(Toy_VM* vm, Toy_OpcodeType opcode) {
Toy_Value right = Toy_popStack(&vm->stack);
Toy_Value left = Toy_popStack(&vm->stack);
@@ -330,6 +354,10 @@ static void process(Toy_VM* vm) {
processRead(vm);
break;
case TOY_OPCODE_DECLARE:
processDeclare(vm);
break;
//arithmetic instructions
case TOY_OPCODE_ADD:
case TOY_OPCODE_SUBTRACT:
@@ -371,9 +399,11 @@ static void process(Toy_VM* vm) {
break;
//not yet implemented
case TOY_OPCODE_DECLARE:
case TOY_OPCODE_ASSIGN:
case TOY_OPCODE_ACCESS:
fprintf(stderr, TOY_CC_ERROR "ERROR: Incomplete opcode %d found, exiting\n" TOY_CC_RESET, opcode);
exit(-1);
case TOY_OPCODE_PASS:
case TOY_OPCODE_ERROR:
case TOY_OPCODE_EOF:
@@ -389,9 +419,10 @@ static void process(Toy_VM* vm) {
//exposed functions
void Toy_initVM(Toy_VM* vm) {
//clear the stack, scope and memory
vm->stack = NULL;
//TODO: clear the scope
vm->stringBucket = NULL;
vm->scopeBucket = NULL;
vm->stack = NULL;
vm->scope = NULL;
Toy_resetVM(vm);
}
@@ -453,9 +484,10 @@ void Toy_bindVMToRoutine(Toy_VM* vm, unsigned char* routine) {
}
//allocate the stack, scope, and memory
vm->stack = Toy_allocateStack();
//TODO: scope
vm->stringBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL);
vm->scopeBucket = Toy_allocateBucket(TOY_BUCKET_SMALL);
vm->stack = Toy_allocateStack();
vm->scope = Toy_pushScope(&vm->scopeBucket, NULL);
}
void Toy_runVM(Toy_VM* vm) {
@@ -471,8 +503,9 @@ void Toy_runVM(Toy_VM* vm) {
void Toy_freeVM(Toy_VM* vm) {
//clear the stack, scope and memory
Toy_freeStack(vm->stack);
//TODO: clear the scope
Toy_popScope(vm->scope);
Toy_freeBucket(&vm->stringBucket);
Toy_freeBucket(&vm->scopeBucket);
//free the bytecode
free(vm->bc);
@@ -499,5 +532,5 @@ void Toy_resetVM(Toy_VM* vm) {
vm->routineCounter = 0;
//NOTE: stack, scope and memory are not altered by reset
//NOTE: stack, scope and memory are not altered during resets
}

View File

@@ -2,8 +2,9 @@
#include "toy_common.h"
#include "toy_stack.h"
#include "toy_bucket.h"
#include "toy_stack.h"
#include "toy_scope.h"
typedef struct Toy_VM {
//hold the raw bytecode
@@ -30,10 +31,11 @@ typedef struct Toy_VM {
Toy_Stack* stack;
//scope - block-level key/value pairs
//TODO: Toy_Scope* scope;
Toy_Scope* scope;
//easy access to memory
Toy_Bucket* stringBucket;
Toy_Bucket* stringBucket; //stores the string literals
Toy_Bucket* scopeBucket; //stores the scopes
} Toy_VM;
TOY_API void Toy_initVM(Toy_VM* vm);