%{
/*
    mmorph, MULTEXT morphology tool
    Version 2.3, October 1995
    Copyright (c) 1994,1995 ISSCO/SUISSETRA, Geneva, Switzerland
    Dominique Petitpierre, <petitp@divsun.unige.ch>
*/
#include "user.h"
#include "parse.h"

s_symbol	*defined_symbol;
static s_symbol	*largest_type_sym;
static t_card		 largest_type_card;
e_symbol_kind    defined_kind;
static s_symbol_set	*value_set;
static s_symbol_set	*variable_set;
static t_boolean	 variable_allowed;
static s_symbol	*current_attribute;
static s_type		*current_type;
static s_rule		*current_rule;
static s_tfs		*current_tfs;
static s_tfs		*common_tfs;
static s_chain		*current_chain;
static s_chain		*affix_tree_chain;
static int		 var_map_card;
static t_letter 	 *current_string;
static t_letter	 *current_base_form;
e_symbol_kind    current_letter_kind;
static s_pair		*current_pair;
static t_flag		 class_flags;
static t_flag		 current_pair_flags;
static int		 current_pair_width;
static s_bitmap	 *current_class;
static s_symbol	  fake_symbol;	/* used for pointing defined_symbol to */
static s_spell_instance *current_spell;
static s_bitmap	 *current_lexical_set;
static int		 current_pattern_length;
static s_chain		 *current_pattern;
static t_boolean	 within_focus;
static t_boolean	 has_concat_pos;

#define LETTER_STRING_SIZE_MAX	128
static t_letter	letter_string[LETTER_STRING_SIZE_MAX]; /* temp storage */
static int		letter_string_size; /* size of data in letter_string */
%}

%union {
    t_str	string;
    s_symbol	*symbol;
    s_chain	*chain;
    t_value	value;
    s_tfs	*tfs;
    s_var_map	*var_map;
    t_letter	*letter_string;
    s_pair	*pair;
    }

%token
	ANY
	BAR
	BIARROW
	WORDBOUNDARY
	CONTEXTBOUNDARY
	CONCATBOUNDARY
	MORPHEMEBOUNDARY
	DOLLAR
	EQUAL
	LANGLE
	LARROW
	LBRA
	NOTEQUAL
	RANGLE
	RARROW
	RBRA
	COERCEARROW
	COLON
	SLASH

	ALPHABETS
	ATTRIBUTES
	CLASSES
	GRAMMAR	  
	LEXICON	  
	PAIRS  
	SPELLING  
	TYPES	    


%token <string>
	DNAME
	NAME
	STRING
	SNAME
	TNAME

%token <letter_string>
	LSTRING

%type <symbol>
	AttNew
	AttName
	TypeNew
	PairName

%type <value>
	ValName
	ValSet
	ValDisjRestSTAR
	ValDisjRest
	ValDisj

%type <tfs>
	TypeName
	Tfs
	VarTfs
	Affix

%type <chain>
	AttValSTAR
	AttSpec
	ConstraintSTAR

%type <var_map>
	AttVal
	ValSpec
	VarName

%type <letter_string>
	Sequence
	SurfSequence
	LexSequence
	AffixString

%type <pair>
	PairPattern

%start	Start

%%

Start	: AlphabetDecl AttDecl TypeDecl GramDecl ClassDeclOPT PairDeclOPT
	  SpellDeclOPT LexDeclSTAR
	{

	}
	;

AttDecl	: AttHead AttDefPLUS
	{
	    defined_symbol=NULL; /* end of definitions */
	    make_symbol_reference(&symbol_set[Attribute]);
	    if (debug & DEBUG_INIT)
		print_symbols_by_ref(&symbol_set[Attribute]);
	}
	;

AttHead	: ATTRIBUTES
	{
	    defined_kind=Attribute;
	    crc32file_addn(&crc, (unsigned char *) "@Attributes ",
			   sizeof("@Attributes "));
	}
	;

AttDef	: AttNew COLON  ValNewPLUS
	{
	    make_symbol_reference(value_set);
	    link_attribute(value_set,$1);
	    defined_symbol=NULL; /* for error message */
	}
	;

AttNew	: DNAME 
	{
	    $$=*new_symbol($1,&symbol_set[Attribute]);
	    value_set=&$$->data->attribute.value_set;
	    defined_symbol=$$;
	    crc32file_addn(&crc, (unsigned char *) $1, (int) strlen($1));
	    crc32file_add(&crc, (unsigned char) ':');
	}
	;

ValNew	: NAME
	{
	    (void) new_symbol($1,value_set);
	    crc32file_addn(&crc, (unsigned char *) $1, (int) strlen($1));
	    crc32file_add(&crc, (unsigned char) ' ');
	}
	;

