aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE9
-rw-r--r--Makefile15
-rw-r--r--README29
-rw-r--r--input3
-rw-r--r--input.ntl4
-rw-r--r--main.c182
-rw-r--r--tl.c210
-rw-r--r--tl.h31
-rw-r--r--tl_example.c20
9 files changed, 318 insertions, 185 deletions
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..4921ae2
--- /dev/null
+++ b/LICENSE
@@ -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
diff --git a/README b/README
new file mode 100644
index 0000000..6715d2c
--- /dev/null
+++ b/README
@@ -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.
diff --git a/input b/input
deleted file mode 100644
index 9c1f87e..0000000
--- a/input
+++ /dev/null
@@ -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}}
diff --git a/main.c b/main.c
deleted file mode 100644
index 3fe5475..0000000
--- a/main.c
+++ /dev/null
@@ -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;
-}
diff --git a/tl.c b/tl.c
new file mode 100644
index 0000000..c4bfc27
--- /dev/null
+++ b/tl.c
@@ -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;
+}
diff --git a/tl.h b/tl.h
new file mode 100644
index 0000000..a80d4b1
--- /dev/null
+++ b/tl.h
@@ -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;
+}