/* main CAPA parser
   Copyright (C) 1992-2000 Michigan State University

   The CAPA system 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.

   The CAPA system 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 the CAPA system; see the file COPYING.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.

   As a special exception, you have permission to link this program
   with the TtH/TtM library and distribute executables, as long as you
   follow the requirements of the GNU GPL in regard to all of the
   software in the executable aside from TtH/TtM.
*/

/* ========================================================================== */
/*            capaGrammarDef.y    created by Isaac Tsai                       */
/*                                1998, 1999 by Isaac Tsai        */
/* no longer there is a length constrain on string concatenation July 13 1998 */
/* /RMAP() function */
/* TODO: new mechanism to collect answer informations */
/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/
%{
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
#include "capaParser.h"   /* _symbol structure def */
#include "capaCommon.h"
#include "capaFunction.h"
#include "capaRQO.h"
#ifdef __hpux
#include <stdlib.h>
#include <alloca.h>
#endif

#ifdef   YACC_DBUG
#define  YYDBUG_PR1(xx)        { printf(xx);    fflush(stdout); }
#define  YYDBUG_PR2(xx,yy)     { printf(xx,yy); fflush(stdout); }
#define  YYDBUG_PR3(xx,yy,zz)  { printf(xx,yy,zz); fflush(stdout); }
#define  YYDBUG_PR4(xx,yy,zz,aa)  { printf(xx,yy,zz,aa); fflush(stdout); }
#define  YYDBUG_SYM(xx)        { switch((xx)->s_type) { \
                                     case IDENTIFIER: \
                                              printf("ID(%s)\n",(xx)->s_name);  break; \
                                     case I_VAR:  case I_CONSTANT: \
                                              printf("INT(%d)\n",(xx)->s_int);  break; \
                                     case R_VAR:  case R_CONSTANT: \
                                              printf("REAL(%.16g)\n",(xx)->s_real); break; \
                                     case S_VAR:  case S_CONSTANT: \
                                              printf("STR(%s)\n",(xx)->s_str); break; \
                               } }
#else  /* YACC_DBUG */
#define  YYDBUG_PR1(xx)        { }
#define  YYDBUG_PR2(xx,yy)     { }
#define  YYDBUG_PR3(xx,yy,zz)  { }
#define  YYDBUG_PR4(xx,yy,zz,aa)  { }
#define  YYDBUG_SYM(xx)        { }
#endif /* YACC_DBUG */

int   yylex();
void  yyerror(char*);
void  free_calc_expr(Symbol*);
void assign_pts (Symbol *, Symbol *, Symbol *);
/******************************************************************************/
/* GLOBAL VARIABLES                                                           */
/******************************************************************************/
int                Lexi_qnum;

extern int         Lexi_line;
extern int         Lexi_pos[MAX_OPENED_FILE];
extern char        Opened_filename[MAX_OPENED_FILE][QUARTER_K];
extern int         Input_idx;
int                Current_line[MAX_OPENED_FILE];
extern int         Func_idx;
extern Symbol      FuncStack[MAX_FUNC_NEST];
extern int         IFstatus[MAX_FUNC_NEST];
extern int         IFcount;
extern int         gUnitError;

int                Parsemode_f;    /* Parser mode flag */

Problem_t         *FirstProblem_p; /* First problem                    */
Problem_t         *LastProblem_p;  /* Last problem                     */
Problem_t         *LexiProblem_p;  /* Current problem                  */
char              *EndText_p;
char              *StartText_p;
char              *ErrorMsg_p;
int                ErrorMsg_count;
WarnMsg_t         *WarnMsg_p;
int                WarnMsg_count;
int                Answer_infospec;
void              (*Status_Func)();
AnswerInfo_t       CurrAnsInfo;

RandQO_t          *QuestionOrder;
PointsList_t      *CurrPtsList;
PointsList_t      *LastPtsList;




#ifdef  YYSTYPE
#undef  YYSTYPE
#endif
#define YYSTYPE  Symbol_p


#define ADD_op          1
#define SUB_op          2
#define MUL_op          3
#define DIV_op          4
#define IDIV_op         5
#define NOT_DEFINED_op  9

/*#define  yyerror  printf*/

%}
%token       NEW_ID
%token       I_CONSTANT  R_CONSTANT     S_CONSTANT
%token       I_VAR       R_VAR          S_VAR

%token       IDENTIFIER  FUNCTION_ID    ARRAY_ID
%token       HINT_LINE   EXPLAIN_LINE   TEXT_LINE    IMPORT_LINE
%token       CAPA_LET  CAPA_DEF  CAPA_DIS   CAPA_END   CAPA_VAR
%token       CAPA_ESC  CAPA_MAP  CAPA_FIG   CAPA_ANS   CAPA_RMAP
%token       CAPA_IF   CAPA_ELSE CAPA_ENDIF CAPA_SUBJ  CAPA_WHILE
%token       CAPA_RQO  CAPA_ENDWHILE        CAPA_START 
%token       ANS_AND         ANS_BOX_SHOW    ANS_CALC        ANS_CI          ANS_COMPARE       ANS_CS
%token       ANS_EVAL        ANS_EXPLAIN     ANS_EXTERNAL    ANS_FMT
%token       ANS_FORMULA     ANS_HINT        ANS_MC          ANS_MINUS
%token       ANS_OFF         ANS_ON          ANS_OR          ANS_ORDERED
%token       ANS_PATH        ANS_PCREDIT     ANS_PLUS        ANS_RANGE       
%token       ANS_SHOW_BR     ANS_SIG         ANS_TOLERANCE   ANS_TRY         ANS_TYPE
%token       ANS_UNFMT       ANS_UNIT        ANS_VERBATIM    ANS_WEIGHT 
%token       VAR_RANGE       VERBATIM
%token       SLASH           FORMAT
%token       EQ_op  NE_op  GT_op  GE_op  LT_op  LE_op  AND_op  OR_op   EoL    

%start       prob_set

%%

prob_set     :  startQ  questions  CAPA_END  { YYDBUG_PR1(" prob_set := startQ questions END\n\n"); }
             ;

questions    :  a_line                  { YYDBUG_PR1(" questions <= a_line");
                                          if (Status_Func != NULL) Status_Func();
                                        }
             |  questions  a_line       { YYDBUG_PR1(" questions <= questions a_line");
                                          if (Status_Func != NULL) Status_Func();
                                        }
             ;
             
startL       :  CAPA_LET                { YYDBUG_PR1("\n begin_let::"); } 
             ;
             
startV       :  CAPA_VAR                { YYDBUG_PR1(" begin_var"); } 
             ;
             
startA       :  CAPA_ANS                { YYDBUG_PR1("\n START ANSWER(/ANS)::\n"); 
                                          init_answerinfo(); 
                                        }
             ;

startSA      :  CAPA_SUBJ               { YYDBUG_PR1("\n START SUBJECT ANSWER(/SUBJECTIVE)::\n");
                                          init_answerinfo();
                                        }
             ;
             
startM       :  CAPA_MAP                { YYDBUG_PR1("\n begin_map::"); } 
             ;

startR       :  CAPA_RMAP               { YYDBUG_PR1("\n begin_rmap::"); } 
             ;

startRQO     :  CAPA_RQO                { YYDBUG_PR1("\n begin_rqo::"); } 
             ;

ans_and_op   :  ANS_AND                 { add_answer_cnt(ANS_AND); YYDBUG_PR1("(AND ,new an answer info)"); }
             ;
             
ans_or_op    :  ANS_OR                  { add_answer_cnt(ANS_OR); YYDBUG_PR1("(OR ,new an answer info)");  }
             ;

a_line       :  startL  statement EoL   { YYDBUG_PR1(" a_line <= startL statement CR\n");  }
             | CAPA_END                 { YYDBUG_PR1(" a_line <=  END\n\n"); }
             |  startRQO rqo_def EoL     { YYDBUG_PR1(" aline <= CAPA_RQO\n");
                                          rqo_finish();
                                        }
             |  CAPA_START              { YYDBUG_PR1(" aline <= CAPA_START\n");
					  start_question_over();
	                                }
             |  HINT_LINE               { append_hint($1->s_str); 
                                          YYDBUG_PR2(" a_line <= Hint_line(%s)\n",$1->s_str);
                                          capa_mfree($1->s_str); capa_mfree((char *)$1);
                                        }
             |  EXPLAIN_LINE            { append_explain($1->s_str); 
                                          YYDBUG_PR2(" a_line <= Explain_line(%s)\n",$1->s_str);
                                          capa_mfree($1->s_str); capa_mfree((char*)$1);
                                        }
             |  IMPORT_LINE  EoL        { YYDBUG_PR1(" a_line <= import_line CR\n");  }
             |  q_text EoL              { YYDBUG_PR1(" a_line <= Qtext CR\n"); append_text("\n");  }
             |  answer_expr             { YYDBUG_PR1(" a_line <= answer_expr (init a new prob) CR\n"); 
                                          init_new_prob(); }
             |  if_expr                 { YYDBUG_PR1(" a_line <= if_expr\n");  }
             |  while_expr              { YYDBUG_PR1(" a_line <= while_expr\n");  }
             |  map_expr   EoL          { YYDBUG_PR1(" a_line <= map_expr CR\n");  }
             |  EoL                     { YYDBUG_PR1(" a_line <= (CR)\n");  }
             |  VERBATIM                { YYDBUG_PR1(" a_line <= (VERBATIM)\n");
	                                  switch(Parsemode_f) {
					  case TeX_MODE: append_text("\\begin{verbatim}");
					    break;
					  case HTML_MODE: append_text("<PRE>");
					    break;
					  }
                                          append_text($1->s_str);
                                          capa_mfree($1->s_str); capa_mfree((char *)$1);
	                                  switch(Parsemode_f) {
					  case TeX_MODE: append_text("\\end{verbatim}\n");
					    break;
					  case HTML_MODE: append_text("</PRE>\n");
					    break;
					  }
                                        }
             |  error EoL               { char  warn_msg[WARN_MSG_LENGTH]; 
                                          YYDBUG_PR1(" a_line := ERROR(CR)\n");
                                          sprintf(warn_msg," Question %d: Syntax error.\n", Lexi_qnum+1);
                                          capa_msg(MESSAGE_ERROR,warn_msg); 
                                          begin_text();
                                        }
             
             ;
             
