Package parkour.edn

Source Code of parkour.edn.EdnReader$VectorReader

/**
*   Copyright (c) Rich Hickey. All rights reserved.
*   The use and distribution terms for this software are covered by the
*   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
*   which can be found in the file LICENSE at the root of this distribution.
*   By using this software in any fashion, you are agreeing to be bound by
*       the terms of this license.
*   You must not remove this notice, or any other, from this software.
**/

package parkour.edn;

import java.io.IOException;
import java.io.PushbackReader;
import java.io.Reader;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

// Bad, but not as bad as individually importing all of `clojure.lang`.
import clojure.lang.*;

public class EdnReader{

final static Keyword TAG_KEY = Keyword.intern(null, "tag");
final static Keyword LINE_KEY = Keyword.intern(null, "line");
final static Keyword COLUMN_KEY = Keyword.intern(null, "column");

static IFn[] macros = new IFn[256];
static IFn[] dispatchMacros = new IFn[256];
static Pattern symbolPat = Pattern.compile("[:]?([\\D&&[^/]].*/)?(/|[\\D&&[^/]][^/]*)");
static Pattern intPat =
        Pattern.compile(
                "([-+]?)(?:(0)|([1-9][0-9]*)|0[xX]([0-9A-Fa-f]+)|0([0-7]+)|([1-9][0-9]?)[rR]([0-9A-Za-z]+)|0[0-9]+)(N)?");
static Pattern ratioPat = Pattern.compile("([-+]?[0-9]+)/([0-9]+)");
static Pattern floatPat = Pattern.compile("([-+]?[0-9]+(\\.[0-9]*)?([eE][-+]?[0-9]+)?)(M)?");

static IFn taggedReader = new TaggedReader();

static
    {
    macros['"'] = new StringReader();
    macros[';'] = new CommentReader();
    macros['^'] = new MetaReader();
    macros['('] = new ListReader();
    macros[')'] = new UnmatchedDelimiterReader();
    macros['['] = new VectorReader();
    macros[']'] = new UnmatchedDelimiterReader();
    macros['{'] = new MapReader();
    macros['}'] = new UnmatchedDelimiterReader();
    macros['\\'] = new CharacterReader();
    macros['#'] = new DispatchReader();


    dispatchMacros['^'] = new MetaReader();
    dispatchMacros['"'] = new RegexReader();
    dispatchMacros['{'] = new SetReader();
    dispatchMacros['<'] = new UnreadableReader();
    dispatchMacros['_'] = new DiscardReader();
    dispatchMacros['\''] = new VarReader();
    }

static boolean nonConstituent(int ch){
    return ch == '@' || ch == '`' || ch == '~';
}

static public Object readString(String s, IPersistentMap opts){
    PushbackReader r = new PushbackReader(new java.io.StringReader(s));
    return read(r, opts);
}

static boolean isWhitespace(int ch){
    return Character.isWhitespace(ch) || ch == ',';
}

static void unread(PushbackReader r, int ch) {
    if(ch != -1)
        try
            {
            r.unread(ch);
            }
        catch(IOException e)
            {
            throw Util.sneakyThrow(e);
            }
}

public static class ReaderException extends RuntimeException{
    final int line;
    final int column;

