|
|
@@ -16,6 +16,15 @@ struct Variable {
|
|
|
char name[];
|
|
|
};
|
|
|
|
|
|
+typedef struct Function Function;
|
|
|
+
|
|
|
+struct Function {
|
|
|
+ Function* next;
|
|
|
+ i32 address;
|
|
|
+ i32 valueAddress;
|
|
|
+ char name[];
|
|
|
+};
|
|
|
+
|
|
|
typedef struct {
|
|
|
Tokenizer* tokenizer;
|
|
|
Code* code;
|
|
|
@@ -23,6 +32,8 @@ typedef struct {
|
|
|
int line;
|
|
|
jmp_buf jump;
|
|
|
Variable* variables;
|
|
|
+ Function* functions;
|
|
|
+ bool inFunction;
|
|
|
} Context;
|
|
|
|
|
|
#define THROW_ERROR(format, ...) \
|
|
|
@@ -82,6 +93,51 @@ static i32 addVariable(Context* c, const char* name) {
|
|
|
return v->index;
|
|
|
}
|
|
|
|
|
|
+static void addRawFunction(
|
|
|
+ Context* c, const char* name, i32 address, i32 valueAddress) {
|
|
|
+ size_t l = strlen(name) + 1;
|
|
|
+ Function* f = memoryAllocate(sizeof(Function) + l);
|
|
|
+ if(f == nullptr) {
|
|
|
+ THROW_ERROR("Too less memory for functions");
|
|
|
+ }
|
|
|
+ f->next = c->functions;
|
|
|
+ f->address = address;
|
|
|
+ f->valueAddress = valueAddress;
|
|
|
+ memcpy(f->name, name, l);
|
|
|
+ c->functions = f;
|
|
|
+}
|
|
|
+
|
|
|
+static void addFunction(Context* c, const char* name, i32 address) {
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ f->address = address;
|
|
|
+ codeRewriteI32(c, (size_t)f->valueAddress, address);
|
|
|
+ }
|
|
|
+ addRawFunction(c, name, address, -1);
|
|
|
+}
|
|
|
+
|
|
|
+static i32 addCallFunction(Context* c, const char* name, i32 address) {
|
|
|
+ for(Function* f = c->functions; f != nullptr; f = f->next) {
|
|
|
+ if(strcmp(f->name, name) == 0 && f->address >= 0) {
|
|
|
+ return f->address;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ addRawFunction(c, name, -1, address);
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+static void checkForInvalidFunctions(Context* c) {
|
|
|
+ for(Function* f = c->functions; f != nullptr; f = f->next) {
|
|
|
+ if(f->address < 0) {
|
|
|
+ THROW_ERROR("Unmapped function call '%s'", f->name);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static void cleanContext(Context* c) {
|
|
|
Variable* v = c->variables;
|
|
|
while(v != nullptr) {
|
|
|
@@ -90,6 +146,14 @@ static void cleanContext(Context* c) {
|
|
|
v = next;
|
|
|
}
|
|
|
c->variables = nullptr;
|
|
|
+
|
|
|
+ Function* f = c->functions;
|
|
|
+ while(f != nullptr) {
|
|
|
+ Function* next = f->next;
|
|
|
+ memoryFree(f);
|
|
|
+ f = next;
|
|
|
+ }
|
|
|
+ c->functions = nullptr;
|
|
|
}
|
|
|
|
|
|
[[noreturn]] static void unexpectedToken(Context* c, Token token) {
|
|
|
@@ -170,6 +234,41 @@ static void compileSetVariable(Context* c, const char* name) {
|
|
|
codePushI32(c, index);
|
|
|
}
|
|
|
|
|
|
+static void compileFunction(Context* c) {
|
|
|
+ if(c->inFunction) {
|
|
|
+ THROW_ERROR("Functions in functions are not allowed");
|
|
|
+ }
|
|
|
+ c->inFunction = true;
|
|
|
+ codePushInstruction(c, JUMP);
|
|
|
+ size_t pos = codeGetWritePosition(c);
|
|
|
+ codePushI32(c, 0);
|
|
|
+
|
|
|
+ Token t = consumeToken(c, LITERAL);
|
|
|
+ addFunction(c, t.stringValue, (i32)codeGetWritePosition(c));
|
|
|
+ consumeToken(c, OPEN_ROUND_BRACKET);
|
|
|
+ consumeToken(c, CLOSE_ROUND_BRACKET);
|
|
|
+ while(true) {
|
|
|
+ if(tokenizerPeek(c->tokenizer).type == END) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ compileLine(c, tokenizerNext(c->tokenizer));
|
|
|
+ }
|
|
|
+ consumeToken(c, END);
|
|
|
+ codePushInstruction(c, RETURN);
|
|
|
+
|
|
|
+ codeRewriteI32(c, pos, (i32)codeGetWritePosition(c));
|
|
|
+ c->inFunction = false;
|
|
|
+}
|
|
|
+
|
|
|
+static void compileCallFunction(Context* c, Token t) {
|
|
|
+ codePushInstruction(c, JUMP_SUB);
|
|
|
+ size_t pos = codeGetWritePosition(c);
|
|
|
+ codePushI32(c, addCallFunction(c, t.stringValue, (i32)pos));
|
|
|
+ consumeToken(c, OPEN_ROUND_BRACKET);
|
|
|
+ consumeToken(c, CLOSE_ROUND_BRACKET);
|
|
|
+ consumeNewline(c);
|
|
|
+}
|
|
|
+
|
|
|
static void compileLine(Context* c, Token token) {
|
|
|
if(token.type == NEWLINE) {
|
|
|
c->line++;
|
|
|
@@ -177,6 +276,9 @@ static void compileLine(Context* c, Token token) {
|
|
|
} else if(token.type == IF) {
|
|
|
compileIf(c);
|
|
|
return;
|
|
|
+ } else if(token.type == FUNCTION) {
|
|
|
+ compileFunction(c);
|
|
|
+ return;
|
|
|
} else if(token.type != LITERAL) {
|
|
|
unexpectedToken(c, token);
|
|
|
}
|
|
|
@@ -190,6 +292,8 @@ static void compileLine(Context* c, Token token) {
|
|
|
codePushInstruction(c, PRINT_NEWLINE);
|
|
|
} else if(tokenizerPeek(c->tokenizer).type == EQUAL) {
|
|
|
compileSetVariable(c, s);
|
|
|
+ } else if(tokenizerPeek(c->tokenizer).type == OPEN_ROUND_BRACKET) {
|
|
|
+ compileCallFunction(c, token);
|
|
|
} else {
|
|
|
THROW_ERROR("Unexpected literal(%s)", s);
|
|
|
}
|
|
|
@@ -203,7 +307,7 @@ static void parseTokens(Context* c) {
|
|
|
|
|
|
while(true) {
|
|
|
Token token = tokenizerNext(c->tokenizer);
|
|
|
- if(token.type == END) {
|
|
|
+ if(token.type == INVALID) {
|
|
|
break;
|
|
|
}
|
|
|
compileLine(c, token);
|
|
|
@@ -212,6 +316,7 @@ static void parseTokens(Context* c) {
|
|
|
if(c->variables != nullptr) {
|
|
|
codeRewriteI32(c, stackVarsLoc, c->variables->index + 1);
|
|
|
}
|
|
|
+ checkForInvalidFunctions(c);
|
|
|
}
|
|
|
|
|
|
Error compileFile(Tokenizer* t, Code* code) {
|