LispReader.java 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838
  1. // Copyright (c) 2000, 2016 Per M.A. Bothner.
  2. // This is free software; for terms and warranty disclaimer see COPYING.
  3. package gnu.kawa.lispexpr;
  4. import gnu.text.*;
  5. import gnu.mapping.*;
  6. import gnu.lists.*;
  7. import gnu.math.*;
  8. import gnu.expr.*;
  9. import gnu.kawa.io.BinaryInPort;
  10. import gnu.kawa.io.InPort;
  11. import gnu.kawa.functions.Arrays;
  12. import gnu.kawa.util.GeneralHashTable;
  13. import gnu.bytecode.PrimType;
  14. import gnu.bytecode.Type;
  15. import java.util.List;
  16. import java.util.regex.*;
  17. import java.lang.reflect.Array;
  18. /** A Lexer for reading S-expressions in generic Lisp-like syntax.
  19. * This class may have outlived its usefulness: It's mostly just a
  20. * wrapper around an InPort plus a helper token-buffer.
  21. * The functionality should be moved to ReadTable, though it is
  22. * unclear what to do about the tokenBuffer.
  23. */
  24. public class LispReader extends Lexer
  25. {
  26. public LispReader(InPort port)
  27. {
  28. super(port);
  29. }
  30. public LispReader(InPort port, SourceMessages messages)
  31. {
  32. super(port, messages);
  33. }
  34. boolean returnMutablePairs;
  35. /** Set whether returned pairs are mutable or not (the default). */
  36. public void setReturnMutablePairs(boolean v) { returnMutablePairs = v; }
  37. GeneralHashTable<Integer,Object> sharedStructureTable;
  38. /** Bind value to index in sharingStructuretable.
  39. * @param value The object being defined.
  40. * @param sharingIndex Back-reference index.
  41. * I.e. the value N in a @code{#N=} form. If negative, do nothing.
  42. * @return The value unchanged.
  43. */
  44. public Object bindSharedObject(int sharingIndex, Object value) {
  45. if (sharingIndex >= 0) {
  46. GeneralHashTable<Integer,Object> map = sharedStructureTable;
  47. if (map == null) {
  48. map = new GeneralHashTable<Integer,Object>();
  49. sharedStructureTable = map;
  50. }
  51. Integer key = Integer.valueOf(sharingIndex);
  52. if (map.get(key, this) != this)
  53. error('w', "a duplicate #n= definition was read");
  54. map.put(key, value);
  55. }
  56. return value;
  57. }
  58. /** Read a #|...|#-style comment (which may contain other nested comments).
  59. * Assumes the initial "#|" has already been read.
  60. */
  61. final public void readNestedComment (char start1, char start2,
  62. char end1, char end2)
  63. throws java.io.IOException, SyntaxException {
  64. int commentNesting = 1;
  65. int startLine = port.getLineNumber();
  66. int startColumn = port.getColumnNumber();
  67. StringBuilder buf = null;
  68. if (port instanceof BinaryInPort && (startLine == 0 || startLine == 1))
  69. buf = new StringBuilder();
  70. do {
  71. int c = read ();
  72. if (buf != null)
  73. buf.append((char) c);
  74. if (c == end1) {
  75. c = read();
  76. if (buf != null)
  77. buf.append((char) c);
  78. if (c == end2)
  79. commentNesting--;
  80. } else if (c == start1) {
  81. c = read();
  82. if (c == start2)
  83. commentNesting++;
  84. }
  85. if (c < 0) {
  86. eofError("unexpected end-of-file in " + start1 + start2
  87. + " comment starting here",
  88. startLine + 1, startColumn - 1);
  89. return;
  90. }
  91. } while (commentNesting > 0);
  92. if (buf != null)
  93. checkEncodingSpec(buf.toString());
  94. }
  95. public void checkEncodingSpec(String line) {
  96. Matcher m = Pattern.compile("coding[:=]\\s*([-a-zA-Z0-9]+)")
  97. .matcher(line);
  98. if (m.find()) {
  99. String enc = m.group(1);
  100. try {
  101. ((BinaryInPort) getPort()).setCharset(enc);
  102. } catch (java.nio.charset.UnsupportedCharsetException ex) {
  103. error('e', "unrecognized encoding name "+enc);
  104. } catch (Exception ex) {
  105. error('e', "cannot set encoding name here");
  106. }
  107. }
  108. }
  109. boolean inQuasiSyntax;
  110. public static final ThreadLocation symbolReadCase
  111. = new ThreadLocation("symbol-read-case");
  112. static { symbolReadCase.setGlobal(Symbol.valueOf("preserve")); }
  113. char readCase = lookupReadCase();
  114. /** Get specification of how symbols should be case-folded.
  115. * @return Either '\0' (unspecified - defaults to preserve case),
  116. * 'P' (means preserve case), 'U' (upcase),
  117. * 'D' (downcase), or 'I' (invert case).
  118. */
  119. public char getReadCase () { return readCase; }
  120. public void setReadCase(char readCase) { this.readCase = readCase; }
  121. static char lookupReadCase()
  122. {
  123. try
  124. {
  125. String read_case_string = symbolReadCase.get("P").toString();
  126. if (read_case_string.length() > 0)
  127. {
  128. char read_case = read_case_string.charAt(0);
  129. if (read_case == 'P') ;
  130. else if (read_case == 'u')
  131. read_case = 'U';
  132. else if (read_case == 'd' || read_case == 'l' || read_case == 'L')
  133. read_case = 'D';
  134. else if (read_case == 'i')
  135. read_case = 'I';
  136. return read_case;
  137. }
  138. }
  139. catch (Exception ex)
  140. {
  141. }
  142. return '\0';
  143. }
  144. public Object readValues (int ch, ReadTable rtable, int sharingIndex)
  145. throws java.io.IOException, SyntaxException {
  146. return readValues(ch, rtable.lookup(ch), rtable, sharingIndex);
  147. }
  148. /** May return zero or multiple values.
  149. * Returns no values if looking at whitespace or a comment. */
  150. public Object readValues (int ch, ReadTableEntry entry, ReadTable rtable,
  151. int sharingIndex)
  152. throws java.io.IOException, SyntaxException {
  153. seenEscapes = false;
  154. return entry.read(this, ch, -1, sharingIndex);
  155. }
  156. public Pair readValuesAndAppend(int ch, ReadTable rtable, Pair last)
  157. throws java.io.IOException, SyntaxException {
  158. int line = port.getLineNumber();
  159. int column = port.getColumnNumber() - 1; // Adjust for ch
  160. Object values = readValues(ch, rtable, -1);
  161. int index = 0;
  162. int next = Values.nextIndex(values, index);
  163. if (next >= 0) {
  164. for (;;) {
  165. Object value = Values.nextValue(values, index);
  166. index = next;
  167. if (value == gnu.expr.QuoteExp.voidExp)
  168. value = Values.empty;
  169. next = Values.nextIndex(values, index);
  170. if (next < 0)
  171. value = handlePostfix(value, rtable, line, column);
  172. Pair pair = makePair(value, line, column);
  173. setCdr(last, pair);
  174. last = pair;
  175. if (next < 0)
  176. break;
  177. }
  178. }
  179. return last;
  180. }
  181. protected Object readAndHandleToken(int ch, int startPos, ReadTable rtable)
  182. throws java.io.IOException, SyntaxException
  183. {
  184. readToken(ch, rtable);
  185. return handleToken(startPos, rtable);
  186. }
  187. protected Object handleToken(int startPos, ReadTable rtable)
  188. throws java.io.IOException, SyntaxException
  189. {
  190. int ch;
  191. char readCase = getReadCase();
  192. int endPos = tokenBufferLength;
  193. if (! seenEscapes)
  194. {
  195. Object value = parseNumber(tokenBuffer, startPos, endPos - startPos,
  196. '\0', 0, SCM_NUMBERS|rtable.extraFlags);
  197. if (value != null && ! (value instanceof String))
  198. {
  199. tokenBufferLength = startPos;
  200. return value;
  201. }
  202. /* Common Lisp only? FIXME
  203. if (isPotentialNumber(tokenBuffer, startPos, endPos))
  204. {
  205. error(value == null ? "not a valid number"
  206. : "not a valid number: " + value);
  207. return IntNum.zero();
  208. }
  209. */
  210. }
  211. if (readCase == 'I')
  212. {
  213. int upperCount = 0;
  214. int lowerCount = 0;
  215. for (int i = startPos; i < endPos; i++)
  216. {
  217. char ci = tokenBuffer[i];
  218. if (ci == TOKEN_ESCAPE_CHAR)
  219. i++;
  220. else if (Character.isLowerCase(ci))
  221. lowerCount++;
  222. else if (Character.isUpperCase(ci))
  223. upperCount++;
  224. }
  225. if (lowerCount == 0)
  226. readCase = 'D';
  227. else if (upperCount == 0)
  228. readCase = 'U';
  229. else
  230. readCase = 'P';
  231. }
  232. boolean handleUri =
  233. (endPos >= startPos + 2
  234. && tokenBuffer[endPos-1] == '}'
  235. && tokenBuffer[endPos-2] != TOKEN_ESCAPE_CHAR
  236. && peek() == ':');
  237. int packageMarker = -1;
  238. int lbrace = -1, rbrace = -1, braceNesting = 0;
  239. int j = startPos;
  240. boolean uriBad = false;
  241. for (int i = startPos; i < endPos; i++)
  242. {
  243. char ci = tokenBuffer[i];
  244. if (ci == TOKEN_ESCAPE_CHAR)
  245. {
  246. if (++ i < endPos)
  247. tokenBuffer[j++] = tokenBuffer[i];
  248. continue;
  249. }
  250. if (handleUri)
  251. {
  252. if (ci == '{')
  253. {
  254. if (lbrace < 0)
  255. lbrace = j;
  256. else if (braceNesting == 0)
  257. uriBad = true;
  258. braceNesting++;
  259. }
  260. else if (ci == '}')
  261. {
  262. braceNesting--;
  263. if (braceNesting < 0)
  264. uriBad = true;
  265. else if (braceNesting == 0)
  266. {
  267. if (rbrace < 0)
  268. rbrace = j;
  269. else
  270. uriBad = true;
  271. }
  272. }
  273. }
  274. if (braceNesting > 0)
  275. ;
  276. else if (ci == ':')
  277. packageMarker = packageMarker >= 0 ? -1 : j;
  278. else if (readCase == 'U')
  279. ci = Character.toUpperCase(ci);
  280. else if (readCase == 'D')
  281. ci = Character.toLowerCase(ci);
  282. tokenBuffer[j++] = ci;
  283. }
  284. endPos = j;
  285. int len = endPos - startPos;
  286. Object result;
  287. if (lbrace >= 0 && rbrace > lbrace)
  288. {
  289. String prefix = lbrace > 0 ? new String(tokenBuffer, startPos, lbrace-startPos) : null;
  290. lbrace++;
  291. String uri = new String(tokenBuffer, lbrace, rbrace-lbrace);
  292. ch = read(); // skip ':' - previously peeked.
  293. ch = read();
  294. Object rightOperand = readValues(ch, rtable.lookup(ch), rtable, -1);
  295. if (! (rightOperand instanceof SimpleSymbol))
  296. error("expected identifier in symbol after '{URI}:'");
  297. // FIXME should allow "compound keyword" - for attribute names
  298. result = Symbol.valueOf(rightOperand.toString(), uri, prefix);
  299. }
  300. else if (rtable.initialColonIsKeyword && packageMarker == startPos && len > 1)
  301. {
  302. startPos++;
  303. String str = new String(tokenBuffer, startPos, endPos-startPos);
  304. result = Keyword.make(str.intern());
  305. }
  306. else if (rtable.finalColonIsKeyword && packageMarker != -1 && packageMarker == endPos - 1
  307. && (len > 1 || seenEscapes))
  308. {
  309. String str = new String(tokenBuffer, startPos, len - 1);
  310. result = Keyword.make(str.intern());
  311. }
  312. else {
  313. if (len == 1 && tokenBuffer[startPos] == '.' && !seenEscapes)
  314. error("invalid use of '.' token");
  315. result = rtable.makeSymbol(new String(tokenBuffer, startPos, len));
  316. }
  317. tokenBufferLength = startPos;
  318. return result;
  319. }
  320. public static final char TOKEN_ESCAPE_CHAR = '\uffff';
  321. /** If true, then tokenbuffer contains escaped characters.
  322. * These are prefixed (in the buffer) by TOKEN_ESCAPE_CHAR.
  323. */
  324. protected boolean seenEscapes;
  325. /** Read token, leaving characters in tokenBuffer.
  326. * Sets seenEscapes if escape characters are seen.
  327. */
  328. void readToken(int ch, ReadTable rtable)
  329. throws java.io.IOException, SyntaxException
  330. {
  331. boolean inEscapes = false;
  332. int braceNesting = 0;
  333. for (;; ch = read())
  334. {
  335. if (ch < 0)
  336. {
  337. if (inEscapes)
  338. eofError("unexpected EOF between escapes");
  339. else
  340. break;
  341. }
  342. ReadTableEntry entry = rtable.lookup(ch);
  343. int kind = entry.getKind();
  344. if (kind == ReadTable.ILLEGAL)
  345. {
  346. if (inEscapes)
  347. {
  348. tokenBufferAppend(TOKEN_ESCAPE_CHAR);
  349. tokenBufferAppend(ch);
  350. continue;
  351. }
  352. if (ch == '}' && --braceNesting >= 0)
  353. {
  354. tokenBufferAppend(ch);
  355. continue;
  356. }
  357. unread(ch);
  358. break;
  359. }
  360. if (! inEscapes && isTerminatingChar(ch, rtable)) {
  361. kind = ReadTable.TERMINATING_MACRO;
  362. }
  363. if (kind == ReadTable.SINGLE_ESCAPE)
  364. {
  365. ch = read();
  366. if (ch < 0)
  367. eofError("unexpected EOF after single escape");
  368. if (rtable.hexEscapeAfterBackslash
  369. // We've allowed hex escapes for a while.
  370. // Allow R7RS general escapes - but only inside |bars|.
  371. && (inEscapes || ch == 'x' || ch == 'X'))
  372. ch = readEscape(ch);
  373. if (ch >= 0)
  374. {
  375. tokenBufferAppend(TOKEN_ESCAPE_CHAR);
  376. tokenBufferAppend(ch);
  377. }
  378. seenEscapes = true;
  379. continue;
  380. }
  381. if (kind == ReadTable.MULTIPLE_ESCAPE)
  382. {
  383. inEscapes = ! inEscapes;
  384. seenEscapes = true;
  385. continue;
  386. }
  387. if (inEscapes)
  388. {
  389. // Step 9:
  390. tokenBufferAppend(TOKEN_ESCAPE_CHAR);
  391. tokenBufferAppend(ch);
  392. }
  393. else
  394. {
  395. // Step 8:
  396. switch (kind)
  397. {
  398. case ReadTable.CONSTITUENT:
  399. if (ch == '{' && entry == ReadTableEntry.brace)
  400. braceNesting++;
  401. /* ... fall through ... */
  402. case ReadTable.NON_TERMINATING_MACRO:
  403. tokenBufferAppend(ch);
  404. continue;
  405. case ReadTable.MULTIPLE_ESCAPE:
  406. inEscapes = true;
  407. seenEscapes = true;
  408. continue;
  409. case ReadTable.TERMINATING_MACRO:
  410. unread(ch);
  411. return;
  412. case ReadTable.WHITESPACE:
  413. // if (readPreservingWhitespace) FIXME
  414. unread(ch);
  415. return;
  416. }
  417. }
  418. }
  419. }
  420. protected boolean isTerminatingChar(int ch, ReadTable rtable)
  421. throws java.io.IOException, SyntaxException
  422. {
  423. if (ch == rtable.postfixLookupOperator) {
  424. int next = port.peek();
  425. if (next == rtable.postfixLookupOperator)
  426. { // Looking at '::'
  427. //unread(ch);
  428. return true;
  429. }
  430. if (validPostfixLookupStart(next, rtable))
  431. return true;
  432. }
  433. return false;
  434. }
  435. public String readTokenString(int ch, ReadTable rtable)
  436. throws java.io.IOException, SyntaxException {
  437. int startPos = tokenBufferLength;
  438. if (ch >= 0)
  439. tokenBufferAppend(ch);
  440. readToken(read(), rtable);
  441. int length = tokenBufferLength - startPos;
  442. String str = new String(tokenBuffer, startPos, length);
  443. tokenBufferLength = startPos;
  444. return str;
  445. }
  446. public Object readObject() throws java.io.IOException, SyntaxException {
  447. return readObject(-1, false);
  448. }
  449. public Object readObject(int sharingIndex, boolean topLevel)
  450. throws java.io.IOException, SyntaxException
  451. {
  452. char saveReadState = ((InPort) port).readState;
  453. int startPos = tokenBufferLength;
  454. ((InPort) port).readState = ' ';
  455. try
  456. {
  457. ReadTable rtable = ReadTable.getCurrent();
  458. for (;;)
  459. {
  460. int line = port.getLineNumber();
  461. int column = port.getColumnNumber();
  462. int ch = port.read();
  463. if (ch < 0)
  464. return Sequence.eofValue; // FIXME
  465. Object value = readValues(ch, rtable, sharingIndex);
  466. if (value == Values.empty)
  467. continue;
  468. value = handlePostfix(value, rtable, line, column);
  469. if (topLevel)
  470. {
  471. // Wrap in begin form so top-level forms have position info.
  472. value = makePair(kawa.standard.begin.begin,
  473. makePair(value, line, column,
  474. port.getLineNumber(),
  475. port.getColumnNumber()),
  476. line, column);
  477. }
  478. return value;
  479. }
  480. }
  481. finally
  482. {
  483. tokenBufferLength = startPos;
  484. ((InPort) port).readState = saveReadState;
  485. }
  486. }
  487. protected boolean validPostfixLookupStart (int ch, ReadTable rtable)
  488. throws java.io.IOException {
  489. if (ch < 0 || ch == rtable.postfixLookupOperator)
  490. return false;
  491. if (ch == ',')
  492. return true;
  493. if (ch == '@')
  494. return true; // To support deprecated (TYPE:@ EXP)
  495. int kind = rtable.lookup(ch).getKind();
  496. return kind == ReadTable.CONSTITUENT
  497. || kind == ReadTable.NON_TERMINATING_MACRO
  498. || kind == ReadTable.MULTIPLE_ESCAPE
  499. || kind == ReadTable.SINGLE_ESCAPE;
  500. }
  501. /** After reading a value check for following {@code '['} or {@code ':'}.
  502. */
  503. protected Object handlePostfix(Object value, ReadTable rtable,
  504. int line, int column)
  505. throws java.io.IOException, SyntaxException {
  506. if (value == QuoteExp.voidExp)
  507. value = Values.empty;
  508. for (;;) {
  509. int ch = port.peek();
  510. String str; int slen;
  511. if (ch == '[' && rtable.defaultBracketMode == -2) {
  512. port.read();
  513. Object lst = ReaderParens.readList(this, null, ch, 1, ']', -1);
  514. value = makePair(value, lst, line, column);
  515. value = makePair(LispLanguage.bracket_apply_sym, value,
  516. line, column);
  517. } else if (ch == rtable.postfixLookupOperator) {
  518. // A kludge to map PreOpWord to ($lookup$ Pre 'Word).
  519. port.read();
  520. int ch2 = port.peek();
  521. Object rightOperand;
  522. if (ch2 == '@') {
  523. error('w',
  524. "deprecated cast syntax TYPE:@ (use ->TYPE instead)");
  525. rightOperand = readAndHandleToken('\\', 0, rtable);
  526. } else {
  527. if (! validPostfixLookupStart(ch2, rtable)) {
  528. unread();
  529. break;
  530. }
  531. ch = port.read();
  532. rightOperand = readValues(ch, rtable.lookup(ch), rtable, -1);
  533. }
  534. value = LList.list2(value,
  535. LList.list2(LispLanguage.quasiquote_sym, rightOperand));
  536. value = makePair(LispLanguage.lookup_sym, value,
  537. line, column);
  538. }
  539. else
  540. break;
  541. }
  542. return value;
  543. }
  544. private boolean isPotentialNumber (char[] buffer, int start, int end)
  545. {
  546. int sawDigits = 0;
  547. for (int i = start; i < end; i++)
  548. {
  549. char ch = buffer[i];
  550. if (Character.isDigit(ch))
  551. sawDigits++;
  552. else if (ch == '-' || ch == '+')
  553. {
  554. if (i + 1 == end)
  555. return false;
  556. }
  557. else if (ch == '#')
  558. return true;
  559. else if (Character.isLetter(ch) || ch == '/'
  560. || ch == '_' || ch == '^')
  561. {
  562. // CommonLisp defines _123 (and ^123) as a "potential number";
  563. // most implementations seem to define it as a symbol.
  564. // Scheme does defines it as a symbol.
  565. if (i == start)
  566. return false;
  567. }
  568. else if (ch != '.')
  569. return false;
  570. }
  571. return sawDigits > 0;
  572. }
  573. static final int SCM_COMPLEX = 1;
  574. public static final int SCM_NUMBERS = SCM_COMPLEX;
  575. public static final int SCM_ANGLE = SCM_NUMBERS << 1;
  576. public static final int SCM_COLATITUDE = SCM_ANGLE << 1;
  577. public static final int SCM_LEXPONENT_IS_BIGDECIMAL = SCM_COLATITUDE << 1;
  578. public static Object parseNumber(CharSequence str, int radix) {
  579. char[] buf;
  580. int len = str.length();
  581. int where;
  582. if (str instanceof FString
  583. && (where = ((FString) str).getSegmentReadOnly(0, len)) >= 0) {
  584. buf = ((FString) str).getBuffer();
  585. } else {
  586. where = 0;
  587. buf = str.toString().toCharArray();
  588. }
  589. return parseNumber(buf, where, len,
  590. '\0', radix, LispReader.SCM_NUMBERS);
  591. }
  592. /** Parse a number.
  593. * @param buffer contains the characters of the number
  594. * @param start startinging index of the number in the buffer
  595. * @param count number of characters in buffer to use
  596. * @param exactness either 'i' or 'I' force an inexact result,
  597. * either 'e' or 'E' force an exact result,
  598. * '\0' yields an inact or inexact depending on the form of the literal,
  599. * while ' ' is like '\0' but does not allow more exactness specifiers.
  600. * @param radix the number base to use or 0 if unspecified
  601. * A negative radix is an overideable default.
  602. * @return the number if a valid number; null or a String-valued error
  603. * message if if there was some error parsing the number.
  604. */
  605. public static Object parseNumber(char[] buffer, int start, int count,
  606. char exactness, int radix, int flags)
  607. {
  608. int end = start + count;
  609. int pos = start;
  610. if (pos >= end)
  611. return "no digits";
  612. char ch = buffer[pos++];
  613. while (ch == '#')
  614. {
  615. if (pos >= end)
  616. return "no digits";
  617. ch = buffer[pos++];
  618. switch (ch)
  619. {
  620. case 'b': case 'B':
  621. if (radix > 0)
  622. return "duplicate radix specifier";
  623. radix = 2;
  624. break;
  625. case 'o': case 'O':
  626. if (radix > 0)
  627. return "duplicate radix specifier";
  628. radix = 8;
  629. break;
  630. case 'd': case 'D':
  631. if (radix > 0)
  632. return "duplicate radix specifier";
  633. radix = 10;
  634. break;
  635. case 'x': case 'X':
  636. if (radix > 0)
  637. return "duplicate radix specifier";
  638. radix = 16;
  639. break;
  640. case 'e': case 'E':
  641. case 'i': case 'I':
  642. if (exactness != '\0')
  643. {
  644. if (exactness == ' ')
  645. return "non-prefix exactness specifier";
  646. else
  647. return "duplicate exactness specifier";
  648. }
  649. exactness = ch;
  650. break;
  651. default:
  652. int value = 0;
  653. for (;;)
  654. {
  655. int dig = Character.digit(ch, 10);
  656. if (dig < 0)
  657. break;
  658. value = 10 * value + dig;
  659. if (pos >= end)
  660. return "missing letter after '#'";
  661. ch = buffer[pos++];
  662. }
  663. if (ch == 'R' || ch == 'r')
  664. {
  665. if (radix > 0)
  666. return "duplicate radix specifier";
  667. if (value < 2 || value > 36)
  668. return "invalid radix specifier";
  669. radix = value;
  670. break;
  671. }
  672. return "unknown modifier '#" + ch + '\'';
  673. }
  674. if (pos >= end)
  675. return "no digits";
  676. ch = buffer[pos++];
  677. }
  678. if (exactness == '\0')
  679. exactness = ' ';
  680. if (radix < 0)
  681. radix = -radix;
  682. else if (radix == 0)
  683. {
  684. radix = 10;
  685. /*
  686. for (int i = count; ; )
  687. {
  688. if (--i < 0)
  689. {
  690. // FIXME - should get *read-base* in CommonLisp:
  691. // radix = *read_base*;
  692. radix = 10;
  693. break;
  694. }
  695. if (buffer[start+i] == '.')
  696. {
  697. radix = 10;
  698. break;
  699. }
  700. }
  701. */
  702. }
  703. boolean negative = ch == '-';
  704. boolean numeratorNegative = negative;
  705. boolean sign_seen = ch == '-' || ch == '+';
  706. if (sign_seen)
  707. {
  708. if (pos >= end)
  709. return "no digits following sign";
  710. ch = buffer[pos++];
  711. }
  712. // Special case for '+i' and '-i'.
  713. if ((ch == 'i' || ch == 'I') &&
  714. (pos == end || buffer[pos] == '+' || buffer[pos] == '-') &&
  715. start == pos - 2 && (flags & SCM_COMPLEX) != 0) {
  716. char sign = buffer[start];
  717. if (sign != '+' && sign != '-')
  718. return "no digits";
  719. if (pos < end) {
  720. Object jmag = parseNumber(buffer, pos, end-pos, exactness,
  721. radix, flags);
  722. if (jmag instanceof String)
  723. return jmag;
  724. if (! (jmag instanceof Quaternion))
  725. return "invalid numeric constant ("+jmag+")";
  726. Quaternion qjmag = (Quaternion) jmag;
  727. RealNum re = qjmag.re();
  728. RealNum im = qjmag.im();
  729. if (!(re.isZero() && im.isZero()))
  730. return "invalid numeric constant";
  731. if (exactness == 'i' || exactness == 'I')
  732. return Quaternion.make(0, negative ? -1 : 1,
  733. qjmag.doubleJmagValue(),
  734. qjmag.doubleKmagValue());
  735. return Quaternion.make(IntNum.zero(), negative ?
  736. IntNum.minusOne() : IntNum.one(),
  737. qjmag.jm(), qjmag.km());
  738. }
  739. if (exactness == 'i' || exactness == 'I')
  740. return new DComplex(0, negative ? -1 : 1);
  741. return negative ? Complex.imMinusOne() : Complex.imOne();
  742. }
  743. // Special case for '+j' and '-j'.
  744. if ((ch == 'j' || ch == 'J') &&
  745. (pos == end || buffer[pos] == '+' || buffer[pos] == '-') &&
  746. start == pos - 2 && (flags & SCM_COMPLEX) != 0) {
  747. char sign = buffer[start];
  748. if (sign != '+' && sign != '-')
  749. return "no digits";
  750. if (pos < end) {
  751. Object kmag = parseNumber(buffer, pos, end-pos, exactness,
  752. radix, flags);
  753. if (kmag instanceof String)
  754. return kmag;
  755. if (! (kmag instanceof Quaternion))
  756. return "invalid numeric constant ("+kmag+")";
  757. Quaternion qkmag = (Quaternion) kmag;
  758. RealNum re = qkmag.re();
  759. RealNum im = qkmag.im();
  760. RealNum jm = qkmag.jm();
  761. if (!(re.isZero() && im.isZero() && jm.isZero()))
  762. return "invalid numeric constant";
  763. if (exactness == 'i' || exactness == 'I')
  764. return Quaternion.make(0, 0, negative ? -1 : 1,
  765. qkmag.doubleKmagValue());
  766. return Quaternion.make(IntNum.zero(), IntNum.zero(),
  767. negative ? IntNum.minusOne() : IntNum.one(),
  768. qkmag.km());
  769. }
  770. if (exactness == 'i' || exactness == 'I')
  771. return new DQuaternion(0, 0, 0, negative ? -1 : 1);
  772. return negative ? Quaternion.jmMinusOne() : Quaternion.jmOne();
  773. }
  774. // Special case for '+k' and '-k'.
  775. if ((ch == 'k' || ch == 'K') && pos == end && start == pos - 2
  776. && (flags & SCM_COMPLEX) != 0) {
  777. char sign = buffer[start];
  778. if (sign != '+' && sign != '-')
  779. return "no digits";
  780. if (exactness == 'i' || exactness == 'I')
  781. return new DQuaternion(0, 0, 0, negative ? -1 : 1);
  782. return negative ? Quaternion.kmMinusOne() : Quaternion.kmOne();
  783. }
  784. int realStart = pos - 1;
  785. boolean hash_seen = false;
  786. int exp_seen = -1;
  787. int digits_start = -1;
  788. int decimal_point = -1;
  789. boolean copy_needed = false;
  790. boolean underscore_seen = false;
  791. IntNum numerator = null;
  792. long lvalue = 0;
  793. loop:
  794. for (;;)
  795. {
  796. int digit = Character.digit(ch, radix);
  797. if (digit >= 0)
  798. {
  799. if (hash_seen && decimal_point < 0)
  800. return "digit after '#' in number";
  801. if (digits_start < 0)
  802. digits_start = pos - 1;
  803. lvalue = radix * lvalue + digit;
  804. }
  805. else
  806. {
  807. switch (ch)
  808. {
  809. /*
  810. case '_':
  811. underscore_seen = true;
  812. break;
  813. */
  814. /*
  815. case '#':
  816. if (radix != 10)
  817. return "'#' in non-decimal number";
  818. if (digits_start < 0)
  819. return "'#' with no preceeding digits in number";
  820. hash_seen = true;
  821. break;
  822. */
  823. case '.':
  824. if (decimal_point >= 0)
  825. return "duplicate '.' in number";
  826. if (radix != 10)
  827. return "'.' in non-decimal number";
  828. decimal_point = pos - 1;
  829. break;
  830. case 'e': case 's': case 'f': case 'd': case 'l':
  831. case 'E': case 'S': case 'F': case 'D': case 'L':
  832. if (pos == end || radix != 10)
  833. {
  834. pos--;
  835. break loop;
  836. }
  837. char next = buffer[pos];
  838. int exp_pos = pos-1;
  839. if (next == '+' || next == '-')
  840. {
  841. if (++ pos >= end
  842. || Character.digit(buffer[pos], 10) < 0)
  843. return "missing exponent digits";
  844. }
  845. else if (Character.digit(next, 10) < 0)
  846. {
  847. pos--;
  848. break loop;
  849. }
  850. if (exp_seen >= 0)
  851. return "duplicate exponent";
  852. if (radix != 10)
  853. return "exponent in non-decimal number";
  854. if (digits_start < 0)
  855. return "mantissa with no digits";
  856. exp_seen = exp_pos;
  857. for (;;)
  858. {
  859. pos++;
  860. if (pos >= end || Character.digit(buffer[pos], 10) < 0)
  861. break loop;
  862. }
  863. case '/':
  864. if (numerator != null)
  865. return "multiple fraction symbol '/'";
  866. if (digits_start < 0)
  867. return "no digits before fraction symbol '/'";
  868. if (exp_seen >= 0 || decimal_point >= 0)
  869. return "fraction symbol '/' following exponent or '.'";
  870. numerator = valueOf(buffer, digits_start, pos - digits_start,
  871. radix, negative, lvalue);
  872. digits_start = -1;
  873. lvalue = 0;
  874. negative = false;
  875. hash_seen = false;
  876. underscore_seen = false;
  877. break;
  878. default:
  879. pos--;
  880. break loop;
  881. }
  882. }
  883. if (pos == end)
  884. break;
  885. ch = buffer[pos++];
  886. }
  887. char infnan = '\0';
  888. if (digits_start < 0)
  889. {
  890. if (sign_seen
  891. && pos + 4 < end && buffer[pos+3] == '.' && buffer[pos+4] == '0')
  892. {
  893. char b0 = buffer[pos];
  894. char b1, b2;
  895. if ((b0 == 'i' || b0 == 'I')
  896. && ((b1 = buffer[pos+1]) == 'n' || b1 == 'N')
  897. && ((b2 = buffer[pos+2]) == 'f' || b2 == 'F'))
  898. {
  899. infnan = 'i';
  900. }
  901. else if ((b0 == 'n' || b0 == 'N')
  902. && ((b1 = buffer[pos+1]) == 'a' || b1 == 'A')
  903. && ((b2 = buffer[pos+2]) == 'n' || b2 == 'N'))
  904. {
  905. infnan = 'n';
  906. }
  907. }
  908. if (infnan == '\0')
  909. return "no digits";
  910. pos += 5;
  911. }
  912. if (hash_seen || underscore_seen)
  913. {
  914. // FIXME make copy, removing '_' and replacing '#' by '0'.
  915. }
  916. boolean inexact = (exactness == 'i' || exactness == 'I'
  917. || (exactness == ' ' && hash_seen));
  918. RealNum number = null;
  919. char exp_char = '\0';
  920. if (infnan != '\0')
  921. {
  922. inexact = true;
  923. double d = infnan == 'i' ? Double.POSITIVE_INFINITY : Double.NaN;
  924. number = new DFloNum(negative ? - d : d);
  925. }
  926. else if (exp_seen >= 0 || decimal_point >= 0)
  927. {
  928. if (digits_start > decimal_point && decimal_point >= 0)
  929. digits_start = decimal_point;
  930. if (numerator != null)
  931. return "floating-point number after fraction symbol '/'";
  932. if (exactness == 'e' || exactness == 'E') {
  933. int exp = 0;
  934. IntNum inumber;
  935. if (decimal_point < 0) {
  936. inumber = valueOf(buffer, digits_start,
  937. exp_seen - digits_start,
  938. radix, negative, lvalue);
  939. }
  940. else {
  941. StringBuilder sbuf = new StringBuilder();
  942. if (negative)
  943. sbuf.append('-');
  944. sbuf.append(buffer, digits_start, decimal_point-digits_start);
  945. decimal_point++;
  946. int fracdigits = (exp_seen >= 0 ? exp_seen : pos)
  947. - decimal_point;
  948. sbuf.append(buffer, decimal_point, fracdigits);
  949. inumber = IntNum.valueOf(sbuf.toString());
  950. exp -= fracdigits;
  951. }
  952. if (exp_seen >= 0) {
  953. exp += Integer.parseInt(new String(buffer, exp_seen+1,
  954. pos - (exp_seen+1)));
  955. }
  956. if (exp > 0)
  957. number = IntNum.times(inumber, IntNum.power(IntNum.ten(), exp));
  958. else if (exp < 0)
  959. number = RatNum.make(inumber, IntNum.power(IntNum.ten(), -exp));
  960. else
  961. number = inumber;
  962. } else {
  963. String str = new String(buffer, digits_start, pos - digits_start);
  964. if (exp_seen >= 0) {
  965. exp_char = Character.toLowerCase(buffer[exp_seen]);
  966. if (exp_char != 'e') {
  967. int prefix = exp_seen - digits_start;
  968. str = str.substring(0, prefix)+'e'+str.substring(prefix+1);
  969. }
  970. }
  971. double d = Convert.parseDouble(str);
  972. number = new DFloNum(negative ? - d : d);
  973. }
  974. }
  975. else
  976. {
  977. IntNum iresult = valueOf(buffer, digits_start, pos - digits_start,
  978. radix, negative, lvalue);
  979. if (numerator == null)
  980. number = iresult;
  981. else
  982. {
  983. // Check for zero denominator values: 0/0, n/0, and -n/0
  984. // (i.e. NaN, Infinity, and -Infinity).
  985. if (iresult.isZero ())
  986. {
  987. boolean numeratorZero = numerator.isZero();
  988. if (inexact)
  989. number = new DFloNum ((numeratorZero ? Double.NaN
  990. : numeratorNegative ? Double.NEGATIVE_INFINITY
  991. : Double.POSITIVE_INFINITY));
  992. else if (numeratorZero)
  993. return "0/0 is undefined";
  994. else
  995. number = RatNum.make(numerator, iresult);
  996. }
  997. else
  998. {
  999. number = RatNum.make(numerator, iresult);
  1000. }
  1001. }
  1002. if (inexact && number.isExact())
  1003. // We want #i-0 or #i-0/1 to be -0.0, not 0.0.
  1004. number = new DFloNum(numeratorNegative && number.isZero() ? -0.0
  1005. : number.doubleValue());
  1006. }
  1007. if (exactness == 'e' || exactness == 'E')
  1008. number = number.toExact();
  1009. if (pos < end)
  1010. {
  1011. ch = buffer[pos++];
  1012. if (ch == '@')
  1013. { /* polar notation */
  1014. Object angle = parseNumber(buffer, pos, end - pos,
  1015. exactness, radix, flags|SCM_ANGLE);
  1016. if (angle instanceof String)
  1017. return angle;
  1018. if (! (angle instanceof RealNum) && ! (angle instanceof RealNum[]))
  1019. return "invalid complex polar constant";
  1020. if (angle instanceof RealNum[]) {
  1021. RealNum[] polars = (RealNum[]) angle;
  1022. if (number.isZero() &&
  1023. (!polars[0].isExact() || !polars[1].isExact() ||
  1024. !polars[2].isExact()))
  1025. return new DFloNum(0.0);
  1026. return Quaternion.polar(number, polars[0], polars[1],
  1027. polars[2]);
  1028. }
  1029. RealNum rangle = (RealNum) angle;
  1030. /* r4rs requires 0@1.0 to be inexact zero, even if (make-polar
  1031. * 0 1.0) is exact zero, so check for this case. */
  1032. if (number.isZero () && !rangle.isExact ())
  1033. return new DFloNum (0.0);
  1034. return Complex.polar (number, rangle);
  1035. }
  1036. if (ch == '%') {
  1037. /* extended polar notation */
  1038. Object colatitude = parseNumber(buffer, pos, end - pos,
  1039. exactness, radix,
  1040. flags|SCM_COLATITUDE);
  1041. if (colatitude instanceof String)
  1042. return colatitude;
  1043. if (!(colatitude instanceof RealNum) &&
  1044. !(colatitude instanceof RealNum[]))
  1045. return "invalid quaternion polar constant";
  1046. if ((flags & SCM_ANGLE) == 0) {
  1047. // number%colatitude or number%colatitude&longitude
  1048. RealNum rangle = IntNum.zero();
  1049. RealNum rcolatitude, rlongitude;
  1050. if (colatitude instanceof RealNum) {
  1051. rcolatitude = (RealNum) colatitude;
  1052. rlongitude = IntNum.zero();
  1053. } else {
  1054. RealNum[] polars = (RealNum[]) colatitude;
  1055. rcolatitude = polars[1];
  1056. rlongitude = polars[2];
  1057. }
  1058. /* r4rs requires 0@1.0 to be inexact zero, even if
  1059. (make-polar 0 1.0) is exact zero, so check for this
  1060. case. */
  1061. if (number.isZero() &&
  1062. (!rcolatitude.isExact() || !rlongitude.isExact()))
  1063. return new DFloNum(0.0);
  1064. return Quaternion.polar(number, rangle, rcolatitude,
  1065. rlongitude);
  1066. }
  1067. if (colatitude instanceof RealNum[]) {
  1068. RealNum[] polars = (RealNum[]) colatitude;
  1069. polars[0] = number;
  1070. return polars;
  1071. }
  1072. return new RealNum[] { number, (RealNum)colatitude, IntNum.zero() };
  1073. }
  1074. if (ch == '&') {
  1075. /* extended polar notation */
  1076. Object longitude = parseNumber(buffer, pos, end - pos,
  1077. exactness, radix, flags);
  1078. if (longitude instanceof String)
  1079. return longitude;
  1080. if (! (longitude instanceof RealNum))
  1081. return "invalid quaternion polar constant";
  1082. RealNum rlongitude = (RealNum) longitude;
  1083. if ((flags & (SCM_ANGLE|SCM_COLATITUDE)) == 0) {
  1084. // number&longitude
  1085. /* r4rs requires 0@1.0 to be inexact zero, even if
  1086. (make-polar 0 1.0) is exact zero, so check for this
  1087. case. */
  1088. if (number.isZero() && !rlongitude.isExact())
  1089. return new DFloNum(0.0);
  1090. return Quaternion.polar(number, IntNum.zero(),
  1091. IntNum.zero(), rlongitude);
  1092. }
  1093. if ((flags & SCM_COLATITUDE) != 0)
  1094. return new RealNum[] { IntNum.zero(), number, rlongitude };
  1095. return new RealNum[] { number, IntNum.zero(), rlongitude };
  1096. }
  1097. if (ch == '-' || ch == '+')
  1098. {
  1099. pos--;
  1100. Object imag = parseNumber(buffer, pos, end - pos,
  1101. exactness, radix, flags);
  1102. if (imag instanceof String)
  1103. return imag;
  1104. if (! (imag instanceof Quaternion))
  1105. return "invalid numeric constant ("+imag+")";
  1106. Quaternion cimag = (Quaternion) imag;
  1107. RealNum re = cimag.re();
  1108. if (! re.isZero())
  1109. return "invalid numeric constant";
  1110. return Quaternion.make(number, cimag.im(), cimag.jm(), cimag.km());
  1111. }
  1112. int lcount = 0;
  1113. for (;;)
  1114. {
  1115. if (! Character.isLetter(ch))
  1116. {
  1117. pos--;
  1118. break;
  1119. }
  1120. lcount++;
  1121. if (pos == end)
  1122. break;
  1123. ch = buffer[pos++];
  1124. }
  1125. if (lcount == 1) {
  1126. char prev = buffer[pos-1];
  1127. if (prev == 'i' || prev == 'I') {
  1128. if (pos < end) {
  1129. Object jmag = parseNumber(buffer, pos, end-pos,
  1130. exactness, radix, flags);
  1131. if (jmag instanceof String)
  1132. return jmag;
  1133. if (! (jmag instanceof Quaternion))
  1134. return "invalid numeric constant ("+jmag+")";
  1135. Quaternion qjmag = (Quaternion) jmag;
  1136. RealNum re = qjmag.re();
  1137. RealNum im = qjmag.im();
  1138. if (!(re.isZero() && im.isZero()))
  1139. return "invalid numeric constant";
  1140. return Quaternion.make(IntNum.zero(), number,
  1141. qjmag.jm(), qjmag.km());
  1142. }
  1143. return Complex.make(IntNum.zero(), number);
  1144. }
  1145. if (prev == 'j' || prev == 'J') {
  1146. if (pos < end) {
  1147. Object kmag = parseNumber(buffer, pos, end-pos,
  1148. exactness, radix, flags);
  1149. if (kmag instanceof String)
  1150. return kmag;
  1151. if (! (kmag instanceof Quaternion))
  1152. return "invalid numeric constant ("+kmag+")";
  1153. Quaternion qkmag = (Quaternion) kmag;
  1154. RealNum re = qkmag.re();
  1155. RealNum im = qkmag.im();
  1156. RealNum jm = qkmag.jm();
  1157. if (!(re.isZero() && im.isZero() && jm.isZero()))
  1158. return "invalid numeric constant";
  1159. return Quaternion.make(IntNum.zero(), IntNum.zero(),
  1160. number, qkmag.km());
  1161. }
  1162. return Quaternion.make(IntNum.zero(), IntNum.zero(),
  1163. number, IntNum.zero());
  1164. }
  1165. if (prev == 'k' || prev == 'K') {
  1166. if (pos < end)
  1167. return "junk after imaginary suffix 'k'";
  1168. return Quaternion.make(IntNum.zero (), IntNum.zero(),
  1169. IntNum.zero(), number);
  1170. }
  1171. }
  1172. return "excess junk after number";
  1173. }
  1174. else if (number instanceof DFloNum && exp_char > 0 && exp_char != 'e')
  1175. {
  1176. double d = number.doubleValue();
  1177. switch (exp_char)
  1178. {
  1179. case 'f': case 's':
  1180. return Float.valueOf((float) d);
  1181. case 'd':
  1182. return Double.valueOf(d);
  1183. case 'l':
  1184. if ((flags & SCM_LEXPONENT_IS_BIGDECIMAL) != 0)
  1185. return java.math.BigDecimal.valueOf(d);
  1186. // else fall through
  1187. }
  1188. }
  1189. return number;
  1190. }
  1191. private static IntNum valueOf (char[] buffer, int digits_start,
  1192. int number_of_digits,
  1193. int radix, boolean negative,
  1194. long lvalue)
  1195. {
  1196. // It turns out that if number_of_digits + radix <= 28
  1197. // then the value will fit in a long without overflow,
  1198. // so we can use the value calculated in lvalue.
  1199. if (number_of_digits + radix <= 28)
  1200. return IntNum.make(negative ? - lvalue : lvalue);
  1201. else
  1202. return IntNum.valueOf(buffer, digits_start, number_of_digits,
  1203. radix, negative);
  1204. }
  1205. /** Reads a C-style String escape sequence.
  1206. * Assume '\\' has already been read.
  1207. * Return the converted character, or -1 on EOF, or -2 to ignore. */
  1208. public int readEscape()
  1209. throws java.io.IOException, SyntaxException
  1210. {
  1211. int c = read();
  1212. if (c < 0)
  1213. {
  1214. eofError("unexpected EOF in character literal");
  1215. return -1;
  1216. }
  1217. return readEscape(c);
  1218. }
  1219. public final int readEscape(int c)
  1220. throws java.io.IOException, SyntaxException
  1221. {
  1222. switch ((char) c)
  1223. {
  1224. case 'a': c = 7; break; // alarm/bell
  1225. case 'b': c = 8; break; // backspace
  1226. case 't': c = 9; break; // tab
  1227. case 'n': c = 10; break; // newline
  1228. case 'v': c = 11; break; // vertical tab
  1229. case 'f': c = 12; break; // formfeed
  1230. case 'r': c = 13; break; // carriage return
  1231. case 'e': c = 27; break; // escape
  1232. case '\"': c = 34; break; // quote
  1233. case '|': c = '|'; break; // vertical bar
  1234. case '\\': c = 92; break; // backslash
  1235. case ' ': // Skip to end of line, inclusive.
  1236. case '\n': // Skip initial whitespace on following line.
  1237. case '\r':
  1238. case '\t':
  1239. for (;;)
  1240. {
  1241. if (c < 0)
  1242. {
  1243. eofError("unexpected EOF in literal");
  1244. return -1;
  1245. }
  1246. if (c == '\n')
  1247. break;
  1248. if (c == '\r')
  1249. {
  1250. if (peek() == '\n')
  1251. skip();
  1252. c = '\n';
  1253. break;
  1254. }
  1255. if (c != ' ' && c != '\t')
  1256. {
  1257. unread(c);
  1258. break;
  1259. }
  1260. c = read();
  1261. }
  1262. if (c != '\n')
  1263. break; // ERROR
  1264. // FIXME: if legacy-compatible non-R6RS-mode: return -2;
  1265. for (;;)
  1266. {
  1267. c = read();
  1268. if (c < 0)
  1269. {
  1270. eofError("unexpected EOF in literal");
  1271. return -1;
  1272. }
  1273. if (c != ' ' && c != '\t')
  1274. {
  1275. unread(c);
  1276. return -2;
  1277. }
  1278. }
  1279. case 'M':
  1280. c = read();
  1281. if (c != '-')
  1282. {
  1283. error("Invalid escape character syntax");
  1284. return '?';
  1285. }
  1286. c = read();
  1287. if (c == '\\')
  1288. c = readEscape();
  1289. return c | 0200;
  1290. case 'C':
  1291. c = read();
  1292. if (c != '-')
  1293. {
  1294. error("Invalid escape character syntax");
  1295. return '?';
  1296. }
  1297. /* ... fall through ... */
  1298. case '^':
  1299. c = read();
  1300. if (c == '\\')
  1301. c = readEscape();
  1302. if (c == '?')
  1303. return 0177;
  1304. return c & (0200 | 037);
  1305. case '0':
  1306. case '1':
  1307. case '2':
  1308. case '3':
  1309. case '4':
  1310. case '5':
  1311. case '6':
  1312. case '7':
  1313. /* An octal escape, as in ANSI C. */
  1314. c = c - '0';
  1315. for (int count = 0; ++count < 3; )
  1316. {
  1317. int d = read();
  1318. int v = Character.digit((char) d, 8);
  1319. if (v >= 0)
  1320. c = (c << 3) + v;
  1321. else
  1322. {
  1323. if (d >= 0)
  1324. unread(d);
  1325. break;
  1326. }
  1327. }
  1328. break;
  1329. case 'u':
  1330. c = 0;
  1331. for (int i = 4; --i >= 0; )
  1332. {
  1333. int d = read ();
  1334. if (d < 0)
  1335. eofError("premature EOF in \\u escape");
  1336. int v = Character.digit ((char) d, 16);
  1337. if (v < 0)
  1338. error("non-hex character following \\u");
  1339. c = 16 * c + v;
  1340. }
  1341. break;
  1342. case 'x':
  1343. case 'X':
  1344. return readHexEscape();
  1345. default: break;
  1346. }
  1347. return c;
  1348. }
  1349. public int readHexEscape ()
  1350. throws java.io.IOException, SyntaxException
  1351. {
  1352. int c = 0;
  1353. /* A hex escape, as in ANSI C. */
  1354. for (;;)
  1355. {
  1356. int d = read();
  1357. int v = Character.digit((char) d, 16);
  1358. if (v >= 0)
  1359. c = (c << 4) + v;
  1360. else
  1361. {
  1362. if (d != ';')
  1363. {
  1364. // FIXME: if strict-R6RS: ERROR
  1365. if (d >= 0)
  1366. unread(d);
  1367. }
  1368. break;
  1369. }
  1370. }
  1371. return c;
  1372. }
  1373. public final Object readObject (int c)
  1374. throws java.io.IOException, SyntaxException
  1375. {
  1376. unread(c);
  1377. return readObject();
  1378. }
  1379. /** Read a "command" - a top-level expression or declaration.
  1380. * Return Sequence.eofValue at end of file. */
  1381. public Object readCommand ()
  1382. throws java.io.IOException, SyntaxException
  1383. {
  1384. return readObject(-1, true);
  1385. }
  1386. protected Object makeNil ()
  1387. {
  1388. return LList.Empty;
  1389. }
  1390. protected Pair makePair (Object car, int line, int column)
  1391. {
  1392. return makePair(car, LList.Empty, line, column);
  1393. }
  1394. protected Pair makePair(Object car, int startline, int startcolumn,
  1395. int endline, int endcolumn) {
  1396. String pname = port.getName();
  1397. Object cdr = LList.Empty;
  1398. if (! returnMutablePairs && pname != null && startline >= 0) {
  1399. long position = SourceMapper.simpleEncode(startline+1, startcolumn+1,
  1400. endline+1, endcolumn+1);
  1401. return PairWithPosition.make(car, cdr, pname, position);
  1402. } else
  1403. return Pair.make(car, cdr);
  1404. }
  1405. protected Pair makePair (Object car, Object cdr, int line, int column)
  1406. {
  1407. String pname = port.getName();
  1408. if (! returnMutablePairs && pname != null && line >= 0)
  1409. return PairWithPosition.make(car, cdr,
  1410. pname, line + 1, column + 1);
  1411. else
  1412. return Pair.make(car, cdr);
  1413. }
  1414. protected Pair makePair2 (Object car, Object cadr, Object cddr,
  1415. int line, int column) {
  1416. return makePair(car, makePair(cadr, cddr, line, column), line, column);
  1417. }
  1418. protected void setCar (Object pair, Object car, int endline, int endcolumn)
  1419. {
  1420. ((Pair) pair).setCarBackdoor(car);
  1421. if (pair instanceof PairWithPosition)
  1422. ((PairWithPosition) pair).setEndLine(endline, endcolumn);
  1423. }
  1424. protected void setCar (Object pair, Object car)
  1425. {
  1426. ((Pair) pair).setCarBackdoor(car);
  1427. }
  1428. protected void setCdr (Object pair, Object cdr)
  1429. {
  1430. ((Pair) pair).setCdrBackdoor(cdr);
  1431. }
  1432. /** Read a number from a LispReader
  1433. * @param previous number of characters already pushed on tokenBuffer
  1434. * @param reader LispReader to read from
  1435. * @param radix base to use or -1 if unspecified
  1436. */
  1437. public static Object readNumberWithRadix(int previous, LispReader reader, int radix)
  1438. throws java.io.IOException, SyntaxException
  1439. {
  1440. int startPos = reader.tokenBufferLength - previous;
  1441. ReadTable rtable = ReadTable.getCurrent();
  1442. for (;;) {
  1443. reader.readToken(reader.read(), rtable);
  1444. // '#' is a terminating-macro character so we have to add it "manually"
  1445. int ch = reader.peek();
  1446. if (ch != '#')
  1447. break;
  1448. reader.tokenBufferAppend(ch);
  1449. reader.skip();
  1450. }
  1451. int endPos = reader.tokenBufferLength;
  1452. if (startPos == endPos)
  1453. {
  1454. reader.error("missing numeric token");
  1455. return IntNum.zero();
  1456. }
  1457. Object result = LispReader.parseNumber(reader.tokenBuffer, startPos,
  1458. endPos - startPos, '\0', radix, 0);
  1459. if (result instanceof String)
  1460. {
  1461. reader.error((String) result);
  1462. return IntNum.zero();
  1463. }
  1464. else if (result == null)
  1465. {
  1466. reader.error("invalid numeric constant");
  1467. return IntNum.zero();
  1468. }
  1469. else
  1470. return result;
  1471. }
  1472. public static Object readCharacter (LispReader reader)
  1473. throws java.io.IOException, SyntaxException
  1474. {
  1475. int ch = reader.read();
  1476. if (ch < 0)
  1477. reader.eofError("unexpected EOF in character literal");
  1478. int startPos = reader.tokenBufferLength;
  1479. reader.tokenBufferAppend(ch);
  1480. reader.readToken(reader.read(), ReadTable.getCurrent());
  1481. char[] tokenBuffer = reader.tokenBuffer;
  1482. int length = reader.tokenBufferLength - startPos;
  1483. if (length == 1 || length == 2) {
  1484. ch = Character.codePointAt(tokenBuffer, startPos,
  1485. reader.tokenBufferLength);
  1486. if (ch > 0xFFFF || length == 1)
  1487. return Char.make(ch);
  1488. }
  1489. String name = new String(tokenBuffer, startPos, length);
  1490. ch = Char.nameToChar(name);
  1491. if (ch >= 0)
  1492. return Char.make(ch);
  1493. ch = tokenBuffer[startPos];
  1494. if (ch == 'x' || ch == 'X')
  1495. {
  1496. int value = 0;
  1497. for (int i = 1; ; i++)
  1498. {
  1499. if (i == length)
  1500. return Char.make(value);
  1501. int v = Character.digit (tokenBuffer[startPos + i], 16);
  1502. if (v < 0)
  1503. break;
  1504. value = 16 * value + v;
  1505. if (value > 0x10FFFF) {
  1506. reader.error("character scalar value greater than #x10FFFF");
  1507. return Char.make('?');
  1508. }
  1509. }
  1510. }
  1511. // FIXME remove - only used for BRL Perhaps a deprecation warning?
  1512. ch = Character.digit(ch, 8);
  1513. if (ch >= 0)
  1514. {
  1515. int value = ch;
  1516. for (int i = 1; ; i++)
  1517. {
  1518. if (i == length)
  1519. return Char.make(value);
  1520. ch = Character.digit(tokenBuffer[startPos + i], 8);
  1521. if (ch < 0)
  1522. break;
  1523. value = 8 * value + ch;
  1524. }
  1525. }
  1526. reader.error("unknown character name: " + name);
  1527. return Char.make('?');
  1528. }
  1529. public static Object readSpecial (LispReader reader)
  1530. throws java.io.IOException, SyntaxException
  1531. {
  1532. int ch = reader.read();
  1533. if (ch < 0)
  1534. reader.eofError("unexpected EOF in #! special form");
  1535. /* Handle Unix #!PROGRAM line at start of file. */
  1536. if ((ch == '/' || ch == ' ')
  1537. && reader.getLineNumber() == 0
  1538. && reader.getColumnNumber() == 3)
  1539. {
  1540. String filename = reader.getName();
  1541. if (filename != null
  1542. && ApplicationMainSupport.commandName.get(null) == null)
  1543. {
  1544. ApplicationMainSupport.commandName.set(filename);
  1545. }
  1546. boolean sawBackslash = false;
  1547. for (;;)
  1548. {
  1549. ch = reader.read();
  1550. if (ch < 0)
  1551. break;
  1552. if (ch == '\\')
  1553. sawBackslash = true;
  1554. else if (ch == '\n' || ch == '\r')
  1555. {
  1556. if (! sawBackslash)
  1557. break;
  1558. sawBackslash = false;
  1559. }
  1560. else if (sawBackslash && ch != ' ' && ch != '\t')
  1561. sawBackslash = false;
  1562. }
  1563. return Values.empty;
  1564. }
  1565. String name = reader.readTokenString(ch, ReadTable.getCurrent());
  1566. if (name.equals("optional"))
  1567. return Special.optional;
  1568. if (name.equals("rest"))
  1569. return Special.rest;
  1570. if (name.equals("key"))
  1571. return Special.key;
  1572. if (name.equals("eof"))
  1573. return Special.eof;
  1574. if (name.equals("void"))
  1575. //return Values.empty;
  1576. return QuoteExp.voidExp;
  1577. if (name.equals("default"))
  1578. return Special.dfault;
  1579. if (name.equals("undefined"))
  1580. return Special.undefined;
  1581. if (name.equals("abstract"))
  1582. return Special.abstractSpecial;
  1583. if (name.equals("native"))
  1584. return Special.nativeSpecial;
  1585. if (name.equals("if"))
  1586. return Special.ifk;
  1587. if (name.equals("null"))
  1588. return null;
  1589. if (name.equals("fold-case"))
  1590. {
  1591. reader.readCase = 'D';
  1592. return Values.empty;
  1593. }
  1594. if (name.equals("no-fold-case"))
  1595. {
  1596. reader.readCase = 'P';
  1597. return Values.empty;
  1598. }
  1599. reader.error("unknown named constant #!"+name);
  1600. return null;
  1601. }
  1602. public static Object readGeneralArray(LispReader in, int rank,
  1603. PrimType elementType)
  1604. throws java.io.IOException, SyntaxException {
  1605. if (rank == -1)
  1606. rank = 1;
  1607. int[] dimensions = new int[rank];
  1608. int[] lowBounds = null;
  1609. boolean error = false;
  1610. int ch = in.read();
  1611. boolean baddim = false;
  1612. int explicitDims = 0;
  1613. if (ch == '@' || ch == ':') {
  1614. for (int r = 0; r < rank; r++) {
  1615. if (ch == '@') {
  1616. ch = in.read();
  1617. boolean neg = ch == '-';
  1618. if (! neg)
  1619. in.unread(ch);
  1620. int low = in.readIntDigits();
  1621. if (low < 0) {
  1622. in.error("expected low-bound after '@'");
  1623. low = 0;
  1624. }
  1625. if (lowBounds == null)
  1626. lowBounds = new int[rank];
  1627. lowBounds[r] = neg ? - low : low;
  1628. ch = in.read();
  1629. if (ch != ':' && r == rank-1)
  1630. break;
  1631. }
  1632. if (ch == ':') {
  1633. explicitDims++;
  1634. int dim = in.readIntDigits();
  1635. if (dim < 0) {
  1636. in.error("expected dimension after ':'");
  1637. error = true;
  1638. }
  1639. dimensions[r] = dim;
  1640. ch = in.read();
  1641. } else if (ch != '@') {
  1642. in.error("missing bounds-specifier (seen "+r
  1643. +" of "+rank+")");
  1644. error = true;
  1645. }
  1646. }
  1647. }
  1648. if (ch == '@' || ch == ':') {
  1649. in.error("too many bounds-specifiers for rank-"
  1650. +rank+" array");
  1651. error = true;
  1652. }
  1653. while (ch >= 0 && Character.isWhitespace(ch))
  1654. ch = in.read();
  1655. SourceLocator sloc =
  1656. PairWithPosition.make(null, null, in.getName(),
  1657. in.getLineNumber()+1, in.getColumnNumber());
  1658. in.unread(ch);
  1659. Object data = in.readObject();
  1660. if (explicitDims == 0) {
  1661. if (! dimensionsFromNested(0, dimensions, data)) {
  1662. in.error("array value is not a nested true list");
  1663. error = true;
  1664. }
  1665. } else if (explicitDims < rank) {
  1666. in.error("only "+explicitDims+" array lengths specified - must be all "+rank+" or none");
  1667. error = true;
  1668. }
  1669. if (error)
  1670. return LList.Empty;
  1671. int size = 1;
  1672. for (int d = dimensions.length; -- d >= 0; )
  1673. size *= dimensions[d];
  1674. Object buffer = elementType == null ? new Object[size]
  1675. : Array.newInstance(elementType.getReflectClass(), size);
  1676. SourceMessages messages = in.getMessages();
  1677. fromNested(buffer, 0, 0, dimensions, data, elementType, sloc, messages);
  1678. return Arrays.makeFromSimple(dimensions, lowBounds,
  1679. buffer, elementType);
  1680. }
  1681. static boolean
  1682. dimensionsFromNested(int dim, int[] dimensions, Object data) {
  1683. int rank = dimensions.length;
  1684. if (dim == rank)
  1685. return true;
  1686. List seq = Sequences.asSequenceOrNull(data);
  1687. if (seq == null)
  1688. return false;
  1689. int len;
  1690. if (seq instanceof Pair)
  1691. len = LList.listLength(seq, false);
  1692. else
  1693. len = seq.size();
  1694. if (len < 0)
  1695. return false;
  1696. if (len > dimensions[dim])
  1697. dimensions[dim] = len;
  1698. for (Object el : seq) {
  1699. if (! dimensionsFromNested(dim+1, dimensions, el))
  1700. return false;
  1701. }
  1702. return true;
  1703. }
  1704. static void fromNested(Object buffer, int index, int dim, int[] dimensions, Object value, PrimType elementType, SourceLocator sloc, SourceMessages messages) {
  1705. int rank = dimensions.length;
  1706. if (dim==rank) {
  1707. char sig1 = elementType == null ? 'L'
  1708. : elementType.getSignature().charAt(0);
  1709. if (sig1 == 'B' || sig1 == 'S' || sig1 == 'I' || sig1 == 'J') {
  1710. String msg = null;
  1711. if (! (value instanceof IntNum))
  1712. msg = "expected integer value";
  1713. else {
  1714. Object nvalue = LangPrimType.convertIntegerLiteral((IntNum) value, elementType, true);
  1715. if (nvalue == null)
  1716. msg = "integer "+value+" not in range of "+elementType.getName();
  1717. else
  1718. value = nvalue;
  1719. }
  1720. if (msg != null) {
  1721. messages.error('e', sloc, msg);
  1722. value = LangPrimType.convertIntegerLiteral(IntNum.zero(),
  1723. elementType, true);
  1724. }
  1725. }
  1726. if (sig1 == 'F' || sig1 == 'D') {
  1727. RealNum rvalue = RealNum.asRealNumOrNull(value);
  1728. if (rvalue != null) {
  1729. if (sig1 == 'F')
  1730. value = Float.valueOf(rvalue.floatValue());
  1731. else
  1732. value = Double.valueOf(rvalue.doubleValue());
  1733. } else {
  1734. messages.error('e', sloc, "expected real value");
  1735. }
  1736. }
  1737. Array.set(buffer, index, value);
  1738. } else {
  1739. dim++;
  1740. int stride = 1;
  1741. for (int i = dim; i < rank; i++)
  1742. stride *= dimensions[i];
  1743. while (value instanceof Pair) {
  1744. Pair pair = (Pair) value;
  1745. if (pair instanceof SourceLocator)
  1746. sloc = (SourceLocator) pair;
  1747. fromNested(buffer, index, dim, dimensions,
  1748. pair.getCar(), elementType, sloc, messages);
  1749. value = pair.getCdr();
  1750. index += stride;
  1751. }
  1752. for (Object el : Sequences.coerceToSequence(value)) {
  1753. fromNested(buffer, index, dim, dimensions,
  1754. el, elementType, sloc, messages);
  1755. index += stride;
  1756. }
  1757. }
  1758. }
  1759. boolean deprecatedXmlEnlosedReported;
  1760. }