Function call, declaration
This commit is contained in:
parent
56d5d9d84b
commit
6d9d68d693
11 changed files with 622 additions and 310 deletions
122
include/ast.h
122
include/ast.h
|
@ -3,13 +3,20 @@
|
|||
|
||||
#include "lexer.h"
|
||||
|
||||
#define DEFAULT_STMTS_SIZE 512
|
||||
#define DEFAULT_ARGS_SIZE 255
|
||||
|
||||
/*
|
||||
expression → literal | unary | binary | grouping ;
|
||||
literal → NUMBER | STRING | "true" | "false" | "nil" ;
|
||||
grouping → "(" expression ")" ;
|
||||
unary → ( "-" | "!" ) expression ;
|
||||
binary → expression operator expression ;
|
||||
operator → "==" | "!=" | "<" | "<=" | ">" | ">=" | "+" | "-" | "*" | "/" ;
|
||||
expression → equality ;
|
||||
equality → comparison ( ( "!=" | "==" ) comparison )* ;
|
||||
comparison → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
|
||||
term → factor ( ( "-" | "+" ) factor )* ;
|
||||
factor → unary ( ( "/" | "*" ) unary )* ;
|
||||
unary → ( "!" | "-" ) unary | primary ;
|
||||
primary → NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" ;
|
||||
statement → exprStmt | ifStmt | printStmt | block ;
|
||||
ifStmt → "if" "(" expression ")" statement ( "else" statement )? ;
|
||||
block → "{" declaration* "}" ;
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
|
@ -32,18 +39,68 @@ typedef enum {
|
|||
VAL_NIL,
|
||||
VAL_NUMBER,
|
||||
VAL_STRING,
|
||||
VAL_FN,
|
||||
} value_type_t;
|
||||
|
||||
typedef enum {
|
||||
STMT_BLOCK,
|
||||
STMT_CLASS,
|
||||
STMT_EXPR,
|
||||
STMT_FUN,
|
||||
STMT_IF,
|
||||
STMT_PRINT,
|
||||
STMT_VAR,
|
||||
STMT_WHILE,
|
||||
} stmt_type_t;
|
||||
|
||||
typedef enum {
|
||||
FN_NATIVE,
|
||||
FN_CUSTOM,
|
||||
} fn_type_t;
|
||||
|
||||
typedef struct expr_t expr_t;
|
||||
typedef struct value_t value_t;
|
||||
typedef struct stmt_t stmt_t;
|
||||
typedef struct fn_t fn_t;
|
||||
|
||||
typedef struct {
|
||||
expr_t **arguments;
|
||||
int length;
|
||||
int capacity;
|
||||
} arg_array_t;
|
||||
|
||||
typedef struct {
|
||||
value_t **arguments;
|
||||
int length;
|
||||
int capacity;
|
||||
} val_array_t;
|
||||
|
||||
typedef struct {
|
||||
stmt_t **statements;
|
||||
int length;
|
||||
int capacity;
|
||||
} stmt_array_t;
|
||||
|
||||
struct value_t {
|
||||
value_type_t type;
|
||||
union {
|
||||
int boolean;
|
||||
double number;
|
||||
char *string;
|
||||
fn_t *function;
|
||||
} as;
|
||||
} value_t;
|
||||
};
|
||||
|
||||
typedef struct expr_t {
|
||||
typedef struct ht_t ht_t;
|
||||
|
||||
struct fn_t {
|
||||
fn_type_t type;
|
||||
int arity;
|
||||
stmt_t *stmt;
|
||||
value_t *(*call)(stmt_t *stmt, val_array_t *arguments, ht_t *env);
|
||||
};
|
||||
|
||||
struct expr_t {
|
||||
expr_type_t type;
|
||||
int line;
|
||||
union {
|
||||
|
@ -59,7 +116,7 @@ typedef struct expr_t {
|
|||
struct {
|
||||
struct expr_t *callee;
|
||||
token_t paren;
|
||||
struct expr_t **arguments;
|
||||
arg_array_t *args;
|
||||
} call;
|
||||
struct {
|
||||
struct expr_t *object;
|
||||
|
@ -69,7 +126,7 @@ typedef struct expr_t {
|
|||
struct expr_t *expression;
|
||||
} grouping;
|
||||
struct {
|
||||
value_t value;
|
||||
value_t *value;
|
||||
} literal;
|
||||
struct {
|
||||
token_t operator;
|
||||
|
@ -96,7 +153,49 @@ typedef struct expr_t {
|
|||
token_t name;
|
||||
} variable;
|
||||
} as;
|
||||
} expr_t;
|
||||
};
|
||||
|
||||
struct stmt_t {
|
||||
stmt_type_t type;
|
||||
union {
|
||||
struct {
|
||||
stmt_array_t *statements;
|
||||
} block;
|
||||
struct {
|
||||
token_t name;
|
||||
token_t superclass;
|
||||
struct stmt_t **methods;
|
||||
} class;
|
||||
struct {
|
||||
expr_t *expression;
|
||||
} expr;
|
||||
struct {
|
||||
token_t name;
|
||||
array_t *params;
|
||||
struct stmt_t *body;
|
||||
} function;
|
||||
struct {
|
||||
expr_t *condition;
|
||||
struct stmt_t *then_branch;
|
||||
struct stmt_t *else_branch;
|
||||
} _if;
|
||||
struct {
|
||||
expr_t *expression;
|
||||
} print;
|
||||
struct {
|
||||
token_t keyword;
|
||||
expr_t *value;
|
||||
} _return;
|
||||
struct {
|
||||
token_t name;
|
||||
expr_t *initializer;
|
||||
} variable;
|
||||
struct {
|
||||
expr_t *condition;
|
||||
struct stmt_t *body;
|
||||
} _while;
|
||||
} as;
|
||||
};
|
||||
|
||||
expr_t *create_binary_expr(token_t *operator, expr_t *left, expr_t *right);
|
||||
expr_t *create_unary_expr(token_t *operator, expr_t *right);
|
||||
|
@ -105,6 +204,7 @@ 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);
|
||||
expr_t *create_call_expr(expr_t *callee, token_t *paren, arg_array_t *args);
|
||||
void print_ast(expr_t *expr);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -13,10 +13,10 @@ typedef struct ht_t {
|
|||
#define DEFAULT_HT_SIZE 50
|
||||
|
||||
ht_t *ht_init(ht_t *env);
|
||||
void ht_add(ht_t *ht, char *name, value_t value);
|
||||
void ht_add(ht_t *ht, char *name, value_t *value);
|
||||
value_t *ht_get(ht_t *ht, token_t *name, int check_enclosing);
|
||||
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_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
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
|
||||
#include "ast.h"
|
||||
#include "env.h"
|
||||
#include "stmt.h"
|
||||
|
||||
void free_val(value_t *value);
|
||||
void runtime_error(const char *message, int line);
|
||||
value_t evaluate(expr_t *expr, ht_t *env);
|
||||
void print_value(value_t value);
|
||||
void evaluate_statements(stmt_array_t *array, ht_t *env);
|
||||
value_t *evaluate(expr_t *expr, ht_t *env);
|
||||
void print_value(value_t *value);
|
||||
void interpret(stmt_array_t *array);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -41,6 +41,7 @@ typedef struct {
|
|||
int capacity;
|
||||
} array_t;
|
||||
|
||||
void token_add(array_t *array, token_t token);
|
||||
array_t *tokenize(char *filename);
|
||||
void print_tokens(token_t *tokens);
|
||||
void free_array(array_t *array);
|
||||
|
|
|
@ -1,19 +1,8 @@
|
|||
#ifndef PARSER_H
|
||||
#define PARSER_H
|
||||
|
||||
/*
|
||||
expression → equality ;
|
||||
equality → comparison ( ( "!=" | "==" ) comparison )* ;
|
||||
comparison → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
|
||||
term → factor ( ( "-" | "+" ) factor )* ;
|
||||
factor → unary ( ( "/" | "*" ) unary )* ;
|
||||
unary → ( "!" | "-" ) unary | primary ;
|
||||
primary → NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" ;
|
||||
*/
|
||||
|
||||
#include "ast.h"
|
||||
#include "lexer.h"
|
||||
#include "stmt.h"
|
||||
|
||||
stmt_array_t *parse(token_t *tks);
|
||||
expr_t *parse_expr(token_t *tks);
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
#ifndef STMT_H
|
||||
#define STMT_H
|
||||
|
||||
#include "ast.h"
|
||||
#include "lexer.h"
|
||||
|
||||
#define DEFAULT_STMTS_SIZE 512
|
||||
|
||||
/*
|
||||
statement → exprStmt | ifStmt | printStmt | block ;
|
||||
ifStmt → "if" "(" expression ")" statement
|
||||
( "else" statement )? ;
|
||||
block → "{" declaration* "}" ;
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
STMT_BLOCK,
|
||||
STMT_CLASS,
|
||||
STMT_EXPR,
|
||||
STMT_FUN,
|
||||
STMT_IF,
|
||||
STMT_PRINT,
|
||||
STMT_VAR,
|
||||
STMT_WHILE,
|
||||
} stmt_type_t;
|
||||
|
||||
typedef struct stmt_t stmt_t;
|
||||
|
||||
typedef struct {
|
||||
stmt_t **statements;
|
||||
int length;
|
||||
int capacity;
|
||||
} stmt_array_t;
|
||||
|
||||
struct stmt_t {
|
||||
stmt_type_t type;
|
||||
union {
|
||||
struct {
|
||||
stmt_array_t *statements;
|
||||
} block;
|
||||
struct {
|
||||
token_t name;
|
||||
token_t superclass;
|
||||
struct stmt_t **methods;
|
||||
} class;
|
||||
struct {
|
||||
expr_t *expression;
|
||||
} expr;
|
||||
struct {
|
||||
token_t name;
|
||||
array_t *params;
|
||||
struct stmt_t **body;
|
||||
} function;
|
||||
struct {
|
||||
expr_t *condition;
|
||||
struct stmt_t *then_branch;
|
||||
struct stmt_t *else_branch;
|
||||
} _if;
|
||||
struct {
|
||||
expr_t *expression;
|
||||
} print;
|
||||
struct {
|
||||
token_t keyword;
|
||||
expr_t *value;
|
||||
} _return;
|
||||
struct {
|
||||
token_t name;
|
||||
expr_t *initializer;
|
||||
} variable;
|
||||
struct {
|
||||
expr_t *condition;
|
||||
struct stmt_t *body;
|
||||
} _while;
|
||||
} as;
|
||||
};
|
||||
|
||||
#endif
|
42
src/ast.c
42
src/ast.c
|
@ -35,28 +35,29 @@ expr_t *create_literal_expr(token_t *token)
|
|||
expr_t *expr = malloc(sizeof(expr_t));
|
||||
expr->type = EXPR_LITERAL;
|
||||
expr->line = token->line;
|
||||
expr->as.literal.value = malloc(sizeof(value_t));
|
||||
switch (token->type) {
|
||||
case TOKEN_NUMBER:
|
||||
expr->as.literal.value.type = VAL_NUMBER;
|
||||
expr->as.literal.value->type = VAL_NUMBER;
|
||||
double num;
|
||||
sscanf(token->value, "%lf", &num);
|
||||
expr->as.literal.value.as.number = num;
|
||||
expr->as.literal.value->as.number = num;
|
||||
break;
|
||||
|
||||
case TOKEN_NIL:
|
||||
expr->as.literal.value.type = VAL_NIL;
|
||||
expr->as.literal.value.as.number = 0;
|
||||
expr->as.literal.value->type = VAL_NIL;
|
||||
expr->as.literal.value->as.number = 0;
|
||||
break;
|
||||
|
||||
case TOKEN_TRUE:
|
||||
case TOKEN_FALSE:
|
||||
expr->as.literal.value.type = VAL_BOOL;
|
||||
expr->as.literal.value.as.boolean = token->type == TOKEN_TRUE;
|
||||
expr->as.literal.value->type = VAL_BOOL;
|
||||
expr->as.literal.value->as.boolean = token->type == TOKEN_TRUE;
|
||||
break;
|
||||
|
||||
case TOKEN_STRING:
|
||||
expr->as.literal.value.type = VAL_STRING;
|
||||
expr->as.literal.value.as.string = strdup(token->value);
|
||||
expr->as.literal.value->type = VAL_STRING;
|
||||
expr->as.literal.value->as.string = strdup(token->value);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -113,14 +114,27 @@ expr_t *create_logical_expr(token_t *operator, expr_t *left, expr_t *right)
|
|||
return expr;
|
||||
}
|
||||
|
||||
expr_t *create_call_expr(expr_t *callee, token_t *paren, arg_array_t *args)
|
||||
{
|
||||
expr_t *expr = malloc(sizeof(expr_t));
|
||||
expr->type = EXPR_CALL;
|
||||
expr->line = paren->line;
|
||||
expr->as.call.callee = callee;
|
||||
expr->as.call.paren.type = paren->type;
|
||||
expr->as.call.paren.value = strdup(paren->value);
|
||||
expr->as.call.paren.line = paren->line;
|
||||
expr->as.call.args = args;
|
||||
return expr;
|
||||
}
|
||||
|
||||
void print_ast(expr_t *expr)
|
||||
{
|
||||
if (!expr)
|
||||
return;
|
||||
if (expr->type == EXPR_LITERAL) {
|
||||
switch (expr->as.literal.value.type) {
|
||||
switch (expr->as.literal.value->type) {
|
||||
case VAL_BOOL:
|
||||
printf("%s", expr->as.literal.value.as.boolean ? "true" : "false");
|
||||
printf("%s", expr->as.literal.value->as.boolean ? "true" : "false");
|
||||
break;
|
||||
|
||||
case VAL_NIL:
|
||||
|
@ -128,7 +142,7 @@ void print_ast(expr_t *expr)
|
|||
break;
|
||||
|
||||
case VAL_NUMBER:;
|
||||
double value = expr->as.literal.value.as.number;
|
||||
double value = expr->as.literal.value->as.number;
|
||||
if (value == (int) value) {
|
||||
printf("%.1f", value);
|
||||
} else {
|
||||
|
@ -137,7 +151,11 @@ void print_ast(expr_t *expr)
|
|||
break;
|
||||
|
||||
case VAL_STRING:
|
||||
printf("%s", expr->as.literal.value.as.string);
|
||||
printf("%s", expr->as.literal.value->as.string);
|
||||
break;
|
||||
|
||||
case VAL_FN:
|
||||
printf("<native fn>");
|
||||
break;
|
||||
}
|
||||
} else if (expr->type == EXPR_BINARY) {
|
||||
|
|
44
src/env.c
44
src/env.c
|
@ -5,6 +5,8 @@
|
|||
#include "env.h"
|
||||
#include "interpreter.h"
|
||||
|
||||
void free_statement(stmt_t *stmt);
|
||||
|
||||
ht_t *ht_init(ht_t *env)
|
||||
{
|
||||
ht_t *ht = malloc(sizeof(ht_t) * DEFAULT_HT_SIZE);
|
||||
|
@ -28,7 +30,7 @@ unsigned int hash(char *key)
|
|||
return h;
|
||||
}
|
||||
|
||||
void ht_add(ht_t *ht, char *name, value_t value)
|
||||
void ht_add(ht_t *ht, char *name, value_t *value)
|
||||
{
|
||||
unsigned int idx = hash(name) % DEFAULT_HT_SIZE;
|
||||
/* Linear probing for collision resolution */
|
||||
|
@ -36,11 +38,14 @@ void ht_add(ht_t *ht, char *name, value_t value)
|
|||
int probe_idx = (idx + i) % DEFAULT_HT_SIZE;
|
||||
if (!ht[probe_idx].name) {
|
||||
ht[probe_idx].name = strdup(name);
|
||||
ht[probe_idx].value.type = value.type;
|
||||
if (value.type == VAL_STRING) {
|
||||
ht[probe_idx].value.as.string = strdup(value.as.string);
|
||||
ht[probe_idx].value.type = value->type;
|
||||
if (value->type == VAL_STRING) {
|
||||
ht[probe_idx].value.as.string = strdup(value->as.string);
|
||||
} else if (value->type == VAL_FN) {
|
||||
ht[probe_idx].value.as.function = malloc(sizeof(fn_t));
|
||||
memcpy(ht[probe_idx].value.as.function, value->as.function, sizeof(fn_t));
|
||||
} else {
|
||||
ht[probe_idx].value.as = value.as;
|
||||
ht[probe_idx].value.as = value->as;
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
|
@ -56,8 +61,15 @@ value_t *ht_get(ht_t *ht, token_t *name, int check_enclosing)
|
|||
/* 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;
|
||||
if (ht[probe_idx].name && !strcmp(ht[probe_idx].name, name->value)) {
|
||||
value_t *val = malloc(sizeof(value_t));
|
||||
memcpy(val, &ht[probe_idx].value, sizeof(value_t));
|
||||
if (val->type == VAL_STRING) {
|
||||
val->as.string = strdup(ht[probe_idx].value.as.string);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
}
|
||||
if (check_enclosing) {
|
||||
if (ht->enclosing) {
|
||||
|
@ -73,7 +85,7 @@ value_t *ht_get(ht_t *ht, token_t *name, int check_enclosing)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void ht_replace(ht_t *ht, char *name, value_t value)
|
||||
void ht_replace(ht_t *ht, char *name, value_t *value)
|
||||
{
|
||||
unsigned int idx = hash(name) % DEFAULT_HT_SIZE;
|
||||
|
||||
|
@ -87,20 +99,22 @@ void ht_replace(ht_t *ht, char *name, value_t value)
|
|||
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);
|
||||
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);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ht_assign(ht_t *ht, token_t *name, value_t value)
|
||||
void ht_assign(ht_t *ht, token_t *name, value_t *value)
|
||||
{
|
||||
if (ht_get(ht, name, 0)) {
|
||||
value_t *val = ht_get(ht, name, 0);
|
||||
if (val) {
|
||||
ht_replace(ht, name->value, value);
|
||||
free_val(val);
|
||||
return;
|
||||
}
|
||||
if (ht->enclosing) {
|
||||
|
@ -119,6 +133,8 @@ void ht_free(ht_t *ht)
|
|||
free(ht[i].name);
|
||||
if (ht[i].value.type == VAL_STRING) {
|
||||
free(ht[i].value.as.string);
|
||||
} else if (ht[i].value.type == VAL_FN) {
|
||||
free(ht[i].value.as.function);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "ast.h"
|
||||
#include "env.h"
|
||||
|
@ -9,12 +10,30 @@
|
|||
#include "lexer.h"
|
||||
#include "parser.h"
|
||||
|
||||
value_t visit_literal(expr_t *expr)
|
||||
void evaluate_statement(stmt_t *stmt, ht_t *env);
|
||||
|
||||
void free_val(value_t *value)
|
||||
{
|
||||
return expr->as.literal.value;
|
||||
if (!value)
|
||||
return;
|
||||
if (value->type == VAL_STRING) {
|
||||
if (value->as.string) {
|
||||
free(value->as.string);
|
||||
}
|
||||
} else if (value->type == VAL_FN) {
|
||||
|
||||
}
|
||||
free(value);
|
||||
}
|
||||
|
||||
value_t visit_grouping(expr_t *expr, ht_t *env)
|
||||
value_t *visit_literal(expr_t *expr)
|
||||
{
|
||||
value_t *val = malloc(sizeof(value_t));
|
||||
memcpy(val, expr->as.literal.value, sizeof(value_t));
|
||||
return val;
|
||||
}
|
||||
|
||||
value_t *visit_grouping(expr_t *expr, ht_t *env)
|
||||
{
|
||||
return evaluate(expr->as.grouping.expression, env);
|
||||
}
|
||||
|
@ -26,56 +45,66 @@ void runtime_error(const char *message, int line)
|
|||
exit(70);
|
||||
}
|
||||
|
||||
value_t visit_binary(expr_t *expr, ht_t *env)
|
||||
value_t *visit_binary(expr_t *expr, ht_t *env)
|
||||
{
|
||||
token_type_t op_type = expr->as.binary.operator.type;
|
||||
value_t right = evaluate(expr->as.binary.right, env);
|
||||
value_t left = evaluate(expr->as.binary.left, env);
|
||||
value_t *right = evaluate(expr->as.binary.right, env);
|
||||
value_t *left = evaluate(expr->as.binary.left, env);
|
||||
|
||||
// Arithmetic
|
||||
if (left.type == VAL_NUMBER && right.type == VAL_NUMBER) {
|
||||
value_t result = {.type = VAL_NUMBER};
|
||||
if (left->type == VAL_NUMBER && right->type == VAL_NUMBER) {
|
||||
value_t *result = malloc(sizeof(value_t));
|
||||
result->type = VAL_NUMBER;
|
||||
switch (op_type) {
|
||||
case TOKEN_PLUS:
|
||||
result.as.number = left.as.number + right.as.number;
|
||||
result->as.number = left->as.number + right->as.number;
|
||||
free_val(left);
|
||||
free_val(right);
|
||||
return result;
|
||||
|
||||
case TOKEN_MINUS:
|
||||
result.as.number = left.as.number - right.as.number;
|
||||
result->as.number = left->as.number - right->as.number;
|
||||
free_val(left);
|
||||
free_val(right);
|
||||
return result;
|
||||
|
||||
case TOKEN_STAR:
|
||||
result.as.number = left.as.number * right.as.number;
|
||||
result->as.number = left->as.number * right->as.number;
|
||||
free_val(left);
|
||||
free_val(right);
|
||||
return result;
|
||||
|
||||
case TOKEN_SLASH:
|
||||
if (right.as.number == 0) {
|
||||
if (right->as.number == 0) {
|
||||
runtime_error("Division by zero.", expr->line);
|
||||
}
|
||||
result.as.number = left.as.number / right.as.number;
|
||||
result->as.number = left->as.number / right->as.number;
|
||||
free_val(left);
|
||||
free_val(right);
|
||||
return result;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
free_val(result);
|
||||
}
|
||||
|
||||
// Comparison
|
||||
if (op_type == TOKEN_EQUAL_EQUAL || op_type == TOKEN_BANG_EQUAL) {
|
||||
int is_equal;
|
||||
if (left.type != right.type) {
|
||||
if (left->type != right->type) {
|
||||
is_equal = 0;
|
||||
} else {
|
||||
switch (left.type) {
|
||||
switch (left->type) {
|
||||
case VAL_NUMBER:
|
||||
is_equal = left.as.number == right.as.number;
|
||||
is_equal = left->as.number == right->as.number;
|
||||
break;
|
||||
|
||||
case VAL_BOOL:
|
||||
is_equal = left.as.boolean == right.as.boolean;
|
||||
is_equal = left->as.boolean == right->as.boolean;
|
||||
break;
|
||||
|
||||
case VAL_STRING:
|
||||
is_equal = strcmp(left.as.string, right.as.string) == 0;
|
||||
is_equal = strcmp(left->as.string, right->as.string) == 0;
|
||||
break;
|
||||
|
||||
case VAL_NIL:
|
||||
|
@ -87,29 +116,41 @@ value_t visit_binary(expr_t *expr, ht_t *env)
|
|||
break;
|
||||
}
|
||||
}
|
||||
value_t result = {.type = VAL_BOOL};
|
||||
result.as.boolean = op_type == TOKEN_EQUAL_EQUAL ? is_equal : !is_equal;
|
||||
value_t *result = malloc(sizeof(value_t));
|
||||
result->type = VAL_BOOL;
|
||||
result->as.boolean = op_type == TOKEN_EQUAL_EQUAL ? is_equal : !is_equal;
|
||||
free_val(left);
|
||||
free_val(right);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Number Comparison
|
||||
if (left.type == VAL_NUMBER && right.type == VAL_NUMBER) {
|
||||
value_t result = {.type = VAL_BOOL };
|
||||
if (left->type == VAL_NUMBER && right->type == VAL_NUMBER) {
|
||||
value_t *result = malloc(sizeof(value_t));
|
||||
result->type = VAL_BOOL;
|
||||
switch (op_type) {
|
||||
case TOKEN_GREATER:
|
||||
result.as.boolean= left.as.number > right.as.number;
|
||||
result->as.boolean = left->as.number > right->as.number;
|
||||
free_val(left);
|
||||
free_val(right);
|
||||
return result;
|
||||
|
||||
case TOKEN_GREATER_EQUAL:
|
||||
result.as.boolean = left.as.number >= right.as.number;
|
||||
result->as.boolean = left->as.number >= right->as.number;
|
||||
free_val(left);
|
||||
free_val(right);
|
||||
return result;
|
||||
|
||||
case TOKEN_LESS:
|
||||
result.as.boolean = left.as.number < right.as.number;
|
||||
result->as.boolean = left->as.number < right->as.number;
|
||||
free_val(left);
|
||||
free_val(right);
|
||||
return result;
|
||||
|
||||
case TOKEN_LESS_EQUAL:
|
||||
result.as.boolean = left.as.number <= right.as.number;
|
||||
result->as.boolean = left->as.number <= right->as.number;
|
||||
free_val(left);
|
||||
free_val(right);
|
||||
return result;
|
||||
|
||||
default: break;
|
||||
|
@ -117,40 +158,46 @@ value_t visit_binary(expr_t *expr, ht_t *env)
|
|||
}
|
||||
|
||||
// String concatenation
|
||||
if (left.type == VAL_STRING && right.type == VAL_STRING) {
|
||||
if (left->type == VAL_STRING && right->type == VAL_STRING) {
|
||||
if (op_type == TOKEN_PLUS) {
|
||||
value_t result = {.type = VAL_STRING};
|
||||
size_t left_len = strlen(left.as.string);
|
||||
size_t right_len = strlen(right.as.string);
|
||||
result.as.string = malloc(left_len + right_len + 1);
|
||||
strcpy(result.as.string, left.as.string);
|
||||
strcat(result.as.string, right.as.string);
|
||||
value_t *result = malloc(sizeof(value_t));
|
||||
result->type = VAL_STRING;
|
||||
size_t left_len = strlen(left->as.string);
|
||||
size_t right_len = strlen(right->as.string);
|
||||
result->as.string = malloc(left_len + right_len + 1);
|
||||
strcpy(result->as.string, left->as.string);
|
||||
strcat(result->as.string, right->as.string);
|
||||
free_val(left);
|
||||
free_val(right);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// String/number comparisons
|
||||
if ((left.type == VAL_STRING && right.type == VAL_NUMBER) ||
|
||||
(left.type == VAL_NUMBER && right.type == VAL_STRING)) {
|
||||
if ((left->type == VAL_STRING && right->type == VAL_NUMBER) ||
|
||||
(left->type == VAL_NUMBER && right->type == VAL_STRING)) {
|
||||
runtime_error("Operands must be numbers.", expr->line);
|
||||
}
|
||||
|
||||
runtime_error("Operands must be two numbers or two strings.", expr->line);
|
||||
return (value_t){.type = VAL_NIL};
|
||||
|
||||
value_t *val = malloc(sizeof(value_t));
|
||||
val->type = VAL_NIL;
|
||||
free_val(left);
|
||||
free_val(right);
|
||||
return val;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
@ -162,45 +209,57 @@ int is_truthy(value_t value)
|
|||
}
|
||||
}
|
||||
|
||||
value_t visit_unary(expr_t *expr, ht_t *env)
|
||||
value_t *visit_unary(expr_t *expr, ht_t *env)
|
||||
{
|
||||
value_t operand = evaluate(expr->as.unary.right, env);
|
||||
value_t *operand = evaluate(expr->as.unary.right, env);
|
||||
|
||||
if (expr->as.unary.operator.type == TOKEN_MINUS) {
|
||||
if (operand.type == VAL_NUMBER) {
|
||||
value_t result = {.type = VAL_NUMBER, .as.number = -operand.as.number};
|
||||
if (operand->type == VAL_NUMBER) {
|
||||
value_t *result = malloc(sizeof(value_t));
|
||||
result->type = VAL_NUMBER;
|
||||
result->as.number = -operand->as.number;
|
||||
free_val(operand);
|
||||
return result;
|
||||
} else {
|
||||
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 = malloc(sizeof(value_t));
|
||||
result->type = VAL_BOOL;
|
||||
result->as.boolean = !is_truthy(operand);
|
||||
free_val(operand);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return (value_t){.type = VAL_NIL};
|
||||
value_t *val = malloc(sizeof(value_t));
|
||||
val->type = VAL_NIL;
|
||||
free_val(operand);
|
||||
return val;
|
||||
}
|
||||
|
||||
value_t visit_variable(expr_t *expr, ht_t *env)
|
||||
value_t *visit_variable(expr_t *expr, ht_t *env)
|
||||
{
|
||||
value_t *val = ht_get(env, &expr->as.variable.name, 1);
|
||||
if (val) {
|
||||
return *val;
|
||||
return val;
|
||||
} else {
|
||||
return (value_t) {.type = VAL_NIL};
|
||||
val = malloc(sizeof(value_t));
|
||||
val->type = VAL_NIL;
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
value_t visit_assign(expr_t *expr, ht_t *env)
|
||||
value_t *visit_assign(expr_t *expr, ht_t *env)
|
||||
{
|
||||
value_t value = evaluate(expr->as.assign.value, env);
|
||||
value_t *value = evaluate(expr->as.assign.value, env);
|
||||
ht_assign(env, &expr->as.assign.name->as.variable.name, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
value_t visit_logical(expr_t *expr, ht_t *env)
|
||||
value_t *visit_logical(expr_t *expr, ht_t *env)
|
||||
{
|
||||
value_t left = evaluate(expr->as.logical.left, env);
|
||||
value_t *left = evaluate(expr->as.logical.left, env);
|
||||
|
||||
if (expr->as.logical.operator.type == TOKEN_OR) {
|
||||
if (is_truthy(left))
|
||||
|
@ -213,11 +272,58 @@ value_t visit_logical(expr_t *expr, ht_t *env)
|
|||
return evaluate(expr->as.logical.right, env);
|
||||
}
|
||||
|
||||
value_t evaluate(expr_t *expr, ht_t *env)
|
||||
void val_add(val_array_t *array, value_t *expr)
|
||||
{
|
||||
if (array->length == array->capacity) {
|
||||
array->capacity *= 2;
|
||||
array->arguments = realloc(array->arguments, array->capacity * sizeof(expr_t *));
|
||||
}
|
||||
array->arguments[array->length++] = expr;
|
||||
}
|
||||
|
||||
void free_vals(val_array_t *array)
|
||||
{
|
||||
for (int i = 0; i < array->length; i++) {
|
||||
free_val(array->arguments[i]);
|
||||
}
|
||||
free(array->arguments);
|
||||
free(array);
|
||||
}
|
||||
|
||||
value_t *visit_call(expr_t *expr, ht_t *env)
|
||||
{
|
||||
value_t *callee = evaluate(expr->as.call.callee, env);
|
||||
if (callee->type != VAL_FN) {
|
||||
runtime_error("Can only call functions and classes.", expr->line);
|
||||
}
|
||||
|
||||
val_array_t *arguments = malloc(sizeof(val_array_t));
|
||||
arguments->arguments = malloc(DEFAULT_ARGS_SIZE * sizeof(value_t *));
|
||||
arguments->length = 0;
|
||||
arguments->capacity = DEFAULT_ARGS_SIZE;
|
||||
|
||||
for (int i = 0; i < expr->as.call.args->length; i++) {
|
||||
value_t *val = evaluate(expr->as.call.args->arguments[i], env);
|
||||
val_add(arguments, val);
|
||||
}
|
||||
|
||||
if (arguments->length != callee->as.function->arity) {
|
||||
char err[512];
|
||||
snprintf(err, 512, "Expected %d arguments but got %d.", callee->as.function->arity, arguments->length);
|
||||
runtime_error(err, expr->line);
|
||||
}
|
||||
value_t *res = callee->as.function->call(callee->as.function->stmt, arguments, env);
|
||||
free_vals(arguments);
|
||||
free_val(callee);
|
||||
return res;
|
||||
}
|
||||
|
||||
value_t *evaluate(expr_t *expr, ht_t *env)
|
||||
{
|
||||
if (!expr) {
|
||||
value_t nil_value = {.type = VAL_NIL };
|
||||
return nil_value;
|
||||
value_t *nil = malloc(sizeof(value_t));
|
||||
nil->type = VAL_NIL;
|
||||
return nil;
|
||||
}
|
||||
switch (expr->type) {
|
||||
case EXPR_LITERAL:
|
||||
|
@ -234,17 +340,19 @@ value_t evaluate(expr_t *expr, ht_t *env)
|
|||
return visit_assign(expr, env);
|
||||
case EXPR_LOGICAL:
|
||||
return visit_logical(expr, env);
|
||||
case EXPR_CALL:
|
||||
return visit_call(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:
|
||||
|
@ -252,66 +360,22 @@ 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;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void evaluate_block(stmt_array_t *array, ht_t *cur_env, ht_t *scope_env)
|
||||
{
|
||||
ht_t *previous = cur_env;
|
||||
cur_env = scope_env;
|
||||
evaluate_statements(array, cur_env);
|
||||
ht_free(scope_env);
|
||||
cur_env = previous;
|
||||
}
|
||||
|
||||
void evaluate_statement(stmt_t *stmt, ht_t *env)
|
||||
{
|
||||
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);
|
||||
break;
|
||||
|
||||
case STMT_VAR: {
|
||||
value_t value = {.type = VAL_NIL};
|
||||
if (stmt->as.variable.initializer) {
|
||||
value = evaluate(stmt->as.variable.initializer, env);
|
||||
}
|
||||
ht_add(env, stmt->as.variable.name.value, value);
|
||||
break;
|
||||
}
|
||||
|
||||
case STMT_BLOCK:
|
||||
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);
|
||||
case VAL_FN:
|
||||
if (value->as.function->type == FN_NATIVE) {
|
||||
printf("<native fn>\n");
|
||||
} else {
|
||||
printf("<fn %s>\n", value->as.function->stmt->as.function.name.value);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -326,3 +390,121 @@ void evaluate_statements(stmt_array_t *array, ht_t *env)
|
|||
evaluate_statement(array->statements[i], env);
|
||||
}
|
||||
}
|
||||
|
||||
void evaluate_block(stmt_array_t *array, ht_t *cur_env, ht_t *scope_env)
|
||||
{
|
||||
ht_t *previous = cur_env;
|
||||
cur_env = scope_env;
|
||||
evaluate_statements(array, cur_env);
|
||||
ht_free(scope_env);
|
||||
cur_env = previous;
|
||||
}
|
||||
|
||||
value_t *_clock(stmt_t *stmt, val_array_t *arguments, ht_t *env)
|
||||
{
|
||||
value_t *val = malloc(sizeof(value_t));
|
||||
val->type = VAL_NUMBER;
|
||||
val->as.number = time(NULL);
|
||||
return val;
|
||||
}
|
||||
|
||||
value_t *_call(stmt_t *stmt, val_array_t *arguments, ht_t *env)
|
||||
{
|
||||
ht_t *fn_env = ht_init(env);
|
||||
for (int i = 0; i < stmt->as.function.params->length; i++) {
|
||||
ht_add(fn_env, stmt->as.function.params->tokens[i].value, arguments->arguments[i]);
|
||||
}
|
||||
|
||||
evaluate_block(stmt->as.function.body->as.block.statements, env, fn_env);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void evaluate_statement(stmt_t *stmt, ht_t *env)
|
||||
{
|
||||
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:;
|
||||
value_t *val = evaluate(stmt->as.print.expression, env);
|
||||
print_value(val);
|
||||
free_val(val);
|
||||
break;
|
||||
|
||||
case STMT_EXPR:;
|
||||
value_t *res = evaluate(stmt->as.expr.expression, env);
|
||||
free_val(res);
|
||||
break;
|
||||
|
||||
case STMT_VAR: {
|
||||
value_t *value = malloc(sizeof(value_t));
|
||||
value->type = VAL_NIL;
|
||||
if (stmt->as.variable.initializer) {
|
||||
free(value);
|
||||
value = evaluate(stmt->as.variable.initializer, env);
|
||||
}
|
||||
ht_add(env, stmt->as.variable.name.value, value);
|
||||
free_val(value);
|
||||
break;
|
||||
}
|
||||
|
||||
case STMT_BLOCK:
|
||||
evaluate_block(stmt->as.block.statements, env, ht_init(env));
|
||||
break;
|
||||
|
||||
case STMT_WHILE:;
|
||||
value_t *cond = evaluate(stmt->as._while.condition, env);
|
||||
while (is_truthy(cond)) {
|
||||
evaluate_statement(stmt->as._while.body, env);
|
||||
free_val(cond);
|
||||
cond = evaluate(stmt->as._while.condition, env);
|
||||
}
|
||||
free_val(cond);
|
||||
break;
|
||||
|
||||
case STMT_FUN:;
|
||||
fn_t *fn = malloc(sizeof(fn_t));
|
||||
fn->type = FN_CUSTOM;
|
||||
fn->arity = stmt->as.function.params->length;
|
||||
fn->stmt = stmt;
|
||||
fn->call = _call;
|
||||
|
||||
value_t *fn_val = malloc(sizeof(value_t));
|
||||
fn_val->type = VAL_FN;
|
||||
fn_val->as.function = fn;
|
||||
ht_add(env, stmt->as.function.name.value, fn_val);
|
||||
free_val(fn_val);
|
||||
free(fn);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void interpret(stmt_array_t *array)
|
||||
{
|
||||
ht_t *env = ht_init(NULL);
|
||||
value_t *clock_fn = malloc(sizeof(value_t));
|
||||
clock_fn->type = VAL_FN;
|
||||
fn_t *fn = malloc(sizeof(fn_t));
|
||||
fn->type = FN_NATIVE;
|
||||
fn->arity = 0;
|
||||
/* Native function don't have body */
|
||||
fn->stmt = NULL;
|
||||
fn->call = _clock;
|
||||
clock_fn->as.function = fn;
|
||||
|
||||
ht_add(env, "clock", clock_fn);
|
||||
evaluate_statements(array, env);
|
||||
ht_free(env);
|
||||
free(clock_fn);
|
||||
free(fn);
|
||||
free_statements(array);
|
||||
}
|
||||
|
|
204
src/parser.c
204
src/parser.c
|
@ -4,11 +4,13 @@
|
|||
#include <errno.h>
|
||||
|
||||
#include "ast.h"
|
||||
#include "interpreter.h"
|
||||
#include "lexer.h"
|
||||
#include "parser.h"
|
||||
|
||||
int current = 0;
|
||||
token_t *tokens;
|
||||
void free_args(arg_array_t *array);
|
||||
expr_t *expression(void);
|
||||
stmt_t *expression_stmt(void);
|
||||
stmt_t *statement(void);
|
||||
|
@ -54,9 +56,7 @@ void free_expr(expr_t *expr)
|
|||
break;
|
||||
|
||||
case EXPR_LITERAL:
|
||||
if (expr->as.literal.value.type == VAL_STRING) {
|
||||
free(expr->as.literal.value.as.string);
|
||||
}
|
||||
free(expr->as.literal.value);
|
||||
free(expr);
|
||||
break;
|
||||
|
||||
|
@ -78,6 +78,13 @@ void free_expr(expr_t *expr)
|
|||
free(expr);
|
||||
break;
|
||||
|
||||
case EXPR_CALL:
|
||||
free_args(expr->as.call.args);
|
||||
free_expr(expr->as.call.callee);
|
||||
free(expr->as.call.paren.value);
|
||||
free(expr);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -109,6 +116,16 @@ int check(token_type_t type)
|
|||
return tokens[current].type == type;
|
||||
}
|
||||
|
||||
int match(token_type_t type)
|
||||
{
|
||||
if (check(type)) {
|
||||
advance();
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
token_t *consume(token_type_t type, char *message)
|
||||
{
|
||||
if (!check(type)) {
|
||||
|
@ -123,21 +140,18 @@ token_t *consume(token_type_t type, char *message)
|
|||
|
||||
expr_t *primary(void)
|
||||
{
|
||||
if (check(TOKEN_FALSE) || check(TOKEN_TRUE) || check(TOKEN_NIL) ||
|
||||
check(TOKEN_NUMBER) || check(TOKEN_STRING)) {
|
||||
token_t *tok = peek();
|
||||
advance();
|
||||
if (match(TOKEN_FALSE) || match(TOKEN_TRUE) || match(TOKEN_NIL) ||
|
||||
match(TOKEN_NUMBER) || match(TOKEN_STRING)) {
|
||||
token_t *tok = previous();
|
||||
return create_literal_expr(tok);
|
||||
}
|
||||
|
||||
if (check(TOKEN_IDENTIFIER)) {
|
||||
token_t *tok = peek();
|
||||
advance();
|
||||
if (match(TOKEN_IDENTIFIER)) {
|
||||
token_t *tok = previous();
|
||||
return create_variable_expr(tok);
|
||||
}
|
||||
|
||||
if (check(TOKEN_LEFT_PAREN)) {
|
||||
advance();
|
||||
if (match(TOKEN_LEFT_PAREN)) {
|
||||
expr_t *expr = expression();
|
||||
consume(TOKEN_RIGHT_PAREN, "Expect ')' after expression.");
|
||||
return create_grouping_expr(expr);
|
||||
|
@ -146,25 +160,67 @@ expr_t *primary(void)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void arg_add(arg_array_t *array, expr_t *expr)
|
||||
{
|
||||
if (array->length == array->capacity) {
|
||||
array->capacity *= 2;
|
||||
array->arguments = realloc(array->arguments, array->capacity * sizeof(expr_t *));
|
||||
}
|
||||
array->arguments[array->length++] = expr;
|
||||
}
|
||||
|
||||
void free_args(arg_array_t *array)
|
||||
{
|
||||
for (int i = 0; i < array->length; i++) {
|
||||
free_expr(array->arguments[i]);
|
||||
}
|
||||
free(array->arguments);
|
||||
free(array);
|
||||
}
|
||||
|
||||
expr_t *call(void)
|
||||
{
|
||||
expr_t *expr = primary();
|
||||
while (1) {
|
||||
if (match(TOKEN_LEFT_PAREN)) {
|
||||
arg_array_t *args = malloc(sizeof(arg_array_t));
|
||||
args->arguments = malloc(DEFAULT_ARGS_SIZE * sizeof(expr_t *));
|
||||
args->length = 0;
|
||||
args->capacity = DEFAULT_ARGS_SIZE;
|
||||
if (!check(TOKEN_RIGHT_PAREN)) {
|
||||
do {
|
||||
if (args->length >= 255) {
|
||||
error(peek(), "Can't have more than 255 arguments.");
|
||||
}
|
||||
arg_add(args, expression());
|
||||
} while (match(TOKEN_COMMA));
|
||||
}
|
||||
token_t *paren = consume(TOKEN_RIGHT_PAREN, "Expect ')' after arguments.");
|
||||
expr = create_call_expr(expr, paren, args);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
expr_t *unary(void)
|
||||
{
|
||||
if (check(TOKEN_BANG) || check(TOKEN_MINUS)) {
|
||||
token_t *operator = peek();
|
||||
advance();
|
||||
if (match(TOKEN_BANG) || match(TOKEN_MINUS)) {
|
||||
token_t *operator = previous();
|
||||
expr_t *right = unary();
|
||||
return create_unary_expr(operator, right);
|
||||
}
|
||||
|
||||
return primary();
|
||||
return call();
|
||||
}
|
||||
|
||||
expr_t *factor(void)
|
||||
{
|
||||
expr_t *expr = unary();
|
||||
|
||||
while (check(TOKEN_SLASH) || check(TOKEN_STAR)) {
|
||||
token_t *operator = peek();
|
||||
advance();
|
||||
while (match(TOKEN_SLASH) || match(TOKEN_STAR)) {
|
||||
token_t *operator = previous();
|
||||
expr_t *right = unary();
|
||||
expr = create_binary_expr(operator, expr, right);
|
||||
}
|
||||
|
@ -176,9 +232,8 @@ expr_t *term(void)
|
|||
{
|
||||
expr_t *expr = factor();
|
||||
|
||||
while (check(TOKEN_MINUS) || check(TOKEN_PLUS)) {
|
||||
token_t *operator = peek();
|
||||
advance();
|
||||
while (match(TOKEN_MINUS) || match(TOKEN_PLUS)) {
|
||||
token_t *operator = previous();
|
||||
expr_t *right = factor();
|
||||
expr = create_binary_expr(operator, expr, right);
|
||||
}
|
||||
|
@ -190,10 +245,9 @@ expr_t *comparison(void)
|
|||
{
|
||||
expr_t *expr = term();
|
||||
|
||||
while (check(TOKEN_GREATER) || check(TOKEN_GREATER_EQUAL) || check(TOKEN_LESS)
|
||||
|| check(TOKEN_LESS_EQUAL)) {
|
||||
token_t *operator = peek();
|
||||
advance();
|
||||
while (match(TOKEN_GREATER) || match(TOKEN_GREATER_EQUAL) || match(TOKEN_LESS)
|
||||
|| match(TOKEN_LESS_EQUAL)) {
|
||||
token_t *operator = previous();
|
||||
expr_t *right = term();
|
||||
expr = create_binary_expr(operator, expr, right);
|
||||
}
|
||||
|
@ -205,9 +259,8 @@ expr_t *equality(void)
|
|||
{
|
||||
expr_t *expr = comparison();
|
||||
|
||||
while (check(TOKEN_BANG_EQUAL) || check(TOKEN_EQUAL_EQUAL)) {
|
||||
token_t *operator = peek();
|
||||
advance();
|
||||
while (match(TOKEN_BANG_EQUAL) || match(TOKEN_EQUAL_EQUAL)) {
|
||||
token_t *operator = previous();
|
||||
expr_t *right = comparison();
|
||||
expr = create_binary_expr(operator, expr, right);
|
||||
}
|
||||
|
@ -219,9 +272,8 @@ expr_t *and(void)
|
|||
{
|
||||
expr_t *expr = equality();
|
||||
|
||||
while (check(TOKEN_AND)) {
|
||||
token_t *operator = peek();
|
||||
advance();
|
||||
while (match(TOKEN_AND)) {
|
||||
token_t *operator = previous();
|
||||
expr_t *right = equality();
|
||||
expr = create_logical_expr(operator, expr, right);
|
||||
}
|
||||
|
@ -233,9 +285,8 @@ expr_t *or(void)
|
|||
{
|
||||
expr_t *expr = and();
|
||||
|
||||
while (check(TOKEN_OR)) {
|
||||
token_t *operator = peek();
|
||||
advance();
|
||||
while (match(TOKEN_OR)) {
|
||||
token_t *operator = previous();
|
||||
expr_t *right = and();
|
||||
expr = create_logical_expr(operator, expr, right);
|
||||
}
|
||||
|
@ -247,9 +298,8 @@ expr_t *assignment(void)
|
|||
{
|
||||
expr_t *expr = or();
|
||||
|
||||
if (check(TOKEN_EQUAL)) {
|
||||
token_t *equals = peek();
|
||||
advance();
|
||||
if (match(TOKEN_EQUAL)) {
|
||||
token_t *equals = previous();
|
||||
expr_t *value = assignment();
|
||||
|
||||
if (expr->type == EXPR_VARIABLE) {
|
||||
|
@ -302,6 +352,11 @@ void free_statement(stmt_t *stmt)
|
|||
free_expr(stmt->as._while.condition);
|
||||
free_statement(stmt->as._while.body);
|
||||
free(stmt);
|
||||
} else if (stmt->type == STMT_FUN) {
|
||||
free(stmt->as.function.name.value);
|
||||
free_array(stmt->as.function.params);
|
||||
free_statement(stmt->as.function.body);
|
||||
free(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -318,10 +373,8 @@ 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();
|
||||
if (match(TOKEN_SEMICOLON)) {
|
||||
} else if (match(TOKEN_VAR)) {
|
||||
initializer = var_declaration();
|
||||
} else {
|
||||
initializer = expression_stmt();
|
||||
|
@ -393,8 +446,7 @@ stmt_t *if_stmt(void)
|
|||
consume(TOKEN_RIGHT_PAREN, "Expect ')' after if condition.");
|
||||
stmt_t *then_branch = statement();
|
||||
stmt_t *else_branch = NULL;
|
||||
if (check(TOKEN_ELSE)) {
|
||||
advance();
|
||||
if (match(TOKEN_ELSE)) {
|
||||
else_branch = statement();
|
||||
}
|
||||
stmt_t *stmt = malloc(sizeof(stmt_t));
|
||||
|
@ -461,36 +513,68 @@ stmt_t *expression_stmt(void)
|
|||
|
||||
stmt_t *statement(void)
|
||||
{
|
||||
if (check(TOKEN_FOR)) {
|
||||
advance();
|
||||
if (match(TOKEN_FOR)) {
|
||||
return for_stmt();
|
||||
}
|
||||
if (check(TOKEN_IF)) {
|
||||
advance();
|
||||
if (match(TOKEN_IF)) {
|
||||
return if_stmt();
|
||||
}
|
||||
if (check(TOKEN_PRINT)) {
|
||||
advance();
|
||||
if (match(TOKEN_PRINT)) {
|
||||
return print_stmt();
|
||||
}
|
||||
if (check(TOKEN_WHILE)) {
|
||||
advance();
|
||||
if (match(TOKEN_WHILE)) {
|
||||
return while_stmt();
|
||||
}
|
||||
if (check(TOKEN_LEFT_BRACE)) {
|
||||
advance();
|
||||
if (match(TOKEN_LEFT_BRACE)) {
|
||||
return block_stmt();
|
||||
}
|
||||
return expression_stmt();
|
||||
}
|
||||
|
||||
stmt_t *function(char *kind)
|
||||
{
|
||||
char err[512];
|
||||
snprintf(err, 512, "Expect %s name.", kind);
|
||||
token_t *name = consume(TOKEN_IDENTIFIER, err);
|
||||
snprintf(err, 512, "Expect '(' after %s name.", kind);
|
||||
consume(TOKEN_LEFT_PAREN, err);
|
||||
array_t *parameters = malloc(sizeof(array_t));
|
||||
parameters->tokens = malloc(DEFAULT_ARGS_SIZE * sizeof(token_t));
|
||||
parameters->length = 0;
|
||||
parameters->capacity = DEFAULT_ARGS_SIZE;
|
||||
|
||||
if (!check(TOKEN_RIGHT_PAREN)) {
|
||||
do {
|
||||
if (parameters->length >= 255) {
|
||||
error(peek(), "Can't have more than 255 parameters.");
|
||||
}
|
||||
|
||||
token_t *param = consume(TOKEN_IDENTIFIER, "Expect parameter name.");
|
||||
token_t param_cpy;
|
||||
memcpy(¶m_cpy, param, sizeof(token_t));
|
||||
param_cpy.value = strdup(param->value);
|
||||
token_add(parameters, param_cpy);
|
||||
} while (match(TOKEN_COMMA));
|
||||
}
|
||||
consume(TOKEN_RIGHT_PAREN, "Expect ')' after parameters.");
|
||||
snprintf(err, 512, "Expect '{' before %s body.", kind);
|
||||
consume(TOKEN_LEFT_BRACE, err);
|
||||
stmt_t *stmt = malloc(sizeof(stmt_t));
|
||||
stmt->type = STMT_FUN;
|
||||
stmt->as.function.name.type = name->type;
|
||||
stmt->as.function.name.value = strdup(name->value);
|
||||
stmt->as.function.name.line = name->line;
|
||||
stmt->as.function.params = parameters;
|
||||
stmt->as.function.body = block_stmt();
|
||||
return stmt;
|
||||
}
|
||||
|
||||
stmt_t *var_declaration(void)
|
||||
{
|
||||
token_t *name = consume(TOKEN_IDENTIFIER, "Expect variable name.");
|
||||
|
||||
expr_t *initializer = NULL;
|
||||
if (check(TOKEN_EQUAL)) {
|
||||
advance();
|
||||
if (match(TOKEN_EQUAL)) {
|
||||
initializer = expression();
|
||||
}
|
||||
|
||||
|
@ -507,8 +591,10 @@ stmt_t *var_declaration(void)
|
|||
|
||||
stmt_t *declaration(void)
|
||||
{
|
||||
if (check(TOKEN_VAR)) {
|
||||
advance();
|
||||
if (match(TOKEN_FUN)) {
|
||||
return function("function");
|
||||
}
|
||||
if (match(TOKEN_VAR)) {
|
||||
return var_declaration();
|
||||
}
|
||||
|
||||
|
@ -522,7 +608,7 @@ stmt_array_t *parse(token_t *tks)
|
|||
return NULL;
|
||||
} else {
|
||||
stmt_array_t *statements = malloc(sizeof(stmt_array_t));
|
||||
statements->statements = malloc(DEFAULT_STMTS_SIZE * sizeof(stmt_t));
|
||||
statements->statements = malloc(DEFAULT_STMTS_SIZE * sizeof(stmt_t *));
|
||||
statements->length = 0;
|
||||
statements->capacity = DEFAULT_STMTS_SIZE;
|
||||
while (!end()) {
|
||||
|
|
7
src/rd.c
7
src/rd.c
|
@ -34,18 +34,15 @@ int main(int argc, char **argv)
|
|||
free_expr(expr);
|
||||
} else if (!strcmp(command, "evaluate")) {
|
||||
expr_t *expr = parse_expr(array->tokens);
|
||||
value_t val = evaluate(expr, NULL);
|
||||
value_t *val = evaluate(expr, NULL);
|
||||
print_value(val);
|
||||
free_array(array);
|
||||
free_expr(expr);
|
||||
} else if (!strcmp(command, "run")) {
|
||||
ht_t *env = ht_init(NULL);
|
||||
stmt_array_t *stmts = parse(array->tokens);
|
||||
if (errno != 65) {
|
||||
evaluate_statements(stmts, env);
|
||||
ht_free(env);
|
||||
interpret(stmts);
|
||||
free_array(array);
|
||||
free_statements(stmts);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Unknown command: %s\n", command);
|
||||
|
|
Loading…
Reference in a new issue