Add HTML escaping to post.wl
This commit is contained in:
@@ -34,6 +34,7 @@ typedef struct {
|
|||||||
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
|
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
|
||||||
#define SIZEOF(X) (int) sizeof(X)
|
#define SIZEOF(X) (int) sizeof(X)
|
||||||
#define ALIGNOF(X) (int) _Alignof(X)
|
#define ALIGNOF(X) (int) _Alignof(X)
|
||||||
|
#define COUNT(X) (int) (sizeof(X)/sizeof((X)[0]))
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
#define UNREACHABLE __builtin_trap()
|
#define UNREACHABLE __builtin_trap()
|
||||||
@@ -68,14 +69,12 @@ static bool is_hex_digit(char c)
|
|||||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
static char to_lower(char c)
|
static char to_lower(char c)
|
||||||
{
|
{
|
||||||
if (c >= 'A' && c <= 'Z')
|
if (c >= 'A' && c <= 'Z')
|
||||||
return c - 'A' + 'a';
|
return c - 'A' + 'a';
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static int hex_digit_to_int(char c)
|
static int hex_digit_to_int(char c)
|
||||||
{
|
{
|
||||||
@@ -98,7 +97,6 @@ static bool streq(String a, String b)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
static bool streqcase(String a, String b)
|
static bool streqcase(String a, String b)
|
||||||
{
|
{
|
||||||
if (a.len != b.len)
|
if (a.len != b.len)
|
||||||
@@ -108,7 +106,7 @@ static bool streqcase(String a, String b)
|
|||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#define REPORT(err, fmt, ...) report((err), __FILE__, __LINE__, fmt, ## __VA_ARGS__)
|
#define REPORT(err, fmt, ...) report((err), __FILE__, __LINE__, fmt, ## __VA_ARGS__)
|
||||||
static void report(Error *err, char *file, int line, char *fmt, ...)
|
static void report(Error *err, char *file, int line, char *fmt, ...)
|
||||||
@@ -161,7 +159,6 @@ static bool grow_alloc(WL_Arena *a, char *p, int new_len)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
static String copystr(String s, WL_Arena *a)
|
static String copystr(String s, WL_Arena *a)
|
||||||
{
|
{
|
||||||
char *p = alloc(a, s.len, 1);
|
char *p = alloc(a, s.len, 1);
|
||||||
@@ -170,7 +167,6 @@ static String copystr(String s, WL_Arena *a)
|
|||||||
memcpy(p, s.ptr, s.len);
|
memcpy(p, s.ptr, s.len);
|
||||||
return (String) { p, s.len };
|
return (String) { p, s.len };
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
// WRITER
|
// WRITER
|
||||||
@@ -193,14 +189,14 @@ static void write_raw_mem(Writer *w, void *ptr, int len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void write_raw_u8 (Writer *w, uint8_t x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
static void write_raw_u8 (Writer *w, uint8_t x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
||||||
//static void write_raw_u16(Writer *w, uint16_t x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
static void write_raw_u16(Writer *w, uint16_t x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
||||||
static void write_raw_u32(Writer *w, uint32_t x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
static void write_raw_u32(Writer *w, uint32_t x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
||||||
//static void write_raw_u64(Writer *w, uint64_t x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
static void write_raw_u64(Writer *w, uint64_t x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
||||||
//static void write_raw_s8 (Writer *w, int8_t x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
static void write_raw_s8 (Writer *w, int8_t x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
||||||
//static void write_raw_s16(Writer *w, int16_t x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
static void write_raw_s16(Writer *w, int16_t x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
||||||
//static void write_raw_s32(Writer *w, int32_t x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
static void write_raw_s32(Writer *w, int32_t x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
||||||
static void write_raw_s64(Writer *w, int64_t x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
static void write_raw_s64(Writer *w, int64_t x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
||||||
//static void write_raw_f32(Writer *w, float x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
static void write_raw_f32(Writer *w, float x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
||||||
static void write_raw_f64(Writer *w, double x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
static void write_raw_f64(Writer *w, double x) { write_raw_mem(w, &x, SIZEOF(x)); }
|
||||||
|
|
||||||
static void write_text(Writer *w, String str)
|
static void write_text(Writer *w, String str)
|
||||||
@@ -265,6 +261,7 @@ typedef enum {
|
|||||||
TOKEN_KWORD_FALSE,
|
TOKEN_KWORD_FALSE,
|
||||||
TOKEN_KWORD_INCLUDE,
|
TOKEN_KWORD_INCLUDE,
|
||||||
TOKEN_KWORD_LEN,
|
TOKEN_KWORD_LEN,
|
||||||
|
TOKEN_KWORD_ESCAPE,
|
||||||
TOKEN_VALUE_FLOAT,
|
TOKEN_VALUE_FLOAT,
|
||||||
TOKEN_VALUE_INT,
|
TOKEN_VALUE_INT,
|
||||||
TOKEN_VALUE_STR,
|
TOKEN_VALUE_STR,
|
||||||
@@ -312,6 +309,7 @@ typedef enum {
|
|||||||
NODE_INCLUDE,
|
NODE_INCLUDE,
|
||||||
NODE_SELECT,
|
NODE_SELECT,
|
||||||
NODE_NESTED,
|
NODE_NESTED,
|
||||||
|
NODE_OPER_ESCAPE,
|
||||||
NODE_OPER_LEN,
|
NODE_OPER_LEN,
|
||||||
NODE_OPER_POS,
|
NODE_OPER_POS,
|
||||||
NODE_OPER_NEG,
|
NODE_OPER_NEG,
|
||||||
@@ -415,7 +413,6 @@ static bool consume_str(Scanner *s, String x)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
static void write_token(Writer *w, Token token)
|
static void write_token(Writer *w, Token token)
|
||||||
{
|
{
|
||||||
switch (token.type) {
|
switch (token.type) {
|
||||||
@@ -436,6 +433,7 @@ static void write_token(Writer *w, Token token)
|
|||||||
case TOKEN_KWORD_FALSE : write_text(w, S("false")); break;
|
case TOKEN_KWORD_FALSE : write_text(w, S("false")); break;
|
||||||
case TOKEN_KWORD_INCLUDE: write_text(w, S("include")); break;
|
case TOKEN_KWORD_INCLUDE: write_text(w, S("include")); break;
|
||||||
case TOKEN_KWORD_LEN : write_text(w, S("len")); break;
|
case TOKEN_KWORD_LEN : write_text(w, S("len")); break;
|
||||||
|
case TOKEN_KWORD_ESCAPE: write_text(w, S("escape")); break;
|
||||||
case TOKEN_VALUE_FLOAT : write_text_f64(w, token.fval); break;
|
case TOKEN_VALUE_FLOAT : write_text_f64(w, token.fval); break;
|
||||||
case TOKEN_VALUE_INT : write_text_s64(w, token.ival); break;
|
case TOKEN_VALUE_INT : write_text_s64(w, token.ival); break;
|
||||||
case TOKEN_OPER_ASS : write_text(w, S("=")); break;
|
case TOKEN_OPER_ASS : write_text(w, S("=")); break;
|
||||||
@@ -469,7 +467,6 @@ static void write_token(Writer *w, Token token)
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static void parser_report(Parser *p, char *fmt, ...)
|
static void parser_report(Parser *p, char *fmt, ...)
|
||||||
{
|
{
|
||||||
@@ -552,6 +549,7 @@ static Token next_token(Parser *p)
|
|||||||
if (streq(kword, S("false"))) return (Token) { .type=TOKEN_KWORD_FALSE };
|
if (streq(kword, S("false"))) return (Token) { .type=TOKEN_KWORD_FALSE };
|
||||||
if (streq(kword, S("include"))) return (Token) { .type=TOKEN_KWORD_INCLUDE };
|
if (streq(kword, S("include"))) return (Token) { .type=TOKEN_KWORD_INCLUDE };
|
||||||
if (streq(kword, S("len"))) return (Token) { .type=TOKEN_KWORD_LEN };
|
if (streq(kword, S("len"))) return (Token) { .type=TOKEN_KWORD_LEN };
|
||||||
|
if (streq(kword, S("escape"))) return (Token) { .type=TOKEN_KWORD_ESCAPE };
|
||||||
|
|
||||||
return (Token) { .type=TOKEN_IDENT, .sval=kword };
|
return (Token) { .type=TOKEN_IDENT, .sval=kword };
|
||||||
}
|
}
|
||||||
@@ -778,8 +776,7 @@ static Node *parse_html(Parser *p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (s->cur == s->len) {
|
if (s->cur == s->len) {
|
||||||
parser_report(p, "Invaid end of source inside HTML tag");
|
ASSERT(0); // TODO
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
s->cur++;
|
s->cur++;
|
||||||
|
|
||||||
@@ -790,8 +787,7 @@ static Node *parse_html(Parser *p)
|
|||||||
while (s->cur < s->len && is_space(s->src[s->cur]))
|
while (s->cur < s->len && is_space(s->src[s->cur]))
|
||||||
s->cur++;
|
s->cur++;
|
||||||
if (s->cur == s->len || s->src[s->cur] != '>') {
|
if (s->cur == s->len || s->src[s->cur] != '>') {
|
||||||
parser_report(p, "Invalid character inside HTML closing tag");
|
ASSERT(0); // TODO
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
s->cur++;
|
s->cur++;
|
||||||
no_body = true;
|
no_body = true;
|
||||||
@@ -835,8 +831,7 @@ static Node *parse_html(Parser *p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (s->cur == s->len) {
|
if (s->cur == s->len) {
|
||||||
parser_report(p, "Missing closing HTML tag </%.*s>", tagname.len, tagname.ptr);
|
ASSERT(0); // TODO
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
s->cur++;
|
s->cur++;
|
||||||
|
|
||||||
@@ -848,20 +843,17 @@ static Node *parse_html(Parser *p)
|
|||||||
|
|
||||||
t = next_token(p);
|
t = next_token(p);
|
||||||
if (t.type != TOKEN_IDENT) {
|
if (t.type != TOKEN_IDENT) {
|
||||||
parser_report(p, "Missing tag name after '</'");
|
ASSERT(0); // TODO
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
String closing_tagname = t.sval;
|
String closing_tagname = t.sval;
|
||||||
|
|
||||||
if (!streq(closing_tagname, tagname)) {
|
if (!streq(closing_tagname, tagname)) {
|
||||||
parser_report(p, "HTML tag mismatch (%.*s != %.*s)", tagname.len, tagname.ptr, closing_tagname.len, closing_tagname.ptr);
|
ASSERT(0); // TODO
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t = next_token(p);
|
t = next_token(p);
|
||||||
if (t.type != TOKEN_OPER_GRT) {
|
if (t.type != TOKEN_OPER_GRT) {
|
||||||
parser_report(p, "Missing '>' in closing HTML tag");
|
ASSERT(0);
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -1134,6 +1126,23 @@ static Node *parse_atom(Parser *p)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TOKEN_KWORD_ESCAPE:
|
||||||
|
{
|
||||||
|
Node *child = parse_atom(p);
|
||||||
|
if (child == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
Node *parent = alloc_node(p);
|
||||||
|
if (parent == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
parent->type = NODE_OPER_ESCAPE;
|
||||||
|
parent->left = child;
|
||||||
|
|
||||||
|
ret = parent;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case TOKEN_IDENT:
|
case TOKEN_IDENT:
|
||||||
{
|
{
|
||||||
Node *node = alloc_node(p);
|
Node *node = alloc_node(p);
|
||||||
@@ -1910,6 +1919,12 @@ static void write_node(Writer *w, Node *node)
|
|||||||
write_text(w, S(")"));
|
write_text(w, S(")"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NODE_OPER_ESCAPE:
|
||||||
|
write_text(w, S("(escape "));
|
||||||
|
write_node(w, node->left);
|
||||||
|
write_text(w, S(")"));
|
||||||
|
break;
|
||||||
|
|
||||||
case NODE_OPER_POS:
|
case NODE_OPER_POS:
|
||||||
write_text(w, S("(+"));
|
write_text(w, S("(+"));
|
||||||
write_node(w, node->left);
|
write_node(w, node->left);
|
||||||
@@ -2003,14 +2018,6 @@ static void write_node(Writer *w, Node *node)
|
|||||||
write_text(w, S(")"));
|
write_text(w, S(")"));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NODE_OPER_SHOVEL:
|
|
||||||
write_text(w, S("("));
|
|
||||||
write_node(w, node->left);
|
|
||||||
write_text(w, S("<<"));
|
|
||||||
write_node(w, node->right);
|
|
||||||
write_text(w, S(")"));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NODE_VALUE_INT:
|
case NODE_VALUE_INT:
|
||||||
write_text_s64(w, node->ival);
|
write_text_s64(w, node->ival);
|
||||||
break;
|
break;
|
||||||
@@ -2206,6 +2213,7 @@ enum {
|
|||||||
OPCODE_CALL,
|
OPCODE_CALL,
|
||||||
OPCODE_RET,
|
OPCODE_RET,
|
||||||
OPCODE_GROUP,
|
OPCODE_GROUP,
|
||||||
|
OPCODE_ESCAPE,
|
||||||
OPCODE_PACK,
|
OPCODE_PACK,
|
||||||
OPCODE_GPOP,
|
OPCODE_GPOP,
|
||||||
OPCODE_FOR,
|
OPCODE_FOR,
|
||||||
@@ -2516,8 +2524,6 @@ 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];
|
||||||
@@ -2552,6 +2558,9 @@ static void cg_pop_scope(Codegen *cg)
|
|||||||
|
|
||||||
call->next = cg->free_list_calls;
|
call->next = cg->free_list_calls;
|
||||||
cg->free_list_calls = call;
|
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;
|
||||||
@@ -2620,10 +2629,13 @@ static void cg_write_pushs(Codegen *cg, String str, bool dont_group)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void walk_node(Codegen *cg, Node *node);
|
static void walk_node(Codegen *cg, Node *node, bool inside_html);
|
||||||
|
|
||||||
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:
|
||||||
@@ -2635,6 +2647,12 @@ static void walk_expr_node(Codegen *cg, Node *node, bool one)
|
|||||||
cg_write_opcode(cg, OPCODE_LEN);
|
cg_write_opcode(cg, OPCODE_LEN);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NODE_OPER_ESCAPE:
|
||||||
|
cg_write_opcode(cg, OPCODE_GROUP);
|
||||||
|
walk_expr_node(cg, node->left, false);
|
||||||
|
cg_write_opcode(cg, OPCODE_ESCAPE);
|
||||||
|
break;
|
||||||
|
|
||||||
case NODE_OPER_POS:
|
case NODE_OPER_POS:
|
||||||
walk_expr_node(cg, node->left, one);
|
walk_expr_node(cg, node->left, one);
|
||||||
break;
|
break;
|
||||||
@@ -2699,6 +2717,9 @@ static void walk_expr_node(Codegen *cg, Node *node, bool one)
|
|||||||
|
|
||||||
case NODE_OPER_SHOVEL:
|
case NODE_OPER_SHOVEL:
|
||||||
{
|
{
|
||||||
|
Node *dst = node->left;
|
||||||
|
Node *src = node->right;
|
||||||
|
|
||||||
walk_expr_node(cg, node->left, true);
|
walk_expr_node(cg, node->left, true);
|
||||||
|
|
||||||
cg_push_scope(cg, SCOPE_ASSIGNMENT);
|
cg_push_scope(cg, SCOPE_ASSIGNMENT);
|
||||||
@@ -2824,7 +2845,7 @@ static void walk_expr_node(Codegen *cg, Node *node, bool one)
|
|||||||
|
|
||||||
Node *child = node->html_attr;
|
Node *child = node->html_attr;
|
||||||
while (child) {
|
while (child) {
|
||||||
walk_node(cg, child);
|
walk_node(cg, child, true);
|
||||||
child = child->next;
|
child = child->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2834,7 +2855,7 @@ static void walk_expr_node(Codegen *cg, Node *node, bool one)
|
|||||||
cg_write_pushs(cg, S(">"), false);
|
cg_write_pushs(cg, S(">"), false);
|
||||||
Node *child = node->html_child;
|
Node *child = node->html_child;
|
||||||
while (child) {
|
while (child) {
|
||||||
walk_node(cg, child);
|
walk_node(cg, child, true);
|
||||||
child = child->next;
|
child = child->next;
|
||||||
}
|
}
|
||||||
cg_write_pushs(cg, S("</"), false);
|
cg_write_pushs(cg, S("</"), false);
|
||||||
@@ -2925,14 +2946,17 @@ 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, bool inside_html)
|
||||||
{
|
{
|
||||||
|
// 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:
|
||||||
for (Node *child = node->left;
|
for (Node *child = node->left;
|
||||||
child; child = child->next) {
|
child; child = child->next) {
|
||||||
walk_node(cg, child);
|
walk_node(cg, child, false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -2940,7 +2964,7 @@ static void walk_node(Codegen *cg, Node *node)
|
|||||||
cg_push_scope(cg, SCOPE_COMPOUND);
|
cg_push_scope(cg, SCOPE_COMPOUND);
|
||||||
for (Node *child = node->left;
|
for (Node *child = node->left;
|
||||||
child; child = child->next)
|
child; child = child->next)
|
||||||
walk_node(cg, child);
|
walk_node(cg, child, inside_html);
|
||||||
cg_pop_scope(cg);
|
cg_pop_scope(cg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -2972,10 +2996,10 @@ static void walk_node(Codegen *cg, Node *node)
|
|||||||
int off1 = cg_write_opcode(cg, OPCODE_VARS);
|
int off1 = cg_write_opcode(cg, OPCODE_VARS);
|
||||||
int off2 = cg_write_u8(cg, 0);
|
int off2 = cg_write_u8(cg, 0);
|
||||||
|
|
||||||
walk_node(cg, node->proc_body);
|
walk_node(cg, node->proc_body, false);
|
||||||
cg_write_opcode(cg, OPCODE_RET);
|
cg_write_opcode(cg, OPCODE_RET);
|
||||||
|
|
||||||
cg_patch_u8 (cg, off2, cg->scopes[cg->num_scopes-1].max_vars);
|
cg_patch_u8 (cg, off2, count_function_vars(cg));
|
||||||
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);
|
||||||
@@ -3028,7 +3052,7 @@ static void walk_node(Codegen *cg, Node *node)
|
|||||||
int p1 = cg_write_u32(cg, 0);
|
int p1 = cg_write_u32(cg, 0);
|
||||||
|
|
||||||
cg_push_scope(cg, SCOPE_IF);
|
cg_push_scope(cg, SCOPE_IF);
|
||||||
walk_node(cg, node->if_branch1);
|
walk_node(cg, node->if_branch1, inside_html);
|
||||||
cg_pop_scope(cg);
|
cg_pop_scope(cg);
|
||||||
|
|
||||||
cg_write_opcode(cg, OPCODE_JUMP);
|
cg_write_opcode(cg, OPCODE_JUMP);
|
||||||
@@ -3038,7 +3062,7 @@ static void walk_node(Codegen *cg, Node *node)
|
|||||||
cg_patch_u32(cg, p1, cg_current_offset(cg));
|
cg_patch_u32(cg, p1, cg_current_offset(cg));
|
||||||
|
|
||||||
cg_push_scope(cg, SCOPE_ELSE);
|
cg_push_scope(cg, SCOPE_ELSE);
|
||||||
walk_node(cg, node->if_branch2);
|
walk_node(cg, node->if_branch2, inside_html);
|
||||||
cg_pop_scope(cg);
|
cg_pop_scope(cg);
|
||||||
|
|
||||||
cg_flush_pushs(cg);
|
cg_flush_pushs(cg);
|
||||||
@@ -3052,7 +3076,7 @@ static void walk_node(Codegen *cg, Node *node)
|
|||||||
int p1 = cg_write_u32(cg, 0);
|
int p1 = cg_write_u32(cg, 0);
|
||||||
|
|
||||||
cg_push_scope(cg, SCOPE_IF);
|
cg_push_scope(cg, SCOPE_IF);
|
||||||
walk_node(cg, node->if_branch1);
|
walk_node(cg, node->if_branch1, inside_html);
|
||||||
cg_pop_scope(cg);
|
cg_pop_scope(cg);
|
||||||
|
|
||||||
cg_flush_pushs(cg);
|
cg_flush_pushs(cg);
|
||||||
@@ -3086,7 +3110,7 @@ static void walk_node(Codegen *cg, Node *node)
|
|||||||
cg_write_u8(cg, var_2);
|
cg_write_u8(cg, var_2);
|
||||||
int p = cg_write_u32(cg, 0);
|
int p = cg_write_u32(cg, 0);
|
||||||
|
|
||||||
walk_node(cg, node->left);
|
walk_node(cg, node->left, inside_html);
|
||||||
|
|
||||||
cg_write_opcode(cg, OPCODE_JUMP);
|
cg_write_opcode(cg, OPCODE_JUMP);
|
||||||
cg_write_u32(cg, start);
|
cg_write_u32(cg, start);
|
||||||
@@ -3115,7 +3139,7 @@ static void walk_node(Codegen *cg, Node *node)
|
|||||||
int p = cg_write_u32(cg, 0);
|
int p = cg_write_u32(cg, 0);
|
||||||
|
|
||||||
cg_push_scope(cg, SCOPE_WHILE);
|
cg_push_scope(cg, SCOPE_WHILE);
|
||||||
walk_node(cg, node->left);
|
walk_node(cg, node->left, inside_html);
|
||||||
cg_pop_scope(cg);
|
cg_pop_scope(cg);
|
||||||
|
|
||||||
cg_write_opcode(cg, OPCODE_JUMP);
|
cg_write_opcode(cg, OPCODE_JUMP);
|
||||||
@@ -3126,12 +3150,12 @@ static void walk_node(Codegen *cg, Node *node)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case NODE_INCLUDE:
|
case NODE_INCLUDE:
|
||||||
walk_node(cg, node->include_root);
|
walk_node(cg, node->include_root, false);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
walk_expr_node(cg, node, false);
|
walk_expr_node(cg, node, false);
|
||||||
if (cg_global_scope(cg) && !inside_assignment(cg))
|
if (cg_global_scope(cg) && !inside_assignment(cg) && !inside_html)
|
||||||
cg_write_opcode(cg, OPCODE_OUTPUT);
|
cg_write_opcode(cg, OPCODE_OUTPUT);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3168,7 +3192,7 @@ static int codegen(Node *node, char *dst, int cap, char *errmsg, int 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);
|
||||||
int off = cg_write_u8(&cg, 0);
|
int off = cg_write_u8(&cg, 0);
|
||||||
walk_node(&cg, node);
|
walk_node(&cg, node, false);
|
||||||
cg_write_opcode(&cg, OPCODE_EXIT);
|
cg_write_opcode(&cg, OPCODE_EXIT);
|
||||||
cg_patch_u8(&cg, off, cg.scopes[0].max_vars);
|
cg_patch_u8(&cg, off, cg.scopes[0].max_vars);
|
||||||
cg_pop_scope(&cg);
|
cg_pop_scope(&cg);
|
||||||
@@ -3271,6 +3295,10 @@ static int write_instr(Writer *w, char *src, int len, String data)
|
|||||||
write_text(w, S("GROUP\n"));
|
write_text(w, S("GROUP\n"));
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
case OPCODE_ESCAPE:
|
||||||
|
write_text(w, S("ESCAPE\n"));
|
||||||
|
return 1;
|
||||||
|
|
||||||
case OPCODE_PACK:
|
case OPCODE_PACK:
|
||||||
write_text(w, S("PACK\n"));
|
write_text(w, S("PACK\n"));
|
||||||
return 1;
|
return 1;
|
||||||
@@ -3452,7 +3480,7 @@ static int write_instr(Writer *w, char *src, int len, String data)
|
|||||||
|
|
||||||
static int write_program(WL_Program program, char *dst, int cap)
|
static int write_program(WL_Program program, char *dst, int cap)
|
||||||
{
|
{
|
||||||
if ((uint32_t) program.len < 3 * sizeof(uint32_t))
|
if (program.len < 3 * sizeof(uint32_t))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
@@ -3466,7 +3494,7 @@ static int write_program(WL_Program program, char *dst, int cap)
|
|||||||
if (magic != WL_MAGIC)
|
if (magic != WL_MAGIC)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (code_len + data_len + 3 * sizeof(uint32_t) != (uint32_t) program.len)
|
if (code_len + data_len + 3 * sizeof(uint32_t) != program.len)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
String code = { program.ptr + 3 * sizeof(uint32_t) , code_len };
|
String code = { program.ptr + 3 * sizeof(uint32_t) , code_len };
|
||||||
@@ -3583,9 +3611,7 @@ WL_AddResult wl_compiler_add(WL_Compiler *compiler, WL_String content)
|
|||||||
if (include->include_root == NULL) {
|
if (include->include_root == NULL) {
|
||||||
|
|
||||||
if (compiler->num_files == FILE_LIMIT) {
|
if (compiler->num_files == FILE_LIMIT) {
|
||||||
snprintf(compiler->err, sizeof(compiler->err), "File limit reached");
|
ASSERT(0); // TODO
|
||||||
compiler->err = true;
|
|
||||||
return (WL_AddResult) { .type=WL_ADD_ERROR };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Make the path relative to the compiled file
|
// TODO: Make the path relative to the compiled file
|
||||||
@@ -4449,29 +4475,7 @@ static void value_convert_to_str_inner(Writer *w, Value v)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case TYPE_MAP:
|
case TYPE_MAP:
|
||||||
{
|
write_text(w, S("<map>"));
|
||||||
write_text(w, S("{"));
|
|
||||||
AggregateValue *agg = (void*) (v & ~(Value) 7);
|
|
||||||
for (int i = 0; i < agg->count; i += 2) {
|
|
||||||
value_convert_to_str_inner(w, agg->vals[i+0]);
|
|
||||||
write_text(w, S(": "));
|
|
||||||
value_convert_to_str_inner(w, agg->vals[i+1]);
|
|
||||||
if (i+2 < agg->count || agg->ext)
|
|
||||||
write_text(w, S(", "));
|
|
||||||
}
|
|
||||||
Extension *ext = agg->ext;
|
|
||||||
while (ext) {
|
|
||||||
for (int i = 0; i < ext->count; i += 2) {
|
|
||||||
value_convert_to_str_inner(w, ext->vals[i+0]);
|
|
||||||
write_text(w, S(": "));
|
|
||||||
value_convert_to_str_inner(w, ext->vals[i+1]);
|
|
||||||
if (i+2 < ext->count || ext->next)
|
|
||||||
write_text(w, S(", "));
|
|
||||||
}
|
|
||||||
ext = ext->next;
|
|
||||||
}
|
|
||||||
write_text(w, S("}"));
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TYPE_ERROR:
|
case TYPE_ERROR:
|
||||||
@@ -4486,6 +4490,140 @@ static int value_convert_to_str(Value v, char *dst, int cap)
|
|||||||
return w.len;
|
return w.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Value value_escape_packed(Value v, WL_Arena *arena, Error *err);
|
||||||
|
|
||||||
|
static int array_escape(Value v, Value *out, int max, WL_Arena *arena, Error *err)
|
||||||
|
{
|
||||||
|
Value v2 = value_empty_array(value_length(v), arena, err);
|
||||||
|
if (v2 == VALUE_ERROR) return -1;
|
||||||
|
|
||||||
|
AggregateValue *src = (void*) (v & ~(Value) 7);
|
||||||
|
|
||||||
|
for (int i = 0; i < src->count; i++) {
|
||||||
|
|
||||||
|
Value child = src->vals[i];
|
||||||
|
|
||||||
|
Value escaped_child = value_escape_packed(child, arena, err);
|
||||||
|
if (escaped_child == VALUE_ERROR)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!value_append(v2, escaped_child, arena, err))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
Extension *ext = src->ext;
|
||||||
|
while (ext) {
|
||||||
|
for (int i = 0; i < ext->count; i++) {
|
||||||
|
|
||||||
|
Value child = src->vals[i];
|
||||||
|
|
||||||
|
Value escaped_child = value_escape_packed(child, arena, err);
|
||||||
|
if (escaped_child == VALUE_ERROR)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!value_append(v2, escaped_child, arena, err))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ext = ext->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max == 0)
|
||||||
|
return -1;
|
||||||
|
out[0] = v2;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int string_escape(Value v, Value *out, int max, WL_Arena *arena, Error *err)
|
||||||
|
{
|
||||||
|
String s = value_to_str(v);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
int num = 0;
|
||||||
|
for (;;) {
|
||||||
|
|
||||||
|
int off = i;
|
||||||
|
while (i < s.len
|
||||||
|
&& s.ptr[i] != '<'
|
||||||
|
&& s.ptr[i] != '>'
|
||||||
|
&& s.ptr[i] != '&'
|
||||||
|
&& s.ptr[i] != '"'
|
||||||
|
&& s.ptr[i] != '\'')
|
||||||
|
i++;
|
||||||
|
String substr = { s.ptr + off, i - off };
|
||||||
|
|
||||||
|
Value escaped_v = value_from_str(substr, arena, err); // TODO: don't copy the string
|
||||||
|
if (escaped_v == VALUE_ERROR) return -1;
|
||||||
|
|
||||||
|
if (num == max) {
|
||||||
|
REPORT(err, "Escape buffer limit reached");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
out[num++] = escaped_v;
|
||||||
|
|
||||||
|
if (i == s.len) break;
|
||||||
|
|
||||||
|
switch (s.ptr[i++]) {
|
||||||
|
case '<' : escaped_v = value_from_str(S("<"), arena, err); break; // TODO: don't come these strings
|
||||||
|
case '>' : escaped_v = value_from_str(S(">"), arena, err); break;
|
||||||
|
case '&' : escaped_v = value_from_str(S("&"), arena, err); break;
|
||||||
|
case '"' : escaped_v = value_from_str(S("""), arena, err); break;
|
||||||
|
case '\'': escaped_v = value_from_str(S("'"), arena, err); break;
|
||||||
|
}
|
||||||
|
if (escaped_v == VALUE_ERROR) return -1;
|
||||||
|
|
||||||
|
if (num == max) {
|
||||||
|
REPORT(err, "Escape buffer limit reached");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
out[num++] = escaped_v;
|
||||||
|
}
|
||||||
|
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int value_escape(Value v, Value *out, int max, WL_Arena *arena, Error *err)
|
||||||
|
{
|
||||||
|
Type t = value_type(v);
|
||||||
|
|
||||||
|
if (t == TYPE_ARRAY)
|
||||||
|
return array_escape(v, out, max, arena, err);
|
||||||
|
|
||||||
|
if (t == TYPE_STRING)
|
||||||
|
return string_escape(v, out, max, arena, err);
|
||||||
|
|
||||||
|
if (max < 1)
|
||||||
|
return -1;
|
||||||
|
out[0] = v;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value value_escape_packed(Value v, WL_Arena *arena, Error *err)
|
||||||
|
{
|
||||||
|
Value tmp[32];
|
||||||
|
int num = value_escape(v, tmp, COUNT(tmp), arena, err);
|
||||||
|
if (num < 0) return VALUE_ERROR;
|
||||||
|
|
||||||
|
Value escaped_v;
|
||||||
|
|
||||||
|
if (num > 1) {
|
||||||
|
|
||||||
|
Value packed = value_empty_array(num, arena, err);
|
||||||
|
if (packed == VALUE_ERROR)
|
||||||
|
return VALUE_ERROR;
|
||||||
|
|
||||||
|
for (int j = 0; j < num; j++)
|
||||||
|
if (!value_append(packed, tmp[j], arena, err))
|
||||||
|
return VALUE_ERROR;
|
||||||
|
escaped_v = packed;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
ASSERT(num == 1);
|
||||||
|
escaped_v = tmp[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return escaped_v;
|
||||||
|
}
|
||||||
|
|
||||||
#undef TYPE_PAIR
|
#undef TYPE_PAIR
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
@@ -4543,7 +4681,7 @@ struct WL_Runtime {
|
|||||||
|
|
||||||
WL_Runtime *wl_runtime_init(WL_Arena *arena, WL_Program program)
|
WL_Runtime *wl_runtime_init(WL_Arena *arena, WL_Program program)
|
||||||
{
|
{
|
||||||
if ((uint32_t) program.len < 3 * sizeof(uint32_t))
|
if (program.len < 3 * sizeof(uint32_t))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
@@ -4645,7 +4783,7 @@ static String rt_read_str(WL_Runtime *rt)
|
|||||||
ASSERT(rt->state == RUNTIME_LOOP);
|
ASSERT(rt->state == RUNTIME_LOOP);
|
||||||
uint32_t off = rt_read_u32(rt);
|
uint32_t off = rt_read_u32(rt);
|
||||||
uint32_t len = rt_read_u32(rt);
|
uint32_t len = rt_read_u32(rt);
|
||||||
ASSERT(off + len <= (uint32_t) rt->data.len);
|
ASSERT(off + len <= rt->data.len);
|
||||||
return (String) { rt->data.ptr + off, len };
|
return (String) { rt->data.ptr + off, len };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4857,6 +4995,37 @@ static void step(WL_Runtime *rt)
|
|||||||
rt_push_group(rt);
|
rt_push_group(rt);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OPCODE_ESCAPE:
|
||||||
|
{
|
||||||
|
ASSERT(rt->num_groups > 0);
|
||||||
|
int start = rt->groups[--rt->num_groups];
|
||||||
|
int end = rt->stack;
|
||||||
|
|
||||||
|
Value escaped[256];
|
||||||
|
int num_escaped = 0;
|
||||||
|
|
||||||
|
for (int i = start; i < end; i++) {
|
||||||
|
Value v = rt->values[i];
|
||||||
|
int num = value_escape(v, escaped + num_escaped, COUNT(escaped) - num_escaped, rt->arena, &rt->err);
|
||||||
|
if (num < 0) break;
|
||||||
|
num_escaped += num;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_escaped > COUNT(escaped)) {
|
||||||
|
REPORT(&rt->err, "Escape buffer limit reached");
|
||||||
|
rt->state = RUNTIME_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rt->stack = start;
|
||||||
|
if (!rt_check_stack(rt, num_escaped)) break;
|
||||||
|
|
||||||
|
for (int i = 0; i < num_escaped; i++)
|
||||||
|
rt->values[rt->stack + i] = escaped[i];
|
||||||
|
rt->stack += num_escaped;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case OPCODE_PACK:
|
case OPCODE_PACK:
|
||||||
rt_pack_group(rt);
|
rt_pack_group(rt);
|
||||||
break;
|
break;
|
||||||
@@ -5156,7 +5325,6 @@ WL_EvalResult wl_runtime_eval(WL_Runtime *rt)
|
|||||||
|
|
||||||
switch (rt->state) {
|
switch (rt->state) {
|
||||||
|
|
||||||
case RUNTIME_BEGIN:
|
|
||||||
case RUNTIME_LOOP:
|
case RUNTIME_LOOP:
|
||||||
UNREACHABLE;
|
UNREACHABLE;
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -179,13 +179,13 @@ let main =
|
|||||||
<main>
|
<main>
|
||||||
<div class="thread-header">
|
<div class="thread-header">
|
||||||
<div class="thread-title">
|
<div class="thread-title">
|
||||||
<span>\post.title</span>
|
<span>\escape(post.title)</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="thread-meta">
|
<div class="thread-meta">
|
||||||
submitted 3 hours ago by <a href="">\post.username</a> | <a href="">\len comments</a>
|
submitted 3 hours ago by <a href="">\escape(post.username)</a> | <a href="">\len(comments)</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="thread-text">
|
<div class="thread-text">
|
||||||
<pre>\post.content</pre>
|
<pre>\escape(post.content)</pre>
|
||||||
</div>
|
</div>
|
||||||
<details>
|
<details>
|
||||||
<summary>
|
<summary>
|
||||||
@@ -209,10 +209,10 @@ let main =
|
|||||||
</div>
|
</div>
|
||||||
<div class="comment-content">
|
<div class="comment-content">
|
||||||
<div class="comment-meta">
|
<div class="comment-meta">
|
||||||
<a href="">\comment.username</a> 2 hours ago
|
<a href="">\escape(comment.username)</a> 2 hours ago
|
||||||
</div>
|
</div>
|
||||||
<div class="comment-text">
|
<div class="comment-text">
|
||||||
<pre>\comment.content</pre>
|
<pre>\escape(comment.content)</pre>
|
||||||
</div>
|
</div>
|
||||||
\if $login_user_id != none:
|
\if $login_user_id != none:
|
||||||
<details>
|
<details>
|
||||||
@@ -235,7 +235,7 @@ let main =
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
\if len root_comments == 0:
|
\if len(root_comments) == 0:
|
||||||
<div id="no-comments">
|
<div id="no-comments">
|
||||||
No comments
|
No comments
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user