TypeDecl	: TypeHead TypeDefPLUS
	{
	    defined_symbol=NULL; /* end of definitions */
	    make_symbol_reference(&symbol_set[Type]);
	    largest_type=largest_type_sym->ordinal;
	    if (debug & DEBUG_INIT)
		print_symbols_by_ref(&symbol_set[Type]);
	    /* TODO: move this to end of spelling rules
               when looking up unknown words works.
	     */
	    if (db_operation & Lookup) {
		crc32file_hash(&crc, crc_hash);
		parsing=FALSE; /* change format of fatal errors */
		tfs_table_init(db_operation, gram_file_name, db_file_name,
			       crc_hash);
		parsing=TRUE;
	    }
	    if (db_operation == Lookup)
		YYACCEPT; /* skip the rest of the file */
	}
	;

TypeHead	: TYPES
	{
	    defined_kind=Type;
	    largest_type_card = 0;
	    crc32file_addn(&crc, (unsigned char *) "@Types ",
			   sizeof("@Types "));
	}
	;

TypeDef	: TypeNew COLON  AttNamePLUS NoProjectAttOPTL
	{
	    if (current_type->project_card==0)
		current_type->project_card=current_type->card;
	    if (current_type->card > largest_type_card) {
		largest_type_sym = defined_symbol;
		largest_type_card = current_type->card;
	    }
	    current_type->attribute_ref=
		  (s_symbol **) new_ref((int) current_type->card,
					 map_chain,
					 (t_ptr) current_chain,
					 ref_is_item);
	    free_chain(current_chain);
	    defined_symbol=NULL; /* for error message */
	}
	;

NoProjectAttOPTL : ProjSep  AttNamePLUS
		{
		}
		| /* empty */
		{
		}
		;

ProjSep	: BAR
	{
	    current_type->project_card= current_type->card;
	    crc32file_add(&crc, (unsigned char) '|');
	}

TypeNew	: DNAME 
	{
	    defined_symbol = *new_symbol($1,&symbol_set[Type]);
	    current_type = (s_type *) defined_symbol->data;
	    current_chain = NULL;
	    crc32file_addn(&crc, (unsigned char *) $1, (int) strlen($1));
	    crc32file_add(&crc, (unsigned char) ':');
	}
	;

AttName	: NAME
	{
	    $$=*find_symbol($1,&symbol_set[Attribute]);
	    current_attribute=$$;
	    value_set= &(($$)->data->attribute.value_set);
	}
	;

GramDecl	: GramHead RulePLUS
	{
	    defined_symbol=NULL; /* end of definitions */
	    make_symbol_reference(&symbol_set[Rule]);
	    if (debug & DEBUG_INIT)
		print_symbols_by_ref(&symbol_set[Rule]);
	    if (! parse_only)
		prepare_rules();
	}
	;

GramHead	: GRAMMAR
	{
	    defined_kind=Rule;
	    goal_card = 0;
	    /* tell tokenizer to interpret strings as lexical strings */
	    current_letter_kind=Lexical_Letter;
	    current_letter_map=lexical_letter_map;
	}
	;

Rule	: RuleNew COLON  RuleBody
	{
	    defined_symbol=NULL; /* for error message */
	}
	;

RuleBody    : VarTfs Rewrite Rhs
	    {
		make_symbol_reference(variable_set);
		current_rule->instance->lhs=$1;
		adjust_var_map(variable_set->ref,current_rule->instance);
	    }
	    | Affix
	    {
		variable_set = NULL; /* no variable to handle */
		current_rule->instance->rule_kind = Lexical;
		current_rule->instance->lhs=$1;
	    }
	    | Tfs
	    {
		current_rule->instance->rule_kind = Goal;
		goal_card++;
		if ($1->var_map != NULL) /* TODO catch this earlier */
		    fatal_error("variables not allowed in goal rule");
		current_rule->instance->first=$1;
		current_rule->instance->lex= (t_letter *) "";/* empty string */
		current_rule->instance->lhs=new_tfs($1->type);
		fill_tfs_with_variables(current_rule->instance->lhs);
		ident_var_map(current_rule->instance->lhs);
	    }
	    ;

Rewrite	    : LARROW
	    {

	    }
	    ;

VarTfs	    : Tfs
	    {
		$$=$1;
	    }
	    ;

AffixString : LSTRING
	    {
		variable_allowed=FALSE;
		$$=$1;
	    }
	    ;

RuleNew	: DNAME 
	{
	    defined_symbol = *new_symbol($1,&symbol_set[Rule]);
	    current_rule = (s_rule *)defined_symbol->data;
	    variable_set = &(current_rule->variable_set);
	    variable_allowed=TRUE;
	}
	;

Tfs	: TypeName AttSpec
	{
	    $$ = $1; /* that's the tfs */
	    if ($2 == NULL)
		$$->var_map = NULL;
	    else {
		$$->var_map = (s_var_map **) new_ref(var_map_card,
						     map_chain,
						     (t_ptr) $2,
						     ref_is_item);
		free_chain($2);
	    }
	}
	;

