nitron

commit 4506e237da6706a7869f25c507715383d7a01272

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