nitron

commit 76c5b8d611f3207b33b5b370f68a0884150dbf59

Author: mesyeti <mesyeti@mesyeti.uk>

basic: add if statements and assignments

 basic/source/compiler.c | 69 +++++++------
 basic/source/compiler.h | 11 -
 basic/source/frontend/c89.c | 39 ++++++-
 basic/source/lexer.c | 4 
 basic/source/lexer.h | 1 
 basic/source/parser.c | 172 ++++++++++++++++++++++++++++++----
 basic/source/semanticAnalysis.c | 81 +++++++++-------
 basic/source/state.c | 6 +
 basic/source/state.h | 1 
 basic/test.bas | 11 ++


diff --git a/basic/source/compiler.c b/basic/source/compiler.c
index df9f561f0e56efa6adbd4548a02c6b99380b2c33..b37cd3093c6494615a61343fbc5a6a43dd374bb0 100644
--- a/basic/source/compiler.c
+++ b/basic/source/compiler.c
@@ -7,42 +7,45 @@ void Frontend_Init(Frontend p_frontend) {
 	frontend = p_frontend;
 }
 
-void Frontend_Finish(void) {
-	frontend.finish();
-}
-
-void Frontend_BeginMain(void) {
-	frontend.beginMain();
-}
-
-void Frontend_CompileFuncDef(FuncDefNode* node) {
-	frontend.compileFuncDef(node);
-}
-
-void Frontend_CompileDim(DimNode* node) {
-	frontend.compileDim(node);
-}
-
-void Frontend_CompileFuncCall(FuncCallNode* node) {
-	frontend.compileFuncCall(node);
-}
-
-void Frontend_CompileExtern(ExternNode* node) {
-	frontend.compileExtern(node);
-}
-
 void Compiler_Init(Node* nodes, size_t len) {
 	compiler.nodes = nodes;
 	compiler.len   = len;
 }
 
