for now, there is much left to change and do.
--- /dev/null
+CFLAGS=-ansi -pedantic -Wall `pkg-config --cflags glib-2.0`
+LIBS=`pkg-config --libs glib-2.0` -ll
+
+targets = cltest
+
+sources = obcl.c main.c parse.c lex.c
+headers = obcl.h
+
+.PHONY: all clean
+
+all: $(targets)
+
+$(targets): $(sources:.c=.o)
+ $(CC) -o $@ $^ $(LIBS)
+
+parse.c: parse.y
+ $(YACC) -d -o$@ $^
+
+lex.c: lex.l
+ $(LEX) -o$@ $^
+
+clean:
+ $(RM) $(targets) *.o core *~ lex.c parse.c parse.h
--- /dev/null
+include "meh.conf";
+include "bummy.conf";
+
+section mouse {
+ mbind titlebar, frame {
+ event click;
+ button middle;
+ action lower;
+ }
+
+ mbind frame {
+ event click;
+ button right;
+ action launch_nukes;
+ }
+}
+
+section theme {
+ theme "merry";
+ font "tahoma-12 bold";
+}
--- /dev/null
+%{
+#include "obcl.h"
+#include "parse.h"
+%}
+
+%option yylineno
+
+DGT [0-9]
+ID [a-zA-Z_][a-zA-Z_\.\-0-9]*
+
+
+ /* string bummy */
+%x str
+%%
+
+ char str_buf[1024];
+ char *str_buf_ptr;
+ int str_char;
+
+ /* begin a string */
+[\"\'] {
+ str_buf_ptr = str_buf;
+ str_char = yytext[0];
+ BEGIN(str);
+ }
+
+ /* end a string */
+<str>[\"\'] {
+ if (yytext[0] == str_char) {
+ BEGIN(INITIAL);
+ *str_buf_ptr = '\0';
+ yylval.string = g_strdup(str_buf);
+ return TOK_STRING;
+ } else {
+ *str_buf_ptr++ = yytext[0];
+ }
+ }
+
+ /* can't have newlines in strings */
+<str>\n {
+ printf("Error: Unterminated string constant.\n");
+ BEGIN(INITIAL);
+ }
+
+ /* handle \" and \' */
+<str>"\\"[\"\'] {
+ if (yytext[1] == str_char)
+ *str_buf_ptr++ = yytext[1];
+ else {
+ *str_buf_ptr++ = yytext[0];
+ *str_buf_ptr++ = yytext[1];
+ }
+ }
+
+ /* eat valid string contents */
+<str>[^\\\n\'\"]+ {
+ char *yptr = yytext;
+ while (*yptr) {
+ *str_buf_ptr++ = *yptr++;
+ }
+ }
+
+ /* numberz */
+{DGT}+ {
+ yylval.num = atof(yytext);
+ return TOK_NUM;
+ }
+
+ /* real numbers */
+{DGT}+"."{DGT}* {
+ yylval.num = atof(yytext);
+ return TOK_NUM;
+ }
+
+ /* identifiers -- names without spaces and other crap in them */
+{ID} {
+ yylval.string = g_strdup(yytext);
+ return TOK_ID;
+ }
+
+ /* skip comments */
+"#".*\n ;
+ /* skip other whitespace */
+[ \n\t]+ ;
+. { return yytext[0]; }
+%%
--- /dev/null
+#include "obcl.h"
+
+int main()
+{
+ GList *lst = cl_parse("foo.conf");
+ cl_print_tree(lst,0);
+ return 0;
+}
+#include "obcl.h"
+
+void free_cl_tree(GList *tree)
+{
+
+}
+
+GList *cl_parse(gchar *file)
+{
+ FILE *fh = fopen(file, "r");
+ if (fh)
+ return cl_parse_fh(fh);
+ else {
+ printf("can't open file %s\n", file);
+ return 0;
+ }
+}
+
+void cl_print_tree(GList *tree, int depth)
+{
+ CLNode *tmp;
+ int tmpd = depth;
+
+ for (; tree; tree = tree->next) {
+ tmp = (CLNode*)tree->data;
+
+ while (tmpd-- > 0)
+ printf(" ");
+ tmpd = depth;
+
+ switch(tmp->type) {
+ case CL_ID:
+ printf("--ID-- %s\n", tmp->u.str);
+ break;
+ case CL_STR:
+ printf("--STR-- %s\n", tmp->u.str);
+ break;
+ case CL_NUM:
+ printf("--NUM-- %.2f\n", tmp->u.num);
+ break;
+ case CL_LIST:
+ printf("--LIST-- %s\n", tmp->u.lb.id);
+ cl_print_tree(tmp->u.lb.list, depth+2);
+ break;
+ case CL_BLOCK:
+ printf("--BLOCK-- %s\n", tmp->u.lb.id);
+ cl_print_tree(tmp->u.lb.block, depth+2);
+ break;
+ case CL_LISTBLOCK:
+ printf("--LISTBLOCK-- %s\n", tmp->u.lb.id);
+ cl_print_tree(tmp->u.lb.list, depth+2);
+ printf("\n");
+ cl_print_tree(tmp->u.lb.block, depth+2);
+ break;
+ }
+ }
+}
--- /dev/null
+#ifndef __obcl_h
+#define __obcl_h
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib.h>
+
+typedef enum CLNodeType {
+ CL_ID,
+ CL_NUM,
+ CL_STR,
+ CL_LIST,
+ CL_BLOCK,
+ CL_LISTBLOCK
+} CLNodeType;
+
+typedef struct CLNode {
+ CLNodeType type;
+ union {
+ struct {
+ gchar *id;
+ GList *list;
+ GList *block;
+ } lb;
+ double num;
+ gchar *str;
+ } u;
+
+} CLNode;
+
+void free_cl_tree(GList *tree);
+GList *cl_parse(gchar *file);
+GList *cl_parse_fh(FILE *file);
+void cl_print_tree(GList *tree, int depth);
+
+GList *parse_file(FILE *fh);
+
+#endif /* __obcl_h */
--- /dev/null
+%{
+#include "obcl.h"
+
+int yylex(void);
+void yyerror(char *msg, ...);
+
+extern int yylineno;
+extern char *yytext;
+GList *config; /* this is what we parse into */
+
+%}
+
+%union {
+ double num;
+ gchar *string;
+ CLNode *node;
+ GList *glist;
+};
+
+%token <num> TOK_NUM
+%token <string> TOK_ID TOK_STRING
+%token TOK_SEP
+
+%type <glist> config
+%type <glist> stmts
+%type <node> stmt
+%type <glist> list
+%type <glist> block
+%type <node> value
+
+%expect 2 /* for now */
+
+%%
+
+config: stmts
+ {
+ config = $$ = $1;
+ }
+ ;
+
+stmts:
+ { $$ = NULL; }
+ | stmt
+ { $$ = g_list_append(NULL, $1); }
+ | stmts stmt
+ { $$ = g_list_append($1, $2); }
+ ;
+
+stmt: TOK_ID list ';'
+ {
+ CLNode *s = g_new(CLNode,1);
+ s->type = CL_LIST;
+ s->u.lb.list = $2;
+ s->u.lb.id = $1;
+ $$ = s;
+ }
+ | TOK_ID list block
+ {
+ CLNode *s = g_new(CLNode,1);
+ s->type = CL_LISTBLOCK;
+ s->u.lb.list = $2;
+ s->u.lb.block = $3;
+ s->u.lb.id = $1;
+ $$ = s;
+ }
+ | TOK_ID block
+ {
+ CLNode *s = g_new(CLNode,1);
+ s->type = CL_BLOCK;
+ s->u.lb.block = $2;
+ s->u.lb.id = $1;
+ $$ = s;
+ }
+ ;
+
+list: value
+ {
+ $$ = g_list_append(NULL, $1);
+ }
+ | list ',' value
+ {
+ $$ = g_list_append($1, $3);
+ }
+ ;
+
+block: '{' stmts '}'
+ {
+ $$ = $2;
+ }
+ ;
+
+value: TOK_ID
+ {
+ CLNode *node = g_new(CLNode,1);
+ node->type = CL_ID;
+ node->u.str = $1;
+ $$ = node;
+ }
+ | TOK_STRING
+ {
+ CLNode *node = g_new(CLNode,1);
+ node->type = CL_STR;
+ node->u.str = $1;
+ $$ = node;
+ }
+ | TOK_NUM
+ {
+ CLNode *node = g_new(CLNode,1);
+ node->type = CL_NUM;
+ node->u.num = $1;
+ $$ = node;
+ }
+ ;
+
+%%
+
+int yywrap()
+{
+ return 1;
+}
+
+/* void yyerror(const char *err) */
+/* { */
+/* fprintf(stderr, "Parse error on line %d, near '%s': %s\n", */
+/* yylineno, yytext, err); */
+/* } */
+
+void yyerror(char *msg, ...)
+{
+ va_list args;
+ va_start(args,msg);
+
+ fprintf(stderr, "Error on line %d, near '%s': ", yylineno, yytext);
+ vfprintf(stderr, msg, args);
+ fprintf(stderr,"\n");
+
+ va_end(args);
+}
+
+GList *cl_parse_fh(FILE *fh)
+{
+ extern FILE *yyin;
+ yyin = fh;
+ yyparse();
+ return config;
+}