%{ /* -*- c -*- */ /* * Grammar for parsing the style sheets * * Copyright (c) 1988-1993 Miguel Santana * Copyright (c) 1995-1999 Akim Demaille, Miguel Santana * */ /* * This file is part of a2ps * * 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, or (at your option) * any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* * $Id: parsessh.y,v 1.71 1998/03/04 17:27:51 demaille Exp $ */ #include "a2ps.h" #include "jobs.h" #include "ffaces.h" #include "ssheet.h" #include "message.h" #include "routines.h" #include "yy2ssh.h" #include "regex.h" #define YYDEBUG 1 #define YYERROR_VERBOSE 1 #define YYPRINT(file, type, value) yyprint (file, type, value) /* We need to use the same `const' as bison, to avoid the following prototypes to diverge from the function declarations */ #undef const #ifndef __cplusplus # ifndef __STDC__ # define const # endif #endif /* Comes from the parser */ extern int sshlineno; /* Comes from the caller */ extern FILE * sshin; extern struct a2ps_job * job; extern const char * sshfilename; /* Local prototypes */ void yyerror PARAMS ((const char *msg)); static void yyprint (); /* Initilizes the obstacks */ void sshlex_initialize PARAMS ((void)); /* Comes from main.c */ extern int highlight_level; int yylex PARAMS ((void)); struct style_sheet * parse_style_sheet PARAMS ((const char * filename)); /* Defines the style sheet being loaded */ static struct style_sheet * parsed_style_sheet = NULL; %} %union { int integer; uchar * string; struct pattern * pattern; struct style_sheet * sheet; struct rule * rule; struct sequence * sequence; struct darray * array; struct words * words; struct faced_string * faced_string; enum face_e face; /* Face */ enum fflag_e fflags; /* Flags for faces */ struct fface_s fface; /* Flagged face */ enum case_sensitiveness sensitiveness; } %token tSTYLE tIS tEND tKEYWORDS tARE tIN tOPERATORS tSEQUENCES %token tFIRST tSECOND tALPHABET tALPHABETS tDOCUMENTATION tEXCEPTIONS %token tCASE tCSTRING tCCHAR tOPTIONAL tCLOSERS %token tWRITTEN tBY tVERSION tREQUIRES tA2PS tANCESTORS %token tFACE %token tFFLAGS %token tSTRING tLATEXSYMBOL %token tREGEX %token tSENSITIVENESS %token tBACK_REF %type regex %type fface fface_sxp %type authors documentation version long_string requirement %type a_rhs %type rule keyword_regex operator_regex %type style_sheet definition_list %type keywords_def keywords_rule_list %type operators_def operators_rule_list %type sequence_list sequence_def %type exception_def_opt %type ancestors_def ancestors_list %type closers_opt %type case_def %type rhs rhs_list %type sequence %% /************************************************************************/ /* Top most */ /************************************************************************/ file : style_sheet { parsed_style_sheet = $1; } ; style_sheet : tSTYLE tSTRING tIS definition_list tEND tSTYLE { $4->name = $2; $4->key = ""; $$ = $4; } ; definition_list : /* empty */ { $$ = new_style_sheet ((const uchar *) ""); } | definition_list tOPTIONAL tKEYWORDS keywords_def tKEYWORDS { if (highlight_level == 2) { words_set_no_face ($4, Plain_fface); words_merge_rules_unique ($1->keywords, $4); } else words_free ($4); $$ = $1; } | definition_list tKEYWORDS keywords_def tKEYWORDS { words_set_no_face ($3, Plain_fface); words_merge_rules_unique ($1->keywords, $3); $$ = $1; } | definition_list tOPTIONAL tOPERATORS operators_def tOPERATORS { if (highlight_level == 2) { words_set_no_face ($4, Plain_fface); words_merge_rules_unique ($1->operators, $4); } else words_free ($4); $$ = $1; } | definition_list tOPERATORS operators_def tOPERATORS { words_set_no_face ($3, Plain_fface); words_merge_rules_unique ($1->operators, $3); $$ = $1; } | definition_list tOPTIONAL sequence_def { if (highlight_level == 2) { da_concat ($1->sequences, $3); da_erase ($3); } else da_free ($3, (da_map_func_t) free_sequence); $$ = $1; } | definition_list sequence_def { da_concat ($1->sequences, $2); da_erase ($2); $$ = $1; } | definition_list ancestors_def { da_concat ($1->ancestors, $2); da_erase ($2); $$ = $1; } | definition_list tALPHABETS tARE tSTRING { string_to_array ($1->alpha1, $4); string_to_array ($1->alpha2, $4); /* This is the syntax table used by regex */ free ($4); $4 = NULL; $$ = $1; } | definition_list tFIRST tALPHABET tIS tSTRING { string_to_array ($1->alpha1, $5); /* This is the syntax table used by regex */ free ($5); $5 = NULL; $$ = $1; } | definition_list tSECOND tALPHABET tIS tSTRING { string_to_array ($1->alpha2, $5); /* This is the syntax table used by regex */ free ($5); $5 = NULL; $$ = $1; } | definition_list case_def { $1->sensitiveness = $2; } | definition_list documentation { $1->documentation = $2; } | definition_list authors { $1->author = $2; } | definition_list version { style_sheet_set_version ($1, (const char *) $2); } | definition_list requirement { /* Make sure now that we won't encounter new tokens. * This avoids nasty error messages, or worse: * unexpected behavior at run time */ if (!style_sheet_set_requirement ($1, (const char *) $2)) error (1, 0, _("cannot process `%s' which requires a2ps version %s"), sshfilename, $2); } ; /************************************************************************/ /* Dealing with the inessential informations */ /************************************************************************/ requirement : tREQUIRES tA2PS tSTRING { $$ = $3 ; } ; | tREQUIRES tA2PS tVERSION tSTRING { $$ = $4 ; } ; documentation : tDOCUMENTATION tIS long_string tEND tDOCUMENTATION { $$ = $3 ; }; long_string: tSTRING { $$ = $1; } | long_string tSTRING { size_t len1; size_t len2; len1 = ustrlen ($1); $1[len1] = '\n'; len2 = ustrlen ($2); $$ = XMALLOC (uchar, len1 + len2 + 2); ustpcpy (ustpncpy ($$, $1, len1 + 1), $2); free ($1); free ($2); } ; authors : tWRITTEN tBY tSTRING { $$ = $3 ; }; version : tVERSION tIS tSTRING { $$ = $3 ; }; | tVERSION tSTRING { $$ = $2 ; }; /************************************************************************/ /* Dealing with the ancestors of a style sheet */ /************************************************************************/ ancestors_def : tANCESTORS tARE ancestors_list tEND tANCESTORS { /* The list of keys of style sheets from which it inherits */ $$ = $3; } ; ancestors_list: tSTRING { /* Create a list of ancestors, and drop the new one in */ $$ = da_new ("Ancestors tmp", 2, da_linear, 2, (da_print_func_t) da_str_print, NULL); da_append ($$, $1); } | ancestors_list ',' tSTRING { da_append ($1, $3); $$ = $1; } ; case_def : tCASE tSENSITIVENESS { $$ = $2 ; } ; /************************************************************************/ /* Rhs */ /* (Lists of (strings/regexp back references, face) */ /************************************************************************/ /* Regex split upon several lines */ regex: tREGEX { $$ = $1; } | regex tREGEX { /* Concatenate $2 to $1 makes $$ */ $$ = $1; $$->pattern = XREALLOC ($$->pattern, char, $1->len + $2->len + 1); strncpy ($$->pattern + $$->len, $2->pattern, $2->len); $$->len += $2->len; free ($2->pattern); } ; rhs: a_rhs { $$ = rhs_new (); rhs_add ($$, $1); } ; a_rhs: tSTRING fface { $$ = faced_string_new ($1, 0, $2); } | tSTRING { $$ = faced_string_new ($1, 0, No_fface); } | fface { $$ = faced_string_new (NULL, 0, $1); } | tBACK_REF fface { $$ = faced_string_new (UNULL, $1, $2); } | tBACK_REF { $$ = faced_string_new (UNULL, $1, No_fface); } | tLATEXSYMBOL { $$ = faced_string_new ($1, 0, Symbol_fface); } ; rhs_list: a_rhs { $$ = rhs_new (); rhs_add ($$, $1); } | rhs_list ',' a_rhs { rhs_add ($1, $3); $$ = $1; } ; /* * The flagged faces (One (true face or Invisible) plus flags) */ fface: tFACE { fface_set_face ($$, $1); fface_reset_flags ($$); } | tFFLAGS { fface_reset_face ($$); fface_set_flags ($$, $1); /* If there is no face, then set Invisible */ fface_add_flags ($$, ff_Invisible); } | '(' fface_sxp ')' { $$ = $2; /* If there is no face, then set Invisible */ if (fface_get_face ($$) == No_face) fface_add_flags ($$, ff_Invisible); } ; fface_sxp: tFACE { fface_set_face($$, $1); fface_reset_flags($$); } | tFFLAGS { fface_reset_face($$); fface_set_flags($$, $1); } | fface_sxp '+' tFACE { /* FIXME: Overloading of the face should be forbidden */ $$ = $1; fface_set_face($$, $3); } | fface_sxp '+' tFFLAGS { $$ = $1; fface_add_flags($$, $3); } ; /************************************************************************/ /* Symbol atoms */ /************************************************************************/ /* A rule is the group of 1. string to match, 2. the rhs * * In the special shortcut where no face is given, see where * rule_list appears for resolving */ rule: tSTRING rhs { $$ = rule_new ($1, NULL, $2, sshfilename, sshlineno); } | tSTRING { $$ = rule_new ($1, NULL, rhs_new_single (UNULL, 0, No_fface), sshfilename, sshlineno); } | '(' tSTRING rhs_list ')' { $$ = rule_new ($2, NULL, $3, sshfilename, sshlineno); } ; /************************************************************************/ /* Keywords lists */ /* We make the difference because the regex must be compiled with a */ /* difference (which is \\b\\(%s\\)\\b) between keywords and operators */ /************************************************************************/ keywords_def : tIN fface tARE keywords_rule_list tEND { words_set_no_face ($4, $2); $$ = $4; } | tARE keywords_rule_list tEND { /* First of all, the No_face must be turned into Plain */ $$ = $2; } ; keywords_rule_list: rule { $$ = words_new ("Keywords: Strings", "Keywords: Regexps", 100, 100); words_add_string ($$, $1); } | keyword_regex { $$ = words_new ("Keywords: Strings", "Keywords: Regexps", 100, 100); words_add_regex ($$, $1); } | keywords_rule_list ',' rule { words_add_string ($1, $3); $$ = $1; } | keywords_rule_list ',' keyword_regex { words_add_regex ($1, $3); $$ = $1; } ; /* A rule is the group of 1. string to match, 2. string to * print, 3. face of the string to print. In the special * shortcut where no face is given, see where rule_list appears * for resolving */ keyword_regex: regex rhs { $$ = keyword_rule_new (UNULL, $1, $2, sshfilename, sshlineno); } | regex { $$ = keyword_rule_new (UNULL, $1, rhs_new_single (UNULL, 0, No_fface), sshfilename, sshlineno); } | '(' regex rhs_list ')' { $$ = keyword_rule_new (UNULL, $2, $3, sshfilename, sshlineno); } ; /************************************************************************/ /* Operators lists */ /************************************************************************/ operators_def: tIN fface tARE operators_rule_list tEND { words_set_no_face ($4, $2); $$ = $4; } | tARE operators_rule_list tEND { /* First of all, the No_face must be turned into Plain */ $$ = $2; } ; operators_rule_list: rule { $$ = words_new ("Operators: Strings", "Operators: Regexps", 100, 100); words_add_string ($$, $1); } | operator_regex { $$ = words_new ("Operators: Strings", "Operators: Regexps", 100, 100); words_add_regex ($$, $1); } | operators_rule_list ',' rule { words_add_string ($1, $3); $$ = $1; } | operators_rule_list ',' operator_regex { words_add_regex ($1, $3); $$ = $1; } ; /* A rule is the group of 1. string to match, 2. string to * print, 3. face of the string to print. In the special * shortcut where no face is given, see where rule_list appears * for resolving */ operator_regex: regex rhs { $$ = rule_new (UNULL, $1, $2, sshfilename, sshlineno); } | regex { $$ = rule_new (UNULL, $1, rhs_new_single (UNULL, 0, No_fface), sshfilename, sshlineno); } | '(' regex rhs_list ')' { $$ = rule_new (UNULL, $2, $3, sshfilename, sshlineno); } ; /************************************************************************/ /* Dealing with the sequences */ /************************************************************************/ sequence_def : tSEQUENCES tARE sequence_list tEND tSEQUENCES { $$ = $3; } ; sequence_list: sequence { $$ = da_new ("Sequence tmp", 100, da_linear, 100, (da_print_func_t) sequence_self_print, NULL); da_append ($$, $1); } | sequence_list ',' sequence { da_append ($1, $3); $$ = $1; } ; /* * I can understand one will get sick reading this. It hurts, indeed, * but is necessary because of shift/reduce conflicts if one uses * * sequence: operators_rule face closers_opt exception_def_opt * * The problem is that when bison/yacc reads this * * . * * it doesn't know whether should be in $1, or in $2. * So we have to inline the rule at hand... */ sequence: /* Expansion of each possibility for * */ tSTRING tLATEXSYMBOL fface closers_opt exception_def_opt { struct rule * open_rule; open_rule = rule_new ($1, NULL, rhs_new_single ($2, 0, Symbol_fface), sshfilename, sshlineno); $$ = sequence_new (open_rule, $3, $4, $5); } | tSTRING tSTRING fface fface closers_opt exception_def_opt { struct rule * open_rule; open_rule = rule_new ($1, NULL, rhs_new_single ($2, 0, $3), sshfilename, sshlineno); $$ = sequence_new (open_rule, $4, $5, $6); } | tSTRING fface fface closers_opt exception_def_opt { struct rule * open_rule; open_rule = rule_new ($1, NULL, rhs_new_single (UNULL, 0, $2), sshfilename, sshlineno); $$ = sequence_new (open_rule, $3, $4, $5); } | tSTRING tSTRING fface closers_opt exception_def_opt { struct rule * open_rule; open_rule = rule_new ($1, NULL, rhs_new_single ($2, 0, $3), sshfilename, sshlineno); $$ = sequence_new (open_rule, $3, $4, $5); } | tSTRING fface closers_opt exception_def_opt { struct rule * open_rule; open_rule = rule_new ($1, NULL, rhs_new_single (UNULL, 0, $2), sshfilename, sshlineno); $$ = sequence_new (open_rule, $2, $3, $4); } | regex tSTRING fface fface closers_opt exception_def_opt { struct rule * open_rule; open_rule = rule_new (UNULL, $1, rhs_new_single ($2, 0, $3), sshfilename, sshlineno); $$ = sequence_new (open_rule, $4, $5, $6); } | regex fface fface closers_opt exception_def_opt { struct rule * open_rule; open_rule = rule_new (UNULL, $1, rhs_new_single (UNULL, 0, $2), sshfilename, sshlineno); $$ = sequence_new (open_rule, $3, $4, $5); } | regex tSTRING fface closers_opt exception_def_opt { struct rule * open_rule; open_rule = rule_new (UNULL, $1, rhs_new_single ($2, 0, $3), sshfilename, sshlineno); $$ = sequence_new (open_rule, $3, $4, $5); } | regex fface closers_opt exception_def_opt { struct rule * open_rule; open_rule = rule_new (UNULL, $1, rhs_new_single (UNULL, 0, No_fface), sshfilename, sshlineno); $$ = sequence_new (open_rule, $2, $3, $4); } | '(' tSTRING rhs_list ')' fface closers_opt exception_def_opt { struct rule * open_rule; open_rule = rule_new ($2, NULL, $3, sshfilename, sshlineno); $$ = sequence_new (open_rule, $5, $6, $7); } | '(' regex rhs_list ')' fface closers_opt exception_def_opt { struct rule * open_rule; open_rule = rule_new (UNULL, $2, $3, sshfilename, sshlineno); $$ = sequence_new (open_rule, $5, $6, $7); } /* End of the brute force expansion */ | tCSTRING { $$ = new_C_string_sequence ("\""); } | tCCHAR { $$ = new_C_string_sequence ("\'"); } ; closers_opt: /* Nothing */ { /* This is a shortcut which means "up to the end of the line". */ $$ = words_new ("Closing: Strings", "Closing: Regexps", 2, 2); words_add_string ($$, rule_new (xustrdup ("\n"), NULL, rhs_new_single (NULL, 0, No_fface), sshfilename, sshlineno)); } | rule { /* Only one */ $$ = words_new ("Closing: Strings", "Closing: Regexps", 2, 2); words_add_string ($$, $1); } | operator_regex { /* Only one */ $$ = words_new ("Closing: Strings", "Closing: Regexps", 2, 2); words_add_regex ($$, $1); }; | tCLOSERS operators_def tCLOSERS { /* Several, comma separated, between () */ $$ = $2; } ; exception_def_opt: /* Nothing */ { $$ = words_new ("Exceptions: Strings", "Exceptions: Regexps", 1, 1); } | tEXCEPTIONS operators_def tEXCEPTIONS { $$ = $2; }; %% void yyerror (const char *msg) { error_at_line (1, 0, sshfilename, sshlineno, msg); } /* * FIXME: Cover the other relevant types */ static void yyprint (FILE *file, int type, YYSTYPE value) { switch (type) { case tBACK_REF: fprintf (file, " \\%d", value.integer); break; case tFFLAGS: putc (' ', file); fflag_self_print (value.fflags, file); break; case tFACE: fprintf (file, " %s", face_to_string (value.face)); break; case tREGEX: fprintf (file, " /%s/", value.pattern->pattern); break; case tSTRING: fprintf (file, " \"%s\"", value.string); break; } } struct style_sheet * parse_style_sheet (const char * filename) { int res; sshfilename = filename; sshlineno = 1; sshin = xrfopen (sshfilename); message (msg_file | msg_sheet | msg_parse, (stderr, "Parsing file `%s'\n", sshfilename)); sshlex_initialize (); if (msg_test (msg_parse)) yydebug = true; res = yyparse (); /* FIXME: test the result of parsing */ if (msg_test (msg_sheet)) { fprintf (stderr, "---------- Right after parsing of %s\n", parsed_style_sheet->key); style_sheet_self_print (parsed_style_sheet, stderr); fprintf (stderr, "---------- End of after parsing of %s\n", parsed_style_sheet->key); } fclose (sshin); return parsed_style_sheet; }