changeset 720:bdd766df1c17

add json parser
author Franklin Schmidt <fschmidt@gmail.com>
date Mon, 30 May 2016 21:27:17 -0600
parents 53fc65ffa65f
children 524ea0056573
files core/src/luan/modules/Parsers.luan core/src/luan/modules/parsers/Json.java core/src/luan/modules/parsers/Theme.java
diffstat 3 files changed, 206 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/core/src/luan/modules/Parsers.luan	Mon May 30 19:19:38 2016 -0600
+++ b/core/src/luan/modules/Parsers.luan	Mon May 30 21:27:17 2016 -0600
@@ -2,12 +2,14 @@
 local BBCode = require "java:luan.modules.parsers.BBCode"
 local Csv = require "java:luan.modules.parsers.Csv"
 local Theme = require "java:luan.modules.parsers.Theme"
+local Json = require "java:luan.modules.parsers.Json"
 
 local M = {}
 
 M.bbcode_to_html = BBCode.toHtml
 M.bbcode_to_text = BBCode.toText
 M.csv_to_list = Csv.toList
+M.parse_json = Json.parse
 M.theme_to_luan = Theme.toLuan
 
 return M
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/luan/modules/parsers/Json.java	Mon May 30 21:27:17 2016 -0600
@@ -0,0 +1,204 @@
+package luan.modules.parsers;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.LinkedHashMap;
+import luan.LuanTable;
+
+
+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();
+		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') );
+		}
+		if( parser.anyOf("eE") ) {
+			parser.anyOf("+-");
+			if( !parser.inCharRange('0','9') )
+				return parser.failure(null);
+			while( parser.inCharRange('0','9') );
+		}
+		String s = parser.textFrom(start);
+		return parser.success(Double.valueOf(s));
+	}
+
+	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") );
+	}
+}
--- a/core/src/luan/modules/parsers/Theme.java	Mon May 30 19:19:38 2016 -0600
+++ b/core/src/luan/modules/parsers/Theme.java	Mon May 30 21:27:17 2016 -0600
@@ -146,10 +146,6 @@
 		return parser.success(rtn);
 	}
 
-	private void InlineSpaces() {
-		while( parser.anyOf(" \t") );
-	}
-
 	private boolean BlankLine() {
 		parser.begin();
 		while( parser.anyOf(" \t") );