use of com.laytonsmith.core.constructs.IVariable in project CommandHelper by EngineHub.
the class Interpreter method execute.
/**
* This executes an entire script. The cmdline_prompt_event is first triggered (if used) and if the event is
* cancelled, nothing happens.
*
* @param script
* @param args
* @param fromFile
* @throws ConfigCompileException
* @throws IOException
*/
public void execute(String script, List<String> args, File fromFile) throws ConfigCompileException, IOException, ConfigCompileGroupException {
CmdlineEvents.cmdline_prompt_input.CmdlinePromptInput input = new CmdlineEvents.cmdline_prompt_input.CmdlinePromptInput(script, inShellMode);
EventUtils.TriggerListener(Driver.CMDLINE_PROMPT_INPUT, "cmdline_prompt_input", input);
if (input.isCancelled()) {
return;
}
ctrlCcount = 0;
if ("exit".equals(script)) {
if (inShellMode) {
inShellMode = false;
return;
}
pl(YELLOW + "Use exit() if you wish to exit.");
return;
}
if ("help".equals(script)) {
pl(getHelpMsg());
return;
}
if (fromFile == null) {
fromFile = new File("Interpreter");
}
boolean localShellMode = false;
if (!inShellMode && script.startsWith("$$")) {
localShellMode = true;
script = script.substring(2);
}
if (inShellMode || localShellMode) {
// Wrap this in shell_adv
if (doBuiltin(script)) {
return;
}
List<String> shellArgs = StringUtils.ArgParser(script);
List<String> escapedArgs = new ArrayList<>();
for (String arg : shellArgs) {
escapedArgs.add(new CString(arg, Target.UNKNOWN).getQuote());
}
script = "shell_adv(" + "array(" + StringUtils.Join(escapedArgs, ",") + ")," + "array(" + "'stdout':closure(@l){sys_out(@l);}," + "'stderr':closure(@l){sys_err(@l);})" + ");";
}
isExecuting = true;
ProfilePoint compile = env.getEnv(GlobalEnv.class).GetProfiler().start("Compilation", LogLevel.VERBOSE);
final ParseTree tree;
try {
TokenStream stream = MethodScriptCompiler.lex(script, fromFile, true);
tree = MethodScriptCompiler.compile(stream);
} finally {
compile.stop();
}
// Environment env = Environment.createEnvironment(this.env.getEnv(GlobalEnv.class));
final List<Variable> vars = new ArrayList<>();
if (args != null) {
// Build the @arguments variable, the $ vars, and $ itself. Note that
// we have special handling for $0, that is the script name, like bash.
// However, it doesn't get added to either $ or @arguments, due to the
// uncommon use of it.
StringBuilder finalArgument = new StringBuilder();
CArray arguments = new CArray(Target.UNKNOWN);
{
// Set the $0 argument
Variable v = new Variable("$0", "", Target.UNKNOWN);
v.setVal(fromFile.toString());
v.setDefault(fromFile.toString());
vars.add(v);
}
for (int i = 0; i < args.size(); i++) {
String arg = args.get(i);
if (i > 0) {
finalArgument.append(" ");
}
Variable v = new Variable("$" + Integer.toString(i + 1), "", Target.UNKNOWN);
v.setVal(new CString(arg, Target.UNKNOWN));
v.setDefault(arg);
vars.add(v);
finalArgument.append(arg);
arguments.push(new CString(arg, Target.UNKNOWN), Target.UNKNOWN);
}
Variable v = new Variable("$", "", false, true, Target.UNKNOWN);
v.setVal(new CString(finalArgument.toString(), Target.UNKNOWN));
v.setDefault(finalArgument.toString());
vars.add(v);
env.getEnv(GlobalEnv.class).GetVarList().set(new IVariable(CArray.TYPE, "@arguments", arguments, Target.UNKNOWN));
}
try {
ProfilePoint p = this.env.getEnv(GlobalEnv.class).GetProfiler().start("Interpreter Script", LogLevel.ERROR);
try {
final MutableObject<Throwable> wasThrown = new MutableObject<>();
scriptThread = new Thread(new Runnable() {
@Override
public void run() {
try {
MethodScriptCompiler.execute(tree, env, new MethodScriptComplete() {
@Override
public void done(String output) {
if (System.console() != null && !"".equals(output.trim())) {
StreamUtils.GetSystemOut().println(output);
}
}
}, null, vars);
} catch (CancelCommandException e) {
// Nothing, though we could have been Ctrl+C cancelled, so we need to reset
// the interrupt flag. But we do that unconditionally below, in the finally,
// in the other thread.
} catch (ConfigRuntimeException e) {
ConfigRuntimeException.HandleUncaughtException(e, env);
// No need for the full stack trace
if (System.console() == null) {
System.exit(1);
}
} catch (NoClassDefFoundError e) {
StreamUtils.GetSystemErr().println(RED + Main.getNoClassDefFoundErrorMessage(e) + reset());
StreamUtils.GetSystemErr().println("Since you're running from standalone interpreter mode, this is not a fatal error, but one of the functions you just used required" + " an actual backing engine that isn't currently loaded. (It still might fail even if you load the engine though.) You simply won't be" + " able to use that function here.");
if (System.console() == null) {
System.exit(1);
}
} catch (InvalidEnvironmentException ex) {
StreamUtils.GetSystemErr().println(RED + ex.getMessage() + " " + ex.getData() + "() cannot be used in this context.");
if (System.console() == null) {
System.exit(1);
}
} catch (RuntimeException e) {
pl(RED + e.toString());
e.printStackTrace(StreamUtils.GetSystemErr());
if (System.console() == null) {
System.exit(1);
}
}
}
}, "MethodScript-Main");
scriptThread.start();
try {
scriptThread.join();
} catch (InterruptedException ex) {
//
}
} finally {
p.stop();
}
} finally {
env.getEnv(GlobalEnv.class).SetInterrupt(false);
isExecuting = false;
}
}
use of com.laytonsmith.core.constructs.IVariable in project CommandHelper by EngineHub.
the class OptimizationUtilities method optimize0.
private static String optimize0(ParseTree node) {
if (node.getData() instanceof CFunction) {
StringBuilder b = new StringBuilder();
boolean first = true;
b.append(((CFunction) node.getData()).val()).append("(");
for (ParseTree child : node.getChildren()) {
if (!first) {
b.append(",");
}
first = false;
b.append(optimize0(child));
}
b.append(")");
return b.toString();
} else if (node.getData() instanceof CString) {
// strings
return new StringBuilder().append("'").append(node.getData().val().replaceAll("\t", "\\t").replaceAll("\n", "\\n").replace("\\", "\\\\").replace("'", "\\'")).append("'").toString();
} else if (node.getData() instanceof IVariable) {
return ((IVariable) node.getData()).getVariableName();
} else if (node.getData() instanceof Variable) {
return ((Variable) node.getData()).getVariableName();
} else if (node.getData() instanceof CSlice) {
return node.getData().val();
} else if (node.getData() instanceof CArray) {
// It's a hardcoded array. This only happens in the course of optimization, if
// the optimizer adds a new array. We still need to handle it appropriately though.
// The values in the array will be constant, guaranteed.
StringBuilder b = new StringBuilder();
b.append("array(");
boolean first = true;
CArray n = (CArray) node.getData();
for (String key : n.stringKeySet()) {
if (!first) {
b.append(",");
}
first = false;
b.append(optimize0(new ParseTree(n.get(key, Target.UNKNOWN), node.getFileOptions())));
}
b.append(")");
return b.toString();
} else {
// static
return node.getData().toString();
}
}
use of com.laytonsmith.core.constructs.IVariable in project CommandHelper by EngineHub.
the class CompositeFunction method exec.
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
ParseTree tree;
if (!cachedScripts.containsKey(this.getClass())) {
try {
String script = script();
tree = MethodScriptCompiler.compile(MethodScriptCompiler.lex(script, null, true)).getChildAt(0);
} catch (ConfigCompileException | ConfigCompileGroupException ex) {
// This is really bad.
throw new Error(ex);
}
if (cacheCompile()) {
cachedScripts.put(this.getClass(), tree);
}
} else {
tree = cachedScripts.get(this.getClass());
}
GlobalEnv env = environment.getEnv(GlobalEnv.class);
IVariableList oldVariables = env.GetVarList();
IVariableList newVariables = new IVariableList();
newVariables.set(new IVariable(CClassType.get("array"), "@arguments", new CArray(t, args.length, args), t));
env.SetVarList(newVariables);
Construct ret = CVoid.VOID;
try {
env.GetScript().eval(tree, environment);
} catch (FunctionReturnException ex) {
ret = ex.getReturn();
}
env.SetVarList(oldVariables);
return ret;
}
use of com.laytonsmith.core.constructs.IVariable in project CommandHelper by EngineHub.
the class MethodScriptCompiler method optimize.
/**
* Recurses down into the tree, attempting to optimize where possible. A few things have strong coupling, for
* information on these items, see the documentation included in the source.
*
* @param tree
* @return
*/
private static void optimize(ParseTree tree, Stack<List<Procedure>> procs, Set<ConfigCompileException> compilerErrors) {
if (tree.isOptimized()) {
// Don't need to re-run this
return;
}
// }
if (!(tree.getData() instanceof CFunction)) {
// There's no way to optimize something that's not a function
return;
}
// If it is a proc definition, we need to go ahead and see if we can add it to the const proc stack
if (tree.getData().val().equals("proc")) {
procs.push(new ArrayList<Procedure>());
}
CFunction cFunction = (CFunction) tree.getData();
Function func;
try {
func = (Function) FunctionList.getFunction(cFunction);
} catch (ConfigCompileException e) {
func = null;
}
if (func != null) {
if (func.getClass().getAnnotation(nolinking.class) != null) {
// It's an unlinking function, so we need to stop at this point
return;
}
}
if (cFunction instanceof CIdentifier) {
// Add the child to the identifier
ParseTree c = ((CIdentifier) cFunction).contained();
tree.addChild(c);
c.getData().setWasIdentifier(true);
}
List<ParseTree> children = tree.getChildren();
if (func instanceof Optimizable && ((Optimizable) func).optimizationOptions().contains(OptimizationOption.PRIORITY_OPTIMIZATION)) {
// would cause an error, even though the user did in fact provide code in that section.
try {
((Optimizable) func).optimizeDynamic(tree.getTarget(), children, tree.getFileOptions());
} catch (ConfigCompileException ex) {
// If an error occurs, we will skip the rest of this element
compilerErrors.add(ex);
return;
} catch (ConfigRuntimeException ex) {
compilerErrors.add(new ConfigCompileException(ex));
return;
}
}
for (int i = 0; i < children.size(); i++) {
ParseTree t = children.get(i);
if (t.getData() instanceof CFunction) {
if (t.getData().val().startsWith("_") || (func != null && func.useSpecialExec())) {
continue;
}
Function f;
try {
f = (Function) FunctionList.getFunction(t.getData());
} catch (ConfigCompileException ex) {
compilerErrors.add(ex);
return;
}
Set<OptimizationOption> options = NO_OPTIMIZATIONS;
if (f instanceof Optimizable) {
options = ((Optimizable) f).optimizationOptions();
}
if (options.contains(OptimizationOption.TERMINAL)) {
if (children.size() > i + 1) {
// First, a compiler warning
CHLog.GetLogger().Log(CHLog.Tags.COMPILER, LogLevel.WARNING, "Unreachable code. Consider removing this code.", children.get(i + 1).getTarget());
// Now, truncate the children
for (int j = children.size() - 1; j > i; j--) {
children.remove(j);
}
break;
}
}
}
}
boolean fullyStatic = true;
boolean hasIVars = false;
for (ParseTree node : children) {
if (node.getData() instanceof CFunction) {
optimize(node, procs, compilerErrors);
}
if (node.getData().isDynamic() && !(node.getData() instanceof IVariable)) {
fullyStatic = false;
}
if (node.getData() instanceof IVariable) {
hasIVars = true;
}
}
// In all cases, at this point, we are either unable to optimize, or we will
// optimize, so set our optimized variable at this point.
tree.setOptimized(true);
if (func == null) {
// It's a proc call. Let's see if we can optimize it
Procedure p = null;
loop: for (List<Procedure> proc : procs) {
for (Procedure pp : proc) {
if (pp.getName().equals(cFunction.val())) {
p = pp;
break loop;
}
}
}
if (p != null) {
try {
Construct c = DataHandling.proc.optimizeProcedure(p.getTarget(), p, children);
if (c != null) {
tree.setData(c);
tree.removeChildren();
return;
}
// else Nope, couldn't optimize.
} catch (ConfigRuntimeException ex) {
// Cool. Caught a runtime error at compile time :D
compilerErrors.add(new ConfigCompileException(ex));
}
}
// so we can't for sure say, but we do know we can't optimize this
return;
}
if (tree.getData().val().equals("proc")) {
// Check for too few arguments
if (children.size() < 2) {
compilerErrors.add(new ConfigCompileException("Incorrect number of arguments passed to proc", tree.getData().getTarget()));
return;
}
// We just went out of scope, so we need to pop the layer of Procedures that
// are internal to us
procs.pop();
// Let's see.
try {
ParseTree root = new ParseTree(new CFunction(__autoconcat__, Target.UNKNOWN), tree.getFileOptions());
Script fakeScript = Script.GenerateScript(root, "*");
Environment env = null;
try {
if (Implementation.GetServerType().equals(Implementation.Type.BUKKIT)) {
CommandHelperPlugin plugin = CommandHelperPlugin.self;
GlobalEnv gEnv = new GlobalEnv(plugin.executionQueue, plugin.profiler, plugin.persistenceNetwork, MethodScriptFileLocations.getDefault().getConfigDirectory(), plugin.profiles, new TaskManager());
env = Environment.createEnvironment(gEnv, new CommandHelperEnvironment());
} else {
env = Static.GenerateStandaloneEnvironment(false);
}
} catch (IOException | DataSourceException | URISyntaxException | Profiles.InvalidProfileException e) {
//
}
Procedure myProc = DataHandling.proc.getProcedure(tree.getTarget(), env, fakeScript, children.toArray(new ParseTree[children.size()]));
// Yep. So, we can move on with our lives now, and if it's used later, it could possibly be static.
procs.peek().add(myProc);
} catch (ConfigRuntimeException e) {
// Well, they have an error in there somewhere
compilerErrors.add(new ConfigCompileException(e));
} catch (NullPointerException e) {
// Nope, can't optimize.
return;
}
}
// the compiler trick functions know how to deal with it specially, even if everything isn't
// static, so do this first.
String oldFunctionName = func.getName();
Set<OptimizationOption> options = NO_OPTIMIZATIONS;
if (func instanceof Optimizable) {
options = ((Optimizable) func).optimizationOptions();
}
if (options.contains(OptimizationOption.OPTIMIZE_DYNAMIC)) {
try {
ParseTree tempNode;
try {
tempNode = ((Optimizable) func).optimizeDynamic(tree.getData().getTarget(), tree.getChildren(), tree.getFileOptions());
} catch (ConfigRuntimeException e) {
// Turn it into a compile exception, then rethrow
throw new ConfigCompileException(e);
}
if (tempNode == Optimizable.PULL_ME_UP) {
if (tree.hasChildren()) {
tempNode = tree.getChildAt(0);
} else {
tempNode = null;
}
}
if (tempNode == Optimizable.REMOVE_ME) {
tree.setData(new CFunction("p", Target.UNKNOWN));
tree.removeChildren();
} else if (tempNode != null) {
tree.setData(tempNode.getData());
tree.setOptimized(tempNode.isOptimized());
tree.setChildren(tempNode.getChildren());
tree.getData().setWasIdentifier(tempNode.getData().wasIdentifier());
optimize(tree, procs, compilerErrors);
tree.setOptimized(true);
// array, so if they have reversed this, make note of that now
if (tempNode.hasBeenMadeStatic()) {
fullyStatic = true;
}
}
// else it wasn't an optimization, but a compile check
} catch (ConfigCompileException ex) {
compilerErrors.add(ex);
}
}
if (!fullyStatic) {
return;
}
// specially from here forward
if (func.preResolveVariables() && hasIVars) {
// Well, this function isn't equipped to deal with IVariables.
return;
}
// don't want to run this now
if (tree.getData().getValue().equals(oldFunctionName) && (options.contains(OptimizationOption.OPTIMIZE_CONSTANT) || options.contains(OptimizationOption.CONSTANT_OFFLINE))) {
Construct[] constructs = new Construct[tree.getChildren().size()];
for (int i = 0; i < tree.getChildren().size(); i++) {
constructs[i] = tree.getChildAt(i).getData();
}
try {
try {
Construct result;
if (options.contains(OptimizationOption.CONSTANT_OFFLINE)) {
List<Integer> numArgsList = Arrays.asList(func.numArgs());
if (!numArgsList.contains(Integer.MAX_VALUE) && !numArgsList.contains(tree.getChildren().size())) {
compilerErrors.add(new ConfigCompileException("Incorrect number of arguments passed to " + tree.getData().val(), tree.getData().getTarget()));
result = null;
} else {
result = func.exec(tree.getData().getTarget(), null, constructs);
}
} else {
result = ((Optimizable) func).optimize(tree.getData().getTarget(), constructs);
}
// If the result is null, it was just a check, it can't optimize further.
if (result != null) {
result.setWasIdentifier(tree.getData().wasIdentifier());
tree.setData(result);
tree.removeChildren();
}
} catch (ConfigRuntimeException e) {
// Turn this into a ConfigCompileException, then rethrow
throw new ConfigCompileException(e);
}
} catch (ConfigCompileException ex) {
compilerErrors.add(ex);
}
}
// It doesn't know how to optimize. Oh well.
}
use of com.laytonsmith.core.constructs.IVariable in project CommandHelper by EngineHub.
the class Procedure method checkPossiblyConstant.
private boolean checkPossiblyConstant(ParseTree tree) {
// individual procs need to be inlined as deemed appropriate.
if (true) {
return false;
}
if (!tree.getData().isDynamic()) {
// If it isn't dynamic, it certainly could be constant
return true;
} else if (tree.getData() instanceof IVariable) {
// contract, but import() itself is dynamic, so this is not an issue.
return true;
} else if (tree.getData() instanceof CFunction) {
// If the function itself is not optimizable, we needn't recurse.
try {
FunctionBase fb = FunctionList.getFunction(tree.getData());
if (fb instanceof Function) {
Function f = (Function) fb;
if (f instanceof DataHandling._return) {
// but if the contents are optimizable, it is still considered constant.
if (!tree.hasChildren()) {
return true;
} else {
return checkPossiblyConstant(tree.getChildAt(0));
}
}
// If it's optimizable, it's possible. If it's restricted, it doesn't matter, because
// we can't optimize it out anyways, because we need to do the permission check
Set<Optimizable.OptimizationOption> o = EnumSet.noneOf(Optimizable.OptimizationOption.class);
if (f instanceof Optimizable) {
o = ((Optimizable) f).optimizationOptions();
}
if (!((o != null && (o.contains(Optimizable.OptimizationOption.OPTIMIZE_DYNAMIC) || o.contains(Optimizable.OptimizationOption.OPTIMIZE_CONSTANT))) && !f.isRestricted())) {
// Nope. Doesn't matter if the children are or not
return false;
}
} else {
return false;
}
} catch (ConfigCompileException e) {
// It's a proc. We will treat this just like any other function call,
}
// Ok, well, we have to check the children first.
for (ParseTree child : tree.getChildren()) {
if (!checkPossiblyConstant(child)) {
// Nope, since our child can't be constant, neither can we
return false;
}
}
// They all check out, so, yep, we could possibly be constant
return true;
} else {
// Uh. Ok, well, nope.
return false;
}
}
Aggregations