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);
+	}
+
+}