Warning: session_start(): open(/var/lib/php/sessions/sess_ct1552lg02ngt0983bg8v531mr, 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.
delegate FunctionDef to Toolbox.
replace const with _const.
settable new G22Utils g22utils;
new L functionContainers;
settable LASClassLoader lasClassLoader;
settable S classDefPrefix;
new LinkedHashMap knownVars;
BuildingScript currentReturnableScript;
BuildingScript currentLoop;
bool inParens;
int idCounter;
new Map classDefs;
// record which vars are known at current parse location
event knownVarsSnapshot(Map knownVars);
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) {}
srecord EvaluableWrapper(Evaluable expr) {}
class BuildingScript {
int id = ++idCounter;
bool returnable, isLoopBody;
BuildingScript returnableParent, loopParent;
new Script script;
new L steps;
Map functionDefs = new Map;
*(bool *returnable) {}
*(bool *returnable, bool *isLoopBody) {}
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(+id, +returnable , +returnableParent, +script);
}
} // end of BuildingScript
Script parse(S text) {
setText(text);
ret parse();
}
Script parse() {
ret (Script) parseScript(true);
}
// if returnable is true, it's always a Script
Evaluable parseScript(bool returnable, bool isLoopBody default false) {
BuildingScript script = new(returnable, isLoopBody);
script.returnableParent = currentReturnableScript;
script.loopParent = currentLoop;
if (returnable)
currentReturnableScript = script;
if (isLoopBody)
currentLoop = script;
ret parseScript(script);
}
Evaluable parseScript(BuildingScript script) {
try {
parseScript_2(script);
var builtScript = script!;
currentReturnableScript = script.returnableParent;
currentLoop = script.loopParent;
ret builtScript;
} catch e {
print("Parsed so far:\n" + script);
throw rethrowAndAppendToMessage(e, squareBracketed(str(lineAndColumn())));
}
}
void parseScript_2(BuildingScript script) {
new AssureAdvance assure;
while (assure!) {
// for auto-completer
knownVarsSnapshot(knownVars);
if (is(";")) continue with next();
if (isOneOf("}", ")")) break;
if (is("def"))
continue with parseFunctionDefinition();
if (is("param")) {
consume();
S var = assertIdentifier(tpp());
knownVars.put(var, new LASValueDescriptor);
continue;
}
if (isOneOf("return", "ret")) {
consume();
Evaluable expr;
if (atCmdEnd())
expr = const(null);
else
expr = parseAssignmentOrExpr();
continue with script.add(new Toolbox.ReturnFromScript(currentReturnableScript.script, expr));
}
if (is("continue")) {
consume();
assertCmdEnd();
if (currentLoop == null)
fail("continue outside of loop");
continue with script.add(new Toolbox.Continue(currentLoop.script));
}
if (is("temp")) {
consume();
Evaluable tempExpr = parseExpr();
print(+tempExpr);
Evaluable body = parseScript(false);
print(+body);
continue with script.add(new Toolbox.TempBlock(tempExpr, body));
}
if (is("class"))
script.add(new Toolbox.ClassDef(new ResolvableLASClass(lasClassLoader, parseClassDef())));
script.add(parseAssignmentOrExpr());
}
knownVarsSnapshot(knownVars);
}
Evaluable parseAssignmentOrExpr() {
try object parseAssignmentOpt();
ret parseExpr();
}
Toolbox.Assignment parseAssignmentOpt() {
S t = token();
if (isIdentifier(t) && eq(token(1), "<") && eq(token(2), "-")) {
print("Found assignment");
next(3);
Evaluable rhs = parseExpr();
knownVars.put(t, new LASValueDescriptor);
ret new Toolbox.Assignment(t, rhs);
}
null;
}
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); }
scaffolded Evaluable parseExpr(bool inner default false) {
ret printIndentedStructIf(scaffoldingEnabled(), "parseExpr done",
linkToSrc(-> parseExpr_impl(inner)));
}
Evaluable parseExpr_impl(bool inner) {
if (atEnd()) null;
S t = token();
printVars parseExpr(token := t);
if (is(";")) null; // empty command
if (is("{"))
ret parseCurlyBlock(false);
// int or double literal
if (is("-") && empty(nextSpace()) && startsWithDigit(token(1))
|| startsWithDigit(t)) {
t = consumeMultiTokenLiteral();
if (swic(t, "0x")) ret const(parseHexInt(dropFirst(t, 2)));
if (swic(t, "0b")) ret const(intFromBinary(dropFirst(t, 2)));
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();
if (is("repeat"))
ret parseRepeatStatement();
if (is("outer")) {
consume();
var a = parseAssignmentOpt();
assertNotNull("Assignment expected", a);
ret new Toolbox.AssignmentToOuterVar(a.var, a.expression);
}
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);
}
if (isOneOf("&", "|") && empty(nextSpace()) && is(1, token())) {
ret parseBinaryOperator();
}
fail("Identifier, literal, operator or opening parentheses expected (got: " + quote(t));
}
Evaluable parseBinaryOperator() {
bool and = is("&");
next(2);
Evaluable a = parseInnerExpr();
Evaluable b = parseInnerExpr();
ret and
? new Toolbox.BoolAnd(a, b)
: new Toolbox.BoolOr(a, b);
}
// 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());
// script-defined class?
LASClassDef cd = classDefs.get(className);
if (cd != null)
ret new Toolbox.NewObject_LASClass(new ResolvableLASClass(lasClassLoader, cd));
// external class
O o = findExternalObject(className);
if (o cast Class)
ret new Toolbox.NewObject(o, parseArguments());
throw new ClassNotFound(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 (o cast EvaluableWrapper) {
ret inner ? o.expr : parseCall(o.expr);
} else if (inner)
ret const(o);
else if (o cast Class) {
// expression starts with a class
Class c = o;
if (atCmdEnd())
ret const(c);
// new without new
if (is("("))
ret new Toolbox.NewObject(c, parseArguments());
/* old object creation syntax (e.g. Pair new a b)
if (is("new")) {
next();
ret new Toolbox.NewObject(c, parseArguments());
} else*/
// check if it's a lambda definition first (with ->)
try object parseLambdaOpt(c);
if (isIdentifier()) {
S name = tpp();
// We have now parsed a class (c) plus an as yet unknown identifier (name) right next to it
// 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 or a lambda
if (isInterface(c))
ret parseLambdaMethodRef(c, name);
// look for field second
var field = getField(o, name);
if (field != null) {
assertCmdEnd();
if (!isStaticField(field))
fail(field + " is not a static field");
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));
}
// parse lambda, e.g. IF1 a -> plus a a
// returns null if it's not a lambda
Evaluable parseLambdaOpt(Class c) {
int nArgs = 0;
while (isIdentifier(token(nArgs))) nArgs++;
if (!(is(nArgs, "-") && is(nArgs+1, ">"))) null; // not a lambda
// read parameter names and add to known variables
S[] argNames = consumeArray(nArgs);
temp tempAddKnownVars(argNames);
skip(2); // skip the arrow
Evaluable body;
// if curly brackets: parse lambda body as returnable script
// TODO: avoid script overhead if there are no return calls inside
if (is("{"))
body = parseScript(true);
else
// no curly brackets => just an expression
body = parseExpr();
// return lambda
ret printIndentedStructIf(scaffoldingEnabled(), "parseLambdaOpt done",
new Toolbox.LambdaDef(c, argNames, body);
}
// method ref, e.g. "IF1 l" meaning "l1 l"
// also, constructor ref, e.g. "IF1 Pair" meaning (x -> new Pair(x))
//
// c must be a single method interface
Evaluable parseLambdaMethodRef(Class c, S name) {
var fdef = lookupFunction(name);
if (fdef != null) {
Evaluable[] curriedArguments = parseArguments();
ret new Toolbox.CurriedScriptFunctionLambda(c, fdef, curriedArguments);
}
O function = findExternalObject(name);
if (function == null)
throw new UnknownObject(name);
if (function cast MethodOnObject) {
O target = function.object;
S targetMethod = function.method;
Evaluable[] curriedArguments = parseArguments();
ret new Toolbox.CurriedMethodLambda(c, target, targetMethod, curriedArguments);
} else if (function cast Class) {
Class c2 = function;
// No currying here yet
assertCmdEnd();
var ctors = constructorsWithNumberOfArguments(c2, 1);
if (empty(ctors))
fail("No single argument constructor found in " + c2);
ret new Toolbox.CurriedConstructorLambda(c, toArray Constructor(ctors), null);
} else
fail(function + " is not an instantiable class or callable method");
}
FunctionDef lookupFunction(S name) {
var script = currentReturnableScript;
while (script != null) {
var f = script.functionDefs.get(name);
printVars("lookupFunction", +script, +name, +f);
if (f != null) ret f;
script = script.returnableParent;
}
null;
}
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, token is: " + quote(token()));
}
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 linkToSrc(start, new Toolbox.CallMethodOrGetField(target, methodName));
}
// add link to source code to parsed object if it supports it
// and doesn't have a source code link already
A linkToSrc(TokPtr start, A a) {
if (a cast IHasTokenRangeWithSrc && a.tokenRangeWithSrc() == null)
a.setTokenRangeWithSrc(TokenRangeWithSrc(start, ptr().plus(-1)));
ret a;
}
// lambda version of linkToSrc
A linkToSrc(IF0 a) {
var start = ptr();
ret linkToSrc(start, 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);
var field = getField(container, name);
if (field != null && isStaticField(field))
ret new EvaluableWrapper(new Toolbox.GetStaticField(field));
}
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));
}
AutoCloseable tempAddKnownVars(S... vars) {
ret tempAddKnownVars(asList(vars));
}
AutoCloseable tempAddKnownVars(Iterable vars) {
ret tempMapPutAll(knownVars, mapWithSingleValue(vars, new LASValueDescriptor));
}
void parseFunctionDefinition() {
consume("def");
S functionName = assertIdentifier(tpp());
new LS args;
while (isIdentifier())
args.add(tpp());
temp tempAddKnownVars(args);
var functionBody = parseCurlyBlock(true);
print("Defined function " + functionName + " in " + currentReturnableScript);
currentReturnableScript.functionDefs.put(functionName,
new Toolbox.FunctionDef(functionName, args, functionBody));
}
Evaluable parseCurlyBlock(bool returnable, bool isLoopBody default false) {
//print(+knownVars);
consume("{");
bool inParensOld = inParens;
inParens = false;
var script = parseScript(returnable, isLoopBody);
consume("}");
inParens = inParensOld;
ret script;
}
Evaluable parseWhileLoop() {
consume("while");
var condition = parseExpr();
var body = parseCurlyBlock(false, true);
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 = -> tempAddKnownVars(var);
finish = -> new Toolbox.ForEach(collection, var, body);
} else if (iIn == 3) {
if (isOneOf("pair", "Pair")) {
// "pair a b" or "Pair a b"
consume();
S varA = consumeIdentifier();
S varB = consumeIdentifier();
printVars(+varA, +varB);
enterBody = -> tempAddKnownVars(varA, varB);
finish = -> new Toolbox.ForPairs(collection, body, varA, varB);
} else {
// "a, b"
S varA = consumeIdentifier();
consume(",");
S varB = consumeIdentifier();
enterBody = -> tempAddKnownVars(varA, varB);
finish = -> new Toolbox.ForKeyValue(collection, body, varA, varB);
}
} else if (iIn == 4) {
consume("index");
S varIndex = consumeIdentifier();
consume(",");
S varElement = consumeIdentifier();
enterBody = -> tempAddKnownVars(varIndex, varElement);
finish = -> new Toolbox.ForIndex(collection, body, varIndex, varElement);
} else
fail("Unknown pattern for 'for' loop");
consume("in");
collection = parseExpr();
print(+collection);
temp enterBody!;
body = parseCurlyBlock(false, true);
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);
}
Evaluable parseRepeatStatement() {
consume("repeat");
var n = parseExpr();
var body = parseCurlyBlock(false);
ret new Toolbox.RepeatN(n, body);
}
// 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
// TODO: sort by priority if classes appear twice
simplyCached SS globalClassNames() {
var packages = mapToTreeSet(importedPackages(), pkg -> pkg + ".");
new SS out;
// add function containers themselves (if they're classes)
for (fc : functionContainers)
if (fc cast Class) {
if (isAnonymousClass(fc)) continue;
out.put(shortClassName(fc), className(fc));
}
// add inner classes of function containers
var classContainers = classContainerPrefixes();
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) + "$");
}
// typed exceptions (unusual, I know!)
srecord noeq UnknownObject(S name) > RuntimeException {
public S getMessage() { ret "Unknown object: " + name; }
}
sclass ClassNotFound > UnknownObject {
*(S className) { super(className); }
public S getMessage() { ret "Class not found: " + name; }
}
LASClassDef parseClassDef() {
consume("class");
new LASClassDef classDef;
if (nempty(classDefPrefix))
classDef.classDefPrefix(classDefPrefix);
S name = consumeIdentifier();
classDef.userGivenName(name);
consume("{");
while (!is("}")) {
if (is(";")) continue with next();
new LASClassDef.FieldDef fd;
fd.name(consumeIdentifier());
consume(":");
S typeName = consumeIdentifier();
O type = findExternalObject(typeName);
if (!type instanceof Class)
fail("Class not found: " + typeName);
fd.type((Class) type);
classDef.fields.add(fd);
}
consume("}");
classDefs.put(name, classDef);
ret classDef;
}
} // end of GazelleV_LeftArrowScriptParser