statement    :  IDENTIFIER '=' calc_expr 
                                        { char  warn_msg[WARN_MSG_LENGTH];
                                          
					if ( $1 != $3 ) { /* /LET a = a */
                                          switch($1->s_type) {
                                             case IDENTIFIER:
                                             case I_VAR: case I_CONSTANT:
                                             case R_VAR: case R_CONSTANT: break;
                                             case S_VAR: case S_CONSTANT: /* free up original used spaces */
                                                    capa_mfree($1->s_str); $1->s_str = NULL; break;
                                             default:    break;
                                          }
                                          switch($3->s_type) {
                                            case IDENTIFIER:
                                                 sprintf(warn_msg,"var \"%s\" not defined before use.\n",$3->s_name);
                                                 capa_msg(MESSAGE_ERROR,warn_msg);
                                                 break;
                                            case I_VAR:  case I_CONSTANT: 
                                                 $1->s_type = I_VAR;
                                                 $1->s_int = $3->s_int; 
                                                 break;
                                            case R_VAR:  case R_CONSTANT:
                                                 $1->s_type = R_VAR;
                                                 $1->s_real = $3->s_real; 
                                                 break;
                                            case S_VAR:  case S_CONSTANT:
                                                 $1->s_type = S_VAR;
                                                 $1->s_str = strsave($3->s_str);
                                                 break;
                                          }
                                          YYDBUG_PR1(" statement <=    ID = calc_expr:: "); YYDBUG_SYM($3);
					  free_calc_expr($3);
					}
                                        }
             |  ARRAY_ID '[' calc_expr ']' '=' calc_expr
                                        {    Symbol  *s_p;
                                             char     warn_msg[WARN_MSG_LENGTH];

					     s_p = get_array_symbol($1,$3,1);
                                             switch(s_p->s_type) {
                                               case IDENTIFIER:
                                               case I_VAR: case I_CONSTANT:
                                               case R_VAR: case R_CONSTANT: break;
                                               case S_VAR: case S_CONSTANT: 
                                                    capa_mfree(s_p->s_str); s_p->s_str = NULL; break;
                                               default:    break;
                                             }
                                             switch($6->s_type) {
                                               case IDENTIFIER:
                                                 sprintf(warn_msg,"var \"%s\" not defined before use.\n",$6->s_name);
                                                 capa_msg(MESSAGE_ERROR,warn_msg);
                                                 break;
                                               case I_VAR:  case I_CONSTANT: 
                                                 s_p->s_type = I_VAR;
                                                 s_p->s_int = $6->s_int;
                                                 break;
                                               case R_VAR:  case R_CONSTANT:
                                                 s_p->s_type = R_VAR;
                                                 s_p->s_real = $6->s_real; 
                                                 break;
                                               case S_VAR:  case S_CONSTANT:
                                                 s_p->s_type = S_VAR;
                                                 s_p->s_str = strsave($6->s_str);
                                                 break;
                                             }
                                             free_calc_expr($6);
                                        }
             ;

rqo_def      :  rqo_speca             { rqo_1spec(); }
             |  rqo_speca ',' rqo_def { rqo_2spec(); }
             ;

rqo_speca    : an_integer                 {start_rqo_type(SINGLE);append_rqo($1);}
             | an_integer '!'             {start_rqo_type(IMMOBILE);append_rqo($1);}
             | an_integer '-' an_integer  {start_rqo_type(RANGE);append_rqo($1);
                                           append_rqo($3);
                                          }
             | an_integer '~' rqo_specb   {prefix_rqo($1);}
             | an_integer '+' rqo_specc   {prefix_rqo($1);}
             ;

rqo_specb    : an_integer                 {start_rqo_type(ALL_MIX);append_rqo($1);}
             | an_integer '~' rqo_specb   {prefix_rqo($1);}
             | an_integer '+' an_integer  {start_rqo_type(LAST_FIXED);append_rqo($1);
                                           append_rqo($3);}
             ;

rqo_specc    : an_integer                 {start_rqo_type(ALL_FIXED);append_rqo($1);}
             | an_integer '+' rqo_specd   {prefix_rqo($1);}
             | an_integer '~' rqo_spece   {prefix_rqo($1);}
             ;

rqo_specd    : an_integer                 {start_rqo_type(ALL_FIXED);append_rqo($1);}
             | an_integer '+' rqo_specd   {prefix_rqo($1);}
             ;

rqo_spece    : an_integer                 {start_rqo_type(FIRST_FIXED);append_rqo($1);}
             | an_integer '~' rqo_spece   {prefix_rqo($1);}
             | an_integer '+' an_integer  {start_rqo_type(BOTH_FIXED);append_rqo($1);
                                           append_rqo($3);}
             ;


q_text       :  TEXT_LINE               { append_text($1->s_str);
                                          capa_mfree($1->s_str); capa_mfree((char *)$1);
                                        }
             |  var_expr                {   }
             |  q_text var_expr         {   }
             |  q_text TEXT_LINE        { append_text($2->s_str); 
                                          capa_mfree($2->s_str); capa_mfree((char *)$2);
                                        }
             ;
             
if_expr      :  CAPA_IF '(' calc_expr  ')'
                                        { int leng=0; /* begin_next_line(); no use, can be get rid of */
                                          YYDBUG_PR2("(IF expr <IFcount=%d>)\n",IFcount);
                                          switch($3->s_type) {
                                            case IDENTIFIER: 
                                                   IFstatus[IFcount] = IF_FALSE;
                                                   begin_next_line();
                                                   break;
                                            case I_CONSTANT: case I_VAR: 
                                                   if(!$3->s_int) {
                                                      IFstatus[IFcount] = IF_FALSE;
                                                      begin_if_skip();
                                                   } else {
                                                      IFstatus[IFcount] = IF_TRUE;
                                                      begin_next_line();
                                                   } 
                                                   break;
                                            case R_CONSTANT: case R_VAR:
                                                   if($3->s_real == 0.0) {
                                                      IFstatus[IFcount] = IF_FALSE;
                                                      begin_if_skip();
                                                   }else{
                                                      IFstatus[IFcount] = IF_TRUE;
                                                      begin_next_line();
                                                   }
                                                   break;
                                            case S_CONSTANT: 
					           if ( $3->s_str) {
						      leng = strlen($3->s_str);
                                                      capa_mfree($3->s_str);
                                                   } 
                                                   if(leng == 0) {
                                                      IFstatus[IFcount] = IF_FALSE;
                                                      begin_if_skip();
                                                   }else{
                                                      IFstatus[IFcount] = IF_TRUE;
                                                      begin_next_line();
                                                   }
                                                   break;
                                            case S_VAR:
					           if ( $3->s_str) {
						      leng = strlen($3->s_str);
                                                      capa_mfree($3->s_str);
                                                   }
						   if(leng == 0) {
                                                      IFstatus[IFcount] = IF_FALSE;
                                                      begin_if_skip();
                                                   }else{
                                                      IFstatus[IFcount] = IF_TRUE;
                                                      begin_next_line();
                                                   }
                                                   break;
                                          }
					  capa_mfree((char*)$3);
                                        }
             ;

while_expr   :  CAPA_WHILE '(' calc_expr  ')'
                                        {
                                          int leng; 
                                          YYDBUG_PR1("(WHILE expr)\n");
                                          switch($3->s_type) {
                                            case IDENTIFIER: /* undefined identifier regarded as false */
                                                   begin_while_skip();
                                                   break;
                                            case I_CONSTANT: case I_VAR: 
                                                   if(!$3->s_int) {
                                                      begin_while_skip();
                                                   } else {
                                                      begin_next_line(); /* skip to EoL and begin S_TEXT */
                                                   } 
                                                   break;
                                            case R_CONSTANT: case R_VAR:
                                                   if($3->s_real == 0.0) {
                                                      begin_while_skip();
                                                   }else{
                                                      begin_next_line(); /* skip to EoL and begin S_TEXT */
                                                   }
                                                   break;
                                            case S_CONSTANT: 
                                                   leng = strlen($3->s_str);
                                                   capa_mfree($3->s_str);
                                                   if(leng == 0) {
                                                      begin_while_skip();
                                                   }else{
                                                      begin_next_line(); /* skip to EoL and begin S_TEXT */
                                                   }
                                                   break;
                                            case S_VAR:
                                                   leng = strlen($3->s_str);
                                                   if(leng == 0) {
                                                      begin_while_skip();
                                                   }else{
                                                      begin_next_line(); /* skip to EoL and begin S_TEXT */
                                                   }
                                                   break;
                                          }
					  capa_mfree((char*)$3);
                                        }
             ;

var_expr     :  startV '(' formated_ans  ')'
                                        { display_var( $3 ) ; }
             ;
             
answer_expr  : answer_spec              { finish_answer_info(); 
                                        }
             | answer_expr ans_and_op  answer_spec
                                        { finish_answer_info(); 
                                          YYDBUG_PR1(" answer_expr <-- AND answers (copy answerinfo)\n"); }
             | answer_expr ans_or_op   answer_spec
                                        { finish_answer_info(); 
                                          YYDBUG_PR1(" answer_expr <-- OR answers (copy answerinfo)\n"); 
}             | startSA  '(' answer_info ')'
                                        { YYDBUG_PR1("\n subjective answer\n");
					  finish_answer_info();
                                          LexiProblem_p->ans_type = ANSWER_IS_SUBJECTIVE;
                                        }
             | startSA  '(' ')'
                                        { YYDBUG_PR1("\n subjective answer\n");
					  finish_answer_info();
                                          LexiProblem_p->ans_type = ANSWER_IS_SUBJECTIVE;
                                        }
             
             ;
             
answer_spec  : startA '(' formated_ans  ')'
                                        { assign_answer( $3 );
                                          YYDBUG_PR1("\nASSIGN Answer\n");
                                        }
             | startA '(' formated_ans ',' answer_info ')'
                                        { assign_answer( $3 );
                                          YYDBUG_PR1("\nASSIGN Answers + Answer Info\n");
                                        }
             ;
	     
answer_info  : ans_infospec
	     | answer_info ',' ans_infospec
	     ;


