%{

/*
   grammar used to build the parser,
   Copyright (C) 1991 Raphael Cerf (e-mail: [email protected])

   This file is part of xetal.

   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 1, 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; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <search.h>
#include "proto.h"
#include "glbl.h"
#include "str.h"
#include "stack.h"

/* output macros */

/* flag for output */
#define out (mode!=-1 || d_macrodef) && (mode!=1 || d_math) && d_ok

/* deleted text */
#define noprint(x) { int i; char *p=S; \
                 sprintf x ; \
                 c_skipped+=strlen(S); \
                 if (do_fill) { \
                 while ((*p!=(char)NULL)) { \
                       if (*p=='\n') ; \
                       else if (*p=='\t') ; \
                       else { \
                               *p=c_fill; \
                       } \
                       p++; \
                 } \
                 fprintf(stdout, "%s", S); \
                 } \
               }

/* displayed text */
#define doprint(x) { sprintf x; fprintf(stdout, "%s", S); }

/* general output macro */
#define print(x, v) { if (out && v) doprint(x) \
               else noprint(x) }

%}

/*
* shift-reduces expected
*/

/*%expect 108*/

/*
* stack type
*/

%union {
       char *y_str;    /* string */
       char y_chr;     /* character */
       int y_int;      /* integer */
}

/*
* terminal symbols
*/

%token          SLASH
%token          NULL_COMMAND
%token          FFEED OTHER
%token <y_chr>  '{' '}' '[' ']'
%token <y_str>  BLS BLSNL
%token <y_chr>  LETTER DIGIT
%token <y_chr>  SLASHED
%token <y_str>  BGIN END INCLUSION COMMAND UNKNOWN COMMENT
%token <y_str>  VERB VERBATIM
%token <y_str>  DEF CTLSEQ0 CTLSEQ1 CTLSEQ2 CTLSEQ3 CATCODE
%token <y_str>  LATEX_DEF LATEX_DEFENV
%token <y_str>  READ SETBOX FONT
%token <y_str>  ASSIGN
%token <y_str>  LATEX_PAR
%token <y_str>  INT_PAR DIMEN_PAR GLUE_PAR MUGLUE_PAR TOKEN_PAR
%token <y_str>  REGISTER
%token <y_str>  OPERATION
%token <y_str>  BOX
%token <y_str>  DOLLAR DDOLLAR OPARE CPARE OBRAC CBRAC
%token <y_str>  ANYTHING ANY_NOT_SLASH
%token <y_str>  NOMBRE LETTRES CARACTERES REEL FILENAME
%token <y_str>  ACCENT
%token <y_str>  H1O1O_COMMAND
%token <y_str>  H1O1_COMMAND H1O2_COMMAND
%token <y_str>  H1OO1_COMMAND
%token <y_str>  H1O_COMMAND H2O_COMMAND
%token <y_str>  H2_COMMAND H3_COMMAND
%token <y_str>  HO1_COMMAND HO2_COMMAND
%token <y_str>  HOO1_COMMAND
%token <y_str>  HO_COMMAND
%token <y_str>  INCLUDEONLY
%token <y_str>  INDENV SLOPENV MINIENV PICTENV
%token <y_str>  K1_COMMAND H1_COMMAND
%token <y_str>  KO1_COMMAND
%token <y_str>  K_COMMAND H_COMMAND
%token <y_str>  LISTENV FIGENV TABBENV TABUENV BIBLENV
%token <y_str>  MATHENV TEXTENV

/*
* non terminal symbols
*/

%type <y_chr>   r_accent
%type <y_int>   def_mode
%type <y_str>   environnement
%type <y_str>   environnement_name
%type <y_str>   lettres, fln
%type <y_str>   filename
%type <y_str>   math_environnement
%type <y_int>   math_mode
%type <y_chr>   operator
%type <y_chr>   signe_ponctuation
%type <y_chr>   special
%type <y_int>   text_mode

%%

tex
       : start lignes nl
       | start lignes end start_END lignes start_N
       ;

start
       :
               { start_file(); }
       ;

lignes
       :
       | entites
       ;

entites
       : entite
       | entites entite
       ;

