From 2bf721867b158692c025ef2532c7ef7adb9ab1b4 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Sat, 20 Aug 2022 06:20:20 +0100 Subject: [PATCH] Comparions and the ! operator work --- scripts/comparisons.toy | 28 ++++++ source/interpreter.c | 151 ++++++++++++++++++++++++++++++- source/lexer.c | 4 +- source/node.h | 1 - source/opcodes.h | 8 ++ source/parser.c | 195 ++++++++++++++++++++++++++++++---------- 6 files changed, 336 insertions(+), 51 deletions(-) create mode 100644 scripts/comparisons.toy diff --git a/scripts/comparisons.toy b/scripts/comparisons.toy new file mode 100644 index 0000000..be9f2c7 --- /dev/null +++ b/scripts/comparisons.toy @@ -0,0 +1,28 @@ + +//test numbers +assert 1 < 2, "1 < 2"; +assert 1 == 1, "1 == 1"; +assert 2 > 1, "2 > 1"; +assert 1 <= 2, "1 <= 2"; +assert 2 >= 1, "2 >= 1"; + + +//test variables +var a = 1; +var b = 2; + +assert a < b, "a < b"; +assert a == a, "a == a"; +assert b > a, "b > a"; +assert a <= b, "a <= b"; +assert b >= a, "b >= a"; + + +//test negation +assert !false, "!false"; + +var c = false; +assert !c, "!c"; + + +print "All good"; diff --git a/source/interpreter.c b/source/interpreter.c index a4dd0b7..a6f3553 100644 --- a/source/interpreter.c +++ b/source/interpreter.c @@ -180,11 +180,14 @@ static bool execNegate(Interpreter* interpreter) { //negate the top literal on the stack Literal lit = popLiteralArray(&interpreter->stack); - if (parseIdentifierToValue(interpreter, &lit)) { + if (!parseIdentifierToValue(interpreter, &lit)) { return false; } - if (IS_INTEGER(lit)) { + if (IS_BOOLEAN(lit)) { + lit = TO_BOOLEAN_LITERAL(!AS_BOOLEAN(lit)); + } + else if (IS_INTEGER(lit)) { lit = TO_INTEGER_LITERAL(-AS_INTEGER(lit)); } else if (IS_FLOAT(lit)) { @@ -440,6 +443,114 @@ static bool execValCast(Interpreter* interpreter) { return true; } +static bool execCompareEqual(Interpreter* interpreter, bool invert) { + Literal rhs = popLiteralArray(&interpreter->stack); + Literal lhs = popLiteralArray(&interpreter->stack); + + parseIdentifierToValue(interpreter, &rhs); + parseIdentifierToValue(interpreter, &lhs); + + bool result = literalsAreEqual(lhs, rhs); + + if (invert) { + result = !result; + } + + pushLiteralArray(&interpreter->stack, TO_BOOLEAN_LITERAL(result)); + + return true; +} + +static bool execCompareLess(Interpreter* interpreter, bool invert) { + Literal rhs = popLiteralArray(&interpreter->stack); + Literal lhs = popLiteralArray(&interpreter->stack); + + parseIdentifierToValue(interpreter, &rhs); + parseIdentifierToValue(interpreter, &lhs); + + //not a number, return falure + if (!(IS_INTEGER(lhs) || IS_FLOAT(lhs))) { + printf("Incorrect type in comparison, value \""); + printLiteral(lhs); + printf("\"\n"); + return false; + } + + if (!(IS_INTEGER(rhs) || IS_FLOAT(rhs))) { + printf("Incorrect type in comparison, value \""); + printLiteral(rhs); + printf("\"\n"); + return false; + } + + //convert to floats - easier + if (IS_INTEGER(lhs)) { + lhs = TO_FLOAT_LITERAL(AS_INTEGER(lhs)); + } + + if (IS_INTEGER(rhs)) { + rhs = TO_FLOAT_LITERAL(AS_INTEGER(rhs)); + } + + bool result; + + if (!invert) { + result = (AS_FLOAT(lhs) < AS_FLOAT(rhs)); + } + else { + result = (AS_FLOAT(lhs) > AS_FLOAT(rhs)); + } + + pushLiteralArray(&interpreter->stack, TO_BOOLEAN_LITERAL(result)); + + return true; +} + +static bool execCompareLessEqual(Interpreter* interpreter, bool invert) { + Literal rhs = popLiteralArray(&interpreter->stack); + Literal lhs = popLiteralArray(&interpreter->stack); + + parseIdentifierToValue(interpreter, &rhs); + parseIdentifierToValue(interpreter, &lhs); + + //not a number, return falure + if (!(IS_INTEGER(lhs) || IS_FLOAT(lhs))) { + printf("Incorrect type in comparison, value \""); + printLiteral(lhs); + printf("\"\n"); + return false; + } + + if (!(IS_INTEGER(rhs) || IS_FLOAT(rhs))) { + printf("Incorrect type in comparison, value \""); + printLiteral(rhs); + printf("\"\n"); + return false; + } + + //convert to floats - easier + if (IS_INTEGER(lhs)) { + lhs = TO_FLOAT_LITERAL(AS_INTEGER(lhs)); + } + + if (IS_INTEGER(rhs)) { + rhs = TO_FLOAT_LITERAL(AS_INTEGER(rhs)); + } + + bool result; + + if (!invert) { + result = (AS_FLOAT(lhs) < AS_FLOAT(rhs)) || literalsAreEqual(lhs, rhs); + } + else { + result = (AS_FLOAT(lhs) > AS_FLOAT(rhs)) || literalsAreEqual(lhs, rhs); + } + + pushLiteralArray(&interpreter->stack, TO_BOOLEAN_LITERAL(result)); + + return true; +} + //the heart of toy static void execInterpreter(Interpreter* interpreter) { unsigned char opcode = readByte(interpreter->bytecode, &interpreter->count); @@ -518,6 +629,42 @@ static void execInterpreter(Interpreter* interpreter) { } break; + case OP_COMPARE_EQUAL: + if (!execCompareEqual(interpreter, false)) { + return; + } + break; + + case OP_COMPARE_NOT_EQUAL: + if (!execCompareEqual(interpreter, true)) { + return; + } + break; + + case OP_COMPARE_LESS: + if (!execCompareLess(interpreter, false)) { + return; + } + break; + + case OP_COMPARE_LESS_EQUAL: + if (!execCompareLessEqual(interpreter, false)) { + return; + } + break; + + case OP_COMPARE_GREATER: + if (!execCompareLess(interpreter, true)) { + return; + } + break; + + case OP_COMPARE_GREATER_EQUAL: + if (!execCompareLessEqual(interpreter, true)) { + return; + } + break; + default: printf("Unknown opcode found %d, terminating\n", opcode); printLiteralArray(&interpreter->stack, "\n"); diff --git a/source/lexer.c b/source/lexer.c index 2381e16..20c1bb0 100644 --- a/source/lexer.c +++ b/source/lexer.c @@ -121,8 +121,8 @@ static Token makeToken(Lexer* lexer, TokenType type) { Token token; token.type = type; - token.lexeme = &lexer->source[lexer->current - 1]; - token.length = 1; + token.length = lexer->current - lexer->start; + token.lexeme = &lexer->source[lexer->current - token.length]; token.line = lexer->line; //BUG #10: this shows TOKEN_EOF twice due to the overarching structure of the program - can't be fixed diff --git a/source/node.h b/source/node.h index c66e6c3..686869f 100644 --- a/source/node.h +++ b/source/node.h @@ -101,4 +101,3 @@ void emitNodeVarTypes(Node** nodeHandle, Literal literal); void emitNodeVarDecl(Node** nodeHandle, Literal identifier, Literal type, Node* expression); void printNode(Node* node); - diff --git a/source/opcodes.h b/source/opcodes.h index 9b3d46f..967ba31 100644 --- a/source/opcodes.h +++ b/source/opcodes.h @@ -35,6 +35,14 @@ typedef enum Opcode { OP_TYPE_CAST, //temporarily change a type of an atomic value + //comparion of values + OP_COMPARE_EQUAL, + OP_COMPARE_NOT_EQUAL, + OP_COMPARE_LESS, + OP_COMPARE_LESS_EQUAL, + OP_COMPARE_GREATER, + OP_COMPARE_GREATER_EQUAL, + //meta OP_SECTION_END, //TODO: add more diff --git a/source/parser.c b/source/parser.c index 90e036a..c5fee77 100644 --- a/source/parser.c +++ b/source/parser.c @@ -97,7 +97,7 @@ typedef enum { PREC_TERNARY, PREC_OR, PREC_AND, - PREC_EQUALITY, + // PREC_EQUALITY, PREC_COMPARISON, PREC_TERM, PREC_FACTOR, @@ -284,6 +284,7 @@ static Opcode binary(Parser* parser, Node** nodeHandle) { //binary() is an infix rule - so only get the RHS of the operator switch(parser->previous.type) { + //arithmetic case TOKEN_PLUS: { parsePrecedence(parser, nodeHandle, PREC_TERM); return OP_ADDITION; @@ -309,11 +310,43 @@ static Opcode binary(Parser* parser, Node** nodeHandle) { return OP_MODULO; } + //assignment case TOKEN_ASSIGN: { parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT); return OP_VAR_ASSIGN; } + //comparison + case TOKEN_EQUAL: { + parsePrecedence(parser, nodeHandle, PREC_COMPARISON); + return OP_COMPARE_EQUAL; + } + + case TOKEN_NOT_EQUAL: { + parsePrecedence(parser, nodeHandle, PREC_COMPARISON); + return OP_COMPARE_NOT_EQUAL; + } + + case TOKEN_LESS: { + parsePrecedence(parser, nodeHandle, PREC_COMPARISON); + return OP_COMPARE_LESS; + } + + case TOKEN_LESS_EQUAL: { + parsePrecedence(parser, nodeHandle, PREC_COMPARISON); + return OP_COMPARE_LESS_EQUAL; + } + + case TOKEN_GREATER: { + parsePrecedence(parser, nodeHandle, PREC_COMPARISON); + return OP_COMPARE_GREATER; + } + + case TOKEN_GREATER_EQUAL: { + parsePrecedence(parser, nodeHandle, PREC_COMPARISON); + return OP_COMPARE_GREATER_EQUAL; + } + default: error(parser, parser->previous, "Unexpected token passed to binary precedence rule"); return OP_EOF; @@ -321,47 +354,63 @@ static Opcode binary(Parser* parser, Node** nodeHandle) { } static Opcode unary(Parser* parser, Node** nodeHandle) { - switch(parser->previous.type) { - case TOKEN_MINUS: { - //temp handle to potentially negate values - Node* tmpNode = NULL; - parsePrecedence(parser, &tmpNode, PREC_TERNARY); //can be a literal + Node* tmpNode = NULL; - //check for negative literals (optimisation) - if (tmpNode->type == NODE_LITERAL) { - //negate directly, if int or float - Literal lit = tmpNode->atomic.literal; + if (parser->previous.type == TOKEN_MINUS) { + //temp handle to potentially negate values + parsePrecedence(parser, &tmpNode, PREC_TERNARY); //can be a literal - if (IS_INTEGER(lit)) { - lit = TO_INTEGER_LITERAL(-AS_INTEGER(lit)); - } + //check for negative literals (optimisation) + if (tmpNode->type == NODE_LITERAL) { + //negate directly, if int or float + Literal lit = tmpNode->atomic.literal; - if (IS_FLOAT(lit)) { - lit = TO_FLOAT_LITERAL(-AS_FLOAT(lit)); - } - - tmpNode->atomic.literal = lit; - *nodeHandle = tmpNode; - - return OP_EOF; + if (IS_INTEGER(lit)) { + lit = TO_INTEGER_LITERAL(-AS_INTEGER(lit)); } - //process the literal without optimizations - if (tmpNode->type == NODE_LITERAL) { - emitNodeUnary(nodeHandle, OP_NEGATE); - nodeHandle = &((*nodeHandle)->unary.child); //re-align after append - (*nodeHandle) = tmpNode; //set negate's child to the literal - return OP_EOF; + if (IS_FLOAT(lit)) { + lit = TO_FLOAT_LITERAL(-AS_FLOAT(lit)); } - error(parser, parser->previous, "Unexpected token passed to unary minus precedence rule"); + tmpNode->atomic.literal = lit; + *nodeHandle = tmpNode; + return OP_EOF; } - - default: - error(parser, parser->previous, "Unexpected token passed to unary precedence rule"); - return OP_EOF; } + + else if (parser->previous.type == TOKEN_NOT) { + //temp handle to potentially negate values + parsePrecedence(parser, &tmpNode, PREC_TERNARY); //can be a literal + + //check for negative literals (optimisation) + if (tmpNode->type == NODE_LITERAL && !IS_IDENTIFIER(tmpNode->atomic.literal)) { + //negate directly, if int or float + Literal lit = tmpNode->atomic.literal; + + if (IS_BOOLEAN(lit)) { + lit = TO_BOOLEAN_LITERAL(!AS_BOOLEAN(lit)); + } + + tmpNode->atomic.literal = lit; + *nodeHandle = tmpNode; + + return OP_EOF; + } + } + + else { + error(parser, parser->previous, "Unexpected token passed to unary precedence rule"); + return OP_EOF; + } + + + //actually emit the negation + emitNodeUnary(nodeHandle, OP_NEGATE); + (*nodeHandle)->unary.child = tmpNode; //set negate's child to the literal + + return OP_EOF; } static Opcode atomic(Parser* parser, Node** nodeHandle) { @@ -522,9 +571,9 @@ ParseRule parseRules[] = { //must match the token types //math operators {NULL, binary, PREC_TERM},// TOKEN_PLUS, {unary, binary, PREC_TERM},// TOKEN_MINUS, - {NULL, binary, PREC_TERM},// TOKEN_MULTIPLY, - {NULL, binary, PREC_TERM},// TOKEN_DIVIDE, - {NULL, binary, PREC_TERM},// TOKEN_MODULO, + {NULL, binary, PREC_FACTOR},// TOKEN_MULTIPLY, + {NULL, binary, PREC_FACTOR},// TOKEN_DIVIDE, + {NULL, binary, PREC_FACTOR},// TOKEN_MODULO, {NULL, NULL, PREC_NONE},// TOKEN_PLUS_ASSIGN, {NULL, NULL, PREC_NONE},// TOKEN_MINUS_ASSIGN, {NULL, NULL, PREC_NONE},// TOKEN_MULTIPLY_ASSIGN, @@ -541,20 +590,20 @@ ParseRule parseRules[] = { //must match the token types {NULL, NULL, PREC_NONE},// TOKEN_BRACKET_RIGHT, {NULL, NULL, PREC_NONE},// TOKEN_BRACE_LEFT, {NULL, NULL, PREC_NONE},// TOKEN_BRACE_RIGHT, - {NULL, NULL, PREC_NONE},// TOKEN_NOT, - {NULL, NULL, PREC_NONE},// TOKEN_NOT_EQUAL, - {NULL, NULL, PREC_NONE},// TOKEN_EQUAL, - {NULL, NULL, PREC_NONE},// TOKEN_LESS, - {NULL, NULL, PREC_NONE},// TOKEN_GREATER, - {NULL, NULL, PREC_NONE},// TOKEN_LESS_EQUAL, - {NULL, NULL, PREC_NONE},// TOKEN_GREATER_EQUAL, + {unary, NULL, PREC_CALL},// TOKEN_NOT, + {NULL, binary, PREC_COMPARISON},// TOKEN_NOT_EQUAL, + {NULL, binary, PREC_COMPARISON},// TOKEN_EQUAL, + {NULL, binary, PREC_COMPARISON},// TOKEN_LESS, + {NULL, binary, PREC_COMPARISON},// TOKEN_GREATER, + {NULL, binary, PREC_COMPARISON},// TOKEN_LESS_EQUAL, + {NULL, binary, PREC_COMPARISON},// TOKEN_GREATER_EQUAL, {NULL, NULL, PREC_NONE},// TOKEN_AND, {NULL, NULL, PREC_NONE},// TOKEN_OR, //other operators {NULL, NULL, PREC_NONE},// TOKEN_COLON, {NULL, NULL, PREC_NONE},// TOKEN_SEMICOLON, - {NULL, NULL, PREC_CALL},// TOKEN_COMMA, + {NULL, NULL, PREC_NONE},// TOKEN_COMMA, {NULL, NULL, PREC_NONE},// TOKEN_DOT, {NULL, NULL, PREC_NONE},// TOKEN_PIPE, {NULL, NULL, PREC_NONE},// TOKEN_REST, @@ -577,6 +626,12 @@ static bool calcStaticBinaryArithmetic(Parser* parser, Node** nodeHandle) { case OP_MULTIPLICATION: case OP_DIVISION: case OP_MODULO: + case OP_COMPARE_EQUAL: + case OP_COMPARE_NOT_EQUAL: + case OP_COMPARE_LESS: + case OP_COMPARE_LESS_EQUAL: + case OP_COMPARE_GREATER: + case OP_COMPARE_GREATER_EQUAL: break; default: @@ -642,6 +697,30 @@ static bool calcStaticBinaryArithmetic(Parser* parser, Node** nodeHandle) { result = TO_INTEGER_LITERAL( AS_INTEGER(lhs) % AS_INTEGER(rhs) ); break; + case OP_COMPARE_EQUAL: + result = TO_BOOLEAN_LITERAL( AS_INTEGER(lhs) == AS_INTEGER(rhs) ); + break; + + case OP_COMPARE_NOT_EQUAL: + result = TO_BOOLEAN_LITERAL( AS_INTEGER(lhs) != AS_INTEGER(rhs) ); + break; + + case OP_COMPARE_LESS: + result = TO_BOOLEAN_LITERAL( AS_INTEGER(lhs) < AS_INTEGER(rhs) ); + break; + + case OP_COMPARE_LESS_EQUAL: + result = TO_BOOLEAN_LITERAL( AS_INTEGER(lhs) <= AS_INTEGER(rhs) ); + break; + + case OP_COMPARE_GREATER: + result = TO_BOOLEAN_LITERAL( AS_INTEGER(lhs) > AS_INTEGER(rhs) ); + break; + + case OP_COMPARE_GREATER_EQUAL: + result = TO_BOOLEAN_LITERAL( AS_INTEGER(lhs) >= AS_INTEGER(rhs) ); + break; + default: printf("[internal] bad opcode argument passed to calcStaticBinaryArithmetic()"); return false; @@ -676,6 +755,30 @@ static bool calcStaticBinaryArithmetic(Parser* parser, Node** nodeHandle) { result = TO_FLOAT_LITERAL( AS_FLOAT(lhs) / AS_FLOAT(rhs) ); break; + case OP_COMPARE_EQUAL: + result = TO_BOOLEAN_LITERAL( AS_FLOAT(lhs) == AS_FLOAT(rhs) ); + break; + + case OP_COMPARE_NOT_EQUAL: + result = TO_BOOLEAN_LITERAL( AS_FLOAT(lhs) != AS_FLOAT(rhs) ); + break; + + case OP_COMPARE_LESS: + result = TO_BOOLEAN_LITERAL( AS_FLOAT(lhs) < AS_FLOAT(rhs) ); + break; + + case OP_COMPARE_LESS_EQUAL: + result = TO_BOOLEAN_LITERAL( AS_FLOAT(lhs) <= AS_FLOAT(rhs) ); + break; + + case OP_COMPARE_GREATER: + result = TO_BOOLEAN_LITERAL( AS_FLOAT(lhs) > AS_FLOAT(rhs) ); + break; + + case OP_COMPARE_GREATER_EQUAL: + result = TO_BOOLEAN_LITERAL( AS_FLOAT(lhs) >= AS_FLOAT(rhs) ); + break; + default: printf("[internal] bad opcode argument passed to calcStaticBinaryArithmetic()"); return false; @@ -794,11 +897,11 @@ static void printStmt(Parser* parser, Node** nodeHandle) { static void assertStmt(Parser* parser, Node** nodeHandle) { //set the node info (*nodeHandle)->type = NODE_BINARY; - (*nodeHandle)->unary.opcode = OP_ASSERT; + (*nodeHandle)->binary.opcode = OP_ASSERT; - parsePrecedence(parser, &((*nodeHandle)->binary.left), PREC_PRIMARY); + parsePrecedence(parser, &((*nodeHandle)->binary.left), PREC_TERNARY); consume(parser, TOKEN_COMMA, "Expected ',' in assert statement"); - parsePrecedence(parser, &((*nodeHandle)->binary.right), PREC_PRIMARY); + parsePrecedence(parser, &((*nodeHandle)->binary.right), PREC_TERNARY); consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of assert statement"); }