ans_infospec : ANS_TOLERANCE '=' a_number
                                       { YYDBUG_PR1(" ans_infospec:= TOL=a_number");
                                         assign_tolerance(TOL_ABSOLUTE,$3);
                                       }
             | ANS_TOLERANCE '=' IDENTIFIER
                                       { assign_tolerance(TOL_ABSOLUTE,$3);
                                       }
             | ANS_TOLERANCE '=' IDENTIFIER '%'
                                       { assign_tolerance(TOL_PERCENTAGE,$3);
                                       }
             | ANS_TOLERANCE '=' a_number '%'
                                       { assign_tolerance(TOL_PERCENTAGE,$3);
                                       }
	     | ANS_COMPARE '=' answer_comp {       }
	     | ANS_SIG '='  answer_sig     {       }
	     | ANS_WEIGHT '=' an_integer
	                               {  assign_weight( $3 );
	                               }
	     | ANS_WEIGHT '=' IDENTIFIER
	                               {  assign_weight( $3 );
	                               }
	     | ANS_HINT '=' an_integer {  assign_hint( $3 );
	                               }
	     | ANS_HINT '=' IDENTIFIER {  assign_hint( $3 );
	                               }
	     | ANS_PCREDIT '=' ANS_ON  {  LexiProblem_p->partial_cdt = 1;
	                               }
	     | ANS_PCREDIT '=' ANS_OFF {  LexiProblem_p->partial_cdt = 0;
	                               }
	     | ANS_SHOW_BR '=' ANS_ON  {  LexiProblem_p->show_br = DO_SHOW;
	                               }
	     | ANS_SHOW_BR '=' ANS_OFF {  LexiProblem_p->show_br = DONOT_SHOW;
	                               }
	     | ANS_VERBATIM '=' ANS_ON  {  LexiProblem_p->verbatim = DO_VERBATIM;
	                                }
	     | ANS_VERBATIM '=' ANS_OFF {  LexiProblem_p->verbatim = DONOT_VERBATIM;
	                                }
	     | ANS_BOX_SHOW '=' ANS_ON {  LexiProblem_p->show_ans_box  = DO_SHOW;
	                               }
	     | ANS_BOX_SHOW '=' ANS_OFF {  LexiProblem_p->show_ans_box = DONOT_SHOW;
	                               }                          
	     | ANS_CALC '=' ANS_FMT    {  CurrAnsInfo.ans_calc = CALC_FORMATED;
	                               }
	     | ANS_CALC '=' ANS_UNFMT  {  CurrAnsInfo.ans_calc = CALC_UNFORMATED;
	                               } 
	     | ANS_TRY '=' an_integer  {  assign_try_limits( $3 );
	                               }
	     | ANS_TRY '=' IDENTIFIER  {  assign_try_limits( $3 );
	                               }
	     | ANS_UNIT '=' S_CONSTANT {  assign_units( $3 ); capa_mfree($3->s_str); capa_mfree((char *)$3);
	                               }
	     | ANS_UNIT '=' IDENTIFIER {  assign_units( $3 );
	                               }
	     | ANS_EVAL '=' var_range {  CurrAnsInfo.ans_pts_list = CurrPtsList; 
	                                 CurrPtsList=NULL; LastPtsList = NULL;  
	                               }
	     ;


var_range    :  '<' S_CONSTANT '@'  pt_list  '>'     { assign_id_list( $2 ); 
                                                       capa_mfree($2->s_str); 
                                                       capa_mfree((char *)$2);
                                                     }
             |  '<' IDENTIFIER '@'  pt_list  '>'     { assign_id_list( $2 );  }
             ;

pt_list      :   pt_list  ','  point_coord          { int idx;
                                                      idx = LastPtsList->pts_idx; idx++;
                                                      LastPtsList->pts_next = new_ptslist( $3 );
                                                      LastPtsList = LastPtsList->pts_next;
                                                      LastPtsList->pts_idx = idx;
                                                      CurrPtsList->pts_idx = idx;
                                                      if( $3->s_type == S_CONSTANT ) {
                                                        capa_mfree($3->s_str); capa_mfree((char *)$3);
                                                      }
                                                    }
             |   pt_list  ','  pt_range             { }
             |   point_coord                        { CurrPtsList = new_ptslist( $1 );  
                                                      LastPtsList = CurrPtsList;
                                                      if( $1->s_type == S_CONSTANT ) {
                                                        capa_mfree($1->s_str); capa_mfree((char *)$1);
                                                      }
                                                    }
             |   pt_range                           { }
             ;

pt_range     :   point_coord   ':'  point_coord  '#'  IDENTIFIER   
                              { 
				assign_pts($1,$3,$5);
				/*PointsList_t *pt;
			        if( LastPtsList != NULL ) {
				  LastPtsList->pts_next = gen_ptslist( $1, $3, $5 );
				  pt = LastPtsList->pts_next;
				  while( pt->pts_next != NULL ) {
				    pt = pt->pts_next;
				  }
				  LastPtsList = pt;
				} else {
				  CurrPtsList = gen_ptslist( $1, $3, $5 );
				  LastPtsList = CurrPtsList;
				}
				if( $1->s_type == S_CONSTANT ) {
				  capa_mfree($1->s_str); capa_mfree((char *)$1);
				}
				if( $3->s_type == S_CONSTANT ) {
				  capa_mfree($3->s_str); capa_mfree((char *)$3);
				}
				*/
			      }
             |   point_coord   ':'  point_coord  '#'  ARRAY_ID '[' calc_expr ']'
                              { assign_pts($1,$3,get_array_symbol($5,$7,1)); }
             |   point_coord   ':'  point_coord  '#'  a_number     
                              { 
				assign_pts($1,$3,$5);
				/*PointsList_t *pt;
			      
				 if( LastPtsList != NULL ) {
				   LastPtsList->pts_next = gen_ptslist( $1, $3, $5 );
				   pt = LastPtsList->pts_next;
				   while( pt->pts_next != NULL ) {
				     pt = pt->pts_next;
				   }
				   LastPtsList = pt;
				 } else {
				   CurrPtsList = gen_ptslist( $1, $3, $5 );
				   LastPtsList = CurrPtsList;
				 }
				 if( $1->s_type == S_CONSTANT ) {
				   capa_mfree($1->s_str); capa_mfree((char *)$1);
				 }
				 if( $3->s_type == S_CONSTANT ) {
				   capa_mfree($3->s_str); capa_mfree((char *)$3);
				 }
				 if( $5->s_type == I_CONSTANT || $5->s_type == R_CONSTANT) {
				   capa_mfree((char *)$5);
				 }
				*/
			      }
             ;


point_coord  :   IDENTIFIER                 { $$ = $1;  }
             |   ARRAY_ID '[' calc_expr ']' { $$ = get_array_symbol($1,$3,1); }
             |   S_CONSTANT                 { $$ = $1;  }
             ;


	     
formated_ans : calc_expr               { $1->s_distype = DEFAULT_FORMAT;
                                         $$ = $1;
                                         $1->s_format = NULL;
                                         YYDBUG_PR2(" formated_ans := calc_expr (type %d)",$1->s_type);
                                       }
             | calc_expr FORMAT        { $1->s_distype = $2->s_distype;
                                         $1->s_format  = strsave($2->s_str); /* **** */
                                         capa_mfree($2->s_str); capa_mfree((char *)$2);
                                         $$ = $1;
                                         YYDBUG_PR1(" formated_ans <= calc_expr FORMAT");
                                       }
             ;
             
             

answer_sig   : an_integer              { assign_sigs( $1->s_int,$1->s_int);
                                         capa_mfree((char *)$1);
	                               }
             | an_integer ANS_PLUS  an_integer
                                       { assign_sigs($1->s_int,$1->s_int + $3->s_int);
                                         capa_mfree((char *)$1);  capa_mfree((char *)$3);
	                               }
             | an_integer ANS_MINUS an_integer
                                       { assign_sigs($1->s_int - $3->s_int,$1->s_int);
                                         capa_mfree((char *)$1);  capa_mfree((char *)$3);
	                               }
             | an_integer ANS_PLUS  an_integer ANS_MINUS an_integer
                                       { assign_sigs($1->s_int - $5->s_int,$1->s_int + $3->s_int);
                                         capa_mfree((char *)$1);  capa_mfree((char *)$3); capa_mfree((char *)$5);
	                               }
	     | an_integer ANS_MINUS  an_integer ANS_PLUS an_integer
                                       { assign_sigs($1->s_int - $3->s_int,$1->s_int + $5->s_int);
                                         capa_mfree((char *)$1);  capa_mfree((char *)$3); capa_mfree((char *)$5);
	                               }
             ;
             
answer_comp  : ANS_CS                  {  CurrAnsInfo.ans_type = ANSWER_IS_STRING_CS; }
             | ANS_CI                  {  CurrAnsInfo.ans_type = ANSWER_IS_STRING_CI; }
             | ANS_MC                  {  CurrAnsInfo.ans_type = ANSWER_IS_CHOICE;    }
             | ANS_FORMULA             {  CurrAnsInfo.ans_type = ANSWER_IS_FORMULA;   }
             | ANS_EXTERNAL            {  CurrAnsInfo.ans_type = ANSWER_IS_EXTERNAL;  }
             ;


map_expr     : startM '(' basic_constr ';' var_list ';' arg_list ')'
                                      { char   key[SMALL_LINE_BUFFER];
                                        char   warn_msg[WARN_MSG_LENGTH];
                                        int    result=0;

                                        YYDBUG_PR1(" map_expr body executed\n");
                                        sprintf(key,"%ld",$3->s_int);
                                        if( $5->s_argc ==  $7->s_argc ) {
                                          result=do_map(key, $5->s_argp, $7->s_argp, $5->s_argc, FORWARD_MAP);
					} else {
					  if ($5->s_argc==1) {
					    Symbol *a_sp;
					    a_sp=build_array_list($5,$7->s_argc);
					    result=do_map(key, a_sp->s_argp, $7->s_argp, a_sp->s_argc, FORWARD_MAP);
					    free_arglist(a_sp->s_argp);
					    a_sp->s_argp=NULL;
					  } else {
					    sprintf(warn_msg,"/MAP arg. counts are not matched.\n");
					    capa_msg(MESSAGE_ERROR,warn_msg);
					  }
                                        }
					if (result!=0) {
					    sprintf(warn_msg,
						    "/MAP had invalid arguments.\n");
					    capa_msg(MESSAGE_ERROR,warn_msg);
					}
                                        free_arglist($5->s_argp);
					$5->s_argp=NULL;
					free_arglist($7->s_argp);
					$7->s_argp=NULL;
                                      }
             | startR '(' basic_constr ';' var_list ';' arg_list ')'
                                      { char   key[SMALL_LINE_BUFFER];
                                        char   warn_msg[WARN_MSG_LENGTH];
					int    result=0;

                                        YYDBUG_PR1(" rmap_expr body executed\n");
                                        sprintf(key,"%ld",$3->s_int);
                                        if( $5->s_argc ==  $7->s_argc ) {
                                          result=do_map(key, $5->s_argp, $7->s_argp, $5->s_argc, REVERSE_MAP);
                                          
                                        } else {
					  if ($5->s_argc==1) {
					    Symbol *a_sp;
					    a_sp=build_array_list($5,$7->s_argc);
					    result=do_map(key, a_sp->s_argp, $7->s_argp, a_sp->s_argc, FORWARD_MAP);
					    free_arglist(a_sp->s_argp);
					    a_sp->s_argp=NULL;
					  } else {
					    sprintf(warn_msg,"/RMAP arg. counts are not matched.\n");
					    capa_msg(MESSAGE_ERROR,warn_msg);
					  }
                                        }
					if (result!=0) {
					    sprintf(warn_msg,
						    "/MAP had invalid arguments.\n");
					    capa_msg(MESSAGE_ERROR,warn_msg);
					}
                                        free_arglist($5->s_argp);
					$5->s_argp=NULL;
                                        free_arglist($7->s_argp);
					$7->s_argp=NULL;
                                      }
             ;



calc_expr    : calc_expr EQ_op  block  { $$ = symbols_op($1, $3, EQ_op);  }
             | calc_expr NE_op  block  { $$ = symbols_op($1, $3, NE_op);  }
             | calc_expr GE_op  block  { $$ = symbols_op($1, $3, GE_op);  }
             | calc_expr GT_op  block  { $$ = symbols_op($1, $3, GT_op);  }
             | calc_expr LE_op  block  { $$ = symbols_op($1, $3, LE_op);  }
             | calc_expr LT_op  block  { $$ = symbols_op($1, $3, LT_op);  }
             | calc_expr AND_op block  { $$ = symbols_op($1, $3, AND_op); }
             | calc_expr OR_op  block  { $$ = symbols_op($1, $3, OR_op);  }
             | block                   { $$ = $1; 
                                         YYDBUG_PR1(" calc_expr <= block "); YYDBUG_SYM($1);   }
             ;

block        : block '+' term          { $$ = symbols_op($1, $3, ADD_op); YYDBUG_PR1("block <= block '+' term "); YYDBUG_SYM($$); }
             | block '-' term          { $$ = symbols_op($1, $3, SUB_op); }
             | term                    { $$ = $1; YYDBUG_PR2(" block <= term YYSTATE(%d) ",yystate); YYDBUG_SYM($1);   }
             ;

term         : term '*' basic_constr   { $$ = symbols_op($1, $3, MUL_op); }
             | term '/' basic_constr   { $$ = symbols_op($1, $3, DIV_op); }
             | term '%' basic_constr   { $$ = symbols_op($1, $3, IDIV_op); }
             | basic_constr            { $$ = $1; 
                                         YYDBUG_PR1(" term <= basic_constr "); YYDBUG_SYM($1);   }
             ;

basic_constr : FUNCTION_ID '('  ')'   {  int tmp;
                                         
                                         Func_idx--;
                                         if(Func_idx >= 0 ) {
                                           tmp = match_function(FuncStack[Func_idx].s_name,0);
                                           $$ = do_function(tmp, 0, NULL );
                                           capa_mfree(FuncStack[Func_idx].s_name);
                                         }
                                         
                                      }
             | FUNCTION_ID '(' arg_list ')'
                                      {  int  tmp;
                                      
                                         Func_idx--;
                                         YYDBUG_PR4(" basic_constr <= FUNCTION<%s><argc=%d> YYSTATE(%d) ",
                                             FuncStack[Func_idx].s_name,$3->s_argc,yystate);
                                         
                                         if(Func_idx >= 0 ) {
                                           tmp = match_function(FuncStack[Func_idx].s_name,$3->s_argc);
					   $$ = do_function(tmp, $3->s_argc, $3->s_argp);
					   capa_mfree(FuncStack[Func_idx].s_name);
					   free_arglist($3->s_argp);
					   $3->s_argp=NULL;
                                         }
                                         YYDBUG_PR1(" basic_constr <= RETURN FUNCT "); YYDBUG_SYM($$);
                                         
                                      }
             | an_array               {   $$ = $1;   }                         
             | IDENTIFIER             { /* do not free identifier */ 
                                          $$ = $1;
                                      }
             | '-' basic_constr       { $$ = $2;
                                          switch($2->s_type) {
                                            case I_VAR:      $$ = (Symbol *)capa_malloc(sizeof(Symbol),1);
                                                             $$->s_type = I_CONSTANT;
                                            case I_CONSTANT: $$->s_int =    - $2->s_int; break;
                                            case R_VAR: $$ = (Symbol *)capa_malloc(sizeof(Symbol),1);
                                                        $$->s_type = R_CONSTANT;
                                            case R_CONSTANT: $$->s_real =   (-1.0)*($2->s_real); 
                                                             break;
                                            case S_VAR:
                                            case S_CONSTANT: break;
                                            default:         break;
                                          }
                                        }
             | '+' basic_constr         { $$ = $2; }
             | S_CONSTANT               { $$ = $1; }
             | a_number                 { $$ = $1; }
             | '(' calc_expr ')'        { $$ = $2; }
             ;

arg_list     : arg_list ',' calc_expr   { $$ = $1;
                                          $$->s_argc++;
                                          $$->s_argp = addto_arglist($1->s_argp, $3); 
                                        }
             | calc_expr                { $$ = $1;
                                          $$->s_argc = 1;
                                          $$->s_argp = new_arglist($1);
                                        }
             ;


var_list     : IDENTIFIER               { /* do not free identifier */
                                          YYDBUG_PR1(" var_list <= ID");
                                          $$ = $1;
                                          $$->s_argc = 1;
                                          $$->s_argp = new_arglist($1);
                                        }
             | ARRAY_ID '[' calc_expr ']'{
                                          YYDBUG_PR1(" var_list <= ARRAYID,calc");
                                          $$ = get_array_symbol($1,$3,1);
                                          $$->s_argc = 1;
                                          $$->s_argp = new_arglist($$);
	                                 }
             | var_list ',' ARRAY_ID '[' calc_expr ']' {
                                          YYDBUG_PR1(" var_list <= var_list,ARRAYID,calc");
	                                  $$ = $1;
					  $$->s_argc++;
					  $$->s_argp = addto_arglist($1->s_argp, 
							    get_array_symbol($3,$5,1));
                                        }
             | var_list ',' IDENTIFIER  { /* do not free identifier */
                                          YYDBUG_PR1(" var_list <= var_list,ID");
                                          $$ = $1;
                                          $$->s_argc++;
                                          $$->s_argp = addto_arglist($1->s_argp, $3); 
                                        }
             ;


a_number     : an_integer               { $$ = $1; }
             | a_real                   { $$ = $1; }
             ;
             
an_integer   : I_CONSTANT               { $$ = $1; }
             ;
             
a_real       : R_CONSTANT               { $$ = $1; } 
             ;

an_array     : ARRAY_ID '[' calc_expr ']'     {   
                                         YYDBUG_PR1(" an_array <= ARRAY_ID '['calc_expr ']' ");
                                         $$=get_array_symbol($1,$3,1);   
                                               } 
             ;

startQ       :                  { /* first matching will occur before first line of input text */
                                  YYDBUG_PR1(" startQ := begin_question\n");
                                  begin_question(); Answer_infospec = 0; 
                                }
             ;



%%
/* ============================================================================  */
ExpNode_p
mk_node(op, left, right) int op; ExpNode_p left; ExpNode_p right;
{ 
  ExpNode     *np;
  
  np = (ExpNode* )malloc(sizeof(ExpNode));
  np->e_type       = op;
  np->e_lsibp      = left;
  np->e_rsibp      = right;
  left->e_parentp  = np;
  right->e_parentp = np;
  return (np);
}

ExpNode_p
mk_leaf(type, valp) int type; Symbol_p valp;
{
  ExpNode     *np;
  
  np = (ExpNode*)malloc(sizeof(ExpNode));
  np->e_type = IDENTIFIER;
  np->e_sp = valp;
  return (np);
  
}

/* ------------------------------------------------------------- */
void free_calc_expr(Symbol *expr)
{
  switch(expr->s_type) {
  case I_CONSTANT:
  case R_CONSTANT: capa_mfree((char *)expr); break;
  case S_CONSTANT: capa_mfree(expr->s_str); capa_mfree((char *)expr); break;
  default: break;
  }
}

/* this is the entry point to array symbol retrieval */
/* array main name and index are  used to locate the symbol */
/* name of the array is used to retrieve the array symbol */

Symbol* get_array_symbol ( name,index,free_symbols ) 
Symbol *name,*index;int free_symbols;
{
  Symbol  *s_p, *a_p;
  char    *key, *tmp;
  int      leng, idx_len;
  leng = strlen(name->s_name)+8; /* [ ] */
  
  switch(index->s_type) {
    case I_VAR:
    case I_CONSTANT: tmp = (char *)capa_malloc(64,1);
      sprintf(tmp,"%ld",index->s_int);
      break;
    case R_VAR: 
    case R_CONSTANT: tmp = (char *)capa_malloc(64,1);
      sprintf(tmp,"%g",index->s_real);
      break;
    case S_VAR:
    case S_CONSTANT: idx_len = strlen(index->s_str); tmp = (char *)capa_malloc(idx_len+4,1);
      sprintf(tmp,"\"%s\"",index->s_str); /* string array index is a bit different from integer one */
      break;
    default:         break;
  }
  idx_len = strlen(tmp);
  
  key = (char *)capa_malloc(idx_len+leng,1);
  sprintf(key,"%s[%s]",name->s_name,tmp);
  a_p = (Symbol *)NULL;
  a_p = find_arrayid(name->s_name); /* use the array name to search array tree */
  if( a_p == NULL ) {
    return NULL;
  }
                                      /* did not check for error! */
  s_p = find_array_by_index(a_p,key); /* use the index portion to search along array linked list */
  capa_mfree((char *)tmp); capa_mfree((char *)key);
  
  if (free_symbols) { /* free both the name symbol and index symbol */
    if( (index->s_type == I_CONSTANT) || (index->s_type == R_CONSTANT) ) 
      capa_mfree((char *)index);
    if(index->s_type == S_CONSTANT) {
      capa_mfree(index->s_str); capa_mfree((char *)index); 
    }
    capa_mfree(name->s_name);  capa_mfree((char *)name);
  }
  return (s_p);
}

Symbol * build_array_list(ar_name,num_elem)
Symbol *ar_name;int num_elem;
{
  int     i;
  Symbol *arg_list,*a_p;
  char    idx_str[MAX_BUFFER_SIZE];
  
  a_p = find_arrayid(ar_name->s_name);
  i = 1;
  sprintf(idx_str,"%s[%d]",ar_name->s_name,i); /* create array elements with integer index */
  arg_list = find_array_by_index(a_p,idx_str); /* will create a new element if not found */
  arg_list->s_argc=1;
  arg_list->s_argp=new_arglist(arg_list);
  
  for (i=2;i<=num_elem;i++) {
      sprintf(idx_str,"%s[%d]",ar_name->s_name,i);
      arg_list->s_argc++;
      arg_list->s_argp=addto_arglist(arg_list->s_argp,find_array_by_index(a_p,idx_str));
  }
  return arg_list;
}