entite
       : anything
       | any_not_slash
       | mot
       | phrase
       | lettre
       | ponctuation
       | macro
       | assignment
       | catcode_def
       | commande
       | commentaire
       | groupe
       | separateur
       | reste
       | error
               { empty_S_stack(); mode=0; start_N(); }
       ;

anything
       : ANYTHING
               {
               print((S, "%s", $1), d_anything);
               }
       ;

any_not_slash
       : ANY_NOT_SLASH
               {
               print_seq($1, d_mot && (mode!=0 || d_text));
               }
       ;

mot
       : LETTRES
               {
               print((S, "%s", $1), d_mot && (mode!=0 || d_text))
               }
       ;

phrase
       : CARACTERES
               {
               print((S, "%s", $1), d_mot && (mode!=0 || d_text))
               }
       ;

lettre
       : LETTER
               {
               print((S, "%c", $1), d_mot && (mode!=0 || d_text))
               }
       ;

ponctuation
       : signe_ponctuation
               {
               print((S, "%c", $1), d_punct && (mode!=0 || d_text))
               }
       ;

signe_ponctuation
       : '.' { $$='.'; }
       | ',' { $$=','; }
       | ';' { $$=';'; }
       | ':' { $$=':'; }
       | '!' { $$='!'; }
       | '?' { $$='?'; }
       | '`' { $$='`'; }
       | '\'' { $$='\''; }
       | SLASH { $$='\\'; }
       | '\"' { $$='\"'; }
       ;

macro
       : start_SQ2 def o_spnl ctlseq o_spnl start_N def_mode oa lignes ca
               { mode=$7; }
       | latex_def start_ARG o_spnl hg_argument def_mode ho_argument hg_argument start_N
               { mode=$5; }
       | latex_defenv start_ARG o_spnl hg_argument def_mode ho_argument hg_argument o_spnl hg_argument start_N
               { mode=$5; }
       ;

start_SQ2
       :
               { start_SQ2(); }
       ;

def
       : DEF
               { print((S, "%s", $1), d_command) }
       ;

o_spnl
       :
       | spaces
       | spaces_newline
       ;

spaces
       : BLS
               { if (d_bls) print((S, "%s", $1), d_bl)
               else print((S, "%s", " "), d_bl) }
       ;

spaces_newline
       : BLSNL
               { print_blnl($1); }
       ;

ctlseq
       : CTLSEQ0
               { print_seq($1, d_command); }
       | CTLSEQ1
               { print_seq($1, d_command); }
       | CTLSEQ2
               { print_seq($1, d_command); }
       | CTLSEQ3
               { print_seq($1, d_command); }
       | slashed o_ctlseq
       ;

slashed
       : SLASHED
               { if (!do_fill_slash) print((S, "%c", $1), d_slashed)
               else print((S, "%c%c", '\\', $1), d_slashed) }
       ;

o_ctlseq
       :
       | ctlseq
       ;

start_N
       :
               { start_N(); }
       ;

def_mode
       :
               { $$=mode; mode=-1; }

oa
       : '{'
               { if (do_fill_acco) print((S, "%c", $1), d_command) }
       ;

ca
       : '}'
               { if (do_fill_acco) print((S, "%c", $1), d_command) }
       ;

latex_def
       : LATEX_DEF
               { print((S, "%s", $1), d_command) }
       ;

start_ARG
       :
               { start_ARG(); }
       ;

hg_argument
       : d_no entite d_yes
       ;

ho_argument
       :  o_spnl
       |  o_spnl ob start_O_ARG d_no lignes d_yes cbrac start_N o_spnl
       ;

start_O_ARG
       :
               { start_O_ARG(); }
       ;

ob
       : '['
               { print((S, "%c", '['), d_command) }
       ;

d_no
       :
               { d_ok=0; }
       ;

d_yes
       :
               { d_ok=1; }
       ;

cbrac
       : CBRAC
               { print((S, "%c", ']'), d_command) }
       ;

latex_defenv
       : LATEX_DEFENV
               { print((S, "%s", $1), d_command) }
       ;

