labels now allow underscored; bug fix
This commit is contained in:
@@ -66,7 +66,7 @@ tinytemplate_compile(const char *src, size_t len,
|
|||||||
First you need to compile the template into a program and then you can evaluate it by executing the program.
|
First you need to compile the template into a program and then you can evaluate it by executing the program.
|
||||||
|
|
||||||
### Compilation
|
### Compilation
|
||||||
The compilation function expects a source string of the template through `src` and with length in bytes `len`. The string doesn't need to be zero-terminated. The program will be compilated by writing its instructions in the `program` buffer. The `program` array is assumed to have space for a maximum of `max_instr` instructions. If more space would be required to memorize the program, the compilation fails returning the status code `TINYTEMPLATE_STATUS_EMEMORY`. At that point the caller program can either abort or try again with a bigger buffer. If compilation succeded, then the number of instruction is written to the variable `num_instr`. A status code of the compilation is returned through the main return value. The status `TINYTEMPLATE_STATUS_DONE` means all went well, while all other status codes refer to a specific error that occurred. If the compilation failed, an error message is written to the caller-provided buffer `errmsg` whith size `errmax`.
|
The compilation function expects a source string of the template through `src` and with length in bytes `len`. The string doesn't need to be zero-terminated. The program will be compilated by writing its instructions in the `prog` buffer. The `prog` array is assumed to have space for a maximum of `max_instr` instructions. If more space would be required to memorize the program, the compilation fails returning the status code `TINYTEMPLATE_STATUS_EMEMORY`. At that point the caller program can either abort or try again with a bigger buffer. If compilation succeded, then the number of instruction is written to the variable `num_instr`. A status code of the compilation is returned through the main return value. The status `TINYTEMPLATE_STATUS_DONE` means all went well, while all other status codes refer to a specific error that occurred. If the compilation failed, an error message is written to the caller-provided buffer `errmsg` whith size `errmax`.
|
||||||
|
|
||||||
Here's an example of how you would use this function:
|
Here's an example of how you would use this function:
|
||||||
```c
|
```c
|
||||||
|
|||||||
@@ -0,0 +1,143 @@
|
|||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
|
#include <Python.h>
|
||||||
|
#include "tinytemplate.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
PyObject *src;
|
||||||
|
tinytemplate_instr_t *program;
|
||||||
|
} Template;
|
||||||
|
|
||||||
|
static void
|
||||||
|
TinyTemplate_dealloc(CustomObject *self)
|
||||||
|
{
|
||||||
|
Py_XDECREF(self->src);
|
||||||
|
Py_TYPE(self)->tp_free((PyObject *) self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
TinyTemplate_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
Template *self;
|
||||||
|
self = (Template *) type->tp_alloc(type, 0);
|
||||||
|
if (self != NULL) {
|
||||||
|
self->src = PyUnicode_FromString("");
|
||||||
|
if (self->src == NULL) {
|
||||||
|
Py_DECREF(self);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (PyObject *) self;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
TinyTemplate_init(Template *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
static char *kwlist[] = {"src", NULL};
|
||||||
|
PyObject *src = NULL;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &src))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (src) {
|
||||||
|
PyObject *tmp = self->src;
|
||||||
|
Py_INCREF(src);
|
||||||
|
self->src = src;
|
||||||
|
Py_XDECREF(tmp);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
TinyTemplate_eval(Template *self, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *src = self->src;
|
||||||
|
|
||||||
|
if (src == NULL) {
|
||||||
|
PyErr_SetString(PyExc_AttributeError, "src");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!PyUnicode_Check(src))
|
||||||
|
return NULL; // Raise exception?
|
||||||
|
|
||||||
|
PyObject *bytes = PyUnicode_AsEncodedString(src, "UTF-8", "strict"); // Owned reference
|
||||||
|
if (bytes == NULL) {
|
||||||
|
..
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *cstr = PyBytes_AS_STRING(bytes);
|
||||||
|
|
||||||
|
..
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMemberDef TinyTemplate_members[] = {
|
||||||
|
{"src", T_OBJECT_EX, offsetof(Template, src), 0, "source string"},
|
||||||
|
{NULL} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyMethodDef TinyTemplate_methods[] = {
|
||||||
|
{"eval", (PyCFunction) TinyTemplate_eval, METH_NOARGS, "Evaluate the template" },
|
||||||
|
{NULL} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyTypeObject TemplateType = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
.tp_name = "tinytemplate.Template",
|
||||||
|
.tp_doc = PyDoc_STR("A template"),
|
||||||
|
.tp_basicsize = sizeof(Template),
|
||||||
|
.tp_itemsize = 0,
|
||||||
|
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
||||||
|
.tp_new = TinyTemplate_new,
|
||||||
|
.tp_init = (initproc) TinyTemplate_init,
|
||||||
|
.tp_dealloc = (destructor) TinyTemplate_dealloc,
|
||||||
|
.tp_members = TinyTemplate_members,
|
||||||
|
.tp_methods = TinyTemplate_methods,
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
compile(PyObject *self, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *src;
|
||||||
|
if (!PyArg_ParseTuple(args, "o", &src))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
TinyTemplate_instr_t *prog = malloc();
|
||||||
|
|
||||||
|
return PyObject_Call((PyObject*) &TemplateType, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyMethodDef methods[] = {
|
||||||
|
{"compile", compile, METH_VARARGS, "Compile a template"},
|
||||||
|
{NULL, NULL, 0, NULL}, /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct PyModuleDef module = {
|
||||||
|
PyModuleDef_HEAD_INIT,
|
||||||
|
"tinytemplate", /* name of module */
|
||||||
|
spam_doc, /* module documentation, may be NULL */
|
||||||
|
0, /* size of per-interpreter state of the module,
|
||||||
|
or -1 if the module keeps state in global variables. */
|
||||||
|
methods,
|
||||||
|
};
|
||||||
|
|
||||||
|
PyMODINIT_FUNC
|
||||||
|
PyInit_tinytemplate(void)
|
||||||
|
{
|
||||||
|
if (PyType_Ready(&TemplateType) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
PyObject *m = PyModule_Create(&module);
|
||||||
|
if (m == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
Py_INCREF(&TemplateType);
|
||||||
|
if (PyModule_AddObject(m, "Template", (PyObject *) &TemplateType) < 0) {
|
||||||
|
Py_DECREF(&TemplateType);
|
||||||
|
Py_DECREF(m);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from distutils.core import setup, Extension
|
||||||
|
setup(name="tinytemplate", version="1.0",
|
||||||
|
ext_modules=[Extension("tinytemplate", ["pytinytemplate.c"])])
|
||||||
+6
-5
@@ -178,6 +178,7 @@ append_instr(compile_state_t *state,
|
|||||||
case OPCODE_ITER:
|
case OPCODE_ITER:
|
||||||
case OPCODE_CHLD:
|
case OPCODE_CHLD:
|
||||||
case OPCODE_IDX:
|
case OPCODE_IDX:
|
||||||
|
instr->operands[0].as_size = va_arg(operands, size_t);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OPCODE_NEXT:
|
case OPCODE_NEXT:
|
||||||
@@ -388,12 +389,12 @@ next_token_kword_or_ident(scanner_t *scanner, slice_t *slice,
|
|||||||
{
|
{
|
||||||
(void) payload;
|
(void) payload;
|
||||||
|
|
||||||
assert(follows_alpha(scanner));
|
assert(follows_alpha(scanner) || follows_char(scanner, '_'));
|
||||||
|
|
||||||
size_t offset = scanner->cur;
|
size_t offset = scanner->cur;
|
||||||
do
|
do
|
||||||
scanner->cur++;
|
scanner->cur++;
|
||||||
while (follows_alpha(scanner));
|
while (follows_alpha(scanner) || follows_char(scanner, '_'));
|
||||||
size_t length = scanner->cur - offset;
|
size_t length = scanner->cur - offset;
|
||||||
|
|
||||||
if (slice) {
|
if (slice) {
|
||||||
@@ -553,7 +554,7 @@ parse_primary(scanner_t *scanner,
|
|||||||
|
|
||||||
// Check if the label matches the iteration label
|
// Check if the label matches the iteration label
|
||||||
if (slice.length == scope->for_child_label.length && !memcmp(scanner->src + slice.offset, scanner->src + scope->for_child_label.offset, slice.length)) {
|
if (slice.length == scope->for_child_label.length && !memcmp(scanner->src + slice.offset, scanner->src + scope->for_child_label.offset, slice.length)) {
|
||||||
append_instr(state, OPCODE_CHLD, slice.offset, slice.length);
|
append_instr(state, OPCODE_CHLD, j);
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -563,7 +564,7 @@ parse_primary(scanner_t *scanner,
|
|||||||
// its length will be 0 and the expression's
|
// its length will be 0 and the expression's
|
||||||
// label won't match.
|
// label won't match.
|
||||||
if (slice.length == scope->for_index_label.length && !memcmp(scanner->src + slice.offset, scanner->src + scope->for_index_label.offset, slice.length)) {
|
if (slice.length == scope->for_index_label.length && !memcmp(scanner->src + slice.offset, scanner->src + scope->for_index_label.offset, slice.length)) {
|
||||||
append_instr(state, OPCODE_IDX, slice.offset, slice.length);
|
append_instr(state, OPCODE_IDX, j);
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1001,7 +1002,7 @@ tinytemplate_compile(const char *src, size_t len,
|
|||||||
size_t raw_off = scanner.cur; // Start offset of the raw block
|
size_t raw_off = scanner.cur; // Start offset of the raw block
|
||||||
while (scanner.cur < scanner.len) {
|
while (scanner.cur < scanner.len) {
|
||||||
// Look for a "{" or the end
|
// Look for a "{" or the end
|
||||||
while (follows_char(&scanner, '{'))
|
while (scanner.cur < scanner.len && !follows_char(&scanner, '{'))
|
||||||
scanner.cur++;
|
scanner.cur++;
|
||||||
|
|
||||||
// If the end wasn't reached (a "{" was found)
|
// If the end wasn't reached (a "{" was found)
|
||||||
|
|||||||
+1
-2
@@ -42,8 +42,7 @@ typedef struct {
|
|||||||
} tinytemplate_array_t;
|
} tinytemplate_array_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *str;
|
const char *str; size_t len;
|
||||||
size_t len;
|
|
||||||
} tinytemplate_string_t;
|
} tinytemplate_string_t;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "../src/tinytemplate.h"
|
||||||
|
|
||||||
|
/* this lets the source compile without afl-clang-fast/lto */
|
||||||
|
#ifndef __AFL_FUZZ_TESTCASE_LEN
|
||||||
|
|
||||||
|
ssize_t fuzz_len;
|
||||||
|
unsigned char fuzz_buf[1024000];
|
||||||
|
|
||||||
|
#define __AFL_FUZZ_TESTCASE_LEN fuzz_len
|
||||||
|
#define __AFL_FUZZ_TESTCASE_BUF fuzz_buf
|
||||||
|
//#define __AFL_FUZZ_INIT() void sync(void);
|
||||||
|
//#define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) > 0 ? 1 : 0)
|
||||||
|
//#define __AFL_INIT() sync()
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
__AFL_FUZZ_INIT();
|
||||||
|
|
||||||
|
//#pragma clang optimize off
|
||||||
|
//#pragma GCC optimize("O0")
|
||||||
|
|
||||||
|
static void callback(void *userp, const char *label, size_t label_len,
|
||||||
|
const char *str, size_t len)
|
||||||
|
{
|
||||||
|
(void) userp;
|
||||||
|
(void) label;
|
||||||
|
(void) label_len;
|
||||||
|
(void) str;
|
||||||
|
(void) len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef __AFL_HAVE_MANUAL_CONTROL
|
||||||
|
__AFL_INIT();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; // must be after __AFL_INIT
|
||||||
|
// and before __AFL_LOOP!
|
||||||
|
tinytemplate_instr_t program[1024];
|
||||||
|
size_t max_instr = sizeof(program)/sizeof(program[0]);
|
||||||
|
|
||||||
|
while (__AFL_LOOP(10000)) {
|
||||||
|
|
||||||
|
int len = __AFL_FUZZ_TESTCASE_LEN; // don't use the macro directly in a
|
||||||
|
// call!
|
||||||
|
|
||||||
|
tinytemplate_status_t status;
|
||||||
|
|
||||||
|
status = tinytemplate_compile((char*) buf, len, program, max_instr, NULL, NULL, 0);
|
||||||
|
if (status == TINYTEMPLATE_STATUS_DONE)
|
||||||
|
status = tinytemplate_eval((char*) buf, program, NULL, NULL, callback, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{{1+2}}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{% if 1 + 2 %}
|
||||||
|
text
|
||||||
|
{% end %}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{% if 1 + 2 %}
|
||||||
|
text
|
||||||
|
{% else %}
|
||||||
|
hoyo
|
||||||
|
{% end %}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{% for item in items %}
|
||||||
|
{{item}}
|
||||||
|
{% end %}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
|
||||||
|
struct {
|
||||||
|
const char *src;
|
||||||
|
const char *exp;
|
||||||
|
const char *params;
|
||||||
|
} cases[] = {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SECTION_INPUT,
|
||||||
|
SECTION_OUTPUT,
|
||||||
|
SECTION_PARAMS,
|
||||||
|
} Section;
|
||||||
|
|
||||||
|
static void runTest(const char *src, size_t len)
|
||||||
|
{
|
||||||
|
size_t params_offset;
|
||||||
|
size_t params_length;
|
||||||
|
size_t input_offset;
|
||||||
|
size_t input_length;
|
||||||
|
size_t output_offset;
|
||||||
|
size_t output_length;
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
while (1) {
|
||||||
|
while (i < len && src[i] != '@')
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if (i < len && isalpha(src[i])) {
|
||||||
|
|
||||||
|
size_t kword_off = i;
|
||||||
|
do
|
||||||
|
i++;
|
||||||
|
while (i < len && isalpha(src[i]));
|
||||||
|
size_t kword_len = i - kword_off;
|
||||||
|
|
||||||
|
bool is_kword = true;
|
||||||
|
Section section;
|
||||||
|
if (kword_len == 6 && !strncmp("params", src + kword_off, 6)) {
|
||||||
|
section = SECTION_PARAMS;
|
||||||
|
} else if (kword_len == 5 && !strncmp("input", src + kword_off, 5)) {
|
||||||
|
section = SECTION_INPUT;
|
||||||
|
} else if (kword_len == 6 && !strncmp("output", src + kword_off, 6)) {
|
||||||
|
section = SECTION_OUTPUT;
|
||||||
|
} else
|
||||||
|
is_kword = false;
|
||||||
|
|
||||||
|
if (is_kword) {
|
||||||
|
// Get to the end of the line
|
||||||
|
while (i < len && (src[i] == ' ' || src[i] == '\t' || src[i] == '\r'))
|
||||||
|
i++;
|
||||||
|
if (i < len && src[i] != '\n') {
|
||||||
|
|
||||||
|
}
|
||||||
|
while (i < len && src[i] != '\n')
|
||||||
|
i++;
|
||||||
|
if (i < len) {
|
||||||
|
assert(src[i] == '\n');
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
switch (section) {
|
||||||
|
case SECTION_INPUT: input_offset = i; break;
|
||||||
|
case SECTION_OUTPUT: output_offset = i; break;
|
||||||
|
case SECTION_PARAMS: params_offset = i; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user