/* ------------------------------------------------------------- */
void        
append_text(str) char *str;  
{            
  char *q;  
  int   i;  
 
  if (!LexiProblem_p->question) {
      if (!(q = capa_malloc(strlen(str)+1,1)))  printf("No room to append.");
      strncpy(q,str,strlen(str)+1);
  } else {
      i =  strlen(LexiProblem_p->question);
      i += (strlen(str)+1);
      q =  capa_malloc(i,1);  /* *** */
      if (!q)  printf("No room to append().");
      strcat(strncpy(q,LexiProblem_p->question, strlen(LexiProblem_p->question)+1), str);
      capa_mfree(LexiProblem_p->question);
  }
  LexiProblem_p->question=q;
}
 
/******************************************************************************/
/* ADD A STRING TO THE CURRENT HINT TEXT BLOCK                                */
/******************************************************************************/
void             /* RETURNS: nothing */
append_hint(str) /* ARGUMENTS:       */
char *str;       /*    String to add */
{                /* LOCAL VARIABLES: */
   char *q;      /*    New string    */
 
   if (!LexiProblem_p->hint) {
      if (!(q = capa_malloc(strlen(str)+1,1)))
         printf("no room");
      strncpy(q,str,strlen(str)+1);
   } else {
      if (!(q = capa_malloc(strlen(LexiProblem_p->hint)+strlen(str)+1,1)))
         printf("no room");
      strcat(strncpy(q,LexiProblem_p->hint,strlen(LexiProblem_p->hint)), str);
      capa_mfree(LexiProblem_p->hint);
   }
   LexiProblem_p->hint=q;
   /* printf("APPEND HINT: %s\n", str); */
}
/******************************************************************************/
/* ADD A STRING TO THE CURRENT EXPLAIN TEXT BLOCK                                */
/******************************************************************************/
void             /* RETURNS: nothing */
append_explain(str) /* ARGUMENTS:       */
char *str;       /*    String to add */
{                /* LOCAL VARIABLES: */
   char *q;      /*    New string    */
 
   if (!LexiProblem_p->explain) {
      if (!(q = capa_malloc(strlen(str)+1,1)))
         printf("no room");
      strncpy(q,str, strlen(str)+1);
   } else {
      if (!(q = capa_malloc(strlen(LexiProblem_p->explain)+strlen(str)+1,1)))
         printf("no room");
      strcat(strncpy(q,LexiProblem_p->explain,strlen(LexiProblem_p->explain)+1), str);
      capa_mfree(LexiProblem_p->explain);
   }
   LexiProblem_p->explain=q;
   /* printf("APPEND EXPLAIN: %s\n", str); */
}

void        
append_error(str) char *str;  
{            
  char *q;  
  int   i;  

  ErrorMsg_count++;
  if (!ErrorMsg_p) {
      if (!(q = capa_malloc(strlen(str)+1,1)))  printf("No room in append.");
      strncpy(q,str,strlen(str)+1);
  } else {
      i =  strlen(ErrorMsg_p);
      i += (strlen(str)+1);
      q =  capa_malloc(i,1);  /* *** */
      if (!q)  printf("No room in append()");
      strcat(strncpy(q,ErrorMsg_p, strlen(ErrorMsg_p)+1), str);
      capa_mfree(ErrorMsg_p);
  }
  ErrorMsg_p=q;
  /* printf("APPEND ERROR: %s\n", str); */
}
void        
append_warn(type,str) int type;char *str;  
{            
  WarnMsg_t  *p, *t;
  char       *q;  

  WarnMsg_count++;
  if (!WarnMsg_p) {
      if (!(p = (WarnMsg_t *)capa_malloc(sizeof(WarnMsg_t),1)))  printf("No room in create WarnMsg_t.");
      if (!(q = capa_malloc(strlen(str)+1,1)))      printf("No room in allocating space for warn msg.");
      strncpy(q,str,strlen(str)+1);
      p->warn_next = NULL;
      p->warn_type = type;
      p->warn_str = q;
      WarnMsg_p=p;
  } else {
      for(t=WarnMsg_p;t->warn_next;t=t->warn_next) {   } /* do nothing within for loop */
      if (!(p = (WarnMsg_t *)capa_malloc(sizeof(WarnMsg_t),1)))  printf("No room in create WarnMsg_t.");
      if (!(q = capa_malloc(strlen(str)+1,1)))      printf("No room in allocating space for warn msg.");
      strncpy(q,str,strlen(str)+1);
      p->warn_next = NULL;
      p->warn_type = type;
      p->warn_str = q;
      t->warn_next = p;
  }
}