    public ReaderException(int line, int column, Throwable cause){
        super(cause);
        this.line = line;
        this.column = column;
    }
}

static public int read1(Reader r){
    try
        {
        return r.read();
        }
    catch(IOException e)
        {
        throw Util.sneakyThrow(e);
        }
}

static final Keyword EOF = Keyword.intern(null,"eof");

static public Object read(PushbackReader r, IPersistentMap opts){
    return read(r,!opts.containsKey(EOF),opts.valAt(EOF),false,opts);
}

static public Object read(PushbackReader r, boolean eofIsError, Object eofValue, boolean isRecursive,
                          Object opts)
{

    try
        {
        for(; ;)
            {
            int ch = read1(r);

            while(isWhitespace(ch))
                ch = read1(r);

            if(ch == -1)
                {
                if(eofIsError)
                    throw Util.runtimeException("EOF while reading");
                return eofValue;
                }

            if(Character.isDigit(ch))
                {
                Object n = readNumber(r, (char) ch);
                if(RT.suppressRead())
                    return null;
                return n;
                }

            IFn macroFn = getMacro(ch);
            if(macroFn != null)
                {
                Object ret = macroFn.invoke(r, (char) ch, opts);
                if(RT.suppressRead())
                    return null;
                //no op macros return the reader
                if(ret == r)
                    continue;
                return ret;
                }

            if(ch == '+' || ch == '-')
                {
                int ch2 = read1(r);
                if(Character.isDigit(ch2))
                    {
                    unread(r, ch2);
                    Object n = readNumber(r, (char) ch);
                    if(RT.suppressRead())
                        return null;
                    return n;
                    }
                unread(r, ch2);
                }

            String token = readToken(r, (char) ch, true);
            if(RT.suppressRead())
                return null;
            return interpretToken(token);
            }
        }
    catch(Exception e)
        {
        if(isRecursive || !(r instanceof LineNumberingPushbackReader))
            throw Util.sneakyThrow(e);
        LineNumberingPushbackReader rdr = (LineNumberingPushbackReader) r;
        //throw Util.runtimeException(String.format("ReaderError:(%d,1) %s", rdr.getLineNumber(), e.getMessage()), e);
        throw new ReaderException(rdr.getLineNumber(), rdr.getColumnNumber(), e);
        }
}

static private String readToken(PushbackReader r, char initch, boolean leadConstituent) {
    StringBuilder sb = new StringBuilder();
    if(leadConstituent && nonConstituent(initch))
        throw Util.runtimeException("Invalid leading character: " + (char)initch);

    sb.append(initch);

    for(; ;)
        {
        int ch = read1(r);

        if(ch == -1 || isWhitespace(ch) || isTerminatingMacro(ch))
            {
            unread(r, ch);
            return sb.toString();
            }
        else if(nonConstituent(ch))
            throw Util.runtimeException("Invalid constituent character: " + (char)ch);
        sb.append((char) ch);
        }
}

static private Object readNumber(PushbackReader r, char initch) {
    StringBuilder sb = new StringBuilder();
    sb.append(initch);

    for(; ;)
        {
        int ch = read1(r);
        if(ch == -1 || isWhitespace(ch) || isMacro(ch))
            {
            unread(r, ch);
            break;
            }
        sb.append((char) ch);
        }

    String s = sb.toString();
    Object n = matchNumber(s);
    if(n == null)
        throw new NumberFormatException("Invalid number: " + s);
    return n;
}

static private int readUnicodeChar(String token, int offset, int length, int base) {
    if(token.length() != offset + length)
        throw new IllegalArgumentException("Invalid unicode character: \\" + token);
    int uc = 0;
    for(int i = offset; i < offset + length; ++i)
        {
        int d = Character.digit(token.charAt(i), base);
        if(d == -1)
            throw new IllegalArgumentException("Invalid digit: " + token.charAt(i));
        uc = uc * base + d;
        }
    return (char) uc;
}

static private int readUnicodeChar(PushbackReader r, int initch, int base, int length, boolean exact) {
    int uc = Character.digit(initch, base);
    if(uc == -1)
        throw new IllegalArgumentException("Invalid digit: " + (char) initch);
    int i = 1;
    for(; i < length; ++i)
        {
        int ch = read1(r);
        if(ch == -1 || isWhitespace(ch) || isMacro(ch))
            {
            unread(r, ch);
            break;
            }
        int d = Character.digit(ch, base);
        if(d == -1)
            throw new IllegalArgumentException("Invalid digit: " + (char) ch);
        uc = uc * base + d;
        }
    if(i != length && exact)
        throw new IllegalArgumentException("Invalid character length: " + i + ", should be: " + length);
    return uc;
}

static private Object interpretToken(String s) {
    if(s.equals("nil"))
        {
        return null;
        }
    else if(s.equals("true"))
        {
        return RT.T;
        }
    else if(s.equals("false"))
        {
        return RT.F;
        }

    Object ret = null;

    ret = matchSymbol(s);
    if(ret != null)
        return ret;

    throw Util.runtimeException("Invalid token: " + s);
}


private static Object matchSymbol(String s){
    Matcher m = symbolPat.matcher(s);
    if(m.matches())
        {
        int gc = m.groupCount();
        String ns = m.group(1);
        String name = m.group(2);
        if(ns != null && ns.endsWith(":/")
           || name.endsWith(":")
           || s.indexOf("::", 1) != -1)
            return null;
        if(s.startsWith("::"))
            {
            return null;
            }
        boolean isKeyword = s.charAt(0) == ':';
        Symbol sym = Symbol.intern(s.substring(isKeyword ? 1 : 0));
        if(isKeyword)
            return Keyword.intern(sym);
        return sym;
        }
    return null;
}


private static Object matchNumber(String s){
    Matcher m = intPat.matcher(s);
    if(m.matches())
        {
        if(m.group(2) != null)
            {
            if(m.group(8) != null)
                return BigInt.ZERO;
            return Numbers.num(0);
            }
        boolean negate = (m.group(1).equals("-"));
        String n;
        int radix = 10;
        if((n = m.group(3)) != null)
            radix = 10;
        else if((n = m.group(4)) != null)
            radix = 16;
        else if((n = m.group(5)) != null)
            radix = 8;
        else if((n = m.group(7)) != null)
            radix = Integer.parseInt(m.group(6));
        if(n == null)
            return null;
        BigInteger bn = new BigInteger(n, radix);
        if(negate)
            bn = bn.negate();
        if(m.group(8) != null)
            return BigInt.fromBigInteger(bn);
        return bn.bitLength() < 64 ?
               Numbers.num(bn.longValue())
                                   : BigInt.fromBigInteger(bn);
        }
    m = floatPat.matcher(s);
    if(m.matches())
        {
        if(m.group(4) != null)
            return new BigDecimal(m.group(1));
        return Double.parseDouble(s);
        }
    m = ratioPat.matcher(s);
    if(m.matches())
        {
        String numerator = m.group(1);
        if (numerator.startsWith("+")) numerator = numerator.substring(1);

        return Numbers.divide(Numbers.reduceBigInt(BigInt.fromBigInteger(new BigInteger(numerator))),
                              Numbers.reduceBigInt(BigInt.fromBigInteger(new BigInteger(m.group(2)))));
        }
    return null;
}

static private IFn getMacro(int ch){
    if(ch < macros.length)
        return macros[ch];
    return null;
}

static private boolean isMacro(int ch){
    return (ch < macros.length && macros[ch] != null);
}

static private boolean isTerminatingMacro(int ch){
    return (ch != '#' && ch != '\'' && isMacro(ch));
}

public static class RegexReader extends AFn{
    public Object invoke(Object reader, Object doublequote, Object opts) {
        StringBuilder sb = new StringBuilder();
        Reader r = (Reader) reader;
        for(int ch = read1(r); ch != '"'; ch = read1(r))
            {
            if(ch == -1)
                throw Util.runtimeException("EOF while reading regex");
            sb.append( (char) ch );
            if(ch == '\\'//escape
                {
                ch = read1(r);
                if(ch == -1)
                    throw Util.runtimeException("EOF while reading regex");
                sb.append( (char) ch ) ;
                }
            }
        return Pattern.compile(sb.toString());
    }
}

public static class StringReader extends AFn{
    public Object invoke(Object reader, Object doublequote, Object opts) {
        StringBuilder sb = new StringBuilder();
        Reader r = (Reader) reader;

        for(int ch = read1(r); ch != '"'; ch = read1(r))
            {
            if(ch == -1)
                throw Util.runtimeException("EOF while reading string");
            if(ch == '\\'//escape
                {
                ch = read1(r);
                if(ch == -1)
                    throw Util.runtimeException("EOF while reading string");
                switch(ch)
                    {
                    case 't':
                        ch = '\t';
                        break;
                    case 'r':
                        ch = '\r';
                        break;
                    case 'n':
                        ch = '\n';
                        break;
                    case '\\':
                        break;
                    case '"':
                        break;
                    case 'b':
                        ch = '\b';
                        break;
                    case 'f':
                        ch = '\f';
                        break;
                    case 'u':
                    {
                    ch = read1(r);
                    if (Character.digit(ch, 16) == -1)
                        throw Util.runtimeException("Invalid unicode escape: \\u" + (char) ch);
                    ch = readUnicodeChar((PushbackReader) r, ch, 16, 4, true);
                    break;
                    }
                    default:
                    {
                    if(Character.isDigit(ch))
                        {
                        ch = readUnicodeChar((PushbackReader) r, ch, 8, 3, false);
                        if(ch > 0377)
                            throw Util.runtimeException("Octal escape sequence must be in range [0, 377].");
                        }
                    else
                        throw Util.runtimeException("Unsupported escape character: \\" + (char) ch);
                    }
                    }
                }
            sb.append((char) ch);
            }
        return sb.toString();
    }
}

public static class CommentReader extends AFn{
    public Object invoke(Object reader, Object semicolon, Object opts) {
        Reader r = (Reader) reader;
        int ch;
        do
            {
            ch = read1(r);
            } while(ch != -1 && ch != '\n' && ch != '\r');
        return r;
    }

}

public static class DiscardReader extends AFn{
    public Object invoke(Object reader, Object underscore, Object opts) {
        PushbackReader r = (PushbackReader) reader;
        read(r, true, null, true, opts);
        return r;
    }
}

public static class DispatchReader extends AFn{
    public Object invoke(Object reader, Object hash, Object opts) {
        int ch = read1((Reader) reader);
        if(ch == -1)
            throw Util.runtimeException("EOF while reading character");
        IFn fn = dispatchMacros[ch];

        if(fn == null) {
            //try tagged reader
            if(Character.isLetter(ch))
                {
                unread((PushbackReader) reader, ch);
                return taggedReader.invoke(reader, ch, opts);
                }

            throw Util.runtimeException(String.format("No dispatch macro for: %c", (char) ch));
        }
        return fn.invoke(reader, ch, opts);
    }
}

public static class MetaReader extends AFn{
    public Object invoke(Object reader, Object caret, Object opts) {
        PushbackReader r = (PushbackReader) reader;
        int line = -1;
        int column = -1;
        if(r instanceof LineNumberingPushbackReader)
            {
            line = ((LineNumberingPushbackReader) r).getLineNumber();
            column = ((LineNumberingPushbackReader) r).getColumnNumber()-1;
            }
        Object meta = read(r, true, null, true, opts);
        if(meta instanceof Symbol || meta instanceof String)
            meta = RT.map(TAG_KEY, meta);
        else if (meta instanceof Keyword)
            meta = RT.map(meta, RT.T);
        else if(!(meta instanceof IPersistentMap))
            throw new IllegalArgumentException("Metadata must be Symbol,Keyword,String or Map");

        Object o = read(r, true, null, true, opts);
        if(o instanceof IMeta)
            {
            if(line != -1 && o instanceof ISeq)
                {
                meta = ((IPersistentMap) meta).assoc(LINE_KEY, line).assoc(COLUMN_KEY, column);
                }
            if(o instanceof IReference)
                {
                ((IReference)o).resetMeta((IPersistentMap) meta);
                return o;
                }
            Object ometa = RT.meta(o);
            for(ISeq s = RT.seq(meta); s != null; s = s.next()) {
            IMapEntry kv = (IMapEntry) s.first();
            ometa = RT.assoc(ometa, kv.getKey(), kv.getValue());
            }
            return ((IObj) o).withMeta((IPersistentMap) ometa);
            }
        else
            throw new IllegalArgumentException("Metadata can only be applied to IMetas");
    }

}

public static class CharacterReader extends AFn{
    public Object invoke(Object reader, Object backslash, Object opts) {
        PushbackReader r = (PushbackReader) reader;
        int ch = read1(r);
        if(ch == -1)
            throw Util.runtimeException("EOF while reading character");
        String token = readToken(r, (char) ch, false);
        if(token.length() == 1)
            return Character.valueOf(token.charAt(0));
        else if(token.equals("newline"))
            return '\n';
        else if(token.equals("space"))
            return ' ';
        else if(token.equals("tab"))
            return '\t';
        else if(token.equals("backspace"))
            return '\b';
        else if(token.equals("formfeed"))
            return '\f';
        else if(token.equals("return"))
            return '\r';
        else if(token.startsWith("u"))
            {
            char c = (char) readUnicodeChar(token, 1, 4, 16);
            if(c >= '\uD800' && c <= '\uDFFF') // surrogate code unit?
                throw Util.runtimeException("Invalid character constant: \\u" + Integer.toString(c, 16));
            return c;
            }
        else if(token.startsWith("o"))
            {
            int len = token.length() - 1;
            if(len > 3)
                throw Util.runtimeException("Invalid octal escape sequence length: " + len);
            int uc = readUnicodeChar(token, 1, len, 8);
            if(uc > 0377)
                throw Util.runtimeException("Octal escape sequence must be in range [0, 377].");
            return (char) uc;
            }
        throw Util.runtimeException("Unsupported character: \\" + token);
    }

}

public static class ListReader extends AFn{
    public Object invoke(Object reader, Object leftparen, Object opts) {
        PushbackReader r = (PushbackReader) reader;
        int line = -1;
        int column = -1;
        if(r instanceof LineNumberingPushbackReader)
            {
            line = ((LineNumberingPushbackReader) r).getLineNumber();
            column = ((LineNumberingPushbackReader) r).getColumnNumber()-1;
            }
        List list = readDelimitedList(')', r, true, opts);
        if(list.isEmpty())
            return PersistentList.EMPTY;
        IObj s = (IObj) PersistentList.create(list);
//      IObj s = (IObj) RT.seq(list);
//      if(line != -1)
//          {
//          return s.withMeta(RT.map(LINE_KEY, line, COLUMN_KEY, column));
//          }
//      else
            return s;
    }

}

public static class VectorReader extends AFn{
    public Object invoke(Object reader, Object leftparen, Object opts) {
        PushbackReader r = (PushbackReader) reader;
        return LazilyPersistentVector.create(readDelimitedList(']', r, true, opts));
    }

}

public static class MapReader extends AFn{
    public Object invoke(Object reader, Object leftparen, Object opts) {
        PushbackReader r = (PushbackReader) reader;
        Object[] a = readDelimitedList('}', r, true, opts).toArray();
        if((a.length & 1) == 1)
            throw Util.runtimeException("Map literal must contain an even number of forms");
        return RT.map(a);
    }

}

public static class SetReader extends AFn{
    public Object invoke(Object reader, Object leftbracket, Object opts) {
        PushbackReader r = (PushbackReader) reader;
        return PersistentHashSet.createWithCheck(readDelimitedList('}', r, true, opts));
    }

}

public static class UnmatchedDelimiterReader extends AFn{
    public Object invoke(Object reader, Object rightdelim, Object opts) {
        throw Util.runtimeException("Unmatched delimiter: " + rightdelim);
    }

}

public static class UnreadableReader extends AFn{
    public Object invoke(Object reader, Object leftangle, Object opts) {
        throw Util.runtimeException("Unreadable form");
    }
}

public static List readDelimitedList(char delim, PushbackReader r, boolean isRecursive, Object opts) {
    final int firstline =
            (r instanceof LineNumberingPushbackReader) ?
            ((LineNumberingPushbackReader) r).getLineNumber() : -1;

    ArrayList<Object> a = new ArrayList<Object>();

    for(; ;)
        {
        int ch = read1(r);

        while(isWhitespace(ch))
            ch = read1(r);

        if(ch == -1)
            {
            if(firstline < 0)
                throw Util.runtimeException("EOF while reading");
            else
                throw Util.runtimeException("EOF while reading, starting at line " + firstline);
            }

        if(ch == delim)
            break;

        IFn macroFn = getMacro(ch);
        if(macroFn != null)
            {
            Object mret = macroFn.invoke(r, (char) ch, opts);
            //no op macros return the reader
            if(mret != r)
                a.add(mret);
            }
        else
            {
            unread(r, ch);

            Object o = read(r, true, null, isRecursive, opts);
            if(o != r)
                a.add(o);
            }
        }


    return a;
}

public static class TaggedReader extends AFn{
    public Object invoke(Object reader, Object firstChar, Object opts){
        PushbackReader r = (PushbackReader) reader;
        Object name = read(r, true, null, false, opts);
        if (!(name instanceof Symbol))
            throw new RuntimeException("Reader tag must be a symbol");
        Symbol sym = (Symbol)name;
        return readTagged(r, sym, (IPersistentMap) opts);
    }