assignment
       : variable start_CAR def_mode o_spnl equal o_spnl value start_N
               { mode=$3; }
       | variable start_CAR def_mode o_spnl start_N
               { mode=$3; }
       | operation o_spnl variable start_CAR def_mode o_by value start_N
               { mode=$5; }
       | start_SQ1 read o_spnl ctlseq start_N
       | start_SQ1 setbox o_spnl ctlseq start_N o_spnl o_equal o_spnl entite
       | start_SQ1 font o_spnl ctlseq start_N o_spnl equal o_spnl filename
               { str_destroy($9); }
       | start_SQ3 assign o_spnl o_ctlseq start_N o_spnl def_mode o_equal entite
               { mode=$7; }
       ;

variable
       : LATEX_PAR
               { print((S, "%s", $1), d_command) }
       | INT_PAR
               { print((S, "%s", $1), d_command) }
       | DIMEN_PAR
               { print((S, "%s", $1), d_command) }
       | GLUE_PAR
               { print((S, "%s", $1), d_command) }
       | MUGLUE_PAR
               { print((S, "%s", $1), d_command) }
       | TOKEN_PAR
               { print((S, "%s", $1), d_command) }
       | start_NB register nombre_unknown start_N
       ;

start_NB
       :
               { start_NB(); }
       ;

register
       : REGISTER
               { print((S, "%s", $1), d_command) }
       ;

nombre_unknown
       : nombre
       | unknown_command
       ;

nombre
       : NOMBRE
               { print((S, "%s", $1), d_command) }
       ;

unknown_command
       : UNKNOWN
               { print((S, "%s", $1), d_command) }
       ;

start_CAR
       :
               { start_CAR(); }
       ;

equal
       : '='
               { print((S, "%s", "="), d_command) }
       ;

value
       : entite
       ;

operation
       : OPERATION
               { print((S, "%s", $1), d_command) }
       ;

o_by
       : o_spnl
       | o_spnl by o_spnl
       ;

by
       : LETTRES
               {
               if (strcmp($1, "by")!=0) {
                       fprintf(stderr, "by expected\n");
               }
               print((S, "%s", $1), d_command)
               }
       ;

start_SQ1
       :
               { start_SQ1(); }
       ;

read
       : READ
               { print((S, "%s", $1), d_command) }
       ;

setbox
       : SETBOX
               { print((S, "%s", $1), d_command) }
       ;

font
       : FONT
               { print((S, "%s", $1), d_command) }
       ;

start_SQ3
       :
               { start_SQ3(); }
       ;

assign
       : ASSIGN
               { print((S, "%s", $1), d_command) }
       ;

o_equal
       : o_spnl
       | o_spnl equal o_spnl
       ;

catcode_def
       : start_SQ0 catcode ctlseq start_N def_mode o_spnl equal o_spnl value
               { mode=$5; }
       ;

start_SQ0
       :
               { start_SQ0(); }
       ;

catcode
       : CATCODE
               { print((S, "%s", $1), d_command) }
       ;

commande
       : begin_end_section
       | verb
       | math
       | box
       | accent
       | s_command
       | k_command
       | h_command
       | ho_command start_ARG ho_argument start_N
       | k1_command start_ARG o_spnl kg_argument start_N
       | ko1_command start_ARG ho_argument kg_argument start_N
       | h1_command start_ARG o_spnl hg_argument start_N
       | h2_command start_ARG o_spnl hg_argument o_spnl hg_argument start_N
       | h3_command start_ARG o_spnl hg_argument o_spnl hg_argument o_spnl hg_argument start_N
       | ho1_command start_ARG ho_argument hg_argument start_N
       | ho2_command start_ARG ho_argument hg_argument o_spnl hg_argument start_N
       | h1o1_command start_ARG o_spnl hg_argument ho_argument hg_argument start_N
       | h1o2_command start_ARG o_spnl hg_argument ho_argument hg_argument o_spnl hg_argument start_N
       | h1o_command start_ARG o_spnl hg_argument ho_argument  start_N
       | h2o_command start_ARG o_spnl hg_argument o_spnl hg_argument ho_argument start_N
       | hoo1_command start_ARG ho_argument ho_argument hg_argument start_N
       | h1oo1_command start_ARG o_spnl hg_argument ho_argument ho_argument hg_argument  start_N
       | h1o1o_command start_ARG o_spnl hg_argument ho_argument hg_argument ho_argument start_N
       | inclusion
       | unknown_command
       ;

