Skip to content
Snippets Groups Projects
Commit a985014d authored by Anders Blomdell's avatar Anders Blomdell
Browse files

Inital version of pluggable parser

parent f6aa76ef
Branches
No related tags found
No related merge requests found
*~
*.o
build/
\ No newline at end of file
Makefile 0 → 100644
LIBRARIES=libmoberg.so
CCFLAGS+=-Wall -Werror -I. -g
LDFLAGS+=-Lbuild/ -lmoberg
MODULES:=$(wildcard modules/*)
LDFLAGS_parse_config=-ldl moberg_driver.o
all: $(LIBRARIES:%=build/%) $(MODULES) test/test_c parse_config
echo $(MODULES)
build/libmoberg.so: moberg.c Makefile | build
$(CC) -o $@ $(CCFLAGS) -lxdg-basedir -shared -fPIC -I. $<
build:
mkdir $@
%: %.o
$(CC) $(LDFLAGS) $(LDFLAGS_$(*)) -o $@ $<
%.o: %.c
$(CC) $(CCFLAGS) -c $<
.PHONY: $(MODULES)
$(MODULES):
$(MAKE) -C $@
.PHONY: test
test: all
LD_LIBRARY_PATH=build \
./parse_config test/*/*.conf
LD_LIBRARY_PATH=build HOME=$$(pwd)/test \
valgrind -q --error-exitcode=1 test/test_c
test/test_c: test/test_c.c Makefile
$(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $<
clean:
rm build/*
parse_config: moberg_config_parser.h
parse_config: moberg_driver.o
...@@ -19,17 +19,25 @@ struct moberg_digital_in_t *moberg_open_digital_in( ...@@ -19,17 +19,25 @@ struct moberg_digital_in_t *moberg_open_digital_in(
Config files are formatted as Config files are formatted as
``` ```
comedi = { comedi {
device = /dev/comedi0 config {
modprobe = [ comedi 8255 comedi_fc mite ni_tio ni_tiocmd ni_pcimio ] /* Parsed by parse_config in libmoberg_comedi.so */
config = [ ni_pcimio ] device = /dev/comedi0 ;
# Moberg mapping[indices] = driver specific[indices] modprobe = [ comedi 8255 comedi_fc mite ni_tio ni_tiocmd ni_pcimio ] ;
map digital_in[0:7] = subdevice[4][0:7] config = [ ni_pcimio ] ;
} }
serial2002 = { /* Moberg mapping[indices] = {driver specific}[indices]
device = /dev/ttyS0 {driver specific} is parsed by parse_map in libmoberg_comedi.so */
baud = 115200 map digital_in[0:7] = subdevice[4][0:7] ;
# Moberg mapping[indices] = driver specific[indices] }
map digital_in[30:37] = digital_in[0:7] serial2002 {
config {
/* Parsed by parse_config in libmoberg_serial2002.so */
device = /dev/ttyS0 ;
baud = 115200 ;
}
/* Moberg mapping[indices] = {driver specific}[indices]
{driver specific} is parsed by parse_map in libmoberg_serial2002.so */
map digital_in[30:37] = digital_in[0:7] ;
} }
``` ```
\ No newline at end of file
moberg.c 0 → 100644
#define _POSIX_C_SOURCE 200809L
#define _GNU_SOURCE /* scandirat */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <basedir.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include "moberg.h"
struct moberg_digital_in_t {
int index;
char *driver;
};
struct moberg_t {
struct {
int count;
struct moberg_digital_in_t *value;
} digital_in;
};
static void parse_config_at(struct moberg_t *config,
int dirfd,
const char *pathname)
{
if (dirfd >= 0) {
int fd = openat(dirfd, pathname, O_RDONLY);
if (fd >= 0) {
printf("Parsing... %s %d %d\n", pathname, dirfd, fd);
close(fd);
}
}
}
static int conf_filter(const struct dirent *entry)
{
char *dot = strrchr(entry->d_name, '.');
if (dot != NULL && strcmp(dot, ".conf") == 0) {
return 1;
} else {
return 0;
}
}
static void parse_config_dir_at(struct moberg_t *config,
int dirfd)
{
if (dirfd >= 0) {
struct dirent **entry = NULL;
int n = scandirat(dirfd, ".", &entry, conf_filter, alphasort);
for (int i = 0 ; i < n ; i++) {
parse_config_at(config, dirfd, entry[i]->d_name);
}
free(entry);
}
}
const struct moberg_t *moberg_init()
{
struct moberg_t *result = malloc(sizeof(struct moberg_t));
const char * const *config_paths = xdgSearchableConfigDirectories(NULL);
const char * const *path;
for (path = config_paths ; *path ; path++) {
int dirfd1 = open(*path, O_DIRECTORY);
if (dirfd >= 0) {
parse_config_at(result, dirfd1, "moberg.conf");
int dirfd2 = openat(dirfd1, "moberg.d", O_DIRECTORY);
if (dirfd2 >= 0) {
parse_config_dir_at(result, dirfd2);
close(dirfd2);
}
close(dirfd1);
}
}
free((const char **)config_paths);
/* Read local default */
/* Read environment default */
/* Parse environment overrides */
return result;
}
moberg.h 0 → 100644
#ifndef __MOBERG_H__
#define __MOBERG_H__
struct moberg_t;
struct moberg_digital_in_t;
const struct moberg_t *moberg_init();
struct moberg_digital_in_t *moberg_open_digital_in(
const struct moberg_t *handle,
int channel);
#endif
#ifndef __MOBERG_CONFIG_PARSER_H__
#define __MOBERG_CONFIG_PARSER_H__
struct moberg_config_parser_context;
struct moberg_config_parser_token;
enum moberg_config_parser_token_kind {
tok_none,
tok_LBRACE = '{',
tok_RBRACE,
tok_LBRACKET,
tok_RBRACKET,
tok_EQUAL,
tok_COLON,
tok_SEMICOLON,
tok_INTEGER,
tok_CONFIG,
tok_MAP,
tok_ANALOGIN,
tok_ANALOGOUT,
tok_DIGITALIN,
tok_DIGITALOUT,
tok_ENCODERIN,
tok_IDENT,
};
struct moberg_config_parser_token {
enum moberg_config_parser_token_kind kind;
union {
struct moberg_config_parser_ident {
int length;
const char *value;
} ident;
struct moberg_config_parser_integer {
int value;
} integer;
} u;
};
int moberg_config_parser_acceptsym(
struct moberg_config_parser_context *c,
enum moberg_config_parser_token_kind kind,
struct moberg_config_parser_token *token);
#endif
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <moberg_driver.h>
#include <dlfcn.h>
struct moberg_driver *moberg_open_driver(struct moberg_config_parser_ident id)
{
struct moberg_driver *result = NULL;
char *driver_name = malloc(sizeof("libmoberg_.so") + id.length + 1);
if (!driver_name) { goto out; }
sprintf(driver_name, "libmoberg_%.*s.so", id.length, id.value);
printf("%s", driver_name);
void *handle = dlopen(driver_name, RTLD_LAZY || RTLD_DEEPBIND);
if (! handle) { goto free_driver_name; }
struct moberg_driver_module *module =
(struct moberg_driver_module *) dlsym(handle, "moberg_module");
if (! module) { goto dlclose_driver; }
result = malloc(sizeof(*result));
if (! result) { goto dlclose_driver; }
result->handle = handle;
result->module = *module;
goto free_driver_name;
dlclose_driver:
dlclose(handle);
free_driver_name:
free(driver_name);
out:
return result;
}
void moberg_close_driver(struct moberg_driver *driver)
{
dlclose(driver->handle);
free(driver);
}
#ifndef __MOBERG_DRIVER_H__
#define __MOBERG_DRIVER_H__
#include <moberg_config_parser.h>
struct moberg_driver_config {
};
struct moberg_driver_map {
};
typedef struct moberg_driver_config (*moberg_driver_parse_config_t)(
struct moberg_config_parser_context *context);
typedef struct moberg_driver_map (*moberg_driver_parse_map_t)(
struct moberg_config_parser_context *context,
enum moberg_config_parser_token_kind kind);
struct moberg_driver {
void *handle;
struct moberg_driver_module {
moberg_driver_parse_config_t parse_config;
moberg_driver_parse_map_t parse_map;
} module;
};
struct moberg_driver *moberg_open_driver(struct moberg_config_parser_ident id);
void moberg_close_driver(struct moberg_driver *driver);
#endif
LIBRARIES=libmoberg_comedi.so
CCFLAGS+=-Wall -Werror -I../.. -g
LDFLAGS+=-Lbuild/ -lmoberg
MODULES=$(wildcard modules/*)
all: $(LIBRARIES:%=../../build/%)
../../build/libmoberg_comedi.so: comedi.c
$(CC) -o $@ $(CCFLAGS) -shared -fPIC $<
../../build/libmoberg_comedi.so: ../../moberg_config_parser.h
#include <moberg_config_parser.h>
#include <moberg_driver.h>
#include <stdio.h>
static struct moberg_driver_config parse_config(
struct moberg_config_parser_context *context)
{
struct moberg_driver_config result;
printf("PARSE_CONFIG %s\n", __FILE__);
/*
const char *buf = context->buf;
while (*buf && *buf != '}') {
buf++;
}
context->buf = buf + 1;
*/
return result;
}
static struct moberg_driver_map parse_map(
struct moberg_config_parser_context *context,
enum moberg_config_parser_token_kind kind)
{
struct moberg_driver_map result;
printf("PARSE_MAP %s\n", __FILE__);
/*
const char *buf = context->buf;
while (*buf && *buf != '}') {
buf++;
}
context->buf = buf + 1;
*/
return result;
}
struct moberg_driver_module moberg_module = {
.parse_config = parse_config,
.parse_map = parse_map,
};
LIBRARIES=libmoberg_serial2002.so
CCFLAGS+=-Wall -Werror -I../.. -g
LDFLAGS+=-Lbuild/ -lmoberg
MODULES=$(wildcard modules/*)
all: $(LIBRARIES:%=../../build/%)
../../build/libmoberg_serial2002.so: serial2002.c
$(CC) -o $@ $(CCFLAGS) -shared -fPIC $<
../../build/libmoberg_serial2002.so: ../../moberg_config_parser.h
#include <moberg_config_parser.h>
#include <moberg_driver.h>
#include <stdio.h>
static struct moberg_driver_config parse_config(
struct moberg_config_parser_context *context)
{
struct moberg_driver_config result;
printf("PARSE_CONFIG %s\n", __FILE__);
/*
const char *buf = context->buf;
while (*buf && *buf != '}') {
buf++;
}
context->buf = buf + 1;
*/
return result;
}
static struct moberg_driver_map parse_map(
struct moberg_config_parser_context *context,
enum moberg_config_parser_token_kind kind)
{
struct moberg_driver_map result;
printf("PARSE_MAP %s\n", __FILE__);
/*
const char *buf = context->buf;
while (*buf && *buf != '}') {
buf++;
}
context->buf = buf + 1;
*/
return result;
}
struct moberg_driver_module moberg_module = {
.parse_config = parse_config,
.parse_map = parse_map,
};
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <moberg_config_parser.h>
#include <moberg_driver.h>
#define token moberg_config_parser_token
#define token_kind moberg_config_parser_token_kind
#define acceptsym moberg_config_parser_acceptsym
typedef struct moberg_config_parser_token token_t;
typedef struct moberg_config_parser_ident ident_t;
typedef struct moberg_config_parser_context {
char *buf; /* Pointer to data to be parsed */
const char *p; /* current parse location */
token_t token;
} context_t;
static const void nextsym_ident_or_keyword(context_t *c)
{
c->token.u.ident.length = 1;
c->token.u.ident.value = c->p;
c->p++;
while (*c->p) {
switch (*c->p) {
case 'a'...'z':
case 'A'...'Z':
case '0'...'9':
case '_':
c->p++;
c->token.u.ident.length++;
break;
default:
goto out;
break;
}
}
out: ;
const char *v = c->token.u.ident.value;
int l = c->token.u.ident.length;
if (strncmp("config", v, l) == 0) {
c->token.kind = tok_CONFIG;
} else if (strncmp("map", v, l) == 0) {
c->token.kind = tok_MAP;
} else if (strncmp("analogin", v, l) == 0) {
c->token.kind = tok_ANALOGIN;
} else if (strncmp("analogout", v, l) == 0) {
c->token.kind = tok_ANALOGOUT;
} else if (strncmp("digitalin", v, l) == 0) {
c->token.kind = tok_DIGITALIN;
} else if (strncmp("digitalout", v, l) == 0) {
c->token.kind = tok_DIGITALOUT;
} else if (strncmp("encoderin", v, l) == 0) {
c->token.kind = tok_ENCODERIN;
} else {
c->token.kind = tok_IDENT;
}
printf("IDENT: %.*s\n", l, v);
}
static const void nextsym_integer(context_t *c)
{
c->token.u.integer.value = 0;
while (*c->p && '0' <= *c->p && *c->p <= '9') {
c->token.u.integer.value *= 10;
c->token.u.integer.value += *c->p - '0';
c->p++;
}
printf("INTEGER: %d\n", c->token.u.integer.value);
}
static int nextsym(context_t *c)
{
c->token.kind = tok_none;
while (c->p && *c->p && c->token.kind == tok_none) {
if (c->p[0] == '/' && c->p[1] == '*') {
printf("Skipping COMMENT\n");
/* Skip comment */
c->p += 2;
while (*c->p && (c->p[0] != '*' || c->p[1] != '/')) {
c->p++;
}
c->p += 2;
continue;
}
switch (*c->p) {
case ' ':
case '\t':
case '\n':
case '\r':
/* Skip whitespace */
c->p++;
break;
case '{':
c->token.kind = tok_LBRACE;
c->p++;
break;
case '}':
c->token.kind = tok_RBRACE;
c->p++;
break;
case '[':
c->token.kind = tok_LBRACKET;
c->p++;
break;
case ']':
c->token.kind = tok_RBRACKET;
c->p++;
break;
case '=':
c->token.kind = tok_EQUAL;
c->p++;
break;
case ':':
c->token.kind = tok_COLON;
c->p++;
break;
case ';':
c->token.kind = tok_SEMICOLON;
c->p++;
break;
case 'a'...'z':
case 'A'...'Z':
case '_':
nextsym_ident_or_keyword(c);
break;
case '0'...'9':
nextsym_integer(c);
break;
default:
printf("UNEXPECTED %c\n\n", *c->p);
c->p++;
break;
}
}
printf("TOKEN %d\n\n", c->token.kind);
if (c->token.kind != tok_none) {
return 1;
} else {
return 0;
}
}
int moberg_config_parser_acceptsym(context_t *c,
enum token_kind kind,
token_t *token)
{
if (c->token.kind == kind) {
if (token) {
*token = c->token;
}
nextsym(c);
return 1;
}
return 0;
}
static int parse_map_range(context_t *c)
{
token_t low, high;
if (! acceptsym(c, tok_LBRACKET, NULL)) { goto err; }
if (! acceptsym(c, tok_INTEGER, &low)) { goto err; }
if (acceptsym(c, tok_COLON, NULL)) {
if (! acceptsym(c, tok_INTEGER, &high)) { goto err; }
} else {
high = low;
}
if (! acceptsym(c, tok_RBRACKET, NULL)) { goto err; }
return 1;
err:
printf("OOPS");
return 0;
}
static int parse_map(context_t *c,
struct moberg_driver *driver)
{
struct token t;
if (acceptsym(c, tok_ANALOGIN, &t) ||
acceptsym(c, tok_ANALOGOUT, &t) ||
acceptsym(c, tok_DIGITALIN, &t) ||
acceptsym(c, tok_DIGITALOUT, &t) ||
acceptsym(c, tok_ENCODERIN, &t)) {
if (! parse_map_range(c)) { goto err; }
if (! acceptsym(c, tok_EQUAL, NULL)) { goto err; }
driver->module.parse_map(c, t.kind);
if (! parse_map_range(c)) { goto err; }
if (! acceptsym(c, tok_SEMICOLON, NULL)) { goto err; }
switch (t.kind) {
case tok_ANALOGIN:
case tok_ANALOGOUT:
case tok_DIGITALIN:
case tok_DIGITALOUT:
case tok_ENCODERIN:
break;
default:
goto err;
}
} else {
goto err;
}
return 1;
err:
return 0;
}
static int parse_device(context_t *c,
struct moberg_driver *driver)
{
if (! acceptsym(c, tok_LBRACE, NULL)) { goto err; }
for (;;) {
if (acceptsym(c, tok_CONFIG, NULL)) {
driver->module.parse_config(c);
} else if (acceptsym(c, tok_MAP, NULL)) {
parse_map(c, driver);
} else {
goto err;
}
}
return 1;
err:
return 0;
}
static int parse_driver(context_t *c,
ident_t name)
{
struct moberg_driver *driver = moberg_open_driver(name);
if (! driver) {
printf("Driver not found\n");
goto err;
} else {
int OK = parse_device(c, driver);
moberg_close_driver(driver);
if (! OK) { goto err; }
}
return 1;
err:
return 0;
}
static int parse_config(context_t *c)
{
for (;;) {
struct token t;
if (acceptsym(c, tok_IDENT, &t)) {
printf("DRIVER=%.*s\n", t.u.ident.length, t.u.ident.value);
if (! parse_driver(c, t.u.ident)) { goto err; }
}
}
return 1;
err:
printf("Failed!!");
return 0;
}
static char *read_fd(int fd)
{
char *result = malloc(10000); /* HACK */
int pos = read(fd, result, 10000);
result[pos] = 0;
return result;
}
int main(int argc, char *argv[])
{
context_t context;
int fd = open(argv[1], O_RDONLY);
context.buf = read_fd(fd);
context.p = context.buf;
close(fd);
printf("%s\n", context.buf);
nextsym(&context);
parse_config(&context);
free(context.buf);
}
comedi {
config {
/* Parsed by parse_config in libmoberg_comedi.so */
device = /dev/comedi0 ;
modprobe = [ comedi 8255 comedi_fc mite ni_tio ni_tiocmd ni_pcimio ] ;
config = [ ni_pcimio ] ;
}
/* Moberg mapping[indices] = {driver specific}[indices]
{driver specific} is parsed by parse_map in libmoberg_comedi.so */
map digital_in[0:7] = {subdevice[4]}[0:7] ;
}
serial2002 {
config {
/* Parsed by parse_config in libmoberg_serial2002.so */
device = /dev/ttyS0 ;
baud = 115200 ;
}
/* Moberg mapping[indices] = {driver specific}[indices]
{driver specific} is parsed by parse_map in libmoberg_serial2002.so */
map digital_in[30:37] = {digital_in}[0:7] ;
}
File added
#include <stdio.h>
#include <stdlib.h>
#include <moberg.h>
int main(int argc, char *argv[])
{
const struct moberg_t *moberg = moberg_init();
free((struct moberg_t*) moberg);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment