Psylla.java
package coneforest.psylla.runtime;
import coneforest.psylla.core.*;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
/**
* The Psylla interpreter launcher.
*/
public class Psylla
{
private final Interpreter interpreter;
public Psylla(final PsyllaConfig psyllaConfig)
throws PsyErrorException
{
interpreter=(psyllaConfig.scriptReader!=null)?
new Interpreter()
{
@Override
public void run()
{
interpret(psyllaConfig.scriptReader);
}
}:
new Interpreter()
{
@Override
public void run()
{
try
{
repl();
}
catch(final PsyErrorException e)
{
// NOP
}
}
};
interpreter.setWriter(psyllaConfig.outputWriter);
interpreter.setErrorWriter(psyllaConfig.errorWriter);
interpreter.setEnvironment(System.getenv());
interpreter.setShellArguments(psyllaConfig.shellArguments);
interpreter.setScriptName(psyllaConfig.scriptName);
interpreter.setRandomSeed(psyllaConfig.randomSeed);
interpreter.setClassPath(psyllaConfig.classPath);
interpreter.setLibraryPath(psyllaConfig.libraryPath);
}
/**
* The entry point to the Psylla program.
*
* @param args the command-line parameters.
*/
public static void main(final String[] args)
{
try
{
launch(System.out, System.err, args);
}
catch(final coneforest.clianthus.processor.ProcessingException ex)
{
System.err.println(ex.getLocalizedMessage());
System.err.println(Messages.getString("useHelpOption"));
System.exit(1);
}
catch(final FileNotFoundException ex)
{
System.err.println(Messages.format("badScript", ex.getLocalizedMessage()));
System.exit(2);
}
catch(final PsyErrorException e)
{
System.err.println(e.getLocalizedMessage());
System.exit(127);
}
}
/**
* Process command-line options and launches the Psylla interpreter.
*
* @param outputWriter the output writer.
* @param errorWriter the error writer.
* @param args the command-line options.
* @return the {@link Psylla} instance launched.
* @throws PsyErrorException when TODO
* @throws coneforest.clianthus.processor.ProcessingException when TODO
* @throws FileNotFoundException when TODO
*/
public static Psylla launch(final PrintWriter outputWriter, final PrintWriter errorWriter,
final String... args)
throws
PsyErrorException,
coneforest.clianthus.processor.ProcessingException,
FileNotFoundException
{
final var cli=new coneforest.clianthus.processor.Processor(
new coneforest.clianthus.option.OptionFlag("help usage h ?"),
new coneforest.clianthus.option.OptionFlag("version V"),
new coneforest.clianthus.option.OptionString("console-encoding C"),
new coneforest.clianthus.option.OptionPath("classpath cp"),
new coneforest.clianthus.option.OptionPath("librarypath lp I"),
new coneforest.clianthus.option.OptionString("eval e"),
new coneforest.clianthus.option.OptionString("locale L"),
new coneforest.clianthus.option.OptionLong("random-seed S"),
new coneforest.clianthus.option.OptionCollectorString("config cfg")
);
final int processed=cli.parse(args, 0);
Psylla.setConsoleEncoding(cli.getValue("console-encoding"));
Psylla.setLocale(cli.getValue("locale"));
if(cli.getValue("help"))
showHelp();
if(cli.getValue("version"))
showVersion();
if(!cli.<List<String>>getValue("config").isEmpty())
showConfig(cli.getValue("config"));
final var psyllaConfig=new PsyllaConfig();
final Reader scriptReader;
final String scriptName;
final String[] shellArguments;
if(cli.getValue("eval")!=null)
{
scriptName="--eval";
//psyllaConfig.setScriptName("--eval");
shellArguments=Arrays.copyOfRange(args, processed, args.length);
scriptReader=new StringReader(cli.getValue("eval"));
}
else if(processed<args.length)
{
scriptName=args[processed];
//psyllaConfig.setScriptName(args[processed]);
shellArguments=Arrays.copyOfRange(args, processed+1, args.length);
scriptReader=scriptName.equals("-")?
new InputStreamReader(System.in):
new FileReader(scriptName);
}
else
{
scriptName="--repl";
//psyllaConfig.setScriptName("--repl");
shellArguments=new String[]{};
scriptReader=null;
}
psyllaConfig.setScriptName(scriptName);
psyllaConfig.setScriptReader(scriptReader);
psyllaConfig.setOutputWriter(outputWriter);
psyllaConfig.setErrorWriter(errorWriter);
psyllaConfig.setShellArguments(shellArguments);
psyllaConfig.setRandomSeed(cli.<Long>getValue("random-seed"));
psyllaConfig.setClassPath(cli.<String[]>getValue("classpath"));
psyllaConfig.setLibraryPath(cli.<String[]>getValue("librarypath"));
final var psylla=new Psylla(psyllaConfig);
psylla.start();
return psylla;
}
public static Psylla launch(
final PrintStream outputStream, final PrintStream errorStream,
final String... args)
throws
PsyErrorException,
coneforest.clianthus.processor.ProcessingException,
FileNotFoundException
{
return launch(new PrintWriter(outputStream), new PrintWriter(errorStream), args);
}
public static void setConsoleEncoding(final String consoleEncoding)
{
final String ce=(consoleEncoding!=null)?
consoleEncoding:
Charset.defaultCharset().toString();
try
{
System.setOut(new PrintStream(System.out, true, ce));
System.setErr(new PrintStream(System.err, true, ce));
}
catch(final UnsupportedEncodingException ex)
{
System.err.println(Messages.format("unsupportedEncoding", consoleEncoding));
System.exit(1);
}
}
public static void setLocale(final String locale)
{
if(locale!=null)
Locale.setDefault(Locale.forLanguageTag(locale));
}
public void start()
{
interpreter.start();
}
public void join()
throws InterruptedException
{
interpreter.join();
}
public void join(final long millis)
throws InterruptedException
{
interpreter.join(millis);
}
private static void showHelp()
{
System.out.println(Messages.getString("help"));
System.exit(0);
}
private static void showVersion()
{
System.out.print(Messages.format("version", Version.getVersion()));
System.exit(0);
}
private static void showConfig(final List<String> patterns)
{
final var propertyNames=Config.stringPropertyNames();
for(final var pattern: patterns)
propertyNames.stream()
.sorted()
.filter(name->Globs.matches(pattern, name))
.forEach(name->System.out.println(name+"=\'"+Config.getProperty(name)+"\'"));
System.exit(0);
}
/**
* The Psylla launcher configuration.
*/
private static class PsyllaConfig
{
private Reader scriptReader;
private String scriptName;
private PrintWriter outputWriter;
private PrintWriter errorWriter;
private String[] shellArguments;
private Long randomSeed;
private String[] classPath, libraryPath;
public PsyllaConfig()
{
}
/**
* Sets the script reader.
*
* @param scriptReader the script reader.
*/
private void setScriptReader(final Reader scriptReader)
{
this.scriptReader=scriptReader;
}
/**
* Sets the output writer.
*
* @param outputWriter the output writer.
*/
private void setOutputWriter(final PrintWriter outputWriter)
{
this.outputWriter=outputWriter;
}
/**
* Sets the error writer.
*
* @param errorWriter the error writer.
*/
private void setErrorWriter(final PrintWriter errorWriter)
{
this.errorWriter=errorWriter;
}
/**
* Sets the script name.
*
* @param scriptName the script name.
*/
private void setScriptName(final String scriptName)
{
this.scriptName=scriptName;
}
/**
* Sets the shell arguments.
*
* @param shellArguments the shell arguments.
*/
private void setShellArguments(final String[] shellArguments)
{
this.shellArguments=shellArguments;
}
/**
* Sets the random seed.
*
* @param randomSeed the random seed.
*/
private void setRandomSeed(final Long randomSeed)
{
this.randomSeed=randomSeed;
}
private void setClassPath(final String[] classPath)
{
this.classPath=classPath;
}
private void setLibraryPath(final String[] libraryPath)
{
this.libraryPath=libraryPath;
}
}
}