changeset 1447:851b9a48cc44

Luan.parse
author Franklin Schmidt <fschmidt@gmail.com>
date Tue, 18 Feb 2020 14:54:35 -0700
parents f2082e9aeaa9
children 6fc083e1d08c
files src/luan/LuanTable.java src/luan/modules/Luan.luan src/luan/modules/lucene/LuceneIndex.java src/luan/modules/lucene/PostgresBackup.java src/luan/modules/parsers/LuanParser.java
diffstat 5 files changed, 312 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/src/luan/LuanTable.java	Tue Feb 11 13:38:55 2020 -0700
+++ b/src/luan/LuanTable.java	Tue Feb 18 14:54:35 2020 -0700
@@ -279,6 +279,14 @@
 		mapToList();
 	}
 
+	public void rawAdd(Object value) {
+		check();
+		if( value==null )
+			throw new IllegalArgumentException("can't insert a nil value");
+		list().add(value);
+		mapToList();
+	}
+
 	public Object removeFromList(int pos) {
 		check();
 		return list().remove(pos-1);
--- a/src/luan/modules/Luan.luan	Tue Feb 11 13:38:55 2020 -0700
+++ b/src/luan/modules/Luan.luan	Tue Feb 18 14:54:35 2020 -0700
@@ -1,5 +1,6 @@
 require "java"
 local BasicLuan = require "java:luan.modules.BasicLuan"
+local LuanParser = require "java:luan.modules.parsers.LuanParser"
 local Boot = require "luan:Boot.luan"
 
 local Luan = {}
@@ -14,6 +15,7 @@
 Luan.load_file = load_file
 Luan.new_error = BasicLuan.new_error
 Luan.pairs = BasicLuan.pairs
+Luan.parse = LuanParser.parse
 Luan.pcall = BasicLuan.pcall
 Luan.range = BasicLuan.range
 Luan.raw_equal = BasicLuan.raw_equal
--- a/src/luan/modules/lucene/LuceneIndex.java	Tue Feb 11 13:38:55 2020 -0700
+++ b/src/luan/modules/lucene/LuceneIndex.java	Tue Feb 18 14:54:35 2020 -0700
@@ -846,7 +846,7 @@
 	}
 
 	public void restore_from_postgres()
-		throws IOException, LuanException, SQLException
+		throws IOException, LuanException, SQLException, ParseException
 	{
 		if( postgresBackup!=null && wasCreated && !postgresBackup.wasCreated ) {
 			luanLogger.error("restoring from postgres");
@@ -855,7 +855,7 @@
 	}
 
 	public void force_restore_from_postgres()
-		throws IOException, LuanException, SQLException
+		throws IOException, LuanException, SQLException, ParseException
 	{
 		luanLogger.warn("start restore_from_postgres");
 		if( postgresBackup==null )
@@ -890,7 +890,7 @@
 		writer.addDocument(toLucene(doc,null));
 	}
 
-	public void check(LuanFunction completer) throws IOException, SQLException, LuanException {
+	public void check(LuanFunction completer) throws IOException, SQLException, LuanException, ParseException {
 		boolean hasPostgres = postgresBackup != null;
 		String msg = "start check";
 		if( hasPostgres )
@@ -905,7 +905,7 @@
 	}
 
 	private void checkPostgres(LuanFunction completer)
-		throws IOException, SQLException, LuanException
+		throws IOException, SQLException, LuanException, ParseException
 	{
 		//luanLogger.info("start postgres check");
 		final PostgresBackup.Checker postgresChecker = postgresBackup.newChecker();
@@ -968,7 +968,7 @@
 	}
 
 	private void checkPostgres(LuanFunction completer,PostgresBackup.Checker postgresChecker,LuanToString lts,long id)
-		throws IOException, SQLException, LuanException
+		throws IOException, SQLException, LuanException, ParseException
 	{
 		//luanLogger.info("check id "+id);
 		writeLock.lock();
--- a/src/luan/modules/lucene/PostgresBackup.java	Tue Feb 11 13:38:55 2020 -0700
+++ b/src/luan/modules/lucene/PostgresBackup.java	Tue Feb 18 14:54:35 2020 -0700
@@ -17,6 +17,8 @@
 import luan.LuanException;
 import luan.modules.Utils;
 import luan.modules.parsers.LuanToString;
+import luan.modules.parsers.LuanParser;
+import goodjava.parser.ParseException;
 import goodjava.logging.Logger;
 import goodjava.logging.LoggerFactory;
 
@@ -156,26 +158,15 @@
 		con.setAutoCommit(true);
 	}
 
-	private static LuanTable newEnv() {
-		LuanTable env = new LuanTable(new Luan());
-		LuanToString.addNumberTypes(env);
-		return env;
-	}
-
-	private static Object eval(String s,LuanTable env) throws LuanException {
-		LuanFunction fn = env.luan().load( "return "+s, "PostgresBackup", false, env );
-		return fn.call();
-	}
-
 	void restoreLucene(LuceneIndex li)
-		throws LuanException, IOException, SQLException
+		throws LuanException, IOException, SQLException, ParseException
 	{
-		LuanTable env = newEnv();
+		Luan luan = new Luan();
 		Statement stmt = con.createStatement();
 		ResultSet rs = stmt.executeQuery("select data from lucene");
 		while( rs.next() ) {
 			String data = rs.getString("data");
-			LuanTable doc = (LuanTable)eval(data,env);
+			LuanTable doc = (LuanTable)LuanParser.parse(luan,data);
 			li.restore(doc);
 		}
 		stmt.close();
@@ -195,7 +186,7 @@
 	final class Checker {
 		private final Connection con;
 		private final PreparedStatement pstmt;
-		private final LuanTable env = newEnv();
+		private final Luan luan = new Luan();
 
 		Checker() throws SQLException {
 			con = newConnection();
@@ -222,13 +213,13 @@
 			return ids;
 		}
 
-		LuanTable getDoc(long id) throws SQLException, LuanException {
+		LuanTable getDoc(long id) throws SQLException, ParseException {
 			pstmt.setLong(1,id);
 			ResultSet rs = pstmt.executeQuery();
 			if( !rs.next() )
 				return null;
 			String data = rs.getString("data");
-			LuanTable doc = (LuanTable)eval(data,env);
+			LuanTable doc = (LuanTable)LuanParser.parse(luan,data);
 			return doc;
 		}
 	}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/luan/modules/parsers/LuanParser.java	Tue Feb 18 14:54:35 2020 -0700
@@ -0,0 +1,289 @@
+package luan.modules.parsers;
+
+import goodjava.parser.Parser;
+import goodjava.parser.ParseException;
+import luan.Luan;
+import luan.LuanTable;
+import luan.LuanException;
+
+
+public final class LuanParser {
+
+	public static Object parse(Luan luan,String text) throws ParseException {
+		return new LuanParser(luan,text).parse();
+	}
+
+	private static final Object NULL = new Object();
+	private final Luan luan;
+	private final Parser parser;
+
+	private LuanParser(Luan luan,String text) {
+		this.luan = luan;
+		this.parser = new Parser(text);
+	}
+
+	private ParseException exception(String msg) {
+		return new ParseException(parser,msg);
+	}
+
+	private Object parse() throws ParseException {
+		do { spaces(); } while( endOfLine() );
+		Object value = requiredValue();
+		do { spaces(); } while( endOfLine() );
+		if( !parser.endOfInput() )
+			throw exception("unexpected text");
+		return value;
+	}
+
+	private Object requiredValue() throws ParseException {
+		Object value = value();
+		if( value == null )
+			throw exception("invalid value");
+		if( value == NULL )
+			return null;
+		return value;
+	}
+
+	private Object value() throws ParseException {
+		if( parser.match("nil") )
+			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 tbl = table();
+		if( tbl != null )
+			return tbl;
+		return null;
+	}
+
+	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() {
+		parser.begin();
+		if( parser.match("double") ) {
+			Number n = inParens();
+			if( n==null )
+				return parser.failure(null);
+			n = Luan.asDouble(n);
+			if( n==null )
+				return parser.failure(null);
+			return n;
+		} else if( parser.match("float") ) {
+			Number n = inParens();
+			if( n==null )
+				return parser.failure(null);
+			n = Luan.asFloat(n);
+			if( n==null )
+				return parser.failure(null);
+			return n;
+		} else if( parser.match("integer") ) {
+			Number n = inParens();
+			if( n==null )
+				return parser.failure(null);
+			n = Luan.asInteger(n);
+			if( n==null )
+				return parser.failure(null);
+			return n;
+		} else if( parser.match("long") ) {
+			Number n = inParens();
+			if( n==null )
+				return parser.failure(null);
+			n = Luan.asLong(n);
+			if( n==null )
+				return parser.failure(null);
+			return n;
+		} else {
+			Number n = untypedNumber();
+			if( n != null )
+				return parser.success(n);
+			else
+				return parser.failure(null);
+		}
+	}
+
+	private Number inParens() {
+		spaces();
+		if( !parser.match('(') )
+			return null;
+		spaces();
+		Number n = untypedNumber();
+		if( n==null )
+			return null;
+		spaces();
+		if( !parser.match(')') )
+			return null;
+		return n;
+	}
+
+	private Number untypedNumber() {
+		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 table() throws ParseException {
+		parser.begin();
+		if( !parser.match('{') )
+			return parser.failure(null);
+		LuanTable tbl = new LuanTable(luan);
+		do {
+			spaces();
+			Object obj = value();
+			if( obj != null ) {
+				if( obj != NULL )
+					tbl.rawAdd(obj);
+				spaces();
+				continue;
+			}
+			Object key = key();
+			if( key != null ) {
+				spaces();
+				requiredMatch('=');
+				spaces();
+				Object value = requiredValue();
+				spaces();
+				try {
+					tbl.rawPut(key,value);
+				} catch(LuanException e) {
+					throw new RuntimeException(e);
+				}
+			}
+		} while( fieldSep() );
+		requiredMatch('}');
+		return parser.success(tbl);
+	}
+
+	private Object key() throws ParseException {
+		if( parser.match('[') ) {
+			spaces();
+			Object key = requiredValue();
+			spaces();
+			requiredMatch(']');
+			return key;
+		}
+		int start = parser.currentIndex();
+		if( nameFirstChar() ) {
+			while( nameChar() );
+			return parser.textFrom(start);
+		}
+		return null;
+	}
+
+	private boolean nameChar() {
+		return nameFirstChar() || parser.inCharRange('0','9');
+	}
+
+	private boolean nameFirstChar() {
+		return parser.inCharRange('a','z') || parser.inCharRange('A','Z') || parser.match('_');
+	}
+
+	private boolean fieldSep() throws ParseException {
+		return parser.anyOf(",;") || endOfLine();
+	}
+
+	private boolean endOfLine() {
+		return parser.match( "\r\n" ) || parser.match( '\r' ) || parser.match( '\n' );
+	}
+
+	private void requiredMatch(char c) throws ParseException {
+		if( !parser.match(c) )
+			throw exception("'"+c+"' expected");
+	}
+
+	private void spaces() {
+		while( parser.anyOf(" \t") );
+	}
+
+}