begin_end_section
       : bgin start_BE_ARG o_spnl oa environnement ca lignes end start_BE_ARG o_spnl oa environnement ca
               { if (strcmp($5, $12)!=0) {
               fprintf(stderr, "%s | %s not matching in begin_end\n", $5, $12);
                       exit(2);
               }
               str_destroy($5);
               str_destroy($12);
               }
       ;

bgin
       : BGIN
               { print((S, "%s", $1), d_command) }
       ;

start_BE_ARG
       :
               { start_BE_ARG(); }
       ;

environnement
       : environnement_name
               { $$=strdup($1);
               print((S, "%s", $1), d_command) }
       ;

environnement_name
       : TEXTENV
       | LISTENV
       | FIGENV
       | TABBENV
       | TABUENV
       | BIBLENV
       | INDENV
       | SLOPENV
       | MINIENV
       | PICTENV
       | VERBATIM
       | UNKNOWN
       ;

end
       : END
               { print((S, "%s", $1), d_command) }
       ;

verb
       : VERB
               { char *p, sch, o;
                 int l;
                 p=$1+5;/* suppress \verb in string p */
                 if (*p=='*') p++;
                 sch=*p;p++;o=*p;*p=(char)NULL;
                 print((S, "%s", $1), d_command)
                 *p=o;l=strlen(p);
                 *(p+l-1)=(char)NULL;
                 print_seq(p, d_mot && (mode!=0 || d_text));
                 print((S, "%c", sch), d_command)
               }
       ;

math
       : bgin start_BE_ARG o_spnl oa math_environnement ca math_mode entites end start_BE_ARG o_spnl oa math_environnement ca
               { mode=$7;
                 if (strcmp($5, $13)!=0) {
               fprintf(stderr, "%s | %s not matching in begin_end\n", $5, $13);
                       exit(2);
               }
               str_destroy($5);
               str_destroy($13);
               }
       | math_mode dollar entites dollar
               { mode=$1; }
       | math_mode ddollar entites ddollar
               { mode=$1; }
       | math_mode opare entites cpare
               { mode=$1; }
       | math_mode obrac entites cbrac
               { mode=$1; }
       ;

math_environnement
       : MATHENV
               { $$=strdup($1);
               print((S, "%s", $1), d_command) }
       ;

math_mode :
               { $$=mode; mode=1; n_formule++; }
       ;

dollar
       : DOLLAR
               { print((S, "%c", '$'), d_command) }
       ;

ddollar
       : DDOLLAR
               { print((S, "%c%c", '$', '$'), d_command) }
       ;

opare
       : OPARE
               { print((S, "%c", '('), d_command) }
       ;

cpare
       : CPARE
               { print((S, "%c", ')'), d_command) }
       ;

obrac
       : OBRAC
               { print((S, "%c", '['), d_command) }
       ;

box
       : start_SQ1 box_name text_mode start_N groupe
               { mode=$3; }
       | start_SQ1 box_name o_spnl ctlseq start_N text_mode groupe
               { mode=$6; }
       ;

box_name
       : BOX
               { print((S, "%s", $1), d_command) }
       ;

text_mode
       : o_spnl
               { $$=mode; if (mode!=-1) mode=0; }
       ;

groupe
       : oa lignes ca
       ;

accent
       : r_accent o_spnl
               {
               if (d_accent) {
               if (s_accent==(char)NULL) {
               print((S, "%c", $1), (mode!=0 || d_text))
               } else {
       print((S, "%c%c", s_accent, $1), (mode!=0 || d_text))
               } } }
       ;

r_accent
       : ACCENT
               { $$=$1[1]; }

s_command
       : COMMAND
               { print((S, "%s", $1), d_command) }
       ;

k_command
       : K_COMMAND
                { print((S, "%s", $1), d_command) }
       ;

h_command
       : H_COMMAND
               { print((S, "%s", $1), d_command) }
       ;

ho_command
       : HO_COMMAND
                { print((S, "%s", $1), d_command) }
       ;

k1_command
       : K1_COMMAND
                { print((S, "%s", $1), d_command) }
       ;

