Mercurial Hosting > luan
view core/src/luan/modules/parsers/Json.java @ 759:ae612dfc57cb 0.21
better handling of longs in rpc and json
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Tue, 19 Jul 2016 09:09:41 -0600 |
parents | c29d11d675fd |
children |
line wrap: on
line source
package luan.modules.parsers; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.LinkedHashMap; import java.util.Iterator; import luan.LuanTable; import luan.LuanException; public final class Json { public static Object parse(String text) throws ParseException { return new Json(text).parse(); } private final Parser parser; private Json(String text) { this.parser = new Parser(text); } private ParseException exception(String msg) { return new ParseException(parser,msg); } private Object parse() throws ParseException { spaces(); Object value = value(); spaces(); if( !parser.endOfInput() ) throw exception("unexpected text"); return value; } private Object value() throws ParseException { if( parser.match("null") ) return null; if( parser.match("true") ) return Boolean.TRUE; if( parser.match("false") ) return Boolean.FALSE; String s = string(); if( s != null ) return s; Number n = number(); if( n != null ) return n; LuanTable a = array(); if( a != null ) return a; LuanTable o = object(); if( o != null ) return o; throw exception("invalid value"); } private String string() throws ParseException { parser.begin(); if( !parser.match('"') ) return parser.failure(null); StringBuilder sb = new StringBuilder(); while( parser.anyChar() ) { char c = parser.lastChar(); switch(c) { case '"': return parser.success(sb.toString()); case '\\': if( parser.anyChar() ) { c = parser.lastChar(); switch(c) { case '"': case '\\': case '/': sb.append(c); continue; case 'b': sb.append('\b'); continue; case 'f': sb.append('\f'); continue; case 'n': sb.append('\n'); continue; case 'r': sb.append('\r'); continue; case 't': sb.append('\t'); continue; case 'u': int n = 0; for( int i=0; i<4; i++ ) { int d; if( parser.inCharRange('0','9') ) { d = parser.lastChar() - '0'; } else if( parser.inCharRange('a','f') ) { d = parser.lastChar() - 'a' + 10; } else if( parser.inCharRange('A','F') ) { d = parser.lastChar() - 'A' + 10; } else { throw exception("invalid hex digit"); } n = 16*n + d; } sb.append((char)n); continue; } } throw exception("invalid escape char"); default: sb.append(c); } } parser.failure(); throw exception("unclosed string"); } private Number number() { int start = parser.begin(); boolean isFloat = false; parser.match('-'); if( !parser.match('0') ) { if( !parser.inCharRange('1','9') ) return parser.failure(null); while( parser.inCharRange('0','9') ); } if( parser.match('.') ) { if( !parser.inCharRange('0','9') ) return parser.failure(null); while( parser.inCharRange('0','9') ); isFloat = true; } if( parser.anyOf("eE") ) { parser.anyOf("+-"); if( !parser.inCharRange('0','9') ) return parser.failure(null); while( parser.inCharRange('0','9') ); isFloat = true; } String s = parser.textFrom(start); Number n; if(isFloat) n = Double.valueOf(s); else n = Long.valueOf(s); return parser.success(n); } private LuanTable array() throws ParseException { parser.begin(); if( !parser.match('[') ) return parser.failure(null); spaces(); if( parser.match(']') ) return parser.success(new LuanTable()); List list = new ArrayList(); list.add( value() ); spaces(); while( parser.match(',') ) { spaces(); list.add( value() ); spaces(); } if( parser.match(']') ) return parser.success(new LuanTable(list)); if( parser.endOfInput() ) { parser.failure(); throw exception("unclosed array"); } throw exception("unexpected text in array"); } private LuanTable object() throws ParseException { parser.begin(); if( !parser.match('{') ) return parser.failure(null); spaces(); if( parser.match('}') ) return parser.success(new LuanTable()); Map map = new LinkedHashMap(); addEntry(map); while( parser.match(',') ) { spaces(); addEntry(map); } if( parser.match('}') ) return parser.success(new LuanTable(map)); if( parser.endOfInput() ) { parser.failure(); throw exception("unclosed object"); } throw exception("unexpected text in object"); } private void addEntry(Map map) throws ParseException { String key = string(); if( key==null ) throw exception("invalid object key"); spaces(); if( !parser.match(':') ) throw exception("':' expected"); spaces(); Object value = value(); spaces(); map.put(key,value); } private void spaces() { while( parser.anyOf(" \t\r\n") ); } public static String toString(Object obj) throws LuanException { StringBuilder sb = new StringBuilder(); toString(obj,sb); return sb.toString(); } private static void toString(Object obj,StringBuilder sb) throws LuanException { if( obj == null || obj instanceof Boolean || obj instanceof Number ) { sb.append(obj); return; } if( obj instanceof String ) { toString((String)obj,sb); return; } if( obj instanceof LuanTable ) { toString((LuanTable)obj,sb); return; } throw new LuanException("can't handle type "+obj.getClass().getName()); } private static void toString(final String s,StringBuilder sb) { sb.append('"'); for( int i=0; i<s.length(); i++ ) { char c = s.charAt(i); switch(c) { case '"': sb.append("\\\""); break; case '\\': sb.append("\\\\"); break; case '\b': sb.append("\\b"); break; case '\f': sb.append("\\f"); break; case '\n': sb.append("\\n"); break; case '\r': sb.append("\\r"); break; case '\t': sb.append("\\t"); break; default: sb.append(c); } } sb.append('"'); } private static void toString(LuanTable t,StringBuilder sb) throws LuanException { if( t.isList() ) { final List list = t.asList(); if( list.isEmpty() ) { sb.append("{}"); return; } sb.append('['); toString(list.get(0),sb); for( int i=1; i<list.size(); i++ ) { sb.append(','); toString(list.get(i),sb); } sb.append(']'); return; } sb.append('{'); Iterator<Map.Entry<Object,Object>> i = t.rawIterator(); toString(i.next(),sb); while( i.hasNext() ) { sb.append(','); toString(i.next(),sb); } sb.append('}'); } private static void toString(Map.Entry<Object,Object> entry,StringBuilder sb) throws LuanException { Object key = entry.getKey(); if( !(key instanceof String) ) throw new LuanException("table keys must be strings"); toString((String)key,sb); sb.append(':'); toString(entry.getValue(),sb); } }