Comment support

This commit is contained in:
2025-08-13 13:21:54 +02:00
parent 078f9eb319
commit 67d1eb2304
7 changed files with 478 additions and 96 deletions
+157 -53
View File
@@ -1,6 +1,6 @@
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "wl.h"
@@ -273,6 +273,7 @@ typedef enum {
TOKEN_OPER_DIV,
TOKEN_OPER_MOD,
TOKEN_OPER_ASS,
TOKEN_OPER_SHOVEL,
TOKEN_PAREN_OPEN,
TOKEN_PAREN_CLOSE,
TOKEN_BRACKET_OPEN,
@@ -319,6 +320,7 @@ typedef enum {
NODE_OPER_MUL,
NODE_OPER_DIV,
NODE_OPER_MOD,
NODE_OPER_SHOVEL,
NODE_VALUE_INT,
NODE_VALUE_FLOAT,
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_DIV : 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_CLOSE : 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 } };
}
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_NQL };
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:
return 1;
case TOKEN_OPER_SHOVEL:
if (flags & IGNORE_LSS)
return -1;
return 1;
case TOKEN_OPER_EQL:
case TOKEN_OPER_NQL:
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_DIV: parent->type = NODE_OPER_DIV; break;
case TOKEN_OPER_MOD: parent->type = NODE_OPER_MOD; break;
case TOKEN_OPER_SHOVEL: parent->type = NODE_OPER_SHOVEL; break;
default:
parser_report(p, "Operator not implemented");
return NULL;
@@ -2485,6 +2495,8 @@ static void cg_pop_scope(Codegen *cg)
ASSERT(cg->num_scopes > 0);
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;
if (cg->num_scopes > 1)
parent_scope = &cg->scopes[cg->num_scopes-2];
@@ -2494,6 +2506,8 @@ static void cg_pop_scope(Codegen *cg)
UnpatchedCall *call = scope->calls;
scope->calls = call->next;
ASSERT(call - cg->calls >= 0 && call - cg->calls < MAX_UNPATCHED_CALLS);
Symbol *sym = cg_find_symbol(cg, call->name, true);
if (sym == NULL) {
@@ -2514,6 +2528,12 @@ static void cg_pop_scope(Codegen *cg)
}
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;
@@ -2531,9 +2551,13 @@ static void cg_append_unpatched_call(Codegen *cg, String name, int p)
UnpatchedCall *call = cg->free_list_calls;
cg->free_list_calls = call->next;
call->name = name;
call->off = p;
ASSERT(call - cg->calls >= 0 && call - cg->calls < MAX_UNPATCHED_CALLS);
call->name = name;
call->off = p;
call->next = NULL;
ASSERT(cg->num_scopes > 0);
Scope *scope = &cg->scopes[cg->num_scopes-1];
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)
{
// 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) {
case NODE_NESTED:
@@ -2655,6 +2682,23 @@ static void walk_expr_node(Codegen *cg, Node *node, bool one)
}
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:
walk_expr_node(cg, node->left, 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)
{
// 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) {
case NODE_GLOBAL:
@@ -2919,7 +2966,7 @@ static void walk_node(Codegen *cg, Node *node)
walk_node(cg, node->proc_body);
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_pop_scope(cg);
@@ -3081,12 +3128,22 @@ static void walk_node(Codegen *cg, Node *node)
}
}
static Codegen init_codegen(char *codebuf, int codecap,
char *databuf, int datacap, char *errmsg, int errcap)
#define WL_MAGIC 0xFEEDBEEF
static int codegen(Node *node, char *dst, int cap, char *errmsg, int errcap)
{
Codegen c = {
.code = { codebuf, codecap, 0 },
.data = { databuf, datacap, 0 },
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 = {
.code = { dst, cap/2, 0 },
.data = { dst + cap/2, cap/2, 0 },
.num_scopes = 0,
.err = false,
.errmsg = errmsg,
@@ -3094,28 +3151,10 @@ static Codegen init_codegen(char *codebuf, int codecap,
.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++)
c.calls[i].next = &c.calls[i+1];
c.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.calls[i].next = &cg.calls[i+1];
cg.calls[MAX_UNPATCHED_CALLS-1].next = NULL;
cg_push_scope(&cg, SCOPE_GLOBAL);
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);
}
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)
@@ -3392,12 +3431,17 @@ static int write_instr(Writer *w, char *src, int len, String data)
case OPCODE_SELECT:
write_text(w, S("SELECT\n"));
return 1;
default:
write_text(w, S("byte "));
write_text_s64(w, src[0]);
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))
return -1;
@@ -3433,6 +3477,30 @@ int wl_dump_program(WL_Program program, char *dst, int cap)
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
/////////////////////////////////////////////////////////////////////////
@@ -3969,17 +4037,19 @@ static Value value_select(Value set, Value key, Error *err)
return VALUE_ERROR;
}
char keybuf[1<<8];
int keylen = value_convert_to_str(key, keybuf, SIZEOF(keybuf));
keylen = MIN(keylen, SIZEOF(keybuf)-1);
keybuf[keylen] = '\0';
char key_buf[1<<8];
int key_len = value_convert_to_str(key, key_buf, SIZEOF(key_buf));
if (key_len > SIZEOF(key_buf)-1)
key_len = SIZEOF(key_buf)-1;
key_buf[key_len] = '\0';
char setbuf[1<<8];
int setlen = value_convert_to_str(set, setbuf, SIZEOF(setbuf));
setlen = MIN(setlen, SIZEOF(setbuf)-1);
setbuf[setlen] = '\0';
char set_buf[1<<8];
int set_len = value_convert_to_str(set, set_buf, SIZEOF(set_buf));
if (set_len > SIZEOF(set_buf)-1)
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;
}
@@ -4680,6 +4750,21 @@ static void rt_pop_group(WL_Runtime *rt)
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)
{
switch (rt_read_u8(rt)) {
@@ -4872,14 +4957,6 @@ static void step(WL_Runtime *rt)
v1 = rt->values[rt->stack-1];
t = value_type(v1);
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");
rt->state = RUNTIME_ERROR;
break;
@@ -5090,11 +5167,17 @@ WL_EvalResult wl_runtime_eval(WL_Runtime *rt)
else {
int len = value_convert_to_str(v, rt->buf, SIZEOF(rt->buf));
if (len > SIZEOF(rt->buf)) {
char *ptr = alloc(rt->arena, len, 1);
len = value_convert_to_str(v, ptr, len);
str = (String) { ptr, len };
} else
char *p = alloc(rt->arena, len, 1);
if (p == NULL) {
REPORT(&rt->err, "Out of memory");
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 };
}
}
rt->cur_output++;
@@ -5531,3 +5614,24 @@ void wl_append(WL_Runtime *rt)
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");
}
+2 -1
View File
@@ -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);
WL_String wl_compiler_error (WL_Compiler *compiler);
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_EvalResult wl_runtime_eval (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);
int wl_arg_count (WL_Runtime *rt);
+1
View File
@@ -2,6 +2,7 @@
procedure page(title, login_user_id, style, main)
<html>
<head>
<meta charset="UTF-8" />
<title>CN - \title</title>
<style>
body {
+220 -29
View File
@@ -1,51 +1,242 @@
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 FROM Comments as C, Users as U WHERE C.parent_post=? AND U.id == C.author", $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, 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 style =
<style>
.child {
border-left: 3px solid #ccc;
.thread-header {
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;
}
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%;
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>
let main =
<main>
<h3>\post.title</h3>
<p>\post.content</p>
<div>
\if $login_user_id != none:
<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 class="thread-header">
<div class="thread-title">
<span>\post.title</span>
</div>
<div class="thread-meta">
submitted 3 hours ago by <a href="">\post.username</a> | <a href="">\len comments</a>
</div>
<div class="thread-text">
\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>
\if len comments == 0:
<span>No comments yet!</span>
else for comment in comments:
<div>
<a href="">\comment.username</a>
<p>
\comment.content
</p>
<div class="child">
\procedure render_comment(comment)
<div class="comment">
<div class="vote-buttons">
<a href=""></a>
<a href=""></a>
</div>
<div class="comment-content">
<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:
<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"></textarea>
<input type="submit" vaue="Publish" />
</form>
<details>
<summary>
reply
</summary>
<div class="add-comment">
<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>
\if len root_comments == 0:
<span>(No comments)</span>
else for comment in root_comments:
render_comment(comment)
</main>
page(post.title, $login_user_id, style, main)
+73
View File
@@ -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)
-5
View File
@@ -1,5 +0,0 @@
<ul>
<li>1. Post A</li>
<li>2. Post B</li>
<li>3. Post C</li>
</ul>
+23 -6
View File
@@ -876,6 +876,7 @@ int main(void)
}
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);
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);
if (!valid_comment_content(content)) {
assert(0); // TODO
@@ -902,8 +918,12 @@ int main(void)
assert(0); // TODO
}
int ret;
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) {
assert(0); // TODO
}
@@ -1009,13 +1029,10 @@ int main(void)
assert(0); // TODO
}
if (num == 0) {
printf("post with id %d does not exist\n", post_id); // TODO
if (num == 0)
evaluate_template_2(builder, arena, "pages/notfound.wl", user_id, -1);
} else {
printf("Evaluating template\n");
else
evaluate_template_2(builder, arena, "pages/post.wl", user_id, post_id);
}
} else {