// See GazelleV_LeftArrowScriptParser // TODO: decide whether we allow calling methods/getting fields on // a null reference (just returning null), or whether we throw a // NullPointerException. Currently we just return null. Probably // that's pretty cool. Null propagation as a default, just like in // JavaX. Just more automatic! sclass GazelleV_LeftArrowScript { replace EXITCHECK with if (ctx.exiting()) null; . // Keep the dot in, it belongs there replace const with _const. macro HANDLENULLREFERENCE { settable bool allowNullReference; O handleNullReference() { if (allowNullReference) null; else throw new NullPointerException(); } } // Base = any script element with a reference to its source code asclass Base > HasTokenRangeWithSrc { RuntimeException rethrowWithSrc(S msg default "", Throwable e) { if (tokenRangeWithSrc() != null) /*throw rethrowAndAppendToMessage(e, squareBracketed( joinNemptiesWithComma(msg, src)));*/ throw new ScriptError(tokenRangeWithSrc(), msg, e); else throw rethrow(e); } S indented() { ret indentedScriptStruct(this); } S shortenedSrc() { ret shortenSrc(tokenRangeWithSrc()); } } sclass ScriptError extends RuntimeException is IHasTokenRangeWithSrc { *(TokenRangeWithSrc src, Throwable reason) { super(reason); tokenRangeWithSrc(src); } *(TokenRangeWithSrc src, S msg, Throwable reason) { super(msg, reason); tokenRangeWithSrc(src); } !include #1036424 // (I)HasTokenRangeWithSrc Include TokenRangeWithSrc src() { ret tokenRangeWithSrc(); } toString { ret super.toString() + "\n " + src() + " {{ " + shortenSrc(src()) + " }}"; } } sS shortenSrc(TokenRangeWithSrc src) { ret src == null ?: shorten(nlToSpace(src.text())); } // Evaluable = a script element that can be evaluated interface Evaluable extends IF0, IHasTokenRangeWithSrc { public O get(VarContext ctx default new FlexibleVarContext); public default LASValueDescriptor returnType() { null; } public default Evaluable optimize() { this; } // informs this object that its return value will not be needed // so it can perform appropriate optimizations public default Evaluable optimizeForReturnValueNotNeeded() { this; } public default O getWithParams(O... params) { ret get(flexibleVarContextFromParams(params)); } } // Base + Evaluable + explicitly stored return type asclass EvaluableBase > Base is Evaluable { settable LASValueDescriptor returnType; bool returnValueNeeded = true; public Evaluable optimizeForReturnValueNotNeeded() { returnValueNeeded = false; ret optimize(); } } /*interface Cmd { // returns true if a return was issued public bool run(VarContext ctx default new FlexibleVarContext); }*/ static new AtomicLong scriptIDCounter; static long scriptID() { ret incAtomicLong(scriptIDCounter); } srecord noeq ListFromScript(Script script) > EvaluableBase { public O get(VarContext ctx) { ret script.getAsList(ctx); } } sclass Script > EvaluableBase { transient long id = scriptID(); // just for printing Map functionDefs; Evaluable[] steps; //settable LASScope scope; Map params; *() {} *(L steps) { this.steps = toTypedArray(Evaluable.class, steps); } public O get(VarContext ctx) { O result = null; var pingSource = pingSource(); for (step : steps) { ping(pingSource); result = step.get(ctx); // exiting from anything? var exiting = ctx.exitFromScript; if (exiting != null) { printVars ifdef ReturnFromScript_debug("Checking exitFromScript", +ctx, +exiting, script := this); // we're the exit point if (exiting == this) { ctx.exitFromScript = null; result = ctx.returnValue; ctx.returnValue(null); ret result; } // otherwise exit further null; } } ret result; } O getAsList(VarContext ctx) { O result = null; var pingSource = pingSource(); new L list; for (step : steps) { ping(pingSource); list.add(step.get(ctx)); // exiting from anything? var exiting = ctx.exitFromScript; if (exiting != null) { printVars ifdef ReturnFromScript_debug("Checking exitFromScript", +ctx, +exiting, script := this); // we're the exit point if (exiting == this) { ctx.exitFromScript = null; result = ctx.returnValue; ctx.returnValue(null); list.add(result); ret list; } // otherwise exit further null; } } ret list; } S toStringLong() { ret pnlToLines(steps); } toString { ret "Script " + n2(id); } FunctionDef getFunction(S name) { ret mapGet(functionDefs, name); } public Script optimizeScript aka optimize() { int n = returnValueNeeded ? steps.length-1 : steps.length; for (int i = 0; i < n; i++) steps[i] = steps[i].optimizeForReturnValueNotNeeded(); for (f : values(functionDefs)) f.optimize(); this; } } // end of Script srecord noeq FunctionDef(S name, S[] args, Script body) > Base { //settable LASScope scope; settable Type returnType = O.class; settable LASValueDescriptor[] argTypes; settable bool synthetic; *(S *name, LS args, Script *body) { this.args = toStringArray(args); } public O callFromOutside(O... args) { ret call(new FlexibleVarContext, args); } public O call(VarContext ctx, O... args) { VarContext ctx2 = /*scope != null && scope.useFixedVars ? new FixedVarContext(ctx, scope.names) :*/ new FlexibleVarContext(ctx); int n = min(l(args), l(this.args)); for i to n: ctx2.put(this.args[i], args[i]); print ifdef GazelleV_LeftArrowScript_debug(ctx2 := ctx2.vars); ret body.get(ctx2); } void optimize { body = body.optimize(); } bool isConstructor() { ret eq(name, ""); } } srecord noeq Assignment(S var, Evaluable expression) > EvaluableBase { public O get(VarContext ctx) { O o = expression.get(ctx); ctx.set(var, o); ret o; } toString { ret var + " <- " + expression; } } /*asclass FixedVarBase > EvaluableBase { settable LASScope scope; S var; int varIdx; S varToStr() { ret var + " [" + varIdx + "]"; } void assertResolved { if (varIdx < 0) fail("Unresolved variable access: " + var); } void resolve { varIdx = scope.resolveVar(var); } } persistable sclass FixedVarAssignment > FixedVarBase { Evaluable expression; *(LASScope *scope, S *var, Evaluable *expression) {} public O get(VarContext ctx) { O o = expression.get(ctx); ctx/FixedVarContext.set(varIdx, o); ret o; } toString { ret varToStr() + " <- " + expression; } }*/ // type isn't used yet srecord noeq VarDeclaration(S var, Class type, Evaluable expression) > EvaluableBase { public O get(VarContext ctx) { try { O o = expression?.get(ctx); ctx.set(var, o); ret o; } catch e { throw rethrowWithSrc(e); } } toString { ret "var " + var + " <- " + expression; } } srecord noeq AssignmentToOuterVar(S var, Evaluable expression) > EvaluableBase { public O get(VarContext ctx) { var parent = ctx.parent(); assertNotNull("No outer variable context", parent); O o = expression.get(ctx); parent.set(var, o); ret o; } toString { ret "outer " + var + " <- " + expression; } } persistable sclass NewObject > EvaluableBase { Class c; Evaluable[] args; *(Class c) { this(c, null); } *(Class *c, Evaluable[] *args) { returnType(new LASValueDescriptor.Exact(c, false)); } public O get(VarContext ctx) { try { ret preciseNuObject(c, evalArgs(args, ctx)); } catch e { throw rethrowWithSrc(e); } } toString { ret "new " + formatFunctionCall(className(c), args); } } // new object of a script-defined class // (we don't generate/resolve these classes until after the complete // parse is done so parsing stays lightweight) persistable sclass NewObject_LASClass > NewObject { ResolvableLASClass lasClass; *(ResolvableLASClass *lasClass) {} *(ResolvableLASClass *lasClass, Evaluable[] *args) {} void resolve { if (c == null) c = lasClass!; } public O get(VarContext ctx) { resolve(); ret super.get(ctx); } toString { ret "new " + formatFunctionCall(str(lasClass), args); } } // new object of a class defined in expression srecord noeq NewObject_UnknownClass(Evaluable classExpr, Evaluable[] args) > NewObject { public O get(VarContext ctx) { try { Class c = cast classExpr.get(ctx); ret preciseNuObject(c, evalArgs(args, ctx)); } catch e { throw rethrowWithSrc(e); } } toString { ret "new " + formatFunctionCall(classExpr, args); } } srecord noeq CallFunction(FunctionDef f, Evaluable[] args) > EvaluableBase { public O get(VarContext ctx) { var evaledArgs = evalArgs(args, ctx); EXITCHECK ret f.call(ctx, evaledArgs); } toString { ret formatFunctionCall(f.name, args); } public LASValueDescriptor returnType() { ret LASValueDescriptor.fromType(f.getReturnType()); } } srecord noeq GetVar(S var) > EvaluableBase { public O get(VarContext ctx) { ret ctx.get(var); } toString { ret var; } } /*persistable sclass GetFixedVar > FixedVarBase { *(LASScope *scope, S *var) {} public O get(VarContext ctx) { assertResolved(); try { ret ctx/FixedVarContext.get(varIdx); } catch ArrayIndexOutOfBoundsException e { assertResolved(); throw e; } } toString { ret var + " [" + varIdx + "]"; } }*/ srecord noeq Const(O value) > EvaluableBase { public O get(VarContext ctx) { ret value; } toString { ret strOrClassName(value); } public LASValueDescriptor returnType() { ret new LASValueDescriptor.KnownValue(value); } // Structuring this object is only done to make a class def hash, // or to show a parsed script to a user. // So the structure just has to be unique, not serializable. // As the contents of "value" could be an unpersistable object, // we just stringify it. O _serialize() { bool ok = isUnproblematicValue(value); printVars ifdef g22const_debug("Const._serialize", className := value?.getClass(), +ok); ret ok ? this : toStringWithClass(value); } } srecord noeq GetStaticField(Field field) > EvaluableBase { public O get(VarContext ctx) ctex { ret field.get(null); } // See Const._serialize S _serialize() { ret str(field); } } abstract srecord noeq CallOnTarget(Evaluable target) > EvaluableBase { HANDLENULLREFERENCE abstract O evalOnTarget(VarContext ctx, O object); public O get(VarContext ctx) { try { O object = target.get(ctx); if (object == null) ret handleNullReference(); ret evalOnTarget(ctx, object); } catch e { throw rethrowWithSrc(e); } } } persistable sclass CallMethodOrGetField > CallOnTarget { S name; *(Evaluable *target, S *name) {} O evalOnTarget(VarContext ctx, O object) { try { ret preciseGetOrCallMethod(object, name); } catch e { throw rethrowWithSrc("Was getting " + name, e); } } } persistable sclass GetField > CallOnTarget { S name; *(Evaluable *target, S *name) {} O evalOnTarget(VarContext ctx, O object) { try { ret _get(object, name); } catch e { throw rethrowWithSrc("Was getting " + name, e); } } } sclass GetVarContext > EvaluableBase { public O get(VarContext ctx) { ret ctx; } } srecord noeq ThrowMethodNotFoundException(CallMethod instruction) > EvaluableBase { public O get(VarContext ctx) { fail("Method not found: " + instruction); } } srecord noeq ThrowNullPointerException(CallMethod instruction) > EvaluableBase { public O get(VarContext ctx) { fail("Null pointer exception: " + instruction); } } persistable sclass CallMethod > CallOnTarget { S methodName; Evaluable[] args; *(Evaluable *target, S *methodName, Evaluable[] *args) {} O evalOnTarget(VarContext ctx, O object) { ret /*call*/newPreciseCall(object, methodName, evalArgs(args, ctx)); } toString { ret target + "." + formatFunctionCall(methodName, args); } // optimize to DirectMethodCallOnKnownTarget if target type // and all argument types are known exactly public Evaluable optimize() { var targetType = target.returnType(); if (targetType != null && targetType.knownValue()) { O o = targetType.value(); if (o == null) ret allowNullReference ? const(null) : new ThrowNullPointerException(this); Class[] argTypes = new[l(args)]; for i over args: { var type = args[i].returnType(); if (type == null || !type.javaClassIsExact()) this; argTypes[i] = type.javaClass(); } // can't optimize varargs L methods = findMethodsNamed_cached(o, methodName); if (any(methods, m -> m.isVarArgs())) this; var method, widening = unpair findMethod_withPrimitiveWidening_onTypes(o, methodName, argTypes); if (method == null) ret new ThrowMethodNotFoundException(this); ret new DirectMethodCallOnKnownTarget(widening, o instanceof Class ? null : o, method, args); } this; } } static O[] evalArgs(Evaluable[] args, VarContext ctx) { ret mapToArrayOrNull(args, arg -> arg.get(ctx)); } // method invocation on unknown object with "magic switch" persistable sclass CallMethodOrGlobalFunction > CallMethod { MethodOnObject globalFunction; static final new O methodNotFoundSentinel; *(Evaluable *target, S *methodName, MethodOnObject *globalFunction, Evaluable[] *args) {} // mark "magic switch" with + toString { ret target + "." + formatFunctionCall(methodName + "+", args); } O evalOnTarget(VarContext ctx, O o) { O[] realArgs = evalArgs(args, ctx); O result = newPreciseCall_sentinel(o, methodName, methodNotFoundSentinel, realArgs); if (result != methodNotFoundSentinel) ret result; print ifdef sentinelDebug("Sentinel triggered: " + globalFunction); ret newPreciseCall( globalFunction.object, globalFunction.method, itemPlusArray(o, realArgs)); } } // method invocation/field get on unknown object with "magic switch" persistable sclass CallMethodOrGetFieldOrGlobalFunction > CallMethodOrGetField { MethodOnObject globalFunction; static final new O notFoundSentinel; *(Evaluable *target, S *name, MethodOnObject *globalFunction) {} // mark "magic switch" with + toString { ret target + "." + name + "+"; } O evalOnTarget(VarContext ctx, O o) { try { O result = preciseGetOrCallMethod_sentinel(o, name, notFoundSentinel); if (result != notFoundSentinel) ret result; print ifdef sentinelDebug("Sentinel triggered: " + globalFunction); ret newPreciseCall( globalFunction.object, globalFunction.method, o); } catch e { throw rethrowWithSrc("Was getting " + name, e); } } } abstract srecord noeq LambdaBase(Class intrface) > EvaluableBase { transient Method implementedMethod; // !customConstructor *(Class *intrface) { implementedMethod = findSingleInterfaceMethodOrFail(intrface); } } // expects the interface method to have exactly one argument sclass LambdaMethodOnArgument > LambdaBase { S methodName; Evaluable[] args; *(Class intrface, S *methodName, Evaluable[] *args) { super(intrface); } public O get(VarContext ctx) { ret proxyFromInvocationHandler(intrface, (proxy, method, actualArgs) -> { if (method.getDeclaringClass() == intrface) ret forwardCall(actualArgs[0], ctx); else ret handleObjectMethodsInProxyInvocationHandler( this, implementedMethod, method, proxy, actualArgs); }); } O forwardCall(O target, VarContext ctx) { ret call(target, methodName, evalArgs(args, ctx)); } } srecord noeq LambdaDef(Class intrface, S[] args, Evaluable body) > EvaluableBase { new Map argTypes; transient Method implementedMethod; // !customConstructor *(Class *intrface, S[] *args, Evaluable *body) { implementedMethod = findSingleInterfaceMethodOrFail(intrface); if (implementedMethod.getParameterCount() != l(args)) fail("Bad parameter count for lambda: " + implementedMethod + " vs: " + joinWithComma(args)); } // We have to create the proxy when the lambda definition is // evaluated since we have to put the context in there public O get(VarContext ctx) { ret proxyFromInvocationHandler(intrface, (proxy, method, actualArgs) -> { ping(); if (method.getDeclaringClass() == intrface) { var ctx2 = new FlexibleVarContext(ctx); var argNames = args; // We already know that the count matches. for i over args: ctx2.put(argNames[i], actualArgs[i]); ret body.get(ctx2); } else ret handleObjectMethodsInProxyInvocationHandler( this, implementedMethod, method, proxy, actualArgs); }); } toString { ret "Lambda(" + shortenedSrc() + ")"; } } abstract srecord noeq CurriedLambdaBase(Class intrface, Evaluable[] curriedArgs) > EvaluableBase { transient Method implementedMethod; // !customConstructor *(Class *intrface, Evaluable[] *curriedArgs) { implementedMethod = findSingleInterfaceMethodOrFail(intrface); } public O get(VarContext ctx) { try { O[] curriedArguments = evalArgs(curriedArgs, ctx); ret proxyFromInvocationHandler(intrface, (proxy, method, actualArgs) -> { // Comparing a Method is more expensive than just // comparing the class (should be enough to distinguish // methods since we are dealing with a single-method class). // tl;dr So far we are getting away with this so all is good ^^ try { if (method.getDeclaringClass() == intrface) ret forwardCall(ctx, concatMethodArgs(curriedArguments, actualArgs)); else ret handleObjectMethodsInProxyInvocationHandler( this, implementedMethod, method, proxy, actualArgs); } catch e { throw rethrowWithSrc(e); } }); } catch e { throw rethrowWithSrc(e); } } abstract O forwardCall(VarContext ctx, O[] args); } persistable sclass CurriedMethodLambda > CurriedLambdaBase { O target; S targetMethod; *(Class intrface, O *target, S *targetMethod, Evaluable[] curriedArgs) { super(intrface, curriedArgs); } O forwardCall(VarContext ctx, O[] args) { ret newPreciseCall(target, targetMethod, args); } } sclass CurriedScriptFunctionLambda > CurriedLambdaBase { FunctionDef f; *(Class intrface, FunctionDef *f, Evaluable[] curriedArgs) { super(intrface, curriedArgs); } O forwardCall(VarContext ctx, O[] args) { ret f.call(ctx, args); } } sclass CurriedConstructorLambda > CurriedLambdaBase { Constructor[] ctors; *(Class intrface, Constructor[] *ctors, Evaluable[] curriedArgs) { super(intrface, curriedArgs); } O forwardCall(VarContext ctx, O[] args) { ret preciseNuObject(ctors, args); } } srecord noeq DirectMethodCallOnKnownTarget(bool widening, O target, Method method, Evaluable[] args) > EvaluableBase { public O get(VarContext ctx) { var evaluatedArgs = evalArgs(args, ctx); ret widening ? invokeMethodWithWidening(method, target, evaluatedArgs) : invokeMethod(method, target, evaluatedArgs); } toString { ret (target == null ? "" : target + ".") + formatFunctionCall(str(method), args); } public LASValueDescriptor returnType() { ret LASValueDescriptor.fromClass(method.getReturnType()); } } srecord noeq While(Evaluable condition, Evaluable body) > EvaluableBase { public O get(VarContext ctx) { while (!ctx.exiting() && (Bool) condition.get(ctx)) { body.get(ctx); } // while loops don't return anything null; } } srecord noeq DoWhile(Evaluable condition, Evaluable body) > EvaluableBase { public O get(VarContext ctx) { do { body.get(ctx); } while (!ctx.exiting() && (Bool) condition.get(ctx)); // while loops don't return anything null; } } // collection is an Iterable, an array etc. abstract srecord noeq ForEachBase(Evaluable collection, Evaluable body) > EvaluableBase { macro ITERATE_ARRAY { out = emptyList(array.length); for (element : array) { EXITCHECK processElement(ctx, out, element); } } public O get(VarContext ctx) { var coll = collection.get(ctx); Iterator iterator; L out; try { if (coll == null) out = new L; else if (coll.getClass().isArray()) { if (coll cast O[]) { var array = coll; ITERATE_ARRAY } else if (coll cast byte[]) { var array = coll; ITERATE_ARRAY } else if (coll cast short[]) { var array = coll; ITERATE_ARRAY } else if (coll cast double[]) { var array = coll; ITERATE_ARRAY } else if (coll cast float[]) { var array = coll; ITERATE_ARRAY } else if (coll cast int[]) { var array = coll; ITERATE_ARRAY } else if (coll cast long[]) { var array = coll; ITERATE_ARRAY } else if (coll cast char[]) { var array = coll; ITERATE_ARRAY } else if (coll cast bool[]) { var array = coll; ITERATE_ARRAY } else fail("todo for each with: " + coll); } else if (coll cast Iterable) { out = emptyList(coll); iterator = coll.iterator(); try { while (iterator.hasNext()) { EXITCHECK var element = iterator.next(); processElement(ctx, out, element); } } finally { if (iterator cast AutoCloseable) ctex { iterator.close(); } } } else fail("Not iterable: " + className(coll)); } finally { loopDone(ctx); } ret out; } abstract void processElement(VarContext ctx, L out, O o); abstract void loopDone(VarContext ctx); } persistable sclass ForEach > ForEachBase { S var; settable LASValueDescriptor varType; *(Evaluable *collection, S *var, Evaluable *body) {} void processElement(VarContext ctx, L out, O o) { ctx.set(var, o); out.add(body.get(ctx)); } void loopDone(VarContext ctx) { ctx.unset(var); } } srecord ForIterator(Evaluable iterable, S var, Evaluable body) > EvaluableBase { public O get(VarContext ctx) { VarContext subContext = new FlexibleVarContext(ctx); var iterable = this.iterable.get(ctx); Iterator iterator = iterator_gen(iterable); ret new MapI(value -> { subContext.set(var, value); ret body.get(subContext); }, iterator); } } srecord ForNested(Evaluable iterable, S var, Evaluable body) > EvaluableBase { public O get(VarContext ctx) { VarContext subContext = new FlexibleVarContext(ctx); var iterable = this.iterable.get(ctx); Iterator iterator = iterator_gen(iterable); ret nestedIterator(iterator, value -> { subContext.set(var, value); ret iterator_gen(body.get(subContext)); }); } } persistable sclass ForPairs > ForEachBase { S varA, varB; *(Evaluable *collection, Evaluable *body, S *varA, S *varB) {} void processElement(VarContext ctx, L out, O o) { Pair p = cast o; ctx.set(varA, p.a); ctx.set(varB, p.b); out.add(body.get(ctx)); } void loopDone(VarContext ctx) { ctx.unset(varA); ctx.unset(varB); } } srecord noeq ForKeyValue(Evaluable map, Evaluable body, S varA, S varB) > EvaluableBase { public O get(VarContext ctx) { Map theMap = (Map) map.get(ctx); L out; try { if (theMap != null) { out = emptyList(theMap.size()); for (entry : theMap.entrySet()) { EXITCHECK ctx.set(varA, entry.getKey()); ctx.set(varB, entry.getValue()); out.add(body.get(ctx)); } } else out = new L; } finally { ctx.unset(varA); ctx.unset(varB); } ret out; } } srecord noeq ForIntTo(Evaluable endValue, S var, Evaluable body) > EvaluableBase { public Evaluable optimize() { if (!returnValueNeeded) body = body.optimizeForReturnValueNotNeeded(); this; } public O get(VarContext ctx) { int n = (Int) endValue.get(ctx), i = 0; L out = returnValueNeeded ? new L : null; try { ctx.put(var, i); while (i < n) { EXITCHECK O o = body.get(ctx); out?.add(o); ctx.set(var, i = (Int) ctx.get(var)+1); } } finally { ctx.unset(var); } ret out; } } persistable sclass ForIndex > EvaluableBase { Evaluable collection, body; S varIndex, varElement; *(Evaluable *collection, Evaluable *body, S *varIndex, S *varElement) {} public O get(VarContext ctx) { ret new ForIndex_instance(collection, body, varIndex, varElement).get(ctx); } } sclass ForIndex_instance > ForEachBase { S varIndex, varElement; int index; *(Evaluable *collection, Evaluable *body, S *varIndex, S *varElement) {} void processElement(VarContext ctx, L out, O o) { ctx.set(varIndex, index++); ctx.set(varElement, o); out.add(body.get(ctx)); } void loopDone(VarContext ctx) { ctx.unset(varIndex); ctx.unset(varElement); } } srecord noeq IfThen(Evaluable condition, Evaluable body, Evaluable elseBranch) > EvaluableBase { IfThen(Evaluable condition, Evaluable body) { this.condition = condition; this.body = body; } public O get(VarContext ctx) { if ((Bool) condition.get(ctx)) ret body.get(ctx); else if (elseBranch != null) ret elseBranch.get(ctx); else null; } } srecord noeq ReturnFromScript(Script script, Evaluable value) > EvaluableBase { public O get(VarContext ctx) { O result = value.get(ctx); printVars ifdef ReturnFromScript_debug("ReturnFromScript", +result, +ctx, +script); ctx.exitFromScript(script); ctx.returnValue(result); null; } toString { ret formatFunctionCall ReturnFromScript(script, value); } } srecord noeq Continue(Script loopBody) > EvaluableBase { public O get(VarContext ctx) { ctx.exitFromScript(loopBody); ctx.returnValue(null); null; } toString { ret formatFunctionCall Continue(loopBody); } } srecord noeq RepeatN(Evaluable n, Evaluable body) > EvaluableBase { public O get(VarContext ctx) { long count = ((Number) n.get(ctx)).longValue(); repeat count { EXITCHECK body.get(ctx); } null; } } persistable srecord BoolAnd(Evaluable a, Evaluable b) > EvaluableBase { public O get(VarContext ctx) { if (!((Bool) a.get(ctx))) false; ret b.get(ctx); } } persistable srecord BoolOr(Evaluable a, Evaluable b) > EvaluableBase { public O get(VarContext ctx) { if (((Bool) a.get(ctx))) true; ret b.get(ctx); } } srecord noeq TempBlock(Evaluable tempExpr, Evaluable body) > EvaluableBase { public O get(VarContext ctx) { temp (AutoCloseable) tempExpr.get(ctx); ret body.get(ctx); } } srecord noeq WillReturn(Evaluable exp, Evaluable body) > EvaluableBase { public O get(VarContext ctx) { body.get(ctx); ret exp.get(ctx); } } srecord ClassDef(ResolvableLASClass lasClass) > EvaluableBase { public O get(VarContext ctx) { ret lasClass!; } } srecord noeq SetField(Evaluable target, S name, Evaluable expr) > EvaluableBase { HANDLENULLREFERENCE public O get(VarContext ctx) { try { O value = expr.get(ctx); O object = target.get(ctx); if (object == null) handleNullReference(); else set(object, name, value); ret value; } catch e { throw rethrowWithSrc(e); } } } srecord noeq SetStaticField(Field field, Evaluable expr) > EvaluableBase { public O get(VarContext ctx) { try { O value = expr.get(ctx); field.set(null, value); ret value; } catch e { throw rethrowWithSrc(e); } } } srecord noeq Throw(Evaluable expr) > EvaluableBase { public O get(VarContext ctx) { throw asRuntimeException((Throwable) expr.get(ctx)); } } srecord noeq TryCatch(Evaluable body, S var, Evaluable catchBlock) > EvaluableBase { public O get(VarContext ctx) { try { ret body.get(ctx); } catch e { var addVar = var == null ?: ctx.tempPut(var, e); temp addVar; ret catchBlock.get(ctx); } } } srecord noeq TryFinally(Evaluable body, Evaluable finallyBlock) > EvaluableBase { public O get(VarContext ctx) { try { ret body.get(ctx); } finally { finallyBlock.get(ctx); } } } static structure_Data structureDataForLAS() { new structure_Data d; d.skipDefaultValues(true); d.shouldIncludeField = field -> { S c = shortClassName(field.getDeclaringClass()); S f = field.getName(); bool shouldInclude = !(eq(c, "HasTokenRangeWithSrc") && eq(f, "src")); //printVars("shouldIncludeField", +c, +f, +shouldInclude); ret shouldInclude; }; ret d; } // with respect to serialization sbool isUnproblematicValue(O o) { ret o == null || o instanceof Number || o instanceof S || o instanceof Bool || o instanceof Class; } // These structs are a. for the user to see the parsed script // and b. for checking if two class defs are equal. // // (The resulting struct string doesn't have to unstructure()-able, // which is why we can e.g. shorten class names.) sS scriptStruct(O o) { S s = struct(o, structureDataForLAS()); LS tok = structTok(s); S prefix = shortName(GazelleV_LeftArrowScript) + "$"; for (int i = 1; i < l(tok); i += 2) tok.set(i, replacePrefix(prefix, "$", tok.get(i))); ret join(tok); } sS indentedScriptStruct(O o) { ret indentStructureString(scriptStruct(o)); } static Evaluable const(O o) { ret new Const(o); } sclass Then > CallOnTarget { CallOnTarget call1, call2; *(CallOnTarget *call1, CallOnTarget *call2) { target = call1.target; call1.target = null; } O evalOnTarget(VarContext ctx, O object) { call1.evalOnTarget(ctx, object); ret call2.evalOnTarget(ctx, object); } } srecord noeq Synchronized(Evaluable target, Evaluable body) > EvaluableBase { public Evaluable optimize() { if (!returnValueNeeded) body = body.optimizeForReturnValueNotNeeded(); this; } public O get(VarContext ctx) { synchronized(target.get(ctx)) { ret body.get(ctx); } } } } // end of GazelleV_LeftArrowScript