aboutsummaryrefslogtreecommitdiff
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
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.
-rw-r--r--NEWS4
-rw-r--r--configure.ac2
-rw-r--r--lib/.gitignore4
-rw-r--r--lib/Makefile.am15
-rw-r--r--lib/diag.c (renamed from src/diag.c)77
-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
-rw-r--r--src/Makefile.am1
-rw-r--r--src/cmdline.opt10
-rw-r--r--src/config.c2
-rw-r--r--src/eclat.c77
-rw-r--r--src/eclat.h19
-rw-r--r--src/error.c64
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/forlan01.at77
-rw-r--r--tests/testsuite.at4
-rw-r--r--tests/tforlan.c100
21 files changed, 1092 insertions, 155 deletions
diff --git a/NEWS b/NEWS
index a38e2aa..ebe5900 100644
--- a/NEWS
+++ b/NEWS
@@ -1,20 +1,20 @@
-Eclat NEWS -- history of user-visible changes. 2012-09-19
+Eclat NEWS -- history of user-visible changes. 2012-09-22
Copyright (C) 2012 Sergey Poznyakoff
See the end of file for copying conditions.
Please send Eclat bug reports to <gray+eclat@gnu.org.ua>
-No news is good news.
+Version 0.1 - No news is good news.
=========================================================================
Copyright information:
Copyright (C) 2012 Sergey Poznyakoff
Permission is granted to anyone to make or distribute verbatim copies
of this document as received, in any medium, provided that the
copyright notice and this permission notice are preserved,
thus giving the recipient permission to redistribute in turn.
Permission is granted to distribute modified versions
diff --git a/configure.ac b/configure.ac
index fd22d37..b0aba5e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -18,24 +18,26 @@ AC_PREREQ(2.63)
AC_INIT([eclat], 0.1, [gray+eclat@gnu.org.ua])
AC_CONFIG_SRCDIR([src/eclat.h])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_HEADER([config.h])
AM_INIT_AUTOMAKE([1.11 gnits tar-ustar dist-xz std-options silent-rules])
dnl AC_CONFIG_LIBOBJ_DIR([src])
# Enable silent rules by default:
AM_SILENT_RULES([yes])
# Checks for programs.
AC_PROG_CC
+AC_PROG_LEX
+AC_PROG_YACC
AC_PROG_RANLIB
# Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS([getopt.h])
# Check for libcurl
AC_ARG_VAR([CURL_CONFIG],[pathname of the curl-config binary])
AC_ARG_VAR([CURL_CFLAGS],[cc flags to use when compiling with libcurl])
AC_ARG_VAR([CURL_LIBS],[curl libraries to link with])
if test -z "$CURL_LIBS"; then
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
@@ -9,27 +9,42 @@
# 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/>.
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\
qaddparm.c\
qcreat.c\
qencode.c\
qfree.c\
reqsign.c\
sha1.c\
sha1.h\
urlencode.c\
xmltree.c
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/src/diag.c b/lib/diag.c
index 30ffa34..d061e9e 100644
--- a/src/diag.c
+++ b/lib/diag.c
@@ -5,30 +5,45 @@
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 "eclat.h"
+#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
-vdiag(grecs_locus_t const *locus, const char *qual, const char *fmt, va_list ap)
+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))
@@ -97,12 +112,70 @@ warn(const char *fmt, ...)
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
@@ -9,24 +9,57 @@
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 <stddef.h>
#include <e