|
|
@@ -22,9 +22,18 @@ struct Function {
|
|
|
Function* next;
|
|
|
i32 address;
|
|
|
i32 valueAddress;
|
|
|
+ i32 arguments;
|
|
|
char name[];
|
|
|
};
|
|
|
|
|
|
+typedef struct Return Return;
|
|
|
+
|
|
|
+struct Return {
|
|
|
+ Return* next;
|
|
|
+ i32 address;
|
|
|
+ bool hasArgument;
|
|
|
+};
|
|
|
+
|
|
|
typedef struct {
|
|
|
Tokenizer* tokenizer;
|
|
|
Code* code;
|
|
|
@@ -33,6 +42,7 @@ typedef struct {
|
|
|
jmp_buf jump;
|
|
|
Variable* variables;
|
|
|
Function* functions;
|
|
|
+ Return* returns;
|
|
|
bool inFunction;
|
|
|
} Context;
|
|
|
|
|
|
@@ -105,7 +115,8 @@ static i32 addVariable(Context* c, const char* name, bool forceGlobal) {
|
|
|
}
|
|
|
|
|
|
static void addRawFunction(
|
|
|
- Context* c, const char* name, i32 address, i32 valueAddress) {
|
|
|
+ Context* c, const char* name, i32 address, i32 valueAddress,
|
|
|
+ i32 arguments) {
|
|
|
size_t l = strlen(name) + 1;
|
|
|
Function* f = memoryAllocate(sizeof(Function) + l);
|
|
|
if(f == nullptr) {
|
|
|
@@ -114,30 +125,38 @@ static void addRawFunction(
|
|
|
f->next = c->functions;
|
|
|
f->address = address;
|
|
|
f->valueAddress = valueAddress;
|
|
|
+ f->arguments = arguments;
|
|
|
memcpy(f->name, name, l);
|
|
|
c->functions = f;
|
|
|
}
|
|
|
|
|
|
-static void addFunction(Context* c, const char* name, i32 address) {
|
|
|
+static void addFunction(
|
|
|
+ Context* c, const char* name, i32 address, i32 arguments) {
|
|
|
for(Function* f = c->functions; f != nullptr; f = f->next) {
|
|
|
if(strcmp(f->name, name) != 0) {
|
|
|
continue;
|
|
|
} else if(f->address >= 0) {
|
|
|
THROW_ERROR("Function '%s' registered again", name);
|
|
|
+ } else if(f->arguments != arguments) {
|
|
|
+ THROW_ERROR("Function '%s' with inconsistent arguments", name);
|
|
|
}
|
|
|
f->address = address;
|
|
|
codeRewriteI32(c, (size_t)f->valueAddress, address);
|
|
|
}
|
|
|
- addRawFunction(c, name, address, -1);
|
|
|
+ addRawFunction(c, name, address, -1, arguments);
|
|
|
}
|
|
|
|
|
|
-static i32 addCallFunction(Context* c, const char* name, i32 address) {
|
|
|
+static i32 addCallFunction(
|
|
|
+ Context* c, const char* name, i32 address, i32 arguments) {
|
|
|
for(Function* f = c->functions; f != nullptr; f = f->next) {
|
|
|
if(strcmp(f->name, name) == 0 && f->address >= 0) {
|
|
|
+ if(f->arguments != arguments) {
|
|
|
+ THROW_ERROR("Function '%s' with inconsistent arguments", name);
|
|
|
+ }
|
|
|
return f->address;
|
|
|
}
|
|
|
}
|
|
|
- addRawFunction(c, name, -1, address);
|
|
|
+ addRawFunction(c, name, -1, address, arguments);
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
@@ -149,6 +168,30 @@ static void checkForInvalidFunctions(Context* c) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static void addReturn(Context* c, i32 address, bool hasArgument) {
|
|
|
+ if(c->returns != nullptr && c->returns->hasArgument != hasArgument) {
|
|
|
+ THROW_ERROR("Inconsistent returns in function");
|
|
|
+ }
|
|
|
+ Return* r = memoryAllocate(sizeof(Return));
|
|
|
+ if(r == nullptr) {
|
|
|
+ THROW_ERROR("Too less memory for returns");
|
|
|
+ }
|
|
|
+ r->next = c->returns;
|
|
|
+ r->hasArgument = hasArgument;
|
|
|
+ r->address = address;
|
|
|
+ c->returns = r;
|
|
|
+}
|
|
|
+
|
|
|
+static void cleanReturns(Context* c) {
|
|
|
+ Return* r = c->returns;
|
|
|
+ while(r != nullptr) {
|
|
|
+ Return* next = r->next;
|
|
|
+ memoryFree(r);
|
|
|
+ r = next;
|
|
|
+ }
|
|
|
+ c->returns = nullptr;
|
|
|
+}
|
|
|
+
|
|
|
static void cleanContext(Context* c) {
|
|
|
Variable* v = c->variables;
|
|
|
while(v != nullptr) {
|
|
|
@@ -165,26 +208,30 @@ static void cleanContext(Context* c) {
|
|
|
f = next;
|
|
|
}
|
|
|
c->functions = nullptr;
|
|
|
+
|
|
|
+ cleanReturns(c);
|
|
|
}
|
|
|
|
|
|
-[[noreturn]] static void unexpectedToken(Context* c, Token token) {
|
|
|
+[[noreturn]] static void unexpectedToken(Context* c, Token token, int line) {
|
|
|
char buffer[128];
|
|
|
tokenizerPrintToken(&token, buffer, sizeof(buffer));
|
|
|
- THROW_ERROR("Unexpected %s token", buffer);
|
|
|
+ THROW_ERROR("Unexpected %s token %d", buffer, line);
|
|
|
}
|
|
|
|
|
|
static Token consumeToken(Context* c, TokenType type) {
|
|
|
Token actual = tokenizerNext(c->tokenizer);
|
|
|
if(actual.type != type) {
|
|
|
- unexpectedToken(c, actual);
|
|
|
+ unexpectedToken(c, actual, __LINE__);
|
|
|
}
|
|
|
return actual;
|
|
|
}
|
|
|
|
|
|
-static Token consumeNewline(Context* c) {
|
|
|
- Token t = consumeToken(c, NEWLINE);
|
|
|
+static void consumeNewline(Context* c) {
|
|
|
+ if(tokenizerPeek(c->tokenizer).type == TT_INVALID) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ consumeToken(c, TT_NEWLINE);
|
|
|
c->line++;
|
|
|
- return t;
|
|
|
}
|
|
|
|
|
|
static void compileReadVariable(Context* c, Token token, bool forceGlobal) {
|
|
|
@@ -193,30 +240,63 @@ static void compileReadVariable(Context* c, Token token, bool forceGlobal) {
|
|
|
codePushI32(c, index);
|
|
|
}
|
|
|
|
|
|
+static void compileCallFunction(Context* c, Token t);
|
|
|
+
|
|
|
static void compileConstant(Context* c) {
|
|
|
Token token = tokenizerNext(c->tokenizer);
|
|
|
- if(token.type == STRING) {
|
|
|
+ if(token.type == TT_STRING) {
|
|
|
codePushInstruction(c, PUSH_CONSTANT_STRING);
|
|
|
codePushConstantString(c, token.stringValue);
|
|
|
- } else if(token.type == INT32) {
|
|
|
+ } else if(token.type == TT_INT32) {
|
|
|
codePushInstruction(c, PUSH_INT32);
|
|
|
codePushI32(c, token.intValue);
|
|
|
- } else if(token.type == LITERAL) {
|
|
|
- compileReadVariable(c, token, false);
|
|
|
- } else if(token.type == DOLLAR) {
|
|
|
- token = consumeToken(c, LITERAL);
|
|
|
+ } else if(token.type == TT_LITERAL) {
|
|
|
+ if(tokenizerPeek(c->tokenizer).type == TT_OPEN_ROUND_BRACKET) {
|
|
|
+ compileCallFunction(c, token);
|
|
|
+ } else {
|
|
|
+ compileReadVariable(c, token, false);
|
|
|
+ }
|
|
|
+ } else if(token.type == TT_DOLLAR) {
|
|
|
+ token = consumeToken(c, TT_LITERAL);
|
|
|
compileReadVariable(c, token, true);
|
|
|
} else {
|
|
|
- unexpectedToken(c, token);
|
|
|
+ unexpectedToken(c, token, __LINE__);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void compileAdd(Context* c) {
|
|
|
+static void compileMul(Context* c) {
|
|
|
compileConstant(c);
|
|
|
- while(tokenizerPeek(c->tokenizer).type == PLUS) {
|
|
|
- tokenizerNext(c->tokenizer);
|
|
|
- compileConstant(c);
|
|
|
- codePushInstruction(c, ADD);
|
|
|
+ while(true) {
|
|
|
+ Token t = tokenizerPeek(c->tokenizer);
|
|
|
+ if(t.type == TT_ASTERISK) {
|
|
|
+ tokenizerNext(c->tokenizer);
|
|
|
+ compileMul(c);
|
|
|
+ codePushInstruction(c, MUL);
|
|
|
+ } else if(t.type == TT_SLASH) {
|
|
|
+ tokenizerNext(c->tokenizer);
|
|
|
+ compileMul(c);
|
|
|
+ codePushInstruction(c, DIV);
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void compileAdd(Context* c) {
|
|
|
+ compileMul(c);
|
|
|
+ while(true) {
|
|
|
+ Token t = tokenizerPeek(c->tokenizer);
|
|
|
+ if(t.type == TT_PLUS) {
|
|
|
+ tokenizerNext(c->tokenizer);
|
|
|
+ compileMul(c);
|
|
|
+ codePushInstruction(c, ADD);
|
|
|
+ } else if(t.type == TT_MINUS) {
|
|
|
+ tokenizerNext(c->tokenizer);
|
|
|
+ compileMul(c);
|
|
|
+ codePushInstruction(c, SUB);
|
|
|
+ } else {
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -231,21 +311,21 @@ static void compileIf(Context* c) {
|
|
|
codePushInstruction(c, JUMP_ON_0);
|
|
|
size_t posIndex = codeGetWritePosition(c);
|
|
|
codePushI32(c, 0);
|
|
|
- consumeToken(c, THEN);
|
|
|
+ consumeToken(c, TT_THEN);
|
|
|
consumeNewline(c);
|
|
|
while(true) {
|
|
|
- if(tokenizerPeek(c->tokenizer).type == ENDIF) {
|
|
|
+ if(tokenizerPeek(c->tokenizer).type == TT_ENDIF) {
|
|
|
break;
|
|
|
}
|
|
|
compileLine(c, tokenizerNext(c->tokenizer));
|
|
|
}
|
|
|
- consumeToken(c, ENDIF);
|
|
|
+ consumeToken(c, TT_ENDIF);
|
|
|
consumeNewline(c);
|
|
|
codeRewriteI32(c, posIndex, (i32)codeGetWritePosition(c));
|
|
|
}
|
|
|
|
|
|
static void compileSetVariable(Context* c, const char* name, bool forceGlobal) {
|
|
|
- consumeToken(c, EQUAL);
|
|
|
+ consumeToken(c, TT_EQUAL);
|
|
|
compileExpression(c);
|
|
|
i32 index = addVariable(c, name, forceGlobal);
|
|
|
codePushInstruction(c, SET_VARIABLE);
|
|
|
@@ -262,38 +342,45 @@ static void compileFunction(Context* c) {
|
|
|
size_t pos = codeGetWritePosition(c);
|
|
|
codePushI32(c, 0);
|
|
|
|
|
|
- Token t = consumeToken(c, LITERAL);
|
|
|
- addFunction(c, t.stringValue, (i32)codeGetWritePosition(c));
|
|
|
+ i32 functionStart = (i32)codeGetWritePosition(c);
|
|
|
+ Token t = consumeToken(c, TT_LITERAL);
|
|
|
|
|
|
codePushInstruction(c, PUSH_STACK_VARIABLES);
|
|
|
size_t stackVarsLoc = codeGetWritePosition(c);
|
|
|
codePushI32(c, 0);
|
|
|
|
|
|
- consumeToken(c, OPEN_ROUND_BRACKET);
|
|
|
+ consumeToken(c, TT_OPEN_ROUND_BRACKET);
|
|
|
i32 vars = 0;
|
|
|
Variable* resetVar = c->variables;
|
|
|
while(true) {
|
|
|
- if(tokenizerPeek(c->tokenizer).type != LITERAL) {
|
|
|
+ if(tokenizerPeek(c->tokenizer).type != TT_LITERAL) {
|
|
|
break;
|
|
|
}
|
|
|
- Token varToken = consumeToken(c, LITERAL);
|
|
|
+ Token varToken = consumeToken(c, TT_LITERAL);
|
|
|
addVariable(c, varToken.stringValue, false);
|
|
|
vars++;
|
|
|
- if(tokenizerPeek(c->tokenizer).type == COMMA) {
|
|
|
- consumeToken(c, COMMA);
|
|
|
+ if(tokenizerPeek(c->tokenizer).type == TT_COMMA) {
|
|
|
+ consumeToken(c, TT_COMMA);
|
|
|
}
|
|
|
}
|
|
|
- consumeToken(c, CLOSE_ROUND_BRACKET);
|
|
|
+ addFunction(c, t.stringValue, functionStart, vars);
|
|
|
+ consumeToken(c, TT_CLOSE_ROUND_BRACKET);
|
|
|
while(true) {
|
|
|
- if(tokenizerPeek(c->tokenizer).type == END) {
|
|
|
+ if(tokenizerPeek(c->tokenizer).type == TT_END) {
|
|
|
break;
|
|
|
}
|
|
|
compileLine(c, tokenizerNext(c->tokenizer));
|
|
|
}
|
|
|
- consumeToken(c, END);
|
|
|
+ consumeToken(c, TT_END);
|
|
|
i32 popVars = hasLocalVar(c) ? -c->variables->index : 0;
|
|
|
- codePushInstruction(c, RETURN);
|
|
|
+ codePushInstruction(c, PUSH_INT32);
|
|
|
+ codePushI32(c, 0);
|
|
|
+ codePushInstruction(c, RETURN_VALUE);
|
|
|
codePushI32(c, popVars);
|
|
|
+ for(Return* r = c->returns; r != nullptr; r = r->next) {
|
|
|
+ codeRewriteI32(c, (size_t)r->address, popVars);
|
|
|
+ }
|
|
|
+ cleanReturns(c);
|
|
|
codeRewriteI32(c, stackVarsLoc, popVars - vars);
|
|
|
|
|
|
while(c->variables != resetVar) {
|
|
|
@@ -313,55 +400,71 @@ static void compileCallFunction(Context* c, Token t) {
|
|
|
codePushInstruction(c, PUSH_INT32);
|
|
|
codePushI32(c, 0);
|
|
|
|
|
|
- consumeToken(c, OPEN_ROUND_BRACKET);
|
|
|
+ consumeToken(c, TT_OPEN_ROUND_BRACKET);
|
|
|
i32 offset = 0;
|
|
|
while(true) {
|
|
|
- if(tokenizerPeek(c->tokenizer).type == CLOSE_ROUND_BRACKET) {
|
|
|
+ if(tokenizerPeek(c->tokenizer).type == TT_CLOSE_ROUND_BRACKET) {
|
|
|
break;
|
|
|
}
|
|
|
offset++;
|
|
|
compileExpression(c);
|
|
|
- if(tokenizerPeek(c->tokenizer).type == COMMA) {
|
|
|
- consumeToken(c, COMMA);
|
|
|
+ if(tokenizerPeek(c->tokenizer).type == TT_COMMA) {
|
|
|
+ consumeToken(c, TT_COMMA);
|
|
|
}
|
|
|
}
|
|
|
- consumeToken(c, CLOSE_ROUND_BRACKET);
|
|
|
+ consumeToken(c, TT_CLOSE_ROUND_BRACKET);
|
|
|
|
|
|
codePushInstruction(c, JUMP_SUB);
|
|
|
size_t pos = codeGetWritePosition(c);
|
|
|
- codePushI32(c, addCallFunction(c, t.stringValue, (i32)pos));
|
|
|
+ codePushI32(c, addCallFunction(c, t.stringValue, (i32)pos, offset));
|
|
|
codePushI32(c, offset);
|
|
|
- consumeNewline(c);
|
|
|
+}
|
|
|
+
|
|
|
+static void compileReturn(Context* c) {
|
|
|
+ if(tokenizerPeek(c->tokenizer).type == TT_NEWLINE) {
|
|
|
+ consumeNewline(c);
|
|
|
+ codePushInstruction(c, PUSH_INT32);
|
|
|
+ codePushI32(c, 0);
|
|
|
+ } else {
|
|
|
+ compileExpression(c);
|
|
|
+ }
|
|
|
+ codePushInstruction(c, RETURN_VALUE);
|
|
|
+ i32 address = (i32)codeGetWritePosition(c);
|
|
|
+ codePushI32(c, 0);
|
|
|
+ addReturn(c, address, true);
|
|
|
}
|
|
|
|
|
|
static void compileLine(Context* c, Token token) {
|
|
|
- if(token.type == NEWLINE) {
|
|
|
+ if(token.type == TT_NEWLINE) {
|
|
|
c->line++;
|
|
|
return;
|
|
|
- } else if(token.type == IF) {
|
|
|
+ } else if(token.type == TT_IF) {
|
|
|
compileIf(c);
|
|
|
return;
|
|
|
- } else if(token.type == FUNCTION) {
|
|
|
+ } else if(token.type == TT_FUNCTION) {
|
|
|
compileFunction(c);
|
|
|
return;
|
|
|
- } else if(token.type == DOLLAR) {
|
|
|
- token = consumeToken(c, LITERAL);
|
|
|
+ } else if(token.type == TT_RETURN) {
|
|
|
+ compileReturn(c);
|
|
|
+ return;
|
|
|
+ } else if(token.type == TT_DOLLAR) {
|
|
|
+ token = consumeToken(c, TT_LITERAL);
|
|
|
compileSetVariable(c, token.stringValue, true);
|
|
|
return;
|
|
|
- } else if(token.type != LITERAL) {
|
|
|
- unexpectedToken(c, token);
|
|
|
+ } else if(token.type != TT_LITERAL) {
|
|
|
+ unexpectedToken(c, token, __LINE__);
|
|
|
}
|
|
|
const char* s = token.stringValue;
|
|
|
if(strcmp(s, "print") == 0) {
|
|
|
- while(tokenizerPeek(c->tokenizer).type != NEWLINE) {
|
|
|
+ while(tokenizerPeek(c->tokenizer).type != TT_NEWLINE) {
|
|
|
compileExpression(c);
|
|
|
codePushInstruction(c, PRINT);
|
|
|
}
|
|
|
consumeNewline(c);
|
|
|
codePushInstruction(c, PRINT_NEWLINE);
|
|
|
- } else if(tokenizerPeek(c->tokenizer).type == EQUAL) {
|
|
|
+ } else if(tokenizerPeek(c->tokenizer).type == TT_EQUAL) {
|
|
|
compileSetVariable(c, s, false);
|
|
|
- } else if(tokenizerPeek(c->tokenizer).type == OPEN_ROUND_BRACKET) {
|
|
|
+ } else if(tokenizerPeek(c->tokenizer).type == TT_OPEN_ROUND_BRACKET) {
|
|
|
compileCallFunction(c, token);
|
|
|
} else {
|
|
|
THROW_ERROR("Unexpected literal(%s)", s);
|
|
|
@@ -376,7 +479,7 @@ static void parseTokens(Context* c) {
|
|
|
|
|
|
while(true) {
|
|
|
Token token = tokenizerNext(c->tokenizer);
|
|
|
- if(token.type == INVALID) {
|
|
|
+ if(token.type == TT_INVALID) {
|
|
|
break;
|
|
|
}
|
|
|
compileLine(c, token);
|