diff --git a/include/scirpt/ast.h b/include/scirpt/ast.h
index ecb0199..ff034dd 100644
--- a/include/scirpt/ast.h
+++ b/include/scirpt/ast.h
@@ -3,6 +3,7 @@
 
 #include "common/generic_array.h"
 #include "common/string.h"
+#include "common/string_array.h"
 #include "scirpt/position.h"
 #include <stdbool.h>
 #include <stdint.h>
@@ -18,13 +19,20 @@ typedef enum {
 	ScirptAstExprTypeBool,
 	ScirptAstExprTypeBlock,
 	ScirptAstExprTypeIf,
+	ScirptAstExprTypeLoop,
+	ScirptAstExprTypeWhile,
+	ScirptAstExprTypeFor,
 	ScirptAstExprTypeMember,
 	ScirptAstExprTypeCall,
 	ScirptAstExprTypeIndex,
 	ScirptAstExprTypeUnary,
 	ScirptAstExprTypeBinary,
 	ScirptAstExprTypeAssign,
+	ScirptAstExprTypeLambda,
+	ScirptAstExprTypeFunction,
 	ScirptAstExprTypeLet,
+	ScirptAstExprTypeReturn,
+	ScirptAstExprTypeBreak,
 } ScirptAstExprType;
 
 typedef struct ScirptAstExpr ScirptAstExpr;
@@ -41,6 +49,21 @@ typedef struct {
 	ScirptAstExpr* falsy;
 } ScirptAstExprIf;
 
