/** * 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.m.ets; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.regex.Pattern; import com.trifork.clj_ds.ISeq; import erjang.EAtom; import erjang.EBitString; import erjang.ECons; import erjang.EFun; import erjang.EList; import erjang.EMap; import erjang.ENumber; import erjang.EObject; import erjang.EPID; import erjang.EPort; import erjang.ERT; import erjang.ERef; import erjang.ESeq; import erjang.ETuple; /** * */ public class EPattern { ETermPattern matcher; private final EObject spec; private Integer[] out_vars; private final int keyidx; @Override public String toString() { return "#Pattern<keyidx=" + keyidx + ";" + spec.toString() + ">"; } /** * @param spec */ public EPattern(int keyidx, EObject spec) { this.keyidx = keyidx; this.spec = spec; SortedSet<Integer> out = new TreeSet<Integer>(); matcher = spec.compileMatch(out); this.out_vars = out.toArray(new Integer[out.size()]); } ESeq match_members(ESeq out, Collection<ETuple> in) { for (EObject elm : in) { EMatchContext res = new EMatchContext(out_vars, elm); if (elm.match(matcher, res)) { out = out.cons(elm); } } return out; } ESeq match(ESeq out, Map<EObject, ETuple> in) { for (ETuple elm : in.values()) { ETuple val = elm.testTuple(); EMatchContext res = new EMatchContext(out_vars, val); if (matcher.match(val, res)) { out = out.cons(res.makeList()); } } return out; } /** return list of matching members */ ESeq match_members(ESeq out, Map<EObject, ETuple> in) { for (ETuple elm : in.values()) { EMatchContext res = new EMatchContext(out_vars, elm); if (matcher.match(elm, res)) { out = out.cons(elm); } } return out; } /** return list of matching members */ ESeq match_members(ESeq out, ISeq in) { while (in != null) { ETuple elm = (ETuple) in.first(); if (elm == null) break; EMatchContext res = new EMatchContext(out_vars, elm); if (matcher.match(elm, res)) { out = out.cons(elm); } in = in.next(); } return out; } /** return list of matching members' vars */ ESeq match_vars(ESeq out, ISeq in) { while (in != null) { ETuple elm = (ETuple) in.first(); if (elm == null) break; EMatchContext res = new EMatchContext(out_vars, elm); if (matcher.match(elm, res)) { out = out.cons(res.makeList()); } in = in.next(); } return out; } public static EPattern compile(int keyidx, ETuple spec) { return new EPattern(keyidx, spec); } /** matcher for '_' */ static class AnyPattern extends ETermPattern { public boolean match(ETuple t, EMatchContext r) { return true; } public boolean match(ENumber n, EMatchContext r) { return true; } public boolean match(EAtom a, EMatchContext r) { return true; } public boolean match(ECons c, EMatchContext r) { return true; } public boolean match(EPID p, EMatchContext r) { return true; } public boolean match(EPort p, EMatchContext r) { return true; } public boolean match(EBitString bs, EMatchContext r) { return true; } } /** matcher for '$N' */ static class VarPattern extends ETermPattern { private Integer name; private boolean free; /** * @param idx2 * @param out */ public VarPattern(int idx1, Set<Integer> out) { this.name = idx1; if (this.free = !out.contains(this.name)) { out.add(this.name); } } public boolean match(ETuple t, EMatchContext r) { if (free) { r.vars.put(name, t); return true; } else { return t.equalsExactly(r.vars.get(name)); } } public boolean match(ENumber t, EMatchContext r) { if (free) { r.vars.put(name, t); return true; } else { return t.equalsExactly(r.vars.get(name)); } } public boolean match(EAtom t, EMatchContext r) { if (free) { r.vars.put(name, t); return true; } else { return t.equalsExactly(r.vars.get(name)); } } public boolean match(EFun fu, EMatchContext r) { if (free) { r.vars.put(name, fu); return true; } else { return fu.equalsExactly(r.vars.get(name)); } } public boolean match(ERef fu, EMatchContext r) { if (free) { r.vars.put(name, fu); return true; } else { return fu.equalsExactly(r.vars.get(name)); } } public boolean match(ECons t, EMatchContext r) { if (free) { r.vars.put(name, t); return true; } else { return t.equalsExactly(r.vars.get(name)); } } public boolean match(EPID t, EMatchContext r) { if (free) { r.vars.put(name, t); return true; } else { return t.equalsExactly(r.vars.get(name)); } } public boolean match(EPort t, EMatchContext r) { if (free) { r.vars.put(name, t); return true; } else { return t.equalsExactly(r.vars.get(name)); } } public boolean match(EBitString t, EMatchContext r) { if (free) { r.vars.put(name, t); return true; } else { return t.equalsExactly(r.vars.get(name)); } } } static class TuplePattern extends ETermPattern { int arity; ETermPattern[] elems; /** * @param tup * @param out */ public TuplePattern(ETuple tup, Set<Integer> out) { arity = tup.arity(); elems = new ETermPattern[arity]; for (int idx1 = 1; idx1 <= arity; idx1++) { elems[idx1 - 1] = tup.elm(idx1).compileMatch(out); } } public boolean match(EObject elm, EMatchContext res) { ETuple tup; if ((tup = elm.testTuple()) == null) return false; return match(tup, res); } public boolean match(ETuple t, EMatchContext r) { if (t.arity() != arity) return false; for (int idx1 = 1; idx1 < elems.length + 1; idx1++) { if (!t.elm(idx1).match(elems[idx1 - 1], r)) return false; } return true; } } static class NilPattern extends ETermPattern { @Override public boolean match(ECons c, EMatchContext r) { return c.isNil(); } } static class ConsPattern extends ETermPattern { ETermPattern head, tail; /** * @param cons * @param out */ public ConsPattern(ECons cons, Set<Integer> out) { head = cons.head().compileMatch(out); tail = cons.tail().compileMatch(out); } public boolean match(ECons c, EMatchContext r) { return (!c.isNil()) && c.head().match(head, r) && c.tail().match(tail, r); } } /** matcher for all the non-recursive types */ static class ValuePattern extends ETermPattern { final EObject value; /** * @param epid */ public ValuePattern(EObject val) { this.value = val; } public boolean match(EPID pid, EMatchContext r) { return pid.equalsExactly(value); } public boolean match(ERef ref, EMatchContext r) { return ref.equalsExactly(value); } public boolean match(EPort port, EMatchContext r) { return port.equalsExactly(value); } public boolean match(EAtom port, EMatchContext r) { return port.equalsExactly(value); } public boolean match(EFun fu, EMatchContext r) { return fu.equalsExactly(value); } public boolean match(EBitString port, EMatchContext r) { return port.equalsExactly(value); } public boolean match(ENumber num, EMatchContext r) { return num.equalsExactly(value); } public boolean match(ETuple t, EMatchContext r) { return t.equalsExactly(value); } @Override public boolean match(ECons c, EMatchContext r) { return c.equalsExactly(value); } } /** * @param eTuple * @param out * @return */ public static ETermPattern compilePattern(ETuple tup, Set<Integer> out) { TuplePattern tp = new TuplePattern(tup, out); for (int i = 0; i < tp.elems.length; i++) { if (!(tp.elems[i] instanceof ValuePattern)) { return tp; } } return new ValuePattern(tup); } /** generic compare-equals matcher */ public static ETermPattern compilePattern(EObject epid, Set<Integer> out) { return new ValuePattern(epid); } public static ETermPattern compilePattern(EMap map, Set<Integer> out) { // TODO: Map matching semantics? return new ValuePattern(map); } /** * @param eCons * @param out * @return */ public static ETermPattern compilePattern(ECons cons, Set<Integer> out) { if (cons.isNil()) { return new NilPattern(); } else { return new ConsPattern(cons, out); } } static Pattern VAR = Pattern.compile("^\\$[0-9]+$"); static EAtom am_ANY = EAtom.intern("_"); /** * @param eAtom * @param out * @return */ public static ETermPattern compilePattern(EAtom am, Set<Integer> out) { String name = am.getName(); if (VAR.matcher(name).matches()) { int idx1 = Integer.parseInt(name.substring(1)); return new VarPattern(idx1, out); } else if (am == am_ANY) { return new AnyPattern(); } else { return new ValuePattern(am); } } /** * @param keypos1 * @return */ public EObject getKey(int keypos1) { if (matcher instanceof TuplePattern) { TuplePattern tm = (TuplePattern) matcher; if (keypos1 < 1 || keypos1 > tm.elems.length) { return null; } ETermPattern m = tm.elems[keypos1 - 1]; if (m instanceof ValuePattern) { ValuePattern vm = (ValuePattern) m; return vm.value; } } else if (matcher instanceof ValuePattern) { ValuePattern vp = (ValuePattern) matcher; if (vp.value instanceof ETuple) { ETuple et = (ETuple) vp.value; if (keypos1 < 1 || keypos1 > et.arity()) return null; return et.elm(keypos1); } } return null; } /** * @param res * @param candidate * @return */ ESeq match(ESeq out, ETuple val) { EMatchContext res = new EMatchContext(out_vars, val); if (matcher.match(val, res)) { out = out.cons(res.makeList()); } return out; } ESeq match_members(ESeq out, ETuple val) { EMatchContext res = new EMatchContext(out_vars, val); if (matcher.match(val, res)) { out = out.cons(val); } return out; } boolean match(ETuple val) { EMatchContext res = new EMatchContext(out_vars, val); return matcher.match(val, res); } }