namespace Eva { public static Term? parse(string str, ...) { va_list va = va_list(); string s = str.chomp(); if(s.length == 0) return null; Term? ret = parse_term(ref s, ref va); if(s.length != 0) return null; else return ret; } private static Term? parse_term(ref string str, ref va_list va) { 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, ref va); } else if(c == '[') { return parse_list(ref str, ref va); } else if(c == '~') { return get_va(ref str, ref va); } 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, ref va_list va) { 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, ref va); 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, ref va_list va) { if(str.get_char() != '[') return null; str = str.next_char(); return parse_list_tail(ref str, ref va); } private static Term? parse_list_tail(ref string str, ref va_list va) { 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, ref va); 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 Cons(head); case ',': return new Cons(head, parse_list_tail(ref str, ref va)); case '|': Term ret = new Cons(head, parse_term(ref str, ref va)); 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; } } private static Term? get_va(ref string str, ref va_list va) { if(str.get_char() != '~') return null; if(str.length < 2) return null; unichar c = str[1]; str = str.offset(2); switch(c) { case 'a': return new Atom(va.arg()); case 's': return new String(va.arg()); case 'i': return new Int(va.arg()); case 'l': return new Int(va.arg()); case 'u': return new UInt(va.arg()); case 'f': case 'd': return new Double(va.arg()); case 'w': return va.arg(); default: return null; } } }