X-Git-Url: https://jxself.org/git/?a=blobdiff_plain;f=parser.h;h=f72b571e42547f2620bcd86e33d3ae76f5b75c14;hb=HEAD;hp=d0b427ecd480f1f1951d63ddeec89b86af76765e;hpb=d94736d23b49e9b6653564cef67f96d028405c11;p=informlib.git diff --git a/parser.h b/parser.h index d0b427e..f72b571 100644 --- a/parser.h +++ b/parser.h @@ -1,9 +1,9 @@ ! ============================================================================== ! PARSER: Front end to parser. ! -! Supplied for use with Inform 6 -- Release 6.12.2 -- Serial number 180520 +! Supplied for use with Inform 6 -- Release 6.12.6 -- Serial number 220219 ! -! Copyright Graham Nelson 1993-2004 and David Griffith 2012-2018 +! Copyright Graham Nelson 1993-2004 and David Griffith 2012-2022 ! ! This file is free software: you can redistribute it and/or modify ! it under the terms of the GNU Affero General Public License as @@ -70,13 +70,10 @@ System_file; ! ------------------------------------------------------------------------------ #Ifndef VN_1633; -Message fatalerror "*** Library 6.12.2 needs Inform v6.33 or later to work ***"; +Message fatalerror "*** Library 6.12.x needs Inform v6.33 or later to work ***"; #Endif; ! VN_ -Constant LibSerial "180520"; -Constant LibRelease "6.12.2"; -Constant LIBRARY_VERSION 612; -Constant Grammar__Version 2; +Include "version"; Constant BEFORE_PARSER 10; Constant AFTER_PARSER 20; @@ -118,8 +115,13 @@ Constant HDR_SCREENHLINES $20; ! byte Constant HDR_SCREENWCHARS $21; ! byte Constant HDR_SCREENWUNITS $22; ! word Constant HDR_SCREENHUNITS $24; ! word +#Iftrue (#version_number == 6); +Constant HDR_FONTHUNITS $26; ! byte +Constant HDR_FONTWUNITS $27; ! byte +#Ifnot; Constant HDR_FONTWUNITS $26; ! byte Constant HDR_FONTHUNITS $27; ! byte +#Endif; Constant HDR_ROUTINEOFFSET $28; ! word Constant HDR_STRINGOFFSET $2A; ! word Constant HDR_BGCOLOUR $2C; ! byte @@ -164,6 +166,7 @@ Fake_Action TheSame; Fake_Action PluralFound; Fake_Action ListMiscellany; Fake_Action Miscellany; +Fake_Action RunTimeError; Fake_Action Prompt; Fake_Action NotUnderstood; Fake_Action Going; @@ -326,6 +329,10 @@ Global inventory_stage = 1; ! 1 or 2 according to the context in which ! "invent" routines of objects are called Global inventory_style; ! List-writer style currently used while ! printing inventories + +Global objects_style; ! List-writer style currently used while +Global places_style; ! printing objects handled or places visited + ! ------------------------------------------------------------------------------ ! Menus and printing ! ------------------------------------------------------------------------------ @@ -376,6 +383,20 @@ Constant CLR_WHITE 9; Constant CLR_PURPLE 7; Constant CLR_AZURE 8; +#Ifdef TARGET_GLULX; +Array GlulxColourValues + --> (-2) + (-1) + $000000 + $EF0000 + $00D600 + $EFEF00 + $006BB5 + $FF00FF + $00EFEF + $FFFFFF; +#Endif; ! TARGET_GLULX + Constant WIN_ALL 0; Constant WIN_STATUS 1; Constant WIN_MAIN 2; @@ -804,7 +825,6 @@ Object thedark "(darkness object)" ! If you want to use the third-person of the narrative voice, you will ! need to replace this selfobj with your own. - Class SelfClass with name ',a' ',b' ',c' ',d' ',e', short_name YOURSELF__TX, @@ -815,7 +835,7 @@ Class SelfClass each_turn NULL, time_out NULL, describe NULL, - article "the", + article THE__TX, add_to_scope 0, capacity 100, parse_name 0, @@ -1444,7 +1464,7 @@ Object InformParser "(Inform Parser)" w2 = a_table-->(3*oops_from - 1); ! Length of word to go #Endif; ! TARGET_ -#IfDef OOPS_CHECK; +#Ifdef OOPS_CHECK; print "[~"; for (i=0 : i(i+w); print "~ --> ~"; @@ -1467,12 +1487,12 @@ Object InformParser "(Inform Parser)" for (i=0 : i(i+w) = buffer2->(i+x1); -#IfDef OOPS_CHECK; +#Ifdef OOPS_CHECK; print (char) buffer2->(i+x1); #Endif; } -#IfDef OOPS_CHECK; +#Ifdef OOPS_CHECK; print "~]^^"; #Endif; @@ -1494,7 +1514,7 @@ Object InformParser "(Inform Parser)" i = (~~i); #Endif; ! TARGET_ if (i == 0) { L__M(##Miscellany, 7); return 0; } - L__M(##Miscellany, 1); + L__M(##Miscellany, 6); return 1; ]; @@ -1692,6 +1712,9 @@ Object InformParser "(Inform Parser)" if (usual_grammar_after == 0) { j = verb_wordnum; + #Ifdef TARGET_ZCODE; + parser_one = 0; + #Endif; i = RunRoutines(actor, grammar); #Ifdef DEBUG; if (parser_trace >= 2 && actor.grammar ~= 0 or NULL) @@ -1700,9 +1723,10 @@ Object InformParser "(Inform Parser)" #Ifdef TARGET_ZCODE; if ((i ~= 0 or 1) && + (parser_one ~= 0 || (UnsignedCompare(i, dict_start) < 0 || UnsignedCompare(i, dict_end) >= 0 || - (i - dict_start) % dict_entry_size ~= 0)) { + (i - dict_start) % dict_entry_size ~= 0))) { usual_grammar_after = j; i=-i; } @@ -1743,14 +1767,14 @@ Object InformParser "(Inform Parser)" if (verb_word == 0 || ((verb_word->#dict_par1) & DICT_VERB) == 0) { - ! So is the first word an object contained in the special object "compass" + ! So is the first word an object contained in the special object "Compass" ! (i.e., a direction)? This needs use of NounDomain, a routine which ! does the object matching, returning the object number, or 0 if none found, ! or REPARSE_CODE if it has restructured the parse table so the whole parse ! must be begun again... wn = verb_wordnum; indef_mode = false; token_filter = 0; - l = NounDomain(compass, 0, NOUN_TOKEN); + l = NounDomain(Compass, 0, NOUN_TOKEN); if (l == REPARSE_CODE) jump ReParse; ! If it is a direction, send back the results: @@ -2226,7 +2250,7 @@ Object InformParser "(Inform Parser)" oops_from = 0; ! ...explain any inferences made (using the pattern)... - + ! This is where the parser replies (the sword) if things aren't clear. if (inferfrom ~= 0 && no_infer_message == false) { print "("; PrintCommand(inferfrom); print ")^"; } @@ -2506,14 +2530,19 @@ Constant UNLIT_BIT = 32; indef_nspec_at = 0; ]; -[ Descriptors allows_multiple o x flag cto type m n; +[ Descriptors allows_multiple o x y flag cto type m n; ResetDescriptors(); if (wn > num_words) return 0; m = wn; for (flag=true : flag :) { o = NextWordStopped(); flag = false; - for (x=1 : x<=LanguageDescriptors-->0 : x=x+4) + for (x=1 : x<=LanguageDescriptors-->0 : x=x+4) { if (o == LanguageDescriptors-->x) { + ! Attempt to compensate for her-her confusion. + for (y = 1 : y<=LanguagePronouns-->0 : y=y+2) { + if (o == LanguagePronouns-->y) + jump PersonalPronoun; + } flag = true; type = LanguageDescriptors-->(x+2); if (type ~= DEFART_PK) indef_mode = true; @@ -2532,6 +2561,9 @@ Constant UNLIT_BIT = 32; if (type == light) indef_type = indef_type | LIT_BIT; if (type == -light) indef_type = indef_type | UNLIT_BIT; } + } + .PersonalPronoun; + if (o == OTHER1__WD or OTHER2__WD or OTHER3__WD) { indef_mode = 1; flag = 1; indef_type = indef_type | OTHER_BIT; @@ -2567,7 +2599,7 @@ Constant UNLIT_BIT = 32; if (actor ~= player) rtrue; if (obj has animate) rtrue; if (obj hasnt talkable) rfalse; - if (action_to_be == ##Ask or ##Answer or ##Tell or ##AskFor) rtrue; + if (action_to_be == ##Ask or ##Answer or ##Tell or ##AskFor or ##AskTo) rtrue; rfalse; ]; @@ -2652,7 +2684,7 @@ Constant UNLIT_BIT = 32; parsed_number = l; return GPR_NUMBER; CREATURE_TOKEN: - if (action_to_be == ##Answer or ##Ask or ##AskFor or ##Tell) + if (action_to_be == ##Answer or ##Ask or ##AskFor or ##AskTo or ##Tell) scope_reason = TALKING_REASON; TOPIC_TOKEN: @@ -2844,12 +2876,6 @@ Constant UNLIT_BIT = 32; #Endif; ! DEBUG l = NounDomain(actors_location, actor, token); if (l == REPARSE_CODE) return l; ! Reparse after Q&A - if (l ~= nothing && l ~= 1 && l notin actor && token == MULTIHELD_TOKEN or MULTIEXCEPT_TOKEN) { - if (ImplicitTake(l)) { - etype = NOTHELD_PE; - jump FailToken; - } - } if (indef_wanted == 100 && l == 0 && number_matched == 0) l = 1; ! ReviseMulti if TAKE ALL FROM empty container @@ -2865,7 +2891,7 @@ Constant UNLIT_BIT = 32; wn = desc_wn; jump TryAgain2; } - if (etype ~=TOOFEW_PE && (multiflag || etype ~= MULTI_PE)) + if ((etype ~=TOOFEW_PE && etype ~= VAGUE_PE) && (multiflag || etype ~= MULTI_PE)) etype = CantSee(); jump FailToken; } ! Choose best error @@ -3162,7 +3188,7 @@ Constant UNLIT_BIT = 32; number_of_classes = 0; - if (match_length == 0 && indef_mode && indef_wanted ~= 100) + if (match_length == 0 && indef_mode && indef_wanted > 0 && indef_wanted < 100) number_matched = 0; ! ask question for 'take three' if (number_matched == 1) i = match_list-->0; if (number_matched > 1) { @@ -3652,6 +3678,18 @@ Constant SCORE__DIVISOR = 20; } if (flag) { + if(match_length == 1) { + k = 0; + for (i=1 : i<=number_of_classes : i++) { + while ((match_classes-->k) ~= i or -i) k++; + j = match_list-->k; + + if(MatchWord(j, match_from)) { + no_infer_message = true; + return j; + } + } + } #Ifdef DEBUG; if (parser_trace >= 4) print " Unable to choose best group, so ask player.]^"; #Endif; ! DEBUG @@ -3775,9 +3813,9 @@ Constant SCORE__DIVISOR = 20; if (its_owner == actors_location) its_score = its_score + l_s; else { #Ifdef TRADITIONAL_TAKE_ALL; - if (its_owner ~= compass) its_score = its_score + SCORE__NOTCOMPASS; + if (its_owner ~= Compass) its_score = its_score + SCORE__NOTCOMPASS; #Ifnot; - if (its_owner ~= compass) + if (its_owner ~= Compass) if (take_all_rule && its_owner && its_owner has static or scenery && (its_owner has supporter || @@ -3871,7 +3909,7 @@ Constant SCORE__DIVISOR = 20; [ Identical o1 o2 p1 p2 n1 n2 i j flag; if (o1 == o2) rtrue; ! This should never happen, but to be on the safe side if (o1 == 0 || o2 == 0) rfalse; ! Similarly - if (parent(o1) == compass || parent(o2) == compass) rfalse; ! Saves time + if (parent(o1) == Compass || parent(o2) == Compass) rfalse; ! Saves time ! What complicates things is that o1 or o2 might have a parsing routine, ! so the parser can't know from here whether they are or aren't the same. @@ -3921,6 +3959,50 @@ Constant SCORE__DIVISOR = 20; rtrue; ]; + +! ---------------------------------------------------------------------------- +! MatchWord matches a word to the name of an object. This is used to assume +! that an object simply named "key" is meant when the player types TAKE KEY +! rather than TAKE BRASS KEY. +! ---------------------------------------------------------------------------- + +[ MatchWord p_obj p_word_no len_name len_input start_input i; + + p_word_no--; + + PrintToBuffer(StorageForShortName, SHORTNAMEBUF_LEN, p_obj); + len_name = StorageForShortName --> 0; + + if(len_name > 9) { + for(i = 9 : i < len_name : i++) + if(StorageForShortName -> (WORDSIZE + i) == 32) rfalse; + len_name = 9; + } + +#Ifdef TARGET_ZCODE; + start_input = (parse + 2) -> (4 * p_word_no + 3); + len_input = (parse + 2) -> (4 * p_word_no + 2); + if(len_input > 9) len_input = 9; + if(len_name ~= len_input) rfalse; + for(i = 0 : i < len_name : i++) { + if(StorageForShortName -> (WORDSIZE + i) ~= buffer -> (start_input + i)) + rfalse; + } +#Ifnot; ! TARGET_GLULX + start_input = parse --> (((p_word_no+1) * 3)) - 2; + len_input = parse --> ((p_word_no+1) * 3 - 1); + if(len_input > 9) len_input = 9; + if(len_name ~= len_input) rfalse; + for(i = 0 : i < len_name : i++) { + if(StorageForShortName -> (WORDSIZE + i) ~= buffer -> (start_input + i + 2)) + rfalse; + } +#Endif; ! TARGET_ + + rtrue; +]; + + ! ---------------------------------------------------------------------------- ! PrintCommand reconstructs the command as it presently reads, from ! the pattern which has been built up @@ -3959,7 +4041,7 @@ Constant SCORE__DIVISOR = 20; if (i >= REPARSE_CODE) print (address) No__Dword(i-REPARSE_CODE); else - if (i in compass && LanguageVerbLikesAdverb(verb_word)) + if (i in Compass && LanguageVerbLikesAdverb(verb_word)) LanguageDirection (i.door_dir); ! the direction name as adverb else print (the) i; @@ -4261,7 +4343,7 @@ Constant SCORE__DIVISOR = 20; if (indef_mode==0 && domain==actors_location && scope_reason==PARSING_REASON && context~=CREATURE_TOKEN) - ScopeWithin(compass); + ScopeWithin(Compass); ! Look through the objects in the domain, avoiding "objectloop" in case ! movements occur, e.g. when trying each_turn. @@ -4488,22 +4570,29 @@ Constant SCORE__DIVISOR = 20; #Ifdef DEBUG; if (threshold >= 0 && parser_trace >= 5) print " ParseNoun returned ", threshold, "^"; #Endif; ! DEBUG - if (threshold < 0) wn++; - if (threshold > 0) { k = threshold; jump MMbyPN; } - - if (threshold == 0 || Refers(obj,wn-1) == 0) { - .NoWordsMatch; - if (indef_mode ~= 0) { - k = 0; parser_action = NULL; - jump MMbyPN; - } - rfalse; + ! Don't arbitrarily increase wn when ParseNoun() returns -1 + if (threshold > 0) { + k = threshold; + wn = j + k; + jump MMbyPN; + } + ! Check wn instead of wn - 1 + if (threshold == 0 || Refers(obj,wn) == 0) { + .NoWordsMatch; + if (indef_mode ~= 0) { + ! Restore wn to pre-ParseNoun() state + k = 0; parser_action = NULL; wn = j; + jump MMbyPN; + } + rfalse; } if (threshold < 0) { - threshold = 1; - dict_flags_of_noun = (w->#dict_par1) & (DICT_X654+DICT_PLUR);!$$01110100; - w = NextWord(); + ! Set threshold to reflect any words consumed by ParseNoun() + threshold = wn - j; + w = NextWord(); ! Ensure w contains actual first word of noun phrase + ! if ParseNoun() moved wn. + dict_flags_of_noun = (w->#dict_par1) & (DICT_X654+DICT_PLUR);!$$01110100; while (Refers(obj, wn-1)) { threshold++; if (w) @@ -4789,7 +4878,6 @@ Constant MAX_DECIMAL_BASE 214748364; default: return GPR_FAIL; } if (digit >= base) return GPR_FAIL; - digit_count++; switch (base) { 16: if (digit_count > 2*WORDSIZE) return GPR_FAIL; 2: if (digit_count > 8*WORDSIZE) return GPR_FAIL; @@ -4841,8 +4929,19 @@ Constant MAX_DECIMAL_BASE 214748364; #Ifdef TARGET_ZCODE; -[ Dword__No w; return (w-(HDR_DICTIONARY-->0 + 7))/9; ]; -[ No__Dword n; return HDR_DICTIONARY-->0 + 7 + 9*n; ]; +[ Dword__No w dp dh; + dp = HDR_DICTIONARY-->0; + dh = dp->0 + 4; + dp = dp + dp->0 + 1; + return (w-(HDR_DICTIONARY-->0 + dh )) / (dp->0); +]; + +[ No__Dword n dp dh; + dp = HDR_DICTIONARY-->0; + dh = dp->0 + 4; + dp = dp + dp->0 + 1; + return HDR_DICTIONARY-->0 + dh + (dp->0 *n); +]; #Ifnot; ! TARGET_GLULX @@ -5145,7 +5244,7 @@ Object InformLibrary "(Inform Library)" } l = multiple_object-->k; PronounNotice(l); - print (name) l, (string) COLON__TX; + print (name) l, (string) COLON__TX, " "; if (inp1 == 0) { inp1 = l; switch (self.actor_act(actor, action, l, second)) { @@ -5315,13 +5414,17 @@ Object InformLibrary "(Inform Library)" if (lightflag == 0) location = thedark; if (j ~= 2) Banner(); -#ifndef NOINITIAL_LOOK; +#Ifndef NOINITIAL_LOOK; ; -#endif; +#Endif; before_first_turn = false; ]; [ GameEpilogue; + if (score ~= last_score) { + if (notify_mode == 1) NotifyTheScore(); + last_score = score; + } print "^^ "; #Ifdef TARGET_ZCODE; #IfV5; style bold; #Endif; ! V5 @@ -6287,46 +6390,36 @@ Object InformLibrary "(Inform Library)" ]; #Ifdef COLOUR; -[ SetColour f b window doclear i fwd bwd swin; - if (window) swin = 5-window; ! 4 for TextGrid, 3 for TextBuffer - - if (clr_on) { - fwd = MakeColourWord(f); - bwd = MakeColourWord(b); - for (i=0 : i<=10: i++) { - if (f == CLR_DEFAULT || b == CLR_DEFAULT) { ! remove style hints - glk_stylehint_clear(swin, i, 7); - glk_stylehint_clear(swin, i, 8); - } - else { - glk_stylehint_set(swin, i, 7, fwd); - glk_stylehint_set(swin, i, 8, bwd); - } - } - ! Now re-open the windows to apply the hints - if (gg_statuswin) glk_window_close(gg_statuswin, 0); - - if (doclear || ( window ~= 1 && (clr_fg ~= f || clr_bg ~= b) ) ) { - glk_window_close(gg_mainwin, 0); - gg_mainwin = glk_window_open(0, 0, 0, 3, GG_MAINWIN_ROCK); - if (gg_scriptstr ~= 0) - glk_window_set_echo_stream(gg_mainwin, gg_scriptstr); - } - - gg_statuswin = glk_window_open($12, gg_statuswin_cursize, - 4, GG_STATUSWIN_ROCK); - if (statuswin_current && gg_statuswin) - MoveCursor(); else MainWindow(); +[ SetColour f b window; + if (window == 0) { ! if setting both together, set reverse + clr_fgstatus = b; + clr_bgstatus = f; } - - if (window ~= 2) { + if (window == 1) { clr_fgstatus = f; clr_bgstatus = b; } - if (window ~= 1) { + if (window == 0 or 2) { clr_fg = f; clr_bg = b; } + if (clr_on) { + if (glk_gestalt(gestalt_GarglkText, 0)) { + if (f >= 0 && f < 10) { + f = GlulxColourValues-->f; + } + else { + f = -2; + } + if (b >= 0 && b < 10) { + b = GlulxColourValues-->b; + } + else { + b = -2; + } + garglk_set_zcolors(f, b); + } + } ]; #Endif; ! COLOUR #Endif; ! TARGET_ @@ -6429,7 +6522,7 @@ Object InformLibrary "(Inform Library)" #Endif; ! TARGET_ZCODE #Ifndef DrawStatusLine; -[ DrawStatusLine width posa posb; +[ DrawStatusLine width posa posb posc posd pose; #Ifdef TARGET_GLULX; ! If we have no status window, we must not try to redraw it. if (gg_statuswin == 0) @@ -6444,7 +6537,13 @@ Object InformLibrary "(Inform Library)" MoveCursor(1, 1); width = ScreenWidth(); - posa = width-26; posb = width-13; + posa = width-26; ! For standard move/score display. + posb = width-13; + posc = width-5; + + posd = width-19; ! For time display. + pose = width-14; + spaces width; MoveCursor(1, 2); @@ -6458,13 +6557,17 @@ Object InformLibrary "(Inform Library)" else print (The) visibility_ceiling; } - - if (sys_statusline_flag && width > 53) { - MoveCursor(1, posa); - print (string) TIME__TX; - LanguageTimeOfDay(sline1, sline2); - } - else { + if (sys_statusline_flag) { + if (width > 29) { + if (width > 39) + MoveCursor(1, posd); + else + MoveCursor(1, pose); + print (string) TIME__TX; + LanguageTimeOfDay(sline1, sline2); + } else + jump DSLContinue; + } else { if (width > 66) { #Ifndef NO_SCORE; MoveCursor(1, posa); @@ -6473,14 +6576,24 @@ Object InformLibrary "(Inform Library)" MoveCursor(1, posb); print (string) MOVES__TX, sline2; } - #Ifndef NO_SCORE; - if (width > 53 && width <= 66) { - MoveCursor(1, posb); - print sline1, "/", sline2; - } - #Endif; + if (width > 53 && width <= 66) { + MoveCursor(1, posb); + #Ifdef NO_SCORE; + print (string) MOVES__TX, sline2; + #Ifnot; + print sline1, "/", sline2; + #Endif; + } + if (width < 53) { + MoveCursor(1, posc); + #Ifdef NO_SCORE; + print (string) MOVES_S__TX, sline2; + #Ifnot; + print sline1, "/", sline2; + #Endif; + } } - + .DSLContinue; MainWindow(); ! set_window ]; #Endif; @@ -6629,10 +6742,8 @@ Object InformLibrary "(Inform Library)" gg_scriptstr = 0; gg_savestr = 0; gg_statuswin_cursize = 0; - #Ifdef DEBUG; gg_commandstr = 0; gg_command_reading = false; - #Endif; ! DEBUG ! Also tell the game to clear its object references. if (IdentifyGlkObject(0) == false) LibraryExtensions.RunWhile(ext_identifyglkobject, 0, 0); @@ -6641,12 +6752,10 @@ Object InformLibrary "(Inform Library)" switch (gg_arguments-->0) { GG_SAVESTR_ROCK: gg_savestr = id; GG_SCRIPTSTR_ROCK: gg_scriptstr = id; - #Ifdef DEBUG; GG_COMMANDWSTR_ROCK: gg_commandstr = id; gg_command_reading = false; GG_COMMANDRSTR_ROCK: gg_commandstr = id; gg_command_reading = true; - #Endif; ! DEBUG default: if (IdentifyGlkObject(1, 1, id, gg_arguments-->0) == false) LibraryExtensions.RunWhile(ext_identifyglkobject, false, 1, 1, id, gg_arguments-->0); } @@ -6910,7 +7019,7 @@ Array StorageForShortName -> WORDSIZE + SHORTNAMEBUF_LEN; PrintCapitalised(a, b, false, true, true); ]; -[ CapitRule str no_caps; +[ Cap str no_caps; if (no_caps) print (string) str; else PrintCapitalised(str,0,true); ]; @@ -6919,7 +7028,7 @@ Array StorageForShortName -> WORDSIZE + SHORTNAMEBUF_LEN; if (o provides articles) { artval=(o.&articles)-->(acode+short_name_case*LanguageCases); if (capitalise) - print (CapitRule) artval; + print (Cap) artval; else print (string) artval; if (pluralise) return; @@ -6969,7 +7078,7 @@ Array StorageForShortName -> WORDSIZE + SHORTNAMEBUF_LEN; } #Endif; ! TARGET_ - CapitRule (artform-->acode, ~~capitalise); ! print article + Cap (artform-->acode, ~~capitalise); ! print article if (pluralise) return; print (PSN__) o; ]; @@ -7244,6 +7353,8 @@ Object LibraryExtensions "(Library Extensions)" ext_printverb 0, ! [C2/R2] ext_timepasses 0, ! [C2/R1] ext_unknownverb 0, ! [C2/R2] + ext_aftersave 0, ! [C2/R2] + ext_afterrestore 0, ! [C2/R2] ! [C1] = Called in all cases ! [C2] = Called if EP is undefined, or returns false ! [C3] = called if EP is undefined, or returns -1