Update WL
This commit is contained in:
@@ -1,20 +1,7 @@
|
|||||||
#ifndef WL_AMALGAMATION
|
#include <stdint.h>
|
||||||
#define WL_AMALGAMATION
|
|
||||||
|
|
||||||
// This file was generated automatically. Do not modify directly!
|
typedef struct WL_Runtime WL_Runtime;
|
||||||
|
typedef struct WL_Compiler WL_Compiler;
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// src/compile.h
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#ifndef WL_PUBLIC_INCLUDED
|
|
||||||
#define WL_PUBLIC_INCLUDED
|
|
||||||
|
|
||||||
#ifndef WL_AMALGAMATION
|
|
||||||
#include "includes.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct WL_State WL_State;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *ptr;
|
char *ptr;
|
||||||
@@ -24,70 +11,78 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
char *ptr;
|
char *ptr;
|
||||||
int len;
|
int len;
|
||||||
} WL_Program;
|
int cur;
|
||||||
|
} WL_Arena;
|
||||||
typedef enum {
|
|
||||||
WL_DONE,
|
|
||||||
WL_ERROR,
|
|
||||||
WL_VAR,
|
|
||||||
WL_CALL,
|
|
||||||
WL_OUTPUT,
|
|
||||||
} WL_ResultType;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
WL_ResultType type;
|
|
||||||
WL_String str;
|
|
||||||
} WL_Result;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *ptr;
|
char *ptr;
|
||||||
int len;
|
int len;
|
||||||
int cur;
|
} WL_Program;
|
||||||
} WL_Arena;
|
|
||||||
|
|
||||||
typedef struct WL_Compiler WL_Compiler;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
WL_COMPILE_RESULT_DONE,
|
WL_ADD_ERROR,
|
||||||
WL_COMPILE_RESULT_FILE,
|
WL_ADD_AGAIN,
|
||||||
WL_COMPILE_RESULT_ERROR,
|
WL_ADD_LINK,
|
||||||
} WL_CompileResultType;
|
} WL_AddResultType;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
WL_CompileResultType type;
|
WL_AddResultType type;
|
||||||
WL_Program program;
|
WL_String path;
|
||||||
WL_String path;
|
} WL_AddResult;
|
||||||
} WL_CompileResult;
|
|
||||||
|
|
||||||
WL_Compiler* WL_Compiler_init (WL_Arena *arena);
|
typedef enum {
|
||||||
void WL_Compiler_free (WL_Compiler *compiler);
|
WL_EVAL_NONE,
|
||||||
WL_CompileResult WL_compile (WL_Compiler *compiler, WL_String file, WL_String content);
|
WL_EVAL_DONE,
|
||||||
WL_State* WL_State_init (WL_Arena *a, WL_Program p, char *err, int errmax);
|
WL_EVAL_ERROR,
|
||||||
void WL_State_free (WL_State *state);
|
WL_EVAL_OUTPUT,
|
||||||
void WL_State_trace (WL_State *state, int trace);
|
WL_EVAL_SYSVAR,
|
||||||
WL_Result WL_eval (WL_State *state);
|
WL_EVAL_SYSCALL,
|
||||||
|
} WL_EvalResultType;
|
||||||
|
|
||||||
void WL_dump_program(WL_Program program);
|
typedef struct {
|
||||||
|
WL_EvalResultType type;
|
||||||
|
WL_String str;
|
||||||
|
} WL_EvalResult;
|
||||||
|
|
||||||
int WL_streq (WL_String a, char *b, int blen);
|
WL_Compiler* wl_compiler_init (WL_Arena *arena);
|
||||||
int WL_peeknone (WL_State *state, int off);
|
WL_AddResult wl_compiler_add (WL_Compiler *compiler, WL_String content);
|
||||||
int WL_peekint (WL_State *state, int off, long long *x);
|
int wl_compiler_link (WL_Compiler *compiler, WL_Program *program);
|
||||||
int WL_peekfloat (WL_State *state, int off, float *x);
|
WL_String wl_compiler_error (WL_Compiler *compiler);
|
||||||
int WL_peekstr (WL_State *state, int off, WL_String *str);
|
int wl_dump_ast (WL_Compiler *compiler, char *dst, int cap);
|
||||||
int WL_popnone (WL_State *state);
|
int wl_dump_program (WL_Program program, char *dst, int cap);
|
||||||
int WL_popint (WL_State *state, long long *x);
|
|
||||||
int WL_popfloat (WL_State *state, float *x);
|
|
||||||
int WL_popstr (WL_State *state, WL_String *str);
|
|
||||||
int WL_popany (WL_State *state);
|
|
||||||
void WL_select (WL_State *state);
|
|
||||||
void WL_pushnone (WL_State *state);
|
|
||||||
void WL_pushint (WL_State *state, long long x);
|
|
||||||
void WL_pushfloat (WL_State *state, float x);
|
|
||||||
void WL_pushstr (WL_State *state, WL_String str);
|
|
||||||
void WL_pusharray (WL_State *state, int cap);
|
|
||||||
void WL_pushmap (WL_State *state, int cap);
|
|
||||||
void WL_insert (WL_State *state);
|
|
||||||
void WL_append (WL_State *state);
|
|
||||||
|
|
||||||
#endif // WL_PUBLIC_INCLUDED
|
WL_Runtime* wl_runtime_init (WL_Arena *arena, WL_Program program);
|
||||||
#endif // WL_AMALGAMATION
|
WL_EvalResult wl_runtime_eval (WL_Runtime *rt);
|
||||||
|
WL_String wl_runtime_error (WL_Runtime *rt);
|
||||||
|
|
||||||
|
bool wl_streq (WL_String a, char *b, int blen);
|
||||||
|
int wl_arg_count (WL_Runtime *rt);
|
||||||
|
bool wl_arg_none (WL_Runtime *rt, int idx);
|
||||||
|
bool wl_arg_bool (WL_Runtime *rt, int idx, bool *x);
|
||||||
|
bool wl_arg_s64 (WL_Runtime *rt, int idx, int64_t *x);
|
||||||
|
bool wl_arg_f64 (WL_Runtime *rt, int idx, double *x);
|
||||||
|
bool wl_arg_str (WL_Runtime *rt, int idx, WL_String *x);
|
||||||
|
bool wl_arg_array (WL_Runtime *rt, int idx);
|
||||||
|
bool wl_arg_map (WL_Runtime *rt, int idx);
|
||||||
|
bool wl_peek_none (WL_Runtime *rt, int off);
|
||||||
|
bool wl_peek_bool (WL_Runtime *rt, int off, bool *x);
|
||||||
|
bool wl_peek_s64 (WL_Runtime *rt, int off, int64_t *x);
|
||||||
|
bool wl_peek_f64 (WL_Runtime *rt, int off, double *x);
|
||||||
|
bool wl_peek_str (WL_Runtime *rt, int off, WL_String *x);
|
||||||
|
bool wl_pop_any (WL_Runtime *rt);
|
||||||
|
bool wl_pop_none (WL_Runtime *rt);
|
||||||
|
bool wl_pop_bool (WL_Runtime *rt, bool *x);
|
||||||
|
bool wl_pop_s64 (WL_Runtime *rt, int64_t *x);
|
||||||
|
bool wl_pop_f64 (WL_Runtime *rt, double *x);
|
||||||
|
bool wl_pop_str (WL_Runtime *rt, WL_String *x);
|
||||||
|
void wl_push_none (WL_Runtime *rt);
|
||||||
|
void wl_push_true (WL_Runtime *rt);
|
||||||
|
void wl_push_false (WL_Runtime *rt);
|
||||||
|
void wl_push_s64 (WL_Runtime *rt, int64_t x);
|
||||||
|
void wl_push_f64 (WL_Runtime *rt, double x);
|
||||||
|
void wl_push_str (WL_Runtime *rt, WL_String x);
|
||||||
|
void wl_push_array (WL_Runtime *rt, int cap);
|
||||||
|
void wl_push_map (WL_Runtime *rt, int cap);
|
||||||
|
void wl_push_arg (WL_Runtime *rt, int idx);
|
||||||
|
void wl_insert (WL_Runtime *rt);
|
||||||
|
void wl_append (WL_Runtime *rt);
|
||||||
|
|||||||
+2
-2
@@ -48,10 +48,10 @@ let main =
|
|||||||
|
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<div>
|
<div>
|
||||||
<a href=link>\post.title</a>
|
<a href=\'"'\link\'"'>\post.title</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span>\post.date</span> | <span><a href=link>\post.num_comments comments</a></span>
|
<span>\post.date</span> | <span><a href=\'"'\link\'"'>\post.num_comments comments</a></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
fun page(title, login_user_id, style, main)
|
procedure page(title, login_user_id, style, main)
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>CN - \title</title>
|
<title>CN - \title</title>
|
||||||
|
|||||||
@@ -1,16 +1,51 @@
|
|||||||
include "pages/page.wl"
|
include "pages/page.wl"
|
||||||
|
|
||||||
let posts = $query("SELECT title, content FROM Posts WHERE id=?", $post_id)
|
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 post = posts[0]
|
let post = posts[0]
|
||||||
|
|
||||||
let style =
|
let style =
|
||||||
<style>
|
<style>
|
||||||
|
.child {
|
||||||
|
border-left: 3px solid #ccc;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
form textarea {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
let main =
|
let main =
|
||||||
<main>
|
<main>
|
||||||
<h3>\post.title</h3>
|
<h3>\post.title</h3>
|
||||||
<p>\post.content</p>
|
<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>
|
||||||
|
\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">
|
||||||
|
\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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
page(post.title, $login_user_id, style, main)
|
page(post.title, $login_user_id, style, main)
|
||||||
+273
-158
@@ -6,7 +6,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "sqlite3.h"
|
#include "sqlite3.h"
|
||||||
#include "chttp.h"
|
#include "chttp.h"
|
||||||
#include "WL.h"
|
#include "wl.h"
|
||||||
#include "sqlite3utils.h"
|
#include "sqlite3utils.h"
|
||||||
|
|
||||||
sqlite3 *db;
|
sqlite3 *db;
|
||||||
@@ -29,186 +29,158 @@ int load_file(char *file, char **data, long *size)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int query_routine(WL_State *state)
|
static int query_routine(WL_Runtime *rt)
|
||||||
{
|
{
|
||||||
long long num_args;
|
int num_args = wl_arg_count(rt);
|
||||||
if (!WL_popint(state, &num_args)) {
|
if (num_args == 0)
|
||||||
assert(0); // TODO
|
return 0;
|
||||||
}
|
|
||||||
if (num_args == 0) {
|
|
||||||
assert(0); // TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
WL_String format;
|
WL_String format;
|
||||||
if (!WL_peekstr(state, -num_args, &format)) {
|
if (!wl_arg_str(rt, 0, &format))
|
||||||
assert(0); // TODO
|
return -1;
|
||||||
}
|
|
||||||
|
|
||||||
sqlite3_stmt *res;
|
sqlite3_stmt *stmt;
|
||||||
int ret = sqlite3_prepare_v2(db, format.ptr, format.len, &res, 0);
|
int ret = sqlite3_prepare_v2(db, format.ptr, format.len, &stmt, 0);
|
||||||
if (ret != SQLITE_OK) {
|
if (ret != SQLITE_OK) {
|
||||||
fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db));
|
fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db));
|
||||||
assert(0); // TODO
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < num_args-1; i++) {
|
for (int i = 1; i < num_args; i++) {
|
||||||
|
|
||||||
|
int64_t ival;
|
||||||
|
double fval;
|
||||||
|
WL_String str;
|
||||||
|
|
||||||
|
if (0) {}
|
||||||
|
else if (wl_arg_none(rt, i))
|
||||||
|
ret = sqlite3_bind_null (stmt, i);
|
||||||
|
else if (wl_arg_s64(rt, i, &ival))
|
||||||
|
ret = sqlite3_bind_int64 (stmt, i, ival);
|
||||||
|
else if (wl_arg_f64(rt, i, &fval))
|
||||||
|
ret = sqlite3_bind_double(stmt, i, fval);
|
||||||
|
else if (wl_arg_str(rt, i, &str))
|
||||||
|
ret = sqlite3_bind_text (stmt, i, str.ptr, str.len, NULL);
|
||||||
|
else assert(0);
|
||||||
|
|
||||||
if (ret != SQLITE_OK) {
|
if (ret != SQLITE_OK) {
|
||||||
fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db));
|
fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db));
|
||||||
|
sqlite3_finalize(stmt);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_push_array(rt, 0);
|
||||||
|
|
||||||
|
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||||
|
|
||||||
|
int num_cols = sqlite3_column_count(stmt);
|
||||||
|
if (num_cols < 0) {
|
||||||
assert(0); // TODO
|
assert(0); // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WL_peeknone(state, -num_args + i + 1)) {
|
wl_push_map(rt, num_cols);
|
||||||
ret = sqlite3_bind_null(res, i+1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
long long ival;
|
for (int i = 0; i < num_cols; i++) {
|
||||||
if (WL_peekint(state, -num_args + i + 1, &ival)) {
|
ret = sqlite3_column_type(stmt, i);
|
||||||
ret = sqlite3_bind_int64(res, i+1, ival);
|
switch (ret) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
float fval;
|
case SQLITE_INTEGER:
|
||||||
if (WL_peekfloat(state, -num_args + i + 1, &fval)) {
|
{
|
||||||
ret = sqlite3_bind_double(res, i+1, fval);
|
int64_t x = sqlite3_column_int64(stmt, i);
|
||||||
continue;
|
wl_push_s64(rt, x);
|
||||||
}
|
|
||||||
|
|
||||||
WL_String str;
|
|
||||||
if (WL_peekstr(state, -num_args + i + 1, &str)) {
|
|
||||||
ret = sqlite3_bind_text(res, i+1, str.ptr, str.len, NULL);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = sqlite3_step(res);
|
|
||||||
if (ret == SQLITE_DONE) {
|
|
||||||
WL_pushnone(state);
|
|
||||||
} else if (ret != SQLITE_ROW) {
|
|
||||||
WL_pushnone(state);
|
|
||||||
} else {
|
|
||||||
|
|
||||||
WL_pusharray(state, 0);
|
|
||||||
do {
|
|
||||||
|
|
||||||
int num_cols = sqlite3_column_count(res);
|
|
||||||
if (num_cols < 0) {
|
|
||||||
assert(0); // TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
WL_pushmap(state, 0);
|
|
||||||
|
|
||||||
for (int i = 0; i < num_cols; i++) {
|
|
||||||
ret = sqlite3_column_type(res, i);
|
|
||||||
switch (ret) {
|
|
||||||
|
|
||||||
case SQLITE_INTEGER:
|
|
||||||
{
|
|
||||||
int64_t x = sqlite3_column_int64(res, i);
|
|
||||||
WL_pushint(state, x);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SQLITE_FLOAT:
|
|
||||||
{
|
|
||||||
double x = sqlite3_column_double(res, i);
|
|
||||||
WL_pushfloat(state, x);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SQLITE_TEXT:
|
|
||||||
{
|
|
||||||
const void *x = sqlite3_column_text(res, i);
|
|
||||||
int n = sqlite3_column_bytes(res, i);
|
|
||||||
WL_pushstr(state, (WL_String) { (char*) x, n });
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SQLITE_BLOB:
|
|
||||||
{
|
|
||||||
const void *x = sqlite3_column_blob(res, i);
|
|
||||||
int n = sqlite3_column_bytes(res, i);
|
|
||||||
WL_pushstr(state, (WL_String) { (char*) x, n });
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SQLITE_NULL:
|
|
||||||
{
|
|
||||||
WL_pushnone(state);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
const char *name = sqlite3_column_name(res, i);
|
case SQLITE_FLOAT:
|
||||||
|
{
|
||||||
|
double x = sqlite3_column_double(stmt, i);
|
||||||
|
wl_push_f64(rt, x);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
WL_pushstr(state, (WL_String) { (char*) name, strlen(name) });
|
case SQLITE_TEXT:
|
||||||
WL_insert(state);
|
{
|
||||||
|
const void *x = sqlite3_column_text(stmt, i);
|
||||||
|
int n = sqlite3_column_bytes(stmt, i);
|
||||||
|
wl_push_str(rt, (WL_String) { (char*) x, n });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQLITE_BLOB:
|
||||||
|
{
|
||||||
|
const void *x = sqlite3_column_blob(stmt, i);
|
||||||
|
int n = sqlite3_column_bytes(stmt, i);
|
||||||
|
wl_push_str(rt, (WL_String) { (char*) x, n });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SQLITE_NULL:
|
||||||
|
{
|
||||||
|
wl_push_none(rt);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
WL_append(state);
|
const char *name = sqlite3_column_name(stmt, i);
|
||||||
|
|
||||||
} while (sqlite3_step(res) == SQLITE_ROW);
|
wl_push_str(rt, (WL_String) { (char*) name, strlen(name) });
|
||||||
|
wl_insert(rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_append(rt);
|
||||||
}
|
}
|
||||||
|
|
||||||
WL_pushint(state, 1);
|
sqlite3_finalize(stmt);
|
||||||
|
|
||||||
sqlite3_finalize(res);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int evaluate_template(HTTP_ResponseBuilder builder,
|
int evaluate_template(HTTP_ResponseBuilder builder,
|
||||||
WL_Program program, WL_Arena arena,
|
WL_Program program, WL_Arena arena, int user_id, int post_id)
|
||||||
char *err, int errmax, int user_id, int post_id)
|
|
||||||
{
|
{
|
||||||
//WL_dump_program(program);
|
//wl_dump_program(program);
|
||||||
|
|
||||||
WL_State *state = WL_State_init(&arena, program, err, errmax);
|
WL_Runtime *rt = wl_runtime_init(&arena, program);
|
||||||
if (state == NULL)
|
if (rt == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
WL_State_trace(state, 0);
|
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
WL_Result result = WL_eval(state);
|
WL_EvalResult result = wl_runtime_eval(rt);
|
||||||
switch (result.type) {
|
switch (result.type) {
|
||||||
|
|
||||||
case WL_DONE:
|
case WL_EVAL_DONE:
|
||||||
WL_State_free(state);
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case WL_ERROR:
|
case WL_EVAL_ERROR:
|
||||||
WL_State_free(state);
|
printf("Error: %s\n", wl_runtime_error(rt).ptr); // TODO
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
case WL_VAR:
|
case WL_EVAL_SYSVAR:
|
||||||
if (WL_streq(result.str, "login_user_id", -1)) {
|
if (wl_streq(result.str, "login_user_id", -1)) {
|
||||||
|
|
||||||
if (user_id < 0)
|
if (user_id < 0)
|
||||||
WL_pushnone(state);
|
wl_push_none(rt);
|
||||||
else
|
else
|
||||||
WL_pushint(state, user_id);
|
wl_push_s64(rt, user_id);
|
||||||
|
|
||||||
} else if (WL_streq(result.str, "post_id", -1)) {
|
} else if (wl_streq(result.str, "post_id", -1)) {
|
||||||
|
|
||||||
if (post_id < 0)
|
if (post_id < 0)
|
||||||
WL_pushnone(state);
|
wl_push_none(rt);
|
||||||
else
|
else
|
||||||
WL_pushint(state, post_id);
|
wl_push_s64(rt, post_id);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WL_CALL:
|
case WL_EVAL_SYSCALL:
|
||||||
if (WL_streq(result.str, "query", -1)) {
|
if (wl_streq(result.str, "query", -1)) {
|
||||||
query_routine(state);
|
query_routine(rt);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WL_OUTPUT:
|
case WL_EVAL_OUTPUT:
|
||||||
http_response_builder_body(builder, (HTTP_String) { result.str.ptr, result.str.len });
|
http_response_builder_body(builder, (HTTP_String) { result.str.ptr, result.str.len });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -222,7 +194,7 @@ void evaluate_template_2(HTTP_ResponseBuilder builder, WL_Arena arena, char *fil
|
|||||||
http_response_builder_status(builder, 200);
|
http_response_builder_status(builder, 200);
|
||||||
http_response_builder_header(builder, HTTP_STR("Content-Type: text/html"));
|
http_response_builder_header(builder, HTTP_STR("Content-Type: text/html"));
|
||||||
|
|
||||||
WL_Compiler *compiler = WL_Compiler_init(&arena);
|
WL_Compiler *compiler = wl_compiler_init(&arena);
|
||||||
if (compiler == NULL) {
|
if (compiler == NULL) {
|
||||||
assert(0); // TODO
|
assert(0); // TODO
|
||||||
}
|
}
|
||||||
@@ -230,7 +202,7 @@ void evaluate_template_2(HTTP_ResponseBuilder builder, WL_Arena arena, char *fil
|
|||||||
char *loaded_files[128];
|
char *loaded_files[128];
|
||||||
int num_loaded_files = 0;
|
int num_loaded_files = 0;
|
||||||
|
|
||||||
WL_CompileResult result;
|
WL_AddResult result;
|
||||||
WL_String path = { file, strlen(file) };
|
WL_String path = { file, strlen(file) };
|
||||||
for (int i = 0;; i++) {
|
for (int i = 0;; i++) {
|
||||||
|
|
||||||
@@ -261,41 +233,40 @@ void evaluate_template_2(HTTP_ResponseBuilder builder, WL_Arena arena, char *fil
|
|||||||
fread(file_data, 1, file_size, f);
|
fread(file_data, 1, file_size, f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
result = WL_compile(compiler, path, (WL_String) { file_data, file_size });
|
result = wl_compiler_add(compiler, (WL_String) { file_data, file_size });
|
||||||
|
|
||||||
loaded_files[num_loaded_files++] = file_data;
|
loaded_files[num_loaded_files++] = file_data;
|
||||||
|
|
||||||
if (result.type == WL_COMPILE_RESULT_ERROR) {
|
if (result.type == WL_ADD_ERROR) {
|
||||||
printf("Compilation of '%.*s' failed\n", path.len, path.ptr);
|
printf("Compilation of '%.*s' failed\n", path.len, path.ptr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.type == WL_COMPILE_RESULT_DONE)
|
if (result.type == WL_ADD_LINK)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
assert(result.type == WL_COMPILE_RESULT_FILE);
|
assert(result.type == WL_ADD_AGAIN);
|
||||||
path = result.path;
|
path = result.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WL_Program program;
|
||||||
|
int ret = wl_compiler_link(compiler, &program);
|
||||||
|
|
||||||
for (int i = 0; i < num_loaded_files; i++)
|
for (int i = 0; i < num_loaded_files; i++)
|
||||||
free(loaded_files[i]);
|
free(loaded_files[i]);
|
||||||
|
|
||||||
WL_Compiler_free(compiler);
|
if (ret < 0) {
|
||||||
|
WL_String err = wl_compiler_error(compiler);
|
||||||
if (result.type == WL_COMPILE_RESULT_ERROR) {
|
|
||||||
http_response_builder_undo(builder);
|
http_response_builder_undo(builder);
|
||||||
http_response_builder_status(builder, 500);
|
http_response_builder_status(builder, 500);
|
||||||
http_response_builder_body(builder, HTTP_STR("Couldn't compile the template"));
|
http_response_builder_body(builder, (HTTP_String) { err.ptr, err.len });
|
||||||
http_response_builder_done(builder);
|
http_response_builder_done(builder);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
WL_Program program = result.program;
|
|
||||||
|
|
||||||
char err[1<<9];
|
if (evaluate_template(builder, program, arena, user_id, post_id) < 0) {
|
||||||
if (evaluate_template(builder, program, arena, err, (int) sizeof(err), user_id, post_id) < 0) {
|
|
||||||
http_response_builder_undo(builder);
|
http_response_builder_undo(builder);
|
||||||
http_response_builder_status(builder, 500);
|
http_response_builder_status(builder, 500);
|
||||||
http_response_builder_body(builder, (HTTP_String) { err, (int) strlen(err) });
|
|
||||||
http_response_builder_done(builder);
|
http_response_builder_done(builder);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -415,7 +386,33 @@ HTTP_String http_getcookie(HTTP_Request *req, HTTP_String name)
|
|||||||
return HTTP_STR("");
|
return HTTP_STR("");
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTP_String http_getparam(HTTP_String body, HTTP_String str)
|
void *alloc(WL_Arena *arena, int num, int align)
|
||||||
|
{
|
||||||
|
int pad = -(uintptr_t) (arena->ptr + arena->cur) & (align-1);
|
||||||
|
if (arena->len - arena->cur < num + pad)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
void *ptr = arena->ptr + arena->cur + pad;
|
||||||
|
arena->cur += num + pad;
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_hex_digit(char c)
|
||||||
|
{
|
||||||
|
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hex_digit_to_int(char c)
|
||||||
|
{
|
||||||
|
if (c >= 'A' && c <= 'F')
|
||||||
|
return c - 'A' + 10;
|
||||||
|
if (c >= 'a' && c <= 'f')
|
||||||
|
return c - 'a' + 10;
|
||||||
|
return c - '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP_String http_getparam(HTTP_String body, HTTP_String str, WL_Arena *arena)
|
||||||
{
|
{
|
||||||
// This is just a best-effort implementation
|
// This is just a best-effort implementation
|
||||||
|
|
||||||
@@ -450,8 +447,47 @@ HTTP_String http_getparam(HTTP_String body, HTTP_String str)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (http_streq(str, name))
|
if (http_streq(str, name)) {
|
||||||
return body;
|
|
||||||
|
bool percent_encoded = false;
|
||||||
|
for (int i = 0; i < body.len; i++)
|
||||||
|
if (body.ptr[i] == '+' || body.ptr[i] == '%') {
|
||||||
|
percent_encoded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!percent_encoded)
|
||||||
|
return body;
|
||||||
|
|
||||||
|
HTTP_String decoded = { alloc(arena, body.len, 1), 0 };
|
||||||
|
if (decoded.ptr == NULL)
|
||||||
|
return (HTTP_String) { NULL, 0 };
|
||||||
|
|
||||||
|
for (int i = 0; i < body.len; i++) {
|
||||||
|
|
||||||
|
char c = body.ptr[i];
|
||||||
|
if (c == '+')
|
||||||
|
c = ' ';
|
||||||
|
else {
|
||||||
|
if (body.ptr[i] == '%') {
|
||||||
|
if (body.len - i < 3
|
||||||
|
|| !is_hex_digit(body.ptr[i+1])
|
||||||
|
|| !is_hex_digit(body.ptr[i+2]))
|
||||||
|
return (HTTP_String) { NULL, 0 };
|
||||||
|
|
||||||
|
int h = hex_digit_to_int(body.ptr[i+1]);
|
||||||
|
int l = hex_digit_to_int(body.ptr[i+2]);
|
||||||
|
c = (h << 4) | l;
|
||||||
|
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decoded.ptr[decoded.len++] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return decoded;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return HTTP_STR("");
|
return HTTP_STR("");
|
||||||
@@ -545,6 +581,12 @@ bool valid_link(HTTP_String str)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool valid_comment_content(HTTP_String str)
|
||||||
|
{
|
||||||
|
(void) str;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
http_global_init();
|
http_global_init();
|
||||||
@@ -586,8 +628,6 @@ int main(void)
|
|||||||
int pool_cap = 1<<20;
|
int pool_cap = 1<<20;
|
||||||
char *pool = malloc(pool_cap);
|
char *pool = malloc(pool_cap);
|
||||||
|
|
||||||
WL_Arena arena = { pool, pool_cap, 0 };
|
|
||||||
|
|
||||||
SessionSet sessions;
|
SessionSet sessions;
|
||||||
session_set_init(&sessions);
|
session_set_init(&sessions);
|
||||||
|
|
||||||
@@ -599,6 +639,8 @@ int main(void)
|
|||||||
int ret = http_server_wait(server, &req, &builder);
|
int ret = http_server_wait(server, &req, &builder);
|
||||||
if (ret < 0) return -1;
|
if (ret < 0) return -1;
|
||||||
|
|
||||||
|
WL_Arena arena = { pool, pool_cap, 0 };
|
||||||
|
|
||||||
//printf("%.*s\n", req->raw.len, req->raw.ptr); // TODO
|
//printf("%.*s\n", req->raw.len, req->raw.ptr); // TODO
|
||||||
|
|
||||||
// If logged in, these are set to non-negative values
|
// If logged in, these are set to non-negative values
|
||||||
@@ -638,8 +680,8 @@ int main(void)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTP_String name = http_getparam(req->body, HTTP_STR("username"));
|
HTTP_String name = http_getparam(req->body, HTTP_STR("username"), &arena);
|
||||||
HTTP_String pass = http_getparam(req->body, HTTP_STR("password"));
|
HTTP_String pass = http_getparam(req->body, HTTP_STR("password"), &arena);
|
||||||
|
|
||||||
name = http_trim(name);
|
name = http_trim(name);
|
||||||
pass = http_trim(pass);
|
pass = http_trim(pass);
|
||||||
@@ -694,10 +736,10 @@ int main(void)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTP_String name = http_getparam(req->body, HTTP_STR("username"));
|
HTTP_String name = http_getparam(req->body, HTTP_STR("username"), &arena);
|
||||||
HTTP_String email = http_getparam(req->body, HTTP_STR("email"));
|
HTTP_String email = http_getparam(req->body, HTTP_STR("email"), &arena);
|
||||||
HTTP_String pass1 = http_getparam(req->body, HTTP_STR("password1"));
|
HTTP_String pass1 = http_getparam(req->body, HTTP_STR("password1"), &arena);
|
||||||
HTTP_String pass2 = http_getparam(req->body, HTTP_STR("password2"));
|
HTTP_String pass2 = http_getparam(req->body, HTTP_STR("password2"), &arena);
|
||||||
|
|
||||||
if (!valid_name(name) || !valid_email(email) || !valid_pass(pass1)) {
|
if (!valid_name(name) || !valid_email(email) || !valid_pass(pass1)) {
|
||||||
http_response_builder_status(builder, 400);
|
http_response_builder_status(builder, 400);
|
||||||
@@ -764,9 +806,9 @@ int main(void)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTP_String title = http_getparam(req->body, HTTP_STR("title"));
|
HTTP_String title = http_getparam(req->body, HTTP_STR("title"), &arena);
|
||||||
HTTP_String link = http_getparam(req->body, HTTP_STR("link"));
|
HTTP_String link = http_getparam(req->body, HTTP_STR("link"), &arena);
|
||||||
HTTP_String content = http_getparam(req->body, HTTP_STR("content"));
|
HTTP_String content = http_getparam(req->body, HTTP_STR("content"), &arena);
|
||||||
|
|
||||||
title = http_trim(title);
|
title = http_trim(title);
|
||||||
link = http_trim(link);
|
link = http_trim(link);
|
||||||
@@ -818,6 +860,74 @@ int main(void)
|
|||||||
http_response_builder_header(builder, (HTTP_String) { location, ret });
|
http_response_builder_header(builder, (HTTP_String) { location, ret });
|
||||||
http_response_builder_done(builder);
|
http_response_builder_done(builder);
|
||||||
|
|
||||||
|
} else if (http_streq(path, HTTP_STR("/api/comment"))) {
|
||||||
|
|
||||||
|
if (req->method != HTTP_METHOD_POST) {
|
||||||
|
http_response_builder_status(builder, 405);
|
||||||
|
http_response_builder_done(builder);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user_id == -1) {
|
||||||
|
http_response_builder_status(builder, 303);
|
||||||
|
http_response_builder_header(builder, HTTP_STR("Location: /index"));
|
||||||
|
http_response_builder_done(builder);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTP_String parent_post_str = http_getparam(req->body, HTTP_STR("parent_post"), &arena);
|
||||||
|
HTTP_String content = http_getparam(req->body, HTTP_STR("content"), &arena);
|
||||||
|
|
||||||
|
int parent_post;
|
||||||
|
{
|
||||||
|
char buf[32];
|
||||||
|
if (parent_post_str.len >= (int) sizeof(buf)) {
|
||||||
|
assert(0); // TODO
|
||||||
|
}
|
||||||
|
memcpy(buf, parent_post_str.ptr, parent_post_str.len);
|
||||||
|
buf[parent_post_str.len] = '\0';
|
||||||
|
|
||||||
|
parent_post = atoi(buf);
|
||||||
|
if (parent_post == 0) {
|
||||||
|
assert(0); // TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
content = http_trim(content);
|
||||||
|
if (!valid_comment_content(content)) {
|
||||||
|
assert(0); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content.len == 0) {
|
||||||
|
assert(0); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
int ret = sqlite3utils_prepare(db, &stmt, "INSERT INTO Comments(author, content, parent_post) VALUES (?, ?, ?)", user_id, content, parent_post);
|
||||||
|
if (ret != SQLITE_OK) {
|
||||||
|
assert(0); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sqlite3_step(stmt);
|
||||||
|
if (ret != SQLITE_DONE) {
|
||||||
|
assert(0); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sqlite3_finalize(stmt);
|
||||||
|
if (ret != SQLITE_OK) {
|
||||||
|
assert(0); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
char location[1<<9];
|
||||||
|
ret = snprintf(location, sizeof(location), "Location: /post?id=%d", parent_post);
|
||||||
|
if (ret < 0 || ret >= (int) sizeof(location)) {
|
||||||
|
assert(0); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
http_response_builder_status(builder, 303);
|
||||||
|
http_response_builder_header(builder, (HTTP_String) { location, ret });
|
||||||
|
http_response_builder_done(builder);
|
||||||
|
|
||||||
} else if (http_streq(path, HTTP_STR("/index"))) {
|
} else if (http_streq(path, HTTP_STR("/index"))) {
|
||||||
|
|
||||||
evaluate_template_2(builder, arena, "pages/index.wl", user_id, -1);
|
evaluate_template_2(builder, arena, "pages/index.wl", user_id, -1);
|
||||||
@@ -860,10 +970,11 @@ int main(void)
|
|||||||
|
|
||||||
} else if (http_streq(path, HTTP_STR("/post"))) {
|
} else if (http_streq(path, HTTP_STR("/post"))) {
|
||||||
|
|
||||||
HTTP_String idstr = http_getparam(req->url.query, HTTP_STR("id"));
|
HTTP_String idstr = http_getparam(req->url.query, HTTP_STR("id"), &arena);
|
||||||
|
|
||||||
char buf[32];
|
char buf[32];
|
||||||
if (idstr.len == 0 || idstr.len >= (int) sizeof(buf)) {
|
if (idstr.len == 0 || idstr.len >= (int) sizeof(buf)) {
|
||||||
|
printf("post id [%.*s] is not defined\n", idstr.len, idstr.ptr); // TODO
|
||||||
evaluate_template_2(builder, arena, "pages/notfound.wl", user_id, -1);
|
evaluate_template_2(builder, arena, "pages/notfound.wl", user_id, -1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -872,6 +983,7 @@ int main(void)
|
|||||||
|
|
||||||
int post_id = atoi(buf);
|
int post_id = atoi(buf);
|
||||||
if (post_id == 0) {
|
if (post_id == 0) {
|
||||||
|
printf("Invalid post id [%s]\n", buf);
|
||||||
evaluate_template_2(builder, arena, "pages/notfound.wl", user_id, -1);
|
evaluate_template_2(builder, arena, "pages/notfound.wl", user_id, -1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -897,10 +1009,13 @@ 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