-void Compiler_Compile(Node* node, bool inFunc) {
+void Compiler_CompileBodyNode(Node* node) {
+	switch (node->i.type) {
+		
+		case NODE_FUNC_CALL: frontend.compileFuncCall(&node->funcCall); break;
+		case NODE_FUNC_DEF:  frontend.compileFuncDef(&node->funcDef);   break;
+		case NODE_DIM:       frontend.compileDim(&node->dim);           break;
+		case NODE_EXTERN:    frontend.compileExtern(&node->external);   break;
+		case NODE_IF:        frontend.compileIf(&node->ifStat);         break;
+		case NODE_BINARY_OP: {
+			if (node->bin.op == TOKEN_ASSIGN) {
+				frontend.compileAssign(&node->bin);
+				break;
+			}
+		} // fall through
+		default: {
+			PrintError(node->i.err, "Unexpected '%s'", Parser_TypeStr(node->i.type));
+		}
+	}
+}
+
+void Compiler_CompileTopNode(Node* node, bool inFunc) {
 	if (inFunc) {
 		switch (node->i.type) {
 			case NODE_FUNC_DEF:  break;
 			case NODE_DIM:       break;
 			case NODE_EXTERN:    break;
-			case NODE_FUNC_CALL: Frontend_CompileFuncCall(&node->funcCall); break;
+			case NODE_FUNC_CALL: frontend.compileFuncCall(&node->funcCall); break;
+			case NODE_IF:        frontend.compileIf(&node->ifStat);         break;
+			case NODE_BINARY_OP: {
+				if (node->bin.op == TOKEN_ASSIGN) {
+					frontend.compileAssign(&node->bin);
+					break;
+				}
+			} // fall through
 			default: {
 				PrintError(node->i.err, "Unexpected '%s'", Parser_TypeStr(node->i.type));
 			}
@@ -50,9 +53,9 @@ 		}
 	}
 	else {
 		switch (node->i.type) {
-			case NODE_FUNC_DEF: Frontend_CompileFuncDef(&node->funcDef); break;
-			case NODE_DIM:      Frontend_CompileDim(&node->dim); break;
-			case NODE_EXTERN:   Frontend_CompileExtern(&node->external); break;
+			case NODE_FUNC_DEF: frontend.compileFuncDef(&node->funcDef); break;
+			case NODE_DIM:      frontend.compileDim(&node->dim);         break;
+			case NODE_EXTERN:   frontend.compileExtern(&node->external); break;
 			default:            break;
 		}
 	}
@@ -61,16 +64,16 @@
 void Compiler_Run(void) {
 	for (size_t i = 0; i < compiler.len; ++ i) {
 		Node* node = &compiler.nodes[i];
-		Compiler_Compile(node, false);
+		Compiler_CompileTopNode(node, false);
 	}
 
-	Frontend_BeginMain();
+	frontend.beginMain();
 
 	for (size_t i = 0; i < compiler.len; ++ i) {
 		Node* node = &compiler.nodes[i];
 
-		Compiler_Compile(node, true);
+		Compiler_CompileTopNode(node, true);
 	}
 
-	Frontend_Finish();
+	frontend.finish();
 }




diff --git a/basic/source/compiler.h b/basic/source/compiler.h
index 5279ef5f9a599baf99af6686f11eeced12cad581..26db868dd7c4acf2e93fcffb7977d6768676ccc0 100644
--- a/basic/source/compiler.h
+++ b/basic/source/compiler.h
@@ -10,15 +10,11 @@ 	void (*compileFuncDef)(FuncDefNode* node);
 	void (*compileDim)(DimNode* node);
 	void (*compileFuncCall)(FuncCallNode* node);
 	void (*compileExtern)(ExternNode* node);
+	void (*compileIf)(IfNode* node);
+	void (*compileAssign)(BinaryNode* node);
 } Frontend;
 
 void Frontend_Init(Frontend p_frontend);
-void Frontend_Finish(void);
-void Frontend_BeginMain(void);
-void Frontend_CompileFuncDef(FuncDefNode* node);
-void Frontend_CompileDim(DimNode* node);
-void Frontend_CompileFuncCall(FuncCallNode* node);
-void Frontend_CompileExtern(ExternNode* node);
 
 typedef struct {
 	Node*  nodes;
@@ -26,7 +22,8 @@ 	size_t len;
 } Compiler;
 
 void Compiler_Init(Node* nodes, size_t len);
-void Compiler_Compile(Node* node, bool inMain);
+void Compiler_CompileBodyNode(Node* node);
+void Compiler_CompileTopNode(Node* node, bool inMain);
 void Compiler_Run(void);
 
 #endif




diff --git a/basic/source/frontend/c89.c b/basic/source/frontend/c89.c
index fff6d42e6d6368671c1bc0f6ef0a64e2bc263c36..8b10294ed608e26c89721bd58f5097465fcdd00f 100644
--- a/basic/source/frontend/c89.c
+++ b/basic/source/frontend/c89.c
@@ -69,7 +69,7 @@ 	CompileDec(node->dec);
 	fprintf(out, " {\n");
 
 	for (size_t i = 0; i < node->bodyLen; ++ i) {
-		Compiler_Compile(&node->body[i], true);
+		Compiler_CompileBodyNode(&node->body[i]);
 	}
 
 	fprintf(out, "}\n");
@@ -79,7 +79,7 @@ static void CompileDim(DimNode* node) {
 	if (inMain) return;
 
 	CompileType(node->varType);
-	fprintf(out, " nitron_%s;\n", node->name);
+	fprintf(out, " %s;\n", node->name);
 }
 
 static void CompileExprNode(Node* node);
@@ -102,7 +102,7 @@ static void CompileExprNode(Node* node) {
 	switch (node->i.type) {
 		case NODE_INT:        fprintf(out, "%d", node->integer.value); break;
 		case NODE_STRING:     fprintf(out, "\"%s\"", node->string.value); break;
-		case NODE_IDENTIFIER: fprintf(out, "nitron_%s", node->ident.name); break;
+		case NODE_IDENTIFIER: fprintf(out, "%s", node->ident.name); break;
 		case NODE_BINARY_OP: {
 			fprintf(out, "(");
 			CompileExprNode(node->bin.left);
@@ -112,10 +112,11 @@ 				case TOKEN_ADD:      fprintf(out, "+");  break;
 				case TOKEN_SUBTRACT: fprintf(out, "-");  break;
 				case TOKEN_MULTIPLY: fprintf(out, "*");  break;
 				case TOKEN_DIVIDE:   fprintf(out, "/");  break;
-				case TOKEN_MOD:      fprintf(out, "%%");  break;
+				case TOKEN_MOD:      fprintf(out, "%%"); break;
 				case TOKEN_EQUAL:    fprintf(out, "=="); break;
 				case TOKEN_LESS:     fprintf(out, "<");  break;
 				case TOKEN_GREATER:  fprintf(out, ">");  break;
+				case TOKEN_ASSIGN:   fprintf(out, "=");  break;
 				default:             assert(0);
 			}
 
@@ -180,6 +181,32 @@ 		}
 	}
 }
 
