Skip to content

Commit

Permalink
Merge pull request #25 from katef/kate/ebnfhtml5
Browse files Browse the repository at this point in the history
EBNF rendering to HTML
  • Loading branch information
katef authored Mar 11, 2019
2 parents a46d4c3 + efd98d6 commit 69f2d27
Show file tree
Hide file tree
Showing 6 changed files with 384 additions and 12 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ PREFIX ?= /usr/local
# layout
SUBDIR += src/bnf
SUBDIR += src/blab
SUBDIR += src/ebnfhtml5
SUBDIR += src/dot
SUBDIR += src/abnf
SUBDIR += src/iso-ebnf
Expand Down
1 change: 1 addition & 0 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ ${BUILD}/bin/kgt: ${BUILD}/src/xalloc.o

.for part in ${PART:Mbnf} ${PART:Mblab} ${PART:Mabnf} ${PART:Miso-ebnf} ${PART:Mrbnf} \
${PART:Msid} ${PART:Mdot} ${PART:Mwsn} \
${PART:Mebnfhtml5} \
${PART:Mrrd} ${PART:Mrrdot} ${PART:Mrrdump} ${PART:Mrrtdump} \
${PART:Mrrparcon} ${PART:Mrrll} ${PART:Mrrta} ${PART:Mrrtext} \
${PART:Msvg} ${PART:Mhtml5}
Expand Down
11 changes: 11 additions & 0 deletions src/ebnfhtml5/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.include "../../share/mk/top.mk"

SRC += src/ebnfhtml5/output.c

PART += ebnfhtml5