/*****************************************************************************/
/*********** if *b is a constant symbol, destroy (free) b ********************/
/*********** if *a is a var symbol, create a new symbol **********************/
/*                 do not free(*a)     */
/*  If either a or b is invalid it propagates the error up the parse tree    */
Symbol *
symbols_op(a, b, op) Symbol *a; Symbol *b; int op;
{
  int     type,  new, leng;
  long    tmp_i, tmp_j;
  double  tmp_p, tmp_q;
  char    a_str[QUARTER_K], *b_str=NULL, *r_strp;
  char    warn_msg[ONE_K];
  Symbol *a_symp;
  
if( a->s_type == IDENTIFIER || b->s_type == IDENTIFIER ) {
   if(a->s_type == IDENTIFIER) { /* a is IDENTIFIER */
     sprintf(warn_msg,"var \"%s\" not defined before use.\n", a->s_name);
     capa_msg(MESSAGE_ERROR,warn_msg);
     return (a);
   } else { /* b is IDENTIFIER */
     sprintf(warn_msg,"var \"%s\" not defined before use.\n",b->s_name);
     capa_msg(MESSAGE_ERROR,warn_msg);
     return (b);
   }
 } else {  /* a and b are neither identifiers */
  if( (a->s_type == I_VAR) ||
      (a->s_type == R_VAR) ||
      (a->s_type == S_VAR) )  {
        a_symp = (Symbol *)capa_malloc(sizeof(Symbol),1);  /* *** */
        new = 1;
  } else {
        new = 0;
        a_symp = a; /* re-use symbol *a */
  }
  if( ((a->s_type == I_CONSTANT)||(a->s_type == I_VAR)) && 
      ((b->s_type == I_CONSTANT)||(b->s_type == I_VAR)) ) { /* both a and b are integer */
    type = I_CONSTANT;
    switch( op ) {
      case ADD_op: a_symp->s_int = a->s_int +  b->s_int ; break;
      case SUB_op: a_symp->s_int = a->s_int -  b->s_int ; break;
      case MUL_op: a_symp->s_int = a->s_int *  b->s_int ; break;
      case DIV_op: if(b->s_int != 0) {
                     a_symp->s_int = a->s_int /  b->s_int ; 
                   } else {
                     sprintf(warn_msg,"division (/) by zero!\n");
                     capa_msg(MESSAGE_ERROR,warn_msg);
                   } break;
      case IDIV_op: if(b->s_int != 0) {
                      a_symp->s_int = (a->s_int %  b->s_int );
                    } else {
                      sprintf(warn_msg,"integer division (%%) by zero!\n");
                      capa_msg(MESSAGE_ERROR,warn_msg);
                    } break;
      case EQ_op:   a_symp->s_int = ((a->s_int ==  b->s_int)? 1: 0); break;
      case NE_op:   a_symp->s_int = ((a->s_int ==  b->s_int)? 0: 1); break;
      case GT_op:   a_symp->s_int = ((a->s_int >   b->s_int)? 1: 0); break;
      case GE_op:   a_symp->s_int = ((a->s_int >=  b->s_int)? 1: 0); break;
      case LT_op:   a_symp->s_int = ((a->s_int <   b->s_int)? 1: 0); break;
      case LE_op:   a_symp->s_int = ((a->s_int <=  b->s_int)? 1: 0); break;
      case AND_op:  a_symp->s_int = ((a->s_int &&  b->s_int)? 1: 0); break;
      case OR_op:   a_symp->s_int = ((a->s_int ||  b->s_int)? 1: 0); break;
    }
  } else {  /* a, b neither are integers */
    if( (a->s_type == S_VAR) || (a->s_type == S_CONSTANT) || 
        (b->s_type == S_VAR) || (b->s_type == S_CONSTANT) ) { /* either a or b is a string */
      type = S_CONSTANT;  /* the return type is a string */
      if( (a->s_type == S_VAR) || (a->s_type == S_CONSTANT) ) { /* a is a string */
	if (a->s_str == NULL || 
	    (((b->s_type == S_VAR) || (b->s_type == S_CONSTANT)) && b->s_str == NULL)) {
	  if (a->s_str == NULL) {
	    sprintf(warn_msg,"variable %s has not yet been assigned a value.\n",a->s_name);
	    capa_msg(MESSAGE_ERROR,warn_msg);
	  }
	  if (((b->s_type == S_VAR) || (b->s_type == S_CONSTANT)) && b->s_str == NULL) {
	    sprintf(warn_msg,"variable %s has not yet been assigned a value.\n",a->s_name);
	    capa_msg(MESSAGE_ERROR,warn_msg);
	  }
	} else { /* a is a valid string */
	  switch( b->s_type ) {
	  case I_VAR:
	  case I_CONSTANT:
	    leng = SMALL_LINE_BUFFER; /* assuming a long integer does not exceed 128 digits*/
	    b_str= capa_malloc(sizeof(char), leng);
	    sprintf(b_str,"%ld", b->s_int);
	    break;
	  case R_VAR:
	  case R_CONSTANT:
	    leng = SMALL_LINE_BUFFER;/*assuming a double does not exceed128chars*/
	    b_str= capa_malloc(sizeof(char), leng);
	    sprintf(b_str,"%.15g", b->s_real);
	    break;
	  case S_VAR:
	  case S_CONSTANT: /* DONE: get rid of limitations on b_str[] */
	    leng =  strlen( b->s_str ) +  1;
	    b_str= capa_malloc(sizeof(char), leng);
	    sprintf(b_str,"%s",b->s_str);
	    /*if(b->s_type == S_CONSTANT)  capa_mfree(b->s_str);*/
	    break;
	  }
	  switch( op ) {
	  case ADD_op:
	    leng =  strlen( a->s_str ) + strlen(b_str) + 1;
	    r_strp = capa_malloc(sizeof(char), leng);    /* **** */
	    strcat(r_strp, a->s_str);
	    strcat(r_strp, b_str);  /* concatenate two strings together */
	    if( !new )   capa_mfree(a->s_str);  
	    a_symp->s_str = r_strp;
	    break;
	  case SUB_op:
	  case MUL_op:
	  case DIV_op:
	  case IDIV_op:
	    if( !new )   capa_mfree(a->s_str);
	    a_symp->s_str = strsave("<<Op NOT DEFINED>>");
	    sprintf(warn_msg,"integer division (%%) cannot accept string operand!\n");
	    capa_msg(MESSAGE_ERROR,warn_msg);
	    break;
	  case EQ_op: a_symp->s_int = (strcmp(a->s_str, b_str) == 0? 1: 0);
	    type = I_CONSTANT;  break;
	  case NE_op: a_symp->s_int = (strcmp(a->s_str, b_str) == 0? 0: 1);
	    type = I_CONSTANT;  break;
	  case GT_op: a_symp->s_int = (strcmp(a->s_str, b_str) > 0? 1: 0);
	    type = I_CONSTANT;  break;
	  case GE_op: a_symp->s_int = (strcmp(a->s_str, b_str) >= 0? 1: 0);
	    type = I_CONSTANT;  break;
	  case LT_op: a_symp->s_int = (strcmp(a->s_str, b_str) < 0? 1: 0);
	    type = I_CONSTANT;  break;
	  case LE_op: a_symp->s_int = (strcmp(a->s_str, b_str) <= 0? 1: 0);
	    type = I_CONSTANT;  break;
	  case AND_op: 
	    if( (a->s_str[0] != 0) && (b_str[0] != 0)) {
	      a_symp->s_int = 1;
	    } else {
	      a_symp->s_int = 0;
	    } 
	    type = I_CONSTANT;  break;
	  case OR_op: 
	    if( (a->s_str[0] != 0) || (b_str[0] != 0)) {
	      a_symp->s_int = 1;
	    } else {
	      a_symp->s_int = 0;
	    } 
	    type = I_CONSTANT;  break;
	  }
	}
	if (b_str!=NULL) capa_mfree(b_str);
      } else {  /* b is string and a is either integer or real */
        switch( a->s_type ) {
         case I_VAR:
         case I_CONSTANT:
                   sprintf(a_str,"%ld", a->s_int);     break;
         case R_VAR:
         case R_CONSTANT:
                   sprintf(a_str,"%.15g", a->s_real);  break;
        }
        switch( op ) {
         case ADD_op:
                   leng =  strlen( b->s_str ) + strlen(a_str) + 1;
                   r_strp = capa_malloc(sizeof(char), leng);   /* *** */
                   strcat(r_strp, a_str);
                   strcat(r_strp, b->s_str);
                   /*if( b->s_type == S_CONSTANT )  capa_mfree(b->s_str);*/
                   a_symp->s_str = r_strp;              break;
         case SUB_op:
         case MUL_op:
         case DIV_op:
         case IDIV_op:
                   a_symp->s_str = strsave("<<Op NOT DEFINED>>");
                   sprintf(warn_msg,"integer division (%%) cannot accept string operand!\n");
                   capa_msg(MESSAGE_ERROR,warn_msg);
                   break;
         case EQ_op: a_symp->s_int = (strcmp(a_str, b->s_str) == 0? 1: 0);
                     type = I_CONSTANT;  break;
         case NE_op: a_symp->s_int = (strcmp(a_str, b->s_str) == 0? 0: 1);
                     type = I_CONSTANT;  break;
         case GT_op: a_symp->s_int = (strcmp(a_str, b->s_str) > 0? 1: 0);
                     type = I_CONSTANT;  break;
         case GE_op: a_symp->s_int = (strcmp(a_str, b->s_str) >= 0? 1: 0);
                     type = I_CONSTANT;  break;
         case LT_op: a_symp->s_int = (strcmp(a_str, b->s_str) < 0? 1: 0);
                     type = I_CONSTANT;  break;
         case LE_op: a_symp->s_int = (strcmp(a_str,b->s_str) <= 0? 1: 0);
                     type = I_CONSTANT;  break;
         case AND_op: if( (a_str[0] != 0) && (b->s_str[0] != 0)) {
                       a_symp->s_int = 1;
                      } else {
                       a_symp->s_int = 0;
                      } 
                     type = I_CONSTANT;  break;
         case OR_op: if( (a_str[0] != 0) || (b_str[0] != 0)) {
                       a_symp->s_int = 1;
                      } else {
                       a_symp->s_int = 0;
                      } 
                     type = I_CONSTANT;  break;
        }
      }
      
    } else { /* both a and b are real */
      type = R_CONSTANT;
      if( (a->s_type == R_CONSTANT)||(a->s_type == R_VAR) ) {
        tmp_p = a->s_real;
      } else {
        tmp_p = (double)a->s_int;
      }
      if( (b->s_type == R_CONSTANT)||(b->s_type == R_VAR) ) {
        tmp_q = b->s_real;
      } else {
        tmp_q = (double)b->s_int;
      }
      switch( op ) {
        case ADD_op: a_symp->s_real =  tmp_p + tmp_q ; break;
        case SUB_op: a_symp->s_real =  tmp_p - tmp_q ; break;
        case MUL_op: a_symp->s_real =  tmp_p * tmp_q ; break;
        case DIV_op: if(tmp_q != 0.0) {
                       a_symp->s_real =  tmp_p / tmp_q ; 
                     } else {
                       /* printf("FDIVISION by ZERO\n"); */
                       sprintf(warn_msg,"division (/) by zero!\n");
                       capa_msg(MESSAGE_ERROR,warn_msg);
                     }
                     break;
        case IDIV_op: if(tmp_q != 0.0 ) {
                        tmp_i =  (long)tmp_p;
                        tmp_j =  (long)tmp_q;
                        a_symp->s_int = tmp_i % tmp_j;
                        type = I_CONSTANT;
                     } else {
                       /* printf("DIVISION by ZERO\n"); */
                       sprintf(warn_msg,"division (/) by zero!\n");
                       capa_msg(MESSAGE_ERROR,warn_msg);
                     }
                      break;
        case EQ_op:   type = I_CONSTANT;
                      a_symp->s_int = ((tmp_p ==  tmp_q)? 1: 0); break;
        case NE_op:   type = I_CONSTANT;
                      a_symp->s_int = ((tmp_p ==  tmp_q)? 0: 1); break;
        case GT_op:   type = I_CONSTANT;
                      a_symp->s_int = ((tmp_p >   tmp_q)? 1: 0); break;
        case GE_op:   type = I_CONSTANT;
                      a_symp->s_int = ((tmp_p >=  tmp_q)? 1: 0); break;
        case LT_op:   type = I_CONSTANT;
                      a_symp->s_int = ((tmp_p <   tmp_q)? 1: 0); break;
        case LE_op:   type = I_CONSTANT;
                      a_symp->s_int = ((tmp_p <=  tmp_q)? 1: 0); break;
        case AND_op:  type = I_CONSTANT;
                      a_symp->s_int = ((tmp_p &&  tmp_q)? 1: 0); break;
        case OR_op:   type = I_CONSTANT;
                      a_symp->s_int = ((tmp_p ||  tmp_q)? 1: 0); break;
      }
    }
  }
  if( (b->s_type == I_CONSTANT) ||
      (b->s_type == R_CONSTANT) )    capa_mfree((char *)b);     /* free symbol *b only */
  if( (b->s_type == S_CONSTANT) ) {  
    capa_mfree((char *)b->s_str);  
    capa_mfree((char *)b); 
  }
  a_symp->s_type =  type;
  return (a_symp);
 } 
}