+static void CompileIf(IfNode* node) {
+	fprintf(out, "if (");
+	CompileExprNode(node->condition);
+	fprintf(out, ") {\n");
+
+	for (size_t i = 0; i < node->bodyLen; ++ i) {
+		Compiler_CompileBodyNode(&node->body[i]);
+	}
+
+	fprintf(out, "}\n");
+	fprintf(out, "else {\n");
+
+	for (size_t i = 0; i < node->elseLen; ++ i) {
+		Compiler_CompileBodyNode(&node->elseBody[i]);
+	}
+
+	fprintf(out, "}\n");
+}
+
+static void CompileAssign(BinaryNode* node) {
+	CompileExprNode(node->left);
+	fprintf(out, " = ");
+	CompileExprNode(node->right);
+	fprintf(out, ";\n");
+}
+
 Frontend Frontend_C89(void) {
 	out    = fopen("out.c", "w");
 	inMain = false;
@@ -212,6 +239,8 @@ 		.beginMain       = &BeginMain,
 		.compileFuncDef  = &CompileFuncDef,
 		.compileDim      = &CompileDim,
 		.compileFuncCall = &CompileFuncCall,
-		.compileExtern   = &CompileExtern
+		.compileExtern   = &CompileExtern,
+		.compileIf       = &CompileIf,
+		.compileAssign   = &CompileAssign
 	};
 }




diff --git a/basic/source/lexer.c b/basic/source/lexer.c
index 7051a3837f322ea88249cc246bdb5eca1d5216bf..8e53cee20d36e2e919285f51190398e5fad1a7dc 100644
--- a/basic/source/lexer.c
+++ b/basic/source/lexer.c
@@ -97,6 +97,7 @@ 		}
 		case '(':  *token = TOKEN(TOKEN_LPAREN,   NULL); return LEXER_TOKEN;
 		case ')':  *token = TOKEN(TOKEN_RPAREN,   NULL); return LEXER_TOKEN;
 		case ',':  *token = TOKEN(TOKEN_COMMA,    NULL); return LEXER_TOKEN;
+		case ';':  *token = TOKEN(TOKEN_LINE,     NULL); return LEXER_TOKEN;
 		case '+':  *token = TOKEN(TOKEN_ADD,      NULL); return LEXER_TOKEN;
 		case '-':  *token = TOKEN(TOKEN_SUBTRACT, NULL); return LEXER_TOKEN;
 		case '*':  *token = TOKEN(TOKEN_MULTIPLY, NULL); return LEXER_TOKEN;
@@ -165,7 +166,8 @@ 				{"quit",     TOKEN_QUIT},
 				{"ptr",      TOKEN_PTR},
 				{"end",      TOKEN_END},
 				{"if",       TOKEN_IF},
-				{"then",     TOKEN_THEN}
+				{"then",     TOKEN_THEN},
+				{"else",     TOKEN_ELSE}
 			};
 
 			for (size_t i = 0; i < sizeof(keywords) / sizeof(Keyword); ++ i) {




diff --git a/basic/source/lexer.h b/basic/source/lexer.h
index 795de53b9dc0571dac3cd3f59e4f283bdd9ea303..3b13d51817a87a47534307f4d0cf0b9087b8721e 100644
--- a/basic/source/lexer.h
+++ b/basic/source/lexer.h
@@ -19,6 +19,7 @@ 	TOKEN_PTR,
 	TOKEN_END,
 	TOKEN_IF,
 	TOKEN_THEN,
+	TOKEN_ELSE,
 
 	// misc
 	TOKEN_IDENTIFIER,




diff --git a/basic/source/parser.c b/basic/source/parser.c
index 9c576721f1b51ac606356abc0b571345566bb051..84b829dee914e7daea8e86999bf6b0330472b56f 100644
--- a/basic/source/parser.c
+++ b/basic/source/parser.c
@@ -77,6 +77,12 @@ 		);
 	}
 }
 
+static Node* NodeToHeap(Node node) {
+	Node* ret = SafeMalloc(sizeof(Node));
+	*ret      = node;
+	return ret;
+}
+
 static Node ParseAtom(Parser* p) {
 	Token* token = &p->tokens[p->i];
 	Node   ret;
@@ -219,8 +225,29 @@
 	return left;
 }
 
