view src/luan/modules/parsers/LuanToString.java @ 1563:8fbcc4747091

remove LuanFunction.luan
author Franklin Schmidt <fschmidt@gmail.com>
date Mon, 09 Nov 2020 01:37:57 -0700
parents b89212fd04b5
children fa066aaa068c
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.LuanException;
import luan.LuanRuntimeException;


public final class LuanToString {
	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;

		void applyOptions(LuanTable options) throws LuanException {
			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;
		}

		public Settings cloneSettings() {
			try {
				return (Settings)clone();
			} catch(CloneNotSupportedException e) {
				throw new RuntimeException(e);
			}
		}
	}
	private static final Settings keySettings = new Settings();
	private static final Set<String> settingsKeys = new HashSet<String>();
	static {
		Collections.addAll(settingsKeys,"strict","number_types","compressed","long_strings","inline");
	}

	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 final Settings settingsInit = new Settings();
	private final LuanTable subOptions;

	public LuanToString(LuanTable options,LuanTable subOptions) throws LuanException {
		this.subOptions = subOptions;
		if( options != null ) {
			checkOptions(options);
			settingsInit.applyOptions(options);
		}
		if( subOptions != null ) {
			for( Map.Entry entry : subOptions.rawIterable() ) {
/*
				if( !(entry.getKey() instanceof String) )
					throw new LuanException("sub_options keys must be strings");
*/
				if( !(entry.getValue() instanceof LuanTable) )
					throw new LuanException("sub_options keys must be tables");
				LuanTable tbl = (LuanTable)entry.getValue();
				checkOptions(tbl);
			}
		}
	}

	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 {
		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 && ((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 ? "=" : " = " );
		if( subOptions != null ) {
			LuanTable options = (LuanTable)subOptions.rawGet(key);
			if( options != null ) {
				settings = settings.cloneSettings();
				settings.applyOptions(options);
			}
		}
		toString( entry.getValue(), sb, indented, settings );
	}

	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("double") );
			env.rawPut( "float", module.fn("float") );
			env.rawPut( "integer", module.fn("integer") );
			env.rawPut( "long", module.fn("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( '"' );
	}

}