TypeName	: TNAME 
	{
	    s_symbol *symbol;

	    symbol = *find_symbol($1,&symbol_set[Type]);
	    current_type = (s_type *) symbol->data;
	    $$=new_tfs(symbol->ordinal);
	    current_tfs=$$;
	    /* fill the type feature structure with full sets */
	    fill_tfs_with_variables($$);
	}
	;

AttSpec	: LBRA AttValSTAR RBRA 
	{
	    $$=$2;
	}
	;

AttVal	: AttName ValSpec
	{
	    $$=$2;
	}
	;

ValSpec	: ValSetSpec
	{
	    $$=NULL;
	}
	| EQUAL DOLLAR VarName
	{
	    $$=$3;
	}
	| EQUAL DOLLAR VarName ValSetSpec
	{
	    $$=$3;
	}
	;

VarName : NAME
	{
	    int pos;
	    s_symbol *variable;

	    pos = reference_position((t_ptr *) current_type->attribute_ref,
			             (t_ptr) current_attribute);
	    if ( pos < 0 )
		fatal_error(
		   "attribute \"%s\" not declared for type \"%s\"",
		    current_attribute->name,
		    symbol_set[Type].ref[current_tfs->type]->name);
	    if (! variable_allowed)
		fatal_error(
		    "a variable is not allowed in this context");
	    variable = *new_symbol($1,variable_set);
	    if (variable->data->variable.attribute_link == NULL)
		variable->data->variable.attribute_link=current_attribute;
	    else
		if (variable->data->variable.attribute_link
			!= current_attribute)
		    fatal_error("variable $%s %s \"%s\" and \"%s\"",
			$1,
			"is used for incompatible attributes:",
			variable->data->variable.attribute_link->name,
			current_attribute->name);
	    MY_MALLOC($$,s_var_map);
	    $$->foreign_index = (t_index) variable->ordinal;
	    $$->local_index = (t_index) pos;
	}
	;

ValSetSpec : ValSet
	{
	    int pos;

	    pos = reference_position((t_ptr *) current_type->attribute_ref,
			             (t_ptr) current_attribute);
	    if ( pos < 0 )
		fatal_error(
		   "attribute \"%s\" not declared for type \"%s\"",
		    current_attribute->name,
		    symbol_set[Type].ref[current_tfs->type]->name);
	    current_tfs->att_list[pos] &= $1;
	    if (current_tfs->att_list[pos] == NO_BITS)
		fatal_error("value set complement for \"%s\" is empty",
			    current_attribute->name);
	}

ValSet	: NOTEQUAL ValDisj
	{
	    $$ = ~ ($2);
	    if ($$ == NO_BITS)
	        /* occurs only when there is MAXVAL values for attribute */
		fatal_error("value set complement is empty");
	}
	| EQUAL ValDisj 
	{
	    $$ = $2;
	}
	;

ValDisj	: ValName ValDisjRestSTAR
	{
	    $$ = ($1 | $2);
	}
	;

ValName	: NAME 
	{
	    
	    $$ = BIT_N((*find_symbol($1,value_set))->ordinal);
	}
	;

ValDisjRest	: BAR ValName 
	{
	    $$ = $2;
	}
	;

Rhs	: VarTfs
	{
	    current_rule->instance->rule_kind=Unary;
	    current_rule->instance->first=$1;
	    current_rule->instance->second=NULL;
	}
	| VarTfs VarTfs
	{
	    current_rule->instance->rule_kind=Binary;
	    current_rule->instance->first=$1;
	    current_rule->instance->second=$2;
	}
	| Affix VarTfs
	{
	    current_rule->instance->rule_kind=Prefix;
	    current_rule->instance->first=$2;
	    current_rule->instance->second=$1;
	}
	| VarTfs Affix
	{
	    current_rule->instance->rule_kind=Suffix;
	    current_rule->instance->first=$1;
	    current_rule->instance->second=$2;
	}
	;

Affix	:   AffixString Tfs 
	{
	    /* affix tfs cannot have variables; see adjust_var_map() */
	    current_rule->instance->lex=$1;
	    current_rule->instance->lex_length=strlen((char *) $1);
	    $$=$2;
	    variable_allowed = TRUE; /* useful only for prefix */
	}

AttDefPLUS	: AttDefPLUS AttDef
	{

	}
	| AttDef
	{

	}
	;

ValNewPLUS	: ValNewPLUS ValNew
	{

	}
	| ValNew
	{

	}
	;

TypeDefPLUS	: TypeDefPLUS TypeDef
	{

	}
	| TypeDef
	{

	}
	;

RulePLUS	: RulePLUS Rule
	{

	}
	| Rule
	{

	}
	;

AttValSTAR	: AttValSTAR AttVal
	{
	    var_map_card++;
	    if ($2 != NULL)
	        $$ = insert_chain_link($1, (t_ptr) $2);
	}
	| /* empty */
	{
	    var_map_card=0;
	    $$=NULL;
	}
	;

