/** * This file is part of Erjang - A JVM-based Erlang VM * * Copyright (c) 2009 by Trifork * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. **/ package erjang; import java.math.BigInteger; import java.nio.ByteBuffer; import java.io.IOException; import java.util.List; import java.util.Set; import java.util.Comparator; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; import erjang.driver.EPortControl; import erjang.m.ets.EMatchContext; import erjang.m.ets.ETermPattern; import erjang.m.java.JavaObject; /** The base class for representing Erlang values. * This class embodies in particular (the base of) the following concerns: * - Dynamic value kind testing; * - Erlang value ordering (incl. "compare-same") and "exact equality" * (as used in matching). * - The unary and binary operators built into the Erlang language * * Furthermore, the class contains helper methods concerned with: * - building and flattening of lists; * - counting up and down, and integer zero-testing. (TODO: consider these) * * Regarding EObjects and collections: * * Erjang will need to form both hash tables and ordered collections * of EObjects. * For hashing purposes, EObject implements equals() and hashCode(). * While EObjects have an ordering (the value ordering defined by Erlang), * it is not total: an integer and its floating-point equivalent are * considered equivalent as far as the ordering is concerned, though * not by equals(). * EObject therefore <i>intentionally</i> does not implement Comparable; * instead, use the explicit comparator EObject.ERLANG_ORDERING. * (This is also the reason that its comparison method is called * erlangCompareTo() rather than plain compareTo().) */ public abstract class EObject { public static final ErlangOrderComparator ERLANG_ORDERING = new ErlangOrderComparator(); public ECons cons(EObject h) { ESmall sm; if ((sm=h.testSmall()) != null && ((sm.value&0xff)==sm.value)) { return new EBinList((byte) sm.value, this); } else { return new EPair(h, this); } } public JavaObject testJavaObject() { return null; } public ERef testReference() { return null; } public EString testString() { return null; } public EBigString testBigString() { return null; } public EFun testFunction() { return null; } public EFun testFunction2(int nargs) { return null; } public EAtom testAtom() { return null; } public EMap testMap() { return null; } public ECons testNonEmptyList() { return null; } public ETuple testTuple() { return null; } public ESeq testSeq() { return null; } public EPID testPID() { return null; } public int asInt() { throw new RuntimeException("cannot convert " + this + " to int"); } public ENumber testNumber() { return null; } public ECons testCons() { return null; } public EInteger testInteger() { return null; } public ENil testNil() { return null; } public boolean isNil() { return testNil() != null; } public boolean isBoolean() { return this==ERT.TRUE || this==ERT.FALSE; } public EAtom testBoolean() { return null; } public EBinary testBinary() { return null; } /** * @return this if this object is an instance of EPort, otherwise null */ public EPort testPort() { return null; } /** * @return this if this object is an instance of ESmall, otherwise null */ public ESmall testSmall() { return null; } public EBig testBig() { return null; } /** * @return */ public EDouble testFloat() { return null; } public void visitIOList(EIOListVisitor out) throws ErlangError { throw ERT.badarg(); } public boolean collectIOList(List<ByteBuffer> out) { return false; } /** * @param rest TODO * @return TODO * @throws CharCollector.CollectingException when encountering * something that can't be decoded as characters using the given * CharCollector. Exception contains the undecoded part of the input. * @throws IOException when out.output throws IOException. * @throws InvalidElementException when the input contains an object * which is neither a list, an integer or a binary (without extra * bits), or the input contains an integer in non-head position. */ public ESeq collectCharList(CharCollector out, ESeq rest) throws CharCollector.CollectingException, CharCollector.InvalidElementException, IOException { throw new CharCollector.InvalidElementException(); } public Type emit_const(MethodVisitor mv) { throw new NotImplemented("emit_const for "+this.getClass().getName()); } // // // @BIF(name="-") public ENumber negate() { throw ERT.badarith(this); } @BIF(name="+") public ENumber add(EObject rhs) { return add(rhs, false); } @BIF(name="+") public ENumber add(ESmall rhs) { return add(rhs.value, false); } @BIF(name="+", type=BIF.Type.GUARD) public final ENumber add$g(ESmall rhs) { return add(rhs.value, true); } public ENumber add(EObject rhs, boolean guard) { if (guard) return null; throw ERT.badarith(this, rhs); } public ENumber add(int lhs, boolean guard) { if (guard) return null; throw ERT.badarith(lhs, this); } public ENumber add(double lhs, boolean guard) { if (guard) return null; throw ERT.badarith(lhs, this); } public ENumber add(BigInteger lhs, boolean guard) { if (guard) return null; throw ERT.badarith(lhs, this); } @BIF(name="-") public ENumber subtract(EObject rhs) { return subtract(rhs, false); } public ENumber subtract(ESmall rhs) { return subtract(rhs, false); } public ENumber subtract(EObject rhs, boolean guard) { if (guard) return null; throw ERT.badarith(this, rhs); } public ENumber subtract(int rhs) { throw ERT.badarith(this, rhs); } ENumber r_subtract(int lhs, boolean guard) { if (guard) return null; throw ERT.badarith(lhs, this); } ENumber r_subtract(double lhs, boolean guard) { if (guard) return null; throw ERT.badarith(lhs, this); } ENumber r_subtract(BigInteger lhs, boolean guard) { if (guard) return null; throw ERT.badarith(lhs, this); } @BIF(name="div") public EInteger idiv(EObject rhs) { throw ERT.badarith(this, rhs); } public EInteger idiv(int rhs) { throw ERT.badarith(this, rhs); } EInteger r_idiv(int lhs) { throw ERT.badarith(lhs, this); } EInteger r_idiv(BigInteger lhs) { throw ERT.badarith(lhs, this); } @BIF(name="rem") public EInteger irem(EObject rhs) { throw ERT.badarith(this, rhs); } EInteger r_irem(int lhs) { throw ERT.badarith(lhs, this); } EInteger r_irem(BigInteger lhs) { throw ERT.badarith(lhs, this); } @BIF(name="/") public EDouble divide(EObject rhs) { throw ERT.badarith(this, rhs); } EDouble r_divide(int lhs) { throw ERT.badarith(lhs, this); } EDouble r_divide(double lhs) { throw ERT.badarith(lhs, this); } EDouble r_divide(BigInteger lhs) { throw ERT.badarith(lhs, this); } @BIF(name="*") public ENumber multiply(EObject rhs) { throw ERT.badarith(this, rhs); } public ENumber r_multiply(int lhs) { throw ERT.badarith(new ESmall(lhs), this); } public EDouble r_multiply(double lhs) { throw ERT.badarith(new EDouble(lhs), this); } public ENumber r_multiply(BigInteger lhs) { throw ERT.badarith(new EBig(lhs), this); } @BIF(name="bsr") public EInteger bsr(EObject rhs) { throw ERT.badarith(this, rhs); } EInteger r_bsr(int lhs) { throw ERT.badarith(lhs, this); } EInteger r_bsr(BigInteger lhs) { throw ERT.badarith(lhs, this); } @BIF(name="bsl") public EInteger bsl(EObject rhs) { throw ERT.badarith(this, rhs); } EInteger r_bsl(int lhs) { throw ERT.badarith(lhs, this); } EInteger r_bsl(BigInteger lhs) { throw ERT.badarith(lhs, this); } @BIF(name="band") public EInteger band(EObject rhs) { throw ERT.badarith(this, rhs); } public EInteger band(int lhs) { throw ERT.badarith(lhs, this); } public EInteger band(BigInteger lhs) { throw ERT.badarith(lhs, this); } @BIF(name="bor") public EInteger bor(EObject rhs) { throw ERT.badarith(this, rhs); } public EInteger bor(int lhs) { throw ERT.badarith(lhs, this); } public EInteger bor(BigInteger lhs) { throw ERT.badarith(lhs, this); } @BIF(name="bxor") public EInteger bxor(EObject rhs) { throw ERT.badarith(this, rhs); } @BIF(name="bxor") public EInteger bxor(ESmall rhs) { return bxor(rhs.value); } public EInteger bxor(int lhs) { throw ERT.badarith(lhs, this); } public EInteger bxor(BigInteger lhs) { throw ERT.badarith(lhs, this); } @BIF(name="bnot") public EInteger bnot() { throw ERT.badarith(this); } // extra convenience public EDouble divide(double rhs) { throw ERT.badarith(this,rhs); } public EInteger irem(int rhs) { throw ERT.badarith(this,rhs); } @Override public abstract int hashCode(); @Override public boolean equals(Object other) { if (other == this) return true; if (other instanceof EObject) { return equalsExactly((EObject) other); } else { return false; } } public boolean equals(EObject other) { if (other == this) return true; return equalsExactly(other); } public final int erlangCompareTo(EObject rhs) { if (rhs == this) return 0; int cmp1 = cmp_order(); int cmp2 = rhs.cmp_order(); if ( cmp1 == cmp2 ) { return compare_same(rhs); } else if (cmp1 < cmp2) { return -1; } else { return 1; } } /** Compare two objects that have same cmp_order */ int compare_same(EObject rhs) { throw new Error("cannot compare"); } int r_compare_same(ESmall lhs) { throw new NotImplemented(); } int r_compare_same(EBig lhs) { throw new NotImplemented(); } int r_compare_same(EDouble lhs) { throw new NotImplemented(); } int r_compare_same(EInternalPID lhs) { throw new NotImplemented(); } /** Erlang "equals exactly", also called "matches" (=:= operator). * Is overridden by some subclasses, inclusing composites. */ public boolean equalsExactly(EObject rhs) { return erlangCompareTo(rhs) == 0; } /** Erlang "compares same" (== operator). */ public final boolean erlangEquals(EObject rhs) { return erlangCompareTo(rhs) == 0; } boolean r_equals_exactly(ESmall lhs) { return false; } boolean r_equals_exactly(EBig lhs) { return false; } boolean r_equals_exactly(EDouble lhs) { return false; } /** used as compare-order for "non-erlang terms", such as * compiled ets queries and the tail marker */ public static final int CMP_ORDER_ERJANG_INTERNAL = -1; public static final int CMP_ORDER_NUMBER = 0; public static final int CMP_ORDER_ATOM = 1; public static final int CMP_ORDER_REFERENCE = 2; public static final int CMP_ORDER_FUN = 3; public static final int CMP_ORDER_PORT = 4; public static final int CMP_ORDER_PID = 5; public static final int CMP_ORDER_TUPLE = 6; public static final int CMP_ORDER_MAP = 7; public static final int CMP_ORDER_LIST = 8; public static final int CMP_ORDER_BITSTRING = 9; /** * number[0] < atom[1] < reference[2] < fun[3] < port[4] < pid[5] < tuple[6] < list[7] < bit string[8] * @return */ int cmp_order() { throw new Error("cannot compare"); } @BIF public EAtom is_function(EObject arity) { return ERT.FALSE; } @BIF public EAtom is_map() { return ERT.FALSE; } @BIF(type=BIF.Type.GUARD, name="is_map") public EAtom is_map_g() { return null; } /** * @param o2 * @return */ public EAtom ge(EObject o2) { return ERT.box ( this.erlangCompareTo(o2) >= 0 ); } /** * @return */ public EBitString testBitString() { return null; } /** * @return non-null if this is an internal port */ public EInternalPort testInternalPort() { return null; } /** * @return */ public EPortControl testPortControl() { return null; } /** * @return */ public EHandle testHandle() { return null; } /** * @return */ public EInternalPID testInternalPID() { return null; } /** * @return true if this term matches the given matcher */ public boolean match(ETermPattern matcher, EMatchContext r) { return false; } /** * @param out * @return */ public ETermPattern compileMatch(Set<Integer> out) { // this should continue to be "not implemented". // subclasses should provide an implementation. throw ERT.badarg(this); } public EBinMatchState testBinMatchState() { return null; } public void encode(EOutputStream eos) { throw new NotImplemented("Encode for "+getClass().getName()); } final public boolean is_eq(EObject other) { return erlangEquals(other); } final public boolean is_eq_exact(EObject other) { return equalsExactly(other); } final public boolean is_ne(EObject other) { return !erlangEquals(other); } final public boolean is_ne_exact(EObject other) { return !equalsExactly(other); } final public boolean is_lt(EObject other) { return this.erlangCompareTo(other) < 0; } final public boolean is_ge(EObject other) { return this.erlangCompareTo(other) >= 0; } /** * @param c1 * @return */ public EObject prepend(ESeq list) { // first, rlist=lists:reverse(list) ESeq rlist = ERT.NIL; while (!list.isNil()) { rlist = rlist.cons(list.head()); list = list.tail(); } // then, prepend rlist on this EObject r = this; while(!rlist.isNil()) { r = r.cons(rlist.head()); rlist = rlist.tail(); } return r; } public ENumber inc() { return ESmall.ONE.add(this); } public ENumber dec() { return ESmall.MINUS_ONE.add(this); } public boolean is_zero() { return false; } public static abstract class ValueComparator implements Comparator<EObject> { public abstract int compare(EObject a, EObject b); } /** Comparator which doesn't distibguish between integers and floats. * Note: this comparator imposes orderings that are inconsistent with equals. */ private static class ErlangOrderComparator extends ValueComparator { public int compare(EObject a, EObject b) { return a.erlangCompareTo(b); } } }