bp/grammar.c

99 lines
2.6 KiB
C

//
// grammar.c - Code for defining grammars (sets of rules)
//
#include <stdlib.h>
#include <string.h>
#include "compiler.h"
#include "file_loader.h"
#include "grammar.h"
#include "utils.h"
__attribute__((nonnull(2,3,4), returns_nonnull))
static def_t *with_backref(def_t *defs, file_t *f, const char *name, match_t *m);
//
// Return a new list of definitions with one added to the front
//
def_t *with_def(def_t *defs, file_t *f, const char *name, vm_op_t *op)
{
def_t *def = new(def_t);
def->next = defs;
def->file = f;
def->name = name;
def->op = op;
return def;
}
//
// Load the given grammar (semicolon-separated definitions)
// and return the first rule defined.
//
def_t *load_grammar(def_t *defs, file_t *f)
{
const char *src = f->contents;
src = after_spaces(src);
while (src < f->end) {
const char *name = src;
src = after_name(name);
check(src > name, "Invalid name for definition: %s", name);
name = strndup(name, (size_t)(src-name));
check(matchchar(&src, ':'), "Expected ':' in definition");
vm_op_t *op = bp_pattern(f, src);
if (op == NULL) break;
defs = with_def(defs, f, name, op);
src = op->end;
src = after_spaces(src);
if (matchchar(&src, ';'))
src = after_spaces(src);
}
if (src < f->end) {
fprint_line(stderr, f, src, NULL, "Invalid BP pattern");
_exit(1);
}
return defs;
}
//
// Look up a backreference or grammar definition by name
//
vm_op_t *lookup(def_t *defs, const char *name)
{
for ( ; defs; defs = defs->next) {
if (streq(defs->name, name))
return defs->op;
}
return NULL;
}
//
// Push a backreference onto the backreference stack
//
static def_t *with_backref(def_t *defs, file_t *f, const char *name, match_t *m)
{
vm_op_t *op = new(vm_op_t);
op->type = VM_BACKREF;
op->start = m->start;
op->end = m->end;
op->len = -1; // TODO: maybe calculate this? (nontrivial because of replacements)
op->args.backref = m;
return with_def(defs, f, name, op);
}
//
// Push all the backreferences contained in a match onto the backreference stack
//
def_t *with_backrefs(def_t *defs, file_t *f, match_t *m)
{
if (m->op->type != VM_REF) {
if (m->op->type == VM_CAPTURE && m->op->args.capture.name)
defs = with_backref(defs, f, m->op->args.capture.name, m->child);
if (m->child) defs = with_backrefs(defs, f, m->child);
if (m->nextsibling) defs = with_backrefs(defs, f, m->nextsibling);
}
return defs;
}
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1