ValDisjRestSTAR	: ValDisjRestSTAR ValDisjRest
	{
	    $$ = ( $1 | $2 );
	}
	| /* empty */
	{
	    $$ = 0L;
	}
	;

AttNamePLUS	: AttNamePLUS AttName
	{
	    current_type->card++;
	    current_chain=insert_chain_link(current_chain, (t_ptr) $2);
	    crc32file_addn(&crc, (unsigned char *) $2->name,
			   (int) strlen($2->name));
	    crc32file_add(&crc, (unsigned char) ' ');
	}
	| AttName
	{
	    current_type->card++;
	    current_chain=insert_chain_link(current_chain, (t_ptr) $1);
	    crc32file_addn(&crc, (unsigned char *) $1->name,
			   (int) strlen($1->name));
	    crc32file_add(&crc, (unsigned char) ' ');
	}
	;

SpellDeclOPT : SpellHead SpellDefPLUS 
	{
	    defined_symbol=NULL; /* end of definitions */
	    make_symbol_reference(&symbol_set[Spelling]);
	    if (debug & DEBUG_INIT)
		print_symbols_by_ref(&symbol_set[Spelling]);
	    max_spell_length=max_left_context_length + max_rest_length;
	    if (!parse_only)
		prepare_spell();
	}
	| /* empty */
	{
	}
	;

SpellHead	: SPELLING
	{
	    defined_kind=Spelling;
	    max_left_context_length=0;
	    max_rest_length=0;
	}
	;

SpellDef	: SpellNew  COLON Arrow LeftContext Focus RightContext
		  ConstraintSTAR
	{
	    current_spell->left_context
		= reverse_chain(current_spell->left_context);
	    if (! has_concat_pos)
		current_spell->concat_pos = NO_CONCAT; /* impossible value */
	    defined_symbol=NULL; /* for error message */
	    max_left_context_length 
		= MAX(current_spell->left_context_length,
		      max_left_context_length);
	    max_rest_length 
		= MAX(current_spell->focus_length
		      + current_spell->right_context_length,
		      max_rest_length);
	    current_spell->constraint = $7;
	}
	;

SpellNew	: DNAME 
	{
	    defined_symbol = *new_symbol($1,&symbol_set[Spelling]);
	    current_spell
		= defined_symbol->data->spelling.instance;
	    current_spell->spell_link=defined_symbol;
	    /* next letter will be found in surface alphabet */
	    current_letter_kind=Surface_Letter;
	    current_letter_map=surface_letter_map;
	    current_pair_flags=BOTH_LETTERS; /* for next explicit Pair */
	    has_concat_pos=FALSE;
	    current_spell->concat_pos=0;
	    current_pattern=NULL; /* prepare for left context */
	    current_pattern_length=0;
	    within_focus=FALSE;
	}
	;


SpellDefPLUS	: SpellDefPLUS SpellDef
	{

	}
	| SpellDef
	{

	}
	;

LeftContext : PatternSTAR
	    {
		current_spell->left_context=current_pattern;
		current_spell->left_context_length=current_pattern_length;
		if (has_concat_pos)
		    current_spell->concat_pos -= current_pattern_length;
		current_pattern=NULL; /* prepare for focus */
		current_pattern_length=0;
		within_focus=TRUE;
	    }
	    ;

RightContext : PatternSTAR
	    {
		current_spell->right_context=current_pattern;
		current_spell->right_context_length=current_pattern_length;
	    }
	    ;

Focus	    : CONTEXTBOUNDARY PatternPLUS CONTEXTBOUNDARY
	    {
		current_spell->focus = current_pattern;
		current_spell->focus_length=current_pattern_length;
		if (! has_concat_pos)
		    current_spell->concat_pos += current_pattern_length;
		current_pattern = NULL; /* prepare for right context */
		current_pattern_length=0;
		within_focus=FALSE;
	    }
	    ;

Arrow	: RARROW
	{
	    current_spell->kind= Optional;
	}
	| BIARROW
	{
	    current_spell->kind= Obligatory;
	}
	| COERCEARROW
	{
	    current_spell->kind = Coerce;
	}
	;


ConstraintSTAR	:  ConstraintSTAR Tfs
	{
	    $$ = insert_chain_link($1, (t_ptr) $2);
	    current_spell->constraint_card++;
	}
	| /* empty */
	{
	    current_spell->constraint_card = 0;
	    $$ = NULL;
	}
	;

PatternPLUS : PatternPLUS Pattern
	    {
	    }
	    | Pattern
	    {
	    }
	    ;

PatternSTAR : PatternPLUS
	    {
	    }
	    | /* empty */
	    {
	    }
	    ;

