Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 30 additions & 9 deletions src/main/java/com/dashjoin/jsonata/Functions.java
Original file line number Diff line number Diff line change
Expand Up @@ -1831,8 +1831,15 @@ public static Object merge(List arg) {
var result = new LinkedHashMap<>();

for (Object obj : arg) {
for (Entry entry : ((Map<Object,Object>)obj).entrySet()) {
result.put(entry.getKey(), entry.getValue());
if (obj instanceof List) {
// Java: JS "for (var prop in obj)" also iterates arrays (keys are the indexes as strings).
// Can happen where the signature is not validated, e.g. $merge(?)
for (int i=0; i<((List)obj).size(); i++)
result.put(""+i, ((List)obj).get(i));
} else if (obj instanceof Map) {
for (Entry entry : ((Map<Object,Object>)obj).entrySet()) {
result.put(entry.getKey(), entry.getValue());
}
}
}
return result;
Expand Down Expand Up @@ -1865,19 +1872,33 @@ public static List reverse(List arr) {
* @throws Throwable
* @returns {Array} - the resultant array
*/
public static List each(Map obj, Object func) throws Throwable {
public static List each(Object obj, Object func) throws Throwable {
if (obj==null) {
return null;
}

var result = createSequence();

for (var key : obj.keySet()) {
var func_args = hofFuncArgs(func, obj.get(key), key, obj);
// invoke func
var val = funcApply(func, func_args);
if(val != null) {
result.add(val);
if (obj instanceof List) {
// Java: JS "for (var key in obj)" also iterates arrays (keys are the indexes as strings).
// Can happen where the signature is not validated, e.g. $each(?, ...)
var list = (List)obj;
for (int i=0; i<list.size(); i++) {
var func_args = hofFuncArgs(func, list.get(i), ""+i, obj);
// invoke func
var val = funcApply(func, func_args);
if(val != null) {
result.add(val);
}
}
} else if (obj instanceof Map) {
for (var key : ((Map)obj).keySet()) {
var func_args = hofFuncArgs(func, ((Map)obj).get(key), key, obj);
// invoke func
var val = funcApply(func, func_args);
if(val != null) {
result.add(val);
}
}
}

Expand Down
144 changes: 85 additions & 59 deletions src/main/java/com/dashjoin/jsonata/Jsonata.java
Original file line number Diff line number Diff line change
Expand Up @@ -1742,16 +1742,16 @@ Jsonata getPerThreadInstance() {
// trampoline loop - this gets invoked as a result of tail-call optimization
// the Object returned a tail-call thunk
// unpack it, evaluate its arguments, and apply the tail call
var next = /* await */ evaluate(((Symbol)result).body.procedure, ((Symbol)result).input, ((Symbol)result).environment);
if(((Symbol)result).body.procedure.type == "variable") {
var next = /* await */ evaluate(((Symbol)((Symbol)result).body).procedure, ((Symbol)result).input, ((Symbol)result).environment);
if(((Symbol)((Symbol)result).body).procedure.type == "variable") {
if (next instanceof Symbol) // Java: not if JFunction
((Symbol)next).token = ((Symbol)result).body.procedure.value;
((Symbol)next).token = ((Symbol)((Symbol)result).body).procedure.value;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bad formatting
closing parenthesis looks like for "if (next instanceof Symbol)" but not

if (next instanceof Symbol) // Java: not if JFunction
((Symbol)next).position = ((Symbol)result).body.procedure.position;
((Symbol)next).position = ((Symbol)((Symbol)result).body).procedure.position;
var evaluatedArgs = new ArrayList<>();
for(var ii = 0; ii < ((Symbol)result).body.arguments.size(); ii++) {
evaluatedArgs.add(/* await */ evaluate(((Symbol)result).body.arguments.get(ii), ((Symbol)result).input, ((Symbol)result).environment));
for(var ii = 0; ii < ((Symbol)((Symbol)result).body).arguments.size(); ii++) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe extract (Symbol)result to var?
so many cast. so many code. and now it's twice cast

evaluatedArgs.add(/* await */ evaluate(((Symbol)((Symbol)result).body).arguments.get(ii), ((Symbol)result).input, ((Symbol)result).environment));
}

result = /* await */ applyInner(next, evaluatedArgs, input, environment);
Expand Down Expand Up @@ -1908,15 +1908,15 @@ Object evaluateLambda(Symbol expr, Object input, Frame environment) {
var evaluatedArgs = new ArrayList<>();
for(var ii = 0; ii < expr.arguments.size(); ii++) {
var arg = expr.arguments.get(ii);
if (arg.type.equals("operator") && (arg.value.equals("?"))) {
if (arg.type.equals("operator") && (""+arg.value).equals("?")) {
Comment thread
aeberhart marked this conversation as resolved.
Resolved
evaluatedArgs.add(arg);
} else {
evaluatedArgs.add(/* await */ evaluate(arg, input, environment));
}
}
// lookup the procedure
var proc = /* await */ evaluate(expr.procedure, input, environment);
if (proc != null && expr.procedure.type.equals("path") && environment.lookup((String)expr.procedure.steps.get(0).value)!=null) {
if (proc == null && expr.procedure.type.equals("path") && environment.lookup((String)expr.procedure.steps.get(0).value)!=null) {
Comment thread
aeberhart marked this conversation as resolved.
Resolved
// help the user out here if they simply forgot the leading $
throw new JException("T1007",
expr.position,
Expand All @@ -1927,8 +1927,8 @@ Object evaluateLambda(Symbol expr, Object input, Frame environment) {
result = partialApplyProcedure((Symbol)proc, (List)evaluatedArgs);
} else if (Utils.isFunction(proc)) {
result = partialApplyNativeFunction((JFunction)proc /*.implementation*/, evaluatedArgs);
// } else if (typeof proc === "function") {
// result = partialApplyNativeFunction(proc, evaluatedArgs);
} else if (proc instanceof JLambda) {
result = partialApplyNativeFunction(new JFunction((JLambda)proc, null), evaluatedArgs);
} else {
throw new JException("T1008",
//stack: (new Error()).stack,
Expand Down Expand Up @@ -1973,12 +1973,12 @@ Object validateArguments(Object signature, Object args, Object context) {
if (i>=args.size()) break;
env.bind(""+proc.arguments.get(i).value, args.get(i));
}
if (proc.body instanceof Symbol) {
result = evaluate(proc.body, proc.input, env);
if (proc.body instanceof JFunction) {
// this is a lambda that wraps a native Object - generated by partially evaluating a native
result = /* await */ applyNativeFunction((JFunction)proc.body, env);
} else if (proc.body instanceof Symbol) {
result = evaluate((Symbol)proc.body, proc.input, env);
} else throw new Error("Cannot execute procedure: "+proc+" "+proc.body);
// if (typeof proc.body === "function") {
// // this is a lambda that wraps a native Object - generated by partially evaluating a native
// result = /* await */ applyNativeFunction(proc.body, env);
return result;
}

Expand All @@ -1997,7 +1997,7 @@ Object partialApplyProcedure(Symbol proc, List<Symbol> args) {
for (var param : proc.arguments) {
// proc.arguments.forEach(Object (param, index) {
Object arg = index<args.size() ? args.get(index) : null;
if ((arg==null) || (arg instanceof Symbol && ("operator".equals(((Symbol)arg).type) && "?".equals(((Symbol)arg).value)))) {
if (arg instanceof Symbol && ("operator".equals(((Symbol)arg).type) && "?".equals(""+((Symbol)arg).value))) {
Comment thread
aeberhart marked this conversation as resolved.
Resolved
unboundArgs.add(param);
} else {
env.bind((String)param.value, arg);
Expand All @@ -2024,58 +2024,53 @@ Object partialApplyNativeFunction(JFunction _native, List args) {
// create a lambda Object that wraps and invokes the native function
// get the list of declared arguments from the native function
// this has to be picked out from the toString() value


//var body = "function($a,$c) { $substring($a,0,$c) }";

List sigArgs = new ArrayList<>();
List partArgs = new ArrayList<>();
for (int i=0; i<_native.getNumberOfArgs(); i++) {
String argName = "$" + (char)('a'+i);
sigArgs.add(argName);
if (i>=args.size() || args.get(i)==null)
partArgs.add(argName);
else
partArgs.add(args.get(i));
}

var body = "function(" + String.join(", ", sigArgs) + "){";
body += "$"+_native.functionName+"("+String.join(", ", sigArgs) + ") }";

if (log.isLoggable(Level.FINE)) log.fine("partial trampoline = "+body);

// var sigArgs = getNativeFunctionArguments(_native);
// sigArgs = sigArgs.stream().map(sigArg -> {
// return "$" + sigArg;
// }).toList();
// var body = "function(" + String.join(", ", sigArgs) + "){ _ }";
if (_native.getNumberOfArgs() < 0) {
// Java: parameters unknown (callable without signature), use the number of args supplied
var fn = new JFunction(_native.function, null);
fn.numberOfArgs = args.size();
_native = fn;
}
var sigArgs = getNativeFunctionArguments(_native);
sigArgs = sigArgs.stream().map(sigArg -> {
return "$" + sigArg;
}).collect(Collectors.toList());
var body = "function(" + String.join(", ", sigArgs) + "){ _ }";

var bodyAST = parser.parse(body);
//bodyAST.body = _native;
bodyAST.body = _native;

var partial = partialApplyProcedure(bodyAST, (List)args);
return partial;
}

/**
* Apply native function
* @param {Object} proc - Procedure
* @param {Object} env - Environment
* @returns {*} Result of applying native function
*/
/* async */ Object applyNativeFunction(JFunction proc, Frame env) {
// Not called in Java - JFunction call directly calls native function
return null;
var sigArgs = getNativeFunctionArguments(proc);
// generate the array of arguments for invoking the function - look them up in the environment
var args = sigArgs.stream().map(sigArg -> {
return env.lookup(sigArg);
}).collect(Collectors.toList());

var result = proc.call(null, args);
return result;
}

/**
* Get native Object arguments
* @param {Function} func - Function
* @returns {*|Array} Native Object arguments
*/
List getNativeFunctionArguments(JFunction func) {
// Not called in Java
return null;
List<String> getNativeFunctionArguments(JFunction func) {
// Java: parameter names are not available, use a, b, c, ...
var sigArgs = new ArrayList<String>();
for (int i=0; i<func.getNumberOfArgs(); i++)
sigArgs.add("" + (char)('a'+i));
return sigArgs;
}

/**
Expand All @@ -2098,34 +2093,34 @@ public static JFunction function(String name, String signature, Class clazz, Obj
}

public static<A,B,R> JFunction function(String name, FnVarArgs<R> func, String signature) {
return new JFunction(func.getJFunctionCallable(), signature);
return new JFunction(func, signature);
}
public static<A,R> JFunction function(String name, Fn0<R> func, String signature) {
return new JFunction(func.getJFunctionCallable(), signature);
return new JFunction(func, signature);
}
public static<A,B,R> JFunction function(String name, Fn1<A,R> func, String signature) {
return new JFunction(func.getJFunctionCallable(), signature);
return new JFunction(func, signature);
}
public static<A,B,R> JFunction function(String name, Fn2<A,B,R> func, String signature) {
return new JFunction(func.getJFunctionCallable(), signature);
return new JFunction(func, signature);
}
public static<A,B,C,R> JFunction function(String name, Fn3<A,B,C,R> func, String signature) {
return new JFunction(func.getJFunctionCallable(), signature);
return new JFunction(func, signature);
}
public static<A,B,C,D,R> JFunction function(String name, Fn4<A,B,C,D,R> func, String signature) {
return new JFunction(func.getJFunctionCallable(), signature);
return new JFunction(func, signature);
}
public static<A,B,C,D,E,R> JFunction function(String name, Fn5<A,B,C,D,E,R> func, String signature) {
return new JFunction(func.getJFunctionCallable(), signature);
return new JFunction(func, signature);
}
public static<A,B,C,D,E,F,R> JFunction function(String name, Fn6<A,B,C,D,E,F,R> func, String signature) {
return new JFunction(func.getJFunctionCallable(), signature);
return new JFunction(func, signature);
}
public static<A,B,C,D,E,F,G,R> JFunction function(String name, Fn7<A,B,C,D,E,F,G,R> func, String signature) {
return new JFunction(func.getJFunctionCallable(), signature);
return new JFunction(func, signature);
}
public static<A,B,C,D,E,F,G,H,R> JFunction function(String name, Fn8<A,B,C,D,E,F,G,H,R> func, String signature) {
return new JFunction(func.getJFunctionCallable(), signature);
return new JFunction(func, signature);
}

/**
Expand Down Expand Up @@ -2181,6 +2176,14 @@ public Frame createFrame(Frame enclosingEnvironment) {
}

public static interface JLambda {
JFunctionCallable getJFunctionCallable();

/**
* @return the number of declared parameters, -1 if unknown (varargs)
*/
default int getNumberOfArgs() {
return -1;
}
}

public static interface FnVarArgs<R> extends JLambda, Function<List<?>, R> {
Expand All @@ -2191,42 +2194,49 @@ default JFunctionCallable getJFunctionCallable() {
}
}
public static interface Fn0<R> extends JLambda, Supplier<R> {
default int getNumberOfArgs() { return 0; }
default JFunctionCallable getJFunctionCallable() {
return (input, args) -> get();
}
}
public static interface Fn1<A,R> extends JLambda, Function<A,R> {
default int getNumberOfArgs() { return 1; }
default JFunctionCallable getJFunctionCallable() {
return (input, args) -> apply((A) args.get(0));
}
}
public static interface Fn2<A,B,R> extends JLambda, BiFunction<A,B,R> {
default int getNumberOfArgs() { return 2; }
default JFunctionCallable getJFunctionCallable() {
return (input, args) -> apply((A) args.get(0), (B) args.get(1));
}
}
public static interface Fn3<A,B,C,R> extends JLambda {
default int getNumberOfArgs() { return 3; }
R apply(A a, B b, C c);
default JFunctionCallable getJFunctionCallable() {
return (input, args) -> apply((A) args.get(0), (B) args.get(1),
(C) args.get(2));
}
}
public static interface Fn4<A,B,C,D,R> extends JLambda {
default int getNumberOfArgs() { return 4; }
R apply(A a, B b, C c, D d);
default JFunctionCallable getJFunctionCallable() {
return (input, args) -> apply((A) args.get(0), (B) args.get(1),
(C) args.get(2), (D) args.get(3));
}
}
public static interface Fn5<A,B,C,D,E,R> extends JLambda {
default int getNumberOfArgs() { return 5; }
R apply(A a, B b, C c, D d, E e);
default JFunctionCallable getJFunctionCallable() {
return (input, args) -> apply((A) args.get(0), (B) args.get(1),
(C) args.get(2), (D) args.get(3), (E) args.get(4));
}
}
public static interface Fn6<A,B,C,D,E,F,R> extends JLambda {
default int getNumberOfArgs() { return 6; }
R apply(A a, B b, C c, D d, E e, F f);
default JFunctionCallable getJFunctionCallable() {
return (input, args) -> apply((A) args.get(0), (B) args.get(1),
Expand All @@ -2235,6 +2245,7 @@ default JFunctionCallable getJFunctionCallable() {
}
}
public static interface Fn7<A,B,C,D,E,F,G,R> extends JLambda {
default int getNumberOfArgs() { return 7; }
R apply(A a, B b, C c, D d, E e, F f, G g);
default JFunctionCallable getJFunctionCallable() {
return (input, args) -> apply((A) args.get(0), (B) args.get(1),
Expand All @@ -2243,6 +2254,7 @@ default JFunctionCallable getJFunctionCallable() {
}
}
public static interface Fn8<A,B,C,D,E,F,G,H,R> extends JLambda {
default int getNumberOfArgs() { return 8; }
R apply(A a, B b, C c, D d, E e, F f, G g, H h);
default JFunctionCallable getJFunctionCallable() {
return (input, args) -> apply((A) args.get(0), (B) args.get(1),
Expand Down Expand Up @@ -2271,6 +2283,12 @@ public static class JFunction implements JFunctionCallable, JFunctionSignatureVa
Signature signature;
Method method;
Object methodInstance;
int numberOfArgs = -1;

public JFunction(JLambda lambda, String signature) {
this(lambda.getJFunctionCallable(), signature);
this.numberOfArgs = lambda.getNumberOfArgs();
}

public JFunction(JFunctionCallable function, String signature) {
this.function = function;
Expand Down Expand Up @@ -2317,8 +2335,16 @@ public Object validate(Object args, Object context) {
return args;
}

/**
* @return the number of declared parameters, -1 if unknown
*/
public int getNumberOfArgs() {
return method != null ? method.getParameterTypes().length : 0;
if (method != null)
return method.getParameterTypes().length;
if (numberOfArgs >= 0)
return numberOfArgs;
// Java: a JFunctionCallable has no declared parameters, use the signature (if any)
return signature != null ? signature.getNumberOfArgs() : -1;
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/dashjoin/jsonata/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public class Symbol implements Cloneable, Serializable {
// Procedure:
Symbol procedure;
List<Symbol> arguments;
Symbol body;
Object body; // Symbol, or JFunction for a partially applied native function
List<Symbol> predicate;
public List<Symbol> stages;
public Object input;
Expand Down Expand Up @@ -1291,7 +1291,7 @@ Symbol processAST(Symbol expr) {
result.arguments = expr.arguments;
result.signature = expr.signature;
result.position = expr.position;
var body = processAST(expr.body);
var body = processAST((Symbol)expr.body);
result.body = tailCallOptimize(body);
break;
case "condition":
Expand Down
Loading
Loading