%{
#include <assert.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "evtr.h"
#include "tok.h"
#include "internal.h"
#include "ktrfmt.tab.h"
int __ktrfmtlex(YYSTYPE *);
#define __ktrfmt_lex __ktrfmtlex
void __ktrfmt_error (struct ktrfmt_parse_ctx *, const char *);
static void do_parse_err(struct ktrfmt_parse_ctx *, const char *, ...)
__printflike(2, 3);
static
void
do_parse_err(struct ktrfmt_parse_ctx *ctx, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vsnprintf(ctx->errbuf, ctx->errbufsz, fmt, ap);
va_end(ap);
ctx->err = !0;
}
#define parse_err(fmt, ...) \
do { \
do_parse_err(ctx, fmt, ##__VA_ARGS__); \
YYABORT; \
} while (0)
static
struct evtr_variable *
evtr_var_new(const char *name)
{
struct evtr_variable *var;
var = calloc(1, sizeof(*var));
if (var) {
if (!(var->name = strdup(name))) {
free(var);
return NULL;
}
var->val.type = EVTR_VAL_NIL;
}
return var;
}
static
char *
uniq_varname(void)
{
static long serno;
static char buf[100];
serno++;
snprintf(buf, sizeof(buf), "@%ld", serno);
return &buf[0];
}
static
int
index_hash(struct ktrfmt_parse_ctx *ctx, const char *hashname,
evtr_variable_value_t val, evtr_var_t *_var)
{
evtr_var_t hsh, var;
uintptr_t ret, key;
hsh = symtab_find(ctx->symtab, hashname);
if (hsh->val.type == EVTR_VAL_NIL) {
printd(PARSE, "creating hash for %s\n", hsh->name);
hsh->val.type = EVTR_VAL_HASH;
hsh->val.hashtab = hash_new();
} else if (hsh->val.type != EVTR_VAL_HASH) {
printd(PARSE, "trying to use type %d as hash\n", hsh->val.type);
return !0;
}
if (val->type == EVTR_VAL_INT) {
key = val->num;
printd(PARSE, "looking up %s[%jd] in %p\n", hsh->name,
val->num, hsh->val.hashtab);
} else if (val->type == EVTR_VAL_STR) {
key = (uintptr_t)val->str;
printd(PARSE, "looking up %s[\"%s\"] in %p\n", hsh->name,
val->str, hsh->val.hashtab);
} else {
do_parse_err(ctx, "trying to index hash '%s' with "
"non-supported value", hashname);
return !0;
}
if (hash_find(hsh->val.hashtab, key, &ret)) {
printd(PARSE, "didn't find it\n");
var = evtr_var_new(uniq_varname());
if (var) {
printd(PARSE, "inserting it as %s\n", var->name);
if (!hash_insert(hsh->val.hashtab, key,
(uintptr_t)var)) {
do_parse_err(ctx, "can't insert temporary "
"variable into hash\n");
return !0;
}
symtab_insert(ctx->symtab, var->name, var);
} else {
do_parse_err(ctx, "out of memory");
}
} else {
var = (struct evtr_variable *)ret;
}
if (!var) {
fprintf(stderr, "no var!\n");
return !0;
}
*_var = var;
return 0;
}
%}
%verbose
%error-verbose
%debug
%name-prefix "__ktrfmt_"
%define api.pure
%parse-param{struct ktrfmt_parse_ctx *ctx}
%union {
struct token *tok;
struct evtr_variable *var;
struct evtr_variable_value *val;
void *na;
}
%token<tok> TOK_ID
%token<tok> TOK_CTOR
%token<tok> TOK_INT
%token<tok> TOK_STR
%token<na> TOK_EQ
%token<na> TOK_LEFT_BRACK
%token<na> TOK_RIGHT_BRACK
%token<na> TOK_DOT
%type<var> constant
%type<var> ctor_args
%type<var> construct_expr
%type<var> primary_expr
%type<var> postfix_expr
%type<var> unary_expr
%type<na> assign_expr
%type<na> expr
%%
input: stmt
stmt: unary_expr {
ctx->var = $1;
}
| expr
;
constant: TOK_INT {
evtr_var_t var;
if (!$1->str)
parse_err("out of memory");
var = evtr_var_new(uniq_varname());
var->val.type = EVTR_VAL_INT;
errno = 0;
var->val.num = strtoll($1->str, NULL, 0);
if (errno) {
parse_err("Can't parse numeric constant '%s'", $1->str);
}
$$ = var;
tok_free($1);
}
| TOK_STR {
evtr_var_t var;
if (!$1->str)
parse_err("out of memory");
var = evtr_var_new(uniq_varname());
var->val.type = EVTR_VAL_STR;
var->val.str = $1->str;
if (!var->val.str) {
parse_err("out of memory");
}
$$ = var;
tok_free($1);
}
;
ctor_args: constant {
evtr_var_t ctor;
ctor = evtr_var_new(uniq_varname());
ctor->val.type = EVTR_VAL_CTOR;
ctor->val.ctor.name = NULL;
TAILQ_INIT(&ctor->val.ctor.args);
TAILQ_INSERT_HEAD(&ctor->val.ctor.args, &$1->val, link);
$$ = ctor;
}
| constant ctor_args {
TAILQ_INSERT_HEAD(&$2->val.ctor.args, &$1->val, link);
$$ = $2;
}
;
construct_expr: TOK_CTOR {
evtr_var_t var;
if (!$1->str)
parse_err("out of memory");
printd(PARSE, "TOK_CTOR\n");
printd(PARSE, "tok: %p, str = %p\n", $1, $1->str);
var = evtr_var_new(uniq_varname());
var->val.type = EVTR_VAL_CTOR;
var->val.ctor.name = $1->str;
TAILQ_INIT(&var->val.ctor.args);
tok_free($1);
$$ = var;
}
| TOK_CTOR ctor_args {
evtr_variable_value_t val;
if (!$1->str)
parse_err("out of memory");
printd(PARSE, "TOK_CTOR\n");
printd(PARSE, "tok: %p, str = %p\n", $1, $1->str);
$2->val.ctor.name = $1->str;
$$ = $2;
printd(PARSE, "CTOR: %s\n", $1->str);
TAILQ_FOREACH(val, &$2->val.ctor.args, link) {
switch (val->type) {
case EVTR_VAL_INT:
printd(PARSE, "\t%jd\n", val->num);
break;
case EVTR_VAL_STR:
printd(PARSE, "\t\"%s\"\n", val->str);
break;
case EVTR_VAL_NIL:
assert(!"can't get here");
default:
;
}
}
}
;
primary_expr: TOK_ID {
evtr_var_t var;
if (!$1->str)
parse_err("out of memory");
printd(PARSE, "TOK_ID\n");
printd(PARSE, "tok: %p, str = %p\n", $1, $1->str);
var = symtab_find(ctx->symtab, $1->str);
if (!var) {
if (!(var = evtr_var_new($1->str))) {
tok_free($1);
parse_err("out of memory");
}
printd(PARSE, "creating var %s\n", $1->str);
symtab_insert(ctx->symtab, $1->str, var);
}
$$ = var;
tok_free($1);
}
| constant {
$$ = $1;
}
;
postfix_expr: postfix_expr TOK_LEFT_BRACK postfix_expr TOK_RIGHT_BRACK {
evtr_var_t var;
if (index_hash(ctx, $1->name, &$3->val, &var))
YYABORT;
$$ = var;
}
| postfix_expr TOK_DOT TOK_ID {
evtr_var_t var, tmp;
if (!$3->str)
parse_err("out of memory");
tmp = evtr_var_new(uniq_varname());
tmp->val.type = EVTR_VAL_STR;
tmp->val.str = $3->str;
if (index_hash(ctx, $1->name, &tmp->val, &var))
YYABORT;
tok_free($3);
$$ = var;
}
| primary_expr {
$$ = $1;
}
;
unary_expr: postfix_expr {
$$ = $1;
}
;
assign_expr: unary_expr TOK_EQ constant {
$1->val = $3->val;
ctx->ev->type = EVTR_TYPE_STMT;
ctx->ev->stmt.var = $1;
ctx->ev->stmt.val = &$3->val;
ctx->ev->stmt.op = EVTR_OP_SET;
}
| unary_expr TOK_EQ construct_expr {
$1->val = $3->val;
ctx->ev->type = EVTR_TYPE_STMT;
ctx->ev->stmt.var = $1;
ctx->ev->stmt.val = &$3->val;
ctx->ev->stmt.op = EVTR_OP_SET;
}
;
expr: assign_expr {
$$ = $1;
}
;
%%
void * __ktrfmt_scan_string(const char *);
void __ktrfmt_delete_buffer(void *);
void
__ktrfmt_error (struct ktrfmt_parse_ctx *ctx, const char *s)
{
do_parse_err(ctx, "%s", s);
}
int
parse_string(evtr_event_t ev, struct symtab *symtab, const char *str,
char *errbuf, size_t errbufsz)
{
void *bufstate;
int ret;
struct ktrfmt_parse_ctx ctx;
printd(PARSE, "parsing \"%s\"\n", str);
ctx.ev = ev;
ctx.symtab = symtab;
ctx.errbuf = errbuf;
ctx.errbuf[0] = '\0';
ctx.errbufsz = errbufsz;
ctx.err = 0;
bufstate = __ktrfmt_scan_string(str);
ret = __ktrfmt_parse(&ctx);
__ktrfmt_delete_buffer(bufstate);
return ret;
}
int
parse_var(const char *str, struct symtab *symtab, struct evtr_variable **var,
char *errbuf, size_t errbufsz)
{
void *bufstate;
int ret;
struct ktrfmt_parse_ctx ctx;
printd(PARSE, "parsing \"%s\"\n", str);
ctx.ev = NULL;
ctx.symtab = symtab;
ctx.var = NULL;
ctx.errbuf = errbuf;
ctx.errbuf[0] = '\0';
ctx.errbufsz = errbufsz;
ctx.err = 0;
bufstate = __ktrfmt_scan_string(str);
ret = __ktrfmt_parse(&ctx);
__ktrfmt_delete_buffer(bufstate);
*var = ctx.var;
return ret;
}