More progress on assembler and evaluator

This commit is contained in:
2025-08-03 00:27:52 +02:00
parent 234087422f
commit 51ea776e55
7 changed files with 814 additions and 118 deletions
+5 -8
View File
@@ -1,6 +1,4 @@
func(1, 3, 5);
title = "My Website";
posts = [
@@ -27,22 +25,21 @@ fun render_comment(c)
<a>$c.author; ($c.date;)</a>
<p>$c.content;</p>
<div class="comment-children">
$for child in c.children:
$ for child in c.children:
render_comment(child);
</div>
</div>
<html>
export <html>
<head>
<title>$(title);</title>
<title>$title;</title>
</head>
<body>
$for post in posts: {
$ for post in posts: {
<div class="post">
<a>$post.title; ($post.date;)</a>
$for comment in post.comments: {
$ for comment in post.comments:
render_comment(comment);
}
</div>
}
</body>
+406 -86
View File
@@ -2,6 +2,19 @@
#include "assemble.h"
#define MAX_SCOPES 32
#define MAX_VARS 1024
#define MAX_STRING_LITERALS 1024
typedef struct FunctionCall FunctionCall;
struct FunctionCall {
FunctionCall *next;
String name;
int off;
};
typedef struct {
String name;
} Variable;
typedef enum {
SCOPE_FUNC,
@@ -13,14 +26,122 @@ typedef enum {
typedef struct {
ScopeType type;
int var_base;
// TODO
FunctionCall *calls;
} Scope;
typedef struct {
int depth;
char *ptr;
int len;
int cap;
b32 err;
} OutputBuffer;
typedef struct {
Arena *a;
OutputBuffer out;
int num_vars;
Variable vars[MAX_VARS];
int num_scopes;
Scope scopes[MAX_SCOPES];
int strings_len;
int strings_cap;
char *strings;
} Assembler;
int add_string_literal(Assembler *a, String str)
{
if (a->strings_cap - a->strings_len < str.len) {
int c = MAX(2 * a->strings_cap, a->strings_len + str.len);
char *p = malloc(c);
if (p == NULL) {
// TODO
}
if (a->strings_cap) {
memcpy(p, a->strings, a->strings_len);
free(a->strings);
}
a->strings = p;
a->strings_cap = c;
}
int off = a->strings_len;
memcpy(a->strings + a->strings_len, str.ptr, str.len);
a->strings_len += str.len;
return off;
}
void append_mem(OutputBuffer *out, void *ptr, int len)
{
if (out->err)
return;
if (out->cap - out->len < len) {
int new_cap = MAX(512, 2 * out->cap);
char *new_ptr = malloc(new_cap);
if (new_ptr == NULL) {
out->err = true;
return;
}
if (out->cap) {
memcpy(new_ptr, out->ptr, out->len);
free(out->ptr);
}
out->ptr = new_ptr;
out->cap = new_cap;
}
memcpy(out->ptr + out->len, ptr, len);
out->len += len;
}
void patch_mem(OutputBuffer *out, int off, void *ptr, int len)
{
if (out->err)
return;
memcpy(out->ptr + off, ptr, len);
}
int append_u8(OutputBuffer *out, uint8_t x)
{
int off = out->len;
append_mem(out, &x, (int) sizeof(x));
return off;
}
int append_u32(OutputBuffer *out, uint32_t x)
{
int off = out->len;
append_mem(out, &x, (int) sizeof(x));
return off;
}
int append_f64(OutputBuffer *out, double x)
{
int off = out->len;
append_mem(out, &x, (int) sizeof(x));
return off;
}
void patch_with_current_offset(OutputBuffer *out, int off)
{
uint32_t x = out->len;
patch_mem(out, off, &x, (int) sizeof(x));
}
int count_nodes(Node *head)
{
int n = 0;
@@ -32,41 +153,85 @@ int count_nodes(Node *head)
return n;
}
void assemble_node(Node *node)
int declare_var(Assembler *a, String name)
{
if (a->num_vars == MAX_VARS)
return -1;
// TODO: check if the variable exists already
int idx = a->num_vars++;
a->vars[idx] = (Variable) { name };
return idx - a->scopes[a->num_scopes-1].var_base;
}
void assemble_html_node_inner(Assembler *a, Node *node, OutputBuffer *tmp)
{
append_u8(tmp, '<');
append_mem(tmp, node->tagname.ptr, node->tagname.len);
Node *attr = node->params;
while (attr) {
append_u8(tmp, ' ');
append_mem(tmp, attr->attr_name.ptr, attr->attr_name.len);
if (attr->attr_value) {
append_u8(tmp, '=');
// TODO
}
attr = attr->next;
}
append_u8(tmp, '>');
Node *child = node->child;
while (child) {
// TODO
child = child->next;
}
append_u8(tmp, '<');
append_u8(tmp, '/');
append_mem(tmp, node->tagname.ptr, node->tagname.len);
append_u8(tmp, '>');
}
void assemble_html_node(Assembler *a, Node *node)
{
OutputBuffer tmp;
assemble_html_node_inner(a, node, &tmp);
}
void assemble_node(Assembler *a, Node *node)
{
switch (node->type) {
case NODE_FUNC_DECL:
{
append_u8 (out, OPCODE_JUMP);
append_u64(out, xxx);
append_u8(&a->out, OPCODE_JUMP);
int p1 = append_u32(&a->out, 0);
// TODO: resolve references
int arg_count = count_nodes(node->func_args);
Node *preallocated[32];
Node *args = preallocated;
if (arg_count > COUNT(preallocated)) {
args = malloc(arg_count * sizeof(Node*));
if (args == NULL) {
Node *args[32];
if (arg_count > COUNT(args)) {
// TODO
}
}
for (int i = arg_count-1; i >= 0; i--) {
append_u8 (out, OPCODE_ASS);
append_u64(out, xxx);
int idx = declare_var(a, args[i]->sval);
if (idx < 0) {
// TODO
}
append_u8(&a->out, OPCODE_SETV);
append_u8(&a->out, idx);
}
if (is_expr(node->func_body)) {
assemble_node(node->func_body);
append_u8(out, OPCODE_RET);
assemble_node(a, node->func_body);
append_u8(&a->out, OPCODE_RET);
} else
assemble_node(node->func_body);
assemble_node(a, node->func_body);
append_u8(out, OPCODE_NOPE);
if (args != preallocated)
free(args);
patch_with_current_offset(&a->out, p1);
}
break;
@@ -78,19 +243,50 @@ void assemble_node(Node *node)
int arg_count = 0;
Node *arg = args;
while (arg) {
assemble_node(arg);
assemble_node(a, arg);
arg_count++;
arg = arg->next;
}
assemble_node(arg);
append_u8(OPCODE_CALL);
append_u32(arg_count);
if (func->type == NODE_VALUE_VAR) {
assemble_node(a, arg);
append_u8(&a->out, OPCODE_SCALL);
append_u8(&a->out, arg_count);
int p = append_u32(&a->out, 0);
FunctionCall *call = alloc(a->a, sizeof(FunctionCall), _Alignof(FunctionCall));
if (call == NULL) {
// TODO
}
call->name = func->sval;
call->off = p;
Scope *scope = &a->scopes[a->num_scopes-1];
call->next = scope->calls;
scope->calls = call;
} else {
assemble_node(a, func);
assemble_node(a, arg);
append_u8(&a->out, OPCODE_DCALL);
append_u8(&a->out, arg_count);
}
}
break;
case NODE_VAR_DECL:
{
int idx = declare_var(a, node->var_name);
if (idx < 0) {
// TODO
}
if (node->var_value) {
assemble_node(a, node->var_value);
append_u8(&a->out, OPCODE_SETV);
append_u8(&a->out, idx);
}
}
break;
@@ -98,9 +294,9 @@ void assemble_node(Node *node)
{
Node *stmt = node->child;
while (stmt) {
assemble_node(stmt);
assemble_node(a, stmt);
if (is_expr(stmt))
append_u8(out, OPCODE_POP);
append_u8(&a->out, OPCODE_POP);
stmt = stmt->next;
}
}
@@ -108,134 +304,258 @@ void assemble_node(Node *node)
case NODE_IFELSE:
{
assemble_node(node->cond);
// If there is no else branch:
//
// <cond>
// JIFP end
// <left>
// end:
// ...
//
// If there is:
//
// <cond>
// JIFP else
// <left>
// JUMP end
// else:
// <right>
// end:
// ...
append_u8 (out, OPCODE_JIFP);
append_u32(out, xxx);
if (node->right) {
assemble_node(node->left);
assemble_node(a, node->cond);
if (node->right)
assemble_node(node->right);
append_u8(&a->out, OPCODE_JIFP);
int p1 = append_u32(&a->out, 0);
assemble_node(a, node->left);
append_u8(&a->out, OPCODE_JUMP);
int p2 = append_u32(&a->out, 0);
patch_with_current_offset(&a->out, p1);
assemble_node(a, node->right);
patch_with_current_offset(&a->out, p2);
} else {
assemble_node(a, node->cond);
append_u8(&a->out, OPCODE_JIFP);
int p1 = append_u32(&a->out, 0);
assemble_node(a, node->left);
patch_with_current_offset(&a->out, p1);
}
}
break;
case NODE_WHILE:
{
// start:
// <cond>
// JIFP end
// <body>
// JUMP start
// end:
// ...
int start = current_offset(&a->out);
assemble_node(a, node->cond);
append_u8(&a->out, OPCODE_JIFP);
int p = append_u32(&a->out, 0);
assemble_node(a, node->left);
append_u8(&a->out, OPCODE_JUMP);
opcode_u32(&a->out, start);
patch_with_current_offset(&a->out, p);
}
break;
case NODE_FOR:
{
assemble_node(node->for_set);
assemble_node(a, node->for_set);
// TODO
}
break;
case NODE_WHILE:
break;
case NODE_SELECT:
break;
case NODE_OPER_POS:
assemble_node(node->left);
assemble_node(a, node->left);
break;
case NODE_OPER_NEG:
assemble_node(node->left);
append(out, OPCODE_NEG);
break;
case NODE_OPER_ASS:
assemble_node(a, node->left);
append_u8(&a->out, OPCODE_NEG);
break;
case NODE_OPER_EQL:
assemble_node(node->left);
assemble_node(node->right);
append(out, OPCODE_EQL);
assemble_node(a, node->left);
assemble_node(a, node->right);
append_u8(&a->out, OPCODE_EQL);
break;
case NODE_OPER_NQL:
assemble_node(node->left);
assemble_node(node->right);
append(out, OPCODE_NQL);
assemble_node(a, node->left);
assemble_node(a, node->right);
append_u8(&a->out, OPCODE_NQL);
break;
case NODE_OPER_LSS:
assemble_node(node->left);
assemble_node(node->right);
append(out, OPCODE_LSS);
assemble_node(a, node->left);
assemble_node(a, node->right);
append_u8(&a->out, OPCODE_LSS);
break;
case NODE_OPER_GRT:
assemble_node(node->left);
assemble_node(node->right);
append(out, OPCODE_GRT);
assemble_node(a, node->left);
assemble_node(a, node->right);
append_u8(&a->out, OPCODE_GRT);
break;
case NODE_OPER_ADD:
assemble_node(node->left);
assemble_node(node->right);
append(out, OPCODE_ADD);
assemble_node(a, node->left);
assemble_node(a, node->right);
append_u8(&a->out, OPCODE_ADD);
break;
case NODE_OPER_SUB:
assemble_node(node->left);
assemble_node(node->right);
append(out, OPCODE_SUB);
assemble_node(a, node->left);
assemble_node(a, node->right);
append_u8(&a->out, OPCODE_SUB);
break;
case NODE_OPER_MUL:
assemble_node(node->left);
assemble_node(node->right);
append(out, OPCODE_MUL);
assemble_node(a, node->left);
assemble_node(a, node->right);
append_u8(&a->out, OPCODE_MUL);
break;
case NODE_OPER_DIV:
assemble_node(node->left);
assemble_node(node->right);
append(out, OPCODE_DIV);
assemble_node(a, node->left);
assemble_node(a, node->right);
append_u8(&a->out, OPCODE_DIV);
break;
case NODE_OPER_MOD:
assemble_node(node->left);
assemble_node(node->right);
append(out, OPCODE_MOD);
assemble_node(a, node->left);
assemble_node(a, node->right);
append_u8(&a->out, OPCODE_MOD);
break;
case NODE_VALUE_INT:
append_u8 (out, OPCODE_PUSHI);
append_u64(out, node->ival);
append_u8(&a->out, OPCODE_PUSHI);
append_u64(&a->out, node->ival);
break;
case NODE_VALUE_FLOAT:
append_u8 (out, OPCODE_PUSHF);
append_f64(out, node->fval);
append_u8 (&a->out, OPCODE_PUSHF);
append_f64(&a->out, node->dval);
break;
case NODE_VALUE_STR:
append_u8 (out, OPCODE_PUSHS);
append_u64(out, xxx);
{
int off = add_string_literal(a, node->sval);
append_u8(&a->out, OPCODE_PUSHS);
append_u32(&a->out, off);
append_u32(&a->out, node->sval.len);
}
break;
case NODE_VALUE_VAR:
append_u8 (out, OPCODE_PUSHV);
append_u64(out, node->ival);
append_u8 (&a->out, OPCODE_PUSHV);
append_u64(&a->out, node->ival);
break;
case NODE_VALUE_HTML:
TODO;
assemble_html_node(a, node);
break;
case NODE_VALUE_ARRAY:
append_u8 (out, OPCODE_PUSHA);
append_u64(out, xxx);
{
append_u8(&a->out, OPCODE_PUSHA);
append_u32(&a->out, count_nodes(node->child));
Node *child = node->child;
while (child) {
assemble_node(a, child);
append_u8(&a->out, OPCODE_APPEND);
child = child->next;
}
}
break;
case NODE_VALUE_MAP:
append_u8 (out, OPCODE_PUSHM);
append_u64(out, xxx);
{
append_u8(&a->out, OPCODE_PUSHM);
append_u64(&a->out, count_nodes(node->child));
Node *child = node->child;
while (child) {
assemble_node(a, child);
assemble_node(a, child->key);
append_u8(&a->out, OPCODE_INSERT1);
child = child->next;
}
}
break;
case NODE_SELECT:
{
Node *set = node->left;
Node *key = node->right;
assemble_node(a, set);
assemble_node(a, key);
append_u8(&a->out, OPCODE_SELECT);
}
break;
case NODE_OPER_ASS:
{
Node *dst = node->left;
Node *src = node->right;
if (dst->type == NODE_VALUE_VAR) {
int idx = find_variable(dst->sval);
if (idx < 0) {
// TODO
}
assemble_node(a, src);
append_u8(&a->out, OPCODE_SETV);
append_u8(&a->out, idx);
} else if (dst->type == NODE_SELECT) {
assemble_node(a, src);
assemble_node(a, dst->left);
assemble_node(a, dst->right);
append_u8(&a->out, OPCODE_INSERT2);
} else {
// TODO ERROR
}
}
break;
}
}
AssembleResult assemble(Node *root)
{
assemble_node(root);
Assembler a = {0};
assemble_node(&a, root);
append_u8(&a.out, OPCODE_NOPE);
// TODO
}
+17 -5
View File
@@ -1,3 +1,7 @@
#ifndef WL_ASSEMBLE_INCLUDED
#define WL_ASSEMBLE_INCLUDED
#include "parse.h"
enum {
OPCODE_NOPE = 0x00,
@@ -17,11 +21,17 @@ enum {
OPCODE_SUB = 0x0E,
OPCODE_MUL = 0x0F,
OPCODE_DIV = 0x10,
OPCODE_MOD = 0x20,
OPCODE_JUMP = 0x30,
OPCODE_JIFP = 0x40,
OPCODE_CALL = 0x41,
OPCODE_RET = 0x42,
OPCODE_MOD = 0x11,
OPCODE_SETV = 0x12,
OPCODE_JUMP = 0x13,
OPCODE_JIFP = 0x14,
OPCODE_SCALL = 0x15,
OPCODE_DCALL = 0x16,
OPCODE_RET = 0x17,
OPCODE_APPEND = 0x18,
OPCODE_INSERT1 = 0x19,
OPCODE_INSERT2 = 0x20,
OPCODE_SELECT = 0x21,
};
typedef struct {
@@ -34,3 +44,5 @@ typedef struct {
} AssembleResult;
AssembleResult assemble(Node *root);
#endif // WL_ASSEMBLE_INCLUDED
+293
View File
@@ -0,0 +1,293 @@
#include "eval.h"
#define FRAME_LIMIT 128
#define EVAL_STACK_LIMIT 128
typedef struct {
} Frame;
typedef union {
uint64_t i;
void* p;
} EvalStackItem;
typedef struct {
Program p;
int off;
int frame_depth;
Frame frames[FRAME_LIMIT];
int eval_depth;
EvalStackItem eval_stack[EVAL_STACK_LIMIT];
} Eval;
void step(Eval *e)
{
uint8_t b = e->p.ptr[e->off++];
switch (b) {
case OPCODE_NOPE:
// Do nothing
break;
case OPCODE_PUSHI:
{
int64_t x;
memcpy(&x, e->p.ptr + e->off, sizeof(x));
e->off += (int) sizeof(x);
e->eval_stack[e->eval_depth++] = x;
}
break;
case OPCODE_PUSHF:
{
double x;
memcpy(&x, e->p.ptr + e->off, sizeof(x));
e->off += (int) sizeof(x);
e->eval_stack[e->eval_depth++] = x;
}
break;
case OPCODE_PUSHS:
{
// TODO
}
break;
case OPCODE_PUSHV:
{
// TODO
}
break;
case OPCODE_PUSHA:
{
// TODO
}
break;
case OPCODE_PUSHM:
{
// TODO
}
break;
case OPCODE_POP:
e->eval_depth--;
break;
case OPCODE_NEG:
{
Value a = e->eval_stack[--e->eval_depth];
// TODO
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_EQL:
{
Value a = e->eval_stack[e->eval_depth-2];
Value b = e->eval_stack[e->eval_depth-1];
e->eval_depth -= 2;
// TODO
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_NQL:
{
Value a = e->eval_stack[e->eval_depth-2];
Value b = e->eval_stack[e->eval_depth-1];
e->eval_depth -= 2;
// TODO
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_LSS:
{
Value a = e->eval_stack[e->eval_depth-2];
Value b = e->eval_stack[e->eval_depth-1];
e->eval_depth -= 2;
// TODO
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_GRT:
{
Value a = e->eval_stack[e->eval_depth-2];
Value b = e->eval_stack[e->eval_depth-1];
e->eval_depth -= 2;
// TODO
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_ADD:
{
Value a = e->eval_stack[e->eval_depth-2];
Value b = e->eval_stack[e->eval_depth-1];
e->eval_depth -= 2;
// TODO
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_SUB:
{
Value a = e->eval_stack[e->eval_depth-2];
Value b = e->eval_stack[e->eval_depth-1];
e->eval_depth -= 2;
// TODO
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_MUL:
{
Value a = e->eval_stack[e->eval_depth-2];
Value b = e->eval_stack[e->eval_depth-1];
e->eval_depth -= 2;
// TODO
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_DIV:
{
Value a = e->eval_stack[e->eval_depth-2];
Value b = e->eval_stack[e->eval_depth-1];
e->eval_depth -= 2;
// TODO
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_MOD:
{
Value a = e->eval_stack[e->eval_depth-2];
Value b = e->eval_stack[e->eval_depth-1];
e->eval_depth -= 2;
// TODO
e->eval_stack[e->eval_depth++] = r;
}
break;
case OPCODE_SETV:
{
// TODO
}
break;
case OPCODE_JUMP:
{
uint32_t x;
memcpy(&x, e->p.ptr + e->off, (int) sizeof(x));
e->off = (int) sizeof(x);
}
break;
case OPCODE_JIFP:
{
// TODO
}
break;
case OPCODE_SCALL:
{
// TODO
}
break;
case OPCODE_DCALL:
{
// TODO
}
break;
case OPCODE_RET:
{
// TODO
}
break;
case OPCODE_APPEND:
{
Value val = e->eval_stack[e->eval_depth-1];
Value set = e->eval_stack[e->eval_depth-2];
e->eval_depth--;
// TODO
}
break;
case OPCODE_INSERT1:
{
Value key = e->eval_stack[e->eval_depth-1];
Value val = e->eval_stack[e->eval_depth-2];
Value set = e->eval_stack[e->eval_depth-3];
e->eval_depth -= 2;
// TODO
}
break;
case OPCODE_INSERT2:
{
Value key = e->eval_stack[e->eval_depth-1];
Value set = e->eval_stack[e->eval_depth-2];
Value val = e->eval_stack[e->eval_depth-3];
e->eval_depth -= 2;
// TODO
}
break;
case OPCODE_SELECT:
{
// TODO
}
break;
default:
break;
}
}
void eval(Program p)
{
Eval e = { p, 0 };
for (;;)
step(&e);
// TODO
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef WL_EVAL_INCLUDED
#define WL_EVAL_INCLUDED
#include "assemble.h"
void eval(Program program);
#endif // WL_EVAL_INCLUDED
+62
View File
@@ -20,6 +20,7 @@ typedef enum {
TOKEN_KWORD_FOR,
TOKEN_KWORD_IN,
TOKEN_KWORD_FUN,
TOKEN_KWORD_LET,
TOKEN_VALUE_FLOAT,
TOKEN_VALUE_INT,
TOKEN_VALUE_STR,
@@ -102,6 +103,7 @@ String tok2str(Token token, char *buf, int max)
case TOKEN_KWORD_FOR: return S("for");
case TOKEN_KWORD_IN: return S("in");
case TOKEN_KWORD_FUN: return S("fun");
case TOKEN_KWORD_LET: return S("let");
case TOKEN_VALUE_FLOAT:
{
@@ -221,6 +223,7 @@ Token next_token(Parser *p)
if (streq(kword, S("for"))) return (Token) { .type=TOKEN_KWORD_FOR };
if (streq(kword, S("in"))) return (Token) { .type=TOKEN_KWORD_IN };
if (streq(kword, S("fun"))) return (Token) { .type=TOKEN_KWORD_FUN };
if (streq(kword, S("let"))) return (Token) { .type=TOKEN_KWORD_LET };
return (Token) { .type=TOKEN_IDENT, .sval=kword };
}
@@ -1299,6 +1302,49 @@ Node *parse_func_decl(Parser *p, int opflags)
return parent;
}
Node *parse_var_decl(Parser *p, int opflags)
{
Token t = next_token(p);
if (t.type != TOKEN_KWORD_LET) {
report(p, "Missing keyword 'let' at the start of a variable declaration");
return NULL;
}
t = next_token(p);
if (t.type != TOKEN_IDENT) {
report(p, "Missing variable name after 'let' keyword");
return NULL;
}
String name = t.sval;
t = next_token(p);
Node *value = NULL;
if (t.type == TOKEN_OPER_ASS) {
value = parse_expr(p, opflags);
if (value == NULL)
return NULL;
t = next_token(p);
}
if (t.type != TOKEN_SEMICOLON) {
report(p, "Missing ';' at the end of a variable declaration");
return NULL;
}
Node *parent = alloc_node(p);
if (parent == NULL)
return NULL;
parent->type = NODE_VAR_DECL;
parent->var_name = name;
parent->var_value = value;
return parent;
}
Node *parse_stmt(Parser *p, int opflags)
{
Scanner saved = p->s;
@@ -1310,6 +1356,9 @@ Node *parse_stmt(Parser *p, int opflags)
case TOKEN_KWORD_FUN:
return parse_func_decl(p, opflags);
case TOKEN_KWORD_LET:
return parse_var_decl(p, opflags);
case TOKEN_KWORD_IF:
return parse_ifelse_stmt(p, opflags);
@@ -1611,6 +1660,19 @@ void print_node(Node *node)
printf(")");
}
break;
case NODE_VAR_DECL:
{
printf("let %.*s",
node->var_name.len,
node->var_name.ptr);
if (node->var_value) {
printf(" = ");
print_node(node);
}
printf(";");
}
break;
}
}
+4
View File
@@ -8,6 +8,7 @@ typedef enum {
NODE_FUNC_DECL,
NODE_FUNC_ARG,
NODE_FUNC_CALL,
NODE_VAR_DECL,
NODE_BLOCK,
NODE_IFELSE,
NODE_FOR,
@@ -65,6 +66,9 @@ struct Node {
String func_name;
Node *func_args;
Node *func_body;
String var_name;
Node *var_value;
};
typedef struct {