nitron

commit 14d7e8c8dc062c12ce383bbf344bdae0c243fc91

Author: mesyeti <mesyeti@mesyeti.uk>

add structures

 basic/source/compiler.c | 16 +-
 basic/source/compiler.h | 1 
 basic/source/frontend/c89.c | 54 ++++++++--
 basic/source/lexer.c | 19 +++
 basic/source/lexer.h | 5 
 basic/source/main.c | 9 +
 basic/source/parser.c | 179 ++++++++++++++++++++++++++++++++--
 basic/source/parser.h | 34 ++++++
 basic/source/semanticAnalysis.c | 87 ++++++++++++++++
 basic/source/state.c | 6 +
 basic/source/state.h | 9 +
 basic/test.bas | 28 ++--
 basic/test2.bas | 1 


diff --git a/basic/source/compiler.c b/basic/source/compiler.c
index b37cd3093c6494615a61343fbc5a6a43dd374bb0..d990335cd0f2ef79f3a16773d0b98f4c3add6ac2 100644
--- a/basic/source/compiler.c
+++ b/basic/source/compiler.c
@@ -20,6 +20,7 @@ 		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_WHILE:     frontend.compileWhile(&node->whileStat);   break;
 		case NODE_BINARY_OP: {
 			if (node->bin.op == TOKEN_ASSIGN) {
 				frontend.compileAssign(&node->bin);
@@ -35,11 +36,14 @@
 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_DEF:   break;
+			case NODE_DIM:        break;
+			case NODE_EXTERN:     break;
+			case NODE_TYPE_DEF:   break;
+			case NODE_TYPE_BLOCK: break;
 			case NODE_FUNC_CALL: frontend.compileFuncCall(&node->funcCall); break;
 			case NODE_IF:        frontend.compileIf(&node->ifStat);         break;
+			case NODE_WHILE:     frontend.compileWhile(&node->whileStat);   break;
 			case NODE_BINARY_OP: {
 				if (node->bin.op == TOKEN_ASSIGN) {
 					frontend.compileAssign(&node->bin);
@@ -53,9 +57,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;
 		}
 	}




diff --git a/basic/source/compiler.h b/basic/source/compiler.h
index 26db868dd7c4acf2e93fcffb7977d6768676ccc0..a6dea3c1f4865a22ba92efdef4d07d16faa865e0 100644
--- a/basic/source/compiler.h
+++ b/basic/source/compiler.h
@@ -12,6 +12,7 @@ 	void (*compileFuncCall)(FuncCallNode* node);
 	void (*compileExtern)(ExternNode* node);
 	void (*compileIf)(IfNode* node);
 	void (*compileAssign)(BinaryNode* node);
+	void (*compileWhile)(WhileNode* node);
 } Frontend;
 
 void Frontend_Init(Frontend p_frontend);




diff --git a/basic/source/frontend/c89.c b/basic/source/frontend/c89.c
index 8b10294ed608e26c89721bd58f5097465fcdd00f..422a6ce4fb8945257103789f2b856ad2247a0de1 100644
--- a/basic/source/frontend/c89.c
+++ b/basic/source/frontend/c89.c
@@ -78,8 +78,11 @@
 static void CompileDim(DimNode* node) {
 	if (inMain) return;
 
+	UsedType usedType = SemanticAnalysis_NodeAsUsedType(node->varType);
+	Type*    type     = &state.types[usedType.typeIdx];
+
 	CompileType(node->varType);
-	fprintf(out, " %s;\n", node->name);
+	fprintf(out, " %s = %s;\n", node->name, type->type == TYPE_PRIM? "0" : "{0}");
 }
 
 static void CompileExprNode(Node* node);
@@ -117,6 +120,7 @@ 				case TOKEN_EQUAL:    fprintf(out, "=="); break;
 				case TOKEN_LESS:     fprintf(out, "<");  break;
 				case TOKEN_GREATER:  fprintf(out, ">");  break;
 				case TOKEN_ASSIGN:   fprintf(out, "=");  break;
+				case TOKEN_DOT:      fprintf(out, ".");  break;
 				default:             assert(0);
 			}
 
