first commit

This commit is contained in:
cozis
2023-06-27 17:13:57 +02:00
commit 2b138a8764
13 changed files with 5308 additions and 0 deletions
+53
View File
@@ -0,0 +1,53 @@
# TinyTemplate
TinyTemplate is a minimal templating engine for C. It can be used as a library or through the command-line interface.
* Very minimal in functionality
* Implemented in one file (plus header)
* No dynamic allocations
* Only depends on libc
## How it looks
Here's how it looks:
```html
<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>
```
if you've already used some templating engines, then
this won't blow your mind.
## Build
To build it you run
```sh
make
```
which will generate the `tt` file, the command-line utility.
## Embed
If you want to embed TinyTemplate in your own C project,
just pop `tinytemplate.c` and `tinytemplate.h` in you own
source tree and compile then as they were your own.
## Command-line usage
The `tt` utility gets the template string from stdin and writes the evaluated version to stdout. Any errors are
reported to stderr, as you would expect. To provide any
parameters to the template, you can specify a json file
containing them. Here's an example usage which transforms
a template into an HTML page
```sh
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.
+230
View File
@@ -0,0 +1,230 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "xjson.h"
#include "tinytemplate.h"
typedef struct wrap_t wrap_t;
struct wrap_t {
eval_context_t *context;
union {
xj_value *value;
wrap_t *next;
};
};
typedef struct {
FILE *stream;
xj_value *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,
tinytemplate_value_t *value);
static bool query_json_object(void *data, const char *key, size_t len,
tinytemplate_type_t *type,
tinytemplate_value_t *value);
static void convert_json_object(eval_context_t *context,
xj_value *child,
tinytemplate_type_t *type,
tinytemplate_value_t *value)
{
switch (child->type) {
case XJ_NULL:
*type = TINYTEMPLATE_TYPE_INT;
value->as_int = 0;
break;
case XJ_INT:
*type = TINYTEMPLATE_TYPE_INT;
value->as_int = child->as_int;
break;
case XJ_FLOAT:
*type = TINYTEMPLATE_TYPE_INT;
value->as_float = child->as_float;
break;
case XJ_BOOL:
*type = TINYTEMPLATE_TYPE_INT;
value->as_int = child->as_bool;
break;
case XJ_ARRAY:
{
iter_t *iter = context->free;
*type = TINYTEMPLATE_TYPE_ARRAY;
value->as_array.data = iter;
value->as_array.next = query_json_array;
if (iter) {
context->free = iter->next;
iter->curs = child->as_array;
}
break;
}
case XJ_OBJECT:
*type = TINYTEMPLATE_TYPE_DICT;
value->as_dict.data = child;
value->as_dict.get = query_json_object;
break;
case XJ_STRING:
*type = TINYTEMPLATE_TYPE_STRING;
value->as_string.str = child->as_string;
value->as_string.len = child->size;
break;
}
}
static bool query_json_array(void *data,
tinytemplate_type_t *type,
tinytemplate_value_t *value)
{
iter_t *iter = data;
if (iter->curs) {
iter->curs = iter->curs->next;
if (iter->curs) {
convert_json_object(iter->curs, type, value);
return true;
}
}
return false;
}
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);
xj_value *child = json_value->as_object;
while (child) {
size_t keylen = strlen(child->key);
if (keylen == len && !strncmp(key, child->key, len))
break;
child = child->next;
}
if (!child)
return false;
convert_json_object(child, type, value);
return true;
}
static void callback(void *userp, const char *str, size_t len)
{
eval_context_t *context = userp;
fwrite(str, 1, len, context->stream);
}
static bool query_root_json_object(void *data, const char *key, size_t len,
tinytemplate_type_t *type,
tinytemplate_value_t *value)
{
eval_context_t *context = data;
if (context->params)
return query_json_object(context->params, key, len, type, value);
else
return false;
}
static void init_context(eval_context_t *context,
FILE *stream, xj_value *root)
{
context->stream=stdout;
context->params=root;
context->free = context->pool;
for (int i = 0; i < TINYTEMPLATE_MAX_ITER_DEPTH-1; i++)
context->pool[i].next = &context->pool[i+1];
context->pool[TINYTEMPLATE_MAX_ITER_DEPTH-1].next = NULL;
}
int main(int argc, char **argv)
{
const char *param_file = NULL;
if (argc > 1)
param_file = argv[1];
char buffer[1 << 16];
char json[1 << 16];
xj_value *root = NULL;
xj_alloc *alloc = NULL;
if (param_file) {
FILE *stream = fopen(param_file, "rb");
if (stream == NULL) {
fprintf(stderr, "Couldn't open parameter file \"%s\"\n", param_file);
return -1;
}
size_t num = fread(buffer, 1, sizeof(buffer), stream);
if (ferror(stream)) {
fprintf(stderr, "Couldn't read from \"%s\"\n", param_file);
fclose(stream);
return -1;
}
alloc = xj_alloc_using(json, sizeof(json), 0, NULL);
xj_error error;
root = xj_decode(buffer, (int) num, alloc, &error);
if (root == NULL) {
fprintf(stderr, "Couldn't decode JSON (%s)\n", error.message);
fclose(stream);
return -1;
}
fclose(stream);
}
size_t num = fread(buffer, 1, sizeof(buffer), stdin);
if (ferror(stdin)) {
fprintf(stderr, "Failed to read from stdin\n");
return -1;
}
char message[256];
tinytemplate_instr_t *program = NULL;
size_t max_instr = 1 << 10;
tinytemplate_status_t status;
do {
max_instr *= 2;
program = realloc(program, max_instr * sizeof(tinytemplate_instr_t));
if (program == NULL) {
fprintf(stderr, "Out of memory");
return -1;
}
status = tinytemplate_compile(buffer, num, program, max_instr, NULL, message, sizeof(message));
if (status != TINYTEMPLATE_STATUS_DONE &&
status != TINYTEMPLATE_STATUS_EMEMORY) {
fprintf(stderr, "Failed to compile template (%s)\n", message);
return -1;
}
} while (status == TINYTEMPLATE_STATUS_EMEMORY);
eval_context_t context;
init_context(&context);
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);
free(program);
return -1;
}
free(program);
return 0;
}
+8
View File
@@ -0,0 +1,8 @@
all: tt
tt: cli.c tinytemplate.c xjson.c
gcc $^ -o $@ -Wall -Wextra -g
clean:
rm tt tt.exe
+13
View File
@@ -0,0 +1,13 @@
<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
View File
@@ -0,0 +1,19 @@
<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
View File
@@ -0,0 +1,12 @@
{
"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": "???"}
]
}
+1236
View File
File diff suppressed because it is too large Load Diff
+1593
View File
File diff suppressed because it is too large Load Diff
+89
View File
@@ -0,0 +1,89 @@
#ifndef TINYTEMPLATE_H
#define TINYTEMPLATE_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#ifndef TINYTEMPLATE_MAX_SCOPE_DEPTH
#define TINYTEMPLATE_MAX_SCOPE_DEPTH 8
#endif
#ifndef TINYTEMPLATE_MAX_EXPR_DEPTH
#define TINYTEMPLATE_MAX_EXPR_DEPTH 8
#endif
#ifndef TINYTEMPLATE_MAX_ITER_DEPTH
#define TINYTEMPLATE_MAX_ITER_DEPTH 8
#endif
typedef enum {
TINYTEMPLATE_TYPE_INT,
TINYTEMPLATE_TYPE_FLOAT,
TINYTEMPLATE_TYPE_DICT,
TINYTEMPLATE_TYPE_ARRAY,
TINYTEMPLATE_TYPE_STRING,
} tinytemplate_type_t;
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 struct {
void *data;
tinytemplate_getter_t get;
} tinytemplate_dict_t;
typedef struct {
void *data;
tinytemplate_nextcallback_t next;
} tinytemplate_array_t;
typedef struct {
const char *str;
size_t len;
} tinytemplate_string_t;
union tinytemplate_value_t {
int64_t as_int;
double as_float;
tinytemplate_dict_t as_dict;
tinytemplate_array_t as_array;
tinytemplate_string_t as_string;
};
typedef struct {
int opcode;
struct {
int64_t as_int;
size_t as_size;
double as_float;
} operands[2];
} tinytemplate_instr_t;
typedef enum {
TINYTEMPLATE_STATUS_DONE,
TINYTEMPLATE_STATUS_ESYMBOL,
TINYTEMPLATE_STATUS_ESCOPE,
TINYTEMPLATE_STATUS_EDEPTH,
TINYTEMPLATE_STATUS_ETYPE,
TINYTEMPLATE_STATUS_EITER,
TINYTEMPLATE_STATUS_EMEMORY,
TINYTEMPLATE_STATUS_ESYNTAX,
TINYTEMPLATE_STATUS_ESEMANT,
} tinytemplate_status_t;
tinytemplate_status_t
tinytemplate_eval(const char *src, const tinytemplate_instr_t *program,
void *userp, tinytemplate_getter_t params,
tinytemplate_callback_t callback,
char *errmsg, size_t errmax);
tinytemplate_status_t
tinytemplate_compile(const char *src, size_t len,
tinytemplate_instr_t *program,
size_t max_instr, size_t *num_instr,
char *errmsg, size_t errmax);
#endif
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+1985
View File
File diff suppressed because it is too large Load Diff
+70
View File
@@ -0,0 +1,70 @@
#ifndef XJSON_H
#define XJSON_H
typedef double xj_f64;
typedef long long xj_i64;
typedef _Bool xj_bool;
_Static_assert(sizeof(xj_f64) == 8, "double isn't 8 bytes long");
_Static_assert(sizeof(xj_i64) == 8, "long long isn't 8 bytes long");
enum {
XJ_NULL,
XJ_BOOL,
XJ_INT,
XJ_FLOAT,
XJ_ARRAY,
XJ_OBJECT,
XJ_STRING,
};
typedef struct xj_value xj_value;
struct xj_value {
int type;
int size;
xj_value *next;
char *key;
union {
xj_i64 as_int;
xj_bool as_bool;
xj_f64 as_float;
xj_value *as_array;
xj_value *as_object;
char *as_string;
};
};
typedef struct {
xj_bool occurred;
xj_bool truncated;
int off, row, col;
char message[128];
} xj_error;
typedef struct xj_alloc xj_alloc;
xj_alloc *xj_alloc_using(void *mem, int size, int ext, void (*free)(void*));
xj_alloc *xj_alloc_new(int size, int ext);
void xj_alloc_del(xj_alloc *alloc);
void *xj_bpalloc(xj_alloc *alloc, int size);
void xj_preport(xj_error *error, const char *src, int off, const char *fmt, ...);
#define xj_report(error, fmt, ...) xj_preport(error, NULL, -1, fmt, ## __VA_ARGS__)
xj_value *xj_value_null(xj_alloc *alloc, xj_error *error);
xj_value *xj_value_bool(xj_bool val, xj_alloc *alloc, xj_error *error);
xj_value *xj_value_int(xj_i64 val, xj_alloc *alloc, xj_error *error);
xj_value *xj_value_float(xj_f64 val, xj_alloc *alloc, xj_error *error);
xj_value *xj_value_array(xj_value *head, xj_alloc *alloc, xj_error *error);
xj_value *xj_value_object(xj_value *head, xj_alloc *alloc, xj_error *error);
xj_value *xj_value_string(const char *str, int len, xj_alloc *alloc, xj_error *error);
xj_value *xj_value_array__nocheck(xj_value *head, int count, xj_alloc *alloc, xj_error *error);
xj_value *xj_value_object__nocheck(xj_value *head, int count, xj_alloc *alloc, xj_error *error);
_Bool xj_array_append(xj_value *array, xj_value *child, xj_error *error);
char *xj_strdup(const char *str, int len, xj_alloc *alloc, xj_error *error);
xj_value *xj_decode(const char *str, int len, xj_alloc *alloc, xj_error *error);
char *xj_encode(xj_value *value, int *len);
#endif /* XJSON_H */