+static Node ParseAssign(Parser* p) {
+	Node left = ParseComparison(p);
+
+	while (p->tokens[p->i].type == TOKEN_ASSIGN) {
+		TokenType op = p->tokens[p->i].type;
+		Advance(p);
+		Node right = ParseComparison(p);
+
+		Node* leftPtr  = SafeMalloc(sizeof(Node));
+		Node* rightPtr = SafeMalloc(sizeof(Node));
+		*leftPtr       = left;
+		*rightPtr      = right;
+
+		left.bin = (BinaryNode) {
+			INFO(NODE_BINARY_OP), leftPtr, rightPtr, op
+		};
+	}
+
+	return left;
+}
+
 static Node ParseBinary(Parser* p) {
-	return ParseComparison(p);
+	return ParseAssign(p);
 }
 
 static Node ParseType(Parser* p) {
@@ -324,16 +351,12 @@
 	return ret;
 }
 
-static Node ParseFuncDef(Parser* p) {
-	FuncDefNode ret;
-	ret.i   = INFO(NODE_FUNC_DEF);
-	ret.dec = ParseFuncDec(p);
-	Advance(p);
-
-	ret.body    = NULL;
-	ret.bodyLen = 0;
+static Node* ParseBody(Parser* p, size_t* size) {
+	Node*  ret = SafeMalloc(8 * sizeof(Node));
+	size_t cap = 8;
+	size_t len = 0;
 
-	while (p->tokens[p->i].type != TOKEN_END) {
+	while ((p->tokens[p->i].type != TOKEN_END) && (p->tokens[p->i].type != TOKEN_ELSE)) {
 		if (p->tokens[p->i].type == TOKEN_LINE) {
 			Advance(p);
 			continue;
@@ -341,10 +364,29 @@ 		}
 
 		Node node = ParseNode(p);
 
-		++ ret.bodyLen;
-		ret.body = SafeRealloc(ret.body, ret.bodyLen * sizeof(Node));
-		ret.body[ret.bodyLen - 1] = node;
+		++ len;
+
+		while (len >= cap) {
+			cap += 8;
+			ret  = SafeRealloc(ret, cap * sizeof(Node));
+		}
+
+		ret[len - 1] = node;
 	}
+
+	ret   = SafeRealloc(ret, len * sizeof(Node));
+	*size = len;
+	return ret;
+}
+
+static Node ParseFuncDef(Parser* p) {
+	FuncDefNode ret;
+	ret.i   = INFO(NODE_FUNC_DEF);
+	ret.dec = ParseFuncDec(p);
+	Advance(p);
+
+	ret.body = ParseBody(p, &ret.bodyLen);
+	Expect(p, TOKEN_END);
 
 	Advance(p);
 	Expect(p, TOKEN_LINE);
@@ -433,14 +475,51 @@ 	node.external = ret;
 	return node;
 }
 
-// static Node ParseIf(Parser* p) {
-// 	Advance(p);
-// 
-// 	IfNode ret;
-// 	ret.i = INFO(NODE_IF);
-// 
-// 	
-// }
+static Node ParseIf(Parser* p) {
+	Advance(p);
+
+	IfNode ret;
+	ret.i = INFO(NODE_IF);
+
+	ret.condition = NodeToHeap(ParseBinary(p));
+
+	Expect(p, TOKEN_THEN);
+	Advance(p);
+	Expect(p, TOKEN_LINE);
+	Advance(p);
+
+	ret.body = ParseBody(p, &ret.bodyLen);
+
+	if (p->tokens[p->i].type == TOKEN_ELSE) {
+		Advance(p);
+
+		switch (p->tokens[p->i].type) {
+			case TOKEN_IF: {
+				ret.elseBody = NodeToHeap(ParseIf(p));
+				ret.elseLen  = 1;
+				break;
+			}
+			case TOKEN_LINE: {
+				Advance(p);
+				ret.elseBody = ParseBody(p, &ret.elseLen);
+				break;
+			}
+			default: {
+				PrintError(
+					p->tokens[p->i].err, "Unexpected '%s'",
+					Lexer_TypeAsString(&p->tokens[p->i])
+				);
+			}
+		}
+	}
+	else {
+		ret.elseLen = 0;
+	}
+
+	Node node;
+	node.ifStat = ret;
+	return node;
+}
 
 static Node ParseNode(Parser* p) {
 	switch (p->tokens[p->i].type) {
@@ -448,7 +527,7 @@ 		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_IF:     return ParseIf(p);
 		case TOKEN_IDENTIFIER: {
 			if (p->i == p->tokenNum - 1) {
 				PrintError(p->tokens[p->i].err, "Unexpected EOF");
@@ -500,7 +579,7 @@
 void Parser_PrintNode(Node* node) {
 	switch (node->i.type) {
 		case NODE_INT: printf("%d", node->integer.value); break;
-		case NODE_STRING: printf("%s", node->string.value); break;
+		case NODE_STRING: printf("\"%s\"", node->string.value); break;
 		case NODE_IDENTIFIER: printf("%s", node->ident.name); break;
 		case NODE_BINARY_OP: {
 			printf("(");
@@ -512,14 +591,19 @@ 				case TOKEN_SUBTRACT: printf("-"); break;
 				case TOKEN_MULTIPLY: printf("*"); break;
 				case TOKEN_DIVIDE:   printf("/"); break;
 				case TOKEN_MOD:      printf("%%"); break;
-				case TOKEN_EQUAL:    printf("="); break;
+				case TOKEN_EQUAL:    printf("=="); break;
 				case TOKEN_LESS:     printf("<"); break;
 				case TOKEN_GREATER:  printf(">"); break;
+				case TOKEN_ASSIGN:   printf("="); break;
 				default:             assert(0);
 			}
 
 			Parser_PrintNode(node->bin.right);
 			printf(")");
+
+			if (node->bin.op == TOKEN_ASSIGN) {
+				puts("");
+			}
 			break;
 		}
 		case NODE_UNARY_OP: {
@@ -538,6 +622,7 @@ 		}
 		case NODE_PTR: {
 			printf("ptr(");
 			Parser_PrintNode(node->ptr.inner);
+			printf(")");
 			break;
 		}
 		case NODE_FUNC_DEF: {
@@ -575,6 +660,26 @@ 			printf("extern ");
 			PrintDec(node->external.dec);
 			break;
 		}
+		case NODE_IF: {
+			printf("if ");
+			Parser_PrintNode(node->ifStat.condition);
+			printf(" then\n");
+
+			for (size_t i = 0; i < node->ifStat.bodyLen; ++ i) {
+				printf("    ");
+				Parser_PrintNode(&node->ifStat.body[i]);
+			}
+
+			puts("else");
+
+			for (size_t i = 0; i < node->ifStat.elseLen; ++ i) {
+				printf("    ");
+				Parser_PrintNode(&node->ifStat.elseBody[i]);
+			}
+
+			puts("end");
+			break;
+		}
 		default: {
 			printf("unknown %d\n", node->i.type);
 			assert(0);
@@ -603,6 +708,14 @@ 	free(p->tokens);
 
 	*lenOut = len;
 	return ret;
+}
+
+static void FreeNodes(Node* nodes, size_t len) {
+	for (size_t i = 0; i < len; ++ i) {
+		Parser_FreeNode(&nodes[i]);
+	}
+
+	free(nodes);
 }
 
 void Parser_FreeNode(Node* node) {
@@ -660,6 +773,15 @@ 			}
 			free(node->funcCall.params);
 			break;
 		}
+		case NODE_IF: {
+			Parser_FreeNode(node->ifStat.condition);
+			FreeNodes(node->ifStat.body, node->ifStat.bodyLen);
+
+			if (node->ifStat.elseLen > 0) {
+				FreeNodes(node->ifStat.elseBody, node->ifStat.elseLen);
+			}
+			break;
+		}
 		default: return;
 	}
 }
@@ -676,6 +798,8 @@ 		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_EXTERN:     return "extern";
+		case NODE_IF:         return "if";
 		default:              return "???";
 	}
 }




diff --git a/basic/source/semanticAnalysis.c b/basic/source/semanticAnalysis.c
index 40e18b6904bd1592a096fb4c8b67d602fc4acc7c..dd44ad0017c195f9bd81388f228550d749f68caf 100644
--- a/basic/source/semanticAnalysis.c
+++ b/basic/source/semanticAnalysis.c
@@ -79,6 +79,30 @@
 #define GET_PRIM(NAME, PTR) \
 	((UsedType) {(PTR), State_GetType(NAME) - state.types, 0})
 
+static UsedType EvalAssign(Node* node, UsedType left, UsedType right) {
+	Type* leftType  = State_GetTypeFromUsed(left);
+	Type* rightType = State_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",
+			State_GetTypeFromUsed(left)->name, State_GetTypeFromUsed(right)->name
+		);
+	}
+
+	return GET_PRIM("unit", 0);
+}
+
 static UsedType EvalBinOp(Node* node) {
 	UsedType left  = SemanticAnalysis_EvalType(node->bin.left);
 	UsedType right = SemanticAnalysis_EvalType(node->bin.right);
@@ -88,6 +112,10 @@ 	Type* rightType = State_GetTypeFromUsed(right);
 
 	if ((leftType->type != TYPE_PRIM) || (rightType->type != TYPE_PRIM)) {
 		PrintError(node->i.err, "You can only use primitive types in binary expressions");
+	}
+
+	if (node->bin.op == TOKEN_ASSIGN) {
+		return EvalAssign(node, left, right);
 	}
 
 	if (left.ptr) {
@@ -333,41 +361,6 @@ 			var.name = NewString(node->dim.name);
 			State_AddVar(var);
 			return GET_PRIM("unit", 0);
 		}
-// 		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 = SemanticAnalysis_EvalType(node->assign.rValue);
-// 
-// 			Type* leftType  = State_GetTypeFromUsed(left);
-// 			Type* rightType = State_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",
-// 					State_GetTypeFromUsed(left)->name, State_GetTypeFromUsed(right)->name
-// 				);
-// 			}
-// 
-// 			return GET_PRIM("unit", 0);
-// 		}
 		case NODE_EXTERN: {
 			if (inFunc) {
 				PrintError(node->i.err, "Nested function definitions are not allowed");
@@ -375,6 +368,24 @@ 			}
 
 			Function func = DecToFunc(node->funcDef.dec);
 			State_AddFunc(func);
+			return GET_PRIM("unit", 0);
+		}
+		case NODE_IF: {
+			Type conditionType = state.types[
+				SemanticAnalysis_EvalType(node->ifStat.condition).typeIdx
+			];
+
+			if (!State_IsInt(conditionType)) {
+				PrintError(node->i.err, "If condition must evaluate to an integer");
+			}
+
+			for (size_t i = 0; i < node->ifStat.bodyLen; ++ i) {
+				SemanticAnalysis_EvalType(&node->ifStat.body[i]);
+			}
+			for (size_t i = 0; i < node->ifStat.elseLen; ++ i) {
+				SemanticAnalysis_EvalType(&node->ifStat.elseBody[i]);
+			}
+			
 			return GET_PRIM("unit", 0);
 		}
 		default: {




diff --git a/basic/source/state.c b/basic/source/state.c
index e3af9ad34f6183431adfe58cb1b6f1f8a5882b05..ece1a8c4a3130ec1836438b4d24a396846640226 100644
--- a/basic/source/state.c
+++ b/basic/source/state.c
@@ -129,3 +129,9 @@
 Type* State_GetTypeFromUsed(UsedType type) {
 	return &state.types[type.typeIdx];
 }
+
+bool State_IsInt(Type type) {
+	return (type.type == TYPE_PRIM) && (
+		(type.primType == PRIM_UINT) || (type.primType == PRIM_INT)
+	);
+}




diff --git a/basic/source/state.h b/basic/source/state.h
index fcdc210ea2c966a5ad728911dd116899fd68d4b9..2af51b78e5775050144b5f848a98fbd3cfe54ebb 100644
--- a/basic/source/state.h
+++ b/basic/source/state.h
@@ -77,5 +77,6 @@ void      State_MakePrimitive(const char* name, size_t size, int primType);
 bool      State_UsedTypeEq(UsedType a, UsedType b);
 void      State_DumpInfo(void);
 Type*     State_GetTypeFromUsed(UsedType type);
+bool      State_IsInt(Type type);
 
 #endif




diff --git a/basic/test.bas b/basic/test.bas
index d480fae239708df93bba9372fba66f3e86bd24d7..1536008312bf9bc0935fe4b9119c8a2dc5e96131 100644
--- a/basic/test.bas
+++ b/basic/test.bas
@@ -7,3 +7,14 @@ 	puts("Hello!")
 end
 
 SayHello()
+
+dim a as int
+a = 5
+
+if a == 5 then
+	puts("a is 5")
+else if a == 6 then
+	puts("a is 6")
+else
+	puts("a is not 5 or 6")
+end