/* ------------------------------------------------------ */
char *
format_toTeX( real ) char *real;
{
  int     idx, length, fraclength, i_exp;
  char   *expo_p, fracS[SMALL_LINE_BUFFER], result[ONE_K], *areal;
  char    warn_msg[WARN_MSG_LENGTH];
  
  length = strlen(real);
  if( index( real, 'e' ) == NULL ) {
    if( index( real, 'E' ) == NULL ) {
      sprintf(result,"%s", real);
    } else {
      expo_p = index(real, 'E'); /*** hpux complained */
      fraclength = length - strlen(expo_p);
      expo_p++; if(expo_p[0] == '+') expo_p++;
      sscanf(expo_p,"%d",&i_exp);
      for(idx=0;idx<fraclength;idx++) fracS[idx] = real[idx];
      fracS[fraclength] = 0;
      if(i_exp == 0 ) {
        sprintf(result,"$%s$", fracS);
      } else {
        sprintf(result,"$%s \\times 10^{%d}$", fracS, i_exp);
      }
    }
  } else {
    if( index( real, 'E' ) == NULL ) {
      expo_p = index(real, 'e'); /*** hpux complained */
      fraclength = length - strlen(expo_p);
      expo_p++; if(expo_p[0] == '+') expo_p++;
      sscanf(expo_p,"%d",&i_exp);
      for(idx=0;idx<fraclength;idx++) fracS[idx] = real[idx];
      fracS[fraclength] = 0;
      if(i_exp == 0 ) {
        sprintf(result,"$%s$", fracS);
      } else {
        sprintf(result,"$%s \\times 10^{%d}$", fracS, i_exp);
      }
    } else {
      sprintf(result,"<<Ill-formed REAL>>");
      sprintf(warn_msg,"number %s is not a valid real number!\n",real);
      capa_msg(MESSAGE_ERROR,warn_msg);
    }
  }
  areal = (char *) capa_malloc(strlen(result)+1, 1);
  strcpy(areal,result);
  return (areal);
}
/* ------------------------------------------------------ */
char *
format_toHTML( real ) char *real;
{
  int     idx, length, fraclength, i_exp;
  char   *expo_p, fracS[SMALL_LINE_BUFFER], result[ONE_K], *areal;
  char    warn_msg[WARN_MSG_LENGTH];
  
  length = strlen(real);
  if( index( real, 'e' ) == NULL ) {
    if( index( real, 'E' ) == NULL ) {
      sprintf(result,"%s", real);
    } else {
      expo_p = index(real, 'E'); /*** hpux complained */
      fraclength = length - strlen(expo_p);
      expo_p++; if(expo_p[0] == '+') expo_p++;
      sscanf(expo_p,"%d",&i_exp);
      for(idx=0;idx<fraclength;idx++) fracS[idx] = real[idx];
      fracS[fraclength] = 0;
      if(i_exp == 0 ) {
        sprintf(result,"%s", fracS);
      } else {
        sprintf(result,"%s&#215;10<sup>%d</sup>", fracS, i_exp); /* &#215 is code for x */
      }
    }
  } else { /* the string contains 'e' char */
    if( index( real, 'E' ) == NULL ) {
      expo_p = index(real, 'e'); /*** hpux complained */
      fraclength = length - strlen(expo_p);
      expo_p++; if(expo_p[0] == '+') expo_p++;
      sscanf(expo_p,"%d",&i_exp);
      for(idx=0;idx<fraclength;idx++) fracS[idx] = real[idx];
      fracS[fraclength] = 0;
      if(i_exp == 0 ) {
        sprintf(result,"%s", fracS);
      } else {
        sprintf(result,"%s&#215;10<sup>%d</sup>", fracS, i_exp); /* &#215 is code for x */
      }
    } else {
      sprintf(result,"<<Ill-formed REAL>>");
      sprintf(warn_msg,"number %s is not a valid real number!\n",real);
      capa_msg(MESSAGE_ERROR,warn_msg);
    }
  }
  areal = (char *) capa_malloc(strlen(result)+1, 1);
  strcpy(areal,result);
  return (areal);
}
/* -- This routine is called when a /ANS is encountered -- */
void
init_answerinfo()
{

  CurrAnsInfo.ans_str      = NULL;
  CurrAnsInfo.ans_type     = 0;
  CurrAnsInfo.ans_calc     = CALC_DEFAULT;
  CurrAnsInfo.ans_tol_type = TOL_ABSOLUTE;
  CurrAnsInfo.ans_tol      = TOL_DEFAULT;
  CurrAnsInfo.ans_sig_ub   = SIG_UB_DEFAULT;
  CurrAnsInfo.ans_sig_lb   = SIG_LB_DEFAULT;
  CurrAnsInfo.ans_id_list  = NULL;
  CurrAnsInfo.ans_pts_list = NULL;
  CurrAnsInfo.ans_fmt[0]      = '\0';
  CurrAnsInfo.ans_unit_str[0] = '\0';
  CurrAnsInfo.ans_unit     = NULL;
  CurrAnsInfo.ans_next     = NULL;
}
/* when encountered a /DIS(variable)  */
void
display_var( s )Symbol *s;
{
  char    *aline;
  char    *tmp_p;
  char    warn_msg[WARN_MSG_LENGTH];

                          
  switch(s->s_type) {
    case IDENTIFIER:
           aline = (char *)capa_malloc(sizeof(char)*ONE_K,1);
           sprintf(aline,"VAR \"%s\" NOT DEFINED!", s->s_name);
           sprintf(warn_msg,"display var \"%s\" not defined before use.\n",s->s_name);
           capa_msg(MESSAGE_ERROR,warn_msg);
          break;
    case I_VAR:  case I_CONSTANT:
           aline = (char *)capa_malloc(sizeof(char)*ONE_K,1);
           sprintf(aline, "%ld", s->s_int);
          break;
    case R_VAR:  case R_CONSTANT:
           aline = (char *)capa_malloc(sizeof(char)*ONE_K,1);
           if(Parsemode_f == TeX_MODE) {
             if(s->s_distype == DEFAULT_FORMAT ) {
                sprintf(aline,"%.15g",s->s_real);
             } else {
                sprintf(aline,s->s_format,s->s_real);
             }
             tmp_p = format_toTeX(aline);
             sprintf(aline,"%s",tmp_p);
             capa_mfree( (char *)tmp_p);
                                                      
           } else {
             if(Parsemode_f == HTML_MODE ) {
                if(s->s_distype == DEFAULT_FORMAT ) {
                   sprintf(aline,"%.15g",s->s_real);
                } else {
                   sprintf(aline,s->s_format,s->s_real);
                }
                tmp_p = format_toHTML(aline);
                sprintf(aline,"%s",tmp_p);
                capa_mfree( (char *)tmp_p);
              } else {
                if(s->s_distype == DEFAULT_FORMAT ) {
                  sprintf(aline,"%.15g",s->s_real);
                } else {
                  sprintf(aline,s->s_format,s->s_real);   
                }
              }
            }
           break;
     case S_VAR:  case S_CONSTANT:  
            if (s->s_str == NULL) {
	      sprintf(warn_msg,"variable %s has not yet been assigned a value.\n",
		      s->s_name);
	      capa_msg(MESSAGE_ERROR,warn_msg);
	      aline=(char *)capa_malloc(9,1);
	      sprintf(aline,"NO VALUE");
            } else {
	      aline = (char *)capa_malloc(strlen(s->s_str)+1,1);
	      sprintf(aline,"%s",s->s_str);
	    }
           break;
   }
   append_text(aline);
   capa_mfree((char *)aline);
   if(s->s_format) { capa_mfree((char *)s->s_format); }
   s->s_format = NULL;
   switch(s->s_type) { /* free up spaces taken by constants */
     case I_CONSTANT:
     case R_CONSTANT: capa_mfree((char *)s); break;
     case S_CONSTANT: capa_mfree(s->s_str); capa_mfree((char *)s); break;
     default:  break;
   }

}

/* Assign the correct answer to CurrAnsInfo first */
void
assign_answer( s ) Symbol *s;
{
  char    aline[QUARTER_K];
  char    warn_msg[WARN_MSG_LENGTH];
                                          
  /*problem_default(LexiProblem_p);*/
  switch(s->s_type) {
    case IDENTIFIER:
            sprintf(warn_msg,"File %s, Line %3d: in /ANS, var %s not defined before use.\n",
              Opened_filename[Input_idx],Current_line[Input_idx],s->s_name);
            capa_msg(MESSAGE_ERROR,warn_msg);
            CurrAnsInfo.ans_str = strsave("ANSWER NOT DEFINED!");
            CurrAnsInfo.ans_type = ANSWER_IS_STRING_CI;
            sprintf(CurrAnsInfo.ans_fmt,"%%s");
	    if (CurrAnsInfo.ans_tol == 0.0) {
		sprintf(warn_msg, "File %s, Line %3d: answer has a numerical value of %ld and an implicit zero tolerance.\n",
		  Opened_filename[Input_idx],Current_line[Input_idx],s->s_int);
		capa_msg(MESSAGE_WARN,warn_msg);
	    }
	break;
     case I_VAR:  case I_CONSTANT:
            sprintf(aline, "%ld", s->s_int);
            CurrAnsInfo.ans_str = strsave(aline);
            CurrAnsInfo.ans_type = ANSWER_IS_INTEGER;
            sprintf(CurrAnsInfo.ans_fmt,"%%ld");
        break;
     case R_VAR:  case R_CONSTANT:
            if(s->s_distype == DEFAULT_FORMAT ) {
               sprintf(aline,"%.15g",s->s_real);
               sprintf(CurrAnsInfo.ans_fmt,"%%.15g");
            } else {
               sprintf(aline,"%.15g",s->s_real);
               strcpy(CurrAnsInfo.ans_fmt,s->s_format);  
            }
            CurrAnsInfo.ans_str = strsave(aline);
            CurrAnsInfo.ans_type = ANSWER_IS_FLOAT;
            if( CurrAnsInfo.ans_tol == 0.0 ) {
                sprintf(warn_msg,"File %s, Line %3d: answer has a numerical value of %s and a zero tolerance.\n",
                        Opened_filename[Input_idx],Current_line[Input_idx],aline);
                capa_msg(MESSAGE_WARN,warn_msg);
            }
         break;
     case S_VAR:  case S_CONSTANT:
            CurrAnsInfo.ans_str = strsave(s->s_str);
            if (s->s_str!=NULL && (strlen(s->s_str)>ANSWER_STRING_LENG-1)) {
	      sprintf(warn_msg,"File %s, Line %3d: answer is too long, max allowed length is %d, current answer is %d\n",
		      Opened_filename[Input_idx],Current_line[Input_idx],
		      ANSWER_STRING_LENG-1, strlen(s->s_str));
	      capa_msg(MESSAGE_ERROR,warn_msg);
	      CurrAnsInfo.ans_str[ANSWER_STRING_LENG-1]='\0';
	    }
            if ( !CurrAnsInfo.ans_type ) { /* not yet specified by str=  answer info */ 
              CurrAnsInfo.ans_type = ANSWER_IS_STRING_CI;
            }
            sprintf(CurrAnsInfo.ans_fmt,"%%s");
         break;
   }
   if(s->s_format) { 
       capa_mfree((char *)s->s_format);
   }
   s->s_format = NULL;
   switch(s->s_type) {
      case I_CONSTANT:
      case R_CONSTANT: capa_mfree((char *)s); break;
      case S_CONSTANT: capa_mfree(s->s_str); capa_mfree((char *)s); break;
     default:  break;
   }

}

/* Assign tolerance to CurrAnsInfo first */
void
assign_tolerance(tol_type, s) int tol_type; Symbol *s;
{
  char  warn_msg[WARN_MSG_LENGTH];
  
  CurrAnsInfo.ans_tol_type  = tol_type;
  switch( s->s_type ) {
      case IDENTIFIER:
            sprintf(warn_msg,"TOL = var, \"%s\" not defined before use.\n",s->s_name);
            capa_msg(MESSAGE_ERROR,warn_msg);
            CurrAnsInfo.ans_tol = 0.0;
          break;
       case I_VAR: case I_CONSTANT: 
           CurrAnsInfo.ans_tol =(double)s->s_int;
          break;
       case R_VAR: case R_CONSTANT:
           CurrAnsInfo.ans_tol = s->s_real;
          break;
       case S_VAR: case S_CONSTANT: CurrAnsInfo.ans_tol = 0.0;
          break;
   }
  free_calc_expr(s);
}

