Author: mesyeti <mesyeti@mesyeti.uk>
working semantic analysis
basic-boot/source/lexer.c | 7 basic-boot/source/lexer.h | 1 basic-boot/source/main.c | 32 +++ basic-boot/source/parser.c | 20 + basic-boot/source/parser.h | 2 basic-boot/source/semanticAnalysis.c | 271 ++++++++++++++++++----------- basic-boot/source/semanticAnalysis.h | 2 basic-boot/source/state.c | 34 +++ basic-boot/source/state.h | 2 basic-boot/test.bas | 9
diff --git a/basic-boot/source/lexer.c b/basic-boot/source/lexer.c index 6bc99427534016514e58cc0457619c268ea5a66c..06ae344d28b518f6745713a23d6a05af98a2075c 100644 --- a/basic-boot/source/lexer.c +++ b/basic-boot/source/lexer.c @@ -162,6 +162,11 @@ *token = TOKEN(TOKEN_QUIT, NULL); String_Free(&string); return LEXER_TOKEN; } + if (!strcmp(string.contents, "ptr")) { + *token = TOKEN(TOKEN_PTR, NULL); + String_Free(&string); + return LEXER_TOKEN; + } if (!strcmp(string.contents, "end")) { *token = TOKEN(TOKEN_END, NULL); String_Free(&string); @@ -205,7 +210,7 @@ } } const char* Lexer_TypeAsString(Token* token) { - return Lexer_TypeVAString(token->type); + return Lexer_TypeVAsString(token->type); } void Lexer_PrintToken(Token* token) { diff --git a/basic-boot/source/lexer.h b/basic-boot/source/lexer.h index 856b046d31b1dffaa1201de6ebeb3247712c30f2..0f3e15fa515f4e38f3882d1480fa461bb3e6a706 100644 --- a/basic-boot/source/lexer.h +++ b/basic-boot/source/lexer.h @@ -1,6 +1,7 @@ #ifndef N_LEXER_H #define N_LEXER_H +#include <stdio.h> #include "error.h" #include "common.h" diff --git a/basic-boot/source/main.c b/basic-boot/source/main.c index 72c0dc0bfe9064fe3d305ded3c7091d1e0f7ec1b..a8e3fa95d0afaa3a9ac44b0c75f86f336a150b30 100644 --- a/basic-boot/source/main.c +++ b/basic-boot/source/main.c @@ -1,10 +1,16 @@ #include <stdio.h> +#include <string.h> #include "lexer.h" +#include "state.h" #include "parser.h" #include "common.h" +#include "semanticAnalysis.h" int main(int argc, char** argv) { const char* source = NULL; + + bool printNodes = false; + bool printTokens = false; if (argc == 1) { printf( @@ -21,6 +27,12 @@ } source = argv[i]; } + else if (strcmp(argv[i], "--dp") == 0) { + printNodes = true; + } + else if (strcmp(argv[i], "--dl") == 0) { + printTokens = true; + } else { fprintf(stderr, "Unknown flag: %s\n", argv[i]); return 1; @@ -39,14 +51,26 @@ fprintf(stderr, "Failed to open '%s'\n", source); return 1; } - Parser parser = Parser_Init(sourceFile, source); + Parser parser = Parser_Init(sourceFile, source, printTokens); size_t len; - Node* ret = Parser_Parse(&parser, &len) ; + Node* ret = Parser_Parse(&parser, &len); - for (size_t i = 0; i < len; ++ i) { - Parser_PrintNode(&ret[i]); + if (printNodes) { + for (size_t i = 0; i < len; ++ i) { + Parser_PrintNode(&ret[i]); + } + + return 0; } + + SemanticAnalysis_Init(8); + + for (size_t i = 0; i < len; ++ i){ + SemanticAnalysis_Analyse(&ret[i]); + } + + State_DumpInfo(); return 0; } diff --git a/basic-boot/source/parser.c b/basic-boot/source/parser.c index e73fcb98333d3a942f52bea9e4df501f6efef394..2ae234724509cccb38c4a2cc5db7aacef610e5c1 100644 --- a/basic-boot/source/parser.c +++ b/basic-boot/source/parser.c @@ -1,10 +1,11 @@ +#include <stdio.h> #include <assert.h> #include <string.h> #include "mem.h" #include "util.h" #include "parser.h" -Parser Parser_Init(FILE* file, const char* fileName) { +Parser Parser_Init(FILE* file, const char* fileName, bool printTokens) { Parser ret; Lexer lexer = Lexer_Init(file, fileName); @@ -39,9 +40,14 @@ ret.tokens = tokens; ret.tokenNum = tokensLen; ret.i = 0; - // for (size_t i = 0; i < tokensLen; ++ i) { - // Lexer_PrintToken(&tokens[i]); - // } + if (printTokens) { + for (size_t i = 0; i < tokensLen; ++ i) { + Lexer_PrintToken(&tokens[i]); + } + + exit(0); + } + return ret; } @@ -218,6 +224,7 @@ switch (p->tokens[p->i].type) { case TOKEN_PTR: { Advance(p); Expect(p, TOKEN_LPAREN); + Advance(p); Node innerType = ParseType(p); ret.ptr = (PtrNode) {INFO(NODE_PTR), SafeMalloc(sizeof(Node))}; @@ -352,9 +359,9 @@ Advance(p); Expect(p, TOKEN_AS); Advance(p); - Expect(p, TOKEN_IDENTIFIER); ret.varType = SafeMalloc(sizeof(Node)); *ret.varType = ParseType(p); + Advance(p); Expect(p, TOKEN_LINE); @@ -639,7 +646,7 @@ default: return; } } -const char* Parser_NodeType(NodeType type) { +const char* Parser_TypeStr(NodeType type) { switch (type) { case NODE_NONE: return "none"; case NODE_INT: return "int"; @@ -652,5 +659,6 @@ case NODE_FUNC_DEF: return "function definition"; case NODE_DIM: return "dim"; case NODE_FUNC_CALL: return "function call"; case NODE_ASSIGN: return "assignment"; + default: return "???"; } } diff --git a/basic-boot/source/parser.h b/basic-boot/source/parser.h index c45c6d8e353de1501a282d42a1b01386faf0db9e..9e643df70c4de70613cbcd791b17f86efca21126 100644 --- a/basic-boot/source/parser.h +++ b/basic-boot/source/parser.h @@ -117,7 +117,7 @@ size_t tokenNum; size_t i; } Parser; -Parser Parser_Init(FILE* file, const char* fileName); +Parser Parser_Init(FILE* file, const char* fileName, bool printTokens); void Parser_PrintNode(Node* node); Node* Parser_Parse(Parser* p, size_t* lenOut); void Parser_FreeNode(Node* node); diff --git a/basic-boot/source/semanticAnalysis.c b/basic-boot/source/semanticAnalysis.c index c77ea59e052c92b4c1b2859fa914cfade6338a0c..c275153bb71bcc85fc06e2e5a9372f795796eb04 100644 --- a/basic-boot/source/semanticAnalysis.c +++ b/basic-boot/source/semanticAnalysis.c @@ -1,5 +1,11 @@ +#include <assert.h> +#include <string.h> +#include "mem.h" +#include "util.h" #include "error.h" -#include "semanticAnalysis" +#include "state.h" +#include "parser.h" +#include "semanticAnalysis.h" void SemanticAnalysis_Init(size_t wordSize) { State_Init(); @@ -30,14 +36,14 @@ void Expect(Node* node, NodeType type) { if (node->i.type != type) { PrintError(node->i.err, - "Expected %s, got %s", Parser_NodeType(type), - Parser_NodeType(node->i.type) + "Expected %s, got %s", Parser_TypeStr(type), + Parser_TypeStr(node->i.type) ); } } void Unexpected(Node* node) { - PrintError(node->i.err, "Unexpected %s", Parser_NodeType(node->i.type)); + PrintError(node->i.err, "Unexpected %s", Parser_TypeStr(node->i.type)); } static UsedType NodeAsUsedType(Node* node) { @@ -80,16 +86,11 @@ static UsedType EvalType(Node* node); static UsedType EvalBinOp(Node* node) { - UsedType left = EvalType(node->bin.left); - UsedType right = EvalType(node->bin.right); - bool swapped = false; - - Type* leftType; - Type* rightType; + UsedType left = EvalType(node->bin.left); + UsedType right = EvalType(node->bin.right); - again: - leftType = TypeFromUsed(left); - rightType = TypeFromUsed(right); + Type* leftType = GetTypeFromUsed(left); + Type* rightType = GetTypeFromUsed(right); if ((leftType->type != TYPE_PRIM) || (rightType->type != TYPE_PRIM)) { PrintError(node->i.err, "You can only use primitive types in binary expressions"); @@ -113,7 +114,12 @@ } else { switch (node->bin.op) { case TOKEN_ADD: - case TOKEN_SUB: return left; + case TOKEN_SUB: { + if (rightType->primType == PRIM_FLOAT) { + PrintError(node->i.err, "Cannot add float to pointer"); + } + return left; + } default: { PrintError( node->i.err, "Operation '%s' not allowed here", @@ -123,51 +129,78 @@ } } } } + else if (right.ptr) { + switch (node->bin.op) { + case TOKEN_ADD: + case TOKEN_SUB: { + if (leftType->primType == PRIM_FLOAT) { + PrintError(node->i.err, "Cannot add float to pointer"); + } + return right; + } + default: { + PrintError( + node->i.err, "Operation '%s' not allowed here", + Lexer_TypeVAsString(node->bin.op) + ); + } + } + } else if (!right.ptr) { switch (node->bin.op) { - case TOKEN_EQUAL: + case TOKEN_EQUAL: return GET_PRIM("bool", 0); case TOKEN_LESS: - case TOKEN_GREATER: return GET_PRIM("bool", 0); + case TOKEN_GREATER: { + if ((leftType->type != TYPE_PRIM) || (rightType->type != TYPE_PRIM)) { + PrintError( + node->i.err, "Operation '%s' only works on primitives", + Lexer_TypeVAsString(node->bin.op) + ); + } + return GET_PRIM("bool", 0); + } case TOKEN_ADD: case TOKEN_SUBTRACT: case TOKEN_MULTIPLY: case TOKEN_DIVIDE: case TOKEN_MOD: { - if (leftType->size == rightType->size) { - if ( - (leftType->primType == PRIM_UINT) && - (rightType->primType == PRIM_INT) - ) { - return PRIM_INT; - } - else if ( - (leftType->primType == PRIM_FLOAT) || - (rightType->primType == PRIM_FLOAT) - ) { - return PRIM_FLOAT; + if ((leftType->type != TYPE_PRIM) || (rightType->type != TYPE_PRIM)) { + PrintError( + node->i.err, "Operation '%s' only works on primitives", + Lexer_TypeVAsString(node->bin.op) + ); + } + + if (leftType->primType == rightType->primType) { + if (leftType->size > rightType->size) { + return left; } else { - return leftType->type; + return right; } - } - else if (leftType->size > rightType->size) { - return leftType; } else { - return rightType; + PrintError( + node->i.err, "Incompatible types in '%s' operation", + Lexer_TypeVAsString(node->bin.op) + ); } } + default: break; } - } - if (!swapped) { - SWAP(UsedType, left, right); - swapped = true; - goto again; + PrintError( + node->i.err, "Operation '%s' not allowed here", + Lexer_TypeVAsString(node->bin.op) + ); } + + exit(1); } static UsedType EvalType(Node* node) { + static bool inFunc = false; + switch (node->i.type) { case NODE_INT: return GET_PRIM("__lit", 0); case NODE_STRING: return GET_PRIM("char", 1); @@ -196,66 +229,14 @@ if ((type->primType == PRIM_UINT) && (node->unary.op == TOKEN_SUBTRACT)) { PrintWarning(node->i.err, "Using negative sign on unsigned value"); } + + return operand; } case NODE_PTR: { UsedType type = EvalType(node->ptr.inner); ++ type.ptr; return type; } - case NODE_FUNC_CALL: { - Function* func = State_GetFunc(node->funcCall.func); - if (!func) { - PrintError(node->i.err, "Function %s does not exist", node->funcCall.func); - } - - if (func->paramsLen != node->funcCall.paramsNum) { - PrintError( - node->i.err, "Expected %d parameters, got %d", - func->paramsLen, node->funcCall.paramsNum - ); - } - - for (size_t i = 0; i < func.paramsLen; ++ i) { - UsedType type = EvalNode(node->funcCall.params[i]); - UsedType paramType = func.params[i].type; - - bool paramLit = strcmp( - TypeFromUsed(paramType).name, "__lit" - ) == 0; - - if (type != paramType) { - if ((type.type == TYPE_PRIM) && (paramType.type == TYPE_PRIM)) { - if ( - (type.primType == paramType.primType) && - (paramType.size >= type.primType) - ) { - continue; - } - if (paramLit) continue; - } - PrintError(node->i.err, - "Expected %s for parameter %s, got %s", - GetTypeFromUsed(paramType).name, - func.params[i].name, - GetTypeFromUsed(type).name - ); - } - } - - if (func.sub) { - return GET_PRIM("unit", 0); - } - else { - return func.ret; - } - } - } -} - -void SemanticAnalysis_Analyse(Node* node) { - static bool inFunc = false; - - switch (node->i.type) { case NODE_FUNC_DEF: { if (inFunc) { PrintError(node->i.err, "Nested function definitions are not allowed"); @@ -268,14 +249,14 @@ if (!func.sub) { func.ret = NodeAsUsedType(node->funcDef.dec.ret); } - func.paramsLen = node.funcDef.dec.paramsNum; + func.paramsLen = node->funcDef.dec.paramsNum; func.params = SafeMalloc(func.paramsLen * sizeof(FuncParam)); for (size_t i = 0; i < func.paramsLen; ++ i) { func.params[i].type = NodeAsUsedType( - node.funcDef.dec.params[i].type + node->funcDef.dec.params[i].type ); - func.params[i].name = node.funcDef.dec.params[i].name; + func.params[i].name = node->funcDef.dec.params[i].name; } func.name = NewString(node->funcDef.dec.name); @@ -294,26 +275,108 @@ State_AddVar(var); } for (size_t i = 0; i < node->funcDef.bodyLen; ++ i) { - SemanticAnalysis_Analyse(node->funcDef.body[i]); + EvalType(&node->funcDef.body[i]); } inFunc = false; State_FreeScope(); - break; + return GET_PRIM("unit", 0); + } + case NODE_FUNC_CALL: { + Function* func = State_GetFunc(node->funcCall.func); + if (!func) { + PrintError(node->i.err, "Function %s does not exist", node->funcCall.func); + } + + if (func->paramsLen != node->funcCall.paramsNum) { + PrintError( + node->i.err, "Expected %d parameters, got %d", + func->paramsLen, node->funcCall.paramsNum + ); + } + + for (size_t i = 0; i < func->paramsLen; ++ i) { + UsedType type = EvalType(&node->funcCall.params[i]); + UsedType param = func->params[i].type; + Type* paramType = GetTypeFromUsed(param); + Type* callType = GetTypeFromUsed(type); + + bool paramLit = strcmp(callType->name, "__lit") == 0; + + if (!State_UsedTypeEq(type, param)) { + if ( + (callType->type == TYPE_PRIM) && (paramType->type == TYPE_PRIM) && + (callType->primType == paramType->primType) && + (paramType->size >= callType->size) + ) { + continue; + } + + if (paramLit && (paramType->type == TYPE_PRIM)) continue; + + PrintError(node->i.err, + "Expected %s for parameter %s, got %s", + paramType->name, func->params[i].name, callType->name + ); + } + } + + if (func->sub) { + return GET_PRIM("unit", 0); + } + else { + return func->ret; + } } case NODE_DIM: { Var var; - var.type = NodeAsUsedType(node.dim.varType); - var.name = NewString(node.dim.name); + var.type = NodeAsUsedType(node->dim.varType); + var.name = NewString(node->dim.name); State_AddVar(var); - break; + return GET_PRIM("unit", 0); } - case NODE_FUNC_CALL: { - Function* func = State_GetFunc(node.funcCall.func); - + case NODE_ASSIGN: { + Var* var = State_GetVar(node->assign.variable); + + if (!var) { + PrintError( + node->i.err, "Variable '%s' doesn't exist", + node->assign.variable + ); + } + + UsedType left = var->type; + UsedType right = EvalType(node->assign.rValue); + + Type* leftType = GetTypeFromUsed(left); + Type* rightType = GetTypeFromUsed(right); + + bool compatible = true; + + if ((left.ptr != right.ptr) || (leftType->type != rightType->type)) { + compatible = false; + } + else if (leftType->type == TYPE_PRIM) { + if (leftType->primType != rightType->primType) compatible = false; + else if (leftType->size < rightType->size) compatible = false; + } + + if (!compatible) { + PrintError( + node->i.err, "Type '%s' cannot be assigned to '%s' variable", + GetTypeFromUsed(left)->name, GetTypeFromUsed(right)->name + ); + } + + return GET_PRIM("unit", 0); } default: { Unexpected(node); - exit(1); } } + + assert(0); +} + +void SemanticAnalysis_Analyse(Node* node) { + EvalType(node); } diff --git a/basic-boot/source/semanticAnalysis.h b/basic-boot/source/semanticAnalysis.h index d5257a26388dc6d8ab0ada249be4fbfe3ea1b932..341ccb8ac4a96ebcd1d94b58b4aad9fa914a881e 100644 --- a/basic-boot/source/semanticAnalysis.h +++ b/basic-boot/source/semanticAnalysis.h @@ -3,7 +3,7 @@ #define N_SEMANTIC_ANALYSIS_H #include "common.h" -void SemanticAnalysis_Init(void); +void SemanticAnalysis_Init(size_t wordSize); void SemanticAnalysis_Analyse(Node* node); #endif diff --git a/basic-boot/source/state.c b/basic-boot/source/state.c index 2f93691366c00812438dea41001aac368f13b492..c2d901ca81d47638cf32a652631f8562859bda74 100644 --- a/basic-boot/source/state.c +++ b/basic-boot/source/state.c @@ -1,3 +1,4 @@ +#include <stdio.h> #include <string.h> #include "mem.h" #include "util.h" @@ -28,7 +29,7 @@ ++ state.scopeSize[scope]; state.scopes[scope] = SafeRealloc( state.scopes[scope], state.scopeSize[scope] * sizeof(Var) ); - state.scopes[scope][state.typeNum - 1] = var; + state.scopes[scope][state.scopeSize[scope] - 1] = var; } bool State_AddScope(void) { @@ -53,10 +54,12 @@ for (size_t i = 0; i < scopeNum; ++ i) { free(scope[i].name); } + + -- state.scopeNum; } Var* State_GetVar(const char* name) { - for (size_t i = state.scopeNum - 1; i --> 0;) { + for (size_t i = state.scopeNum; i --> 0;) { for (size_t j = 0; j < state.scopeSize[i]; ++ j) { if (strcmp(state.scopes[i][j].name, name) == 0) { return &state.scopes[i][j]; @@ -95,3 +98,30 @@ type.size = size; type.primType = primType; State_AddType(type); } + +bool State_UsedTypeEq(UsedType a, UsedType b) { + return (a.ptr == b.ptr) && (a.typeIdx == b.typeIdx) && (a.array == b.array); +} + +void State_DumpInfo(void) { + puts("TYPES"); + puts("====="); + for (size_t i = 0; i < state.typeNum; ++ i) { + printf("- %s\n", state.types[i].name); + } + puts("\n"); + + puts("GLOBALS"); + puts("======="); + for (size_t i = 0; i < state.scopeSize[0]; ++ i) { + printf("- %s\n", state.scopes[0][i].name); + } + puts("\n"); + + puts("FUNCTIONS"); + puts("========="); + for (size_t i = 0; i < state.funcNum; ++ i) { + printf("- %s\n", state.funcs[i].name); + } + puts("\n"); +} diff --git a/basic-boot/source/state.h b/basic-boot/source/state.h index 8346dccda6ed781412ba6add459f3ecc4c666395..bff6a6faeea769784107c85cb53b3b8e1e1da066 100644 --- a/basic-boot/source/state.h +++ b/basic-boot/source/state.h @@ -74,5 +74,7 @@ Var* State_GetVar(const char* name); Type* State_GetType(const char* name); Function* State_GetFunc(const char* name); void State_MakePrimitive(const char* name, size_t size, int primType); +bool State_UsedTypeEq(UsedType a, UsedType b); +void State_DumpInfo(void); #endif diff --git a/basic-boot/test.bas b/basic-boot/test.bas index 322b3b51d45b9deaa12f7bd1024131f47e44a2fb..af13a8b74c4cbcb49dd5ac303c39c0578ebf48fb 100644 --- a/basic-boot/test.bas +++ b/basic-boot/test.bas @@ -9,3 +9,12 @@ manul 1, (2 * 3) + -5 * 4 foo = 3 + 4 + +dim a as int +dim b as f32 +dim c as int + +manul a + c, 5 + +dim myPtr as ptr(int) +myPtr = myPtr * 5