mirror of
https://github.com/krgamestudios/Toy.git
synced 2026-04-15 14:54:07 +10:00
Expression statements and assignemnts are working
This commit is contained in:
@@ -52,18 +52,20 @@ print [:];
|
||||
//test nested compounds
|
||||
print [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
|
||||
|
||||
//not ready yet
|
||||
//var declarations
|
||||
var x = 31;
|
||||
var y : int = 42;
|
||||
var arr : [int] = [1, 2, 3, 42];
|
||||
var dict : [string, int] = ["hello": 1, "world":2];
|
||||
|
||||
//print x;
|
||||
//print x + y;
|
||||
//print arr;
|
||||
//print dict;
|
||||
//printing expressions
|
||||
print x;
|
||||
print x + y;
|
||||
print arr;
|
||||
print dict;
|
||||
|
||||
//test asserts at the end of the file
|
||||
assert x, "This won't be seen";
|
||||
assert true, "This won't be seen";
|
||||
assert false, "This is a failed assert, and will end execution";
|
||||
|
||||
|
||||
@@ -110,13 +110,27 @@ static void consumeShort(unsigned short bytes, unsigned char* tb, int* count) {
|
||||
*count += 2;
|
||||
}
|
||||
|
||||
static Literal parseIdentifierToValue(Interpreter* interpreter, Literal literal) {
|
||||
//this converts identifiers to values
|
||||
if (IS_IDENTIFIER(literal)) {
|
||||
if (!getScopeVariable(interpreter->scope, literal, &literal)) {
|
||||
printf("Undeclared variable \"");;
|
||||
printLiteral(literal);
|
||||
printf("\"\n");
|
||||
return TO_NULL_LITERAL;
|
||||
}
|
||||
}
|
||||
|
||||
return literal;
|
||||
}
|
||||
|
||||
//each available statement
|
||||
static bool execAssert(Interpreter* interpreter) {
|
||||
Literal rhs = popLiteralArray(&interpreter->stack);
|
||||
Literal lhs = popLiteralArray(&interpreter->stack);
|
||||
Literal lhs = parseIdentifierToValue(interpreter, popLiteralArray(&interpreter->stack));
|
||||
|
||||
if (!IS_STRING(rhs)) {
|
||||
printf("[internal] The interpreter's assert keyword needs a string as the second argument, received: ");
|
||||
printf("The assert keyword needs a string as the second argument, received: ");
|
||||
printLiteral(rhs);
|
||||
printf("\n");
|
||||
return false;
|
||||
@@ -132,7 +146,7 @@ static bool execAssert(Interpreter* interpreter) {
|
||||
|
||||
static bool execPrint(Interpreter* interpreter) {
|
||||
//print what is on top of the stack, then pop it
|
||||
Literal lit = popLiteralArray(&interpreter->stack);
|
||||
Literal lit = parseIdentifierToValue(interpreter, popLiteralArray(&interpreter->stack));
|
||||
|
||||
printLiteralCustom(lit, interpreter->printOutput);
|
||||
|
||||
@@ -152,7 +166,7 @@ static bool execPushLiteral(Interpreter* interpreter, bool lng) {
|
||||
index = (int)readByte(interpreter->bytecode, &interpreter->count);
|
||||
}
|
||||
|
||||
//push from cache to stack
|
||||
//push from cache to stack (DO NOT account for identifiers - will do that later)
|
||||
pushLiteralArray(&interpreter->stack, interpreter->literalCache.literals[index]);
|
||||
|
||||
return true;
|
||||
@@ -160,7 +174,7 @@ static bool execPushLiteral(Interpreter* interpreter, bool lng) {
|
||||
|
||||
static bool execNegate(Interpreter* interpreter) {
|
||||
//negate the top literal on the stack
|
||||
Literal lit = popLiteralArray(&interpreter->stack);
|
||||
Literal lit = parseIdentifierToValue(interpreter, popLiteralArray(&interpreter->stack));
|
||||
|
||||
if (IS_INTEGER(lit)) {
|
||||
lit = TO_INTEGER_LITERAL(-AS_INTEGER(lit));
|
||||
@@ -180,8 +194,8 @@ static bool execNegate(Interpreter* interpreter) {
|
||||
}
|
||||
|
||||
static bool execArithmetic(Interpreter* interpreter, Opcode opcode) {
|
||||
Literal rhs = popLiteralArray(&interpreter->stack);
|
||||
Literal lhs = popLiteralArray(&interpreter->stack);
|
||||
Literal rhs = parseIdentifierToValue(interpreter, popLiteralArray(&interpreter->stack));
|
||||
Literal lhs = parseIdentifierToValue(interpreter, popLiteralArray(&interpreter->stack));
|
||||
|
||||
//type coersion
|
||||
if (IS_FLOAT(lhs) && IS_INTEGER(rhs)) {
|
||||
@@ -264,7 +278,11 @@ static bool execArithmetic(Interpreter* interpreter, Opcode opcode) {
|
||||
}
|
||||
|
||||
//wrong types
|
||||
printf("Bad arithmetic argument\n");
|
||||
printf("Bad arithmetic argument ");
|
||||
printLiteral(lhs);
|
||||
printf(" and ");
|
||||
printLiteral(rhs);
|
||||
printf("\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -286,19 +304,35 @@ static bool execVarDecl(Interpreter* interpreter, bool lng) {
|
||||
Literal type = interpreter->literalCache.literals[typeIndex];
|
||||
|
||||
if (!declareScopeVariable(interpreter->scope, identifier, type)) {
|
||||
printf("Can't redefine the variable \"");;
|
||||
printf("Can't redefine the variable \"");;
|
||||
printLiteral(identifier);
|
||||
printf("\"\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!setScopeVariable(interpreter->scope, identifier, popLiteralArray(&interpreter->stack) )) {
|
||||
if (!setScopeVariable(interpreter->scope, identifier, parseIdentifierToValue(interpreter, popLiteralArray(&interpreter->stack)) )) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool execVarAssign(Interpreter* interpreter) {
|
||||
Literal rhs = parseIdentifierToValue(interpreter, popLiteralArray(&interpreter->stack));
|
||||
Literal lhs = popLiteralArray(&interpreter->stack);
|
||||
|
||||
if (!IS_IDENTIFIER(lhs)) {
|
||||
printf("Can't assign to a non-variable \"");;
|
||||
printLiteral(lhs);
|
||||
printf("\"\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
setScopeVariable(interpreter->scope, lhs, rhs);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//the heart of toy
|
||||
static void execInterpreter(Interpreter* interpreter) {
|
||||
unsigned char opcode = readByte(interpreter->bytecode, &interpreter->count);
|
||||
@@ -365,6 +399,12 @@ static void execInterpreter(Interpreter* interpreter) {
|
||||
}
|
||||
break;
|
||||
|
||||
case OP_VAR_ASSIGN:
|
||||
if (!execVarAssign(interpreter)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("Unknown opcode found %d, terminating\n", opcode);
|
||||
printLiteralArray(&interpreter->stack, "\n");
|
||||
|
||||
@@ -14,7 +14,7 @@ typedef struct Interpreter {
|
||||
unsigned char* bytecode;
|
||||
int length;
|
||||
int count;
|
||||
LiteralArray literalCache; //read-only - built from the bytecode
|
||||
LiteralArray literalCache; //read-only - built from the bytecode, refreshed each time new bytecode is provided
|
||||
|
||||
//operation
|
||||
Scope* scope;
|
||||
|
||||
@@ -28,9 +28,10 @@ typedef enum Opcode {
|
||||
OP_TYPE_DECL, //declare a type to be used (as a literal)
|
||||
OP_TYPE_DECL_LONG, //declare a type to be used (as a long literal)
|
||||
|
||||
OP_VAR_DECL,
|
||||
OP_VAR_DECL_LONG,
|
||||
// OP_VAR_ASSIGN, //stack: literal name, literal value
|
||||
OP_VAR_DECL, //declare a variable to be used (as a literal)
|
||||
OP_VAR_DECL_LONG, //declare a variable to be used (as a long literal)
|
||||
|
||||
OP_VAR_ASSIGN, //assign to a literal
|
||||
|
||||
//meta
|
||||
OP_SECTION_END,
|
||||
|
||||
@@ -300,6 +300,11 @@ static Opcode binary(Parser* parser, Node** nodeHandle, bool canBeAssigned) {
|
||||
return OP_MODULO;
|
||||
}
|
||||
|
||||
case TOKEN_ASSIGN: {
|
||||
parsePrecedence(parser, nodeHandle, PREC_ASSIGNMENT);
|
||||
return OP_VAR_ASSIGN;
|
||||
}
|
||||
|
||||
default:
|
||||
error(parser, parser->previous, "Unexpected token passed to binary precedence rule");
|
||||
return OP_EOF;
|
||||
@@ -384,6 +389,17 @@ static Opcode atomic(Parser* parser, Node** nodeHandle, bool canBeAssigned) {
|
||||
}
|
||||
}
|
||||
|
||||
static Opcode identifier(Parser* parser, Node** nodeHandle, bool canBeAssigned) {
|
||||
//make a copy of the string
|
||||
Token identifierToken = parser->previous;
|
||||
char* cpy = copyString(identifierToken.lexeme, identifierToken.length);
|
||||
Literal identifier = _toIdentifierLiteral(cpy, strlen(cpy)); //BUGFIX: use this instead of the macro
|
||||
|
||||
emitNodeLiteral(nodeHandle, identifier);
|
||||
|
||||
return OP_EOF;
|
||||
}
|
||||
|
||||
ParseRule parseRules[] = { //must match the token types
|
||||
//types
|
||||
{atomic, NULL, PREC_PRIMARY},// TOKEN_NULL,
|
||||
@@ -419,7 +435,7 @@ ParseRule parseRules[] = { //must match the token types
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_WHILE,
|
||||
|
||||
//literal values
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_IDENTIFIER,
|
||||
{identifier, NULL, PREC_PRIMARY},// TOKEN_IDENTIFIER,
|
||||
{atomic, NULL, PREC_PRIMARY},// TOKEN_LITERAL_TRUE,
|
||||
{atomic, NULL, PREC_PRIMARY},// TOKEN_LITERAL_FALSE,
|
||||
{atomic, NULL, PREC_PRIMARY},// TOKEN_LITERAL_INTEGER,
|
||||
@@ -439,6 +455,7 @@ ParseRule parseRules[] = { //must match the token types
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_MODULO_ASSIGN,
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_PLUS_PLUS,
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_MINUS_MINUS,
|
||||
{NULL, binary, PREC_ASSIGNMENT},// TOKEN_ASSIGN,
|
||||
|
||||
//logical operators
|
||||
{grouping, NULL, PREC_CALL},// TOKEN_PAREN_LEFT,
|
||||
@@ -458,7 +475,6 @@ ParseRule parseRules[] = { //must match the token types
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_OR,
|
||||
|
||||
//other operators
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_ASSIGN,
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_COLON,
|
||||
{NULL, NULL, PREC_NONE},// TOKEN_SEMICOLON,
|
||||
{NULL, NULL, PREC_CALL},// TOKEN_COMMA,
|
||||
@@ -650,26 +666,26 @@ static void expression(Parser* parser, Node** nodeHandle) {
|
||||
}
|
||||
|
||||
//statements
|
||||
static void blockStmt(Parser* parser, Node* node) {
|
||||
static void blockStmt(Parser* parser, Node** nodeHandle) {
|
||||
//init
|
||||
node->type = NODE_BLOCK;
|
||||
node->block.nodes = NULL;
|
||||
node->block.capacity = 0;
|
||||
node->block.count = 0;
|
||||
(*nodeHandle)->type = NODE_BLOCK;
|
||||
(*nodeHandle)->block.nodes = NULL;
|
||||
(*nodeHandle)->block.capacity = 0;
|
||||
(*nodeHandle)->block.count = 0;
|
||||
|
||||
//sub-scope, compile it and push it up in a node
|
||||
while (!match(parser, TOKEN_BRACE_RIGHT)) {
|
||||
if (node->block.capacity < node->block.count + 1) {
|
||||
int oldCapacity = node->block.capacity;
|
||||
if ((*nodeHandle)->block.capacity < (*nodeHandle)->block.count + 1) {
|
||||
int oldCapacity = (*nodeHandle)->block.capacity;
|
||||
|
||||
node->block.capacity = GROW_CAPACITY(oldCapacity);
|
||||
node->block.nodes = GROW_ARRAY(Node, node->block.nodes, oldCapacity, node->block.capacity);
|
||||
(*nodeHandle)->block.capacity = GROW_CAPACITY(oldCapacity);
|
||||
(*nodeHandle)->block.nodes = GROW_ARRAY(Node, (*nodeHandle)->block.nodes, oldCapacity, (*nodeHandle)->block.capacity);
|
||||
}
|
||||
|
||||
//use the next node in sequence
|
||||
node->block.nodes[node->block.count].type = NODE_ERROR; //BUGFIX: so freeing won't break the damn thing
|
||||
(*nodeHandle)->block.nodes[(*nodeHandle)->block.count].type = NODE_ERROR; //BUGFIX: so freeing won't break the damn thing
|
||||
|
||||
Node* ptr = &(node->block.nodes[node->block.count++]);
|
||||
Node* ptr = &((*nodeHandle)->block.nodes[(*nodeHandle)->block.count++]);
|
||||
|
||||
//process the grammar rule for this line
|
||||
declaration(parser, &ptr);
|
||||
@@ -681,53 +697,54 @@ static void blockStmt(Parser* parser, Node* node) {
|
||||
}
|
||||
}
|
||||
|
||||
static void printStmt(Parser* parser, Node* node) {
|
||||
static void printStmt(Parser* parser, Node** nodeHandle) {
|
||||
//set the node info
|
||||
node->type = NODE_UNARY;
|
||||
node->unary.opcode = OP_PRINT;
|
||||
expression(parser, &(node->unary.child));
|
||||
(*nodeHandle)->type = NODE_UNARY;
|
||||
(*nodeHandle)->unary.opcode = OP_PRINT;
|
||||
expression(parser, &((*nodeHandle)->unary.child));
|
||||
|
||||
consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of print statement");
|
||||
}
|
||||
|
||||
static void assertStmt(Parser* parser, Node* node) {
|
||||
static void assertStmt(Parser* parser, Node** nodeHandle) {
|
||||
//set the node info
|
||||
node->type = NODE_BINARY;
|
||||
node->unary.opcode = OP_ASSERT;
|
||||
(*nodeHandle)->type = NODE_BINARY;
|
||||
(*nodeHandle)->unary.opcode = OP_ASSERT;
|
||||
|
||||
parsePrecedence(parser, &(node->binary.left), PREC_PRIMARY);
|
||||
parsePrecedence(parser, &((*nodeHandle)->binary.left), PREC_PRIMARY);
|
||||
consume(parser, TOKEN_COMMA, "Expected ',' in assert statement");
|
||||
parsePrecedence(parser, &(node->binary.right), PREC_PRIMARY);
|
||||
parsePrecedence(parser, &((*nodeHandle)->binary.right), PREC_PRIMARY);
|
||||
|
||||
consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of assert statement");
|
||||
}
|
||||
|
||||
//precedence functions
|
||||
static void expressionStmt(Parser* parser, Node* node) {
|
||||
error(parser, parser->previous, "Expression statements not yet implemented");
|
||||
static void expressionStmt(Parser* parser, Node** nodeHandle) {
|
||||
expression(parser, nodeHandle);
|
||||
consume(parser, TOKEN_SEMICOLON, "Expected ';' at the end of expression statement");
|
||||
}
|
||||
|
||||
static void statement(Parser* parser, Node* node) {
|
||||
static void statement(Parser* parser, Node** nodeHandle) {
|
||||
//block
|
||||
if (match(parser, TOKEN_BRACE_LEFT)) {
|
||||
blockStmt(parser, node);
|
||||
blockStmt(parser, nodeHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
//print
|
||||
if (match(parser, TOKEN_PRINT)) {
|
||||
printStmt(parser, node);
|
||||
printStmt(parser, nodeHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
//assert
|
||||
if (match(parser, TOKEN_ASSERT)) {
|
||||
assertStmt(parser, node);
|
||||
assertStmt(parser, nodeHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
//default
|
||||
expressionStmt(parser, node);
|
||||
expressionStmt(parser, nodeHandle);
|
||||
}
|
||||
|
||||
//declarations and definitions
|
||||
@@ -840,7 +857,7 @@ static void declaration(Parser* parser, Node** nodeHandle) { //assume nodeHandle
|
||||
varDecl(parser, nodeHandle);
|
||||
}
|
||||
else {
|
||||
statement(parser, *nodeHandle);
|
||||
statement(parser, nodeHandle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ unsigned char* compileString(char* source, size_t* size) {
|
||||
while(node != NULL) {
|
||||
//pack up and leave
|
||||
if (node->type == NODE_ERROR) {
|
||||
printf(ERROR "error node detected\n" RESET);
|
||||
freeNode(node);
|
||||
freeCompiler(&compiler);
|
||||
freeParser(&parser);
|
||||
@@ -163,6 +164,7 @@ void repl() {
|
||||
while(node != NULL) {
|
||||
//pack up and restart
|
||||
if (node->type == NODE_ERROR) {
|
||||
printf(ERROR "error node detected\n" RESET);
|
||||
error = true;
|
||||
freeNode(node);
|
||||
break;
|
||||
|
||||
@@ -55,6 +55,7 @@ typedef enum TokenType {
|
||||
TOKEN_MODULO_ASSIGN,
|
||||
TOKEN_PLUS_PLUS,
|
||||
TOKEN_MINUS_MINUS,
|
||||
TOKEN_ASSIGN,
|
||||
|
||||
//logical operators
|
||||
TOKEN_PAREN_LEFT,
|
||||
@@ -74,7 +75,6 @@ typedef enum TokenType {
|
||||
TOKEN_OR,
|
||||
|
||||
//other operators
|
||||
TOKEN_ASSIGN,
|
||||
TOKEN_COLON,
|
||||
TOKEN_SEMICOLON,
|
||||
TOKEN_COMMA,
|
||||
|
||||
Reference in New Issue
Block a user