mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
WIP: Fixed strings and scopes, still reworking impacted areas
This commit is contained in:
@@ -181,11 +181,13 @@ void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
|
|||||||
(*astHandle) = tmp;
|
(*astHandle) = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* expr) {
|
void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_ValueType valueType, bool constant, Toy_Ast* expr) {
|
||||||
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));
|
||||||
|
|
||||||
tmp->type = TOY_AST_VAR_DECLARE;
|
tmp->type = TOY_AST_VAR_DECLARE;
|
||||||
tmp->varDeclare.name = name;
|
tmp->varDeclare.name = name;
|
||||||
|
tmp->varDeclare.valueType = valueType;
|
||||||
|
tmp->varDeclare.constant = constant;
|
||||||
tmp->varDeclare.expr = expr;
|
tmp->varDeclare.expr = expr;
|
||||||
|
|
||||||
(*astHandle) = tmp;
|
(*astHandle) = tmp;
|
||||||
|
|||||||
@@ -185,6 +185,8 @@ typedef struct Toy_AstPrint {
|
|||||||
typedef struct Toy_AstVarDeclare {
|
typedef struct Toy_AstVarDeclare {
|
||||||
Toy_AstType type;
|
Toy_AstType type;
|
||||||
Toy_String* name;
|
Toy_String* name;
|
||||||
|
Toy_ValueType valueType;
|
||||||
|
bool constant;
|
||||||
Toy_Ast* expr;
|
Toy_Ast* expr;
|
||||||
} Toy_AstVarDeclare;
|
} Toy_AstVarDeclare;
|
||||||
|
|
||||||
@@ -273,7 +275,7 @@ void Toy_private_emitAstContinue(Toy_Bucket** bucketHandle, Toy_Ast** rootHandle
|
|||||||
void Toy_private_emitAstReturn(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
void Toy_private_emitAstReturn(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||||
void Toy_private_emitAstPrint(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_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_ValueType valueType, bool constant, Toy_Ast* expr);
|
||||||
void Toy_private_emitAstVariableAssignment(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* expr);
|
void Toy_private_emitAstVariableAssignment(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* expr);
|
||||||
void Toy_private_emitAstVariableAccess(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
void Toy_private_emitAstVariableAccess(Toy_Bucket** bucketHandle, Toy_Ast** astHandle);
|
||||||
|
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ static unsigned int emitString(Toy_ModuleCompiler** mb, Toy_String* str) {
|
|||||||
|
|
||||||
//move the string into the data section
|
//move the string into the data section
|
||||||
if (str->info.type == TOY_STRING_NODE) {
|
if (str->info.type == TOY_STRING_NODE) {
|
||||||
char* buffer = Toy_getStringRawBuffer(str);
|
char* buffer = Toy_getStringRaw(str);
|
||||||
|
|
||||||
dataAddr = emitCStringToData(&(*mb)->data, &(*mb)->dataCapacity, &(*mb)->dataCount, buffer);
|
dataAddr = emitCStringToData(&(*mb)->data, &(*mb)->dataCapacity, &(*mb)->dataCount, buffer);
|
||||||
|
|
||||||
@@ -157,9 +157,6 @@ static unsigned int emitString(Toy_ModuleCompiler** mb, Toy_String* str) {
|
|||||||
else if (str->info.type == TOY_STRING_LEAF) {
|
else if (str->info.type == TOY_STRING_LEAF) {
|
||||||
dataAddr = emitCStringToData(&(*mb)->data, &(*mb)->dataCapacity, &(*mb)->dataCount, str->leaf.data);
|
dataAddr = emitCStringToData(&(*mb)->data, &(*mb)->dataCapacity, &(*mb)->dataCount, str->leaf.data);
|
||||||
}
|
}
|
||||||
else if (str->info.type == TOY_STRING_NAME) {
|
|
||||||
dataAddr = emitCStringToData(&(*mb)->data, &(*mb)->dataCapacity, &(*mb)->dataCount, str->name.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
//mark the position within the jump index, reusing an existing entry if it exists
|
//mark the position within the jump index, reusing an existing entry if it exists
|
||||||
for (unsigned int i = 0; i < (*mb)->jumpsCount; i++) {
|
for (unsigned int i = 0; i < (*mb)->jumpsCount; i++) {
|
||||||
@@ -193,15 +190,8 @@ static unsigned int emitParameters(Toy_ModuleCompiler* mb, Toy_Ast* ast) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//check the string type
|
|
||||||
if (TOY_VALUE_IS_STRING(ast->value.value) == false || TOY_VALUE_AS_STRING(ast->value.value)->info.type != TOY_STRING_NAME) {
|
|
||||||
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Function parameters must be name strings\n" TOY_CC_RESET);
|
|
||||||
mb->panic = true;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//the address within the data section
|
//the address within the data section
|
||||||
unsigned int dataAddr = emitCStringToData(&(mb->data), &(mb->dataCapacity), &(mb->dataCount), TOY_VALUE_AS_STRING(ast->value.value)->name.data);
|
unsigned int dataAddr = emitCStringToData(&(mb->data), &(mb->dataCapacity), &(mb->dataCount), TOY_VALUE_AS_STRING(ast->value.value)->leaf.data);
|
||||||
|
|
||||||
//check the param index for that entry i.e. don't reuse parameter names
|
//check the param index for that entry i.e. don't reuse parameter names
|
||||||
for (unsigned int i = 0; i < mb->paramCount; i++) {
|
for (unsigned int i = 0; i < mb->paramCount; i++) {
|
||||||
@@ -215,7 +205,7 @@ static unsigned int emitParameters(Toy_ModuleCompiler* mb, Toy_Ast* ast) {
|
|||||||
|
|
||||||
//emit to the param index
|
//emit to the param index
|
||||||
EMIT_INT(&mb, param, dataAddr);
|
EMIT_INT(&mb, param, dataAddr);
|
||||||
EMIT_INT(&mb, param, (unsigned int)(TOY_VALUE_AS_STRING(ast->value.value)->name.varType)); //don't forget this bit
|
EMIT_INT(&mb, param, TOY_VALUE_UNKNOWN); //TODO: encode function parameter types properly
|
||||||
|
|
||||||
//this returns the number of written parameters
|
//this returns the number of written parameters
|
||||||
return 1;
|
return 1;
|
||||||
@@ -293,7 +283,7 @@ static unsigned int writeInstructionUnary(Toy_ModuleCompiler** mb, Toy_AstUnary
|
|||||||
|
|
||||||
EMIT_BYTE(mb, code, TOY_OPCODE_READ);
|
EMIT_BYTE(mb, code, TOY_OPCODE_READ);
|
||||||
EMIT_BYTE(mb, code, TOY_VALUE_STRING);
|
EMIT_BYTE(mb, code, TOY_VALUE_STRING);
|
||||||
EMIT_BYTE(mb, code, TOY_STRING_NAME);
|
EMIT_BYTE(mb, code, TOY_STRING_LEAF);
|
||||||
EMIT_BYTE(mb, code, name->info.length); //store the length (max 255)
|
EMIT_BYTE(mb, code, name->info.length); //store the length (max 255)
|
||||||
|
|
||||||
emitString(mb, name);
|
emitString(mb, name);
|
||||||
@@ -328,7 +318,7 @@ static unsigned int writeInstructionUnary(Toy_ModuleCompiler** mb, Toy_AstUnary
|
|||||||
|
|
||||||
EMIT_BYTE(mb, code, TOY_OPCODE_READ);
|
EMIT_BYTE(mb, code, TOY_OPCODE_READ);
|
||||||
EMIT_BYTE(mb, code, TOY_VALUE_STRING);
|
EMIT_BYTE(mb, code, TOY_VALUE_STRING);
|
||||||
EMIT_BYTE(mb, code, TOY_STRING_NAME);
|
EMIT_BYTE(mb, code, TOY_STRING_LEAF);
|
||||||
EMIT_BYTE(mb, code, name->info.length); //store the length (max 255)
|
EMIT_BYTE(mb, code, name->info.length); //store the length (max 255)
|
||||||
|
|
||||||
emitString(mb, name);
|
emitString(mb, name);
|
||||||
@@ -344,7 +334,7 @@ static unsigned int writeInstructionUnary(Toy_ModuleCompiler** mb, Toy_AstUnary
|
|||||||
|
|
||||||
EMIT_BYTE(mb, code, TOY_OPCODE_READ);
|
EMIT_BYTE(mb, code, TOY_OPCODE_READ);
|
||||||
EMIT_BYTE(mb, code, TOY_VALUE_STRING);
|
EMIT_BYTE(mb, code, TOY_VALUE_STRING);
|
||||||
EMIT_BYTE(mb, code, TOY_STRING_NAME);
|
EMIT_BYTE(mb, code, TOY_STRING_LEAF);
|
||||||
EMIT_BYTE(mb, code, name->info.length); //store the length (max 255)
|
EMIT_BYTE(mb, code, name->info.length); //store the length (max 255)
|
||||||
|
|
||||||
emitString(mb, name);
|
emitString(mb, name);
|
||||||
@@ -793,9 +783,9 @@ static unsigned int writeInstructionVarDeclare(Toy_ModuleCompiler** mb, Toy_AstV
|
|||||||
|
|
||||||
//delcare with the given name string
|
//delcare with the given name string
|
||||||
EMIT_BYTE(mb, code, TOY_OPCODE_DECLARE);
|
EMIT_BYTE(mb, code, TOY_OPCODE_DECLARE);
|
||||||
EMIT_BYTE(mb, code, Toy_getNameStringVarType(ast.name));
|
EMIT_BYTE(mb, code, ast.type);
|
||||||
EMIT_BYTE(mb, code, ast.name->info.length); //quick optimisation to skip a 'strlen()' call
|
EMIT_BYTE(mb, code, ast.name->info.length); //quick optimisation to skip a 'strlen()' call
|
||||||
EMIT_BYTE(mb, code, Toy_getNameStringVarConstant(ast.name) ? 1 : 0); //check for constness
|
EMIT_BYTE(mb, code, ast.constant); //check for constness
|
||||||
|
|
||||||
emitString(mb, ast.name);
|
emitString(mb, ast.name);
|
||||||
|
|
||||||
@@ -806,14 +796,14 @@ static unsigned int writeInstructionAssign(Toy_ModuleCompiler** mb, Toy_AstVarAs
|
|||||||
unsigned int result = 0;
|
unsigned int result = 0;
|
||||||
|
|
||||||
//target is a name string
|
//target is a name string
|
||||||
if (ast.target->type == TOY_AST_VALUE && TOY_VALUE_IS_STRING(ast.target->value.value) && TOY_VALUE_AS_STRING(ast.target->value.value)->info.type == TOY_STRING_NAME) {
|
if (ast.target->type == TOY_AST_VALUE && TOY_VALUE_IS_STRING(ast.target->value.value)) {
|
||||||
//name string
|
//name string
|
||||||
Toy_String* target = TOY_VALUE_AS_STRING(ast.target->value.value);
|
Toy_String* target = TOY_VALUE_AS_STRING(ast.target->value.value);
|
||||||
|
|
||||||
//emit the name string
|
//emit the name string
|
||||||
EMIT_BYTE(mb, code, TOY_OPCODE_READ);
|
EMIT_BYTE(mb, code, TOY_OPCODE_READ);
|
||||||
EMIT_BYTE(mb, code, TOY_VALUE_STRING);
|
EMIT_BYTE(mb, code, TOY_VALUE_STRING);
|
||||||
EMIT_BYTE(mb, code, TOY_STRING_NAME);
|
EMIT_BYTE(mb, code, TOY_STRING_LEAF);
|
||||||
EMIT_BYTE(mb, code, target->info.length); //store the length (max 255)
|
EMIT_BYTE(mb, code, target->info.length); //store the length (max 255)
|
||||||
|
|
||||||
emitString(mb, target);
|
emitString(mb, target);
|
||||||
@@ -967,7 +957,7 @@ static unsigned int writeInstructionAssign(Toy_ModuleCompiler** mb, Toy_AstVarAs
|
|||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int writeInstructionAccess(Toy_ModuleCompiler** mb, Toy_AstVarAccess ast) {
|
static unsigned int writeInstructionAccess(Toy_ModuleCompiler** mb, Toy_AstVarAccess ast) {
|
||||||
if (!(ast.child->type == TOY_AST_VALUE && TOY_VALUE_IS_STRING(ast.child->value.value) && TOY_VALUE_AS_STRING(ast.child->value.value)->info.type == TOY_STRING_NAME)) {
|
if (!(ast.child->type == TOY_AST_VALUE && TOY_VALUE_IS_STRING(ast.child->value.value))) {
|
||||||
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Found a non-name-string in a value node when trying to write access\n" TOY_CC_RESET);
|
fprintf(stderr, TOY_CC_ERROR "COMPILER ERROR: Found a non-name-string in a value node when trying to write access\n" TOY_CC_RESET);
|
||||||
(*mb)->panic = true;
|
(*mb)->panic = true;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -978,7 +968,7 @@ static unsigned int writeInstructionAccess(Toy_ModuleCompiler** mb, Toy_AstVarAc
|
|||||||
//push the name
|
//push the name
|
||||||
EMIT_BYTE(mb, code, TOY_OPCODE_READ);
|
EMIT_BYTE(mb, code, TOY_OPCODE_READ);
|
||||||
EMIT_BYTE(mb, code, TOY_VALUE_STRING);
|
EMIT_BYTE(mb, code, TOY_VALUE_STRING);
|
||||||
EMIT_BYTE(mb, code, TOY_STRING_NAME);
|
EMIT_BYTE(mb, code, TOY_STRING_LEAF);
|
||||||
EMIT_BYTE(mb, code, name->info.length); //store the length (max 255)
|
EMIT_BYTE(mb, code, name->info.length); //store the length (max 255)
|
||||||
|
|
||||||
emitString(mb, name);
|
emitString(mb, name);
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ typedef struct ParsingTuple {
|
|||||||
|
|
||||||
static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle, ParsingPrecedence precRule);
|
static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle, ParsingPrecedence precRule);
|
||||||
|
|
||||||
static Toy_AstFlag nameString(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
|
static Toy_AstFlag identifier(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
|
||||||
static Toy_AstFlag literal(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
|
static Toy_AstFlag literal(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
|
||||||
static Toy_AstFlag unary(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
|
static Toy_AstFlag unary(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
|
||||||
static Toy_AstFlag binary(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
|
static Toy_AstFlag binary(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
|
||||||
@@ -127,7 +127,7 @@ static ParsingTuple parsingRulesetTable[] = {
|
|||||||
{PREC_PRIMARY,literal,NULL},// TOY_TOKEN_NULL,
|
{PREC_PRIMARY,literal,NULL},// TOY_TOKEN_NULL,
|
||||||
|
|
||||||
//variable names (initially handled as a string)
|
//variable names (initially handled as a string)
|
||||||
{PREC_PRIMARY,nameString,NULL},// TOY_TOKEN_NAME,
|
{PREC_PRIMARY,identifier,NULL},// TOY_TOKEN_NAME,
|
||||||
|
|
||||||
//types
|
//types
|
||||||
{PREC_NONE,NULL,NULL},// TOY_TOKEN_TYPE_BOOLEAN,
|
{PREC_NONE,NULL,NULL},// TOY_TOKEN_TYPE_BOOLEAN,
|
||||||
@@ -266,9 +266,9 @@ static Toy_ValueType readType(Toy_Parser* parser) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Toy_AstFlag nameString(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
static Toy_AstFlag identifier(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
|
||||||
//emit the name string
|
//emit the name string
|
||||||
Toy_String* name = Toy_createNameStringLength(bucketHandle, parser->previous.lexeme, parser->previous.length, TOY_VALUE_UNKNOWN, false);
|
Toy_String* name = Toy_toStringLength(bucketHandle, parser->previous.lexeme, parser->previous.length);
|
||||||
Toy_Value value = TOY_VALUE_FROM_STRING(name);
|
Toy_Value value = TOY_VALUE_FROM_STRING(name);
|
||||||
Toy_private_emitAstValue(bucketHandle, rootHandle, value);
|
Toy_private_emitAstValue(bucketHandle, rootHandle, value);
|
||||||
|
|
||||||
@@ -388,7 +388,7 @@ static Toy_AstFlag literal(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_As
|
|||||||
|
|
||||||
buffer[i] = '\0';
|
buffer[i] = '\0';
|
||||||
unsigned int len = i - escapeCounter; //NOTE: len is ONLY the string length
|
unsigned int len = i - escapeCounter; //NOTE: len is ONLY the string length
|
||||||
Toy_private_emitAstValue(bucketHandle, rootHandle, TOY_VALUE_FROM_STRING(Toy_createStringLength(bucketHandle, buffer, len)));
|
Toy_private_emitAstValue(bucketHandle, rootHandle, TOY_VALUE_FROM_STRING(Toy_toStringLength(bucketHandle, buffer, len)));
|
||||||
|
|
||||||
return TOY_AST_FLAG_NONE;
|
return TOY_AST_FLAG_NONE;
|
||||||
}
|
}
|
||||||
@@ -434,7 +434,7 @@ static Toy_AstFlag unary(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast*
|
|||||||
parsePrecedence(bucketHandle, parser, &primary, PREC_PRIMARY);
|
parsePrecedence(bucketHandle, parser, &primary, PREC_PRIMARY);
|
||||||
|
|
||||||
//double check it's a name string within an access NOTE: doing some fiddling with the existing AST here
|
//double check it's a name string within an access NOTE: doing some fiddling with the existing AST here
|
||||||
if (primary->type != TOY_AST_VAR_ACCESS || primary->varAccess.child->type != TOY_AST_VALUE || TOY_VALUE_IS_STRING(primary->varAccess.child->value.value) != true || TOY_VALUE_AS_STRING(primary->varAccess.child->value.value)->info.type != TOY_STRING_NAME) {
|
if (primary->type != TOY_AST_VAR_ACCESS || primary->varAccess.child->type != TOY_AST_VALUE || TOY_VALUE_IS_STRING(primary->varAccess.child->value.value) != true) {
|
||||||
printError(parser, parser->previous, "Unexpected non-name-string token in unary-prefix operator precedence rule");
|
printError(parser, parser->previous, "Unexpected non-name-string token in unary-prefix operator precedence rule");
|
||||||
Toy_private_emitAstError(bucketHandle, rootHandle);
|
Toy_private_emitAstError(bucketHandle, rootHandle);
|
||||||
}
|
}
|
||||||
@@ -685,7 +685,7 @@ static Toy_AstFlag unaryPostfix(Toy_Bucket** bucketHandle, Toy_Parser* parser, T
|
|||||||
nameRule(bucketHandle, parser, &primary); //this is to skip the call to advance() at the beginning of parsePrecedence()
|
nameRule(bucketHandle, parser, &primary); //this is to skip the call to advance() at the beginning of parsePrecedence()
|
||||||
|
|
||||||
//double check it's a name string within an access NOTE: doing some fiddling with the existing AST here
|
//double check it's a name string within an access NOTE: doing some fiddling with the existing AST here
|
||||||
if (primary->type != TOY_AST_VAR_ACCESS || primary->varAccess.child->type != TOY_AST_VALUE || TOY_VALUE_IS_STRING(primary->varAccess.child->value.value) != true || TOY_VALUE_AS_STRING(primary->varAccess.child->value.value)->info.type != TOY_STRING_NAME) {
|
if (primary->type != TOY_AST_VAR_ACCESS || primary->varAccess.child->type != TOY_AST_VALUE || TOY_VALUE_IS_STRING(primary->varAccess.child->value.value) != true) {
|
||||||
printError(parser, parser->previous, "Unexpected non-name-string token in unary-postfix operator precedence rule");
|
printError(parser, parser->previous, "Unexpected non-name-string token in unary-postfix operator precedence rule");
|
||||||
Toy_private_emitAstError(bucketHandle, rootHandle);
|
Toy_private_emitAstError(bucketHandle, rootHandle);
|
||||||
return TOY_AST_FLAG_NONE;
|
return TOY_AST_FLAG_NONE;
|
||||||
@@ -924,7 +924,7 @@ static void makeVariableDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* p
|
|||||||
}
|
}
|
||||||
|
|
||||||
//build the name string
|
//build the name string
|
||||||
Toy_String* nameStr = Toy_createNameStringLength(bucketHandle, nameToken.lexeme, nameToken.length, varType, constant);
|
Toy_String* nameStr = Toy_toStringLength(bucketHandle, nameToken.lexeme, nameToken.length);
|
||||||
|
|
||||||
//if there's an assignment, read it, or default to null
|
//if there's an assignment, read it, or default to null
|
||||||
Toy_Ast* expr = NULL;
|
Toy_Ast* expr = NULL;
|
||||||
@@ -936,7 +936,7 @@ static void makeVariableDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* p
|
|||||||
}
|
}
|
||||||
|
|
||||||
//finally, emit the declaration as an Ast
|
//finally, emit the declaration as an Ast
|
||||||
Toy_private_emitAstVariableDeclaration(bucketHandle, rootHandle, nameStr, expr);
|
Toy_private_emitAstVariableDeclaration(bucketHandle, rootHandle, nameStr, varType, constant, expr);
|
||||||
|
|
||||||
consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of var statement");
|
consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of var statement");
|
||||||
}
|
}
|
||||||
@@ -952,7 +952,7 @@ static void makeFunctionDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* p
|
|||||||
|
|
||||||
//build the name string
|
//build the name string
|
||||||
Toy_Token nameToken = parser->previous;
|
Toy_Token nameToken = parser->previous;
|
||||||
Toy_String* nameStr = Toy_createNameStringLength(bucketHandle, nameToken.lexeme, nameToken.length, TOY_VALUE_FUNCTION, true);
|
Toy_String* nameStr = Toy_toStringLength(bucketHandle, nameToken.lexeme, nameToken.length);
|
||||||
|
|
||||||
//read the function parameters (done manually to avoid other syntax issues)
|
//read the function parameters (done manually to avoid other syntax issues)
|
||||||
Toy_Ast* params = NULL;
|
Toy_Ast* params = NULL;
|
||||||
@@ -970,23 +970,25 @@ static void makeFunctionDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* p
|
|||||||
advance(parser);
|
advance(parser);
|
||||||
Toy_Token nameToken = parser->previous;
|
Toy_Token nameToken = parser->previous;
|
||||||
|
|
||||||
|
//TODO: fix this with param type info
|
||||||
//read the type specifier if present
|
//read the type specifier if present
|
||||||
Toy_ValueType varType = TOY_VALUE_ANY;
|
// Toy_ValueType varType = TOY_VALUE_ANY;
|
||||||
bool constant = true; //parameters are immutable
|
// bool constant = true; //parameters are immutable
|
||||||
|
|
||||||
if (match(parser, TOY_TOKEN_OPERATOR_COLON)) {
|
if (match(parser, TOY_TOKEN_OPERATOR_COLON)) {
|
||||||
varType = readType(parser);
|
// varType = readType(parser);
|
||||||
|
readType(parser);
|
||||||
|
|
||||||
if (match(parser, TOY_TOKEN_KEYWORD_CONST)) {
|
if (match(parser, TOY_TOKEN_KEYWORD_CONST)) {
|
||||||
constant = true;
|
// constant = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//emit the parameter as a name string
|
//emit the parameter as a name string
|
||||||
Toy_String* name = Toy_createNameStringLength(bucketHandle, nameToken.lexeme, nameToken.length, varType, constant);
|
Toy_String* name = Toy_toStringLength(bucketHandle, nameToken.lexeme, nameToken.length);
|
||||||
Toy_Value value = TOY_VALUE_FROM_STRING(name);
|
Toy_Value value = TOY_VALUE_FROM_STRING(name);
|
||||||
Toy_Ast* ast = NULL;
|
Toy_Ast* ast = NULL;
|
||||||
Toy_private_emitAstValue(bucketHandle, &ast, value);
|
Toy_private_emitAstValue(bucketHandle, &ast, value); //TODO: params with type info
|
||||||
|
|
||||||
//add to the params aggregate (is added backwards, because weird)
|
//add to the params aggregate (is added backwards, because weird)
|
||||||
Toy_private_emitAstAggregate(bucketHandle, ¶ms, TOY_AST_FLAG_COLLECTION, ast);
|
Toy_private_emitAstAggregate(bucketHandle, ¶ms, TOY_AST_FLAG_COLLECTION, ast);
|
||||||
|
|||||||
@@ -23,61 +23,118 @@ static void incrementRefCount(Toy_Scope* scope) {
|
|||||||
static void decrementRefCount(Toy_Scope* scope) {
|
static void decrementRefCount(Toy_Scope* scope) {
|
||||||
for (Toy_Scope* iter = scope; iter; iter = iter->next) {
|
for (Toy_Scope* iter = scope; iter; iter = iter->next) {
|
||||||
iter->refCount--;
|
iter->refCount--;
|
||||||
if (iter->refCount == 0 && iter->table != NULL) {
|
if (iter->refCount == 0 && iter->data != NULL) {
|
||||||
Toy_freeTable(iter->table);
|
//free the scope entries when this scope is no longer needed
|
||||||
iter->table = NULL;
|
for (unsigned int i = 0; i < iter->capacity; i++) {
|
||||||
|
Toy_freeString(&(iter->data[i].key));
|
||||||
|
Toy_freeValue(iter->data[i].value);
|
||||||
|
}
|
||||||
|
free(iter->data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Toy_TableEntry* lookupScope(Toy_Scope* scope, Toy_String* key, unsigned int hash, bool recursive) {
|
static Toy_ScopeEntry* lookupScopeEntryPtr(Toy_Scope* scope, Toy_String* key, unsigned int hash, bool recursive) {
|
||||||
//terminate
|
//terminate
|
||||||
if (scope == NULL) {
|
if (scope == NULL || scope->data == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
//continue after a dummy
|
//probe for the correct location
|
||||||
if (scope->table == NULL) {
|
unsigned int probe = hash % scope->capacity;
|
||||||
return recursive ? lookupScope(scope->next, key, hash, recursive) : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
//copy and modify the code from Toy_lookupTable, so it can behave slightly differently
|
|
||||||
unsigned int probe = hash % scope->table->capacity;
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
//found the entry
|
//found the entry
|
||||||
if (TOY_VALUE_IS_STRING(scope->table->data[probe].key) && Toy_compareStrings(TOY_VALUE_AS_STRING(scope->table->data[probe].key), key) == 0) {
|
if (Toy_compareStrings(&(scope->data[probe].key), key) == 0) {
|
||||||
return &(scope->table->data[probe]);
|
return &(scope->data[probe]);
|
||||||
}
|
}
|
||||||
|
|
||||||
//if its an empty slot (didn't find it here)
|
//if its an empty slot (didn't find it here)
|
||||||
if (TOY_VALUE_IS_NULL(scope->table->data[probe].key)) {
|
if (scope->data[probe].key.info.length == 0) {
|
||||||
return recursive ? lookupScope(scope->next, key, hash, recursive) : NULL;
|
return recursive ? lookupScopeEntryPtr(scope->next, key, hash, recursive) : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
//adjust and continue
|
//adjust and continue
|
||||||
probe = (probe + 1) % scope->table->capacity;
|
probe = (probe + 1) % scope->capacity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void probeAndInsert(Toy_Scope* scope, Toy_String* key, Toy_Value value, Toy_ValueType type, bool constant) {
|
||||||
|
//make the entry
|
||||||
|
unsigned int probe = Toy_hashString(key) % scope->capacity;
|
||||||
|
Toy_ScopeEntry entry = (Toy_ScopeEntry){ .key = *key, .value = value, .type = type, .constant = constant, .psl = 0 };
|
||||||
|
|
||||||
|
//probe
|
||||||
|
while (true) {
|
||||||
|
//if we're overriding an existing value
|
||||||
|
if (Toy_compareStrings(&(scope->data[probe].key), &(entry.key)) == 0) {
|
||||||
|
scope->data[probe] = entry;
|
||||||
|
scope->maxPsl = entry.psl > scope->maxPsl ? entry.psl : scope->maxPsl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if this spot is free, insert and return
|
||||||
|
if (TOY_VALUE_IS_NULL(scope->data[probe].value)) {
|
||||||
|
scope->data[probe] = entry;
|
||||||
|
scope->count++;
|
||||||
|
scope->maxPsl = entry.psl > scope->maxPsl ? entry.psl : scope->maxPsl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if the new entry is "poorer", insert it and shift the old one
|
||||||
|
if (scope->data[probe].psl < entry.psl) {
|
||||||
|
Toy_ScopeEntry tmp = scope->data[probe];
|
||||||
|
scope->data[probe] = entry;
|
||||||
|
entry = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
//adjust and continue
|
||||||
|
probe++;
|
||||||
|
probe &= scope->capacity - 1; //DOOM hack
|
||||||
|
entry.psl++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Toy_ScopeEntry* adjustScopeEntries(Toy_Scope* scope, unsigned int newCapacity) {
|
||||||
|
//allocate and zero a new Toy_ScopeEntry array in memory
|
||||||
|
Toy_ScopeEntry* newEntries = malloc(newCapacity * sizeof(Toy_ScopeEntry));
|
||||||
|
|
||||||
|
if (newEntries == NULL) {
|
||||||
|
fprintf(stderr, TOY_CC_ERROR "ERROR: Failed to allocate space for 'Toy_Scope' entries\n" TOY_CC_RESET);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//wipe the memory
|
||||||
|
memset(newEntries, 0, newCapacity * sizeof(Toy_ScopeEntry));
|
||||||
|
|
||||||
|
if (scope == NULL) { //for initial allocations
|
||||||
|
return newEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
//movethe old data into the new block of memory
|
||||||
|
unsigned int oldCapacity = scope->capacity;
|
||||||
|
Toy_ScopeEntry* oldEntries = scope->data;
|
||||||
|
scope->capacity = newCapacity;
|
||||||
|
scope->data = newEntries;
|
||||||
|
|
||||||
|
//for each existing entry in the old array, copy it into the new array
|
||||||
|
for (unsigned int i = 0; i < oldCapacity; i++) {
|
||||||
|
if (oldEntries[i].key.info.length > 0) {
|
||||||
|
probeAndInsert(scope, &(oldEntries[i].key), oldEntries[i].value, oldEntries[i].type, oldEntries[i].constant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//clean up and return
|
||||||
|
free(oldEntries);
|
||||||
|
return newEntries;
|
||||||
|
}
|
||||||
|
|
||||||
//exposed functions
|
//exposed functions
|
||||||
Toy_Scope* Toy_pushScope(Toy_Bucket** bucketHandle, Toy_Scope* scope) {
|
Toy_Scope* Toy_pushScope(Toy_Bucket** bucketHandle, Toy_Scope* scope) {
|
||||||
Toy_Scope* newScope = (Toy_Scope*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Scope));
|
Toy_Scope* newScope = (Toy_Scope*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Scope));
|
||||||
|
|
||||||
newScope->next = scope;
|
newScope->next = scope;
|
||||||
newScope->table = Toy_allocateTable();
|
newScope->data = adjustScopeEntries(NULL, TOY_SCOPE_INITIAL_CAPACITY);
|
||||||
newScope->refCount = 0;
|
|
||||||
|
|
||||||
incrementRefCount(newScope);
|
|
||||||
|
|
||||||
return newScope;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_Scope* Toy_private_pushDummyScope(Toy_Bucket** bucketHandle, Toy_Scope* scope) {
|
|
||||||
Toy_Scope* newScope = (Toy_Scope*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Scope));
|
|
||||||
|
|
||||||
newScope->next = scope;
|
|
||||||
newScope->table = NULL;
|
|
||||||
newScope->refCount = 0;
|
newScope->refCount = 0;
|
||||||
|
|
||||||
incrementRefCount(newScope);
|
incrementRefCount(newScope);
|
||||||
@@ -94,66 +151,49 @@ Toy_Scope* Toy_popScope(Toy_Scope* scope) {
|
|||||||
return scope->next;
|
return scope->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) {
|
void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_ValueType type, Toy_Value value, bool constant) {
|
||||||
if (key->info.type != TOY_STRING_NAME) {
|
Toy_ScopeEntry* entryPtr = lookupScopeEntryPtr(scope, key, Toy_hashString(key), false);
|
||||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope only allows name strings as keys\n" TOY_CC_RESET);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scope->table == NULL) {
|
|
||||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Can't declare in a dummy scope\n" TOY_CC_RESET);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_TableEntry* entryPtr = lookupScope(scope, key, Toy_hashString(key), false);
|
|
||||||
|
|
||||||
if (entryPtr != NULL) {
|
if (entryPtr != NULL) {
|
||||||
char buffer[key->info.length + 256];
|
char buffer[key->info.length + 256];
|
||||||
sprintf(buffer, "Can't redefine a variable: %s", key->name.data);
|
sprintf(buffer, "Can't redefine a variable: %s", key->leaf.data);
|
||||||
Toy_error(buffer);
|
Toy_error(buffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//type check
|
//type check
|
||||||
Toy_ValueType kt = Toy_getNameStringVarType(key);
|
if (type != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && type != value.type && value.type != TOY_VALUE_REFERENCE) {
|
||||||
if (kt != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && kt != value.type && value.type != TOY_VALUE_REFERENCE) {
|
|
||||||
char buffer[key->info.length + 256];
|
char buffer[key->info.length + 256];
|
||||||
sprintf(buffer, "Incorrect value type in declaration of '%s' (expected %s, got %s)", key->name.data, Toy_private_getValueTypeAsCString(kt), Toy_private_getValueTypeAsCString(value.type));
|
sprintf(buffer, "Incorrect value type in declaration of '%s' (expected %s, got %s)", key->leaf.data, Toy_private_getValueTypeAsCString(type), Toy_private_getValueTypeAsCString(value.type));
|
||||||
Toy_error(buffer);
|
Toy_error(buffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_insertTable(&scope->table, TOY_VALUE_FROM_STRING(Toy_copyString(key)), value);
|
probeAndInsert(scope, Toy_copyString(key), value, type, constant);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) {
|
void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) {
|
||||||
if (key->info.type != TOY_STRING_NAME) {
|
Toy_ScopeEntry* entryPtr = lookupScopeEntryPtr(scope, key, Toy_hashString(key), true);
|
||||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope only allows name strings as keys\n" TOY_CC_RESET);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_TableEntry* entryPtr = lookupScope(scope, key, Toy_hashString(key), true);
|
|
||||||
|
|
||||||
if (entryPtr == NULL) {
|
if (entryPtr == NULL) {
|
||||||
char buffer[key->info.length + 256];
|
char buffer[key->info.length + 256];
|
||||||
sprintf(buffer, "Undefined variable: %s\n", key->name.data);
|
sprintf(buffer, "Undefined variable: %s\n", key->leaf.data);
|
||||||
Toy_error(buffer);
|
Toy_error(buffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//type check
|
//type check
|
||||||
Toy_ValueType kt = Toy_getNameStringVarType( TOY_VALUE_AS_STRING(entryPtr->key) );
|
if (entryPtr->type != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && entryPtr->type != value.type && value.type != TOY_VALUE_REFERENCE) {
|
||||||
if (kt != TOY_VALUE_ANY && value.type != TOY_VALUE_NULL && kt != value.type && value.type != TOY_VALUE_REFERENCE) {
|
|
||||||
char buffer[key->info.length + 256];
|
char buffer[key->info.length + 256];
|
||||||
sprintf(buffer, "Incorrect value type in assignment of '%s' (expected %s, got %s)", key->name.data, Toy_private_getValueTypeAsCString(kt), Toy_private_getValueTypeAsCString(value.type));
|
sprintf(buffer, "Incorrect value type in assignment of '%s' (expected %s, got %s)", key->leaf.data, Toy_private_getValueTypeAsCString(entryPtr->type), Toy_private_getValueTypeAsCString(value.type));
|
||||||
Toy_error(buffer);
|
Toy_error(buffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//constness check
|
//constness check
|
||||||
if (Toy_getNameStringVarConstant( TOY_VALUE_AS_STRING(entryPtr->key) )) {
|
if (entryPtr->constant) {
|
||||||
char buffer[key->info.length + 256];
|
char buffer[key->info.length + 256];
|
||||||
sprintf(buffer, "Can't reassign to constant variable %s", key->name.data);
|
sprintf(buffer, "Can't assign to a constant variable %s", key->leaf.data);
|
||||||
Toy_error(buffer);
|
Toy_error(buffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -162,16 +202,11 @@ void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key) {
|
Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key) {
|
||||||
if (key->info.type != TOY_STRING_NAME) {
|
Toy_ScopeEntry* entryPtr = lookupScopeEntryPtr(scope, key, Toy_hashString(key), true);
|
||||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope only allows name strings as keys\n" TOY_CC_RESET);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_TableEntry* entryPtr = lookupScope(scope, key, Toy_hashString(key), true);
|
|
||||||
|
|
||||||
if (entryPtr == NULL) {
|
if (entryPtr == NULL) {
|
||||||
char buffer[key->info.length + 256];
|
char buffer[key->info.length + 256];
|
||||||
sprintf(buffer, "Undefined variable: %s\n", key->name.data);
|
sprintf(buffer, "Undefined variable: %s\n", key->leaf.data);
|
||||||
Toy_error(buffer);
|
Toy_error(buffer);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -180,12 +215,6 @@ Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Toy_isDeclaredScope(Toy_Scope* scope, Toy_String* key) {
|
bool Toy_isDeclaredScope(Toy_Scope* scope, Toy_String* key) {
|
||||||
if (key->info.type != TOY_STRING_NAME) {
|
Toy_ScopeEntry* entryPtr = lookupScopeEntryPtr(scope, key, Toy_hashString(key), true);
|
||||||
fprintf(stderr, TOY_CC_ERROR "ERROR: Toy_Scope only allows name strings as keys\n" TOY_CC_RESET);
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_TableEntry* entryPtr = lookupScope(scope, key, Toy_hashString(key), true);
|
|
||||||
|
|
||||||
return entryPtr != NULL;
|
return entryPtr != NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,27 +3,50 @@
|
|||||||
#include "toy_common.h"
|
#include "toy_common.h"
|
||||||
|
|
||||||
#include "toy_bucket.h"
|
#include "toy_bucket.h"
|
||||||
#include "toy_value.h"
|
|
||||||
#include "toy_string.h"
|
#include "toy_string.h"
|
||||||
#include "toy_table.h"
|
#include "toy_value.h"
|
||||||
|
|
||||||
//wraps Toy_Table, restricting keys to name strings, and handles scopes as a linked list
|
//keys are leaf-only strings
|
||||||
|
typedef struct Toy_ScopeEntry {
|
||||||
|
Toy_String key;
|
||||||
|
Toy_Value value;
|
||||||
|
Toy_ValueType type;
|
||||||
|
bool constant;
|
||||||
|
unsigned int psl;
|
||||||
|
} Toy_ScopeEntry;
|
||||||
|
|
||||||
|
//holds a table-like collection of variables TODO: check bitness
|
||||||
typedef struct Toy_Scope {
|
typedef struct Toy_Scope {
|
||||||
struct Toy_Scope* next;
|
struct Toy_Scope* next;
|
||||||
Toy_Table* table;
|
|
||||||
unsigned int refCount;
|
unsigned int refCount;
|
||||||
|
unsigned int capacity;
|
||||||
|
unsigned int count;
|
||||||
|
unsigned int maxPsl;
|
||||||
|
Toy_ScopeEntry* data;
|
||||||
} Toy_Scope;
|
} Toy_Scope;
|
||||||
|
|
||||||
//handle deep scopes - the scope is stored in the bucket, not the table
|
//handle deep scopes - the scope is stored in the bucket, not the table
|
||||||
TOY_API Toy_Scope* Toy_pushScope(Toy_Bucket** bucketHandle, Toy_Scope* scope);
|
TOY_API Toy_Scope* Toy_pushScope(Toy_Bucket** bucketHandle, Toy_Scope* scope);
|
||||||
TOY_API Toy_Scope* Toy_popScope(Toy_Scope* scope);
|
TOY_API Toy_Scope* Toy_popScope(Toy_Scope* scope);
|
||||||
TOY_API Toy_Scope* Toy_private_pushDummyScope(Toy_Bucket** bucketHandle, Toy_Scope* scope); //doesn't delcare a table for storage
|
|
||||||
|
|
||||||
//manage the contents
|
//manage the contents
|
||||||
TOY_API void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_Value value);
|
TOY_API void Toy_declareScope(Toy_Scope* scope, Toy_String* key, Toy_ValueType type, Toy_Value value, bool constant);
|
||||||
TOY_API void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value);
|
TOY_API void Toy_assignScope(Toy_Scope* scope, Toy_String* key, Toy_Value value);
|
||||||
TOY_API Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key);
|
TOY_API Toy_Value* Toy_accessScopeAsPointer(Toy_Scope* scope, Toy_String* key);
|
||||||
|
|
||||||
TOY_API bool Toy_isDeclaredScope(Toy_Scope* scope, Toy_String* key);
|
TOY_API bool Toy_isDeclaredScope(Toy_Scope* scope, Toy_String* key);
|
||||||
|
|
||||||
//TODO: delcare with a custom table (game engine entities)
|
//some useful sizes, could be swapped out as needed
|
||||||
|
#ifndef TOY_SCOPE_INITIAL_CAPACITY
|
||||||
|
#define TOY_SCOPE_INITIAL_CAPACITY 8
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//NOTE: The DOOM hack needs a power of 2
|
||||||
|
#ifndef TOY_SCOPE_EXPANSION_RATE
|
||||||
|
#define TOY_SCOPE_EXPANSION_RATE 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//expand when the contents passes a certain percentage (80%) of the capacity
|
||||||
|
#ifndef TOY_SCOPE_EXPANSION_THRESHOLD
|
||||||
|
#define TOY_SCOPE_EXPANSION_THRESHOLD 0.8f
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -9,18 +9,6 @@
|
|||||||
//utils
|
//utils
|
||||||
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
|
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
|
||||||
|
|
||||||
static void deepCopyUtil(char* dest, Toy_String* str) {
|
|
||||||
//sometimes, "clever" can be a bad thing...
|
|
||||||
if (str->info.type == TOY_STRING_NODE) {
|
|
||||||
deepCopyUtil(dest, str->node.left);
|
|
||||||
deepCopyUtil(dest + str->node.left->info.length, str->node.right);
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
memcpy(dest, str->leaf.data, str->info.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void incrementRefCount(Toy_String* str) {
|
static void incrementRefCount(Toy_String* str) {
|
||||||
str->info.refCount++;
|
str->info.refCount++;
|
||||||
if (str->info.type == TOY_STRING_NODE) {
|
if (str->info.type == TOY_STRING_NODE) {
|
||||||
@@ -37,69 +25,19 @@ static void decrementRefCount(Toy_String* str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int hashCString(const char* string) {
|
//exposed functions
|
||||||
unsigned int hash = 2166136261u;
|
Toy_String* Toy_toString(Toy_Bucket** bucketHandle, const char* cstring) {
|
||||||
|
return Toy_toStringLength(bucketHandle, cstring, strlen(cstring));
|
||||||
for (unsigned int i = 0; string[i]; i++) {
|
|
||||||
hash *= string[i];
|
|
||||||
hash ^= 16777619;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return hash;
|
Toy_String* Toy_toStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length) {
|
||||||
}
|
Toy_String* ret = (Toy_String*)Toy_partitionBucket(bucketHandle, sizeof(Toy_String));
|
||||||
|
|
||||||
static Toy_String* partitionStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length) {
|
|
||||||
Toy_String* ret = (Toy_String*)Toy_partitionBucket(bucketHandle, sizeof(Toy_String) + length + 1);
|
|
||||||
|
|
||||||
ret->info.type = TOY_STRING_LEAF;
|
ret->info.type = TOY_STRING_LEAF;
|
||||||
ret->info.length = length;
|
ret->info.length = length;
|
||||||
ret->info.refCount = 1;
|
ret->info.refCount = 1;
|
||||||
ret->info.cachedHash = 0; //don't calc until needed
|
ret->info.cachedHash = 0; //don't calc until needed
|
||||||
memcpy(ret->leaf.data, cstring, length + 1);
|
ret->leaf.data = cstring; //don't make a local copy
|
||||||
ret->leaf.data[length] = '\0';
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
//exposed functions
|
|
||||||
Toy_String* Toy_createString(Toy_Bucket** bucketHandle, const char* cstring) {
|
|
||||||
unsigned int length = strlen(cstring);
|
|
||||||
|
|
||||||
return Toy_createStringLength(bucketHandle, cstring, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_String* Toy_createStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length) {
|
|
||||||
//normal behaviour
|
|
||||||
if (length < (*bucketHandle)->capacity - sizeof(Toy_String) - 1) {
|
|
||||||
return partitionStringLength(bucketHandle, cstring, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
//break the string up if it's too long
|
|
||||||
Toy_String* result = NULL;
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < length; i += (*bucketHandle)->capacity - sizeof(Toy_String) - 1) { //increment by the amount actually used by the cstring
|
|
||||||
unsigned int amount = MIN((length - i), (*bucketHandle)->capacity - sizeof(Toy_String) - 1);
|
|
||||||
Toy_String* fragment = partitionStringLength(bucketHandle, cstring + i, amount);
|
|
||||||
|
|
||||||
result = result == NULL ? fragment : Toy_concatStrings(bucketHandle, result, fragment);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_String* Toy_createNameStringLength(Toy_Bucket** bucketHandle, const char* cname, unsigned int length, Toy_ValueType varType, bool constant) {
|
|
||||||
assert(varType != TOY_VALUE_NULL && "Can't declare a name string with variable type 'null'");
|
|
||||||
|
|
||||||
Toy_String* ret = (Toy_String*)Toy_partitionBucket(bucketHandle, sizeof(Toy_String) + length + 1);
|
|
||||||
|
|
||||||
ret->info.type = TOY_STRING_NAME;
|
|
||||||
ret->info.length = length;
|
|
||||||
ret->info.refCount = 1;
|
|
||||||
ret->info.cachedHash = 0; //don't calc until needed
|
|
||||||
memcpy(ret->name.data, cname, length + 1);
|
|
||||||
ret->name.data[length] = '\0';
|
|
||||||
ret->name.varType = varType;
|
|
||||||
ret->name.varConstant = constant;
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -110,42 +48,8 @@ Toy_String* Toy_copyString(Toy_String* str) {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_String* Toy_deepCopyString(Toy_Bucket** bucketHandle, Toy_String* str) {
|
|
||||||
assert(str->info.refCount != 0 && "Can't deep copy a string with refcount of zero");
|
|
||||||
|
|
||||||
//handle deep copies of strings that are too long for the bucket capacity
|
|
||||||
if (sizeof(Toy_String) + str->info.length + 1 > (*bucketHandle)->capacity) {
|
|
||||||
char* buffer = Toy_getStringRawBuffer(str);
|
|
||||||
Toy_String* result = Toy_createStringLength(bucketHandle, buffer, str->info.length); //handles the fragmenting
|
|
||||||
free(buffer);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_String* ret = (Toy_String*)Toy_partitionBucket(bucketHandle, sizeof(Toy_String) + str->info.length + 1);
|
|
||||||
|
|
||||||
if (str->info.type == TOY_STRING_NODE || str->info.type == TOY_STRING_LEAF) {
|
|
||||||
ret->info.type = TOY_STRING_LEAF;
|
|
||||||
ret->info.length = str->info.length;
|
|
||||||
ret->info.refCount = 1;
|
|
||||||
ret->info.cachedHash = str->info.cachedHash;
|
|
||||||
deepCopyUtil(ret->leaf.data, str); //copy each leaf into the buffer
|
|
||||||
ret->leaf.data[ret->info.length] = '\0';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ret->info.type = TOY_STRING_NAME;
|
|
||||||
ret->info.length = str->info.length;
|
|
||||||
ret->info.refCount = 1;
|
|
||||||
ret->info.cachedHash = str->info.cachedHash;
|
|
||||||
memcpy(ret->name.data, str->name.data, str->info.length + 1);
|
|
||||||
ret->name.data[ret->info.length] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
Toy_String* Toy_concatStrings(Toy_Bucket** bucketHandle, Toy_String* left, Toy_String* right) {
|
Toy_String* Toy_concatStrings(Toy_Bucket** bucketHandle, Toy_String* left, Toy_String* right) {
|
||||||
assert(left->info.refCount != 0 && right->info.refCount != 0 && "Can't concatenate a string with a refcount of zero");
|
assert(left->info.refCount != 0 && right->info.refCount != 0 && "Can't concatenate a string with a refcount of zero");
|
||||||
assert(left->info.type != TOY_STRING_NAME && right->info.type != TOY_STRING_NAME && "Can't concatenate a name string");
|
|
||||||
|
|
||||||
Toy_String* ret = (Toy_String*)Toy_partitionBucket(bucketHandle, sizeof(Toy_String));
|
Toy_String* ret = (Toy_String*)Toy_partitionBucket(bucketHandle, sizeof(Toy_String));
|
||||||
|
|
||||||
@@ -175,21 +79,20 @@ unsigned int Toy_getStringRefCount(Toy_String* str) {
|
|||||||
return str->info.refCount;
|
return str->info.refCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
Toy_ValueType Toy_getNameStringVarType(Toy_String* str) {
|
static void getStringRawUtil(char* dest, Toy_String* str) {
|
||||||
assert(str->info.type == TOY_STRING_NAME && "Can't get the variable type of a non-name string");
|
//sometimes, "clever" can be a bad thing...
|
||||||
|
if (str->info.type == TOY_STRING_NODE) {
|
||||||
return str->name.varType;
|
getStringRawUtil(dest, str->node.left);
|
||||||
|
getStringRawUtil(dest + str->node.left->info.length, str->node.right);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Toy_getNameStringVarConstant(Toy_String* str) {
|
else {
|
||||||
assert(str->info.type == TOY_STRING_NAME && "Can't get the variable constness of a non-name string");
|
memcpy(dest, str->leaf.data, str->info.length);
|
||||||
|
}
|
||||||
return str->name.varConstant;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char* Toy_getStringRawBuffer(Toy_String* str) {
|
char* Toy_getStringRaw(Toy_String* str) {
|
||||||
assert(str->info.type != TOY_STRING_NAME && "Can't get raw string buffer of a name string");
|
assert(str->info.refCount != 0 && "Can't build a raw string from a string with refcount of zero");
|
||||||
assert(str->info.refCount != 0 && "Can't get raw string buffer of a string with refcount of zero");
|
|
||||||
|
|
||||||
//BUGFIX: Make sure it's aligned, and there's space for the null
|
//BUGFIX: Make sure it's aligned, and there's space for the null
|
||||||
unsigned int len = (str->info.length + 3) & ~3;
|
unsigned int len = (str->info.length + 3) & ~3;
|
||||||
@@ -199,7 +102,7 @@ char* Toy_getStringRawBuffer(Toy_String* str) {
|
|||||||
|
|
||||||
char* buffer = malloc(len);
|
char* buffer = malloc(len);
|
||||||
|
|
||||||
deepCopyUtil(buffer, str);
|
getStringRawUtil(buffer, str);
|
||||||
buffer[str->info.length] = '\0';
|
buffer[str->info.length] = '\0';
|
||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
@@ -284,11 +187,6 @@ int Toy_compareStrings(Toy_String* left, Toy_String* right) {
|
|||||||
return left->info.length - right->info.length;
|
return left->info.length - right->info.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (left->info.type == TOY_STRING_NAME || right->info.type == TOY_STRING_NAME) {
|
|
||||||
assert (left->info.type == right->info.type && "Can't compare a name string to a non-name string");
|
|
||||||
return strncmp(left->name.data, right->name.data, left->info.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
//util pointers
|
//util pointers
|
||||||
const char* leftHead = NULL;
|
const char* leftHead = NULL;
|
||||||
const char* rightHead = NULL;
|
const char* rightHead = NULL;
|
||||||
@@ -296,22 +194,30 @@ int Toy_compareStrings(Toy_String* left, Toy_String* right) {
|
|||||||
return deepCompareUtil(left, right, &leftHead, &rightHead);
|
return deepCompareUtil(left, right, &leftHead, &rightHead);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned int hashCString(const char* string) {
|
||||||
|
unsigned int hash = 2166136261u;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; string[i]; i++) {
|
||||||
|
hash *= string[i];
|
||||||
|
hash ^= 16777619;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int Toy_hashString(Toy_String* str) {
|
unsigned int Toy_hashString(Toy_String* str) {
|
||||||
if (str->info.cachedHash != 0) {
|
if (str->info.cachedHash != 0) {
|
||||||
return str->info.cachedHash;
|
return str->info.cachedHash;
|
||||||
}
|
}
|
||||||
else if (str->info.type == TOY_STRING_NODE) {
|
else if (str->info.type == TOY_STRING_NODE) {
|
||||||
//TODO: I wonder if it would be possible to discretely swap the composite node string with a new leaf string here? Would that speed up other parts of the code by not having to walk the tree in future? - needs to be benchmarked
|
//TODO: I wonder if it would be possible to discretely swap the composite node string with a new leaf string here? Would that speed up other parts of the code by not having to walk the tree in future? - needs to be benchmarked
|
||||||
char* buffer = Toy_getStringRawBuffer(str);
|
char* buffer = Toy_getStringRaw(str);
|
||||||
str->info.cachedHash = hashCString(buffer);
|
str->info.cachedHash = hashCString(buffer);
|
||||||
free(buffer);
|
free(buffer);
|
||||||
}
|
}
|
||||||
else if (str->info.type == TOY_STRING_LEAF) {
|
else {
|
||||||
str->info.cachedHash = hashCString(str->leaf.data);
|
str->info.cachedHash = hashCString(str->leaf.data);
|
||||||
}
|
}
|
||||||
else if (str->info.type == TOY_STRING_NAME) {
|
|
||||||
str->info.cachedHash = hashCString(str->name.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return str->info.cachedHash;
|
return str->info.cachedHash;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
#include "toy_common.h"
|
#include "toy_common.h"
|
||||||
|
|
||||||
#include "toy_bucket.h"
|
#include "toy_bucket.h"
|
||||||
#include "toy_value.h"
|
|
||||||
|
|
||||||
//forward declare
|
//forward declare
|
||||||
union Toy_String_t;
|
union Toy_String_t;
|
||||||
@@ -12,7 +11,6 @@ union Toy_String_t;
|
|||||||
typedef enum Toy_StringType {
|
typedef enum Toy_StringType {
|
||||||
TOY_STRING_NODE,
|
TOY_STRING_NODE,
|
||||||
TOY_STRING_LEAF,
|
TOY_STRING_LEAF,
|
||||||
TOY_STRING_NAME,
|
|
||||||
} Toy_StringType;
|
} Toy_StringType;
|
||||||
|
|
||||||
typedef struct Toy_StringInfo {
|
typedef struct Toy_StringInfo {
|
||||||
@@ -30,43 +28,28 @@ typedef struct Toy_StringNode {
|
|||||||
|
|
||||||
typedef struct Toy_StringLeaf {
|
typedef struct Toy_StringLeaf {
|
||||||
Toy_StringInfo _padding;
|
Toy_StringInfo _padding;
|
||||||
char data[];
|
const char* data;
|
||||||
} Toy_StringLeaf;
|
} Toy_StringLeaf;
|
||||||
|
|
||||||
typedef struct Toy_StringName {
|
|
||||||
Toy_StringInfo _padding;
|
|
||||||
Toy_ValueType varType;
|
|
||||||
bool varConstant;
|
|
||||||
char data[];
|
|
||||||
} Toy_StringName;
|
|
||||||
|
|
||||||
typedef union Toy_String_t {
|
typedef union Toy_String_t {
|
||||||
Toy_StringInfo info;
|
Toy_StringInfo info;
|
||||||
Toy_StringNode node;
|
Toy_StringNode node;
|
||||||
Toy_StringLeaf leaf;
|
Toy_StringLeaf leaf;
|
||||||
Toy_StringName name;
|
|
||||||
} Toy_String;
|
} Toy_String;
|
||||||
|
|
||||||
//
|
//
|
||||||
TOY_API Toy_String* Toy_createString(Toy_Bucket** bucketHandle, const char* cstring);
|
TOY_API Toy_String* Toy_toString(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_toStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length);
|
||||||
|
|
||||||
TOY_API Toy_String* Toy_createNameStringLength(Toy_Bucket** bucketHandle, const char* cname, unsigned int length, Toy_ValueType varType, bool constant); //for variable names
|
|
||||||
|
|
||||||
TOY_API Toy_String* Toy_copyString(Toy_String* str);
|
TOY_API Toy_String* Toy_copyString(Toy_String* str);
|
||||||
TOY_API Toy_String* Toy_deepCopyString(Toy_Bucket** bucketHandle, Toy_String* str);
|
|
||||||
|
|
||||||
TOY_API Toy_String* Toy_concatStrings(Toy_Bucket** bucketHandle, Toy_String* left, Toy_String* right);
|
TOY_API Toy_String* Toy_concatStrings(Toy_Bucket** bucketHandle, Toy_String* left, Toy_String* right);
|
||||||
|
|
||||||
TOY_API void Toy_freeString(Toy_String* str);
|
TOY_API void Toy_freeString(Toy_String* str);
|
||||||
|
|
||||||
TOY_API unsigned int Toy_getStringLength(Toy_String* str);
|
TOY_API unsigned int Toy_getStringLength(Toy_String* str);
|
||||||
TOY_API unsigned int Toy_getStringRefCount(Toy_String* str);
|
TOY_API unsigned int Toy_getStringRefCount(Toy_String* str);
|
||||||
TOY_API Toy_ValueType Toy_getNameStringVarType(Toy_String* str);
|
|
||||||
TOY_API bool Toy_getNameStringVarConstant(Toy_String* str);
|
|
||||||
|
|
||||||
TOY_API char* Toy_getStringRawBuffer(Toy_String* str); //allocates the buffer on the heap, needs to be freed
|
|
||||||
|
|
||||||
|
TOY_API char* Toy_getStringRaw(Toy_String* str); //allocates the buffer on the heap, needs to be freed
|
||||||
TOY_API int Toy_compareStrings(Toy_String* left, Toy_String* right); //return value mimics strcmp()
|
TOY_API int Toy_compareStrings(Toy_String* left, Toy_String* right); //return value mimics strcmp()
|
||||||
|
|
||||||
TOY_API unsigned int Toy_hashString(Toy_String* string);
|
TOY_API unsigned int Toy_hashString(Toy_String* string);
|
||||||
|
|||||||
@@ -434,7 +434,7 @@ Toy_String* Toy_stringifyValue(Toy_Bucket** bucketHandle, Toy_Value value) {
|
|||||||
|
|
||||||
switch(value.type) {
|
switch(value.type) {
|
||||||
case TOY_VALUE_NULL:
|
case TOY_VALUE_NULL:
|
||||||
return Toy_createString(bucketHandle, "<null>");
|
return Toy_createString(bucketHandle, "<null>"); //TODO: remake "createString" to to handle params like this
|
||||||
|
|
||||||
case TOY_VALUE_BOOLEAN:
|
case TOY_VALUE_BOOLEAN:
|
||||||
return Toy_createString(bucketHandle, value.as.boolean ? "<true>" : "<false>");
|
return Toy_createString(bucketHandle, value.as.boolean ? "<true>" : "<false>");
|
||||||
|
|||||||
Reference in New Issue
Block a user