diff --git a/main.wl b/main.wl index 1f5e905..5d360c1 100644 --- a/main.wl +++ b/main.wl @@ -1,6 +1,4 @@ -func(1, 3, 5); - title = "My Website"; posts = [ @@ -27,22 +25,21 @@ fun render_comment(c) $c.author; ($c.date;)

$c.content;

- $for child in c.children: + $ for child in c.children: render_comment(child);
- +export - $(title); + $title; - $for post in posts: { + $ for post in posts: {
$post.title; ($post.date;) - $for comment in post.comments: { + $ for comment in post.comments: render_comment(comment); - }
} diff --git a/src/assemble.c b/src/assemble.c index bfa0d74..51c2f2f 100644 --- a/src/assemble.c +++ b/src/assemble.c @@ -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) { - // TODO - } + 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: + // + // + // JIFP end + // + // end: + // ... + // + // If there is: + // + // + // JIFP else + // + // JUMP end + // else: + // + // 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: + // + // JIFP end + // + // 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 } diff --git a/src/assemble.h b/src/assemble.h index f5dbd12..452cebc 100644 --- a/src/assemble.h +++ b/src/assemble.h @@ -1,27 +1,37 @@ +#ifndef WL_ASSEMBLE_INCLUDED +#define WL_ASSEMBLE_INCLUDED + +#include "parse.h" enum { - OPCODE_NOPE = 0x00, - OPCODE_PUSHI = 0x01, - OPCODE_PUSHF = 0x02, - OPCODE_PUSHS = 0x03, - OPCODE_PUSHV = 0x04, - OPCODE_PUSHA = 0x05, - OPCODE_PUSHM = 0x06, - OPCODE_POP = 0x07, - OPCODE_NEG = 0x08, - OPCODE_EQL = 0x09, - OPCODE_NQL = 0x0A, - OPCODE_LSS = 0x0B, - OPCODE_GRT = 0x0C, - OPCODE_ADD = 0x0D, - 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_NOPE = 0x00, + OPCODE_PUSHI = 0x01, + OPCODE_PUSHF = 0x02, + OPCODE_PUSHS = 0x03, + OPCODE_PUSHV = 0x04, + OPCODE_PUSHA = 0x05, + OPCODE_PUSHM = 0x06, + OPCODE_POP = 0x07, + OPCODE_NEG = 0x08, + OPCODE_EQL = 0x09, + OPCODE_NQL = 0x0A, + OPCODE_LSS = 0x0B, + OPCODE_GRT = 0x0C, + OPCODE_ADD = 0x0D, + OPCODE_SUB = 0x0E, + OPCODE_MUL = 0x0F, + OPCODE_DIV = 0x10, + 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 { @@ -33,4 +43,6 @@ typedef struct { Program program; } AssembleResult; -AssembleResult assemble(Node *root); \ No newline at end of file +AssembleResult assemble(Node *root); + +#endif // WL_ASSEMBLE_INCLUDED \ No newline at end of file diff --git a/src/eval.c b/src/eval.c new file mode 100644 index 0000000..d6a34ba --- /dev/null +++ b/src/eval.c @@ -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 +} \ No newline at end of file diff --git a/src/eval.h b/src/eval.h new file mode 100644 index 0000000..ae8db83 --- /dev/null +++ b/src/eval.h @@ -0,0 +1,8 @@ +#ifndef WL_EVAL_INCLUDED +#define WL_EVAL_INCLUDED + +#include "assemble.h" + +void eval(Program program); + +#endif // WL_EVAL_INCLUDED diff --git a/src/parse.c b/src/parse.c index f9e2af1..a103127 100644 --- a/src/parse.c +++ b/src/parse.c @@ -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; } } diff --git a/src/parse.h b/src/parse.h index f3c58a1..1b566a3 100644 --- a/src/parse.h +++ b/src/parse.h @@ -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 {