From 94de356834be40b1e14c85a016a2e0a9f1768053 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 11 Jul 2010 08:28:49 +0200 Subject: Added support for parsing strings to complex terms --- src/Atom.vala | 2 +- src/CMakeLists.txt | 3 +- src/List.vala | 6 + src/Parse.vala | 392 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/Pid.vala | 4 +- src/Port.vala | 4 +- src/Ref.vala | 4 +- src/String.vala | 2 +- src/Term.vala | 8 +- src/Util.vala | 4 +- test/Test.vala | 2 +- 11 files changed, 417 insertions(+), 14 deletions(-) create mode 100644 src/Parse.vala diff --git a/src/Atom.vala b/src/Atom.vala index bb3a6fd..012b506 100644 --- a/src/Atom.vala +++ b/src/Atom.vala @@ -46,7 +46,7 @@ namespace Eva { } public void encode(Erl.Buffer buffer) { - char[]? array = string_to_array(value); + char[]? array = string_to_binary(value); assert(array != null); buffer.encode_atom(array); } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 92f8340..bbe5c19 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,10 +9,11 @@ vala_precompile(EVA_C Int.vala List.vala PacketHandler.vala + Parse.vala Pid.vala Port.vala - String.vala Ref.vala + String.vala Term.vala Tuple.vala UInt.vala diff --git a/src/List.vala b/src/List.vala index 7637998..9340fd7 100644 --- a/src/List.vala +++ b/src/List.vala @@ -50,6 +50,12 @@ namespace Eva { _tail = null; } + public static List from_list(Gee.List list) { + if(list.is_empty) + return empty; + else + return new List(list.first(), List.from_list(list[1:list.size])); + } protected bool do_match(Term o, Gee.Map vars, Gee.Map aliases) { if(o is Var) { diff --git a/src/Parse.vala b/src/Parse.vala new file mode 100644 index 0000000..7cadd35 --- /dev/null +++ b/src/Parse.vala @@ -0,0 +1,392 @@ +namespace Eva { + internal static Term? parse(string str) { + string s = str.chomp(); + + if(s.length == 0) + return null; + + Term? ret = parse_term(ref s); + + if(s.length != 0) + return null; + else + return ret; + } + + private static Term? parse_term(ref string str) { + str = str.chug(); + unichar c = str.get_char(); + + if(c.isdigit() || c == '-') { + return parse_number(ref str); + } + else if(c.islower() || c == '\'') { + return parse_atom(ref str); + } + else if(c == '"') { + return parse_string(ref str); + } + else if(c.isupper() || c == '_') { + return parse_var(ref str); + } + else if(c == '<') { + return parse_binary(ref str); + } + else if(c == '{') { + return parse_tuple(ref str); + } + else if(c == '[') { + return parse_list(ref str); + } + else { + return null; + } + } + + private static Term? parse_number(ref string str) { + bool positive = true; + bool fractional = false; + + unowned string s = str; + + if(s.get_char() == '-') { + positive = false; + s = s.next_char(); + + if(s.length == 0 || !s.get_char().isdigit()) + return null; + } + + if(s.get_char() == '.') + return null; + + while(s.length > 0) { + unichar c = s.get_char(); + + if(c == '.') { + if(fractional) + return null; + + fractional = true; + + s = s.next_char(); + if(s.length == 0 || !s.get_char().isdigit()) + return null; + + continue; + } + + if(!c.isdigit()) + break; + + s = s.next_char(); + } + + string number = str.substring(0, str.pointer_to_offset(s)); + str = s; + + if(fractional) { + return new Double(number.to_double()); + } + else if(positive) { + return new UInt(number.to_ulong()); + } + else { + return new Int(number.to_long()); + } + } + + private static Term? parse_atom(ref string str) { + if(str.get_char() == '\'') { + string atom = ""; + unowned string s = str.next_char(); + + while(s.length > 0) { + unichar c = s.get_char(); + + if(c == '\'') + break; + + if(c == '\\') { + s = s.next_char(); + if(s.length == 0) + return null; + c = s.get_char(); + } + + if(c > 255) { + return null; + } + + string buf = string.nfill(6, 0); + c.to_utf8(buf); + atom += buf; + + s = s.next_char(); + } + + if(s.length == 0) + return null; + + Term ret = new Atom(atom); + str = s.next_char(); + + return ret; + } + else { + unowned string s = str; + + while(s.length > 0) { + unichar c = s.get_char(); + + if(c != '_' && !c.isalnum()) { + break; + } + + s = s.next_char(); + } + + Term ret = new Atom(str.substring(0, str.pointer_to_offset(s))); + str = s; + + return ret; + } + } + + private static Term? parse_string(ref string str) { + string ret = do_parse_string(ref str); + if(ret == null) + return null; + + return new String(ret); + } + + private static string? do_parse_string(ref string str) { + if(str.get_char() != '"') { + return null; + } + + string retstr = ""; + unowned string s = str.next_char(); + + while(s.length > 0) { + unichar c = s.get_char(); + + if(c == '\"') + break; + + if(c == '\\') { + s = s.next_char(); + if(s.length == 0) + return null; + c = s.get_char(); + } + + string buf = string.nfill(6, 0); + c.to_utf8(buf); + retstr += buf; + + s = s.next_char(); + } + + if(s.length == 0) + return null; + + str = s.next_char(); + + return retstr; + } + + private static Term? parse_var(ref string str) { + unowned string s = str; + + while(s.length > 0) { + unichar c = s.get_char(); + + if(c != '_' && !c.isalnum()) { + break; + } + + s = s.next_char(); + } + + Term ret = new Var(str.substring(0, str.pointer_to_offset(s))); + str = s; + + return ret; + } + + private static Term? parse_tuple(ref string str) { + Gee.ArrayList list = new Gee.ArrayList(); + + if(str.get_char() != '{') + return null; + + unowned string s = str.next_char(); + + while(s.length > 0 && s.get_char().isspace()) { + s = s.next_char(); + } + + if(s.length == 0) + return null; + + if(s.get_char() == '}') { + str = s.next_char(); + return new Tuple({}); + } + + while(s.length > 0) { + if(s.get_char().isspace()) { + s = s.next_char(); + continue; + } + + str = s; + Term t = parse_term(ref str); + if(t == null) + return null; + list.add(t); + + s = str; + + while(s.length > 0 && s.get_char().isspace()) { + s = s.next_char(); + } + + unichar c = s.get_char(); + if(c == '}') { + str = s.next_char(); + Term[] terms = list.to_array(); + return new Tuple(terms); + } + else if(c != ',') { + return null; + } + else { + s = s.next_char(); + } + } + + return null; + } + + private static Term? parse_list(ref string str) { + if(str.get_char() != '[') + return null; + + str = str.next_char(); + return parse_list_tail(ref str); + } + + private static Term? parse_list_tail(ref string str) { + str = str.chug(); + + if(str.length == 0) + return null; + + if(str.get_char() == ']') { + str = str.next_char(); + return List.empty; + } + + Term head = parse_term(ref str); + if(head == null) + return null; + + str = str.chug(); + if(str.length == 0) + return null; + unichar c = str.get_char(); + str = str.next_char(); + + switch(c) { + case ']': + return new List(head); + case ',': + return new List(head, parse_list_tail(ref str)); + case '|': + Term ret = new List(head, parse_term(ref str)); + + str = str.chug(); + if(str.length == 0 || str.get_char() != ']') + return null; + str = str.next_char(); + + return ret; + default: + return null; + } + } + + private static Term? parse_binary(ref string str) { + if(str.length < 4) + return null; + + if(str[0:2] != "<<") + return null; + + str = str.offset(2).chug(); + + if(str[0:2] == ">>") { + str = str.offset(2); + return new Binary({}); + } + + if(str.get_char() == '"') { + string ret = do_parse_string(ref str); + if(ret == null) + return null; + + str = str.chug(); + + if(str.length < 2 || str[0:2] != ">>") + return null; + + str = str.offset(2); + char[] binary = string_to_binary(ret); + return new Binary(binary); + } + else { + Gee.ArrayList data = new Gee.ArrayList(); + + while(str.length > 0) { + unichar c = str.get_char(); + + if(c != '-' && !c.isdigit()) + return null; + + unowned string beg = str; + unowned string end = str.next_char(); + + while(end.length > 0 && end.get_char().isdigit()) + end = end.next_char(); + + data.add((char)beg.substring(0, beg.pointer_to_offset(end)).to_int()); + + str = end.chug(); + + if(str.length >= 2 && str[0:2] == ">>") { + str = str.offset(2); + + // Workaround for libgee to_array bug 597737 + char[] array = new char[data.size]; + int index = 0; + foreach(char i in data) { + array[index++] = i; + } + + return new Binary(array); + } + else if(str.length != 0 && str.get_char() == ',') { + str = str.next_char().chug(); + } + else { + return null; + } + } + + return null; + } + } +} diff --git a/src/Pid.vala b/src/Pid.vala index 3f71df7..68f9a4f 100644 --- a/src/Pid.vala +++ b/src/Pid.vala @@ -6,7 +6,7 @@ namespace Eva { 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); + this.create(binary_to_string(pid.node, Erl.MAXATOMLEN), pid.num, pid.serial, pid.creation); } private Pid.create(string node0, uint num0, uint serial0, uint creation0) { @@ -32,7 +32,7 @@ namespace Eva { public void encode(Erl.Buffer buffer) { Erl.Pid pid = Erl.Pid(); - char[]? nodedata = string_to_array(node); + char[]? nodedata = string_to_binary(node); assert(nodedata != null); Memory.copy(pid.node, nodedata, int.min(Erl.MAXATOMLEN, nodedata.length)); diff --git a/src/Port.vala b/src/Port.vala index b67c808..ae3980d 100644 --- a/src/Port.vala +++ b/src/Port.vala @@ -5,7 +5,7 @@ namespace Eva { public uint creation {get; construct;} public Port(Erl.Port port) { - this.create(array_to_string(port.node, Erl.MAXATOMLEN), port.id, port.creation); + this.create(binary_to_string(port.node, Erl.MAXATOMLEN), port.id, port.creation); } private Port.create(string node0, uint id0, uint creation0) { @@ -31,7 +31,7 @@ namespace Eva { public void encode(Erl.Buffer buffer) { Erl.Port port = Erl.Port(); - char[]? nodedata = string_to_array(node); + char[]? nodedata = string_to_binary(node); assert(nodedata != null); Memory.copy(port.node, nodedata, int.min(Erl.MAXATOMLEN, nodedata.length)); diff --git a/src/Ref.vala b/src/Ref.vala index 2510d78..b1fd4e9 100644 --- a/src/Ref.vala +++ b/src/Ref.vala @@ -6,7 +6,7 @@ namespace Eva { 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); + this.create(binary_to_string(reference.node, Erl.MAXATOMLEN), reference.len, reference.n, reference.creation); } private Ref.create(string node0, int len0, uint* n0, uint creation0) { @@ -46,7 +46,7 @@ namespace Eva { public void encode(Erl.Buffer buffer) { Erl.Ref reference = Erl.Ref(); - char[]? nodedata = string_to_array(node); + char[]? nodedata = string_to_binary(node); assert(nodedata != null); Memory.copy(reference.node, nodedata, int.min(Erl.MAXATOMLEN, nodedata.length)); diff --git a/src/String.vala b/src/String.vala index 50563c2..ea749c1 100644 --- a/src/String.vala +++ b/src/String.vala @@ -27,7 +27,7 @@ namespace Eva { } public void encode(Erl.Buffer buffer) { - char[]? array = string_to_array(value); + char[]? array = string_to_binary(value); if(array != null) { buffer.encode_string(array); } diff --git a/src/Term.vala b/src/Term.vala index 698bfc2..273cfa2 100644 --- a/src/Term.vala +++ b/src/Term.vala @@ -60,7 +60,7 @@ namespace Eva { 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)); + return new Atom(binary_to_string(value, size)); case Erl.TermType.SMALL_TUPLE: case Erl.TermType.LARGE_TUPLE: @@ -87,7 +87,7 @@ namespace Eva { 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)); + return new String(binary_to_string(value, size)); case Erl.TermType.BINARY: char[] value = new char[size]; @@ -129,5 +129,9 @@ namespace Eva { return new List(head, tail); } + + public Term? parse(string str) { + return Eva.parse(str); + } } } diff --git a/src/Util.vala b/src/Util.vala index 7c704fb..28cce58 100644 --- a/src/Util.vala +++ b/src/Util.vala @@ -1,5 +1,5 @@ namespace Eva { - private string array_to_string(uchar* array, ulong len) { + private string binary_to_string(uchar* array, ulong len) { string ret = ""; for(ulong i = 0; i < len; ++i) { @@ -15,7 +15,7 @@ namespace Eva { return ret; } - private char[]? string_to_array(string str) { + private char[]? string_to_binary(string str) { char[] ret = new char[str.len()]; int index = 0; diff --git a/test/Test.vala b/test/Test.vala index 86907ea..85afc6a 100644 --- a/test/Test.vala +++ b/test/Test.vala @@ -3,7 +3,7 @@ namespace Eva { public static int main(string[] args) { MainLoop main = new MainLoop(); - Eva.PacketHandler handler = new Eva.PacketHandler(new UnixInputStream(3, true), new UnixOutputStream(4, true), 4); + PacketHandler handler = new PacketHandler(new UnixInputStream(3, true), new UnixOutputStream(4, true), 4); handler.received_term.connect((term) => {stdout.printf("Received term %s\n", term.to_string()); handler.send(term); main.quit();}); handler.start(); -- cgit v1.2.3