diff --git a/include/ast.h b/include/ast.h
index cd2a187..5d5f272 100644
--- a/include/ast.h
+++ b/include/ast.h
@@ -104,6 +104,7 @@ expr_t *create_literal_expr(token_t *token);
 expr_t *create_grouping_expr(expr_t *expression);
 expr_t *create_variable_expr(token_t *name);
 expr_t *create_assign_expr(expr_t *name, expr_t *value);
+expr_t *create_logical_expr(token_t *operator, expr_t *left, expr_t *right);
 void print_ast(expr_t *expr);
 
 #endif
diff --git a/include/interpreter.h b/include/interpreter.h
index decb592..a2672f8 100644
--- a/include/interpreter.h
+++ b/include/interpreter.h
@@ -7,7 +7,7 @@
 
 void runtime_error(const char *message, int line);
 value_t evaluate(expr_t *expr, ht_t *env);
-void print_value(value_t *value);
+void print_value(value_t value);
 void evaluate_statements(stmt_array_t *array, ht_t *env);
 
 #endif
diff --git a/include/stmt.h b/include/stmt.h
index efd1e23..af2bdba 100644
--- a/include/stmt.h
+++ b/include/stmt.h
@@ -7,7 +7,9 @@
 #define DEFAULT_STMTS_SIZE 512
 
 /*
- statement      → exprStmt | printStmt | block ;
+statement      → exprStmt | ifStmt | printStmt | block ;
+ifStmt         → "if" "(" expression ")" statement
+               ( "else" statement )? ;
  block          → "{" declaration* "}" ;
 */
 
@@ -25,7 +27,7 @@ typedef enum {
 typedef struct stmt_t stmt_t;
 
 typedef struct {
-	stmt_t *statements;
+	stmt_t **statements;
 	int length;
 	int capacity;
 } stmt_array_t;
@@ -51,8 +53,8 @@ struct stmt_t {
 		} function;
 		struct {
 			expr_t *condition;
-			struct stmt_t *thenBranch;
-			struct stmt_t *elseBranch;
+			struct stmt_t *then_branch;
+			struct stmt_t *else_branch;
 		} _if;
 		struct {
 			expr_t *expression;
diff --git a/src/ast.c b/src/ast.c
index f953528..4a5cdb5 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -13,8 +13,7 @@ expr_t *create_binary_expr(token_t *operator, expr_t *left, expr_t *right)
 	expr->as.binary.left = left;
 	expr->as.binary.right = right;
 	expr->as.binary.operator.type = operator->type;
-	char *bin_op_val = strdup(operator->value);
-	expr->as.binary.operator.value = bin_op_val;
+	expr->as.binary.operator.value = strdup(operator->value);
 	expr->as.binary.operator.line = operator->line;
 	return expr;
 }
@@ -25,8 +24,7 @@ expr_t *create_unary_expr(token_t *operator, expr_t *right)
 	expr->type = EXPR_UNARY;
 	expr->line = operator->line;
 	expr->as.unary.operator.type = operator->type;
-	char *u_op_val = strdup(operator->value);
-	expr->as.unary.operator.value = u_op_val;
+	expr->as.unary.operator.value = strdup(operator->value);
 	expr->as.unary.operator.line = operator->line;
 	expr->as.unary.right = right;
 	return expr;
@@ -58,8 +56,7 @@ expr_t *create_literal_expr(token_t *token)
 
 		case TOKEN_STRING:
 			expr->as.literal.value.type = VAL_STRING;
-			char *tkvalue = strdup(token->value);
-			expr->as.literal.value.as.string = tkvalue;
+			expr->as.literal.value.as.string = strdup(token->value);
 			break;
 
 		default:
@@ -86,8 +83,7 @@ expr_t *create_variable_expr(token_t *name)
 	expr->type = EXPR_VARIABLE;
 	expr->line = name->line;
 	expr->as.variable.name.type = name->type;
-	char *name_val = strdup(name->value);
-	expr->as.variable.name.value = name_val;
+	expr->as.variable.name.value = strdup(name->value);
 	expr->as.variable.name.line = name->line;
 
 	return expr;
@@ -104,6 +100,19 @@ expr_t *create_assign_expr(expr_t *name, expr_t *value)
 	return expr;
 }
 
