Author: mesyeti <mesyeti@mesyeti.uk>
add imports
basic/lib.bas | 5 ++ basic/source/compiler.c | 26 ++++++++--- basic/source/compiler.h | 2 basic/source/frontend/c89.c | 54 +++++++++++++++++++++++-- basic/source/frontend/c89.h | 2 basic/source/import.c | 59 ++++++++++++++++++++++++++++ basic/source/import.h | 13 ++++++ basic/source/lexer.c | 4 + basic/source/lexer.h | 2 basic/source/main.c | 44 +++++++++++++++++++- basic/source/options.c | 6 ++ basic/source/options.h | 14 ++++++ basic/source/parser.c | 73 ++++++++++++++++++++++++++++++++-- basic/source/parser.h | 10 ++++ basic/source/semanticAnalysis.c | 2 basic/source/state.c | 25 +++++++++++ basic/source/state.h | 1 basic/source/util.c | 26 ++++++++++++ basic/source/util.h | 4 + basic/test.bas | 14 +----
diff --git a/basic/lib.bas b/basic/lib.bas new file mode 100644 index 0000000000000000000000000000000000000000..f526bfdebfd7c3243eb9b8249b13d5491622decc --- /dev/null +++ b/basic/lib.bas @@ -0,0 +1,5 @@ +extern func puts(str as ptr(char)) i32 + +sub PrintStrLn(str as ptr(char)) + puts(str) +end diff --git a/basic/source/compiler.c b/basic/source/compiler.c index d990335cd0f2ef79f3a16773d0b98f4c3add6ac2..be44878fc65b5df8a663184962e55f7e58d23efc 100644 --- a/basic/source/compiler.c +++ b/basic/source/compiler.c @@ -41,9 +41,11 @@ case NODE_DIM: break; case NODE_EXTERN: break; case NODE_TYPE_DEF: break; case NODE_TYPE_BLOCK: break; - case NODE_FUNC_CALL: frontend.compileFuncCall(&node->funcCall); break; - case NODE_IF: frontend.compileIf(&node->ifStat); break; - case NODE_WHILE: frontend.compileWhile(&node->whileStat); break; + case NODE_PROGRAM: break; + case NODE_IMPORT: break; + case NODE_FUNC_CALL: frontend.compileFuncCall(&node->funcCall); break; + case NODE_IF: frontend.compileIf(&node->ifStat); break; + case NODE_WHILE: frontend.compileWhile(&node->whileStat); break; case NODE_BINARY_OP: { if (node->bin.op == TOKEN_ASSIGN) { frontend.compileAssign(&node->bin); @@ -66,18 +68,26 @@ } } void Compiler_Run(void) { + bool program = false; + for (size_t i = 0; i < compiler.len; ++ i) { Node* node = &compiler.nodes[i]; Compiler_CompileTopNode(node, false); + + if (node->i.type == NODE_PROGRAM) { + program = true; + } } - frontend.beginMain(); + if (program) { + frontend.beginMain(); - for (size_t i = 0; i < compiler.len; ++ i) { - Node* node = &compiler.nodes[i]; + for (size_t i = 0; i < compiler.len; ++ i) { + Node* node = &compiler.nodes[i]; - Compiler_CompileTopNode(node, true); + Compiler_CompileTopNode(node, true); + } } - frontend.finish(); + frontend.finish(program); } diff --git a/basic/source/compiler.h b/basic/source/compiler.h index a6dea3c1f4865a22ba92efdef4d07d16faa865e0..7cbc036cf50b6ad3ce9329f9063de654d0cdc20f 100644 --- a/basic/source/compiler.h +++ b/basic/source/compiler.h @@ -4,7 +4,7 @@ #include "parser.h" typedef struct { - void (*finish)(void); + void (*finish)(bool program); void (*beginMain)(void); void (*compileFuncDef)(FuncDefNode* node); void (*compileDim)(DimNode* node); diff --git a/basic/source/frontend/c89.c b/basic/source/frontend/c89.c index 5294a3098d097ffd76a25ef0c420532b3f766c32..c9c894aecf0889005bd5c8f36e5b5c7dd51c67ec 100644 --- a/basic/source/frontend/c89.c +++ b/basic/source/frontend/c89.c @@ -1,17 +1,27 @@ #include <stdio.h> #include <assert.h> #include "c89.h" +#include "../util.h" #include "../state.h" +#include "../options.h" #include "../semanticAnalysis.h" static FILE* out; static bool inMain; -static void Finish(void) { - fprintf(out, "}\n"); +static void Finish(bool program) { + if (program) { + fprintf(out, "}\n"); + } fflush(out); - const char* cmd = "gcc out.c -o out -fno-builtin"; + char cmd[512]; + + snprintf( + cmd, 512, "gcc %s %s.c -o %s -fno-builtin", + options.obj? "-c" : "", options.out, options.out + ); + puts(cmd); system(cmd); } @@ -249,9 +259,18 @@ fprintf(out, "} nitron_%s;", type->name); } -Frontend Frontend_C89(void) { - out = fopen("out.c", "w"); +Frontend Frontend_C89() { + char* path = ConcatString(options.out, ".c"); + + out = fopen(path, "w"); inMain = false; + + if (!out) { + fprintf(stderr, "Failed to open '%s'\n", path); + exit(1); + } + + free(path); fprintf(out, "#include <stdint.h>\n"); fprintf(out, "#include <stdlib.h>\n"); @@ -275,6 +294,31 @@ Var* var = &state.scopes[0][i]; CompileUsedType(var->type); fprintf(out, " nitron_%s;\n", var->name); + } + + // declare all functions + for (size_t i = 0; i < state.funcNum; ++ i) { + Function* func = &state.funcs[i]; + + if (func->sub) { + fprintf(out, "void"); + } + else { + CompileUsedType(func->ret); + } + + fprintf(out, " %s(", func->name); + + for (size_t j = 0; j < func->paramsLen; ++ j) { + CompileUsedType(func->params[j].type); + fprintf(out, " %s", func->params[j].name); + + if (j < func->paramsLen - 1) { + fprintf(out, ", "); + } + } + + fprintf(out, ");\n"); } return (Frontend) { diff --git a/basic/source/frontend/c89.h b/basic/source/frontend/c89.h index 6edada43c8f3f07c258e762f901b999433737a98..081b217add16ac7b107ab5b813affee1e7ec66eb 100644 --- a/basic/source/frontend/c89.h +++ b/basic/source/frontend/c89.h @@ -3,6 +3,6 @@ #define N_FRONTEND_C89_H #include "../compiler.h" -Frontend Frontend_C89(void); +Frontend Frontend_C89(); #endif diff --git a/basic/source/import.c b/basic/source/import.c new file mode 100644 index 0000000000000000000000000000000000000000..27f5a8bd2e8be5dc46d9442b968de533aa28a604 --- /dev/null +++ b/basic/source/import.c @@ -0,0 +1,59 @@ +#include "mem.h" +#include "util.h" +#include "state.h" +#include "import.h" + +static char** paths; +static size_t pathsLen; + +void Import_Init(void) { + paths = NULL; + pathsLen = 0; +} + +void Import_Free(void) { + if (paths) { + free(paths); + } +} + +void Import_AddPath(const char* path) { + ++ pathsLen; + + paths = SafeRealloc(paths, pathsLen * sizeof(char*)); + paths[pathsLen - 1] = NewString(path); +} + +const char* Import_Search(const char* name) { + for (size_t i = 0; i < pathsLen; ++ i) { + // TODO: improve using some string builder thingy + char* str1 = ConcatString(paths[i], "/"); + char* str2 = ConcatString(str1, name); + char* str3 = ConcatString(str2, ".bas"); + free(str1); + free(str2); + + if (FileExists(str3)) { + return str3; + } + + free(str3); + } + + return NULL; +} + +void Import_Run(Node* nodes, size_t len) { + for (size_t i = 0; i < len; ++ i) { + if (nodes[i].i.type == NODE_IMPORT) { + const char* path = Import_Search(nodes[i].import.name); + + if (!path) { + PrintError(nodes[i].i.err, "Couldn't find '%s'", nodes[i].import.name); + } + if (!State_ImportFile(path)) { + PrintError(nodes[i].i.err, "Failed to import '%s'", nodes[i].import.name); + } + } + } +} diff --git a/basic/source/import.h b/basic/source/import.h new file mode 100644 index 0000000000000000000000000000000000000000..28b8f2ec4160bb17d64470699ab60b1b8f09f2e1 --- /dev/null +++ b/basic/source/import.h @@ -0,0 +1,13 @@ +#ifndef N_IMPORT_H +#define N_IMPORT_H + +#include "common.h" +#include "parser.h" + +void Import_Init(void); +void Import_Free(void); +void Import_AddPath(const char* path); +const char* Import_Search(const char* name); +void Import_Run(Node* nodes, size_t len); + +#endif diff --git a/basic/source/lexer.c b/basic/source/lexer.c index f89957c27c34fce0e6732624e39f333853c991d0..90404adda62e05457c7b27eae04ed267139b0c24 100644 --- a/basic/source/lexer.c +++ b/basic/source/lexer.c @@ -178,7 +178,9 @@ {"else", TOKEN_ELSE}, {"while", TOKEN_WHILE}, {"do", TOKEN_DO}, {"type", TOKEN_TYPE}, - {"union", TOKEN_UNION} + {"union", TOKEN_UNION}, + {"program", TOKEN_PROGRAM}, + {"import", TOKEN_IMPORT} }; for (size_t i = 0; i < sizeof(keywords) / sizeof(Keyword); ++ i) { diff --git a/basic/source/lexer.h b/basic/source/lexer.h index ae41e856addb88076f84857f63faf1732a4e6918..48e11c8255f290178d8bf33d7a55184ce107b7bc 100644 --- a/basic/source/lexer.h +++ b/basic/source/lexer.h @@ -24,6 +24,8 @@ TOKEN_WHILE, TOKEN_DO, TOKEN_TYPE, TOKEN_UNION, + TOKEN_PROGRAM, + TOKEN_IMPORT, // misc TOKEN_IDENTIFIER, diff --git a/basic/source/main.c b/basic/source/main.c index 9df35583c37a392556ade14513c63b1bd522fac0..363f4cdb1b3c62ff85bae89c423d439909fceaf3 100644 --- a/basic/source/main.c +++ b/basic/source/main.c @@ -1,12 +1,22 @@ #include <stdio.h> #include <string.h> +#include "util.h" #include "lexer.h" #include "state.h" #include "parser.h" #include "common.h" +#include "import.h" +#include "options.h" #include "semanticAnalysis.h" #include "frontend/c89.h" +static const char* usage = + "Usage: %s SOURCE [FLAGS]\n\n" + "Flags:\n" + " -i PATH - Add an import path\n" + " -o FILE - Set output file\n" + " --obj - Compile to an object file instead of an executable\n"; + int main(int argc, char** argv) { const char* source = NULL; @@ -14,10 +24,11 @@ bool printNodes = false; bool printTokens = false; bool printState = false; + Import_Init(); + if (argc == 1) { - printf( - "Usage: %s SOURCE [FLAGS]\n", argv[0] - ); + printf(usage, argv[0]); + return 0; } for (int i = 1; i < argc; ++ i) { @@ -38,6 +49,26 @@ } else if (strcmp(argv[i], "--ds") == 0) { printState = true; } + else if (strcmp(argv[i], "-i") == 0) { + ++ i; + if (i >= argc) { + fprintf(stderr, "-i expects PATH parameter\n"); + return 1; + } + + Import_AddPath(argv[i]); + } + else if (strcmp(argv[i], "-o") == 0) { + ++ i; + if (i >= argc) { + fprintf(stderr, "-o expects FILE parameter\n"); + } + + options.out = argv[i]; + } + else if (strcmp(argv[i], "--obj") == 0) { + options.obj = true; + } else { fprintf(stderr, "Unknown flag: %s\n", argv[i]); return 1; @@ -69,7 +100,12 @@ return 0; } + char* sourceDir = DirName(source); + Import_AddPath(sourceDir); + free(sourceDir); + SemanticAnalysis_Init(8); + Import_Run(ret, len); for (size_t i = 0; i < len; ++ i){ SemanticAnalysis_Analyse(&ret[i]); @@ -83,6 +119,8 @@ Compiler_Init(ret, len); Frontend_Init(Frontend_C89()); Compiler_Run(); + + Import_Free(); return 0; } diff --git a/basic/source/options.c b/basic/source/options.c new file mode 100644 index 0000000000000000000000000000000000000000..41b61f2a7cdb7e0e27d29091e69b0c828fc463dd --- /dev/null +++ b/basic/source/options.c @@ -0,0 +1,6 @@ +#include "options.h" + +Options options = { + .out = "out", + .obj = false +}; diff --git a/basic/source/options.h b/basic/source/options.h new file mode 100644 index 0000000000000000000000000000000000000000..0bd01cf9b9891bb8fe629525d3ba8bd4199b165a --- /dev/null +++ b/basic/source/options.h @@ -0,0 +1,14 @@ +#ifndef N_OPTIONS_H +#define N_OPTIONS_H + +#include "common.h" + +typedef struct { + const char* out; + + bool obj; +} Options; + +extern Options options; + +#endif diff --git a/basic/source/parser.c b/basic/source/parser.c index 7d8deeac160ff733981d06c78d17548c4ef735db..5e2a744114afdf6c5f07f22353260cca243f6632 100644 --- a/basic/source/parser.c +++ b/basic/source/parser.c @@ -638,15 +638,44 @@ node.typeBlock = ret; return node; } +static Node ParseProgram(Parser* p) { + Advance(p); + Expect(p, TOKEN_IDENTIFIER); + Advance(p); + Expect(p, TOKEN_LINE); + + Node node; + node.i = INFO(NODE_PROGRAM); + return node; +} + +static Node ParseImport(Parser* p) { + ImportNode ret; + ret.i = INFO(NODE_IMPORT); + + Advance(p); + Expect(p, TOKEN_IDENTIFIER); + ret.name = NewString(p->tokens[p->i].contents); + + Advance(p); + Expect(p, TOKEN_LINE); + + Node node; + node.import = ret; + return node; +} + static Node ParseNode(Parser* p) { switch (p->tokens[p->i].type) { case TOKEN_FUNC: - case TOKEN_SUB: return ParseFuncDef(p); - case TOKEN_DIM: return ParseDim(p); - case TOKEN_EXTERN: return ParseExtern(p); - case TOKEN_IF: return ParseIf(p); - case TOKEN_WHILE: return ParseWhile(p); - case TOKEN_TYPE: return ParseTypeDef(p); + case TOKEN_SUB: return ParseFuncDef(p); + case TOKEN_DIM: return ParseDim(p); + case TOKEN_EXTERN: return ParseExtern(p); + case TOKEN_IF: return ParseIf(p); + case TOKEN_WHILE: return ParseWhile(p); + case TOKEN_TYPE: return ParseTypeDef(p); + case TOKEN_PROGRAM: return ParseProgram(p); + case TOKEN_IMPORT: return ParseImport(p); case TOKEN_LPAREN: case TOKEN_ACCESS: case TOKEN_IDENTIFIER: { @@ -833,6 +862,14 @@ } puts("end"); break; } + case NODE_PROGRAM: { + puts("program (name)"); + break; + } + case NODE_IMPORT: { + printf("import %s\n", node->import.name); + break; + } default: { printf("unknown %d\n", node->i.type); assert(0); @@ -940,6 +977,28 @@ Parser_FreeNode(node->whileStat.condition); FreeNodes(node->whileStat.body, node->whileStat.bodyLen); break; } + case NODE_TYPE_DEF: { + free(node->typeDef.name); + Parser_FreeNode(node->typeDef.type); + break; + } + case NODE_TYPE_BLOCK: { + free(node->typeBlock.name); + + for (size_t i = 0; i < node->typeBlock.fieldsNum; ++ i) { + free(node->typeBlock.fields[i].name); + Parser_FreeNode(node->typeBlock.fields[i].type); + } + + if (node->typeBlock.fields) { + free(node->typeBlock.fields); + } + break; + } + case NODE_IMPORT: { + free(node->import.name); + break; + } default: return; } } @@ -961,6 +1020,8 @@ case NODE_IF: return "if"; case NODE_WHILE: return "while"; case NODE_TYPE_DEF: return "type def"; case NODE_TYPE_BLOCK: return "type block"; + case NODE_PROGRAM: return "program"; + case NODE_IMPORT: return "import"; default: return "???"; } } diff --git a/basic/source/parser.h b/basic/source/parser.h index afda080435be4578eba425dc8401b6b0c203504e..0bb03b42b3ad8fce6e3750b92757feabd3ccfe04 100644 --- a/basic/source/parser.h +++ b/basic/source/parser.h @@ -20,7 +20,9 @@ NODE_EXTERN, NODE_IF, NODE_WHILE, NODE_TYPE_DEF, - NODE_TYPE_BLOCK + NODE_TYPE_BLOCK, + NODE_PROGRAM, + NODE_IMPORT } NodeType; typedef struct { @@ -135,6 +137,11 @@ TypeBlockField* fields; size_t fieldsNum; } TypeBlockNode; +typedef struct { + NodeInfo i; + char* name; +} ImportNode; + union Node { NodeInfo i; IntNode integer; @@ -151,6 +158,7 @@ IfNode ifStat; WhileNode whileStat; TypeDefNode typeDef; TypeBlockNode typeBlock; + ImportNode import; }; typedef struct { diff --git a/basic/source/semanticAnalysis.c b/basic/source/semanticAnalysis.c index f3c6326aed9182df120d911c44d66cb35c876fce..86d59ee8a3f4ac1c0555a6796bddfa5a330780b7 100644 --- a/basic/source/semanticAnalysis.c +++ b/basic/source/semanticAnalysis.c @@ -495,6 +495,8 @@ State_AddType(newType); return GET_PRIM("unit", 0); } + case NODE_PROGRAM: return GET_PRIM("unit", 0); + case NODE_IMPORT: return GET_PRIM("unit", 0); default: { Unexpected(node); } diff --git a/basic/source/state.c b/basic/source/state.c index cbc1d5d22a58a42866e7bbc3fe35afe4afd1afb9..216160a16844058fa5187d8339e8479fa6181406 100644 --- a/basic/source/state.c +++ b/basic/source/state.c @@ -3,6 +3,8 @@ #include#include "mem.h" #include "util.h" #include "state.h" +#include "parser.h" +#include "semanticAnalysis.h" State state; @@ -141,3 +143,26 @@ return (type.type == TYPE_PRIM) && ( (type.primType == PRIM_UINT) || (type.primType == PRIM_INT) ); } + +bool State_ImportFile(const char* path) { + FILE* file = fopen(path, "r"); + + if (!file) { + return false; + } + + Parser parser = Parser_Init(file, path, false); + + size_t len; + Node* nodes = Parser_Parse(&parser, &len); + + for (size_t i = 0; i < len; ++ i) { + SemanticAnalysis_EvalType(&nodes[i]); + } + + for (size_t i = 0; i < len; ++ i) { + Parser_FreeNode(&nodes[i]); + } + free(nodes); + return true; +} diff --git a/basic/source/state.h b/basic/source/state.h index a52b2bed4cd11062a5743e1852a08a80ed514053..decf9f57253ba9f349bcc40562a1dd26754b6492 100644 --- a/basic/source/state.h +++ b/basic/source/state.h @@ -83,5 +83,6 @@ bool State_UsedTypeEq(UsedType a, UsedType b); void State_DumpInfo(void); Type* State_GetTypeFromUsed(UsedType type); bool State_IsInt(Type type); +bool State_ImportFile(const char* path); #endif diff --git a/basic/source/util.c b/basic/source/util.c index 3524bb0b4c307b7a93a3b07c1c2c13fe438d9956..329b7cd160d0dcaa8ce7a5298d49f6faf6540d44 100644 --- a/basic/source/util.c +++ b/basic/source/util.c @@ -1,3 +1,4 @@ +#include <stdio.h> #include <string.h> #include "mem.h" #include "common.h" @@ -14,3 +15,28 @@ strcpy(ret, first); strcat(ret, second); return ret; } + +bool FileExists(const char* name) { + FILE* file = fopen(name, "r"); + + if (!file) { + return false; + } + + fclose(file); + return true; +} + +char* DirName(const char* name) { + char* ret = NewString(name); + char* slash = strchr(ret, '/'); + + if (slash) { + *slash = 0; + return ret; + } + else { + free(ret); + return NewString("."); + } +} diff --git a/basic/source/util.h b/basic/source/util.h index aa57880a037cf533331c0e4cdedfcef4953b4465..549ac204e7c3d0d2a4d6e6ecd968840c3b369b96 100644 --- a/basic/source/util.h +++ b/basic/source/util.h @@ -1,7 +1,7 @@ #ifndef N_UTIL_H #define N_UTIL_H -#include <stdio.h> +#include "common.h" #define FUNCTION_POINTER(TYPE, NAME, ...) TYPE (*NAME)(__VA_ARGS__) #define MIN(A, B) (((A) < (B))? (A) : (B)) @@ -18,5 +18,7 @@ } while (0); char* NewString(const char* src); char* ConcatString(const char* first, const char* second); +bool FileExists(const char* name); +char* DirName(const char* name); #endif diff --git a/basic/test.bas b/basic/test.bas index a5731efea260b2d45a679be8a4c84801d8f33ac6..532d91881fffa19498acf9852c9bc48d1077876e 100644 --- a/basic/test.bas +++ b/basic/test.bas @@ -1,16 +1,10 @@ 'NITRON +program test + extern func puts(str as ptr(char)) i32 extern func printf(str as ptr(char), val as int) i32 -dim foo as int -foo = 5 - -dim fooPtr as ptr(int) -fooPtr = &foo - -printf("foo = %d\n", ^fooPtr) +import lib -^fooPtr = 6 - -printf("foo = %d\n", foo) +PrintStrLn("hello world!")