FunctionDecl func,
Unit shellScope,
boolean addToWhereBlock) {
rawLine = rawLine.trim() + '\n';
Executable executable = new Executable(new StringReader(rawLine));
Node parsedLine;
try {
Parser parser = new LexprParser(new Tokenizer(rawLine).tokenize(), shellScope);
parsedLine = parser.line();
if (parsedLine == null || !parser.getErrors().isEmpty()) {
executable.printErrors(parser.getErrors());
return "";
}
// If this is an assignment, just check the rhs portion of it.
// This is a bit hacky but prevents verification from balking about new
// vars declared in the lhs.
if (parsedLine instanceof Assignment) {
new Reducer(parsedLine).reduce();
Assignment assignment = (Assignment) parsedLine;
// Strip the lhs of the assignment if this is a simple variable setter
// as that will happen after the fact in a where-block.
// However we still do have Assignment nodes that assign "in-place", i.e.
// mutate the state of existing variables (example: a.b = c), and these need
// to continue untouched.
if (assignment.lhs() instanceof Variable)
func.children().add(assignment.rhs());
else
func.children().add(parsedLine);
} else
func.children().add(parsedLine);
// Compress nodes and eliminate redundancies.
new Reducer(func).reduce();
shellScope.loadDeps("<shell>");
executable.runMain(true);
executable.compileExpression(shellScope);
if (executable.hasErrors()) {
executable.printStaticErrorsIfNecessary();
return "";
}
} catch (Exception e) {
e.printStackTrace();
return new LoopError("malformed expression " + rawLine);
}
try {
Object result = Loop.safeEval(executable, null);
if (addToWhereBlock && parsedLine instanceof Assignment) {
Assignment assignment = (Assignment) parsedLine;
boolean shouldReplace = false, shouldAddToWhere = true;
if (assignment.lhs() instanceof Variable) {
String name = ((Variable) assignment.lhs()).name;
shellContext.put(name, result);
// Look up the value of the RHS of the variable from the shell context,
// if this is the second reference to the same variable.
assignment.setRhs(new LexprParser(new Tokenizer(
"`loop.LoopShell`.shellObtain('" + name + "')").tokenize()).parse());
shouldReplace = true;
} else
shouldAddToWhere = false; // Do not add state-mutating assignments to where block.
// If this assignment is already present in the current scope, we should replace it.
if (shouldReplace)
for (Iterator<Node> iterator = func.whereBlock().iterator(); iterator.hasNext(); ) {
Node node = iterator.next();
if (node instanceof Assignment && ((Assignment) node).lhs() instanceof Variable) {
iterator.remove();
}
}