kg_argument
       : entite
       ;

ko1_command
       : KO1_COMMAND
                { print((S, "%s", $1), d_command) }
       ;

h1_command
       : H1_COMMAND
               { print((S, "%s", $1), d_command) }
       ;

h2_command
       : H2_COMMAND
                { print((S, "%s", $1), d_command) }
       ;

h3_command
       : H3_COMMAND
                { print((S, "%s", $1), d_command) }
       ;

ho1_command
       : HO1_COMMAND
                { print((S, "%s", $1), d_command) }
       ;

ho2_command
       : HO2_COMMAND
                { print((S, "%s", $1), d_command) }
       ;

h1o1_command
       : H1O1_COMMAND
                { print((S, "%s", $1), d_command) }
       ;

h1o2_command
       : H1O2_COMMAND
                { print((S, "%s", $1), d_command) }
       ;

h1o_command
       : H1O_COMMAND
                { print((S, "%s", $1), d_command) }

h2o_command
       : H2O_COMMAND
                { print((S, "%s", $1), d_command) }
       ;

hoo1_command
       : HOO1_COMMAND
                { print((S, "%s", $1), d_command) }
       ;

h1oo1_command
       : H1OO1_COMMAND
                { print((S, "%s", $1), d_command) }
       ;

h1o1o_command
       : H1O1O_COMMAND
                { print((S, "%s", $1), d_command) }
       ;

inclusion
       : INCLUDEONLY start_FLN filename start_N
               { char *p=$3;int i, old_n_ionly;
               if (n_ionly==-1) n_ionly=0;
               old_n_ionly=n_ionly;
               if ((if_name[n_ionly++]=strtok(p, ","))!=(char *)NULL) {
                       p=(char *)NULL;
       while ((if_name[n_ionly++]=strtok(p, ","))!=(char *)NULL);
                       n_ionly--;
               for (i=old_n_ionly; i<n_ionly; i++) {
                       if_name[i]=strdup(if_name[i]);
               } }
               str_destroy($3);
               }
       | inclusion_command start_FLN filename start_N
               { int i;
               if (do_incl) {

               FILE *fd=(FILE *)NULL;
               char i_name[W_LGTH];

               strcpy(i_name, $3);
               for (i=0; i<n_ionly; i++) {
                       if (strcmp(i_name,if_name[i])==0) break;
               }
               if (i==n_ionly) {
                       if (d_warning)
                       fprintf(stderr, "%s not included\n", i_name);
               } else {
                       strcat(i_name, ".tex");
                       if ((fd=fopen($3, "r"))==(FILE *)NULL) {
                               if ((fd=fopen(i_name, "r"))==(FILE *)NULL) {
                       if (d_warning)
                       fprintf(stderr, "Can't read file: %s\n", $3);
                               }
               }       }
               if (fd!=(FILE *)NULL) {
                       fich_t n_fich;

                       n_fich.name=strdup($3);
                       n_fich.fd=fd;
                       n_fich.line=lineno;
#ifdef FLEX_SCANNER
                       n_fich.last_string=(char *)Input();
#else
                       n_fich.last_char=(char)Input();
#endif
                       f_push(&n_fich);
                       *f_in=fd;
                       f_name=n_fich.name;
                       lineno=0L;
                       if (d_fname==0) d_fname=-1;
                       if (d_nl) doprint((S, "%s", "\n"))
                       start_file();
                       inclusion_file();
               } }
               str_destroy($3);
               }
       ;

start_FLN
       :
               { start_FLN(); }
       ;

filename
       : oa lettres ca
               { $$=$2; }
       | oa fln ca
               { $$=$2; }
       | o_spnl lettres
               { $$=$2; }
       | o_spnl fln
               { $$=$2; }
       | o_spnl UNKNOWN
               { $$=strdup($2); }
       ;

lettres
       : LETTRES
               { $$=strdup($1); }
       ;

fln
       : FILENAME
               { $$=strdup($1); }
       ;

inclusion_command
       : INCLUSION
               { print((S, "%s", $1), d_command) }
       ;

commentaire
       : COMMENT
               {
               $1[strlen($1)-1]=(char)NULL;
               print((S, "%s", $1), d_comment)
               print_nl();
               }
       ;

