/**
* 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.io.IOException;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
public final class EList extends ESeq {
private final EObject head;
private final ESeq tail;
public EList(EObject h, ESeq tail) {
if (tail == null) tail = ERT.NIL;
if (h == null)
throw new NullPointerException();
this.head = h;
this.tail = tail;
}
public ECons testNonEmptyList() {
return this;
}
// only for ENil!
protected EList() {
head = tail = null;
}
@Override
public EList cons(EObject h) {
return new EList(h, this);
}
@Override
public EObject head() {
return head;
}
@Override
public ESeq tail() {
return tail;
}
@Override
public String toString() {
try {
// TODO: make this faster, we generate too many exceptions
// on account of this piece of code!
ESeq str = EString.make(this);
return str.toString();
} catch (ErlangException e) {
// ignor e//
}
StringBuffer sb = new StringBuffer("[");
assert (this instanceof EList);
ESeq val = this;
while ((val.testNil()) == null) {
if (val != this) { sb.append(","); }
sb.append(val.head());
val = val.tail();
}
sb.append("]");
return sb.toString();
}
static Type ELIST_TYPE = Type.getType(EList.class);
static Type ESEQ_TYPE = Type.getType(ESeq.class);
static Type ETERM_TYPE = Type.getType(EObject.class);
static String CONSTRUCTOR_DESC = "(" + ETERM_TYPE.getDescriptor() + ESEQ_TYPE.getDescriptor() + ")V";
@Override
public Type emit_const(MethodVisitor fa) {
Type type = ELIST_TYPE;
fa.visitTypeInsn(Opcodes.NEW, type.getInternalName());
fa.visitInsn(Opcodes.DUP);
((EObject)head).emit_const(fa);
((EObject)tail).emit_const(fa);
fa.visitMethodInsn(Opcodes.INVOKESPECIAL,
type.getInternalName(), "<init>", CONSTRUCTOR_DESC);
return type;
}
/**
* @param messages
* @return
*/
public static ESeq make(Object... messages) {
ESeq result = ERT.NIL;
for (int i = messages.length-1; i >= 0; i--) {
result = result.cons((EObject)messages[i]);
}
return result;
}
public static ESeq make(int... messages) {
ESeq result = ERT.NIL;
for (int i = messages.length-1; i >= 0; i--) {
result = result.cons(ERT.box( messages[i] ));
}
return result;
}
public static EObject read(EInputStream buf) throws IOException {
final int arity = buf.read_list_head();
EObject[] elems;
EObject tail;
if (arity > 0) {
elems = new EObject[arity];
for (int i = 0; i < arity; i++) {
elems[i] = buf.read_any();
}
/* discard the terminating nil (empty list) or read tail */
if (buf.peek1() == EExternal.nilTag) {
buf.read_nil();
tail = ERT.NIL;
} else {
tail = buf.read_any();
}
EObject res = tail;
for (int i = arity-1; i >= 0; i--) {
res = res.cons(elems[i]);
}
return res;
} else {
return ERT.NIL;
}
}
@Override
public void encode(EOutputStream eos) {
int len = this.length();
eos.write_list_head(len);
ESeq curr = this;
while (!curr.isNil()) {
eos.write_any(curr.head());
curr = curr.tail();
}
eos.write_nil();
}
}