Pattern	    : CONCATBOUNDARY
	    {
		if (has_concat_pos)
		    fatal_error("concatenation position already specified");
		has_concat_pos=TRUE;
		current_spell->concat_pos += current_pattern_length;
		current_pattern
		    = insert_chain_link(current_pattern,
					(t_ptr) morpheme_boundary_pair);
		current_pattern_length++;
	    }
	    | MORPHEMEBOUNDARY
	    {
		current_pattern
		    = insert_chain_link(current_pattern,
					(t_ptr) morpheme_boundary_pair);
		current_pattern_length++;
	    }
	    | WORDBOUNDARY
	    {
		current_pattern
		    = insert_chain_link(current_pattern,
					(t_ptr) word_boundary_pair);
		current_pattern_length++;
	    }
	    | PairPattern
	    {
		current_pattern
		    = insert_chain_link(current_pattern, (t_ptr) $1);
		current_pattern_length += lexical_width($1);
	    }
	    ;


LexDeclSTAR	: LexDeclPLUS
		{
		}
		| /* empty */
		{
		}
		;

LexDeclPLUS	: LexDeclPLUS LexHead LexDefSTAR
	{

	}
	|  FirstLexHead LexDefSTAR
	{
	}
	;

LexHead	: LEXICON
	{
	}
	;

FirstLexHead	: LEXICON
	{
	    defined_kind = Lexicon;
	    variable_set=NULL; /* prevent variables in lexical Tfs */
	    variable_allowed=FALSE;
	    if ((db_operation & Update)) {
	 	/* options mentionned -a */
		augmenting=TRUE;
		initfile(augment_file_name); /* splice in file now */
	    }
	    /* otherwise splice it at the end of this file, cf tokenize.l */
	    /* tell tokenizer to interpret strings as lexical strings */
	    current_letter_kind=Lexical_Letter;
	    current_letter_map=lexical_letter_map;
	    normalize = normalize_flag;
	}
	;

LexDef	: CommonTfs LexicalPLUS
	{
	    free_tfs(common_tfs);
	    if (! parse_only) {
		map_chain(affix_tree_chain, free_affix_tree);
		free_chain(affix_tree_chain);
	    }
	}
	;

CommonTfs : Tfs
	  {
	    common_tfs = $1;
	    if (! parse_only)
		affix_tree_chain = build_affix_trees(common_tfs);
	    else
		if (normalize) {
		    print_out("\t");
		    print_tfs1(outfile, common_tfs, FALSE, FALSE, FALSE);
		}
	  }
	  ;

LexicalPLUS : LexicalPLUS Lexical
	    {
	    }
	    | Lexical
	    {
	    }
	    ;

Lexical	: LexNew BaseFormOPTL
	{
	    parsing=FALSE;
	    if (! parse_only)
		generate_surface(current_string, current_base_form,
			         affix_tree_chain);
	    else
		if (normalize) {
		    print_out("\"");
		    print_string(outfile, current_string);
		    if (current_string != current_base_form
		        && strcmp((char *) current_string,
			          (char *) current_base_form)) {
			print_out("\" = \"");
			print_string(outfile, current_base_form);
		    }
		    print_out("\"\n");
		}
	    parsing=TRUE;
	    if (current_string != current_base_form)
		MY_FREE(current_base_form);
	    MY_FREE(current_string);
	}
	;

BaseFormOPTL :  EQUAL LSTRING
	    {
		/* TODO should not be in the lexical alphabet but user */
		current_base_form = $2;
	    }
	    |
	    {
		current_base_form = current_string;
	    }
	    ;

LexNew	: LSTRING 
	{
	    /* save string */
	    current_string = $1;
	}

LexDefSTAR	: LexDefSTAR LexDef
	{

	}
	| /* empty */
	{

	}
	;

AlphabetDecl : AlphabetHead AlphabetDef
	     {
		defined_symbol=NULL; /* end of definitions */
		allow_string_letters=FALSE;
	     }
	     ;

AlphabetHead : ALPHABETS
	     {
		t_str filler_symbol_name;

		defined_kind=Alphabet;
		init_symbol_set(&symbol_set[Pair], Lexical_Letter);
		/* fill the slot with ordinal number 0 with a dummy symbol
		   so that letter strings can also terminate with 0;
		   Use strdup, just in case the symbol is freed one day.
                 */
		MY_STRDUP(filler_symbol_name,""); /* impossible symbol name */
		(void) new_symbol(filler_symbol_name,&symbol_set[Pair]);
		/*  declare word boundary and morpheme boundary */
		MY_STRDUP(filler_symbol_name," + "); /* unlikely symbol name */
		morpheme_boundary_pair
		    = (s_pair *) (*new_symbol(filler_symbol_name,
				   &symbol_set[Pair]))->data;
		morpheme_boundary = morpheme_boundary_pair->lexical;
		/* tinker with the symbol table */
		morpheme_boundary_pair->surface= empty_string;
		first_lexical_letter=*morpheme_boundary;
		MY_STRDUP(filler_symbol_name," ~ "); /* unlikely symbol name */
		word_boundary_pair
		    = (s_pair *) (*new_symbol(filler_symbol_name,
				   &symbol_set[Pair]))->data;
		word_boundary=word_boundary_pair->lexical;
		/* tinker with the symbol table */
		word_boundary_pair->surface= empty_string;
		allow_string_letters=TRUE; /* allow ASTRING strings */
		MY_CALLOC(surface_letter_map,LETTER_MAP_SIZE,t_letter);
		MY_CALLOC(lexical_letter_map,LETTER_MAP_SIZE,t_letter);
		MY_CALLOC(letter_to_char_map,LETTER_MAP_SIZE,unsigned char);
		crc32file_addn(&crc, (unsigned char *) "@Alphabets ",
		               sizeof("@Alphabets "));
	     }
	     ;

