A basic new function has been implemented
This commit is contained in:
parent
e56f862183
commit
334cfbef75
|
@ -0,0 +1,6 @@
|
|||
package serene.simple;
|
||||
|
||||
public interface INamespace {
|
||||
public Object lookupSymbol(String symbolName);
|
||||
public void insertSymbol(String symbolName, Object symbolValue);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package serene.simple;
|
||||
|
||||
|
||||
public class Utils {
|
||||
public static Class<?> getClassOf(Object target) {
|
||||
if (target instanceof Class) {
|
||||
return (Class<?>) target;
|
||||
}
|
||||
|
||||
return target.getClass();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package serene.simple.builtin;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import serene.simple.BaseScope;
|
||||
import serene.simple.SereneException;
|
||||
import static serene.simple.Utils.getClassOf;
|
||||
|
||||
public class NewFn extends AFn {
|
||||
public String fnName() {
|
||||
return "new";
|
||||
};
|
||||
|
||||
public Object eval(BaseScope scope) throws SereneException {
|
||||
List<Object> args = this.arguments();
|
||||
|
||||
if (args.size() == 0) {
|
||||
throw new SereneException("Needs a class to instantiate.");
|
||||
}
|
||||
else if(args.size() == 1) {
|
||||
return this.createObject((Class<?>) args.get(0));
|
||||
}
|
||||
|
||||
return this.createObject((Class<?>) args.get(0), args.subList(1, args.size()));
|
||||
}
|
||||
|
||||
private Object createObject(Class<?> c) {
|
||||
return this.createObject(c, (List<Object>) new ArrayList<Object>());
|
||||
}
|
||||
|
||||
|
||||
private Object createObject(Class c, List<Object> args) {
|
||||
Constructor<?> constructor = this.getConstructor(c, args);
|
||||
//Object[] argvals = new Object[args.size()];
|
||||
Object[] argvals = (Object[]) args.toArray();
|
||||
|
||||
for(int i = 0; i < args.size(); i++)
|
||||
|
||||
if(constructor != null) {
|
||||
try {
|
||||
return constructor.newInstance(
|
||||
Reflector.boxArgs(constructor.getParameterTypes(), argvals));
|
||||
}
|
||||
catch(Exception e) {
|
||||
throw new SereneException(
|
||||
String.format("Can't instantiate class '%s' because: %s", c.getName(), e.getMessage()));
|
||||
|
||||
}
|
||||
}
|
||||
return Reflector.invokeConstructor(c, argvals);
|
||||
}
|
||||
|
||||
private Constructor<?> getConstructor(Class<?> c, List<Object> args) {
|
||||
ArrayList<Constructor<?>> possibleInitFns = new ArrayList<Constructor<?>>();
|
||||
Constructor<?>[] inits = c.getConstructors();
|
||||
ArrayList<Class<?>[]> params = new ArrayList<Class<?>[]>();
|
||||
ArrayList<Class<?>> rets = new ArrayList<Class<?>>();
|
||||
// Constructor index on the possibleInitFns List
|
||||
int id = 0;
|
||||
|
||||
for (int i = 0; i < inits.length; i++){
|
||||
Constructor<?> initFn = inits[i];
|
||||
if(initFn.getParameterTypes().length == args.size())
|
||||
{
|
||||
possibleInitFns.add(initFn);
|
||||
params.add(initFn.getParameterTypes());
|
||||
rets.add(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (possibleInitFns.isEmpty()) {
|
||||
throw new IllegalArgumentException("No matching constructor found for " + c);
|
||||
}
|
||||
|
||||
if (possibleInitFns.size() > 1) {
|
||||
id = findMatchingConstructor(c.getName(), params, args, rets);
|
||||
}
|
||||
|
||||
return id == 0 ? null : (Constructor<?>) possibleInitFns.get(id);
|
||||
}
|
||||
|
||||
public int findMatchingConstructor(String methodName,
|
||||
ArrayList<Class<?>[]> paramlists,
|
||||
List<Object> args,
|
||||
List<Class<?>> rets) {
|
||||
// presumes matching lengths
|
||||
int matchIdx = -1;
|
||||
boolean tied = false;
|
||||
boolean foundExact = false;
|
||||
|
||||
for (int i = 0; i < paramlists.size(); i++) {
|
||||
boolean match = true;
|
||||
int exact = 0;
|
||||
|
||||
for (int p = 0; match && p < args.size(); ++p) {
|
||||
Object arg = args.get(p);
|
||||
Class<?> aclass = getClassOf(arg);
|
||||
Class<?> pclass = paramlists.get(i)[p];
|
||||
|
||||
// TODO: handle null aclass here here
|
||||
if (aclass == pclass)
|
||||
exact++;
|
||||
else
|
||||
match = Reflector.paramArgTypeMatch(pclass, aclass);
|
||||
}
|
||||
|
||||
if (exact == args.size()) {
|
||||
if (!foundExact || matchIdx == -1 || rets.get(matchIdx).isAssignableFrom(rets.get(i)))
|
||||
matchIdx = i;
|
||||
|
||||
tied = false;
|
||||
foundExact = true;
|
||||
|
||||
}
|
||||
else if (match && !foundExact) {
|
||||
if (matchIdx == -1)
|
||||
matchIdx = i;
|
||||
else {
|
||||
if (subsumes(paramlists.get(i), paramlists.get(matchIdx))) {
|
||||
matchIdx = i;
|
||||
tied = false;
|
||||
}
|
||||
else if (Arrays.equals(paramlists.get(matchIdx), paramlists.get(i))) {
|
||||
if (rets.get(matchIdx).isAssignableFrom(rets.get(i)))
|
||||
matchIdx = i;
|
||||
}
|
||||
else if (!(subsumes(paramlists.get(matchIdx), paramlists.get(i))))
|
||||
tied = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tied)
|
||||
throw new IllegalArgumentException(
|
||||
"More than one matching method found: " + methodName);
|
||||
|
||||
return matchIdx;
|
||||
}
|
||||
|
||||
static public boolean subsumes(Class<?>[] c1, Class<?>[] c2){
|
||||
//Presumes matching lengths
|
||||
Boolean better = false;
|
||||
|
||||
for(int i = 0; i < c1.length; i++) {
|
||||
if(c1[i] != c2[i]) {
|
||||
if(!c1[i].isPrimitive() && c2[i].isPrimitive()
|
||||
||
|
||||
c2[i].isAssignableFrom(c1[i]))
|
||||
better = true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return better;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
/**
|
||||
* I have borrowed the foundations of this namespace from Clojure
|
||||
* Which is published under EPL license.
|
||||
*/
|
||||
|
||||
package serene.simple.builtin;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import serene.simple.SereneException;
|
||||
|
||||
public class Reflector {
|
||||
static Object boxArg(Class<?> paramType, Object arg) {
|
||||
if (!paramType.isPrimitive())
|
||||
return paramType.cast(arg);
|
||||
else if (paramType == boolean.class)
|
||||
return Boolean.class.cast(arg);
|
||||
else if (paramType == char.class)
|
||||
return Character.class.cast(arg);
|
||||
else if (arg instanceof Number) {
|
||||
Number n = (Number) arg;
|
||||
if (paramType == int.class)
|
||||
return n.intValue();
|
||||
else if (paramType == float.class)
|
||||
return n.floatValue();
|
||||
else if (paramType == double.class)
|
||||
return n.doubleValue();
|
||||
else if (paramType == long.class)
|
||||
return n.longValue();
|
||||
else if (paramType == short.class)
|
||||
return n.shortValue();
|
||||
else if (paramType == byte.class)
|
||||
return n.byteValue();
|
||||
}
|
||||
throw new IllegalArgumentException(
|
||||
"Unexpected param type, expected: " + paramType + ", given: " + arg.getClass().getName());
|
||||
}
|
||||
|
||||
|
||||
static Object[] boxArgs(Class<?>[] params, Object[] args) {
|
||||
if (params.length == 0)
|
||||
return null;
|
||||
Object[] ret = new Object[params.length];
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
Object arg = args[i];
|
||||
Class<?> paramType = params[i];
|
||||
ret[i] = boxArg(paramType, arg);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static Object invokeConstructor(Class<?> c, Object[] args) {
|
||||
try {
|
||||
Constructor<?>[] allctors = c.getConstructors();
|
||||
ArrayList<Object> ctors = new ArrayList<Object>();
|
||||
|
||||
for (int i = 0; i < allctors.length; i++) {
|
||||
Constructor<?> ctor = allctors[i];
|
||||
if (ctor.getParameterTypes().length == args.length)
|
||||
ctors.add(ctor);
|
||||
}
|
||||
if (ctors.isEmpty()) {
|
||||
throw new IllegalArgumentException("No matching ctor found" + " for " + c);
|
||||
} else if (ctors.size() == 1) {
|
||||
Constructor<?> ctor = (Constructor<?>) ctors.get(0);
|
||||
return ctor.newInstance(boxArgs(ctor.getParameterTypes(), args));
|
||||
} else // overloaded w/same arity
|
||||
{
|
||||
for (Iterator iterator = ctors.iterator(); iterator.hasNext();) {
|
||||
Constructor<?> ctor = (Constructor<?>) iterator.next();
|
||||
Class<?>[] params = ctor.getParameterTypes();
|
||||
if (isCongruent(params, args)) {
|
||||
Object[] boxedArgs = boxArgs(params, args);
|
||||
return ctor.newInstance(boxedArgs);
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No matching ctor found" + " for " + c);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new SereneException(
|
||||
String.format("Can't intantiate '%s': %s", c.getName(), e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
static public boolean paramArgTypeMatch(Class<?> paramType, Class<?> argType) {
|
||||
if (argType == null)
|
||||
return !paramType.isPrimitive();
|
||||
if (paramType == argType || paramType.isAssignableFrom(argType))
|
||||
return true;
|
||||
if (paramType == int.class)
|
||||
return argType == Integer.class || argType == long.class || argType == Long.class || argType == short.class
|
||||
|| argType == byte.class;// || argType == FixNum.class;
|
||||
else if (paramType == float.class)
|
||||
return argType == Float.class || argType == double.class;
|
||||
else if (paramType == double.class)
|
||||
return argType == Double.class || argType == float.class;// || argType == DoubleNum.class;
|
||||
else if (paramType == long.class)
|
||||
return argType == Long.class || argType == int.class || argType == short.class || argType == byte.class;// ||
|
||||
// argType
|
||||
// ==
|
||||
// BigNum.class;
|
||||
else if (paramType == char.class)
|
||||
return argType == Character.class;
|
||||
else if (paramType == short.class)
|
||||
return argType == Short.class;
|
||||
else if (paramType == byte.class)
|
||||
return argType == Byte.class;
|
||||
else if (paramType == boolean.class)
|
||||
return argType == Boolean.class;
|
||||
return false;
|
||||
}
|
||||
|
||||
static boolean isCongruent(Class<?>[] params, Object[] args) {
|
||||
boolean ret = false;
|
||||
if (args == null)
|
||||
return params.length == 0;
|
||||
if (params.length == args.length) {
|
||||
ret = true;
|
||||
for (int i = 0; ret && i < params.length; i++) {
|
||||
Object arg = args[i];
|
||||
Class<?> argType = (arg == null) ? null : arg.getClass();
|
||||
Class<?> paramType = params[i];
|
||||
ret = paramArgTypeMatch(paramType, argType);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue