#include #include #include #include #include "Compiler.h" #include "DataType.h" #include "tokenizer/Tokenizer.h" #include "utils/FunctionMap.h" #include "utils/Variables.h" #include "vm/Operation.h" #define ERROR_LENGTH 256 //#define RETURN_BUFFER 16 //#define BREAK_BUFFER 32 static jmp_buf errorJump; static char error[ERROR_LENGTH] = {'\0'}; static ByteCode* code; static int16 line = 1; static Variables vars; // static FunctionMap functions; /*static int returns[RETURN_BUFFER]; static int returnIndex = 0; static int returnState = 0; static int breaks[BREAK_BUFFER]; static int breakIndex = 0; static int forWhileStack = 0; static int continueAt = 0;*/ typedef struct { Operation intOp; Operation floatOp; Operation boolOp; const char* name; } TypedOp; static const TypedOp TYPED_MUL = {OP_MUL_INT, OP_MUL_FLOAT, OP_NOTHING, "*"}; static const TypedOp TYPED_DIV = {OP_DIV_INT, OP_DIV_FLOAT, OP_NOTHING, "/"}; static const TypedOp TYPED_MOD = {OP_MOD_INT, OP_NOTHING, OP_NOTHING, "%"}; static const TypedOp TYPED_ADD = {OP_ADD_INT, OP_ADD_FLOAT, OP_NOTHING, "+"}; static const TypedOp TYPED_SUB = {OP_SUB_INT, OP_SUB_FLOAT, OP_NOTHING, "-"}; static const TypedOp TYPED_LESS = {OP_LESS_INT, OP_LESS_FLOAT, OP_NOTHING, "<"}; static const TypedOp TYPED_LESS_EQUAL = {OP_GREATER_INT, OP_GREATER_FLOAT, OP_NOTHING, "<="}; static const TypedOp TYPED_GREATER = {OP_GREATER_INT, OP_GREATER_FLOAT, OP_NOTHING, ">"}; static const TypedOp TYPED_GREATER_EQUAL = {OP_LESS_INT, OP_LESS_FLOAT, OP_NOTHING, ">="}; static const TypedOp TYPED_EQUAL = {OP_EQUAL_INT, OP_EQUAL_FLOAT, OP_EQUAL_BOOL, "=="}; static const TypedOp TYPED_NOT_EQUAL = {OP_EQUAL_INT, OP_EQUAL_FLOAT, OP_EQUAL_BOOL, "!="}; static void cError(const char* format, ...) { va_list args; va_start(args, format); vsnprintf(error, ERROR_LENGTH, format, args); va_end(args); longjmp(errorJump, 0); } static void cInvalidOperation(DataType a, DataType b, const char* op) { cError("invalid operation: %s %s %s", dtGetName(a), op, dtGetName(b)); } static void cNotDeclared(const char* name) { cError("variable %s has not been declared", name); } static void cNotInitialized(const char* name) { cError("variable %s has not been initialized", name); } /*static int cAddVar(const char* var) { int index = vars[varIndex].entries; simAdd(vars + varIndex, var, &index); return index; }*/ static void cUnexpectedToken(Token t) { cError("unexpected token on line %d: %s", line, tGetName(t)); } static void cAddOperation(Operation token) { unsigned char c = token; bcAddBytes(code, &c, 1); } /*static int cReserveInt() { return bcReserveBytes(code, sizeof(int)); } static void cSetInt(int p, int i) { bcSetBytes(code, p, &i, sizeof(int)); }*/ static void cAddInt(int i) { bcAddBytes(code, &i, sizeof(int)); } static void cAddInt16(int16 i) { bcAddBytes(code, &i, sizeof(int16)); } /*static int cAddPush(int offset) { cAddOperation(OP_PUSH_VARS); int p = cReserveInt(); cAddInt(offset); return p; } static void cAddPop(int p, int vars) { cAddOperation(OP_POP_VARS); cAddInt(vars); cSetInt(p, vars); }*/ static Token cReadTokenAndLine() { Token t = tReadToken(); if(tReadInt16(&line)) { return t; } return T_END; } static void cConsumeToken(Token wanted) { Token t = cReadTokenAndLine(); if(wanted != t) { cError("unexpected token on line %d: expected '%s' got '%s'", line, tGetName(wanted), tGetName(t)); } } static bool cConsumeTokenIf(Token t) { if(tPeekToken() == t) { cReadTokenAndLine(); return true; } return false; } static void cConstantInt() { int value; if(!tReadInt(&value)) { cError("int token without an int on line %d", line); } cAddOperation(OP_PUSH_INT); cAddInt(value); } static void cConstantFloat() { float value; if(!tReadFloat(&value)) { cError("float token without a float on line %d", line); } cAddOperation(OP_PUSH_FLOAT); bcAddBytes(code, &value, sizeof(float)); } /*static void cConstantString() { int length; const char* s = tReadString(&length); if(s == NULL) { cError("text without string on line %d", line); } cAddOperation(OP_PUSH_CONST_STRING); cAddInt(length); bcAddBytes(code, s, length); }*/ static const char* cReadString() { int length; const char* literal = tReadString(&length); if(literal == NULL) { cError("literal without string on line %d", line); } return literal; } static DataType cExpression(); /*static int cCallFunctionArguments() { int arguments = 0; while(!cConsumeTokenIf(T_CLOSE_BRACKET)) { arguments++; cExpression(); if(cConsumeTokenIf(T_COMMA) && tPeekToken() == T_CLOSE_BRACKET) { cUnexpectedToken(tPeekToken()); } } return arguments; } static void cCallFunction(const char* literal, bool noReturn) { cAddOperation(OP_PUSH_INT); cAddInt(0); int arguments = cCallFunctionArguments(); Function* f = fmSearch(&functions, literal, arguments); cAddOperation(OP_GOSUB); if(f == NULL) { fmEnqueue(&functions, literal, arguments, line, cReserveInt(), noReturn); cAddInt(arguments); cAddOperation(OP_NOTHING); } else { if(!noReturn && !f->returns) { cError("function '%s' needs a return value on line %d", f->name, line); } cAddInt(f->address); cAddInt(arguments); if(f->returns && noReturn) { cAddOperation(OP_POP); } } }*/ static void cAddReference(int address) { // if(cConsumeTokenIf(T_OPEN_SQUARE_BRACKET)) { // cExpression(); // cAddOperation(OP_REFERENCE_FROM_ARRAY); // cAddInt(cAddVar(var)); // cConsumeToken(T_CLOSE_SQUARE_BRACKET); //} else { cAddOperation(OP_REFERENCE_FROM_VAR); cAddInt(address); //} } static DataType cLiteral() { const char* literal = cReadString(); /*if(cConsumeTokenIf(T_OPEN_BRACKET)) { cCallFunction(literal, false); return; }*/ Variable* v = vSearch(&vars, literal); if(v == NULL) { cNotDeclared(literal); } else if(!v->initialized) { cNotInitialized(literal); } cAddReference(v->address); cAddOperation(OP_DEREFERENCE_INT); return v->type; /*if(cConsumeTokenIf(T_INCREMENT)) { cAddOperation(OP_POST_INCREMENT); } else if(cConsumeTokenIf(T_DECREMENT)) { cAddOperation(OP_POST_DECREMENT); } else if(cConsumeTokenIf(T_POINT)) { cConsumeToken(T_LITERAL); const char* access = cReadString(); if(strcmp(access, "length") == 0) { cAddOperation(OP_ARRAY_LENGTH); } else { cError("'%s' not supported after . on line %d", access, line); } } else { cAddOperation(OP_DEREFERENCE); }*/ } /*static void cArray() { cConsumeToken(T_OPEN_SQUARE_BRACKET); cExpression(); cConsumeToken(T_CLOSE_SQUARE_BRACKET); cAddOperation(OP_ALLOCATE_ARRAY); }*/ static DataType cBracketPrimary() { DataType result = cExpression(); cConsumeToken(T_CLOSE_BRACKET); return result; } static DataType cPrimary() { Token t = cReadTokenAndLine(); switch(t) { case T_CONST_INT: cConstantInt(); return DT_INT; case T_FLOAT: cConstantFloat(); return DT_FLOAT; // case T_TEXT: cConstantString(); break; // case T_NULL: cAddOperation(OP_PUSH_NULL); break; case T_TRUE: cAddOperation(OP_PUSH_TRUE); return DT_BOOL; case T_FALSE: cAddOperation(OP_PUSH_FALSE); return DT_BOOL; case T_OPEN_BRACKET: return cBracketPrimary(); case T_LITERAL: return cLiteral(); // case T_ARRAY: cArray(); break; default: cUnexpectedToken(t); return DT_VOID; } } /*static void cPreChange(Operation op) { cConsumeToken(T_LITERAL); cAddReference(cReadString()); cAddOperation(op); }*/ static DataType cPreUnary() { if(cConsumeTokenIf(T_SUB)) { DataType result = cPrimary(); switch(result) { case DT_INT: cAddOperation(OP_INVERT_SIGN_INT); break; case DT_FLOAT: cAddOperation(OP_INVERT_SIGN_FLOAT); break; default: cError("cannot invert sign of %s", dtGetName(result)); } return result; } /*else if(cConsumeTokenIf(T_INCREMENT)) { cPreChange(OP_PRE_INCREMENT); } else if(cConsumeTokenIf(T_DECREMENT)) { cPreChange(OP_PRE_DECREMENT); } else if(cConsumeTokenIf(T_NOT)) { int counter = 1; while(cConsumeTokenIf(T_NOT)) { counter++; } cPrimary(); cAddOperation(OP_NOT); if((counter & 1) == 0) { cAddOperation(OP_NOT); } } else if(cConsumeTokenIf(T_BIT_NOT)) { cPrimary(); cAddOperation(OP_BIT_NOT); } else {*/ return cPrimary(); //} } static void cAddTypeOperation(DataType a, DataType b, const TypedOp* op) { if(a == DT_INT && b == DT_INT && op->intOp != OP_NOTHING) { cAddOperation(op->intOp); } else if(a == DT_FLOAT && b == DT_FLOAT && op->floatOp != OP_NOTHING) { cAddOperation(op->floatOp); } else if(a == DT_BOOL && b == DT_BOOL && op->boolOp != OP_NOTHING) { cAddOperation(op->boolOp); } else { cInvalidOperation(a, b, op->name); } } static DataType cMul() { DataType a = cPreUnary(); while(true) { if(cConsumeTokenIf(T_MUL)) { cAddTypeOperation(a, cPreUnary(), &TYPED_MUL); } else if(cConsumeTokenIf(T_DIV)) { cAddTypeOperation(a, cPreUnary(), &TYPED_DIV); } else if(cConsumeTokenIf(T_MOD)) { cAddTypeOperation(a, cPreUnary(), &TYPED_MOD); } else { break; } } return a; } static DataType cAdd() { DataType a = cMul(); while(true) { if(cConsumeTokenIf(T_ADD)) { cAddTypeOperation(a, cMul(), &TYPED_ADD); } else if(cConsumeTokenIf(T_SUB)) { cAddTypeOperation(a, cMul(), &TYPED_SUB); } else { break; } } return a; } static DataType cShift() { return cAdd(); /*while(true) { if(cConsumeTokenIf(T_LEFT_SHIFT)) { cAdd(); cAddOperation(OP_LEFT_SHIFT); } else if(cConsumeTokenIf(T_RIGHT_SHIFT)) { cAdd(); cAddOperation(OP_RIGHT_SHIFT); } else { break; } }*/ } static DataType cComparison() { DataType a = cShift(); while(true) { if(cConsumeTokenIf(T_LESS)) { cAddTypeOperation(a, cShift(), &TYPED_LESS); a = DT_BOOL; } else if(cConsumeTokenIf(T_LESS_EQUAL)) { cAddTypeOperation(a, cShift(), &TYPED_LESS_EQUAL); cAddOperation(OP_NOT); a = DT_BOOL; } else if(cConsumeTokenIf(T_GREATER)) { cAddTypeOperation(a, cShift(), &TYPED_GREATER); a = DT_BOOL; } else if(cConsumeTokenIf(T_GREATER_EQUAL)) { cAddTypeOperation(a, cShift(), &TYPED_GREATER_EQUAL); cAddOperation(OP_NOT); a = DT_BOOL; } else { break; } } return a; } static DataType cEqual() { DataType a = cComparison(); while(true) { if(cConsumeTokenIf(T_EQUAL)) { cAddTypeOperation(a, cComparison(), &TYPED_EQUAL); a = DT_BOOL; } else if(cConsumeTokenIf(T_NOT_EQUAL)) { cAddTypeOperation(a, cComparison(), &TYPED_NOT_EQUAL); cAddOperation(OP_NOT); a = DT_BOOL; } else { break; } } return a; } static DataType cBitAnd() { return cEqual(); /*while(cConsumeTokenIf(T_BIT_AND)) { cEqual(); cAddOperation(OP_BIT_AND); }*/ } static DataType cBitXor() { return cBitAnd(); /*while(cConsumeTokenIf(T_BIT_XOR)) { cBitAnd(); cAddOperation(OP_BIT_XOR); }*/ } static DataType cBitOr() { return cBitXor(); /*while(cConsumeTokenIf(T_BIT_OR)) { cBitXor(); cAddOperation(OP_BIT_OR); }*/ } static DataType cAnd() { return cBitOr(); /*while(cConsumeTokenIf(T_AND)) { cAddOperation(OP_DUPLICATE); cAddOperation(OP_IF_GOTO); int p = cReserveInt(); cBitOr(); cAddOperation(OP_AND); cSetInt(p, code->length); }*/ } static DataType cOr() { return cAnd(); /*while(cConsumeTokenIf(T_OR)) { cAddOperation(OP_DUPLICATE); cAddOperation(OP_NOT); cAddOperation(OP_IF_GOTO); int p = cReserveInt(); cAnd(); cAddOperation(OP_OR); cSetInt(p, code->length); }*/ } static DataType cExpression() { return cOr(); } /*static void cOperationSet(Operation op) { cAddOperation(OP_DUPLICATE); cAddOperation(OP_DEREFERENCE); cExpression(); cAddOperation(op); cAddOperation(OP_SET); }*/ static void cLineLiteral() { const char* literal = cReadString(); /*if(cConsumeTokenIf(T_OPEN_BRACKET)) { cCallFunction(literal, true); return; }*/ Variable* v = vSearch(&vars, literal); if(v == NULL) { cNotDeclared(literal); } cAddReference(v->address); cConsumeToken(T_SET); DataType dt = cExpression(); if(v->type != dt) { cInvalidOperation(v->type, dt, "="); } if(dt == DT_INT) { cAddOperation(OP_SET_INT); } else { cError("%s cannot be set", dtGetName(dt)); } v->initialized = true; /*Token t = cReadTokenAndLine(); switch(t) { case T_SET: cExpression(); cAddOperation(OP_SET); break; case T_ADD_SET: cOperationSet(OP_ADD); break; case T_SUB_SET: cOperationSet(OP_SUB); break; case T_MUL_SET: cOperationSet(OP_MUL); break; case T_DIV_SET: cOperationSet(OP_DIV); break; case T_MOD_SET: cOperationSet(OP_MOD); break; case T_BIT_AND_SET: cOperationSet(OP_BIT_AND); break; case T_BIT_OR_SET: cOperationSet(OP_BIT_OR); break; case T_BIT_XOR_SET: cOperationSet(OP_BIT_XOR); break; case T_LEFT_SHIFT_SET: cOperationSet(OP_LEFT_SHIFT); break; case T_RIGHT_SHIFT_SET: cOperationSet(OP_RIGHT_SHIFT); break; case T_INCREMENT: cAddOperation(OP_POST_INCREMENT); cAddOperation(OP_POP); break; case T_DECREMENT: cAddOperation(OP_POST_DECREMENT); cAddOperation(OP_POP); break; default: cUnexpectedToken(t); }*/ } /*static int cFunctionArguments() { int arguments = 0; while(!cConsumeTokenIf(T_CLOSE_BRACKET)) { cConsumeToken(T_LITERAL); arguments++; cAddVar(cReadString()); if(cConsumeTokenIf(T_COMMA) && tPeekToken() != T_LITERAL) { cUnexpectedToken(tPeekToken()); } } return arguments; } static void cLine(Token t); static void cConsumeBody() { cConsumeToken(T_OPEN_CURVED_BRACKET); int oldLine = line; while(!cConsumeTokenIf(T_CLOSE_CURVED_BRACKET)) { Token t = cReadTokenAndLine(); if(t == T_END) { cError( "unexpected end of file: non closed curved bracket on line %d", oldLine); } cLine(t); } } static void cLinkReturns() { for(int i = 0; i < returnIndex; i++) { cSetInt(returns[i], vars[1].entries); } returnIndex = 0; } static void cFunctionBody(const char* name, int arguments) { int oldLine = line; cAddOperation(OP_GOTO); int gotoIndex = cReserveInt(); int address = code->length; returnState = 0; int p = cAddPush(arguments); cConsumeBody(); cAddPop(p, vars[1].entries); cLinkReturns(); if(!fmAdd(&functions, name, arguments, address, returnState == 2)) { cError("function registered twice on line %d", oldLine); } cAddOperation(OP_RETURN); cSetInt(gotoIndex, code->length); } static void cFunction() { if(varIndex == 1) { cError("function inside function on line %d", line); } cConsumeToken(T_LITERAL); const char* name = cReadString(); cConsumeToken(T_OPEN_BRACKET); varIndex = 1; vars[1].entries = 0; cFunctionBody(name, cFunctionArguments()); varIndex = 0; } static void cAddReturn() { cAddOperation(OP_POP_VARS); returns[returnIndex++] = cReserveInt(vars); cAddOperation(OP_RETURN); } static void cReturn() { if(varIndex == 0) { cError("return without a function on line %d", line); } else if(returnIndex >= RETURN_BUFFER) { cError("too much returns in function around line %d", line); } if(cConsumeTokenIf(T_SEMICOLON)) { if(returnState == 2) { cError("mixed return type on line %d", line); } returnState = 1; cAddReturn(); } else { if(returnState == 1) { cError("mixed return type on line %d", line); } returnState = 2; cExpression(); cAddOperation(OP_SET_RETURN); cAddReturn(); cConsumeToken(T_SEMICOLON); } }*/ static void cPrint() { DataType dt = cExpression(); switch(dt) { case DT_INT: cAddOperation(OP_PRINT_INT); break; case DT_FLOAT: cAddOperation(OP_PRINT_FLOAT); break; case DT_BOOL: cAddOperation(OP_PRINT_BOOL); break; default: cError("cannot print data type %s", dtGetName(dt)); } cConsumeToken(T_SEMICOLON); } /*static void cIf() { cConsumeToken(T_OPEN_BRACKET); cExpression(); cConsumeToken(T_CLOSE_BRACKET); cAddOperation(OP_IF_GOTO); int ifP = cReserveInt(); cConsumeBody(); cSetInt(ifP, code->length); if(cConsumeTokenIf(T_ELSE)) { cAddOperation(OP_GOTO); int elseP = cReserveInt(); cSetInt(ifP, code->length); if(cConsumeTokenIf(T_IF)) { cIf(); } else { cConsumeBody(); } cSetInt(elseP, code->length); } } static void cConsumeBreaks(int start, int address) { for(int i = start; i < breakIndex; i++) { cSetInt(breaks[i], address); } breakIndex = start; } static void cWhile() { int start = code->length; cConsumeToken(T_OPEN_BRACKET); cExpression(); cConsumeToken(T_CLOSE_BRACKET); cAddOperation(OP_IF_GOTO); int ifP = cReserveInt(); int breakStart = breakIndex; forWhileStack++; int oldContinue = continueAt; continueAt = start; cConsumeBody(); continueAt = oldContinue; forWhileStack--; cAddOperation(OP_GOTO); cAddInt(start); cSetInt(ifP, code->length); cConsumeBreaks(breakStart, code->length); }*/ static void cInt() { cConsumeToken(T_LITERAL); const char* var = cReadString(); Variable* v = vSearch(&vars, var); if(v != NULL) { cError("%s has already been declared", var); } v = vAdd(&vars, var, DT_INT); if(tPeekToken() == T_SEMICOLON) { return; } cConsumeToken(T_SET); cAddReference(v->address); DataType dt = cExpression(); if(dt != DT_INT) { cInvalidOperation(DT_INT, dt, "="); } cAddOperation(OP_SET_INT); v->initialized = true; } static void cLineExpression(Token t) { switch(t) { case T_LITERAL: cLineLiteral(); break; case T_INT: cInt(); break; /*case T_INCREMENT: cPreChange(OP_PRE_INCREMENT); cAddOperation(OP_POP); break; case T_DECREMENT: cPreChange(OP_PRE_DECREMENT); cAddOperation(OP_POP); break;*/ default: cUnexpectedToken(t); } } /*static void cFor() { cConsumeToken(T_OPEN_BRACKET); cLineExpression(cReadTokenAndLine()); cConsumeToken(T_SEMICOLON); int startCheck = code->length; cExpression(); cConsumeToken(T_SEMICOLON); cAddOperation(OP_IF_GOTO); int end = cReserveInt(); cAddOperation(OP_GOTO); int beginBody = cReserveInt(); int startPerLoop = code->length; cLineExpression(cReadTokenAndLine()); cAddOperation(OP_GOTO); cAddInt(startCheck); cConsumeToken(T_CLOSE_BRACKET); cSetInt(beginBody, code->length); int breakStart = breakIndex; forWhileStack++; int oldContinue = continueAt; continueAt = startPerLoop; cConsumeBody(); continueAt = oldContinue; forWhileStack--; cAddOperation(OP_GOTO); cAddInt(startPerLoop); cSetInt(end, code->length); cConsumeBreaks(breakStart, code->length); } static void cBreak() { if(forWhileStack == 0) { cError("break without for or while on line %d", line); } else if(breakIndex >= BREAK_BUFFER) { cError("too much breaks around line %d", line); } cAddOperation(OP_GOTO); breaks[breakIndex++] = cReserveInt(); cConsumeToken(T_SEMICOLON); } static void cContinue() { if(forWhileStack == 0) { cError("continue without for or while on line %d", line); } cAddOperation(OP_GOTO); cAddInt(continueAt); cConsumeToken(T_SEMICOLON); }*/ static void cLine(Token t) { cAddOperation(OP_LINE); cAddInt16(line); switch(t) { case T_PRINT: cPrint(); break; // case T_FUNCTION: cFunction(); break; // case T_RETURN: cReturn(); break; // case T_IF: cIf(); break; // case T_WHILE: cWhile(); break; // case T_FOR: cFor(); break; // case T_BREAK: cBreak(); break; // case T_CONTINUE: cContinue(); break; default: cLineExpression(t); cConsumeToken(T_SEMICOLON); } } static void cForEachLine() { Token t = cReadTokenAndLine(); while(t != T_END) { cLine(t); t = cReadTokenAndLine(); } } /*static void cLinkQueuedFunctions() { for(int i = 0; i < functions.queueEntries; i++) { Function* f = fmSearch(&functions, functions.queue[i].name, functions.queue[i].arguments); if(f == NULL) { cError("unknown function on line %d", functions.queue[i].line); } else if(!functions.queue[i].noReturn && !f->returns) { cError("function '%s' needs a return value on line %d", f->name, functions.queue[i].line); } cSetInt(functions.queue[i].reserved, f->address); if(functions.queue[i].noReturn && f->returns) { code->code[functions.queue[i].reserved + sizeof(int) * 2] = OP_POP; } } }*/ static void cAllocAndCompile() { // varIndex = 0; // returnIndex = 0; // returnState = 0; // forWhileStack = 0; // breakIndex = 0; vInit(&vars); // fmInit(&functions); if(!setjmp(errorJump)) { // int p = cAddPush(0); cForEachLine(); // cAddPop(p, vars[varIndex].entries); // cLinkQueuedFunctions(); } // fmDelete(&functions); vDelete(&vars); } ByteCode* cCompile() { error[0] = '\0'; code = bcInit(); cAllocAndCompile(); if(error[0] != '\0') { bcDelete(code); return NULL; } return code; } const char* cGetError() { return error; } int cGetLine() { return line; }