Warning: session_start(): open(/var/lib/php/sessions/sess_0p3c19qb9lhvo3b9jfuc0k30rc, O_RDWR) failed: No space left on device (28) in /var/www/tb-usercake/models/config.php on line 51
Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/tb-usercake/models/config.php on line 51
/* e.g.
overlay <- ScreenOverlay
bounds <- rightScreenBounds
overlay bounds bounds
overlay show
Can separate commands with ";" also.
For how to define functions in a script see #1033988.
Note: LeftArrowScriptAutoCompleter uses a lot of this class's internals
*/
sclass GazelleV_LeftArrowScriptParser > SimpleLeftToRightParser {
replace Toolbox with GazelleV_LeftArrowScript.
delegate Script to Toolbox.
delegate Evaluable to Toolbox.
replace const with _const.
settable new G22Utils g22utils;
new L functionContainers;
new LinkedHashMap knownVars;
BuildingScript currentReturnableScript;
bool inParens;
replace print with if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) print.
replace printVars with if (GazelleV_LeftArrowScriptParser.this.scaffoldingEnabled()) printVars.
// object can be a class
srecord MethodOnObject(O object, S method) {}
class BuildingScript {
bool returnable;
new Script script;
new L steps;
Map functionDefs = new Map;
*(bool *returnable) {}
void add(Evaluable step) { if (step != null) steps.add(step); }
Evaluable get() {
// if the last command is a return from THIS script,
// convert into a simple expression
var lastStep = last(steps);
if (lastStep cast Toolbox.ReturnFromScript)
if (lastStep.script == script)
replaceLast(steps, lastStep.value);
// if the script is only step, is not returnable
// and defines no functions, replace it with its only step
if (!returnable && l(steps) == 1 && empty(functionDefs))
ret first(steps);
// make and return actual script
script.functionDefs = functionDefs;
script.steps = toTypedArray(Evaluable.class, steps);
ret script;
}
S toStringLong() { ret pnlToLines(steps); }
toString { ret formatRecordVars BuildingScript(+script, +returnable); }
}
Script parse(S text) {
setText(text);
ret (Script) parseScript(true);
}
// if returnable is true, it's always a Script
Evaluable parseScript(bool returnable) {
BuildingScript script = new(returnable);
var lastReturnableScript = currentReturnableScript;
if (returnable) currentReturnableScript = script;
try {
parseScript_2(script);
var builtScript = script!;
currentReturnableScript = lastReturnableScript;
ret builtScript;
} catch e {
print("Parsed so far:\n" + script);
throw rethrowAndAppendToMessage(e, squareBracketed(str(lineAndColumn())));
}
}
void parseScript_2(BuildingScript script) {
while (mainLoop()) {
if (is(";")) continue with next();
if (is("}")) break;
if (is("def"))
continue with parseFunctionDefinition();
if (is("param")) {
consume();
S var = assertIdentifier(tpp());
knownVars.put(var, new LASValueDescriptor);
continue;
}
if (is("return")) {
consume();
var expr = parseAssignmentOrExpr();
continue with script.add(new Toolbox.ReturnFromScript(currentReturnableScript.script, expr));
}
script.add(parseAssignmentOrExpr());
}
}
Evaluable parseAssignmentOrExpr() {
S t = token();
print("next tokens: " + quote(token(1)) + " " + quote(token(2)));
if (isIdentifier(t) && eq(token(1), "<") && eq(token(2), "-")) {
print("Found assignment");
next(3);
knownVars.put(t, new LASValueDescriptor);
ret new Toolbox.Assignment(t, parseExpr());
} else
ret parseExpr();
}
Evaluable parseOptionalInnerExpression() {
printVars parseOptionalInnerExpression(+token());
if (atCmdEnd() || is("{")) null;
ret parseInnerExpr();
}
Evaluable const(O o) { ret new Toolbox.Const(o); }
Evaluable parseInnerExpr() { ret parseExpr(true); }
Evaluable parseExpr(bool inner default false) {
if (atEnd()) null;
S t = token();
printVars parseExpr(token := t);
if (is(";")) null; // empty command
// int or double literal
if (is("-") && empty(nextSpace()) && startsWithDigit(token(1))
|| startsWithDigit(t)) {
t = consumeMultiTokenLiteral();
if (isInteger(t)) ret const(parseInt(t));
if (endsWith(t, "f")) ret const(parseFloat(t));
ret const(parseDouble(t));
}
if (isQuoted(t)) {
consume();
ret const(unquote(t));
}
if (isIdentifier(t)) {
if (is("while"))
ret parseWhileLoop();
if (is("for"))
ret parseForEach();
if (is("if"))
ret parseIfStatement();
consume();
print("Consumed identifier " + t + ", next token: " + token() + ", inner: " + inner);
ret parseExprStartingWithIdentifier(t, inner);
}
// nested expression
if (eq(t, "(")) {
bool inParensOld = inParens;
inParens = true;
consume();
//print("Consumed opening parens (level " + parenLevels + ")");
var e = parseExpr();
//print("Consuming closing parens for expr: " + e + " (closing paren level " + parenLevels + ")");
consume(")");
inParens = inParensOld;
ret inner ? e : parseCall(e);
}
fail("Identifier, literal or opening parentheses expected (got: " + quote(t));
}
// t is last consumed token (the identifier the expression starts with)
Evaluable parseExprStartingWithIdentifier(S t, bool inner) {
if (eq(t, "true")) ret const(true);
if (eq(t, "false")) ret const(false);
if (eq(t, "null")) ret const(null);
if (eq(t, "new")) {
S className = assertIdentifier(tpp());
O o = findExternalObject(className);
if (o cast Class)
ret new Toolbox.NewObject(o, parseArguments());
fail("Class not found: " + className);
}
var type = knownVars.get(t);
if (type != null) {
var e = new Toolbox.GetVar(t).returnType(type);
print("Found var acccess: " + e + ", " + (!inner ? "Checking for call" : "Returning expression"));
ret inner ? e : parseCall(e);
}
if (!inner) {
var fdef = lookupFunction(t);
if (fdef != null)
ret new Toolbox.CallFunction(fdef, parseArguments());
}
if (eq(t, "_context"))
ret new Toolbox.GetVarContext;
O o = findExternalObject(t);
if (o == null)
throw new UnknownObject(t);
else if (inner)
ret const(o);
else if (o cast Class) {
Class c = o;
/*if (atCmdEnd())
ret new Toolbox.NewObject(o);*/
/* old object creation syntax (e.g. Pair new a b)
if (is("new")) {
next();
ret new Toolbox.NewObject(c, parseArguments());
} else*/
if (isIdentifier()) {
S name = tpp();
// look for a static method call
if (hasMethodNamed(c, name))
ret new Toolbox.CallMethod(const(c), name, parseArguments());
// if the class is an interface, it's a lambda method reference
if (isInterface(c))
ret parseLambdaMethodRef(c, name);
// look for field second
var field = getField(o, name);
if (field != null) {
assertCmdEnd();
ret new Toolbox.GetStaticField(field);
}
fail(name + " not found in " + o + " (looked for method or field)");
} else
fail("Method name expected: " + token());
} else if (o cast MethodOnObject) {
if (inner) fail("Can't call methods in arguments");
ret new Toolbox.CallMethod(const(o.object), o.method, parseArguments());
} else
ret parseCall(const(o));
}
// (e.g. "IF1 l" meaning "l1 l")
// c must be a single method interface
Evaluable parseLambdaMethodRef(Class c, S name) {
var fdef = lookupFunction(name);
if (fdef != null)
fail("TODO");
//ret new Toolbox.CallFunction(fdef, parseArguments());
O function = findExternalObject(name);
if (function == null)
throw new UnknownObject(name);
if (!function instanceof MethodOnObject)
fail(function + " is not a callable method");
cast function to MethodOnObject;
O target = function.object;
S targetMethod = function.method;
Evaluable[] curriedArguments = parseArguments();
ret new Toolbox.CurriedMethodLambda(c, target, targetMethod, curriedArguments);
}
FunctionDef lookupFunction(S name) {
ret currentReturnableScript.functionDefs.get(name);
}
Evaluable[] parseArguments() {
ret toArrayOrNull(Evaluable.class, parseArgumentsAsList());
}
L parseArgumentsAsList() {
//ret collectWhileNotNull(-> parseOptionalInnerExpression());
new L l;
try {
while (true) {
Evaluable a = parseOptionalInnerExpression();
if (a == null) break;
l.add(a);
}
ret l;
} on fail {
print("Arguments parsed so far: " + l);
}
}
S consumeMultiTokenLiteral() {
ret consumeUntilSpaceOr(-> atCmdEnd());
}
bool atCmdEnd() {
ret
!inParens && atEndOrLineBreak()
|| is(";") || is("}") || is(")");
}
void assertCmdEnd() { if (!atCmdEnd()) fail("Expected end of command"); }
Evaluable parseCall(Evaluable target) {
if (atCmdEnd() || !isIdentifier()) ret target;
var start = ptr();
S methodName = tpp();
var args = parseArguments();
if (nempty(args))
ret new Toolbox.CallMethod(target, methodName, args);
else
ret src(start, new Toolbox.CallMethodOrGetField(target, methodName));
}
A src(TokPtr start, A a) {
if (a cast IHasTokenRangeWithSrc)
a.setTokenRangeWithSrc(TokenRangeWithSrc(start, ptr()));
ret a;
}
// can return MethodOnObject
swappable O findExternalObject(S name) ctex {
//try object findClassThroughDefaultClassFinder(name);
//try object findClassInStandardImports(name);
S fullName = globalClassNames().get(name);
if (fullName != null)
ret Class.forName(fullName);
fOr (container : functionContainers)
if (hasMethodNamed(container, name))
ret new MethodOnObject(container, name);
null;
}
selfType allowTheWorld() { ret allowTheWorld(mc()); }
// first containers within argument list have priority
// last calls to allowTheWorld have priority
selfType allowTheWorld(O... functionContainers) {
fOr (O o : reversed(functionContainers))
if (!contains(this.functionContainers, o)) {
this.functionContainers.add(0, o);
globalClassNames_cache = null; // recalculate
}
this;
}
void printFunctionDefs(Script script) {
print(values(script.functionDefs));
}
void parseFunctionDefinition() {
consume("def");
S functionName = assertIdentifier(tpp());
new LS args;
while (isIdentifier())
args.add(tpp());
temp tempMapPutAll(knownVars, mapWithSingleValue(args, new LASValueDescriptor));
var functionBody = parseCurlyBlock(true);
currentReturnableScript.functionDefs.put(functionName,
new Toolbox.FunctionDef(functionName, args, functionBody));
}
Evaluable parseCurlyBlock(bool returnable) {
//print(+knownVars);
consume("{");
bool inParensOld = inParens;
inParens = false;
var script = parseScript(returnable);
consume("}");
inParens = inParensOld;
ret script;
}
Evaluable parseWhileLoop() {
consume("while");
var condition = parseExpr();
var body = parseCurlyBlock(false);
ret new Toolbox.While(condition, body);
}
Evaluable parseForEach() {
ret new ParseForEach()!;
}
class ParseForEach {
Evaluable collection, body;
IF0 finish;
IF0 enterBody;
Evaluable get() {
consume("for");
int iIn = relativeIndexOf("in");
if (iIn < 0) fail("for without in");
print(+iIn);
if (iIn == 1) { // just a variable
S var = consumeIdentifier();
enterBody = -> tempMapPut(knownVars, var, new LASValueDescriptor);
finish = -> new Toolbox.ForEach(collection, var, body);
} else if (iIn == 3) { // "pair a b" or "Pair a b"
consumeOneOf("pair", "Pair");
S varA = consumeIdentifier();
S varB = consumeIdentifier();
printVars(+varA, +varB);
enterBody = -> combineAutoCloseables(
tempMapPut(knownVars, varA, new LASValueDescriptor),
tempMapPut(knownVars, varB, new LASValueDescriptor));
finish = -> new Toolbox.ForPairs(collection, body, varA, varB);
} else
fail("Unknown pattern for 'for' loop");
consume("in");
collection = parseExpr();
print(+collection);
temp enterBody!;
body = parseCurlyBlock(false);
ret finish!;
}
}
Evaluable parseIfStatement() {
consume("if");
var condition = parseExpr();
var body = parseCurlyBlock(false);
Evaluable elseBranch = null;
if (is("else")) {
consume();
if (is("if"))
elseBranch = parseIfStatement();
else
elseBranch = parseCurlyBlock(false);
}
ret new Toolbox.IfThen(condition, body, elseBranch);
}
// declare an external variable with optional type info
void addVar(S var, LASValueDescriptor type default new) { knownVars.put(var, type); }
void addVar(S var, Class type, bool canBeNull) { addVar(var, new LASValueDescriptor.NonExact(type, canBeNull); }
// short name to full name
simplyCached SS globalClassNames() {
var packages = mapToTreeSet(importedPackages(), pkg -> pkg + ".");
// add inner classes of function containers
var classContainers = classContainerPrefixes();
new SS out;
for (className : g22utils.classNameResolver().allFullyQualifiedClassNames()) {
if (isAnonymousClassName(className)) continue;
if (!contains(className, '$')) {
S pkg = longestPrefixInTreeSet(className, packages);
if (pkg != null) {
S shortName = dropPrefix(pkg, className);
if (!shortName.contains("."))
out.put(shortName, className);
}
}
S container = longestPrefixInTreeSet(className, classContainers);
if (container != null)
out.put(dropPrefix(container, className), className);
}
ret out;
}
swappable Cl importedPackages() {
ret itemPlus("java.lang", standardImports_fullyImportedPackages());
}
TreeSet classContainerPrefixes() {
ret mapToTreeSet(functionContainers, fc -> className(fc) + "$");
}
srecord noeq UnknownObject(S name) > RuntimeException {
public S getMessage() { ret "Unknown object: " + name; }
}
} // end of GazelleV_LeftArrowScriptParser