namespace Eva { public interface Term : Object { public abstract string to_string(); public bool equals(Term o) { return (match(o) != null); } public Gee.Map? match(Term o) { Gee.Map vars = new Gee.HashMap(); Gee.Map aliases = new Gee.HashMap(); if(!do_match(o, vars, aliases)) { return null; } else { foreach(string a in aliases.keys) { string v = Var.alias(a, aliases); if(v in vars) vars[a] = vars[v]; } return vars; } } protected abstract bool do_match(Term o, Gee.Map vars, Gee.Map aliases); public abstract void encode(Erl.Buffer buffer); public static Term decode(void *buffer) { int index = 0; int version; Erl.decode_version(buffer, ref index, out version); return do_decode(buffer, ref index); } private static Term do_decode(void *buffer, ref int index) { int type, size; assert(Erl.get_type(buffer, ref index, out type, out size) == 0); switch(type) { case Erl.TermType.SMALL_INTEGER: char value; assert(Erl.decode_char(buffer, ref index, out value) == 0); return new Int((uchar)value); case Erl.TermType.INTEGER: long value; assert(Erl.decode_long(buffer, ref index, out value) == 0); return new Int(value); case Erl.TermType.FLOAT: double value; assert(Erl.decode_double(buffer, ref index, out value) == 0); return new Double(value); case Erl.TermType.ATOM: char[] value = new char[size+1]; assert(Erl.decode_atom(buffer, ref index, value) == 0); return new Atom(array_to_string(value, size)); case Erl.TermType.SMALL_TUPLE: case Erl.TermType.LARGE_TUPLE: assert(Erl.decode_tuple_header(buffer, ref index, out size) == 0); Term[] terms = new Term[size]; for(int i = 0; i < size; ++i) { terms[i] = do_decode(buffer, ref index); } return new Tuple(terms); case Erl.TermType.NIL: case Erl.TermType.LIST: List term = new List(); assert(Erl.decode_list_header(buffer, ref index, out size) == 0); if(size != 0) { for(int i = 0; i < size; ++i) { term.list.add(do_decode(buffer, ref index)); } term.tail = do_decode(buffer, ref index); } return term; case Erl.TermType.STRING: char[] value = new char[size+1]; assert(Erl.decode_string(buffer, ref index, value) == 0); return new String(array_to_string(value, size)); case Erl.TermType.BINARY: char[] value = new char[size]; long l; assert(Erl.decode_binary(buffer, ref index, value, out l) == 0); return new Binary(value); case Erl.TermType.PID: Erl.Pid value; assert(Erl.decode_pid(buffer, ref index, out value) == 0); return new Pid(value); case Erl.TermType.PORT: Erl.Port value; assert(Erl.decode_port(buffer, ref index, out value) == 0); return new Port(value); case Erl.TermType.REFERENCE: case Erl.TermType.NEW_REFERENCE: Erl.Ref value; assert(Erl.decode_ref(buffer, ref index, out value) == 0); return new Ref(value); default: assert_not_reached(); } } } public class Int : Object, Term { public long value {get; construct;} public Int(long v) { Object(value: v); } public string to_string() { return value.to_string(); } protected bool do_match(Term o, Gee.Map vars, Gee.Map aliases) { if(o is Var) { return o.do_match(this, vars, aliases); } if(o is Int) { return value == (o as Int).value; } else if(o is UInt) { return value == (o as UInt).value; } else { return false; } } public void encode(Erl.Buffer buffer) { buffer.encode_long(value); } } public class UInt : Object, Term { public ulong value {get; construct;} public UInt(ulong v) { Object(value: v); } public string to_string() { return value.to_string(); } protected bool do_match(Term o, Gee.Map vars, Gee.Map aliases) { if(o is Var) { return o.do_match(this, vars, aliases); } if(o is Int) { return value == (o as Int).value; } else if(o is UInt) { return value == (o as UInt).value; } else { return false; } } public void encode(Erl.Buffer buffer) { buffer.encode_ulong(value); } } public class Double : Object, Term { public double value {get; construct;} public Double(double v) { Object(value: v); } public string to_string() { return value.to_string(); } protected bool do_match(Term o, Gee.Map vars, Gee.Map aliases) { if(o is Var) { return o.do_match(this, vars, aliases); } if(o is Double) { return value == (o as Double).value; } else { return false; } } public void encode(Erl.Buffer buffer) { buffer.encode_double(value); } } public class Atom : Object, Term { public string value {get; construct;} public Atom(string v) { assert(v.validate()); Object(value: v); } public string to_string() { if(value.length == 0) return "''"; bool special = false; unichar c = value.get_char(); if(!c.islower()) special = true; for(unowned string rest = value.next_char(); rest.length != 0 && !special; rest = rest.next_char()) { c = rest.get_char(); if(!(c == '_' || c.isalnum())) special = true; } if(!special) { return value; } else { return "'" + value.replace("\\", "\\\\").replace("'", "\\'") + "'"; } } protected bool do_match(Term o, Gee.Map vars, Gee.Map aliases) { if(o is Var) { return o.do_match(this, vars, aliases); } if(o is Atom) { return value == (o as Atom).value; } else { return false; } } public void encode(Erl.Buffer buffer) { char[]? array = string_to_array(value); assert(array != null); buffer.encode_atom(array); } } public class Tuple : Object, Term { private Term[] elements {get; set;} public Tuple(Term[] terms) { elements = terms; } public int size { get { return elements.length; } } public new unowned Term get(int i) { assert(i >= 0 && i < size); return elements[i]; } public string to_string() { string ret = "{"; bool first = true; foreach(Term term in elements) { if(first) { first = false; ret += term.to_string(); } else { ret += "," + term.to_string(); } } return ret + "}"; } protected bool do_match(Term o, Gee.Map vars, Gee.Map aliases) { if(o is Var) { return o.do_match(this, vars, aliases); } if(!(o is Tuple)) return false; Tuple t = o as Tuple; if(t.size != size) return false; for(int i = 0; i < size; ++i) { if(!this[i].do_match(t[i], vars, aliases)) return false; } return true; } public void encode(Erl.Buffer buffer) { buffer.encode_tuple_header(size); foreach(Term term in elements) { term.encode(buffer); } } } public class List : Object, Term { private Term? _tail; public Gee.List list {get; construct;} public Term? tail { get { return _tail; } set { if(value is List) { List l = value as List; list.add_all(l.list); _tail = l._tail; } else { _tail = value; } } } public List() { Gee.List list0 = new Gee.LinkedList(); Object(list: list0); tail = null; } protected bool do_match(Term o, Gee.Map vars, Gee.Map aliases) { if(o is Var) { return o.do_match(this, vars, aliases); } if(o is List) { List l = o as List; if(list.size != l.list.size) return false; if((tail == null) != (l.tail == null)) return false; if(tail != null && !tail.do_match(l.tail, vars, aliases)) return false; Gee.Iterator lt = l.list.iterator(); lt.first(); foreach(Term t in list) { if(!t.do_match(lt.get(), vars, aliases)) return false; lt.next(); } return true; } else if(o is String) { if(tail != null) return false; string s = ""; foreach(Term t in list) { unichar c; if(t is Int) { Int i = t as Int; if(i.value < 0 || i.value > 255) return false; c = (unichar)i.value; } else if(t is UInt) { UInt i = t as UInt; if(i.value > 255) return false; c = (unichar)i.value; } else { return false; } string buf = string.nfill(6, 0); c.to_utf8(buf); s += buf; } return (s == (o as String).value); } else { return false; } } public string to_string() { string ret = "["; bool first = true; foreach(Term term in list) { if(first) { first = false; ret += term.to_string(); } else { ret += "," + term.to_string(); } } if(tail != null) ret += "|" + tail.to_string(); return ret + "]"; } public void encode(Erl.Buffer buffer) { if(!list.is_empty) { buffer.encode_list_header(list.size); foreach(Term term in list) { term.encode(buffer); } } if(tail == null) buffer.encode_empty_list(); else tail.encode(buffer); } } public class String : Object, Term { public string value {get; construct;} public String(string v) { Object(value: v); } public string to_string() { return "\"" + value.replace("\\", "\\\\").replace("\"", "\\\"") + "\""; } protected bool do_match(Term o, Gee.Map vars, Gee.Map aliases) { if(o is Var) { return o.do_match(this, vars, aliases); } if(o is String) { return (value == (o as String).value); } else if(o is List) { return o.do_match(this, vars, aliases); } else { return false; } } public void encode(Erl.Buffer buffer) { char[]? array = string_to_array(value); if(array != null) { buffer.encode_string(array); } else { string_to_list(value).encode(buffer); } } } public class Binary : Object, Term { public void* value {get; private set;} public long len {get; private set;} public Binary(char[] v) { value = Memory.dup(v, (uint)(sizeof(char)*v.length)); len = v.length; } public string to_string() { return "#Bin"; } protected bool do_match(Term o, Gee.Map vars, Gee.Map aliases) { if(o is Var) { return o.do_match(this, vars, aliases); } if(o is Binary) { Binary b = o as Binary; if(b.len != len) return false; return (Memory.cmp(value, b.value, len) == 0); } else { return false; } } public void encode(Erl.Buffer buffer) { buffer.encode_binary((char*)value, len); } } public class Pid : Object, Term { public string node {get; construct;} public uint num {get; construct;} public uint serial {get; construct;} public uint creation {get; construct;} public Pid(Erl.Pid pid) { this.create(array_to_string(pid.node, Erl.MAXATOMLEN), pid.num, pid.serial, pid.creation); } private Pid.create(string node0, uint num0, uint serial0, uint creation0) { Object(node: node0, num: num0, serial: serial0, creation: creation0); } public string to_string() { return "<" + node + "." + num.to_string() + "." + serial.to_string() + ">"; } protected bool do_match(Term o, Gee.Map vars, Gee.Map aliases) { if(o is Var) { return o.do_match(this, vars, aliases); } if(!(o is Pid)) return false; Pid p = o as Pid; return (node == p.node && num == p.num && serial == p.serial && creation == p.creation); } public void encode(Erl.Buffer buffer) { Erl.Pid pid = Erl.Pid(); char[]? nodedata = string_to_array(node); assert(nodedata != null); Memory.copy(pid.node, nodedata, int.min(Erl.MAXATOMLEN, nodedata.length)); pid.node[int.min(Erl.MAXATOMLEN, nodedata.length)] = 0; pid.num = num; pid.serial = serial; pid.creation = creation; buffer.encode_pid(pid); } } public class Port : Object, Term { public string node {get; construct;} public uint id {get; construct;} public uint creation {get; construct;} public Port(Erl.Port port) { this.create(array_to_string(port.node, Erl.MAXATOMLEN), port.id, port.creation); } private Port.create(string node0, uint id0, uint creation0) { Object(node: node0, id: id0, creation: creation0); } public string to_string() { return "#Port"; } protected bool do_match(Term o, Gee.Map vars, Gee.Map aliases) { if(o is Var) { return o.do_match(this, vars, aliases); } if(!(o is Port)) return false; Port p = o as Port; return (node == p.node && id == p.id && creation == p.creation); } public void encode(Erl.Buffer buffer) { Erl.Port port = Erl.Port(); char[]? nodedata = string_to_array(node); assert(nodedata != null); Memory.copy(port.node, nodedata, int.min(Erl.MAXATOMLEN, nodedata.length)); port.node[int.min(Erl.MAXATOMLEN, nodedata.length)] = 0; port.id = id; port.creation = creation; buffer.encode_port(port); } } public class Ref : Object, Term { public string node {get; construct;} public int len {get; construct;} public uint[] n {get; private set;} public uint creation {get; construct;} public Ref(Erl.Ref reference) { this.create(array_to_string(reference.node, Erl.MAXATOMLEN), reference.len, reference.n, reference.creation); } private Ref.create(string node0, int len0, uint* n0, uint creation0) { Object(node: node0, len: len0, creation: creation0); n = new uint[len]; for(int i = 0; i < len; ++i) { n[i] = n0[i]; } } public string to_string() { return "#Ref"; } protected bool do_match(Term o, Gee.Map vars, Gee.Map aliases) { if(o is Var) { return o.do_match(this, vars, aliases); } if(!(o is Ref)) return false; Ref r = o as Ref; if(node != r.node || len != r.len || creation != r.creation) { return false; } for(int i = 0; i < len; ++i) { if(n[i] != r.n[i]) return false; } return true; } public void encode(Erl.Buffer buffer) { Erl.Ref reference = Erl.Ref(); char[]? nodedata = string_to_array(node); assert(nodedata != null); Memory.copy(reference.node, nodedata, int.min(Erl.MAXATOMLEN, nodedata.length)); reference.node[int.min(Erl.MAXATOMLEN, nodedata.length)] = 0; reference.len = len; for(int i = 0; i < len; ++i) { reference.n[i] = n[i]; } reference.creation = creation; buffer.encode_ref(reference); } } public class Var : Term, Object { public string name {get; construct;} public Var(string name0) { assert(name0.get_char() == '_' || name0.get_char().isupper()); Object(name: name0); } public string to_string() { return name; } internal static string alias(string key, Gee.Map aliases) { if(!(key in aliases)) return key; else return alias(aliases[key], aliases); } protected bool do_match(Term o, Gee.Map vars, Gee.Map aliases) { string key = alias(name, aliases); if(o is Var) { Var v = o as Var; string vkey = alias(v.name, aliases); if(key == vkey) return true; if(key in vars && vkey in vars) return vars[key].do_match(vars[vkey], vars, aliases); else if(!(vkey in vars)) { aliases[vkey] = key; return true; } else /* !(key in vars) */ { aliases[key] = vkey; return true; } } else { if(key in vars) { return vars[key].do_match(o, vars, aliases); } else { vars[key] = o; return true; } } } public void encode(Erl.Buffer buffer) { assert_not_reached(); } } }