Comment support
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "wl.h"
|
#include "wl.h"
|
||||||
@@ -273,6 +273,7 @@ typedef enum {
|
|||||||
TOKEN_OPER_DIV,
|
TOKEN_OPER_DIV,
|
||||||
TOKEN_OPER_MOD,
|
TOKEN_OPER_MOD,
|
||||||
TOKEN_OPER_ASS,
|
TOKEN_OPER_ASS,
|
||||||
|
TOKEN_OPER_SHOVEL,
|
||||||
TOKEN_PAREN_OPEN,
|
TOKEN_PAREN_OPEN,
|
||||||
TOKEN_PAREN_CLOSE,
|
TOKEN_PAREN_CLOSE,
|
||||||
TOKEN_BRACKET_OPEN,
|
TOKEN_BRACKET_OPEN,
|
||||||
@@ -319,6 +320,7 @@ typedef enum {
|
|||||||
NODE_OPER_MUL,
|
NODE_OPER_MUL,
|
||||||
NODE_OPER_DIV,
|
NODE_OPER_DIV,
|
||||||
NODE_OPER_MOD,
|
NODE_OPER_MOD,
|
||||||
|
NODE_OPER_SHOVEL,
|
||||||
NODE_VALUE_INT,
|
NODE_VALUE_INT,
|
||||||
NODE_VALUE_FLOAT,
|
NODE_VALUE_FLOAT,
|
||||||
NODE_VALUE_STR,
|
NODE_VALUE_STR,
|
||||||
@@ -440,6 +442,7 @@ static void write_token(Writer *w, Token token)
|
|||||||
case TOKEN_OPER_MUL : write_text(w, S("*")); break;
|
case TOKEN_OPER_MUL : write_text(w, S("*")); break;
|
||||||
case TOKEN_OPER_DIV : write_text(w, S("/")); break;
|
case TOKEN_OPER_DIV : write_text(w, S("/")); break;
|
||||||
case TOKEN_OPER_MOD : write_text(w, S("%")); break;
|
case TOKEN_OPER_MOD : write_text(w, S("%")); break;
|
||||||
|
case TOKEN_OPER_SHOVEL : write_text(w, S("<<")); break;
|
||||||
case TOKEN_PAREN_OPEN : write_text(w, S("(")); break;
|
case TOKEN_PAREN_OPEN : write_text(w, S("(")); break;
|
||||||
case TOKEN_PAREN_CLOSE : write_text(w, S(")")); break;
|
case TOKEN_PAREN_CLOSE : write_text(w, S(")")); break;
|
||||||
case TOKEN_BRACKET_OPEN : write_text(w, S("[")); break;
|
case TOKEN_BRACKET_OPEN : write_text(w, S("[")); break;
|
||||||
@@ -677,6 +680,7 @@ static Token next_token(Parser *p)
|
|||||||
return (Token) { .type=TOKEN_VALUE_STR, .sval=(String) { .ptr=buf, .len=len } };
|
return (Token) { .type=TOKEN_VALUE_STR, .sval=(String) { .ptr=buf, .len=len } };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (consume_str(&p->s, S("<<"))) return (Token) { .type=TOKEN_OPER_SHOVEL };
|
||||||
if (consume_str(&p->s, S("=="))) return (Token) { .type=TOKEN_OPER_EQL };
|
if (consume_str(&p->s, S("=="))) return (Token) { .type=TOKEN_OPER_EQL };
|
||||||
if (consume_str(&p->s, S("!="))) return (Token) { .type=TOKEN_OPER_NQL };
|
if (consume_str(&p->s, S("!="))) return (Token) { .type=TOKEN_OPER_NQL };
|
||||||
if (consume_str(&p->s, S("<"))) return (Token) { .type=TOKEN_OPER_LSS };
|
if (consume_str(&p->s, S("<"))) return (Token) { .type=TOKEN_OPER_LSS };
|
||||||
@@ -1016,6 +1020,11 @@ static int precedence(Token t, int flags)
|
|||||||
case TOKEN_OPER_ASS:
|
case TOKEN_OPER_ASS:
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
case TOKEN_OPER_SHOVEL:
|
||||||
|
if (flags & IGNORE_LSS)
|
||||||
|
return -1;
|
||||||
|
return 1;
|
||||||
|
|
||||||
case TOKEN_OPER_EQL:
|
case TOKEN_OPER_EQL:
|
||||||
case TOKEN_OPER_NQL:
|
case TOKEN_OPER_NQL:
|
||||||
return 2;
|
return 2;
|
||||||
@@ -1435,6 +1444,7 @@ static Node *parse_expr_inner(Parser *p, Node *left, int min_prec, int flags)
|
|||||||
case TOKEN_OPER_MUL: parent->type = NODE_OPER_MUL; break;
|
case TOKEN_OPER_MUL: parent->type = NODE_OPER_MUL; break;
|
||||||
case TOKEN_OPER_DIV: parent->type = NODE_OPER_DIV; break;
|
case TOKEN_OPER_DIV: parent->type = NODE_OPER_DIV; break;
|
||||||
case TOKEN_OPER_MOD: parent->type = NODE_OPER_MOD; break;
|
case TOKEN_OPER_MOD: parent->type = NODE_OPER_MOD; break;
|
||||||
|
case TOKEN_OPER_SHOVEL: parent->type = NODE_OPER_SHOVEL; break;
|
||||||
default:
|
default:
|
||||||
parser_report(p, "Operator not implemented");
|
parser_report(p, "Operator not implemented");
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -2485,6 +2495,8 @@ static void cg_pop_scope(Codegen *cg)
|
|||||||
ASSERT(cg->num_scopes > 0);
|
ASSERT(cg->num_scopes > 0);
|
||||||
Scope *scope = &cg->scopes[cg->num_scopes-1];
|
Scope *scope = &cg->scopes[cg->num_scopes-1];
|
||||||
|
|
||||||
|
ASSERT(scope->type == SCOPE_PROC || scope->type == SCOPE_GLOBAL || scope->max_vars == 0);
|
||||||
|
|
||||||
Scope *parent_scope = NULL;
|
Scope *parent_scope = NULL;
|
||||||
if (cg->num_scopes > 1)
|
if (cg->num_scopes > 1)
|
||||||
parent_scope = &cg->scopes[cg->num_scopes-2];
|
parent_scope = &cg->scopes[cg->num_scopes-2];
|
||||||
@@ -2494,6 +2506,8 @@ static void cg_pop_scope(Codegen *cg)
|
|||||||
UnpatchedCall *call = scope->calls;
|
UnpatchedCall *call = scope->calls;
|
||||||
scope->calls = call->next;
|
scope->calls = call->next;
|
||||||
|
|
||||||
|
ASSERT(call - cg->calls >= 0 && call - cg->calls < MAX_UNPATCHED_CALLS);
|
||||||
|
|
||||||
Symbol *sym = cg_find_symbol(cg, call->name, true);
|
Symbol *sym = cg_find_symbol(cg, call->name, true);
|
||||||
|
|
||||||
if (sym == NULL) {
|
if (sym == NULL) {
|
||||||
@@ -2514,6 +2528,12 @@ static void cg_pop_scope(Codegen *cg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cg_patch_u32(cg, call->off, sym->off);
|
cg_patch_u32(cg, call->off, sym->off);
|
||||||
|
|
||||||
|
call->next = cg->free_list_calls;
|
||||||
|
cg->free_list_calls = call;
|
||||||
|
|
||||||
|
// TODO: remove
|
||||||
|
ASSERT(cg->scopes[cg->num_scopes-1].calls == NULL || (cg->scopes[cg->num_scopes-1].calls - cg->calls >= 0 && cg->scopes[cg->num_scopes-1].calls - cg->calls < MAX_UNPATCHED_CALLS));
|
||||||
}
|
}
|
||||||
|
|
||||||
cg->num_syms = scope->idx_syms;
|
cg->num_syms = scope->idx_syms;
|
||||||
@@ -2531,9 +2551,13 @@ static void cg_append_unpatched_call(Codegen *cg, String name, int p)
|
|||||||
UnpatchedCall *call = cg->free_list_calls;
|
UnpatchedCall *call = cg->free_list_calls;
|
||||||
cg->free_list_calls = call->next;
|
cg->free_list_calls = call->next;
|
||||||
|
|
||||||
call->name = name;
|
ASSERT(call - cg->calls >= 0 && call - cg->calls < MAX_UNPATCHED_CALLS);
|
||||||
call->off = p;
|
|
||||||
|
|
||||||
|
call->name = name;
|
||||||
|
call->off = p;
|
||||||
|
call->next = NULL;
|
||||||
|
|
||||||
|
ASSERT(cg->num_scopes > 0);
|
||||||
Scope *scope = &cg->scopes[cg->num_scopes-1];
|
Scope *scope = &cg->scopes[cg->num_scopes-1];
|
||||||
|
|
||||||
call->next = scope->calls;
|
call->next = scope->calls;
|
||||||
@@ -2582,6 +2606,9 @@ static void walk_node(Codegen *cg, Node *node);
|
|||||||
|
|
||||||
static void walk_expr_node(Codegen *cg, Node *node, bool one)
|
static void walk_expr_node(Codegen *cg, Node *node, bool one)
|
||||||
{
|
{
|
||||||
|
// TODO: remove
|
||||||
|
ASSERT(cg->scopes[cg->num_scopes-1].calls == NULL || (cg->scopes[cg->num_scopes-1].calls - cg->calls >= 0 && cg->scopes[cg->num_scopes-1].calls - cg->calls < MAX_UNPATCHED_CALLS));
|
||||||
|
|
||||||
switch (node->type) {
|
switch (node->type) {
|
||||||
|
|
||||||
case NODE_NESTED:
|
case NODE_NESTED:
|
||||||
@@ -2655,6 +2682,23 @@ static void walk_expr_node(Codegen *cg, Node *node, bool one)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NODE_OPER_SHOVEL:
|
||||||
|
{
|
||||||
|
Node *dst = node->left;
|
||||||
|
Node *src = node->right;
|
||||||
|
|
||||||
|
walk_expr_node(cg, node->left, true);
|
||||||
|
|
||||||
|
cg_push_scope(cg, SCOPE_ASSIGNMENT);
|
||||||
|
walk_expr_node(cg, node->right, true);
|
||||||
|
cg_pop_scope(cg);
|
||||||
|
|
||||||
|
cg_write_opcode(cg, OPCODE_APPEND);
|
||||||
|
if (!one)
|
||||||
|
cg_write_opcode(cg, OPCODE_POP);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case NODE_OPER_EQL:
|
case NODE_OPER_EQL:
|
||||||
walk_expr_node(cg, node->left, true);
|
walk_expr_node(cg, node->left, true);
|
||||||
walk_expr_node(cg, node->right, true);
|
walk_expr_node(cg, node->right, true);
|
||||||
@@ -2871,6 +2915,9 @@ static void walk_expr_node(Codegen *cg, Node *node, bool one)
|
|||||||
|
|
||||||
static void walk_node(Codegen *cg, Node *node)
|
static void walk_node(Codegen *cg, Node *node)
|
||||||
{
|
{
|
||||||
|
// TODO: remove
|
||||||
|
ASSERT(cg->scopes[cg->num_scopes-1].calls == NULL || (cg->scopes[cg->num_scopes-1].calls - cg->calls >= 0 && cg->scopes[cg->num_scopes-1].calls - cg->calls < MAX_UNPATCHED_CALLS));
|
||||||
|
|
||||||
switch (node->type) {
|
switch (node->type) {
|
||||||
|
|
||||||
case NODE_GLOBAL:
|
case NODE_GLOBAL:
|
||||||
@@ -2919,7 +2966,7 @@ static void walk_node(Codegen *cg, Node *node)
|
|||||||
walk_node(cg, node->proc_body);
|
walk_node(cg, node->proc_body);
|
||||||
cg_write_opcode(cg, OPCODE_RET);
|
cg_write_opcode(cg, OPCODE_RET);
|
||||||
|
|
||||||
cg_patch_u8 (cg, off2, count_function_vars(cg));
|
cg_patch_u8 (cg, off2, cg->scopes[cg->num_scopes-1].max_vars);
|
||||||
cg_patch_u32(cg, off0, cg_current_offset(cg));
|
cg_patch_u32(cg, off0, cg_current_offset(cg));
|
||||||
|
|
||||||
cg_pop_scope(cg);
|
cg_pop_scope(cg);
|
||||||
@@ -3081,12 +3128,22 @@ static void walk_node(Codegen *cg, Node *node)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Codegen init_codegen(char *codebuf, int codecap,
|
#define WL_MAGIC 0xFEEDBEEF
|
||||||
char *databuf, int datacap, char *errmsg, int errcap)
|
|
||||||
|
static int codegen(Node *node, char *dst, int cap, char *errmsg, int errcap)
|
||||||
{
|
{
|
||||||
Codegen c = {
|
char *hdr;
|
||||||
.code = { codebuf, codecap, 0 },
|
if (cap < SIZEOF(uint32_t) * 3)
|
||||||
.data = { databuf, datacap, 0 },
|
hdr = NULL;
|
||||||
|
else {
|
||||||
|
hdr = dst;
|
||||||
|
dst += SIZEOF(uint32_t) * 3;
|
||||||
|
cap -= SIZEOF(uint32_t) * 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
Codegen cg = {
|
||||||
|
.code = { dst, cap/2, 0 },
|
||||||
|
.data = { dst + cap/2, cap/2, 0 },
|
||||||
.num_scopes = 0,
|
.num_scopes = 0,
|
||||||
.err = false,
|
.err = false,
|
||||||
.errmsg = errmsg,
|
.errmsg = errmsg,
|
||||||
@@ -3094,28 +3151,10 @@ static Codegen init_codegen(char *codebuf, int codecap,
|
|||||||
.data_off = -1,
|
.data_off = -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
c.free_list_calls = c.calls;
|
cg.free_list_calls = cg.calls;
|
||||||
for (int i = 0; i < MAX_UNPATCHED_CALLS-1; i++)
|
for (int i = 0; i < MAX_UNPATCHED_CALLS-1; i++)
|
||||||
c.calls[i].next = &c.calls[i+1];
|
cg.calls[i].next = &cg.calls[i+1];
|
||||||
c.calls[MAX_UNPATCHED_CALLS-1].next = NULL;
|
cg.calls[MAX_UNPATCHED_CALLS-1].next = NULL;
|
||||||
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define WL_MAGIC 0xFEEDBEEF
|
|
||||||
|
|
||||||
static int codegen(Node *node, char *dst, int cap, char *errmsg, int errcap)
|
|
||||||
{
|
|
||||||
char *hdr;
|
|
||||||
if (cap < sizeof(uint32_t) * 3)
|
|
||||||
hdr = NULL;
|
|
||||||
else {
|
|
||||||
hdr = dst;
|
|
||||||
dst += sizeof(uint32_t) * 3;
|
|
||||||
cap -= sizeof(uint32_t) * 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
Codegen cg = init_codegen(dst, cap/2, dst + cap/2, cap/2, errmsg, errcap);
|
|
||||||
|
|
||||||
cg_push_scope(&cg, SCOPE_GLOBAL);
|
cg_push_scope(&cg, SCOPE_GLOBAL);
|
||||||
cg_write_opcode(&cg, OPCODE_VARS);
|
cg_write_opcode(&cg, OPCODE_VARS);
|
||||||
@@ -3141,7 +3180,7 @@ static int codegen(Node *node, char *dst, int cap, char *errmsg, int errcap)
|
|||||||
memmove(dst + cg.code.len, dst + cap/2, cg.data.len);
|
memmove(dst + cg.code.len, dst + cap/2, cg.data.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
return cg.code.len + cg.data.len + sizeof(uint32_t) * 3;
|
return cg.code.len + cg.data.len + SIZEOF(uint32_t) * 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int write_instr(Writer *w, char *src, int len, String data)
|
static int write_instr(Writer *w, char *src, int len, String data)
|
||||||
@@ -3392,12 +3431,17 @@ static int write_instr(Writer *w, char *src, int len, String data)
|
|||||||
case OPCODE_SELECT:
|
case OPCODE_SELECT:
|
||||||
write_text(w, S("SELECT\n"));
|
write_text(w, S("SELECT\n"));
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
default:
|
||||||
|
write_text(w, S("byte "));
|
||||||
|
write_text_s64(w, src[0]);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int wl_dump_program(WL_Program program, char *dst, int cap)
|
static int write_program(WL_Program program, char *dst, int cap)
|
||||||
{
|
{
|
||||||
if (program.len < 3 * sizeof(uint32_t))
|
if (program.len < 3 * sizeof(uint32_t))
|
||||||
return -1;
|
return -1;
|
||||||
@@ -3433,6 +3477,30 @@ int wl_dump_program(WL_Program program, char *dst, int cap)
|
|||||||
return w.len;
|
return w.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wl_dump_program(WL_Program program)
|
||||||
|
{
|
||||||
|
char buf[1<<10];
|
||||||
|
int len = write_program(program, buf, SIZEOF(buf));
|
||||||
|
|
||||||
|
if (len < 0) {
|
||||||
|
printf("Invalid program\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len > SIZEOF(buf)) {
|
||||||
|
char *p = malloc(len+1);
|
||||||
|
if (p == NULL) {
|
||||||
|
printf("Out of memory\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
write_program(program, p, len);
|
||||||
|
p[len] = '\0';
|
||||||
|
fwrite(p, 1, len, stdout);
|
||||||
|
} else {
|
||||||
|
fwrite(buf, 1, len, stdout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
// COMPILER
|
// COMPILER
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
@@ -3969,17 +4037,19 @@ static Value value_select(Value set, Value key, Error *err)
|
|||||||
return VALUE_ERROR;
|
return VALUE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
char keybuf[1<<8];
|
char key_buf[1<<8];
|
||||||
int keylen = value_convert_to_str(key, keybuf, SIZEOF(keybuf));
|
int key_len = value_convert_to_str(key, key_buf, SIZEOF(key_buf));
|
||||||
keylen = MIN(keylen, SIZEOF(keybuf)-1);
|
if (key_len > SIZEOF(key_buf)-1)
|
||||||
keybuf[keylen] = '\0';
|
key_len = SIZEOF(key_buf)-1;
|
||||||
|
key_buf[key_len] = '\0';
|
||||||
|
|
||||||
char setbuf[1<<8];
|
char set_buf[1<<8];
|
||||||
int setlen = value_convert_to_str(set, setbuf, SIZEOF(setbuf));
|
int set_len = value_convert_to_str(set, set_buf, SIZEOF(set_buf));
|
||||||
setlen = MIN(setlen, SIZEOF(setbuf)-1);
|
if (set_len > SIZEOF(set_buf)-1)
|
||||||
setbuf[setlen] = '\0';
|
set_len = SIZEOF(set_buf)-1;
|
||||||
|
set_buf[set_len] = '\0';
|
||||||
|
|
||||||
REPORT(err, "Invalid key '%s' used in access to map '%s'", keybuf, setbuf);
|
REPORT(err, "Invalid key '%s' used in access to map '%s'", key_buf, set_buf);
|
||||||
return VALUE_ERROR;
|
return VALUE_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4680,6 +4750,21 @@ static void rt_pop_group(WL_Runtime *rt)
|
|||||||
rt->stack = rt->groups[--rt->num_groups];
|
rt->stack = rt->groups[--rt->num_groups];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void value_print(Value v)
|
||||||
|
{
|
||||||
|
char buf[1<<8];
|
||||||
|
int len = value_convert_to_str(v, buf, SIZEOF(buf));
|
||||||
|
if (len < SIZEOF(buf))
|
||||||
|
fwrite(buf, 1, len, stdout);
|
||||||
|
else {
|
||||||
|
len = SIZEOF(buf)-1;
|
||||||
|
fwrite(buf, 1, len, stdout);
|
||||||
|
fprintf(stdout, " [...]");
|
||||||
|
}
|
||||||
|
putc('\n', stdout);
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
static void step(WL_Runtime *rt)
|
static void step(WL_Runtime *rt)
|
||||||
{
|
{
|
||||||
switch (rt_read_u8(rt)) {
|
switch (rt_read_u8(rt)) {
|
||||||
@@ -4872,14 +4957,6 @@ static void step(WL_Runtime *rt)
|
|||||||
v1 = rt->values[rt->stack-1];
|
v1 = rt->values[rt->stack-1];
|
||||||
t = value_type(v1);
|
t = value_type(v1);
|
||||||
if (t != TYPE_ARRAY && t != TYPE_MAP) {
|
if (t != TYPE_ARRAY && t != TYPE_MAP) {
|
||||||
{
|
|
||||||
char buf[1<<9];
|
|
||||||
int len = value_convert_to_str(v1, buf, SIZEOF(buf));
|
|
||||||
if (len > SIZEOF(buf)-1)
|
|
||||||
len = SIZEOF(buf)-1;
|
|
||||||
buf[len] = '\0';
|
|
||||||
printf("len on (%s) type %d\n", buf, value_type(v1)); // TODO
|
|
||||||
}
|
|
||||||
REPORT(&rt->err, "Invalid operation 'len' on non-aggregate value");
|
REPORT(&rt->err, "Invalid operation 'len' on non-aggregate value");
|
||||||
rt->state = RUNTIME_ERROR;
|
rt->state = RUNTIME_ERROR;
|
||||||
break;
|
break;
|
||||||
@@ -5090,11 +5167,17 @@ WL_EvalResult wl_runtime_eval(WL_Runtime *rt)
|
|||||||
else {
|
else {
|
||||||
int len = value_convert_to_str(v, rt->buf, SIZEOF(rt->buf));
|
int len = value_convert_to_str(v, rt->buf, SIZEOF(rt->buf));
|
||||||
if (len > SIZEOF(rt->buf)) {
|
if (len > SIZEOF(rt->buf)) {
|
||||||
char *ptr = alloc(rt->arena, len, 1);
|
char *p = alloc(rt->arena, len, 1);
|
||||||
len = value_convert_to_str(v, ptr, len);
|
if (p == NULL) {
|
||||||
str = (String) { ptr, len };
|
REPORT(&rt->err, "Out of memory");
|
||||||
} else
|
rt->state = RUNTIME_ERROR;
|
||||||
|
return (WL_EvalResult) { .type=WL_EVAL_ERROR };
|
||||||
|
}
|
||||||
|
len = value_convert_to_str(v, p, len);
|
||||||
|
str = (String) { p, len };
|
||||||
|
} else {
|
||||||
str = (String) { rt->buf, len };
|
str = (String) { rt->buf, len };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rt->cur_output++;
|
rt->cur_output++;
|
||||||
@@ -5531,3 +5614,24 @@ void wl_append(WL_Runtime *rt)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wl_runtime_dump(WL_Runtime *rt)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < rt->num_frames; i++) {
|
||||||
|
printf("=== frame %d ===\n", i);
|
||||||
|
|
||||||
|
Frame *frame = &rt->frames[i];
|
||||||
|
|
||||||
|
int num_vars;
|
||||||
|
if (i+1 < rt->num_frames)
|
||||||
|
num_vars = frame->varbase - rt->frames[i+1].varbase;
|
||||||
|
else
|
||||||
|
num_vars = frame->varbase - rt->vars;
|
||||||
|
|
||||||
|
for (int j = 0; j < num_vars; j++) {
|
||||||
|
printf(" %d = ", j);
|
||||||
|
value_print(rt->values[frame->varbase - j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("===============\n");
|
||||||
|
}
|
||||||
|
|||||||
@@ -49,11 +49,12 @@ WL_AddResult wl_compiler_add (WL_Compiler *compiler, WL_String content);
|
|||||||
int wl_compiler_link (WL_Compiler *compiler, WL_Program *program);
|
int wl_compiler_link (WL_Compiler *compiler, WL_Program *program);
|
||||||
WL_String wl_compiler_error (WL_Compiler *compiler);
|
WL_String wl_compiler_error (WL_Compiler *compiler);
|
||||||
int wl_dump_ast (WL_Compiler *compiler, char *dst, int cap);
|
int wl_dump_ast (WL_Compiler *compiler, char *dst, int cap);
|
||||||
int wl_dump_program (WL_Program program, char *dst, int cap);
|
void wl_dump_program (WL_Program program);
|
||||||
|
|
||||||
WL_Runtime* wl_runtime_init (WL_Arena *arena, WL_Program program);
|
WL_Runtime* wl_runtime_init (WL_Arena *arena, WL_Program program);
|
||||||
WL_EvalResult wl_runtime_eval (WL_Runtime *rt);
|
WL_EvalResult wl_runtime_eval (WL_Runtime *rt);
|
||||||
WL_String wl_runtime_error (WL_Runtime *rt);
|
WL_String wl_runtime_error (WL_Runtime *rt);
|
||||||
|
void wl_runtime_dump (WL_Runtime *rt);
|
||||||
|
|
||||||
bool wl_streq (WL_String a, char *b, int blen);
|
bool wl_streq (WL_String a, char *b, int blen);
|
||||||
int wl_arg_count (WL_Runtime *rt);
|
int wl_arg_count (WL_Runtime *rt);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
procedure page(title, login_user_id, style, main)
|
procedure page(title, login_user_id, style, main)
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
<title>CN - \title</title>
|
<title>CN - \title</title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
|
|||||||
+220
-29
@@ -1,51 +1,242 @@
|
|||||||
include "pages/page.wl"
|
include "pages/page.wl"
|
||||||
|
|
||||||
let posts = $query("SELECT title, content FROM Posts WHERE id=?", $post_id)
|
let posts = $query("SELECT U.username, P.title, P.content FROM Posts as P, Users as U WHERE P.id=? AND U.id=P.author", $post_id)
|
||||||
let comments = $query("SELECT C.id, U.username, C.content FROM Comments as C, Users as U WHERE C.parent_post=? AND U.id == C.author", $post_id)
|
let comments = $query("SELECT C.id, U.username, C.content, C.parent_post, C.parent_comment FROM Comments as C, Users as U WHERE C.parent_post=? AND U.id=C.author", $post_id)
|
||||||
|
|
||||||
|
let lookup = {}
|
||||||
|
|
||||||
|
for comment in comments: {
|
||||||
|
comment.child = []
|
||||||
|
lookup[comment.id] = comment
|
||||||
|
}
|
||||||
|
|
||||||
|
let root_comments = []
|
||||||
|
|
||||||
|
for comment in comments: {
|
||||||
|
if comment.parent_comment == none:
|
||||||
|
root_comments << comment
|
||||||
|
else
|
||||||
|
lookup[comment.parent_comment].child << comment
|
||||||
|
}
|
||||||
|
|
||||||
let post = posts[0]
|
let post = posts[0]
|
||||||
|
|
||||||
let style =
|
let style =
|
||||||
<style>
|
<style>
|
||||||
.child {
|
.thread-header {
|
||||||
border-left: 3px solid #ccc;
|
padding: 15px 0;
|
||||||
|
border-bottom: 2px solid #E8D4A9;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.thread-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.thread-title a {
|
||||||
|
color: #1D2B42;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.thread-title a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.thread-meta {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #7A5F2A;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.thread-meta a {
|
||||||
|
color: #7A5F2A;
|
||||||
|
}
|
||||||
|
.thread-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #1D2B42;
|
||||||
|
line-height: 160%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.thread-actions {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.thread-actions a {
|
||||||
|
color: #7A5F2A;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Comment styles */
|
||||||
|
.comment {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border-left: 1px solid #E8D4A9;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
}
|
}
|
||||||
form textarea {
|
|
||||||
|
.comment-meta {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #7A5F2A;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.comment-meta a {
|
||||||
|
color: #7A5F2A;
|
||||||
|
}
|
||||||
|
.comment-text {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #1D2B42;
|
||||||
|
line-height: 150%;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.comment-actions {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
.comment-actions a {
|
||||||
|
color: #7A5F2A;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
.comment-actions a:hover {
|
||||||
|
color: #1D2B42;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-buttons {
|
||||||
|
float: left;
|
||||||
|
width: 15px;
|
||||||
|
margin-right: 8px;
|
||||||
|
font-size: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.vote-buttons a {
|
||||||
|
display: block;
|
||||||
|
color: #7A5F2A;
|
||||||
|
text-decoration: none;
|
||||||
|
line-height: 100%;
|
||||||
|
}
|
||||||
|
.vote-buttons a:hover {
|
||||||
|
color: #1D2B42;
|
||||||
|
}
|
||||||
|
.comment-content {
|
||||||
|
margin-left: 23px;
|
||||||
|
}
|
||||||
|
.comment-child {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
.add-comment {
|
||||||
|
margin: 20px 0;
|
||||||
|
padding: 15px;
|
||||||
|
background: #E8D4A9;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid #D4C298;
|
||||||
|
}
|
||||||
|
.add-comment form {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.add-comment form textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 80px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
background: #F7E6C0;
|
||||||
|
border: 1px solid #D4C298;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
.add-comment form input[type=submit] {
|
||||||
|
background: #5780C9;
|
||||||
|
color: #F7E6C0;
|
||||||
|
border: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
.add-comment input[type=submit]:hover {
|
||||||
|
background: #1D2B42;
|
||||||
|
}
|
||||||
|
summary {
|
||||||
|
list-style: none;
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #7A5F2A;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsed {
|
||||||
|
color: #7A5F2A;
|
||||||
|
font-size: 11px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.collapsed:hover {
|
||||||
|
color: #1D2B42;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
let main =
|
let main =
|
||||||
<main>
|
<main>
|
||||||
<h3>\post.title</h3>
|
<div class="thread-header">
|
||||||
<p>\post.content</p>
|
<div class="thread-title">
|
||||||
<div>
|
<span>\post.title</span>
|
||||||
\if $login_user_id != none:
|
</div>
|
||||||
<form action="/api/comment" method="POST">
|
<div class="thread-meta">
|
||||||
<input type="hidden" name="parent_post" value=\'"'\$post_id\'"' />
|
submitted 3 hours ago by <a href="">\post.username</a> | <a href="">\len comments</a>
|
||||||
<textarea name="content"></textarea>
|
</div>
|
||||||
<input type="submit" vaue="Publish" />
|
<div class="thread-text">
|
||||||
</form>
|
\post.content
|
||||||
|
</div>
|
||||||
|
<details>
|
||||||
|
<summary>
|
||||||
|
reply
|
||||||
|
</summary>
|
||||||
|
<div class="add-comment">
|
||||||
|
<form action="/api/comment" method="POST">
|
||||||
|
<input type="hidden" name="parent_post" value=\'"'\$post_id\'"' />
|
||||||
|
<textarea name="content" placeholder="Add a comment..."></textarea>
|
||||||
|
<input type="submit" vaue="Publish" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
</div>
|
</div>
|
||||||
\if len comments == 0:
|
|
||||||
<span>No comments yet!</span>
|
\procedure render_comment(comment)
|
||||||
else for comment in comments:
|
<div class="comment">
|
||||||
<div>
|
<div class="vote-buttons">
|
||||||
<a href="">\comment.username</a>
|
<a href="">▲</a>
|
||||||
<p>
|
<a href="">▼</a>
|
||||||
\comment.content
|
</div>
|
||||||
</p>
|
<div class="comment-content">
|
||||||
<div class="child">
|
<div class="comment-meta">
|
||||||
|
<a href="">\comment.username</a> 2 hours ago
|
||||||
|
</div>
|
||||||
|
<div class="comment-text">
|
||||||
|
\comment.content
|
||||||
|
</div>
|
||||||
\if $login_user_id != none:
|
\if $login_user_id != none:
|
||||||
<form action="/api/comment" method="POST">
|
<details>
|
||||||
<input type="hidden" name="parent_post" value=\'"'\$post_id\'"' />
|
<summary>
|
||||||
<input type="hidden" name="parent_comment" value=\'"'\comment.id\'"' />
|
reply
|
||||||
<textarea name="content"></textarea>
|
</summary>
|
||||||
<input type="submit" vaue="Publish" />
|
<div class="add-comment">
|
||||||
</form>
|
<form action="/api/comment" method="POST">
|
||||||
|
<input type="hidden" name="parent_post" value=\'"'\$post_id\'"' />
|
||||||
|
<input type="hidden" name="parent_comment" value=\'"'\comment.id\'"' />
|
||||||
|
<textarea name="content" placeholder="Add a comment..."></textarea>
|
||||||
|
<input type="submit" vaue="Publish" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
<div class="comment-child">
|
||||||
|
\for child in comment.child:
|
||||||
|
render_comment(child)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
\if len root_comments == 0:
|
||||||
|
<span>(No comments)</span>
|
||||||
|
else for comment in root_comments:
|
||||||
|
render_comment(comment)
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
page(post.title, $login_user_id, style, main)
|
page(post.title, $login_user_id, style, main)
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
include "pages/page.wl"
|
||||||
|
|
||||||
|
let posts = $query("SELECT title, content FROM Posts WHERE id=?", $post_id)
|
||||||
|
let comments = $query("SELECT C.id, U.username, C.content, C.parent_post, C.parent_comment FROM Comments as C, Users as U WHERE C.parent_post=? AND U.id == C.author", $post_id)
|
||||||
|
|
||||||
|
let lookup = {}
|
||||||
|
|
||||||
|
for comment in comments: {
|
||||||
|
comment.child = []
|
||||||
|
lookup[comment.id] = comment
|
||||||
|
}
|
||||||
|
|
||||||
|
let roots = []
|
||||||
|
|
||||||
|
for comment in comments: {
|
||||||
|
if comment.parent_comment == none:
|
||||||
|
roots << comment
|
||||||
|
else
|
||||||
|
lookup[comment.parent_comment].child << comment
|
||||||
|
}
|
||||||
|
|
||||||
|
comments = roots
|
||||||
|
|
||||||
|
let post = posts[0]
|
||||||
|
|
||||||
|
let style =
|
||||||
|
<style>
|
||||||
|
.child {
|
||||||
|
border-left: 3px solid #ccc;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
form textarea {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
procedure render_comment(parent)
|
||||||
|
<div>
|
||||||
|
<a href="">\parent.username</a>
|
||||||
|
<p>
|
||||||
|
\parent.content
|
||||||
|
</p>
|
||||||
|
<div class="child">
|
||||||
|
\if $login_user_id != none:
|
||||||
|
<form action="/api/comment" method="POST">
|
||||||
|
<input type="hidden" name="parent_post" value=\'"'\$post_id\'"' />
|
||||||
|
<input type="hidden" name="parent_comment" value=\'"'\parent.id\'"' />
|
||||||
|
<textarea name="content"></textarea>
|
||||||
|
<input type="submit" vaue="Publish" />
|
||||||
|
</form>
|
||||||
|
\for child in parent.child:
|
||||||
|
render_comment(child)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
let main =
|
||||||
|
<main>
|
||||||
|
<h3>\post.title</h3>
|
||||||
|
<p>\post.content</p>
|
||||||
|
<div>
|
||||||
|
<form action="/api/comment" method="POST">
|
||||||
|
<input type="hidden" name="parent_post" value=\'"'\$post_id\'"' />
|
||||||
|
<textarea name="content"></textarea>
|
||||||
|
<input type="submit" vaue="Publish" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
\if len comments == 0:
|
||||||
|
<span>No comments yet!</span>
|
||||||
|
else for comment in comments:
|
||||||
|
render_comment(comment)
|
||||||
|
</main>
|
||||||
|
|
||||||
|
page(post.title, $login_user_id, style, main)
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<ul>
|
|
||||||
<li>1. Post A</li>
|
|
||||||
<li>2. Post B</li>
|
|
||||||
<li>3. Post C</li>
|
|
||||||
</ul>
|
|
||||||
+23
-6
@@ -876,6 +876,7 @@ int main(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
HTTP_String parent_post_str = http_getparam(req->body, HTTP_STR("parent_post"), &arena);
|
HTTP_String parent_post_str = http_getparam(req->body, HTTP_STR("parent_post"), &arena);
|
||||||
|
HTTP_String parent_comment_str = http_getparam(req->body, HTTP_STR("parent_comment"), &arena);
|
||||||
HTTP_String content = http_getparam(req->body, HTTP_STR("content"), &arena);
|
HTTP_String content = http_getparam(req->body, HTTP_STR("content"), &arena);
|
||||||
|
|
||||||
int parent_post;
|
int parent_post;
|
||||||
@@ -893,6 +894,21 @@ int main(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int parent_comment;
|
||||||
|
{
|
||||||
|
char buf[32];
|
||||||
|
if (parent_comment_str.len >= (int) sizeof(buf))
|
||||||
|
parent_comment = -1;
|
||||||
|
else {
|
||||||
|
memcpy(buf, parent_comment_str.ptr, parent_comment_str.len);
|
||||||
|
buf[parent_comment_str.len] = '\0';
|
||||||
|
|
||||||
|
parent_comment = atoi(buf);
|
||||||
|
if (parent_comment == 0)
|
||||||
|
parent_comment = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
content = http_trim(content);
|
content = http_trim(content);
|
||||||
if (!valid_comment_content(content)) {
|
if (!valid_comment_content(content)) {
|
||||||
assert(0); // TODO
|
assert(0); // TODO
|
||||||
@@ -902,8 +918,12 @@ int main(void)
|
|||||||
assert(0); // TODO
|
assert(0); // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ret;
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
int ret = sqlite3utils_prepare(db, &stmt, "INSERT INTO Comments(author, content, parent_post) VALUES (?, ?, ?)", user_id, content, parent_post);
|
if (parent_comment == -1)
|
||||||
|
ret = sqlite3utils_prepare(db, &stmt, "INSERT INTO Comments(author, content, parent_post) VALUES (?, ?, ?)", user_id, content, parent_post);
|
||||||
|
else
|
||||||
|
ret = sqlite3utils_prepare(db, &stmt, "INSERT INTO Comments(author, content, parent_post, parent_comment) VALUES (?, ?, ?, ?)", user_id, content, parent_post, parent_comment);
|
||||||
if (ret != SQLITE_OK) {
|
if (ret != SQLITE_OK) {
|
||||||
assert(0); // TODO
|
assert(0); // TODO
|
||||||
}
|
}
|
||||||
@@ -1009,13 +1029,10 @@ int main(void)
|
|||||||
assert(0); // TODO
|
assert(0); // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
if (num == 0) {
|
if (num == 0)
|
||||||
printf("post with id %d does not exist\n", post_id); // TODO
|
|
||||||
evaluate_template_2(builder, arena, "pages/notfound.wl", user_id, -1);
|
evaluate_template_2(builder, arena, "pages/notfound.wl", user_id, -1);
|
||||||
} else {
|
else
|
||||||
printf("Evaluating template\n");
|
|
||||||
evaluate_template_2(builder, arena, "pages/post.wl", user_id, post_id);
|
evaluate_template_2(builder, arena, "pages/post.wl", user_id, post_id);
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user