AlphabetDef  :  LexicalNew COLON LetterNewPLUS SurfaceNew COLON LetterNewPLUS
	     {
		MY_FREE(fake_symbol.name);
		alphabet_size=symbol_set[Pair].card;
		make_symbol_reference(&symbol_set[Pair]);
		if (debug & DEBUG_INIT) {
		    print_symbols_by_ref(&symbol_set[Pair]);
		}
	     }
	     ;

LexicalNew  : DNAME
	     {
		/* don't care about name, first is lexical, second surface */
		fake_symbol.name=$1;
		fake_symbol.ordinal=0;
		defined_symbol=&fake_symbol;
		symbol_set[Pair].kind = Lexical_Letter;
		current_letter_map=lexical_letter_map;
		crc32file_addn(&crc, (unsigned char *) "Lexical:",
			       sizeof("Lexical:"));
	     }
	     ;

SurfaceNew  : DNAME
	     {
		/* don't care about name, first is lexical, second surface */
		defined_kind=Alphabet;
		MY_FREE(fake_symbol.name);
		fake_symbol.name=$1;
		fake_symbol.ordinal=0;
		symbol_set[Pair].kind = Surface_Letter;
		current_letter_map=surface_letter_map;
		lexical_alphabet_size=symbol_set[Pair].card;
		crc32file_addn(&crc, (unsigned char *) "Surface:",
			       sizeof("Surface:"));
	     }
	     ;

LetterNewPLUS  : LetterNewPLUS LetterNew
	    {
	    }
	    |  LetterNew
	    {
	    }
	    ;

LetterNew   : NAME
	    {
		s_symbol *symbol;
		int	  length;

		symbol= *new_symbol($1,&symbol_set[Pair]);
		if ((length = strlen(symbol->name)) == 1) {
		    current_letter_map[(unsigned char) *symbol->name]
			    = (t_letter) symbol->ordinal;
		    letter_to_char_map[symbol->ordinal]
			    = (unsigned char) *symbol->name;
		}
		crc32file_addn(&crc, (unsigned char *) $1 , (int) length);
		crc32file_add(&crc, (unsigned char) ' ');
	    }
	    | STRING
	    { 	/* allow special characters to be declared  as letter */
		/* TODO: cleanup the cases where this name could be printed */
		s_symbol *symbol;
		
		symbol= *new_symbol($1,&symbol_set[Pair]);
		if (strlen(symbol->name) != 1)
			fatal_error("%s %s %s",
			    "Sorry: cannot accept string definition of",
			    symbol_kind_str[current_letter_kind],
			    "longer than one character");
		 else {
		    current_letter_map[(unsigned char) *symbol->name]
			    = (t_letter) symbol->ordinal;
		    letter_to_char_map[symbol->ordinal]
			    = (unsigned char) *symbol->name;
		}
		crc32file_add(&crc, (unsigned char) '"');
		crc32file_add(&crc, (unsigned char) *$1);
		crc32file_add(&crc, (unsigned char) '"');
	    }
	    ;

PairDeclOPT : PairHead PairDefPLUS
	    {
		defined_symbol=NULL; /* end of definitions */
		    if (debug & DEBUG_INIT) { /* TODO change this */
			print_symbol_table(&symbol_set[Pair]);
		    }
		MY_FREE(current_lexical_set);
	    }
	    | /* empty */
	    {
	    }
	    ;

PairHead    : PAIRS
	    {
		defined_kind=Pair;
		symbol_set[Pair].kind = Pair; 
		current_lexical_set=new_bitmap((long) alphabet_size);
	    }
	    ;

PairDefPLUS : PairDefPLUS PairDef
	    {
		
	    }
	    | PairDef
	    {
	    }
	    ;
	     
PairDef    : PairNew COLON PairPLUS
	    {
		defined_symbol=NULL; /* for error message */
	    }
	    ;

PairNew	    :   DNAME
	    {
		defined_symbol = *new_symbol($1,&symbol_set[Pair]);

		current_pair = (s_pair *) defined_symbol->data;
		/* next letter will be found in surface alphabet */
		current_letter_kind=Surface_Letter;
		current_letter_map=surface_letter_map;
		current_pair_flags=BOTH_LETTERS; /* for next explicit Pair */
		empty_bitmap(current_lexical_set);
		current_pair_width=-1;
	    }
	    ;

