home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / EFFO / pd3.lzh / SBPROLOG2.2 / MODLIB / MODLIB_SRC / $read.P < prev    next >
Text File  |  1991-08-10  |  35KB  |  1,004 lines

  1. /*
  2.     File   : READ.PL
  3.     Author : D.H.D.Warren + Richard O'Keefe
  4.     Updated: 5 July 1984
  5.     Purpose: Read Prolog terms in Dec-10 syntax.
  6. */
  7. /*
  8.     Modified by Alan Mycroft to regularise the functor modes.
  9.     This is both easier to understand (there are no more '?'s),
  10.     and also fixes bugs concerning the curious interaction of cut with
  11.     the state of parameter instantiation.
  12.  
  13.     Since this file doesn't provide "metaread", it is considerably
  14.     simplified.  The token list format has been changed somewhat, see
  15.     the comments in the RDTOK file.
  16.  
  17.     I have added the rule X(...) -> apply(X,[...]) for Alan Mycroft.
  18. */
  19.  
  20. /*
  21.     Modified by Saumya Debray, SUNY @ Stony Brook, to cut away DEC-10 syntax
  22.     that isn't used by C-Prolog 1.5 : "public" and "mode" declarations have
  23.     been deleted, and the builtins ttynl/0 and ttyput/1 replaced by nl/0
  24.     and put/1.    (April 2, 1985)
  25. */
  26.  
  27. $read_export([$read/1,$read/2]).
  28.  
  29. /* $read_use($bmeta,[$atom/1,$atomic/1,$integer/1,$number/1,$structure/1,
  30.     $functor0/2,$bldstr/3,$arg/3,$arity/2,$real/1,$floor/2]).
  31.    $read_use($meta,[$functor/3,$univ/2,$length/2]).
  32.    $read_use($bio,[$writename/1,$writeqname/1,$put/1,$nl/0,$tab/1,
  33.     $tell/1,_,$telling/1,$told/0,$get/1,$get0/1,$see/1,$seeing/1,
  34.     $seen/0]).
  35.    $read_use($io,[$write/1,$writeq/1,$display/1,$print/1]).
  36.    $read_use($blist,[$append/3,$member/2,$memberchk/2]).
  37.    $read_use($retr,[$retract/1,_,_]).
  38.    $read_use($name,[$name/2,$name0/2]).
  39. */
  40.  
  41. /*
  42.    $read(?Answer).
  43. */
  44.  
  45. $read(Answer) :- $read(Answer,_).
  46.  
  47. /*
  48.     $read(?Answer, ?Variables)
  49.     reads a term from the current input stream and unifies it with
  50.     Answer.  Variables is bound to a list of [Atom=Variable] pairs.
  51. */
  52.  
  53. $read(Answer, Variables) :-
  54.         repeat,
  55.             $read_tokens(Tokens, Variables),
  56.             (   $read(Tokens, 1200, Term, LeftOver), $read_all(LeftOver) ;
  57.             $read_syntax_error(Tokens)
  58.             ),
  59.         !,
  60.         Answer = Term.
  61.  
  62. /*
  63.     $read_all(+Tokens)
  64.     checks that there are no unparsed tokens left over.
  65. */
  66.  
  67. $read_all([]) :- !.
  68. $read_all(S) :-
  69.         $read_syntax_error(['operator expected after expression'], S).
  70.  
  71. /*
  72.     $read_expect(Token, TokensIn, TokensOut)
  73.     reads the next token, checking that it is the one expected, and
  74.     giving an error message if it is not.  It is used to look for
  75.     right brackets of various sorts, as they're all we can be sure of.
  76. */
  77.  
  78. $read_expect(Token, [Token|Rest], Rest) :- !.
  79. $read_expect(Token, S0, _) :-
  80.         $read_syntax_error([Token,'or operator expected'], S0).
  81.  
  82. /*
  83.     I want to experiment with having the operator information held as
  84.     ordinary Prolog facts.  For the moment the following predicates
  85.     remain as interfaces to curr_op.
  86.     $read_prefixop(O -> Self, Rarg)
  87.     $read_postfixop(O -> Larg, Self)
  88.     $read_infixop(O -> Larg, Self, Rarg)
  89. */
  90.  
  91. $read_prefixop(Op, Prec, Prec) :-
  92.         $read_curr_op(Prec, fy, Op), !.
  93. $read_prefixop(Op, Prec, Less) :-
  94.         $read_curr_op(Prec, fx, Op), !,
  95.         Less is Prec-1.
  96.  
  97.  
  98. $read_postfixop(Op, Prec, Prec) :-
  99.         $read_curr_op(Prec, yf, Op), !.
  100. $read_postfixop(Op, Less, Prec) :-
  101.         $read_curr_op(Prec, xf, Op), !, Less is Prec-1.
  102.  
  103.  
  104. $read_infixop(Op, Less, Prec, Less) :-
  105.         $read_curr_op(Prec, xfx, Op), !, Less is Prec-1.
  106. $read_infixop(Op, Less, Prec, Prec) :-
  107.         $read_curr_op(Prec, xfy, Op), !, Less is Prec-1.
  108. $read_infixop(Op, Prec, Prec, Less) :-
  109.         $read_curr_op(Prec, yfx, Op), !, Less is Prec-1.
  110.  
  111.  
  112. $read_ambigop(F, L1, O1, R1, L2, O2) :-
  113.         $read_postfixop(F, L2, O2),
  114.         $read_infixop(F, L1, O1, R1), !.
  115.  
  116. /*
  117.     $read(+TokenList, +Precedence, -Term, -LeftOver)
  118.     parses a Token List in a context of given Precedence,
  119.     returning a Term and the unread Left Over tokens.
  120. */
  121.  
  122. $read([Token|RestTokens], Precedence, Term, LeftOver) :-
  123.         $read(Token, RestTokens, Precedence, Term, LeftOver).
  124. $read([], _, _, _) :-
  125.         $read_syntax_error(['expression expected'], []).
  126.  
  127. /*
  128.     $read(+Token, +RestTokens, +Precedence, -Term, -LeftOver)
  129. */
  130.  
  131. $read(var(Variable,_), ['('|S1], Precedence, Answer, S) :- !,
  132.         $read(S1, 999, Arg1, S2),
  133.         $read_args(S2, RestArgs, S3), !,
  134.         $read_exprtl0(S3,apply(Variable,[Arg1|RestArgs]),Precedence,Answer,S).
  135.  
  136. $read(var(Variable,_), S0, Precedence, Answer, S) :- !,
  137.         $read_exprtl0(S0, Variable, Precedence, Answer, S).
  138.  
  139. $read(atom(-), [number(Num)|S1], Precedence, Answer, S) :-
  140.         Negative is -Num, !,
  141.         $read_exprtl0(S1, Negative, Precedence, Answer, S).
  142.  
  143. $read(atom(Functor), ['('|S1], Precedence, Answer, S) :- !,
  144.         $read(S1, 999, Arg1, S2),
  145.         $read_args(S2, RestArgs, S3),
  146.         $univ(Term,[Functor,Arg1|RestArgs]), !,
  147.         $read_exprtl0(S3, Term, Precedence, Answer, S).
  148.  
  149. $read(atom(Functor), S0, Precedence, Answer, S) :-
  150.         $read_prefixop(Functor, Prec, Right), !,
  151.         $read_aft_pref_op(Functor, Prec, Right, S0, Precedence, Answer, S).
  152.  
  153. $read(atom(Atom), S0, Precedence, Answer, S) :- !,
  154.         $read_exprtl0(S0, Atom, Precedence, Answer, S).
  155.  
  156. $read(number(Num), S0, Precedence, Answer, S) :- !,
  157.         $read_exprtl0(S0, Num, Precedence, Answer, S).
  158.  
  159. $read('[', [']'|S1], Precedence, Answer, S) :- !,
  160.         $read_exprtl0(S1, [], Precedence, Answer, S).
  161.  
  162. $read('[', S1, Precedence, Answer, S) :- !,
  163.         $read(S1, 999, Arg1, S2),
  164.         $read_list(S2, RestArgs, S3), !,
  165.         $read_exprtl0(S3, [Arg1|RestArgs], Precedence, Answer, S).
  166.  
  167. $read('(', S1, Precedence, Answer, S) :- !,
  168.         $read(S1, 1200, Term, S2),
  169.         $read_expect(')', S2, S3), !,
  170.         $read_exprtl0(S3, Term, Precedence, Answer, S).
  171.  
  172. $read(' (', S1, Precedence, Answer, S) :- !,
  173.         $read(S1, 1200, Term, S2),
  174.         $read_expect(')', S2, S3), !,
  175.         $read_exprtl0(S3, Term, Precedence, Answer, S).
  176.  
  177. $read('{', ['}'|S1], Precedence, Answer, S) :- !,
  178.         $read_exprtl0(S1, '{}', Precedence, Answer, S).
  179.  
  180. $read('{', S1, Precedence, Answer, S) :- !,
  181.         $read(S1, 1200, Term, S2),
  182.         $read_expect('}', S2, S3), !,
  183.         $read_exprtl0(S3, '{}'(Term), Precedence, Answer, S).
  184.  
  185. $read(string(List), S0, Precedence, Answer, S) :- !,
  186.         $read_exprtl0(S0, List, Precedence, Answer, S).
  187.  
  188. $read(Token, S0, _, _, _) :-
  189.         $read_syntax_error([Token,'cannot start an expression'], S0).
  190.  
  191. /*
  192.     $read_args(+Tokens, -TermList, -LeftOver)
  193.     parses {',' expr(999)} ')' and returns a list of terms.
  194. */
  195.  
  196. $read_args([Tok|S1], Term, S) :-
  197.     '_$savecp'(CP),
  198.     $read_args1(Tok,Term,S,S1,CP), '_$cutto'(CP).
  199. $read_args(S, _, _) :-
  200.         $read_syntax_error([', or ) expected in arguments'], S).
  201.  
  202.  
  203. $read_args1(',',[Term|Rest],S,S1,CP) :- 
  204.         $read(S1, 999, Term, S2), '_$cutto'(CP),
  205.         $read_args(S2, Rest, S).
  206. $read_args1(')',[],S,S,_).
  207.  
  208.  
  209. /*
  210.     $read_list(+Tokens, -TermList, -LeftOver)
  211.     parses {',' expr(999)} ['|' expr(999)] ']' and returns a list of terms.
  212. */
  213.  
  214. $read_list([Tok|S1],Term,S) :-
  215.     '_$savecp'(CP),
  216.     $read_list1(Tok,Term,S,S1,CP),
  217.     '_$cutto'(CP).
  218. $read_list(S, _, _) :-
  219.         $read_syntax_error([', | or ] expected in list'], S).
  220.  
  221.  
  222. $read_list1(',',[Term|Rest],S,S1,CP) :-
  223.         $read(S1, 999, Term, S2), '_$cutto'(CP),
  224.         $read_list(S2, Rest, S).
  225. $read_list1('|',Rest,S,S1,CP) :-
  226.         $read(S1, 999, Rest, S2), '_$cutto'(CP),
  227.         $read_expect(']', S2, S).
  228. $read_list1(']',[],S,S,_).
  229.  
  230. /*
  231.     $read_aft_pref_op(+Op, +Prec, +ArgPrec, +Rest, +Precedence, -Ans, -LeftOver)
  232. */
  233.  
  234. $read_aft_pref_op(Op, Oprec, Aprec, S0, Precedence, _, _) :-
  235.         Precedence < Oprec, !,
  236.         $read_syntax_error(['prefix operator',Op,'in context with precedence '
  237.             ,Precedence], S0).
  238.  
  239. $read_aft_pref_op(Op, Oprec, Aprec, S0, Precedence, Answer, S) :-
  240.         $read_peepop(S0, S1),
  241.         $read_prefix_is_atom(S1, Oprec), /* can't cut but would like to */
  242.         $read_exprtl(S1, Oprec, Op, Precedence, Answer, S).
  243.  
  244. $read_aft_pref_op(Op, Oprec, Aprec, S1, Precedence, Answer, S) :-
  245.         $read(S1, Aprec, Arg, S2),
  246.         $univ(Term,[Op,Arg]), !,
  247.         $read_exprtl(S2, Oprec, Term, Precedence, Answer, S).
  248.  
  249. /*
  250.     The next clause fixes a bug concerning "mop dop(1,2)" where
  251.     mop is monadic and dop dyadic with higher Prolog priority.
  252. */
  253.  
  254. $read_peepop([atom(F),'('|S1], [atom(F),'('|S1]) :- !.
  255. $read_peepop([atom(F)|S1], [infixop(F,L,P,R)|S1]) :- 
  256.     $read_infixop(F, L, P, R).
  257. $read_peepop([atom(F)|S1], [postfixop(F,L,P)|S1]) :- 
  258.     $read_postfixop(F, L, P).
  259. $read_peepop(S0, S0).
  260.  
  261. /*
  262.     $read_prefix_is_atom(+TokenList, +Precedence)
  263.     is true when the right context TokenList of a prefix operator
  264.     of result precedence Precedence forces it to be treated as an
  265.     atom, e.g. (- = X), p(-), [+], and so on.
  266. */
  267.  
  268. $read_prefix_is_atom([Token|_], Precedence) :-
  269.         $read_prefix_is_atom(Token, Precedence).
  270.  
  271. $read_prefix_is_atom(infixop(_,L,_,_), P) :- L >= P.
  272. $read_prefix_is_atom(postfixop(_,L,_), P) :- L >= P.
  273. $read_prefix_is_atom(')', _).
  274. $read_prefix_is_atom(']', _).
  275. $read_prefix_is_atom('}', _).
  276. $read_prefix_is_atom('|', P) :- 1100 >= P.
  277. $read_prefix_is_atom(',', P) :- 1000 >= P.
  278. $read_prefix_is_atom([],  _).
  279.  
  280. /*
  281.     $read_exprtl0(+Tokens, +Term, +Prec, -Answer, -LeftOver)
  282.     is called by read/4 after it has read a primary (the Term).
  283.     It checks for following postfix or infix operators.
  284. */
  285.  
  286. $read_exprtl0([Tok|S1], Term, Precedence, Answer, S) :-
  287.     '_$savecp'(CP),
  288.     $read_exprtl01(Tok,Term,Precedence,Answer,S,S1,CP),
  289.     '_$cutto'(CP).
  290. $read_exprtl0(S, Term, _, Term, S).
  291.  
  292.  
  293. $read_exprtl01(atom(F), Term, Precedence, Answer,S,S1,CP) :-
  294.         $read_ambigop(F, L1, O1, R1, L2, O2), '_$cutto'(CP),
  295.         ( $read_exprtl([infixop(F,L1,O1,R1)|S1],0,Term,Precedence,Answer,S)
  296.         ; $read_exprtl([postfixop(F,L2,O2) |S1],0,Term,Precedence,Answer,S)
  297.         ).
  298. $read_exprtl01(atom(F), Term, Precedence, Answer, S,S1,CP) :-
  299.         $read_infixop(F, L1, O1, R1), '_$cutto'(CP),
  300.         $read_exprtl([infixop(F,L1,O1,R1)|S1],0,Term,Precedence,Answer,S).
  301. $read_exprtl01(atom(F),Term,Precedence,Answer,S,S1,CP) :-
  302.         $read_postfixop(F, L2, O2), '_$cutto'(CP),
  303.         $read_exprtl([postfixop(F,L2,O2) |S1],0,Term,Precedence,Answer,S).
  304. $read_exprtl01(',', Term, Precedence, Answer, S,S1,CP) :-
  305.         Precedence >= 1000, '_$cutto'(CP),
  306.         $read(S1, 1000, Next, S2), '_$cutto'(CP),
  307.         $read_exprtl(S2, 1000, (Term,Next), Precedence, Answer, S).
  308. $read_exprtl01('|', Term, Precedence, Answer, S,S1,CP) :-
  309.         Precedence >= 1100, '_$cutto'(CP),
  310.         $read(S1, 1100, Next, S2), '_$cutto'(CP),
  311.         $read_exprtl(S2, 1100, (Term;Next), Precedence, Answer, S).
  312. $read_exprtl01(Thing, _, _, _, _,S1,CP) :-
  313.         $read_cfexpr(Thing, Culprit), '_$cutto'(CP),
  314.         $read_syntax_error([Culprit,'follows expression'], [Thing|S1]).
  315.  
  316.  
  317. $read_cfexpr(atom(_),       atom).
  318. $read_cfexpr(var(_,_),      variable).
  319. $read_cfexpr(number(_),     number).
  320. $read_cfexpr(string(_),     string).
  321. $read_cfexpr(' (',          bracket).
  322. $read_cfexpr('(',           bracket).
  323. $read_cfexpr('[',           bracket).
  324. $read_cfexpr('{',           bracket).
  325.  
  326.  
  327.  
  328. $read_exprtl([Tok|S1], C, Term, Precedence, Answer, S) :-
  329.     '_$savecp'(CP),
  330.     $read_exprtl1(Tok,C,Term,Precedence,Answer,S,S1,CP),
  331.     '_$cutto'(CP).
  332. $read_exprtl(S, _, Term, _, Term, S).
  333.  
  334. $read_exprtl1(infixop(F,L,O,R), C, Term, Precedence, Answer, S, S1,CP) :-
  335.         Precedence >= O, C =< L, '_$cutto'(CP),
  336.         $read(S1, R, Other, S2),
  337.         $univ(Expr,[F,Term,Other]), /*!,*/
  338.         $read_exprtl(S2, O, Expr, Precedence, Answer, S).
  339. $read_exprtl1(postfixop(F,L,O), C, Term, Precedence, Answer, S, S1,CP) :-
  340.         Precedence >= O, C =< L, '_$cutto'(CP),
  341.         $univ(Expr,[F,Term]),
  342.         $read_peepop(S1, S2),
  343.         $read_exprtl(S2, O, Expr, Precedence, Answer, S).
  344. $read_exprtl1(',', C, Term, Precedence, Answer, S, S1,CP) :-
  345.         Precedence >= 1000, C < 1000, '_$cutto'(CP),
  346.         $read(S1, 1000, Next, S2), /*!,*/
  347.         $read_exprtl(S2, 1000, (Term,Next), Precedence, Answer, S).
  348. $read_exprtl1('|', C, Term, Precedence, Answer, S, S1, CP) :-
  349.         Precedence >= 1100, C < 1100, '_$cutto'(CP),
  350.         $read(S1, 1100, Next, S2), /*!,*/
  351.         $read_exprtl(S2, 1100, (Term;Next), Precedence, Answer, S).
  352.  
  353. /*
  354.     This business of syntax errors is tricky.  When an error is detected,
  355.     we have to write out a message.  We also have to note how far it was
  356.     to the end of the input, and for this we are obliged to use the data-
  357.     base.  Then we fail all the way back to $read(), and that prints the
  358.     input list with a marker where the error was noticed.  If subgoal_of
  359.     were available in compiled code we could use that to find the input
  360.     list without hacking the data base.  The really hairy thing is that
  361.     the original code noted a possible error and backtracked on, so that
  362.     what looked at first sight like an error sometimes turned out to be
  363.     a wrong decision by the parser.  This version of the parser makes
  364.     fewer wrong decisions, and my goal was to get it to do no backtracking
  365.     at all.  This goal has not yet been met, and it will still occasionally
  366.     report an error message and then decide that it is happy with the input
  367.     after all.  Sorry about that.
  368. */
  369.  
  370. /*  Modified by Saumya Debray, Nov 18 1986, to use SB-Prolog's database
  371.     facilities to print out error messages.                */
  372.  
  373. $read_syntax_error(Message, List) :-
  374. /*    $print('**'), $print_list(Message), $nl, */
  375.  
  376.     $length(List,Length),
  377.     $symtype('_$synerr'(_),X),
  378.     ( (X =:= 0 ; not('_$synerr'(_))) ->    /* _$synerr/1 undefined */
  379.         $assert('_$synerr'(Length)) ;
  380.         true
  381.     ),
  382.     !,
  383.     fail.
  384.  
  385. $read_syntax_error(List) :-
  386.         $nl, $print('*** syntax error ***'), $nl,
  387.     '_$synerr'(AfterError),
  388.     $retract('_$synerr'(AfterError)),
  389.     $length(List,Length),
  390.     BeforeError is Length - AfterError,
  391.     $read_display_list(List,BeforeError), !,
  392.     fail.
  393.  
  394. $read_display_list(X, 0) :-
  395.     $print('<<here>> '), !,
  396.     $read_display_list(X, 99999).
  397. $read_display_list([Head|Tail], BeforeError) :-
  398.     $print_token(Head),
  399.     $writename(' '),
  400.     Left is BeforeError-1, !,
  401.     $read_display_list(Tail, Left).
  402. $read_display_list([], _) :-
  403.     $nl.
  404.  
  405.  
  406. $print_list([]) :- $nl.
  407. $print_list([Head|Tail]) :-
  408.     $tab(1),
  409.         $print_token(Head),
  410.         $print_list(Tail).
  411.  
  412. $print_token(atom(X))    :- !, $print(X).
  413. $print_token(var(V,X))   :- !, $print(X).
  414. $print_token(number(X)) :-  !, $print(X).
  415. $print_token(string(X))  :- !, $print(X).
  416. $print_token(X)          :-    $print(X).
  417.  
  418. /*      An attempt at defining the "curr_op" predicate for read.       */
  419.  
  420. /* could add the clause:
  421.     op(Prec,Assoc,Op) :- assert_fact($read_curr_op(Prec,Assoc,Op)).
  422.    to implement op */
  423.  
  424. $read_curr_op(1200,xfx,(':-')).
  425. $read_curr_op(1200,xfx,('-->')).
  426. $read_curr_op(1200,fx,(':-')).
  427. $read_curr_op(1198,xfx,('::-')).
  428. $read_curr_op(1100,xfy,';').
  429. $read_curr_op(1050,xfy,'->').
  430. $read_curr_op(1000,xfy,',').
  431. $read_curr_op(900,fy,not).
  432. $read_curr_op(900,fy,'\+').
  433. $read_curr_op(900,fy,spy).
  434. $read_curr_op(900,fy,nospy).
  435. $read_curr_op(700,xfx,'=').
  436. $read_curr_op(700,xfx,is).
  437. $read_curr_op(700,xfx,'=..').
  438. $read_curr_op(700,xfx,'==').
  439. $read_curr_op(700,xfx,'\==').
  440. $read_curr_op(700,xfx,'@<').
  441. $read_curr_op(700,xfx,'@>').
  442. $read_curr_op(700,xfx,'@=<').
  443. $read_curr_op(700,xfx,'@>=').
  444. $read_curr_op(700,xfx,'=:=').
  445. $read_curr_op(700,xfx,'=\=').
  446. $read_curr_op(700,xfx,'<').
  447. $read_curr_op(700,xfx,'>').
  448. $read_curr_op(700,xfx,'=<').
  449. $read_curr_op(700,xfx,'>=').
  450. $read_curr_op(661,xfy,'.').    /* !! */
  451. $read_curr_op(500,yfx,'+').
  452. $read_curr_op(500,yfx,'-').
  453. $read_curr_op(500,yfx,'/\').
  454. $read_curr_op(500,yfx,'\/').
  455. $read_curr_op(500,fx,'+').
  456. $read_curr_op(500,fx,'-').
  457. $read_curr_op(500,fx,'\').
  458. $read_curr_op(400,yfx,'*').
  459. $read_curr_op(400,yfx,'/').
  460. $read_curr_op(400,yfx,'//').
  461. $read_curr_op(400,yfx,'<<').
  462. $read_curr_op(400,yfx,'>>').
  463. $read_curr_op(300,xfx,mod).
  464. $read_curr_op(200,xfy,'^').
  465.  
  466.  
  467.  
  468. /*
  469.     File   : RDTOK.PL
  470.     Author : R.A.O'Keefe
  471.     Updated: 5 July 1984
  472.     Purpose: Tokeniser in reasonably standard Prolog.
  473. */
  474. /*  This tokeniser is meant to complement the library READ routine.
  475.     It recognises Dec-10 Prolog with the following exceptions:
  476.  
  477.         %( is not accepted as an alternative to {
  478.  
  479.         %) is not accepted as an alternative to )
  480.  
  481.         NOLC convention is not supported (read_name could be made to do it)
  482.  
  483.         ,.. is not accepted as an alternative to | (hooray!)
  484.  
  485.         large integers are not read in as xwd(Top18Bits,Bottom18Bits)
  486.  
  487.         After a comma, "(" is read as ' (' rather than '('.  This does the
  488.         parser no harm at all, and the Dec-10 tokeniser's behaviour here
  489.         doesn't actually buy you anything.  This tokeniser guarantees never
  490.         to return '(' except immediately after an atom, yielding ' (' every
  491.         other where.
  492.  
  493.     In particular, radix notation is EXACTLY as in Dec-10 Prolog version 3.53.
  494.     Some times might be of interest.  Applied to an earlier version of this file:
  495.         this code took                  1.66 seconds
  496.         the Dec-10 tokeniser took       1.28 seconds
  497.         A Pascal version took           0.96 seconds
  498.     The Dec-10 tokeniser was called via the old RDTOK interface, with
  499.     which this file is compatible.  One reason for the difference in
  500.     speed is the way variables are looked up: this code uses a linear
  501.     list, while the Dec-10 tokeniser uses some sort of tree.  The Pascal
  502.     version is the program WLIST which lists "words" and their frequencies.
  503.     It uses a hash table.  Another difference is the way characters are
  504.     classified: the Dec-10 tokeniser and WLIST have a table which maps
  505.     ASCII codes to character classes, and don't do all this comparison
  506.     and and memberchking.  We could do that without leaving standard Prolog,
  507.     but what do you want from one evening's work?
  508. */    
  509.  
  510. /*  Modified by Saumya Debray to be compatible with C-Prolog syntax.  This
  511.     involved (i) deleting "public" and "mode" declarations, and (ii)
  512.     replacing ttynl/0 by nl/0, ttyput/1 by put/1.  (Apr 6, 1985)    */
  513.  
  514. /*
  515.     $read_tokens(TokenList, Dictionary)
  516.     returns a list of tokens.  It is needed to "prime" read_tokens/2
  517.     with the initial blank, and to check for end of file.  The
  518.     Dictionary is a list of AtomName=Variable pairs in no particular order.
  519.     The way end of file is handled is that everything else FAILS when it
  520.     hits character "-1", sometimes printing a warning.  It might have been
  521.     an idea to return the atom 'end_of_file' instead of the same token list
  522.     that you'd have got from reading "end_of_file. ", but (1) this file is
  523.     for compatibility, and (b) there are good practical reasons for wanting
  524.     this behaviour. */
  525.  
  526. $read_tokens(TokenList, Dictionary) :-
  527.         $read_tokens(32, Dict, ListOfTokens),
  528.         $append(Dict, [], Dict), !, /*  fill in the "hole" at the end */
  529.         Dictionary = Dict,              /*  unify explicitly so we read and */
  530.         TokenList = ListOfTokens.       /*  then check even with filled in */
  531.                     /*  arguments */
  532. $read_tokens([atom(end_of_file)], []).   /*  only thing that can go wrong */
  533.  
  534. /*  read_tokens/3 modified by Saumya Debray : June 18, 1985 : to consist
  535.      of a single clause with a search-tree structure over it that permits
  536.      more efficient compiled code to be generated.  The tree is skewed, so
  537.      that those characters expected to be encountered more often are
  538.      closer to the top of the tree (the assumption here is that lower
  539.      case letters are the most frequent, followed by upper case letters
  540.      and numbers).                              */
  541.  
  542. $read_tokens(Ch,Dict,Tokens) :-
  543.     ((Ch >= 97,
  544.       ((Ch =< 122, Tokens = [atom(A)|TokRest],
  545.         $read_name(Ch,S,NextCh), $name(A,S),
  546.         $read_aft_atom(NextCh,Dict,TokRest)
  547.        ) ;
  548.        (Ch > 122,
  549.         ((Ch =:= 124, Tokens = ['|'|TokRest],
  550.           $get0(NextCh), $read_tokens(NextCh,Dict,TokRest)
  551.          ) ;
  552.          (Ch =\= 124,
  553.           ((Ch =:= 123, Tokens = ['{'|TokRest], $get0(NextCh),
  554.             $read_tokens(NextCh,Dict,TokRest)
  555.            ) ;
  556.            (Ch =\= 123,
  557.             ((Ch =:= 125, Tokens = ['}'|TokRest], $get0(NextCh),
  558.           $read_tokens(NextCh,Dict,TokRest)
  559.          ) ;
  560.          (Ch =\= 125, Tokens = [atom(A)|TokRest], $get0(AnotherCh),
  561.           $read_symbol(AnotherCh,Chars,NextCh), $name(A,[Ch|Chars]),
  562.           $read_aft_atom(NextCh,Dict,Tokens)
  563.          ))))))))) ;
  564.      (Ch < 97,
  565.       ((Ch < 65,
  566.         ((Ch < 48,
  567.           ((Ch =< 39,
  568.             ((Ch =< 34,
  569.           ((Ch =< 32,
  570.             ((Ch >= 0,
  571.               ((Ch =:= 26, fail) ;
  572.                (Ch =\= 26,
  573.                 $get0(NextCh), $read_tokens(NextCh,Dict,Tokens)
  574.                )
  575.               )
  576.              ) ;
  577.              (Ch < 0, fail)
  578.             )
  579.            ) ;
  580.            (Ch > 32,
  581.             ((Ch =:= 33, Tokens = [atom('!')|TokRest],
  582.               $get0(NextCh), $read_tokens(NextCh,Dict,TokRest)
  583.              ) ;
  584.              (Ch =\= 33, Tokens = [string(S)|TokRest],
  585.               $read_string(S,34,NextCh),
  586.               $read_tokens(NextCh,Dict,TokRest)
  587.              ))))
  588.          ) ;
  589.          (Ch > 34,
  590.           ((Ch =< 37,
  591.             ((Ch =:= 37, $read_skip_comment,
  592.               $get0(NextCh), $read_tokens(NextCh,Dict,Tokens)
  593.              ) ;
  594.              (Ch =\= 37, 
  595.                       Tokens = [atom(A)|TokRest],
  596.                     $read_name(Ch,S,NextCh), $name(A,S),
  597.                     $read_aft_atom(NextCh,Dict,TokRest)
  598.              )
  599.             )
  600.            ) ;
  601.            (Ch > 37, Tokens = [atom(A)|TokRest],
  602.             ((Ch =:= 39, 
  603.               $read_string(S,39,NextCh), $name(A,S),
  604.               $read_aft_atom(NextCh,Dict,TokRest)
  605.              ) ;
  606.              (Ch =\= 39,
  607.               $get0(AnotherCh), $read_symbol(AnotherCh,Chars,NextCh),
  608.               $name(A,[Ch|Chars]),
  609.               $read_aft_atom(NextCh,Dict,TokRest)
  610.              ))))))
  611.            ) ;
  612.            (Ch > 39,
  613.             ((Ch =< 42,
  614.           ((Ch =:= 40, Tokens = [' ('|TokRest],
  615.             $get0(NextCh), $read_tokens(NextCh,Dict,TokRest)
  616.            ) ;
  617.            (Ch =\= 40,
  618.             ((Ch =:= 41, Tokens = [')'|TokRest],
  619.               $get0(NextCh), $read_tokens(NextCh,Dict,TokRest)
  620.              ) ;
  621.              (Ch =\= 41, Tokens = [atom(A)|TokRest],
  622.               $get0(AnotherCh), $read_symbol(AnotherCh,Chars,NextCh),
  623.               $name(A,[Ch|Chars]),
  624.               $read_aft_atom(NextCh,Dict,TokRest)
  625.              ))))
  626.          ) ;
  627.          (Ch > 42,
  628.           ((Ch =:= 44, Tokens = [','|TokRest],
  629.             $get0(NextCh), $read_tokens(NextCh,Dict,TokRest)
  630.            ) ;
  631.            (Ch =\= 44,
  632.             ((Ch =:= 46, $get0(NextCh),
  633.               $read_fullstop(NextCh,Dict,Tokens)
  634.              ) ;
  635.              (Ch =\= 46,
  636.               ((Ch =:= 47,
  637.                 $get0(NextCh), $read_solidus(NextCh,Dict,Tokens)
  638.                ) ;
  639.                (Ch =\= 47, Tokens = [atom(A)|TokRest],
  640.                 $get0(AnotherCh), $read_symbol(AnotherCh,Chars,NextCh),
  641.                 $name(A,[Ch|Chars]),
  642.                 $read_aft_atom(NextCh,Dict,TokRest)
  643.                ))))))))))
  644.          ) ;
  645.          (Ch >= 48,
  646.           ((Ch =< 57, Tokens = [number(I)|TokRest],
  647.             $read_number(Ch,I,NextCh),
  648.         (NextCh = '_$end_of_clause' ->
  649.             TokRest = [] ;
  650.             $read_tokens(NextCh,Dict,TokRest)
  651.         )
  652.            ) ;
  653.            (Ch > 57,
  654.             ((Ch =:= 59, Tokens = [atom((';'))|TokRest],
  655.           $get0(NextCh), $read_tokens(NextCh,Dict,TokRest)
  656.          ) ;
  657.          (Ch =\= 59, Tokens = [atom(A)|TokRest],
  658.           $get0(AnotherCh), $read_symbol(AnotherCh,Chars,NextCh),
  659.           $name(A,[Ch|Chars]), $read_aft_atom(NextCh,Dict,TokRest)
  660.          ))))))
  661.        ) ;
  662.        (Ch >= 65,
  663.         ((Ch =< 90, Tokens = [var(Var,Name)|TokRest],
  664.           $read_name(Ch,S,NextCh), $name(Name,S),
  665.           $read_lookup(Dict, Name=Var),
  666.           $read_tokens(NextCh,Dict,TokRest)
  667.          ) ;
  668.          (Ch > 90,
  669.           ((Ch =< 93,
  670.             ((Ch =:= 91, Tokens = ['['|TokRest],
  671.           $get0(NextCh), $read_tokens(NextCh,Dict,TokRest)  
  672.          ) ;
  673.          (Ch =\= 91,
  674.           ((Ch =\= 92, Tokens = [']'|TokRest],
  675.             $get0(NextCh), $read_tokens(NextCh,Dict,TokRest)
  676.            ) ;
  677.            (Ch =:= 92, Tokens = [atom(A)|TokRest],
  678.             $get0(AnotherCh),
  679.             $read_symbol(AnotherCh,Chars,NextCh),
  680.             $name(A,[Ch|Chars]),
  681.             $read_aft_atom(NextCh,Dict,TokRest)
  682.            ))))
  683.            ) ;
  684.            (Ch > 93,
  685.             ((Ch =:= 95, Tokens = [var(Var,Name)|TokRest],
  686.           $read_name(Ch,S,NextCh),
  687.           ((S = "_", Name = '_') ;
  688.            ($name(Name,S), $read_lookup(Dict, Name=Var))
  689.           ),
  690.           $read_tokens(NextCh,Dict,TokRest)
  691.          ) ;
  692.          (Ch =\= 95, Tokens = [atom(A)|TokRest],
  693.           $get0(AnotherCh), $read_symbol(AnotherCh,Chars,NextCh),
  694.           $name(A,[Ch|Chars]),
  695.           $read_aft_atom(NextCh,Dict,TokRest)
  696.          )))))))))
  697.     ).
  698.  
  699. $read_skip_comment :-
  700.     repeat,
  701.         $get0(Ch),
  702.         (Ch = 13 ; Ch < 0 ; Ch = 31 ; Ch = 26),
  703.     !,
  704.     Ch =\= 26,
  705.     Ch > 0.        /*  fail on EOF */
  706.  
  707.  
  708.  
  709. /*
  710.     The only difference between $read_aft_atom(Ch, Dict, Tokens) and
  711.     read_tokens/3 is what they do when Ch is "(".  rd_aft_atom
  712.     finds the token to be '(', while read_tokens finds the token to be
  713.     ' ('.  This is how the parser can tell whether <atom> <paren> must
  714.     be an operator application or an ordinary function symbol application.
  715.     See the library file READ.PL for details. */
  716.  
  717. /*  Modified by Saumya Debray : June 18, 1985 : to use the conditional
  718.     to avoid both the cut and the laying down of the choice point.    */
  719.  
  720. $read_aft_atom(Ch,Dict,Tokens) :-
  721.     ((Ch =:= 40, Tokens = ['('|TokRest], $get0(NextCh),
  722.       $read_tokens(NextCh,Dict,TokRest)
  723.      ) ;
  724.      (Ch =\= 40, $read_tokens(Ch,Dict,Tokens))
  725.     ).
  726.  
  727. /*
  728.     $read_string(Chars, Quote, NextCh)
  729.     reads the body of a string delimited by Quote characters.
  730.     The result is a list of ASCII codes.  There are two complications.
  731.     If we hit the end of the file inside the string this predicate FAILS.
  732.     It does not return any special structure.  That is the only reason
  733.     it can ever fail.  The other complication is that when we find a Quote
  734.     we have to look ahead one character in case it is doubled.  Note that
  735.     if we find an end-of-file after the quote we *don't* fail, we return
  736.     a normal string and the end of file character is returned as NextCh.
  737.     If we were going to accept C-like escape characters, as I think we
  738.     should, this would need changing (as would the code for 0'x).  But
  739.     the purpose of this module is not to present my ideal syntax but to
  740.     present something which will read present-day Prolog programs. */
  741.  
  742. $read_string(Chars, Quote, NextCh) :-
  743.         $get0(Ch),
  744.         $read_string(Ch, Chars, Quote, NextCh).
  745.  
  746.  
  747. $read_string(Eofsym, _, Quote, Eofsym) :- 
  748.     (Eofsym is 26; Eofsym is -1),  /* new */
  749.         $print('! end of line or file in '), $put(Quote),
  750.         $print(token), $put(Quote), nl,
  751.         !, fail.
  752. $read_string(Quote, Chars, Quote, NextCh) :- !,
  753.         $get0(Ch),                               /* closing or doubled quote */
  754.         $read_more_string(Ch, Quote, Chars, NextCh).
  755. $read_string(Char, [Char|Chars], Quote, NextCh) :-
  756.         $read_string(Chars, Quote, NextCh).      /* ordinary character */
  757.  
  758.  
  759. $read_more_string(Quote, Quote, [Quote|Chars], NextCh) :- !,
  760.         $read_string(Chars, Quote, NextCh).      /* doubled quote */
  761. $read_more_string(NextCh, _, [], NextCh).             /* end */
  762.  
  763.  
  764. /*
  765.     $read_solidus(Ch, Dict, Tokens)
  766.     checks to see whether /Ch is a /* comment or a symbol.  If the
  767.     former, it skips the comment.  If the latter it just calls read_symbol.
  768.     We have to take great care with /* comments to handle end of file
  769.     inside a comment, which is why read_solidus/2 passes back an end of
  770.     file character or a (forged) blank that we can give to read_tokens.
  771. */
  772.  
  773. $read_solidus(42, Dict, Tokens) :- !,
  774.         $get0(Ch),
  775.         $read_solidus(Ch, NextCh),
  776.         $read_tokens(NextCh, Dict, Tokens).
  777. $read_solidus(Ch, Dict, [atom(A)|Tokens]) :-
  778.         $read_symbol(Ch, Chars, NextCh),         /* might read 0 chars */
  779.         $name(A, [47|Chars]),
  780.         $read_tokens(NextCh, Dict, Tokens).
  781. $read_solidus(Ch, LastCh) :-
  782.     Ch =:= -1,$print('! end of file in /*comment'), nl;
  783.     Ch =\= -1,
  784.      (Ch =:= 26,$print('! end of file in /*comment'), nl;
  785.       Ch =\= 26,$get0(NextCh),
  786.        (Ch =:= 42,
  787.          (NextCh =\= 47, $read_solidus(NextCh,LastCh);
  788.           NextCh =:= 47, LastCh=32)
  789.        ;
  790.         Ch =\= 42, $read_solidus(NextCh,LastCh)
  791.        )
  792.      ).
  793.  
  794. /* old read_solidus/2
  795. $read_solidus(Ch, Ch) :- (Ch is -1 ; Ch is 26), !,
  796.         $print('! end of file in /*comment'), nl.
  797. $read_solidus(42, LastCh) :-
  798.         $get0(NextCh),
  799.         NextCh =\= 47, !,      
  800.         $read_solidus(NextCh, LastCh).
  801. $read_solidus(42, 32) :- !.
  802. $read_solidus(_, LastCh) :-
  803.         $get0(NextCh),
  804.         $read_solidus(NextCh, LastCh).
  805. */
  806. /*
  807.     $read_name(Char, String, LastCh)
  808.     reads a sequence of letters, digits, and underscores, and returns
  809.     them as String.  The first character which cannot join this sequence
  810.     is returned as LastCh. */
  811.  
  812. /* modified by Saumya Debray : June 18, 1985 : to use search tree structure */
  813.  
  814. $read_name(Ch,ChList,LastCh) :-
  815.     ((Ch >= 65,
  816.       ((Ch =< 90, ChList = [Ch | Chars], 
  817.         $get0(NextCh), $read_name(NextCh, Chars, LastCh)
  818.        ) ;
  819.        (Ch > 90,
  820.         ((Ch =:= 95, ChList = [Ch | Chars], 
  821.               $get0(NextCh), $read_name(NextCh, Chars, LastCh)
  822.          ) ;
  823.          (Ch =\= 95,
  824.           ((Ch >= 97, 
  825.             ((Ch =< 122, ChList = [Ch | Chars], 
  826.               $get0(NextCh), $read_name(NextCh, Chars, LastCh)
  827.              ) ;
  828.          (Ch > 122, ChList = [], LastCh = Ch)
  829.         )) ;
  830.            (Ch < 97, ChList = [], LastCh = Ch)
  831.           )))))) ;
  832.      (Ch < 65, 
  833.       ((Ch >= 48,
  834.         ((Ch =< 57, ChList = [Ch | Chars], $get0(NextCh),
  835.           $read_name(NextCh,Chars,LastCh)
  836.          ) ;
  837.          (Ch > 57, ChList = [], LastCh = Ch)
  838.         )) ;
  839.        (Ch < 48, 
  840.         ((Ch =:= 36, ChList = [Ch | Chars], $get0(NextCh),
  841.           $read_name(NextCh,Chars,LastCh)
  842.          ) ;
  843.          (Ch =\= 36,
  844.           ChList = [], LastCh = Ch)
  845.          )
  846.        )
  847.       ))).
  848.       
  849. /* **********************************************************************
  850. $read_name(Char, [Char|Chars], LastCh) :-
  851.         ( Char >= 97, Char =< 122       [* a..z *]
  852.         ; Char >= 65, Char =< 90        [* A..Z *]
  853.         ; Char >= 48, Char =< 57        [* 0..9 *]
  854.         ; Char = 95                     [*  _   *]
  855.         ), !,
  856.         $get0(NextCh),
  857.         $read_name(NextCh, Chars, LastCh).
  858. $read_name(LastCh, [], LastCh).
  859. ********************************************************************** */
  860. /*
  861.     $read_symbol(Ch, String, NextCh)
  862.     reads the other kind of atom which needs no quoting: one which is
  863.     a string of "symbol" characters.  Note that it may accept 0
  864.     characters, this happens when called from read_fullstop. */
  865.  
  866. $read_symbol(Char, [Char|Chars], LastCh) :-
  867. /*        memberchk(Char, "#$&*+-./:<=>?@\^`~"), */
  868.     $read_chkspec(Char),
  869.         !,
  870.         $get0(NextCh),
  871.         $read_symbol(NextCh, Chars, LastCh).
  872. $read_symbol(LastCh, [], LastCh).
  873.  
  874. $read_chkspec(0'#).    /* '#' 35 */
  875. $read_chkspec(0'$).    /* '$' 36 */
  876. $read_chkspec(0'&).    /* '&' 38 */
  877. $read_chkspec(0'*).    /* '*' 42 */
  878. $read_chkspec(0'+).    /* '+' 43 */
  879. $read_chkspec(0'-).    /* '-' 45 */
  880. $read_chkspec(0'.).    /* '.' 46 */
  881. $read_chkspec(0'/).    /* '/' 47 */
  882. $read_chkspec(0':).    /* ':' 58 */
  883. $read_chkspec(0'<).    /* '<' 60 */
  884. $read_chkspec(0'=).    /* '=' 61 */
  885. $read_chkspec(0'>).    /* '>' 62 */
  886. $read_chkspec(0'?).    /* '?' 63 */
  887. $read_chkspec(0'@).    /* '@' 64 */
  888. $read_chkspec(0'\).    /* '\' 92 */
  889. $read_chkspec(0'^).    /* '^' 94 */
  890. $read_chkspec(0'`).    /* '`' 96 */
  891. $read_chkspec(0'~).    /* '~' 12 */
  892.  
  893. /*
  894.     $read_fullstop(Char, Dict, Tokens)
  895.     looks at the next character after a full stop.  There are
  896.     three cases:
  897.         (a) the next character is an end of file.  We treat this
  898.             as an unexpected end of file.  The reason for this is
  899.             that we HAVE to handle end of file characters in this
  900.             module or they are gone forever; if we failed to check
  901.             for end of file here and just accepted .<EOF> like .<NL>
  902.             the caller would have no way of detecting an end of file
  903.             and the next call would abort.
  904.         (b) the next character is a layout character.  This is a
  905.             clause terminator.
  906.         (c) the next character is anything else.  This is just an
  907.             ordinary symbol and we call read_symbol to process it.
  908. */
  909.  
  910. $read_fullstop(Ch, _, _) :-
  911.     (Ch =:= -1 ; Ch =:= 26), !,
  912.         $print('! end of file just after full stop'), $nl,
  913.         fail.
  914. $read_fullstop(Ch, _, []) :-
  915.         Ch =< 32, !.            /* END OF CLAUSE */
  916. $read_fullstop(Ch, Dict, [atom(A)|Tokens]) :-
  917.         $read_symbol(Ch, S, NextCh),
  918.         $name(A, [46|S]),
  919.         $read_tokens(NextCh, Dict, Tokens).
  920.  
  921.  
  922. /*
  923.     read_number is complicated by having to understand radix notation.
  924.     There are three forms of integer:
  925.         0 ' <any character>     - the ASCII code for that character
  926.         <digit> ' <digits>      - the digits, read in that base
  927.         <digits>                - the digits, read in base 10.
  928.     Note that radix 16 is not understood, because 16 is two digits,
  929.     and that all the decimal digits are accepted in each base (this
  930.     is also true of C).  So 2'89 = 25.  I can't say I care for this,
  931.     but it does no great harm, and that's what Dec-10 Prolog does.
  932.     The X =\= -1 (and 26) tests are to make sure we don't miss an end 
  933.     of file character.  The tokeniser really should be in C, not least to
  934.     make handling end of file characters bearable.  If we hit an end
  935.     of file inside an integer, read_number will fail.
  936. */
  937.  
  938. /*
  939.    Modified by Saumya Debray, Nov 1986, to handle floating point numbers
  940. */
  941.  
  942. $read_number(BaseChar, Val, NextCh) :-
  943.         Base is BaseChar - 48,
  944.         $get0(Ch),
  945.         Ch =\= 26, Ch > 0,
  946.         (   Ch =\= 39, $read_digits(Ch, Base, 10, Val, NextCh)
  947.         ;   Base >= 1, $read_digits(0, Base, Val, NextCh)
  948.         ;   $get0(Val), Val =\= 26, $get0(NextCh)
  949.         ),  !.
  950.  
  951. $read_digits(SoFar, Base, Value, NextCh) :-
  952.         $get0(Ch),
  953.     ((Ch > 0, Ch =\= 26) ->
  954.             $read_digits(Ch, SoFar, Base, Value, NextCh) ;
  955.         ($print('! end of file in number !'), fail)
  956.     ).
  957.  
  958. $read_digits(46, SoFar, Base, Value, NextCh) :-
  959.     !,
  960.     $get0(Ch),
  961.     ((Ch =:= 26 ; Ch < 0) ->
  962.         ($print('! end of file just after full stop'), $nl, fail) ;
  963.          ((Ch =< 32,                /* end of clause */
  964.           Value = SoFar, NextCh = '_$end_of_clause'
  965.              ) ;
  966.              (Ch > 32,
  967.               ( (Ch >= 48, Ch =< 57) -> 
  968.                     ($floor(Divisor,Base),
  969.               $read_fraction(Ch, 0, Base, Divisor, Fraction, NextCh),
  970.               Value is SoFar+Fraction) ;
  971.                  (Value = SoFar, NextCh = Ch)
  972.               )
  973.              )
  974.             )
  975.     ).
  976. $read_digits(Digit, SoFar, Base, Value, NextCh) :-
  977.         Digit >= 48, Digit =< 57,
  978.         !,
  979.         Next is SoFar*Base-48+Digit,
  980.         $read_digits(Next, Base, Value, NextCh).
  981. $read_digits(LastCh, Value, _, Value, LastCh).
  982.  
  983. $read_fraction(Digit, SoFar, Base, Divisor, Value, NextCh) :-
  984.         Digit >= 48, Digit =< 57,
  985.         !,
  986.         Next is SoFar + ((Digit-48)/Divisor),
  987.     Divisor1 is Divisor*Base,
  988.     $get0(Ch),
  989.     ((Ch < 0 ; Ch =:= 26) ->
  990.         ($print('! end of file in number !'), fail) ;
  991.             $read_fraction(Ch, Next, Base, Divisor1, Value, NextCh)
  992.     ).
  993. $read_fraction(LastCh, Value, _, _, Value, LastCh).
  994.     
  995.  
  996. /*
  997.     read_lookup is identical to memberchk except for argument order and
  998.     mode declaration.
  999. */
  1000.  
  1001. $read_lookup([X|_], X) :- !.
  1002. $read_lookup([_|T], X) :- $read_lookup(T, X). 
  1003.  
  1004.