diff --git a/scripts/fizzbuzz.toy b/scripts/fizzbuzz.toy new file mode 100644 index 0000000..d399804 --- /dev/null +++ b/scripts/fizzbuzz.toy @@ -0,0 +1,27 @@ + + +//moment of truth +var counter: int = 1; + +while (counter <= 100) { + var result: string = ""; + + if (counter % 3 == 0) { + result = result .. "fizz"; + } + + if (counter % 5 == 0) { + result = result .. "buzz"; + } + + //finally + if (result == "") { + print counter; + } + else { + print result; + } + + counter += 1; +} + diff --git a/scripts/looping.toy b/scripts/looping.toy new file mode 100644 index 0000000..39117ec --- /dev/null +++ b/scripts/looping.toy @@ -0,0 +1,9 @@ +var a: int = 0; + +while(a < 10) { + print a; + a += 1; +} + +print "Finished"; + diff --git a/source/toy_ast.c b/source/toy_ast.c index c576cf8..4850745 100644 --- a/source/toy_ast.c +++ b/source/toy_ast.c @@ -116,6 +116,16 @@ void Toy_private_emitAstIfThenElse(Toy_Bucket** bucketHandle, Toy_Ast** astHandl (*astHandle) = tmp; } +void Toy_private_emitAstWhileThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch) { + Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); + + tmp->type = TOY_AST_WHILE_THEN; + tmp->whileThen.condBranch = condBranch; + tmp->whileThen.thenBranch = thenBranch; + + (*astHandle) = tmp; +} + void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) { Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); diff --git a/source/toy_ast.h b/source/toy_ast.h index c369ced..c11a015 100644 --- a/source/toy_ast.h +++ b/source/toy_ast.h @@ -19,6 +19,7 @@ typedef enum Toy_AstType { TOY_AST_ASSERT, TOY_AST_IF_THEN_ELSE, + TOY_AST_WHILE_THEN, TOY_AST_PRINT, TOY_AST_VAR_DECLARE, @@ -131,6 +132,12 @@ typedef struct Toy_AstIfThenElse { Toy_Ast* elseBranch; } Toy_AstIfThenElse; +typedef struct Toy_AstWhileThen { + Toy_AstType type; + Toy_Ast* condBranch; + Toy_Ast* thenBranch; +} Toy_AstWhileThen; + typedef struct Toy_AstPrint { Toy_AstType type; Toy_Ast* child; @@ -177,6 +184,7 @@ union Toy_Ast { //32 | 64 BITNESS Toy_AstCompound compound; //16 | 24 Toy_AstAssert assert; //16 | 24 Toy_AstIfThenElse ifThenElse; //16 | 32 + Toy_AstWhileThen whileThen; //16 | 24 Toy_AstPrint print; //8 | 16 Toy_AstVarDeclare varDeclare; //16 | 24 Toy_AstVarAssign varAssign; //16 | 24 @@ -198,6 +206,7 @@ void Toy_private_emitAstCompound(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, void Toy_private_emitAstAssert(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* child, Toy_Ast* msg); void Toy_private_emitAstIfThenElse(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch, Toy_Ast* elseBranch); +void Toy_private_emitAstWhileThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch); 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); diff --git a/source/toy_parser.c b/source/toy_parser.c index d84f763..9910bf5 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -352,31 +352,33 @@ static Toy_AstFlag literal(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_As case TOY_TOKEN_LITERAL_STRING: { char buffer[parser->previous.length + 1]; unsigned int escapeCounter = 0; - unsigned int i = 0, o = 0; - do { - buffer[i] = parser->previous.lexeme[o]; - if (buffer[i] == '\\' && parser->previous.lexeme[++o]) { - escapeCounter++; - //also handle escape characters - switch(parser->previous.lexeme[o]) { - case 'n': - buffer[i] = '\n'; - break; - case 't': - buffer[i] = '\t'; - break; - case '\\': - buffer[i] = '\\'; - break; - case '"': - buffer[i] = '"'; - break; + if (parser->previous.length > 0) { //BUGFIX: compensate for zero-length strings + do { + buffer[i] = parser->previous.lexeme[o]; + if (buffer[i] == '\\' && parser->previous.lexeme[++o]) { + escapeCounter++; + + //also handle escape characters + switch(parser->previous.lexeme[o]) { + case 'n': + buffer[i] = '\n'; + break; + case 't': + buffer[i] = '\t'; + break; + case '\\': + buffer[i] = '\\'; + break; + case '"': + buffer[i] = '"'; + break; + } } - } - i++; - } while (parser->previous.lexeme[o++] && i < parser->previous.length); + i++; + } while (parser->previous.lexeme[o++] && i < parser->previous.length); + } buffer[i] = '\0'; unsigned int len = i - escapeCounter; //NOTE: len is ONLY the string length @@ -699,6 +701,21 @@ static void makeIfThenElseStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, To Toy_private_emitAstIfThenElse(bucketHandle, rootHandle, condBranch, thenBranch, elseBranch); } +static void makeWhileStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { + Toy_Ast* condBranch = NULL; + Toy_Ast* thenBranch = NULL; + + //while (condBranch) + consume(parser, TOY_TOKEN_OPERATOR_PAREN_LEFT, "Expected '(' after 'while' keyword"); + makeExpr(bucketHandle, parser, &condBranch); + consume(parser, TOY_TOKEN_OPERATOR_PAREN_RIGHT, "Expected ')' after 'while' condition"); + + // { thenBranch } + makeDeclarationStmt(bucketHandle, parser, &thenBranch); + + Toy_private_emitAstWhileThen(bucketHandle, rootHandle, condBranch, thenBranch); +} + static void makePrintStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { makeExpr(bucketHandle, parser, rootHandle); Toy_private_emitAstPrint(bucketHandle, rootHandle); @@ -771,10 +788,15 @@ static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro return; } + //TODO: break & continue + //while-then + else if (match(parser, TOY_TOKEN_KEYWORD_WHILE)) { + makeWhileStmt(bucketHandle, parser, rootHandle); + return; + } + //for-pre-clause-post-then - //break - //continue //return //import diff --git a/source/toy_routine.c b/source/toy_routine.c index 9036ee9..55d2537 100644 --- a/source/toy_routine.c +++ b/source/toy_routine.c @@ -319,7 +319,7 @@ static unsigned int writeInstructionIfThenElse(Toy_Routine** rt, Toy_AstIfThenEl unsigned int thenEndAddr = SKIP_INT(rt, code); //parameter to be written later - //emit then branch + //emit then-branch writeRoutineCode(rt, ast.thenBranch); if (ast.elseBranch != NULL) { @@ -349,6 +349,37 @@ static unsigned int writeInstructionIfThenElse(Toy_Routine** rt, Toy_AstIfThenEl return 0; } +static unsigned int writeInstructionWhileThen(Toy_Routine** rt, Toy_AstWhileThen ast) { + //TODO: begin + unsigned int beginAddr = CURRENT_ADDRESS(rt, code); + + //cond-branch + writeRoutineCode(rt, ast.condBranch); + + //emit the jump word (opcode, type, condition, padding) + EMIT_BYTE(rt, code, TOY_OPCODE_JUMP); + EMIT_BYTE(rt, code, TOY_OP_PARAM_JUMP_RELATIVE); + EMIT_BYTE(rt, code, TOY_OP_PARAM_JUMP_IF_FALSE); + EMIT_BYTE(rt, code, 0); + + unsigned int endAddr = SKIP_INT(rt, code); //parameter to be written later + + //emit then-branch + writeRoutineCode(rt, ast.thenBranch); + + //jump to begin to repeat the conditional test + EMIT_BYTE(rt, code, TOY_OPCODE_JUMP); + EMIT_BYTE(rt, code, TOY_OP_PARAM_JUMP_RELATIVE); + EMIT_BYTE(rt, code, TOY_OP_PARAM_JUMP_ALWAYS); + EMIT_BYTE(rt, code, 0); + + EMIT_INT(rt, code, beginAddr - (CURRENT_ADDRESS(rt, code) + 4)); //this sets a negative value + + OVERWRITE_INT(rt, code, endAddr, CURRENT_ADDRESS(rt, code) - (endAddr + 4)); + + return 0; +} + static unsigned int writeInstructionPrint(Toy_Routine** rt, Toy_AstPrint ast) { //the thing to print writeRoutineCode(rt, ast.child); @@ -603,6 +634,10 @@ static unsigned int writeRoutineCode(Toy_Routine** rt, Toy_Ast* ast) { result += writeInstructionIfThenElse(rt, ast->ifThenElse); break; + case TOY_AST_WHILE_THEN: + result += writeInstructionWhileThen(rt, ast->whileThen); + break; + case TOY_AST_PRINT: result += writeInstructionPrint(rt, ast->print); break; diff --git a/tests/cases/test_ast.c b/tests/cases/test_ast.c index 10bd98a..f90be48 100644 --- a/tests/cases/test_ast.c +++ b/tests/cases/test_ast.c @@ -26,6 +26,7 @@ int test_sizeof_ast_64bit() { TEST_SIZEOF(Toy_AstCompound, 24); TEST_SIZEOF(Toy_AstAssert, 24); TEST_SIZEOF(Toy_AstIfThenElse, 32); + TEST_SIZEOF(Toy_AstWhileThen, 24); TEST_SIZEOF(Toy_AstPrint, 16); TEST_SIZEOF(Toy_AstVarDeclare, 24); TEST_SIZEOF(Toy_AstVarAssign, 24); @@ -61,6 +62,7 @@ int test_sizeof_ast_32bit() { TEST_SIZEOF(Toy_AstCompound, 16); TEST_SIZEOF(Toy_AstAssert, 12); TEST_SIZEOF(Toy_AstIfThenElse, 16); + TEST_SIZEOF(Toy_AstWhileThen, 12); TEST_SIZEOF(Toy_AstPrint, 8); TEST_SIZEOF(Toy_AstVarDeclare, 12); TEST_SIZEOF(Toy_AstVarAssign, 16);