%{

/*
    grammar used to build the parser,	
    Copyright (C) 1991 Raphael Cerf (e-mail: cerf@ens.ens.fr)

    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)
	} }
}
