clean up of files and fixed docs
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
tt
|
||||
tt.exe
|
||||
@@ -50,4 +50,4 @@ cat page.tt | ./tt params.json > page.html
|
||||
```
|
||||
|
||||
## Internals
|
||||
TinyTemplate works by compiling a template string into a bytecode program, and then evaluating the program to generate the output.
|
||||
TinyTemplate works by compiling a template string into a bytecode program, and then evaluating the program to generate the output. (TODO)
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Francesco",
|
||||
"fruits": ["Orange", "Peach", "Mango"],
|
||||
"chocolate": false
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>TinyTemplate example</title>
|
||||
</head>
|
||||
<body>
|
||||
<a>Hello, my name is {{name}} and my favourite fruits are:</a>
|
||||
<ul>
|
||||
{% for fruit in fruits %}
|
||||
<li>{{fruit}}</li>
|
||||
{% end %}
|
||||
</ul>
|
||||
{% if chocolate %}
|
||||
<a>but I also like chocolate!</a>
|
||||
{% else %}
|
||||
<a>and I do NOT like chocolate!</a>
|
||||
{% end %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
all: tt
|
||||
|
||||
tt: cli.c tinytemplate.c xjson.c
|
||||
gcc $^ -o $@ -Wall -Wextra -g
|
||||
tt: src/cli.c src/tinytemplate.c 3p/xjson.c
|
||||
gcc $^ -o $@ -Wall -Wextra -g -I3p
|
||||
|
||||
clean:
|
||||
rm tt tt.exe
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>{{username}}s blog!</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Hei, welcome to my blog! I'm {{name}} {{surname}} and I'm {{age}} years old!</p>
|
||||
<ul>
|
||||
{% for post in posts %}
|
||||
<li>{{post.title}} - {{post.date}}</li>
|
||||
{% end %}
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>coziss blog!</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Hei, welcome to my blog! I'm Francesco Cozzuto and I'm 24 years old!</p>
|
||||
<ul>
|
||||
|
||||
<li>Scriviamo un server TCP - 10/9/2023</li>
|
||||
|
||||
<li>Gli assert - 4/5/2022</li>
|
||||
|
||||
<li>Big e little endian: Ordinamento dei byte - 14/5/2022</li>
|
||||
|
||||
<li>Complessità computazionale - ???</li>
|
||||
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"username": "cozis",
|
||||
"name": "Francesco",
|
||||
"surname": "Cozzuto",
|
||||
"age": 24,
|
||||
"posts": [
|
||||
{"title": "Scriviamo un server TCP", "date": "10/9/2023"},
|
||||
{"title": "Gli assert", "date": "4/5/2022"},
|
||||
{"title": "Big e little endian: Ordinamento dei byte", "date": "14/5/2022"},
|
||||
{"title": "Complessità computazionale", "date": "???"}
|
||||
]
|
||||
}
|
||||
+31
-24
@@ -6,6 +6,8 @@
|
||||
#include "tinytemplate.h"
|
||||
|
||||
typedef struct wrap_t wrap_t;
|
||||
typedef struct eval_context_t eval_context_t;
|
||||
|
||||
struct wrap_t {
|
||||
eval_context_t *context;
|
||||
union {
|
||||
@@ -14,12 +16,12 @@ struct wrap_t {
|
||||
};
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct eval_context_t {
|
||||
FILE *stream;
|
||||
xj_value *params;
|
||||
wrap_t params;
|
||||
wrap_t *free;
|
||||
wrap_t pool[TINYTEMPLATE_MAX_ITER_DEPTH];
|
||||
} eval_context_t;
|
||||
};
|
||||
|
||||
static bool query_json_array(void *data,
|
||||
tinytemplate_type_t *type,
|
||||
@@ -58,13 +60,14 @@ static void convert_json_object(eval_context_t *context,
|
||||
|
||||
case XJ_ARRAY:
|
||||
{
|
||||
iter_t *iter = context->free;
|
||||
wrap_t *wrap = context->free;
|
||||
*type = TINYTEMPLATE_TYPE_ARRAY;
|
||||
value->as_array.data = iter;
|
||||
value->as_array.data = wrap;
|
||||
value->as_array.next = query_json_array;
|
||||
if (iter) {
|
||||
context->free = iter->next;
|
||||
iter->curs = child->as_array;
|
||||
if (wrap) {
|
||||
context->free = wrap->next;
|
||||
wrap->value = child->as_array;
|
||||
wrap->context = context;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -87,15 +90,13 @@ static bool query_json_array(void *data,
|
||||
tinytemplate_type_t *type,
|
||||
tinytemplate_value_t *value)
|
||||
{
|
||||
iter_t *iter = data;
|
||||
wrap_t *wrap = data;
|
||||
|
||||
if (iter->curs) {
|
||||
iter->curs = iter->curs->next;
|
||||
if (iter->curs) {
|
||||
convert_json_object(iter->curs, type, value);
|
||||
if (wrap->value) {
|
||||
convert_json_object(wrap->context, wrap->value, type, value);
|
||||
wrap->value = wrap->value->next;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -103,10 +104,11 @@ static bool query_json_object(void *data, const char *key, size_t len,
|
||||
tinytemplate_type_t *type,
|
||||
tinytemplate_value_t *value)
|
||||
{
|
||||
xj_value *json_value = data;
|
||||
assert(json_value->type == XJ_OBJECT);
|
||||
wrap_t *wrap = data;
|
||||
|
||||
xj_value *child = json_value->as_object;
|
||||
assert(wrap->value->type == XJ_OBJECT);
|
||||
|
||||
xj_value *child = wrap->value->as_object;
|
||||
while (child) {
|
||||
size_t keylen = strlen(child->key);
|
||||
if (keylen == len && !strncmp(key, child->key, len))
|
||||
@@ -116,12 +118,16 @@ static bool query_json_object(void *data, const char *key, size_t len,
|
||||
if (!child)
|
||||
return false;
|
||||
|
||||
convert_json_object(child, type, value);
|
||||
convert_json_object(wrap->context, child, type, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void callback(void *userp, const char *str, size_t len)
|
||||
static void callback(void *userp, const char *lbl, size_t lbllen,
|
||||
const char *str, size_t len)
|
||||
{
|
||||
(void) lbl;
|
||||
(void) lbllen;
|
||||
|
||||
eval_context_t *context = userp;
|
||||
fwrite(str, 1, len, context->stream);
|
||||
}
|
||||
@@ -131,8 +137,8 @@ static bool query_root_json_object(void *data, const char *key, size_t len,
|
||||
tinytemplate_value_t *value)
|
||||
{
|
||||
eval_context_t *context = data;
|
||||
if (context->params)
|
||||
return query_json_object(context->params, key, len, type, value);
|
||||
if (context->params.value)
|
||||
return query_json_object(&context->params, key, len, type, value);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
@@ -140,8 +146,9 @@ static bool query_root_json_object(void *data, const char *key, size_t len,
|
||||
static void init_context(eval_context_t *context,
|
||||
FILE *stream, xj_value *root)
|
||||
{
|
||||
context->stream=stdout;
|
||||
context->params=root;
|
||||
context->stream = stream;
|
||||
context->params.context = context;
|
||||
context->params.value = root;
|
||||
context->free = context->pool;
|
||||
|
||||
for (int i = 0; i < TINYTEMPLATE_MAX_ITER_DEPTH-1; i++)
|
||||
@@ -217,7 +224,7 @@ int main(int argc, char **argv)
|
||||
} while (status == TINYTEMPLATE_STATUS_EMEMORY);
|
||||
|
||||
eval_context_t context;
|
||||
init_context(&context);
|
||||
init_context(&context, stdout, root);
|
||||
|
||||
if (tinytemplate_eval(buffer, program, &context, query_root_json_object, callback, message, sizeof(message)) != TINYTEMPLATE_STATUS_DONE) {
|
||||
fprintf(stderr, "Failed to evaluate template (%s)\n", message);
|
||||
@@ -73,6 +73,7 @@ typedef enum {
|
||||
OPCODE_NEXT,
|
||||
OPCODE_CHLD,
|
||||
OPCODE_IDX,
|
||||
OPCODE_BLOCK,
|
||||
} opcode_t;
|
||||
|
||||
// Represents a substring of the template
|
||||
@@ -85,15 +86,20 @@ typedef enum {
|
||||
SCOPE_IF,
|
||||
SCOPE_IF_ELSE,
|
||||
SCOPE_FOR,
|
||||
SCOPE_BLOCK,
|
||||
} scope_type_t;
|
||||
|
||||
typedef struct {
|
||||
scope_type_t type;
|
||||
|
||||
size_t if_jcnd;
|
||||
size_t if_jump;
|
||||
|
||||
size_t for_next;
|
||||
slice_t for_child_label;
|
||||
slice_t for_index_label;
|
||||
|
||||
slice_t block_name;
|
||||
} scope_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -134,6 +140,7 @@ typedef enum {
|
||||
TOKEN_KWORD_FOR,
|
||||
TOKEN_KWORD_ELSE,
|
||||
TOKEN_KWORD_END,
|
||||
TOKEN_KWORD_BLOCK,
|
||||
TOKEN_OPER_MOD,
|
||||
} token_t;
|
||||
|
||||
@@ -191,6 +198,7 @@ append_instr(compile_state_t *state,
|
||||
case OPCODE_GETS:
|
||||
case OPCODE_PUSHV:
|
||||
case OPCODE_PUSHS:
|
||||
case OPCODE_BLOCK:
|
||||
instr->operands[0].as_size = va_arg(operands, size_t);
|
||||
instr->operands[1].as_size = va_arg(operands, size_t);
|
||||
break;
|
||||
@@ -372,6 +380,11 @@ next_token_kword_or_ident(scanner_t *scanner, slice_t *slice,
|
||||
if (!strncmp(scanner->src + offset, "else", length))
|
||||
return TOKEN_KWORD_ELSE;
|
||||
break;
|
||||
|
||||
case 5: // block
|
||||
if (!strncmp(scanner->src + offset, "block", length))
|
||||
return TOKEN_KWORD_BLOCK;
|
||||
break;
|
||||
}
|
||||
|
||||
return TOKEN_IDENT;
|
||||
@@ -756,7 +769,6 @@ selection_construct_start(scanner_t *scanner,
|
||||
return DONE;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
iteration_construct_start(scanner_t *scanner,
|
||||
compile_state_t *state,
|
||||
@@ -812,13 +824,48 @@ iteration_construct_start(scanner_t *scanner,
|
||||
return DONE;
|
||||
}
|
||||
|
||||
static status_t
|
||||
block_construct_start(scanner_t *scanner,
|
||||
compile_state_t *state,
|
||||
error_t *error)
|
||||
{
|
||||
// This function is called after "{% block" is parsed
|
||||
assert(scanner->src[scanner->cur-5] == 'b'
|
||||
&& scanner->src[scanner->cur-4] == 'l'
|
||||
&& scanner->src[scanner->cur-3] == 'o'
|
||||
&& scanner->src[scanner->cur-2] == 'c'
|
||||
&& scanner->src[scanner->cur-1] == 'k');
|
||||
|
||||
if (state->scope_depth == TINYTEMPLATE_MAX_SCOPE_DEPTH) {
|
||||
report(error, "Scope depth limit reached");
|
||||
return ESCOPE;
|
||||
}
|
||||
|
||||
slice_t label;
|
||||
if (next_token(scanner, &label, NULL) != TOKEN_IDENT) {
|
||||
report(error, "Missing block label");
|
||||
return ESYNTAX;
|
||||
}
|
||||
|
||||
append_instr(state, OPCODE_BLOCK, label.offset, label.length);
|
||||
|
||||
tinytemplate_status_t status;
|
||||
if ((status = close_construct(scanner, error, "block")) != DONE)
|
||||
return status;
|
||||
|
||||
state->scope_stack[state->scope_depth].type = SCOPE_BLOCK;
|
||||
state->scope_stack[state->scope_depth].block_name = label;
|
||||
state->scope_depth++;
|
||||
return DONE;
|
||||
}
|
||||
|
||||
static void
|
||||
resolve_scope(compile_state_t *state)
|
||||
{
|
||||
assert(state->scope_depth > 0);
|
||||
|
||||
scope_t *scope = state->scope_stack
|
||||
+ state->scope_depth - 1;
|
||||
+ state->scope_depth-1;
|
||||
|
||||
switch (scope->type) {
|
||||
|
||||
@@ -841,6 +888,17 @@ resolve_scope(compile_state_t *state)
|
||||
instr = state->program + scope->if_jump;
|
||||
instr->operands[0].as_size = state->num_instr;
|
||||
break;
|
||||
|
||||
case SCOPE_BLOCK:
|
||||
{
|
||||
// Get parent block name
|
||||
slice_t parent_label = {0, 0};
|
||||
for (int i = state->scope_depth-2; i >= 0; i--)
|
||||
if (state->scope_stack[i].type == SCOPE_BLOCK)
|
||||
parent_label = state->scope_stack[i].block_name;
|
||||
append_instr(state, OPCODE_BLOCK, parent_label.offset, parent_label.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state->scope_depth--;
|
||||
@@ -900,6 +958,10 @@ construct_else(scanner_t *scanner,
|
||||
case SCOPE_FOR:
|
||||
report(error, "Bad {%% else %%} coupled with {%% for .. %%}");
|
||||
return ESEMANT;
|
||||
|
||||
case SCOPE_BLOCK:
|
||||
report(error, "Bad {%% else %%} coupled with {%% block .. %%}");
|
||||
return ESEMANT;
|
||||
}
|
||||
|
||||
return DONE;
|
||||
@@ -918,7 +980,7 @@ control_flow_block(scanner_t *scanner,
|
||||
case TOKEN_KWORD_FOR: return iteration_construct_start(scanner, state, error);
|
||||
case TOKEN_KWORD_END: return construct_end(scanner, state, error);
|
||||
case TOKEN_KWORD_ELSE: return construct_else(scanner, state, error);
|
||||
|
||||
case TOKEN_KWORD_BLOCK: return block_construct_start(scanner, state, error);
|
||||
default:
|
||||
report(error, "Bad token [%.*s] after [{%%]",
|
||||
(int) slice.length, scanner->src + slice.offset);
|
||||
@@ -1050,13 +1112,28 @@ tinytemplate_eval(const char *src, const instr_t *program, void *userp,
|
||||
tinytemplate_value_t stack[TINYTEMPLATE_MAX_EXPR_DEPTH];
|
||||
size_t stack_depth = 0;
|
||||
|
||||
slice_t label = {0, 0};
|
||||
|
||||
bool done = false;
|
||||
int index = 0;
|
||||
while (!done) {
|
||||
const tinytemplate_instr_t *instr = &program[index++];
|
||||
switch (instr->opcode) {
|
||||
case OPCODE_NOPE: /* Do nothing */ break;
|
||||
case OPCODE_DONE: done = true; break;
|
||||
|
||||
case OPCODE_NOPE:
|
||||
/* Do nothing */
|
||||
break;
|
||||
|
||||
case OPCODE_DONE:
|
||||
done = true;
|
||||
break;
|
||||
|
||||
case OPCODE_BLOCK:
|
||||
label = (slice_t) {
|
||||
.offset = instr->operands[0].as_size,
|
||||
.length = instr->operands[1].as_size,
|
||||
};
|
||||
break;
|
||||
|
||||
case OPCODE_ITER:
|
||||
assert(stack_depth > 0); // ITER excepts a value on the stack
|
||||
@@ -1233,7 +1310,8 @@ tinytemplate_eval(const char *src, const instr_t *program, void *userp,
|
||||
{
|
||||
size_t offset = instr->operands[0].as_size;
|
||||
size_t length = instr->operands[1].as_size;
|
||||
callback(userp, src + offset, length);
|
||||
callback(userp, src + label.offset, label.length,
|
||||
src + offset, length);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1248,7 +1326,8 @@ tinytemplate_eval(const char *src, const instr_t *program, void *userp,
|
||||
char text[128];
|
||||
int num = snprintf(text, sizeof(text), "%lld", stack[stack_depth-1].as_int);
|
||||
assert(num > 0);
|
||||
callback(userp, text, (size_t) num);
|
||||
callback(userp, src + label.offset, label.length,
|
||||
text, (size_t) num);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1257,12 +1336,14 @@ tinytemplate_eval(const char *src, const instr_t *program, void *userp,
|
||||
char text[128];
|
||||
int num = snprintf(text, sizeof(text), "%lf", stack[stack_depth-1].as_float);
|
||||
assert(num > 0);
|
||||
callback(userp, text, (size_t) num);
|
||||
callback(userp, src + label.offset, label.length,
|
||||
text, (size_t) num);
|
||||
break;
|
||||
}
|
||||
|
||||
case TINYTEMPLATE_TYPE_STRING:
|
||||
callback(userp, stack[stack_depth-1].as_string.str,
|
||||
callback(userp, src + label.offset, label.length,
|
||||
stack[stack_depth-1].as_string.str,
|
||||
stack[stack_depth-1].as_string.len);
|
||||
break;
|
||||
|
||||
@@ -29,7 +29,7 @@ typedef union tinytemplate_value_t tinytemplate_value_t;
|
||||
|
||||
typedef bool (*tinytemplate_nextcallback_t)(void*, tinytemplate_type_t*, tinytemplate_value_t*);
|
||||
typedef bool (*tinytemplate_getter_t)(void*, const char*, size_t, tinytemplate_type_t*, tinytemplate_value_t*);
|
||||
typedef void (*tinytemplate_callback_t)(void *userp, const char *str, size_t len);
|
||||
typedef void (*tinytemplate_callback_t)(void *userp, const char *lbl, size_t lblen, const char *str, size_t len);
|
||||
|
||||
typedef struct {
|
||||
void *data;
|
||||
Reference in New Issue
Block a user