aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2012-09-22 16:15:48 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2012-09-22 16:30:07 +0300
commit0666fc3caae8e2db660d781e43bee2258bf06a00 (patch)
tree97380903872520efa3b2bf659465e63f2cf51a2a /lib
parent7f3dd0599ac3fb3a69c512b0ecfd043c67ca94ee (diff)
downloadeclat-0666fc3caae8e2db660d781e43bee2258bf06a00.tar.gz
eclat-0666fc3caae8e2db660d781e43bee2258bf06a00.tar.bz2
Introduce output formatting language
* configure.ac: Check for lex and yacc. * lib/diag.c: New file (moved from ../src with edits) * lib/forlan.c: New file. * lib/forlan.h: New file. * lib/forlangrm.y: New file. * lib/forlanlex.l: New file. * lib/.gitignore: Add new files. * lib/Makefile.am: Add new file. * lib/libeclat.h: Add diagnostics-related stuff. * src/Makefile.am (eclat_SOURCES): Remove diag.c * src/cmdline.opt (set_program_name): Move to ../lib/diag.c * src/diag.c: Remove (see above). * src/config.c: Reflect changes to the diagnostics subsystem. * src/eclat.c: Likewise. * src/eclat.h: Remove diagnostics-related stuff. It lives in libeclat.h from now on. * src/error.c: Remove. * tests/forlan01.at: New testcase. * tests/testsuite.at: Include forlan01.at * tests/tforlan.c: New file. * tests/.gitignore: Add new files. * tests/Makefile.am: Add new files.
Diffstat (limited to 'lib')
-rw-r--r--lib/.gitignore4
-rw-r--r--lib/Makefile.am15
-rw-r--r--lib/diag.c181
-rw-r--r--lib/forlan.c258
-rw-r--r--lib/forlan.h123
-rw-r--r--lib/forlangrm.y237
-rw-r--r--lib/forlanlex.l137
-rw-r--r--lib/libeclat.h33
8 files changed, 988 insertions, 0 deletions
diff --git a/lib/.gitignore b/lib/.gitignore
new file mode 100644
index 0000000..e0d9b22
--- /dev/null
+++ b/lib/.gitignore
@@ -0,0 +1,4 @@
+forlangrm.c
+forlangrm.h
+forlangrm.output
+forlanlex.c
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 2c1d3a8..50b28a5 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -18,6 +18,12 @@ noinst_LIBRARIES=libeclat.a
libeclat_a_SOURCES=\
base64.c\
+ diag.c\
+ forlan.c\
+ forlan.h\
+ forlangrm.h\
+ forlangrm.y\
+ forlanlex.l\
hmac_sha1.c\
libeclat.h\
q2url.c\
@@ -33,3 +39,12 @@ libeclat_a_SOURCES=\
AM_LDFLAGS = $(CURL_LIBS)
INCLUDES = -I$(top_srcdir)/grecs/src/ $(CURL_CFLAGS)
+
+forlanlex.c: forlangrm.h
+forlangrm.c forlangrm.h: forlangrm.y
+
+AM_YFLAGS=-tdv
+AM_LFLAGS=-dvp
+
+
+
diff --git a/lib/diag.c b/lib/diag.c
new file mode 100644
index 0000000..d061e9e
--- /dev/null
+++ b/lib/diag.c
@@ -0,0 +1,181 @@
+/* This file is part of Eclat.
+ Copyright (C) 2012 Sergey Poznyakoff.
+
+ Eclat is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ Eclat is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Eclat. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "libeclat.h"
+#include <string.h>
+#include <sysexits.h>
+
+const char *program_name;
+struct debug_category debug_category[LIBECLAT_DBG_MAX];
+int debug_avail;
+
+void
+set_program_name(const char *arg)
+{
+ program_name = strrchr(arg, '/');
+ if (!program_name)
+ program_name = arg;
+ else
+ program_name++;
+}
+
+void
+vdiag(grecs_locus_t const *locus, const char *qual, const char *fmt,
+ va_list ap)
+{
+ if (program_name)
+ fprintf(stderr, "%s: ", program_name);
+
+ if (locus) {
+ size_t size = 0;
+
+ if (locus->beg.col == 0)
+ fprintf(stderr, "%s:%u",
+ locus->beg.file,
+ locus->beg.line);
+ else if (strcmp(locus->beg.file, locus->end.file))
+ fprintf(stderr, "%s:%u.%u-%s:%u.%u",
+ locus->beg.file,
+ locus->beg.line, locus->beg.col,
+ locus->end.file,
+ locus->end.line, locus->end.col);
+ else if (locus->beg.line != locus->end.line)
+ fprintf(stderr, "%s:%u.%u-%u.%u",
+ locus->beg.file,
+ locus->beg.line, locus->beg.col,
+ locus->end.line, locus->end.col);
+ else
+ fprintf(stderr, "%s:%u.%u-%u",
+ locus->beg.file,
+ locus->beg.line, locus->beg.col,
+ locus->end.col);
+ fprintf(stderr, ": ");
+ }
+
+ if (qual)
+ fprintf(stderr, "%s: ", qual);
+ vfprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+}
+
+void
+diag(grecs_locus_t const *locus, const char *qual, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vdiag(locus, qual, fmt, ap);
+ va_end(ap);
+}
+
+void
+die(int status, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vdiag(NULL, NULL, fmt, ap);
+ va_end(ap);
+ exit(status);
+}
+
+void
+err(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vdiag(NULL, NULL, fmt, ap);
+ va_end(ap);
+}
+
+void
+warn(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vdiag(NULL, "warning", fmt, ap);
+ va_end(ap);
+}
+
+void
+debug_printf(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vdiag(NULL, "debug", fmt, ap);
+ va_end(ap);
+}
+
+static struct debug_category *
+find_category(const char *arg, size_t len)
+{
+ struct debug_category *dp;
+
+ for (dp = debug_category; dp < debug_category + debug_avail; dp++)
+ if (dp->length == len && memcmp(dp->name, arg, len) == 0)
+ return dp;
+ return NULL;
+}
+
+int
+parse_debug_level(const char *arg)
+{
+ unsigned long lev;
+ char *p;
+ size_t len = strcspn(arg, ".");
+ struct debug_category *dp;
+
+ if (arg[len] == 0) {
+ lev = strtoul(arg, &p, 10);
+ if (*p)
+ return -1;
+ for (dp = debug_category; dp < debug_category + debug_avail;
+ dp++)
+ dp->level = lev;
+ return 0;
+ }
+
+ dp = find_category(arg, len);
+ if (!dp)
+ return -1;
+
+ p = (char*) arg + len;
+ if (*p == 0)
+ lev = 100;
+ else if (*p != '.')
+ return -1;
+ else {
+ lev = strtoul(p + 1, &p, 10);
+ if (*p)
+ return -1;
+ }
+ dp->level = lev;
+ return 0;
+}
+
+int
+debug_register(char *name)
+{
+ if (debug_avail >= LIBECLAT_DBG_MAX)
+ die(EX_SOFTWARE, "no more debug slots available");
+ debug_category[debug_avail].name = grecs_strdup(name);
+ debug_category[debug_avail].length = strlen(name);
+ debug_category[debug_avail].level = 0;
+ return debug_avail++;
+}
diff --git a/lib/forlan.c b/lib/forlan.c
new file mode 100644
index 0000000..0854d08
--- /dev/null
+++ b/lib/forlan.c
@@ -0,0 +1,258 @@
+/* This file is part of Eclat.
+ Copyright (C) 2012 Sergey Poznyakoff.
+
+ Eclat is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ Eclat is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Eclat. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "libeclat.h"
+#include "grecs.h"
+#include "forlan.h"
+
+int forlan_dbg = -1;
+
+void
+forlan_init()
+{
+ forlan_dbg = debug_register("forlan");
+}
+
+union forlan_node *
+forlan_node_create(enum forlan_type type)
+{
+ union forlan_node *p = grecs_zalloc(sizeof(*p));
+ p->type = type;
+ return p;
+}
+
+static void f_dump_node(FILE *fp, union forlan_node *p, int *num, int lev);
+
+
+static void
+free_type_null(union forlan_node *p)
+{
+ warn("freeing undefined forlan_node");
+}
+
+void
+dump_null(FILE *fp, union forlan_node *p, int *num, int lev)
+{
+ fprintf(fp, "[undefined node]\n");
+}
+
+static void
+free_type_comp(union forlan_node *p)
+{
+ forlan_node_free(p->comp.node);
+}
+void
+dump_comp(FILE *fp, union forlan_node *p, int *num, int lev)
+{
+ fprintf(fp, "COMP");
+ if (p->comp.abs)
+ fprintf(fp, " ABS");
+ fputc('\n', fp);
+ forlan_dump_node(fp, p->comp.node, num, lev + 1);
+}
+
+static void
+free_type_test(union forlan_node *p)
+{
+ free(p->test.comp);
+ free(p->test.value);
+}
+void
+dump_test(FILE *fp, union forlan_node *p, int *num, int lev)
+{
+ fprintf(fp, "TEST: %s[%s]\n", p->test.comp, p->test.value);
+}
+
+static void
+free_type_func(union forlan_node *p)
+{
+ grecs_list_free(p->func.args);
+}
+void
+dump_func(FILE *fp, union forlan_node *p, int *num, int lev)
+{
+ struct grecs_list_entry *ep;
+ fprintf(fp, "CALL: %s\n", p->func.fp);
+ if (p->func.args)
+ for (ep = p->func.args->head; ep; ep = ep->next)
+ forlan_dump_node(fp, ep->data, num, lev + 1);
+}
+
+static void
+free_type_cond(union forlan_node *p)
+{
+ forlan_node_free((union forlan_node *)p->cond.expr);
+ forlan_node_free(p->cond.iftrue);
+ forlan_node_free(p->cond.iffalse);
+}
+void
+dump_cond(FILE *fp, union forlan_node *p, int *num, int lev)
+{
+ int n = *num;
+ fprintf(fp, "COND\n");
+ forlan_dump_node(fp, p->cond.expr, num, lev + 1);
+ fprintf(fp, "%04d: %*.*sIFTRUE %04d\n", ++*num, lev, lev, "", n);
+ forlan_dump_node(fp, p->cond.iftrue, num, lev + 1);
+ fprintf(fp, "%04d: %*.*sIFFALSE %04d\n", ++*num, lev, lev, "", n);
+ forlan_dump_node(fp, p->cond.iffalse, num, lev + 1);
+}
+
+static void
+free_type_stmt(union forlan_node *p)
+{
+ forlan_node_free(p->stmt.stmt);
+ forlan_node_free(p->stmt.next);
+}
+void
+dump_stmt(FILE *fp, union forlan_node *p, int *num, int lev)
+{
+ f_dump_node(fp, p->stmt.stmt, num, lev);
+ forlan_dump_node(fp, p->stmt.next, num, lev);
+}
+
+static void
+free_type_lit(union forlan_node *p)
+{
+ free(p->lit.string);
+}
+void
+dump_lit(FILE *fp, union forlan_node *p, int *num, int lev)
+{
+ fprintf(fp, "LIT: \"%s\"\n", p->lit.string);
+}
+
+static void
+free_type_expr(union forlan_node *p)
+{
+ forlan_node_free(p->expr.arg[0]);
+ forlan_node_free(p->expr.arg[1]);
+}
+void
+dump_expr(FILE *fp, union forlan_node *p, int *num, int lev)
+{
+ static char *opstr[] = { "NODE", "AND", "OR", "NOT" };
+
+ fprintf(fp, "%s\n", opstr[p->expr.opcode]);
+ forlan_dump_node(fp, p->expr.arg[0], num, lev + 1);
+ if (p->expr.arg[1])
+ forlan_dump_node(fp, p->expr.arg[1], num, lev + 1);
+}
+
+static void
+free_type_last(union forlan_node *p)
+{
+}
+void
+dump_last(FILE *fp, union forlan_node *p, int *num, int lev)
+{
+ fprintf(fp, "LAST\n");
+}
+
+struct forlan_node_method {
+ void (*f_free)(union forlan_node *);
+ void (*f_dump)(FILE *fp, union forlan_node *node, int *num, int lev);
+};
+
+static struct forlan_node_method f_tab[] = {
+ free_type_null, dump_null, /* Unknown/unset type */
+ free_type_comp, dump_comp, /* A path component */
+ free_type_test, dump_test, /* Value test (f[X]) */
+ free_type_func, dump_func, /* Function call */
+ free_type_cond, dump_cond, /* Conditional */
+ free_type_stmt, dump_stmt, /* Statement */
+ free_type_lit, dump_lit, /* Literal */
+ free_type_expr, dump_expr, /* Boolean expression */
+ free_type_last, dump_last, /* "last" */
+};
+
+void
+forlan_node_free(union forlan_node *p)
+{
+ if (!p)
+ return;
+ if (p->type > sizeof(f_tab) / sizeof(f_tab[0]))
+ abort();
+ if (f_tab[p->type].f_free)
+ f_tab[p->type].f_free(p);
+ free(p);
+}
+
+static void
+stmt_list_free_entry(void *p)
+{
+ forlan_node_free(p);
+}
+
+struct grecs_list *
+forlan_stmt_list()
+{
+ struct grecs_list *lp;
+
+ lp = grecs_list_create();
+ lp->free_entry = stmt_list_free_entry;
+ return lp;
+}
+
+union forlan_node *
+forlan_stmt_from_list(struct grecs_list *list)
+{
+ union forlan_node **tail = NULL, *ret = NULL;
+ struct grecs_list_entry *ep;
+
+ for (ep = list->head; ep; ep = ep->next) {
+ union forlan_node *sp = forlan_node_create(forlan_type_stmt);
+ sp->stmt.stmt = ep->data;
+ if (tail)
+ *tail = sp;
+ else
+ ret = sp;
+ tail = &sp->stmt.next;
+ }
+ list->free_entry = NULL;
+ grecs_list_free(list);
+ return ret;
+}
+
+static void
+f_dump_node(FILE *fp, union forlan_node *p, int *num, int lev)
+{
+ if (p) {
+ if (p->type > sizeof(f_tab) / sizeof(f_tab[0]))
+ abort();
+ if (f_tab[p->type].f_dump)
+ f_tab[p->type].f_dump(fp, p, num, lev);
+ else
+ fprintf(fp, "type %d", p->type);
+ } else
+ fprintf(fp, "NULL");
+}
+
+void
+forlan_dump_node(FILE *fp, union forlan_node *p, int *num, int lev)
+{
+ if (!p)
+ return;
+ ++*num;
+ fprintf(fp, "%04d: %*.*s", *num, lev, lev, "");
+ f_dump_node(fp, p, num, lev);
+}
+
+void
+forlan_dump_tree(FILE *fp, union forlan_node *node)
+{
+ int n = 0;
+ forlan_dump_node(fp, node, &n, 0);
+}
diff --git a/lib/forlan.h b/lib/forlan.h
new file mode 100644
index 0000000..4dadc70
--- /dev/null
+++ b/lib/forlan.h
@@ -0,0 +1,123 @@
+/* This file is part of Eclat.
+ Copyright (C) 2012 Sergey Poznyakoff.
+
+ Eclat is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ Eclat is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Eclat. If not, see <http://www.gnu.org/licenses/>. */
+
+#define FORLAN_DBG_LEX 3
+#define FORLAN_DBG_GRAM 2
+#define FORLAN_DBG_EVAL 1
+
+extern int forlan_dbg;
+
+void forlan_init();
+void forlan_lex_begin(const char *input, size_t length,
+ struct grecs_locus_point *pt);
+void forlan_lex_end(void);
+int forlan_parse(const char *input, size_t length,
+ struct grecs_locus_point *pt);
+
+union forlan_node; /* Declared below */
+
+enum forlan_type {
+ forlan_type_null, /* Unknown/unset type */
+ forlan_type_comp, /* A path component */
+ forlan_type_test, /* Value test (f[X]) */
+ forlan_type_func, /* Function call */
+ forlan_type_cond, /* Conditional */
+ forlan_type_stmt, /* Statement */
+ forlan_type_lit, /* Literal */
+ forlan_type_expr, /* Boolean expression */
+ forlan_type_last /* Return last evaluated grecs_node */
+};
+
+/* A path component */
+struct forlan_node_comp {
+ enum forlan_type type;
+ int abs;
+ union forlan_node *node;
+};
+
+/* Path test: .path.comp[value] */
+struct forlan_node_test {
+ enum forlan_type type;
+ char *comp;
+ char *value;
+};
+
+/* Function call */
+struct forlan_node_func {
+ enum forlan_type type;
+ void *fp; /* FIXME: replace with typedef */
+ struct grecs_list *args; /* Arguments are struct forlan_node * */
+};
+
+/* Conditional */
+struct forlan_node_cond {
+ enum forlan_type type;
+ union forlan_node *expr; /* Controlling expression */
+ union forlan_node *iftrue; /* Run this if expr yields true */
+ union forlan_node *iffalse; /* Run this if expr yields false */
+};
+
+/* Statement or statement list */
+struct forlan_node_stmt {
+ enum forlan_type type;
+ union forlan_node *stmt;
+ union forlan_node *next;
+};
+
+/* Literal string */
+struct forlan_node_lit {
+ enum forlan_type type;
+ char *string;
+};
+
+/* Boolean opcodes */
+enum forlan_opcode {
+ forlan_opcode_node, /* Evaluate node, set 'last' */
+ forlan_opcode_and, /* Boolean AND */
+ forlan_opcode_or, /* Boolean OR */
+ forlan_opcode_not /* Boolean NOT */
+};
+
+/* Boolean expression */
+struct forlan_node_expr {
+ enum forlan_type type;
+ enum forlan_opcode opcode;
+ union forlan_node *arg[2];
+};
+
+/* Now get all this together */
+union forlan_node {
+ enum forlan_type type;
+ struct forlan_node_comp comp; /* A path component */
+ struct forlan_node_test test; /* Value test (f[X]) */
+ struct forlan_node_func func; /* Function call */
+ struct forlan_node_cond cond; /* Conditional */
+ struct forlan_node_stmt stmt; /* Statement */
+ struct forlan_node_lit lit; /* Literal */
+ struct forlan_node_expr expr; /* Boolean expression */
+ /* forlan_type_last needs no additional data */
+};
+
+union forlan_node *forlan_node_create(enum forlan_type type);
+void forlan_node_free(union forlan_node *);
+struct grecs_list *forlan_stmt_list(void);
+struct grecs_list *forlan_complist(void);
+union forlan_node *forlan_stmt_from_list(struct grecs_list *list);
+
+extern union forlan_node *forlan_parse_tree;
+
+void forlan_dump_node(FILE *fp, union forlan_node *p, int *num, int lev);
+void forlan_dump_tree(FILE *fp, union forlan_node *node);
diff --git a/lib/forlangrm.y b/lib/forlangrm.y
new file mode 100644
index 0000000..1b7781d
--- /dev/null
+++ b/lib/forlangrm.y
@@ -0,0 +1,237 @@
+%{
+/* This file is part of Eclat.
+ Copyright (C) 2012 Sergey Poznyakoff.
+
+ Eclat is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ Eclat is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Eclat. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "libeclat.h"
+#include <grecs.h>
+#include <grecs-locus.h>
+#include "forlangrm.h"
+#include "forlan.h"
+
+static int yyerror(char *);
+union forlan_node *forlan_parse_tree;
+%}
+%error-verbose
+%locations
+
+%union {
+ char *string;
+ union forlan_node *node;
+ struct grecs_list *list;
+};
+
+%token <string> STRING IDENT
+%token LAST IF ELSE
+
+%left OR
+%left AND
+%left NOT
+
+%type <node> stmt stmt_cond stmt_expr stmt_blk cond bool node comp funcall arg
+%type <list> stmtlist complist arglist
+%type <string> string
+
+%%
+input : stmtlist
+ {
+ forlan_parse_tree = forlan_stmt_from_list($1);
+ }
+ ;
+
+stmtlist : stmt
+ {
+ $$ = forlan_stmt_list();
+ grecs_list_append($$, $1);
+ }
+ | stmtlist stmt
+ {
+ grecs_list_append($1, $2);
+ $$ = $1;
+ }
+ ;
+
+stmt : stmt_cond
+ | stmt_expr
+ | stmt_blk
+ ;
+
+stmt_blk : '{' stmtlist '}'
+ {
+ $$ = forlan_stmt_from_list($2);
+ }
+ ;
+
+stmt_cond : IF cond stmt
+ {
+ $$ = forlan_node_create(forlan_type_cond);
+ $$->cond.expr = $2;
+ $$->cond.iftrue = $3;
+ $$->cond.iffalse = NULL;
+ }
+ | IF cond stmt ELSE stmt
+ {
+ $$ = forlan_node_create(forlan_type_cond);
+ $$->cond.expr = $2;
+ $$->cond.iftrue = $3;
+ $$->cond.iffalse = $5;
+ }
+ ;
+
+cond : bool
+ ;
+
+bool : node
+ {
+ $$ = forlan_node_create(forlan_type_expr);
+ $$->expr.opcode = forlan_opcode_node;
+ $$->expr.arg[0] = $1;
+ }
+ | bool AND bool
+ {
+ $$ = forlan_node_create(forlan_type_expr);
+ $$->expr.opcode = forlan_opcode_and;
+ $$->expr.arg[0] = $1;
+ $$->expr.arg[1] = $3;
+ }
+ | bool OR bool
+ {
+ $$ = forlan_node_create(forlan_type_expr);
+ $$->expr.opcode = forlan_opcode_or;
+ $$->expr.arg[0] = $1;
+ $$->expr.arg[1] = $3;
+ }
+ | NOT bool
+ {
+ $$ = forlan_node_create(forlan_type_expr);
+ $$->expr.opcode = forlan_opcode_not;
+ $$->expr.arg[0] = $2;
+ }
+ | '(' bool ')'
+ {
+ $$ = $2;
+ }
+ ;
+
+node : complist
+ {
+ $$ = forlan_node_create(forlan_type_comp);
+ $$->comp.abs = 0;
+ $$->comp.node = forlan_stmt_from_list($1);
+ }
+ | '.' complist
+ {
+ $$ = forlan_node_create(forlan_type_comp);
+ $$->comp.abs = 1;
+ $$->comp.node = forlan_stmt_from_list($2);
+ }
+ | LAST
+ {
+ $$ = forlan_node_create(forlan_type_last);
+ }
+ ;
+
+complist : comp
+ {
+ $$ = forlan_stmt_list();
+ grecs_list_append($$, $1);
+ }
+ | complist '.' comp
+ {
+ grecs_list_append($1, $3);
+ $$ = $1;
+ }
+ ;
+
+comp : IDENT
+ {
+ $$ = forlan_node_create(forlan_type_lit);
+ $$->lit.string = $1;
+ }
+ | IDENT '[' string ']'
+ {
+ $$ = forlan_node_create(forlan_type_test);
+ $$->test.comp = $1;
+ $$->test.value = $3;
+ }
+ | funcall
+ ;
+
+string : IDENT
+ | STRING
+ ;
+
+funcall : IDENT '(' ')'
+ {
+ $$ = forlan_node_create(forlan_type_func);
+ $$->func.fp = $1; //FIXME
+ $$->func.args = NULL;
+ }
+ | IDENT '(' arglist ')'
+ {
+ $$ = forlan_node_create(forlan_type_func);
+ $$->func.fp = $1; //FIXME
+ $$->func.args = $3;
+ }
+ ;
+
+arglist : arg
+ {
+ $$ = forlan_stmt_list();
+ grecs_list_append($$, $1);
+ }
+ | arglist ',' arg
+ {
+ grecs_list_append($1, $3);
+ $$ = $1;
+ }
+ ;
+
+arg : node
+ | STRING
+ {
+ $$ = forlan_node_create(forlan_type_lit);
+ $$->lit.string = $1;
+ }
+ ;
+
+stmt_expr : funcall ';'
+ ;
+%%
+static int
+yyerror(char *s)
+{
+ grecs_error(&yylloc, 0, "%s", s);
+ return 0;
+}
+
+int
+forlan_parser()
+{
+ yydebug = debug_level(forlan_dbg) >= FORLAN_DBG_GRAM;
+ return yyparse();
+}
+
+int
+forlan_parse(const char *input, size_t length, struct grecs_locus_point *pt)
+{
+ int rc;
+ forlan_lex_begin(input, length, pt);
+ rc = forlan_parser();
+ forlan_lex_end();
+ return rc;
+}
+
+
diff --git a/lib/forlanlex.l b/lib/forlanlex.l
new file mode 100644
index 0000000..99966ed
--- /dev/null
+++ b/lib/forlanlex.l
@@ -0,0 +1,137 @@
+%{
+/* This file is part of Eclat.
+ Copyright (C) 2012 Sergey Poznyakoff.
+
+ Eclat is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ Eclat is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Eclat. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "libeclat.h"
+#include <grecs.h>
+#include <grecs-locus.h>
+#include "forlangrm.h"
+#include "forlan.h"
+
+static const char *forlan_input_base;
+static size_t forlan_input_len;
+static size_t forlan_input_pos;
+
+#undef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ do { \
+ size_t __s = forlan_input_len - forlan_input_pos; \
+ if (__s > max_size) \
+ __s = max_size; \
+ if (__s > 0) { \
+ memcpy(buf, forlan_input_base, __s); \
+ forlan_input_pos += __s; \
+ } \
+ result = __s; \
+ } while(0)
+
+#define YY_USER_ACTION do { \
+ if (YYSTATE == 0) { \
+ yylloc.beg = grecs_current_locus_point; \
+ yylloc.beg.col++; \
+ } \
+ grecs_current_locus_point.col += yyleng; \
+ yylloc.end = grecs_current_locus_point; \
+ } while (0);
+
+static int yywrap(void);
+
+%}
+
+WS [ \t\f][ \t\f]*
+IDC [a-zA-Z_0-9-]
+%x COMMENT ML STR
+%%
+ /* Comments */
+"/*" BEGIN(COMMENT);
+<COMMENT>[^*\n]* /* eat anything that's not a '*' */
+<COMMENT>"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */
+<COMMENT>\n grecs_locus_point_advance_line(grecs_current_locus_point);
+<COMMENT>"*"+"/" BEGIN(INITIAL);
+"//".* ;
+ /* Keywords */
+if return IF;
+else return ELSE;
+last return LAST;
+! return NOT;
+"&&" return AND;
+"||" return OR;
+{IDC}{IDC}* { grecs_line_begin();
+ grecs_line_add(yytext, yyleng);
+ yylval.string = grecs_line_finish();
+ return IDENT; }
+ /* Quoted strings */
+\"[^\\"\n]*\" { grecs_line_begin();
+ grecs_line_add(yytext + 1, yyleng - 2);
+ yylval.string = grecs_line_finish();
+ return STRING; }
+\"[^\\"\n]*\\\n { BEGIN(STR);
+ grecs_line_begin();
+ grecs_line_acc_grow_unescape_last(yytext + 1,
+ yyleng - 1);
+ grecs_locus_point_advance_line(grecs_current_locus_point); }
+\"[^\\"\n]*\\. { BEGIN(STR);
+ grecs_line_begin();
+ grecs_line_acc_grow_unescape_last(yytext + 1,
+ yyleng - 1); }
+<STR>\"[^\\"\n]*\\\n { grecs_line_acc_grow_unescape_last(yytext, yyleng);
+ grecs_locus_point_advance_line(grecs_current_locus_point); }
+<STR>[^\\"\n]*\\. { grecs_line_acc_grow_unescape_last(yytext, yyleng); }
+<STR>[^\\"\n]*\" { BEGIN(INITIAL);
+ if (yyleng > 1)
+ grecs_line_add(yytext, yyleng - 1);
+ yylval.string = grecs_line_finish();
+ return STRING; }
+ /* Other tokens */
+{WS} ;
+\n { grecs_locus_point_advance_line(grecs_current_locus_point); }
+[.,;{}()\[\]] return yytext[0];
+. { if (isascii(yytext[0]) && isprint(yytext[0]))
+ grecs_error(&yylloc, 0,
+ _("stray character %c"), yytext[0]);
+ else
+ grecs_error(&yylloc, 0,
+ _("stray character \\%03o"),
+ (unsigned char) yytext[0]); }
+%%
+
+static int
+yywrap()
+{
+ return 1;
+}
+
+void
+forlan_lex_begin(const char *input, size_t length,
+ struct grecs_locus_point *pt)
+{
+ forlan_input_base = input;
+ forlan_input_len = length;
+ grecs_current_locus_point = *pt;
+ yy_flex_debug = debug_level(forlan_dbg) >= FORLAN_DBG_LEX;
+ grecs_line_acc_create();
+}
+
+void
+forlan_lex_end()
+{
+ grecs_line_acc_free();
+}
+
+
+
+
+
diff --git a/lib/libeclat.h b/lib/libeclat.h
index 670c51b..5f37cc9 100644
--- a/lib/libeclat.h
+++ b/lib/libeclat.h
@@ -18,6 +18,39 @@
#include <expat.h>
#include "grecs.h"
+extern const char *program_name;
+
+struct debug_category {
+ const char *name;
+ size_t length;
+ int level;
+};
+
+extern struct debug_category debug_category[];
+#define LIBECLAT_DBG_MAX 64
+
+#define debug_level(cat) ((cat >= 0 && cat < LIBECLAT_DBG_MAX) ? \
+ debug_category[cat].level : 0)
+#define debug(cat, lev, s) \
+ do { \
+ if (debug_level(cat) >= (lev)) \
+ debug_printf s; \
+ } while(0)
+
+void set_program_name(const char *arg);
+
+void die(int status, const char *fmt, ...);
+void vdiag(grecs_locus_t const *locus, const char *qual, const char *fmt,
+ va_list ap);
+void diag(grecs_locus_t const *locus, const char *qual, const char *fmt, ...);
+void err(const char *fmt, ...);
+void warn(const char *fmt, ...);
+void debug_printf(const char *fmt, ...);
+
+int parse_debug_level(const char *arg);
+int debug_register(char *name);
+
+
void hmac_sha1(const void *text, size_t textlen,
const void *key, size_t keylen,
void *digest);

Return to:

Send suggestions and report system problems to the System administrator.