lexer/parser: proper error messages
Error messages now look like this: 13.02.2010 19:42:30 - ERROR: 13.02.2010 19:42:30 - ERROR: CONFIG: syntax error, unexpected <word>, expecting default/stacking/tabbed or stack-limit 13.02.2010 19:42:30 - ERROR: CONFIG: in file "inv", line 15: 13.02.2010 19:42:30 - ERROR: CONFIG: new_container foobar 13.02.2010 19:42:30 - ERROR: CONFIG: ^^^^^^ 13.02.2010 19:42:30 - ERROR:
This commit is contained in:
parent
01297af20a
commit
64cf88403d
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* i3 - an improved dynamic tiling window manager
|
||||
*
|
||||
* © 2009 Michael Stapelberg and contributors
|
||||
* © 2009-2010 Michael Stapelberg and contributors
|
||||
*
|
||||
* See file LICENSE for license information.
|
||||
*
|
||||
|
@ -25,6 +25,21 @@ typedef struct Config Config;
|
|||
extern Config config;
|
||||
extern SLIST_HEAD(modes_head, Mode) modes;
|
||||
|
||||
/**
|
||||
* Used during the config file lexing/parsing to keep the state of the lexer
|
||||
* in order to provide useful error messages in yyerror().
|
||||
*
|
||||
*/
|
||||
struct context {
|
||||
int line_number;
|
||||
char *line_copy;
|
||||
const char *filename;
|
||||
|
||||
/* These are the same as in YYLTYPE */
|
||||
int first_column;
|
||||
int last_column;
|
||||
};
|
||||
|
||||
/**
|
||||
* Part of the struct Config. It makes sense to group colors for background,
|
||||
* border and text as every element in i3 has them (window decorations, bar).
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
%option nounput
|
||||
%option noinput
|
||||
%option noyy_top_state
|
||||
%option stack
|
||||
|
||||
%{
|
||||
/*
|
||||
|
@ -13,19 +15,57 @@
|
|||
|
||||
#include "data.h"
|
||||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
int yycolumn = 1;
|
||||
|
||||
#define YY_DECL int yylex (struct context *context)
|
||||
|
||||
#define YY_USER_ACTION { \
|
||||
context->first_column = yycolumn; \
|
||||
context->last_column = yycolumn+yyleng-1; \
|
||||
yycolumn += yyleng; \
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%Start BIND_COND
|
||||
%Start BINDSYM_COND
|
||||
%Start BIND_AWS_COND
|
||||
%Start BINDSYM_AWS_COND
|
||||
%Start BIND_A2WS_COND
|
||||
%Start ASSIGN_COND
|
||||
%Start COLOR_COND
|
||||
%Start SCREEN_COND
|
||||
%Start SCREEN_AWS_COND
|
||||
EOL (\r?\n)
|
||||
|
||||
%s BIND_COND
|
||||
%s BINDSYM_COND
|
||||
%s BIND_AWS_COND
|
||||
%s BINDSYM_AWS_COND
|
||||
%s BIND_A2WS_COND
|
||||
%s ASSIGN_COND
|
||||
%s COLOR_COND
|
||||
%s SCREEN_COND
|
||||
%s SCREEN_AWS_COND
|
||||
%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 = strdup(yytext);
|
||||
|
||||
yyless(0);
|
||||
yy_pop_state();
|
||||
yy_set_bol(true);
|
||||
yycolumn = 1;
|
||||
}
|
||||
|
||||
|
||||
<BIND_A2WS_COND>[^\n]+ { BEGIN(INITIAL); yylval.string = strdup(yytext); return STR; }
|
||||
^[ \t]*#[^\n]* { return TOKCOMMENT; }
|
||||
<COLOR_COND>[0-9a-fA-F]+ { yylval.string = strdup(yytext); return HEX; }
|
||||
|
@ -69,7 +109,11 @@ control { return TOKCONTROL; }
|
|||
ctrl { return TOKCONTROL; }
|
||||
shift { return TOKSHIFT; }
|
||||
→ { return TOKARROW; }
|
||||
\n /* ignore end of line */;
|
||||
{EOL} {
|
||||
FREE(context->line_copy);
|
||||
context->line_number++;
|
||||
yy_push_state(BUFFER_LINE);
|
||||
}
|
||||
<SCREEN_AWS_COND>x { return (int)yytext[0]; }
|
||||
<BIND_COND>[ \t]+ { BEGIN(BIND_AWS_COND); return WHITESPACE; }
|
||||
<BINDSYM_COND>[ \t]+ { BEGIN(BINDSYM_AWS_COND); return WHITESPACE; }
|
||||
|
@ -91,4 +135,11 @@ shift { return TOKSHIFT; }
|
|||
<BINDSYM_AWS_COND>[a-zA-Z0-9_]+ { yylval.string = strdup(yytext); return WORD; }
|
||||
[a-zA-Z]+ { yylval.string = strdup(yytext); return WORD; }
|
||||
. { return (int)yytext[0]; }
|
||||
|
||||
<<EOF>> {
|
||||
while (yy_start_stack_ptr > 0)
|
||||
yy_pop_state();
|
||||
yyterminate();
|
||||
}
|
||||
|
||||
%%
|
||||
|
|
|
@ -24,17 +24,32 @@
|
|||
#include "log.h"
|
||||
|
||||
typedef struct yy_buffer_state *YY_BUFFER_STATE;
|
||||
extern int yylex(void);
|
||||
extern int yylex(struct context *context);
|
||||
extern int yyparse(void);
|
||||
extern FILE *yyin;
|
||||
YY_BUFFER_STATE yy_scan_string(const char *);
|
||||
|
||||
static struct bindings_head *current_bindings;
|
||||
static struct context *context;
|
||||
|
||||
int yydebug = 1;
|
||||
/* 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 yydebug = 1;
|
||||
|
||||
void yyerror(const char *str) {
|
||||
fprintf(stderr,"error: %s\n",str);
|
||||
void yyerror(const char *error_message) {
|
||||
ELOG("\n");
|
||||
ELOG("CONFIG: %s\n", error_message);
|
||||
ELOG("CONFIG: in file \"%s\", line %d:\n",
|
||||
context->filename, context->line_number);
|
||||
ELOG("CONFIG: %s\n", context->line_copy);
|
||||
ELOG("CONFIG: ");
|
||||
for (int c = 1; c <= context->last_column; c++)
|
||||
if (c >= context->first_column)
|
||||
printf("^");
|
||||
else printf(" ");
|
||||
printf("\n");
|
||||
ELOG("\n");
|
||||
}
|
||||
|
||||
int yywrap() {
|
||||
|
@ -149,11 +164,16 @@ void parse_file(const char *f) {
|
|||
|
||||
yy_scan_string(new);
|
||||
|
||||
context = scalloc(sizeof(struct context));
|
||||
context->filename = f;
|
||||
|
||||
if (yyparse() != 0) {
|
||||
fprintf(stderr, "Could not parse configfile\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
FREE(context->line_copy);
|
||||
free(context);
|
||||
free(new);
|
||||
free(buf);
|
||||
}
|
||||
|
@ -162,6 +182,7 @@ void parse_file(const char *f) {
|
|||
|
||||
%expect 1
|
||||
%error-verbose
|
||||
%lex-param { struct context *context }
|
||||
|
||||
%union {
|
||||
int number;
|
||||
|
|
11
src/mainx.c
11
src/mainx.c
|
@ -150,6 +150,7 @@ int main(int argc, char *argv[], char *env[]) {
|
|||
int i, screens, opt;
|
||||
char *override_configpath = NULL;
|
||||
bool autostart = true;
|
||||
bool only_check_config = false;
|
||||
xcb_connection_t *conn;
|
||||
xcb_property_handlers_t prophs;
|
||||
xcb_intern_atom_cookie_t atom_cookies[NUM_ATOMS];
|
||||
|
@ -170,7 +171,7 @@ int main(int argc, char *argv[], char *env[]) {
|
|||
|
||||
start_argv = argv;
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "c:vahld:V", long_options, &option_index)) != -1) {
|
||||
while ((opt = getopt_long(argc, argv, "c:Cvahld:V", long_options, &option_index)) != -1) {
|
||||
switch (opt) {
|
||||
case 'a':
|
||||
LOG("Autostart disabled using -a\n");
|
||||
|
@ -179,6 +180,10 @@ int main(int argc, char *argv[], char *env[]) {
|
|||
case 'c':
|
||||
override_configpath = sstrdup(optarg);
|
||||
break;
|
||||
case 'C':
|
||||
LOG("Checking configuration file only (-C)\n");
|
||||
only_check_config = true;
|
||||
break;
|
||||
case 'v':
|
||||
printf("i3 version " I3_VERSION " © 2009 Michael Stapelberg and contributors\n");
|
||||
exit(EXIT_SUCCESS);
|
||||
|
@ -218,6 +223,10 @@ int main(int argc, char *argv[], char *env[]) {
|
|||
die("Cannot open display\n");
|
||||
|
||||
load_configuration(conn, override_configpath, false);
|
||||
if (only_check_config) {
|
||||
LOG("Done checking configuration file. Exiting.\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* Create the initial container on the first workspace. This used to
|
||||
* be part of init_table, but since it possibly requires an X
|
||||
|
|
Loading…
Reference in New Issue