Mercurial Hosting > luan
view src/luan/modules/parsers/LuanToString.java @ 1693:45eaaf5146f3
add backup autostart
author | Vadim Filimonov <fffilimonov@yandex.ru> |
---|---|
date | Mon, 20 Jun 2022 10:29:43 +0300 |
parents | 582384548a69 |
children | f8f5c51f5b36 |
line wrap: on
line source
package luan.modules.parsers; import java.util.List; import java.util.Map; import java.util.Set; import java.util.HashSet; import java.util.Collections; import luan.Luan; import luan.LuanTable; import luan.LuanFunction; import luan.LuanException; import luan.LuanRuntimeException; public final class LuanToString { private static final Set<String> settingsKeys = new HashSet<String>(); static { Collections.addAll(settingsKeys ,"strict" ,"number_types" ,"compressed" ,"long_strings" ,"inline" ,"no_name_keys" ); } private static void checkOptions(LuanTable options) throws LuanException { for( Map.Entry entry : options.rawIterable() ) { if( !settingsKeys.contains(entry.getKey()) ) throw new LuanException("invalid option: "+entry.getKey()); if( !(entry.getValue() instanceof Boolean) ) throw new LuanException("options values must be boolean"); } } public static class Settings implements Cloneable { public boolean strict = false; public boolean numberTypes = false; public boolean compressed = false; public boolean longStrings = false; public boolean inline = false; public boolean noNameKeys = false; void applyOptions(LuanTable options) throws LuanException { checkOptions(options); Boolean b; b = (Boolean)options.rawGet("strict"); if( b != null ) strict = b; b = (Boolean)options.rawGet("number_types"); if( b != null ) numberTypes = b; b = (Boolean)options.rawGet("compressed"); if( b != null ) compressed = b; b = (Boolean)options.rawGet("long_strings"); if( b != null ) longStrings = b; b = (Boolean)options.rawGet("inline"); if( b != null ) inline = b; b = (Boolean)options.rawGet("no_name_keys"); if( b != null ) noNameKeys = b; } public Settings cloneSettings() { try { return (Settings)clone(); } catch(CloneNotSupportedException e) { throw new RuntimeException(e); } } } private static final Settings keySettings = new Settings(); public final Settings settingsInit = new Settings(); private final Luan luan; private final LuanFunction fnOptions; private final LuanTable stack = new LuanTable(); public LuanToString() { this.luan = null; this.fnOptions = null; } public LuanToString(Luan luan) throws LuanException { this.luan = luan; this.fnOptions = null; } public LuanToString(Luan luan,LuanTable options) throws LuanException { this.luan = luan; this.fnOptions = null; settingsInit.applyOptions(options); } public LuanToString(Luan luan,LuanFunction fnOptions) throws LuanException { this.luan = luan; this.fnOptions = fnOptions; LuanTable options = getOptions(); if( options != null ) settingsInit.applyOptions(options); } private LuanTable getOptions() throws LuanException { if( fnOptions == null ) return null; Object rtn = fnOptions.call(luan,stack); if( !(rtn==null || rtn instanceof LuanTable) ) throw new LuanException("options-function must return table or nil"); return (LuanTable)rtn; } public String toString(Object obj) throws LuanException { StringBuilder sb = new StringBuilder(); toString(obj,sb,0,settingsInit); return sb.toString(); } private void toString(Object obj,StringBuilder sb,int indented,Settings settings) throws LuanException { if( obj == null ) { sb.append( "nil" ); return; } if( obj instanceof Boolean ) { sb.append( obj ); return; } if( obj instanceof Number ) { toString((Number)obj,sb,settings); return; } if( obj instanceof String ) { toString((String)obj,sb,settings); return; } if( obj instanceof LuanTable ) { toString((LuanTable)obj,sb,indented,settings); return; } if( settings.strict ) throw new LuanException("can't handle type "+Luan.type(obj)); sb.append( '<' ); sb.append( obj ); sb.append( '>' ); } private void toString(LuanTable tbl,StringBuilder sb,int indented,Settings settings) throws LuanException { if( tbl.getMetatable()!=null ) { if( settings.strict ) throw new LuanException("can't handle metatables when strict"); if( luan==null ) throw new LuanException("can't handle metatables when luan isn't set"); LuanFunction pairs = luan.getHandlerFunction("__pairs",tbl); if( pairs != null ) { sb.append( '{' ); boolean first = true; for( Object obj : tbl.iterable(luan) ) { Map.Entry entry = (Map.Entry)obj; if( settings.compressed ) { if( first ) first = false; else sb.append( ',' ); } else if( settings.inline ) { if( first ) { first = false; sb.append( ' ' ); } else sb.append( ", " ); } else { first = false; indent(sb,indented+1); } toString(entry,sb,indented+1,settings); } if( !first ) { if( settings.compressed ) { } else if( settings.inline ) { sb.append( ' ' ); } else { indent(sb,indented); } } sb.append( '}' ); return; } } List list = tbl.asList(); Map map = tbl.rawMap(); sb.append( '{' ); boolean first = true; for( Object obj : list ) { if( settings.compressed ) { if( first ) first = false; else sb.append( ',' ); } else if( settings.inline ) { if( first ) { first = false; sb.append( ' ' ); } else sb.append( ", " ); } else { indent(sb,indented+1); } toString(obj,sb,indented+1,settings); } for( Object obj : map.entrySet() ) { Map.Entry entry = (Map.Entry)obj; if( settings.compressed ) { if( first ) first = false; else sb.append( ',' ); } else if( settings.inline ) { if( first ) { first = false; sb.append( ' ' ); } else sb.append( ", " ); } else { indent(sb,indented+1); } toString(entry,sb,indented+1,settings); } if( !list.isEmpty() || !map.isEmpty() ) { if( settings.compressed ) { } else if( settings.inline ) { sb.append( ' ' ); } else { indent(sb,indented); } } sb.append( '}' ); return; } private void toString(Map.Entry entry,StringBuilder sb,int indented,Settings settings) throws LuanException { Object key = entry.getKey(); if( key instanceof String && !settings.noNameKeys && ((String)key).matches("[a-zA-Z_][a-zA-Z_0-9]*") ) { sb.append( (String)key ); } else { sb.append( '[' ); toString( key, sb, indented, keySettings ); sb.append( ']' ); } sb.append( settings.compressed ? "=" : " = " ); stack.rawAdd(key); // push LuanTable options = getOptions(); if( options != null ) { settings = settings.cloneSettings(); settings.applyOptions(options); } toString( entry.getValue(), sb, indented, settings ); stack.removeFromList(stack.rawLength()); // pop } private void indent(StringBuilder sb,int indented) { sb.append( '\n' ); for( int i=0; i<indented; i++ ) { sb.append( '\t' ); } } private void toString(Number n,StringBuilder sb,Settings settings) throws LuanException { if( settings.numberTypes ) { sb.append( n.getClass().getSimpleName().toLowerCase() ); sb.append( '(' ); } sb.append( Luan.toString(n) ); if( settings.numberTypes ) sb.append( ')' ); } public static void addNumberTypes(Luan luan,LuanTable env) { try { LuanTable module = (LuanTable)luan.require("luan:Number.luan"); env.rawPut( "double", module.fn(luan,"double") ); env.rawPut( "float", module.fn(luan,"float") ); env.rawPut( "integer", module.fn(luan,"integer") ); env.rawPut( "long", module.fn(luan,"long") ); } catch(LuanException e) { throw new LuanRuntimeException(e); } } private void toString(String s,StringBuilder sb,Settings settings) { if( settings.longStrings ) { StringBuilder start = new StringBuilder("[["); if( s.indexOf('\n') != -1 ) start.append('\n'); StringBuilder end = new StringBuilder("]]"); while( s.contains(end) ) { start.insert(1,'='); end.insert(1,'='); } sb.append(start); sb.append(s); sb.append(end); return; } sb.append( '"' ); sb.append( Luan.stringEncode(s) ); sb.append( '"' ); } }