Variable assign, printing

This commit is contained in:
Night Kaly 2025-01-16 00:04:09 +00:00
parent 1d297cf0f0
commit 4a0f325634
Signed by: night0721
SSH key fingerprint: SHA256:B/hgVwUoBpx5vdNsXl9w8XwZljA9766uk6T4ubZp5HM
8 changed files with 232 additions and 22 deletions

View file

@ -103,6 +103,7 @@ expr_t *create_unary_expr(token_t *operator, expr_t *right);
expr_t *create_literal_expr(token_t *token); expr_t *create_literal_expr(token_t *token);
expr_t *create_grouping_expr(expr_t *expression); expr_t *create_grouping_expr(expr_t *expression);
expr_t *create_variable_expr(token_t *name); expr_t *create_variable_expr(token_t *name);
expr_t *create_assign_expr(token_t *name, expr_t *value);
void print_ast(expr_t *expr); void print_ast(expr_t *expr);
#endif #endif

21
include/env.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef ENV_H
#define ENV_H
#include "ast.h"
#include "lexer.h"
typedef struct {
char *name;
value_t value;
} ht_t;
#define DEFAULT_HT_SIZE 50
ht_t *ht_init(void);
void ht_add(ht_t *ht, char *name, value_t value);
value_t *ht_get(ht_t *ht, token_t *name);
void ht_replace(ht_t *ht, char *name, value_t value);
void ht_assign(ht_t *ht, token_t *name, value_t value);
void ht_free(ht_t *ht);
#endif

View file

@ -4,6 +4,7 @@
#include "ast.h" #include "ast.h"
#include "stmt.h" #include "stmt.h"
void runtime_error(const char *message, int line);
value_t evaluate(expr_t *expr); value_t evaluate(expr_t *expr);
void print_value(value_t *value); void print_value(value_t *value);
void print_statements(stmt_array_t *array); void print_statements(stmt_array_t *array);

View file

