#include #include #include #include #include "Compiler.h" #include "DataType.h" #include "tokenizer/Tokenizer.h" #include "utils/Functions.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 Functions functions; static Functions functionQueue; static int returns[RETURN_BUFFER]; static int returnIndex = 0; static bool hasReturn = false; static DataType returnType = DT_VOID; 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 const TypedOp TYPED_BIT_OR = {OP_BIT_OR, OP_NOTHING, OP_NOTHING, "|"}; static const TypedOp TYPED_BIT_XOR = {OP_BIT_XOR, OP_NOTHING, OP_NOTHING, "^"}; static const TypedOp TYPED_BIT_AND = {OP_BIT_AND, OP_NOTHING, OP_NOTHING, "&"}; static const TypedOp TYPED_LEFT_SHIFT = {OP_LEFT_SHIFT, OP_NOTHING, OP_NOTHING, "<<"}; static const TypedOp TYPED_RIGHT_SHIFT = {OP_RIGHT_SHIFT, OP_NOTHING, OP_NOTHING, ">>"}; 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 cDeclared(const char* name) { cError("%s has already been declared", name); } static void cTooMuchArguments() { cError("too much function arguments"); } 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 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 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 void cCallFunctionArguments(Function* f) { while(!cConsumeTokenIf(T_CLOSE_BRACKET)) { DataType dt = cExpression(); if(fAddArgument(f, dt)) { cTooMuchArguments(); } if(cConsumeTokenIf(T_COMMA) && tPeekToken() == T_CLOSE_BRACKET) { cUnexpectedToken(tPeekToken()); } } } static DataType cCallFunction(const char* name) { cAddOperation(OP_PUSH_INT); cAddInt(0); Function f; fInit(&f, name, line); cCallFunctionArguments(&f); cAddOperation(OP_GOSUB); Function* found = fsSearch(&functions, &f); if(found == NULL) { cError("unknown function"); } if(found->address == -1) { f.returnType = found->returnType; f.address = cReserveInt(); fsAdd(&functionQueue, &f); } else { cAddInt(found->address); } cAddInt(found->size); return found->returnType; } static DataType cLoadVariable(Variable* v) { switch(v->type) { case DT_INT: cAddOperation(OP_LOAD_INT); break; case DT_BOOL: cAddOperation(OP_LOAD_BOOL); break; case DT_FLOAT: cAddOperation(OP_LOAD_FLOAT); break; default: cError("cannot load type %s", dtGetName(v->type)); } cAddInt(v->address); return v->type; } static void cStoreVariable(Variable* v, DataType dt, const char* name) { if(v->type != dt) { cInvalidOperation(v->type, dt, name); } switch(v->type) { case DT_INT: cAddOperation(OP_STORE_INT); break; case DT_BOOL: cAddOperation(OP_STORE_BOOL); break; case DT_FLOAT: cAddOperation(OP_STORE_FLOAT); break; default: if(dtIsArray(v->type)) { cAddOperation(OP_STORE_ARRAY); } else { cError("cannot store type %s", dtGetName(v->type)); } } cAddInt(v->address); } static DataType cPostChange(Variable* v, int change, const char* name) { if(v->type != DT_INT) { cError("%s needs an int", name); } cAddOperation(OP_LOAD_INT); cAddInt(v->address); cAddOperation(OP_LOAD_INT); cAddInt(v->address); cAddOperation(OP_PUSH_INT); cAddInt(change); cAddOperation(OP_ADD_INT); cAddOperation(OP_STORE_INT); cAddInt(v->address); return DT_INT; } static DataType cLiteral() { const char* literal = cReadString(); if(cConsumeTokenIf(T_OPEN_BRACKET)) { DataType dt = cCallFunction(literal); if(dt == DT_VOID) { cError("function returns void"); } return dt; } Variable* v = vSearch(&vars, literal); if(v == NULL) { cNotDeclared(literal); } if(cConsumeTokenIf(T_INCREMENT)) { return cPostChange(v, 1, "++"); } else if(cConsumeTokenIf(T_DECREMENT)) { return cPostChange(v, -1, "--"); } return cLoadVariable(v); } static DataType cBracketPrimary() { DataType result = cExpression(); cConsumeToken(T_CLOSE_BRACKET); return result; } static DataType cAllocArray(DataType dt, Operation op) { cConsumeToken(T_OPEN_SQUARE_BRACKET); DataType index = cExpression(); if(index != DT_INT) { cError("array size must be an int"); } cConsumeToken(T_CLOSE_SQUARE_BRACKET); cAddOperation(op); return dtArray(dt, 1); } static DataType cPrimary() { Token t = cReadTokenAndLine(); switch(t) { case T_CONST_INT: cConstantInt(); return DT_INT; case T_CONST_FLOAT: cConstantFloat(); return DT_FLOAT; 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_INT: return cAllocArray(DT_INT, OP_INT_ARRAY); default: cUnexpectedToken(t); return DT_VOID; } } static DataType cPreChange(int change, const char* name) { cConsumeToken(T_LITERAL); const char* literal = cReadString(); Variable* v = vSearch(&vars, literal); if(v == NULL) { cNotDeclared(literal); } else if(v->type != DT_INT) { cError("%s needs an int", name); } cAddOperation(OP_LOAD_INT); cAddInt(v->address); cAddOperation(OP_PUSH_INT); cAddInt(change); cAddOperation(OP_ADD_INT); cAddOperation(OP_STORE_INT); cAddInt(v->address); cAddOperation(OP_LOAD_INT); cAddInt(v->address); return DT_INT; } 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)) { return cPreChange(1, "++"); } else if(cConsumeTokenIf(T_DECREMENT)) { return cPreChange(-1, "--"); } else if(cConsumeTokenIf(T_NOT)) { int counter = 1; while(cConsumeTokenIf(T_NOT)) { counter++; } DataType result = cPrimary(); if(result != DT_BOOL) { cError("! needs a bool not %s", dtGetName(result)); } cAddOperation(OP_NOT); if((counter & 1) == 0) { cAddOperation(OP_NOT); } return DT_BOOL; } else if(cConsumeTokenIf(T_BIT_NOT)) { DataType result = cPrimary(); if(result == DT_INT) { cAddOperation(OP_BIT_NOT); } else { cError("~ needs an int not %s", dtGetName(result)); } return result; } 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() { DataType a = cAdd(); while(true) { if(cConsumeTokenIf(T_LEFT_SHIFT)) { cAddTypeOperation(a, cAdd(), &TYPED_LEFT_SHIFT); } else if(cConsumeTokenIf(T_RIGHT_SHIFT)) { cAddTypeOperation(a, cAdd(), &TYPED_RIGHT_SHIFT); } else { break; } } return a; } 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() { DataType a = cEqual(); while(cConsumeTokenIf(T_BIT_AND)) { DataType b = cEqual(); cAddTypeOperation(a, b, &TYPED_BIT_AND); } return a; } static DataType cBitXor() { DataType a = cBitAnd(); while(cConsumeTokenIf(T_BIT_XOR)) { DataType b = cBitAnd(); cAddTypeOperation(a, b, &TYPED_BIT_XOR); } return a; } static DataType cBitOr() { DataType a = cBitXor(); while(cConsumeTokenIf(T_BIT_OR)) { DataType b = cBitXor(); cAddTypeOperation(a, b, &TYPED_BIT_OR); } return a; } static DataType cAnd() { DataType a = cBitOr(); while(cConsumeTokenIf(T_AND)) { cAddOperation(OP_PEEK_FALSE_GOTO); int p = cReserveInt(); DataType b = cBitOr(); if(a != DT_BOOL || b != DT_BOOL) { cInvalidOperation(a, b, "&&"); } cAddOperation(OP_AND); cSetInt(p, code->length); } return a; } static DataType cOr() { DataType a = cAnd(); while(cConsumeTokenIf(T_OR)) { cAddOperation(OP_PEEK_TRUE_GOTO); int p = cReserveInt(); DataType b = cAnd(); if(a != DT_BOOL || b != DT_BOOL) { cInvalidOperation(a, b, "||"); } cAddOperation(OP_OR); cSetInt(p, code->length); } return a; } static DataType cExpression() { return cOr(); } static void cOperationSet(Variable* v, const TypedOp* op) { DataType a = cLoadVariable(v); DataType b = cExpression(); cAddTypeOperation(a, b, op); cStoreVariable(v, b, "="); } static void cAddPostLineChange(Variable* v, int change, const char* name) { if(v->type != DT_INT) { cError("%s needs an int", name); } cAddOperation(OP_LOAD_INT); cAddInt(v->address); cAddOperation(OP_PUSH_INT); cAddInt(change); cAddOperation(OP_ADD_INT); cAddOperation(OP_STORE_INT); cAddInt(v->address); } static void cLineLiteral() { const char* literal = cReadString(); if(cConsumeTokenIf(T_OPEN_BRACKET)) { DataType dt = cCallFunction(literal); if(dt != DT_VOID) { cError("function returns %s not void", dtGetName(dt)); } return; } Variable* v = vSearch(&vars, literal); if(v == NULL) { cNotDeclared(literal); } Token t = cReadTokenAndLine(); switch(t) { case T_SET: cStoreVariable(v, cExpression(), "="); break; case T_ADD_SET: cOperationSet(v, &TYPED_ADD); break; case T_SUB_SET: cOperationSet(v, &TYPED_SUB); break; case T_MUL_SET: cOperationSet(v, &TYPED_MUL); break; case T_DIV_SET: cOperationSet(v, &TYPED_DIV); break; case T_MOD_SET: cOperationSet(v, &TYPED_MOD); break; case T_BIT_AND_SET: cOperationSet(v, &TYPED_BIT_AND); break; case T_BIT_OR_SET: cOperationSet(v, &TYPED_BIT_OR); break; case T_BIT_XOR_SET: cOperationSet(v, &TYPED_BIT_XOR); break; case T_LEFT_SHIFT_SET: cOperationSet(v, &TYPED_LEFT_SHIFT); break; case T_RIGHT_SHIFT_SET: cOperationSet(v, &TYPED_RIGHT_SHIFT); break; case T_INCREMENT: cAddPostLineChange(v, 1, "++"); break; case T_DECREMENT: cAddPostLineChange(v, -1, "--"); break; default: cUnexpectedToken(t); } } static void cLine(Token t); static void cConsumeBody() { int oldLine = line; while(!cConsumeTokenIf(T_CLOSE_CURVED_BRACKET)) { Token t = cReadTokenAndLine(); if(t == T_END) { line = oldLine; cError("unexpected end of file: non closed curved bracket"); } cLine(t); } } static void cConsumeScope() { Scope scope; vEnterScope(&vars, &scope); cConsumeBody(); vLeaveScope(&vars, &scope); } static void cAddReturn(Operation op) { cAddOperation(op); returns[returnIndex++] = cReserveInt(); } static void cReturn() { if(returnIndex >= RETURN_BUFFER) { cError("too much returns in function"); } hasReturn = true; if(returnType == DT_VOID) { cConsumeToken(T_SEMICOLON); cAddReturn(OP_RETURN); return; } DataType dt = cExpression(); if(dt != returnType) { cError("wrong return type, should be %s", dtGetName(returnType)); } switch(dt) { case DT_INT: cAddReturn(OP_RETURN_INT); break; case DT_BOOL: cAddReturn(OP_RETURN_BOOL); break; case DT_FLOAT: cAddReturn(OP_RETURN_FLOAT); break; default: cError("cannot return %s", dtGetName(dt)); } 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 type %s", dtGetName(dt)); } cConsumeToken(T_SEMICOLON); } static void cIf() { cConsumeToken(T_OPEN_BRACKET); DataType dt = cExpression(); if(dt != DT_BOOL) { cError("if expects a bool not %s", dtGetName(dt)); } cConsumeToken(T_CLOSE_BRACKET); cAddOperation(OP_IF_GOTO); int ifP = cReserveInt(); cConsumeToken(T_OPEN_CURVED_BRACKET); cConsumeScope(); cSetInt(ifP, code->length); if(cConsumeTokenIf(T_ELSE)) { cAddOperation(OP_GOTO); int elseP = cReserveInt(); cSetInt(ifP, code->length); if(cConsumeTokenIf(T_IF)) { cIf(); } else { cConsumeToken(T_OPEN_CURVED_BRACKET); cConsumeScope(); } 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); DataType dt = cExpression(); if(dt != DT_BOOL) { cError("while expects a bool not %s", dtGetName(dt)); } cConsumeToken(T_CLOSE_BRACKET); cAddOperation(OP_IF_GOTO); int ifP = cReserveInt(); int breakStart = breakIndex; forWhileStack++; int oldContinue = continueAt; continueAt = start; cConsumeToken(T_OPEN_CURVED_BRACKET); cConsumeScope(); continueAt = oldContinue; forWhileStack--; cAddOperation(OP_GOTO); cAddInt(start); cSetInt(ifP, code->length); cConsumeBreaks(breakStart, code->length); } static DataType cDimension(DataType dt) { int dimension = 0; while(cConsumeTokenIf(T_MUL)) { dimension++; } if(dimension > 0) { dt = dtArray(dt, dimension); } return dt; } static void cDeclare(DataType dt) { dt = cDimension(dt); cConsumeToken(T_LITERAL); const char* var = cReadString(); Variable* v = vSearchScope(&vars, var); if(v != NULL) { cDeclared(var); } v = vAdd(&vars, var, dt); cConsumeToken(T_SET); cStoreVariable(v, cExpression(), "="); } static void cAddPreLineChange(int change, const char* name) { cConsumeToken(T_LITERAL); const char* literal = cReadString(); Variable* v = vSearch(&vars, literal); if(v == NULL) { cNotDeclared(literal); } cAddPostLineChange(v, change, name); } static void cLineExpression(Token t) { switch(t) { case T_LITERAL: cLineLiteral(); break; case T_INT: cDeclare(DT_INT); break; case T_BOOL: cDeclare(DT_BOOL); break; case T_FLOAT: cDeclare(DT_FLOAT); break; case T_INCREMENT: cAddPreLineChange(1, "++"); break; case T_DECREMENT: cAddPreLineChange(-1, "--"); break; default: cUnexpectedToken(t); } } static void cFor() { Scope scope; vEnterScope(&vars, &scope); cConsumeToken(T_OPEN_BRACKET); cLineExpression(cReadTokenAndLine()); cConsumeToken(T_SEMICOLON); int startCheck = code->length; DataType dt = cExpression(); if(dt != DT_BOOL) { cError("for expects a bool not %s", dtGetName(dt)); } 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; cConsumeToken(T_OPEN_CURVED_BRACKET); cConsumeBody(); continueAt = oldContinue; forWhileStack--; cAddOperation(OP_GOTO); cAddInt(startPerLoop); cSetInt(end, code->length); cConsumeBreaks(breakStart, code->length); vLeaveScope(&vars, &scope); } 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) { hasReturn = false; cAddOperation(OP_LINE); cAddInt16(line); switch(t) { case T_OPEN_CURVED_BRACKET: cConsumeScope(); break; case T_PRINT: cPrint(); 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 cFunctionArgument(Function* f); static void cFunctionCommaOrEnd(Function* f) { if(cConsumeTokenIf(T_CLOSE_BRACKET)) { return; } cConsumeToken(T_COMMA); cFunctionArgument(f); } static void cFunctionAddArgument(Function* f, DataType dt) { cConsumeToken(T_LITERAL); const char* name = cReadString(); Variable* v = vSearchScope(&vars, name); if(v != NULL) { cDeclared(name); } vAdd(&vars, name, dt); if(fAddArgument(f, dt)) { cTooMuchArguments(); } cFunctionCommaOrEnd(f); } static void cFunctionArgument(Function* f) { Token t = cReadTokenAndLine(); switch(t) { case T_INT: cFunctionAddArgument(f, DT_INT); break; default: cUnexpectedToken(t); } } static void cFunctionArguments(Function* f) { cConsumeToken(T_OPEN_BRACKET); if(!cConsumeTokenIf(T_CLOSE_BRACKET)) { cFunctionArgument(f); } } static int cReserve(int offset) { cAddOperation(OP_RESERVE); int p = cReserveInt(); cAddInt(offset); return p; } static void cFree(int p, int bytes) { cAddOperation(OP_RETURN); cAddInt(bytes); cSetInt(p, bytes); } static void cLinkReturns(int bytes) { for(int i = 0; i < returnIndex; i++) { cSetInt(returns[i], bytes); } returnIndex = 0; } static void cInnerFunction(Function* f) { cConsumeToken(T_OPEN_CURVED_BRACKET); int p = cReserve(f->size); returnIndex = 0; hasReturn = false; cConsumeScope(); if(returnType != DT_VOID && !hasReturn) { cError("missing return"); } cFree(p, vars.maxAddress); cLinkReturns(vars.maxAddress); } static bool cForwardFunction(Function* found, Function* f) { if(!cConsumeTokenIf(T_SEMICOLON)) { return false; } else if(found != NULL) { cError("function registered twice"); } f->address = -1; fsAdd(&functions, f); return true; } static void cBuildFunction(Function* f, DataType rType) { cConsumeToken(T_LITERAL); fInit(f, cReadString(), line); f->returnType = rType; vReset(&vars); cFunctionArguments(f); } static void cFunction(DataType rType) { Function f; cBuildFunction(&f, rType); Function* found = fsSearch(&functions, &f); if(cForwardFunction(found, &f)) { return; } cAddOperation(OP_LINE); cAddInt16(line); cAddOperation(OP_GOTO); int end = cReserveInt(); f.address = code->length; if(found != NULL) { if(found->address == -1) { found->address = f.address; } else { cError("function registered twice"); } } else { fsAdd(&functions, &f); } returnType = rType; cInnerFunction(&f); cSetInt(end, code->length); } static void cGlobalScope(Token t) { switch(t) { case T_VOID: cFunction(DT_VOID); break; case T_INT: cFunction(DT_INT); break; case T_BOOL: cFunction(DT_BOOL); break; case T_FLOAT: cFunction(DT_FLOAT); break; default: cUnexpectedToken(t); } } static void cCallMain() { Function f; fInit(&f, "main", line); Function* found = fsSearch(&functions, &f); if(found != NULL && found->returnType == DT_VOID) { cAddOperation(OP_PUSH_INT); cAddInt(0); cAddOperation(OP_GOSUB); cAddInt(found->address); cAddInt(found->size); } } static void cForEachLine() { Token t = cReadTokenAndLine(); while(t != T_END) { cGlobalScope(t); t = cReadTokenAndLine(); } cCallMain(); } static void cLinkQueuedFunctions() { for(int i = 0; i < functionQueue.entries; i++) { Function* f = functionQueue.data + i; Function* found = fsSearch(&functions, f); if(found == NULL) { line = f->line; cError("unknown function"); } else if(f->returnType != found->returnType) { line = f->line; cError("function return type is not %s", dtGetName(f->returnType)); } cSetInt(f->address, found->address); } } static void cAllocAndCompile() { forWhileStack = 0; breakIndex = 0; vInit(&vars); fsInit(&functions); fsInit(&functionQueue); if(!setjmp(errorJump)) { cForEachLine(); cLinkQueuedFunctions(); } fsDelete(&functionQueue); fsDelete(&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; }