Mercurial Hosting > luan
diff src/luan/modules/parsers/Json.java @ 775:1a68fc55a80c
simplify dir structure
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Fri, 26 Aug 2016 14:36:40 -0600 |
parents | core/src/luan/modules/parsers/Json.java@ae612dfc57cb |
children | 88b5b81cad4a |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/luan/modules/parsers/Json.java Fri Aug 26 14:36:40 2016 -0600 @@ -0,0 +1,313 @@ +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); + } + +}