|
@@ -1,3 +1,4 @@
|
|
|
+#include <setjmp.h>
|
|
|
#include <stdarg.h>
|
|
|
#include <stdio.h>
|
|
|
|
|
@@ -11,6 +12,7 @@
|
|
|
#define ERROR_LENGTH 256
|
|
|
#define VARS 256
|
|
|
|
|
|
+static jmp_buf errorJump;
|
|
|
static char error[ERROR_LENGTH] = {'\0'};
|
|
|
|
|
|
static ByteCode* code;
|
|
@@ -26,6 +28,7 @@ static void cError(const char* format, ...) {
|
|
|
va_start(args, format);
|
|
|
vsnprintf(error, ERROR_LENGTH, format, args);
|
|
|
va_end(args);
|
|
|
+ longjmp(errorJump, 0);
|
|
|
}
|
|
|
|
|
|
static int cAddVar(const char* var) {
|
|
@@ -63,6 +66,19 @@ static void cAddFloat(float f) {
|
|
|
bcAddBytes(code, &f, sizeof(float));
|
|
|
}
|
|
|
|
|
|
+static int cAddPush(int offset) {
|
|
|
+ cAddOperation(OP_PUSH);
|
|
|
+ int p = cReserveInt();
|
|
|
+ cAddInt(offset);
|
|
|
+ return p;
|
|
|
+}
|
|
|
+
|
|
|
+static void cAddPop(int p, int vars) {
|
|
|
+ cAddOperation(OP_POP);
|
|
|
+ cAddInt(vars);
|
|
|
+ cSetInt(p, vars);
|
|
|
+}
|
|
|
+
|
|
|
static Token cReadTokenAndLine() {
|
|
|
Token t = tReadToken();
|
|
|
if(tReadInt16(&line)) {
|
|
@@ -71,13 +87,11 @@ static Token cReadTokenAndLine() {
|
|
|
return T_END;
|
|
|
}
|
|
|
|
|
|
-static bool cConsumeToken(Token wanted) {
|
|
|
+static void cConsumeToken(Token wanted) {
|
|
|
Token t = cReadTokenAndLine();
|
|
|
- if(wanted == t) {
|
|
|
- return true;
|
|
|
+ if(wanted != t) {
|
|
|
+ cError("unexpected token on line %d: expected '%s' got '%s'", line, tGetTokenName(wanted), tGetTokenName(t));
|
|
|
}
|
|
|
- cError("unexpected token on line %d: expected '%s' got '%s'", line, tGetTokenName(wanted), tGetTokenName(t));
|
|
|
- return false;
|
|
|
}
|
|
|
|
|
|
static bool cConsumeTokenIf(Token t) {
|
|
@@ -88,259 +102,197 @@ static bool cConsumeTokenIf(Token t) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
-static bool cExpression();
|
|
|
-
|
|
|
-static bool cConstantInt() {
|
|
|
+static void cConstantInt() {
|
|
|
int value;
|
|
|
- if(tReadInt(&value)) {
|
|
|
- cAddOperation(OP_PUSH_INT);
|
|
|
- cAddInt(value);
|
|
|
- return true;
|
|
|
+ if(!tReadInt(&value)) {
|
|
|
+ cError("int token without an int on line %d", line);
|
|
|
}
|
|
|
- return false;
|
|
|
+ cAddOperation(OP_PUSH_INT);
|
|
|
+ cAddInt(value);
|
|
|
}
|
|
|
|
|
|
-static bool cConstantFloat() {
|
|
|
+static void cConstantFloat() {
|
|
|
float value;
|
|
|
- if(tReadFloat(&value)) {
|
|
|
- cAddOperation(OP_PUSH_FLOAT);
|
|
|
- cAddFloat(value);
|
|
|
- return true;
|
|
|
+ if(!tReadFloat(&value)) {
|
|
|
+ cError("float token without a float on line %d", line);
|
|
|
}
|
|
|
- return false;
|
|
|
+ cAddOperation(OP_PUSH_FLOAT);
|
|
|
+ cAddFloat(value);
|
|
|
}
|
|
|
|
|
|
-static bool cGetVar() {
|
|
|
+static const char* cReadString() {
|
|
|
const char* literal = tReadString();
|
|
|
if(literal == NULL) {
|
|
|
cError("literal without string on line %d", line);
|
|
|
- return false;
|
|
|
}
|
|
|
+ return literal;
|
|
|
+}
|
|
|
+
|
|
|
+static void cGetVar() {
|
|
|
cAddOperation(OP_GET);
|
|
|
- cAddInt(cAddVar(literal));
|
|
|
- return true;
|
|
|
+ cAddInt(cAddVar(cReadString()));
|
|
|
}
|
|
|
|
|
|
-static bool cPrimary() {
|
|
|
+static void cExpression();
|
|
|
+
|
|
|
+static void cPrimary() {
|
|
|
Token t = cReadTokenAndLine();
|
|
|
switch(t) {
|
|
|
- case T_INT: return cConstantInt();
|
|
|
- case T_FLOAT: return cConstantFloat();
|
|
|
- case T_NULL: cAddOperation(OP_PUSH_NULL); return true;
|
|
|
- case T_TRUE: cAddOperation(OP_PUSH_TRUE); return true;
|
|
|
- case T_FALSE: cAddOperation(OP_PUSH_FALSE); return true;
|
|
|
- case T_OPEN_BRACKET: return cExpression() && cConsumeToken(T_CLOSE_BRACKET);
|
|
|
- case T_LITERAL: return cGetVar();
|
|
|
- default: cUnexpectedToken(t); return false;
|
|
|
+ case T_INT: cConstantInt(); break;
|
|
|
+ case T_FLOAT: cConstantFloat(); break;
|
|
|
+ case T_NULL: cAddOperation(OP_PUSH_NULL); break;
|
|
|
+ case T_TRUE: cAddOperation(OP_PUSH_TRUE); break;
|
|
|
+ case T_FALSE: cAddOperation(OP_PUSH_FALSE); break;
|
|
|
+ case T_OPEN_BRACKET:
|
|
|
+ cExpression();
|
|
|
+ cConsumeToken(T_CLOSE_BRACKET);
|
|
|
+ break;
|
|
|
+ case T_LITERAL: cGetVar(); break;
|
|
|
+ default: cUnexpectedToken(t); break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static bool cMul() {
|
|
|
- if(!cPrimary()) {
|
|
|
- return false;
|
|
|
- }
|
|
|
+static void cMul() {
|
|
|
+ cPrimary();
|
|
|
while(cConsumeTokenIf(T_MUL)) {
|
|
|
- if(!cPrimary()) {
|
|
|
- return false;
|
|
|
- }
|
|
|
+ cPrimary();
|
|
|
cAddOperation(OP_MUL);
|
|
|
}
|
|
|
- return true;
|
|
|
}
|
|
|
|
|
|
-static bool cAdd() {
|
|
|
- if(!cMul()) {
|
|
|
- return false;
|
|
|
- }
|
|
|
+static void cAdd() {
|
|
|
+ cMul();
|
|
|
while(cConsumeTokenIf(T_ADD)) {
|
|
|
- if(!cMul()) {
|
|
|
- return false;
|
|
|
- }
|
|
|
+ cMul();
|
|
|
cAddOperation(OP_ADD);
|
|
|
}
|
|
|
- return true;
|
|
|
}
|
|
|
|
|
|
-static bool cExpression() {
|
|
|
- return cAdd();
|
|
|
+static void cExpression() {
|
|
|
+ cAdd();
|
|
|
}
|
|
|
|
|
|
-static bool cSetVar(const char* literal) {
|
|
|
- bool b = cExpression() && cConsumeToken(T_SEMICOLON);
|
|
|
+static void cSetVar(const char* literal) {
|
|
|
+ cExpression();
|
|
|
+ cConsumeToken(T_SEMICOLON);
|
|
|
cAddOperation(OP_SET);
|
|
|
cAddInt(cAddVar(literal));
|
|
|
- return b;
|
|
|
}
|
|
|
|
|
|
static int cCallFunctionArguments() {
|
|
|
int arguments = 0;
|
|
|
while(!cConsumeTokenIf(T_CLOSE_BRACKET)) {
|
|
|
arguments++;
|
|
|
- if(!cExpression()) {
|
|
|
- return -1;
|
|
|
- } else if(cConsumeTokenIf(T_COMMA) && tPeekToken() == T_CLOSE_BRACKET) {
|
|
|
+ cExpression();
|
|
|
+ if(cConsumeTokenIf(T_COMMA) && tPeekToken() == T_CLOSE_BRACKET) {
|
|
|
cUnexpectedToken(tPeekToken());
|
|
|
- return -1;
|
|
|
}
|
|
|
}
|
|
|
return arguments;
|
|
|
}
|
|
|
|
|
|
-static bool cCallFunction(const char* literal) {
|
|
|
+static void cCallFunction(const char* literal) {
|
|
|
cAddOperation(OP_PUSH_INT);
|
|
|
cAddInt(0);
|
|
|
int arguments = cCallFunctionArguments();
|
|
|
- if(arguments == -1) {
|
|
|
- return false;
|
|
|
- }
|
|
|
int address = fmSearchAddress(&functions, literal, arguments);
|
|
|
if(address == -1) {
|
|
|
cError("unknown function on line %d", line);
|
|
|
- return false;
|
|
|
}
|
|
|
cAddOperation(OP_GOSUB);
|
|
|
cAddInt(address);
|
|
|
cAddInt(arguments);
|
|
|
- return cConsumeToken(T_SEMICOLON);
|
|
|
+ cConsumeToken(T_SEMICOLON);
|
|
|
}
|
|
|
|
|
|
-static bool cLiteral() {
|
|
|
- const char* literal = tReadString();
|
|
|
- if(literal == NULL) {
|
|
|
- cError("literal without string on line %d", line);
|
|
|
- return false;
|
|
|
- }
|
|
|
+static void cLiteral() {
|
|
|
+ const char* literal = cReadString();
|
|
|
Token t = cReadTokenAndLine();
|
|
|
- if(t == T_SET) {
|
|
|
- return cSetVar(literal);
|
|
|
- } else if(t == T_OPEN_BRACKET) {
|
|
|
- return cCallFunction(literal);
|
|
|
+ switch(t) {
|
|
|
+ case T_SET: cSetVar(literal); break;
|
|
|
+ case T_OPEN_BRACKET: cCallFunction(literal); break;
|
|
|
+ default: cUnexpectedToken(t);
|
|
|
}
|
|
|
- cUnexpectedToken(t);
|
|
|
- return false;
|
|
|
}
|
|
|
|
|
|
static int cFunctionArguments() {
|
|
|
int arguments = 0;
|
|
|
while(!cConsumeTokenIf(T_CLOSE_BRACKET)) {
|
|
|
- if(!cConsumeToken(T_LITERAL)) {
|
|
|
- return -1;
|
|
|
- }
|
|
|
+ cConsumeToken(T_LITERAL);
|
|
|
arguments++;
|
|
|
- const char* arg = tReadString();
|
|
|
- if(arg == NULL) {
|
|
|
- cError("function argument literal without string on line %d", line);
|
|
|
- return -1;
|
|
|
- }
|
|
|
- cAddVar(arg);
|
|
|
+ cAddVar(cReadString());
|
|
|
if(cConsumeTokenIf(T_COMMA) && tPeekToken() != T_LITERAL) {
|
|
|
cUnexpectedToken(tPeekToken());
|
|
|
- return -1;
|
|
|
}
|
|
|
}
|
|
|
return arguments;
|
|
|
}
|
|
|
|
|
|
-static bool cLine();
|
|
|
-
|
|
|
-static bool cFunctionInnerBody(int arguments) {
|
|
|
- cAddOperation(OP_PUSH);
|
|
|
- int pushIndex = cReserveInt();
|
|
|
- cAddInt(arguments);
|
|
|
+static void cLine(Token t);
|
|
|
|
|
|
+static void cFunctionInnerBody(int arguments) {
|
|
|
+ int p = cAddPush(arguments);
|
|
|
int oldLine = line;
|
|
|
while(!cConsumeTokenIf(T_CLOSE_CURVED_BRACKET)) {
|
|
|
- if(cConsumeTokenIf(T_END)) {
|
|
|
+ Token t = cReadTokenAndLine();
|
|
|
+ if(t == T_END) {
|
|
|
cError("unexpected end of file: function not closed on line %d", oldLine);
|
|
|
- return false;
|
|
|
- } else if(!cLine()) {
|
|
|
- return false;
|
|
|
}
|
|
|
+ cLine(t);
|
|
|
}
|
|
|
-
|
|
|
- cAddOperation(OP_POP);
|
|
|
- cAddInt(vars[1].entries);
|
|
|
- cSetInt(pushIndex, vars[1].entries);
|
|
|
- return true;
|
|
|
+ cAddPop(p, vars[1].entries);
|
|
|
}
|
|
|
|
|
|
-static bool cFunctionBody(const char* name, int arguments) {
|
|
|
- if(!cConsumeToken(T_OPEN_CURVED_BRACKET)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
+static void cFunctionBody(const char* name, int arguments) {
|
|
|
+ cConsumeToken(T_OPEN_CURVED_BRACKET);
|
|
|
cAddOperation(OP_GOTO);
|
|
|
int gotoIndex = cReserveInt();
|
|
|
|
|
|
if(!fmAdd(&functions, name, arguments, code->length)) {
|
|
|
cError("function registered twice on line %d", line);
|
|
|
- return false;
|
|
|
- } else if(!cFunctionInnerBody(arguments)) {
|
|
|
- return false;
|
|
|
}
|
|
|
+ cFunctionInnerBody(arguments);
|
|
|
|
|
|
cAddOperation(OP_RETURN);
|
|
|
cSetInt(gotoIndex, code->length);
|
|
|
- return true;
|
|
|
}
|
|
|
|
|
|
-static bool cFunction() {
|
|
|
+static void cFunction() {
|
|
|
if(varIndex == 1) {
|
|
|
cError("function inside function on line %d", line);
|
|
|
- return false;
|
|
|
- } else if(!cConsumeToken(T_LITERAL)) {
|
|
|
- return false;
|
|
|
}
|
|
|
- const char* name = tReadString();
|
|
|
- if(name == NULL) {
|
|
|
- cError("function literal without a function name on line %d", line);
|
|
|
- return false;
|
|
|
- } else if(!cConsumeToken(T_OPEN_BRACKET)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
+ cConsumeToken(T_LITERAL);
|
|
|
+ const char* name = cReadString();
|
|
|
+ cConsumeToken(T_OPEN_BRACKET);
|
|
|
varIndex = 1;
|
|
|
vars[1].entries = 0;
|
|
|
- int arguments = cFunctionArguments();
|
|
|
- if(arguments == -1 || !cFunctionBody(name, arguments)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
+ cFunctionBody(name, cFunctionArguments());
|
|
|
varIndex = 0;
|
|
|
- return true;
|
|
|
}
|
|
|
|
|
|
-static bool cPrint() {
|
|
|
- bool b = cExpression() && cConsumeToken(T_SEMICOLON);
|
|
|
+static void cPrint() {
|
|
|
+ cExpression();
|
|
|
+ cConsumeToken(T_SEMICOLON);
|
|
|
cAddOperation(OP_PRINT);
|
|
|
- return b;
|
|
|
}
|
|
|
|
|
|
-static bool cLine() {
|
|
|
- Token t = cReadTokenAndLine();
|
|
|
- if(t == T_END) {
|
|
|
- return false;
|
|
|
- }
|
|
|
+static void cLine(Token t) {
|
|
|
cAddOperation(OP_LINE);
|
|
|
cAddInt16(line);
|
|
|
- if(t == T_PRINT) {
|
|
|
- return cPrint();
|
|
|
- } else if(t == T_LITERAL) {
|
|
|
- return cLiteral();
|
|
|
- } else if(t == T_FUNCTION) {
|
|
|
- return cFunction();
|
|
|
+ switch(t) {
|
|
|
+ case T_PRINT: cPrint(); break;
|
|
|
+ case T_LITERAL: cLiteral(); break;
|
|
|
+ case T_FUNCTION: cFunction(); break;
|
|
|
+ default: cUnexpectedToken(t);
|
|
|
}
|
|
|
- cUnexpectedToken(t);
|
|
|
- return false;
|
|
|
}
|
|
|
|
|
|
static void cForEachLine() {
|
|
|
- cAddOperation(OP_PUSH);
|
|
|
- int globalVars = cReserveInt();
|
|
|
- cAddInt(0);
|
|
|
- while(cLine()) {
|
|
|
+ Token t = cReadTokenAndLine();
|
|
|
+ while(t != T_END) {
|
|
|
+ cLine(t);
|
|
|
+ t = cReadTokenAndLine();
|
|
|
}
|
|
|
- cAddOperation(OP_POP);
|
|
|
- cSetInt(globalVars, vars[varIndex].entries);
|
|
|
- cAddInt(vars[varIndex].entries);
|
|
|
}
|
|
|
|
|
|
static void cAllocAndCompile() {
|
|
@@ -348,7 +300,11 @@ static void cAllocAndCompile() {
|
|
|
simInit(vars);
|
|
|
simInit(vars + 1);
|
|
|
fmInit(&functions);
|
|
|
- cForEachLine();
|
|
|
+ if(!setjmp(errorJump)) {
|
|
|
+ int p = cAddPush(0);
|
|
|
+ cForEachLine();
|
|
|
+ cAddPop(p, vars[varIndex].entries);
|
|
|
+ }
|
|
|
fmDelete(&functions);
|
|
|
simDelete(vars + 1);
|
|
|
simDelete(vars);
|