Comparions and the ! operator work

This commit is contained in:
2022-08-20 06:20:20 +01:00
parent 8eca419138
commit 2bf721867b
6 changed files with 336 additions and 51 deletions

28
scripts/comparisons.toy Normal file
View File

@@ -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";

View File

@@ -180,11 +180,14 @@ static bool execNegate(Interpreter* interpreter) {
//negate the top literal on the stack //negate the top literal on the stack
Literal lit = popLiteralArray(&interpreter->stack); Literal lit = popLiteralArray(&interpreter->stack);
if (parseIdentifierToValue(interpreter, &lit)) { if (!parseIdentifierToValue(interpreter, &lit)) {
return false; 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)); lit = TO_INTEGER_LITERAL(-AS_INTEGER(lit));
} }
else if (IS_FLOAT(lit)) { else if (IS_FLOAT(lit)) {
@@ -440,6 +443,114 @@ static bool execValCast(Interpreter* interpreter) {
return true; 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 //the heart of toy
static void execInterpreter(Interpreter* interpreter) { static void execInterpreter(Interpreter* interpreter) {
unsigned char opcode = readByte(interpreter->bytecode, &interpreter->count); unsigned char opcode = readByte(interpreter->bytecode, &interpreter->count);
@@ -518,6 +629,42 @@ static void execInterpreter(Interpreter* interpreter) {
} }
break; 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: default:
printf("Unknown opcode found %d, terminating\n", opcode); printf("Unknown opcode found %d, terminating\n", opcode);
printLiteralArray(&interpreter->stack, "\n"); printLiteralArray(&interpreter->stack, "\n");

View File

@@ -121,8 +121,8 @@ static Token makeToken(Lexer* lexer, TokenType type) {
Token token; Token token;
token.type = type; token.type = type;
token.lexeme = &lexer->source[lexer->current - 1]; token.length = lexer->current - lexer->start;
token.length = 1; token.lexeme = &lexer->source[lexer->current - token.length];
token.line = lexer->line; token.line = lexer->line;
//BUG #10: this shows TOKEN_EOF twice due to the overarching structure of the program - can't be fixed //BUG #10: this shows TOKEN_EOF twice due to the overarching structure of the program - can't be fixed

View File

@@ -101,4 +101,3 @@ void emitNodeVarTypes(Node** nodeHandle, Literal literal);
void emitNodeVarDecl(Node** nodeHandle, Literal identifier, Literal type, Node* expression); void emitNodeVarDecl(Node** nodeHandle, Literal identifier, Literal type, Node* expression);
void printNode(Node* node); void printNode(Node* node);

View File

@@ -35,6 +35,14 @@ typedef enum Opcode {
OP_TYPE_CAST, //temporarily change a type of an atomic value 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 //meta
OP_SECTION_END, OP_SECTION_END,
//TODO: add more //TODO: add more

View File

@@ -97,7 +97,7 @@ typedef enum {
PREC_TERNARY, PREC_TERNARY,
PREC_OR, PREC_OR,
PREC_AND, PREC_AND,
PREC_EQUALITY, // PREC_EQUALITY,
PREC_COMPARISON, PREC_COMPARISON,
PREC_TERM, PREC_TERM,
PREC_FACTOR, 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 //binary() is an infix rule - so only get the RHS of the operator
switch(parser->previous.type) { switch(parser->previous.type) {
//arithmetic
case TOKEN_PLUS: { case TOKEN_PLUS: {
parsePrecedence(parser, nodeHandle, PREC_TERM); parsePrecedence(parser, nodeHandle, PREC_TERM);
return OP_ADDITION; return OP_ADDITION;
@@ -309,11 +310,43 @@ static Opcode binary(Parser* parser, Node** nodeHandle) {
return OP_MODULO; return OP_MODULO;
} }
//assignment
case TOKEN_ASSIGN: { case TOKEN_ASSIGN: {
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT); parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
return OP_VAR_ASSIGN; 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: default:
error(parser, parser->previous, "Unexpected token passed to binary precedence rule"); error(parser, parser->previous, "Unexpected token passed to binary precedence rule");
return OP_EOF; return OP_EOF;
@@ -321,47 +354,63 @@ static Opcode binary(Parser* parser, Node** nodeHandle) {
} }
static Opcode unary(Parser* parser, Node** nodeHandle) { static Opcode unary(Parser* parser, Node** nodeHandle) {
switch(parser->previous.type) { Node* tmpNode = NULL;
case TOKEN_MINUS: {
//temp handle to potentially negate values
Node* tmpNode = NULL;
parsePrecedence(parser, &tmpNode, PREC_TERNARY); //can be a literal
//check for negative literals (optimisation) if (parser->previous.type == TOKEN_MINUS) {
if (tmpNode->type == NODE_LITERAL) { //temp handle to potentially negate values
//negate directly, if int or float parsePrecedence(parser, &tmpNode, PREC_TERNARY); //can be a literal
Literal lit = tmpNode->atomic.literal;
if (IS_INTEGER(lit)) { //check for negative literals (optimisation)
lit = TO_INTEGER_LITERAL(-AS_INTEGER(lit)); if (tmpNode->type == NODE_LITERAL) {
} //negate directly, if int or float
Literal lit = tmpNode->atomic.literal;
if (IS_FLOAT(lit)) { if (IS_INTEGER(lit)) {
lit = TO_FLOAT_LITERAL(-AS_FLOAT(lit)); lit = TO_INTEGER_LITERAL(-AS_INTEGER(lit));
}
tmpNode->atomic.literal = lit;
*nodeHandle = tmpNode;
return OP_EOF;
} }
//process the literal without optimizations if (IS_FLOAT(lit)) {
if (tmpNode->type == NODE_LITERAL) { lit = TO_FLOAT_LITERAL(-AS_FLOAT(lit));
emitNodeUnary(nodeHandle, OP_NEGATE);
nodeHandle = &((*nodeHandle)->unary.child); //re-align after append
(*nodeHandle) = tmpNode; //set negate's child to the literal
return OP_EOF;
} }
error(parser, parser->previous, "Unexpected token passed to unary minus precedence rule"); tmpNode->atomic.literal = lit;
*nodeHandle = tmpNode;
return OP_EOF; 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) { static Opcode atomic(Parser* parser, Node** nodeHandle) {
@@ -522,9 +571,9 @@ ParseRule parseRules[] = { //must match the token types
//math operators //math operators
{NULL, binary, PREC_TERM},// TOKEN_PLUS, {NULL, binary, PREC_TERM},// TOKEN_PLUS,
{unary, binary, PREC_TERM},// TOKEN_MINUS, {unary, binary, PREC_TERM},// TOKEN_MINUS,
{NULL, binary, PREC_TERM},// TOKEN_MULTIPLY, {NULL, binary, PREC_FACTOR},// TOKEN_MULTIPLY,
{NULL, binary, PREC_TERM},// TOKEN_DIVIDE, {NULL, binary, PREC_FACTOR},// TOKEN_DIVIDE,
{NULL, binary, PREC_TERM},// TOKEN_MODULO, {NULL, binary, PREC_FACTOR},// TOKEN_MODULO,
{NULL, NULL, PREC_NONE},// TOKEN_PLUS_ASSIGN, {NULL, NULL, PREC_NONE},// TOKEN_PLUS_ASSIGN,
{NULL, NULL, PREC_NONE},// TOKEN_MINUS_ASSIGN, {NULL, NULL, PREC_NONE},// TOKEN_MINUS_ASSIGN,
{NULL, NULL, PREC_NONE},// TOKEN_MULTIPLY_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_BRACKET_RIGHT,
{NULL, NULL, PREC_NONE},// TOKEN_BRACE_LEFT, {NULL, NULL, PREC_NONE},// TOKEN_BRACE_LEFT,
{NULL, NULL, PREC_NONE},// TOKEN_BRACE_RIGHT, {NULL, NULL, PREC_NONE},// TOKEN_BRACE_RIGHT,
{NULL, NULL, PREC_NONE},// TOKEN_NOT, {unary, NULL, PREC_CALL},// TOKEN_NOT,
{NULL, NULL, PREC_NONE},// TOKEN_NOT_EQUAL, {NULL, binary, PREC_COMPARISON},// TOKEN_NOT_EQUAL,
{NULL, NULL, PREC_NONE},// TOKEN_EQUAL, {NULL, binary, PREC_COMPARISON},// TOKEN_EQUAL,
{NULL, NULL, PREC_NONE},// TOKEN_LESS, {NULL, binary, PREC_COMPARISON},// TOKEN_LESS,
{NULL, NULL, PREC_NONE},// TOKEN_GREATER, {NULL, binary, PREC_COMPARISON},// TOKEN_GREATER,
{NULL, NULL, PREC_NONE},// TOKEN_LESS_EQUAL, {NULL, binary, PREC_COMPARISON},// TOKEN_LESS_EQUAL,
{NULL, NULL, PREC_NONE},// TOKEN_GREATER_EQUAL, {NULL, binary, PREC_COMPARISON},// TOKEN_GREATER_EQUAL,
{NULL, NULL, PREC_NONE},// TOKEN_AND, {NULL, NULL, PREC_NONE},// TOKEN_AND,
{NULL, NULL, PREC_NONE},// TOKEN_OR, {NULL, NULL, PREC_NONE},// TOKEN_OR,
//other operators //other operators
{NULL, NULL, PREC_NONE},// TOKEN_COLON, {NULL, NULL, PREC_NONE},// TOKEN_COLON,
{NULL, NULL, PREC_NONE},// TOKEN_SEMICOLON, {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_DOT,
{NULL, NULL, PREC_NONE},// TOKEN_PIPE, {NULL, NULL, PREC_NONE},// TOKEN_PIPE,
{NULL, NULL, PREC_NONE},// TOKEN_REST, {NULL, NULL, PREC_NONE},// TOKEN_REST,
@@ -577,6 +626,12 @@ static bool calcStaticBinaryArithmetic(Parser* parser, Node** nodeHandle) {
case OP_MULTIPLICATION: case OP_MULTIPLICATION:
case OP_DIVISION: case OP_DIVISION:
case OP_MODULO: 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; break;
default: default:
@@ -642,6 +697,30 @@ static bool calcStaticBinaryArithmetic(Parser* parser, Node** nodeHandle) {
result = TO_INTEGER_LITERAL( AS_INTEGER(lhs) % AS_INTEGER(rhs) ); result = TO_INTEGER_LITERAL( AS_INTEGER(lhs) % AS_INTEGER(rhs) );
break; 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: default:
printf("[internal] bad opcode argument passed to calcStaticBinaryArithmetic()"); printf("[internal] bad opcode argument passed to calcStaticBinaryArithmetic()");
return false; return false;
@@ -676,6 +755,30 @@ static bool calcStaticBinaryArithmetic(Parser* parser, Node** nodeHandle) {
result = TO_FLOAT_LITERAL( AS_FLOAT(lhs) / AS_FLOAT(rhs) ); result = TO_FLOAT_LITERAL( AS_FLOAT(lhs) / AS_FLOAT(rhs) );
break; 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: default:
printf("[internal] bad opcode argument passed to calcStaticBinaryArithmetic()"); printf("[internal] bad opcode argument passed to calcStaticBinaryArithmetic()");
return false; return false;
@@ -794,11 +897,11 @@ static void printStmt(Parser* parser, Node** nodeHandle) {
static void assertStmt(Parser* parser, Node** nodeHandle) { static void assertStmt(Parser* parser, Node** nodeHandle) {
//set the node info //set the node info
(*nodeHandle)->type = NODE_BINARY; (*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"); 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"); consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of assert statement");
} }