/* Problem weight is per problem based */
void
assign_weight( s ) Symbol *s;
{ char warn_msg[WARN_MSG_LENGTH];
  YYDBUG_PR1(" weight = identifier\n");
  switch( s->s_type ) {
    case IDENTIFIER:
            sprintf(warn_msg,"WGT = var, \"%s\" not defined before use.\n", s->s_name);
            capa_msg(MESSAGE_ERROR,warn_msg);
            LexiProblem_p->weight = WEIGHT_DEFAULT;
           break;
    case I_VAR: case I_CONSTANT:
            if( s->s_int < 0 ) {
              sprintf(warn_msg,"WGT = %ld, weight cannot be less than zero.\n", s->s_int);
	      capa_msg(MESSAGE_ERROR,warn_msg);
	      LexiProblem_p->weight = WEIGHT_DEFAULT;
            } else {
              LexiProblem_p->weight = s->s_int; 
            }
            break;
     case R_VAR: case R_CONSTANT:
            if( s->s_real < 0.0 ) {
               sprintf(warn_msg,"WGT = %g, weight cannot be less than zero.\n", s->s_real);
	       capa_msg(MESSAGE_ERROR,warn_msg);
	       LexiProblem_p->weight = WEIGHT_DEFAULT;
            } else {
               LexiProblem_p->weight = (int)(s->s_real); 
            }
            break;
      case S_VAR: case S_CONSTANT: LexiProblem_p->weight = WEIGHT_DEFAULT; break;
    }
  free_calc_expr(s);
}
/* Answer try limit is per problem based */
void
assign_try_limits( s ) Symbol *s;
{ char warn_msg[WARN_MSG_LENGTH];
  switch( s->s_type ) {
    case IDENTIFIER:
	    sprintf(warn_msg,"TRY = var, \"%s\" not defined before use.\n",s->s_name);
            capa_msg(MESSAGE_ERROR,warn_msg);
            LexiProblem_p->tries = MAX_TRIES;
           break;
    case I_VAR: case I_CONSTANT:
            if(s->s_int <= 0) {
	       sprintf(warn_msg,"TRIES = %ld, tries cannot be less than or equal to zero.\n",s->s_int);
	       capa_msg(MESSAGE_ERROR,warn_msg);
	       LexiProblem_p->tries = MAX_TRIES;
	    } else {
	       LexiProblem_p->tries = s->s_int;
	    }
	   break;
     case R_VAR:  case R_CONSTANT:
            if(s->s_real <= 0.0) {
	       sprintf(warn_msg,"TRIES = %g, tries cannot be less than or equal to zero.\n",s->s_real);
	       capa_msg(MESSAGE_ERROR,warn_msg);
	       LexiProblem_p->tries = MAX_TRIES;
	     } else {
	       LexiProblem_p->tries = (int)(s->s_real);
	     }
            break;
      case S_VAR: case S_CONSTANT: LexiProblem_p->tries = MAX_TRIES; break;
   }	    
  free_calc_expr(s);
}
/* Answer hint is per problem based */
void
assign_hint( s ) Symbol *s;
{  char warn_msg[WARN_MSG_LENGTH];
	                                 
    switch( s->s_type ) {
      case IDENTIFIER:
               sprintf(warn_msg,"HINT = var, \"%s\" not defined before use.\n", s->s_name);
               capa_msg(MESSAGE_ERROR,warn_msg);
               LexiProblem_p->show_hint = SHOW_HINT_DEFAULT;
             break;
      case I_VAR: case I_CONSTANT:
               if( s->s_int < 0 ) {
                  sprintf(warn_msg,"HINT = %ld, show hint cannot be less than zero.\n", s->s_int);
	          capa_msg(MESSAGE_ERROR,warn_msg);
	          LexiProblem_p->show_hint = SHOW_HINT_DEFAULT;
               } else {
                  LexiProblem_p->show_hint = s->s_int; 
               }
             break;
       case R_VAR: case R_CONSTANT:
               if( s->s_real < 0.0 ) {
                  sprintf(warn_msg,"HINT = %g, show hint cannot be less than zero.\n", s->s_real);
	          capa_msg(MESSAGE_ERROR,warn_msg);
	          LexiProblem_p->show_hint = SHOW_HINT_DEFAULT;
               } else {
                  LexiProblem_p->weight = (int)(s->s_real); 
               }
             break;
       case S_VAR: case S_CONSTANT: LexiProblem_p->show_hint = SHOW_HINT_DEFAULT; break;
    }
  free_calc_expr(s);
}

/* Assign answer units string to CurrAnsInfo first */
void
assign_units( s ) Symbol *s;
{
  char    symb_str[ONE_TWO_EIGHT];
  char    warn_msg[WARN_MSG_LENGTH];
  
  switch( s->s_type ) {
    case IDENTIFIER:
           sprintf(warn_msg,"UNIT = var, \"%s\" not defined before use.\n", s->s_name);
           capa_msg(MESSAGE_ERROR,warn_msg);
           break;
    case I_VAR: case I_CONSTANT:
           sprintf(warn_msg,"UNIT = %ld, unit cannot be a number.\n", s->s_int);
           capa_msg(MESSAGE_ERROR,warn_msg);
           break;
    case R_VAR: case R_CONSTANT:
           sprintf(warn_msg,"UNIT = %g, unit cannot be a number.\n", s->s_real);
	   capa_msg(MESSAGE_ERROR,warn_msg);
           break;
    case S_VAR: case S_CONSTANT:
           strcpy(symb_str,s->s_str);
	   strcpy(CurrAnsInfo.ans_unit_str,symb_str);
	   CurrAnsInfo.ans_unit = u_parse_unit(symb_str);
	   if (gUnitError) {
	      sprintf(warn_msg,"Error in unit specified: %s\n",symb_str);
	      capa_msg(MESSAGE_ERROR,warn_msg);
	   }
	break;
  }
}
void
assign_sigs( lb, ub ) int lb; int ub;
{
  CurrAnsInfo.ans_sig_lb = lb;
  CurrAnsInfo.ans_sig_ub = ub;
}

void
assign_id_list( s ) Symbol *s;
{
  char    warn_msg[WARN_MSG_LENGTH];
  
  switch( s->s_type ) {
    case IDENTIFIER:
           sprintf(warn_msg,"Eval = < ID @ Pts >, \"%s\" not defined before use.\n", s->s_name);
           capa_msg(MESSAGE_ERROR,warn_msg);
           break;
    case I_VAR: case I_CONSTANT:
           sprintf(warn_msg,"Eval = < %ld @ Pts >, ID cannot be a number.\n", s->s_int);
           capa_msg(MESSAGE_ERROR,warn_msg);
           break;
    case R_VAR: case R_CONSTANT:
           sprintf(warn_msg,"Eval = < %.16g @ Pts >, ID cannot be a number.\n", s->s_real);
	   capa_msg(MESSAGE_ERROR,warn_msg);
           break;
    case S_VAR: case S_CONSTANT:
	   CurrAnsInfo.ans_id_list = strsave(s->s_str);
	   break;
  }
}

/* void assign_pts ( Symbol* coord1, Symbol* coord2, Symbol* num) { */
void assign_pts (coord1, coord2, num)Symbol *coord1;Symbol *coord2;Symbol *num;
{
  PointsList_t *pt;
  if( LastPtsList != NULL ) {
    LastPtsList->pts_next = gen_ptslist( coord1, coord2, num );
    pt = LastPtsList->pts_next;
    while( pt->pts_next != NULL ) {
      pt = pt->pts_next;
    }
    LastPtsList = pt;
  } else {
    CurrPtsList = gen_ptslist( coord1, coord2, num );
    LastPtsList = CurrPtsList;
  }
  if(coord1->s_type == S_CONSTANT) {
    capa_mfree(coord1->s_str); capa_mfree((char *)coord1);
  }
  if(coord2->s_type == S_CONSTANT) {
    capa_mfree(coord2->s_str); capa_mfree((char *)coord2);
  }
  if(num->s_type == I_CONSTANT || num->s_type == R_CONSTANT) {
    capa_mfree((char *)num);
  }
}

/* =========================================================================== */






/* =========================================================================== */

void start_question_over()
{
  free_problems(LexiProblem_p);
  LexiProblem_p = (Problem_t *) capa_malloc(sizeof(Problem_t),1); 
  problem_default(LexiProblem_p);
  begin_question();
}

void
init_new_prob()
{
  if (LastProblem_p) {
     LastProblem_p->next = LexiProblem_p;
  } else {
     FirstProblem_p      = LexiProblem_p;
  }
  LastProblem_p = LexiProblem_p;
  Lexi_qnum++;  
  LexiProblem_p = (Problem_t *) capa_malloc(sizeof(Problem_t),1); /* *** */
  problem_default(LexiProblem_p);
}
void
add_answer_cnt(op)int op;
{
  LexiProblem_p->ans_cnt++;
  if(LexiProblem_p->ans_op == 0) { /* the very first /AND or /OR */
    LexiProblem_p->ans_op = op;
  } else {
    if( LexiProblem_p->ans_op != op ) { char    warn_msg[WARN_MSG_LENGTH];
      sprintf(warn_msg,"When specifying multiple answers, either /AND or /OR can be used, but not both.\n");
      capa_msg(MESSAGE_ERROR,warn_msg);
    }
  }
}

/* -- called when forming answer_expr */
void
finish_answer_info()
{
  AnswerInfo_t  *ai;
  
  if( LexiProblem_p->ans_cnt == 1 ) {  /* Only one answer is defined */
      LexiProblem_p->answer    = CurrAnsInfo.ans_str;
      LexiProblem_p->ans_type  = CurrAnsInfo.ans_type;
      LexiProblem_p->calc      = CurrAnsInfo.ans_calc;
      LexiProblem_p->tol_type  = CurrAnsInfo.ans_tol_type;
      LexiProblem_p->tolerance = CurrAnsInfo.ans_tol;
      LexiProblem_p->sig_ubound= CurrAnsInfo.ans_sig_ub;
      LexiProblem_p->sig_lbound= CurrAnsInfo.ans_sig_lb;
      LexiProblem_p->id_list   = CurrAnsInfo.ans_id_list;
      LexiProblem_p->pts_list  = CurrAnsInfo.ans_pts_list;
      strcpy(LexiProblem_p->ans_fmt,CurrAnsInfo.ans_fmt);
      strcpy(LexiProblem_p->unit_str,CurrAnsInfo.ans_unit_str);
      LexiProblem_p->ans_unit  = CurrAnsInfo.ans_unit;
  } else {
      ai = (AnswerInfo_t  *)capa_malloc(sizeof(AnswerInfo_t),1);
      ai->ans_str       = CurrAnsInfo.ans_str;
      ai->ans_type      = CurrAnsInfo.ans_type;
      ai->ans_calc      = CurrAnsInfo.ans_calc;
      ai->ans_tol_type  = CurrAnsInfo.ans_tol_type;
      ai->ans_tol       = CurrAnsInfo.ans_tol;
      ai->ans_sig_ub    = CurrAnsInfo.ans_sig_ub;
      ai->ans_sig_lb    = CurrAnsInfo.ans_sig_lb;
      ai->ans_id_list   = CurrAnsInfo.ans_id_list;
      ai->ans_pts_list  = CurrAnsInfo.ans_pts_list;
      strcpy(ai->ans_fmt,CurrAnsInfo.ans_fmt);
      strcpy(ai->ans_unit_str,CurrAnsInfo.ans_unit_str);
      ai->ans_unit      = CurrAnsInfo.ans_unit;
      ai->ans_next      = NULL;
      if(LexiProblem_p->ans_cnt == 2) {
        LexiProblem_p->ans_list = ai;
      } else {
        (LexiProblem_p->ans_list_last)->ans_next = ai;
      }
      LexiProblem_p->ans_list_last = ai;
  }

}

/* === End of capaGrammarDef.y ============================================= */

