mirror of
https://github.com/ascii-boxes/boxes.git
synced 2025-07-25 17:37:18 +02:00
1068 lines
32 KiB
Plaintext
1068 lines
32 KiB
Plaintext
%{
|
|
/*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#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<ANZ_SIDES; ++i) {
|
|
if (i == 0 || i == 2) {
|
|
/*
|
|
* horizontal
|
|
*/
|
|
for (j=0; j<SHAPES_PER_SIDE-1; ++j) {
|
|
if (designs[design_idx].shape[sides[i][j]].height == 0)
|
|
continue;
|
|
for (k=j+1; k<SHAPES_PER_SIDE; ++k) {
|
|
if (designs[design_idx].shape[sides[i][k]].height == 0)
|
|
continue;
|
|
if (designs[design_idx].shape[sides[i][j]].height
|
|
!= designs[design_idx].shape[sides[i][k]].height) {
|
|
yyerror ("All shapes on horizontal sides must be of "
|
|
"equal height (%s: %d, %s: %d)\n",
|
|
shape_name[sides[i][j]],
|
|
designs[design_idx].shape[sides[i][j]].height,
|
|
shape_name[sides[i][k]],
|
|
designs[design_idx].shape[sides[i][k]].height);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/*
|
|
* vertical
|
|
*/
|
|
for (j=0; j<SHAPES_PER_SIDE-1; ++j) {
|
|
if (designs[design_idx].shape[sides[i][j]].width == 0)
|
|
continue;
|
|
for (k=j+1; k<SHAPES_PER_SIDE; ++k) {
|
|
if (designs[design_idx].shape[sides[i][k]].width == 0)
|
|
continue;
|
|
if (designs[design_idx].shape[sides[i][j]].width
|
|
!= designs[design_idx].shape[sides[i][k]].width) {
|
|
yyerror ("All shapes on vertical sides must be of "
|
|
"equal width (%s: %d, %s: %d)\n",
|
|
shape_name[sides[i][j]],
|
|
designs[design_idx].shape[sides[i][j]].width,
|
|
shape_name[sides[i][k]],
|
|
designs[design_idx].shape[sides[i][k]].width);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0; /* all clear */
|
|
}
|
|
|
|
|
|
|
|
static int corner_check()
|
|
/*
|
|
* Check that no corners are elastic.
|
|
*
|
|
* RETURNS: == 0 no problem detected
|
|
* != 0 on error
|
|
*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*/
|
|
{
|
|
int c;
|
|
|
|
#ifdef PARSER_DEBUG
|
|
fprintf (stderr, "corner_check()\n");
|
|
#endif
|
|
|
|
for (c=0; c<ANZ_CORNERS; ++c) {
|
|
if (designs[design_idx].shape[corners[c]].elastic) {
|
|
yyerror ("Corners may not be elastic (%s)", shape_name[corners[c]]);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0; /* all clear */
|
|
}
|
|
|
|
|
|
|
|
static shape_t non_existent_elastics()
|
|
{
|
|
shape_t i;
|
|
|
|
#ifdef PARSER_DEBUG
|
|
fprintf (stderr, "non_existent_elastics()\n");
|
|
#endif
|
|
|
|
for (i=0; i<ANZ_SHAPES; ++i) {
|
|
if (designs[design_idx].shape[i].elastic
|
|
&& isempty(designs[design_idx].shape+i))
|
|
return i;
|
|
}
|
|
|
|
return (shape_t) ANZ_SHAPES; /* all elastic shapes exist */
|
|
}
|
|
|
|
|
|
|
|
static int insufficient_elasticity()
|
|
{
|
|
int i, j, ef;
|
|
|
|
#ifdef PARSER_DEBUG
|
|
fprintf (stderr, "insufficient_elasticity()\n");
|
|
#endif
|
|
|
|
for (i=0; i<ANZ_SIDES; ++i) {
|
|
for (j=1,ef=0; j<4; ++j)
|
|
if (designs[design_idx].shape[sides[i][j]].elastic)
|
|
++ef;
|
|
if (ef != 1 && ef != 2)
|
|
return 1; /* error */
|
|
}
|
|
|
|
return 0; /* all clear */
|
|
}
|
|
|
|
|
|
|
|
static int adjoining_elastics()
|
|
{
|
|
int i, j, ef;
|
|
|
|
#ifdef PARSER_DEBUG
|
|
fprintf (stderr, "adjoining_elastics()\n");
|
|
#endif
|
|
|
|
for (i=0; i<ANZ_SIDES; ++i) {
|
|
ef = 0;
|
|
for (j=1; j<4; ++j) {
|
|
if (isempty(designs[design_idx].shape+sides[i][j]))
|
|
continue;
|
|
if (designs[design_idx].shape[sides[i][j]].elastic) {
|
|
if (ef)
|
|
return 1; /* error detected */
|
|
else
|
|
ef = 1;
|
|
}
|
|
else {
|
|
ef = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0; /* all clear */
|
|
}
|
|
|
|
|
|
|
|
static int perform_se_check()
|
|
/*
|
|
* (shape-elastic check)
|
|
*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*/
|
|
{
|
|
shape_t s_rc;
|
|
|
|
s_rc = non_existent_elastics();
|
|
if (s_rc != ANZ_SHAPES) {
|
|
yyerror ("Shape %s has been specified as elastic but doesn't exist",
|
|
shape_name[s_rc]);
|
|
return 1;
|
|
}
|
|
|
|
if (corner_check()) {
|
|
/* Error message printed in check func */
|
|
return 1;
|
|
}
|
|
|
|
if (insufficient_elasticity()) {
|
|
yyerror ("There must be exactly one or two elastic shapes per side");
|
|
return 1;
|
|
}
|
|
|
|
if (adjoining_elastics()) {
|
|
yyerror ("Two adjoining shapes may not be elastic");
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static void recover()
|
|
/*
|
|
* Reset parser to neutral state, so a new design can be parsed.
|
|
*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*/
|
|
{
|
|
pflicht = 0;
|
|
time_for_se_check = 0;
|
|
anz_shapespec = 0;
|
|
chg_strdelims ('\\', '\"');
|
|
|
|
/*
|
|
* Clear current design
|
|
*/
|
|
BFREE (designs[design_idx].name);
|
|
BFREE (designs[design_idx].author);
|
|
BFREE (designs[design_idx].created);
|
|
BFREE (designs[design_idx].revision);
|
|
BFREE (designs[design_idx].revdate);
|
|
BFREE (designs[design_idx].sample);
|
|
memset (designs+design_idx, 0, sizeof(design_t));
|
|
designs[design_idx].indentmode = DEF_INDENTMODE;
|
|
}
|
|
|
|
|
|
|
|
static int design_needed (const char *name, const int design_idx)
|
|
/*
|
|
* Return true if design of name name will be needed later on
|
|
*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*/
|
|
{
|
|
if (opt.design_choice_by_user) {
|
|
return !strcasecmp (name, (char *) opt.design);
|
|
}
|
|
else {
|
|
if (opt.r || opt.l)
|
|
return 1;
|
|
if (design_idx == 0)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
%}
|
|
|
|
|
|
%union {
|
|
char *s;
|
|
char c;
|
|
shape_t shape;
|
|
sentry_t sentry;
|
|
int num;
|
|
}
|
|
|
|
%token YSHAPES YELASTIC YPADDING YSAMPLE YENDSAMPLE YBOX YEND YUNREC
|
|
%token YREPLACE YREVERSE YTO YWITH YCHGDEL
|
|
%token <s> KEYWORD
|
|
%token <s> WORD
|
|
%token <s> STRING
|
|
%token <shape> SHAPE
|
|
%token <num> YNUMBER
|
|
%token <c> YRXPFLAG
|
|
%token <s> YDELWORD
|
|
|
|
%type <sentry> shape_def
|
|
%type <sentry> shape_lines
|
|
%type <c> 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<design_idx; ++i) {
|
|
if (strcasecmp ($2, designs[i].name) == 0) {
|
|
yyerror ("duplicate box design name -- %s", $2);
|
|
YYERROR;
|
|
}
|
|
}
|
|
|
|
p = $2;
|
|
while (*p) {
|
|
if (*p < 32 || *p > 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; sc<ANZ_SIDES; ++sc,side=(side+1)%ANZ_SIDES) {
|
|
shape_t nshape; /* next shape */
|
|
sentry_t *c; /* corner to be processed */
|
|
c = designs[design_idx].shape + sides[side][SHAPES_PER_SIDE-1];
|
|
|
|
if (isempty(c)) {
|
|
nshape = findshape (c, SHAPES_PER_SIDE);
|
|
if (side == BLEF || side == BRIG) {
|
|
if (nshape == SHAPES_PER_SIDE)
|
|
c->height = 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; side<ANZ_SIDES; ++side) {
|
|
int found = 0;
|
|
for (i=1; i<SHAPES_PER_SIDE-1; ++i) {
|
|
if (isempty (designs[design_idx].shape + sides[side][i]))
|
|
continue;
|
|
else
|
|
found = 1;
|
|
}
|
|
if (!found) {
|
|
sentry_t *c = designs[design_idx].shape
|
|
+ sides[side][SHAPES_PER_SIDE/2];
|
|
if (side == BLEF || side == BRIG) {
|
|
c->width = 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<ANZ_SIDES; ++i) {
|
|
size_t c = 0;
|
|
if (i % 2) { /* vertical sides */
|
|
for (j=0; j<SHAPES_PER_SIDE; ++j) {
|
|
if (!isempty(designs[design_idx].shape + sides[i][j]))
|
|
c += designs[design_idx].shape[sides[i][j]].height;
|
|
}
|
|
if (c > designs[design_idx].minheight)
|
|
designs[design_idx].minheight = c;
|
|
}
|
|
else { /* horizontal sides */
|
|
for (j=0; j<SHAPES_PER_SIDE; ++j) {
|
|
if (!isempty(designs[design_idx].shape + sides[i][j]))
|
|
c += designs[design_idx].shape[sides[i][j]].width;
|
|
}
|
|
if (c > designs[design_idx].minwidth)
|
|
designs[design_idx].minwidth = c;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Compute height of highest shape in design
|
|
*/
|
|
for (i=0; i<ANZ_SHAPES; ++i) {
|
|
if (isempty(designs[design_idx].shape + i))
|
|
continue;
|
|
if (designs[design_idx].shape[i].height > 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: */
|