added useful readme and added includes

This commit is contained in:
Julian Daube 2017-08-08 22:24:00 +02:00
parent 381bd6acff
commit 7fe73e879e
13 changed files with 443 additions and 25 deletions

View File

@ -1,3 +1,76 @@
# nhtml # nhtml
Not html, a very simple template engine !html, a very simple template language
Syntax
------------
As said above, the Template Language is simple.
That also implies a few limitations:
1. No Parametric instantiation (for now) since this would bloat the compiler too much
(I was in no need for that feature when writing this compiler).
2. No language integration of any kind.
This makes this template compiler useful for static webcontent only!
Sooo, how does it work?
* text enclosed with `""` will be placed in the html output, but will be html escaped and `\n` will be replaced with `<br/>`.
* text enclosed with () will be placed *without any changes*.
* One can include other files by writing either
`@<filename>`, e.g. `@hi.nhtml` or
`@"<filename>"`, e.g. `@"filename with spaces.nhtml"`
These files will pe pasted in place unless they end with `.nhtml`, in which case
they get compiled before getting pasted.
* There are comment blocks (`/*text*/`) and linecomments (`//text`) like there are in C.
* A left curly brace (`{`) opens a tag, a coresponding right curly brace (`}`) closes it.
* Finally, tag names are simply written as text and can be accompied by an attribute set
enclosed in `[]`. So in order to e.g. produce
`<div style="color:white">Hi</div>`, the corresponding nhtml would look something like
`div[style=color:white]{"Hi"}`.
* If the Attributes Key-Name is `class`, its pair can be written as `.<value>` instead of `class=<value>`.
Note that multiple mentions of the same attribute key will result in the attribute values
beeing concatenated with a space. So e.g.
`p[.A class=hi]{}` will become `<p class="A hi"></p>`.
That about covers everything this little thing can do :)
For a simple demo, look in example.
How to compile
--------------
The Project used cmake to build. It has no hard dependencies but it requires unix-style pathnames. (`/path/to/file`) for includes to work.
## on linux
Simply install cmake for your distro
### Ubuntu
`apt-get install cmake`
### Arch Linux
`pacman -S cmake`
The create a subdirectory for the build to reside in
`mkdir build`
`cd build`
And run cmake
Release: `cmake -G"Unix Makefiles" ..`
Debug: `cmake -G"Unix Makefiles" .. -DCMAKE_BUILD_TYPE=DEBUG`
after that run `make` and you should be left with a compiled version of the !html compiler `nhtmlc`
How to use
----------
```
./nhtmlc [-o <filename>] file [file...]
-I Add Path to include paths
--output
-o The output file to write the html to
When missing this option, stdout is used instead
-v Enable verbose output
--help Print this usage
```
When given multple input files, the resulting html is concatenated into output.

15
example/Makefile Normal file
View File

@ -0,0 +1,15 @@
all: entchen.html
NHTMLC:=../build/nhtmlc
%.html: %.nhtml
$(NHTMLC) $< -o $@
index.html: head.nhtml entchen.nhtml
$(NHTMLC) entchen.nhtml -o $@
clean:
rm -f entchen.html
.PHONY: all clean

9
example/entchen.css Normal file
View File

@ -0,0 +1,9 @@
.strophe {
border-width:1px;
border-type:solid;
border-color:black;
background-color:gray;
font-type:italic;
color: white;
padding: 0.5em;
}

35
example/entchen.nhtml Normal file
View File

@ -0,0 +1,35 @@
/*
* Generates the body of our document
*
* File created by Julian Daube
* date: 08.08.2017
*/
@head.nhtml
body {
style {
@entchen.css
}
h3{ "Alle meine Entchen" }
p[.strophe] {
"Alle meine Entchen schwimmen auf dem See,
schwimmen auf dem See,
Köpfchen in das Wasser, Schwänzchen in die Höh'."
}
p[.strophe] {
"Alle meine Täubchen gurren auf dem Dach,
gurren auf dem Dach,
eins fliegt in die Lüfte, fliegen alle nach."
}
p[.strophe] {
"Alle meine Hühner scharren in dem Stroh,
scharren in dem Stroh,
finden sie ein Körnchen, sind sie alle froh."
}
p[.strophe] {
"Alle meine Gänschen watscheln durch den Grund,
watscheln durch den Grund,
suchen in dem Tümpel, werden kugelrund."
}
}

11
example/head.nhtml Normal file
View File