separateur
       : spaces
       | spaces_newline
       ;

reste
       : operator
               { print((S, "%c", $1), d_operator) }
       | special
               { print((S, "%c", $1), d_special) }
       | slashed
       | other
       ;

operator
       : '+' { $$='+'; }
       | '-' { $$='-'; }
       | '*' { $$='*'; }
       | '/' { $$='/'; }
       | '=' { $$='='; }
       | '|' { $$='|'; }
       | '<' { $$='<'; }
       | '>' { $$='>'; }
       | '(' { $$='('; }
       | ')' { $$=')'; }
       | '@' { $$='@'; }
       | '[' { $$='['; }
       | ']' { $$=']'; }
       ;

special
       : '#' { $$='#'; }
       | '%' { $$='%'; }
       | '&' { $$='&'; }
       | '\\' { $$='\\'; }
       | '^' { $$='^'; }
       | '_' { $$='_'; }
       | '~' { $$='~'; }
       ;

other
       : OTHER
               { print((S, "\\ASCII:%d ", yylval.y_chr), d_warning) }
       | FFEED
               { print((S, "%c", 12), d_mot && (mode!=0 || d_text)) }
       ;

nl
       :
               { if (d_nl) doprint((S, "%s", "\n")) }
       ;

start_END
       :
               { start_END(); }
       ;
%%

/*
* yyerror function for LEX
*/
yyerror(s)
char *s;
{
       if (d_error) {

       if ((strcmp("", f_name)!=0))
               fprintf(stderr, "[%s:%d] %s\n", f_name, lineno+1, s);
       else fprintf(stderr, "[%d] %s", lineno+1, s);

       }
}

/*
* display new file name
*/
int start_file()
{
       if (n_nl && ((lineno+1) % N == 0)) {
       if ((strcmp("", f_name)==0) || (d_fname==0))
       doprint((S, "[%d] ", lineno+1))
               else {
       if (d_fname==-1) d_fname=0;
       doprint((S, "[%s:%d] ", f_name, lineno+1))
       } } else if (n_nl) {
       if (d_fname==-1) d_fname=0;
       if (strcmp("", f_name)!=0) doprint((S, "[%s] ", f_name))
       }
}

/*
* ouput a \n with line number if necessary
*/
print_nl()
{
       if (d_nl) doprint((S, "%s", "\n"))
       if (n_nl && ((lineno+2) % N == 0)) {
       if ((strcmp("", f_name)==0) || (d_fname==0))
       doprint((S, "[%d] ", lineno+2))
               else {
       if (d_fname==-1) d_fname=0;
       doprint((S, "[%s:%d] ", f_name, lineno+2))
       } }
       lineno++;
}

/*
* output a sequence containing blanks
* and possibly a \n
*/
print_blnl(s)
char *s;
{
       char *p=strchr(s, '\n');

       *p=(char)NULL;
       if (strlen(s)>0)
       if (d_bls) print((S, "%s", s), d_bl)
       else print((S, "%s", " "), d_bl)
       *p='\n';
       if (d_nl) doprint((S, "%s", "\n"))
       if (n_nl && ((lineno+2) % N == 0)) {
       if ((strcmp("", f_name)==0) || (d_fname==0)) {
               doprint((S, "[%d] ", lineno+2))
       } else {
       if (d_fname==-1) d_fname=0;
       doprint((S, "[%s:%d] ", f_name, lineno+2))
       } }
       if (*(p+1)!=(char)NULL)
       if (d_bls) print((S, "%s", p+1), d_bl)
       else print((S, "%s", " "), d_bl)
       lineno++;
}

/*
* output a sequence of characters
* eventually containing several \n
*/
print_seq(p, flg)
char *p;
int flg;
{
       char *q;

       q=p;
       while (*q!=(char)NULL) {

       while (*q!='\n' && *q!=(char)NULL) q++;
       if (*q=='\n') {
               *q=(char)NULL;
               if (strlen(p)>0) print((S, "%s", p), flg)
               print_nl();
               p=++q;
       } else {
               if (strlen(p)>0) print((S, "%s", p), flg)
       } }
}