/** * This file is part of Erjang - A JVM-based Erlang VM * * Copyright (c) 2015 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 java.util.Set; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import com.trifork.clj_ds.IMapEntry; import com.trifork.clj_ds.IPersistentMap; import com.trifork.clj_ds.ISeq; import com.trifork.clj_ds.PersistentHashMap; import com.trifork.clj_ds.PersistentTreeMap; import erjang.m.ets.EMatchContext; import erjang.m.ets.EPattern; import erjang.m.ets.ETermPattern; public final class EMap extends EObject { public static final EMap EMPTY = new EMap(); private final IPersistentMap<EObject,EObject> _map; public EMap() { _map = new PersistentTreeMap<EObject, EObject>(null, EObject.ERLANG_ORDERING); } public EMap(IPersistentMap<EObject,EObject> it) { _map = it; } public EMap testMap() { return this; } int cmp_order() { return CMP_ORDER_MAP; } @Override public EAtom is_map() { return ERT.TRUE; } @Override public EAtom is_map_g() { return ERT.TRUE; } @Override int compare_same(EObject rhs) { EMap other = rhs.testMap(); if (other == null) throw ERT.badarg(this, rhs); int mysize = _map.count(); int othersize = other._map.count(); // compare sizes if (mysize < othersize) return -1; if (othersize > mysize) return 1; // then compare the keys ISeq<IMapEntry<EObject, EObject>> seq1 = _map.seq(); ISeq<IMapEntry<EObject, EObject>> seq2 = other._map.seq(); while (seq1 != null) { IMapEntry<EObject, EObject> ent1 = seq1.first(); IMapEntry<EObject, EObject> ent2 = seq2.first(); int keycompare = ent1.getKey().erlangCompareTo( ent2.getKey() ); if (keycompare != 0) return keycompare; seq1 = seq1.next(); seq2 = seq2.next(); } // finally, all keys must be the same so we compare the values seq1 = _map.seq(); seq2 = other._map.seq(); while (seq1 != null) { IMapEntry<EObject, EObject> ent1 = seq1.first(); IMapEntry<EObject, EObject> ent2 = seq2.first(); int keycompare = ent1.getValue().erlangCompareTo( ent2.getValue() ); if (keycompare != 0) return keycompare; seq1 = seq1.next(); seq2 = seq2.next(); } return 0; } /** * @return true if this term matches the given matcher */ public boolean match(ETermPattern matcher, EMatchContext r) { return matcher.match(this, r); } /** * @param out * @return */ public ETermPattern compileMatch(Set<Integer> out) { return EPattern.compilePattern(this, out); } public static final EAtom am_Elixir_Regex = EAtom.intern("Elixir.Regex"); public static final EAtom am___struct__ = EAtom.intern("__struct__"); public static final EAtom am_re_pattern = EAtom.intern("re_pattern"); public static final EAtom am_source = EAtom.intern("source"); public static final EAtom am_opts = EAtom.intern("opts"); @Override public Type emit_const(MethodVisitor mv) { // System.out.println("EMIT" +this); boolean is_elixir_regex = has_key(am___struct__) && get(am___struct__) == am_Elixir_Regex; Type type = Type.getType(this.getClass()); mv.visitFieldInsn(Opcodes.GETSTATIC, type.getInternalName(), "EMPTY", "Lerjang/EMap;"); for(ISeq<IMapEntry<EObject, EObject>> iit = _map.seq(); iit != null; iit = iit.next()) { IMapEntry<EObject, EObject> ent = iit.first(); // System.out.println("CONST #{ "+ ent.getKey() + " => " + ent.getValue() + "}"); ent.getKey().emit_const(mv); if (is_elixir_regex && ent.getKey() == am_re_pattern) { get(am_source).emit_const(mv); if (has_key(am_opts)) { get(am_opts).emit_const(mv); } else { EBinary.fromString("").emit_const(mv); } mv.visitMethodInsn(Opcodes.INVOKESTATIC, "erjang/ERT", "compile_elixir_regex", "(Lerjang/EObject;Lerjang/EObject;)Lerjang/EObject;"); } else { ent.getValue().emit_const(mv); } mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, type.getInternalName(), "put", "(Lerjang/EObject;Lerjang/EObject;)Lerjang/EMap;"); } return type; } @Override public String toString() { StringBuilder sb = new StringBuilder("#{"); boolean first = true; for(ISeq<IMapEntry<EObject, EObject>> iit = _map.seq(); iit != null; iit = iit.next()) { if (!first) sb.append(','); else first = false; IMapEntry<EObject, EObject> ent = iit.first(); sb.append( ent.getKey() ).append("=>").append(ent.getValue()); } sb.append("}"); return sb.toString(); } public int hashCode() { return to_list().hashCode(); } public EObject get(EObject key) { EObject v = _map.valAt(key, null); if (v == null) { throw ERT.badkey(key); } return v; } public EObject get(EObject key, EObject defaultValue) { return _map.valAt(key, defaultValue); } public EObject find(EObject key) { EObject v = _map.valAt(key, null); if (v == null) { return ERT.am_error; } return new ETuple2(ERT.am_ok, v); } public EMap put(EObject key, EObject value) { EMap res = new EMap( _map.assoc(key, value) ); // System.out.println( String.valueOf(this) + ".put("+key+","+ value+ ") => "+res); return res; } public EMap update(EObject key, EObject value) { if (!_map.containsKey(key)) throw ERT.badkey(key); return put(key, value); } public EMap remove(EObject key) { if (_map.containsKey(key)) { try { return new EMap( _map.without(key) ); } catch (Exception e) { throw new RuntimeException(e); } } else { return this; } } public static EMap from_list(ESeq list) { IPersistentMap<EObject,EObject> res = PersistentHashMap.EMPTY; while (!list.isNil()) { ETuple2 t = ETuple2.cast(list.head()); if (t == null) { throw ERT.badarg(list); } res = res.assoc(t.elem1, t.elem2); list = list.tail(); } return new EMap(res); } // used by codegen public boolean has_key(EObject key) { boolean res = _map.containsKey(key); // System.out.println( String.valueOf(this) + ".has_key("+key+") => "+res); // // if (res == false) { // (new Throwable()).printStackTrace(); // } return res; } public EAtom is_key(EObject key) { return ERT.box( _map.containsKey(key) ); } public ESeq to_list() { ESeq it = ERT.NIL; for(ISeq<IMapEntry<EObject, EObject>> iit = _map.seq(); iit != null; iit = iit.next()) { IMapEntry<EObject, EObject> ent = iit.first(); it = it.cons( new ETuple2( ent.getKey(), ent.getValue() )); } return it; } public ESeq keys() { ESeq it = ERT.NIL; for(ISeq<IMapEntry<EObject, EObject>> iit = _map.seq(); iit != null; iit = iit.next()) { IMapEntry<EObject, EObject> ent = iit.first(); it = it.cons( ent.getKey() ); } return it; } public ESeq values() { ESeq it = ERT.NIL; for(ISeq<IMapEntry<EObject, EObject>> iit = _map.seq(); iit != null; iit = iit.next()) { IMapEntry<EObject, EObject> ent = iit.first(); it = it.cons( ent.getValue() ); } return it; } public EMap merge(EMap other) { IPersistentMap<EObject, EObject> res = _map; for(ISeq<IMapEntry<EObject, EObject>> iit = other._map.seq(); iit != null; iit = iit.next()) { IMapEntry<EObject, EObject> ent = iit.first(); res = res.assoc(ent.getKey(), ent.getValue()); } return new EMap(res); } public boolean containsKey(EObject key) { return _map.containsKey(key); } public int map_size() { return _map.count(); } public EMap put_map_assoc(EObject prefetched2, EObject prefetched3) { // TODO Auto-generated method stub return null; } public EMap put_map_exact(EObject prefetched2, EObject prefetched3) { // TODO Auto-generated method stub return null; } public static EMap read(EInputStream buf) throws IOException { final int arity = buf.read_map_head(); if (arity > 0) { IPersistentMap<EObject,EObject> _m = EMPTY._map; for (int i = 0; i < arity; i++) { EObject key = buf.read_any(); EObject value = buf.read_any(); _m = _m.assoc(key, value); } // System.out.println("DID READ "+(new EMap(_m))); return new EMap(_m); } else { return EMPTY; } } @Override public void encode(EOutputStream eos) { eos.write_map_head(this.map_size()); for(ISeq<IMapEntry<EObject, EObject>> iit = _map.seq(); iit != null; iit = iit.next()) { IMapEntry<EObject, EObject> ent = iit.first(); eos.write_any(ent.getKey()); eos.write_any(ent.getValue()); } } }