/*
* Vitry, copyright (C) Hans Hoglund 2011
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* See COPYING.txt for details.
*/
package vitry.runtime;
import static vitry.runtime.VitryRuntime.*;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import vitry.Build;
import vitry.runtime.StandardFunction.Binary;
import vitry.runtime.StandardFunction.Unary;
import vitry.prelude.*;
import vitry.runtime.error.*;
import vitry.runtime.struct.*;
import vitry.runtime.util.Strings;
/**
* Runtime system for the Vitry programming language.
*
* @author Hans Hoglund
*/
public final class VitryRuntime
{
public static final List NIL = new Nil();
public static final Symbol TRUE = Symbol.intern("true");
public static final Symbol FALSE = Symbol.intern("false");
public static final Symbol WILDCARD = Symbol.intern("_");
public static final Atom ANY = new Any();
public static final Set BOTTOM = new Bottom();
public static final Union BOOL = unionOf(TRUE, FALSE);
public static final Set NAT = NativeSet.forClass(BigInteger.class);
public static final Set INT = NativeSet.forClass(BigInteger.class);
public static final Set RAT = NativeSet.forClass(BigRational.class);
public static final Set FLOAT = NativeSet.forClass(Float.class);
public static final Set DOUBLE = NativeSet.forClass(Double.class);
public static final Set COMPLEX = null;
public static final Set CHAR = NativeSet.forClass(Character.class);
public static final Set STR = NativeSet.forClass(String.class);
static final int MIN_ARITY = 1;
static final int MAX_ARITY = 0xf;
private static final Symbol PRIM_DOUBLE = Symbol.intern("double");
private static final Symbol PRIM_FLOAT = Symbol.intern("float");
private static final Symbol PRIM_LONG = Symbol.intern("long");
private static final Symbol PRIM_INT = Symbol.intern("int");
private static final Symbol PRIM_CHAR = Symbol.intern("char");
private static final Symbol PRIM_SHORT = Symbol.intern("short");
private static final Symbol PRIM_BYTE = Symbol.intern("byte");
private static final Symbol PRIM_BOOLEAN = Symbol.intern("boolean");
/**
* Used to determine classpath etc.
*/
private final Properties setup;
/**
* Used to load modules.
*/
private ClassLoader classLoader;
// /**
// * Loaded modules.
// */
// private Seq<Module> modules;
/**
* Used to execute interpreted code.
*/
private Eval interpreter;
/**
* For the gensym facility and other things.
*/
private BigInteger uniqueState = BigInteger.valueOf(0x2177375305f7L);
/**
* Standard prelude
*
* This is non-static so that bootstrap functions can access the runtime.
*/
private Module prelude;
/**
* Classes interned for reflection.
*
* The symbol is full class name or java primitive (int, boolean etc).
*/
private final Env<Symbol, Class<?>> internedClasses = new HashEnv<Symbol, Class<?>>();
public VitryRuntime() {
this(System.getProperties());
}
public VitryRuntime(Properties setup) {
this(setup, Thread.currentThread().getContextClassLoader());
}
public VitryRuntime(Properties setup, ClassLoader classLoader)
{
this.setup = setup;
this.classLoader = classLoader;
this.interpreter = new Interpreter(this);
this.prelude = bootstrapPrelude();
initPrelude(this.prelude);
this.prelude = loadPrelude(this.prelude);
}
private Module bootstrapPrelude()
{
return new Module(Seqs.from(Symbol.intern("Vitry"), Symbol.intern("Prelude")));
}
private Module loadPrelude(Module bootstrapPrelude)
{
/*
* TODO Naive version
* We should probably obtain interpreted modules from
* getResourceAsStream() instead
*/
Function parseFile = (Function) bootstrapPrelude.getValue("parseFile");
Function eval = (Function) bootstrapPrelude.getValue("eval");
Object ast = parseFile.apply("bin/vitry/Prelude.vitry");
Module m = (Module) eval.apply(ast);
return m;
}
private void initPrelude(Module prelude)
{
prelude.def("nil", NIL);
prelude.def("true", TRUE);
prelude.def("false", FALSE);
prelude.def("bool", BOOL);
prelude.def("empty", BOTTOM);
prelude.def("()", NIL);
prelude.def("[]", productOf(NIL, new list_()));
prelude.def("{}", productOf(BOTTOM, new set_()));
prelude.def("_", ANY);
prelude.def("(,)", new product_());
prelude.def("[,]", new list_());
prelude.def("{,}", new set_());
prelude.def("(|)", new union_());
prelude.def("(&)", new intersection_());
// prelude.def("(->)", NIL); // TODO
// prelude.def("(<->)", NIL); // TODO
prelude.def("arity", new arity_());
prelude.def("id", new id());
prelude.def("const", new const_());
prelude.def("flip", new flip());
prelude.def("(.)", new compose());
prelude.def("(==)", new eq());
prelude.def("nat", NAT);
prelude.def("int", INT);
prelude.def("rat", RAT);
prelude.def("float", FLOAT);
prelude.def("double", DOUBLE);
prelude.def("complex", COMPLEX);
prelude.def("char", CHAR);
prelude.def("str", STR);
prelude.def("(<)", new lt());
prelude.def("(>)", new gt());
prelude.def("(+)", new add());
prelude.def("(-)", new sub());
prelude.def("(*)", new mul());
prelude.def("(/)", new div());
prelude.def("(%)", new mod());
prelude.def("(%%)", new modp());
prelude.def("(^)", new pow());
prelude.def("[+]", new add());
prelude.def("[-]", new sub());
prelude.def("[*]", new mul());
prelude.def("[/]", new div());
prelude.def("[%]", new mod());
prelude.def("[%%]", new modp());
prelude.def("[^]", new pow());
prelude.def("NaN", Double.NaN);
prelude.def("Infinity", Double.POSITIVE_INFINITY);
prelude.def("(++)", new concatenate());
prelude.def("[++]", new concatenate());
prelude.def("prepend", new prepend());
prelude.def("head", new head());
prelude.def("tail", new tail());
prelude.def("last", new last());
prelude.def("init", new init());
prelude.def("index", new index());
prelude.def("drop", new drop());
prelude.def("take", new take());
prelude.def("map", new map());
prelude.def("foldl", new foldl());
prelude.def("foldr", new foldr());
prelude.def("(..)", new range());
prelude.def("[..]", new range());
prelude.def("reverse", new reverse());
prelude.def("search", new search());
prelude.def("sortBy", new sortBy());
prelude.def("force", new force());
prelude.def("now", new now());
prelude.def("random", new random());
prelude.def("parse", new parse(this));
prelude.def("print", new print());
prelude.def("parseFile", new parseFile(this));
prelude.def("eval", new eval_(this));
prelude.def("error", new error_());
prelude.def("writeFile", new writeFile(this));
prelude.def("replaceFile", new replaceFile());
prelude.def("repl", new repl(this, prelude));
prelude.def("load", new load(prelude));
prelude.def("quit", new quit());
// Internal
prelude.def("__rt", this);
prelude.def("__stdin", System.in);
prelude.def("__stdout", System.out);
prelude.def("__stderr", System.err);
prelude.def("__delay", new delay());
prelude.def("rewrite", new rewrite(this));
prelude.def("seq", new seq());
prelude.def("array", new array());
prelude.def("sarray", new sarray());
prelude.def("iarray", new iarray());
prelude.def("symbol", new symbol_());
prelude.def("string", new string_());
prelude.def("errorString", new errorString());
prelude.def("parseDecl", new parseDecl(this));
prelude.def("class", new class_(this));
prelude.def("new", new new_(this));
prelude.def("method", new method(this, prelude));
prelude.def("classOf", new classOf(this));
// Fixities
prelude.defFix("(..')", 12, false, false);
prelude.defFix("(.'')", 12, false, false);
prelude.defFix("(.')", 12, false, false);
prelude.defFix("(..)", 12, false, false);
prelude.defFix("(.)", 12, false, false);
prelude.defFix("(^^)", 11, true, false);
prelude.defFix("(^)", 11, true, false);
prelude.defFix("(%%)", 10, true, false);
prelude.defFix("(%)", 10, true, false);
prelude.defFix("(/)", 10, true, false);
prelude.defFix("(*)", 10, true, false);
prelude.defFix("[%]", 10, true, false);
prelude.defFix("[/]", 10, true, false);
prelude.defFix("[*]", 10, true, false);
prelude.defFix("(-)", 9, true, false);
prelude.defFix("(+)", 9, true, false);
prelude.defFix("[-]", 9, true, false);
prelude.defFix("[+]", 9, true, false);
prelude.defFix("(++)", 9, true, false);
prelude.defFix("[++]", 9, true, false);
prelude.defFix("[,]", 8, true, true);
prelude.defFix("{,}", 8, true, true);
prelude.defFix("(,)", 8, true, true);
prelude.defFix("(&)", 7, true, false);
prelude.defFix("(|)", 6, true, false);
prelude.defFix("(->)", 5, false, false);
prelude.defFix("(<->)", 4, false, false);
prelude.defFix("(<)", 3, true, false);
prelude.defFix("(<=)", 3, true, false);
prelude.defFix("(>=)", 3, true, false);
prelude.defFix("(>)", 3, true, false);
prelude.defFix("(!=)", 3, true, false);
prelude.defFix("(==)", 3, true, false);
prelude.defFix("(&&)", 2, false, false);
prelude.defFix("(||)", 1, false, false);
prelude.defFix("($!)", 0, true, false);
prelude.defFix("($)", 0, false, false);
}
// Accessors
public Properties getSystemProperties() {
return setup;
}
public ClassLoader getClassLoader() {
return classLoader;
}
public Eval getInterpreter() {
return interpreter;
}
public void setClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public void setInterpreter(Eval interpreter) {
this.interpreter = interpreter;
}
public Module getPrelude() {
return prelude;
}
/**
* Returns the current value of the state counter.
*/
public BigInteger getUniqueState() {
return uniqueState;
}
/**
* Advances the state counter by one. Not synchronized.
*/
public BigInteger advanceUniqueState() {
uniqueState = uniqueState.add(BigInteger.ONE);
return uniqueState;
}
public Product internSymbolAndClass(String name) throws ClassNotFoundException {
Symbol sym = Symbol.intern(name);
return productOf(sym, internClass(sym));
}
public Class<?> internClass(Symbol name) throws ClassNotFoundException {
if (name.equals(PRIM_BOOLEAN)) return Boolean.TYPE;
if (name.equals(PRIM_BYTE)) return Byte.TYPE;
if (name.equals(PRIM_SHORT)) return Short.TYPE;
if (name.equals(PRIM_CHAR)) return Character.TYPE;
if (name.equals(PRIM_INT)) return Integer.TYPE;
if (name.equals(PRIM_LONG)) return Long.TYPE;
if (name.equals(PRIM_FLOAT)) return Float.TYPE;
if (name.equals(PRIM_DOUBLE)) return Double.TYPE;
if (!internedClasses.hasBinding(name)) {
Class<?> c = Class.forName(name.toString());
internedClasses.define(name, c);
}
return internedClasses.lookup(name);
}
public static Symbol internIfNotSymbol(Object s) {
if (s instanceof Symbol) return (Symbol) s;
return Symbol.intern((String) s);
}
public static List getVersion() {
return listOf(Build.MAJOR_VERSION, Build.MINOR_VERSION, Build.RELEASE_VERSION);
}
// Data
public static Symbol toVitryBool(boolean a) {
return a ? TRUE : FALSE;
}
public static boolean toPrimBool(Symbol a) {
return a != FALSE;
}
public static boolean isInvertible(Object f) {
return (f instanceof InvertibleFunction);
}
public static Product product(Seq<Pattern> s) {
if (Seqs.isNil(s)) return null; // TODO should be NIL
if (s instanceof Product) return (Product) s;
return productFrom(s);
}
public static List list(Seq<Pattern> s) {
if (Seqs.isNil(s)) return NIL;
if (s instanceof List) return (List) s;
return listFrom(s);
}
public static Set set(Seq<Pattern> s) {
if (Seqs.isNil(s)) throw new NullPointerException("Can not make a set from null");
if (s instanceof Set) return (Set) s;
return setFrom(s);
}
public static Union union(Seq<Pattern> s) {
if (Seqs.isNil(s)) throw new NullPointerException("Can not make a union from null");
if (s instanceof Union) return (Union) s;
return unionFrom(s);
}
public static Intersection intersection(Seq<Pattern> s) {
if (Seqs.isNil(s)) throw new NullPointerException("Can not make an intersection from null");
if (s instanceof Intersection) return (Intersection) s;
return intersectionFrom(s);
}
public static Product productOf(Object... args) {
return productFrom(Native.wrapAll(new ArraySeq<Object>(args)));
}
public static List listOf(Object... args) {
return listFrom(Native.wrapAll(new ArraySeq<Object>(args)));
}
public static Set setOf(Object... args) {
return setFrom(Native.wrapAll(new ArraySeq<Object>(args)));
}
public static Union unionOf(Object... args) {
return unionFrom(Native.wrapAll(new ArraySeq<Object>(args)));
}
public static Intersection intersectionOf(Object... args) {
return intersectionFrom(Native.wrapAll(new ArraySeq<Object>(args)));
}
public static Product productFrom(Seq<Pattern> s) {
return new StdProduct(s);
}
public static List listFrom(Seq<Pattern> s) {
return new StdList(s);
}
public static Set setFrom(Seq<Pattern> s) {
return new StdSet(s);
}
public static Union unionFrom(Seq<Pattern> s) {
return new StdUnion(s);
}
public static Intersection intersectionFrom(Seq<Pattern> s) {
return new StdIntersection(s);
}
private static Map<String, BigInteger> ints;
static
{
if (Build.MEMOIZE_NUMBERS)
{
ints = new java.util.WeakHashMap<String, BigInteger>();
ints.put("0", BigInteger.ZERO);
ints.put("1", BigInteger.ONE);
ints.put("10", BigInteger.TEN);
}
}
public static final BigInteger intFrom(String s)
{
if (Build.MEMOIZE_NUMBERS)
{
BigInteger v = ints.get(s);
if (v == null)
{
v = new BigInteger(s);
ints.put(s, v);
}
return v;
}
else
{
return new BigInteger(s);
}
}
// private Symbol nextUnique() {
// byte[] val = uniqueState.toByteArray();
// char[] str = new char[val.length / 2 + 1];
// for (int i = 0; i < val.length; i += 2) {
// if ( (str.length & 1) == 1) str[i / 2] = (char) (val[i]);
// else
// str[i / 2] = (char) ( (val[i] << 8) | val[i + 1]);
//
// }
// advanceUniqueState();
// return Symbol.intern(new String(str));
// }
public static <T> T throwDeconstructNil()
{
throw new TypeError("Can not deconstruct ()");
}
}
final class Any extends Atom
{
Any() {
}
@Override
public boolean eq(Atom o)
{
return o == this;
}
@Override
public boolean match(Atom o)
{
return true;
}
@Override
public boolean match(Object o)
{
return true;
}
@Override
public boolean match(Tagged p)
{
return true;
}
@Override
public boolean match(Product p)
{
return true;
}
@Override
public boolean match(Function p)
{
return true;
}
@Override
public boolean match(List p)
{
return true;
}
@Override
public boolean match(Set p)
{
return true;
}
@Override
public boolean match(Union p)
{
return true;
}
@Override
public boolean match(Intersection a)
{
return true;
}
@Override
public boolean match(Type p)
{
return true;
}
public String toString()
{
return "_";
}
}
final class Bottom extends AbstractSet
{
Bottom() {
}
@Override
public boolean eq(Set o)
{
return o == this;
}
@Override
public boolean match(Set a)
{
return a == this;
}
@Override
public boolean match(Union a)
{
return false;
}
@Override
public boolean match(Intersection a)
{
return false;
}
public String toString()
{
return "{}";
}
public int hashCode()
{
return -1;
}
public boolean isNil()
{
return false;
}
public boolean hasTail()
{
return false;
}
public Pattern head()
{
return throwUnsupported();
}
public Seq<Pattern> tail()
{
return throwUnsupported();
}
public Iterator<Pattern> iterator()
{
return ITER;
}
static <T> T throwUnsupported()
{
throw new UnsupportedOperationException("{} has no members.");
}
static final SeqIterator<Pattern> ITER = new Iter();
static class Iter extends SeqIterator<Pattern>
{
private Iter() {
super(VitryRuntime.NIL);
}
@Override
public boolean hasNext()
{
return false;
}
@Override
public Pattern next()
{
return Bottom.throwUnsupported();
}
@Override
public void remove()
{
Bottom.throwUnsupported();
}
}
}
final class Nil extends Atom implements List, Finite<Pattern>
{
Nil() {
}
public boolean eq(Atom o)
{
return o == this || Seqs.isNil(o);
}
public boolean eq(List o)
{
return Seqs.isNil(o);
}
public boolean match(List p)
{
return Seqs.isNil(p);
}
public String toString()
{
return "()";
}
public void toString(Appendable a) throws IOException
{
a.append(toString());
}
public String toFiniteString()
{
return toString();
}
public void toFiniteString(Appendable a) throws IOException
{
a.append(toString());
}
public List prepend(Pattern head)
{
// TODO product by default?
return list(new PairSeq<Pattern>(head, this));
}
public boolean isNil()
{
return true;
}
public boolean hasTail()
{
return false;
}
@SuppressWarnings("unchecked")
public <U> Seq<U> map(Function fn)
{
return (Seq<U>) NIL;
}
public Product mapProduct(Function fn)
{
return VitryRuntime.throwDeconstructNil();
}
public List mapList(Function fn)
{
return NIL;
}
public int length()
{
return 0;
}
public Pattern head()
{
return VitryRuntime.throwDeconstructNil();
}
public Product tail()
{
return VitryRuntime.throwDeconstructNil();
}
public Iterator<Pattern> iterator()
{
return ITER;
}
public SeqIterator<Pattern> seqIterator()
{
return ITER;
}
public boolean canDestruct()
{
return false;
}
public Seq<Pattern> destruct()
{
return throwDeconstructNil();
}
static final SeqIterator<Pattern> ITER = new Iter();
static class Iter extends SeqIterator<Pattern>
{
private Iter() {
super(VitryRuntime.NIL);
}
@Override
public boolean hasNext()
{
return false;
}
@Override
public Pattern next()
{
return VitryRuntime.throwDeconstructNil();
}
@Override
public void remove()
{
VitryRuntime.throwDeconstructNil();
}
}
}
final class StdProduct extends AbstractProduct
{
final Seq<Pattern> elements;
public StdProduct(Seq<Pattern> elements) {
this.elements = elements;
}
public Iterator<Pattern> iterator()
{
return elements.iterator();
}
final public Pattern head()
{
return elements.head();
}
public Product tail()
{
return product(elements.tail());
}
public boolean isNil()
{
return elements.isNil();
}
public boolean hasTail()
{
return elements.hasTail();
}
}
final class StdList extends AbstractList
{
final Seq<Pattern> elements;
public StdList(Seq<Pattern> elements) {
this.elements = elements;
}
public Iterator<Pattern> iterator()
{
return elements.iterator();
}
public Pattern head()
{
return elements.head();
}
public List tail()
{
return list(elements.tail());
}
public boolean isNil()
{
return elements.isNil();
}
public boolean hasTail()
{
return elements.hasTail();
}
}
final class StdSet extends AbstractSet
{
final Seq<Pattern> elements;
public StdSet(Seq<Pattern> elements) {
this.elements = elements;
}
public Iterator<Pattern> iterator()
{
return elements.iterator();
}
public Pattern head()
{
return elements.head();
}
public Set tail()
{
return set(elements.tail());
}
public boolean isNil()
{
return elements.isNil();
}
public boolean hasTail()
{
return elements.hasTail();
}
}
final class StdUnion extends Union
{
final Seq<Pattern> elements;
public StdUnion(Seq<Pattern> elements) {
this.elements = elements;
}
public Iterator<Pattern> iterator()
{
return elements.iterator();
}
public Pattern head()
{
return elements.head();
}
public Union tail()
{
return union(elements.tail());
}
public boolean isNil()
{
return elements.isNil();
}
public boolean hasTail()
{
return elements.hasTail();
}
}
final class StdIntersection extends Intersection
{
final Seq<Pattern> elements;
public StdIntersection(Seq<Pattern> elements) {
this.elements = elements;
}
public Iterator<Pattern> iterator()
{
return elements.iterator();
}
public Pattern head()
{
return elements.head();
}
public Intersection tail()
{
return intersection(elements.tail());
}
public boolean isNil()
{
return elements.isNil();
}
public boolean hasTail()
{
return elements.hasTail();
}
}
final class eq extends Binary
{
public Object apply(Object a, Object b)
{
if (b instanceof Pattern)
{
if (a instanceof Pattern)
{
return toVitryBool(( (Pattern) a).eqFor((Pattern) b));
}
return toVitryBool(( (Pattern) b).eq(a));
}
if (a instanceof Pattern)
{
return apply(b, a);
}
return toVitryBool(a.equals(b));
}
}
// Constructors
final class product_ extends InvertibleRestFunction
{
public Seq<?> applyVarInverse(Object obj) throws InvocationError
{
if (Seqs.isNil(obj))
{
TypeError.throwWrongStructor(obj, this);
}
if (obj instanceof Product)
{
return Native.unwrapAll(((Destructible) obj).destruct());
}
if (obj instanceof List)
{
List l = (List) obj;
Object x = Native.unwrap(l.head());
Seq<Pattern> xs = list((List) obj).tail();
return new ArraySeq<Object>(new Object[]{x, xs});
}
return TypeError.throwWrongStructor(obj, this);
}
public Object applyVar(Seq<?> args)
{
return VitryRuntime.productOf(Seqs.toArray(args));
}
public String toString()
{
return "(,)";
}
}
final class list_ extends InvertibleRestFunction
{
public Seq<?> applyVarInverse(Object a) throws InvocationError
{
if (a instanceof List)
{
return Native.unwrapAll(((List) a).destruct());
}
return TypeError.throwWrongStructor(a, this);
}
public Object applyVar(Seq<?> args)
{
return VitryRuntime.listOf(Seqs.toArray(args));
}
public String toString()
{
return "[,]";
}
}
final class set_ extends RestFunction
{
public Object applyVar(Seq<?> args)
{
return VitryRuntime.setOf(Seqs.toArray(args));
}
public String toString()
{
return "{,}";
}
}
final class union_ extends RestFunction
{
public Object applyVar(Seq<?> args)
{
return VitryRuntime.unionOf(Seqs.toArray(args));
}
public String toString()
{
return "(|)";
}
}
final class intersection_ extends RestFunction
{
public Object applyVar(Seq<?> args)
{
return VitryRuntime.intersectionOf(Seqs.toArray(args));
}
public String toString()
{
return "(&)";
}
}
// Function primitives
final class arity_ extends Unary
{
public Object apply(Object a)
{
return ((Arity) a).getArity();
}
}
final class id extends Unary
{
public Object apply(Object a)
{
return a;
}
}
final class const_ extends Unary
{
public Object apply(final Object a)
{
return new Unary()
{
public Object apply(Object b)
{
return a;
}
};
}
}
final class flip extends Unary
{
public Object apply(final Object f)
{
return new Binary() {
public Object apply(Object x, Object y) throws InvocationError
{
return ((Function) f).apply(y, x);
}
};
}
}
final class compose extends Binary
{
public Object apply(final Object f, final Object g)
{
final Function f2 = (Function) f;
final Function g2 = (Function) g;
return new Unary()
{
public Object apply(Object x) throws InvocationError
{
return f2.apply(g2.apply(x));
}
};
}
}
// Lists
final class concatenate extends Binary
{
public Object apply(Object a, Object b)
{
// TODO is unwrapping necessary?
if (a instanceof List)
{
if (b instanceof List)
{
return list(Seqs.concat((List) a, (List) b));
}
else
{
String a2 = CharSeq.toString(Native.unwrapAll((List) a));
String b2 = (String) Native.unwrap(b);
return (a2).concat(b2);
}
}
else
{
if (b instanceof List)
{
String a2 = (String) Native.unwrap(a);
String b2 = CharSeq.toString(Native.unwrapAll((List) b));
return (a2).concat(b2);
}
else
{
a = Native.unwrap(a);
b = Native.unwrap(b);
return ((String) a).concat((String) b);
}
}
}
}
final class prepend extends Binary
{
public Object apply(Object x, Object xs)
{
if (xs instanceof CharSequence) {
xs = list(Native.wrapAll(CharSeq.from((CharSequence) xs)));
}
// TODO figure out why wrapping is necessary
// return Seqs.cons(Native.wrap(x), (List) xs);
return list(Seqs.cons(Native.wrap(x), (List) xs));
}
}
final class head extends Unary
{
public Object apply(Object xs)
{
if (xs instanceof CharSequence) {
CharSequence chars = (CharSequence) xs;
if (chars.length() == 0) VitryRuntime.throwDeconstructNil();
return chars.charAt(0);
}
return Native.unwrap(((List) xs).head());
}
}
final class tail extends Unary
{
public Object apply(Object xs)
{
if (xs instanceof CharSequence) {
CharSequence chars = (CharSequence) xs;
if (chars.length() == 0) VitryRuntime.throwDeconstructNil();
if (chars.length() == 1) return NIL;
return list(Native.wrapAll(CharSeq.from(((CharSequence) xs).subSequence(1, chars.length()))));
}
return ((List) xs).tail();
}
}
final class last extends Unary
{
public Object apply(Object xs)
{
if (xs instanceof CharSequence) {
CharSequence chars = (CharSequence) xs;
if (chars.length() == 0) VitryRuntime.throwDeconstructNil();
return chars.charAt(chars.length() - 1);
}
if (Seqs.isNil(xs)) VitryRuntime.throwDeconstructNil();
return Native.unwrap(Seqs.last(((List) xs)));
}
}
final class init extends Unary
{
public Object apply(Object xs)
{
if (xs instanceof CharSequence) {
CharSequence chars = (CharSequence) xs;
if (chars.length() == 0) VitryRuntime.throwDeconstructNil();
if (chars.length() == 1) return NIL;
return list(Native.wrapAll(CharSeq.from(((CharSequence) xs).subSequence(0, chars.length() - 1))));
}
if (Seqs.isNil(xs)) VitryRuntime.throwDeconstructNil();
return list(Seqs.init((List)xs));
}
}
final class drop extends Binary
{
public Object apply(Object n, Object xs) throws InvocationError
{
if (xs instanceof CharSequence)
{
xs = list(Native.wrapAll(CharSeq.from((CharSequence) xs)));
}
if (Seqs.isNil(xs)) return NIL;
// Must memoize as xs may be a stream
return list(new MemoizedSeq<Pattern>(new DropSeq<Pattern>((List) xs, ((Number) n).intValue())));
}
}
final class take extends Binary
{
public Object apply(Object n, Object xs) throws InvocationError
{
if (xs instanceof CharSequence)
{
xs = list(Native.wrapAll(CharSeq.from((CharSequence) xs)));
}
// TODO handle nil case in TakeSeq instead
if (Seqs.isNil(xs) || ((Number) n).intValue() < 1) return NIL;
// Must memoize as xs may be a stream
return list(new MemoizedSeq<Pattern>(new TakeSeq<Pattern>((List) xs, ((Number) n).intValue())));
}
}
final class map extends Binary
{
public Object apply(Object f, Object xs)
{
if (xs instanceof CharSequence) {
xs = list(Native.wrapAll(CharSeq.from((CharSequence) xs)));
}
// TODO handle nil case in MapSeq instead
if (Seqs.isNil(xs)) return NIL;
return listFrom(Native.wrapAll(Native.unwrapAll((List) xs).map((Function) f)));
}
}
final class foldl extends StandardFunction
{
public foldl() {
super(3);
}
public Object apply(Object f, Object u, Object xs)
{
if (xs instanceof CharSequence) {
xs = list(Native.wrapAll(CharSeq.from((CharSequence) xs)));
}
if (Seqs.isNil((Seq<?>) xs)) return u;
return Seqs.foldlUnwrap((Function) f, u, (List) xs);
}
}
final class foldr extends StandardFunction
{
public foldr() {
super(3);
}
public Object apply(Object f, Object u, Object xs)
{
if (xs instanceof CharSequence) {
xs = list(Native.wrapAll(CharSeq.from((CharSequence) xs)));
}
return Seqs.foldrUnwrap((Function) f, u, (List) xs);
}
}
final class range extends Binary
{
public Object apply(Object min, Object max)
{
BigInteger minNum = (BigInteger) min;
BigInteger maxNum = (BigInteger) max;
if (minNum.compareTo(maxNum) >= 0) return NIL;
return list(Native.wrapAll(RangeSeq.create(minNum, maxNum)));
}
}
final class reverse extends Unary
{
public Object apply(Object xs)
{
if (xs instanceof CharSequence) {
throw new UnsupportedOperationException("str reverse TODO"); // TODO
}
if (Seqs.isNil(xs)) return NIL;
return list(Seqs.reverse((List) xs));
}
}
final class search extends Binary
{
public Object apply(Object e, Object xs) throws InvocationError
{
Object[] a = Seqs.toArray(Native.unwrapAll((Seq<?>) xs));
int n = Arrays.binarySearch(a, e);
return BigInteger.valueOf(n);
}
}
final class sortBy extends Binary
{
static final Symbol LT = Symbol.intern("smaller");
static final Symbol EQ = Symbol.intern("equal");
static final Symbol GT = Symbol.intern("larger");
public Object apply(Object f, Object xs) throws InvocationError
{
if (xs instanceof CharSequence) {
throw new UnsupportedOperationException("str sort TODO"); // TODO
}
Object[] a = Seqs.toArray(Native.unwrapAll((Seq<?>) xs));
Arrays.sort(a, new Comp((Function) f));
return listFrom(Native.wrapAll(Seqs.from(a)));
}
static class Comp implements Comparator<Object>
{
private final Function f;
public Comp(Function f) {
this.f = f;
}
public int compare(Object x, Object y)
{
Symbol res = (Symbol) f.apply(x, y);
if (res.eq(LT)) return -1;
if (res.eq(EQ)) return 0;
if (res.eq(GT)) return 1;
throw new TypeError(res + " does not conform to ordering");
}
}
}
final class index extends StandardFunction
{
public index() {
super(2);
}
public Object apply(Object n, Object xs)
{
if (xs instanceof String) {
xs = CharSeq.from((String) xs);
}
return Native.unwrap(Seqs.nth((Seq<?>) xs, ((Number) n).intValue()));
}
}
//final class unfold extends Binary
//{
// public Object apply(Object f, Object init) throws InvocationError
// {
// return listFrom(Native.wrapAll(new UnfoldSeq<Object>((Function) f, init)));
// }
//}
final class delay extends Unary
{
public Object apply(Object thunk) throws InvocationError
{
return listFrom(new ThunkSeq<Pattern>((Function) thunk));
}
}
/**
* Force evaluation of a list.
*/
final class force extends Binary
{
public Object apply(Object xs) throws InvocationError
{
Pattern[] elements = Seqs.toArray((List) xs, new Pattern[0]);
return listFrom(Seqs.<Pattern>from(elements));
}
}
// Internal
final class symbol_ extends Unary
{
public Object apply(Object a)
{
if (a instanceof Seq)
a = CharSeq.toString((Seq<?>) a);
return Symbol.intern((String) a);
}
}
final class string_ extends Unary
{
public Object apply(Object a) throws InvocationError
{
return a.toString();
}
}
final class errorString extends Unary
{
public Object apply(Object v) throws InvocationError
{
if (v instanceof List)
{
return ((List) v).toFiniteString();
}
return v.toString();
}
}
final class seq extends StandardFunction.Unary
{
public Object apply(Object a) {
return listOf((Object[]) a);
}
}
final class array extends StandardFunction.Unary
{
public Object apply(Object a) {
return Seqs.toArray(Native.unwrapAll((Seq<?>) a));
}
}
final class sarray extends StandardFunction.Unary
{
public Object apply(Object a) {
return Seqs.toArray(Native.<String>unwrapAll((Seq<?>) a), new String[0]);
}
}
final class iarray extends StandardFunction.Unary
{
public Object apply(Object a) {
Number[] ints = Seqs.toArray(Native.<Number>unwrapAll((Seq<?>) a), new Number[0]);
int[] primInts = new int[ints.length];
for (int i = 0; i < ints.length; i++)
{
primInts[i] = ints[i].intValue();
}
return primInts;
}
}
final class random extends Unary
{
public Object apply(Object a) throws InvocationError
{
return BigInteger.valueOf((long) (Math.random() * ((Number) a).longValue()));
}
}
final class eval_ extends StandardFunction
{
private VitryRuntime rt;
public eval_(VitryRuntime rt) {
super(1, rt.getPrelude());
this.rt = rt;
}
public Object apply(Object a)
{
return rt.getInterpreter().eval(a);
}
}
class print extends Unary
{
public Object apply(Object a) {
if (a instanceof List) {
try
{
((List) a).toString(System.out);
System.out.println();
return a;
}
catch (IOException e)
{
// Fall back on standard printing
}
catch (RuntimeException e)
{
System.out.println();
throw e;
}
}
// if (a instanceof String)
// {
// System.out.println("\"" + StrUtils.escape((String) a) + "\"");
// return a;
// }
System.out.println(a);
return a;
}
}
class error_ extends Unary
{
public Object apply(Object a) {
throw new StandardError((String) a);
}
}
class quit extends Unary
{
public Object apply(Object a) throws InvocationError {
if (a instanceof Number)
System.exit(((Number) a).intValue());
else
System.exit(-1);
assert false : "never reached";
return null;
}
}
class now extends Unary
{
public Object apply(Object a) {
return BigInteger.valueOf(System.currentTimeMillis());
}
}
class class_ extends StandardFunction
{
private static final String[] AUTO_PREFIXES = {
"",
"java.lang.",
"vitry.runtime.",
"vitry.prelude."
};
final VitryRuntime rt;
public class_(VitryRuntime rt) {
super(1);
this.rt = rt;
}
public Object apply(Object nameStr) throws InvocationError
{
Object res = null;
for (String p : AUTO_PREFIXES)
{
try
{
res = Seqs.first(rt.internSymbolAndClass(p + ((String) nameStr)));
}
catch (ClassNotFoundException _)
{
}
}
if (res != null)
return res;
else
throw new ResolveError("Could not find class " + nameStr);
}
}
class classOf extends StandardFunction
{
final VitryRuntime rt;
public classOf(VitryRuntime rt) {
super(1);
this.rt = rt;
}
public Object apply(Object obj) throws InvocationError {
Symbol ref = Symbol.intern(obj.getClass().getName());
try {
rt.internClass(ref);
} catch (ClassNotFoundException e) {
throw new AssertionError("Failed interning a loaded class");
}
return ref;
}
}
/**
* Return a function wrapping a method.
*/
final class method extends StandardFunction
{
private static final Class<?>[] dummy = new Class<?>[0];
private final VitryRuntime rt;
public method(VitryRuntime rt, Scope prelude) {
super(3, prelude);
this.rt = rt;
}
public Object apply(Object r, Object n, Object t) throws InvocationError
{
Symbol className = VitryRuntime.internIfNotSymbol(r);
String methodName = n.toString();
@SuppressWarnings("unchecked")
Seq<Symbol> typeNames = (Seq<Symbol>) t;
final Method m;
try
{
Class<?> clazz = rt.internClass(className);
Class<?>[] types = null;
if (typeNames != null)
{
types = Seqs.toArray(typeNames.<Class<?>> map(new StandardFunction.Unary()
{
public Object apply(Object n) throws InvocationError
{
try
{
return rt.internClass((Symbol) n);
}
catch (ClassNotFoundException _)
{
}
return throwResolveClass(n);
}
}), dummy);
}
m = clazz.getMethod(methodName, types);
final int arity = types.length + (isStatic(m) ? 0 : 1);
switch (arity) {
case 0:
return new StandardFunction(1) {
public Object apply(Object _) throws InvocationError
{
try
{
return m.invoke(null);
}
catch (Exception e)
{
}
return throwInvoke(m);
}
};
case 1:
return new StandardFunction(1) {
public Object apply(Object a) throws InvocationError
{
try
{
if (isStatic(m))
return m.invoke(null, a);
else
return m.invoke(a);
}
catch (Exception e)
{
}
return throwInvoke(m, a);
}
};
case 2:
return new StandardFunction(2) {
public Object apply(Object a, Object b) throws InvocationError
{
try
{
if (isStatic(m))
return m.invoke(null, a, b);
else
return m.invoke(a, b);
}
catch (Exception e)
{
}
return throwInvoke(m, a);
}
};
case 3:
return new StandardFunction(3) {
public Object apply(Object a, Object b, Object c) throws InvocationError
{
try
{
if (isStatic(m))
return m.invoke(null, a, b, c);
else
return m.invoke(a, b, c);
}
catch (Exception e)
{
}
return throwInvoke(m, a);
}
};
case 4:
return new StandardFunction(4) {
public Object apply(Object a, Object b, Object c, Object d) throws InvocationError
{
try
{
if (isStatic(m))
return m.invoke(null, a, b, c, d);
else
return m.invoke(a, b, c, d);
}
catch (Exception e)
{
}
return throwInvoke(m, a);
}
};
case 5:
return new StandardFunction(5) {
public Object apply(Object a, Object b, Object c, Object d, Object e) throws InvocationError
{
try
{
if (isStatic(m))
return m.invoke(null, a, b, c, d, e);
else
return m.invoke(a, b, c, d, e);
}
catch (Exception ex)
{
}
return throwInvoke(m, a);
}
};
default:
throw new RuntimeException("Has not implemented reflection for arity > 5");
}
}
catch (ClassNotFoundException e)
{
return throwResolveClass(className);
}
catch (SecurityException e)
{
return throwResolveMethod(methodName);
}
catch (NoSuchMethodException e)
{
return throwResolveMethod(methodName);
}
}
boolean isStatic(final Method m)
{
return Modifier.isStatic(m.getModifiers());
}
<T> T throwInvoke(Method method)
{
throw new InvocationError("Could not call method " + method + " for no arguments");
}
<T> T throwInvoke(Method method, Object args) throws InvocationError
{
throw new InvocationError("Could not call method " + method + " for arguments " + args);
}
<T> T throwResolveMethod(Object name) throws ResolveError
{
throw new ResolveError("Could not find method " + name);
}
<T> T throwResolveClass(Object name) throws ResolveError
{
throw new ResolveError("Could not find class " + name);
}
}
class new_ extends Binary
{
private VitryRuntime rt;
public new_(VitryRuntime rt) {
this.rt = rt;
}
public Object apply(Object c, Object a) {
try
{
Class<?> cl = rt.internClass((Symbol) c);
if (Seqs.isNil(a))
{
return cl.newInstance();
}
else
{
// TODO should accept subclasses of ctor args...
Seq<?> args = Native.unwrapAll((Seq<?>) a);
Seq<Class<?>> argTypes = args.map(new Unary(){
public Object apply(Object a) throws InvocationError {
return a.getClass();
}
});
return vitry.runtime.util.Utils.getConstructor(cl, Seqs.<Class<?>>toArray(argTypes, new Class<?>[0]))
.newInstance(Seqs.toArray(args));
}
}
catch (Exception e)
{
throw new ResolveError("Could not initiate class " + c, e);
}
}
}