+typedef struct {
+	ScirptAstExpr* body;
+} ScirptAstExprLoop;
+
+typedef struct {
+	ScirptAstExpr* condition;
+	ScirptAstExpr* body;
+} ScirptAstExprWhile;
+
+typedef struct {
+	HeapString subject;
+	ScirptAstExpr* value;
+	ScirptAstExpr* body;
+} ScirptAstExprFor;
+
 typedef struct {
 	ScirptAstExpr* subject;
 	HeapString value;
@@ -72,12 +95,16 @@ typedef enum {
 	ScirptAstExprBinaryTypeMultiply,
 	ScirptAstExprBinaryTypeDivide,
 	ScirptAstExprBinaryTypeModulo,
-	ScirptAstExprBinaryTypeEqual,
-	ScirptAstExprBinaryTypeInequal,
+	ScirptAstExprBinaryTypeExponent,
 	ScirptAstExprBinaryTypeLt,
 	ScirptAstExprBinaryTypeGt,
 	ScirptAstExprBinaryTypeLtEqual,
 	ScirptAstExprBinaryTypeGtEqual,
+	ScirptAstExprBinaryTypeIn,
+	ScirptAstExprBinaryTypeEqual,
+	ScirptAstExprBinaryTypeInequal,
+	ScirptAstExprBinaryTypeAnd,
+	ScirptAstExprBinaryTypeOr,
 } ScirptAstExprBinaryType;
 
 typedef struct {
@@ -87,24 +114,45 @@ typedef struct {
 } ScirptAstExprBinary;
 
 typedef enum {
-	ScirptAstExprAssignTypeAssign,
+	ScirptAstExprAssignTypeEqual,
 	ScirptAstExprAssignTypeAdd,
 	ScirptAstExprAssignTypeSubtract,
 	ScirptAstExprAssignTypeMultiply,
 	ScirptAstExprAssignTypeDivide,
 	ScirptAstExprAssignTypeModulo,
+	ScirptAstExprAssignTypeExponent,
 } ScirptAstExprAssignType;
 
 typedef struct {
+	ScirptAstExprAssignType type;
 	ScirptAstExpr* subject;
 	ScirptAstExpr* value;
 } ScirptAstExprAssign;
 
+typedef struct {
+	HeapStringArray params;
+	ScirptAstExpr* body;
+} ScirptAstExprLambda;
+
+typedef struct {
+	HeapString subject;
+	HeapStringArray params;
+	ScirptAstExpr* body;
+} ScirptAstExprFunction;
+
 typedef struct {
 	HeapString subject;
 	ScirptAstExpr* value;
 } ScirptAstExprLet;
 
+typedef struct {
+	ScirptAstExpr* value;
+} ScirptAstExprReturn;
+
+typedef struct {
+	ScirptAstExpr* value;
+} ScirptAstExprBreak;
+
 struct ScirptAstExpr {
 	ScirptAstExprType type;
 	ScirptPosition pos;
@@ -116,13 +164,20 @@ struct ScirptAstExpr {
 		bool bool_value;
 		ScirptAstExprBlock block;
 		ScirptAstExprIf if_expr;
+		ScirptAstExprLoop loop;
+		ScirptAstExprWhile while_expr;
+		ScirptAstExprFor for_expr;
 		ScirptAstExprMember member;
 		ScirptAstExprCall call;
 		ScirptAstExprIndex index;
 		ScirptAstExprUnary unary;
 		ScirptAstExprBinary binary;
-		// assign
-		// let
+		ScirptAstExprAssign assign;
+		ScirptAstExprLambda lambda;
+		ScirptAstExprFunction function;
+		ScirptAstExprLet let;
+		ScirptAstExprReturn return_statement;
+		ScirptAstExprBreak break_statement;
 	};
 };
 
diff --git a/include/scirpt/token.h b/include/scirpt/token.h
index 30f8702..b512e03 100644
--- a/include/scirpt/token.h
+++ b/include/scirpt/token.h
@@ -29,6 +29,8 @@ typedef enum {
 	ScirptTokenTypeMinusEqual,
 	ScirptTokenTypeAsterisk,
 	ScirptTokenTypeAsteriskEqual,
+	ScirptTokenTypeAsteriskAsterisk,
+	ScirptTokenTypeAsteriskAsteriskEqual,
 	ScirptTokenTypeSlash,
 	ScirptTokenTypeSlashEqual,
 	ScirptTokenTypePercent,
@@ -50,6 +52,7 @@ typedef enum {
 	ScirptTokenTypeLet,
 	ScirptTokenTypeIf,
 	ScirptTokenTypeElse,
+	ScirptTokenTypeLoop,
 	ScirptTokenTypeWhile,
 	ScirptTokenTypeFor,
 	ScirptTokenTypeIn,
diff --git a/scirpt/ast.c b/scirpt/ast.c
index 9671ecf..e15ddec 100644
--- a/scirpt/ast.c
+++ b/scirpt/ast.c
@@ -1,4 +1,6 @@
 #include "scirpt/ast.h"
+#include "common/string.h"
+#include "common/string_array.h"
 #include <stddef.h>
 #include <stdlib.h>
 
@@ -34,6 +36,18 @@ void scirpt_ast_expr_delete(ScirptAstExpr* expr)
 			if (expr->if_expr.falsy)
 				scirpt_ast_expr_delete(expr->if_expr.falsy);
 			break;
+		case ScirptAstExprTypeLoop:
+			scirpt_ast_expr_delete(expr->loop.body);
+			break;
+		case ScirptAstExprTypeWhile:
+			scirpt_ast_expr_delete(expr->while_expr.condition);
+			scirpt_ast_expr_delete(expr->while_expr.body);
+			break;
+		case ScirptAstExprTypeFor:
+			heapstring_destroy(&expr->for_expr.subject);
+			scirpt_ast_expr_delete(expr->for_expr.value);
+			scirpt_ast_expr_delete(expr->for_expr.body);
+			break;
 		case ScirptAstExprTypeMember:
 			scirpt_ast_expr_delete(expr->member.subject);
 			heapstring_destroy(&expr->member.value);
@@ -49,12 +63,38 @@ void scirpt_ast_expr_delete(ScirptAstExpr* expr)
 			scirpt_ast_expr_delete(expr->index.value);
 			break;
 		case ScirptAstExprTypeUnary:
+			scirpt_ast_expr_delete(expr->unary.subject);
 			break;
 		case ScirptAstExprTypeBinary:
+			scirpt_ast_expr_delete(expr->binary.left);
+			scirpt_ast_expr_delete(expr->binary.right);
 			break;
 		case ScirptAstExprTypeAssign:
+			scirpt_ast_expr_delete(expr->assign.subject);
+			scirpt_ast_expr_delete(expr->assign.value);
+			break;
+		case ScirptAstExprTypeLambda:
+			for (size_t i = 0; i < expr->lambda.params.length; ++i)
+				heapstring_destroy(&expr->lambda.params.data[i]);
+			heapstring_array_destroy(&expr->lambda.params);
+			scirpt_ast_expr_delete(expr->lambda.body);
+			break;
+		case ScirptAstExprTypeFunction:
+			heapstring_destroy(&expr->function.subject);
+			for (size_t i = 0; i < expr->function.params.length; ++i)
+				heapstring_destroy(&expr->function.params.data[i]);
+			heapstring_array_destroy(&expr->function.params);
+			scirpt_ast_expr_delete(expr->function.body);
 			break;
 		case ScirptAstExprTypeLet:
+			heapstring_destroy(&expr->let.subject);
+			scirpt_ast_expr_delete(expr->let.value);
+			break;
+		case ScirptAstExprTypeReturn:
+			scirpt_ast_expr_delete(expr->return_statement.value);
+			break;
+		case ScirptAstExprTypeBreak:
+			scirpt_ast_expr_delete(expr->break_statement.value);
 			break;
 	}
 }
diff --git a/scirpt/lexer.c b/scirpt/lexer.c
index cd177aa..a5be82c 100644
--- a/scirpt/lexer.c
+++ b/scirpt/lexer.c
@@ -60,6 +60,7 @@ void scirpt_lexer_create(
 	add_keyword(keywords, "let", TT(Let));
 	add_keyword(keywords, "if", TT(If));
 	add_keyword(keywords, "else", TT(Else));
+	add_keyword(keywords, "loop", TT(Loop));
 	add_keyword(keywords, "while", TT(While));
 	add_keyword(keywords, "for", TT(For));
 	add_keyword(keywords, "in", TT(In));
@@ -177,9 +178,7 @@ ScirptToken scirpt_lexer_level_2(ScirptLexer* lexer)
 				lexer, TT(Minus), '=', TT(MinusEqual)
 			);
 		case '*':
-			return single_or_double_token(
-				lexer, TT(Asterisk), '=', TT(AsteriskEqual)
-			);
+			return scirpt_lexer_asterisk_token(lexer);
 		case '/':
 			return single_token(lexer, TT(Slash));
 		case '%':
@@ -273,6 +272,26 @@ ScirptToken scirpt_lexer_dot_token(ScirptLexer* lexer)
 	}
 }
 
