diff options
-rw-r--r-- | LICENSE | 9 | ||||
-rw-r--r-- | Makefile | 15 | ||||
-rw-r--r-- | README | 29 | ||||
-rw-r--r-- | input | 3 | ||||
-rw-r--r-- | input.ntl | 4 | ||||
-rw-r--r-- | main.c | 182 | ||||
-rw-r--r-- | tl.c | 210 | ||||
-rw-r--r-- | tl.h | 31 | ||||
-rw-r--r-- | tl_example.c | 20 |
9 files changed, 318 insertions, 185 deletions
@@ -0,0 +1,9 @@ +Copyright 2020 Aleksey Veresov + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from +the use of this software. + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1ddb25d --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +DEBUG ?= yes + +CFLAGS = -xc -ansi -Wall +ifeq '$(DEBUG)' 'yes' +CFLAGS += -g -O0 +else +CFLAGS += -O3 +endif + +tl_example: tl_example.c tl.c + gcc $(CFLAGS) $^ -I. -o $@ + +.PHONY: clean +clean: + rm -f tl_example @@ -0,0 +1,29 @@ + Description +Template language made simple. + + Overview +NewbieTL is FOSS: legal info is in the 'LICENSE' file. +It is written in ANSI C without dependencies except for C standard library. +NewbieTL is a module, not a library, so it is intended to be changed +as you wish and included directly into code of your project. + + Compiling +Compilation is described in Makefile, however it is mostly about flags, +since the thing is so simple. Type 'make' to produce working example. + + Example +Example sets some data and applies the translator. +Play with input.ntl, passing it to the example as './tl_example < input.ntl'. + + Usage +Form any data you want in tl_list with tl_new, tl_named_data, +tl_named_list and tl_plain_list and pass the data into tl_translate. +It will substitute any {name} with data of name, and any +@name{body} with body repeatedly translated for each element in list of name, +so the same rules as for whole text are applied for body, +but in context extended with the element named elements. +Also you can screen characters with \, so \@ is @ in output, +however newline character will disapear, you can output it with \n. + + Motivation +Templates can be fun and simple. @@ -1,3 +0,0 @@ -Hello, {username}! -@articles{{title} by {author} -} diff --git a/input.ntl b/input.ntl new file mode 100644 index 0000000..b6ba135 --- /dev/null +++ b/input.ntl @@ -0,0 +1,4 @@ +Hello, {username}! + +There are several artciles for you:\ +@articles{\n{title} by {author}} @@ -1,182 +0,0 @@ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - - -struct reus_list { - const char *name; - const char *data; - struct reus_list *list; - struct reus_list *next; -}; - -struct reus { - struct reus_list *variables; - char *name; - char *body; - int opened; -}; - -typedef void *(*reus_state)(struct reus *r, char c); - - -void reus_string_add(char **str, char c) -{ - int *header; - if (!*str) { - header = malloc(9); - *str = (char *) header + 8; - header[0] = 1; - header[1] = 0; - (*str)[0] = 0; - } else header = (int *)(*str - 8); - if (header[0] == ++header[1]) { - header = realloc(header, 8 + (header[0] <<= 1)); - *str = (char *) header + 8; - } - (*str)[header[1] - 1] = c; - (*str)[header[1]] = 0; -} - - -struct reus_list *reus_list_new(const char *name, - const char *data, - struct reus_list *list, - struct reus_list *next) -{ - struct reus_list *res = malloc(sizeof(*res)); - res->name = name; - res->data = data; - res->list = list; - res->next = next; - return res; -} - -struct reus_list *reus_list_union(struct reus_list *a, struct reus_list *b) -{ - if (!a) return b ? reus_list_union(b, 0) : 0; - return reus_list_new(a->name, a->data, - reus_list_union(a->list, 0), - reus_list_union(a->next, b)); -} - -struct reus_list *reus_list_get(struct reus_list *l, char *name) -{ - if (!l) return 0; - if (!strcmp(l->name, name)) return l; - return reus_list_get(l->next, name); -} - -void reus_list_free(struct reus_list *l) -{ - if (l->list) reus_list_free(l->list); - if (l->next) reus_list_free(l->next); - free(l); -} - - -void reus_init(struct reus *r) -{ - r->variables = 0; - r->name = 0; - r->body = 0; - r->opened = 0; -} - -void reus_free(struct reus *r) -{ - if (r->variables) reus_list_free(r->variables); - if (r->name) free(r->name - 8); - if (r->body) free(r->body - 8); -} - - -void *reus_state_at(struct reus *r, char c); -void *reus_state_subs(struct reus *r, char c); -void *reus_state_text(struct reus *r, char c) -{ - if (c == '@') return reus_state_at; - if (c == '{') return reus_state_subs; - putchar(c); - return reus_state_text; -} - -void *reus_state_subs(struct reus *r, char c) -{ - if (c == '}') { - struct reus_list *var = reus_list_get(r->variables, r->name); - if (var && var->data) { - printf("%s", var->data); - } - free(r->name - 8); - r->name = 0; - return reus_state_text; - } - reus_string_add(&r->name, c); - return reus_state_subs; -} - -void reus_at(struct reus *r) -{ - struct reus_list *var = reus_list_get(r->variables, r->name); - for (var = var->list; var; var = var->next) { - char *c; - struct reus sr; - reus_state s = reus_state_text; - reus_init(&sr); - sr.variables = reus_list_union(var->list, r->variables); - for (c = r->body; *c; ++c) { - s = s(&sr, *c); - } - reus_free(&sr); - } -} -void *reus_state_body(struct reus *r, char c); -void *reus_state_at(struct reus *r, char c) -{ - if (c == '{') return reus_state_body; - reus_string_add(&r->name, c); - return reus_state_at; -} - -void *reus_state_body(struct reus *r, char c) -{ - if (c == '{') ++r->opened; - if (c == '}' && !r->opened--) { - reus_at(r); - r->opened = 0; - free(r->name - 8); - r->name = 0; - free(r->body - 8); - r->body = 0; - return reus_state_text; - } - reus_string_add(&r->body, c); - return reus_state_body; -} - - -int main() -{ - int c; - struct reus r; - reus_state s = reus_state_text; - reus_init(&r); - r.variables = - reus_list_new("username", "aversey", 0, - reus_list_new("articles", 0, - reus_list_new(0, 0, - reus_list_new("title", "Smooth Sort", 0, - reus_list_new("author", "Edsger Dijkstra", 0, 0)), - reus_list_new(0, 0, - reus_list_new("title", "Your Own Site", 0, - reus_list_new("author", "Aleksey Veresov", 0, 0)), - reus_list_new(0, 0, - reus_list_new("title", "Bullshit", 0, - reus_list_new("author", "Ongo Gablogian", 0, 0)), 0))), 0)); - while ((c = getchar()) != EOF) { - s = s(&r, c); - } - reus_free(&r); - return 0; -} @@ -0,0 +1,210 @@ +#include "tl.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * public list functions + */ +struct tl_list *tl_new(const char *name, + const char *data, + struct tl_list *list, + struct tl_list *next) +{ + struct tl_list *res = malloc(sizeof(*res)); + res->name = name; + res->data = data; + res->list = list; + res->next = next; + return res; +} + +struct tl_list *tl_plain_list(struct tl_list *list, struct tl_list *next) +{ + return tl_new(0, 0, list, next); +} + +struct tl_list *tl_named_list(const char *name, + struct tl_list *list, + struct tl_list *next) +{ + return tl_new(name, 0, list, next); +} + +struct tl_list *tl_named_data(const char *name, + const char *data, + struct tl_list *next) +{ + return tl_new(name, data, 0, next); +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * internal list functions + */ +static struct tl_list *tl_list_union(struct tl_list *a, struct tl_list *b) +{ + if (!a) return b ? tl_list_union(b, 0) : 0; + return tl_new(a->name, a->data, + tl_list_union(a->list, 0), + tl_list_union(a->next, b)); +} + +static struct tl_list *tl_list_get(struct tl_list *l, char *name) +{ + if (!l) return 0; + if (!strcmp(l->name, name)) return l; + return tl_list_get(l->next, name); +} + +static void tl_list_free(struct tl_list *l) +{ + if (l->list) tl_list_free(l->list); + if (l->next) tl_list_free(l->next); + free(l); +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * string + */ +static int *tl_string_size(char *str) { return ((int *) str) - 2; } +static int *tl_string_len(char *str) { return ((int *) str) - 1; } + +static void tl_string_add(char **str, char c) +{ + static const int shift = 2 * sizeof(int); + if (!*str) { + *str = malloc(shift + 1); + *str += shift; + *tl_string_size(*str) = 1; + *tl_string_len(*str) = 0; + (*str)[0] = 0; + } + ++*tl_string_len(*str); + if (*tl_string_size(*str) == *tl_string_len(*str)) { + *tl_string_size(*str) *= 2; + *str = realloc(*str - shift, shift + *tl_string_size(*str)); + *str += shift; + } + (*str)[*tl_string_len(*str) - 1] = c; + (*str)[*tl_string_len(*str)] = 0; +} + +static void tl_string_free(char *str) +{ + if (str) free(str - 2 * sizeof(int)); +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * template language translator as automata + */ +struct tl { + struct tl_list *variables; + char *name; + char *body; + int opened; +}; +typedef void *(*tl_state)(struct tl *t, char c); + +static void tl_free(struct tl *t) +{ + if (t->variables) tl_list_free(t->variables); + if (t->name) tl_string_free(t->name); + if (t->body) tl_string_free(t->body); +} + +static void *tl_state_text(struct tl *t, char c); +void tl_translate(struct tl_list *l) +{ + int c; + tl_state state = tl_state_text; + struct tl automata = { l, 0, 0, 0 }; + while ((c = getchar()) != EOF) { + state = state(&automata, c); + } + tl_free(&automata); +} + + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * automata states + */ +static void *tl_state_backslash(struct tl *t, char c); +static void *tl_state_at(struct tl *t, char c); +static void *tl_state_subs(struct tl *t, char c); +static void *tl_state_text(struct tl *t, char c) +{ + if (c == '\\') return tl_state_backslash; + if (c == '@') return tl_state_at; + if (c == '{') return tl_state_subs; + putchar(c); + return tl_state_text; +} + +static void *tl_state_subs(struct tl *t, char c) +{ + if (c == '}') { + struct tl_list *var = tl_list_get(t->variables, t->name); + if (var && var->data) { + printf("%s", var->data); + } + tl_string_free(t->name); + t->name = 0; + return tl_state_text; + } + tl_string_add(&t->name, c); + return tl_state_subs; +} + +static void tl_at(struct tl *t) +{ + struct tl_list *var = tl_list_get(t->variables, t->name); + for (var = var->list; var; var = var->next) { + char *c; + tl_state state = tl_state_text; + struct tl automata = { 0, 0, 0, 0 }; + automata.variables = tl_list_union(var->list, t->variables); + for (c = t->body; *c; ++c) { + state = state(&automata, *c); + } + tl_free(&automata); + } +} +static void *tl_state_body(struct tl *t, char c); +static void *tl_state_at(struct tl *t, char c) +{ + if (c == '{') return tl_state_body; + tl_string_add(&t->name, c); + return tl_state_at; +} + +static void *tl_state_body(struct tl *t, char c) +{ + if (c == '{') ++t->opened; + if (c == '}' && !t->opened--) { + tl_at(t); + t->opened = 0; + free(t->name - 8); + t->name = 0; + free(t->body - 8); + t->body = 0; + return tl_state_text; + } + tl_string_add(&t->body, c); + return tl_state_body; +} + +static void *tl_state_backslash(struct tl *t, char c) +{ + if (c == '\n') return tl_state_text; + if (c == 'n') { + putchar('\n'); + return tl_state_text; + } + putchar(c); + return tl_state_text; +} @@ -0,0 +1,31 @@ +#ifndef NEWBIETL_INCLUDED +#define NEWBIETL_INCLUDED + + +struct tl_list { + const char *name; + const char *data; + struct tl_list *list; + struct tl_list *next; +}; + +struct tl_list *tl_new(const char *name, + const char *data, + struct tl_list *list, + struct tl_list *next); + +struct tl_list *tl_plain_list(struct tl_list *list, struct tl_list *next); + +struct tl_list *tl_named_list(const char *name, + struct tl_list *list, + struct tl_list *next); + +struct tl_list *tl_named_data(const char *name, + const char *data, + struct tl_list *next); + + +void tl_translate(struct tl_list *l); + + +#endif diff --git a/tl_example.c b/tl_example.c new file mode 100644 index 0000000..b8f9779 --- /dev/null +++ b/tl_example.c @@ -0,0 +1,20 @@ +#include <tl.h> + + +int main() +{ + struct tl_list *l = + tl_named_data("username", "aversey", + tl_named_list("articles", + tl_plain_list( + tl_named_data("title", "Substitution Processes", + tl_named_data("author", "Edsger Dijkstra", 0)), + tl_plain_list( + tl_named_data("title", "NewbieTL", + tl_named_data("author", "Aleksey Veresov", 0)), + tl_plain_list( + tl_named_data("title", "Bullshit", + tl_named_data("author", "Ongo Gablogian", 0)), 0))), 0)); + tl_translate(l); + return 0; +} |