Author: mesyeti <mesyeti@mesyeti.uk>
semantic analysis stuff (definitely will not compile)
basic-boot/source/error.c | 64 ++++- basic-boot/source/error.h | 1 basic-boot/source/lexer.c | 8 basic-boot/source/lexer.h | 1 basic-boot/source/parser.c | 16 + basic-boot/source/parser.h | 2 basic-boot/source/preCompiler.h | 6 basic-boot/source/semanticAnalysis.c | 319 ++++++++++++++++++++++++++++++ basic-boot/source/semanticAnalysis.h | 9 basic-boot/source/state.c | 97 +++++++++ basic-boot/source/state.h | 68 ++++++
diff --git a/basic-boot/source/error.c b/basic-boot/source/error.c index bb331eb8faa5d914f58d3ac7e7afe8ccc14b2b96..2149922c18c01d2bd86757208d040c3983d37e91 100644 --- a/basic-boot/source/error.c +++ b/basic-boot/source/error.c @@ -4,30 +4,34 @@ #include#include "mem.h" #include "error.h" +#define FMT(RET, FORMAT) do { \ + int n = 0; \ + size_t size = 0; \ + va_list ap; \ +\ + va_start(ap, FORMAT); \ + n = vsnprintf(RET, size, FORMAT, ap); \ + va_end(ap); \ +\ + assert(n >= 0); \ +\ + size = n + 1; \ + RET = (char*) SafeMalloc(size); \ + if (RET == NULL) { \ + return; \ + } \ +\ + va_start(ap, FORMAT); \ + n = vsnprintf(RET, size, FORMAT, ap); \ + va_end(ap); \ +\ + assert(n >= 0); \ +} while (0); + void PrintError(ErrorInfo err, const char* format, ...) { char* ret = NULL; - int n = 0; - size_t size = 0; - va_list ap; - - va_start(ap, format); - n = vsnprintf(ret, size, format, ap); - va_end(ap); - - assert(n >= 0); - - size = n + 1; - ret = (char*) SafeMalloc(size); - if (ret == NULL) { - return; - } - - va_start(ap, format); - n = vsnprintf(ret, size, format, ap); - va_end(ap); - - assert(n >= 0); + FMT(ret, format); #if 0 fprintf(stderr, "error: %s:%d: %s\n", err.file, (int) err.line, ret); @@ -43,3 +47,21 @@ #endif exit(1); } + +void PrintWarning(ErrorInfo err, const char* format, ...) { + char* ret = NULL; + + FMT(ret, format); + + #if 0 + fprintf(stderr, "error: %s:%d: %s\n", err.file, (int) err.line, ret); + #else + #define YELLOW "\x1b[33m" + #define BLUE "\x1b[34m" + #define RESET "\x1b[0m" + fprintf( + stderr, YELLOW "warning: " BLUE "%s:%d: " RESET "%s\n", + err.file, (int) err.line, ret + ); + #endif +} diff --git a/basic-boot/source/error.h b/basic-boot/source/error.h index e443d59abce632c0afc88b29cef90aeb7e064ffc..de5ff9f8e56c78843c3d6aefae933fcc325e3de4 100644 --- a/basic-boot/source/error.h +++ b/basic-boot/source/error.h @@ -9,5 +9,6 @@ size_t line; } ErrorInfo; void PrintError(ErrorInfo err, const char* format, ...); +void PrintWarning(ErrorInfo err, const char* format, ...); #endif diff --git a/basic-boot/source/lexer.c b/basic-boot/source/lexer.c index d5babcafa8a3d1116b530ab87527b38ea95dc83b..6bc99427534016514e58cc0457619c268ea5a66c 100644 --- a/basic-boot/source/lexer.c +++ b/basic-boot/source/lexer.c @@ -174,8 +174,8 @@ } } } -const char* Lexer_TypeAsString(Token* token) { - switch (token->type) { +const char* Lexer_TypeVAsString(TokenType type) { + switch (type) { case TOKEN_NULL: return "null"; case TOKEN_EXTERN: return "extern"; case TOKEN_FUNC: return "func"; @@ -202,6 +202,10 @@ case TOKEN_LESS: return "less"; case TOKEN_GREATER: return "greater"; default: assert(0); } +} + +const char* Lexer_TypeAsString(Token* token) { + return Lexer_TypeVAString(token->type); } void Lexer_PrintToken(Token* token) { diff --git a/basic-boot/source/lexer.h b/basic-boot/source/lexer.h index 039cf9d70327e5061db6802f9dfdb08abc5491e2..856b046d31b1dffaa1201de6ebeb3247712c30f2 100644 --- a/basic-boot/source/lexer.h +++ b/basic-boot/source/lexer.h @@ -61,6 +61,7 @@ Lexer Lexer_Init(FILE* file, const char* fileName); void Lexer_Free(Lexer* lexer); int Lexer_Next(Lexer* lexer, Token* token); +const char* Lexer_TypeVAsString(TokenType type); const char* Lexer_TypeAsString(Token* token); void Lexer_PrintToken(Token* token); void Lexer_FreeToken(Token* token); diff --git a/basic-boot/source/parser.c b/basic-boot/source/parser.c index 15ab34963e2c9c42be747d340f024385d4538b88..e73fcb98333d3a942f52bea9e4df501f6efef394 100644 --- a/basic-boot/source/parser.c +++ b/basic-boot/source/parser.c @@ -638,3 +638,19 @@ } default: return; } } + +const char* Parser_NodeType(NodeType type) { + switch (type) { + case NODE_NONE: return "none"; + case NODE_INT: return "int"; + case NODE_STRING: return "string"; + case NODE_IDENTIFIER: return "identifier"; + case NODE_BINARY_OP: return "binary operation"; + case NODE_UNARY_OP: return "unary operation"; + case NODE_PTR: return "ptr"; + case NODE_FUNC_DEF: return "function definition"; + case NODE_DIM: return "dim"; + case NODE_FUNC_CALL: return "function call"; + case NODE_ASSIGN: return "assignment"; + } +} diff --git a/basic-boot/source/parser.h b/basic-boot/source/parser.h index c2e997bc58e5facc7a83355fe4a773f915abe012..c45c6d8e353de1501a282d42a1b01386faf0db9e 100644 --- a/basic-boot/source/parser.h +++ b/basic-boot/source/parser.h @@ -122,4 +122,6 @@ void Parser_PrintNode(Node* node); Node* Parser_Parse(Parser* p, size_t* lenOut); void Parser_FreeNode(Node* node); +const char* Parser_TypeStr(NodeType type); + #endif diff --git a/basic-boot/source/preCompiler.h b/basic-boot/source/preCompiler.h deleted file mode 100644 index a8301d1820668f79533fe14b72a3b45b15ab56f9..0000000000000000000000000000000000000000 --- a/basic-boot/source/preCompiler.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef N_PRECOMPILER_H -#define N_PRECOMPILER_H - -#include "state.h" - -#endif diff --git a/basic-boot/source/semanticAnalysis.c b/basic-boot/source/semanticAnalysis.c new file mode 100644 index 0000000000000000000000000000000000000000..c77ea59e052c92b4c1b2859fa914cfade6338a0c --- /dev/null +++ b/basic-boot/source/semanticAnalysis.c @@ -0,0 +1,319 @@ +#include "error.h" +#include "semanticAnalysis" + +void SemanticAnalysis_Init(size_t wordSize) { + State_Init(); + + // add primitive types + State_MakePrimitive("u8", 1, PRIM_UINT); + State_MakePrimitive("i8", 1, PRIM_INT); + State_MakePrimitive("u16", 2, PRIM_UINT); + State_MakePrimitive("i16", 2, PRIM_INT); + State_MakePrimitive("u32", 4, PRIM_UINT); + State_MakePrimitive("i32", 4, PRIM_INT); + State_MakePrimitive("u64", 8, PRIM_UINT); + State_MakePrimitive("i64", 8, PRIM_INT); + State_MakePrimitive("uint", wordSize, PRIM_UINT); + State_MakePrimitive("int", wordSize, PRIM_INT); + State_MakePrimitive("char", 1, PRIM_UINT); + State_MakePrimitive("f32", 4, PRIM_FLOAT); + State_MakePrimitive("f64", 8, PRIM_FLOAT); + State_MakePrimitive("bool", 1, PRIM_UINT); + State_MakePrimitive("__lit", 0, PRIM_INT); + + Type type; + type.name = NewString("unit"); + type.type = TYPE_UNIT; + State_AddType(type); +} + +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) + ); + } +} + +void Unexpected(Node* node) { + PrintError(node->i.err, "Unexpected %s", Parser_NodeType(node->i.type)); +} + +static UsedType NodeAsUsedType(Node* node) { + UsedType ret; + ret.ptr = 0; + ret.array = false; // no arrays yet + + while (true) { + switch (node->i.type) { + case NODE_PTR: { + node = node->ptr.inner; + ++ ret.ptr; + continue; + } + case NODE_IDENTIFIER: { + Type* type = State_GetType(node->ident.name); + + if (!type) { + PrintError(node->i.err, "Type '%s' does not exist", node->ident.name); + } + + ret.typeIdx = type - state.types; + return ret; + } + default: { + Unexpected(node); + exit(1); + } + } + } +} + +static Type* GetTypeFromUsed(UsedType type) { + return &state.types[type.typeIdx]; +} + +#define GET_PRIM(NAME, PTR) \ + ((UsedType) {(PTR), State_GetType(NAME) - state.types, 0}) + +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; + + again: + leftType = TypeFromUsed(left); + rightType = TypeFromUsed(right); + + if ((leftType->type != TYPE_PRIM) || (rightType->type != TYPE_PRIM)) { + PrintError(node->i.err, "You can only use primitive types in binary expressions"); + } + + if (left.ptr) { + if (right.ptr) { + switch (node->bin.op) { + case TOKEN_EQUAL: + case TOKEN_LESS: + case TOKEN_GREATER: return GET_PRIM("bool", 0); + case TOKEN_SUB: return GET_PRIM("uint", 0); + default: { + PrintError( + node->i.err, "Operation '%s' not allowed here", + Lexer_TypeVAsString(node->bin.op) + ); + } + } + } + else { + switch (node->bin.op) { + case TOKEN_ADD: + case TOKEN_SUB: return left; + 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_LESS: + case TOKEN_GREATER: 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; + } + else { + return leftType->type; + } + } + else if (leftType->size > rightType->size) { + return leftType; + } + else { + return rightType; + } + } + } + } + + if (!swapped) { + SWAP(UsedType, left, right); + swapped = true; + goto again; + } +} + +static UsedType EvalType(Node* node) { + switch (node->i.type) { + case NODE_INT: return GET_PRIM("__lit", 0); + case NODE_STRING: return GET_PRIM("char", 1); + case NODE_IDENTIFIER: { + Var* var = State_GetVar(node->ident.name); + + if (!var) { + PrintError( + node->i.err, "Variable '%s' doesn't exist", node->ident.name + ); + } + + return var->type; + } + case NODE_BINARY_OP: return EvalBinOp(node); + case NODE_UNARY_OP: { + UsedType operand = EvalType(node->unary.operand); + Type* type = GetTypeFromUsed(operand); + + if (type->type != TYPE_PRIM) { + PrintError(node->i.err, + "Only primitive types can be used with unary operators" + ); + } + + if ((type->primType == PRIM_UINT) && (node->unary.op == TOKEN_SUBTRACT)) { + PrintWarning(node->i.err, "Using negative sign on unsigned value"); + } + } + 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"); + } + + Function func; + func.sub = node->funcDef.dec.ret != NULL; + + if (!func.sub) { + func.ret = NodeAsUsedType(node->funcDef.dec.ret); + } + + 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 + ); + func.params[i].name = node.funcDef.dec.params[i].name; + } + + func.name = NewString(node->funcDef.dec.name); + State_AddFunc(func); + + // now analyse the body + State_AddScope(); + inFunc = true; + + // add parameters to this scope + for (size_t i = 0; i < func.paramsLen; ++ i) { + Var var; + var.type = func.params[i].type; + var.name = NewString(func.params[i].name); + State_AddVar(var); + } + + for (size_t i = 0; i < node->funcDef.bodyLen; ++ i) { + SemanticAnalysis_Analyse(node->funcDef.body[i]); + } + inFunc = false; + State_FreeScope(); + break; + } + case NODE_DIM: { + Var var; + var.type = NodeAsUsedType(node.dim.varType); + var.name = NewString(node.dim.name); + State_AddVar(var); + break; + } + case NODE_FUNC_CALL: { + Function* func = State_GetFunc(node.funcCall.func); + + } + default: { + Unexpected(node); + exit(1); + } + } +} diff --git a/basic-boot/source/semanticAnalysis.h b/basic-boot/source/semanticAnalysis.h new file mode 100644 index 0000000000000000000000000000000000000000..d5257a26388dc6d8ab0ada249be4fbfe3ea1b932 --- /dev/null +++ b/basic-boot/source/semanticAnalysis.h @@ -0,0 +1,9 @@ +#ifndef N_SEMANTIC_ANALYSIS_H +#define N_SEMANTIC_ANALYSIS_H + +#include "common.h" + +void SemanticAnalysis_Init(void); +void SemanticAnalysis_Analyse(Node* node); + +#endif diff --git a/basic-boot/source/state.c b/basic-boot/source/state.c new file mode 100644 index 0000000000000000000000000000000000000000..2f93691366c00812438dea41001aac368f13b492 --- /dev/null +++ b/basic-boot/source/state.c @@ -0,0 +1,97 @@ +#include <string.h> +#include "mem.h" +#include "util.h" +#include "state.h" + +State state; + +void State_Init(void) { + state.types = NULL; + state.typeNum = 0; + state.scopes[0] = NULL; + state.scopeSize[0] = 0; + state.scopeNum = 1; + state.funcs = NULL; + state.funcNum = 0; +} + +void State_AddType(Type type) { + ++ state.typeNum; + state.types = SafeRealloc(state.types, state.typeNum * sizeof(Type)); + state.types[state.typeNum - 1] = type; +} + +void State_AddVar(Var var) { + size_t scope = state.scopeNum - 1; + + ++ state.scopeSize[scope]; + state.scopes[scope] = SafeRealloc( + state.scopes[scope], state.scopeSize[scope] * sizeof(Var) + ); + state.scopes[scope][state.typeNum - 1] = var; +} + +bool State_AddScope(void) { + if (state.scopeNum >= 16) return false; + + ++ state.scopeNum; + state.scopeSize[state.scopeNum - 1] = 0; + state.scopes[state.scopeNum - 1] = NULL; + return true; +} + +void State_AddFunc(Function func) { + ++ state.funcNum; + state.funcs = SafeRealloc(state.funcs, state.funcNum * sizeof(Function)); + state.funcs[state.funcNum - 1] = func; +} + +void State_FreeScope(void) { + Var* scope = state.scopes[state.scopeNum - 1]; + size_t scopeNum = state.scopeSize[state.scopeNum - 1]; + + for (size_t i = 0; i < scopeNum; ++ i) { + free(scope[i].name); + } +} + +Var* State_GetVar(const char* name) { + for (size_t i = state.scopeNum - 1; 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]; + } + } + } + + return NULL; +} + +Type* State_GetType(const char* name) { + for (size_t i = 0; i < state.typeNum; ++ i) { + if (strcmp(state.types[i].name, name) == 0) { + return &state.types[i]; + } + } + + return NULL; +} + +Function* State_GetFunc(const char* name) { + for (size_t i = 0; i < state.funcNum; ++ i) { + if (strcmp(state.funcs[i].name, name) == 0) { + return &state.funcs[i]; + } + } + + return NULL; +} + +void State_MakePrimitive(const char* name, size_t size, int primType) { + Type type; + type.type = TYPE_PRIM; + type.name = NewString(name); + type.size = size; + type.primType = primType; + State_AddType(type); +} diff --git a/basic-boot/source/state.h b/basic-boot/source/state.h index 3615b4e6458066adecadecde1dcc84c8f82dda4c..8346dccda6ed781412ba6add459f3ecc4c666395 100644 --- a/basic-boot/source/state.h +++ b/basic-boot/source/state.h @@ -3,12 +3,76 @@ #define N_STATE_H #include "common.h" +enum { + TYPE_PRIM, // is this "primitive", or Primula the manul? + TYPE_STRUCT, + TYPE_UNIT +}; + +enum { + PRIM_UINT, + PRIM_INT, + PRIM_FLOAT +}; + typedef struct { - char* name; + size_t ptr; + size_t typeIdx; + size_t array; // 0 if not array, contains length if not array +} UsedType; + +typedef struct { + int type; + char* name; + size_t size; + + // only for "struct" types + UsedType* structure; + size_t structLen; + + // only for "primitive" types + int primType; } Type; typedef struct { - + UsedType type; + char* name; +} Var; + +typedef struct { + UsedType type; + char* name; +} FuncParam; + +typedef struct { + bool sub; + UsedType ret; + FuncParam* params; + size_t paramsLen; + char* name; +} Function; + +typedef struct { + Type* types; + size_t typeNum; + Var* scopes[16]; + size_t scopeSize[16]; + size_t scopeNum; + Function* funcs; + size_t funcNum; } State; + +extern State state; + +void State_Init(void); +void State_AddType(Type type); +void State_AddVar(Var var); +bool State_AddScope(void); +void State_AddFunc(Function func); +void State_FreeScope(void); +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); #endif