COMMENT ------------------------------------------------- GNOSIS provides a simple CAI authoring language which makes it easy for any teacher with a basic understanding of "programmed" textbooks to develop computerized tutorials for his students. Although material already written in such a format can be transformed virtually as-is by GNOSIS into a form suitable for computer delivery, the teacher would normally be writing his own "script." This script, because it simulates the dialogue one might overhear if that teacher were tutoring a student in private, can be made to "come to life" on a computer terminal by the addition of a few simple GNOSIS command words. The computer driven tutorial is, in reality, a compiled version of the ALGOL program GNOSIS writes in response to the script prepared by the teacher. As in the ordinary tutoring environment, the student's path through the material can be guided in various and subtle ways, e.g., by routing students having difficulty to supplementary material. The program qua tutor can also react in helpful ways to a variety of anticipated false answers to questions, e.g., by giving appropriate diagnostic commentary or hints, followed by a repetition of the wrongly answered question. Although unanticipated responses can be saved for inclusion in the teacher reports (along with statistics on individual student performance), the data which are generated exist for the sake of improving the lesson pedagogically. This is in keeping with the general design philosophy that GNOSIS should be a TEACHING system rather than a TESTING system. If anyone makes any additions or modifications to the GNOSIS translator, then please send a copy either to (1)Jacob Palme, Research Institute of National Defense, S-10450 Stockholm 80, Sweden, or (2)Dr. Walter Maner, Department of Philosophy, Old Dominion University, Norfolk, Virginia 23508, USA. With your cooperation, we can prepare more powerful versions of GNOSIS for distribution to GNOSIS users like yourself. THANK YOU. ALPHABETICAL LIST OF GLOBAL VARIABLES ===================================== NAME TYPE CONTENT ------------------------------------------------------------------- A INTEGER A ALLNEUTRAL BOOLEAN TRUE IF ALL ANSWER PATTERNS SO FAR TO THIS . QUESTION HAVE BEEN NEUTRAL. THIS INFLUENCES . THE WORDING OF MESSAGES FOR UNEXPECTED STUDENT . ANSWERS. ANSWER STRING BUFFER FOR INPUT LINES OF THE LESSON. ONE LINE . AT A TIME IS STORED IN THIS BUFFER. ANSWERLENGTH INTEGER LENGTH OF LESSON LINE. BELL BOOLEAN TRUE IF LESSON CONTAINS A %BELL COMMAND . RINGS BELL ON STUDENT'S TERMINAL WHEN INPUT . FROM HIM IS REQUIRED. BLANK INTEGER CARRIAGERETURN INTEGER 13 CHARVALUE INTEGER TEMPORARY STORAGE OF INTEGRAL VALUE OF ONE . CHARACTER IN A STRING WHICH IS . BEING OUTPUTTED. COLON INTEGER : CONTROLCHAR INTEGER BINARY VALUE OF THE FIRST CHARACTER IN THE . LESSON. THIS CHARACTER BEGINS ALL COMMAND . LINES. IT IS USUALLY = "%". CONTROLINLINE BOOLEAN CONTROLCHAR INSIDE INPUT LINE. CONTROLSTART STRING SUBSTRING OF CHARACTER 2-4 OF COMMAND. COPYRIGHT STRING LESSON AUTHOR'S COPYRIGHT NOTICE DISK BOOLEAN TRUE IF LESSON CONTAINS A %DISK COMMAND. . SENDS TEACHER REPORT TO A DISK FILE . WITH THE NAME "LESSONNAME.DTA". DOT INTEGER . DOUBLEQUOTE INTEGER " EMPTY BOOLEAN TRUE IF TEACHER ANSWER PATTERN IS EMPTY. EQUALSIGN INTEGER = ERRLINES INTEGER COUNTER OF LINES TO BE PRINTED AFTER ERROR. EXCLAMATION INTEGER ! EXTRA BOOLEAN ARE EXTRA WORDS PERMITTED IN STUDENT ANSWERS? FIRSTRIGHT STRING FIRST RIGHT ANSWER TO A QUESTION. FORMFEED INTEGER 12 IFSET BOOLEAN ARRAY IF ON GNOSIS-TIME SWITCH IS ACTIVE. IFVAL BOOLEAN ARRAY VALUE OF IF ON GNOSIS-TIME SWITCH. INFILE STRING FILENAME WITH EXTENSION OF INPUT FILE. JUSTIFY BOOLEAN TRUE IF TEXT BLOCKS ARE TO BE RIGHT . JUSTIFIED ("RAGGED RIGHT") AT COLUMN 72 KEEP BOOLEAN KEEP TEACHER REPORTS AT COMPUTING CENTER, . DO NOT ASK THE STUDENT TO SEND THEM IN. LAB STRING USED FOR GENERATION OF LABELS IN THE PRODUCED LABELCOUNT INTEGER COUNTER OF NUMBER OF MAINLABS. LACK BOOLEAN TRUE AFTER A %LACK COMMAND. LANGUAGE INTEGER LANGUAGE = 1 -> LESSON IS IN ENGLISH, . (ENGLISH IS THE DEFAULT) . LANGUAGE = 2 -> LESSON IS IN SWEDISH. LCAA INTEGER } = BINARY VALUE OF SWEDISH LETTER LCAE INTEGER { LCE INTEGER e LCOE INTEGER ` LEFTSQUARE INTEGER [ LESSONNAME STRING FILENAME OF THE LESSON WITH EXTENSION REMOVED. . THIS NAME WITH THE EXTENSION ".ALG" IS THE . NAME OF THE OUTPUT FILE. LINEFEED INTEGER 10 = BINARY VALUE OF ASCII CHARACTER LINENUMBER STRING LINENUMBER FOR LINENUMBERED FILES. LINENUMBERED BOOLEAN THE GNOSIS TEXT IS A LINE-NUMBERED FILE. LOCK BOOLEAN TRUE IF LESSON CONTAINS A %LOCK COMMAND. . DISABLES BACKSTEPPING AND SKIPPING . PROVISIONS OF GNOSIS. MAINLAB STRING USED FOR GENERATION OF LABELS IN THE PRODUCED . ALGOL PROGRAM, TO BE USED FOR BACKSTEPPING. NAME BOOLEAN TRUE IF LESSON CONTAINS A %NAME COMMAND. . STUDENT'S FIRST NAME USED FOR I/O AND . HIS FULL NAME GOES TO TEACHER REPORT (IF ANY). NEUTRAL BOOLEAN TRUE FOR A %NEUTRAL COMMAND. OPENED BOOLEAN TRUE WHEN THE OUTPUT FILE FROM GNOSIS . HAS BEEN OPENED AND THE PREAMBLE OF THE . ALGOL TRANSLATION OF THE LESSON HAS BEEN . OUTPUTTED. ORDER BOOLEAN MUST STUDENT ANSWER WORDS BE IN ORDER? POS INTEGER POSITION IN THE TEXT OF THE LESSON LINE. POS2 INTEGER POSITION IN THE TEXT OF THE LESSON LINE. PREVIOUSLAB STRING LABEL FOR PREVIOUS TEXT OR QUESTION. GNOSIS . WRITES INTO LESSON A GOTO STATEMENT WITH THIS . LABEL AS ARGUMENT SO STUDENT MAY BACK UP IN . THE LESSON ONE ITEM AT A TIME. PUT BOOLEAN TRUE IF THE LESSON CONTAINED A %TEACHER COMMAND, . SO THAT TEACHER REPORTS ARE TO BE GENERATED. QCOUNT INTEGER COUNTER OF THE NUMBER OF QUESTIONS . (%QUESTION COMMANDS) IN THE LESSON. QUESTION STRING QUESTION IDENTIFICATION WHICH IS OUTPUT . ON THE TEACHER REPORTS TO SHOW HIM WHICH . QUESTION GAVE UNEXPECTED ANSWERS. . ALGOL PROGRAM, TO BE USED INSIDE QUESTIONS. RIGHT BOOLEAN TRUE FOR A %RIGHT COMMAND, FALSE FOR . A %WRONG COMMAND. RIGHTFOUND BOOLEAN TRUE IF ANY RIGHT ANSWER HAS BEEN FOUND . TO THE CURRENT OR PREVIOUS QUESTION. RIGHTSQUARE INTEGER ] SAME STRING LABEL TO JUMP TO IF A %SAME COMMAND IS . ENCOUNTERD. SAVEANLEN INTEGER SAVED VALUE OF ANSWERLENGTH. SEMICOLON INTEGER ; COMMENT SGNOSIS BOOLEAN DO NOT PRINT INITIAL MESSAGE TELLING THE . STUDENT THAT HE IS RUNNING GNOSIS. SKIP BOOLEAN SKIP INPUT LINES BECAUSE OF GNOSIS-TIME . SWITCH SETTINGS. SWITCHCOUNT INTEGER NUMBER OF GNOSIS-TIME SWITCHES. SWITCHNAME STRING ARRAY NAME OF ALL GNOSIS-TIME SWITCHES. SWITCHNUMBER INTEGER INDEX OF ONE GNOSIS-TIME SWITCH. SWITCHVAL BOOLEAN ARRAY SETTING OF GNOSIS-TIME SWITCH VALUES. SWITCHWORD STRING NAME OF ONE GNOSIS-TIME SWITCH. TEACHER STRING NAME AND ADDRESS OF THE TEACHER. TEMP TEMPORARY STRING. TEXCOUNT INTEGER COUNTER OF THE NUMBER OF TEXT SEGMENTS . (%TEXT COMMANDS) IN THE LESSON UCAA INTEGER $ UCAE INTEGER # UCE INTEGER E UCOE INTEGER @ UPARROW INTEGER ^ WORD STRING ONE WORD FROM A TEACHER ANSWER PATTERN. WPOS INTEGER POSITION IN THE TEXT OF THE LESSON LINE. Z INTEGER Z ZEROPRINTED BOOLEAN A TEST FOR EMPTY STUDENT ANSWERS . HAS BEEN PRINTED FOR THIS QUESTION. ------------------------------------------------------------------- ; BEGIN INTEGER controlchar, language, carriagereturn, linefeed, tabchar, blank, exclamation, answerlength, saveanlen, pos, pos2, wpos, charvalue, doublequote, a, z, formfeed, dot, qcount, texcount, semicolon, colon, equalsign, errlines, labelcount, linecount, leftsquare, rightsquare, switchcount, switchnumber, rightmargin, lcaa, lcae, lcoe, ucaa, ucae, ucoe, lce, uce; STRING controlstart, word, lessonname, copyright, teacher, answer, firstright, previouslab, nextlesson, temp, question, lab, mainlab, same, infile, switchword, linenumber; STRING ARRAY switchname[1:9]; BOOLEAN extra, empty, order, right, neutral, allneutral, opened, put, sgnosis, nocontrolc, transferlesson, justify, controlinline, bell, name, lock, disk, lack, zeroprinted, keep, rightfound, skip, linenumbered; BOOLEAN ARRAY switchval[1:9], ifset[0:9], ifval[1:9]; FORWARD PROCEDURE makemainlab, getcontrolstart, upcase, pastoperation; FORWARD STRING PROCEDURE getword; PROCEDURE delet(s); STRING s; COMMENT ------------------------------------------------- Delet will delete non-empty STRINGs, but will not (like Delete) cause an error if an attempt is made to delete an empty string. -----------------------------------------------------------; IF Length(s) > 0 THEN Delete(s); PROCEDURE errmess(toggle, message); BOOLEAN toggle; VALUE message; STRING message; COMMENT ------------------------------------------------- This procedure causes a GNOSIS error (or warning) message to be displayed. The troublesome line and the next three lines of the lesson are displayed on the terminal. If the souce file contains line numbers, these will be displayed as reference points. If it does not, then the APPROXIMATE line numbers which are generated by the 'getline' PROCEDURE will be displayed. The identification number of the GNOSIS error message corresponds to the line in THIS file (i.e., GNOSIS.ALG) from which this error procedure was called. This is useful for debugging purposes since same kind of error can occur at many different points (e.g., errors in precedence). -----------------------------------------------------------; BEGIN Selectoutput(0); IF errlines = 0 THEN BEGIN IF linenumbered THEN BEGIN temp:= Copy(linenumber); Write(temp); delet(temp) END ELSE BEGIN Write("LINE "); Print(linecount - 1, 5); Write(": ") END; temp:= Copy(answer,1,answerlength); Write(temp); Newline; IF answerlength > 0 THEN delet(temp) END; errlines:= 3; IF toggle THEN Write("****** ERROR Message No. ") ELSE Write("****** WARNING Message No. "); Write(message); Newline; Selectoutput(2) END of PR*CEDURE errmess; PROCEDURE getline; COMMENT ------------------------------------------------- 'Getline' reads in a line of the lesson script. Any sequence of characters (including the null sequence) which is terminated by a or constitutes a line. A immediately after a will not cause any blank line. This procedure ignores any line beginning with a '!' in column one, thus conforming to DEC comment conventions. -----------------------------------------------------------; BEGIN INTEGER s, t, i; BOOLEAN taberror; getnextline: linecount:= linecount+1; taberror:= FALSE; IF linenumbered THEN BEGIN FOR i:= 1 STEP 1 UNTIL 5 DO BEGIN Insymbol(t); linenumber.[i]:= t END; Insymbol(t); taberror:= t > 32 END; controlinline:= FALSE; FOR i:= 1 STEP 1 UNTIL 133 DO BEGIN loop: Insymbol(t); answer.[i]:= t; IF t = controlchar THEN BEGIN IF i # 1 THEN controlinline:= TRUE END; IF t = linefeed THEN GOTO out; IF t = formfeed THEN BEGIN Outsymbol(t); IF i = 1 THEN GOTO loop; i:= i+1; GOTO out END END; errmess(FALSE, "0316: Long input line will be truncated to 132 characters."); out: answerlength:= i-2; IF answer.[1] = exclamation THEN GOTO getnextline; IF taberror THEN errmess(FALSE, "0322: Character after line-number is not TAB.") ELSE IF controlinline THEN errmess(FALSE, "0324: Command character not first in line."); IF errlines > 0 THEN BEGIN errlines:= errlines-1; Selectoutput(0); IF linenumbered THEN BEGIN temp:= Copy(linenumber); Write(temp); delet(temp) END ELSE BEGIN Write("LINE "); Print(linecount - 1, 5); Write(": ") END; temp:= Copy(answer,1,answerlength); Write(temp); Newline; IF answerlength > 0 THEN delet(temp); IF errlines = 0 THEN Write("----------------------------------------------------------------------[N]"); Selectoutput(2) END; COMMENT ------------------------------------------------- Now we check to see whether the current line is an %IF, %IFNOT or %IFEND command line and, if so, "skip" will be reset accordingly. "Skip" is the BOOLEAN which GNOSIS checks to determine whether or not to skip certain lines. -----------------------------------------------------------; IF answer.[1] # controlchar THEN GOTO noif; getcontrolstart(3); IF controlstart = "IF" THEN COMMENT -------------------------------------------------- Since the parameter passed to 'getcontrolstart' was 3 rather than the usual 4, %IF, %IFNOT and %IFEND will look the same at this point. (They do not differ in their first three characters.) -----------------------------------------------------------; BEGIN pastoperation; switchword:= getword; upcase(switchword); IF Length(switchword) <= 0 THEN errmess(TRUE, "0368: No switchname in %IF-class command.[N]****** Line will be ignored."); FOR switchnumber:= 1 STEP 1 UNTIL switchcount DO IF switchname[switchnumber] = switchword THEN GOTO found; errmess(TRUE, "0371: Unknown switchname in %IF-class command.[N]****** Line will be ignored."); GOTO getnextline; found: IF answer.[4] = lce OR answer.[4] = uce THEN COMMENT -------------------------------------------------- Since this command line began with "%IF" and had an "E" in the fourth position, it must be an %IFEND command. This command turns off the %IF/%IFNOT facility. In other words, the SCOPE of each %IF or %IFNOT extends to the next occurrence of a %IFEND command. Thus, all text following %IFEND will be included in the translated lesson as usual, regardless of previous %IF's or %IFNOT's. Subsequent %IF's or %IFNOT's will, of course, create another need for an %IFEND to define THEIR scope -- unless that scope extends all the way to the end of the lesson. %IFEND must therefore follow some %IF or %IFNOT command. -----------------------------------------------------------; BEGIN IF NOT ifset[switchnumber] THEN errmess(FALSE, "0392: %IFEND on non-active switch -- will have no effect."); ifset[switchnumber]:= FALSE END ELSE BEGIN ifset[switchnumber]:= TRUE; ifval[switchnumber]:= answer.[4] = blank COMMENT -------------------------------------------------- The line above handles both %IFNOT and %IF command lines. If no blank follows the "IF", then the appropriate item in the BOOLEAN ARRAY 'ifval' is switched FALSE since the trailing character (in position 4) can only be an "N" (i.e., a %IFNOT command line). On the othr hand, if a blank does follow the "IF", then the item is switched TRUE (i.e., since the command was a %IF). Assuming has been declared and initialized by a %SWITCH command, the occurrence of %IF will cause the text between the %IF and the next %IFEND to be deposited in the translated lesson ONLY if was initialized TRUE. Otherwise these lines of text will be skipped. Correspondingly, the text between %IFNOT and the next %IFEND command will be included in the ALGOL translation of the lesson ONLY if is FALSE. -----------------------------------------------------------; END; skip:= FALSE; FOR switchnumber:= 1 STEP 1 UNTIL switchcount DO skip:= skip OR ( ifset[switchnumber] AND NOT ( ifval[switchnumber] EQV switchval[switchnumber] )); GOTO getnextline END ELSE noif: IF skip THEN GOTO getnextline; pos:= 1 END of PR*CEDURE getline; PROCEDURE upcase(s); STRING s; COMMENT -------------------------------------------------- 'Upcase' will transform all letters in the parameter STRING from lower case to upper case. If the language of the lesson is swedish, then the swedish letters }, { and ` will also be transformed to upper case -----------------------------------------------------------; BEGIN INTEGER t, i; FOR i:= 1 STEP 1 UNTIL Length(s) DO BEGIN IF s.[i] >= 97 AND s.[i] <= 122 THEN s.[i]:= s.[i]-32 ELSE IF language = 2 THEN BEGIN IF s.[i] = lcaa THEN s.[i]:= ucaa ELSE IF s.[i] = lcae THEN s.[i]:= ucae ELSE IF s.[i] = lcoe THEN s.[i]:= ucoe END END END of PR*CEDURE upcase; PROCEDURE getcontrolstart(i); VALUE i; INTEGER i; COMMENT -------------------------------------------------- 'Getcontrolstart' extracts that part of a command line which contains the beginning of the command text. This beginning determines what GNOSIS will do until it encounters another command line. The PROCEDURE also passes over unrecognized commands and any non-command line(s) which may follow them. This ensures that the error scan will be able to generate meaningful messages for the user and, with a little luck, may enable GNOSIS to produce a program the ALGOL compiler will accept despite user error. -----------------------------------------------------------; BEGIN repeat: delet(controlstart); controlstart:= Copy(answer,2,i); upcase(controlstart); IF i = 4 THEN BEGIN COMMENT -------------------------------------------------- We scan down this list, eliminating the most frequently used GNOSIS commands first. The semicolon after 'THEN' is a dummy statement used to outwit the compiler (it still complains some). This is as close as we can come to a case statement in ALGOL. -----------------------------------------------------------; IF controlstart = "RIG" THEN ; ELSE IF controlstart = "WRO" THEN ; ELSE IF controlstart = "SAM" THEN ; ELSE IF controlstart = "ORD" THEN ; ELSE IF controlstart = "NOO" THEN ; ELSE IF controlstart = "EXT" THEN ; ELSE IF controlstart = "NOE" THEN ; ELSE IF controlstart = "QUE" THEN ; ELSE IF controlstart = "NEU" THEN ; ELSE IF controlstart = "LAC" THEN ; ELSE IF controlstart = "GO " THEN ; ELSE IF controlstart = "GOT" THEN ; ELSE IF controlstart = "TEX" THEN ; ELSE IF controlstart = "JUS" THEN ; ELSE IF controlstart = "NOJ" THEN ; ELSE IF controlstart = "HEL" THEN ; ELSE IF controlstart = "VAR" THEN ; ELSE IF controlstart = "PRO" THEN ; ELSE IF controlstart = "INI" THEN ; ELSE IF controlstart = "ALG" THEN ; ELSE IF controlstart = "QEN" THEN ; ELSE IF controlstart = "END" THEN ; ELSE IF controlstart = "TEA" THEN ; ELSE IF controlstart = "NAM" THEN ; ELSE IF controlstart = "KEE" THEN ; ELSE IF controlstart = "BEL" THEN ; ELSE IF controlstart = "DSK" THEN ; ELSE IF controlstart = "DIS" THEN ; ELSE IF controlstart = "LOC" THEN ; ELSE IF controlstart = "COP" THEN ; ELSE IF controlstart = "LAN" THEN ; ELSE IF controlstart = "SGN" THEN ; ELSE IF controlstart = "SWI" THEN ; ELSE IF controlstart = "NOC" THEN ; ELSE IF controlstart = "NEX" THEN ; ELSE IF controlstart = "FIN" THEN ; ELSE IF controlstart = "IF " THEN ; ELSE IF controlstart = "IFE" THEN ; ELSE IF controlstart = "IFN" THEN ; ELSE BEGIN errmess(TRUE, "0527: Unrecognized command line found. As a result,[N]****** this line up to next command line will be ignored."); getline; WHILE answer.[1] # controlchar DO getline; i:= 4; GOTO repeat END END END of PR*CEDURE getcontrolstart; STRING PROCEDURE getword; COMMENT -------------------------------------------------- 'Getword' scans the input line, beginning at 'pos', and finds a word. For purposes of this PROCEDURE, any STRING of non-blank characters terminated by a blank or a will constitute a word. Leading and trailing blanks are not assumed to be part of the word. 'Getword' will move 'pos' to the end of the found word. -----------------------------------------------------------; BEGIN INTEGER pos1, t; pos1:= pos; IF pos1 # 1 THEN GOTO blankloop; textloop: t:= answer.[pos1]; IF t = carriagereturn THEN GOTO null; IF t # blank THEN BEGIN pos1:= pos1+1; GOTO textloop END; blankloop: t:= answer.[pos1]; IF t = carriagereturn THEN GOTO null; IF t = blank THEN BEGIN pos1:= pos1+1; GOTO blankloop END; IF answer.[pos1] = carriagereturn THEN GOTO null; pos:= pos1+1; wordloop: t:= answer.[pos]; IF t # blank AND t # carriagereturn THEN BEGIN pos:= pos+1; GOTO wordloop END; getword:= Copy(answer,pos1,pos-1); GOTO out; null: getword:= ""; out: END of PR*CEDURE getword; PROCEDURE alglin(s); VALUE s; STRING s; COMMENT -------------------------------------------------- 'Alglin' will dump the STRING passed to it as a parameter into the translated lesson. Since this parameter could contain characters which are not supposed to have their usual ALGOL function (i.e., the semicolon, the left and right square bracket, and the doubequote), we could not take advantage of the built-in PROCEDURE 'Write'. A is output after the STRING. -----------------------------------------------------------; BEGIN FOR wpos:= 1 STEP 1 UNTIL Length(s) DO Outsymbol(s.[wpos]); Newline END of PR*CEDURE alglin; PROCEDURE textline(s); VALUE s; STRING s; COMMENT -------------------------------------------------- 'Textline' will output a line in an Algol 10 program. This line contains a call to the Algol 10 PROCEDURE "Write" such that the input STRING is written. Example: If the input STRING is "ABCD[I]" then this PROCEDURE will output the line "Write("ABCD[[I]]"). We developed this PROCEDURE so that we would not go bonkers trying to nest square brackets and doublequotes -- not to mention a 'Write' inside another 'Write'. However, the program was mostly finished before we began to take advantage of it. -----------------------------------------------------------; BEGIN INTEGER pos, t; Write("Write("""); FOR pos:= 1 STEP 1 UNTIL Length(s) DO BEGIN t:= s.[pos]; IF t = doublequote OR t = semicolon OR t = leftsquare OR t = rightsquare THEN Outsymbol(t); Outsymbol(t) END; Write("[[N]]"");;[N]") END of PR*CEDURE textline; PROCEDURE textlines; COMMENT -------------------------------------------------- 'Textlines' will take text from the GNOSIS lesson and translate that text to an Algol 10 program sequence which, in turn, will write that text to the user. Succesive lines from the GNOSIS lesson are translated until a line is found which contains a GNOSIS command. In %NOJUSTIFY mode, GNOSIS watches vertical spacing only, ensuring that scrolling will cease as soon as the screen is full. (A call to 'pause' is inserted for this purpose.) In %JUSTIFY mode, GNOSIS not only watches vertical spacing but also edits the text in such a way that a semi-justified right margin is produced (at or before column 72). In this mode, GNOSIS will be sensitive to the occurrence of the semicolon, right and left square bracket, and the doublequote. This sensitivity will probably be removed in later versions. -----------------------------------------------------------; BEGIN INTEGER outpos, screenlines; STRING nextword; BOOLEAN writingaline, morewords; STRING PROCEDURE getnextword; COMMENT -------------------------------------------------- Same as 'getword' except does not scan past first word in 'answer'. -----------------------------------------------------------; BEGIN INTEGER pos1, t; pos1:= pos; t:= answer.[pos1]; IF t = carriagereturn THEN GOTO null; blankloop: t:= answer.[pos1]; IF t = carriagereturn THEN GOTO null; IF t = blank OR t = tabchar THEN BEGIN pos1:= pos1+1; GOTO blankloop END; IF answer.[pos1] = carriagereturn THEN GOTO null; pos:= pos1+1; wordloop: t:= answer.[pos]; IF t # blank AND t # carriagereturn THEN BEGIN pos:= pos+1; GOTO wordloop END; getnextword:= Copy(answer,pos1,pos-1); GOTO out; null: getnextword:= ""; out: END of PR*CEDURE getnextword; getline; IF NOT justify THEN BEGIN screenlines:= 0; WHILE answer.[1] # controlchar DO BEGIN IF answerlength = 0 THEN BEGIN screenlines:= screenlines + 1; IF screenlines < 22 THEN Write("Newline;;[N]") ELSE BEGIN screenlines:= 0; Write("pause;;[N]") END END ELSE BEGIN screenlines:=screenlines + 1; IF screenlines > 21 THEN BEGIN Write("pause;;[N]"); screenlines:= 0 END; textline((temp:=Copy(answer,1,answerlength))); delet(temp) END; getline END END ELSE BEGIN writingaline:= FALSE; screenlines:= 0; outpos:= 1; WHILE answer.[1] # controlchar DO BEGIN IF answerlength = 0 THEN BEGIN IF writingaline THEN BEGIN Write("[[N]]"");;[N]"); screenlines:= screenlines + 1; IF screenlines < 22 THEN BEGIN Write("Newline;;[N]"); screenlines:= screenlines + 1 END ELSE BEGIN Write("pause;;[N]"); screenlines:= 0 END; writingaline:= FALSE; outpos:= 1 END ELSE BEGIN screenlines:= screenlines + 1; IF screenlines < 22 THEN BEGIN Write("Newline;;[N]"); screenlines:= screenlines + 1 END ELSE BEGIN Write("pause;;[N]"); screenlines:= 0 END END END ELSE BEGIN morewords:= TRUE; IF NOT writingaline THEN Write("Write("""); writingaline:= TRUE; WHILE morewords DO BEGIN delet(nextword); nextword:= getnextword; IF nextword = "" THEN morewords:= FALSE ELSE BEGIN outpos:= outpos + Length(nextword) + 1; IF outpos < rightmargin THEN BEGIN Write(nextword); Write(" ") END ELSE BEGIN Write("[[N]]"");;[N]"); screenlines:= screenlines + 1; IF screenlines > 21 THEN BEGIN Write("pause;;[N]"); screenlines:= 0 END; outpos:= Length(nextword) + 1; Write("Write("""); Write(nextword); Write(" ") END END END morewords loop; END writing cycle for string currently in "answer"; getline END WHIL* answer.[1] # controlchar; IF writingaline THEN Write("[[N]]"");;[N]") END; IF screenlines > 18 THEN Write("pause;;[N]"); getcontrolstart(4) END of PR*CEDURE textlines; PROCEDURE pastoperation; COMMENT -------------------------------------------------- 'Pastoperation' will scan an input line (usually a GNOSIS command line) and find the first non-blank character after the first blank. Thus, the PROCEDURE scans past the command word. The array address of the found character is stored in 'pos'. -----------------------------------------------------------; BEGIN FOR pos:= 1 STEP 1 UNTIL answerlength+1 DO IF answer.[pos] = blank THEN GOTO bskip; bskip: FOR pos:= pos STEP 1 UNTIL answerlength+1 DO IF answer.[pos] # blank THEN GOTO out; out: END of PR*CEDURE pastoperation; PROCEDURE shortalgol; COMMENT -------------------------------------------------- Many GNOSIS commands can contain a short ALGOL program sequence after the end of the command (e.g., '%QUE IF lasterrors > 0 THEN'). 'Shortalgol' will transfer that short ALGOL program sequence unchanged from the GNOSIS lesson into the ALGOL translation of the lesson. -----------------------------------------------------------; BEGIN pastoperation; IF pos <= answerlength THEN alglin((temp:=Copy(answer,pos,answerlength))); delet(temp) END of PR*CEDURE shortalgol; PROCEDURE goanywherecommands; COMMENT -------------------------------------------------- 'Goanywherecommands' will determine whether the GNOSIS command at the current command line is one of te go-anywhere commands (i.e., %JUSTIFY, %NOJUSTIFY, %EXTRA, %NOEXTRA, %ORDER, %NOORDER, %GOTO, or %ALGOL) and take appropriate action. The first six will set internal BOOLEAN variables in the GNOSIS program. %ALGOL will cause an ALGOL program sequence to be transferred unchanged from the GNOSIS lesson into the ALGOL translation of that lesson. If there are more than one of these commands in succession, they will all be handled by this PROCEDURE. The PROCEDURE returns when a GNOSIS command is encountered which is none of the eight handled by this PROCEDURE. Note: In terms of their behavior, %IF, %IFEND, and %IFNOT function like the 'goanywhere' commands. They, too, may appear anywhere in the lesson as long as a %SWITCH command precedes them. -----------------------------------------------------------; BEGIN loop: IF controlstart = "JUS" THEN justify:= TRUE ELSE IF controlstart = "NOJ" THEN justify:= FALSE ELSE COMMENT -------------------------------------------------- The %JUSTIFY command causes all text, includin the text in %QUESTION blocks and %HELP blocks, to be semi-justified to produce a ragged right at column =< 72. It also causes such text material to be paged out in such a way that nothing will be lost scrolling off the top of a CRT. Although GNOSIS begins processing text material in the %NOJUSTIFY mode, an occurrence of the %JUSTIFY command will change th mode of processing until a subsequent %NOJUSTIFY command is encountered. In %NOJUSTIFY mode GNOSIS continues to recognize when the CRT screen heigth is about to be exceeded and arranges for the lesson to issue a call to the 'pause' PROCEDURE at the appropriate time. However, there is in this mode no attempt at right-justification. Consequently, in %NOJUSTIFY mode, text will be displayed to the student exactly as it appears in the GNOSIS source script. -----------------------------------------------------------; IF controlstart = "EXT" THEN extra:= TRUE ELSE IF controlstart = "NOE" THEN extra:= FALSE ELSE COMMENT -------------------------------------------------- The above commands influence the way in which student answers are compared to canned answer patterns. After a %EXTRA command, and until the next %NOEXTRA command, student answers will be considered correct even if they contain extra characters not part of the %RIGHT answer pattern PROVIDED they do at least contain all the essential characters. If they answer DOES contain extra characters, then GNOSIS will flag the essential characters with uparrows. -----------------------------------------------------------; IF controlstart = "ORD" THEN order:= TRUE ELSE IF controlstart = "NOO" THEN order:= FALSE ELSE COMMENT -------------------------------------------------- These commands also influence the way in which student answers are compared to canned answer patterns. After a %ORDER command -- and until the next %NOORDER command -- a student's answer will be considered correct only if the elements of the student's answer appear in PRECISELY the same order as the elements of the canned answer pattern. -----------------------------------------------------------; IF controlstart = "GOT" OR controlstart = "GO " THEN COMMENT -------------------------------------------------- The %GOTO command works just the way 'GOTO' works in ALGOL itself. Since the old version of GNOSIS allowed %GOTO command lines to end without a semicolon and the new version doesn't, the code below was added to make the old and new versions compatible. Also, in this version, the %GOTO command can appear anywhere in the lesson script, not (as was formerly the case) just in %QUESTION environments. -----------------------------------------------------------; BEGIN INTEGER i; alglin((temp:= Copy(answer,2,answerlength))); i:= answerlength; WHILE answer.[i] = blank AND i > 4 DO i:= i-1; IF answer.[i] # semicolon THEN BEGIN errmess(FALSE, "0943: Semicolon omitted at end of '%GOTO' command.[N]****** GNOSIS will supply one for you."); Outsymbol(semicolon); Newline END; delet(temp) END ELSE IF controlstart = "ALG" THEN COMMENT -------------------------------------------------- This command causes one or more lines of ALGOL code to be transferred unchanged to the ALGOL translation of the lesson. The segment must be legal in ALGOL and, in addition, must mesh with the GNOSIS lesson at the point of isertion. -----------------------------------------------------------; BEGIN shortalgol; algloop: getline; IF answer.[1] = controlchar THEN GOTO out; IF answerlength = 0 THEN Newline ELSE BEGIN alglin((temp:=Copy(answer,1,answerlength))); delet(temp) END; GOTO algloop END ELSE GOTO return; getline; IF answer.[1] # controlchar THEN BEGIN errmess(TRUE, "0974: A command line is required at this point. As a result,[N]****** this line up to next command line will be ignored."); WHILE answer.[1] # controlchar DO getline END; out: getcontrolstart(4); GOTO loop; return: END of PR*CEDURE goanywherecommands; PROCEDURE putaway; COMMENT -------------------------------------------------- 'Putaway' writes into the lesson identifying phrases. If teacher reports are generated and the student's answer to a question is unexpected, then this identifier will be paired with his answer and both will be routed to the teacher reports. If the teacher gave the question a label, then this label will also be included as part of the identifying phrase. -----------------------------------------------------------; BEGIN INTEGER t; BEGIN Write("putaway(""%QUE"); Print(qcount,2); IF Length(question) > 0 THEN BEGIN Write(";;;;"); FOR pos2:= 1 STEP 1 UNTIL Length(question) DO BEGIN t:= question.[pos2]; IF t = leftsquare OR t = rightsquare OR t = semicolon OR t = doublequote THEN Outsymbol(t); Outsymbol(t) END END; Write(""");;[N]") END END of PR*CEDURE putaway; PROCEDURE unknownmessage; COMMENT -------------------------------------------------- 'Unknownmessage' will output that ALGOL program sequence which handles an answer from the student which was not expected by the teacher (i.e., a "%NEUTRAL or %WRONG" command with no teacher answer pattern following it). The ALGOL program produced will give an appropriate message to the student and write the unexpected answer into the teacher reports (if any) so that the teacher can study the answer in order to improve the lesson. Unless ALL the answer commands following a particular %QUESTION command have been %NEUTRAL commands, 'unknownmessage' causes a call on 'wrongmessage' to be issued. -----------------------------------------------------------; BEGIN Write("BEGIN[N]"); IF allneutral THEN BEGIN IF language = 1 THEN Write("Write(""I don't understand your answer.[[N]]"");;[N]") ELSE IF language = 2 THEN Write("Write(""Jag f`rst}r inte ditt svar.[[N]]"");;[N]") END ELSE Write("wrongmessage;;[N]"); IF put THEN putaway END of PR*CEDURE unknownmessage; PROCEDURE zeroprint; COMMENT -------------------------------------------------- 'Zeroprint' will output an ALGOL program sequence which will determine whether the student answer was empty and, in that case, tell the student what happened and give the question once more to the student. -----------------------------------------------------------; BEGIN Write("IF answerlength = 0 THEN[N]"); Write("BEGIN[N]"); Write("noanswer;;[N]GOTO repeat;;[N]END;;[N]"); zeroprinted:= TRUE END of PR*CEDURE zeroprint; PROCEDURE checkprecedence(n); INTEGER n; COMMENT ------------------------------------------------- 'Checkprecedence' does some rudimentary checking to determine whether the lesson author used GNOSIS commands in the proper order, warns him if he did not, and tries to recover from the error. Not all errors of precedence are caught by this check since some are context-dependent. A complete precedence table for GNOSIS commands appears below: ======================================================= PRECEDENCE COMMAND COMMENT ======================================================= 0 %ALGOL Use of these commands is 0 %EXTRA unrestricted. They may go 0 %GOTO anywhere in the lesson. 0 %IF 0 %IFEND However, the %SWITCH 0 %IFNOT command must precede 0 %JUSTIFsY %IF, %IFENDs and %IFNOTs. 0 %NOEXTRA 0 %NOJUSTIFY 0 %NOORDER 0 %ORDER ------------------------------------------------------- 1 %BELL These commands (if used) 1 %COPYRIGHT must precede all those > 1. 1 %DISK (also %DSK or %DSIC) 1 %KEEP 1 %LANGUAGE 1 %LOCK 1 %NAME 1 %NEXTLESSON 1 %NOCONTROLC 1 %SGNOSIS 1 %SWITCH 1 %TEACHER ------------------------------------------------------- 2 %VARIABLES This command (if used) must precede all those > 2. ------------------------------------------------------- 3 %HELP These commands (if used) 3 %PROCEDURE must precede all those > 3. ------------------------------------------------------- 4 %INITIALIZE This command (if used) must precede all those > 4. ------------------------------------------------------- 5 %LACK These commands (if used) 5 %NEUTRAL must precede all those > 5. 5 %QEND 5 %QUESTION After %QUESTION, %RIGHTs 5 %RIGHT precede %WRONGs (or %LACKs). 5 %SAME %NEUTRALs can go anywhere.. 5 %TEXT Empty patterns of each type 5 %WRONG should appear last. ------------------------------------------------------- 6 %FINISH This command (if used) must precede only the %END. ------------------------------------------------------- 7 %END This command must be last. ======================================================= Note: GNOSIS will accept some contextually awkward orderings of the answer pattern commands. For example, GNOSIS will accept %NEUTRAL at any point after %QUESTION even though, in the context of the lesson, that occurrence of %NEUTRAL may have unintended effects. -----------------------------------------------------------; BEGIN BOOLEAN legalcommand; STRING ARRAY command [1:16]; INTEGER i; command[1]:= "LAN"; command[2]:= "TEA"; command[3]:= "COP"; command[4]:= "NOC"; command[5]:= "NEX"; command[6]:= "SGN"; command[7]:= "SWI"; command[8]:= "KEE"; command[9]:= "LOC"; command[10]:= "BEL"; command[11]:= "NAM"; command[12]:= "DIS"; command[13]:= "DSK"; command[14]:= "VAR"; command[15]:= "HEL"; command[16]:= "PRO"; WHILE NOT legalcommand DO BEGIN FOR i:= 1 STEP 1 UNTIL n DO BEGIN IF controlstart = command[i] THEN BEGIN errmess(FALSE, "1161: Command is out of place here. As a result,[N]****** this line up to next command line will be ignored."); getline; WHILE answer.[1] # controlchar DO getline; getcontrolstart(4); GOTO earlyexit END ELSE IF i = n THEN legalcommand:= TRUE; END; earlyexit: END END PR*CEDURE checkprecedence; PROCEDURE putvariables; COMMENT ------------------------------------------------- The translated GNOSIS lesson is prefaced by (1) a preamble of global variable declarations (output by "putvariables"), (2) a set of global PROCEDURE declarations (output by "putprocedures"), (3) a set of statements to initialize the value of global variables (output by "putvalues"), and (4) a lesson header (output by putheader) which does some additional initialization of I/O. -----------------------------------------------------------; BEGIN Write("BEGIN"); Write(" COMMENT: This is the lesson """); Write(lessonname); Write(""";;[N]"); IF put AND teacher # "" THEN BEGIN Write("COMMENT: This lesson was written by[N]"); alglin(teacher); Write(";;"); Newline END; COMMENT ------------------------------------------------- Output of data declarations follows. -----------------------------------------------------------; Write("BOOLEAN firsttry, ihaveblanked, nopause, skip, back, stop, message;;[N]"); Write("INTEGER lasterrors, score, latescore, qcount, lastqcount, lastqkount, carriagereturn, linefeed, pos, blank;;[N]"); Write("INTEGER lastscorecount, lastlatescorecount, lastscorekount, lastlatescorekount;;[N]"); IF language = 2 THEN BEGIN Write("INTEGER lcaa, lcae, lcoe, ucaa, ucae, ucoe;;[N]") END; Write("INTEGER randno, answerlength, controlstart, controlg, uparrow;;[N]"); Write("INTEGER lastrights;;[N]"); Write("STRING answer, answercopy, char, endofline, lessonname, firstname, tempfile1, tempfile2, prompt;;[N]"); Write("STRING ARRAY right, rightatlast, wrong[[0:9]];;[2N]"); END of PR*CEDURE putvariables; PROCEDURE putprocedures; COMMENT ------------------------------------------------- 'Putprocedures' is the second in a series of four procedures to output the lesson preface -----------------------------------------------------------; BEGIN IF nocontrolc THEN Write("EXTERNAL PROCEDURE stopkc;;[2N]"); IF transferlesson THEN Write("EXTERNAL PROCEDURE pub, nolpt, run, r;;[2N]"); IF put THEN Write("FORWARD PROCEDURE putaway;;[N]"); Write("FORWARD BOOLEAN PROCEDURE anywhere;;[2N]"); Write("PROCEDURE getline;;[N]"); Write("COMMENT: 'Getline' reads in a line of text input by the student,[N]"); Write("dumping it in the STRING 'answer'. Column one of the line is checked[N]"); Write("to see if the student typed '%', indicating that he wanted[N]"); Write("to interrupt the flow of the lesson;;[N]"); Write("BEGIN[N]INTEGER s, t, i;;[N]BOOLEAN quit;;[N]"); Write("Breakoutput;;[N]quit:= FALSE;;[N]"); Write("[N]again:[N]FOR i:= 1 STEP 1 UNTIL 132 DO[N]"); Write("BEGIN[N]Insymbol(t);;[N]"); Write("COMMENT: Lower case transform;;[N]"); Write("IF t >= 97 AND t <= 122 THEN t:= t-32"); IF language = 1 THEN Write(";;[N]") ELSE IF language = 2 THEN BEGIN Write(" ELSE[N]"); Write("IF t = lcaa THEN t:= ucaa ELSE[N]"); Write("IF t = lcae THEN t:= ucae ELSE[N]"); Write("IF t = lcoe THEN t:= ucoe;;[N]") END; Write("answer.[[i]]:= t;;[N]"); Write("IF t = carriagereturn THEN GOTO out;;[N]"); Write("END;;[N]"); Write("GOTO again;;[N]"); Write("[N]out:[N]Insymbol(t);;[N]i:= i+1;;[N]answer.[[i]]:= t;;[N]pos:= 1;;[N]answerlength:= i-2;;[N]"); Write("IF answerlength < 0 THEN answerlength:= 0;;[N]"); Write("Delete(answercopy);;[N]answercopy:= Copy(answer,1,i);;[N]"); Write("ihaveblanked:= FALSE;;[N]"); Write("IF answerlength = 1 THEN[N]"); Write("BEGIN[N]IF answer.[[1]] = controlstart THEN[N]"); IF language = 1 THEN BEGIN Write("BEGIN[N]Write(""Answer 'STOP'"");;[N]"); IF NOT lock THEN Write("Write("", 'SKIP', 'BACK', "");;[N]"); Write("Write(""or 'CONTINUE'.[[N]]"");;[N]") END ELSE IF language = 2 THEN BEGIN Write("BEGIN[N]Write(""Svara """"STOPP"""" om Du vill l{mna lektionen,[[N]]"");;[N]"); IF NOT lock THEN Write("Write(""""""SKIPPA"""" om Du vill skippa denna fr}ga,[[N]]"");;[N]"); IF NOT lock THEN Write("Write(""""""BACKA"""" om Du vill g} tillbaka till en tidigare del av lektionen,[[N]]"");;[N]"); Write("Write(""""""FORTS#TT"""" om Du vill forts{tta.[[N]]"");;[N]") END; IF bell THEN Write("Outsymbol(controlg);;[N]"); Write("Write(prompt);;[N]Breakoutput;;[N]"); Write("quit:= TRUE;;[N]GOTO again;;[N]"); Write("END;;[N]END;;[N]"); Write("IF quit THEN[N]"); Write("BEGIN[N]quit:= FALSE;;[N]IF anywhere("""); IF language = 1 THEN Write("STOP") ELSE IF language = 2 THEN Write("STOPP"); Write(""") THEN[N]BEGIN[N]"); Write("stop:= TRUE;;[N]"); IF language = 1 THEN Write("Write(""Lesson aborted."");;[N]") ELSE IF language = 2 THEN Write("Write(""Lektionen avbr`t.[[N]]"");;[N]"); Write("GOTO exit;;[N]END;;[N]"); Write("pos:= 1;;[N]IF anywhere("""); IF language = 1 THEN Write("SKIP") ELSE IF language = 2 THEN Write("SKIPPA"); Write(""") THEN[N]"); Write("BEGIN[N]Write("""); IF NOT lock THEN BEGIN IF language = 1 THEN Write("Question skipped.") ELSE IF language = 2 THEN Write("Fr}gan skippad."); Write("[[N]]"");;[N]skip:= TRUE;;[N]") END ELSE BEGIN IF language = 1 THEN Write("??? Skipping not allowed in this lesson.") ELSE IF language = 2 THEN Write("???"); Write("[[N]]"");;[N]") END; Write("END[N]ELSE[N]BEGIN[N]"); Write("pos:= 1;;[N]IF anywhere("""); IF language = 1 THEN Write("BACK") ELSE IF language = 2 THEN Write("BACKA"); Write(""") THEN[N]"); Write("BEGIN[N]Write("""); IF NOT lock THEN BEGIN IF language = 1 THEN Write("Lesson is backing.") ELSE IF language = 2 THEN Write("Lektionen g}r tillbaka."); Write("[[N]]"");;[N]back:= TRUE;;[N]") END ELSE BEGIN IF language = 1 THEN Write("??? Backing not allowed in this lesson.") ELSE IF language = 2 THEN Write("???"); Write("[[N]]"");;[N]") END; Write("END[N]ELSE[N]BEGIN[N]"); IF language = 1 THEN BEGIN Write("Write(""Please continue, then, giving your response[[N]]"");;[N]"); Write("Write(""to the PREVIOUS '-->' or 'RETURN' prompt...[[N]]"");;[N]") END ELSE IF language = 2 THEN Write("Write(""Du vill inte avbryta nu. Forts{tt d} med ditt ordinarie svar.[[N]]"");;[N]"); Write("GOTO again;;[N]"); Write("END;;[N]END;;[N]"); Write("END;;[2N]exit:[N]END;;[2N]"); Write("BOOLEAN PROCEDURE here(comp);;[N]VALUE comp;;[N]STRING comp;;[N]"); Write("COMMENT: 'Here' determines whether the argument string can be found[N]"); Write("beginning at 'pos' in the student answer. Blanks, but nothing[N]"); Write("else, may precede the matching word. 'Here' is mostly used[N]"); Write("for answers matched in the %NOEXTRA mode;;[N]"); Write("BEGIN[N]INTEGER cpos;;[N]"); Write("[N]blankline:[N]IF answercopy.[[pos]] = blank THEN[N]"); Write("BEGIN[N]pos:= pos+1;;[N]GOTO blankline;;[N]"); Write("END;;[N]"); Write("FOR cpos:= 1 STEP 1 UNTIL Length(comp) DO[N]"); Write("BEGIN[N]"); Write("IF answercopy.[[pos]] # comp.[[cpos]] THEN GOTO nofit;;[N]"); Write("pos:= pos+1;;[N]"); Write("END;;[N]"); Write("here:= TRUE;;[N]GOTO out;;[N]"); Write("[N]nofit:[N]here:= FALSE;;[N]"); Write("[N]out:[N]END;;[2N]"); Write("BOOLEAN PROCEDURE anywhere(comp);;[N]VALUE comp;;[N]STRING comp;;[N]"); Write("COMMENT: 'Anywhere' determines whether the argument string can be [N]"); Write("found anywhere in the student answer after 'pos'. Blanks and[N]"); Write("unmatched strings will be bypassed. 'Anywhere' is used in[N]"); Write("the %EXTRA mode and in the %NOEXTRA %NOORDER mode.[N]"); Write("'Anywhere' will substitute blanks for all characters in[N]"); Write("the matched strings. This allows the program (e.g., in the[N]"); Write("%NOEXTRA mode) to determine whether there are[N]"); Write("any non-blank characters in the student answer. In the[N]"); Write("%NOEXTRA mode, his answer will be wrong if any non-blank characters remain;;[N]"); Write("BEGIN[N]INTEGER cpos, nextpos;;[N]"); Write("[N]blankline:[N]IF answercopy.[[pos]] = blank THEN[N]"); Write("BEGIN[N]pos:= pos+1;;[N]GOTO blankline;;[N]"); Write("END;;[N]"); Write("nextpos:= pos+1;;[N]"); Write("FOR cpos:= 1 STEP 1 UNTIL Length(comp) DO[N]"); Write("BEGIN[N]"); Write("IF answercopy.[[pos]] # comp.[[cpos]] THEN GOTO nofit;;[N]"); Write("pos:= pos+1;;[N]"); Write("END;;[N]"); Write("FOR cpos:= pos-Length(comp) STEP 1 UNTIL pos-1 DO[N]"); Write("BEGIN[N]answercopy.[[cpos]]:= blank;;[N]"); Write("END;;[N]"); Write("anywhere:= ihaveblanked:= TRUE;;[N]GOTO out;;[N]"); Write("[N]nofit:[N]IF answercopy.[[pos]] # carriagereturn THEN[N]"); Write("BEGIN[N]pos:= nextpos;;[N]GOTO blankline;;[N]"); Write("END;;[N]"); Write("anywhere:= FALSE;;[N]"); Write("[N]out:[N]END;;[2N]"); Write("PROCEDURE restore;;[N]"); Write("COMMENT: Since 'anywhere' substitutes blanks for matched parts of the student[N]"); Write("answer, the original response must be restored before comparing[N]"); Write("it to other target patterns. This restoration is done here;;[N]"); Write("BEGIN[N]ihaveblanked:= FALSE;;[N]"); Write("Delete(answercopy);;[N]answercopy:= Copy(answer,1,answerlength+2);;[N]"); Write("END;;[2N]"); Write("INTEGER PROCEDURE random;;[N]"); Write("COMMENT: 'Random' is a number generator which gives a random[N]"); Write("number between 0 and 9. The generator will not repeat[N]"); Write("itself until after 862 generations;;[N]"); Write("BEGIN[N]"); Write("randno:= randno*10 rem 863;;[N]"); Write("random:= randno div 87;;[N]"); Write("END;;[2N]"); Write("PROCEDURE personalize(message);;[N]VALUE message;;[N]STRING message;;[N]"); Write("COMMENT: 'Personalize' uses the student's firstname in conversational[N]"); Write("I/O to give a personal touch to the lessons. To avoid monotony[N]"); Write("and to further efficiency, the probability that the message will be[N]"); Write("personalized is only .2;;[N]"); Write("BEGIN[N]IF firstname = """" OR random < 8 THEN Write(message)[N]"); Write("ELSE BEGIN[N]INTEGER i, j;;[N]i:= 1;;[N]j:= Length(message);;[N]"); Write("WHILE i < j DO[N]BEGIN[N]Outsymbol(message.[[i]]);;i:= i + 1;;[N]END;;[N]"); Write("Write("", "");;[N]Write(firstname);;[N]Outsymbol(message.[[j]]);;[N]"); Write("END;;[N]END;;[2N]"); Write("PROCEDURE rightmessage;;[N]"); Write("COMMENT: 'Rightmessage' is called when a student has[N]"); Write("made a correct answer. He is told about this, and the[N]"); Write("score is updated. The message to the student is different[N]"); Write("depending on whether he succeeds on the fist try or[N]"); Write("only after previous fruitless attempts. Random numbers are[N]"); Write("used to choose between ten different messages to give[N]"); Write("some variation to the conversation;;[N]"); Write("BEGIN[N]"); Write("lastrights:= lastrights+1;;[N]"); Write("IF firsttry THEN[N]"); Write("BEGIN[N]score:= score+1;;[N]qcount:= qcount+1;;[N]firsttry:= FALSE;;[N]"); Write("personalize(right[[random]]);;[N]"); Write( "END[N]ELSE[N]BEGIN[N]personalize(right[[random]]);;[N]IF lasterrors > 1 THEN Write(rightatlast[[random]]);;[N]END;;[N]"); IF language = 1 THEN BEGIN Write("IF lasterrors > 3 THEN[N]BEGIN[N]"); Write("personalize(""[[N]]I must admit that last one was a bit tricky!"");;[N]"); Write("Newline;;[N]END;;[N]"); END; Write("latescore:= latescore+1;;[N]Newline;;[N]"); Write("END;;[2N]"); Write("PROCEDURE wrongmessage;;[N]"); Write("COMMENT: 'Wrongmessage' is called when a student[N]"); Write("has made an erroneous answer. He is told about this,[N]"); Write("and the lesson statistics are updated. Random numbers[N]"); Write("are used to choose between ten different messages in order[N]"); Write("provide some variation in the conversation;;[N]"); Write("BEGIN[N]"); Write("lasterrors:= lasterrors+1;;[N]"); Write("IF firsttry THEN[N]"); Write("BEGIN[N]qcount:= qcount+1;;[N]firsttry:= FALSE;;[N]"); Write("END;;[N]"); Write("personalize(wrong[[random]]);;[N]Newline;;[N]"); Write("END;;[2N]"); Write("PROCEDURE pause;;[N]"); Write("COMMENT: Lesson text is given to the student in small portions.[N]"); Write("One reason for this is that it is easier to read a little[N]"); Write("at a time. Another reason is that display terminals have[N]"); Write("a limited screen size. The 'pause' PROCEDURE produces a pause[N]"); Write("in the display of text to the student, and she can continue[N]"); Write("whenever she has finished examining the displayed material.[N]"); Write("To go on, she simply pushes the 'RETURN' key. Normally, this[N]"); Write("PROCEDURE is called between each %TEXT or %QUESTION,[N]"); Write("but GNOSIS will introduce ADDITIONAL automatic pause points[N]"); Write("within large blocks of text or question material.[N]"); Write("The teacher can inhibit pauses BETWEEN '%' commands by 'nopause:= TRUE', but[N]"); Write("the pauses which GNOSIS inserts in long text blocks cannot be inhibited;;[N]"); Write("IF nopause THEN nopause:= FALSE ELSE[N]"); Write("BEGIN[2N][N]"); IF language = 1 THEN Write("Write(""[[N]]Push RETURN "");;[N]") ELSE IF language = 2 THEN Write("Write(""[[N]]Tryck RETURN "");;[N]"); Write("write(prompt);;[N]"); IF bell THEN Write("Outsymbol(controlg);;[N]"); Write("getline;;[N]Newline;;[N]"); Write("IF stop THEN[N]BEGIN[N]stop:= FALSE;;[N]GOTO endoflesson[N]END;;[N]"); Write("IF answerlength > 0 THEN[N]"); Write("BEGIN[N]pos:= 1;;[N]"); Write("IF NOT here(endofline) THEN[N]BEGIN[N]"); IF language = 1 THEN textline("Eh? The computer expected you would just push the RETURN button.") ELSE IF language = 2 THEN textline("??? Datorn v{ntade sig bara RETURN fr}n Dig."); Write("END;;[N]END;;[N]"); Write("END;;[2N]"); Write("PROCEDURE page;;[N]"); Write("COMMENT: 'Page' can be called by the teacher when he[N]"); Write("wants to blank the screen on a display terminal).[N]"); Write("The effect produced is, of course, terminal dependent;;[N]"); Write("BEGIN[N]pause;;[N]Outsymbol(12);;[N]Outsymbol(12);;[N]Newline;;[N]"); Write("END;;[2N]"); Write("PROCEDURE extratest;;[N]"); Write("COMMENT: 'Extratest' will flag and display those parts of the student answer [N]"); Write("which WERE matched by the teacher answer pattern IF the[N]"); Write("student response contained extra (i.e., superfluous) characters[N]"); Write("'Extratest' is called automatically for %RIGHT answers in the[N]"); Write("%EXTRA mode. Recall that 'here' scans past leading blanks;;[N]"); Write("BEGIN[N]"); Write("pos:= 1;;[N]IF NOT here(endofline) THEN[N]"); Write("BEGIN[N]Newline;;[N]"); Write("FOR pos:= 1 STEP 1 UNTIL answerlength + 2 DO Outsymbol(answer.[[pos]]);;[N]"); Write("FOR pos:= 1 STEP 1 UNTIL answerlength DO[N]"); Write("IF answercopy.[[pos]] = blank AND answer.[[pos]] # blank THEN Outsymbol(uparrow) ELSE Outsymbol(blank);;[N]"); Write("Newline;;[N]Newline;;[N]"); Write("END;;[N]"); Write("END;;[2N]"); Write("REAL PROCEDURE percent;;[N]"); Write("COMMENT: 'Percent' returns the percentage of questions answered correctly[N]"); Write("by the student on his very first try since the start of the lesson;;[N]"); Write("percent:= IF qcount = 0 or score = 0 THEN 0.0 ELSE 100.0 * score/qcount;;[2N]"); Write("REAL PROCEDURE latepercent;;[N]"); Write("COMMENT: 'Latepercent' returns the percentage of questions answered correctly[N]"); Write("by the student since the start of the lesson whether[N]"); Write("student was correct on the first try or afterward;;[N]"); Write("latepercent:= IF qcount = 0 or latescore = 0 THEN 0.0 ELSE 100.0 * latescore/qcount;;[2N]"); Write("INTEGER PROCEDURE lastscore;;[N]"); Write("COMMENT: 'Lastscore' returns number of questions answered correctly on the[N]"); Write("very first try since this PROCEDURE was last called;;[N]"); Write("BEGIN[N]lastscore:= score - lastscorecount;;[N]lastscorecount:= score[N]END;;[2N]"); Write("INTEGER PROCEDURE lastlatescore;;[N]"); Write("COMMENT: 'Lastlatescore' returns number of questions answered correctly[N]"); Write("since this PROCEDURE was last called whether student was correct on the[N]"); Write("very first try or afterward;;[N]"); Write("BEGIN[N]lastlatescore:= latescore - lastlatescorecount;;[N]lastlatescorecount:= latescore[N]END;;[2N]"); Write("REAL PROCEDURE lastpercent;;[N]"); Write("COMMENT: 'Lastpercent' returns the percentage of questions answered correctly[N]"); Write("on the very first try since this PROCEDURE was last called;;[N]"); Write("BEGIN[N]INTEGER x;;[N]"); Write("x:= score - lastscorekount;;[N]lastscorekount:= score;;[N]lastqcount:= qcount - lastqcount;;[N]"); Write("lastpercent:= IF lastqcount = 0 or x = 0 THEN 0.0 ELSE 100.0 * x/lastqcount;;[N]"); Write("END;;[2N]"); Write("REAL PROCEDURE lastlatepercent;;[N]"); Write("COMMENT: 'Lastlatepercent' returns the percentage of questions answered correctly[N]"); Write("since this PROCEDURE was last called whether student was correct on the first try[N]"); Write("or afterward;;[N]"); Write("BEGIN[N]INTEGER x;;[N]"); Write("x:= latescore - lastlatescorekount;;[N]lastlatescorekount:= latescore;;[N]lastqkount:= qcount - lastqkount;;[N]"); Write("lastlatepercent:= IF lastqkount = 0 or x = 0 THEN 0.0 ELSE 100.0 * x/lastqkount;;[N]"); Write("END;;[2n]"); IF put THEN BEGIN Write("PROCEDURE putaway(question);;[N]"); Write("COMMENT: If the lesson contained a report-generating command,[N]"); Write("then unexpected student answers are written into a pair of reports,[N]"); Write(".DTA and .SRT, which the teacher can use to improve the[N]"); Write("lesson. 'Putaway' writes the unexpected answers in these files;;[N]"); Write("VALUE question;;[N]STRING question;;[N]"); Write("BEGIN[N]INTEGER i;;[N]"); Write("Breakoutput;;[N]"); Write("Selectoutput(2);;[N]"); Write("Write(question);;[N]Write("": "");;[N]"); Write("FOR i:= 1 STEP 1 UNTIL answerlength DO Outsymbol(answer.[[i]]);;[N]"); Write("Newline;;[N]"); Write("Breakoutput;;[N]"); Write("Selectoutput(3);;[N]"); Write("Write(question);;[N]Write("": "");;[N]"); Write("FOR i:= 1 STEP 1 UNTIL answerlength DO Outsymbol(answer.[[i]]);;[N]"); Write("Newline;;[N]"); Write("Breakoutput;;[N]"); Write("Selectoutput(0);;[N]"); Write("END;;[2N]"); IF teacher # "" THEN BEGIN Write("PROCEDURE putmessage;;[N]"); Write("COMMENT: If no disk files are being generated for teacher reports,[N]"); Write("'Putmessage' will tell the student to send hardcopy reports [N]"); Write("to the teacher -- unless no teacher's name and address[N]"); Write("was supplied with %TEACHER command);;[N]"); Write("BEGIN[N]"); IF language = 1 THEN BEGIN textline("He needs the listing to improve the lesson."); IF NOT name THEN textline("You can send it anonymously - the teacher will"); IF NOT name THEN textline("not know which student sent it to him."); Write("Newline;;[N]"); textline("The name and address of the teacher is:") END ELSE IF language = 2 THEN BEGIN textline("Han beh`ver den f`r att kunna g`ra lektionen b{ttre."); IF NOT name THEN textline("Du kan s{nda den anonymt - l{raren beh`ver inte veta"); IF NOT name THEN textline("vilken elev som s{nde den till honom."); Write("Newline;;[N]"); textline("L{rarens namn och adress {r:") END; textline(teacher); Write("Newline;;[N]END;;[2N]") END END; Write("PROCEDURE noanswer;;[N]"); Write("BEGIN[N]"); IF language = 1 THEN BEGIN Write("personalize(""??? You gave no answer at all."");;[N]Newline;;[N]"); Write("Write(""If you want to interrupt the lesson, then type the single character """"") END ELSE IF language = 2 THEN BEGIN Write("personalize(""??? Du gav inget svar alls."");;[N]Newline;;[N]"); Write("Write(""Om Du vill g} ut ur fr}gan, s} skriv det enda tecknet """"") END; Outsymbol(controlchar); Write(""""".[[N]]"");;[N]"); Write("end noanswer;;[2N]"); Write("PROCEDURE tryagain;;[N]"); Write("COMMENT: Questions output to students who give no answer, a wrong[N]"); Write("answer, or an incomplete answer are usually repeated by GNOSIS.[N]"); Write("'Tryagain' will output to such students a 'try again' message[N]"); Write("which varies according to circumstances. In %NEUTRAL mode,[N]"); Write("the message will always be 'Please try again'. Otherwise,[N]"); Write("the content of the message depends on the number of times[N]"); Write("the student has failed to give a correct response;;[N]"); IF language = 1 THEN BEGIN Write("BEGIN[N]Newline;;[N]"); Write("personalize(""Please try again."");;[N]"); Write("IF lasterrors = 3 THEN Write(""[[N]]You have at least eliminated some of the possibilities."") ELSE[N]"); Write("IF lasterrors = 4 THEN Write(""[[N]](You might want to rethink some of your previous answers.)"") ELSE[N]"); Write("IF lasterrors > 4 THEN Write(""[[N]]Or...interrupt the lesson by typing the symbol """""); Outsymbol(controlchar); Write("""""."");;[N]"); Write("Newline;;[N]Newline;;[N]"); Write("END;;[2N]") END ELSE IF language = 2 THEN BEGIN Write("BEGIN[N]Newline;;[N]"); Write("personalize(""F`rs`k igen."");;[N]"); Write("IF lasterrors = 3 THEN Write(""[[N]]Ddu har eliminerat flera m`jligheter redan."") ELSE[N]"); Write("IF lasterrors > 3 THEN Write(""[[N]]Eller...avbryt lektionen genom att skriva symbolen """""); Outsymbol(controlchar); Write("""""."");;[N]"); Write("[N]Newline;;[N]Newline;;[N]END;;[2N]") END; Write("PROCEDURE giveanswer;;[N]"); Write("COMMENT: 'Giveanswer' displays a right answer to a student who is skipping[N]"); Write("the question or who repeatedly fails to give a right answer.[N]"); Write("The answer given will always be the one following the FIRST[N]"); Write("%RIGHT command for the preceding %QUESTION;;[N]"); Write("BEGIN[N]"); IF language = 1 THEN BEGIN Write("Newline;;[N]personalize(""Just for the record,"");;[N]"); Write("Write("" GNOSIS will simulate an acceptable answer:[[2N]]-->"");;[N]"); END ELSE IF language = 2 THEN BEGIN Write("Newline;;[N]personalize(""F`r den h{ndelse du {r intresserad,"");;[N]"); Write("Write("" h{r {r ett svar[[N]]"");;[N]"); Write("Write(""vilket skulle ha varit acceptabelt:[2N]-->"");;[N]") END; Write("END;;[N]"); END of PR*CEDURE putprocedures; PROCEDURE putvalues; COMMENT ------------------------------------------------- 'Putvalues' writes code which handles the initialization of variables before the start of the lesson. This is phase three of the preface output routines. -----------------------------------------------------------; BEGIN Write( "[4N]lasterrors:= latescore:= score:= lastqcount:= lastqkount:= qcount:= 0;;[N]carriagereturn:= 13;;[N]linefeed:= 10;;[N]"); Write("lastrights:= lastscorecount:= lastscorekount:= lastlatescorecount:= lastlatescorekount:= 0;;[N]"); Write("blank:= "" "".[[1]];;uparrow:= ""^"".[[1]];;[N]controlg:="""".[[1]];;[N]"); IF language = 2 THEN BEGIN Write("ucaa:= ""$"".[[1]];;[N]ucae:= ""#"".[[1]];;[N]ucoe:= ""@"".[[1]];;[N]"); Write("lcaa:= ""}"".[[1]];;[N]lcae:= ""{"".[[1]];;[N]lcoe:= ""`"".[[1]];;[N]") END; Write("answer:= Newstring(135,7);;[N]answercopy:= Copy("" "");;[N]firstname:= """";;[N]prompt:= Copy(""-->"");;[N]"); Write("answer.[[134]]:= carriagereturn;;[N]answer.[[135]]:= linefeed;;[N]"); Write("char:= Newstring(1,7);;[N]"); Write("endofline:= Newstring(1,7);;[N]endofline.[[1]]:= carriagereturn;;[N]"); Write("randno:= 1;;[N]nopause:= skip:= message:= FALSE;;[N]"); Write("controlstart:= """); Outsymbol(controlchar); Write(""".[[1]];;[N]"); Write("lessonname:= """); Write(lessonname); Write(""";;[N]"); BEGIN COMMENT ------------------------------------------------- Here the 30 STRINGs with different responses to right and wrong student answers are initialized. A simple PROCEDURE "m" is used to simplify the repetitive part of this initialization. -----------------------------------------------------------; INTEGER type, index; STRING arrayname; PROCEDURE m(message); STRING message; BEGIN IF index = 9 THEN BEGIN index:= 0; type:= type+1; IF type = 1 THEN arrayname:= "right" ELSE IF type = 2 THEN arrayname:= "rightatlast" ELSE IF type = 3 THEN arrayname:= "wrong" END ELSE index:= index+1; Write(arrayname); Write("[["); Print(index,1); Write("]]:= """); Write(message); Write(""";;[N]") END of PR*CEDURE m; type:= 0; index:= 9; IF language = 1 THEN BEGIN m("Good for you!"); m("Very good."); m("Sure!"); m("Bravo!"); m("Exactly right!"); m("Right!"); m("Well done!"); m("That's it!"); m("Excellent!"); m("OK!"); m(" You're doing better!"); m(" Now you're catching on!"); m(" You're improving!"); m(" Yo've got the idea now!"); m(" I knew you could do it."); m(" There is hope for you after all!"); m(" You're making progress."); m(" Aren't you glad you kept trying?"); m(" Keep up the good work!"); m(" You've got the hang of it now."); m("Wrong!"); m("No!"); m("You gave the wrong answer."); m("Your answer was wrong."); m("Incorrect!"); m("No, you're wrong this time."); m("Sorry."); m("That wasn't correct."); m("You're mistaken."); m("No, you missed this one.") END ELSE IF language = 2 THEN BEGIN m("Bra gjort!"); m("Mycket bra."); m("Javisst!"); m("Utm{rkt!"); m("Precis r{tt."); m("R{tt!"); m("Korrekt."); m("Du svarade r{tt igen."); m("Bra!"); m("OK!"); m(" Just det - Du klarar dig b{ttre nu."); m(" Nu b`rjar Du fatta!"); m(" B{ttre och b{ttre!"); m(" Nu har Du fattat det hela!"); m(" Javisst! Nu klarar Du det h{r."); m(" S}ja, till slut g}r det bra!"); m(" Du g`r framsteg."); m(" #ntligen r{tt!"); m(" Bra! Forts{tt p} det s{ttet."); m(" Just det. Bara man anstr{nger sig lite s} g}r det bra."); m("Fel!"); m("Nej!"); m("Du valde fel svar."); m("Ditt svar var felaktigt."); m("Det {r inte r{tt!"); m("Nej, den h{r g}ngen svarade Du fel."); m("Tyv{rr inte r{tt."); m("Det d{r {r inte riktigt."); m("Nej, inte riktigt s}."); m("Nej, den fr}gan missade Du.") END; Newline END; END of PR*CEDURE putvalues; PROCEDURE putheader; COMMENT ------------------------------------------------- 'Putheader' is the last of four procedures used to output the lesson preface. This procedure writes code which handles I/O initialization. -----------------------------------------------------------; BEGIN Write("[N]Input(0,""TTY"");;[N]"); IF put THEN BEGIN IF NOT disk THEN Write("Output(2,""LPT"");;[N]Selectoutput(2);;[N]"); IF disk THEN Write("Input(1,""DSK"");;[N]Output(2,""DSK"");;[N]Output(3,""DSK"");;[N]"); IF nocontrolc THEN Write("stopkc;;[N]"); IF NOT disk AND NOT keep THEN BEGIN IF language = 1 THEN textline("Please send this listing to the teacher.") ELSE IF language = 2 THEN textline("L{raren {r tacksam om Du s{nder denna lista till honom."); Write("putmessage;;[N]") END; Write("Breakoutput;;[N]"); Write("Selectoutput(0);;[2N]") END; COMMENT ------------------------------------------------- Now we write code which displays certain standard text material to the student at the beginning of a new lesson. Most of this code will NOT be written into the lesson, however, if the %SGNOSIS command was used. -----------------------------------------------------------; IF name THEN BEGIN IF language = 1 THEN BEGIN Write("Write(""[[N]]Hello! This is GNOSIS talking.[[N]]"");;[N]"); Write("[N]repeat:[N]Write(""I'd like to know your full name--[[N]]just type it in after the arrow.[[N]]"");;[N]"); IF put THEN BEGIN COMMENT The following code brings GNOSIS into compliance with privacy laws; Write("Write(""[[NT]](Unless you enter a made-up name, GNOSIS will"");;[N]"); Write("Write(""[[NT]]generate personal data based on this session.)[[2N]]"");;[N]") END; IF bell THEN Write("Outsymbol(controlg);;[N]"); Write("Write(prompt);;[N]getline;;[N]"); Write("IF answerlength < 6 THEN[N]"); Write("BEGIN[N]Write(""That's too short to be your FULL name.[[N]]"");;[N]Newline;;[N]GOTO repeat;;[N]END;;[N]"); Write("FOR pos:= 1 STEP 1 UNTIL answerlength DO IF answer.[[pos]] = blank THEN GOTO continue;;[N]"); Write("Write(""That's only ONE of your names.[[N]]"");;[N]GOTO repeat;;[N]"); Write("[N]continue:[N]Write(""Glad to meet you, "");;[N]") END ELSE IF language = 2 THEN BEGIN Write("Write(""[[N]]Hej! Det h{r {r GNOSIS-programmet som talar.[[N]]"");;[N]"); Write("[N]repeat:[N]Write(""Jag skulle vilja veta ditt fullst{ndiga namn--skriv det p} terminalen.[[N]]"");;[N]"); IF bell THEN Write("Outsymbol(controlg);;[N]"); Write("Write(prompt);;[N]getline;;[N]"); Write("IF answerlength < 6 THEN[N]"); Write("BEGIN[N]Write(""???[[N]]"");;[N]Newline;;[N]GOTO repeat;;[N]END;;[N]"); Write("FOR pos:= 1 STEP 1 UNTIL answerlength DO IF answer.[[pos]] = blank THEN GOTO continue;;[N]"); Write("Write(""???[[N]]"");;[N]GOTO repeat;;[N]"); Write("[N]continue:[N]Write(""Trevligt att tr{ffas, "");;[N]") END; Write("BEGIN[N]INTEGER pos, t;;[N]"); Write("pos:=1;;[N]"); Write("[N]nameloop:[N]t:=answer.[[pos]];;[N]"); Write("IF pos 0 THEN BEGIN INTEGER i, j; mainlab:= Copy("ZQXAAA"); Write("IF FALSE THEN[N]BEGIN[N]"); j:= labelcount; FOR i:= 1 STEP 1 UNTIL j DO BEGIN makemainlab; Newline; Write(mainlab); Write(":[N]") END; IF language = 1 THEN BEGIN Write("Write(""NOTE: The backing procedure is unable to go back just ONE step [[N]]"");;[N]"); Write("Write(""from this point. It is going all the way back to the start.[[N]]"");;[N]") END ELSE IF language = 2 THEN BEGIN Write("Write(""Jag kan inte g} en liten bit bak}t p.g.a. lektionens komplexitet[[N]]"");;[N]"); Write("Write(""utan m}ste backa till lektionens b`rjan. F`rl}t![[N]]"");;[N]") END; Write("GOTO start;;[N]END;;[N]") END; Write("[N]endoflesson:[N]Newline;;[N]"); IF NOT sgnosis THEN BEGIN IF language = 1 THEN BEGIN Write("Write(""During the lesson """""); Write(lessonname); Write(""""" you answered"");;[N]Print(qcount,3);;[N]"); Write("Write("" questions,[[N]]and you gave the right answer"");;[N]Print(latescore,3);;[N]"); Write("Write("" times.[[N]]On"");;[N]Print(score,3);;[N]"); Write("Write("" questions your answer was right on your very first try.[[N]]"");;[N]") END ELSE IF language = 2 THEN BEGIN Write("Write(""Lektion """""); Write(lessonname); Write(""""" {r slut.[[N]]Du har svarat p}"");;[N]Print(qcount,3);;[N]"); Write("Write("" fr}gor, [[N]]och Du gav r{tt svar"");;[N]Print(latescore,3);;[N]"); Write("Write("" g}nger.[[N]]P}"");;[N]Print(score,3);;[N]"); Write("Write("" fr}gor gav Du r{tt svar f`rsta g}ngen Du fick fr}gan.[[N]]"");;[N]") END END; IF put THEN BEGIN IF NOT keep AND NOT disk THEN BEGIN IF language = 1 THEN BEGIN Write("Write(""[[2N]]This lesson has produced a report sheet for the teacher.[[N]]"");;[N]"); Write("Write(""This sheet will be printed on the line printer.[[N]]"");;[N]") END ELSE IF language = 2 THEN BEGIN textline("[[2N]]Denna lektion har producerat ett rapportblad till l{raren."); textline("Detta blad kommer att skrivas ut p} radskrivaren.") END; IF language = 1 THEN BEGIN textline("Please send the line printer listing (not the listing"); textline("at the conversational terminal!) to the teacher.") END ELSE IF language = 2 THEN BEGIN textline("L{raren {r tacksam om Du s{nder radskrivarlistan"); textline("(inte dialogterminallistan!) till honom.") END; Write("putmessage;;[N]") END; Write("Breakoutput;;[N]"); Write("Selectoutput(2);;[N]Newline;;[N]"); IF language = 1 THEN Write("Write(""SCORES: "");;[N]") ELSE IF language = 2 THEN Write(" Write(""Antal r{tt: "");;[N]"); Write( "Print(latepercent,2,1);;[N]Write(""% ("");;[N]Print(percent,2,1);;[N]Write(""%)[[NT]]-"");;[N]Print(score,3);;[N]" ); IF language = 1 THEN Write("Write("" right answers on first tries and[[NT]]-"");;[N]") ELSE IF language = 2 THEN Write("Write("" f`rstasvar och[[NT]]-"");;[N]"); Write("Print(latescore,3);;[N]"); IF language = 1 THEN Write( "Write("" total right out of a total of[[NT]]-"");;[N]Print(qcount,3);;[N]Write("" questions attempted.[[2N]]"");;[N]") ELSE IF language = 2 THEN Write("Write("" totalt av[[NT]]-"");;[N]Print(qcount,3);;[N]Write("" givna fr}gor.[[2N]]"");;[N]"); Write("Breakoutput;;[N]"); Write("Selectoutput(0);;[N]") END; Write("Newline;;[N]"); IF put THEN BEGIN IF language = 1 THEN BEGIN Write("Write(""If you have any message for the teacher, then type it now, one[[N]]"");;[N]"); Write("Write(""line at a time. Finish with an EXTRA push on the RETURN key.[[2N]]"");;[N]") END ELSE IF language = 2 THEN BEGIN Write("Write(""Om Du har n}got meddelande till l{raren, s} skriv det nu.[[N]]"");;[N]"); Write("Write(""Sluta med att trycka en extra g}ng p} RETURN-knappen.[[2N]]"");;[N]") END; Write("[N]mess:[N]"); IF bell THEN Write("Outsymbol(controlg);;[N]"); Write("Selectoutput(0);;[N]"); Write("Write(prompt);;[N]Breakoutput;;[N]getline;;[N]"); IF NOT lock THEN BEGIN Write("IF back THEN[N]BEGIN[N]back:= FALSE;;[N]GOTO "); Write(mainlab); Write(";;[N]END;;[N]") END; Write("IF answerlength > 0 THEN[N]"); Write("BEGIN[N]message:= TRUE;;[N]"); Write("Selectoutput(2);;[N]"); IF language = 1 THEN Write("Write(""MESSAGE: "");;[N]") ELSE IF language = 2 THEN Write("Write(""MEDDELANDE: "");;[N]"); Write("FOR pos:= 1 STEP 1 UNTIL answerlength DO Outsymbol(answer.[[pos]]);;[N]"); Write("GOTO mess;;[N]END;;[N]") END; Write("Breakoutput;;[N]"); Write("Selectoutput(0);;[N]"); IF NOT sgnosis THEN BEGIN IF language = 1 THEN Write("Write(""Leaving the lesson """"") ELSE IF language = 2 THEN Write("Write(""G}r ut ur lektion """""); Write(lessonname); Write("""""...[[N]]"");;[N]") END; IF disk THEN BEGIN Write("Openfile(1,"""); Write(lessonname); Write(".DTA"", %155);;[N]"); Write("Selectinput(1);;[N]Breakoutput;;[N]Selectoutput(2);;[N]Transfile;;[N]"); Write("Openfile (1,"""");;[N]Openfile (2,"""); Write(lessonname); Write(".DTA"", %155);;[N]"); Write("Closefile(1);;[N]"); Write("Openfile(1,"""); Write(lessonname); Write(".SRT"", %155);;[N]"); Write("Selectinput(1);;[N]Selectoutput(3);;[N]Transfile;;[N]"); Write("Openfile (1,"""");;[N]Openfile (3,"""); Write(lessonname); Write(".SRT"", %155);;[N]") END; Write("Selectoutput(0);;[N]"); IF language=1 THEN BEGIN Write("Write(""[[2N]]Have a nice day"");;[N]"); Write("IF firstname # """" THEN[N]BEGIN[N]Write("", "");;[N]Write(firstname);;[N]END;;[N]"); Write("Write(""!"");;[N]") END END of PR*CEDURE putfinish; PROCEDURE gotoput; COMMENT ------------------------------------------------- 'Gotoput' determines whether the current GNOSIS command is a "%SAME" command and, if so, outputs the appropriate "GOTO" into the translated Algol program. If the command is not %SAME, then a default GOTO statement is output. Of course, if the teacher provided an explicit %GOTO in the previous line, then the default GOTO statement (which is always output) is superfluous. -----------------------------------------------------------; BEGIN goanywherecommands; IF controlstart = "SAM" THEN COMMENT ------------------------------------------------- This command is used to avoid storing duplicate comments whenever the comment provided for the prvious %RIGHT, %WRONG, %LACK, or %NEUTRAL answer pattern can be reused. Obviously, the first answer command after a %QUESTION command cannot be followed by a %SAME command since there would, in this case, be no previous comment to reuse. -----------------------------------------------------------; BEGIN IF same = "" THEN errmess(TRUE, "2177: No previous comment available.[N]****** Command will be ignored.") ELSE BEGIN Write("GOTO SAME"); Write(same); Write(";;[N]") END; getline; IF answer.[1] # controlchar THEN BEGIN errmess(TRUE, "2187: A command line is required at this point. As a result,[N]****** this line up to next command line will be ignored."); WHILE answer.[1] # controlchar DO getline END; getcontrolstart(4) END ELSE BEGIN delet(same); same:= Copy(lab); IF NOT right AND NOT neutral THEN Write("GOTO repeat;;[N]") ELSE Write("GOTO next;;[N]"); empty:= FALSE END; Newline; Write(lab); Write(":[N]") END of PR*CEDURE gotoput; PROCEDURE makelabel; COMMENT ------------------------------------------------- Extra labels have to be created for insertion into the translated ALGOL program. 'Makelabel' will generate a new label at each call (i.e, QZXAAA, QZXAAB, QZXAAC etc.). The label is stored in the global STRING 'lab' which is initialized to "QZXAAA" for each new %QUESTION command in the lesson. -----------------------------------------------------------; BEGIN INTEGER i, k; i:= Length(lab); letterchange: k:= lab.[i]; IF k >= z THEN BEGIN lab.[i]:= a; i:= i-1; GOTO letterchange END ELSE lab.[i]:= k+1 END of PR*CEDURE makelabel; PROCEDURE makemainlab; COMMENT ------------------------------------------------- A different set of labels have to be created for insertion in the translated ALGOL program. 'Makemainlab' will generate a new label for this purpose at each call (i.e., ZQXAAA, ZQXAAB, ZQXAAC etc.). The label is stored in the global STRING "mainlab" which is initialized to "ZQXAAA" once for each lesson. This set of labels is used to enable the student to skip backward in a lesson. His backward path will be strictly linear -- not necessarily the reverse of the path he followed when moving forward. -----------------------------------------------------------; BEGIN INTEGER i, k; labelcount:= labelcount+1; i:= Length(mainlab); letterchange: k:= mainlab.[i]; IF k >= z THEN BEGIN mainlab.[i]:= a; i:= i-1; GOTO letterchange END ELSE mainlab.[i]:= k+1 END of PR*CEDURE makemainlabel; PROCEDURE openoutput; COMMENT ------------------------------------------------- The output file is given the same filename as the input GNOSIS lesson but another extension: '.ALG'. That file is opened by the PROCEDURE 'openoutput'. -----------------------------------------------------------; BEGIN STRING filename; INTEGER i; filename:= Newstring(Length(lessonname)+5,7); FOR i:= 1 STEP 1 UNTIL Length(lessonname) DO filename.[i]:= lessonname.[i]; filename.[i+1]:= ".".[1]; filename.[i+2]:= "A".[1]; filename.[i+3]:= "L".[1]; filename.[i+4]:= "G".[1]; Openfile(2,filename,%057); opened:= TRUE; Selectoutput(2) END of PR*CEDURE openoutput; COMMENT ------------------------------------------------- Initialization of program CONSTANTS follows. -----------------------------------------------------------; carriagereturn:= 13; linefeed:= 10; formfeed:= 12; tabchar:= 9; linenumber:= Newstring(6,7); linenumber.[6]:= 9; answer:= Newstring(135,7); answer.[134]:= carriagereturn; answer.[135]:= linefeed; colon:= ":".[1]; equalsign:= "=".[1]; semicolon:= ";;".[1]; dot:= ".".[1]; blank:= " ".[1]; exclamation:= "!".[1]; doublequote:= """".[1]; leftsquare:= "[[".[1]; rightsquare:= "]]".[1]; rightmargin:= 72; temp:= controlstart:= lessonname:= word:= teacher:= question:= word:= ""; a:= "A".[1]; z:= "Z".[1]; lab:= ""; lcaa:= "}".[1]; lcae:= "{".[1]; lcoe:= "`".[1]; lce:= "e".[1]; ucaa:= "$".[1]; ucae:= "#".[1]; ucoe:= "@".[1]; uce:= "E".[1]; COMMENT ------------------------------------------------- Channel 3 is binary to check if the file is linenumbered. -----------------------------------------------------------; Input(0,"TTY"); Input(1,"DSK"); Input(3,"DSK",11); Output(2,"DSK"); Write("GNOSIS Version 2 (June, 1978)[N]"); Write("Copyright (c) 1978 by Jacob Palme and Walter Maner"); Write("[2N]Do NOT type ahead while running the GNOSIS translator.[N]"); start: COMMENT ------------------------------------------------- Initialization of program VARIABLES follows. Program defaults are handled first. GNOSIS returns to this point after a lesson has been translated and initializes for the translation of another lesson. -----------------------------------------------------------; switchcount:= 0; linenumbered:= skip:= FALSE; linecount:= 0; labelcount:= 0; mainlab:= Copy("ZQXAAA"); COMMENT ------------------------------------------------- Many of the following BOOLEAN values will be reset when GNOSIS begins to read the lesson script. -----------------------------------------------------------; extra:= TRUE; sgnosis:= put:= keep:= order:= opened:= nocontrolc:= disk:= transferlesson:= lock:= bell:= name:= FALSE; language:= 1; COMMENT ------------------------------------------------- GNOSIS defaults to language 1 (English). -----------------------------------------------------------; errlines:= controlchar:= qcount:= texcount:= 0; delet(teacher); teacher:= ""; Write("[2N]Enter lesson name with extension, or push RETURN to exit from GNOSIS.[N]"); Write("-->[B]"); getline; infile:= Copy(answer,1,answerlength); IF infile = "" THEN GOTO stop; upcase(infile); pos:= 1; WHILE pos < Length(infile) AND infile.[pos] # ".".[1] DO pos:= pos + 1; IF pos = Length(infile) THEN BEGIN STRING temp; temp:= Newstring(Length(infile)+5,7); FOR pos:= 1 STEP 1 UNTIL Length(infile) DO temp.[pos]:=infile.[pos]; temp.[pos+1]:= ".".[1]; temp.[pos+2]:= "G".[1]; temp.[pos+3]:= "N".[1]; temp.[pos+4]:= "O".[1]; infile:= Newstring(Length(temp),7); infile:= temp END; Write("[2N]Translation of the lesson """); Write(infile); Write(""" has begun...[2N]"); COMMENT ------------------------------------------------- Now we check if the file is linenumbered or not. -----------------------------------------------------------; Openfile(3,infile); Selectinput(3); Insymbol(pos); linenumbered:= pos REM 2 # 0; Closefile(3); Openfile(1,infile); Selectinput(1); COMMENT ------------------------------------------------- The part before the extension of the lessonname is found. -----------------------------------------------------------; FOR pos:= 1 STEP 1 UNTIL Length(infile) DO IF infile.[pos] = dot THEN BEGIN pos:= pos-1; GOTO leavefor END; leavefor: delet(teacher); delet(copyright); delet(lessonname); lessonname:= Copy(infile,1,pos); openoutput; COMMENT ------------------------------------------------- The first lesson line is read, and the first character of that line is assumed to be the command indicator (usually '%'). -----------------------------------------------------------; startloop: getline; IF controlchar = 0 THEN BEGIN WHILE answerlength = 0 DO getline; controlchar:= answer.[1]; IF controlchar # "%".[1] THEN errmess(FALSE, "2430: You are not using '%' as a command indicator.") END; IF answer.[1] # controlchar THEN BEGIN errmess(TRUE, "2435: A command line is required at this point. As a result,[N]****** this line up to next command line will be ignored."); WHILE answer.[1] # controlchar DO getline END; COMMENT ------------------------------------------------- Here the program starts looking for those GNOSIS commands which are expected before the start of the lesson, that is %BELL, %KEEP, %LOCK, %DISK, %SGNOSIS, %NAME, %TEACHER and %LANGUAGE command lines. -----------------------------------------------------------; getcontrolstart(4); goanywherecommands; IF controlstart = "NOC" THEN BEGIN COMMENT ------------------------------------------------- In this mode, if a student types a CTRL-C, he will not be returned to the monitor level. Instead, he will be logged off the system without further ado. (Thus CTRL-C's become functionally equivalent to the 'BYE' command in BASIC.) -----------------------------------------------------------; nocontrolc:= TRUE; errmess(FALSE, "2456: The GNOLIB.REL file must reside in 'SYS:'. [N]****** Load as follows: '.LOAD .ALG, SYS:GNOLIB.REL'."); GOTO startloop END; IF controlstart = "NEX" THEN COMMENT ------------------------------------------------- This command tells GNOSIS which lesson to run after the student finishes the current lesson. Transfer to this lesson is wholly automatic at the point of exit, just as if the new lesson had been called up by GNOSIS directly, without intervention of the monitor. The next lesson can be any RUN-able or R-able program, i.e., any program with the extension .SAV, .EXE or .SHR). 'run("")' is should appear on the same line, after this command, when the lesson resides in the student's area Or, 'r("")' should appear when it resides in the SYS: area. Technical Note: The literal which replaces '' must be a single filename, without extension. -----------------------------------------------------------; BEGIN nextlesson:= getword; upcase(nextlesson); transferlesson:= TRUE; errmess(FALSE, "2482: The GNOLIB.REL file must reside in 'SYS:'. [N]****** Load as follows: '.LOAD .ALG, SYS:GNOLIB.REL'."); GOTO startloop END; IF controlstart = "LAN" THEN COMMENT ------------------------------------------------- This command tells the system which language to use in communication with the student. The command is not necessary if the language is to be English. (The language used in communicating with the teacher at the time his script is being processed by GNOSIS is not affected by the %LANGUAGE command. It is always English.) -----------------------------------------------------------; BEGIN delet(word); word:= getword; upcase(word); IF word = "ENGLISH" THEN language:= 1 ELSE IF word = "SWEDISH" THEN language:= 2 ELSE BEGIN errmess(FALSE, "2501: Unknown language.[N]****** GNOSIS will default to English..") END; GOTO startloop END; IF controlstart = "SGN" THEN COMMENT -------------------------------------------------- GNOSIS lessons normally output a header which tells the student that he is running a GNOSIS lesson, that it was authored by so-and-so, and that he can do certain things to control his path through the lesson. Also, at the end of the question-and-answer section of the lesson, GNOSIS will normally output a corresponding trailer which tells the student how he scored. %SGNOSIS (for (S)top GNOSIS) will inhibit the display of this header and trailer. The command is useful when you want to use GNOSIS to administer a questionnaire. -----------------------------------------------------------; BEGIN sgnosis:= TRUE; GOTO startloop END; IF controlstart = "SWI" THEN BEGIN COMMENT ------------------------------------------------- The %SWITCH command declares and initializes a BOOLEAN variable used to control skipping in conjunction with %IF, %IFNOT and %IFEND commands. The variable appears after '%SWITCH', on the same command line. -----------------------------------------------------------; pastoperation; switchword:= getword; upcase(switchword); IF Length(switchword) <= 0 THEN errmess(TRUE, "2530: No switchname in %SWITCH command.[N]****** Line will be ignored."); FOR switchnumber:= 1 STEP 1 UNTIL switchcount DO IF switchword = switchname[switchnumber] THEN errmess(FALSE, "2536: This switch is doubly defined."); IF switchcount = 9 THEN errmess(TRUE, "2538: More than 9 switches not allowed.[N]THIS switch will replace the old number nine.") ELSE switchcount:= switchcount+1; switchname[switchcount]:= Copy(switchword); switchword:= getword; upcase(switchword); IF switchword = "TRUE" THEN switchval[switchcount]:= TRUE ELSE IF switchword = "FALSE" THEN switchval[switchcount]:= FALSE ELSE errmess(TRUE, "2545: Switch value neither 'TRUE' nor 'FALSE'.[N]****** The effect of this error is undefined."); ifset[switchcount]:= FALSE; GOTO startloop END; IF controlstart = "KEE" THEN BEGIN COMMENT ------------------------------------------------- This command is useful only in association with a %TEACHER command, i.e., in report-generating lessons. Unless %KEEP is used in such a lesson (or unless %DISK is used, which executes an implicit %KEEP command), the student will be directed to pick up the teacher report generated by the lesson (after it has been printed by the lineprinter), and to bring this report to the teacher. This command is probably not needed any longer since reports can conveniently be kept on disk. -----------------------------------------------------------; keep:= TRUE; GOTO startloop END; IF controlstart = "LOC" THEN COMMENT ------------------------------------------------- This command PERMANENTLY disables the backstepping and skipping provisions of GNOSIS. The student is in effect "locked" into the lesson path defined by the teacher unless the teacher explicitly supplies branching opportunities in the lesson itself. (Note, however, that the student can still END the lesson by typing 'stop'.) There does not exist, as yet, a corresponding %UNLOCK command. -----------------------------------------------------------; BEGIN lock:= TRUE; GOTO startloop END; IF controlstart = "BEL" THEN BEGIN COMMENT ------------------------------------------------- This command causes the bell to be rung at the student's terminal whenever the computer is awaiting input from him. Useful in noisy environments and with first-time computer users. -----------------------------------------------------------; bell:= TRUE; GOTO startloop END; IF controlstart = "NAM" THEN COMMENT ------------------------------------------------- This command causes the GNOSIS lesson to exchange greetings with the student at the start of the lesson. The student's full name is requested (and routed to the .DTA file, if any) and his first name is saved for use in I/O routines. -----------------------------------------------------------; BEGIN name:= TRUE; GOTO startloop END; IF controlstart = "DIS" OR controlstart = "DSK" THEN BEGIN COMMENT ------------------------------------------------- This command causes teacher reports to be created at lesson runtime, then routed to preexisting disk files named .DTA and .SRT. The .DTA file contains a page of data for each run of the lesson. These data include the student's name, a trace of his unexpected answers, lesson statistics, and any message he may have left for the teacher. The .SRT file contains only the "unexpected answer" traces, converted to a format suitable for sorting. Unless the %DISK (or %DISC or %DSK) command is used, then teacher reports (if any) will be routed to the lineprinter and no machine readable records will remain. The reports queued to the lineprinter are equivalent to those which would have been contained in the .DTA file except that each page is printed as a separate item. Without the %DISK command, no .SRT file is generated. When using the %DISK command, care must be taken to ensure that the blank files .DTA and .SRT have been created IN THE ACCOUNT WHERE THE LESSON WILL BE RUN. They must exist there before the lesson is used for the first time. Otherwise a mysterious error message will be sent to the student by ALGDDT as he tries to exit from the lesson. A protection of <155> should be given to these report files. This isn't adequate for all purposes, of course, but it is a workable compromise for the teacher who wishes to inspect, print or copy-and-sort these records from another account. The %DISK command executes implicit %TEACHER and %KEEP commands. Thus, an explicit %TEACHER command will be required only if it is necessary to add the teacher's name and address. Technical Note: Unless %NAME is used in conjunction with %DISK, only one student at a time may use a report-generating lesson. When the %NAME command is used, however, temporary .DTA and .SRT files (based on his first name) are opened at runtime. Writing to these files (until the very end of the lesson) usually avoids any conflict-of-access problems connected with writing the main .DTA and .SRT files. -----------------------------------------------------------; disk:= keep:= put:= TRUE; errmess(FALSE, "2651: Ignore this message if you have created"); Selectoutput(0); Write("****** "); Write(lessonname); Write(".DTA<155> and "); Write(lessonname); Write(".SRT<155> files to receive teacher reports."); Newline; Selectoutput(2); GOTO startloop END; IF controlstart = "COP" THEN BEGIN COMMENT ------------------------------------------------- If this command is used then, each time the lesson is run, a copyright notice will be output as part of the lesson header. This copyright applies only to the lesson itself, of course -- not to the GNOSIS software. -----------------------------------------------------------; pastoperation; copyright:= Copy(answer,pos,answerlength); GOTO startloop END; IF controlstart = "TEA" THEN BEGIN COMMENT ------------------------------------------------- If this command is included in a lesson, then teacher reports (of some kind) will be produced for the lesson, and students will be told who authored the lesson each time the lesson is run. If the %TEACHER command is used with an empty argument, then an anonymous teacher report will be generated and no information about lesson authorship will be displayed to the student at runtime. The .DTA teacher report contains the following information: ---- The student's full name, if operating in %NAME mode. ---- Unexpected answers to questions in the lesson. This trace will include any unforeseen student response or, roughly speaking, all those responses students entered without getting any specific diagnostic feedback. Recurrent unanticipated responses usually signal a need to rewrite the lesson with that response as an argument for an answer command -- followed of course by appropriate comments or hints. ---- Basic statistical data about the student's performance. This data should not be taken too seriously, especially when the lesson permits the student to exercise control over skipping and backstepping. ---- Student comments about the lesson. GNOSIS can store as lengthy a comment as the student wants to type, even if his comment should run on for pages. The .SRT report, which is generated if %DISK is used in conjunction with %TEACHER, contains the trace of unexpected answers in a form ready to be input to the utility program SORT. The following command string usually does the job: .R SORT *x.SRT=x.SRT/KEY:1:60:A/ALPHA/RECORD:132 where 'x' = . -----------------------------------------------------------; pastoperation; teacher:= Copy(answer,pos,answerlength); IF teacher = "" THEN BEGIN errmess(FALSE, "2727: Ignore this message if you meant to omit[N]****** teacher's name and address."); keep:=TRUE END; put:=TRUE; GOTO startloop END; COMMENT ------------------------------------------------- Since all of the 'IF controlstart = ' tests above have failed, the current command line must be something other than %BELL, %COPYRIGHT, %DISK (or %DSK or %DISC), %KEEP, %LANGUAGE, %LOCK, %NAME, %NEXTLESSON, %NOCONTROLC, %SGNOSIS, %SWITCH or %TEACHER. This being so, it is time to output that part of the lesson preface which contains the data declarations. -----------------------------------------------------------; putvariables; WHILE controlstart = "VAR" DO COMMENT ------------------------------------------------- The %VARIABLES command causes user-defined variables (if any) to be written into the lesson. -----------------------------------------------------------; BEGIN controlstart:= Copy("ALG"); goanywherecommands END; checkprecedence(13); COMMENT ------------------------------------------------- Now it is time to output the user's own PROCEDURE declarations. -----------------------------------------------------------; putprocedures; WHILE (controlstart = "PRO" OR controlstart = "HEL") DO BEGIN WHILE controlstart = "PRO" DO COMMENT ------------------------------------------------- This command is used to make a standard ALGOL PROCEDURE declaration. Code followingthe %PROCEDURE command is processed like code following the %ALGOL command except that it is written into the lesson at the point where PROCEDURE declarations belong. The %PROCEDURE command itself is not copied, since it might as well have been abbreviated to '%PRO'. Thus the terms 'PROCEDURE', 'EXTERNAL PROCEDURE', and so on should occur immediately after '%PROCEDURE' even though this might appear redundant. -----------------------------------------------------------; BEGIN controlstart:= Copy("ALG"); Newline; Newline; goanywherecommands END controlstart = "PRO"; WHILE controlstart = "HEL" DO COMMENT ------------------------------------------------- The %HELP command causes GNOSIS to establish a golbal help text, i.e., one which is potentially accessible by the student from any part of the lesson. '%ALGOL ', wherever it may appear in the script, will subsequently cause the help text to be displayed. The name of the help procedure (limited to a single word) must immediately follow '%HELP', on the same command line. Unlike the %PROCEDURE command (see above), GNOSIS does in his case write the word 'PROCEDURE' into the translated lesson. -----------------------------------------------------------; BEGIN Write("[2N]PROCEDURE "); Write(getword); Write(";;[N]"); Write("BEGIN[N]"); textlines; Write("END;;[N]"); goanywherecommands END controlstart = "HEL"; END "PRO" or "HEL"; checkprecedence(14); putvalues; WHILE controlstart = "INI" DO COMMENT ------------------------------------------------- This command is normally used to initialize global variables which have been declared with the %VARIABLES command, although it could just as well be used to reinitialize GNOSIS system parameters if that should ever be necessary. %INITIALIZE operates like %ALGOL (see above), except that the code following %INITIALIZE is written into the lesson at the point where data are initialized, just before the start of the main program. -----------------------------------------------------------; BEGIN controlstart:= Copy("ALG"); Newline; Newline; goanywherecommands END; checkprecedence(16); COMMENT ------------------------------------------------- Now we finish the lesson preface by outputting the final portion of the header. This code sets up some I/O routines and does some further initialization. -----------------------------------------------------------; putheader; whichcontrol: COMMENT ------------------------------------------------- Check which of the remaining GNOSIS command lines we have here. -----------------------------------------------------------; goanywherecommands; IF controlstart = "QEN" THEN COMMENT ------------------------------------------------- This command is used because, as a side-effect, it causes GNOSIS to finishing writing code for a particular question-and-answer processing loop. This command is rarely used since %TEXT, %QUESTION and %END contain this command implicitly. However, %QEND is sometimes useful before certain %ALGOL commands since those commands do NOT implicitly close the question-and-answer loop. So, for example, if you had an independent block of ALGOL code to insert between two %QUESTION commands, it would be necessary to use %QEND after the first question and answer patterns. -----------------------------------------------------------; BEGIN shortalgol; GOTO checkline END; IF controlstart = "TEX" THEN COMMENT ------------------------------------------------- The text on the lines following this command, and until the next GNOSIS command, will be displayed to the student. Like many other commands, the %TEXT command can take any single line of ALGOL code as an argument. Usually this code will contain conditions (as arguments for IF-statemens) and/or labels. -----------------------------------------------------------; BEGIN texcount:= texcount + 1; Write("[4N]COMMENT: Text"); Print(texcount,2); Write(";;[N]"); shortalgol; previouslab:= mainlab; makemainlab; Newline; Write(mainlab); Write(":[N]"); textlines; Write("BEGIN[N]pause;;[N]"); IF NOT lock THEN BEGIN COMMENT ------------------------------------------------- When the student executes a BACK command, he is backed to the previous %TEXT or %QUESTION. What usually follows is a repetition of the material he just encountered, but not always. If he arrived at the point where he issues the BACK command via a GOTO, then the backstepping (which is alaways linear) will not return him to the material just encountered. We have a fix for this, but do not feel that the additional overhead required by the fix can be justified. -----------------------------------------------------------; Write("IF back THEN[N]BEGIN[N]back:= FALSE;;[N]GOTO "); Write(previouslab); Write(";;[N]END;;[N]") END; Write("END;;[N]"); GOTO whichcontrol END; IF controlstart = "QUE" THEN BEGIN COMMENT ------------------------------------------------- Code which initializes question handling is written into the lesson at this point. With the exception of the incremental comment GNOSIS supplies to identify the question, this code is rewritten in essentially the same form for each occurrence of '%QUESTION' in the lesson script. The %QUESTION command can take any single line of ALGOL code as an argument. Uusally this code will contain conditions (as arguments for IF-statements) and/or labels. If a label is supplied following this command, then it is extracted for further use in preparing identifying phrases for the teacher reports. GNOSIS assumes teachers will usually not bother to label their questions, so a count is kept of the number of %QUESTION commands encountered in the lesson. The value of this count will be paried with students' unexpected answers to the question so that the the question which generated these unexpected answers may be identified in the teacher reports. -----------------------------------------------------------; INTEGER errcount; delet(lab); lab:= Copy("QZXAAA"); delet(same); same:= ""; errcount:= 0; allneutral:= TRUE; rightfound:= zeroprinted:= FALSE; qcount:= qcount+1; Write("[4N]COMMENT: Question"); Print(qcount,2); Write(";;[N]"); IF put THEN BEGIN INTEGER pos1, pos2; delet(question); FOR pos1:= 1 STEP 1 UNTIL answerlength DO BEGIN IF answer.[pos1] = colon THEN BEGIN IF pos1 < answerlength THEN pos1:= pos1 + 1 ELSE BEGIN pos2:= pos1 - 1; GOTO ditto END; IF answer.[pos1] # equalsign THEN BEGIN pos2:= pos1 - 2; pos1:= pos1 - 1; ditto: IF answer.[pos1] # blank AND answer.[pos1] # semicolon THEN BEGIN pos1:= pos1 - 1; GOTO ditto END; delet(question); question:= Copy(answer,pos1+1,pos2); pos1:= pos2 + 1 END END END END; shortalgol; previouslab:= mainlab; makemainlab; Newline; Write(mainlab); Write(":[N]"); Write("BEGIN[N]INTEGER subblock;;[N]"); Write("firsttry:= TRUE;;[N]lastrights:= lasterrors:= 0;;[N]"); Write("ihaveblanked:= FALSE;;[N]"); COMMENT ------------------------------------------------- Repetition of the same question loop begins here. -----------------------------------------------------------; Write("IF FALSE THEN[N]"); Write("BEGIN[2N]repeat:[N]"); Write("tryagain;;[N]"); Write("END;;[N]"); Write("Newline;;[N]"); COMMENT ------------------------------------------------- Output of the question text - Input of the answer. -----------------------------------------------------------; textlines; IF bell THEN Write("Outsymbol(controlg);;[N]"); Write("Write(prompt);;[N]getline;;[N]"); Write("IF stop THEN[N]"); Write("BEGIN[N]"); IF put THEN BEGIN saveanlen:= answerlength; answerlength:= 0; putaway; answerlength:= saveanlen END; Write("stop:= FALSE;;[N]GOTO endoflesson;;[N]END;;[N]"); IF NOT lock THEN BEGIN Write("IF back THEN[N]"); Write("BEGIN[N]"); IF put THEN BEGIN saveanlen:= answerlength; answerlength:= 0; putaway; answerlength:= saveanlen END; Write("back:= FALSE;;[N]GOTO "); Write(previouslab); Write(";;[N]END;;[N]"); Write("IF skip THEN[N]"); Write("BEGIN[N]"); IF put THEN BEGIN saveanlen:= answerlength; answerlength:= 0; putaway; answerlength:= saveanlen END; Write("skip:= FALSE;;[N]GOTO simulate;;[N]END;;[N]") END; COMMENT ------------------------------------------------- Loop for answer commands: %RIGHT, %WRONG, %LACK or %NEUTRAL. -----------------------------------------------------------; answerfind: neutral:= FALSE; goanywherecommands; lack:= FALSE; IF controlstart = "RIG" THEN BEGIN COMMENT ------------------------------------------------- This command causes GNOSIS to search for an answer pattern and, if it is found, to consider it correct. If a match for the canned answer is found in the student's response, then the following happens: ---- The GNOSIS system gives the student one of several stock messages, all of which have the effect of providing positive feedback. ---- If he had previously missed this question then, depending on the circumstances, GNOSIS provides additional stock messages designed to encourage him. ---- The text on the optional line(s) following the %RIGHT command (until the next GNOSIS command) is displayed to the student. ---- Various statistical variables (see %TEXT command) are updated. ---- GNOSIS moves on to the next item of business. We save first %RIGHT answer to give to the student (1) if he executes a SKIP command or (2) if he repeatedly fails to give a right answer. -----------------------------------------------------------; right:= TRUE; IF NOT rightfound THEN BEGIN IF pos <= answerlength THEN BEGIN delet(firstright); pastoperation; firstright:= Copy(answer, pos, answerlength); rightfound:= TRUE; pos:= 1 END END END ELSE IF controlstart = "WRO" THEN right:= FALSE ELSE COMMENT ------------------------------------------------- This command causes GNOSIS to search for an answer pattern and, if it is found, to consider it wrong. If a match for the canned answer is found in the student's response, then the following happens: ---- The GNOSIS system gives the student one of several stock messages, all of which have the effect of providing negative feedback. ---- The text on the optional line(s) following the %WRONG command (until the next GNOSIS command) is displayed to the student. ---- If the student is still getting meaningful help from the program, he is recycled through the question once more. Otherwise, GNOSIS simulates a correct answer (plus commentary for such an answer) and moves on to the next item of business. -----------------------------------------------------------; IF controlstart = "NEU" THEN neutral:= TRUE ELSE COMMENT ------------------------------------------------- This command tells GNOSIS to search for an answer and, if it is found, to interpret it as being neither correct nor incorrect. If the neutral target text is found in the student's response, then any line(s) of comments or hints provided by the teacher will be displayed to the student at that point. The default for %NEUTRAL commands is '%GOTO next' so the teacher who wants a question repeated after GNOSIS has found a neutral match will have to insert '%GOTO repeat' after that %NEUTRAL command (and comments, if any). Technical Note: The procedure "extratest" is NOT called for %NEUTRAL commands with empty answer patterns. Thus clever use of '%NEUTRAL ' should enable the teacher to trap most misspelled answers with no prejudice to the student's score -- or ego. -----------------------------------------------------------; IF controlstart = "LAC" THEN COMMENT ------------------------------------------------- This command causes GNOSIS to search for something which (a) is essential to a correct response but which (b) is likely to be missing. If the the item is not found in the student's answer, then he receives appropriate negative feedback from GNOSIS and the text (if any) on the optional line(s) after the %LACK command is displayed to him at that point. If more than one element is expected to be lacking from the student's response, then he receives feedback and commentary (if any) as soon as ANY of the elements cannot be detected in his response. The chief advantage of the %LACK command does not lie in its increased efficiency but rather in the opportunity it provides for writing highly specific diagnostic comments. Technical Note: Regardless of context, %LACK works as if %NOORDER and %EXTRA were declared immediately above it. -----------------------------------------------------------; BEGIN lack:= TRUE; right:= FALSE; IF NOT zeroprinted THEN zeroprint END ELSE BEGIN COMMENT ------------------------------------------------- There are no more answer commands (i.e., %RIGHT, %WRONG, %LACK or %NEUTRAL) for the current question, so now we write the trailing code for this question segment and recycle via 'GOTO whichcontrol' to the next non-answer command. -----------------------------------------------------------; IF NOT zeroprinted THEN zeroprint; unknownmessage; Write("END;;[N]"); Write("[N]simulate:[N]"); IF rightfound THEN BEGIN Write("giveanswer;;[N]"); textline(firstright); Write("Write(right[[random]]);;[N]Newline;;[N]GOTO SAMEQZXAAB;;[N]") END; Write("[N]next:[N]"); Write("pause;;[N]"); IF NOT lock THEN BEGIN Write("IF back THEN[N]BEGIN[N]back:= FALSE;;[N]GOTO "); Write(previouslab); Write(";;[N]END;;[N]") END; Write("END;;[N]"); GOTO whichcontrol END; Write("IF ihaveblanked THEN restore;;[N]"); delet(word); word:= getword; upcase(word); IF word = "" THEN empty:= TRUE; IF empty AND NOT right AND NOT neutral AND NOT lack THEN BEGIN COMMENT ------------------------------------------------- %WRONG with no answer pattern causes GNOSIS to generate code which handles unexpected answers. -----------------------------------------------------------; errcount:= errcount+1; IF errcount = 1 THEN BEGIN IF NOT zeroprinted THEN zeroprint END; Write("IF lasterrors ="); Print(errcount - 1,2,0); Write(" THEN[N]"); makelabel; unknownmessage; Newline; Write("SAME"); Write(lab); Write(":[N]"); textlines; gotoput; Write("END;;[N]"); GOTO answerfind END ELSE BEGIN COMMENT ------------------------------------------------- Now we interpret the teacher answer pattern and translate it into an ALGOL program sequence for comparing the student's answer with the pattern. -----------------------------------------------------------; IF NOT neutral THEN allneutral:= FALSE; IF errcount # 0 THEN BEGIN errmess(TRUE, "3200: Expected answers must precede first [N]****** empty answer pattern. Command will be ignored."); orderloop: getline; IF answer.[1] # controlchar THEN GOTO orderloop; getcontrolstart(4); GOTO answerfind END; makelabel; IF order THEN BEGIN Write("pos:= 1;;"); Newline END; WHILE word # "" DO BEGIN IF NOT lack THEN BEGIN IF NOT order THEN Write("pos:= 1;;[N]"); Write("IF NOT "); IF NOT order OR extra THEN Write("anyw"); Write("here(""") END; IF lack THEN Write("pos:= 1;;[N]IF NOT anywhere("""); FOR wpos:= 1 STEP 1 UNTIL Length(word) DO BEGIN charvalue:= word.[wpos]; IF charvalue = doublequote THEN Outsymbol(charvalue); IF charvalue = semicolon THEN Outsymbol(semicolon); Outsymbol(charvalue) END; Write(""") THEN GOTO "); IF lack THEN Write("LACK"); Write(lab); Write(";;[N]"); delet(word); word:= getword; upcase(word) END; IF NOT(extra OR lack) AND NOT(neutral AND empty) THEN BEGIN IF NOT order THEN Write("pos:= 1;;[N]"); Write("IF NOT here(endofline) THEN GOTO "); Write(lab); Write(";;[N]") END; IF lack THEN BEGIN Write("GOTO "); Write(lab); Write(";;[N]"); Newline; Write("LACK"); Write(lab); Write(":[N]") END; COMMENT ------------------------------------------------- This code will tell the student whether he was right or wrong and will update the score statistics. -----------------------------------------------------------; IF NOT neutral THEN BEGIN IF right THEN Write("rightmessage") ELSE Write("wrongmessage"); Write(";;[N]") END; IF lack THEN BEGIN IF put THEN putaway END; IF extra THEN BEGIN IF (neutral AND NOT empty) OR right THEN Write("extratest;;[N]") END; Newline; Write("SAME"); Write(lab); Write(":[N]"); COMMENT ------------------------------------------------- This code will output the comment the teacher wrote to go with a student's answer. -----------------------------------------------------------; textlines; gotoput; GOTO answerfind END END; IF controlstart = "FIN" THEN COMMENT ------------------------------------------------- This command will enable the user to place one or more lines of ALGOL code in the GNOSIS translated lesson immediately before its final 'END'. (Ordinarily this would be possible only through editing the .ALG file produced by GNOSIS.) '%ALGOL ' MUST follow the %FINISH command. Note too that %FINISH does NOT terminate the lesson script. A '%END' is still necessary to close the lesson. -----------------------------------------------------------; BEGIN putfinish; getline; IF answer.[1] # controlchar THEN GOTO finclose; getcontrolstart(4); goanywherecommands; GOTO finclose END; IF controlstart = "END" THEN COMMENT ------------------------------------------------- This is -- MUST be -- the last command of a lesson. Any text after this command will be ignored by GNOSIS. -----------------------------------------------------------; BEGIN putfinish; finclose: IF transferlesson THEN Write(nextlesson); Write("[N]END;;[N]"); delet(infile); Selectinput(0); Selectoutput(0); Closefile(1); Closefile(2); Write("...and is ready.[N]"); GOTO start END; COMMENT ------------------------------------------------- Since 'getcontrolstart' has validated the command, the only possible reason for reaching this point would be an error in precedence. Thus it is not necessary to call 'checkprecedence' to discover this. -----------------------------------------------------------; errmess(TRUE, "3330: Command is out of place here. As a result,[N]****** this line up to next command line will be ignored."); checkline: getline; IF answer.[1] # controlchar THEN BEGIN errmess(TRUE, "3340: A command line is required at this point. As a result,[N]****** this line up to next command line will be ignored."); WHILE answer.[1] # controlchar DO getline END; controlline: getcontrolstart(4); GOTO whichcontrol; stop: END