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")) {