12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838 |
- // Copyright (c) 2000, 2016 Per M.A. Bothner.
- // This is free software; for terms and warranty disclaimer see COPYING.
- package gnu.kawa.lispexpr;
- import gnu.text.*;
- import gnu.mapping.*;
- import gnu.lists.*;
- import gnu.math.*;
- import gnu.expr.*;
- import gnu.kawa.io.BinaryInPort;
- import gnu.kawa.io.InPort;
- import gnu.kawa.functions.Arrays;
- import gnu.kawa.util.GeneralHashTable;
- import gnu.bytecode.PrimType;
- import gnu.bytecode.Type;
- import java.util.List;
- import java.util.regex.*;
- import java.lang.reflect.Array;
- /** A Lexer for reading S-expressions in generic Lisp-like syntax.
- * This class may have outlived its usefulness: It's mostly just a
- * wrapper around an InPort plus a helper token-buffer.
- * The functionality should be moved to ReadTable, though it is
- * unclear what to do about the tokenBuffer.
- */
- public class LispReader extends Lexer
- {
- public LispReader(InPort port)
- {
- super(port);
- }
- public LispReader(InPort port, SourceMessages messages)
- {
- super(port, messages);
- }
- boolean returnMutablePairs;
- /** Set whether returned pairs are mutable or not (the default). */
- public void setReturnMutablePairs(boolean v) { returnMutablePairs = v; }
- GeneralHashTable<Integer,Object> sharedStructureTable;
- /** Bind value to index in sharingStructuretable.
- * @param value The object being defined.
- * @param sharingIndex Back-reference index.
- * I.e. the value N in a @code{#N=} form. If negative, do nothing.
- * @return The value unchanged.
- */
- public Object bindSharedObject(int sharingIndex, Object value) {
- if (sharingIndex >= 0) {
- GeneralHashTable<Integer,Object> map = sharedStructureTable;
- if (map == null) {
- map = new GeneralHashTable<Integer,Object>();
- sharedStructureTable = map;
- }
- Integer key = Integer.valueOf(sharingIndex);
- if (map.get(key, this) != this)
- error('w', "a duplicate #n= definition was read");
- map.put(key, value);
- }
- return value;
- }
- /** Read a #|...|#-style comment (which may contain other nested comments).
- * Assumes the initial "#|" has already been read.
- */
- final public void readNestedComment (char start1, char start2,
- char end1, char end2)
- throws java.io.IOException, SyntaxException {
- int commentNesting = 1;
- int startLine = port.getLineNumber();
- int startColumn = port.getColumnNumber();
- StringBuilder buf = null;
- if (port instanceof BinaryInPort && (startLine == 0 || startLine == 1))
- buf = new StringBuilder();
- do {
- int c = read ();
- if (buf != null)
- buf.append((char) c);
- if (c == end1) {
- c = read();
- if (buf != null)
- buf.append((char) c);
- if (c == end2)
- commentNesting--;
- } else if (c == start1) {
- c = read();
- if (c == start2)
- commentNesting++;
- }
- if (c < 0) {
- eofError("unexpected end-of-file in " + start1 + start2
- + " comment starting here",
- startLine + 1, startColumn - 1);
- return;
- }
- } while (commentNesting > 0);
- if (buf != null)
- checkEncodingSpec(buf.toString());
- }
- public void checkEncodingSpec(String line) {
- Matcher m = Pattern.compile("coding[:=]\\s*([-a-zA-Z0-9]+)")
- .matcher(line);
- if (m.find()) {
- String enc = m.group(1);
- try {
- ((BinaryInPort) getPort()).setCharset(enc);
- } catch (java.nio.charset.UnsupportedCharsetException ex) {
- error('e', "unrecognized encoding name "+enc);
- } catch (Exception ex) {
- error('e', "cannot set encoding name here");
- }
- }
- }
- boolean inQuasiSyntax;
- public static final ThreadLocation symbolReadCase
- = new ThreadLocation("symbol-read-case");
- static { symbolReadCase.setGlobal(Symbol.valueOf("preserve")); }
- char readCase = lookupReadCase();
- /** Get specification of how symbols should be case-folded.
- * @return Either '\0' (unspecified - defaults to preserve case),
- * 'P' (means preserve case), 'U' (upcase),
- * 'D' (downcase), or 'I' (invert case).
- */
- public char getReadCase () { return readCase; }
- public void setReadCase(char readCase) { this.readCase = readCase; }
- static char lookupReadCase()
- {
- try
- {
- String read_case_string = symbolReadCase.get("P").toString();
- if (read_case_string.length() > 0)
- {
- char read_case = read_case_string.charAt(0);
- if (read_case == 'P') ;
- else if (read_case == 'u')
- read_case = 'U';
- else if (read_case == 'd' || read_case == 'l' || read_case == 'L')
- read_case = 'D';
- else if (read_case == 'i')
- read_case = 'I';
- return read_case;
- }
- }
- catch (Exception ex)
- {
- }
- return '\0';
- }
- public Object readValues (int ch, ReadTable rtable, int sharingIndex)
- throws java.io.IOException, SyntaxException {
- return readValues(ch, rtable.lookup(ch), rtable, sharingIndex);
- }
- /** May return zero or multiple values.
- * Returns no values if looking at whitespace or a comment. */
- public Object readValues (int ch, ReadTableEntry entry, ReadTable rtable,
- int sharingIndex)
- throws java.io.IOException, SyntaxException {
- seenEscapes = false;
- return entry.read(this, ch, -1, sharingIndex);
- }
- public Pair readValuesAndAppend(int ch, ReadTable rtable, Pair last)
- throws java.io.IOException, SyntaxException {
- int line = port.getLineNumber();
- int column = port.getColumnNumber() - 1; // Adjust for ch
- Object values = readValues(ch, rtable, -1);
- int index = 0;
- int next = Values.nextIndex(values, index);
- if (next >= 0) {
- for (;;) {
- Object value = Values.nextValue(values, index);
- index = next;
- if (value == gnu.expr.QuoteExp.voidExp)
- value = Values.empty;
- next = Values.nextIndex(values, index);
- if (next < 0)
- value = handlePostfix(value, rtable, line, column);
- Pair pair = makePair(value, line, column);
- setCdr(last, pair);
- last = pair;
- if (next < 0)
- break;
- }
- }
- return last;
- }
- protected Object readAndHandleToken(int ch, int startPos, ReadTable rtable)
- throws java.io.IOException, SyntaxException
- {
- readToken(ch, rtable);
- return handleToken(startPos, rtable);
- }
- protected Object handleToken(int startPos, ReadTable rtable)
- throws java.io.IOException, SyntaxException
- {
- int ch;
- char readCase = getReadCase();
- int endPos = tokenBufferLength;
- if (! seenEscapes)
- {
- Object value = parseNumber(tokenBuffer, startPos, endPos - startPos,
- '\0', 0, SCM_NUMBERS|rtable.extraFlags);
- if (value != null && ! (value instanceof String))
- {
- tokenBufferLength = startPos;
- return value;
- }
- /* Common Lisp only? FIXME
- if (isPotentialNumber(tokenBuffer, startPos, endPos))
- {
- error(value == null ? "not a valid number"
- : "not a valid number: " + value);
- return IntNum.zero();
- }
- */
- }
- if (readCase == 'I')
- {
- int upperCount = 0;
- int lowerCount = 0;
- for (int i = startPos; i < endPos; i++)
- {
- char ci = tokenBuffer[i];
- if (ci == TOKEN_ESCAPE_CHAR)
- i++;
- else if (Character.isLowerCase(ci))
- lowerCount++;
- else if (Character.isUpperCase(ci))
- upperCount++;
- }
- if (lowerCount == 0)
- readCase = 'D';
- else if (upperCount == 0)
- readCase = 'U';
- else
- readCase = 'P';
- }
- boolean handleUri =
- (endPos >= startPos + 2
- && tokenBuffer[endPos-1] == '}'
- && tokenBuffer[endPos-2] != TOKEN_ESCAPE_CHAR
- && peek() == ':');
- int packageMarker = -1;
- int lbrace = -1, rbrace = -1, braceNesting = 0;
- int j = startPos;
- boolean uriBad = false;
- for (int i = startPos; i < endPos; i++)
- {
- char ci = tokenBuffer[i];
- if (ci == TOKEN_ESCAPE_CHAR)
- {
- if (++ i < endPos)
- tokenBuffer[j++] = tokenBuffer[i];
- continue;
- }
- if (handleUri)
- {
- if (ci == '{')
- {
- if (lbrace < 0)
- lbrace = j;
- else if (braceNesting == 0)
- uriBad = true;
- braceNesting++;
- }
- else if (ci == '}')
- {
- braceNesting--;
- if (braceNesting < 0)
- uriBad = true;
- else if (braceNesting == 0)
- {
- if (rbrace < 0)
- rbrace = j;
- else
- uriBad = true;
- }
- }
- }
- if (braceNesting > 0)
- ;
- else if (ci == ':')
- packageMarker = packageMarker >= 0 ? -1 : j;
- else if (readCase == 'U')
- ci = Character.toUpperCase(ci);
- else if (readCase == 'D')
- ci = Character.toLowerCase(ci);
- tokenBuffer[j++] = ci;
- }
- endPos = j;
- int len = endPos - startPos;
- Object result;
- if (lbrace >= 0 && rbrace > lbrace)
- {
- String prefix = lbrace > 0 ? new String(tokenBuffer, startPos, lbrace-startPos) : null;
- lbrace++;
- String uri = new String(tokenBuffer, lbrace, rbrace-lbrace);
- ch = read(); // skip ':' - previously peeked.
- ch = read();
- Object rightOperand = readValues(ch, rtable.lookup(ch), rtable, -1);
- if (! (rightOperand instanceof SimpleSymbol))
- error("expected identifier in symbol after '{URI}:'");
- // FIXME should allow "compound keyword" - for attribute names
- result = Symbol.valueOf(rightOperand.toString(), uri, prefix);
- }
- else if (rtable.initialColonIsKeyword && packageMarker == startPos && len > 1)
- {
- startPos++;
- String str = new String(tokenBuffer, startPos, endPos-startPos);
- result = Keyword.make(str.intern());
- }
- else if (rtable.finalColonIsKeyword && packageMarker != -1 && packageMarker == endPos - 1
- && (len > 1 || seenEscapes))
- {
- String str = new String(tokenBuffer, startPos, len - 1);
- result = Keyword.make(str.intern());
- }
- else {
- if (len == 1 && tokenBuffer[startPos] == '.' && !seenEscapes)
- error("invalid use of '.' token");
- result = rtable.makeSymbol(new String(tokenBuffer, startPos, len));
- }
- tokenBufferLength = startPos;
- return result;
- }
- public static final char TOKEN_ESCAPE_CHAR = '\uffff';
- /** If true, then tokenbuffer contains escaped characters.
- * These are prefixed (in the buffer) by TOKEN_ESCAPE_CHAR.
- */
- protected boolean seenEscapes;
- /** Read token, leaving characters in tokenBuffer.
- * Sets seenEscapes if escape characters are seen.
- */
- void readToken(int ch, ReadTable rtable)
- throws java.io.IOException, SyntaxException
- {
- boolean inEscapes = false;
- int braceNesting = 0;
- for (;; ch = read())
- {
- if (ch < 0)
- {
- if (inEscapes)
- eofError("unexpected EOF between escapes");
- else
- break;
- }
- ReadTableEntry entry = rtable.lookup(ch);
- int kind = entry.getKind();
- if (kind == ReadTable.ILLEGAL)
- {
- if (inEscapes)
- {
- tokenBufferAppend(TOKEN_ESCAPE_CHAR);
- tokenBufferAppend(ch);
- continue;
- }
- if (ch == '}' && --braceNesting >= 0)
- {
- tokenBufferAppend(ch);
- continue;
- }
- unread(ch);
- break;
- }
- if (! inEscapes && isTerminatingChar(ch, rtable)) {
- kind = ReadTable.TERMINATING_MACRO;
- }
-
- if (kind == ReadTable.SINGLE_ESCAPE)
- {
- ch = read();
- if (ch < 0)
- eofError("unexpected EOF after single escape");
- if (rtable.hexEscapeAfterBackslash
- // We've allowed hex escapes for a while.
- // Allow R7RS general escapes - but only inside |bars|.
- && (inEscapes || ch == 'x' || ch == 'X'))
- ch = readEscape(ch);
- if (ch >= 0)
- {
- tokenBufferAppend(TOKEN_ESCAPE_CHAR);
- tokenBufferAppend(ch);
- }
- seenEscapes = true;
- continue;
- }
- if (kind == ReadTable.MULTIPLE_ESCAPE)
- {
- inEscapes = ! inEscapes;
- seenEscapes = true;
- continue;
- }
- if (inEscapes)
- {
- // Step 9:
- tokenBufferAppend(TOKEN_ESCAPE_CHAR);
- tokenBufferAppend(ch);
- }
- else
- {
- // Step 8:
- switch (kind)
- {
- case ReadTable.CONSTITUENT:
- if (ch == '{' && entry == ReadTableEntry.brace)
- braceNesting++;
- /* ... fall through ... */
- case ReadTable.NON_TERMINATING_MACRO:
- tokenBufferAppend(ch);
- continue;
- case ReadTable.MULTIPLE_ESCAPE:
- inEscapes = true;
- seenEscapes = true;
- continue;
- case ReadTable.TERMINATING_MACRO:
- unread(ch);
- return;
- case ReadTable.WHITESPACE:
- // if (readPreservingWhitespace) FIXME
- unread(ch);
- return;
- }
- }
- }
- }
- protected boolean isTerminatingChar(int ch, ReadTable rtable)
- throws java.io.IOException, SyntaxException
- {
- if (ch == rtable.postfixLookupOperator) {
- int next = port.peek();
- if (next == rtable.postfixLookupOperator)
- { // Looking at '::'
- //unread(ch);
- return true;
- }
- if (validPostfixLookupStart(next, rtable))
- return true;
- }
- return false;
- }
- public String readTokenString(int ch, ReadTable rtable)
- throws java.io.IOException, SyntaxException {
- int startPos = tokenBufferLength;
- if (ch >= 0)
- tokenBufferAppend(ch);
- readToken(read(), rtable);
- int length = tokenBufferLength - startPos;
- String str = new String(tokenBuffer, startPos, length);
- tokenBufferLength = startPos;
- return str;
- }
- public Object readObject() throws java.io.IOException, SyntaxException {
- return readObject(-1, false);
- }
- public Object readObject(int sharingIndex, boolean topLevel)
- throws java.io.IOException, SyntaxException
- {
- char saveReadState = ((InPort) port).readState;
- int startPos = tokenBufferLength;
- ((InPort) port).readState = ' ';
- try
- {
- ReadTable rtable = ReadTable.getCurrent();
- for (;;)
- {
- int line = port.getLineNumber();
- int column = port.getColumnNumber();
- int ch = port.read();
- if (ch < 0)
- return Sequence.eofValue; // FIXME
- Object value = readValues(ch, rtable, sharingIndex);
- if (value == Values.empty)
- continue;
- value = handlePostfix(value, rtable, line, column);
- if (topLevel)
- {
- // Wrap in begin form so top-level forms have position info.
- value = makePair(kawa.standard.begin.begin,
- makePair(value, line, column,
- port.getLineNumber(),
- port.getColumnNumber()),
- line, column);
- }
- return value;
- }
- }
- finally
- {
- tokenBufferLength = startPos;
- ((InPort) port).readState = saveReadState;
- }
- }
- protected boolean validPostfixLookupStart (int ch, ReadTable rtable)
- throws java.io.IOException {
- if (ch < 0 || ch == rtable.postfixLookupOperator)
- return false;
- if (ch == ',')
- return true;
- if (ch == '@')
- return true; // To support deprecated (TYPE:@ EXP)
- int kind = rtable.lookup(ch).getKind();
- return kind == ReadTable.CONSTITUENT
- || kind == ReadTable.NON_TERMINATING_MACRO
- || kind == ReadTable.MULTIPLE_ESCAPE
- || kind == ReadTable.SINGLE_ESCAPE;
- }
- /** After reading a value check for following {@code '['} or {@code ':'}.
- */
- protected Object handlePostfix(Object value, ReadTable rtable,
- int line, int column)
- throws java.io.IOException, SyntaxException {
- if (value == QuoteExp.voidExp)
- value = Values.empty;
- for (;;) {
- int ch = port.peek();
- String str; int slen;
- if (ch == '[' && rtable.defaultBracketMode == -2) {
- port.read();
- Object lst = ReaderParens.readList(this, null, ch, 1, ']', -1);
- value = makePair(value, lst, line, column);
- value = makePair(LispLanguage.bracket_apply_sym, value,
- line, column);
- } else if (ch == rtable.postfixLookupOperator) {
- // A kludge to map PreOpWord to ($lookup$ Pre 'Word).
- port.read();
- int ch2 = port.peek();
- Object rightOperand;
- if (ch2 == '@') {
- error('w',
- "deprecated cast syntax TYPE:@ (use ->TYPE instead)");
- rightOperand = readAndHandleToken('\\', 0, rtable);
- } else {
- if (! validPostfixLookupStart(ch2, rtable)) {
- unread();
- break;
- }
- ch = port.read();
- rightOperand = readValues(ch, rtable.lookup(ch), rtable, -1);
- }
- value = LList.list2(value,
- LList.list2(LispLanguage.quasiquote_sym, rightOperand));
- value = makePair(LispLanguage.lookup_sym, value,
- line, column);
- }
- else
- break;
- }
- return value;
- }
- private boolean isPotentialNumber (char[] buffer, int start, int end)
- {
- int sawDigits = 0;
- for (int i = start; i < end; i++)
- {
- char ch = buffer[i];
- if (Character.isDigit(ch))
- sawDigits++;
- else if (ch == '-' || ch == '+')
- {
- if (i + 1 == end)
- return false;
- }
- else if (ch == '#')
- return true;
- else if (Character.isLetter(ch) || ch == '/'
- || ch == '_' || ch == '^')
- {
- // CommonLisp defines _123 (and ^123) as a "potential number";
- // most implementations seem to define it as a symbol.
- // Scheme does defines it as a symbol.
- if (i == start)
- return false;
- }
- else if (ch != '.')
- return false;
- }
- return sawDigits > 0;
- }
- static final int SCM_COMPLEX = 1;
- public static final int SCM_NUMBERS = SCM_COMPLEX;
- public static final int SCM_ANGLE = SCM_NUMBERS << 1;
- public static final int SCM_COLATITUDE = SCM_ANGLE << 1;
- public static final int SCM_LEXPONENT_IS_BIGDECIMAL = SCM_COLATITUDE << 1;
- public static Object parseNumber(CharSequence str, int radix) {
- char[] buf;
- int len = str.length();
- int where;
- if (str instanceof FString
- && (where = ((FString) str).getSegmentReadOnly(0, len)) >= 0) {
- buf = ((FString) str).getBuffer();
- } else {
- where = 0;
- buf = str.toString().toCharArray();
- }
- return parseNumber(buf, where, len,
- '\0', radix, LispReader.SCM_NUMBERS);
- }
- /** Parse a number.
- * @param buffer contains the characters of the number
- * @param start startinging index of the number in the buffer
- * @param count number of characters in buffer to use
- * @param exactness either 'i' or 'I' force an inexact result,
- * either 'e' or 'E' force an exact result,
- * '\0' yields an inact or inexact depending on the form of the literal,
- * while ' ' is like '\0' but does not allow more exactness specifiers.
- * @param radix the number base to use or 0 if unspecified
- * A negative radix is an overideable default.
- * @return the number if a valid number; null or a String-valued error
- * message if if there was some error parsing the number.
- */
- public static Object parseNumber(char[] buffer, int start, int count,
- char exactness, int radix, int flags)
- {
- int end = start + count;
- int pos = start;
- if (pos >= end)
- return "no digits";
- char ch = buffer[pos++];
- while (ch == '#')
- {
- if (pos >= end)
- return "no digits";
- ch = buffer[pos++];
- switch (ch)
- {
- case 'b': case 'B':
- if (radix > 0)
- return "duplicate radix specifier";
- radix = 2;
- break;
- case 'o': case 'O':
- if (radix > 0)
- return "duplicate radix specifier";
- radix = 8;
- break;
- case 'd': case 'D':
- if (radix > 0)
- return "duplicate radix specifier";
- radix = 10;
- break;
- case 'x': case 'X':
- if (radix > 0)
- return "duplicate radix specifier";
- radix = 16;
- break;
- case 'e': case 'E':
- case 'i': case 'I':
- if (exactness != '\0')
- {
- if (exactness == ' ')
- return "non-prefix exactness specifier";
- else
- return "duplicate exactness specifier";
- }
- exactness = ch;
- break;
- default:
- int value = 0;
- for (;;)
- {
- int dig = Character.digit(ch, 10);
- if (dig < 0)
- break;
- value = 10 * value + dig;
- if (pos >= end)
- return "missing letter after '#'";
- ch = buffer[pos++];
- }
- if (ch == 'R' || ch == 'r')
- {
- if (radix > 0)
- return "duplicate radix specifier";
- if (value < 2 || value > 36)
- return "invalid radix specifier";
- radix = value;
- break;
- }
- return "unknown modifier '#" + ch + '\'';
- }
- if (pos >= end)
- return "no digits";
- ch = buffer[pos++];
- }
- if (exactness == '\0')
- exactness = ' ';
- if (radix < 0)
- radix = -radix;
- else if (radix == 0)
- {
- radix = 10;
- /*
- for (int i = count; ; )
- {
- if (--i < 0)
- {
- // FIXME - should get *read-base* in CommonLisp:
- // radix = *read_base*;
- radix = 10;
- break;
- }
- if (buffer[start+i] == '.')
- {
- radix = 10;
- break;
- }
- }
- */
- }
- boolean negative = ch == '-';
- boolean numeratorNegative = negative;
- boolean sign_seen = ch == '-' || ch == '+';
- if (sign_seen)
- {
- if (pos >= end)
- return "no digits following sign";
- ch = buffer[pos++];
- }
- // Special case for '+i' and '-i'.
- if ((ch == 'i' || ch == 'I') &&
- (pos == end || buffer[pos] == '+' || buffer[pos] == '-') &&
- start == pos - 2 && (flags & SCM_COMPLEX) != 0) {
- char sign = buffer[start];
- if (sign != '+' && sign != '-')
- return "no digits";
- if (pos < end) {
- Object jmag = parseNumber(buffer, pos, end-pos, exactness,
- radix, flags);
- if (jmag instanceof String)
- return jmag;
- if (! (jmag instanceof Quaternion))
- return "invalid numeric constant ("+jmag+")";
- Quaternion qjmag = (Quaternion) jmag;
- RealNum re = qjmag.re();
- RealNum im = qjmag.im();
- if (!(re.isZero() && im.isZero()))
- return "invalid numeric constant";
- if (exactness == 'i' || exactness == 'I')
- return Quaternion.make(0, negative ? -1 : 1,
- qjmag.doubleJmagValue(),
- qjmag.doubleKmagValue());
- return Quaternion.make(IntNum.zero(), negative ?
- IntNum.minusOne() : IntNum.one(),
- qjmag.jm(), qjmag.km());
- }
- if (exactness == 'i' || exactness == 'I')
- return new DComplex(0, negative ? -1 : 1);
- return negative ? Complex.imMinusOne() : Complex.imOne();
- }
- // Special case for '+j' and '-j'.
- if ((ch == 'j' || ch == 'J') &&
- (pos == end || buffer[pos] == '+' || buffer[pos] == '-') &&
- start == pos - 2 && (flags & SCM_COMPLEX) != 0) {
- char sign = buffer[start];
- if (sign != '+' && sign != '-')
- return "no digits";
- if (pos < end) {
- Object kmag = parseNumber(buffer, pos, end-pos, exactness,
- radix, flags);
- if (kmag instanceof String)
- return kmag;
- if (! (kmag instanceof Quaternion))
- return "invalid numeric constant ("+kmag+")";
- Quaternion qkmag = (Quaternion) kmag;
- RealNum re = qkmag.re();
- RealNum im = qkmag.im();
- RealNum jm = qkmag.jm();
- if (!(re.isZero() && im.isZero() && jm.isZero()))
- return "invalid numeric constant";
- if (exactness == 'i' || exactness == 'I')
- return Quaternion.make(0, 0, negative ? -1 : 1,
- qkmag.doubleKmagValue());
- return Quaternion.make(IntNum.zero(), IntNum.zero(),
- negative ? IntNum.minusOne() : IntNum.one(),
- qkmag.km());
- }
- if (exactness == 'i' || exactness == 'I')
- return new DQuaternion(0, 0, 0, negative ? -1 : 1);
- return negative ? Quaternion.jmMinusOne() : Quaternion.jmOne();
- }
- // Special case for '+k' and '-k'.
- if ((ch == 'k' || ch == 'K') && pos == end && start == pos - 2
- && (flags & SCM_COMPLEX) != 0) {
- char sign = buffer[start];
- if (sign != '+' && sign != '-')
- return "no digits";
- if (exactness == 'i' || exactness == 'I')
- return new DQuaternion(0, 0, 0, negative ? -1 : 1);
- return negative ? Quaternion.kmMinusOne() : Quaternion.kmOne();
- }
- int realStart = pos - 1;
- boolean hash_seen = false;
- int exp_seen = -1;
- int digits_start = -1;
- int decimal_point = -1;
- boolean copy_needed = false;
- boolean underscore_seen = false;
- IntNum numerator = null;
- long lvalue = 0;
- loop:
- for (;;)
- {
- int digit = Character.digit(ch, radix);
- if (digit >= 0)
- {
- if (hash_seen && decimal_point < 0)
- return "digit after '#' in number";
- if (digits_start < 0)
- digits_start = pos - 1;
- lvalue = radix * lvalue + digit;
- }
- else
- {
- switch (ch)
- {
- /*
- case '_':
- underscore_seen = true;
- break;
- */
- /*
- case '#':
- if (radix != 10)
- return "'#' in non-decimal number";
- if (digits_start < 0)
- return "'#' with no preceeding digits in number";
- hash_seen = true;
- break;
- */
- case '.':
- if (decimal_point >= 0)
- return "duplicate '.' in number";
- if (radix != 10)
- return "'.' in non-decimal number";
- decimal_point = pos - 1;
- break;
- case 'e': case 's': case 'f': case 'd': case 'l':
- case 'E': case 'S': case 'F': case 'D': case 'L':
- if (pos == end || radix != 10)
- {
- pos--;
- break loop;
- }
- char next = buffer[pos];
- int exp_pos = pos-1;
- if (next == '+' || next == '-')
- {
- if (++ pos >= end
- || Character.digit(buffer[pos], 10) < 0)
- return "missing exponent digits";
- }
- else if (Character.digit(next, 10) < 0)
- {
- pos--;
- break loop;
- }
- if (exp_seen >= 0)
- return "duplicate exponent";
- if (radix != 10)
- return "exponent in non-decimal number";
- if (digits_start < 0)
- return "mantissa with no digits";
- exp_seen = exp_pos;
- for (;;)
- {
- pos++;
- if (pos >= end || Character.digit(buffer[pos], 10) < 0)
- break loop;
- }
- case '/':
- if (numerator != null)
- return "multiple fraction symbol '/'";
- if (digits_start < 0)
- return "no digits before fraction symbol '/'";
- if (exp_seen >= 0 || decimal_point >= 0)
- return "fraction symbol '/' following exponent or '.'";
- numerator = valueOf(buffer, digits_start, pos - digits_start,
- radix, negative, lvalue);
- digits_start = -1;
- lvalue = 0;
- negative = false;
- hash_seen = false;
- underscore_seen = false;
- break;
- default:
- pos--;
- break loop;
- }
- }
- if (pos == end)
- break;
- ch = buffer[pos++];
- }
- char infnan = '\0';
- if (digits_start < 0)
- {
- if (sign_seen
- && pos + 4 < end && buffer[pos+3] == '.' && buffer[pos+4] == '0')
- {
- char b0 = buffer[pos];
- char b1, b2;
- if ((b0 == 'i' || b0 == 'I')
- && ((b1 = buffer[pos+1]) == 'n' || b1 == 'N')
- && ((b2 = buffer[pos+2]) == 'f' || b2 == 'F'))
- {
- infnan = 'i';
- }
- else if ((b0 == 'n' || b0 == 'N')
- && ((b1 = buffer[pos+1]) == 'a' || b1 == 'A')
- && ((b2 = buffer[pos+2]) == 'n' || b2 == 'N'))
- {
- infnan = 'n';
- }
- }
- if (infnan == '\0')
- return "no digits";
- pos += 5;
- }
- if (hash_seen || underscore_seen)
- {
- // FIXME make copy, removing '_' and replacing '#' by '0'.
- }
- boolean inexact = (exactness == 'i' || exactness == 'I'
- || (exactness == ' ' && hash_seen));
- RealNum number = null;
- char exp_char = '\0';
- if (infnan != '\0')
- {
- inexact = true;
- double d = infnan == 'i' ? Double.POSITIVE_INFINITY : Double.NaN;
- number = new DFloNum(negative ? - d : d);
- }
- else if (exp_seen >= 0 || decimal_point >= 0)
- {
- if (digits_start > decimal_point && decimal_point >= 0)
- digits_start = decimal_point;
- if (numerator != null)
- return "floating-point number after fraction symbol '/'";
- if (exactness == 'e' || exactness == 'E') {
- int exp = 0;
- IntNum inumber;
- if (decimal_point < 0) {
- inumber = valueOf(buffer, digits_start,
- exp_seen - digits_start,
- radix, negative, lvalue);
- }
- else {
- StringBuilder sbuf = new StringBuilder();
- if (negative)
- sbuf.append('-');
- sbuf.append(buffer, digits_start, decimal_point-digits_start);
- decimal_point++;
- int fracdigits = (exp_seen >= 0 ? exp_seen : pos)
- - decimal_point;
- sbuf.append(buffer, decimal_point, fracdigits);
- inumber = IntNum.valueOf(sbuf.toString());
- exp -= fracdigits;
- }
- if (exp_seen >= 0) {
- exp += Integer.parseInt(new String(buffer, exp_seen+1,
- pos - (exp_seen+1)));
- }
- if (exp > 0)
- number = IntNum.times(inumber, IntNum.power(IntNum.ten(), exp));
- else if (exp < 0)
- number = RatNum.make(inumber, IntNum.power(IntNum.ten(), -exp));
- else
- number = inumber;
- } else {
- String str = new String(buffer, digits_start, pos - digits_start);
- if (exp_seen >= 0) {
- exp_char = Character.toLowerCase(buffer[exp_seen]);
- if (exp_char != 'e') {
- int prefix = exp_seen - digits_start;
- str = str.substring(0, prefix)+'e'+str.substring(prefix+1);
- }
- }
- double d = Convert.parseDouble(str);
- number = new DFloNum(negative ? - d : d);
- }
- }
- else
- {
- IntNum iresult = valueOf(buffer, digits_start, pos - digits_start,
- radix, negative, lvalue);
- if (numerator == null)
- number = iresult;
- else
- {
- // Check for zero denominator values: 0/0, n/0, and -n/0
- // (i.e. NaN, Infinity, and -Infinity).
- if (iresult.isZero ())
- {
- boolean numeratorZero = numerator.isZero();
- if (inexact)
- number = new DFloNum ((numeratorZero ? Double.NaN
- : numeratorNegative ? Double.NEGATIVE_INFINITY
- : Double.POSITIVE_INFINITY));
- else if (numeratorZero)
- return "0/0 is undefined";
- else
- number = RatNum.make(numerator, iresult);
- }
- else
- {
- number = RatNum.make(numerator, iresult);
- }
- }
- if (inexact && number.isExact())
- // We want #i-0 or #i-0/1 to be -0.0, not 0.0.
- number = new DFloNum(numeratorNegative && number.isZero() ? -0.0
- : number.doubleValue());
- }
- if (exactness == 'e' || exactness == 'E')
- number = number.toExact();
- if (pos < end)
- {
- ch = buffer[pos++];
- if (ch == '@')
- { /* polar notation */
- Object angle = parseNumber(buffer, pos, end - pos,
- exactness, radix, flags|SCM_ANGLE);
- if (angle instanceof String)
- return angle;
- if (! (angle instanceof RealNum) && ! (angle instanceof RealNum[]))
- return "invalid complex polar constant";
- if (angle instanceof RealNum[]) {
- RealNum[] polars = (RealNum[]) angle;
- if (number.isZero() &&
- (!polars[0].isExact() || !polars[1].isExact() ||
- !polars[2].isExact()))
- return new DFloNum(0.0);
- return Quaternion.polar(number, polars[0], polars[1],
- polars[2]);
- }
- RealNum rangle = (RealNum) angle;
- /* r4rs requires 0@1.0 to be inexact zero, even if (make-polar
- * 0 1.0) is exact zero, so check for this case. */
- if (number.isZero () && !rangle.isExact ())
- return new DFloNum (0.0);
- return Complex.polar (number, rangle);
- }
- if (ch == '%') {
- /* extended polar notation */
- Object colatitude = parseNumber(buffer, pos, end - pos,
- exactness, radix,
- flags|SCM_COLATITUDE);
- if (colatitude instanceof String)
- return colatitude;
- if (!(colatitude instanceof RealNum) &&
- !(colatitude instanceof RealNum[]))
- return "invalid quaternion polar constant";
- if ((flags & SCM_ANGLE) == 0) {
- // number%colatitude or number%colatitude&longitude
- RealNum rangle = IntNum.zero();
- RealNum rcolatitude, rlongitude;
- if (colatitude instanceof RealNum) {
- rcolatitude = (RealNum) colatitude;
- rlongitude = IntNum.zero();
- } else {
- RealNum[] polars = (RealNum[]) colatitude;
- rcolatitude = polars[1];
- rlongitude = polars[2];
- }
- /* r4rs requires 0@1.0 to be inexact zero, even if
- (make-polar 0 1.0) is exact zero, so check for this
- case. */
- if (number.isZero() &&
- (!rcolatitude.isExact() || !rlongitude.isExact()))
- return new DFloNum(0.0);
- return Quaternion.polar(number, rangle, rcolatitude,
- rlongitude);
- }
- if (colatitude instanceof RealNum[]) {
- RealNum[] polars = (RealNum[]) colatitude;
- polars[0] = number;
- return polars;
- }
- return new RealNum[] { number, (RealNum)colatitude, IntNum.zero() };
- }
- if (ch == '&') {
- /* extended polar notation */
- Object longitude = parseNumber(buffer, pos, end - pos,
- exactness, radix, flags);
- if (longitude instanceof String)
- return longitude;
- if (! (longitude instanceof RealNum))
- return "invalid quaternion polar constant";
- RealNum rlongitude = (RealNum) longitude;
- if ((flags & (SCM_ANGLE|SCM_COLATITUDE)) == 0) {
- // number&longitude
- /* r4rs requires 0@1.0 to be inexact zero, even if
- (make-polar 0 1.0) is exact zero, so check for this
- case. */
- if (number.isZero() && !rlongitude.isExact())
- return new DFloNum(0.0);
- return Quaternion.polar(number, IntNum.zero(),
- IntNum.zero(), rlongitude);
- }
- if ((flags & SCM_COLATITUDE) != 0)
- return new RealNum[] { IntNum.zero(), number, rlongitude };
- return new RealNum[] { number, IntNum.zero(), rlongitude };
- }
- if (ch == '-' || ch == '+')
- {
- pos--;
- Object imag = parseNumber(buffer, pos, end - pos,
- exactness, radix, flags);
- if (imag instanceof String)
- return imag;
- if (! (imag instanceof Quaternion))
- return "invalid numeric constant ("+imag+")";
- Quaternion cimag = (Quaternion) imag;
- RealNum re = cimag.re();
- if (! re.isZero())
- return "invalid numeric constant";
- return Quaternion.make(number, cimag.im(), cimag.jm(), cimag.km());
- }
- int lcount = 0;
- for (;;)
- {
- if (! Character.isLetter(ch))
- {
- pos--;
- break;
- }
- lcount++;
- if (pos == end)
- break;
- ch = buffer[pos++];
- }
- if (lcount == 1) {
- char prev = buffer[pos-1];
- if (prev == 'i' || prev == 'I') {
- if (pos < end) {
- Object jmag = parseNumber(buffer, pos, end-pos,
- exactness, radix, flags);
- if (jmag instanceof String)
- return jmag;
- if (! (jmag instanceof Quaternion))
- return "invalid numeric constant ("+jmag+")";
- Quaternion qjmag = (Quaternion) jmag;
- RealNum re = qjmag.re();
- RealNum im = qjmag.im();
- if (!(re.isZero() && im.isZero()))
- return "invalid numeric constant";
- return Quaternion.make(IntNum.zero(), number,
- qjmag.jm(), qjmag.km());
- }
- return Complex.make(IntNum.zero(), number);
- }
- if (prev == 'j' || prev == 'J') {
- if (pos < end) {
- Object kmag = parseNumber(buffer, pos, end-pos,
- exactness, radix, flags);
- if (kmag instanceof String)
- return kmag;
- if (! (kmag instanceof Quaternion))
- return "invalid numeric constant ("+kmag+")";
- Quaternion qkmag = (Quaternion) kmag;
- RealNum re = qkmag.re();
- RealNum im = qkmag.im();
- RealNum jm = qkmag.jm();
- if (!(re.isZero() && im.isZero() && jm.isZero()))
- return "invalid numeric constant";
- return Quaternion.make(IntNum.zero(), IntNum.zero(),
- number, qkmag.km());
- }
- return Quaternion.make(IntNum.zero(), IntNum.zero(),
- number, IntNum.zero());
- }
- if (prev == 'k' || prev == 'K') {
- if (pos < end)
- return "junk after imaginary suffix 'k'";
- return Quaternion.make(IntNum.zero (), IntNum.zero(),
- IntNum.zero(), number);
- }
- }
- return "excess junk after number";
- }
- else if (number instanceof DFloNum && exp_char > 0 && exp_char != 'e')
- {
- double d = number.doubleValue();
- switch (exp_char)
- {
- case 'f': case 's':
- return Float.valueOf((float) d);
- case 'd':
- return Double.valueOf(d);
- case 'l':
- if ((flags & SCM_LEXPONENT_IS_BIGDECIMAL) != 0)
- return java.math.BigDecimal.valueOf(d);
- // else fall through
- }
- }
- return number;
- }
- private static IntNum valueOf (char[] buffer, int digits_start,
- int number_of_digits,
- int radix, boolean negative,
- long lvalue)
- {
- // It turns out that if number_of_digits + radix <= 28
- // then the value will fit in a long without overflow,
- // so we can use the value calculated in lvalue.
- if (number_of_digits + radix <= 28)
- return IntNum.make(negative ? - lvalue : lvalue);
- else
- return IntNum.valueOf(buffer, digits_start, number_of_digits,
- radix, negative);
- }
- /** Reads a C-style String escape sequence.
- * Assume '\\' has already been read.
- * Return the converted character, or -1 on EOF, or -2 to ignore. */
- public int readEscape()
- throws java.io.IOException, SyntaxException
- {
- int c = read();
- if (c < 0)
- {
- eofError("unexpected EOF in character literal");
- return -1;
- }
- return readEscape(c);
- }
- public final int readEscape(int c)
- throws java.io.IOException, SyntaxException
- {
- switch ((char) c)
- {
- case 'a': c = 7; break; // alarm/bell
- case 'b': c = 8; break; // backspace
- case 't': c = 9; break; // tab
- case 'n': c = 10; break; // newline
- case 'v': c = 11; break; // vertical tab
- case 'f': c = 12; break; // formfeed
- case 'r': c = 13; break; // carriage return
- case 'e': c = 27; break; // escape
- case '\"': c = 34; break; // quote
- case '|': c = '|'; break; // vertical bar
- case '\\': c = 92; break; // backslash
- case ' ': // Skip to end of line, inclusive.
- case '\n': // Skip initial whitespace on following line.
- case '\r':
- case '\t':
- for (;;)
- {
- if (c < 0)
- {
- eofError("unexpected EOF in literal");
- return -1;
- }
- if (c == '\n')
- break;
- if (c == '\r')
- {
- if (peek() == '\n')
- skip();
- c = '\n';
- break;
- }
- if (c != ' ' && c != '\t')
- {
- unread(c);
- break;
- }
- c = read();
- }
- if (c != '\n')
- break; // ERROR
- // FIXME: if legacy-compatible non-R6RS-mode: return -2;
- for (;;)
- {
- c = read();
- if (c < 0)
- {
- eofError("unexpected EOF in literal");
- return -1;
- }
- if (c != ' ' && c != '\t')
- {
- unread(c);
- return -2;
- }
- }
- case 'M':
- c = read();
- if (c != '-')
- {
- error("Invalid escape character syntax");
- return '?';
- }
- c = read();
- if (c == '\\')
- c = readEscape();
- return c | 0200;
- case 'C':
- c = read();
- if (c != '-')
- {
- error("Invalid escape character syntax");
- return '?';
- }
- /* ... fall through ... */
- case '^':
- c = read();
- if (c == '\\')
- c = readEscape();
- if (c == '?')
- return 0177;
- return c & (0200 | 037);
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- /* An octal escape, as in ANSI C. */
- c = c - '0';
- for (int count = 0; ++count < 3; )
- {
- int d = read();
- int v = Character.digit((char) d, 8);
- if (v >= 0)
- c = (c << 3) + v;
- else
- {
- if (d >= 0)
- unread(d);
- break;
- }
- }
- break;
- case 'u':
- c = 0;
- for (int i = 4; --i >= 0; )
- {
- int d = read ();
- if (d < 0)
- eofError("premature EOF in \\u escape");
- int v = Character.digit ((char) d, 16);
- if (v < 0)
- error("non-hex character following \\u");
- c = 16 * c + v;
- }
- break;
- case 'x':
- case 'X':
- return readHexEscape();
- default: break;
- }
- return c;
- }
- public int readHexEscape ()
- throws java.io.IOException, SyntaxException
- {
- int c = 0;
- /* A hex escape, as in ANSI C. */
- for (;;)
- {
- int d = read();
- int v = Character.digit((char) d, 16);
- if (v >= 0)
- c = (c << 4) + v;
- else
- {
- if (d != ';')
- {
- // FIXME: if strict-R6RS: ERROR
- if (d >= 0)
- unread(d);
- }
- break;
- }
- }
- return c;
- }
- public final Object readObject (int c)
- throws java.io.IOException, SyntaxException
- {
- unread(c);
- return readObject();
- }
- /** Read a "command" - a top-level expression or declaration.
- * Return Sequence.eofValue at end of file. */
- public Object readCommand ()
- throws java.io.IOException, SyntaxException
- {
- return readObject(-1, true);
- }
- protected Object makeNil ()
- {
- return LList.Empty;
- }
- protected Pair makePair (Object car, int line, int column)
- {
- return makePair(car, LList.Empty, line, column);
- }
- protected Pair makePair(Object car, int startline, int startcolumn,
- int endline, int endcolumn) {
- String pname = port.getName();
- Object cdr = LList.Empty;
- if (! returnMutablePairs && pname != null && startline >= 0) {
- long position = SourceMapper.simpleEncode(startline+1, startcolumn+1,
- endline+1, endcolumn+1);
- return PairWithPosition.make(car, cdr, pname, position);
- } else
- return Pair.make(car, cdr);
- }
- protected Pair makePair (Object car, Object cdr, int line, int column)
- {
- String pname = port.getName();
- if (! returnMutablePairs && pname != null && line >= 0)
- return PairWithPosition.make(car, cdr,
- pname, line + 1, column + 1);
- else
- return Pair.make(car, cdr);
- }
- protected Pair makePair2 (Object car, Object cadr, Object cddr,
- int line, int column) {
- return makePair(car, makePair(cadr, cddr, line, column), line, column);
- }
- protected void setCar (Object pair, Object car, int endline, int endcolumn)
- {
- ((Pair) pair).setCarBackdoor(car);
- if (pair instanceof PairWithPosition)
- ((PairWithPosition) pair).setEndLine(endline, endcolumn);
- }
- protected void setCar (Object pair, Object car)
- {
- ((Pair) pair).setCarBackdoor(car);
- }
- protected void setCdr (Object pair, Object cdr)
- {
- ((Pair) pair).setCdrBackdoor(cdr);
- }
- /** Read a number from a LispReader
- * @param previous number of characters already pushed on tokenBuffer
- * @param reader LispReader to read from
- * @param radix base to use or -1 if unspecified
- */
- public static Object readNumberWithRadix(int previous, LispReader reader, int radix)
- throws java.io.IOException, SyntaxException
- {
- int startPos = reader.tokenBufferLength - previous;
- ReadTable rtable = ReadTable.getCurrent();
- for (;;) {
- reader.readToken(reader.read(), rtable);
- // '#' is a terminating-macro character so we have to add it "manually"
- int ch = reader.peek();
- if (ch != '#')
- break;
- reader.tokenBufferAppend(ch);
- reader.skip();
- }
- int endPos = reader.tokenBufferLength;
- if (startPos == endPos)
- {
- reader.error("missing numeric token");
- return IntNum.zero();
- }
- Object result = LispReader.parseNumber(reader.tokenBuffer, startPos,
- endPos - startPos, '\0', radix, 0);
- if (result instanceof String)
- {
- reader.error((String) result);
- return IntNum.zero();
- }
- else if (result == null)
- {
- reader.error("invalid numeric constant");
- return IntNum.zero();
- }
- else
- return result;
- }
- public static Object readCharacter (LispReader reader)
- throws java.io.IOException, SyntaxException
- {
- int ch = reader.read();
- if (ch < 0)
- reader.eofError("unexpected EOF in character literal");
- int startPos = reader.tokenBufferLength;
- reader.tokenBufferAppend(ch);
- reader.readToken(reader.read(), ReadTable.getCurrent());
- char[] tokenBuffer = reader.tokenBuffer;
- int length = reader.tokenBufferLength - startPos;
- if (length == 1 || length == 2) {
- ch = Character.codePointAt(tokenBuffer, startPos,
- reader.tokenBufferLength);
- if (ch > 0xFFFF || length == 1)
- return Char.make(ch);
- }
- String name = new String(tokenBuffer, startPos, length);
- ch = Char.nameToChar(name);
- if (ch >= 0)
- return Char.make(ch);
- ch = tokenBuffer[startPos];
- if (ch == 'x' || ch == 'X')
- {
- int value = 0;
- for (int i = 1; ; i++)
- {
- if (i == length)
- return Char.make(value);
- int v = Character.digit (tokenBuffer[startPos + i], 16);
- if (v < 0)
- break;
- value = 16 * value + v;
- if (value > 0x10FFFF) {
- reader.error("character scalar value greater than #x10FFFF");
- return Char.make('?');
- }
- }
- }
- // FIXME remove - only used for BRL Perhaps a deprecation warning?
- ch = Character.digit(ch, 8);
- if (ch >= 0)
- {
- int value = ch;
- for (int i = 1; ; i++)
- {
- if (i == length)
- return Char.make(value);
- ch = Character.digit(tokenBuffer[startPos + i], 8);
- if (ch < 0)
- break;
- value = 8 * value + ch;
- }
- }
- reader.error("unknown character name: " + name);
- return Char.make('?');
- }
- public static Object readSpecial (LispReader reader)
- throws java.io.IOException, SyntaxException
- {
- int ch = reader.read();
- if (ch < 0)
- reader.eofError("unexpected EOF in #! special form");
- /* Handle Unix #!PROGRAM line at start of file. */
- if ((ch == '/' || ch == ' ')
- && reader.getLineNumber() == 0
- && reader.getColumnNumber() == 3)
- {
- String filename = reader.getName();
- if (filename != null
- && ApplicationMainSupport.commandName.get(null) == null)
- {
- ApplicationMainSupport.commandName.set(filename);
- }
- boolean sawBackslash = false;
- for (;;)
- {
- ch = reader.read();
- if (ch < 0)
- break;
- if (ch == '\\')
- sawBackslash = true;
- else if (ch == '\n' || ch == '\r')
- {
- if (! sawBackslash)
- break;
- sawBackslash = false;
- }
- else if (sawBackslash && ch != ' ' && ch != '\t')
- sawBackslash = false;
- }
- return Values.empty;
- }
- String name = reader.readTokenString(ch, ReadTable.getCurrent());
- if (name.equals("optional"))
- return Special.optional;
- if (name.equals("rest"))
- return Special.rest;
- if (name.equals("key"))
- return Special.key;
- if (name.equals("eof"))
- return Special.eof;
- if (name.equals("void"))
- //return Values.empty;
- return QuoteExp.voidExp;
- if (name.equals("default"))
- return Special.dfault;
- if (name.equals("undefined"))
- return Special.undefined;
- if (name.equals("abstract"))
- return Special.abstractSpecial;
- if (name.equals("native"))
- return Special.nativeSpecial;
- if (name.equals("if"))
- return Special.ifk;
- if (name.equals("null"))
- return null;
- if (name.equals("fold-case"))
- {
- reader.readCase = 'D';
- return Values.empty;
- }
- if (name.equals("no-fold-case"))
- {
- reader.readCase = 'P';
- return Values.empty;
- }
- reader.error("unknown named constant #!"+name);
- return null;
- }
- public static Object readGeneralArray(LispReader in, int rank,
- PrimType elementType)
- throws java.io.IOException, SyntaxException {
- if (rank == -1)
- rank = 1;
- int[] dimensions = new int[rank];
- int[] lowBounds = null;
- boolean error = false;
- int ch = in.read();
- boolean baddim = false;
- int explicitDims = 0;
- if (ch == '@' || ch == ':') {
- for (int r = 0; r < rank; r++) {
- if (ch == '@') {
- ch = in.read();
- boolean neg = ch == '-';
- if (! neg)
- in.unread(ch);
- int low = in.readIntDigits();
- if (low < 0) {
- in.error("expected low-bound after '@'");
- low = 0;
- }
- if (lowBounds == null)
- lowBounds = new int[rank];
- lowBounds[r] = neg ? - low : low;
- ch = in.read();
- if (ch != ':' && r == rank-1)
- break;
- }
- if (ch == ':') {
- explicitDims++;
- int dim = in.readIntDigits();
- if (dim < 0) {
- in.error("expected dimension after ':'");
- error = true;
- }
-
- dimensions[r] = dim;
- ch = in.read();
- } else if (ch != '@') {
- in.error("missing bounds-specifier (seen "+r
- +" of "+rank+")");
- error = true;
-
- }
- }
- }
- if (ch == '@' || ch == ':') {
- in.error("too many bounds-specifiers for rank-"
- +rank+" array");
- error = true;
- }
- while (ch >= 0 && Character.isWhitespace(ch))
- ch = in.read();
- SourceLocator sloc =
- PairWithPosition.make(null, null, in.getName(),
- in.getLineNumber()+1, in.getColumnNumber());
- in.unread(ch);
- Object data = in.readObject();
- if (explicitDims == 0) {
- if (! dimensionsFromNested(0, dimensions, data)) {
- in.error("array value is not a nested true list");
- error = true;
- }
- } else if (explicitDims < rank) {
- in.error("only "+explicitDims+" array lengths specified - must be all "+rank+" or none");
- error = true;
- }
- if (error)
- return LList.Empty;
- int size = 1;
- for (int d = dimensions.length; -- d >= 0; )
- size *= dimensions[d];
- Object buffer = elementType == null ? new Object[size]
- : Array.newInstance(elementType.getReflectClass(), size);
- SourceMessages messages = in.getMessages();
- fromNested(buffer, 0, 0, dimensions, data, elementType, sloc, messages);
- return Arrays.makeFromSimple(dimensions, lowBounds,
- buffer, elementType);
- }
- static boolean
- dimensionsFromNested(int dim, int[] dimensions, Object data) {
- int rank = dimensions.length;
- if (dim == rank)
- return true;
- List seq = Sequences.asSequenceOrNull(data);
- if (seq == null)
- return false;
- int len;
- if (seq instanceof Pair)
- len = LList.listLength(seq, false);
- else
- len = seq.size();
- if (len < 0)
- return false;
- if (len > dimensions[dim])
- dimensions[dim] = len;
- for (Object el : seq) {
- if (! dimensionsFromNested(dim+1, dimensions, el))
- return false;
- }
- return true;
- }
- static void fromNested(Object buffer, int index, int dim, int[] dimensions, Object value, PrimType elementType, SourceLocator sloc, SourceMessages messages) {
- int rank = dimensions.length;
- if (dim==rank) {
- char sig1 = elementType == null ? 'L'
- : elementType.getSignature().charAt(0);
- if (sig1 == 'B' || sig1 == 'S' || sig1 == 'I' || sig1 == 'J') {
- String msg = null;
- if (! (value instanceof IntNum))
- msg = "expected integer value";
- else {
- Object nvalue = LangPrimType.convertIntegerLiteral((IntNum) value, elementType, true);
- if (nvalue == null)
- msg = "integer "+value+" not in range of "+elementType.getName();
- else
- value = nvalue;
- }
- if (msg != null) {
- messages.error('e', sloc, msg);
- value = LangPrimType.convertIntegerLiteral(IntNum.zero(),
- elementType, true);
- }
- }
- if (sig1 == 'F' || sig1 == 'D') {
- RealNum rvalue = RealNum.asRealNumOrNull(value);
- if (rvalue != null) {
- if (sig1 == 'F')
- value = Float.valueOf(rvalue.floatValue());
- else
- value = Double.valueOf(rvalue.doubleValue());
- } else {
- messages.error('e', sloc, "expected real value");
- }
- }
- Array.set(buffer, index, value);
- } else {
- dim++;
- int stride = 1;
- for (int i = dim; i < rank; i++)
- stride *= dimensions[i];
- while (value instanceof Pair) {
- Pair pair = (Pair) value;
- if (pair instanceof SourceLocator)
- sloc = (SourceLocator) pair;
- fromNested(buffer, index, dim, dimensions,
- pair.getCar(), elementType, sloc, messages);
- value = pair.getCdr();
- index += stride;
- }
- for (Object el : Sequences.coerceToSequence(value)) {
- fromNested(buffer, index, dim, dimensions,
- el, elementType, sloc, messages);
- index += stride;
- }
- }
- }
- boolean deprecatedXmlEnlosedReported;
- }
|