Q2Read.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. package gnu.q2.lang;
  2. import gnu.kawa.lispexpr.*;
  3. import gnu.expr.*;
  4. import gnu.text.*;
  5. import gnu.mapping.*;
  6. import gnu.lists.*;
  7. import gnu.expr.Keyword;
  8. import gnu.kawa.io.InPort;
  9. import gnu.kawa.xml.MakeAttribute;
  10. import kawa.lang.*;
  11. /** A class to read Scheme forms (S-expressions). */
  12. public class Q2Read extends LispReader
  13. {
  14. public static Symbol wordSym = Symbol.valueOf("$word$");
  15. void init()
  16. {
  17. ((InPort) port).readState = ' ';
  18. }
  19. public Q2Read(InPort port)
  20. {
  21. super(port);
  22. init();
  23. }
  24. public Q2Read(InPort port, SourceMessages messages)
  25. {
  26. super(port, messages);
  27. init();
  28. }
  29. /** Skip initial tabs and spaces.
  30. * @return indentation, encoded as @{code (numberOfTabs<<16)+numberOfSpaces}.
  31. */
  32. int skipIndentation ()
  33. throws java.io.IOException, SyntaxException
  34. {
  35. int numTabs = 0, numSpaces = 0;
  36. int ch = port.read();
  37. while (ch == '\t')
  38. {
  39. numTabs++;
  40. ch = port.read();
  41. }
  42. while (ch == ' ')
  43. {
  44. numSpaces++;
  45. ch = port.read();
  46. }
  47. if (ch < 0)
  48. return -1;
  49. port.unread();
  50. return (numTabs << 16) + numSpaces;
  51. }
  52. int curIndentation;
  53. boolean resetNeeded;
  54. /** Read a "command".
  55. * Assume curIndentation has been set.
  56. * After return, the read position is before the first non-WS character
  57. * of the next command (on the next line); curIndentation has been
  58. * updated to that of the initial whitespace of that line; and a
  59. * mark() has been set at the start of the line.
  60. * Exception: If singleLine, returned position is *before* newline,
  61. * and mark is not set.
  62. */
  63. Object readIndentCommand (boolean singleLine)
  64. throws java.io.IOException, SyntaxException
  65. {
  66. int startIndentation = curIndentation;
  67. Pair head = new Pair(null, LList.Empty);
  68. Pair last = head;
  69. Object obj = LList.Empty;
  70. PairWithPosition pair = null;
  71. Object prev = null;
  72. ReadTable rtable = ReadTable.getCurrent();
  73. for (;;)
  74. {
  75. int ch = read();
  76. if (ch < 0)
  77. break;
  78. if (ch == ' ' || ch == '\t')
  79. continue;
  80. unread();
  81. if (ch == ')')
  82. break;
  83. if (ch == '\r' || ch == '\n')
  84. {
  85. Operator rhsNeeded = null;
  86. if (singleLine)
  87. {
  88. prev = last.getCar();
  89. Q2Translator tr = (Q2Translator) Compilation.getCurrent();
  90. Operator op = tr.checkIfOperator(prev);
  91. if (op != null && (op.flags & Operator.RHS_NEEDED) != 0)
  92. rhsNeeded = op;
  93. else
  94. break;
  95. }
  96. ch = read(); // re-read newline
  97. port.mark(Integer.MAX_VALUE);
  98. resetNeeded = true;
  99. int subIndentation = skipIndentation(); // skipHorSpace.
  100. if (subIndentation == -1 && rhsNeeded != null)
  101. eofError("missing right operand after "+rhsNeeded.getName());
  102. LList qresult = LList.Empty;
  103. curIndentation = subIndentation;
  104. for (;;)
  105. {
  106. if (curIndentation == -1)
  107. break;
  108. if (subIndentation != curIndentation)
  109. {
  110. break;
  111. }
  112. int comparedIndent = Q2.compareIndentation(subIndentation, startIndentation);
  113. if (comparedIndent == Integer.MIN_VALUE)
  114. {
  115. error('e', "cannot compare indentation - mix of tabs and spaces");
  116. break;
  117. }
  118. if (comparedIndent == -1 || comparedIndent == 1)
  119. {
  120. error('e', "indentation must differ by 2 or more");
  121. }
  122. else if (comparedIndent <= 0)
  123. {
  124. // reset to start of line FIXME
  125. break;
  126. }
  127. // comparedIndent >= 2
  128. int line = port.getLineNumber();
  129. int column = port.getColumnNumber();
  130. Object val = readIndentCommand(false);
  131. if (val == LList.Empty)
  132. break;
  133. qresult = makePair(val, qresult, line, column);
  134. }
  135. if (qresult != LList.Empty)
  136. {
  137. qresult = new Pair(kawa.standard.begin.begin,
  138. LList.reverseInPlace(qresult));
  139. Pair t = new Pair(qresult, LList.Empty);
  140. last.setCdrBackdoor(t);
  141. last = t;
  142. }
  143. prev = qresult;
  144. break;
  145. }
  146. int line = port.getLineNumber();
  147. int column = port.getColumnNumber();
  148. ch = port.read();
  149. if (ch < 0)
  150. break;
  151. last = readValuesAndAppend(ch, rtable, last);
  152. }
  153. return makeCommand(head.getCdr());
  154. }
  155. private boolean isSubWordStart(int ch, ReadTable rtable) {
  156. if (ch == '{' || ch == '[' || ch == '(')
  157. return true;
  158. int kind = rtable.lookup(ch).getKind();
  159. return kind != ReadTable.TERMINATING_MACRO
  160. && kind != ReadTable.WHITESPACE
  161. && kind != ReadTable.ILLEGAL;
  162. }
  163. public Pair readValuesAndAppend(int ch, ReadTable rtable, Pair last)
  164. throws java.io.IOException, SyntaxException {
  165. int line = port.getLineNumber();
  166. int column = port.getColumnNumber() - 1; // Adjust for ch
  167. Pair next = super.readValuesAndAppend(ch, rtable, last);
  168. ch = port.peek();
  169. if (isSubWordStart(ch, rtable)) {
  170. Pair head = makePair(wordSym, line, column);
  171. setCdr(head, next);
  172. Pair first = next;
  173. Pair last2 = next;
  174. for (;;) {
  175. last2 = super.readValuesAndAppend(port.read(), rtable, last2);
  176. ch = port.peek();
  177. if (! isSubWordStart(ch, rtable))
  178. break;
  179. }
  180. if (first.getCar() instanceof SimpleSymbol) {
  181. Symbol op = LispLanguage.constructNamespace.getSymbol(first.getCar().toString());
  182. head = makePair(op, line, column);
  183. setCdr(head, first.getCdr());
  184. }
  185. next = makePair(head, line, column);
  186. setCdr(last, next);
  187. }
  188. return next;
  189. }
  190. Object makeCommand (Object command)
  191. {
  192. return command;
  193. }
  194. boolean singleLine()
  195. {
  196. return isInteractive() && nesting <= 1;
  197. }
  198. public Object readCommand ()
  199. throws java.io.IOException, SyntaxException
  200. {
  201. int indent = skipIndentation();
  202. if (indent < 0)
  203. return Sequence.eofValue;
  204. curIndentation = indent;
  205. char saveReadState = pushNesting('-');
  206. try
  207. {
  208. Object result = readIndentCommand(singleLine());
  209. if (resetNeeded)
  210. {
  211. resetNeeded = false;
  212. int line = port.getLineNumber();
  213. int column = port.getColumnNumber();
  214. port.reset();
  215. }
  216. if (result instanceof Pair)
  217. {
  218. Pair presult = (Pair) result;
  219. if (presult.getCdr() == LList.Empty
  220. && presult.getCar() == Special.eof)
  221. return Special.eof;
  222. }
  223. return result;
  224. }
  225. finally
  226. {
  227. popNesting(saveReadState);
  228. }
  229. }
  230. @Override
  231. protected boolean isTerminatingChar(int ch, ReadTable rtable)
  232. throws java.io.IOException, SyntaxException
  233. {
  234. return ch == '^' || super.isTerminatingChar(ch, rtable);
  235. }
  236. @Override
  237. protected Object handlePostfix(Object value, ReadTable rtable,
  238. int line, int column)
  239. throws java.io.IOException, SyntaxException {
  240. if (port.peek() == '^') {
  241. port.read();
  242. int rline = port.getLineNumber();
  243. int rcolumn = port.getColumnNumber();
  244. int ch = port.read();
  245. LList r;
  246. if (ch < 0 || ch == ']' || ch == ')' || ch == '}' ||
  247. Character.isWhitespace(ch)) {
  248. unread(ch);
  249. r = LList.Empty;
  250. } else {
  251. Object rightOperand
  252. = readValues(ch, rtable.lookup(ch), rtable, -1);
  253. r = makePair(rightOperand, rline, rcolumn,
  254. port.getLineNumber(), port.getColumnNumber());
  255. }
  256. r = Pair.make(value, r);
  257. return Pair.make(Q2.defineSym, r);
  258. }
  259. return super.handlePostfix(value, rtable, line, column);
  260. }
  261. // RULE: Every newline (not preceded by backslash)
  262. // is equivalent to ';'
  263. // RULE: If a line is followed by one or more lines that are
  264. // indented more, add a '(BEGIN' at the end of this line, and
  265. // and a ')' at the end of the last line indented more.
  266. // RULE: Forms separate ';' make a "block": Each form is
  267. // either a declaration (visible throughout the block);
  268. // or an expression. Value of block is concatenation of
  269. // values of expressions (producting multiple values).
  270. //
  271. /* if parens:
  272. x + (a b
  273. c d
  274. e f) + y
  275. == x + (a b (c d (e f))) + y [OLD]
  276. == x + (a b (c d (; e f))) + y OR[*]
  277. == x + (a b; c d (; e f)) + y
  278. [*] New RULE[?]: Indentation relative to most recent lparen
  279. What about:
  280. x + (a b
  281. c d
  282. e f) + y
  283. == x + (a b (; c d (; e f))) + y OR
  284. ERROR
  285. */
  286. /*
  287. a b (d e
  288. f g h)
  289. */
  290. /*
  291. a b c
  292. d e
  293. */
  294. /* <body>
  295. [%x%]
  296. a b c
  297. e f g
  298. h i
  299. j k
  300. == [%x%] (CONCAT (a b c) (e f g (h i)) (j k))
  301. == [%x%] (; a b c; e f g (; h i); j k)
  302. [%x%] a b c
  303. e f g
  304. == ???
  305. [%x%] a b c (e f g)
  306. OR:
  307. [%x%] (CONCAT (a b c) (e f g))
  308. == [%x%] a b c (; e f g)
  309. f a b c
  310. d e
  311. == f a (b c) (d e) [probably not]
  312. == f a b c (; d e)
  313. if e1
  314. then
  315. a b
  316. c d
  317. else
  318. e f
  319. g h
  320. ==
  321. if e1 then (CONCAT (a b) (c d)) else (CONCAT (e f) (g h))
  322. f
  323. a b
  324. c d
  325. == f (CONCAT (a b) (c d))
  326. OR f (a b) (c d)
  327. == f (; a b; c d)
  328. DEPENDING ON f
  329. Even if former, what about explicit:
  330. f (a b) (c d)
  331. Same?
  332. Depends on whether f takes a <body> or an <arguments>
  333. */
  334. /*
  335. public Object readCommand (boolean forceList)
  336. throws java.io.IOException, SyntaxException
  337. {
  338. int line = port.getLineNumber();
  339. int startColumn = port.getColumnNumber();
  340. int lastColumn = startColumn;
  341. Object obj = LList.Empty;
  342. PairWithPosition pair = null, last = null;
  343. for (;;)
  344. {
  345. int ch = read();
  346. if (ch < 0)
  347. break;
  348. if (ch == ' ' || ch == '\t')
  349. continue;
  350. unread();
  351. if (ch == ')')
  352. break;
  353. line = port.getLineNumber();
  354. int column = port.getColumnNumber();
  355. while (ch == '\r' || ch == '\n')
  356. {
  357. if (singleLine())
  358. return obj;
  359. ch = read();
  360. skipIndentation(); // skipHorSpace.
  361. column = port.getColumnNumber();
  362. ch = peek();
  363. if (column <= startColumn)
  364. break;
  365. }
  366. if (column <= startColumn && last != null)
  367. break;
  368. Object next;
  369. if (column == lastColumn && last != null)
  370. next = readCommand();
  371. else if (column < lastColumn && last != null)
  372. {
  373. PairWithPosition p = pair;
  374. for (;;)
  375. {
  376. Object n = p.getCdr();
  377. if (n == LList.Empty)
  378. break;
  379. PairWithPosition np = (PairWithPosition) n;
  380. int pColumn = np.getColumnNumber()-1;
  381. if (pColumn >= column)
  382. {
  383. if (pColumn > column)
  384. error('e', "some tokens on previous line indented more than current line");
  385. n = np.getCdr();
  386. if (n != LList.Empty)
  387. {
  388. if (((PairWithPosition) n).getColumnNumber()-1==column)
  389. {
  390. p = (PairWithPosition) n;
  391. continue;
  392. }
  393. last = (PairWithPosition)
  394. makePair(np, port.getLineNumber(), column);
  395. p.setCdrBackdoor(last);
  396. }
  397. break;
  398. }
  399. p = np;
  400. }
  401. next = readCommand();
  402. }
  403. else
  404. next = readObject();
  405. if (next == Sequence.eofValue)
  406. break;
  407. lastColumn = column;
  408. String filename = port.getName();
  409. PairWithPosition cur = PairWithPosition.make(next, LList.Empty,
  410. filename, line+1, column+1);
  411. if (last == null)
  412. {
  413. pair = cur;
  414. obj = cur;
  415. }
  416. else if (last.getCar() instanceof Keyword)
  417. {
  418. Object name = new QuoteExp(((Keyword) last.getCar()).getName());
  419. last.setCar(new PairWithPosition(last, MakeAttribute.makeAttribute,
  420. new PairWithPosition(last, name, cur)));
  421. continue;
  422. }
  423. else
  424. last.setCdrBackdoor(cur);
  425. last = cur;
  426. }
  427. if (! forceList)
  428. {
  429. if (obj == last)
  430. obj = last.getCar();
  431. else if (last == null)
  432. obj = QuoteExp.voidExp;
  433. }
  434. return obj;
  435. }
  436. */
  437. public static Object readObject(InPort port)
  438. throws java.io.IOException, SyntaxException
  439. {
  440. return (new Q2Read(port)).readObject();
  441. }
  442. /** Record '[' location for error messages. */
  443. String expressionStartFile;
  444. int expressionStartLine;
  445. int expressionStartColumn;
  446. void saveExpressionStartPosition()
  447. {
  448. expressionStartFile = port.getName();
  449. expressionStartLine = port.getLineNumber();
  450. expressionStartColumn = port.getColumnNumber();
  451. }
  452. public static final ReaderExtendedLiteral braces
  453. = new ReaderExtendedLiteral('\\');
  454. static class ReadTableEntry extends ReaderDispatchMisc
  455. {
  456. public Object read (Lexer in, int ch, int count)
  457. throws java.io.IOException, SyntaxException
  458. {
  459. switch (ch)
  460. {
  461. case '(': return readParens(in);
  462. case ';': return Symbol.valueOf(";");
  463. case '{':
  464. int startLine = in.getLineNumber() + 1;
  465. int startColumn = in.getColumnNumber() - 2;
  466. ReadTable rtable = ReadTable.getCurrent();
  467. return braces.readNamedLiteral((LispReader) in, rtable,
  468. null, '{', startLine, startColumn);
  469. case '|':
  470. in.error("unexpected '|'");
  471. return Values.empty;
  472. default:
  473. throw new Error();
  474. }
  475. }
  476. public Object readParens (Lexer in)
  477. throws java.io.IOException, SyntaxException
  478. {
  479. Q2Read reader = (Q2Read) in;
  480. char saveReadState = reader.pushNesting('(');
  481. int startLine = reader.getLineNumber();
  482. //set('(', ReaderParens.getInstance('(', ')'));
  483. int startColumn = reader.getColumnNumber();
  484. InPort port = reader.getPort();
  485. boolean lambdaParamList = false;
  486. if (port.peek() == '|') {
  487. lambdaParamList = true;
  488. port.skip();
  489. ReadTable rtable = ReadTable.getCurrent();
  490. Pair head = new Pair(null, LList.Empty);
  491. Pair last = head;
  492. for (;;) {
  493. int ch = reader.read();
  494. if (ch < 0)
  495. reader.eofError("unexpected EOF in vector starting here",
  496. startLine + 1, startColumn);
  497. if (ch == '|' && reader.peek() == ')') {
  498. port.skip();
  499. break;
  500. }
  501. if (rtable.lookup(ch).getKind() == ReadTable.WHITESPACE)
  502. continue;
  503. last = reader.readValuesAndAppend(ch, rtable, last);
  504. }
  505. return Operator.makeLambda(head.getCdr());
  506. }
  507. try
  508. {
  509. Object result = reader.readIndentCommand(false);
  510. int ch = port.peek();
  511. if (ch == ')')
  512. port.skip();
  513. else {
  514. String msg = "missing ')' after '(' starting here";
  515. if (ch < 0)
  516. reader.eofError(msg, startLine + 1, startColumn);
  517. else
  518. reader.error('e', port.getName(), startLine + 1, startColumn,
  519. msg);
  520. }
  521. if (reader.resetNeeded)
  522. {
  523. reader.resetNeeded = false;
  524. port.mark(0);
  525. }
  526. return reader.makeCommand(result);
  527. }
  528. finally
  529. {
  530. reader.popNesting(saveReadState);
  531. }
  532. }
  533. }
  534. }