+ScirptToken scirpt_lexer_asterisk_token(ScirptLexer* lexer)
+{
+	ScirptPosition start = pos(lexer);
+	step(lexer);
+	if (current_is(lexer, '=')) {
+		step(lexer);
+		return token(lexer, TT(AsteriskEqual), start);
+	} else if (current_is(lexer, '*')) {
+		step(lexer);
+		if (current_is(lexer, '=')) {
+			step(lexer);
+			return token(lexer, TT(AsteriskAsteriskEqual), start);
+		} else {
+			return token(lexer, TT(AsteriskAsterisk), start);
+		}
+	} else {
+		return token(lexer, TT(Asterisk), start);
+	}
+}
+
 ScirptToken scirpt_lexer_slash_token(ScirptLexer* lexer)
 {
 
diff --git a/scirpt/lexer.h b/scirpt/lexer.h
index cb14dfa..46057da 100644
--- a/scirpt/lexer.h
+++ b/scirpt/lexer.h
@@ -27,6 +27,7 @@ ScirptToken scirpt_lexer_id_token(ScirptLexer* lexer);
 ScirptToken scirpt_lexer_int_token(ScirptLexer* lexer);
 ScirptToken scirpt_lexer_string_token(ScirptLexer* lexer);
 ScirptToken scirpt_lexer_dot_token(ScirptLexer* lexer);
+ScirptToken scirpt_lexer_asterisk_token(ScirptLexer* lexer);
 ScirptToken scirpt_lexer_slash_token(ScirptLexer* lexer);
 void scirpt_lexer_step(ScirptLexer* lexer);
 ScirptPosition scirpt_lexer_pos(const ScirptLexer* lexer);
diff --git a/scirpt/main.c b/scirpt/main.c
index 50ea603..26177cd 100644
--- a/scirpt/main.c
+++ b/scirpt/main.c
@@ -9,8 +9,9 @@
 
 int main(void)
 {
-	const char* text = "if 123 { \"abc\"; abc()() }";
-	printf("= test program = \"\"\"\n%s\n\"\"\"\n", text);
+	const char* text
+		= "if 123 { \"abc\"; abc()() }; for a in b { print(123, 123, 123,) }";
+	printf("\nprogram = \"\"\"\n%s\n\"\"\"\n", text);
 
 	{
 		printf("\n- test lexer\n");
diff --git a/scirpt/parser.c b/scirpt/parser.c
index 563ecde..fdcf2d8 100644
--- a/scirpt/parser.c
+++ b/scirpt/parser.c
@@ -1,10 +1,12 @@
 #include "scirpt/parser.h"
 #include "common/string.h"
+#include "common/string_array.h"
 #include "parser.h"
 #include "scirpt/ast.h"
 #include "scirpt/lexer.h"
 #include "scirpt/position.h"
 #include "scirpt/token.h"
+#include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -55,6 +57,11 @@ ScirptAstExpr* scirpt_parser_next(ScirptParser* parser)
 		return alloc_expr((ScirptAstExpr) {
 			.type = AET(Eof),
 		});
+	} else if (parser->current.type == TT(Semicolon)) {
+		step(parser);
+		while (current_is(parser, TT(Semicolon)))
+			step(parser);
+		return scirpt_parser_next(parser);
 	} else {
 		return scirpt_parser_parse_statement(parser);
 	}
@@ -94,19 +101,291 @@ void scirpt_parser_destroy(ScirptParser* parser)
 
 ScirptAstExpr* scirpt_parser_parse_statement(ScirptParser* parser)
 {
-	return scirpt_parser_parse_expr(parser);
+	switch (parser->current.type) {
+		case TT(If):
+			return scirpt_parser_parse_if(parser);
+		case TT(Loop):
+			return scirpt_parser_parse_loop(parser);
+		case TT(While):
+			return scirpt_parser_parse_while(parser);
+		case TT(For):
+			return scirpt_parser_parse_for(parser);
+		case TT(Let):
+			return scirpt_parser_parse_let(parser);
+		case TT(Fn):
+			return scirpt_parser_parse_function(parser);
+		case TT(Return):
+			return scirpt_parser_parse_return(parser);
+		case TT(Break):
+			return scirpt_parser_parse_break(parser);
+		default:
+			return scirpt_parser_parse_assign(parser);
+	}
+}
+
+static inline ScirptAstExpr* assign(
+	ScirptAstExprAssignType type,
+	ScirptAstExpr* subject,
+	ScirptAstExpr* value,
+	ScirptPosition pos
+)
+{
+	return alloc_expr((ScirptAstExpr) {
+		.type = AET(Assign),
+		.pos = pos,
+		.assign = (ScirptAstExprAssign) {
+			.type = type,
+			.subject = subject,
+			.value = value,
+		},
+	});
+}
+
+ScirptAstExpr* scirpt_parser_parse_assign(ScirptParser* parser)
+{
+	ScirptPosition pos = parser->current.pos;
+	ScirptAstExpr* subject = scirpt_parser_parse_expr(parser);
+	if (current_is(parser, TT(Equal))) {
+		step(parser);
+		ScirptAstExpr* value = scirpt_parser_parse_expr(parser);
+		return assign(ScirptAstExprAssignTypeEqual, subject, value, pos);
+	} else if (current_is(parser, TT(PlusEqual))) {
+		step(parser);
+		ScirptAstExpr* value = scirpt_parser_parse_expr(parser);
+		return assign(ScirptAstExprAssignTypeAdd, subject, value, pos);
+	} else if (current_is(parser, TT(MinusEqual))) {
+		step(parser);
+		ScirptAstExpr* value = scirpt_parser_parse_expr(parser);
+		return assign(ScirptAstExprAssignTypeSubtract, subject, value, pos);
+	} else if (current_is(parser, TT(AsteriskEqual))) {
+		step(parser);
+		ScirptAstExpr* value = scirpt_parser_parse_expr(parser);
+		return assign(ScirptAstExprAssignTypeMultiply, subject, value, pos);
+	} else if (current_is(parser, TT(SlashEqual))) {
+		step(parser);
+		ScirptAstExpr* value = scirpt_parser_parse_expr(parser);
+		return assign(ScirptAstExprAssignTypeDivide, subject, value, pos);
+	} else if (current_is(parser, TT(PercentEqual))) {
+		step(parser);
+		ScirptAstExpr* value = scirpt_parser_parse_expr(parser);
+		return assign(ScirptAstExprAssignTypeModulo, subject, value, pos);
+	} else if (current_is(parser, TT(AsteriskAsteriskEqual))) {
+		step(parser);
+		ScirptAstExpr* value = scirpt_parser_parse_expr(parser);
+		return assign(ScirptAstExprAssignTypeExponent, subject, value, pos);
+	} else {
+		return subject;
+	}
 }
 
 ScirptAstExpr* scirpt_parser_parse_expr(ScirptParser* parser)
 {
-	return scirpt_parser_parse_member_call_index_expr(parser);
+	return scirpt_parser_parse_or(parser);
+}
+
+static inline ScirptAstExpr* binary(
+	ScirptAstExprBinaryType type,
+	ScirptAstExpr* left,
+	ScirptAstExpr* right,
+	ScirptPosition pos
+)
+{
+	return alloc_expr((ScirptAstExpr) {
+		.type = AET(Binary),
+		.pos = pos,
+		.binary = (ScirptAstExprBinary) {
+			.type = type,
+			.left = left,
+			.right = right,
+		},
+	});
+}
+
+ScirptAstExpr* scirpt_parser_parse_or(ScirptParser* parser)
+{
+	ScirptPosition pos = parser->current.pos;
+	ScirptAstExpr* left = scirpt_parser_parse_and(parser);
+	while (!done(parser)) {
+		if (current_is(parser, TT(Or))) {
+			step(parser);
+			ScirptAstExpr* right = scirpt_parser_parse_and(parser);
+			left = binary(ScirptAstExprBinaryTypeOr, left, right, pos);
+		} else {
+			break;
+		}
+	}
+	return left;
+}
+
+ScirptAstExpr* scirpt_parser_parse_and(ScirptParser* parser)
+{
+	ScirptPosition pos = parser->current.pos;
+	ScirptAstExpr* left = scirpt_parser_parse_equality(parser);
+	while (!done(parser)) {
+		if (current_is(parser, TT(And))) {
+			step(parser);
+			ScirptAstExpr* right = scirpt_parser_parse_equality(parser);
+			left = binary(ScirptAstExprBinaryTypeAnd, left, right, pos);
+		} else {
+			break;
+		}
+	}
+	return left;
+}
+
+ScirptAstExpr* scirpt_parser_parse_equality(ScirptParser* parser)
+{
+	ScirptPosition pos = parser->current.pos;
+	ScirptAstExpr* left = scirpt_parser_parse_comparison(parser);
+	while (!done(parser)) {
+		if (current_is(parser, TT(EqualEqual))) {
+			step(parser);
+			ScirptAstExpr* right = scirpt_parser_parse_comparison(parser);
+			left = binary(ScirptAstExprBinaryTypeEqual, left, right, pos);
+		} else if (current_is(parser, TT(ExclamationEqual))) {
+			step(parser);
+			ScirptAstExpr* right = scirpt_parser_parse_comparison(parser);
+			left = binary(ScirptAstExprBinaryTypeInequal, left, right, pos);
+		} else {
+			break;
+		}
+	}
+	return left;
+}
+
+ScirptAstExpr* scirpt_parser_parse_comparison(ScirptParser* parser)
+{
+	ScirptPosition pos = parser->current.pos;
+	ScirptAstExpr* left = scirpt_parser_parse_add_subtract(parser);
+	if (current_is(parser, TT(Lt))) {
+		step(parser);
+		ScirptAstExpr* right = scirpt_parser_parse_add_subtract(parser);
+		return binary(ScirptAstExprBinaryTypeLt, left, right, pos);
+	} else if (current_is(parser, TT(Gt))) {
+		step(parser);
+		ScirptAstExpr* right = scirpt_parser_parse_add_subtract(parser);
+		return binary(ScirptAstExprBinaryTypeGt, left, right, pos);
+	} else if (current_is(parser, TT(LtEqual))) {
+		step(parser);
+		ScirptAstExpr* right = scirpt_parser_parse_add_subtract(parser);
+		return binary(ScirptAstExprBinaryTypeLtEqual, left, right, pos);
+	} else if (current_is(parser, TT(GtEqual))) {
+		step(parser);
+		ScirptAstExpr* right = scirpt_parser_parse_add_subtract(parser);
+		return binary(ScirptAstExprBinaryTypeGtEqual, left, right, pos);
+	} else if (current_is(parser, TT(In))) {
+		step(parser);
+		ScirptAstExpr* right = scirpt_parser_parse_add_subtract(parser);
+		return binary(ScirptAstExprBinaryTypeIn, left, right, pos);
+	} else {
+		return left;
+	}
+}
+
+ScirptAstExpr* scirpt_parser_parse_add_subtract(ScirptParser* parser)
+{
+	ScirptPosition pos = parser->current.pos;
+	ScirptAstExpr* left = scirpt_parser_parse_multiply_divide_modulo(parser);
+	while (!done(parser)) {
+		if (current_is(parser, TT(Plus))) {
+			step(parser);
+			ScirptAstExpr* right
+				= scirpt_parser_parse_multiply_divide_modulo(parser);
+			left = binary(ScirptAstExprBinaryTypeAdd, left, right, pos);
+		} else if (current_is(parser, TT(Minus))) {
+			step(parser);
+			ScirptAstExpr* right
+				= scirpt_parser_parse_multiply_divide_modulo(parser);
+			left = binary(ScirptAstExprBinaryTypeSubtract, left, right, pos);
+		} else {
+			break;
+		}
+	}
+	return left;
+}
+
+ScirptAstExpr* scirpt_parser_parse_multiply_divide_modulo(ScirptParser* parser)
+{
+	ScirptPosition pos = parser->current.pos;
+	ScirptAstExpr* left = scirpt_parser_parse_negate(parser);
+	while (!done(parser)) {
+		if (current_is(parser, TT(Asterisk))) {
+			step(parser);
+			ScirptAstExpr* right = scirpt_parser_parse_negate(parser);
+			left = binary(ScirptAstExprBinaryTypeMultiply, left, right, pos);
+		} else if (current_is(parser, TT(Slash))) {
+			step(parser);
+			ScirptAstExpr* right = scirpt_parser_parse_negate(parser);
+			left = binary(ScirptAstExprBinaryTypeDivide, left, right, pos);
+		} else if (current_is(parser, TT(Percent))) {
+			step(parser);
+			ScirptAstExpr* right = scirpt_parser_parse_negate(parser);
+			left = binary(ScirptAstExprBinaryTypeModulo, left, right, pos);
+		} else {
+			break;
+		}
+	}
+	return left;
+}
+
+static inline ScirptAstExpr*
+unary(ScirptAstExprUnaryType type, ScirptAstExpr* subject, ScirptPosition pos)
+{
+	return alloc_expr((ScirptAstExpr) {
+		.type = AET(Unary),
+		.pos = pos,
+		.unary = (ScirptAstExprUnary) {
+			.type = type,
+			.subject = subject,
+		}, 
+	});
+}
+
+ScirptAstExpr* scirpt_parser_parse_negate(ScirptParser* parser)
+{
+	if (current_is(parser, TT(Minus))) {
+		ScirptPosition pos = parser->current.pos;
+		step(parser);
+		ScirptAstExpr* subject = scirpt_parser_parse_negate(parser);
+		return unary(ScirptAstExprUnaryTypeNegate, subject, pos);
+	} else {
+		return scirpt_parser_parse_exponent(parser);
+	}
+}
+
+ScirptAstExpr* scirpt_parser_parse_exponent(ScirptParser* parser)
+{
+	ScirptPosition pos = parser->current.pos;
+	ScirptAstExpr* left = scirpt_parser_parse_unary(parser);
+	while (!done(parser)) {
+		if (current_is(parser, TT(AsteriskAsterisk))) {
+			step(parser);
+			ScirptAstExpr* right = scirpt_parser_parse_unary(parser);
+			left = binary(ScirptAstExprBinaryTypeExponent, left, right, pos);
+		} else {
+			break;
+		}
+	}
+	return left;
+}
+
+ScirptAstExpr* scirpt_parser_parse_unary(ScirptParser* parser)
+{
+	ScirptPosition pos = parser->current.pos;
+	if (current_is(parser, TT(Not))) {
+		step(parser);
+		ScirptAstExpr* subject = scirpt_parser_parse_unary(parser);
+		return unary(ScirptAstExprUnaryTypeNot, subject, pos);
+	} else {
+		return scirpt_parser_parse_member_call_index_expr(parser);
+	}
 }
 
 ScirptAstExpr* scirpt_parser_parse_member_call_index_expr(ScirptParser* parser)
 {
 	ScirptPosition pos = parser->current.pos;
 	ScirptAstExpr* subject = scirpt_parser_parse_operand(parser);
-	while (true) {
+	while (!done(parser)) {
 		if (current_is(parser, TT(Dot))) {
 			step(parser);
 			if (!current_is(parser, TT(Id))) {
@@ -248,6 +527,14 @@ ScirptAstExpr* scirpt_parser_parse_operand(ScirptParser* parser)
 			return scirpt_parser_parse_block(parser);
 		case TT(If):
 			return scirpt_parser_parse_if(parser);
+		case TT(Loop):
+			return scirpt_parser_parse_loop(parser);
+		case TT(While):
+			return scirpt_parser_parse_while(parser);
+		case TT(For):
+			return scirpt_parser_parse_for(parser);
+		case TT(Fn):
+			return scirpt_parser_parse_lambda(parser);
 		case TT(Eof): {
 			error(
 				parser,
@@ -295,6 +582,7 @@ ScirptAstExpr* scirpt_parser_parse_operand(ScirptParser* parser)
 			});
 		}
 		default: {
+			fprintf(stderr, "unhandled token %d\n", parser->current.type);
 			error(
 				parser,
 				heapstring_from_cstring("expected value"),
@@ -374,6 +662,8 @@ ScirptAstExpr* scirpt_parser_parse_string(ScirptParser* parser)
 static inline bool requires_semicolon(ScirptAstExprType type)
 {
 	switch (type) {
+		case AET(Let):
+			return true;
 		default:
 			return false;
 	}
@@ -396,7 +686,9 @@ ScirptAstExpr* scirpt_parser_parse_block(ScirptParser* parser)
 			if (requires_semicolon(statement->type)) {
 				error(
 					parser,
-					heapstring_from_cstring("';' required"),
+					heapstring_from_cstring(
+						"expected ';', required for statement"
+					),
 					parser->current.pos
 				);
 			}
@@ -407,7 +699,9 @@ ScirptAstExpr* scirpt_parser_parse_block(ScirptParser* parser)
 		error(
 			parser, heapstring_from_cstring("expected '}'"), parser->current.pos
 		);
-		// TODO clean up statements
+		for (size_t i = 0; i < statements.length; ++i)
+			scirpt_ast_expr_delete(statements.data[i]);
+		scirpt_ast_expr_array_destroy(&statements);
 		return alloc_expr((ScirptAstExpr) {
 			.type = AET(Error),
 			.pos = pos,
@@ -463,6 +757,375 @@ ScirptAstExpr* scirpt_parser_parse_if(ScirptParser* parser)
 	});
 }
 
+ScirptAstExpr* scirpt_parser_parse_loop(ScirptParser* parser)
+{
+	ScirptPosition pos = parser->current.pos;
+	step(parser);
+	if (!current_is(parser, TT(LBrace))) {
+		error(
+			parser, heapstring_from_cstring("expected '{'"), parser->current.pos
+		);
+		return alloc_expr((ScirptAstExpr) {
+			.type = AET(Error),
+		});
+	}
+	ScirptAstExpr* body = scirpt_parser_parse_expr(parser);
+	return alloc_expr((ScirptAstExpr) {
+		.type = AET(Loop),
+		.pos = pos,
+		.loop = (ScirptAstExprLoop) {
+			.body = body,
+		},
+	});
+}
+
+ScirptAstExpr* scirpt_parser_parse_while(ScirptParser* parser)
+{
+	ScirptPosition pos = parser->current.pos;
+	step(parser);
+	ScirptAstExpr* condition = scirpt_parser_parse_expr(parser);
+	if (!current_is(parser, TT(LBrace))) {
+		error(
+			parser, heapstring_from_cstring("expected '{'"), parser->current.pos
+		);
+		scirpt_ast_expr_delete(condition);
+		return alloc_expr((ScirptAstExpr) {
+			.type = AET(Error),
+		});
+	}
+	ScirptAstExpr* body = scirpt_parser_parse_expr(parser);
+	return alloc_expr((ScirptAstExpr) {
+		.type = AET(While),
+		.pos = pos,
+		.while_expr = (ScirptAstExprWhile) {
+			.condition = condition,
+			.body = body,
+		},
+	});
+}
+
+ScirptAstExpr* scirpt_parser_parse_for(ScirptParser* parser)
+{
+	ScirptPosition pos = parser->current.pos;
+	step(parser);
+	if (!current_is(parser, TT(Id))) {
+		error(
+			parser, heapstring_from_cstring("expected Id"), parser->current.pos
+		);
+		return alloc_expr((ScirptAstExpr) {
+			.type = AET(Error),
+		});
+	}
+	HeapString subject = heapstring_from(
+		&parser->text[parser->current.pos.index], parser->current.length
+	);
+	step(parser);
+	if (!current_is(parser, TT(In))) {
+		error(
+			parser,
+			heapstring_from_cstring("expected 'in'"),
+			parser->current.pos
+		);
+		heapstring_destroy(&subject);
+		return alloc_expr((ScirptAstExpr) {
+			.type = AET(Error),
+		});
+	}
+	step(parser);
+	ScirptAstExpr* value = scirpt_parser_parse_expr(parser);
+	if (!current_is(parser, TT(LBrace))) {
+		error(
+			parser, heapstring_from_cstring("expected '{'"), parser->current.pos
+		);
+		heapstring_destroy(&subject);
+		scirpt_ast_expr_delete(value);
+		return alloc_expr((ScirptAstExpr) {
+			.type = AET(Error),
+		});
+	}
+	ScirptAstExpr* body = scirpt_parser_parse_expr(parser);
+	return alloc_expr((ScirptAstExpr) {
+		.type = AET(While),
+		.pos = pos,
+		.while_expr = (ScirptAstExprWhile) {
+			.condition = value,
+			.body = body,
+		},
+	});
+}
+
+ScirptAstExpr* scirpt_parser_parse_lambda(ScirptParser* parser)
+{
+	ScirptPosition pos = parser->current.pos;
+	step(parser);
+	if (!current_is(parser, TT(LParen))) {
+		error(
+			parser, heapstring_from_cstring("expected '('"), parser->current.pos
+		);
+		return alloc_expr((ScirptAstExpr) {
+			.type = AET(Error),
+		});
+	}
+	step(parser);
+	HeapStringArray params;
+	heapstring_array_construct(&params);
+	if (!done(parser) && parser->current.type == TT(RParen)) {
+		step(parser);
+		if (!current_is(parser, TT(Id))) {
+			error(
+				parser,
+				heapstring_from_cstring("expected Id"),
+				parser->current.pos
+			);
+			heapstring_array_destroy(&params);
+			return alloc_expr((ScirptAstExpr) {
+				.type = AET(Error),
+			});
+		}
+		HeapString param = heapstring_from(
+			&parser->text[parser->current.pos.index], parser->current.length
+		);
+		heapstring_array_append(&params, param);
+		step(parser);
+		while (current_is(parser, TT(Comma))) {
+			step(parser);
+			if (done(parser) || parser->current.type == TT(RParen))
+				break;
+			if (!current_is(parser, TT(Id))) {
+				error(
+					parser,
+					heapstring_from_cstring("expected Id"),
+					parser->current.pos
+				);
+				for (size_t i = 0; i < params.length; ++i)
+					heapstring_destroy(&params.data[i]);
+				heapstring_array_destroy(&params);
+				return alloc_expr((ScirptAstExpr) {
+					.type = AET(Error),
+				});
+			}
+			HeapString param = heapstring_from(
+				&parser->text[parser->current.pos.index], parser->current.length
+			);
+			heapstring_array_append(&params, param);
+			step(parser);
+		}
+	}
+	if (!current_is(parser, TT(RParen))) {
+		error(
+			parser, heapstring_from_cstring("expected ')'"), parser->current.pos
+		);
+		for (size_t i = 0; i < params.length; ++i)
+			heapstring_destroy(&params.data[i]);
+		heapstring_array_destroy(&params);
+		return alloc_expr((ScirptAstExpr) {
+			.type = AET(Error),
+			.pos = pos,
+		});
+	}
+	step(parser);
+	if (!current_is(parser, TT(LBrace))) {
+		error(
+			parser, heapstring_from_cstring("expected '{'"), parser->current.pos
+		);
+		for (size_t i = 0; i < params.length; ++i)
+			heapstring_destroy(&params.data[i]);
+		heapstring_array_destroy(&params);
+		return alloc_expr((ScirptAstExpr) {
+			.type = AET(Error),
+		});
+	}
+	ScirptAstExpr* body = scirpt_parser_parse_expr(parser);
+	return alloc_expr((ScirptAstExpr) {
+		.type = AET(Lambda),
+		.pos = pos,
+		.lambda = (ScirptAstExprLambda) {
+			.params = params,
+			.body = body,
+		},
+	});
+}
+
+ScirptAstExpr* scirpt_parser_parse_function(ScirptParser* parser)
+{
+	ScirptPosition pos = parser->current.pos;
+	step(parser);
+	if (!current_is(parser, TT(Id))) {
+		error(
+			parser, heapstring_from_cstring("expected Id"), parser->current.pos
+		);
+		return alloc_expr((ScirptAstExpr) {
+			.type = AET(Error),
+		});
+	}
+	HeapString subject = heapstring_from(
+		&parser->text[parser->current.pos.index], parser->current.length
+	);
+	step(parser);
+	if (!current_is(parser, TT(LParen))) {
+		error(
+			parser, heapstring_from_cstring("expected '('"), parser->current.pos
+		);
+		return alloc_expr((ScirptAstExpr) {
+			.type = AET(Error),
+		});
+	}
+	step(parser);
+	HeapStringArray params;
+	heapstring_array_construct(&params);
+	if (!done(parser) && parser->current.type == TT(RParen)) {
+		step(parser);
+		if (!current_is(parser, TT(Id))) {
+			error(
+				parser,
+				heapstring_from_cstring("expected Id"),
+				parser->current.pos
+			);
+			heapstring_destroy(&subject);
+			heapstring_array_destroy(&params);
+			return alloc_expr((ScirptAstExpr) {
+				.type = AET(Error),
+			});
+		}
+		HeapString param = heapstring_from(
+			&parser->text[parser->current.pos.index], parser->current.length
+		);
+		heapstring_array_append(&params, param);
+		step(parser);
+		while (current_is(parser, TT(Comma))) {
+			step(parser);
+			if (done(parser) || parser->current.type == TT(RParen))
+				break;
+			if (!current_is(parser, TT(Id))) {
+				error(
+					parser,
+					heapstring_from_cstring("expected Id"),
+					parser->current.pos
+				);
+				heapstring_destroy(&subject);
+				for (size_t i = 0; i < params.length; ++i)
+					heapstring_destroy(&params.data[i]);
+				heapstring_array_destroy(&params);
+				return alloc_expr((ScirptAstExpr) {
+					.type = AET(Error),
+				});
+			}
+			HeapString param = heapstring_from(
+				&parser->text[parser->current.pos.index], parser->current.length
+			);
+			heapstring_array_append(&params, param);
+			step(parser);
+		}
+	}
+	if (!current_is(parser, TT(RParen))) {
+		error(
+			parser, heapstring_from_cstring("expected ')'"), parser->current.pos
+		);
+		heapstring_destroy(&subject);
+		for (size_t i = 0; i < params.length; ++i)
+			heapstring_destroy(&params.data[i]);
+		heapstring_array_destroy(&params);
+		return alloc_expr((ScirptAstExpr) {
+			.type = AET(Error),
+			.pos = pos,
+		});
+	}
+	step(parser);
+	if (!current_is(parser, TT(LBrace))) {
+		error(
+			parser, heapstring_from_cstring("expected '{'"), parser->current.pos
+		);
+		heapstring_destroy(&subject);
+		for (size_t i = 0; i < params.length; ++i)
+			heapstring_destroy(&params.data[i]);
+		heapstring_array_destroy(&params);
+		return alloc_expr((ScirptAstExpr) {
+			.type = AET(Error),
+		});
+	}
+	ScirptAstExpr* body = scirpt_parser_parse_expr(parser);
+	return alloc_expr((ScirptAstExpr) {
+		.type = AET(Function),
+		.pos = pos,
+		.function = (ScirptAstExprFunction) {
+			.subject = subject,
+			.params = params,
+			.body = body,
+		},
+	});
+}
+
+ScirptAstExpr* scirpt_parser_parse_let(ScirptParser* parser)
+{
+	ScirptPosition pos = parser->current.pos;
+	step(parser);
+	if (!current_is(parser, TT(Id))) {
+		error(
+			parser, heapstring_from_cstring("expected Id"), parser->current.pos
+		);
+		return alloc_expr((ScirptAstExpr) {
+			.type = AET(Error),
+		});
+	}
+	HeapString subject = heapstring_from(
+		&parser->text[parser->current.pos.index], parser->current.length
+	);
+	step(parser);
+	if (!current_is(parser, TT(Equal))) {
+		error(
+			parser, heapstring_from_cstring("expected '='"), parser->current.pos
+		);
+		heapstring_destroy(&subject);
+		return alloc_expr((ScirptAstExpr) {
+			.type = AET(Error),
+		});
+	}
+	step(parser);
+	ScirptAstExpr* value = scirpt_parser_parse_expr(parser);
+	return alloc_expr((ScirptAstExpr) {
+		.type = AET(Let),
+		.pos = pos,
+		.let = (ScirptAstExprLet) {
+			.subject = subject,
+			.value = value,
+		},
+	});
+}
+
+ScirptAstExpr* scirpt_parser_parse_return(ScirptParser* parser)
+{
+	ScirptPosition pos = parser->current.pos;
+	step(parser);
+	ScirptAstExpr* value = NULL;
+	if (!done(parser) && parser->current.type != TT(Semicolon)) {
+		value = scirpt_parser_parse_expr(parser);
+	}
+	return alloc_expr((ScirptAstExpr) {
+		.type = AET(Return),
+		.pos = pos,
+		.return_statement = (ScirptAstExprReturn) {
+			.value = value,
+		},
+	});
+}
+
+ScirptAstExpr* scirpt_parser_parse_break(ScirptParser* parser)
+{
+	ScirptPosition pos = parser->current.pos;
+	step(parser);
+	ScirptAstExpr* value = NULL;
+	if (!done(parser) && parser->current.type != TT(Semicolon)) {
+		value = scirpt_parser_parse_expr(parser);
+	}
+	return alloc_expr((ScirptAstExpr) {
+		.type = AET(Break),
+		.pos = pos,
+		.break_statement = (ScirptAstExprBreak) {
+			.value = value,
+		},
+	});
+}
+
 void scirpt_parser_error(
 	ScirptParser* parser, HeapString message, ScirptPosition pos
 )
diff --git a/scirpt/parser.h b/scirpt/parser.h
index a4eb1f1..14a8ba8 100644
--- a/scirpt/parser.h
+++ b/scirpt/parser.h
@@ -26,13 +26,31 @@ void scirpt_parser_construct(
 );
 void scirpt_parser_destroy(ScirptParser* parser);
 ScirptAstExpr* scirpt_parser_parse_statement(ScirptParser* parser);
+ScirptAstExpr* scirpt_parser_parse_assign(ScirptParser* parser);
 ScirptAstExpr* scirpt_parser_parse_expr(ScirptParser* parser);
+ScirptAstExpr* scirpt_parser_parse_or(ScirptParser* parser);
+ScirptAstExpr* scirpt_parser_parse_and(ScirptParser* parser);
+ScirptAstExpr* scirpt_parser_parse_equality(ScirptParser* parser);
+ScirptAstExpr* scirpt_parser_parse_comparison(ScirptParser* parser);
+ScirptAstExpr* scirpt_parser_parse_add_subtract(ScirptParser* parser);
+ScirptAstExpr* scirpt_parser_parse_multiply_divide_modulo(ScirptParser* parser);
+ScirptAstExpr* scirpt_parser_parse_negate(ScirptParser* parser);
+ScirptAstExpr* scirpt_parser_parse_exponent(ScirptParser* parser);
+ScirptAstExpr* scirpt_parser_parse_unary(ScirptParser* parser);
 ScirptAstExpr* scirpt_parser_parse_member_call_index_expr(ScirptParser* parser);
 ScirptAstExpr* scirpt_parser_parse_operand(ScirptParser* parser);
 ScirptAstExpr* scirpt_parser_parse_int_or_float(ScirptParser* parser);
 ScirptAstExpr* scirpt_parser_parse_string(ScirptParser* parser);
 ScirptAstExpr* scirpt_parser_parse_block(ScirptParser* parser);
 ScirptAstExpr* scirpt_parser_parse_if(ScirptParser* parser);
+ScirptAstExpr* scirpt_parser_parse_loop(ScirptParser* parser);
+ScirptAstExpr* scirpt_parser_parse_while(ScirptParser* parser);
+ScirptAstExpr* scirpt_parser_parse_for(ScirptParser* parser);
+ScirptAstExpr* scirpt_parser_parse_lambda(ScirptParser* parser);
+ScirptAstExpr* scirpt_parser_parse_function(ScirptParser* parser);
+ScirptAstExpr* scirpt_parser_parse_let(ScirptParser* parser);
+ScirptAstExpr* scirpt_parser_parse_return(ScirptParser* parser);
+ScirptAstExpr* scirpt_parser_parse_break(ScirptParser* parser);
 void scirpt_parser_error(
 	ScirptParser* parser, HeapString message, ScirptPosition pos
 );