    static Keyword READERS = Keyword.intern(null,"readers");
    static Keyword DEFAULT = Keyword.intern(null,"default");

    private Object readTagged(PushbackReader reader, Symbol tag, IPersistentMap opts){
        Object o = read(reader, true, null, true, opts);

        ILookup readers = (ILookup)RT.get(opts, READERS);
        IFn dataReader = (IFn)RT.get(readers, tag);
        if(dataReader == null)
            dataReader = (IFn)RT.get(RT.DEFAULT_DATA_READERS.deref(),tag);
        if(dataReader == null){
            IFn defaultReader = (IFn)RT.get(opts, DEFAULT);
            if(defaultReader != null)
                return defaultReader.invoke(tag, o);
            else
                throw new RuntimeException("No reader function for tag " + tag.toString());
        }
        else
            return dataReader.invoke(o);
    }

}

public static class VarReader extends AFn {
    static final IFn REQUIRE = RT.var("clojure.core", "require");

    public Object invoke(Object reader, Object firstCchar, Object opts) {
        PushbackReader r = (PushbackReader) reader;
        Object name = read(r, true, null, false, opts);
        if (!(name instanceof Symbol))
            throw new RuntimeException("Var must be a symbol");
        Symbol sym = (Symbol)name;
        Symbol ns = Symbol.intern(sym.getNamespace());
        Symbol n = Symbol.intern(sym.getName());
        REQUIRE.invoke(ns);
        return Var.intern(ns, n);
    }
}

}
TOP

Related Classes of parkour.edn.EdnReader$VectorReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
script> ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');