+expr_t *create_logical_expr(token_t *operator, expr_t *left, expr_t *right)
+{
+	expr_t *expr = malloc(sizeof(expr_t));
+	expr->type = EXPR_LOGICAL;
+	expr->line = operator->line;
+	expr->as.logical.left = left;
+	expr->as.logical.right = right;
+	expr->as.logical.operator.type = operator->type;
+	expr->as.logical.operator.value = strdup(operator->value);
+	expr->as.logical.operator.line = operator->line;
+	return expr;
+}
+
 void print_ast(expr_t *expr)
 {
 	if (!expr)
diff --git a/src/env.c b/src/env.c
index 6fdb5a6..03bbe96 100644
--- a/src/env.c
+++ b/src/env.c
@@ -84,12 +84,13 @@ void ht_replace(ht_t *ht, char *name, value_t value)
 			break;
 		}
 		if (!strcmp(ht[probe_idx].name, name)) {
-			ht[probe_idx].value.type = value.type;
-			if (value.type == VAL_STRING) {
+			if (ht[probe_idx].value.type == VAL_STRING) {
 				free(ht[probe_idx].value.as.string);
+			}
+			ht[probe_idx].value.type = value.type;
+			ht[probe_idx].value.as = value.as;
+			if (value.type == VAL_STRING) {
 				ht[probe_idx].value.as.string = strdup(value.as.string);
-			} else {
-				ht[probe_idx].value.as = value.as;
 			}
 			return;
 		}
diff --git a/src/interpreter.c b/src/interpreter.c
index a13be7c..de8780b 100644
--- a/src/interpreter.c
+++ b/src/interpreter.c
@@ -140,17 +140,17 @@ value_t visit_binary(expr_t *expr, ht_t *env)
 
 }
 
-int is_truthy(value_t *value)
+int is_truthy(value_t value)
 {
-	switch (value->type) {
+	switch (value.type) {
 		case VAL_NIL:
 			return 0;
 
 		case VAL_BOOL:
-			return value->as.boolean;
+			return value.as.boolean;
 
 		case VAL_NUMBER:
-			if (value->as.number == 0)
+			if (value.as.number == 0)
 				return 0;
 			return 1;
 
@@ -174,7 +174,7 @@ value_t visit_unary(expr_t *expr, ht_t *env)
 			runtime_error("Operand must be a number.", expr->line);
 		}
 	} else if (expr->as.unary.operator.type == TOKEN_BANG) {
-		value_t result = {.type = VAL_BOOL, .as.boolean = !is_truthy(&operand)};
+		value_t result = {.type = VAL_BOOL, .as.boolean = !is_truthy(operand)};
 		return result;
 	}
 
@@ -198,6 +198,21 @@ value_t visit_assign(expr_t *expr, ht_t *env)
     return value;
 }
 
+value_t visit_logical(expr_t *expr, ht_t *env)
+{
+	value_t left = evaluate(expr->as.logical.left, env);
+
+    if (expr->as.logical.operator.type == TOKEN_OR) {
+      if (is_truthy(left))
+		  return left;
+    } else {
+      if (!is_truthy(left))
+		  return left;
+    }
+
+    return evaluate(expr->as.logical.right, env);
+}
+
 value_t evaluate(expr_t *expr, ht_t *env)
 {
 	if (!expr) {
@@ -217,17 +232,19 @@ value_t evaluate(expr_t *expr, ht_t *env)
 			return visit_variable(expr, env);
 		case EXPR_ASSIGN:
 			return visit_assign(expr, env);
+		case EXPR_LOGICAL:
+			return visit_logical(expr, env);
 		default:
 			exit(65);
 			break;
 	}
 }
 
