private static LambdaN extract_function(final JsContext ctx, String funcname) { String func_mRegex = String.format("(function %1$s|[\\{;]%1$s\\p{Space}*=\\p{Space}*function)", escape(funcname)) + "\\((?<args>[a-z,]+)\\)\\{(?<code>[^\\}]+)\\}"; final Matcher func_m = Pattern.compile(func_mRegex).matcher(ctx.jscode); if (!func_m.find()) { throw new JsError("Could not find JS function " + funcname); } final String[] argnames = mscpy(func_m.group("args").split(",")); return build_function(ctx, argnames, func_m.group("code")); }
private static JsObject extract_object(final JsContext ctx, String objname) { JsObject obj = new JsObject(); String obj_mRegex = String.format("(var\\p{Space}+)?%1$s\\p{Space}*=\\p{Space}*\\{", escape(objname)) + "\\p{Space}*(?<fields>([a-zA-Z$0-9]+\\p{Space}*:\\p{Space}*function\\(.*?\\)\\p{Space}*\\{.*?\\})*)\\}\\p{Space}*;"; final Matcher obj_m = Pattern.compile(obj_mRegex).matcher(ctx.jscode); obj_m.find(); String fields = obj_m.group("fields"); // Currently, it only supports function definitions final Matcher fields_m = Pattern.compile( "(?<key>[a-zA-Z$0-9]+)\\p{Space}*:\\p{Space}*function\\((?<args>[a-z,]+)\\)\\{(?<code>[^\\}]+)\\}") .matcher(fields); while (fields_m.find()) { final String[] argnames = mscpy(fields_m.group("args").split(",")); LambdaN f = build_function(ctx, argnames, fields_m.group("code")); obj.functions.put(fields_m.group("key"), f); } return obj; }
private static Object interpret_statement( final JsContext ctx, String stmt, final Map<String, Object> local_vars, final int allow_recursion) { if (allow_recursion < 0) { throw new JsError("Recursion limit reached"); } if (stmt.startsWith("var ")) { stmt = stmt.substring("var ".length()); } final Matcher ass_m = Pattern.compile("^(?<out>[a-z]+)(\\[(?<index>.+?)\\])?=(?<expr>.*)$").matcher(stmt); Lambda1 assign; String expr; if (ass_m.find()) { if (ass_m.group("index") != null) { final Object lvar = local_vars.get(ass_m.group("out")); final Object idx = interpret_expression(ctx, ass_m.group("index"), local_vars, allow_recursion); assert idx instanceof Integer; assign = new Lambda1() { @SuppressWarnings("unchecked") @Override public Object eval(Object val) { ((List<Object>) lvar).set((Integer) idx, val); return val; } }; expr = ass_m.group("expr"); } else { final String var = ass_m.group("out"); assign = new Lambda1() { @Override public Object eval(Object val) { local_vars.put(var, val); return val; } }; expr = ass_m.group("expr"); } } else if (stmt.startsWith("return ")) { assign = new Lambda1() { @Override public Object eval(Object v) { return v; } }; expr = stmt.substring("return ".length()); } else { // Try interpreting it as an expression expr = stmt; assign = new Lambda1() { @Override public Object eval(Object v) { return v; } }; } Object v = interpret_expression(ctx, expr, local_vars, allow_recursion); return assign.eval(v); }
@SuppressWarnings("unchecked") private static Object interpret_expression( final JsContext ctx, String expr, Map<String, Object> local_vars, int allow_recursion) { if (isdigit(expr)) { return Integer.valueOf(expr); } if (isalpha(expr)) { return local_vars.get(expr); } // try: // return json.loads(expr) // except ValueError: // pass Object jsl = json_loads(expr); if (jsl != null) { return jsl; } Matcher m = Pattern.compile("^(?<var>[a-zA-Z0-9_]+)\\.(?<member>[^\\(]+)(\\((?<args>[^\\(\\)]*)\\))?$") .matcher(expr); if (m.find()) { String variable = m.group("var"); String member = m.group("member"); String arg_str = m.group("args"); Object obj = null; if (local_vars.containsKey(variable)) { obj = local_vars.get(variable); } else { if (!ctx.objects.containsKey(variable)) { ctx.objects.put(variable, extract_object(ctx, variable)); } obj = ctx.objects.get(variable); } if (arg_str == null) { // Member access if (member.equals("length")) { return len(obj); } return ((JsObject) obj).functions.get(member).eval(new Object[] {}); } if (!expr.endsWith(")")) { throw new JsError("Error parsing js code"); } List<Object> argvals = null; if (arg_str.equals("")) { argvals = new ArrayList<Object>(); } else { argvals = new ArrayList<Object>(); for (String v : arg_str.split(",")) { argvals.add(interpret_expression(ctx, v, local_vars, 20)); } } if (member.equals("split")) { // assert argvals == ('',) return list(obj); } if (member.equals("join")) { // assert len(argvals) == 1 return join((List<Object>) obj, argvals.get(0)); } if (member.equals("reverse")) { // assert len(argvals) == 0 reverse(obj); return obj; } if (member.equals("slice")) { // assert len(argvals) == 1 return slice(obj, (Integer) argvals.get(0)); } if (member.equals("splice")) { // assert isinstance(obj, list) int index = (Integer) argvals.get(0); int howMany = (Integer) argvals.get(1); List<Object> res = new ArrayList<Object>(); List<Object> list = (List<Object>) obj; for (int i = index; i < Math.min(index + howMany, len(obj)); i++) { res.add(list.remove(index)); } return res.toArray(); } return ((JsObject) obj).functions.get(member).eval(argvals.toArray()); } m = Pattern.compile("^(?<in>[a-z]+)\\[(?<idx>.+)\\]$").matcher(expr); if (m.find()) { Object val = local_vars.get(m.group("in")); Object idx = interpret_expression(ctx, m.group("idx"), local_vars, allow_recursion - 1); return ((List<?>) val).get((Integer) idx); } m = Pattern.compile("^(?<a>.+?)(?<op>[%])(?<b>.+?)$").matcher(expr); if (m.find()) { Object a = interpret_expression(ctx, m.group("a"), local_vars, allow_recursion); Object b = interpret_expression(ctx, m.group("b"), local_vars, allow_recursion); return (Integer) a % (Integer) b; } m = Pattern.compile("^(?<func>[a-zA-Z]+)\\((?<args>[a-z0-9,]+)\\)$").matcher(expr); if (m.find()) { String fname = m.group("func"); if (!ctx.functions.containsKey(fname) && ctx.jscode.length() > 0) { ctx.functions.put(fname, extract_function(ctx, fname)); } List<Object> argvals = new ArrayList<Object>(); for (String v : m.group("args").split(",")) { if (isdigit(v)) { argvals.add(Integer.valueOf(v)); } else { argvals.add(local_vars.get(v)); } } return ctx.functions.get(fname).eval(argvals.toArray()); } throw new JsError(String.format("Unsupported JS expression %s", expr)); }