/* 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; L functionContainers; new LinkedHashSet knownVars; Map functionDefs = new Map; // object can be a class srecord MethodOnObject(O object, S method) {} Script parse(S text) { setText(text); ret parseScript(); } Script parseScript() { new Script script; script.functionDefs = functionDefs; try { parseScript_2(script); ret script; } catch e { print("Parsed so far:\n" + script); throw rethrowAndAppendToMessage(e, squareBracketed(str(lineAndColumn()))); } } void parseScript_2(Script script) { while (mainLoop()) { if (is(";")) continue with next(); if (is("}")) break; S t = token(); if (is("def")) continue with parseFunctionDefinition(); if (is("while")) continue with script.add(parseWhileLoop()); if (is("if")) continue with script.add(parseIfStatement()); if (isIdentifier(t) && eq(token(1), "<") && eq(token(2), "-")) { next(3); knownVars.add(t); script.add(new Toolbox.Assignment(t, parseExpr())); } else addUnlessNull(script, parseExpr()); } } Evaluable parseOptionalInnerExpression() { 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; if (is(";")) null; S t = token(); // int or double literal if (is("-") && empty(nextSpace()) && startsWithDigit(token(1)) || startsWithDigit(t)) { t = consumeUntilSpaceOrCmdEnd(); ret isInteger(t) ? const(parseInt(t)) : const(parseDouble(t)); } consume(); if (isQuoted(t)) ret const(unquote(t)); if (was("true")) ret const(true); if (was("false")) ret const(false); if (was("null")) ret const(null); if (was("new")) { S className = tpp(); O o = findExternalObject(className); if (o cast Class) ret new Toolbox.NewObject(o, parseExpressions()); fail("Class not found: " + className); } if (knownVars.contains(t)) { var e = new Toolbox.GetVar(t); ret inner ? e : parseCall(e); } if (!inner) { var fdef = functionDefs.get(t); if (fdef != null) ret new Toolbox.CallFunction(fdef, parseExpressions()); } O o = findExternalObject(t); if (o == null) fail("Unknown object: " + t); else if (inner) ret const(o); else if (o cast Class) { /*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(o, parseExpressions()); } else*/ if (isIdentifier()) { S name = tpp(); // look for method first if (hasMethodNamed(o, name)) ret new Toolbox.CallMethod(const(o), name, parseExpressions()); // 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, parseExpressions()); } else ret parseCall(const(o)); } L parseExpressions() { ret collectWhileNotNull(-> parseOptionalInnerExpression()); } S consumeUntilSpaceOrCmdEnd() { ret consumeUntilSpaceOr(-> atCmdEnd()); } bool atCmdEnd() { ret atEndOrLineBreak() || is(";") || is("}"); } void assertCmdEnd() { if (!atCmdEnd()) fail("Expected end of command"); } Evaluable parseCall(Evaluable target) { if (atCmdEnd()) ret target; S methodName = tpp(); var args = parseExpressions(); ret new Toolbox.CallMethod(target, methodName, args); } // can return MethodOnObject swappable O findExternalObject(S name) { //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()); } selfType allowTheWorld(O... functionContainers) { this.functionContainers = asList(functionContainers); } void printFunctionDefs { print(values(functionDefs)); } void parseFunctionDefinition() { consume("def"); S functionName = assertIdentifier(tpp()); new LS args; while (isIdentifier()) args.add(tpp()); temp tempAddAll(knownVars, args); var functionBody = parseCurlyBlock(); functionDefs.put(functionName, new Toolbox.FunctionDef(functionName, args, functionBody)); } Evaluable parseCurlyBlock() { //print(+knownVars); consume("{"); var script = parseScript(); consume("}"); ret script; } Evaluable parseWhileLoop() { consume("while"); var condition = parseExpr(); var body = parseCurlyBlock(); ret new Toolbox.While(condition, body); } Evaluable parseIfStatement() { consume("if"); var condition = parseExpr(); var body = parseCurlyBlock(); ret new Toolbox.IfThen(condition, body); } // declare an external variable void addVar(S var) { knownVars.add(var); } // short name to full name simplyCached SS globalClassNames() { null; } /* XXX var packages = mapToTreeSet(importedPackages(), pkg -> pkg + "."); // add inner classes of function containers var classContainers = mapToTreeSet(functionContainers, fc -> className(fc) + "$"); new SS out; for (className : g22utils.classNameResolver().allFullyQualifiedClassNames()) if (!contains(className, '$')) { S pkg = longestPrefixInTreeSet(className, packages); if (pkg != null) out.put(dropPrefix(pkg, className), className); } S container = longestPrefixInTreeSet(className, classContainers); if (container != null) out.put(dropPrefix(container, className), className); } ret out; }*/ }