@@ -207,20 +211,47 @@ 	CompileExprNode(node->right);
 	fprintf(out, ";\n");
 }
 
+static void CompileWhile(WhileNode* node) {
+	fprintf(out, "while (");
+	CompileExprNode(node->condition);
+	fprintf(out, ") {\n");
+
+	for (size_t i = 0; i < node->bodyLen; ++ i) {
+		Compiler_CompileBodyNode(&node->body[i]);
+	}
+
+	fprintf(out, "}\n");
+}
+
+static void CompileStruct(Type* type) {
+	// fprintf(out, "typedef %s {\n", node->isUnion? "union" : "struct");
+	fprintf(out, "typedef struct {\n");
+
+	for (size_t i = 0; i < type->structLen; ++ i) {
+		fprintf(out, "    ");
+		CompileUsedType(type->structure[i].type);
+		fprintf(out, " %s;\n", type->structure[i].name);
+	}
+
+	fprintf(out, "} nitron_%s;", type->name);
+}
+
 Frontend Frontend_C89(void) {
 	out    = fopen("out.c", "w");
 	inMain = false;
 
 	fprintf(out, "#include <stdint.h>\n");
 	fprintf(out, "#include <stdlib.h>\n");
+	fprintf(out, "#include <string.h>\n");
 
 	// define all types
 	for (size_t i = 0; i < state.typeNum; ++ i) {
 		Type* type = &state.types[i];
 
 		switch (type->type) {
-			case TYPE_PRIM: CompilePrim(type); break;
-			case TYPE_UNIT: break;
+			case TYPE_PRIM:   CompilePrim(type); break;
+			case TYPE_UNIT:   break;
+			case TYPE_STRUCT: CompileStruct(type); break;
 			default: assert(0);
 		}
 	}
@@ -234,13 +265,14 @@ 		fprintf(out, " nitron_%s;\n", var->name);
 	}
 
 	return (Frontend) {
-		.finish          = &Finish,
-		.beginMain       = &BeginMain,
-		.compileFuncDef  = &CompileFuncDef,
-		.compileDim      = &CompileDim,
-		.compileFuncCall = &CompileFuncCall,
-		.compileExtern   = &CompileExtern,
-		.compileIf       = &CompileIf,
-		.compileAssign   = &CompileAssign
+		.finish           = &Finish,
+		.beginMain        = &BeginMain,
+		.compileFuncDef   = &CompileFuncDef,
+		.compileDim       = &CompileDim,
+		.compileFuncCall  = &CompileFuncCall,
+		.compileExtern    = &CompileExtern,
+		.compileIf        = &CompileIf,
+		.compileAssign    = &CompileAssign,
+		.compileWhile     = &CompileWhile
 	};
 }




diff --git a/basic/source/lexer.c b/basic/source/lexer.c
index 8e53cee20d36e2e919285f51190398e5fad1a7dc..ff940fc2b707be22ff79e91fa07f5f562395f927 100644
--- a/basic/source/lexer.c
+++ b/basic/source/lexer.c
@@ -24,6 +24,7 @@ 		case '\n':
 		case '\t':
 		case '(':
 		case ')':
+		case '.':
 		case ',':
 		case '+':
 		case '-':
@@ -33,7 +34,8 @@ 		case '%':
 		case '=':
 		case '<':
 		case '>':
-		case '\'': return true;
+		case '\'':
+		case ';':  return true;
 		default:   return false;
 	}
 }
@@ -96,6 +98,7 @@ 			return LEXER_TOKEN;
 		}
 		case '(':  *token = TOKEN(TOKEN_LPAREN,   NULL); return LEXER_TOKEN;
 		case ')':  *token = TOKEN(TOKEN_RPAREN,   NULL); return LEXER_TOKEN;
