Editor's note. These listings have been scanned from a paper copy. I've proofed the text, but typos may remain.
This appendix contains listings of the functions that have been written for PAL, the functions that were developed but not used (analystr.fun and showparm.fun), tidy.bat, and the Prolog prototype spelling assistant (proto.pro), in that order.
The order of the PAL function listings is that of the include files under function definitions, in palfun.c. The filename is shown in block letters at the start of each file, and function names are in bold lowercase. No changes have been made to the code, although there has been some reformatting of text.
/* PALFUN.C */
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <graph.h>
#include <stdarg.h>
#define AND &&
#define HELPKEY 59 /* 0;59 = Fl */
#define MAXANSLEN 80
/* WARNING: if this maximum length of answer is changed, corresponding
initialisation of ANSWER in hidden.inc must be changed */
#define NOT !
#define OR ||
/* ----------------------- WINDOW PARAMETERS ------------ */
#define HEAD_R1 1 /* first row of header */
#define HEAD_Cl 1 /* first column of header */
#define HEAD_R2 1 /* last row of header */
#define HEAD_C2 vc.numtextcols /* last column of header */
#define MAIN_R1 2 /* first row of main screen */
#define MAIN_Cl 1 /* first column of main screen */
#define MAIN_R2 vc.numtextrows-1 /* last row of main screen */
#define MAIN_C2 vc.numtextcols /* last column of main screen */
#define MESS_R1 vc.numtextrows /* first row of message area */
#define MESS_Cl 1 /* first column of message area */
#define MESS_R2 vc.numtextrows /* last row of message area */
#define MESS_C2 vc.numtextcols /* last column of message area */
/* screen window coordinates defined using above values */
#define HEADCOORD HEAD_R1,HEAD_Cl,HEAD_R2,HEAD_C2
#define MAINCOORD MAIN_R1,MAIN_Cl,MAIN_R2,MAIN_C2
#define MESSCOORD MESS_RI,MESS_Cl,MESS_R2,MESS_C2
enum boolean { FALSE, TRUE };
enum mres
{ HELPREQ, MATCH, NOANS, NOMATCH, PM1, PM2, PM3, PM4, PM5,
PM6, PM7, PM8, OPT1, OPT2, OPT3, OPT4, OPT5, OPT6, AE};
struct matchparams
{
enum boolean blanktest;
enum boolean casetest;
enum boolean puncttest;
enum boolean spelltest;
};
struct matchparams defaultmp = { FALSE, FALSE, FALSE, FALSE };
struct matchparams currentmp = { FALSE, FALSE, FALSE, FALSE };
struct videoconfig vc;
struct rccoord oldpos;
/* ------------------- FUNCTION DECLARATIONS --------------- */
enum mres ( char *model, char *answer, char *mps );
extern void authprog( void );
void cleanstr( char *raw, char *conv );
void clearall( void );
void clearbetween( int rowl, int row2 );
void cleardisplay( void );
enum mres findword( char *model, char *answer, char *mps );
void getanswer( char *answer, int length );
void header( char *sometext );
enum boolean ispunctchar( char ch );
enum boolean legalsmp( char *mpstring );
enum mres match_any(int reqmatches, char *model, char *answer, char *mps );
enum mres mc(int numb_of_optns, char *stem, char *option,
[editor's note: code is missing from the paper copy]
/* Can't use eg #define mc2 mc(2, hence these functions to call mc */
enum mres mc2( char *st, char *ol, char *o2
enum mres mc3( char *st, char *ol, char *o2, char *o3
enum mres mc4( char *st, char *ol, char *o2, char *o3, char *o4 );
enum mres mc5( char *st, char *ol, char *o2, char *o3, char *o4, char *o5 );
enum mres mc6( char *st, char *ol, char *o2, char *o3, char *o4, char *o5, char *o6 );
void message( char *sometext);
void pause( void );
void putcursor( int rowpos, int colpos );
void setcurrentmp(char *matchstring);
void set_match_params( char *matchstring);
enum mres test( char *modelstr, char *answerstr );
void text( char *sometext );
/* --------------------- FUNCTION DEFINITIONS -------------- */
#include "alphamat.fun" /* alpha match */
#include "cleanstr.fun" /* for alphamatch, findword, matchany */
#include "clear.fun" /* all clear functions */
#include "findword.fun" /* findword */
#include "get.fun" /* getanswer */
#include "header.fun" /* header */
#include "ispunctn.fun" /* ispunctchar */
#include "matchprm.fun" /* set_match_params, setcurrentmp */
#include "matchany.fun" /* matchany */
#include "mc.fun" /* multiple choice function */
#include "message.fun"
#include "pause.fun" /* pause */
#include "putcurs.fun" /* putcursor */
#include "test_vl.fun" /* test for match */
void text(char *sometext)
{ _outtext(sometext); }
/* This is defined as 'write' in hidden.inc. It could not be called by this
name due to interference with write.asm The function should be made to
accept control characters, so that variables can be printed */
main()
{
_getvideoconfig( &vc );
/* suitable message if can't find one */
_clearscreen( _GCLEARSCREEN );
_settextwindow( MAINCOORD );
authprog(); /* this will contain the author's code */
/* when authprog has finished, all author errors could be
displayed - simply define an errorbuffer globally, and
AE function could write to it */
}
/* HIDDEN.INC */
void authprog()
{
#define ALL "B C S P" /* match parameter string */
#define AND &&
#define ANSWER char /* use in conjunction with SET */
#define DEF "default" /* match parameter string */
#define END }
#define IS ==
#define ISNT !=
#define NONE " " /* use with message, header, s_m_params */
#define OR ||
#define MATCHRES int /* results of the enum type mres */
#define SET [80] = " " /* 80 = MAXANSLEN */
/* The following correspond to the enum type mres, which is used in the
matching functions. If the order of the results in the enum type is
changed, so must the values here, otherwise inconsistencies will arise. */
#define HELPREQ 0
#define MATCH 1
#define NOANS 2
#define NOMATCH 3
#define PM1 4
#define PM2 5
#define PM3 6
#define PM4 7
#define PM5 8
#define PM6 9
#define PM7 10
#define PM8 11
#define OPT1 12
#define OPT2 13
#define OPT3 14
#define OPT4 15
#define OPT5 16
#define OPT6 17
/* so author doesn't type () at end of these functions */
#define clearall clearall()
#define cleardisplay cleardisplay()
#define pause pause()
#define write text
/* couldn't make a function called write because of write.asm,
so have to rename */
/* ALPHAMAT.FUN */
enum mres alphamatch(char *model, char *answer, char *mps)
{
char tidymod[MAXANSLEN], tidyans[MAXANSLEN];
strcpy(tidymod, "+"); /* initialisation */
strcpy(tidyans, "+");
/* No attempt to answer */
{ if( strlen( answer ) == 0 ) return NOANS; }
if( strcmp(answer, "?") == 0) return HELPREQ;
/* help requested, no need to test */
else /* analyse answer */
{
setcurrentmp( mps); /* set current match params */
cleanstr( model, tidymod );
cleanstr( answer, tidyans );
return( test( tidymod, tidyans ));
/* ie MATCH or NOMATCH */
}
}
/* CLEANSTR.FUN */
void cleanstr( char *raw , char *conv )
{
/* Takes a string (raw), and manipulates it according to the current
match parameters. Options are to convert to upper case, strip
punctuation, and remove unnecessary blanks. */
int convcounter;
char temp[MAXANSLEN];
char *ptr;
strcpy( conv, raw );
if( NOT currentmp.casetest) /* convert to upper case */
{ strcpy(temp, conv); strcpy(conv, strupr(temp) ); }
if( NOT currentmp.puncttest)
/* punctuation unimportant, strip it from string */
{
strcpy(temp, conv); ptr = temp; convcounter = 0;
while( *ptr )
{
if( ! ispunctchar( *ptr ))
{ conv[convcounter] = *ptr; convcounter++; }
ptr++;
}
conv[convcounter] = '\0';
}
/* Blanks unimportant, remove from string, but leave one blank
between words. */
if( NOT currentmp.blanktest )
{
strcpy(temp, conv); ptr = temp;
conv[0] = ' '; convcounter = 1;
while( *ptr)
{
if( ( *ptr != ' ' || (conv[convcounter-1] != ' ') )
{ conv[convcounter] = *ptr; convcounter++; }
ptr++;
}
if( conv[convcounter-1) == ' ')
conv[convcounter] = '\01';
else
{ conv[convcounter] ' '; conv[convcounter+l] = '\0'; }
} /* endif not blanktest */
}
/* CLEAR.FUN */
void clearbetween(int rowl, int row2)
{
#define bottom MAIN_R2 - HEAD_R2
/* check row coordinates are legal */
if( rowl >= 1 AND row2 <= bottom AND rowl <= row2 )
{
oldpos = _gettextposition();
if( oldpos.row >= rowl AND oldpos.row <= row2 )
{
/* if cursor in range to clear, reset start of first cleared row */
oldpos.row = rowl;
oldpos.col = 1;
}
_settextwindow( rowl+MAIN_R1-1, MAIN_C1, row2+MAIN_R1-1, MAIN_C2 );
/* add row numbers to main window top row because settextwindow
uses absolute screen values */
_clearscreen( _GWINDOW );
_settextwindow( MAINCOORD
_settextposition(oldpos.row, oldpos.col);
}
else /* illegal row parameters */
{
char tb[250]; /*text buffer */
int c; /* counter for buffer */
_clearscreen( _GWINDOW );
c = sprintf(tb, "Author error in clearbetween: illegal row parameters.");
c+= sprintf(tb+c,"\n row values must be between 1 and %d inclusive,
and rowl <= row2", bottom);
c+= sprintf(tb+c,"\n You set: rowl = %d, row2 = %d",rowl,row2);
c+= sprintf(tb+c,"\n Press any key to continue... ");
_outtext(tb);
getch();
_clearscreen( _GWINDOW );
}
}
void clearall(void)
{
_clearscreen( _GCLEARSCREEN );
_settextwindow( MAINCOORD );
}
void cleardisplay(void)
{
_clearscreen( _GWINDOW );
}
/* FINDWORD.FUN */<
enum mres findword( char *model, char *answer, char *mps )
{
/* Tokenise answer, test each token for match with model,
return MATCH if a match is found, NOMATCH if no match found,
NOANS if no attempt was made to answer (ie empty string),
HELPREQ if Fl was pressed. */
#define maxwords 9
char tidymod[MAXANSLEN], tidyans[MAXANSLEN], seps[]
char toks[maxwords][MAXANSLEN]; /* max nine tokens */
char *p;
int count, numb;
/* No attempt to answer */
{ if( strlen( answer == 0 ) return NOANS; }
if( strcmp(answer, "?") == 0) return HELPREQ;
/* help requested, no need to test */
else /* analyse answer */
{
setcurrentmp( mps ); /* set current match params */
strcpy(tidymod," ");
/* initialise, so tidymod points somewhere */
strcpy(tidyans, " ");
cleanstr( model, tidymod );
cleanstr( answer, tidyans );
/* break into tokens */
count = 0;
p = strtok( tidyans, seps );
while( (p != NULL) AND ( count < maxwords ) )
{
strcpy( toks[count], " "); /* leading space */
strcat( toks[count], p );
strcat( toks[count], " "); /* trailing space */
p = strtok( NULL, seps);
count++;
numb = count;
}
for( count = 0; count < numb; count++)
/* find first match */
if( test( tidymod, toks[count] ) == MATCH )
return MATCH;
return NOMATCH; /* no match found */
} /* endelse analyse answer */
}
/* GET.FUN */
void getanswer( char *answer, int length)
{
char currchar[2]; /* to convert char to string */
char prnbuf[170];
int pos = 0, key, counter;
struct rccoord textpos;
oldpos = _gettextposition();
if( (length+oldpos.col <= MAIN_C2) AND (length > 0) AND (length < MAXANSLEN) )
{ /* length of allowed answer < right extremity of window */
for( counter = 1; counter <= length; counter += 1)
_outtext("_");
_settextposition(oldpos.row, oldpos.col);
message("Type answer, press Enter. Back arrow to delete. Fl = Help");
/* Can't use a textwindow to constrain the length of an input string
therefore can't use gets(). Can't use ungetch(), as it may only be
used once. Therefore, code in while loop below is necessary */
while(l)
{
key = getch(); /* don't echo yet */
if( key == 13 ) break; /* Enter key pressed */
if( key == 0 ) /* extended key pressed */
{
if( getch() == HELPKEY)
/* help requested, return "?" */
{ answer[0] = '?'; pos = 1; break; }
}
if( isprint(key) AND (pos < length ))
{ /* It's a printable character, display, and store */
answer[pos] = (char)key;
currchar[0] = (char)key;
currchar[l] = '\0'; /* key to string */
_outtext( currchar );
pos += 1;
/* Alternative is to use printf and keep track of cursor location.
This is necessary if the arrow keys are used, so that the cursor
location and answer position are always synchronised. The use of
switch is probably better in this case, since a lot of redundant
if clauses would not be tested */
}
if( (pos > 0) AND (key == 8) ) /* backspace key == 8 */
{
pos -= 1; /* overwrite answer position */
textpos = _gettextposition();
_settextposition(textpos.row, textpos.col-1);
/* go back one column */
_outtext("_"); /* remove last char from screen */
_settextposition(textpos.row, textpos.col-1);
/* don't want line */
}
} /* endwhile */
answer[pos] = '\0'; /* strings terminate with null */
} /* endif */
else
{
sprintf(prnbuf,"\nAuthor error in getanswer:\n length
less than 1 or length+current position > border.\nor length > maximum
allowed length %d\n Press any key to continue...", MAXANSLEN -1);
_outtext(prnbuf);
getch();
_clearscreen( GWINDOW );
answer[0] = '\0';
}
}
/* HEADER.FUN */
void header( char *sometext)
{
char printbuff[200];
if( strlen( sometext ) > ( HEAD_C2 - HEAD_C1 +1 ) )
{
_clearscreen( _GWINDOW );
sprintf( printbuff, "Author error in header: length of message too long.\n
maximum %d characters allowed. Your message is %d characters\npress any key to
continue..." HEAD_C2 - HEAD_C1 +1 ), strlen( sometext ) );
_outtext( printbuff );
getch();
_clearscreen( _GWINDOW );
}
else
{
oldpos = _gettextposition();
_settextwindow( HEADCOORD );
_clearscreen( _GWINDOW );
_outtext( sometext);
_settextwindow( MAINCOORD );
settextposition(oldpos.row, oldpos.col);
}
}
/* ISPUNCTN.FUN */
enum boolean ispunctchar( char ch )
{
/* NOTE WELL the format for defining punctuation characters.
It can be changed at will, but note that wild characters used in
matching functions are ? and *. If they are defined as punctuation,
then they should not be used in model answers, as they will be
removed. Note the backslash preceding double and single quotes.
The double backslash represents a single ASCII backslash. */
char punctuation[] = "!\"$^&{}[]:;@_\',./~#|\\";
char *ptr = punctuation;
while( *ptr ) /* go through punctuation array */
if( *ptr == ch) return TRUE; /* match found */
else *ptr++;
return FALSE; /* no match, ch is not punctuation */
}
/* MATCHANY.FUN */
enum mres match_any(int reqmatches, char *model, char *answer, char *mps )
{
/* Tokenise model and answer strings, test each token for match with model,
return MATCH if required number of matches found, NOMATCH if no matches
at all, and PM1 to PM8 if only some of the required matches found. */
#define maxwords 9
#define minwords 2
char tidymod[MAXANSLEN], tidyans[MAXANSLEN], seps[] = " ";
char anstoks[maxwords)[MAXANSLEN];
char modtoks[maxwords][MAXANSLEN];
char printbuff[150];
char *p;
int wordsinmodel, wordsinans, count, modcount, wordsfound = 0;
if( reqmatches < minwords OR reqmatches > maxwords)
{
sprintf( printbuff, "\n Author error in match any: required
number of matches is out of range (%d to %d).", minwords, maxwords );
goto error;
}
if( strlen( answer ) == 0 ) return NOANS;
/* No attempt to answer */
if( strcmp(answer, "?") == 0) return HELPREQ;
/* help requested, no need to test */
else /* analyse answer */
{
setcurrentmp( mps );
strcpy( tidymod, " "); /* initialise string */
strcpy( tidyans, " ");
cleanstr( model, tidymod );
cleanstr( answer, tidyans );
/* break model into tokens */
count = 0;
p = strtok( tidymod, seps );
while( p != NULL )
{
if( count > maxwords)
{
sprintf( printbuff, "\n Author error in match any: number of
words in model is greater\n than the maximum allowed (%d).", maxwords );
goto error;
}
strcpy( modtoks[count], " " ); /* leading space */
strcat( modtoks[count], p );
strcat( modtoks(count], " " ); /* trailing space */
p = strtok( NULL, seps);
count++;
}
wordsinmodel = count;
if( wordsinmodel < reqmatches)
{
sprintf( printbuff, "\n Author error in match_any: required
number of matches (%d) is greater\n than the number of words (%d) in
the model answer.", reqmatches, wordsinmodel );
goto error;
}
/* break answer into tokens */
count = 0;
p = strtok( tidyans, seps );
while( (p != NULL) AND ( count < maxwords ) )
{
strcpy( anstoks[count], /* leading space */
strcat( anstoks[count), p
strcat( anstoks[count), /* trailing space */
p = strtok( NULL, seps);
count++;
}
wordsinans = count;
for( modcount = 0; modcount < wordsinmodel; modcount++ )
/* for each word in model, try to find match in answer */
for( count = 0; count < wordsinans; count++)
/* find first match */
if( test( modtoks[modcount], anstoks[count) == MATCH)
{
wordsfound++; break;
/* break to prevent repetitions in answer being analysed as correct */
}
if( wordsfound >= reqmatches ) return MATCH;
if( wordsfound == 0 ) return NOMATCH;
switch( wordsfound )
{
case 1: return PM1;
case 2: return PM2;
case 3: return PM3;
case 4: return PM4;
case 5: return PM5;
case 6: return PM6;
case 7: return PM7;
case 8: return PM8;
}
} /* endelse analyse answer */
error:
_outtext( printbuff );
_outtext( "\npress any key to continue... ");
getch();
return AE;
}
/* MATCHPRM.FUN */
void set match_params(char *matchstring)
{
/* if B C or P specified in matchstring, then S is implied. */
if(legalsmp( matchstring ) )
{
if( strchr( matchstring, 'S') == NULL)
defaultmp.spelltest = FALSE;
/* WARNING: this IF/ELSE must be at the top, otherwise def.stest
may be reset */
else
defaultmp.spelltest = TRUE;
if( strchr( matchstring, 'B') == NULL)
defaultmp.blanktest = FALSE;
else
{ defaultmp.blanktest = TRUE;
defaultmp.spelltest = TRUE; }
if( strchr( matchstring, 'C') == NULL)
defaultmp.casetest = FALSE;
else
{ defaultmp.casetest = TRUE;
defaultmp.spelltest = TRUE; }
if( strchr( matchstring, 'P') == NULL)
defaultmp.puncttest = FALSE;
else
{
defaultmp.puncttest = TRUE;
defaultmp.spelltest = TRUE;
}
}
else /* not legal smp string */
{
_outtext("\nauthor error in set match arams: illegal character(s).\n
Press any key to continue...");
getch();
}
}
void setcurrentmp(char *matchstring)
{
/* if B C or P specified in matchstring, then S is implied. */
if( strcmp( matchstring, "default") == 0)
currentmp = defaultmp;
else /*set new current mps */
{
if(legalsmp( matchstring ) )
{
if( strchr( matchstring, 'S') == NULL) currentmp.spelltest = FALSE;
/* WARNING: this IF/ELSE must be at the top, otherwise curr.stest
may be reset */
else
currentmp.spelltest = TRUE;
if( strchr( matchstring, 'B') == NULL) currentmp.blanktest = FALSE;
else
{ currentmp.blanktest = TRUE;
currentmp.spelltest = TRUE; }
if( strchr( matchstring, 'C') == NULL) currentmp.casetest = FALSE;
else
{ currentmp.casetest = TRUE;
currentmp.spelltest = TRUE; }
if( strchr( matchstring, 'P') == NULL) currentmp.puncttest = FALSE;
else
{ currentmp.puncttest = TRUE;
currentmp.spelltest = TRUE; }
}
else /* not legal smp string */
{
_outtext("\nAuthor error in match parameter list of a matching
function: \nillegal character (s) . \nPress any key to continue...");
getch();
}
} /* endelse set new mps */
}
enum boolean legalsmp( char* rnpstring)
{
/* NOTE: doesn't check for duplicate characters */
char *ptr = mpstring;
while( *ptr )
{
/* if the character pointed to is in the list, continue */
if( strchr( " ,BCPS", *ptr) != NULL) *ptr++;
else return FALSE; /* not in list */
}
return TRUE;
}
/* MC.FUN */
enum mres mc2( char *st, char *ol, char *o2 )
{ return mc(2, st, ol, o2 ); }
enum mres mc3( char *st, char *ol, char *o2, char *o3 )
{ return mc(3, st, ol, o2, o3 ); }
enum mres mc4( char *st, char *ol, char *o2, char *o3, char *o4 )
{ return mc(4, st, ol, o2, o3, o4 ); }
enum mres mc5( char *st, char *ol, char *o2, char *o3, char *o4, char *o5 )
{ return mc(5, st, ol, o2, o3, o4, o5 ); }
enum mres mc6( char *st, char *ol, char *o2, char *o3, char *o4, char *o5, char *o6 )
{ return mc(6, st, ol, o2, o3, o4, o5, o6 ); }
enum mres mc(int numb_of_optns, char *stem, char *option, ...)
{
#define spacing 2 /* one line between options */
#define minoptns 2 /* minimum number of options allowed */
#define maxoptns 6 /* maximum number of options allowed */
#define maxcols 80 /* need constant, can't use vc.numtextcols */
#define reverse _settextcolor( (short)bgd ); _setbkcolor( (long)fore );
#define normal _settextcolor( fore ); _setbkcolor( bgd );
/* global variable MAIN_R2 = last line of main window */
struct rccoord oldpos;
int curr_row, count, longest, key, startrow, endrow;
short fore;
long bgd;
va_list marker;
char buffer[maxoptns][maxcols];
char printbuff[200];
char *temp;
enum boolean errorfound = FALSE;
enum boolean helpflag = FALSE;
oldpos = _gettextposition(); /* start on new line */
_settextposition(oldpos.row+l, 0);
oldpos = _gettextposition();
if( (oldpos.row + (numb_of_optns * spacing) ) > MAIN_R2
{
errorfound = TRUE;
_outtext( "\nauthor error in mc: not enough screen lines to display
all options\nUse one of the clear functions to remove previous text.\n
Press any key...");
getch();
}
if( NOT errorfound) /* no author errors, continue (1) */
{
fore = _gettextcolor(); /* for reverse video highlight bar */
bgd = _getbkcolor();
_settextposition(oldpos.row, 1);
_outtext( stem );
va_start( marker, option);
/* initialise variable arguments */
strcpy( buffer[0], option);
longest = strlen( option);
count = 1;
while( count < numb_of_optns ) /* collect rest of options */
{
temp = va_arg( marker, char );
strcpy( buffer[count), temp );
if( strlen( buffer[count)) > longest)
longest = strlen( buffer[count] );
count++;
}
va_end( marker );
if( longest > maxcols )
{
errorfound = TRUE;
sprintf( printbuff, "\nauthor error in mc:\nlength of an option
is greater than maximum allowed (%d).\nPress any key...", maxcols);
_outtext( printbuff )
getch();
}
if( NOT errorfound ) /* continue (2) */
{
/* Fill strings with blanks, to length of longest,
so all cursors same length */
for( count = 0; count < numb_of_optns; count++)
{
while( strlen( buffer[count]) < longest )
strcat( buffer[count], " ");
strcat( buffer[count], "\0");
}
/* Get row position for first option, and display it */
oldpos = _gettextposition();
startrow = oldpos.row +spacing;
curr row = startrow;
_settextposition( curr_row, 1);
reverse;
_outtext( buffer[0] );
normal;
/* display remainder of options */
for( count = 1; count < numb_of_optns; count ++)
{
curr_row += spacing;
_settextposition( curr_row, 1 );
_outtext( buffer[count] );
}
endrow = curr_row; /* save this last row, and */
curr_row = startrow; /* move cursor to first option */
_settextposition( startrow, 1);
count = 0; /* current buffer index */
message("Up/down arrows to move bar, Enter to select, Fl = Help.");
_displaycursor( _GCURSOROFF );
while( 1 )
/* move cursor through options until Enter */
{
key = getch();
if(key == 13) break;
if(key == 0)
{
key = getch();
/* extended key was pressed, get second value, test up/down/help */
if(key == HELPKEY ) /* HELPKEY = 59 = Fl */
{
helpflag = TRUE; break;
}
if(key == 72) /* up arrow */
{
normal;
settextposition( curr_row, 1);
/* reset cursor to start of line */
_outtext( buffer(count] );
reverse; /* for next outtext */
if( curr_row = startrow )
{ curr_row == endrow; count = numb_of_optns-1; }
else { curr_row -= spacing; count--; }
_settextposition( curr_row, 1);
_outtext( buffer[count] );
}
if(key == 80) /* down arrow */
{
normal;
_settextposition( curr_row, 1 );
_outtext( buffer[count] );
reverse;
if( curr_row == endrow )
{ curr_row = startrow; count = 0; }
else { curr_row += spacing; count++; }
_settextposition( curr_row, 1 );
_outtext( buffer[count] );
}
} /* endif key == 0 */
} /* endwhile */
_settextposition( endrow+l, 0 );
normal;
_displaycursor( _GCURSORON );
message( " " ); /* clear message area */
if( helpflag ) return HELPREQ;
else
switch( count )
{
case 0: return OPTI;
case 1: return OPT2;
case 2: return OPT3;
case 3: return OPT4;
case 4: return OPT5;
case 5: return OPT6;
}
} /* endif not errorfound (2) */
} /* endif not errorfound (1) */
return AE; /* error has been found */
}
/* MESSAGE.FUN */
void message( char* sometext)
{
char printbuff(200];
if( strlen( sometext ) > ( MESS_C2 - MESS_C1 +1 ) )
{
_clearscreen( _GWINDOW );
sprintf( printbuff, "Author error in message: length of message too
long.\nmaximum %d characters allowed. Your message is %d characters\n
Press any key to continue...", ( MESS_C2 - MESS_Cl +1 ), strlen( sometext ) );
_outtext( printbuff );
getch();
clearscreen( _GWINDOW );
}
else
{
oldpos = _gettextposition();
_settextwindow( MESSCOORD );
_clearscreen( _GWINDOW );
_outtext( sometext);
_settextwindow( MAINCOORD );
settextposition(oldpos.row, oldpos.col);
}
}
/* PAUSE.FUN */
void pause( void )
{
oldpos = _gettextposition();
_settextwindow( MESSCOORD );
_clearscreen( _GWINDOW );
_outtext(" Press any key to continue...");
getch();
_clearscreen( _GWINDOW );
_settextwindow( MAINCOORD );
_settextposition( oldpos.row, oldpos.col );
}
/* PUTCURS.FUN */
void putcursor( int rowpos, int colpos )
{
char printbuff[180];
if( (rowpos < 1 ) OR ( rowpos > (MAIN_R2 - MAIN_R1 +1) ) OR
(colpos < 1 ) OR ( colpos > (MAIN-C2 - MAIN-C1 +1) ) )
{
_clearscreen( _GWINDOW );
sprintf( Printbuff, "Author error in putcursor; values out of range.\n
\nRow must be between 1 and %d inclusive\nCol must be between 1 and
%d inclusive.\nYou set row = %d, col %d", (MAIN_R2 - MAIN_R1 + 1),
(MAIN_C2 - MAIN_C1 +1), rowpos, colpos);
_outtext( printbuff);
_outtext("\nPress any key to continue...");
getch();
clearscreen( _GWINDOW );
}
else
_settextposition( rowpos, colpos);
}
/* TEST_VI.FUN */
enum mres test( char* modelstr, char* answerstr )
{
/* This is the matcher (version 1). It does not deal withwildcards or
wild characters. It returns either MATCH or NOMATCH. MATCH indicates
an acceptable match, NOMATCH indicates failure to match. Only one
error is allowed in the answer string.
If the length of the model and answer strings are the same, then one
of three possibilities exists for an acceptable match; a perfect match,
a substitution error, or a transposition error. If the length of the
model is less than the length of the answer, then a possible Insertion
error has been made. If the length of the model is greater than the
length of the answer, then a possible omission error has been made.
If none of these situations is detected, the match fails. */
int result, length, modelcount, answercount;
enum boolean errorfound = FALSE;
if( ! currentmp.spelltest )
{
if( strlen( answerstr ) == strlen( modelstr ))
/* test for Perfect match, Substitution, Transposition */
{
length = strlen( modelstr ); modelcount = 0;
while( (! errorfound) AND (modelcount < length) )
{
if(answerstr[modelcount] == modelstr[modelcount]) modelcount++;
else /* error found */
{
errorfound = TRUE;
if((answerstr[modelcount] == modelstr[modelcount+l]) AND
(answerstr[modelcount+l] == modelstr[modelcountl])) modelcount += 2;
/* transposition error */
else
modelcount++; /* substitution or other error */
} /* endelse error found */
} /* endwhile not error found */
while(modelcount < length) /* test rest of string */
if(answerstr[modelcount] == modelstr[modelcount])
modelcount++;
else
return NOMATCH; /* another error, abandon check */
return MATCH; /* no more errors found */
} /* endif (test for P, S, T) */
/* test for single Omission */
if( strlen( answerstr ) == ( strlen( modelstr ) -1 ) )
{
modelcount = 0;
length = strlen( answerstr );
for( answercount = 0; answercount < length; answercount++)
{
if( answerstr[answercount] == modelstr[modelcount] ) modelcount++;
/* OK so far */
else /* error found */
{
if( (! errorfound AND /* first error */
( answerstr[answercount] == modelstr[modelcount+l]) )
/* and if following char is not error, continue */
{
errorfound = TRUE;
modelcount += 2; /* ie, skip one char */
}
else
/* This is not the first error, or the following character is
also an error - abandon checking */
return NOMATCH;
} /* end else error found */
} /* end for */
return MATCH;
/* if we've got this far, only one omission found */
} /* end if (test for omission */
/* test for single Insertion */
if( strlen(answerstr) == (strlen(modelstr)+l) )
{
answercount = 0;
length = strlen( modelstr );
for( modelcount = 0; modelcount < length; modelcount++)
{
if( answerstr[answercount) == modelstr[modelcount] ) answercount++;
else /* error found */
{ /* if this is the first error */
if( ( ! errorfound ) AND
( answerstr[answercount+l] == modelstr[modelcount] ) )
/* and if following char is not error, continue */
{
errorfound = TRUE;
answercount += 2; /* ie, skip one char */
}
else
/* This is not the first error, or the following character is
also an error - abandon checking */
return NOMATCH;
} /* end else error found */
} /* end for */
return MATCH;
/* if we've got this far, only one insertion found */
} /* end if (test for insertion ) */
return NOMATCH; /* not P, S, T, 0, or I , therefore fail */
} /* endif not spelltest */
else
/* no spell check required, compare strings, and return result */
{
result = strcmp(modelstr, answerstr);
if( result == 0) return MATCH;
else return NOMATCH;
}
}
/* ANALYSTR.FUN */
void analyse_string( char* model, char matchres )
{
/* This function is redundant, and is not included in the
PAL authoring package. It is replaced by branching functions (if and goto) */
char prnbuff[200];
_outtext("\n This function (analyse_string) is for prototyping purposes
only.\n In practice it would be replaced by branching functions.\n");
switch( matchres) /* build output message */
{
case 'T' :
/* perfect match, or everything required found */
_outtext("\nYour answer is acceptable.");
break;
case 'F' : /* total failure */
sprintf( prnbuff, "\nYour answer is not acceptable, the correct
answer is:\n %s", model);
_outtext( prnbuff );
break;
case 'N' : /* no attempt */
sprintf( prnbuff, "\nYou did not attempt to answer, the correct
answer is:\n %s", model);
outtext( prnbuff );
break;
case '?' : /* help requested */
_outtext("\n help requested, none available. ");
break;
/* partially correct answer */
case '1' : case '2' : case '3' : case '4' :
case '5' : case '6' : case '7' : case '8' :
sprintf( prnbuff, "\n Your answer is partially correct; you
found %c item(s). The full list is:\n %s", matchres, model);
_outtext( prnbuff );
break;
case 'X' : /* author error in a previous function */
_outtext("\n no response - author error in previous function.");
break;
default :
/* error - but should never happen, but if it did, the program
should terminate */
_outtext("\n Error - illegal matchresult");
}
_outtext("\n Press any key to continue...");
getch();
clearscreen( _GWINDOW );
}
/* SHOWPARM.FUN */
void showparms()
{
/* This function was used during testing to check the operation of
setcurrentmp when it is called by the matching functions. */
char prnbuff[80] = " ";
if(currentmp.blanktest) strcat( prnbuff, "blanktest ON ");
else strcat( prnbuff, "blanktest OFF ");
if(currentmp.casetest) strcat( prnbuff, "casetest ON ");
else strcat( prnbuff, "casetest OFF ");
if(currentmp.puncttest) strcat( prnbuff, "puncttest ON ");
else strcat( prnbuff, "puncttest OFF ");
if(currentmp.spelltest) strcat( prnbuff, "spelltest ON ");
else strcat( prnbuff, "spelltest OFF ");
{ /* this is equivalent to header, without AE test */
oldpos = _gettextposition();
_settextwindow( HEADCOORD );
_clearscreen( _GWINDOW );
_outtext( prnbuff);
_settextwindow( MAINCOORD );
settextposition(oldpos.row, oldpos.col);
}
}
/* TIDY.BAT */
echo off
rem TIDY.BAT
rem this batch file deletes unwanted sym, ilk, mdt, and
rem obj files.
echo
echo Removing ILK SYM MTD and OBJ files. Please wait...
rem replaceable parameters are converted to uppercase.
for %%x in (*.obj) do if not %%x == PALFUN.OBJ del %%x
for %%x in (*.ilk) do del %%x
for %%x in (*.sym) do del %%x
for %%x in (*.mdt) do del %%x
echo
echo Done - all unwanted files removed.
/* PROTO.PRO */
/* This is the prototype for the spelling assistant. It deals with the
four major types of keyboard error. Spaces, commas, and full stops
are removed from the model and answer. Case is important. Wildcards
and wild characters cannot be used.
The user is prompted to input a model, and then an answer.
The implementation version is Turbo Prolog, version 1.0, which
purists do not consider to be true prolog. */
domains
charlist=char*
predicates
string_chlist(string,charlist)
perfect(charlist,charlist)
omission(charlist,charlist)
insertion(charlist,charlist)
substitution(charlist,charlist)
transposition(charlist,charlist)
go
test(charlist,charlist)
charmatch(charlist,charlist)
/* charmatch is fix for transposition pred */
clean(charlist,charlist)
remove(char)
goal
go.
clauses
string_chlist("",[]).
string_chlist(S,[H|T]):-
frontchar(S,H,S1), string_chlist(S1,T).
go:-
write(" input model: "),
readln(Model),
write("input answer: "),
readln(Answer),
string_chlist(Model, Mod_chlist),
string_chlist(Answer,Ans_chlist),
clean(Mod_chlist,Clean_mod),
clean(Ans_chlist,Clean_ans),!,
Write("\nclean model = ",Clean_mod),
write("\nclean answer = ",Clean_ans),
test(Clean_mod,Clean_ans).
test(Model,Answer):- perfect(Model,Answer).
test(Model,Answer):- omission(Model,Answer).
test(Model,Answer):- insertion(Model,Answer).
test(Model,Answer):- substitution(Model,Answer).
test(Model,Answer):- transposition(Model,Answer).
perfect(Mod_ans,Mod_ans):- nl, write("perfect match ").
omission([H|T1],[H|T2)):- omission(Tl,T2).
omission([_|T1,T):- nl, write("omission ").
insertion([H|TI],[H|T2]):- insertion(TI,T2).
insertion(T,[_|T]):- nl, write("insertion ").
substitution([H|T1],[H|T2]):- substitution(TI,T2).
substitution([_|T],[_|T]):- nl, write("substitution error ").
transposition([H|T1],[H|T2]):- transposition(TI,T2).
transposition([_|T],[_|T2]):- charmatch(T,T2), nl, write("tranposition ").
charmatch([_|H],[_|H]).
clean([],[]).
clean([C|Restl],[C|Rest2]):- remove(C), clean(Restl,Rest2).
clean([_|Rest], Rest2):- clean(Rest,Rest2).
remove(C):- C <> ' ', C <> '.', C <> ','.
Preface | Contents | 1 Introduction | 2 Review | 3 Req. analysis | 4 Req. documents | 5 Specification | 6 Design | 7 Verification | 8 Discussion | 9 PAL manual | Appendix A | Appendix B | Appendix C | Glossary | References | Index