@ -0,0 +1,11 @@
/*
* Generates the document header
*
* file generated by Julian Daube
* date: 08.08.2017
*/
!DOCTYPE[html]
head {
title { "Alle meine Entchen" }
meta[charset=utf-8]
}

18
inc/includes.h Normal file
View File

@ -0,0 +1,18 @@
/*
* includes.h
*
* Created on: 08.08.2017
* Author: julian
*/
#ifndef INC_INCLUDES_H_
#define INC_INCLUDES_H_
#include <nhtml_string.h>
#include <stdio.h>
void include_add_path(const char * path);
int include_file(string_t *filename, FILE * output);
#endif /* INC_INCLUDES_H_ */

View File

@ -24,10 +24,18 @@ typedef struct {
*/ */
int string_append(string_t *str, char c); int string_append(string_t *str, char c);
/**
* \brief append src to dest
* will enlarge dest to accommodate src
* \return -1 on failure (check errno)
* \return 0 on success
*/
int string_concat(string_t * dest, string_t * src);
/** /**
* \brief Erase the String from memory * \brief Erase the String from memory
*/ */
void string_destroy(string_t s); void string_destroy(string_t *s);
/** /**
* \brief copy the contents of a string * \brief copy the contents of a string
@ -36,5 +44,10 @@ void string_destroy(string_t s);
*/ */
string_t string_copy(string_t old); string_t string_copy(string_t old);
/**
* \brief creates copy of str
* \return a copy of str
*/
string_t string_from_cstr(const char * str);
#endif /* NHTML_STRING_H_ */ #endif /* NHTML_STRING_H_ */

17
inc/parser.h Normal file
View File

@ -0,0 +1,17 @@
/*
* parser.h
*
* Created on: 08.08.2017
* Author: julian
*/
#ifndef INC_PARSER_H_
#define INC_PARSER_H_
#include <stdio.h>
int strip(FILE * input);
int parse_node(int current, FILE * stream, FILE * output);
#endif /* INC_PARSER_H_ */

View File

@ -5,4 +5,5 @@ set(SOURCE
${pwd}/nhtml_string.c ${pwd}/nhtml_string.c
${pwd}/attribute.c ${pwd}/attribute.c
${pwd}/html.c ${pwd}/html.c
${pwd}/includes.c
PARENT_SCOPE) PARENT_SCOPE)

View File

