%{
/* This file is part of fileserv.
Copyright (C) 2017, 2018 Sergey Poznyakoff
Fileserv 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.
Fileserv 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 fileserv. If not, see . */
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include "mtint.h"
#include "grammar.h"
static void
yyprint (FILE *output, unsigned short toknum, YYSTYPE val)
{
switch (toknum)
{
case TYPE:
case IDENT:
case STRING:
fprintf (output, "[%lu] %s", (unsigned long) val.string.len,
val.string.ptr);
break;
case EOL:
fprintf (output, "\\n");
break;
default:
if (isprint (toknum))
fprintf (output, "'%c'", toknum);
else
fprintf (output, "tok(%d)", toknum);
break;
}
}
#define YYPRINT yyprint
extern int yyerror (char *s);
extern int yylex (void);
static void
arg_list_append (struct arg_list *al, struct mimetypes_string const *str)
{
struct arg_elt *elt;
elt = malloc (sizeof *elt);
if (!elt)
mimetypes_nomem ();
elt->string.ptr = str->ptr;
elt->string.len = str->len;
LLE_APPEND (al, elt, link);
}
static void
arg_list_destroy (struct arg_list *al)
{
struct arg_elt *elt;
while ((elt = LL_FIRST (al)) != NULL)
{
/* FIXME: free? */
LLE_UNLINK (al, elt, link);
}
}
//FIXME
struct rule_list rule_list = LL_HEAD_INITIALIZER;
static struct node *make_node (enum node_type type,
struct locus_range const *loc);
static struct node *make_binary_node (int op,
struct node *left, struct node *rigth,
struct locus_range const *loc);
static struct node *make_negation_node (struct node *p,
struct locus_range const *loc);
static struct node *make_suffix_node (struct mimetypes_string *suffix,
struct locus_range const *loc);
static struct node *make_functional_node (char *ident, struct arg_list *list,
struct locus_range const *loc);
static size_t errors;
%}
%locations
%expect 15
%token TYPE IDENT
%token STRING
%token EOL BOGUS PRIORITY
%left ','
%left '+'
%type arg
%type arglist
%type function stmt rule maybe_rule
%type priority maybe_priority
%union {
struct mimetypes_string string;
char *s;
struct arg_list list;
int result;
struct node *node;
}
%%
input : list
;
list : rule_line
| list EOL rule_line
;
rule_line: /* empty */
| TYPE maybe_rule maybe_priority
{
struct rule *p = malloc (sizeof (*p));
if (!p)
mimetypes_nomem ();
LLE_APPEND (&rule_list, p, link);
locus_range_init (&p->loc);
p->type = $1.ptr;
p->node = $2;
p->priority = $3;
locus_point_copy (&p->loc.beg, &@1.beg);
locus_point_copy (&p->loc.end, &@3.end);
#if 0
YY_LOCATION_PRINT (stderr, p->loc);
fprintf (stderr, ": rule %s\n", p->type);
#endif
}
| BOGUS
{
YYERROR;
}
| error
{
errors++;
// arg_list_destroy (); //FIXME
lex_next_rule ();
yyerrok;
yyclearin;
}
;
maybe_rule: /* empty */
{
$$ = make_node (true_node, &yylloc);
}
| rule
;
rule : stmt
| rule rule %prec ','
{
struct locus_range lr;
lr.beg = @1.beg;
lr.end = @2.end;
$$ = make_binary_node (L_OR, $1, $2, &lr);
}
| rule ',' rule
{
struct locus_range lr;
lr.beg = @1.beg;
lr.end = @3.end;
$$ = make_binary_node (L_OR, $1, $3, &lr);
}
| rule '+' rule
{
struct locus_range lr;
lr.beg = @1.beg;
lr.end = @3.end;
$$ = make_binary_node (L_AND, $1, $3, &lr);
}
;
stmt : '!' stmt
{
$$ = make_negation_node ($2, &@2);
}
| '(' rule ')'
{
$$ = $2;
}
| STRING
{
$$ = make_suffix_node (&$1, &@1);
}
| function
| BOGUS
{
YYERROR;
}
;
priority : PRIORITY '(' arglist ')'
{
if (LL_COUNT (&$3) != 1)
{
mimetypes_error_at (&@1, "priority takes single numberic argument");
YYERROR;
}
$$ = atoi (LL_FIRST (&$3)->string.ptr);
arg_list_destroy (&$3);
}
;
maybe_priority: /* empty */
{
$$ = 100;
}
| priority
;
function : IDENT '(' arglist ')'
{
struct locus_range lr;
lr.beg = @1.beg;
lr.end = @4.end;
$$ = make_functional_node ($1.ptr, &$3, &lr);
if (!$$)
YYERROR;
}
;
arglist : arg
{
LL_HEAD_INIT (&$$);
arg_list_append (&$$, &$1);
}
| arglist ',' arg
{
arg_list_append (&$1, &$3);
$$ = $1;
}
;
arg : STRING
| BOGUS
{
YYERROR;
}
;
%%
int
mimetypes_parse (const char *name)
{
int rc;
char *p;
if (mimetypes_open (name))
return 1;
p = getenv ("MIMETYPES_DEBUG_GRAM");
yydebug = p ? (*p - '0') : 0;
rc = yyparse ();
mimetypes_close ();
return rc || errors;
}
static struct node *
make_node (enum node_type type, struct locus_range const *loc)
{
struct node *p = malloc (sizeof *p);
if (!p)
mimetypes_nomem ();
p->type = type;
locus_range_init (&p->loc);
locus_range_copy (&p->loc, loc);
return p;
}
static struct node *
make_binary_node (int op, struct node *left, struct node *right,
struct locus_range const *loc)
{
struct node *node = make_node (binary_node, loc);
node->v.bin.op = op;
node->v.bin.arg1 = left;
node->v.bin.arg2 = right;
return node;
}
struct node *
make_negation_node (struct node *p, struct locus_range const *loc)
{
struct node *node = make_node (negation_node, loc);
node->v.arg = p;
return node;
}
struct node *
make_suffix_node (struct mimetypes_string *suffix,
struct locus_range const *loc)
{
struct node *node = make_node (suffix_node, loc);
node->v.suffix = *suffix;
return node;
}
struct node *
make_functional_node (char *ident, struct arg_list *list,
struct locus_range const *loc)
{
size_t count, i;
struct builtin_tab const *p;
struct node *node;
union argument *args;
struct arg_elt *elt;
p = find_builtin (ident);
if (!p)
{
mimetypes_error_at (loc, "unknown builtin: %s", ident);
return NULL;
}
count = LL_COUNT (list);
i = strlen (p->args);
if (count < i)
{
mimetypes_error_at (loc, "too few arguments in call to %s", ident);
return NULL;
}
else if (count > i)
{
mimetypes_error_at (loc, "too many arguments in call to %s", ident);
return NULL;
}
args = calloc (count, sizeof *args);
if (!args)
mimetypes_nomem ();
i = 0;
LL_FOREACH (list, elt, link)
{
char *tmp;
switch (p->args[i])
{
case 'd':
args[i].number = strtoul (elt->string.ptr, &tmp, 0);
if (*tmp)
goto err;
break;
case 's':
args[i].string = elt->string;
break;
case 'x':
{
int rc = regcomp (&args[i].rx, elt->string.ptr,
REG_EXTENDED|REG_NOSUB);
if (rc)
{
char errbuf[512];
regerror (rc, &args[i].rx, errbuf, sizeof errbuf);
mimetypes_error_at (loc, "%s", errbuf);
return NULL;
}
}
break;
case 'c':
args[i].c = strtoul (elt->string.ptr, &tmp, 0);
if (*tmp)
goto err;
break;
default:
abort ();
}
i++;
}
node = make_node (functional_node, loc);
node->v.function.fun = p->handler;
node->v.function.args = args;
return node;
err:
mimetypes_error_at (loc, "argument %lu has wrong type in call to %s",
(unsigned long) i, ident);
return NULL;
}