nitron

commit c819f8f504e3aa3085baf65fc9c85b350badbe46

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