A basic new function has been implemented

This commit is contained in:
Sameer Rahmani 2019-12-28 19:46:36 +00:00
parent e56f862183
commit 334cfbef75
4 changed files with 314 additions and 0 deletions

View File

@ -0,0 +1,6 @@
package serene.simple;
public interface INamespace {
public Object lookupSymbol(String symbolName);
public void insertSymbol(String symbolName, Object symbolValue);
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}