tok.red 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. module tok; % Identifier and reserved character reading.
  2. % Author: Anthony C. Hearn.
  3. % Modifications by: Arthur Norman.
  4. % Copyright (c) 1995 RAND. All rights reserved.
  5. fluid '(!*adjprec !*defn !*eoldelimp !*lower !*minusliter !*quotenewnam
  6. semic!*);
  7. % Note *raise is global in following for consistency with the SL Report.
  8. global '(!$eof!$
  9. !$eol!$
  10. !*micro!-version
  11. !*raise
  12. !*savecomments!*
  13. comment!*
  14. crbuf!*
  15. crbuf1!*
  16. crchar!*
  17. curline!*
  18. cursym!*
  19. eof!*
  20. ifl!*
  21. nxtsym!*
  22. outl!*
  23. ttype!*);
  24. flag('(adjprec),'switch);
  25. !*quotenewnam := t;
  26. crchar!* := '! ;
  27. curline!* := 1;
  28. % The function TOKEN defined below is used for reading identifiers
  29. % and reserved characters (such as parentheses and infix operators).
  30. % It is called by the function SCAN, which translates reserved
  31. % characters into their internal name, and sets up the output of the
  32. % input line. The following definitions of TOKEN and SCAN are quite
  33. % general, but also inefficient. The reading process can often be
  34. % speeded up considerably if these functions (especially token) are
  35. % written in terms of the explicit LISP used.
  36. symbolic procedure prin2x u;
  37. outl!* := u . outl!*;
  38. symbolic procedure mkstrng u;
  39. %converts the uninterned id U into a string;
  40. %if strings are not constants, this should be replaced by
  41. %list('string,u);
  42. u;
  43. symbolic procedure readch1;
  44. begin scalar x;
  45. if null terminalp()
  46. then progn(x := readch(),
  47. x eq !$eol!$ and (curline!* := curline!*+1),
  48. return x)
  49. else if crbuf1!*
  50. then begin x := car crbuf1!*; crbuf1!* := cdr crbuf1!* end
  51. else x := readch();
  52. crbuf!* := x . crbuf!*;
  53. return x
  54. end;
  55. symbolic procedure tokquote;
  56. begin
  57. crchar!* := readch1();
  58. nxtsym!* := mkquote rread();
  59. ttype!* := 4;
  60. return nxtsym!*
  61. end;
  62. put('!','tokprop,'tokquote);
  63. symbolic procedure token!-number x;
  64. % Read and return a valid number from input.
  65. % Adjusted by A.C. Norman to be less sensitive to input case and to
  66. % support hex numbers.
  67. begin scalar dotp,power,sign,y,z;
  68. power := 0;
  69. ttype!* := 2;
  70. num1:
  71. if y or null(x eq '!)) then y := x . y;
  72. if dotp then power := power - 1;
  73. num2:
  74. if (x := readch1()) eq '!.
  75. then if dotp
  76. then rerror('rlisp,3,"Syntax error: improper number")
  77. else progn(dotp := t, go to num2)
  78. else if digit x then go to num1
  79. else if y = '(!0) and (x eq '!x or x eq '!X) then go to hexnum
  80. else if x eq '!\ then progn(readch(), go to num2)
  81. else if null(x eq '!e or x eq '!E) then go to ret;
  82. % Case of number with embedded or trailing E.
  83. dotp := t;
  84. if (x := readch1()) eq '!- then sign := t
  85. else if x eq '!+ then nil
  86. else if null digit x then go to ret
  87. else z := list x;
  88. nume1:
  89. if null digit(x := readch1()) then go to nume2;
  90. z := x . z;
  91. go to nume1;
  92. hexnum:
  93. y := 0;
  94. hexnum1:
  95. if not (z := get(x := readch1(), 'hexdigit)) then go to ret1;
  96. y := 16*y + z;
  97. go to hexnum1;
  98. nume2:
  99. if null z then rerror('rlisp,4,"Syntax error: improper number");
  100. z := compress reversip!* z;
  101. if sign then power := power - z else power := power + z;
  102. ret:
  103. y := compress reversip!* y;
  104. ret1:
  105. nxtsym!* := if dotp then '!:dn!: . (y . power)
  106. else if !*adjprec then '!:int!: . (y . nil)
  107. else y;
  108. crchar!* := x;
  109. return nxtsym!*
  110. end;
  111. deflist(
  112. '((!0 0) (!1 1) (!2 2) (!3 3) (!4 4)
  113. (!5 5) (!6 6) (!7 7) (!8 8) (!9 9)
  114. (!a 10) (!b 11) (!c 12) (!d 13) (!e 14) (!f 15)
  115. (!A 10) (!B 11) (!C 12) (!D 13) (!E 14) (!F 15)), 'hexdigit);
  116. symbolic procedure token1;
  117. begin scalar x,y;
  118. x := crchar!*;
  119. a: if seprp x and null(x eq !$eol!$ and !*eoldelimp)
  120. then progn(x := readch1(), go to a)
  121. else if digit x then return token!-number x
  122. else if liter x then go to letter
  123. else if (y := get(x,'tokprop)) then return lispapply(y,nil)
  124. else if x eq '!% and null !*savecomments!* then go to coment
  125. else if x eq '!! and null(!*micro!-version and null !*defn)
  126. then go to escape
  127. else if x eq '!" then go to string;
  128. ttype!* := 3;
  129. if x eq !$eof!$ then prog2(crchar!* := '! ,filenderr());
  130. nxtsym!* := x;
  131. if delcp x then crchar!*:= '! else crchar!*:= readch1();
  132. if null(x eq '!- and digit crchar!* and !*minusliter)
  133. then go to c;
  134. x := token!-number crchar!*;
  135. if numberp x then return apply1('minus,x); % For bootstrapping.
  136. rplaca(cdr x,apply1('minus,cadr x)); % Also for booting.
  137. return x;
  138. escape:
  139. begin scalar raise,!*lower;
  140. raise := !*raise;
  141. !*raise := nil;
  142. y := x . y;
  143. x := readch1();
  144. !*raise := raise
  145. end;
  146. letter:
  147. ttype!* := 0;
  148. let1:
  149. y := x . y;
  150. if digit (x := readch1()) or liter x then go to let1
  151. else if x eq '!! then go to escape
  152. else if x eq '!- and !*minusliter
  153. then progn(y := '!! . y, go to let1)
  154. else if x eq '!_ then go to let1; % Allow _ as letter.
  155. nxtsym!* := intern compress reversip!* y;
  156. crchar!* := x;
  157. c: return nxtsym!*;
  158. % minusl:
  159. % if digit (x := readch1())
  160. % then progn(crchar!* := x, return(nxtsym!* := 'minus))
  161. % else progn(y := '!- . '!! . y, go to letter);
  162. string:
  163. begin scalar raise,!*lower;
  164. raise := !*raise;
  165. !*raise := nil;
  166. strinx:
  167. y := x . y;
  168. if (x := readch1()) eq !$eof!$
  169. then progn(!*raise := raise,
  170. crchar!* := '! ,
  171. lpriw("***** End-of-file in string",nil),
  172. filenderr())
  173. else if null(x eq '!") then go to strinx;
  174. y := x . y;
  175. % Now check for embedded string character.
  176. x := readch1();
  177. if x eq '!" then go to strinx;
  178. nxtsym!* := mkstrng compress reversip!* y;
  179. !*raise := raise
  180. end;
  181. ttype!* := 1;
  182. crchar!* := x;
  183. go to c;
  184. coment:
  185. if null(readch1() eq !$eol!$) then go to coment;
  186. x := readch1();
  187. go to a
  188. end;
  189. symbolic procedure tokbquote;
  190. begin
  191. crchar!* := readch1();
  192. nxtsym!* := list('backquote,rread());
  193. ttype!* := 3;
  194. return nxtsym!*
  195. end;
  196. put('!`,'tokprop,'tokbquote);
  197. symbolic procedure token;
  198. %This provides a hook for a faster TOKEN;
  199. token1();
  200. symbolic procedure filenderr;
  201. begin
  202. eof!* := eof!*+1;
  203. if terminalp() then error1()
  204. else error(99,if ifl!*
  205. then list("End-of-file read in file",car ifl!*)
  206. else "End-of-file read")
  207. end;
  208. symbolic procedure ptoken;
  209. begin scalar x;
  210. x := token();
  211. if x eq '!) and eqcar(outl!*,'! ) then outl!*:= cdr outl!*;
  212. %an explicit reference to OUTL!* used here;
  213. prin2x x;
  214. if null ((x eq '!() or (x eq '!))) then prin2x '! ;
  215. return x
  216. end;
  217. symbolic procedure rread1;
  218. % Modified to use QUOTENEWNAM's for ids.
  219. % Note that handling of reals uses symbolic mode, regardless of
  220. % actual mode.
  221. begin scalar x,y;
  222. x := ptoken();
  223. if null (ttype!*=3)
  224. then return if idp x
  225. then if !*quotenewnam
  226. and (y := get(x,'quotenewnam))
  227. then y
  228. else x
  229. else if eqcar(x,'!:dn!:)
  230. then dnform(x,nil,'symbolic)
  231. else x
  232. else if x eq '!( then return rrdls()
  233. else if null (x eq '!+ or x eq '!-) then return x;
  234. y := ptoken();
  235. if eqcar(y,'!:dn!:) then y := dnform(y,nil,'symbolic);
  236. if null numberp y
  237. then progn(nxtsym!* := " ",
  238. symerr("Syntax error: improper number",nil))
  239. else if x eq '!- then y := apply1('minus,y);
  240. % We need this construct for bootstrapping purposes.
  241. return y
  242. end;
  243. symbolic procedure rrdls;
  244. begin scalar x,y,z;
  245. a: x := rread1();
  246. if null (ttype!*=3) then go to b
  247. else if x eq '!) then return z
  248. else if null (x eq '!.) then go to b;
  249. x := rread1();
  250. y := ptoken();
  251. if null (ttype!*=3) or null (y eq '!))
  252. then progn(nxtsym!* := " ",symerr("Invalid S-expression",nil))
  253. else return nconc(z,x);
  254. b: z := nconc(z,list x);
  255. go to a
  256. end;
  257. symbolic procedure rread;
  258. progn(prin2x " '",rread1());
  259. symbolic procedure delcp u;
  260. % Returns true if U is a semicolon, dollar sign, or other delimiter.
  261. % This definition replaces the one in the BOOT file.
  262. flagp(u,'delchar);
  263. flag('(!; !$),'delchar);
  264. symbolic procedure toknump x;
  265. numberp x or eqcar(x,'!:dn!:) or eqcar(x,'!:int!:);
  266. % The following version of SCAN provides RLISP with a facility for
  267. % conditional compilation. The protocol is that text is included or
  268. % excluded at the level of tokens. Control by use of new reserved
  269. % tokens !#if, !#else and !#endif. These are used in the form:
  270. % !#if (some Lisp expression for use as a condition)
  271. % ... RLISP input ...
  272. % !#else
  273. % ... alternative RLISP input ...
  274. % !#endif
  275. %
  276. % The form
  277. % !#if C1 ... !#elif C2 ... !#elif C3 ... !#else ... !#endif
  278. % is also supported.
  279. %
  280. % Conditional compilation can be nested. If the Lisp expression used
  281. % to guard a condition causes an error it is taken to be a FALSE
  282. % condition. It is not necessary to have an !#else before !#endif if no
  283. % alternative text is needed. Although the examples here put !#if etc
  284. % at the start of lines this is not necessary (though it may count as
  285. % good style?). Since the condition will be read using RLISPs own
  286. % list-reader there could be conditional compilation guarding parts of
  287. % it - the exploitation of that possibility is to be discouraged!
  288. % Making the condition a raw Lisp expression makes sure that parsing it
  289. % is easy. It makes it possible to express arbitrary conditions, but it
  290. % is hoped that most conditions will not be very elaborate - things like
  291. % !#if (not (member 'csl lispsystem!*))
  292. % error();
  293. % !#else
  294. % magic();
  295. % !#endif
  296. % or
  297. % !#if debugging!-mode % NB if variable is unset that counts as nil
  298. % print "message"; % so care should be taken to select the most
  299. % !#endif % useful default sense for such tests
  300. % should be about as complicated as reasonable people need.
  301. %
  302. % Two further facilities are provided:
  303. % !#eval (any lisp expression)
  304. % causes that expression to be evaluated at parse time. Apart from any
  305. % side-effects in the evaluation the text involved is all ignored. It is
  306. % expected that this will only be needed in rather curious cases, for
  307. % instance to set system-specific options for a compiler.
  308. % !#define symbol value
  309. % where the value should be another symbol, a string or a number,
  310. % causes the first symbol to be mapped onto the second value wherever
  311. % it occurs in subsequent input. This uses exactly the same mechanism
  312. % as the existing REDUCE "define" statement and so has the same
  313. % limitations. The use of a hook in SCAN to support this ensures that
  314. % the !#define can be written anywhere in REDUCE source code (eg within
  315. % a procedure definition) and will still apply while the program
  316. % involved is parsed. No special facility for undoing the effect of a
  317. % !#define is provided, but the general-purpose !#eval could be used to
  318. % remove the 'newnam property that is involved.
  319. symbolic procedure scan;
  320. begin scalar bool,x,y;
  321. if null (cursym!* eq '!*semicol!*) then go to b;
  322. a: nxtsym!* := token();
  323. b: if null atom nxtsym!* and null toknump nxtsym!*
  324. then go to q1
  325. else if nxtsym!* eq 'else or cursym!* eq '!*semicol!*
  326. then outl!* := nil;
  327. prin2x nxtsym!*;
  328. c: if null idp nxtsym!* then go to l
  329. else if (x:=get(nxtsym!*,'newnam)) and
  330. (null (x=nxtsym!*)) then go to new
  331. else if nxtsym!* eq 'Comment then go to comm
  332. else if nxtsym!* eq '!#if then go to conditional
  333. else if nxtsym!* eq '!#else or
  334. nxtsym!* eq '!#elif then progn(nxtsym!* := x := nil,
  335. go to skipping)
  336. else if nxtsym!* eq '!#endif then go to a
  337. else if nxtsym!* eq '!#eval then progn(
  338. errorset(rread(), !*backtrace, nil),
  339. go to a)
  340. else if nxtsym!* eq '!#define then progn(
  341. x := errorset(rread(), !*backtrace, nil),
  342. progn(if errorp x then go to a),
  343. y := errorset(rread(), !*backtrace, nil),
  344. progn(if errorp y then go to a),
  345. put(x, 'newnam, y),
  346. go to a)
  347. else if null(ttype!* = 3) then go to l
  348. else if nxtsym!* eq !$eof!$ then return filenderr()
  349. else if nxtsym!* eq '!' then rederr "Invalid QUOTE"
  350. else if !*eoldelimp and nxtsym!* eq !$eol!$ then go to delim
  351. else if null (x:= get(nxtsym!*,'switch!*)) then go to l
  352. else if eqcar(cdr x,'!*semicol!*) then go to delim;
  353. bool := seprp crchar!*;
  354. sw1: nxtsym!* := token();
  355. if null(ttype!* = 3) then go to sw2
  356. else if nxtsym!* eq !$eof!$ then return filenderr()
  357. else if car x then go to sw3;
  358. sw2: cursym!*:=cadr x;
  359. bool := nil;
  360. if cursym!* eq '!*rpar!* then go to l2 else return cursym!*;
  361. sw3: if bool or null (y:= atsoc(nxtsym!*,car x)) then go to sw2;
  362. prin2x nxtsym!*;
  363. x := cdr y;
  364. if null car x and cadr x eq '!*Comment!*
  365. then progn(comment!* := read!-comment(),go to a);
  366. go to sw1;
  367. conditional:
  368. % The conditional expression used here must be written in Lisp form
  369. x := errorset(rread(), !*backtrace, nil);
  370. % errors in evaluation count as NIL
  371. if null errorp x and car x then go to a;
  372. x := nil;
  373. skipping:
  374. % I support nesting of conditional inclusion.
  375. if nxtsym!* eq '!#endif then
  376. if null x then go to a else x := cdr x
  377. else if nxtsym!* eq '!#if then x := nil . x
  378. else if (nxtsym!* eq '!#else) and null x then go to a
  379. else if (nxtsym!* eq '!#elif) and null x then go to conditional;
  380. nxtsym!* := token();
  381. if (ttype!*=3) and (nxtsym!* eq !$eof!$)
  382. then return filenderr()
  383. else go to skipping;
  384. comm: if delcp crchar!* and null(crchar!* eq !$eol!$)
  385. then progn(crchar!* := '! , condterpri(), go to a);
  386. crchar!* := readch();
  387. go to comm;
  388. delim:
  389. semic!*:=nxtsym!*;
  390. return (cursym!*:='!*semicol!*);
  391. new: nxtsym!* := x;
  392. if stringp x then go to l
  393. else if atom x then go to c
  394. else go to l;
  395. q1: if null (car nxtsym!* eq 'string) then go to l;
  396. prin2x " ";
  397. prin2x cadr(nxtsym!* := mkquote cadr nxtsym!*);
  398. l: cursym!*:=nxtsym!*;
  399. nxtsym!* := token();
  400. if nxtsym!* eq !$eof!$ and ttype!* = 3 then return filenderr();
  401. l2: if numberp nxtsym!*
  402. or (atom nxtsym!* and null get(nxtsym!*,'switch!*))
  403. then prin2x " ";
  404. return cursym!*
  405. end;
  406. endmodule;
  407. end;