]> git.sur5r.net Git - i3/i3/commitdiff
add first version of a new flex/bison based command parser
authorMichael Stapelberg <michael@stapelberg.de>
Wed, 14 Apr 2010 18:26:56 +0000 (20:26 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Wed, 14 Apr 2010 18:26:56 +0000 (20:26 +0200)
Makefile
include/con.h
src/cmdparse.l [new file with mode: 0644]
src/cmdparse.y [new file with mode: 0644]
src/con.c
src/nc.c

index 810a78637a05351f6dc9642e34130126fc924387..368dc777b4c1daf9957f503d3fcc0d36b1105eb3 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@ TOPDIR=$(shell pwd)
 include $(TOPDIR)/common.mk
 
 # Depend on the object files of all source-files in src/*.c and on all header files
-AUTOGENERATED:=src/cfgparse.tab.c src/cfgparse.yy.c
+AUTOGENERATED:=src/cfgparse.tab.c src/cfgparse.yy.c src/cmdparse.tab.c src/cmdparse.yy.c
 FILES:=src/ipc.c src/nc.c src/log.c src/util.c src/tree.c src/xcb.c src/manage.c src/workspace.c src/x.c src/floating.c src/click.c src/config.c src/handlers.c src/randr.c src/xinerama.c src/con.c src/load_layout.c src/render.c src/window.c
 FILES:=$(FILES:.c=.o)
 HEADERS:=$(filter-out include/loglevels.h,$(wildcard include/*.h))
@@ -23,7 +23,7 @@ src/%.o: src/%.c ${HEADERS}
        echo "CC $<"
        $(CC) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/$(shell basename $< .c)/ { print NR }' loglevels.tmp))" -c -o $@ $<
 
-all: src/cfgparse.y.o src/cfgparse.yy.o ${FILES}
+all: src/cfgparse.y.o src/cfgparse.yy.o src/cmdparse.y.o src/cmdparse.yy.o ${FILES}
        echo "LINK i3"
        $(CC) -o i3 $^ $(LDFLAGS)
 
@@ -44,11 +44,23 @@ src/cfgparse.yy.o: src/cfgparse.l src/cfgparse.y.o ${HEADERS}
        flex -i -o$(@:.o=.c) $<
        $(CC) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/cfgparse.l/ { print NR }' loglevels.tmp))" -c -o $@ $(@:.o=.c)
 
+src/cmdparse.yy.o: src/cmdparse.l src/cmdparse.y.o ${HEADERS}
+       echo "LEX $<"
+       flex -P cmdyy -i -o$(@:.o=.c) $<
+       $(CC) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/cmdparse.l/ { print NR }' loglevels.tmp))" -c -o $@ $(@:.o=.c)
+
+
 src/cfgparse.y.o: src/cfgparse.y ${HEADERS}
        echo "YACC $<"
        bison --debug --verbose -b $(basename $< .y) -d $<
        $(CC) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/cfgparse.y/ { print NR }' loglevels.tmp))" -c -o $@ $(<:.y=.tab.c)
 
+src/cmdparse.y.o: src/cmdparse.y ${HEADERS}
+       echo "YACC $<"
+       bison -p cmdyy --debug --verbose -b $(basename $< .y) -d $<
+       $(CC) $(CFLAGS) -DLOGLEVEL="(1 << $(shell awk '/cmdparse.y/ { print NR }' loglevels.tmp))" -c -o $@ $(<:.y=.tab.c)
+
+
 install: all
        echo "INSTALL"
        $(INSTALL) -d -m 0755 $(DESTDIR)$(PREFIX)/bin
index 9b14d897d43834d1cf1e52e75b4b5f7de39c0534..71f73e3d0cf32a68ec5ab8dd5494dbb0a8c3b6a0 100644 (file)
@@ -13,6 +13,7 @@ Con *con_by_frame_id(xcb_window_t frame);
 Con *con_for_window(i3Window *window, Match **store_match);
 void con_attach(Con *con, Con *parent);
 void con_detach(Con *con);
+bool match_matches_window(Match *match, i3Window *window);
 
 enum { WINDOW_ADD = 0, WINDOW_REMOVE = 1 };
 void con_fix_percent(Con *con, int action);
diff --git a/src/cmdparse.l b/src/cmdparse.l
new file mode 100644 (file)
index 0000000..684a588
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009-2010 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ * cmdparse.l: the lexer for commands you send to i3 (or bind on keys)
+ *
+ */
+%option nounput
+%option noinput
+%option noyy_top_state
+%option stack
+
+%{
+#include <stdio.h>
+#include <string.h>
+#include "cmdparse.tab.h"
+
+#include "config.h"
+#include "util.h"
+
+int cmdyycolumn = 1;
+
+#define YY_DECL int yylex (struct context *context)
+
+#define YY_USER_ACTION { \
+    context->first_column = cmdyycolumn; \
+    context->last_column = cmdyycolumn+yyleng-1; \
+    cmdyycolumn += yyleng; \
+}
+
+%}
+
+EOL (\r?\n)
+
+/* handle everything up to \n as a string */
+%s WANT_STRING
+/* first expect a whitespace, then a string */
+%s WANT_WS_STRING
+/* handle a quoted string or everything up to the next whitespace */
+%s WANT_QSTRING
+
+%x BUFFER_LINE
+
+%%
+
+    {
+        /* This is called when a new line is lexed. We only want the
+         * first line to match to go into state BUFFER_LINE */
+        if (context->line_number == 0) {
+            context->line_number = 1;
+            BEGIN(INITIAL);
+            yy_push_state(BUFFER_LINE);
+        }
+    }
+
+<BUFFER_LINE>^[^\r\n]*/{EOL}? {
+    /* save whole line */
+    context->line_copy = sstrdup(yytext);
+
+    yyless(0);
+    yy_pop_state();
+    yy_set_bol(true);
+    cmdyycolumn = 1;
+}
+
+<WANT_STRING>[^\n]+             { BEGIN(INITIAL); cmdyylval.string = sstrdup(yytext); return STR; }
+<WANT_WS_STRING>[ \t]*          { BEGIN(WANT_STRING); return WHITESPACE; }
+<WANT_QSTRING>\"[^\"]+\"        {
+                                  BEGIN(INITIAL);
+                                  /* strip quotes */
+                                  char *copy = sstrdup(yytext+1);
+                                  copy[strlen(copy)-1] = '\0';
+                                  cmdyylval.string = copy;
+                                  return STR;
+                                }
+
+[ \t]*                          { return WHITESPACE; }
+attach                          { return TOK_ATTACH; }
+exec                            { BEGIN(WANT_WS_STRING); return TOK_EXEC; }
+exit                            { return TOK_EXIT; }
+reload                          { return TOK_RELOAD; }
+restart                         { return TOK_RESTART; }
+kill                            { return TOK_KILL; }
+fullscreen                      { return TOK_FULLSCREEN; }
+global                          { return TOK_GLOBAL; }
+layout                          { return TOK_LAYOUT; }
+default                         { return TOK_DEFAULT; }
+stacked                         { return TOK_STACKED; }
+tabbed                          { return TOK_TABBED; }
+border                          { return TOK_BORDER; }
+none                            { return TOK_NONE; }
+1pixel                          { return TOK_1PIXEL; }
+mode                            { return TOK_MODE; }
+tiling                          { return TOK_TILING; }
+floating                        { return TOK_FLOATING; }
+workspace                       { return TOK_WORKSPACE; }
+focus                           { return TOK_FOCUS; }
+move                            { return TOK_MOVE; }
+
+class                           { BEGIN(WANT_QSTRING); return TOK_CLASS; }
+
+.                               { return (int)yytext[0]; }
+
+<<EOF>> {
+    while (yy_start_stack_ptr > 0)
+        yy_pop_state();
+    yyterminate();
+}
+
+%%
diff --git a/src/cmdparse.y b/src/cmdparse.y
new file mode 100644 (file)
index 0000000..eb5ab71
--- /dev/null
@@ -0,0 +1,274 @@
+%{
+/*
+ * vim:ts=4:sw=4:expandtab
+ *
+ * i3 - an improved dynamic tiling window manager
+ * © 2009-2010 Michael Stapelberg and contributors (see also: LICENSE)
+ *
+ * cmdparse.y: the parser for commands you send to i3 (or bind on keys)
+ *
+
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "all.h"
+
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+extern int cmdyylex(struct context *context);
+extern int cmdyyparse(void);
+extern FILE *cmdyyin;
+YY_BUFFER_STATE cmdyy_scan_string(const char *);
+
+static struct bindings_head *current_bindings;
+static struct context *context;
+static Match current_match;
+
+/*
+ * Helper data structure for an operation window (window on which the operation
+ * will be performed). Used to build the TAILQ owindows.
+ *
+ */
+typedef struct owindow {
+    Con *con;
+    TAILQ_ENTRY(owindow) owindows;
+} owindow;
+static TAILQ_HEAD(owindows_head, owindow) owindows;
+
+/* We don’t need yydebug for now, as we got decent error messages using
+ * yyerror(). Should you ever want to extend the parser, it might be handy
+ * to just comment it in again, so it stays here. */
+//int cmdyydebug = 1;
+
+void cmdyyerror(const char *error_message) {
+    ELOG("\n");
+    ELOG("CMD: %s\n", error_message);
+    ELOG("CMD: in file \"%s\", line %d:\n",
+            context->filename, context->line_number);
+    ELOG("CMD:   %s\n", context->line_copy);
+    ELOG("CMD:   ");
+    for (int c = 1; c <= context->last_column; c++)
+        if (c >= context->first_column)
+                printf("^");
+        else printf(" ");
+    printf("\n");
+    ELOG("\n");
+}
+
+int cmdyywrap() {
+    return 1;
+}
+
+void parse_cmd(const char *new) {
+
+    //const char *new = "[level-up workspace] attach $output, focus";
+
+    cmdyy_scan_string(new);
+
+    context = scalloc(sizeof(struct context));
+    context->filename = "cmd";
+    if (cmdyyparse() != 0) {
+            fprintf(stderr, "Could not parse configfile\n");
+            exit(1);
+    }
+    printf("done\n");
+
+    FREE(context->line_copy);
+    free(context);
+}
+
+%}
+
+%error-verbose
+%lex-param { struct context *context }
+
+%union {
+    char *string;
+}
+
+%token TOK_ATTACH "attach"
+%token TOK_EXEC "exec"
+%token TOK_EXIT "exit"
+%token TOK_RELOAD "reload"
+%token TOK_RESTART "restart"
+%token TOK_KILL "kill"
+%token TOK_FULLSCREEN "fullscreen"
+%token TOK_GLOBAL "global"
+%token TOK_LAYOUT "layout"
+%token TOK_DEFAULT "default"
+%token TOK_STACKED "stacked"
+%token TOK_TABBED "tabbed"
+%token TOK_BORDER "border"
+%token TOK_NONE "none"
+%token TOK_1PIXEL "1pixel"
+%token TOK_MODE "mode"
+%token TOK_TILING "tiling"
+%token TOK_FLOATING "floating"
+%token TOK_WORKSPACE "workspace"
+%token TOK_FOCUS "focus"
+%token TOK_MOVE "move"
+
+%token TOK_CLASS "class"
+
+%token WHITESPACE "<whitespace>"
+%token STR "<string>"
+
+%%
+
+commands: /* empty */
+    | commands optwhitespace ';' optwhitespace command
+    | command
+    {
+        owindow *current;
+
+        printf("single command completely parsed, dropping state...\n");
+        while (!TAILQ_EMPTY(&owindows)) {
+            current = TAILQ_FIRST(&owindows);
+            TAILQ_REMOVE(&owindows, current, owindows);
+            free(current);
+        }
+    }
+    ;
+
+optwhitespace:
+    | WHITESPACE
+    ;
+
+command:
+    match optwhitespace operations
+    ;
+
+match:
+    | matchstart optwhitespace criteria optwhitespace matchend
+    {
+        printf("match parsed\n");
+    }
+    ;
+
+matchstart:
+    '['
+    {
+        printf("start\n");
+        memset(&current_match, '\0', sizeof(Match));
+        TAILQ_INIT(&owindows);
+        /* copy all_cons */
+        Con *con;
+        TAILQ_FOREACH(con, &all_cons, all_cons) {
+            if (con->window == NULL)
+                continue;
+
+            owindow *ow = smalloc(sizeof(owindow));
+            ow->con = con;
+            TAILQ_INSERT_TAIL(&owindows, ow, owindows);
+        }
+    }
+    ;
+
+matchend:
+    ']'
+    {
+        owindow *next, *current;
+
+        printf("match specification finished, matching...\n");
+        /* copy the old list head to iterate through it and start with a fresh
+         * list which will contain only matching windows */
+        struct owindows_head old = owindows;
+        TAILQ_INIT(&owindows);
+        for (next = TAILQ_FIRST(&old); next != TAILQ_END(&old);) {
+            /* make a copy of the next pointer and advance the pointer to the
+             * next element as we are going to invalidate the element’s
+             * next/prev pointers by calling TAILQ_INSERT_TAIL later */
+            current = next;
+            next = TAILQ_NEXT(next, owindows);
+
+            printf("checking if con %p / %s matches\n", current->con, current->con->name);
+            if (match_matches_window(&current_match, current->con->window)) {
+                printf("matches!\n");
+                TAILQ_INSERT_TAIL(&owindows, current, owindows);
+            } else {
+                printf("doesnt match\n");
+                free(current);
+            }
+        }
+
+        TAILQ_FOREACH(current, &owindows, owindows) {
+            printf("matching: %p / %s\n", current->con, current->con->name);
+        }
+
+    }
+    ;
+
+criteria:
+    TOK_CLASS '=' STR
+    {
+        printf("criteria: class = %s\n", $<string>3);
+        current_match.class = $<string>3;
+    }
+    ;
+
+operations:
+    operation
+    | operation optwhitespace
+    | operations ',' optwhitespace operation
+    ;
+
+operation:
+    exec
+    | exit
+    /*| reload
+    | restart
+    | mark
+    | fullscreen
+    | layout
+    | border
+    | mode
+    | workspace
+    | move*/
+    | attach
+    | focus
+    | kill
+    ;
+
+exec:
+    TOK_EXEC WHITESPACE STR
+    {
+        printf("should execute %s\n", $<string>3);
+    }
+    ;
+
+exit:
+    TOK_EXIT
+    {
+        printf("exit, bye bye\n");
+    }
+    ;
+
+attach:
+    TOK_ATTACH
+    {
+        printf("should attach\n");
+    }
+    ;
+
+focus:
+    TOK_FOCUS
+    {
+        printf("should focus\n");
+    }
+    ;
+
+kill:
+    TOK_KILL
+    {
+        owindow *current;
+
+        printf("killing!\n");
+        TAILQ_FOREACH(current, &owindows, owindows) {
+            printf("matching: %p / %s\n", current->con, current->con->name);
+            tree_close(current->con);
+        }
+
+    }
+    ;
index 75440973fbfcc45478e19a513238582f0189fa57..5c2204589b2bf6941cf968ba24923eb5d85558cb 100644 (file)
--- a/src/con.c
+++ b/src/con.c
@@ -203,7 +203,7 @@ Con *con_by_frame_id(xcb_window_t frame) {
     return NULL;
 }
 
-static bool match_matches_window(Match *match, i3Window *window) {
+bool match_matches_window(Match *match, i3Window *window) {
     /* TODO: pcre, full matching, … */
     if (match->class != NULL && strcasecmp(match->class, window->class_class) == 0) {
         LOG("match made by window class (%s)\n", window->class_class);
index 2b523ee56232f7a5d3c9ddd7e322b15e96a38e43..92dba08d2c7d79c716df6e6d5b1243a01f459e22 100644 (file)
--- a/src/nc.c
+++ b/src/nc.c
@@ -109,8 +109,16 @@ void parse_command(const char *command) {
         start_application(command + strlen("exec "));
     else if (strcasecmp(command, "restart") == 0)
         i3_restart();
-    else if (strcasecmp(command, "floating") == 0)
-        toggle_floating_mode(focused, false);
+    else if (strcasecmp(command, "floating") == 0) {
+        //toggle_floating_mode(focused, false);
+            parse_cmd("exit");
+    parse_cmd("exec /usr/bin/bleh");
+    parse_cmd("exec kill -9 33");
+    parse_cmd("kill");
+    parse_cmd("[ class=\"Xpdf\" ] kill");
+    parse_cmd("[ class=\"firefox\" ] kill");
+
+    }
 
     tree_render();
 
@@ -121,6 +129,7 @@ void parse_command(const char *command) {
 }
 
 int main(int argc, char *argv[]) {
+    //parse_cmd("[ foo ] attach, attach ; focus");
     int screens;
     char *override_configpath = NULL;
     bool autostart = true;