Compare commits
7 commits
dc566b4345
...
56d5d9d84b
Author | SHA1 | Date | |
---|---|---|---|
56d5d9d84b | |||
8f122d3523 | |||
7243d2057c | |||
78c3e8ec39 | |||
4a0f325634 | |||
![]() |
4367ad966d | ||
1d297cf0f0 |
11 changed files with 818 additions and 137 deletions
174
docs/grammar.md
Normal file
174
docs/grammar.md
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
# Grammar for Radish
|
||||||
|
|
||||||
|
> Extremely experimental and subject to completely change
|
||||||
|
|
||||||
|
## Hello World
|
||||||
|
|
||||||
|
```rd
|
||||||
|
// import io
|
||||||
|
const io = #import("io");
|
||||||
|
|
||||||
|
// print hello world
|
||||||
|
pub fn main() -> {
|
||||||
|
io.println(stdout, "hello, world!");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
```rd
|
||||||
|
pub fn main() -> {
|
||||||
|
// default immutable
|
||||||
|
let x = 3;
|
||||||
|
// mut keyword gains mutability
|
||||||
|
let mut y = 4;
|
||||||
|
|
||||||
|
y += x;
|
||||||
|
|
||||||
|
// non-null by default
|
||||||
|
let z? = nil;
|
||||||
|
|
||||||
|
// type can be explicitly denoted with `let <name>: <type>`
|
||||||
|
let score: i32 = 10;
|
||||||
|
|
||||||
|
// strings are by default 0 terminated u8 array
|
||||||
|
let str = "hello world";
|
||||||
|
|
||||||
|
// explicit 0 terminated
|
||||||
|
let greet: u8[:0] = "hello world";
|
||||||
|
|
||||||
|
// explicit 2 terminated
|
||||||
|
let greet2: u8[:2] == "hello again";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## some maths
|
||||||
|
|
||||||
|
```rd
|
||||||
|
fn x3summation(n: i32) i32 -> {
|
||||||
|
return n * (n + 1) * (2 * n - 1) / 3;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```rd
|
||||||
|
// can use single statement instead of block
|
||||||
|
fn abs_deg(deg: f64) f64 -> deg % 360;
|
||||||
|
```
|
||||||
|
|
||||||
|
## if statements
|
||||||
|
|
||||||
|
```rd
|
||||||
|
fn greet(is_human: bool) -> {
|
||||||
|
if is_human {
|
||||||
|
io.println(stdout, "Hello, human!");
|
||||||
|
} else {
|
||||||
|
io.println(stdout, "Hello, robot!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
fn greet_more(is_human: bool) -> {
|
||||||
|
// if statements can return values
|
||||||
|
let target = if is_human "human" else "robot";
|
||||||
|
io.println(stdout, "Hello, {s}!", target);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Loops
|
||||||
|
|
||||||
|
```
|
||||||
|
fn game_loop() -> {
|
||||||
|
loop {
|
||||||
|
game_logic();
|
||||||
|
|
||||||
|
if need_exit() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
fn number_print() -> {
|
||||||
|
for i in 0..=10 {
|
||||||
|
if i == 3 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prints 0 to 10 inclusive
|
||||||
|
io.println(stdout, "{}", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Union return
|
||||||
|
|
||||||
|
```
|
||||||
|
fn read_input() u8[:0] | IOErr -> {
|
||||||
|
// takes input from stdin until newline or eof, no newline included
|
||||||
|
return io.scan_line(stdin, 0);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Switch case
|
||||||
|
|
||||||
|
```
|
||||||
|
fn number(n :i32) u8[:0] -> {
|
||||||
|
return match n {
|
||||||
|
0 -> "zero";
|
||||||
|
1 -> "one";
|
||||||
|
2..=9 -> "single digits";
|
||||||
|
n >= 10 -> "big numbers";
|
||||||
|
_ -> "negative numbers";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pattern matching
|
||||||
|
|
||||||
|
```rd
|
||||||
|
struct User {
|
||||||
|
name: u8[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn username() User | CreationFailed -> {
|
||||||
|
// pattern match to return username or failed
|
||||||
|
return match io.scan_line(stdin, 0) {
|
||||||
|
u8[] line -> User{name: line};
|
||||||
|
IOErr -> CreationFailed{err.st()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Type Alias
|
||||||
|
|
||||||
|
```rd
|
||||||
|
type Success = i32;
|
||||||
|
type Failure = IOErr | ParseErr | UserNotFoundErr;
|
||||||
|
type Signal = Success | Failure;
|
||||||
|
|
||||||
|
fn get_a_signal() Signal -> {
|
||||||
|
// do a lot of stuff
|
||||||
|
// open files
|
||||||
|
if cannot_open return IOErr{err.st()};
|
||||||
|
|
||||||
|
// parse data
|
||||||
|
if failed_parsing return ParseErr{err.st()};
|
||||||
|
|
||||||
|
// get user
|
||||||
|
if cannot_find_user return UserNotFoundErr{err.st()};
|
||||||
|
|
||||||
|
// integer data found
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signal_handler(signal: Signal) -> {
|
||||||
|
match signal {
|
||||||
|
Success data -> io.println(stdout, "{}", data);
|
||||||
|
UserNotFoundErr -> io.println(stdout, "user not found");
|
||||||
|
_ -> io.println(stdout, "internal error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -48,7 +48,7 @@ typedef struct expr_t {
|
||||||
int line;
|
int line;
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
token_t name;
|
struct expr_t *name;
|
||||||
struct expr_t *value;
|
struct expr_t *value;
|
||||||
} assign;
|
} assign;
|
||||||
struct {
|
struct {
|
||||||
|
@ -103,6 +103,8 @@ 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(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);
|
void print_ast(expr_t *expr);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
22
include/env.h
Normal file
22
include/env.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef ENV_H
|
||||||
|
#define ENV_H
|
||||||
|
|
||||||
|
#include "ast.h"
|
||||||
|
#include "lexer.h"
|
||||||
|
|
||||||
|
typedef struct ht_t {
|
||||||
|
char *name;
|
||||||
|
value_t value;
|
||||||
|
struct ht_t *enclosing;
|
||||||
|
} 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);
|
||||||
|
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_free(ht_t *ht);
|
||||||
|
|
||||||
|
#endif
|
|
@ -2,10 +2,12 @@
|
||||||
#define INTERPRETER_H
|
#define INTERPRETER_H
|
||||||
|
|
||||||
#include "ast.h"
|
#include "ast.h"
|
||||||
|
#include "env.h"
|
||||||
#include "stmt.h"
|
#include "stmt.h"
|
||||||
|
|
||||||
value_t evaluate(expr_t *expr);
|
void runtime_error(const char *message, int line);
|
||||||
void print_value(value_t *value);
|
value_t evaluate(expr_t *expr, ht_t *env);
|
||||||
void print_statements(stmt_array_t *array);
|
void print_value(value_t value);
|
||||||
|
void evaluate_statements(stmt_array_t *array, ht_t *env);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -6,6 +6,13 @@
|
||||||
|
|
||||||
#define DEFAULT_STMTS_SIZE 512
|
#define DEFAULT_STMTS_SIZE 512
|
||||||
|
|
||||||
|
/*
|
||||||
|
statement → exprStmt | ifStmt | printStmt | block ;
|
||||||
|
ifStmt → "if" "(" expression ")" statement
|
||||||
|
( "else" statement )? ;
|
||||||
|
block → "{" declaration* "}" ;
|
||||||
|
*/
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
STMT_BLOCK,
|
STMT_BLOCK,
|
||||||
STMT_CLASS,
|
STMT_CLASS,
|
||||||
|
@ -17,11 +24,19 @@ typedef enum {
|
||||||
STMT_WHILE,
|
STMT_WHILE,
|
||||||
} stmt_type_t;
|
} stmt_type_t;
|
||||||
|
|
||||||
typedef struct stmt_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;
|
stmt_type_t type;
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
struct stmt_t **statements;
|
stmt_array_t *statements;
|
||||||
} block;
|
} block;
|
||||||
struct {
|
struct {
|
||||||
token_t name;
|
token_t name;
|
||||||
|
@ -38,8 +53,8 @@ typedef struct stmt_t {
|
||||||
} function;
|
} function;
|
||||||
struct {
|
struct {
|
||||||
expr_t *condition;
|
expr_t *condition;
|
||||||
struct stmt_t *thenBranch;
|
struct stmt_t *then_branch;
|
||||||
struct stmt_t *elseBranch;
|
struct stmt_t *else_branch;
|
||||||
} _if;
|
} _if;
|
||||||
struct {
|
struct {
|
||||||
expr_t *expression;
|
expr_t *expression;
|
||||||
|
@ -57,12 +72,6 @@ typedef struct stmt_t {
|
||||||
struct stmt_t *body;
|
struct stmt_t *body;
|
||||||
} _while;
|
} _while;
|
||||||
} as;
|
} as;
|
||||||
} stmt_t;
|
};
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
struct stmt_t *statements;
|
|
||||||
int length;
|
|
||||||
int capacity;
|
|
||||||
} stmt_array_t;
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
36
src/ast.c
36
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.left = left;
|
||||||
expr->as.binary.right = right;
|
expr->as.binary.right = right;
|
||||||
expr->as.binary.operator.type = operator->type;
|
expr->as.binary.operator.type = operator->type;
|
||||||
char *bin_op_val = strdup(operator->value);
|
expr->as.binary.operator.value = strdup(operator->value);
|
||||||
expr->as.binary.operator.value = bin_op_val;
|
|
||||||
expr->as.binary.operator.line = operator->line;
|
expr->as.binary.operator.line = operator->line;
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
@ -25,8 +24,7 @@ expr_t *create_unary_expr(token_t *operator, expr_t *right)
|
||||||
expr->type = EXPR_UNARY;
|
expr->type = EXPR_UNARY;
|
||||||
expr->line = operator->line;
|
expr->line = operator->line;
|
||||||
expr->as.unary.operator.type = operator->type;
|
expr->as.unary.operator.type = operator->type;
|
||||||
char *u_op_val = strdup(operator->value);
|
expr->as.unary.operator.value = strdup(operator->value);
|
||||||
expr->as.unary.operator.value = u_op_val;
|
|
||||||
expr->as.unary.operator.line = operator->line;
|
expr->as.unary.operator.line = operator->line;
|
||||||
expr->as.unary.right = right;
|
expr->as.unary.right = right;
|
||||||
return expr;
|
return expr;
|
||||||
|
@ -58,8 +56,7 @@ expr_t *create_literal_expr(token_t *token)
|
||||||
|
|
||||||
case TOKEN_STRING:
|
case TOKEN_STRING:
|
||||||
expr->as.literal.value.type = VAL_STRING;
|
expr->as.literal.value.type = VAL_STRING;
|
||||||
char *tkvalue = strdup(token->value);
|
expr->as.literal.value.as.string = strdup(token->value);
|
||||||
expr->as.literal.value.as.string = tkvalue;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -86,13 +83,36 @@ expr_t *create_variable_expr(token_t *name)
|
||||||
expr->type = EXPR_VARIABLE;
|
expr->type = EXPR_VARIABLE;
|
||||||
expr->line = name->line;
|
expr->line = name->line;
|
||||||
expr->as.variable.name.type = name->type;
|
expr->as.variable.name.type = name->type;
|
||||||
char *name_val = strdup(name->value);
|
expr->as.variable.name.value = strdup(name->value);
|
||||||
expr->as.variable.name.value = name_val;
|
|
||||||
expr->as.variable.name.line = name->line;
|
expr->as.variable.name.line = name->line;
|
||||||
|
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expr_t *create_assign_expr(expr_t *name, expr_t *value)
|
||||||
|
{
|
||||||
|
expr_t *expr = malloc(sizeof(expr_t));
|
||||||
|
expr->type = EXPR_ASSIGN;
|
||||||
|
expr->line = name->as.variable.name.line;
|
||||||
|
expr->as.assign.name = name;
|
||||||
|
expr->as.assign.value = 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)
|
void print_ast(expr_t *expr)
|
||||||
{
|
{
|
||||||
if (!expr)
|
if (!expr)
|
||||||
|
|
127
src/env.c
Normal file
127
src/env.c
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "env.h"
|
||||||
|
#include "interpreter.h"
|
||||||
|
|
||||||
|
ht_t *ht_init(ht_t *env)
|
||||||
|
{
|
||||||
|
ht_t *ht = malloc(sizeof(ht_t) * DEFAULT_HT_SIZE);
|
||||||
|
for (int i = 0; i < DEFAULT_HT_SIZE; i++) {
|
||||||
|
ht[i].value.type = VAL_NIL;
|
||||||
|
ht[i].name = NULL;
|
||||||
|
}
|
||||||
|
if (env) {
|
||||||
|
ht->enclosing = env;
|
||||||
|
} else {
|
||||||
|
ht->enclosing = 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 = strdup(name);
|
||||||
|
ht[probe_idx].value.type = value.type;
|
||||||
|
if (value.type == VAL_STRING) {
|
||||||
|
ht[probe_idx].value.as.string = strdup(value.as.string);
|
||||||
|
} else {
|
||||||
|
ht[probe_idx].value.as = value.as;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
ht_replace(ht, name, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value_t *ht_get(ht_t *ht, token_t *name, int check_enclosing)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
if (check_enclosing) {
|
||||||
|
if (ht->enclosing) {
|
||||||
|
return ht_get(ht->enclosing, name, 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
ht_replace(ht->enclosing, name, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!strcmp(ht[probe_idx].name, name)) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ht_assign(ht_t *ht, token_t *name, value_t value)
|
||||||
|
{
|
||||||
|
if (ht_get(ht, name, 0)) {
|
||||||
|
ht_replace(ht, name->value, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ht->enclosing) {
|
||||||
|
ht_assign(ht->enclosing, name, 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);
|
||||||
|
if (ht[i].value.type == VAL_STRING) {
|
||||||
|
free(ht[i].value.as.string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(ht);
|
||||||
|
}
|
||||||
|
|
|
@ -4,22 +4,19 @@
|
||||||
#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"
|
||||||
|
#include "parser.h"
|
||||||
value_t visit_literal(expr_t *expr);
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
return expr->as.literal.value;
|
return expr->as.literal.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t visit_grouping(expr_t *expr)
|
value_t visit_grouping(expr_t *expr, ht_t *env)
|
||||||
{
|
{
|
||||||
return evaluate(expr->as.grouping.expression);
|
return evaluate(expr->as.grouping.expression, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
void runtime_error(const char *message, int line)
|
void runtime_error(const char *message, int line)
|
||||||
|
@ -29,11 +26,11 @@ void runtime_error(const char *message, int line)
|
||||||
exit(70);
|
exit(70);
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t visit_binary(expr_t *expr)
|
value_t visit_binary(expr_t *expr, ht_t *env)
|
||||||
{
|
{
|
||||||
token_type_t op_type = expr->as.binary.operator.type;
|
token_type_t op_type = expr->as.binary.operator.type;
|
||||||
value_t right = evaluate(expr->as.binary.right);
|
value_t right = evaluate(expr->as.binary.right, env);
|
||||||
value_t left = evaluate(expr->as.binary.left);
|
value_t left = evaluate(expr->as.binary.left, env);
|
||||||
|
|
||||||
// Arithmetic
|
// Arithmetic
|
||||||
if (left.type == VAL_NUMBER && right.type == VAL_NUMBER) {
|
if (left.type == VAL_NUMBER && right.type == VAL_NUMBER) {
|
||||||
|
@ -133,10 +130,9 @@ value_t visit_binary(expr_t *expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// String/number comparisons
|
// String/number comparisons
|
||||||
if (left.type == VAL_STRING && right.type == VAL_NUMBER ||
|
if ((left.type == VAL_STRING && right.type == VAL_NUMBER) ||
|
||||||
left.type == VAL_NUMBER && right.type == VAL_STRING ) {
|
(left.type == VAL_NUMBER && right.type == VAL_STRING)) {
|
||||||
runtime_error("Operands must be numbers.", expr->line);
|
runtime_error("Operands must be numbers.", expr->line);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime_error("Operands must be two numbers or two strings.", expr->line);
|
runtime_error("Operands must be two numbers or two strings.", expr->line);
|
||||||
|
@ -144,17 +140,17 @@ value_t visit_binary(expr_t *expr)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int is_truthy(value_t *value)
|
int is_truthy(value_t value)
|
||||||
{
|
{
|
||||||
switch (value->type) {
|
switch (value.type) {
|
||||||
case VAL_NIL:
|
case VAL_NIL:
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case VAL_BOOL:
|
case VAL_BOOL:
|
||||||
return value->as.boolean;
|
return value.as.boolean;
|
||||||
|
|
||||||
case VAL_NUMBER:
|
case VAL_NUMBER:
|
||||||
if (value->as.number == 0)
|
if (value.as.number == 0)
|
||||||
return 0;
|
return 0;
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
@ -166,9 +162,9 @@ int is_truthy(value_t *value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t visit_unary(expr_t *expr)
|
value_t visit_unary(expr_t *expr, ht_t *env)
|
||||||
{
|
{
|
||||||
value_t operand = evaluate(expr->as.unary.right);
|
value_t operand = evaluate(expr->as.unary.right, env);
|
||||||
|
|
||||||
if (expr->as.unary.operator.type == TOKEN_MINUS) {
|
if (expr->as.unary.operator.type == TOKEN_MINUS) {
|
||||||
if (operand.type == VAL_NUMBER) {
|
if (operand.type == VAL_NUMBER) {
|
||||||
|
@ -178,14 +174,46 @@ value_t visit_unary(expr_t *expr)
|
||||||
runtime_error("Operand must be a number.", expr->line);
|
runtime_error("Operand must be a number.", expr->line);
|
||||||
}
|
}
|
||||||
} else if (expr->as.unary.operator.type == TOKEN_BANG) {
|
} 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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (value_t){.type = VAL_NIL};
|
return (value_t){.type = VAL_NIL};
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t evaluate(expr_t *expr)
|
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;
|
||||||
|
} else {
|
||||||
|
return (value_t) {.type = VAL_NIL};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value_t visit_assign(expr_t *expr, ht_t *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 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) {
|
if (!expr) {
|
||||||
value_t nil_value = {.type = VAL_NIL };
|
value_t nil_value = {.type = VAL_NIL };
|
||||||
|
@ -195,22 +223,28 @@ value_t evaluate(expr_t *expr)
|
||||||
case EXPR_LITERAL:
|
case EXPR_LITERAL:
|
||||||
return visit_literal(expr);
|
return visit_literal(expr);
|
||||||
case EXPR_BINARY:
|
case EXPR_BINARY:
|
||||||
return visit_binary(expr);
|
return visit_binary(expr, env);
|
||||||
case EXPR_UNARY:
|
case EXPR_UNARY:
|
||||||
return visit_unary(expr);
|
return visit_unary(expr, env);
|
||||||
case EXPR_GROUPING:
|
case EXPR_GROUPING:
|
||||||
return visit_grouping(expr);
|
return visit_grouping(expr, env);
|
||||||
|
case EXPR_VARIABLE:
|
||||||
|
return visit_variable(expr, env);
|
||||||
|
case EXPR_ASSIGN:
|
||||||
|
return visit_assign(expr, env);
|
||||||
|
case EXPR_LOGICAL:
|
||||||
|
return visit_logical(expr, env);
|
||||||
default:
|
default:
|
||||||
exit(65);
|
exit(65);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_value(value_t *value)
|
void print_value(value_t value)
|
||||||
{
|
{
|
||||||
switch (value->type) {
|
switch (value.type) {
|
||||||
case VAL_BOOL:
|
case VAL_BOOL:
|
||||||
printf("%s\n", value->as.boolean == 1 ? "true" : "false");
|
printf("%s\n", value.as.boolean == 1 ? "true" : "false");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VAL_NIL:
|
case VAL_NIL:
|
||||||
|
@ -218,14 +252,14 @@ void print_value(value_t *value)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VAL_STRING:
|
case VAL_STRING:
|
||||||
printf("%s\n", value->as.string);
|
printf("%s\n", value.as.string);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VAL_NUMBER:
|
case VAL_NUMBER:
|
||||||
if (value->as.number == (int) value->as.number) {
|
if (value.as.number == (int) value.as.number) {
|
||||||
printf("%d\n", (int) value->as.number);
|
printf("%d\n", (int) value.as.number);
|
||||||
} else {
|
} else {
|
||||||
printf("%g\n", value->as.number);
|
printf("%g\n", value.as.number);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -234,18 +268,61 @@ void print_value(value_t *value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_statement(stmt_t stmt)
|
void evaluate_block(stmt_array_t *array, ht_t *cur_env, ht_t *scope_env)
|
||||||
{
|
{
|
||||||
if (stmt.type == STMT_PRINT) {
|
ht_t *previous = cur_env;
|
||||||
value_t obj = evaluate(stmt.as.print.expression);
|
cur_env = scope_env;
|
||||||
print_value(&obj);
|
evaluate_statements(array, cur_env);
|
||||||
} else if (stmt.type == STMT_EXPR) {
|
ht_free(scope_env);
|
||||||
evaluate(stmt.as.expr.expression);
|
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);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void print_statements(stmt_array_t *array)
|
|
||||||
|
void evaluate_statements(stmt_array_t *array, ht_t *env)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < array->length; i++) {
|
for (int i = 0; i < array->length; i++) {
|
||||||
print_statement(array->statements[i]);
|
evaluate_statement(array->statements[i], env);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -263,13 +263,7 @@ array_t *tokenize(char *filename)
|
||||||
int found = 0;
|
int found = 0;
|
||||||
for (int i = 0; i < sizeof(reserved_keywords) / sizeof(reserved_keywords[0]); i++) {
|
for (int i = 0; i < sizeof(reserved_keywords) / sizeof(reserved_keywords[0]); i++) {
|
||||||
if (strcmp(id, reserved_keywords[i].keyword) == 0) {
|
if (strcmp(id, reserved_keywords[i].keyword) == 0) {
|
||||||
char upper_id[len + 1];
|
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
upper_id[i] = toupper(id[i]);
|
|
||||||
}
|
|
||||||
upper_id[len] = 0;
|
|
||||||
token_add(tokens, token_gen(reserved_keywords[i].token_type, id, line));
|
token_add(tokens, token_gen(reserved_keywords[i].token_type, id, line));
|
||||||
|
|
||||||
found = 1;
|
found = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
384
src/parser.c
384
src/parser.c
|
@ -1,5 +1,6 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include "ast.h"
|
#include "ast.h"
|
||||||
|
@ -9,8 +10,15 @@
|
||||||
int current = 0;
|
int current = 0;
|
||||||
token_t *tokens;
|
token_t *tokens;
|
||||||
expr_t *expression(void);
|
expr_t *expression(void);
|
||||||
|
stmt_t *expression_stmt(void);
|
||||||
|
stmt_t *statement(void);
|
||||||
|
stmt_t *var_declaration(void);
|
||||||
|
stmt_t *declaration(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 +59,24 @@ 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(expr->as.assign.name);
|
||||||
|
free_expr(expr->as.assign.value);
|
||||||
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -80,19 +106,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 +125,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 +149,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 +163,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 +177,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 +192,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 +206,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,88 +215,306 @@ expr_t *equality(void)
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
expr_t *expression(void)
|
expr_t *and(void)
|
||||||
{
|
{
|
||||||
return equality();
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt_t print_stmt(void)
|
expr_t *or(void)
|
||||||
{
|
{
|
||||||
expr_t *value = expression();
|
expr_t *expr = and();
|
||||||
consume(TOKEN_SEMICOLON, "Expect ; after value.");
|
|
||||||
return (stmt_t) {
|
while (check(TOKEN_OR)) {
|
||||||
.type = STMT_PRINT,
|
token_t *operator = peek();
|
||||||
.as.print.expression = value,
|
advance();
|
||||||
};
|
expr_t *right = and();
|
||||||
|
expr = create_logical_expr(operator, expr, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt_t expression_stmt(void)
|
expr_t *assignment(void)
|
||||||
{
|
{
|
||||||
expr_t *expr = expression();
|
expr_t *expr = or();
|
||||||
consume(TOKEN_SEMICOLON, "Expect ; after expression.");
|
|
||||||
return (stmt_t) {
|
|
||||||
.type = STMT_EXPR,
|
|
||||||
.as.expr.expression = expr,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt_t statement(void)
|
|
||||||
{
|
|
||||||
if (check(TOKEN_PRINT))
|
|
||||||
return print_stmt();
|
|
||||||
return expression_stmt();
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt_t var_declaration(void)
|
|
||||||
{
|
|
||||||
token_t *name = consume(TOKEN_IDENTIFIER, "Expect variable name.");
|
|
||||||
|
|
||||||
expr_t *initializer;
|
|
||||||
if (check(TOKEN_EQUAL)) {
|
if (check(TOKEN_EQUAL)) {
|
||||||
initializer = expression();
|
token_t *equals = peek();
|
||||||
|
advance();
|
||||||
|
expr_t *value = assignment();
|
||||||
|
|
||||||
|
if (expr->type == EXPR_VARIABLE) {
|
||||||
|
return create_assign_expr(expr, value);
|
||||||
|
}
|
||||||
|
error(equals, "Invalid assignment target.");
|
||||||
}
|
}
|
||||||
|
|
||||||
consume(TOKEN_SEMICOLON, "Expect ';' after variable declaration.");
|
return expr;
|
||||||
return (stmt_t) {
|
|
||||||
.type = STMT_VAR,
|
|
||||||
.as.variable.name.type = name->type,
|
|
||||||
.as.variable.name.value = name->value,
|
|
||||||
.as.variable.name.line = name->line,
|
|
||||||
.as.variable.initializer = initializer,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt_t declaration(void)
|
expr_t *expression(void)
|
||||||
{
|
{
|
||||||
if (check(TOKEN_VAR))
|
return assignment();
|
||||||
return var_declaration();
|
|
||||||
|
|
||||||
return statement();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
if (array->length == array->capacity) {
|
||||||
array->capacity *= 2;
|
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;
|
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)
|
void free_statements(stmt_array_t *array)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < array->length; i++) {
|
for (int i = 0; i < array->length; i++) {
|
||||||
if (array->statements[i].type == STMT_PRINT) {
|
free_statement(array->statements[i]);
|
||||||
free_expr(array->statements[i].as.print.expression);
|
|
||||||
}
|
|
||||||
if (array->statements[i].type == STMT_EXPR) {
|
|
||||||
free_expr(array->statements[i].as.expr.expression);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
free(array->statements);
|
free(array->statements);
|
||||||
free(array);
|
free(array);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.");
|
||||||
|
stmt_t *stmt = malloc(sizeof(stmt_t));
|
||||||
|
stmt->type = STMT_PRINT;
|
||||||
|
stmt->as.print.expression = value;
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
statements->length = 0;
|
||||||
|
statements->capacity = DEFAULT_STMTS_SIZE;
|
||||||
|
|
||||||
|
|
||||||
|
while (!check(TOKEN_RIGHT_BRACE) && !end()) {
|
||||||
|
stmt_add(statements, declaration());
|
||||||
|
}
|
||||||
|
|
||||||
|
consume(TOKEN_RIGHT_BRACE, "Expect '}' after block.");
|
||||||
|
|
||||||
|
stmt_t *stmt = malloc(sizeof(stmt_t));
|
||||||
|
stmt->type = STMT_BLOCK;
|
||||||
|
stmt->as.block.statements = statements;
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt_t *expression_stmt(void)
|
||||||
|
{
|
||||||
|
expr_t *expr = expression();
|
||||||
|
consume(TOKEN_SEMICOLON, "Expect ; after expression.");
|
||||||
|
stmt_t *stmt = malloc(sizeof(stmt_t));
|
||||||
|
stmt->type = STMT_EXPR;
|
||||||
|
stmt->as.expr.expression = expr;
|
||||||
|
return stmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
return expression_stmt();
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt_t *var_declaration(void)
|
||||||
|
{
|
||||||
|
token_t *name = consume(TOKEN_IDENTIFIER, "Expect variable name.");
|
||||||
|
|
||||||
|
expr_t *initializer = NULL;
|
||||||
|
if (check(TOKEN_EQUAL)) {
|
||||||
|
advance();
|
||||||
|
initializer = expression();
|
||||||
|
}
|
||||||
|
|
||||||
|
consume(TOKEN_SEMICOLON, "Expect ';' after variable declaration.");
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (check(TOKEN_VAR)) {
|
||||||
|
advance();
|
||||||
|
return var_declaration();
|
||||||
|
}
|
||||||
|
|
||||||
|
return statement();
|
||||||
|
}
|
||||||
|
|
||||||
stmt_array_t *parse(token_t *tks)
|
stmt_array_t *parse(token_t *tks)
|
||||||
{
|
{
|
||||||
tokens = tks;
|
tokens = tks;
|
||||||
|
|
8
src/rd.c
8
src/rd.c
|
@ -34,14 +34,16 @@ int main(int argc, char **argv)
|
||||||
free_expr(expr);
|
free_expr(expr);
|
||||||
} else if (!strcmp(command, "evaluate")) {
|
} else if (!strcmp(command, "evaluate")) {
|
||||||
expr_t *expr = parse_expr(array->tokens);
|
expr_t *expr = parse_expr(array->tokens);
|
||||||
value_t val = evaluate(expr);
|
value_t val = evaluate(expr, NULL);
|
||||||
print_value(&val);
|
print_value(val);
|
||||||
free_array(array);
|
free_array(array);
|
||||||
free_expr(expr);
|
free_expr(expr);
|
||||||
} else if (!strcmp(command, "run")) {
|
} else if (!strcmp(command, "run")) {
|
||||||
|
ht_t *env = ht_init(NULL);
|
||||||
stmt_array_t *stmts = parse(array->tokens);
|
stmt_array_t *stmts = parse(array->tokens);
|
||||||
if (errno != 65) {
|
if (errno != 65) {
|
||||||
print_statements(stmts);
|
evaluate_statements(stmts, env);
|
||||||
|
ht_free(env);
|
||||||
free_array(array);
|
free_array(array);
|
||||||
free_statements(stmts);
|
free_statements(stmts);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue