comparison src/luan/modules/parsers/LuanToString.java @ 1553:26c51acf00f3

improve stringify
author Franklin Schmidt <fschmidt@gmail.com>
date Tue, 06 Oct 2020 19:31:57 -0600
parents 46d4baaad64d
children 83249ce59d13
comparison
equal deleted inserted replaced
1552:46d4baaad64d 1553:26c51acf00f3
1 package luan.modules.parsers; 1 package luan.modules.parsers;
2 2
3 import java.util.List; 3 import java.util.List;
4 import java.util.Map; 4 import java.util.Map;
5 import java.util.Set; 5 import java.util.Set;
6 import java.util.HashSet;
6 import java.util.Collections; 7 import java.util.Collections;
7 import luan.Luan; 8 import luan.Luan;
8 import luan.LuanTable; 9 import luan.LuanTable;
9 import luan.LuanException; 10 import luan.LuanException;
10 import luan.LuanRuntimeException; 11 import luan.LuanRuntimeException;
11 12
12 13
13 public final class LuanToString { 14 public final class LuanToString {
14 public boolean strict = false; 15 public static class Settings implements Cloneable {
15 public boolean numberTypes = false; 16 public boolean strict = false;
16 public boolean compressed = false; 17 public boolean numberTypes = false;
17 public boolean useLongStrings = false; 18 public boolean compressed = false;
18 public Set longStringKeys = Collections.emptySet(); 19 public boolean longStrings = false;
20 public boolean inline = false;
21
22 void applyOptions(LuanTable options) throws LuanException {
23 Boolean b;
24 b = (Boolean)options.get("strict");
25 if( b != null )
26 strict = b;
27 b = (Boolean)options.get("number_types");
28 if( b != null )
29 numberTypes = b;
30 b = (Boolean)options.get("compressed");
31 if( b != null )
32 compressed = b;
33 b = (Boolean)options.get("long_strings");
34 if( b != null )
35 longStrings = b;
36 b = (Boolean)options.get("inline");
37 if( b != null )
38 inline = b;
39 }
40
41 public Settings cloneSettings() {
42 try {
43 return (Settings)clone();
44 } catch(CloneNotSupportedException e) {
45 throw new RuntimeException(e);
46 }
47 }
48 }
49 private static final Settings keySettings = new Settings();
50 private static final Set<String> settingsKeys = new HashSet<String>();
51 static {
52 Collections.addAll(settingsKeys,"strict","number_types","compressed","long_strings","inline");
53 }
54
55 private static void checkOptions(LuanTable options) throws LuanException {
56 for( Map.Entry entry : options.iterable() ) {
57 if( !settingsKeys.contains(entry.getKey()) )
58 throw new LuanException("invalid option: "+entry.getKey());
59 if( !(entry.getValue() instanceof Boolean) )
60 throw new LuanException("options values must be boolean");
61 }
62 }
63
64 public final Settings settingsInit = new Settings();
65 private final LuanTable subOptions;
66
67 public LuanToString(LuanTable options,LuanTable subOptions) throws LuanException {
68 this.subOptions = subOptions;
69 if( options != null ) {
70 checkOptions(options);
71 settingsInit.applyOptions(options);
72 }
73 if( subOptions != null ) {
74 for( Map.Entry entry : subOptions.iterable() ) {
75 /*
76 if( !(entry.getKey() instanceof String) )
77 throw new LuanException("sub_options keys must be strings");
78 */
79 if( !(entry.getValue() instanceof LuanTable) )
80 throw new LuanException("sub_options keys must be tables");
81 LuanTable tbl = (LuanTable)entry.getValue();
82 checkOptions(tbl);
83 }
84 }
85 }
19 86
20 public String toString(Object obj) throws LuanException { 87 public String toString(Object obj) throws LuanException {
21 StringBuilder sb = new StringBuilder(); 88 StringBuilder sb = new StringBuilder();
22 toString(obj,sb,0,useLongStrings); 89 toString(obj,sb,0,settingsInit);
23 return sb.toString(); 90 return sb.toString();
24 } 91 }
25 92
26 private void toString(Object obj,StringBuilder sb,int indented,boolean longStrings) throws LuanException { 93 private void toString(Object obj,StringBuilder sb,int indented,Settings settings) throws LuanException {
27 if( obj == null ) { 94 if( obj == null ) {
28 sb.append( "nil" ); 95 sb.append( "nil" );
29 return; 96 return;
30 } 97 }
31 if( obj instanceof Boolean ) { 98 if( obj instanceof Boolean ) {
32 sb.append( obj ); 99 sb.append( obj );
33 return; 100 return;
34 } 101 }
35 if( obj instanceof Number ) { 102 if( obj instanceof Number ) {
36 toString((Number)obj,sb); 103 toString((Number)obj,sb,settings);
37 return; 104 return;
38 } 105 }
39 if( obj instanceof String ) { 106 if( obj instanceof String ) {
40 toString((String)obj,sb,longStrings); 107 toString((String)obj,sb,settings);
41 return; 108 return;
42 } 109 }
43 if( obj instanceof LuanTable ) { 110 if( obj instanceof LuanTable ) {
44 toString((LuanTable)obj,sb,indented,longStrings); 111 toString((LuanTable)obj,sb,indented,settings);
45 return; 112 return;
46 } 113 }
47 if( strict ) 114 if( settings.strict )
48 throw new LuanException("can't handle type "+Luan.type(obj)); 115 throw new LuanException("can't handle type "+Luan.type(obj));
49 sb.append( '<' ); 116 sb.append( '<' );
50 sb.append( obj ); 117 sb.append( obj );
51 sb.append( '>' ); 118 sb.append( '>' );
52 } 119 }
53 120
54 private void toString(LuanTable tbl,StringBuilder sb,int indented,boolean longStrings) throws LuanException { 121 private void toString(LuanTable tbl,StringBuilder sb,int indented,Settings settings) throws LuanException {
55 List list = tbl.asList(); 122 List list = tbl.asList();
56 Map map = tbl.rawMap(); 123 Map map = tbl.rawMap();
57 sb.append( '{' ); 124 sb.append( '{' );
58 boolean first = true; 125 boolean first = true;
59 for( Object obj : list ) { 126 for( Object obj : list ) {
60 if( !compressed ) 127 if( settings.compressed ) {
128 if( first )
129 first = false;
130 else
131 sb.append( ',' );
132 } else if( settings.inline ) {
133 if( first ) {
134 first = false;
135 sb.append( ' ' );
136 } else
137 sb.append( ", " );
138 } else {
61 indent(sb,indented+1); 139 indent(sb,indented+1);
62 else if( first ) 140 }
63 first = false; 141 toString(obj,sb,indented+1,settings);
64 else
65 sb.append( ',' );
66 toString(obj,sb,indented+1,longStrings);
67 } 142 }
68 for( Object obj : map.entrySet() ) { 143 for( Object obj : map.entrySet() ) {
69 Map.Entry entry = (Map.Entry)obj; 144 Map.Entry entry = (Map.Entry)obj;
70 if( !compressed ) 145 if( settings.compressed ) {
146 if( first )
147 first = false;
148 else
149 sb.append( ',' );
150 } else if( settings.inline ) {
151 if( first ) {
152 first = false;
153 sb.append( ' ' );
154 } else
155 sb.append( ", " );
156 } else {
71 indent(sb,indented+1); 157 indent(sb,indented+1);
72 else if( first ) 158 }
73 first = false; 159 toString(entry,sb,indented+1,settings);
74 else 160 }
75 sb.append( ',' ); 161 if( !list.isEmpty() || !map.isEmpty() ) {
76 toString(entry,sb,indented+1); 162 if( settings.compressed ) {
77 } 163 } else if( settings.inline ) {
78 if( !compressed && (!list.isEmpty() || !map.isEmpty()) ) 164 sb.append( ' ' );
79 indent(sb,indented); 165 } else {
166 indent(sb,indented);
167 }
168 }
80 sb.append( '}' ); 169 sb.append( '}' );
81 return; 170 return;
82 } 171 }
83 172
84 private void toString(Map.Entry entry,StringBuilder sb,int indented) throws LuanException { 173 private void toString(Map.Entry entry,StringBuilder sb,int indented,Settings settings) throws LuanException {
85 Object key = entry.getKey(); 174 Object key = entry.getKey();
86 if( key instanceof String && ((String)key).matches("[a-zA-Z_][a-zA-Z_0-9]*") ) { 175 if( key instanceof String && ((String)key).matches("[a-zA-Z_][a-zA-Z_0-9]*") ) {
87 sb.append( (String)key ); 176 sb.append( (String)key );
88 } else { 177 } else {
89 sb.append( '[' ); 178 sb.append( '[' );
90 toString( key, sb, indented, false ); 179 toString( key, sb, indented, keySettings );
91 sb.append( ']' ); 180 sb.append( ']' );
92 } 181 }
93 sb.append( compressed ? "=" : " = " ); 182 sb.append( settings.compressed ? "=" : " = " );
94 toString( entry.getValue(), sb, indented, longStringKeys.contains(key) ); 183 LuanTable options = (LuanTable)subOptions.get(key);
184 if( options != null ) {
185 settings = settings.cloneSettings();
186 settings.applyOptions(options);
187 }
188 toString( entry.getValue(), sb, indented, settings );
95 } 189 }
96 190
97 private void indent(StringBuilder sb,int indented) { 191 private void indent(StringBuilder sb,int indented) {
98 sb.append( '\n' ); 192 sb.append( '\n' );
99 for( int i=0; i<indented; i++ ) { 193 for( int i=0; i<indented; i++ ) {
100 sb.append( '\t' ); 194 sb.append( '\t' );
101 } 195 }
102 } 196 }
103 197
104 private void toString(Number n,StringBuilder sb) throws LuanException { 198 private void toString(Number n,StringBuilder sb,Settings settings) throws LuanException {
105 if( numberTypes ) { 199 if( settings.numberTypes ) {
106 sb.append( n.getClass().getSimpleName().toLowerCase() ); 200 sb.append( n.getClass().getSimpleName().toLowerCase() );
107 sb.append( '(' ); 201 sb.append( '(' );
108 } 202 }
109 sb.append( Luan.toString(n) ); 203 sb.append( Luan.toString(n) );
110 if( numberTypes ) 204 if( settings.numberTypes )
111 sb.append( ')' ); 205 sb.append( ')' );
112 } 206 }
113 207
114 public static void addNumberTypes(LuanTable env) { 208 public static void addNumberTypes(LuanTable env) {
115 try { 209 try {
121 } catch(LuanException e) { 215 } catch(LuanException e) {
122 throw new LuanRuntimeException(e); 216 throw new LuanRuntimeException(e);
123 } 217 }
124 } 218 }
125 219
126 private void toString(String s,StringBuilder sb,boolean longStrings) { 220 private void toString(String s,StringBuilder sb,Settings settings) {
127 if( longStrings ) { 221 if( settings.longStrings ) {
128 StringBuilder start = new StringBuilder("[["); 222 StringBuilder start = new StringBuilder("[[");
129 if( s.indexOf('\n') != -1 ) 223 if( s.indexOf('\n') != -1 )
130 start.append('\n'); 224 start.append('\n');
131 StringBuilder end = new StringBuilder("]]"); 225 StringBuilder end = new StringBuilder("]]");
132 while( s.contains(end) ) { 226 while( s.contains(end) ) {