+		case '.':  *token = TOKEN(TOKEN_DOT,      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;
@@ -167,7 +170,11 @@ 				{"ptr",      TOKEN_PTR},
 				{"end",      TOKEN_END},
 				{"if",       TOKEN_IF},
 				{"then",     TOKEN_THEN},
-				{"else",     TOKEN_ELSE}
+				{"else",     TOKEN_ELSE},
+				{"while",    TOKEN_WHILE},
+				{"do",       TOKEN_DO},
+				{"type",     TOKEN_TYPE},
+				{"union",    TOKEN_UNION}
 			};
 
 			for (size_t i = 0; i < sizeof(keywords) / sizeof(Keyword); ++ i) {
@@ -195,12 +202,20 @@ 		case TOKEN_AS:         return "as";
 		case TOKEN_QUIT:       return "quit";
 		case TOKEN_PTR:        return "ptr";
 		case TOKEN_END:        return "end";
+		case TOKEN_IF:         return "if";
+		case TOKEN_THEN:       return "then";
+		case TOKEN_ELSE:       return "else";
+		case TOKEN_WHILE:      return "while";
+		case TOKEN_DO:         return "do";
+		case TOKEN_TYPE:       return "type";
+		case TOKEN_UNION:      return "union";
 		case TOKEN_IDENTIFIER: return "identifier";
 		case TOKEN_STRING:     return "string";
 		case TOKEN_INT:        return "int";
 		case TOKEN_LINE:       return "line";
 		case TOKEN_LPAREN:     return "lparen";
 		case TOKEN_RPAREN:     return "rparen";
+		case TOKEN_DOT:        return "dot";
 		case TOKEN_COMMA:      return "comma";
 		case TOKEN_ADD:        return "add";
 		case TOKEN_SUBTRACT:   return "subtract";




diff --git a/basic/source/lexer.h b/basic/source/lexer.h
index 3b13d51817a87a47534307f4d0cf0b9087b8721e..dcc26c65179b83f62e0d450574587b1c93411605 100644
--- a/basic/source/lexer.h
+++ b/basic/source/lexer.h
@@ -20,6 +20,10 @@ 	TOKEN_END,
 	TOKEN_IF,
 	TOKEN_THEN,
 	TOKEN_ELSE,
+	TOKEN_WHILE,
+	TOKEN_DO,
+	TOKEN_TYPE,
+	TOKEN_UNION,
 
 	// misc
 	TOKEN_IDENTIFIER,
@@ -30,6 +34,7 @@
 	// characters
 	TOKEN_LPAREN,
 	TOKEN_RPAREN,
+	TOKEN_DOT,
 	TOKEN_COMMA,
 
 	// operators




diff --git a/basic/source/main.c b/basic/source/main.c
index 6c2baa46efe19dc613bea1c4f69f295514d9fa0a..9df35583c37a392556ade14513c63b1bd522fac0 100644
--- a/basic/source/main.c
+++ b/basic/source/main.c
@@ -12,6 +12,7 @@ 	const char* source = NULL;
 
 	bool printNodes  = false;
 	bool printTokens = false;
+	bool printState  = false;
 
 	if (argc == 1) {
 		printf(
@@ -33,6 +34,9 @@ 			printNodes = true;
 		}
 		else if (strcmp(argv[i], "--dl") == 0) {
 			printTokens = true;
+		}
+		else if (strcmp(argv[i], "--ds") == 0) {
+			printState = true;
 		}
 		else {
 			fprintf(stderr, "Unknown flag: %s\n", argv[i]);
@@ -69,6 +73,11 @@ 	SemanticAnalysis_Init(8);
 
 	for (size_t i = 0; i < len; ++ i){
 		SemanticAnalysis_Analyse(&ret[i]);
+	}
+
+	if (printState) {
+		State_DumpInfo();
+		return 0;
 	}
 
 	Compiler_Init(ret, len);




diff --git a/basic/source/parser.c b/basic/source/parser.c
index 84b829dee914e7daea8e86999bf6b0330472b56f..d15c1cc7833cc07e123e42265fc21629e2c56161 100644
--- a/basic/source/parser.c
+++ b/basic/source/parser.c
@@ -225,13 +225,34 @@
 	return left;
 }
 
+static Node ParseDot(Parser* p) {
+	Node left = ParseComparison(p);
+
+	while (p->tokens[p->i].type == TOKEN_DOT) {
+		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 ParseAssign(Parser* p) {
-	Node left = ParseComparison(p);
+	Node left = ParseDot(p);
 
 	while (p->tokens[p->i].type == TOKEN_ASSIGN) {
 		TokenType op = p->tokens[p->i].type;
 		Advance(p);
-		Node right = ParseComparison(p);
+		Node right = ParseDot(p);
 
 		Node* leftPtr  = SafeMalloc(sizeof(Node));
 		Node* rightPtr = SafeMalloc(sizeof(Node));
@@ -521,6 +542,100 @@ 	node.ifStat = ret;
 	return node;
 }
 
+static Node ParseWhile(Parser* p) {
+	Advance(p);
+
+	WhileNode ret;
+	ret.i = INFO(NODE_WHILE);
+
+	ret.condition = NodeToHeap(ParseBinary(p));
+
+	Expect(p, TOKEN_DO);
+	Advance(p);
+	Expect(p, TOKEN_LINE);
+	Advance(p);
+
+	ret.body = ParseBody(p, &ret.bodyLen);
+	Expect(p, TOKEN_END);
+
+	Node node;
+	node.whileStat = ret;
+	return node;
+}
+
+static Node ParseTypeDef(Parser* p) {
+	Advance(p);
+
+	char* name;
+	Expect(p, TOKEN_IDENTIFIER);
+	name = NewString(p->tokens[p->i].contents);
+
+	Advance(p);
+
+	if (p->tokens[p->i].type == TOKEN_ASSIGN) {
+		Advance(p);
+
+		TypeDefNode ret;
+		ret.i    = INFO(NODE_TYPE_DEF);
+		ret.name = name;
+		ret.type = NodeToHeap(ParseType(p));
+
+		if (ret.type->i.type == NODE_PTR) {
+			PrintError(p->tokens[p->i].err, "Cannot use pointer for type definition");
+		}
+
+		Advance(p);
+		Expect(p, TOKEN_LINE);
+
+		Node node;
+		node.typeDef = ret;
+		return node;
+	}
+
+	TypeBlockNode ret;
+	ret.i    = INFO(NODE_TYPE_BLOCK);
+	ret.name = name;
+
+	if (p->tokens[p->i].type == TOKEN_UNION) {
+		ret.isUnion = true;
+		Advance(p);
+	}
+
+	Expect(p, TOKEN_LINE);
+	Advance(p);
+
+	ret.fields    = NULL;
+	ret.fieldsNum = 0;
+
+	while (p->tokens[p->i].type != TOKEN_END) {
+		TypeBlockField field;
+
+		Expect(p, TOKEN_IDENTIFIER);
+		field.name = NewString(p->tokens[p->i].contents);
+		Advance(p);
+
+		Expect(p, TOKEN_AS);
+		Advance(p);
+
+		field.type = NodeToHeap(ParseType(p));
+		Advance(p);
+		Expect(p, TOKEN_LINE);
+		Advance(p);
+
+		// TODO: improve this
+		++ ret.fieldsNum;
+		ret.fields = SafeRealloc(ret.fields, ret.fieldsNum * sizeof(TypeBlockField));
+		ret.fields[ret.fieldsNum - 1] = field;
+	}
+
+	Advance(p);
+	Expect(p, TOKEN_LINE);
+
+	Node node;
+	node.typeBlock = ret;
+	return node;
+}
+
 static Node ParseNode(Parser* p) {
 	switch (p->tokens[p->i].type) {
 		case TOKEN_FUNC:
@@ -528,6 +643,8 @@ 		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_WHILE:  return ParseWhile(p);
+		case TOKEN_TYPE:   return ParseTypeDef(p);
 		case TOKEN_IDENTIFIER: {
 			if (p->i == p->tokenNum - 1) {
 				PrintError(p->tokens[p->i].err, "Unexpected EOF");
@@ -586,15 +703,16 @@ 			printf("(");
 			Parser_PrintNode(node->bin.left);
 
 			switch (node->bin.op) {
-				case TOKEN_ADD:      printf("+"); break;
-				case TOKEN_SUBTRACT: printf("-"); break;
-				case TOKEN_MULTIPLY: printf("*"); break;
-				case TOKEN_DIVIDE:   printf("/"); break;
+				case TOKEN_ADD:      printf("+");  break;
+				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_LESS:     printf("<"); break;
-				case TOKEN_GREATER:  printf(">"); break;
-				case TOKEN_ASSIGN:   printf("="); break;
+				case TOKEN_LESS:     printf("<");  break;
+				case TOKEN_GREATER:  printf(">");  break;
+				case TOKEN_ASSIGN:   printf("=");  break;
+				case TOKEN_DOT:      printf(".");  break;
 				default:             assert(0);
 			}
 
@@ -629,9 +747,8 @@ 		case NODE_FUNC_DEF: {
 			PrintDec(node->funcDef.dec);
 
 			for (size_t i = 0; i < node->funcDef.bodyLen; ++ i) {
-				printf("\t");
+				printf("    ");
 				Parser_PrintNode(&node->funcDef.body[i]);
-				puts("");
 			}
 			puts("end");
 			break;
@@ -680,6 +797,38 @@
 			puts("end");
 			break;
 		}
+		case NODE_WHILE: {
+			printf("while ");
+			Parser_PrintNode(node->whileStat.condition);
+			printf(" do\n");
+
+			for (size_t i = 0; i < node->whileStat.bodyLen; ++ i) {
+				printf("    ");
+				Parser_PrintNode(&node->whileStat.body[i]);
+			}
+
+			puts("end");
+			break;
+		}
+		case NODE_TYPE_DEF: {
+			printf("type %s = ", node->typeDef.name);
+			Parser_PrintNode(node->typeDef.type);
+			puts("");
+			break;
+		}
+		case NODE_TYPE_BLOCK: {
+			printf(
+				"type %s%s\n", node->typeBlock.name, node->typeBlock.isUnion? " union" : ""
+			);
+
+			for (size_t i = 0; i < node->typeBlock.fieldsNum; ++ i) {
+				printf("%s as ", node->typeBlock.fields[i].name);
+				Parser_PrintNode(node->typeBlock.fields[i].type);
+				puts("");
+			}
+			puts("end");
+			break;
+		}
 		default: {
 			printf("unknown %d\n", node->i.type);
 			assert(0);
@@ -782,6 +931,11 @@ 				FreeNodes(node->ifStat.elseBody, node->ifStat.elseLen);
 			}
 			break;
 		}
+		case NODE_WHILE: {
+			Parser_FreeNode(node->whileStat.condition);
+			FreeNodes(node->whileStat.body, node->whileStat.bodyLen);
+			break;
+		}
 		default: return;
 	}
 }
@@ -800,6 +954,9 @@ 		case NODE_DIM:        return "dim";
 		case NODE_FUNC_CALL:  return "function call";
 		case NODE_EXTERN:     return "extern";
 		case NODE_IF:         return "if";
+		case NODE_WHILE:      return "while";
+		case NODE_TYPE_DEF:   return "type def";
+		case NODE_TYPE_BLOCK: return "type block";
 		default:              return "???";
 	}
 }




diff --git a/basic/source/parser.h b/basic/source/parser.h
index 83a7a494083d44d595164b4e308c2719daba5907..afda080435be4578eba425dc8401b6b0c203504e 100644
--- a/basic/source/parser.h
+++ b/basic/source/parser.h
@@ -17,7 +17,10 @@ 	NODE_FUNC_DEF,
 	NODE_DIM,
 	NODE_FUNC_CALL,
 	NODE_EXTERN,
-	NODE_IF
+	NODE_IF,
+	NODE_WHILE,
+	NODE_TYPE_DEF,
+	NODE_TYPE_BLOCK
 } NodeType;
 
 typedef struct {
@@ -106,6 +109,32 @@ 	Node*    elseBody;
 	size_t   elseLen;
 } IfNode;
 
+typedef struct {
+	NodeInfo i;
+	Node*    condition;
+	Node*    body;
+	size_t   bodyLen;
+} WhileNode;
+
+typedef struct {
+	NodeInfo i;
+	char*    name;
+	Node*    type;
+} TypeDefNode;
+
+typedef struct {
+	char* name;
+	Node* type;
+} TypeBlockField;
+
+typedef struct {
+	NodeInfo        i;
+	char*           name;
+	bool            isUnion;
+	TypeBlockField* fields;
+	size_t          fieldsNum;
+} TypeBlockNode;
+
 union Node {
 	NodeInfo       i;
 	IntNode        integer;
@@ -119,6 +148,9 @@ 	DimNode        dim;
 	FuncCallNode   funcCall;
 	ExternNode     external;
 	IfNode         ifStat;
+	WhileNode      whileStat;
+	TypeDefNode    typeDef;
+	TypeBlockNode  typeBlock;
 };
 
 typedef struct {




diff --git a/basic/source/semanticAnalysis.c b/basic/source/semanticAnalysis.c
index dd44ad0017c195f9bd81388f228550d749f68caf..86137ff47ad93546256e5e75aeb39d198a306f45 100644
--- a/basic/source/semanticAnalysis.c
+++ b/basic/source/semanticAnalysis.c
@@ -103,8 +103,35 @@
 	return GET_PRIM("unit", 0);
 }
 
+static UsedType EvalDot(Node* node, UsedType left) {
+	Type* leftType = State_GetTypeFromUsed(left);
+
+	if (node->bin.right->i.type != NODE_IDENTIFIER) {
+		PrintError(node->i.err, "Right operand of `.` operator must be identifier");
+	}
+	if (leftType->type != TYPE_STRUCT) {
+		PrintError(node->i.err, "Using `.` operator on primitive or unit type");
+	}
+
+	const char* member = node->bin.right->ident.name;
+
+	for (size_t i = 0; i < leftType->structLen; ++ i) {
+		if (strcmp(leftType->structure[i].name, member) == 0) {
+			return leftType->structure[i].type;
+		}
+	}
+
+	PrintError(node->i.err, "Type '%s' does not have member '%s'", leftType->name, member);
+	return GET_PRIM("unit", 0);
+}
+
 static UsedType EvalBinOp(Node* node) {
-	UsedType left  = SemanticAnalysis_EvalType(node->bin.left);
+	UsedType left = SemanticAnalysis_EvalType(node->bin.left);
+
+	if (node->bin.op == TOKEN_DOT) {
+		return EvalDot(node, left);
+	}
+
 	UsedType right = SemanticAnalysis_EvalType(node->bin.right);
 
 	Type* leftType  = State_GetTypeFromUsed(left);
@@ -386,6 +413,64 @@ 			for (size_t i = 0; i < node->ifStat.elseLen; ++ i) {
 				SemanticAnalysis_EvalType(&node->ifStat.elseBody[i]);
 			}
 			
+			return GET_PRIM("unit", 0);
+		}
+		case NODE_WHILE: {
+			Type conditionType = state.types[
+				SemanticAnalysis_EvalType(node->whileStat.condition).typeIdx
+			];
+
+			if (!State_IsInt(conditionType)) {
+				PrintError(node->i.err, "While condition must evaluate to an integer");
+			}
+
+			for (size_t i = 0; i < node->whileStat.bodyLen; ++ i) {
+				SemanticAnalysis_EvalType(&node->whileStat.body[i]);
+			}
+
+			return GET_PRIM("unit", 0);
+		}
+		case NODE_TYPE_DEF: {
+			if (State_GetType(node->typeDef.name) != NULL) {
+				PrintError(node->i.err, "Type '%s' already exists", node->typeDef.name);
+			}
+
+			assert(node->typeDef.type->i.type == NODE_IDENTIFIER);
+
+			const char* fromName = node->typeDef.type->ident.name;
+			Type*       from     = State_GetType(fromName);
+
+			if (!from) {
+				PrintError(node->i.err, "Type '%s' does not exist", fromName);
+			}
+
+			Type newType = *from;
+			newType.name = NewString(node->typeDef.name);
+			State_AddType(newType);
+
+			return GET_PRIM("unit", 0);
+		}
+		case NODE_TYPE_BLOCK: {
+			if (State_GetType(node->typeBlock.name) != NULL) {
+				PrintError(node->i.err, "Type '%s' already exists", node->typeBlock.name);
+			}
+
+			Type newType;
+			newType.type = TYPE_STRUCT;
+			newType.name = NewString(node->typeBlock.name);
+			newType.size = 0;
+
+			newType.structure = SafeMalloc(node->typeBlock.fieldsNum * sizeof(TypeField));
+			newType.structLen = node->typeBlock.fieldsNum;
+
+			for (size_t i = 0; i < node->typeBlock.fieldsNum; ++ i) {
+				newType.structure[i] = (TypeField) {
+					.name = NewString(node->typeBlock.fields[i].name),
+					.type = SemanticAnalysis_NodeAsUsedType(node->typeBlock.fields[i].type)
+				};
+			}
+
+			State_AddType(newType);
 			return GET_PRIM("unit", 0);
 		}
 		default: {




diff --git a/basic/source/state.c b/basic/source/state.c
index ece1a8c4a3130ec1836438b4d24a396846640226..cbc1d5d22a58a42866e7bbc3fe35afe4afd1afb9 100644
--- a/basic/source/state.c
+++ b/basic/source/state.c
@@ -108,6 +108,12 @@ 	puts("TYPES");
 	puts("=====");
 	for (size_t i = 0; i < state.typeNum; ++ i) {
 		printf("- %s\n", state.types[i].name);
+
+		if (state.types[i].type == TYPE_STRUCT) {
+			for (size_t j = 0; j < state.types[i].structLen; ++ j) {
+				printf("    - %s\n", state.types[i].structure[j].name);
+			}
+		}
 	}
 	puts("\n");
 




diff --git a/basic/source/state.h b/basic/source/state.h
index 2af51b78e5775050144b5f848a98fbd3cfe54ebb..a52b2bed4cd11062a5743e1852a08a80ed514053 100644
--- a/basic/source/state.h
+++ b/basic/source/state.h
@@ -22,13 +22,18 @@ 	size_t array; // 0 if not array, contains length if not array
 } UsedType;
 
 typedef struct {
+	char*    name;
+	UsedType type;
+} TypeField;
+
+typedef struct {
 	int    type;
 	char*  name;
 	size_t size;
 
 	// only for "struct" types
-	UsedType* structure;
-	size_t    structLen;
+	TypeField* structure;
+	size_t     structLen;
 
 	// only for "primitive" types
 	int primType;




diff --git a/basic/test.bas b/basic/test.bas
index 1536008312bf9bc0935fe4b9119c8a2dc5e96131..3068b3bdf95fd46971f33ffeab1e095d2ecb58ac 100644
--- a/basic/test.bas
+++ b/basic/test.bas
@@ -1,20 +1,22 @@
 'NITRON
 
-extern func puts(str as ptr(char)) int
+extern func puts(str as ptr(char)) i32
+extern func printf(str as ptr(char), val as int) i32
 
-sub SayHello()
-	puts("Hello!")
+type MyStruct
+	bol as int
+	polly as int
 end
 
-SayHello()
+type MyInt = int
 
-dim a as int
-a = 5
+dim manul as MyStruct
 
-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
+manul.bol   = 11
+manul.polly = 10
+
+' should work
+printf("bol: %d\n", manul.bol)
+
+' should also work
+printf("polly: %d\n", manul.polly)




diff --git a/basic/test2.bas b/basic/test2.bas
new file mode 100644
index 0000000000000000000000000000000000000000..08fb2be0209cd470a4560c6a56948753682be1e5
--- /dev/null
+++ b/basic/test2.bas
@@ -0,0 +1 @@
+print(1+2+3+4)