.for src in ${SRC:Msrc/ebnfhtml5/*.c}
${BUILD}/lib/ebnfhtml5.o: ${BUILD}/${src:R}.o
${BUILD}/lib/ebnfhtml5.opic: ${BUILD}/${src:R}.opic
.endfor

19 changes: 19 additions & 0 deletions src/ebnfhtml5/io.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright 2014-2017 Katherine Flavel
*
* See LICENCE for the full copyright terms.
*/

#ifndef KGT_EBNFHTML5_IO_H
#define KGT_EBNFHTML5_IO_H

struct ast_rule;

void
ebnf_html5_output(const struct ast_rule *grammar);

void
ebnf_xhtml5_output(const struct ast_rule *grammar);

#endif

337 changes: 337 additions & 0 deletions src/ebnfhtml5/output.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,337 @@
/*
* Copyright 2014-2019 Katherine Flavel
*
* See LICENCE for the full copyright terms.
*/

/*
* Extended Backus-Naur Form Output, pretty-printed to HTML.
*
* This is my own made-up dialect. It's intended for ease
* of human consumption (i.e. in documentation), rather than
* to be parsed by machine.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>

#include "../txt.h"
#include "../ast.h"
#include "../rrd/node.h"

#include "io.h"

static void output_alt(const struct ast_alt *alt);

/* TODO: centralise */
static int
xml_escputc(FILE *f, char c)
{
const char *name;

assert(f != NULL);

switch (c) {
case '&': return fputs("&amp;", f);
case '<': return fputs("&lt;", f);
case '>': return fputs("&gt;", f);

case '\a': name = "BEL"; break;
case '\b': name = "BS"; break;
case '\f': name = "FF"; break;
case '\n': name = "LF"; break;
case '\r': name = "CR"; break;
case '\t': name = "TAB"; break;
case '\v': name = "VT"; break;

default:
if (!isprint((unsigned char) c)) {
return fprintf(f, "&#x3008;<tspan class='hex'>%02X</tspan>&#x3009;", (unsigned char) c);
}

return fprintf(f, "%c", c);
}

return fprintf(f, "&#x3008;<tspan class='esc'>%s</tspan>&#x3009;", name);
}

static int
atomic(const struct ast_term *term)
{
assert(term != NULL);

switch (term->type) {
case TYPE_EMPTY:
case TYPE_RULE:
case TYPE_CI_LITERAL:
case TYPE_CS_LITERAL:
case TYPE_TOKEN:
case TYPE_PROSE:
return 1;

case TYPE_GROUP:
if (term->u.group->next != NULL) {
return 0;
}

if (term->u.group->terms->next != NULL) {
return 0;
}

return atomic(term->u.group->terms);
}
}

static const char *
rep(unsigned min, unsigned max)
{
if (min == 1 && max == 1) {
/* no operator */
return "\0";
}

if (min == 1 && max == 1) {
return "()";
}

if (min == 0 && max == 1) {
return "[]";
}

if (min == 0 && max == 0) {
return "{}";
}

return "()";
}

static void
output_literal(const char *prefix, const struct txt *t)
{
size_t i;

assert(t != NULL);
assert(t->p != NULL);

printf("<tt class='literal %s'>&quot;", prefix);

for (i = 0; i < t->n; i++) {
xml_escputc(stdout, t->p[i]);
}

printf("&quot;</tt>");
}

static void
output_term(const struct ast_term *term)
{
const char *r;

r = rep(term->min, term->max);

if (!r[0] && !atomic(term)) {
r = "()";
}

if (r[0]) {
printf("<span class='rep'>%c</span> ", r[0]);
}

switch (term->type) {
case TYPE_EMPTY:
printf("<span class='empty'>&epsilon;</span>");
break;

case TYPE_RULE:
printf("<a href='#%s' class='rule' data-min='%u' data-max='%u'>",
term->u.rule->name, term->min, term->max);
printf("%s", term->u.rule->name);
printf("</a>");
break;

case TYPE_CI_LITERAL:
output_literal("ci", &term->u.literal);
break;

case TYPE_CS_LITERAL:
output_literal("cs", &term->u.literal);
break;

case TYPE_TOKEN:
printf("<span class='token'>");
printf("%s", term->u.token);
printf("</span>");
break;

case TYPE_PROSE:
printf("<span class='prose'>");
printf("%s", term->u.prose);
printf("</span>");
break;

case TYPE_GROUP: {
const struct ast_alt *alt;

for (alt = term->u.group; alt != NULL; alt = alt->next) {
output_alt(alt);

if (alt->next != NULL) {
printf("<span class='pipe'> | </span>");
}
}
}

break;
}

if (r[0]) {
printf(" <span class='rep'>%c</span>", r[1]);
}

if (term->max > 1) {
printf("<sub class='rep'>{%u, %u}</sub>", term->min, term->max);
}
}

static void
output_alt(const struct ast_alt *alt)
{
const struct ast_term *term;

for (term = alt->terms; term != NULL; term = term->next) {
printf("<span class='alt'>");
output_term(term);
printf("</span>\n");

if (term->next != NULL) {
printf("<span class='cat'> </span>");
}
}
}

static void
output_rule(const struct ast_rule *rule)
{
const struct ast_alt *alt;

printf(" <dl class='bnf'>\n");

printf(" <dt>");
printf("<a name='%s'>", rule->name);
printf("%s", rule->name);
printf("</a>:");
printf("</dt>\n");

printf(" <dd>");

for (alt = rule->alts; alt != NULL; alt = alt->next) {
if (alt != rule->alts) {
printf("<span class='pipe'> | </span>");
}

printf("\n");
printf(" ");
output_alt(alt);

if (alt->next != NULL) {
printf("<br/>\n");
printf(" ");
}
}

printf(" </dd>\n");

printf(" </dl>\n");
printf("\n");
}

static void
output(const struct ast_rule *grammar, int xml)
{
const struct ast_rule *p;

printf(" <head>\n");
if (xml) {
printf(" <meta charset='UTF-8'/>\n");
}

printf(" <style>\n");

printf(" dl.bnf span.token {\n");
printf(" text-transform: uppercase;\n");
printf(" }\n");
printf(" \n");
printf(" dl.bnf span.cat {\n");
printf(" margin-right: 0.5ex;\n");
printf(" }\n");
printf(" \n");
printf(" dl.bnf dd > span.pipe {\n");
printf(" float: left;\n");
printf(" width: 1ex;\n");
printf(" margin-left: -1.8ex;\n");
printf(" text-align: right;\n");
printf(" padding-right: .8ex; /* about the width of a space */\n");
printf(" }\n");
printf(" \n");
printf(" dl.bnf dt {\n");
printf(" display: block;\n");
printf(" min-width: 8em;\n");
printf(" padding-right: 1em;\n");
printf(" }\n");
printf(" \n");
printf(" dl.bnf a.rule {\n");
printf(" text-decoration: none;\n");
printf(" }\n");
printf(" \n");
printf(" dl.bnf a.rule:hover {\n");
printf(" text-decoration: underline;\n");
printf(" }\n");
printf(" \n");
printf(" /* page stuff */\n");
printf(" dl.bnf { margin: 2em 4em; }\n");
printf(" dl.bnf dt { margin: 0.25em 0; }\n");
printf(" dl.bnf dd { margin-left: 2em; }\n");

printf(" </style>\n");

printf(" </head>\n");

printf(" <body>\n");

for (p = grammar; p != NULL; p = p->next) {
output_rule(p);
}

printf(" </body>\n");
}

void
ebnf_html5_output(const struct ast_rule *grammar)
{
printf("<!DOCTYPE html>\n");
printf("<html>\n");
printf("\n");

output(grammar, 0);

printf("</html>\n");
}

void
ebnf_xhtml5_output(const struct ast_rule *grammar)
{
printf("<?xml version='1.0' encoding='utf-8'?>\n");
printf("<!DOCTYPE html>\n");
printf("<html xml:lang='en' lang='en'\n");
printf(" xmlns='http://www.w3.org/1999/xhtml'\n");
printf(" xmlns:xlink='http://www.w3.org/1999/xlink'>\n");
printf("\n");

output(grammar, 1);

printf("</html>\n");
}

Loading

0 comments on commit 69f2d27

Please sign in to comment.