PairPLUS    : PairPLUS Pair
	    {
	    }
	    | Pair
	    {
	    }
	    ;

Pair	    : SurfSequence SLASH LexSequence
	    {
		int lexical_width;

		if (current_pair_flags & LEXICAL_IS_CLASS)
		    lexical_width=1;
		else 
		    lexical_width=strlen((char *) $3);
		if (lexical_width > 1) { /* TODO: print pair */
		    fatal_error("%s\nthe lexical part %s",
			    "Sorry, not yet implemented.", 
			    "of the pair cannot be longer than one symbol");
		}
		if (current_pair_width == -1)
		    current_pair_width = lexical_width;
		else
		    if (current_pair_width != lexical_width)
			fatal_error("%s %s",
				    "you cannot mix pairs with different",
				    "lexical lengths");
		if (lexical_width==0
		    && !(current_pair_flags & SURFACE_IS_CLASS)
		    && *($1) == NUL_LETTER)
		    fatal_error("empty pair <>/<> is not allowed");
		add_explicit_pair(current_pair,$1,$3,current_pair_flags,
				  current_lexical_set);
		current_pair_flags=BOTH_LETTERS; /* restore for next */
	    }
	    | PairName
	    {
		int pair_width;

		/* TODO factorize this code and similar above */
		if ($1 == defined_symbol)
		    fatal_error("pair name is used in its own definition");
		pair_width=lexical_width((s_pair *) $1->data);
		if (current_pair_width == -1)
		    current_pair_width = pair_width;
		else
		    if (current_pair_width != pair_width)
			fatal_error("%s %s",
				    "you cannot mix pairs with different",
				    "lexical lengths");
		add_pair(current_pair, (s_pair *) $1->data,
			 current_lexical_set);
	    }
	    ;

PairPattern : SurfSequence SLASH LexSequence
	    { /* similar to Pair above */
		int lexical_width;

		if (! (current_pair_flags & LEXICAL_IS_CLASS)) {
		    lexical_width=strlen((char *) $3);
		    if (lexical_width > 1) /* TODO: print pair */
			fatal_error("%s\nthe lexical part of th pair %s",
				"Sorry, not yet implemented.", 
				"cannot be longer than one symbol");
		    else
			if (lexical_width == 0
			    && ! (current_pair_flags & LEXICAL_IS_CLASS)
			    && *($1) == NUL_LETTER)
			    fatal_error("empty pair <>/<> is not allowed");
		}
		$$=new_pair($1,$3,current_pair_flags,(s_pair *) NULL);
		if (within_focus)
		    is_pair_concrete($$);
		current_pair_flags=BOTH_LETTERS; /* restore for next */
	    }
	    | PairName
	    {
		$$= (s_pair *) $1->data;
		if (within_focus)
		    is_pair_concrete($$);
	    }
	    ;


PairName    : NAME
	    {
		$$= *find_symbol($1,&symbol_set[Pair]);
		if (! ($$->kind == Pair
		       || $$->kind == Pair_Letter
		       || $$->kind == Pair_Class))
		    fatal_error("%s \"%s\"",
				"There is no pair associated to symbol",
				$$->name);
	    }
	    ;

SurfSequence : Sequence
	    {
		current_letter_kind=Lexical_Letter; /* alternate */
		current_letter_map=lexical_letter_map;
		$$=$1;
	    }
	    ;

LexSequence : Sequence
	    {
		current_letter_kind=Surface_Letter; /* alternate */
		current_letter_map=surface_letter_map;
		$$=$1;
	    }
	    ;

Sequence    : LANGLE LetterSTAR RANGLE
	    {
		letter_string[letter_string_size]= NUL_LETTER;
		MY_STRDUP($$,letter_string);
	    }
	    | LSTRING
	    {
		$$= (t_letter *) $1;
	    }
	    | NAME
	    {
		s_symbol   *pair_symbol;

		pair_symbol = *find_symbol($1, &symbol_set[Pair]);
		if (pair_symbol->kind== Pair
		    || (current_letter_kind == Surface_Letter
			&& (pair_symbol->kind == Lexical_Letter
			    || pair_symbol->kind == Lexical_Class))
		    || (current_letter_kind == Lexical_Letter
			&& (pair_symbol->kind == Surface_Letter
			    || pair_symbol->kind == Surface_Class)))
		    fatal_error("symbol \"%s\" is not a %s",
				pair_symbol->name,
				symbol_kind_str[current_letter_kind]);
		if (pair_symbol->data->pair.pair_flags == BOTH_CLASSES)
		    if (current_letter_kind == Surface_Letter)
			current_pair_flags |= SURFACE_IS_CLASS;
		    else
			current_pair_flags |= LEXICAL_IS_CLASS;
		if (current_letter_kind == Surface_Letter)
		    $$ = pair_symbol->data->pair.surface;
		else
		    $$ = pair_symbol->data->pair.lexical;
	    }
	    | ANY
	    {
		$$ = letter_any;
	    }
	    ;