@ -31,9 +31,10 @@ int attr_set_append(attr_set_t * set, attr_t *new_entry) {
attr_t * new_ptr = attr_set_find(set, new_entry->name.c_str); attr_t * new_ptr = attr_set_find(set, new_entry->name.c_str);
if (new_ptr != NULL) { if (new_ptr != NULL) {
// already contained in set // already contained in set
// just change entries value // concatenate values
string_destroy(new_ptr->value); //new_ptr->value = string_copy(new_entry->value);
new_ptr->value = string_copy(new_entry->value); string_append(&new_ptr->value, ' ');
string_concat(&new_ptr->value, &new_entry->value);
return 0; return 0;
} }
@ -58,11 +59,8 @@ attr_t attr_copy(attr_t * attr) {
void attr_destroy(attr_t * attr) { void attr_destroy(attr_t * attr) {
// clear memory // clear memory
string_destroy(attr->name); string_destroy(&attr->name);
string_destroy(attr->value); string_destroy(&attr->value);
// reset memory content
memset(attr, 0, sizeof(attr_t));
} }

94
src/includes.c Normal file
View File

@ -0,0 +1,94 @@
/*
* includes.c
*
* Created on: 08.08.2017
* Author: julian
*/
#include <includes.h>
#include <parser.h>
#include <nhtml_string.h>
#include <stdlib.h> // needs: malloc, realloc
#include <memory.h> // needs: memcpy
string_t* include_paths;
size_t num_includes = 0;
void include_paths_init() {
include_paths = malloc(sizeof(string_t));
num_includes = 1;
include_paths[0] = string_from_cstr("."); // search in current dir by default
}
const char * extension(const string_t * path) {
int i;
for (i = path->len; i > 0; i--) {
if (path->c_str[i] == '/')
return NULL;
if (path->c_str[i] == '.')
break;
}
return path->c_str + i;
}
void include_add_path(const char * path) {
if (num_includes == 0) {
include_paths_init();
}
include_paths = realloc(include_paths, sizeof(const char*)*(num_includes+1));
include_paths[num_includes] = string_from_cstr(path);
num_includes++;
}
int include_file(string_t *filename, FILE * output) {
FILE * input = NULL;
char * tempPath = NULL;
if(num_includes == 0) {
include_paths_init();
}
for (size_t i = 0; i < num_includes; ++i) {
// actual length is length + 2 (2* end char)
tempPath = malloc(filename->len + include_paths[i].len);
if (tempPath == NULL) {
continue;
}
// Concatenate the two paths
// adding a safety slash to separate the two
memcpy(tempPath, include_paths[i].c_str, include_paths[i].len-1);
tempPath[include_paths->len-1] = '/';
memcpy(tempPath + include_paths->len, filename->c_str, filename->len);
input = fopen(tempPath, "r");
if (input != NULL) {
break;
}
}
if (input == NULL) {
return 0;
}
const char * ext = extension(filename);
#ifdef DEBUG
printf("include %s, ext: %s\n", filename->c_str, ext);
#endif
if (ext && !strcmp(ext, ".nhtml")) {
// (re)parse whole file
int current = strip(input);
while((current = parse_node(current, input, output)) != EOF);
} else {
// copy file 1:1
int current = 0;
while((current = fgetc(input)) != EOF) {
fputc(current, output);
}
}
fclose(input);
return 1;
}

View File

@ -7,9 +7,14 @@
#include <getopt.h> // needs: getopt_long #include <getopt.h> // needs: getopt_long
// Project specific includes // Project specific includes
#include "html.h" #include <html.h>
#include "attribute.h" #include <attribute.h>
#include "nhtml_string.h" #include <nhtml_string.h>
#include <includes.h>
// globals
int verbose = 0;
int strip(FILE * stream) { int strip(FILE * stream) {
int current = 0; int current = 0;
@ -45,8 +50,7 @@ int parse_attr(FILE * stream, attr_set_t * output) {
} }
if (isKey && buffer == '.') { if (isKey && buffer == '.') {
isKey = 0; isKey = 0;
current_attr.name.c_str = "class"; current_attr.name = string_from_cstr("class");
current_attr.name.len = 6;
continue; continue;
} }
if (buffer == '=') { if (buffer == '=') {
@ -70,7 +74,7 @@ int parse_attr(FILE * stream, attr_set_t * output) {
printf("parsed attr: %s=%s\n", current_attr.name.c_str, current_attr.value); printf("parsed attr: %s=%s\n", current_attr.name.c_str, current_attr.value);
#endif #endif
//memset(output, 0, sizeof(attr_set_t)); attr_destroy(&current_attr);
return strip(stream); return strip(stream);
} }
@ -122,11 +126,102 @@ int readName(FILE *stream, node_t *node) {
return current; return current;
} }
// Parse comments
int parse_comment(int current, FILE * input) {
current = fgetc(input);
if (current == '/') {
// line comment
while((current = fgetc(input)) != EOF) {
if (current == '\n') break;
}
} else
if (current == '*') {
// block comment
unsigned char comment_done = 0;
while((current = fgetc(input)) != EOF) {
if (current == '*') {
comment_done = 1;
} else
if (comment_done && current == '/') {
break;
} else
comment_done = 0;
}
}
return strip(input);
}
int parse_include(FILE * input, FILE * output) {
// handle include
int current = fgetc(input);
string_t filename = {};
// two possibilities
// 1. include with "" <= supports arbitrary paths
// 2. include without ""
// reads till a nonpath, char (!{")
// then strips empty chars from start and end of path
// both methods then try to find the file in the include paths
if (current == EOF) return current;
if (current == '"') { // Method 1
char escaped = 0;
while((current = fgetc(input)) != EOF) {
if (!escaped && current == '\\') {
escaped = 1;
continue;
}
if (!escaped && current == '"') {
break;
}
escaped = 0;
string_append(&filename, current);
}
} else {
string_append(&filename, current);
while((current = fgetc(input)) != EOF) {
switch(current) {
case '{':
case '!':
case '"':
case '}':
break;
}
if (isspace(current)) {
break;
}
string_append(&filename, current);
}
}
if (filename.c_str == NULL) {
fprintf(stderr, "include command without filename!\n");
return strip(input);
}
// handle filename
if (verbose) printf("include file %s\n", filename.c_str);
if (!include_file(&filename, output)) {
fprintf(stderr, "could not include file %s\n", filename.c_str);
}
return strip(input);
}
int parse_node(int current, FILE * stream, FILE * output) { int parse_node(int current, FILE * stream, FILE * output) {
if (current == '"' || current == '(') { if (current == '"' || current == '(') {
if (current == '(') current = ')'; if (current == '(') current = ')';
return parse_text(current, stream, output); return parse_text(current, stream, output);
} else
if (current == '/') {
return parse_comment(current, stream);
} else
if (current == '@') {
return parse_include(stream, output);
} }
// normal node // normal node
@ -161,6 +256,7 @@ done:
} }
// long options // long options
static struct option long_options[] = { static struct option long_options[] = {
{"output", required_argument, 0, 'o'}, {"output", required_argument, 0, 'o'},
@ -168,13 +264,14 @@ static struct option long_options[] = {
{} {}
}; };
int verbose = 1;
void usage(int argc, char ** args) { void usage(int argc, char ** args) {
printf("usage: %s [-o <filename>] file [file...]\n", args[0]); printf("usage: %s [-o <filename>] file [file...]\n", args[0]);
printf("-I\tAdd Path to include paths\n");
printf("--output\n"); printf("--output\n");
printf("-o\tThe output file to write the html to\n"); printf("-o\tThe output file to write the html to\n");
printf("\tWhen missing this option, stdout is used instead\n"); printf("\tWhen missing this option, stdout is used instead\n");
printf("-v\tEnable verbose output\n");
printf("--help Print this usage\n"); printf("--help Print this usage\n");
} }
@ -183,16 +280,22 @@ int main(int argc, char ** args) {
FILE* output = NULL; FILE* output = NULL;
// parse arguments // parse arguments
while((i = getopt_long(argc, args, "o:v", long_options, NULL)) != -1) { while((i = getopt_long(argc, args, "o:I:v", long_options, NULL)) != -1) {
switch(i) { switch(i) {
case 'o': case 'o':
printf("output: %s\n", optarg);
output = fopen(optarg, "w"); output = fopen(optarg, "w");
if (output == NULL) { if (output == NULL) {
fprintf(stderr, "could not create file: %s\n", strerror(errno)); fprintf(stderr, "could not create output file: %s (%s)\n", optarg, strerror(errno));
return -1; return -1;
} }
break; break;
case 'I':
// add include dir
include_add_path(optarg);
break;
case 'v':
verbose = 1;
break;
case '?': case '?':
usage(argc, args); usage(argc, args);
return -1; return -1;
@ -204,6 +307,7 @@ int main(int argc, char ** args) {
if (output == NULL) { if (output == NULL) {
output = stdout; output = stdout;
// disable output when printing to stdout
verbose = 0; verbose = 0;
} }
@ -211,7 +315,7 @@ int main(int argc, char ** args) {
// parse all the files // parse all the files
for(i = optind;i < argc; i++) { for(i = optind;i < argc; i++) {
filename = args[argc-1]; filename = args[i];
if (verbose) printf("starting conversion of %s\n", filename); if (verbose) printf("starting conversion of %s\n", filename);
FILE * handle = fopen(filename, "r"); FILE * handle = fopen(filename, "r");

View File

@ -7,7 +7,8 @@
#include "nhtml_string.h" #include "nhtml_string.h"
#include <stdlib.h> // needs: malloc, free #include <stdlib.h> // needs: malloc, free
#include <memory.h> // needs: memcpy #include <memory.h> // needs: memcpy, memset
#include <string.h> // needs: strlen, strdup
int string_append(string_t *str, char c) { int string_append(string_t *str, char c) {
if (str->c_str == NULL) { if (str->c_str == NULL) {
@ -29,12 +30,35 @@ int string_append(string_t *str, char c) {
return 0; return 0;
} }
void string_destroy(string_t s) { int string_concat(string_t * dest, string_t *src) {
free(s.c_str); if (src->c_str == NULL) {
return 0;
}
if (dest->c_str == NULL) {
dest->len++;
}
char * new_ptr = realloc(dest->c_str, dest->len + src->len-1);
if (new_ptr == NULL) {
errno = ENOMEM;
return -1;
}
memcpy(dest->c_str + dest->len-1, src->c_str, src->len);
return 0;
}
void string_destroy(string_t *s) {
free(s->c_str);
memset(s, 0, sizeof(string_t));
} }
string_t string_copy(string_t old) { string_t string_copy(string_t old) {
string_t tmp = {}; // initialize with 0 string_t tmp = {}; // initialize with 0
if (old.len == 0) {
return tmp;
}
tmp.c_str = malloc(old.len); tmp.c_str = malloc(old.len);
if (tmp.c_str == NULL) { if (tmp.c_str == NULL) {
@ -48,3 +72,9 @@ string_t string_copy(string_t old) {
return tmp; return tmp;
} }
string_t string_from_cstr(const char * str) {
string_t temp;
temp.len = strlen(str)+1;
temp.c_str = strdup(str);
return temp;
}