@ -6,6 +6,12 @@
#define DEFAULT_STMTS_SIZE 512 #define DEFAULT_STMTS_SIZE 512
/*
program statement* EOF ;
statement exprStmt | printStmt ;
exprStmt expression ";" ;
printStmt "print" expression ";" ;
*/
typedef enum { typedef enum {
STMT_BLOCK, STMT_BLOCK,
STMT_CLASS, STMT_CLASS,

View file

@ -93,6 +93,19 @@ expr_t *create_variable_expr(token_t *name)
return expr; return expr;
} }
expr_t *create_assign_expr(token_t *name, expr_t *value)
{
expr_t *expr = malloc(sizeof(expr_t));
expr->type = EXPR_ASSIGN;
expr->line = name->line;
expr->as.assign.name.type = name->type;
expr->as.assign.name.value = name->value;
expr->as.assign.name.line = name->line;
expr->as.assign.value = value;
return expr;
}
void print_ast(expr_t *expr) void print_ast(expr_t *expr)
{ {
if (!expr) if (!expr)

93
src/env.c Normal file
View file

@ -0,0 +1,93 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "env.h"
#include "interpreter.h"
ht_t *ht_init(void)
{
ht_t *ht = malloc(sizeof(ht_t) * DEFAULT_HT_SIZE);
for (int i = 0; i < DEFAULT_HT_SIZE; i++) {
ht[i].name = NULL;
}
return ht;
}
unsigned int hash(char *key)
{
unsigned int h = 0;
for (; *key; key++)
h = 31 * h + *key;
return h;
}
void ht_add(ht_t *ht, char *name, value_t value)
{
unsigned int idx = hash(name) % DEFAULT_HT_SIZE;
/* Linear probing for collision resolution */
for (int i = 0; i < DEFAULT_HT_SIZE; i++) {
int probe_idx = (idx + i) % DEFAULT_HT_SIZE;
if (!ht[probe_idx].name) {
ht[probe_idx].name = name;
memcpy(&ht[probe_idx].value, &value, sizeof(value));
return;
} else {
ht_replace(ht, name, value);
return;
}
}
}
value_t *ht_get(ht_t *ht, token_t *name)
{
unsigned int idx = hash(name->value) % DEFAULT_HT_SIZE;
/* Linear probing to search for the key */
for (int i = 0; i < DEFAULT_HT_SIZE; i++) {
int probe_idx = (idx + i) % DEFAULT_HT_SIZE;
if (ht[probe_idx].name && !strcmp(ht[probe_idx].name, name->value))
return &ht[probe_idx].value;
}
char err[512];
snprintf(err, 512, "Undefined variable '%s'.", name->value);
runtime_error(err, name->line);
return NULL;
}
void ht_replace(ht_t *ht, char *name, value_t value)
{
unsigned int idx = hash(name) % DEFAULT_HT_SIZE;
for (int i = 0; i < DEFAULT_HT_SIZE; i++) {
int probe_idx = (idx + i) % DEFAULT_HT_SIZE;
if (!ht[probe_idx].name)
break;
if (!strcmp(ht[probe_idx].name, name)) {
memcpy(&ht[probe_idx].value, &value, sizeof(value));
return;
}
}
}
void ht_assign(ht_t *ht, token_t *name, value_t value)
{
if (ht_get(ht, name)) {
ht_replace(ht, name->value, value);
return;
}
char err[512];
snprintf(err, 512, "Undefined variable '%s'.", name->value);
runtime_error(err, name->line);
}
void ht_free(ht_t *ht)
{
for (int i = 0; i < DEFAULT_HT_SIZE; i++) {
if (ht[i].value.type != VAL_NIL) {
free(ht[i].name);
}
}
free(ht);
}

View file

@ -4,13 +4,11 @@
#include <errno.h> #include <errno.h>
#include "ast.h" #include "ast.h"
#include "env.h"
#include "interpreter.h" #include "interpreter.h"
#include "lexer.h" #include "lexer.h"
value_t visit_literal(expr_t *expr); ht_t *ht;
value_t visit_grouping(expr_t *expr);
value_t visit_binary(expr_t *expr);
value_t visit_unary(expr_t *expr);
value_t visit_literal(expr_t *expr) value_t visit_literal(expr_t *expr)
{ {
@ -184,6 +182,23 @@ value_t visit_unary(expr_t *expr)
return (value_t){.type = VAL_NIL}; return (value_t){.type = VAL_NIL};
} }
value_t visit_variable(expr_t *expr)
{
value_t *val = ht_get(ht, &expr->as.variable.name);
if (val) {
return *val;
} else {
return (value_t) {.type = VAL_NIL};
}
}
value_t visit_assign(expr_t *expr)
{
value_t value = evaluate(expr->as.assign.value);
ht_assign(ht, &expr->as.assign.name, value);
return value;
}
value_t evaluate(expr_t *expr) value_t evaluate(expr_t *expr)
{ {
if (!expr) { if (!expr) {
@ -199,6 +214,10 @@ value_t evaluate(expr_t *expr)
return visit_unary(expr); return visit_unary(expr);
case EXPR_GROUPING: case EXPR_GROUPING:
return visit_grouping(expr); return visit_grouping(expr);
case EXPR_VARIABLE:
return visit_variable(expr);
case EXPR_ASSIGN:
return visit_assign(expr);
default: default:
exit(65); exit(65);
break; break;
@ -240,10 +259,18 @@ void print_statement(stmt_t stmt)
print_value(&obj); print_value(&obj);
} else if (stmt.type == STMT_EXPR) { } else if (stmt.type == STMT_EXPR) {
evaluate(stmt.as.expr.expression); evaluate(stmt.as.expr.expression);
} else if (stmt.type == STMT_VAR) {
value_t value = {.type = VAL_NIL};
if (stmt.as.variable.initializer) {
value = evaluate(stmt.as.variable.initializer);
}
ht_add(ht, stmt.as.variable.name.value, value);
} }
} }
void print_statements(stmt_array_t *array) void print_statements(stmt_array_t *array)
{ {
ht = ht_init();
for (int i = 0; i < array->length; i++) { for (int i = 0; i < array->length; i++) {
print_statement(array->statements[i]); print_statement(array->statements[i]);
} }

View file

@ -11,6 +11,9 @@ token_t *tokens;
expr_t *expression(void); expr_t *expression(void);
void synchronize(void); void synchronize(void);
/*
* Syntax error
*/
void error(token_t *token, char *message) void error(token_t *token, char *message)
{ {
if (token->type == TOKEN_EOF) { if (token->type == TOKEN_EOF) {
@ -51,6 +54,16 @@ void free_expr(expr_t *expr)
} }
free(expr); free(expr);
break; break;
case EXPR_VARIABLE:
free(expr->as.variable.name.value);
free(expr);
break;
case EXPR_ASSIGN:
free(expr->as.assign.name.value);
free_expr(expr->as.assign.value);
break;
default: default:
break; break;
@ -80,19 +93,17 @@ void advance(void)
int check(token_type_t type) int check(token_type_t type)
{ {
if (tokens[current].type == type) { return tokens[current].type == type;
advance();
return 1;
} else {
return 0;
}
} }
token_t *consume(token_type_t type, char *message) { token_t *consume(token_type_t type, char *message)
{
if (!check(type)) { if (!check(type)) {
error(peek(), message); error(peek(), message);
} else { } else {
return peek(); token_t *tok = peek();
advance();
return tok;
} }
return NULL; return NULL;
} }
@ -101,14 +112,19 @@ expr_t *primary(void)
{ {
if (check(TOKEN_FALSE) || check(TOKEN_TRUE) || check(TOKEN_NIL) || if (check(TOKEN_FALSE) || check(TOKEN_TRUE) || check(TOKEN_NIL) ||
check(TOKEN_NUMBER) || check(TOKEN_STRING)) { check(TOKEN_NUMBER) || check(TOKEN_STRING)) {
return create_literal_expr(previous()); token_t *tok = peek();
advance();
return create_literal_expr(tok);
} }
if (check(TOKEN_IDENTIFIER)) { if (check(TOKEN_IDENTIFIER)) {
return create_variable_expr(previous()); token_t *tok = peek();
advance();
return create_variable_expr(tok);
} }
if (check(TOKEN_LEFT_PAREN)) { if (check(TOKEN_LEFT_PAREN)) {
advance();
expr_t *expr = expression(); expr_t *expr = expression();
consume(TOKEN_RIGHT_PAREN, "Expect ')' after expression."); consume(TOKEN_RIGHT_PAREN, "Expect ')' after expression.");
return create_grouping_expr(expr); return create_grouping_expr(expr);
@ -120,7 +136,8 @@ expr_t *primary(void)
expr_t *unary(void) expr_t *unary(void)
{ {
if (check(TOKEN_BANG) || check(TOKEN_MINUS)) { if (check(TOKEN_BANG) || check(TOKEN_MINUS)) {
token_t *operator = previous(); token_t *operator = peek();
advance();
expr_t *right = unary(); expr_t *right = unary();
return create_unary_expr(operator, right); return create_unary_expr(operator, right);
} }
@ -133,7 +150,8 @@ expr_t *factor(void)
expr_t *expr = unary(); expr_t *expr = unary();
while (check(TOKEN_SLASH) || check(TOKEN_STAR)) { while (check(TOKEN_SLASH) || check(TOKEN_STAR)) {
token_t *operator = previous(); token_t *operator = peek();
advance();
expr_t *right = unary(); expr_t *right = unary();
expr = create_binary_expr(operator, expr, right); expr = create_binary_expr(operator, expr, right);
} }
@ -146,7 +164,8 @@ expr_t *term(void)
expr_t *expr = factor(); expr_t *expr = factor();
while (check(TOKEN_MINUS) || check(TOKEN_PLUS)) { while (check(TOKEN_MINUS) || check(TOKEN_PLUS)) {
token_t *operator = previous(); token_t *operator = peek();
advance();
expr_t *right = factor(); expr_t *right = factor();
expr = create_binary_expr(operator, expr, right); expr = create_binary_expr(operator, expr, right);
} }
@ -160,7 +179,8 @@ expr_t *comparison(void)
while (check(TOKEN_GREATER) || check(TOKEN_GREATER_EQUAL) || check(TOKEN_LESS) while (check(TOKEN_GREATER) || check(TOKEN_GREATER_EQUAL) || check(TOKEN_LESS)
|| check(TOKEN_LESS_EQUAL)) { || check(TOKEN_LESS_EQUAL)) {
token_t *operator = previous(); token_t *operator = peek();
advance();
expr_t *right = term(); expr_t *right = term();
expr = create_binary_expr(operator, expr, right); expr = create_binary_expr(operator, expr, right);
} }
@ -173,7 +193,8 @@ expr_t *equality(void)
expr_t *expr = comparison(); expr_t *expr = comparison();
while (check(TOKEN_BANG_EQUAL) || check(TOKEN_EQUAL_EQUAL)) { while (check(TOKEN_BANG_EQUAL) || check(TOKEN_EQUAL_EQUAL)) {
token_t *operator = previous(); token_t *operator = peek();
advance();
expr_t *right = comparison(); expr_t *right = comparison();
expr = create_binary_expr(operator, expr, right); expr = create_binary_expr(operator, expr, right);
} }
@ -181,9 +202,28 @@ expr_t *equality(void)
return expr; return expr;
} }
expr_t *assignment(void)
{
expr_t *expr = equality();
if (check(TOKEN_EQUAL)) {
token_t *equals = peek();
advance();
expr_t *value = assignment();
if (expr->type == EXPR_VARIABLE) {
token_t name = expr->as.variable.name;
return create_assign_expr(&name, value);
}
error(equals, "Invalid assignment target.");
}
return expr;
}
expr_t *expression(void) expr_t *expression(void)
{ {
return equality(); return assignment();
} }
stmt_t print_stmt(void) stmt_t print_stmt(void)
@ -208,8 +248,10 @@ stmt_t expression_stmt(void)
stmt_t statement(void) stmt_t statement(void)
{ {
if (check(TOKEN_PRINT)) if (check(TOKEN_PRINT)) {
advance();
return print_stmt(); return print_stmt();
}
return expression_stmt(); return expression_stmt();
} }
@ -219,6 +261,7 @@ stmt_t var_declaration(void)
expr_t *initializer = NULL; expr_t *initializer = NULL;
if (check(TOKEN_EQUAL)) { if (check(TOKEN_EQUAL)) {
advance();
initializer = expression(); initializer = expression();
} }
@ -234,8 +277,10 @@ stmt_t var_declaration(void)
stmt_t declaration(void) stmt_t declaration(void)
{ {
if (check(TOKEN_VAR)) if (check(TOKEN_VAR)) {
advance();
return var_declaration(); return var_declaration();
}
return statement(); return statement();
} }
@ -258,6 +303,9 @@ void free_statements(stmt_array_t *array)
if (array->statements[i].type == STMT_EXPR) { if (array->statements[i].type == STMT_EXPR) {
free_expr(array->statements[i].as.expr.expression); free_expr(array->statements[i].as.expr.expression);
} }
if (array->statements[i].type == STMT_VAR) {
free_expr(array->statements[i].as.variable.initializer);
}
} }
free(array->statements); free(array->statements);
free(array); free(array);