%{ /* * File: parser.y * Date created: March 16, 1999 (Tuesday, 17:17h) * Author: Copyright (C) 1999 Thomas Jensen * tsjensen@stud.informatik.uni-erlangen.de * Version: $Id: parser.y,v 1.21 1999/08/18 15:38:44 tsjensen Exp tsjensen $ * Language: GNU bison (ANSI C) * Purpose: Yacc parser for boxes configuration files * * Remarks: o This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * o This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * o You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * Revision History: * * $Log: parser.y,v $ * Revision 1.21 1999/08/18 15:38:44 tsjensen * Added new tokens YCHGDEL and YDELWORD * Added code for DELIMITER statements * * Revision 1.20 1999/08/16 16:29:25 tsjensen * Changed SAMPLE block syntax to get rid of all the quoting sh*t in SAMPLE * blocks. Samples now look authentic even in the config file. Very simple now. * Added new token YENDSAMPLE in the process. * * Revision 1.19 1999/08/14 19:19:25 tsjensen * Added anz_shapespec variable to count non-deepempty user-specified shapes * (must at least be 1) * Moved yylex declaration to lexer.h, where it belongs * Bugfix: bison and yacc seem to differ in the way action code is treated * which is associated with an error token. Since bison calls the action * routine whenever a token is skipped while recovering, added skipping * variable to ensure that only one "skipping to next design" message is * printed. That was not necessary before. %-/ * Removed existence check from corner_check() routine (obsolete). * Added code to generate required shapes which were not specified by the user * Clean-up in shape_def rule (use freeshape() and some more) * * Revision 1.18 1999/07/23 16:14:17 tsjensen * Added computation of height of highest shape in design (maxshapeheight) * Options -l and -d together now call quickinfo mode -> parse only 1 design * * Revision 1.17 1999/07/22 12:27:16 tsjensen * Added GNU GPL disclaimer * Renamed parser.h include to lexer.h (same file) * Added include config.h * * Revision 1.16 1999/07/02 11:54:52 tsjensen * Some minor changes to please compiler * Communication of speed mode to lexer * Bugfix: Forgot to check for opt.l before calling YYACCEPT * * Revision 1.15 1999/06/30 12:13:47 tsjensen * Now parsing only those designs which will be needed later on * Checks formerly done in boxes.c now done here (no valid designs etc.) * * Revision 1.14 1999/06/28 18:32:51 tsjensen * Unified appearance of error messages, which are now all based on yyerror() * Eliminated duplicate code by introducing intermediate rules * New tokens YTO, YWITH, and YRXPFLAG to reduce strcasecmp() usage * New token YUNREC for integration of lexer into error handling * Some code restructuring and rule renaming for better consistency * Added out-of-memory-checks to many strdup()s and such * * Revision 1.13 1999/06/28 12:15:47 tsjensen * Added error handling. Now skips to next design on error. * Replaced DEBUG macro with PARSER_DEBUG, which is now activated in boxes.h * Added rule first_rule, which performs init and cleanup formerly done in main() * Introduced symbols YBOX and YEND * * Revision 1.12 1999/06/22 12:01:01 tsjensen * Added #undef DEBUG, because DEBUGging is now activated in boxes.h * Added #include tools.h * * Revision 1.11 1999/06/20 14:18:51 tsjensen * Adden YPADDING and YNUMBER tokens plus code for padding blocks * * Revision 1.10 1999/06/17 19:04:45 tsjensen * Added detection of empty sample blocks (we don't want that) * Added detection of duplicate sample blocks * * Revision 1.9 1999/06/14 12:13:01 tsjensen * Added YREVERSE token * Added code for regexp reversion * * Revision 1.7 1999/04/09 13:31:54 tsjensen * Added checks for duplicate design names * Added checks for valid design names (no extended ASCII or ctrl chars) * Removed all code related to OFFSET blocks (obsolete) * * Revision 1.6 1999/04/04 16:07:53 tsjensen * Enforced use of PROJECT macro * Added "indent" directive to grammar * Added "replace" directive to grammar * * Revision 1.5 1999/03/30 13:29:50 tsjensen * Added computation of minimum width/height of each design. * * Revision 1.4 1999/03/30 09:37:51 tsjensen * It drew a correct box for the first time! * * Revision 1.3 1999/03/24 17:29:12 tsjensen * Added detection of empty shapes ("") which are now cleared (+warning) * Changed rcs string to #ident directive * * Revision 1.1 1999/03/18 15:10:06 tsjensen * Initial revision * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "config.h" #include #include #include #include "shape.h" #include "boxes.h" #include "tools.h" #include "lexer.h" const char rcsid_parser_y[] = "$Id: parser.y,v 1.21 1999/08/18 15:38:44 tsjensen Exp tsjensen $"; static int pflicht = 0; static int time_for_se_check = 0; static int anz_shapespec = 0; /* number of user-specified shapes */ int speeding = 0; /* true if we're skipping designs, */ /* but no error */ static int skipping = 0; /* used to limit "skipping" msgs */ static int check_sizes() /* * For the author's convenience, it is required that shapes on one side * have equal width (vertical sides) and height (horizontal sides). * * RETURNS: == 0 no problem detected * != 0 on error (prints error message, too) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ { int i, j, k; #ifdef PARSER_DEBUG fprintf (stderr, "check_sizes()\n"); #endif for (i=0; i KEYWORD %token WORD %token STRING %token SHAPE %token YNUMBER %token YRXPFLAG %token YDELWORD %type shape_def %type shape_lines %type rflag %start first_rule %% first_rule: { /* * Initialize parser data structures */ designs = (design_t *) calloc (1, sizeof(design_t)); if (designs == NULL) { perror (PROJECT); YYABORT; } designs->indentmode = DEF_INDENTMODE; } config_file { /* * Clean up parser data structures */ design_t *tmp; if (design_idx == 0) { BFREE (designs); anz_designs = 0; if (opt.design_choice_by_user) { fprintf (stderr, "%s: unknown box design -- %s\n", PROJECT, (char *) opt.design); } else { yyerror ("no valid designs found"); } YYABORT; } --design_idx; anz_designs = design_idx + 1; tmp = (design_t *) realloc (designs, anz_designs*sizeof(design_t)); if (!tmp) { perror (PROJECT); YYABORT; } designs = tmp; } ; config_file: config_file design_or_error | design_or_error ; design_or_error: design | error { if (!speeding && !skipping) { recover(); yyerror ("skipping to next design"); skipping = 1; } } ; design: YBOX WORD { chg_strdelims ('\\', '\"'); skipping = 0; if (!design_needed ($2, design_idx)) { speeding = 1; begin_speedmode(); YYERROR; } } layout YEND WORD { design_t *tmp; int i; unsigned char *p; #ifdef PARSER_DEBUG fprintf (stderr, "--------- ADDING DESIGN \"%s\".\n", $2); #endif if (strcasecmp ($2, $6)) { yyerror ("box design name differs at BOX and END"); YYERROR; } if (pflicht < 3) { yyerror ("entries SAMPLE, SHAPES, and ELASTIC are mandatory"); YYERROR; } for (i=0; i 126) { yyerror ("box design name must consist of printable standard " "ASCII characters."); YYERROR; } ++p; } designs[design_idx].name = (char *) strdup ($2); if (designs[design_idx].name == NULL) { perror (PROJECT); YYABORT; } pflicht = 0; time_for_se_check = 0; anz_shapespec = 0; /* * Check if we need to continue parsing. If not, return. * The condition here must correspond to design_needed(). */ if (opt.design_choice_by_user || (!opt.r && !opt.l)) { anz_designs = design_idx + 1; YYACCEPT; } /* * Allocate space for next design */ ++design_idx; tmp = (design_t *) realloc (designs, (design_idx+1)*sizeof(design_t)); if (tmp == NULL) { perror (PROJECT); YYABORT; } designs = tmp; memset (&(designs[design_idx]), 0, sizeof(design_t)); designs[design_idx].indentmode = DEF_INDENTMODE; } ; layout: layout entry | layout block | entry | block ; entry: KEYWORD STRING { #ifdef PARSER_DEBUG fprintf (stderr, "entry rule fulfilled [%s = %s]\n", $1, $2); #endif if (strcasecmp ($1, "author") == 0) { designs[design_idx].author = (char *) strdup ($2); if (designs[design_idx].author == NULL) { perror (PROJECT); YYABORT; } } else if (strcasecmp ($1, "revision") == 0) { designs[design_idx].revision = (char *) strdup ($2); if (designs[design_idx].revision == NULL) { perror (PROJECT); YYABORT; } } else if (strcasecmp ($1, "created") == 0) { designs[design_idx].created = (char *) strdup ($2); if (designs[design_idx].created == NULL) { perror (PROJECT); YYABORT; } } else if (strcasecmp ($1, "revdate") == 0) { designs[design_idx].revdate = (char *) strdup ($2); if (designs[design_idx].revdate == NULL) { perror (PROJECT); YYABORT; } } else if (strcasecmp ($1, "indent") == 0) { if (strcasecmp ($2, "text") == 0 || strcasecmp ($2, "box") == 0 || strcasecmp ($2, "none") == 0) { designs[design_idx].indentmode = $2[0]; } else { yyerror ("indent keyword must be followed by \"text\", " "\"box\", or \"none\""); YYERROR; } } else { yyerror ("internal parser error (unrecognized: %s) in line %d " "of %s.", $1, __LINE__, __FILE__); YYERROR; } } | YCHGDEL YDELWORD { if (strlen($2) != 2) { yyerror ("invalid string delimiter specification -- %s", $2); YYERROR; } if (($2)[0] == ($2)[1]) { yyerror ("string delimiter and escape char may not be the same"); YYERROR; } if (strchr (LEX_SDELIM, ($2)[1]) == NULL) { yyerror ("invalid string delimiter -- %c (try one of %s)", ($2)[1], LEX_SDELIM); YYERROR; } chg_strdelims ($2[0], $2[1]); } | WORD STRING { #ifdef PARSER_DEBUG fprintf (stderr, "%s: Discarding entry [%s = %s].\n", PROJECT, $1, $2); #endif } ; block: YSAMPLE STRING YENDSAMPLE { /* * SAMPLE block (STRING is non-empty if we get here) */ char *line; #ifdef PARSER_DEBUG fprintf (stderr, "SAMPLE block rule satisfied\n"); #endif if (designs[design_idx].sample) { yyerror ("duplicate SAMPLE block"); YYERROR; } line = (char *) strdup ($2); if (line == NULL) { perror (PROJECT); YYABORT; } designs[design_idx].sample = line; ++pflicht; } | YSHAPES '{' slist '}' { int i,j; shape_t fshape; /* found shape */ int fside; /* first side */ int sc; /* side counter */ int side; /* effective side */ int rc; /* received return code */ /* * At least one shape must be specified */ if (anz_shapespec < 1) { yyerror ("must specify at least one non-empty shape per design"); YYERROR; } /* * Ensure that all corners have been specified. Generate corners * as necessary, starting at any side which already includes at * least one shape in order to ensure correct measurements. */ fshape = findshape (designs[design_idx].shape, ANZ_SHAPES); if (fshape == ANZ_SHAPES) { yyerror ("internal error"); YYABORT; /* never happens ;-) */ } fside = on_side (fshape, 0); if (fside == ANZ_SIDES) { yyerror ("internal error"); YYABORT; /* never happens ;-) */ } for (sc=0,side=fside; scheight = 1; else c->height = c[nshape].height; c->width = designs[design_idx].shape[fshape].width; } else { if (nshape == SHAPES_PER_SIDE) c->width = 1; else c->width = c[nshape].width; c->height = designs[design_idx].shape[fshape].height; } c->elastic = 0; rc = genshape (c->width, c->height, &(c->chars)); if (rc) YYABORT; } fshape = sides[side][SHAPES_PER_SIDE-1]; } /* * For all sides whose side shapes have not been defined, generate * an elastic middle side shape. */ for (side=0; sidewidth = designs[design_idx].shape[sides[side][0]].width; c->height = 1; } else { c->width = 1; c->height = designs[design_idx].shape[sides[side][0]].height; } c->elastic = 1; rc = genshape (c->width, c->height, &(c->chars)); if (rc) YYABORT; } } if (check_sizes()) YYERROR; ++pflicht; if (++time_for_se_check > 1) { if (perform_se_check() != 0) YYERROR; } /* * Compute minimum height/width of a box of current design */ for (i=0; i designs[design_idx].minheight) designs[design_idx].minheight = c; } else { /* horizontal sides */ for (j=0; j designs[design_idx].minwidth) designs[design_idx].minwidth = c; } } /* * Compute height of highest shape in design */ for (i=0; i designs[design_idx].maxshapeheight) designs[design_idx].maxshapeheight = designs[design_idx].shape[i].height; } #ifdef PARSER_DEBUG fprintf (stderr, "Minimum box dimensions: width %d height %d\n", designs[design_idx].minwidth, designs[design_idx].minheight); fprintf (stderr, "Maximum shape height: %d\n", designs[design_idx].maxshapeheight); #endif } | YELASTIC '(' elist ')' { ++pflicht; if (++time_for_se_check > 1) { if (perform_se_check() != 0) YYERROR; } } | YREPLACE rflag STRING YWITH STRING { int a = designs[design_idx].anz_reprules; #ifdef PARSER_DEBUG fprintf (stderr, "Adding replacement rule: \"%s\" with \"%s\" (%c)\n", $3, $5, $2); #endif designs[design_idx].reprules = (reprule_t *) realloc (designs[design_idx].reprules, (a+1) * sizeof(reprule_t)); if (designs[design_idx].reprules == NULL) { perror (PROJECT); YYABORT; } memset (&(designs[design_idx].reprules[a]), 0, sizeof(reprule_t)); designs[design_idx].reprules[a].search = (char *) strdup ($3); designs[design_idx].reprules[a].repstr = (char *) strdup ($5); if (designs[design_idx].reprules[a].search == NULL || designs[design_idx].reprules[a].repstr == NULL) { perror (PROJECT); YYABORT; } designs[design_idx].reprules[a].line = yylineno; designs[design_idx].reprules[a].mode = $2; designs[design_idx].anz_reprules = a + 1; } | YREVERSE rflag STRING YTO STRING { int a = designs[design_idx].anz_revrules; #ifdef PARSER_DEBUG fprintf (stderr, "Adding reversion rule: \"%s\" to \"%s\" (%c)\n", $3, $5, $2); #endif designs[design_idx].revrules = (reprule_t *) realloc (designs[design_idx].revrules, (a+1) * sizeof(reprule_t)); if (designs[design_idx].revrules == NULL) { perror (PROJECT); YYABORT; } memset (&(designs[design_idx].revrules[a]), 0, sizeof(reprule_t)); designs[design_idx].revrules[a].search = (char *) strdup ($3); designs[design_idx].revrules[a].repstr = (char *) strdup ($5); if (designs[design_idx].revrules[a].search == NULL || designs[design_idx].revrules[a].repstr == NULL) { perror (PROJECT); YYABORT; } designs[design_idx].revrules[a].line = yylineno; designs[design_idx].revrules[a].mode = $2; designs[design_idx].anz_revrules = a + 1; } | YPADDING '{' wlist '}' { #ifdef PARSER_DEBUG fprintf (stderr, "Padding set to (l%d o%d r%d u%d)\n", designs[design_idx].padding[BLEF], designs[design_idx].padding[BTOP], designs[design_idx].padding[BRIG], designs[design_idx].padding[BBOT]); #endif } ; rflag: YRXPFLAG { $$ = $1; } | { $$ = 'g'; } ; elist: elist ',' elist_entry | elist_entry; elist_entry: SHAPE { #ifdef PARSER_DEBUG fprintf (stderr, "Marked \'%s\' shape as elastic\n", shape_name[(int)$1]); #endif designs[design_idx].shape[$1].elastic = 1; } ; slist: slist slist_entry | slist_entry ; slist_entry: SHAPE shape_def { #ifdef PARSER_DEBUG fprintf (stderr, "Adding shape spec for \'%s\' (width %d " "height %d)\n", shape_name[$1], $2.width, $2.height); #endif if (isempty (designs[design_idx].shape + $1)) { designs[design_idx].shape[$1] = $2; if (!isdeepempty(&($2))) ++anz_shapespec; } else { yyerror ("duplicate specification for %s shape", shape_name[$1]); YYERROR; } } ; shape_def: '(' shape_lines ')' { if ($2.width == 0 || $2.height == 0) { yyerror ("minimum shape dimension is 1x1 - clearing"); freeshape (&($2)); } $$ = $2; } | '(' ')' { $$ = SENTRY_INITIALIZER; } ; shape_lines: shape_lines ',' STRING { sentry_t rval = $1; size_t slen = strlen ($3); char **tmp; #ifdef PARSER_DEBUG fprintf (stderr, "Extending a shape entry\n"); #endif if (slen != rval.width) { yyerror ("all elements of a shape spec must be of equal length"); YYERROR; } rval.height++; tmp = (char **) realloc (rval.chars, rval.height*sizeof(char*)); if (tmp == NULL) { perror (PROJECT": shape_lines11"); YYABORT; } rval.chars = tmp; rval.chars[rval.height-1] = (char *) strdup ($3); if (rval.chars[rval.height-1] == NULL) { perror (PROJECT": shape_lines12"); YYABORT; } $$ = rval; } | STRING { sentry_t rval = SENTRY_INITIALIZER; #ifdef PARSER_DEBUG fprintf (stderr, "Initializing a shape entry with first line\n"); #endif rval.width = strlen ($1); rval.height = 1; rval.chars = (char **) malloc (sizeof(char*)); if (rval.chars == NULL) { perror (PROJECT": shape_lines21"); YYABORT; } rval.chars[0] = (char *) strdup ($1); if (rval.chars[0] == NULL) { perror (PROJECT": shape_lines22"); YYABORT; } $$ = rval; } ; wlist: wlist wlist_entry | wlist_entry; wlist_entry: WORD YNUMBER { if ($2 < 0) { yyerror ("padding must be a positive integer (%s %d) (ignored)", $1, $2); } else { size_t len1 = strlen ($1); if (len1 <= 3 && !strncasecmp ("all", $1, len1)) { designs[design_idx].padding[BTOP] = $2; designs[design_idx].padding[BBOT] = $2; designs[design_idx].padding[BLEF] = $2; designs[design_idx].padding[BRIG] = $2; } else if (len1 <= 10 && !strncasecmp ("horizontal", $1, len1)) { designs[design_idx].padding[BRIG] = $2; designs[design_idx].padding[BLEF] = $2; } else if (len1 <= 8 && !strncasecmp ("vertical", $1, len1)) { designs[design_idx].padding[BTOP] = $2; designs[design_idx].padding[BBOT] = $2; } else if (len1 <= 3 && !strncasecmp ("top", $1, len1)) { designs[design_idx].padding[BTOP] = $2; } else if (len1 <= 5 && !strncasecmp ("right", $1, len1)) { designs[design_idx].padding[BRIG] = $2; } else if (len1 <= 4 && !strncasecmp ("left", $1, len1)) { designs[design_idx].padding[BLEF] = $2; } else if (len1 <= 6 && !strncasecmp ("bottom", $1, len1)) { designs[design_idx].padding[BBOT] = $2; } else { yyerror ("invalid padding area %s (ignored)", $1); } } } ; %% /*EOF*/ /* vim: set sw=4 cindent: */