LetterSTAR  : LetterSTAR NAME
	    {
		if (letter_string_size >= LETTER_STRING_SIZE_MAX - 1)
		    fatal_error("symbol sequence too long, maximum=%d",
				LETTER_STRING_SIZE_MAX - 1);
		letter_string[letter_string_size++]
		    = *find_letter($2, current_letter_kind);
	    }
	    | /* empty */
	    {
		letter_string_size=0;
	    }
	    ;


ClassDeclOPT   : ClassHead ClassDefPLUS
		{
		    defined_symbol=NULL; /* end of definitions */
		    allow_string_letters=FALSE;
		    if (debug & DEBUG_INIT) { /* TODO change this */
			print_symbol_table(&symbol_set[Pair]);
		    }
		}
		| /* empty */
		{
		}
		;
ClassHead   : CLASSES
	    {
		defined_kind=Pair_Class;
		symbol_set[Pair].kind = Pair_Class;
		allow_string_letters=TRUE;
	    }
	    ;

ClassDefPLUS:	ClassDefPLUS ClassDef
	    {
	    }
	    |	ClassDef
	    {
	    }
	    ;

ClassDef    : ClassNew COLON ClassPLUS
	    {
		if (class_flags & SURFACE_FLAG)
		    if (class_flags & LEXICAL_FLAG) {
			defined_symbol->kind=Pair_Class;
			defined_symbol->data->class.lexical = current_class;
		    }
		    else
			defined_symbol->kind=Surface_Class;
		else {
		    defined_symbol->kind = Lexical_Class;
		    defined_symbol->data->class.lexical = current_class;
		    defined_symbol->data->class.surface = NULL;
		}
		defined_symbol=NULL; /* for error message */
	    }
	    ;

ClassNew    : DNAME
	    {
		defined_symbol = *new_symbol($1,&symbol_set[Pair]);
		current_class = defined_symbol->data->class.surface;
		class_flags = SURFACE_FLAG | LEXICAL_FLAG;
	    }
	    ;

ClassPLUS   : ClassPLUS Class
	    {
	    }
	    | Class
	    {
	    }
	    ;

Class : NAME
      {
	  s_symbol *class_symbol;

	  class_symbol = *find_symbol($1,&symbol_set[Pair]);
	  switch (class_symbol->kind) {
	      case Surface_Letter:
		      class_flags &= SURFACE_FLAG;
		      set_bit(current_class,
			      (long) *(class_symbol->data->pair.surface));
		      break;
	      case Lexical_Letter:
		      class_flags &= LEXICAL_FLAG;
	      /*FALLTHROUGH*/
	      case Pair_Letter:
		      set_bit(current_class,
			      (long) *(class_symbol->data->pair.lexical));
		      break;
	      case Surface_Class:
		      class_flags &= SURFACE_FLAG;
		      assign_or(current_class,
				class_symbol->data->class.surface);
		      break;
	      case Lexical_Class:
		      class_flags &= LEXICAL_FLAG;
	      /*FALLTHROUGH*/
	      case Pair_Class:
		      if (class_symbol == defined_symbol)
			fatal_error(
			"symbol class is used in its own definition");
		      assign_or(current_class,
				class_symbol->data->class.lexical);
		      break;
	      default:
		      fatal_error("program bug: no such class kind");
		      break;	
	  }
	  if (class_flags == 0)
		fatal_error("you cannot mix %s (class=\"%s\", symbol=\"%s\")",
			    "pure lexical and surface symbols in same class",
			    defined_symbol->name,
			    class_symbol->name);
      }
      | STRING
      { /* allow special characters to be used as a class definition */
	/* TODO: cleanup the cases where this name could be printed ?? */
	s_symbol *class_symbol;

	class_symbol= *find_symbol($1,&symbol_set[Pair]);
	switch (class_symbol->kind) {
	    case Surface_Letter:
		    class_flags &= SURFACE_FLAG;
		    set_bit(current_class,
			    (long) *(class_symbol->data->pair.surface));
		    break;
	    case Lexical_Letter:
		    class_flags &= LEXICAL_FLAG;
	    /*FALLTHROUGH*/
	    case Pair_Letter:
		    set_bit(current_class,
			    (long) *(class_symbol->data->pair.lexical));
		    break;
	    default:
		    fatal_error("program bug: no such class kind");
		    break;	
	}
	if (class_flags == 0)
	      fatal_error("%s %s (class=\"%s\", symbol=\"%s\")",
			  "you cannot mix pure lexical and surface",
			  "symbols in the same class",
			  defined_symbol->name,
			  class_symbol->name);
      }
      ;
