Implemented scopes

This commit is contained in:
2024-10-27 13:44:09 +11:00
parent d22b18ed17
commit c5206daaea
10 changed files with 90 additions and 9 deletions

22
scripts/testificate.toy Normal file
View File

@@ -0,0 +1,22 @@
//normal scope stuff
var answer = 42;
print answer;
{
var answer = 7;
print answer;
}
print answer;
//I wonder if...
var question = 42;
print question;
{
var question = question;
print question;
}
print question;

View File

@@ -4,6 +4,7 @@ void Toy_private_initAstBlock(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
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_BLOCK; tmp->type = TOY_AST_BLOCK;
tmp->block.innerScope = false;
tmp->block.child = NULL; tmp->block.child = NULL;
tmp->block.next = NULL; tmp->block.next = NULL;
tmp->block.tail = NULL; tmp->block.tail = NULL;

View File

@@ -69,6 +69,7 @@ typedef union Toy_Ast Toy_Ast;
typedef struct Toy_AstBlock { typedef struct Toy_AstBlock {
Toy_AstType type; Toy_AstType type;
bool innerScope;
Toy_Ast* child; //begin encoding the line Toy_Ast* child; //begin encoding the line
Toy_Ast* next; //'next' is either an AstBlock or null Toy_Ast* next; //'next' is either an AstBlock or null
Toy_Ast* tail; //'tail' - either points to the tail of the current list, or null; only used by the head of a list as an optimisation Toy_Ast* tail; //'tail' - either points to the tail of the current list, or null; only used by the head of a list as an optimisation

View File

@@ -33,6 +33,9 @@ typedef enum Toy_OpcodeType {
//control instructions //control instructions
TOY_OPCODE_RETURN, TOY_OPCODE_RETURN,
TOY_OPCODE_SCOPE_PUSH,
TOY_OPCODE_SCOPE_POP,
//various action instructions //various action instructions
TOY_OPCODE_PRINT, TOY_OPCODE_PRINT,
TOY_OPCODE_CONCAT, TOY_OPCODE_CONCAT,

View File

@@ -565,6 +565,9 @@ static void makeExpr(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro
parsePrecedence(bucketHandle, parser, rootHandle, PREC_ASSIGNMENT); parsePrecedence(bucketHandle, parser, rootHandle, PREC_ASSIGNMENT);
} }
//forward declarations
static void makeBlockStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle);
static void makePrintStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { static void makePrintStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
makeExpr(bucketHandle, parser, rootHandle); makeExpr(bucketHandle, parser, rootHandle);
Toy_private_emitAstPrint(bucketHandle, rootHandle); Toy_private_emitAstPrint(bucketHandle, rootHandle);
@@ -608,7 +611,14 @@ static void makeVariableDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* p
} }
static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) {
//block //inner scope
if (match(parser, TOY_TOKEN_OPERATOR_BRACE_LEFT)) {
makeBlockStmt(bucketHandle, parser, rootHandle);
consume(parser, TOY_TOKEN_OPERATOR_BRACE_RIGHT, "Expected '}' at the end of block scope");
(*rootHandle)->block.innerScope = true;
return;
}
//assert //assert
//if-then-else //if-then-else
//while-then //while-then
@@ -619,7 +629,7 @@ static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro
//import //import
//check for empty lines //check for empty lines
if (match(parser, TOY_TOKEN_OPERATOR_SEMICOLON)) { else if (match(parser, TOY_TOKEN_OPERATOR_SEMICOLON)) {
Toy_private_emitAstPass(bucketHandle, rootHandle); Toy_private_emitAstPass(bucketHandle, rootHandle);
return; return;
} }
@@ -658,7 +668,7 @@ static void makeBlockStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast
Toy_private_initAstBlock(bucketHandle, rootHandle); Toy_private_initAstBlock(bucketHandle, rootHandle);
//read a series of statements into the block //read a series of statements into the block
while (!match(parser, TOY_TOKEN_EOF)) { while (parser->current.type != TOY_TOKEN_OPERATOR_BRACE_RIGHT && !match(parser, TOY_TOKEN_EOF)) {
//process the grammar rules //process the grammar rules
Toy_Ast* stmt = NULL; Toy_Ast* stmt = NULL;
makeDeclarationStmt(bucketHandle, parser, &stmt); makeDeclarationStmt(bucketHandle, parser, &stmt);
@@ -695,6 +705,11 @@ Toy_Ast* Toy_scanParser(Toy_Bucket** bucketHandle, Toy_Parser* parser) {
makeBlockStmt(bucketHandle, parser, &rootHandle); makeBlockStmt(bucketHandle, parser, &rootHandle);
//don't emit this error if we're already panicking
if (parser->panic != true && parser->previous.type != TOY_TOKEN_EOF) {
printError(parser, parser->previous, "Expected 'EOF' and the end of the parser scan (possibly an extra '}' was found)");
}
return rootHandle; return rootHandle;
} }

View File

@@ -410,8 +410,22 @@ static void writeRoutineCode(Toy_Routine** rt, Toy_Ast* ast) {
//determine how to write each instruction based on the Ast //determine how to write each instruction based on the Ast
switch(ast->type) { switch(ast->type) {
case TOY_AST_BLOCK: case TOY_AST_BLOCK:
if (ast->block.innerScope) {
EMIT_BYTE(rt, code, TOY_OPCODE_SCOPE_PUSH);
EMIT_BYTE(rt, code, 0);
EMIT_BYTE(rt, code, 0);
EMIT_BYTE(rt, code, 0);
}
writeRoutineCode(rt, ast->block.child); writeRoutineCode(rt, ast->block.child);
writeRoutineCode(rt, ast->block.next); writeRoutineCode(rt, ast->block.next);
if (ast->block.innerScope) {
EMIT_BYTE(rt, code, TOY_OPCODE_SCOPE_POP);
EMIT_BYTE(rt, code, 0);
EMIT_BYTE(rt, code, 0);
EMIT_BYTE(rt, code, 0);
}
break; break;
case TOY_AST_VALUE: case TOY_AST_VALUE:

View File

@@ -482,6 +482,14 @@ static void process(Toy_VM* vm) {
//temp terminator //temp terminator
return; return;
case TOY_OPCODE_SCOPE_PUSH:
vm->scope = Toy_pushScope(&vm->scopeBucket, vm->scope);
break;
case TOY_OPCODE_SCOPE_POP:
vm->scope = Toy_popScope(vm->scope);
break;
//various action instructions //various action instructions
case TOY_OPCODE_PRINT: case TOY_OPCODE_PRINT:
processPrint(vm); processPrint(vm);

View File

@@ -48,7 +48,7 @@ int test_sizeof_ast_32bit() {
//run for each type //run for each type
TEST_SIZEOF(Toy_AstType, 4); TEST_SIZEOF(Toy_AstType, 4);
TEST_SIZEOF(Toy_AstBlock, 16); TEST_SIZEOF(Toy_AstBlock, 20);
TEST_SIZEOF(Toy_AstValue, 12); TEST_SIZEOF(Toy_AstValue, 12);
TEST_SIZEOF(Toy_AstUnary, 12); TEST_SIZEOF(Toy_AstUnary, 12);
TEST_SIZEOF(Toy_AstBinary, 16); TEST_SIZEOF(Toy_AstBinary, 16);
@@ -61,7 +61,7 @@ int test_sizeof_ast_32bit() {
TEST_SIZEOF(Toy_AstPass, 4); TEST_SIZEOF(Toy_AstPass, 4);
TEST_SIZEOF(Toy_AstError, 4); TEST_SIZEOF(Toy_AstError, 4);
TEST_SIZEOF(Toy_AstEnd, 4); TEST_SIZEOF(Toy_AstEnd, 4);
TEST_SIZEOF(Toy_Ast, 16); TEST_SIZEOF(Toy_Ast, 20);
#undef TEST_SIZEOF #undef TEST_SIZEOF

View File

@@ -0,0 +1,19 @@
//shadowing
var answer = 42;
print answer; //42
{
var answer = 7;
print answer; //7
}
print answer; //42
//rebinding
var question = 42;
print question; //42
{
var question = question;
print question; //42
}
print question; //42
//TODO: scope test case

View File

@@ -7,15 +7,13 @@ var empty;
//assign a previously existing variable //assign a previously existing variable
answer = 6 * 9; answer = 6 * 9;
//access a variable
answer = answer + 1; answer = answer + 1;
//compound assignments
answer += 5; answer += 5;
answer -= 5; answer -= 5;
answer *= 9; answer *= 9;
answer /= 2; answer /= 2;
answer %= 10; answer %= 10;