-void print_value(value_t *value)
+void print_value(value_t value)
 {
-	switch (value->type) {
+	switch (value.type) {
 		case VAL_BOOL:
-			printf("%s\n", value->as.boolean == 1 ? "true" : "false");
+			printf("%s\n", value.as.boolean == 1 ? "true" : "false");
 			break;
 
 		case VAL_NIL:
@@ -235,14 +252,14 @@ void print_value(value_t *value)
 			break;
 
 		case VAL_STRING:
-			printf("%s\n", value->as.string);
+			printf("%s\n", value.as.string);
 			break;
 
 		case VAL_NUMBER:
-			if (value->as.number == (int) value->as.number) {
-				printf("%d\n", (int) value->as.number);
+			if (value.as.number == (int) value.as.number) {
+				printf("%d\n", (int) value.as.number);
 			} else {
-				printf("%g\n", value->as.number);
+				printf("%g\n", value.as.number);
 			}
 			break;
 
@@ -260,28 +277,42 @@ void evaluate_block(stmt_array_t *array, ht_t *cur_env, ht_t *scope_env)
 	cur_env = previous;
 }
 
-void evaluate_statement(stmt_t stmt, ht_t *env)
+void evaluate_statement(stmt_t *stmt, ht_t *env)
 {
-	switch (stmt.type) {
-		case STMT_PRINT:;
-			value_t obj = evaluate(stmt.as.print.expression, env);
-			print_value(&obj);
+	switch (stmt->type) {
+		case STMT_IF:
+			if (is_truthy(evaluate(stmt->as._if.condition, env))) {
+				evaluate_statement(stmt->as._if.then_branch, env);
+			} else if (stmt->as._if.else_branch) {
+				evaluate_statement(stmt->as._if.else_branch, env);
+			}
+			break;
+
+		case STMT_PRINT:
+			print_value(evaluate(stmt->as.print.expression, env));
 			break;
 
 		case STMT_EXPR:
-			evaluate(stmt.as.expr.expression, env);
+			evaluate(stmt->as.expr.expression, env);
 			break;
 
-		case STMT_VAR:;
+		case STMT_VAR: {
 			value_t value = {.type = VAL_NIL};
-			if (stmt.as.variable.initializer) {
-				value = evaluate(stmt.as.variable.initializer, env);
+			if (stmt->as.variable.initializer) {
+				value = evaluate(stmt->as.variable.initializer, env);
 			}
-			ht_add(env, stmt.as.variable.name.value, value);
+			ht_add(env, stmt->as.variable.name.value, value);
 			break;
+		}
 
 		case STMT_BLOCK:
-			evaluate_block(stmt.as.block.statements, env, ht_init(env));
+			evaluate_block(stmt->as.block.statements, env, ht_init(env));
+			break;
+
+		case STMT_WHILE:
+			while (is_truthy(evaluate(stmt->as._while.condition, env))) {
+				evaluate_statement(stmt->as._while.body, env);
+			}
 			break;
 
 		default:
diff --git a/src/parser.c b/src/parser.c
index 3a34605..704c93a 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -10,7 +10,10 @@
 int current = 0;
 token_t *tokens;
 expr_t *expression(void);
-stmt_t declaration(void);
+stmt_t *expression_stmt(void);
+stmt_t *statement(void);
+stmt_t *var_declaration(void);
+stmt_t *declaration(void);
 void synchronize(void);
 
 /*
@@ -68,6 +71,13 @@ void free_expr(expr_t *expr)
 			free(expr);
 			break;
 
+		case EXPR_LOGICAL:
+			free(expr->as.logical.operator.value);
+			free_expr(expr->as.logical.left);
+			free_expr(expr->as.logical.right);
+			free(expr);
+			break;
+
 		default:
 			break;
 	}
@@ -205,10 +215,38 @@ expr_t *equality(void)
 	return expr;
 }
 
-expr_t *assignment(void)
+expr_t *and(void)
 {
 	expr_t *expr = equality();
 
+    while (check(TOKEN_AND)) {
+		token_t *operator = peek();
+		advance();
+		expr_t *right = equality();
+		expr = create_logical_expr(operator, expr, right);
+    }
+
+    return expr;
+}
+
+expr_t *or(void)
+{
+	expr_t *expr = and();
+
+    while (check(TOKEN_OR)) {
+		token_t *operator = peek();
+		advance();
+		expr_t *right = and();
+		expr = create_logical_expr(operator, expr, right);
+    }
+
+    return expr;
+}
+
+expr_t *assignment(void)
+{
+	expr_t *expr = or();
+
 	if (check(TOKEN_EQUAL)) {
 		token_t *equals = peek();
 		advance();
@@ -228,47 +266,170 @@ expr_t *expression(void)
 	return assignment();
 }
 
-void stmt_add(stmt_array_t *array, stmt_t stmt)
+void stmt_add(stmt_array_t *array, stmt_t *stmt)
 {
 	if (array->length == array->capacity) {
 		array->capacity *= 2;
-		array->statements = realloc(array->statements, array->capacity * sizeof(stmt_t));
+		array->statements = realloc(array->statements, array->capacity * sizeof(stmt_t *));
 	}
 	array->statements[array->length++] = stmt;
 }
 
+void free_statement(stmt_t *stmt)
+{
+	if (!stmt) {
+		return;
+	}
+	if (stmt->type == STMT_PRINT) {
+		free_expr(stmt->as.print.expression);
+		free(stmt);
+	} else if (stmt->type == STMT_EXPR) {
+		free_expr(stmt->as.expr.expression);
+		free(stmt);
+	} else if (stmt->type == STMT_VAR) {
+		free(stmt->as.variable.name.value);
+		free_expr(stmt->as.variable.initializer);
+		free(stmt);
+	} else if (stmt->type == STMT_BLOCK) {
+		free_statements(stmt->as.block.statements);
+		free(stmt);
+	} else if (stmt->type == STMT_IF) {
+		free_expr(stmt->as._if.condition);
+		free_statement(stmt->as._if.then_branch);
+		free_statement(stmt->as._if.else_branch);
+		free(stmt);
+	} else if (stmt->type == STMT_WHILE) {
+		free_expr(stmt->as._while.condition);
+		free_statement(stmt->as._while.body);
+		free(stmt);
+	}
+}
+
 void free_statements(stmt_array_t *array)
 {
 	for (int i = 0; i < array->length; i++) {
-		if (array->statements[i].type == STMT_PRINT) {
-			free_expr(array->statements[i].as.print.expression);
-		}
-		if (array->statements[i].type == STMT_EXPR) {
-			free_expr(array->statements[i].as.expr.expression);
-		}
-		if (array->statements[i].type == STMT_VAR) {
-			free(array->statements[i].as.variable.name.value);
-			free_expr(array->statements[i].as.variable.initializer);
-		}
-		if (array->statements[i].type == STMT_BLOCK) {
-			free_statements(array->statements[i].as.block.statements);
-		}
+		free_statement(array->statements[i]);
 	}
 	free(array->statements);
 	free(array);
 }
 
-stmt_t print_stmt(void)
+stmt_t *for_stmt(void)
+{
+	consume(TOKEN_LEFT_PAREN, "Expect '(' after 'for'.");
+	stmt_t *initializer = NULL;
+    if (check(TOKEN_SEMICOLON)) {
+		advance();
+    } else if (check(TOKEN_VAR)) {
+		advance();
+		initializer = var_declaration();
+    } else {
+		initializer = expression_stmt();
+	}
+
+	expr_t *condition = NULL;
+    if (!check(TOKEN_SEMICOLON)) {
+		condition = expression();
+    }
+    consume(TOKEN_SEMICOLON, "Expect ';' after loop condition.");
+
+	expr_t *increment = NULL;
+    if (!check(TOKEN_RIGHT_PAREN)) {
+		increment = expression();
+    }
+    consume(TOKEN_RIGHT_PAREN, "Expect ')' after for clauses.");
+
+	stmt_t *body = statement();
+	if (increment) {
+		stmt_t *body_incremented = malloc(sizeof(stmt_t));
+		body_incremented->type = STMT_BLOCK;
+		stmt_array_t *statements = malloc(sizeof(stmt_array_t));
+		statements->statements = malloc(DEFAULT_STMTS_SIZE * sizeof(stmt_t));
+		statements->length = 0;
+		statements->capacity = DEFAULT_STMTS_SIZE;
+		stmt_add(statements, body);
+		body_incremented->as.block.statements = statements;
+
+		stmt_t *stmt = malloc(sizeof(stmt_t));
+		stmt->type = STMT_EXPR;
+		stmt->as.expr.expression = increment;
+		stmt_add(statements, stmt);
+		body = body_incremented;
+    }
+	if (!condition) {
+		token_t tok;
+		tok.type = TOKEN_TRUE;
+		tok.value = strdup("true");
+		tok.line = -1;
+		condition = create_literal_expr(&tok);
+	}
+	stmt_t *stmt = malloc(sizeof(stmt_t));
+	stmt->type = STMT_WHILE;
+	stmt->as._while.condition = condition;
+	stmt->as._while.body = body;
+
+	body = stmt;
+
+	if (initializer) {
+		stmt_t *body_initialized = malloc(sizeof(stmt_t));
+		body_initialized->type = STMT_BLOCK;
+		stmt_array_t *statements = malloc(sizeof(stmt_array_t));
+		statements->statements = malloc(DEFAULT_STMTS_SIZE * sizeof(stmt_t));
+		statements->length = 0;
+		statements->capacity = DEFAULT_STMTS_SIZE;
+		stmt_add(statements, initializer);
+		stmt_add(statements, body);
+		body_initialized->as.block.statements = statements;
+		body = body_initialized;
+    }
+
+    return body;
+}
+
+stmt_t *if_stmt(void)
+{
+	consume(TOKEN_LEFT_PAREN, "Expect '(' after 'if'.");
+	expr_t *cond = expression();
+	consume(TOKEN_RIGHT_PAREN, "Expect ')' after if condition.");
+	stmt_t *then_branch = statement();
+    stmt_t *else_branch = NULL;
+    if (check(TOKEN_ELSE)) {
+		advance();
+		else_branch = statement();
+    }
+	stmt_t *stmt = malloc(sizeof(stmt_t));
+	stmt->type = STMT_IF;
+	stmt->as._if.condition = cond;
+	stmt->as._if.then_branch = then_branch;
+	stmt->as._if.else_branch = else_branch;
+	return stmt;
+}
+
+stmt_t *print_stmt(void)
 {
 	expr_t *value = expression();
 	consume(TOKEN_SEMICOLON, "Expect ; after value.");
-	return (stmt_t) {
-		.type = STMT_PRINT,
-		.as.print.expression = value,
-	};
+	stmt_t *stmt = malloc(sizeof(stmt_t));
+	stmt->type = STMT_PRINT;
+	stmt->as.print.expression = value;
+	return stmt;
 }
 
-stmt_t block_stmt(void)
+stmt_t *while_stmt(void)
+{
+	consume(TOKEN_LEFT_PAREN, "Expect '(' after 'while'.");
+    expr_t *condition = expression();
+    consume(TOKEN_RIGHT_PAREN, "Expect ')' after condition.");
+    stmt_t *body = statement();
+
+	stmt_t *stmt = malloc(sizeof(stmt_t));
+	stmt->type = STMT_WHILE;
+	stmt->as._while.condition = condition;
+	stmt->as._while.body = body;
+	return stmt;
+}
+
+stmt_t *block_stmt(void)
 {
 	stmt_array_t *statements = malloc(sizeof(stmt_array_t));
 	statements->statements = malloc(DEFAULT_STMTS_SIZE * sizeof(stmt_t));
@@ -281,28 +442,41 @@ stmt_t block_stmt(void)
     }
 
     consume(TOKEN_RIGHT_BRACE, "Expect '}' after block.");
-    return (stmt_t) {
-		.type = STMT_BLOCK,
-		.as.block.statements = statements,
-	};
+
+	stmt_t *stmt = malloc(sizeof(stmt_t));
+	stmt->type = STMT_BLOCK;
+	stmt->as.block.statements = statements;
+	return stmt;
 }
 
-stmt_t expression_stmt(void)
+stmt_t *expression_stmt(void)
 {
 	expr_t *expr = expression();
 	consume(TOKEN_SEMICOLON, "Expect ; after expression.");
-	return (stmt_t) {
-		.type = STMT_EXPR,
-		.as.expr.expression = expr,
-	};
+	stmt_t *stmt = malloc(sizeof(stmt_t));
+	stmt->type = STMT_EXPR;
+	stmt->as.expr.expression = expr;
+	return stmt;
 }
 
-stmt_t statement(void)
+stmt_t *statement(void)
 {
+	if (check(TOKEN_FOR)) {
+		advance();
+		return for_stmt();
+	}
+	if (check(TOKEN_IF)) {
+		advance();
+		return if_stmt();
+	}
 	if (check(TOKEN_PRINT)) {
 		advance();
 		return print_stmt();
 	}
+	if (check(TOKEN_WHILE)) {
+		advance();
+		return while_stmt();
+	}
 	if (check(TOKEN_LEFT_BRACE)) {
 		advance();
 		return block_stmt();
@@ -310,7 +484,7 @@ stmt_t statement(void)
 	return expression_stmt();
 }
 
-stmt_t var_declaration(void)
+stmt_t *var_declaration(void)
 {
 	token_t *name = consume(TOKEN_IDENTIFIER, "Expect variable name.");
 
@@ -321,16 +495,17 @@ stmt_t var_declaration(void)
 	}
 
 	consume(TOKEN_SEMICOLON, "Expect ';' after variable declaration.");
-	return (stmt_t) {
-		.type = STMT_VAR,
-		.as.variable.name.type = name->type,
-		.as.variable.name.value = strdup(name->value),
-		.as.variable.name.line = name->line,
-		.as.variable.initializer = initializer,
-	};
+
+	stmt_t *stmt = malloc(sizeof(stmt_t));
+	stmt->type = STMT_VAR;
+	stmt->as.variable.name.type = name->type;
+	stmt->as.variable.name.value = strdup(name->value);
+	stmt->as.variable.name.line = name->line;
+	stmt->as.variable.initializer = initializer;
+	return stmt;
 }
 
-stmt_t declaration(void)
+stmt_t *declaration(void)
 {
 	if (check(TOKEN_VAR)) {
 		advance();
diff --git a/src/rd.c b/src/rd.c
index bf1b214..db608fd 100644
--- a/src/rd.c
+++ b/src/rd.c
@@ -35,7 +35,7 @@ int main(int argc, char **argv)
 	} else if (!strcmp(command, "evaluate")) {
 		expr_t *expr = parse_expr(array->tokens);
 		value_t val = evaluate(expr, NULL);
-		print_value(&val);
+		print_value(val);
 		free_array(array);
 		free_expr(expr);
 	} else if (!strcmp(command, "run")) {