diff --git a/scripts/fizzbuzz.toy b/scripts/fizzbuzz.toy index 9c37ab6..3441d17 100644 --- a/scripts/fizzbuzz.toy +++ b/scripts/fizzbuzz.toy @@ -1,8 +1,8 @@ //standard example, using 'while' instead of 'for', because it's not ready yet -var counter: int = 1; +var counter: int = 0; -while (counter <= 100) { +while (++counter <= 100) { var result: string = ""; if (counter % 3 == 0) { @@ -20,7 +20,5 @@ while (counter <= 100) { else { print counter; } - - counter += 1; } diff --git a/scripts/valgrind.toy b/scripts/valgrind.toy index 6c056d3..8cd8d1f 100644 --- a/scripts/valgrind.toy +++ b/scripts/valgrind.toy @@ -9,5 +9,18 @@ assert --a == 42; assert a == 42; + print a; +} + + +//increment & decrement (postfix) +{ + var a = 42; + assert a == 42; + assert a++ == 42; + assert a == 43; + assert a-- == 43; + assert a == 42; + print a; } \ No newline at end of file diff --git a/source/toy_ast.h b/source/toy_ast.h index 5a1fd83..560da5a 100644 --- a/source/toy_ast.h +++ b/source/toy_ast.h @@ -46,6 +46,10 @@ typedef enum Toy_AstFlag { TOY_AST_FLAG_DIVIDE = 4, TOY_AST_FLAG_MODULO = 5, + TOY_AST_FLAG_AND = 6, + TOY_AST_FLAG_OR = 7, + TOY_AST_FLAG_CONCAT = 8, + TOY_AST_FLAG_ASSIGN = 10, TOY_AST_FLAG_ADD_ASSIGN = 11, TOY_AST_FLAG_SUBTRACT_ASSIGN = 12, @@ -66,14 +70,12 @@ typedef enum Toy_AstFlag { TOY_AST_FLAG_PAIR = 33, TOY_AST_FLAG_INDEX = 34, - TOY_AST_FLAG_AND = 40, - TOY_AST_FLAG_OR = 41, - TOY_AST_FLAG_CONCAT = 42, - //unary flags - TOY_AST_FLAG_NEGATE = 43, - TOY_AST_FLAG_PREFIX_INCREMENT = 44, - TOY_AST_FLAG_PREFIX_DECREMENT = 45, + TOY_AST_FLAG_NEGATE = 40, + TOY_AST_FLAG_PREFIX_INCREMENT = 41, + TOY_AST_FLAG_PREFIX_DECREMENT = 42, + TOY_AST_FLAG_POSTFIX_INCREMENT = 43, + TOY_AST_FLAG_POSTFIX_DECREMENT = 44, // TOY_AST_FLAG_TERNARY, } Toy_AstFlag; diff --git a/source/toy_parser.c b/source/toy_parser.c index cccbf6f..61cadd9 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -119,6 +119,7 @@ static Toy_AstFlag binary(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast static Toy_AstFlag group(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); static Toy_AstFlag compound(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); static Toy_AstFlag aggregate(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); +static Toy_AstFlag unaryPostfix(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); //precedence definitions static ParsingTuple parsingRulesetTable[] = { @@ -180,8 +181,8 @@ static ParsingTuple parsingRulesetTable[] = { {PREC_ASSIGNMENT,NULL,binary},// TOY_TOKEN_OPERATOR_MULTIPLY_ASSIGN, {PREC_ASSIGNMENT,NULL,binary},// TOY_TOKEN_OPERATOR_DIVIDE_ASSIGN, {PREC_ASSIGNMENT,NULL,binary},// TOY_TOKEN_OPERATOR_MODULO_ASSIGN, - {PREC_CALL,unary,NULL},// TOY_TOKEN_OPERATOR_INCREMENT, - {PREC_CALL,unary,NULL},// TOY_TOKEN_OPERATOR_DECREMENT, + {PREC_CALL,unary,unaryPostfix},// TOY_TOKEN_OPERATOR_INCREMENT, + {PREC_CALL,unary,unaryPostfix},// TOY_TOKEN_OPERATOR_DECREMENT, {PREC_ASSIGNMENT,NULL,binary},// TOY_TOKEN_OPERATOR_ASSIGN, //comparator operators @@ -223,6 +224,10 @@ static ParsingTuple parsingRulesetTable[] = { {PREC_NONE,NULL,NULL},// TOY_TOKEN_EOF, }; +static ParsingTuple* getParsingRule(Toy_TokenType type) { + return &parsingRulesetTable[type]; +} + static Toy_ValueType readType(Toy_Parser* parser) { advance(parser); @@ -429,7 +434,7 @@ static Toy_AstFlag unary(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast* //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) { - printError(parser, parser->previous, "Unexpected non-name-string token in unary operator increment precedence rule"); + printError(parser, parser->previous, "Unexpected non-name-string token in unary-prefix operator precedence rule"); Toy_private_emitAstError(bucketHandle, rootHandle); } else { @@ -666,8 +671,41 @@ static Toy_AstFlag aggregate(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_ } } -static ParsingTuple* getParsingRule(Toy_TokenType type) { - return &parsingRulesetTable[type]; +static Toy_AstFlag unaryPostfix(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { + //grab the var name, rejecting any other token types + if (parser->previous.type != TOY_TOKEN_NAME) { + printError(parser, parser->previous, "Unexpected parameter passed to unary-postfix precedence rule"); + Toy_private_emitAstError(bucketHandle, rootHandle); + return TOY_AST_FLAG_NONE; + } + + Toy_Ast* primary = NULL; + ParsingRule nameRule = getParsingRule(parser->previous.type)->prefix; + 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 + 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) { + printError(parser, parser->previous, "Unexpected non-name-string token in unary-postfix operator precedence rule"); + Toy_private_emitAstError(bucketHandle, rootHandle); + return TOY_AST_FLAG_NONE; + } + + (*rootHandle) = primary->varAccess.child; + + //output the postfix AST + if (match(parser, TOY_TOKEN_OPERATOR_INCREMENT)) { + Toy_private_emitAstUnary(bucketHandle, rootHandle, TOY_AST_FLAG_POSTFIX_INCREMENT); + return TOY_AST_FLAG_POSTFIX_INCREMENT; + } + else if (match(parser, TOY_TOKEN_OPERATOR_DECREMENT)) { + Toy_private_emitAstUnary(bucketHandle, rootHandle, TOY_AST_FLAG_POSTFIX_DECREMENT); + return TOY_AST_FLAG_POSTFIX_DECREMENT; + } + else { + printError(parser, parser->previous, "Unexpected token passed to unary-postfix precedence rule"); + Toy_private_emitAstError(bucketHandle, rootHandle); + return TOY_AST_FLAG_NONE; + } } //grammar rules @@ -721,6 +759,10 @@ static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_A else if (flag >= 30 && flag <= 39) { Toy_private_emitAstAggregate(bucketHandle, rootHandle, flag, ptr); } + else if (flag >= 40 && flag <= 49) { + (*rootHandle) = ptr; + continue; + } else { //BUGFIX: '&&' and '||' are special cases, with short-circuit logic if (flag == TOY_AST_FLAG_AND || flag == TOY_AST_FLAG_OR) { diff --git a/source/toy_routine.c b/source/toy_routine.c index 4cd46e8..026e923 100644 --- a/source/toy_routine.c +++ b/source/toy_routine.c @@ -224,6 +224,63 @@ static unsigned int writeInstructionUnary(Toy_Routine** rt, Toy_AstUnary ast) { result = 1; } + else if (ast.flag == TOY_AST_FLAG_POSTFIX_INCREMENT || ast.flag == TOY_AST_FLAG_POSTFIX_DECREMENT) { //NOTE: ditto + //read the var name onto the stack + Toy_String* name = TOY_VALUE_AS_STRING(ast.child->value.value); + + EMIT_BYTE(rt, code, TOY_OPCODE_READ); + EMIT_BYTE(rt, code, TOY_VALUE_STRING); + EMIT_BYTE(rt, code, TOY_STRING_NAME); + EMIT_BYTE(rt, code, name->info.length); //store the length (max 255) + + emitString(rt, name); + + //access the value (postfix++ and postfix--) + EMIT_BYTE(rt, code, TOY_OPCODE_ACCESS); + EMIT_BYTE(rt, code,0); + EMIT_BYTE(rt, code,0); + EMIT_BYTE(rt, code,0); + + //read the var name onto the stack (again) + name = TOY_VALUE_AS_STRING(ast.child->value.value); + + EMIT_BYTE(rt, code, TOY_OPCODE_READ); + EMIT_BYTE(rt, code, TOY_VALUE_STRING); + EMIT_BYTE(rt, code, TOY_STRING_NAME); + EMIT_BYTE(rt, code, name->info.length); //store the length (max 255) + + emitString(rt, name); + + //duplicate the var name, then get the value + EMIT_BYTE(rt, code,TOY_OPCODE_DUPLICATE); + EMIT_BYTE(rt, code, TOY_OPCODE_ACCESS); //squeezed + EMIT_BYTE(rt, code,0); + EMIT_BYTE(rt, code,0); + + //read the integer '1' + EMIT_BYTE(rt, code, TOY_OPCODE_READ); + EMIT_BYTE(rt, code, TOY_VALUE_INTEGER); + EMIT_BYTE(rt, code, 0); + EMIT_BYTE(rt, code, 0); + + EMIT_INT(rt, code, 1); + + //add (or subtract) the two values, then assign (pops the second duplicate, and leaves value on the stack) + EMIT_BYTE(rt, code, ast.flag == TOY_AST_FLAG_POSTFIX_INCREMENT ? TOY_OPCODE_ADD : TOY_OPCODE_SUBTRACT); + EMIT_BYTE(rt, code,TOY_OPCODE_ASSIGN); //squeezed + EMIT_BYTE(rt, code,0); + EMIT_BYTE(rt, code,0); + + //remove the lingering value (yep, this is UGLY) + EMIT_BYTE(rt, code, TOY_OPCODE_ELIMINATE); + EMIT_BYTE(rt, code,0); + EMIT_BYTE(rt, code,0); + EMIT_BYTE(rt, code,0); + + //leaves one value on the stack + result = 1; + } + else { fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST unary flag found\n" TOY_CC_RESET); exit(-1); diff --git a/tests/integrations/test_variables.toy b/tests/integrations/test_variables.toy index f434ffe..5d0d01a 100644 --- a/tests/integrations/test_variables.toy +++ b/tests/integrations/test_variables.toy @@ -145,4 +145,16 @@ print !false; //true assert a == 42, "prefix increment & decrement 1.5"; } +//increment & decrement (postfix) +{ + var a = 42; + assert a == 42, "postfix increment & decrement 1.1"; + assert a++ == 42, "postfix increment & decrement 1.2"; + assert a == 43, "postfix increment & decrement 1.3"; + assert a-- == 43, "postfix increment & decrement 1.4"; + assert a == 42, "postfix increment & decrement 1.5"; + + print a; +} + //TODO: type casting