[tracker/parser: 1/2] SPARQL: Use hand-written parser



commit 51656d40bbe91f4f7660a1934614b8fe55f9dca6
Author: Jürg Billeter <j bitron ch>
Date:   Wed Aug 5 15:51:18 2009 +0200

    SPARQL: Use hand-written parser

 src/libtracker-common/posix.vapi                |    2 +
 src/libtracker-data/.gitignore                  |    1 +
 src/libtracker-data/Makefile.am                 |    5 +-
 src/libtracker-data/tracker-sparql-query.vala   | 1103 +++++++++++++++++------
 src/libtracker-data/tracker-sparql-scanner.vala |  835 +++++++++++++++++
 5 files changed, 1678 insertions(+), 268 deletions(-)
---
diff --git a/src/libtracker-common/posix.vapi b/src/libtracker-common/posix.vapi
index 92836b6..f9dc55d 100644
--- a/src/libtracker-common/posix.vapi
+++ b/src/libtracker-common/posix.vapi
@@ -21,5 +21,7 @@
 namespace Posix {
 	[CCode (cheader_filename = "string.h")]
 	public static size_t strcspn (string s1, string s2);
+	[CCode (cheader_filename = "string.h")]
+	public static unowned string strchr (string s, int c);
 }
 
diff --git a/src/libtracker-data/.gitignore b/src/libtracker-data/.gitignore
index 0275713..8ff3464 100644
--- a/src/libtracker-data/.gitignore
+++ b/src/libtracker-data/.gitignore
@@ -1,2 +1,3 @@
 tracker-sparql-query.c
 tracker-sparql-query.h
+tracker-sparql-scanner.c
diff --git a/src/libtracker-data/Makefile.am b/src/libtracker-data/Makefile.am
index b9ee48a..15c3374 100644
--- a/src/libtracker-data/Makefile.am
+++ b/src/libtracker-data/Makefile.am
@@ -19,7 +19,8 @@ libtracker_datadir = $(libdir)/tracker-$(TRACKER_API_VERSION)
 libtracker_data_LTLIBRARIES = libtracker-data.la
 
 libtracker_data_la_VALASOURCES = 					\
-	tracker-sparql-query.vala
+	tracker-sparql-query.vala					\
+	tracker-sparql-scanner.vala
 
 libtracker_data_la_SOURCES = 						\
 	tracker-data-backup.c						\
@@ -39,7 +40,7 @@ noinst_HEADERS =							\
 	tracker-turtle.h
 
 libtracker-data.vala.stamp: $(libtracker_data_la_VALASOURCES)
-	$(VALAC) -C $(VALAFLAGS) -H tracker-sparql-query.h ../rasqal/rasqal.vapi ../libtracker-common/libtracker-common.vapi libtracker-data.vapi ../libtracker-db/libtracker-db.vapi $^
+	$(VALAC) -C $(VALAFLAGS) -H tracker-sparql-query.h ../rasqal/rasqal.vapi ../libtracker-common/posix.vapi ../libtracker-common/libtracker-common.vapi libtracker-data.vapi ../libtracker-db/libtracker-db.vapi $^
 	touch $@
 
 libtracker_data_la_LDFLAGS =						\
diff --git a/src/libtracker-data/tracker-sparql-query.vala b/src/libtracker-data/tracker-sparql-query.vala
index cb0801c..b5db457 100644
--- a/src/libtracker-data/tracker-sparql-query.vala
+++ b/src/libtracker-data/tracker-sparql-query.vala
@@ -46,7 +46,7 @@ public class Tracker.SparqlQuery : Object {
 	class LiteralBinding : DataBinding {
 		public bool is_fts_match;
 		public string literal;
-		public Rasqal.Literal.Type literal_type;
+		// public Rasqal.Literal.Type literal_type;
 	}
 
 	// Represents a mapping of a SPARQL variable to a SQL table and column
@@ -216,9 +216,33 @@ public class Tracker.SparqlQuery : Object {
 		}
 	}
 
+	SparqlScanner scanner;
+
+	// token buffer
+	TokenInfo[] tokens;
+	// index of current token in buffer
+	int index;
+	// number of tokens in buffer
+	int size;
+
+	const int BUFFER_SIZE = 32;
+
+	struct TokenInfo {
+		public SparqlTokenType type;
+		public SourceLocation begin;
+		public SourceLocation end;
+	}
+
 	string query_string;
 	bool update_extensions;
 
+	string current_subject;
+	bool current_subject_is_var;
+	string current_predicate;
+	bool current_predicate_is_var;
+
+	HashTable<string,string> prefix_map;
+
 	StringBuilder pattern_sql;
 
 	// All SQL tables
@@ -246,6 +270,9 @@ public class Tracker.SparqlQuery : Object {
 	string error_message;
 
 	public SparqlQuery (string query) {
+		tokens = new TokenInfo[BUFFER_SIZE];
+		prefix_map = new HashTable<string,string>.full (str_hash, str_equal, g_free, g_free);
+
 		this.query_string = query;
 	}
 
@@ -254,6 +281,7 @@ public class Tracker.SparqlQuery : Object {
 		this.update_extensions = true;
 	}
 
+#if 0
 	string get_sql_for_literal (Rasqal.Literal literal) {
 		assert (literal.type == Rasqal.Literal.Type.VARIABLE);
 
@@ -279,8 +307,9 @@ public class Tracker.SparqlQuery : Object {
 		}
 		return "NULL";
 	}
+#endif
 
-	string generate_bnodeid_handler (Rasqal.Query? query, string? user_bnodeid) {
+	string generate_bnodeid (string? user_bnodeid) {
 		// user_bnodeid is NULL for anonymous nodes
 		if (user_bnodeid == null) {
 			return ":%d".printf (++bnodeid);
@@ -299,51 +328,112 @@ public class Tracker.SparqlQuery : Object {
 		}
 	}
 
+#if 0
 	void error_handler (Raptor.Locator? locator, string message) {
 		if (error_message == null) {
 			// return first, not last, error message
 			error_message = message;
 		}
 	}
+#endif
 
 	private bool is_ask { get; set; }
 
-	public DBResultSet? execute () throws Error {
-		var world = new Rasqal.World ();
-		world.open ();
+	inline bool next () {
+		index = (index + 1) % BUFFER_SIZE;
+		size--;
+		if (size <= 0) {
+			SourceLocation begin, end;
+			SparqlTokenType type = scanner.read_token (out begin, out end);
+			tokens[index].type = type;
+			tokens[index].begin = begin;
+			tokens[index].end = end;
+			size = 1;
+		}
+		return (tokens[index].type != SparqlTokenType.EOF);
+	}
 
-		// use LAQRS - extension to SPARQL - to support aggregation
-		var query = new Rasqal.Query (world, "laqrs", null);
+	inline SparqlTokenType current () {
+		return tokens[index].type;
+	}
 
-		foreach (Namespace ns in Ontology.get_namespaces ()) {
-			query.add_prefix (new Rasqal.Prefix (world, ns.prefix, new Raptor.Uri (ns.uri)));
+	inline SparqlTokenType last () {
+		int last_index = (index + BUFFER_SIZE - 1) % BUFFER_SIZE;
+		return tokens[last_index].type;
+	}
+
+	inline bool accept (SparqlTokenType type) {
+		if (current () == type) {
+			next ();
+			return true;
+		}
+		return false;
+	}
+
+	bool expect (SparqlTokenType type) throws SparqlError {
+		if (accept (type)) {
+			return true;
 		}
 
-		query.declare_prefixes ();
+		throw new SparqlError.PARSE ("expected %s", type.to_string ());
+	}
+
+	inline SourceLocation get_location () {
+		return tokens[index].begin;
+	}
+
+	void set_location (SourceLocation location) {
+		scanner.seek (location);
+		size = 0;
+		index = 0;
+		next ();
+	}
+
+	string get_current_string () {
+		return ((string) tokens[index].begin.pos).ndup ((tokens[index].end.pos - tokens[index].begin.pos));
+	}
+
+	string get_last_string (int strip = 0) {
+		int last_index = (index + BUFFER_SIZE - 1) % BUFFER_SIZE;
+		return ((string) (tokens[last_index].begin.pos + strip)).ndup ((tokens[last_index].end.pos - tokens[last_index].begin.pos - 2 * strip));
+	}
 
-		base_uuid = new uchar[16];
-		uuid_generate (base_uuid);
-		query.set_generate_bnodeid_handler (generate_bnodeid_handler);
+	void parse_prologue () throws SparqlError {
+		if (accept (SparqlTokenType.BASE)) {
+			expect (SparqlTokenType.IRI_REF);
+		}
+		while (accept (SparqlTokenType.PREFIX)) {
+			string ns = "";
+			if (accept (SparqlTokenType.PN_PREFIX)) {
+				ns = get_last_string ();
+			}
+			expect (SparqlTokenType.COLON);
+			expect (SparqlTokenType.IRI_REF);
+			string uri = get_last_string ();
+			prefix_map.insert (ns, uri.substring (1, uri.length - 2));
+		}
+	}
 
-		query.set_warning_handler (error_handler);
-		query.set_error_handler (error_handler);
-		query.set_fatal_error_handler (error_handler);
+	public DBResultSet? execute () throws Error {
+		scanner = new SparqlScanner ((char*) query_string, (long) query_string.size ());
+		next ();
 
-		query.prepare (this.query_string, null);
-		if (error_message != null) {
-			throw new SparqlError.PARSE (error_message);
+		foreach (Namespace ns in Ontology.get_namespaces ()) {
+			prefix_map.insert (ns.prefix, ns.uri);
 		}
 
+		parse_prologue ();
+
 		if (!update_extensions) {
-			if (query.get_verb () == Rasqal.QueryVerb.SELECT) {
-				return execute_select (query);
-			} else if (query.get_verb () == Rasqal.QueryVerb.CONSTRUCT) {
+			if (current () == SparqlTokenType.SELECT) {
+				return execute_select ();
+			} else if (current () == SparqlTokenType.CONSTRUCT) {
 				throw new SparqlError.INTERNAL ("CONSTRUCT is not supported");
-			} else if (query.get_verb () == Rasqal.QueryVerb.DESCRIBE) {
+			} else if (current () == SparqlTokenType.DESCRIBE) {
 				throw new SparqlError.INTERNAL ("DESCRIBE is not supported");
-			} else if (query.get_verb () == Rasqal.QueryVerb.ASK) {
+			} else if (current () == SparqlTokenType.ASK) {
 				is_ask = true;
-				return execute_select (query);
+				return execute_select ();
 			} else {
 				throw new SparqlError.PARSE ("DELETE and INSERT are not supported in query mode");
 			}
@@ -354,18 +444,16 @@ public class Tracker.SparqlQuery : Object {
 			Data.begin_transaction ();
 
 			try {
-				unowned Rasqal.Query operation = query;
-				while (operation != null) {
-					if (operation.get_verb () == Rasqal.QueryVerb.INSERT) {
-						execute_insert (operation);
-					} else if (operation.get_verb () == Rasqal.QueryVerb.DELETE) {
-						execute_delete (operation);
-					} else if (operation.get_verb () == Rasqal.QueryVerb.DROP) {
-						Data.delete_resource_description (operation.get_data_graph (0).name_uri.as_string ());
+				while (current () != SparqlTokenType.EOF) {
+					if (current () == SparqlTokenType.INSERT) {
+						execute_insert ();
+					} else if (current () == SparqlTokenType.DELETE) {
+						execute_delete ();
+					} else if (current () == SparqlTokenType.DROP) {
+						// Data.delete_resource_description (operation.get_data_graph (0).name_uri.as_string ());
 					} else {
 						throw new SparqlError.PARSE ("SELECT, CONSTRUCT, DESCRIBE, and ASK are not supported in update mode");
 					}
-					operation = operation.next ();
 				}
 			} finally {
 				Data.commit_transaction ();
@@ -406,8 +494,10 @@ public class Tracker.SparqlQuery : Object {
 				}
 			} else if (binding.is_datetime) {
 				stmt.bind_int (i, string_to_date (binding.literal));
+#if 0
 			} else if (binding.literal_type == Rasqal.Literal.Type.INTEGER) {
 				stmt.bind_int (i, binding.literal.to_int ());
+#endif
 			} else {
 				stmt.bind_text (i, binding.literal);
 			}
@@ -417,51 +507,59 @@ public class Tracker.SparqlQuery : Object {
 		return stmt.execute ();
 	}
 
-	DBResultSet? execute_select (Rasqal.Query query) throws Error {
+	DBResultSet? execute_select () throws Error {
 		// SELECT query
 
 		pattern_sql = new StringBuilder ();
 		var_map = new HashTable<string,VariableBinding>.full (str_hash, str_equal, g_free, g_object_unref);
 		predicate_variable_map = new HashTable<string,PredicateVariable>.full (str_hash, str_equal, g_free, g_object_unref);
 
-		if (query.get_query_graph_pattern () == null) {
-			throw new SparqlError.PARSE ("Missing WHERE clause");
-		}
-
-		// process WHERE clause
-		visit_graph_pattern (query.get_query_graph_pattern ());
+		var select_variables = new List<string> ();
 
 		// build SQL
 		var sql = new StringBuilder ();
-
 		sql.append ("SELECT ");
-		if (query.get_distinct ()) {
+
+		expect (SparqlTokenType.SELECT);
+
+		if (accept (SparqlTokenType.DISTINCT)) {
 			sql.append ("DISTINCT ");
+		} else if (accept (SparqlTokenType.REDUCED)) {
 		}
 
 		if (is_ask) {
 			sql.append ("COUNT(1) > 0");
 		}
 
-		bool first = true;
-		for (int var_idx = 0; true; var_idx++) {
-			weak Rasqal.Variable variable = query.get_variable (var_idx);
-			if (variable == null) {
-				break;
+		if (accept (SparqlTokenType.STAR)) {
+			// TODO
+		} else {
+			if (current () == SparqlTokenType.VAR) {
+				while (accept (SparqlTokenType.VAR)) {
+					select_variables.append (get_last_string ().substring (1));
+				}
+			} else {
+				// TODO: error
 			}
+		}
+
+		if (accept (SparqlTokenType.FROM)) {
+			accept (SparqlTokenType.NAMED);
+			expect (SparqlTokenType.IRI_REF);
+		}
+
+		accept (SparqlTokenType.WHERE);
 
+		visit_group_graph_pattern ();
+
+		bool first = true;
+		foreach (string variable in select_variables) {
 			if (!first) {
 				sql.append (", ");
 			} else {
 				first = false;
 			}
-			
-			if (variable.expression != null) {
-				// LAQRS aggregate expression
-				sql.append (get_sql_for_expression (variable.expression));
-			} else {
-				sql.append (get_sql_for_variable (variable.name));
-			}
+			sql.append (get_sql_for_variable (variable));
 		}
 
 		// select from results of WHERE clause
@@ -469,6 +567,9 @@ public class Tracker.SparqlQuery : Object {
 		sql.append (pattern_sql.str);
 		sql.append (")");
 
+		pattern_sql.truncate (0);
+
+#if 0
 		// GROUP BY (SPARQL extension, LAQRS)
 		first = true;
 		for (int group_idx = 0; true; group_idx++) {
@@ -494,55 +595,75 @@ public class Tracker.SparqlQuery : Object {
 				sql.append (" DESC");
 			}
 		}
+#endif
 
-		// ORDER BY
-		first = true;
-		for (int order_idx = 0; true; order_idx++) {
-			weak Rasqal.Expression order = query.get_order_condition (order_idx);
-			if (order == null) {
-				break;
-			}
+		if (accept (SparqlTokenType.ORDER)) {
+			expect (SparqlTokenType.BY);
+			sql.append (" ORDER BY ");
+			bool first_order = true;
+			do {
+				if (first_order) {
+					first_order = false;
+				} else {
+					sql.append (", ");
+				}
+				if (accept (SparqlTokenType.ASC)) {
+					parse_bracketted_expression ();
+					sql.append (pattern_sql.str);
+					sql.append (" ASC");
+				} else if (accept (SparqlTokenType.DESC)) {
+					parse_bracketted_expression ();
+					sql.append (pattern_sql.str);
+					sql.append (" DESC");
+				} else {
+					parse_primary_expression ();
+					sql.append (pattern_sql.str);
+				}
+				pattern_sql.truncate (0);
+			} while (current () != SparqlTokenType.LIMIT && current () != SparqlTokenType.OFFSET && current () != SparqlTokenType.EOF);
+		}
 
-			if (!first) {
-				sql.append (", ");
-			} else {
-				sql.append (" ORDER BY ");
-				first = false;
-			}
-			assert (order.op == Rasqal.Op.ORDER_COND_ASC || order.op == Rasqal.Op.ORDER_COND_DESC);
-			assert (order.arg1.op == Rasqal.Op.LITERAL);
-			assert (order.arg1.literal.type == Rasqal.Literal.Type.VARIABLE);
-			string variable_name = order.arg1.literal.as_variable ().name;
+		int limit = -1;
+		int offset = -1;
 
-			sql.append (get_sql_for_variable (variable_name));
-
-			if (order.op == Rasqal.Op.ORDER_COND_DESC) {
-				sql.append (" DESC");
+		if (accept (SparqlTokenType.LIMIT)) {
+			expect (SparqlTokenType.INTEGER);
+			limit = get_last_string ().to_int ();
+			if (accept (SparqlTokenType.OFFSET)) {
+				expect (SparqlTokenType.INTEGER);
+				offset = get_last_string ().to_int ();
+			}
+		} else if (accept (SparqlTokenType.OFFSET)) {
+			expect (SparqlTokenType.INTEGER);
+			offset = get_last_string ().to_int ();
+			if (accept (SparqlTokenType.LIMIT)) {
+				expect (SparqlTokenType.INTEGER);
+				limit = get_last_string ().to_int ();
 			}
 		}
 
 		// LIMIT and OFFSET
-		if (query.get_limit () >= 0) {
-			sql.append_printf (" LIMIT %d", query.get_limit ());
-			if (query.get_offset () >= 0) {
-				sql.append_printf (" OFFSET %d", query.get_offset ());
+		if (limit >= 0) {
+			sql.append_printf (" LIMIT %d", limit);
+			if (offset >= 0) {
+				sql.append_printf (" OFFSET %d", offset);
 			}
-		} else if (query.get_offset () >= 0) {
-			sql.append_printf (" LIMIT -1 OFFSET %d", query.get_offset ());
+		} else if (offset >= 0) {
+			sql.append_printf (" LIMIT -1 OFFSET %d", offset);
 		}
 
 		return exec_sql (sql.str);
 	}
 
-	void execute_insert (Rasqal.Query query) throws Error {
-		execute_update (query, false);
+	void execute_insert () throws Error {
+		execute_update (false);
 	}
 
-	void execute_delete (Rasqal.Query query) throws Error {
-		execute_update (query, true);
+	void execute_delete () throws Error {
+		execute_update (true);
 	}
 
-	void execute_update (Rasqal.Query query, bool delete_statements) throws Error {
+	void execute_update (bool delete_statements) throws Error {
 		// INSERT or DELETE
 
 		pattern_sql = new StringBuilder ();
@@ -551,6 +672,7 @@ public class Tracker.SparqlQuery : Object {
 
 		var sql = new StringBuilder ();
 
+#if 0
 		// process WHERE clause
 		if (query.get_query_graph_pattern () != null) {
 			visit_graph_pattern (query.get_query_graph_pattern ());
@@ -639,153 +761,625 @@ public class Tracker.SparqlQuery : Object {
 				}
 			} while (result_set.iter_next ());
 		}
+#endif
 	}
 
-	void visit_graph_pattern (Rasqal.GraphPattern graph_pattern) throws SparqlError {
-		bool first_where = true;
-		if (graph_pattern.get_operator () == Rasqal.GraphPattern.Operator.BASIC) {
-			tables = new List<DataTable> ();
-			table_map = new HashTable<string,DataTable>.full (str_hash, str_equal, g_free, g_object_unref);
+	string parse_var_or_term (out bool is_var) throws SparqlError {
+		string result = "";
+		is_var = false;
+		if (current () == SparqlTokenType.VAR) {
+			is_var = true;
+			next ();
+			result = get_last_string ().substring (1);
+		} else if (current () == SparqlTokenType.IRI_REF) {
+			next ();
+			result = get_last_string ();
+		} else if (current () == SparqlTokenType.PN_PREFIX) {
+			// prefixed name with namespace foo:bar
+			next ();
+			string ns = get_last_string ();
+			expect (SparqlTokenType.COLON);
+			result = prefix_map.lookup (ns) + get_last_string ().substring (1);
+		} else if (current () == SparqlTokenType.COLON) {
+			// prefixed name without namespace :bar
+			next ();
+			result = prefix_map.lookup ("") + get_last_string ().substring (1);
+		} else if (current () == SparqlTokenType.INTEGER) {
+			next ();
+		} else if (current () == SparqlTokenType.DECIMAL) {
+			next ();
+		} else if (current () == SparqlTokenType.DOUBLE) {
+			next ();
+		} else if (current () == SparqlTokenType.TRUE) {
+			next ();
+			result = "true";
+		} else if (current () == SparqlTokenType.FALSE) {
+			next ();
+			result = "false";
+		} else if (current () == SparqlTokenType.OPEN_BRACKET) {
+			next ();
+
+			result = generate_bnodeid (null);
+
+			string old_subject = current_subject;
+			bool old_subject_is_var = current_subject_is_var;
+
+			current_subject = result;
+			current_subject_is_var = true;
+			parse_property_list_not_empty ();
+			expect (SparqlTokenType.CLOSE_BRACKET);
+
+			current_subject = old_subject;
+			current_subject_is_var = old_subject_is_var;
+
+			is_var = true;
+		} else {
+			// TODO error
+		}
+		return result;
+	}
 
-			pattern_variables = new List<string> ();
-			pattern_var_map = new HashTable<string,VariableBindingList>.full (str_hash, str_equal, g_free, g_object_unref);
+	void parse_object_list () throws SparqlError {
+		while (true) {
+			parse_object ();
+			if (accept (SparqlTokenType.COMMA)) {
+				continue;
+			}
+			break;
+		}
+	}
 
-			pattern_bindings = new List<LiteralBinding> ();
+	void parse_property_list_not_empty () throws SparqlError {
+		while (true) {
+			var old_predicate = current_predicate;
+			var old_predicate_is_var = current_predicate_is_var;
+
+			current_predicate = null;
+			current_predicate_is_var = false;
+			if (current () == SparqlTokenType.VAR) {
+				current_predicate_is_var = true;
+				next ();
+				current_predicate = get_last_string ().substring (1);
+			} else if (current () == SparqlTokenType.IRI_REF) {
+				next ();
+				current_predicate = get_last_string ();
+			} else if (current () == SparqlTokenType.PN_PREFIX) {
+				next ();
+				string ns = get_last_string ();
+				expect (SparqlTokenType.COLON);
+				current_predicate = prefix_map.lookup (ns) + get_last_string ().substring (1);
+			} else if (current () == SparqlTokenType.COLON) {
+				next ();
+				current_predicate = prefix_map.lookup ("") + get_last_string ().substring (1);
+			} else if (current () == SparqlTokenType.A) {
+				next ();
+				current_predicate = get_last_string ();
+			} else {
+				// TODO error
+			}
+			parse_object_list ();
 
-			pattern_sql.append ("SELECT ");
+			current_predicate = old_predicate;
+			current_predicate_is_var = old_predicate_is_var;
 
-			for (int triple_idx = 0; true; triple_idx++) {
-				weak Rasqal.Triple triple = graph_pattern.get_triple (triple_idx);
-				if (triple == null) {
-					break;
+			if (accept (SparqlTokenType.SEMICOLON)) {
+				continue;
+			}
+			break;
+		}
+	}
+
+
+	void parse_graph_pattern_not_triples (int group_graph_pattern_start) throws SparqlError {
+		if (current () == SparqlTokenType.OPTIONAL) {
+			parse_optional_graph_pattern (group_graph_pattern_start);
+		} else if (current () == SparqlTokenType.OPEN_BRACE) {
+			// parse_group_or_union_graph_pattern ();
+		} else if (current () == SparqlTokenType.GRAPH) {
+			// parse_graph_graph_pattern ();
+		}
+	}
+
+	void parse_bound_call () throws SparqlError {
+		expect (SparqlTokenType.BOUND);
+		expect (SparqlTokenType.OPEN_PARENS);
+		pattern_sql.append ("(");
+		parse_expression ();
+		pattern_sql.append (") IS NOT NULL");
+		expect (SparqlTokenType.CLOSE_PARENS);
+	}
+
+	void parse_regex () throws SparqlError {
+		expect (SparqlTokenType.REGEX);
+		expect (SparqlTokenType.OPEN_PARENS);
+		pattern_sql.append ("SparqlRegex(");
+		parse_expression ();
+		pattern_sql.append (", ");
+		expect (SparqlTokenType.COMMA);
+		parse_expression ();
+		pattern_sql.append (", ");
+		if (accept (SparqlTokenType.COMMA)) {
+			parse_expression ();
+		} else {
+			pattern_sql.append ("''");
+		}
+		pattern_sql.append (")");
+		expect (SparqlTokenType.CLOSE_PARENS);
+	}
+
+	void parse_primary_expression () throws SparqlError {
+		switch (current ()) {
+		case SparqlTokenType.OPEN_PARENS:
+			parse_bracketted_expression ();
+			break;
+		case SparqlTokenType.IRI_REF:
+		case SparqlTokenType.DECIMAL:
+		case SparqlTokenType.DOUBLE:
+		case SparqlTokenType.TRUE:
+		case SparqlTokenType.FALSE:
+			next ();
+			break;
+		case SparqlTokenType.STRING_LITERAL1:
+		case SparqlTokenType.STRING_LITERAL2:
+		case SparqlTokenType.STRING_LITERAL_LONG1:
+		case SparqlTokenType.STRING_LITERAL_LONG2:
+			next ();
+			pattern_sql.append ("?");
+
+			var binding = new LiteralBinding ();
+
+			switch (last ()) {
+			case SparqlTokenType.STRING_LITERAL1:
+			case SparqlTokenType.STRING_LITERAL2:
+				var sb = new StringBuilder ();
+
+				string s = get_last_string (1);
+				string* p = s;
+				string* end = p + s.size ();
+				while ((long) p < (long) end) {
+					string* q = Posix.strchr (p, '\\');
+					if (q == null) {
+						sb.append_len (p, (long) (end - p));
+						p = end;
+					} else {
+						sb.append_len (p, (long) (q - p));
+						p = q + 1;
+						switch (((char*) p)[0]) {
+						case '\'':
+						case '"':
+						case '\\':
+							sb.append_c (((char*) p)[0]);
+							break;
+						case 'b':
+							sb.append_c ('\b');
+							break;
+						case 'f':
+							sb.append_c ('\f');
+							break;
+						case 'n':
+							sb.append_c ('\n');
+							break;
+						case 'r':
+							sb.append_c ('\r');
+							break;
+						case 't':
+							sb.append_c ('\t');
+							break;
+						}
+						p++;
+					}
 				}
+				binding.literal = sb.str;
+				break;
+			case SparqlTokenType.STRING_LITERAL_LONG1:
+			case SparqlTokenType.STRING_LITERAL_LONG2:
+				binding.literal = get_last_string (3);
+				break;
+			}
+
+			bindings.append (binding);
+
+			break;
+		case SparqlTokenType.INTEGER:
+			next ();
+			pattern_sql.append (get_last_string ());
+			break;
+		case SparqlTokenType.VAR:
+			next ();
+			string variable_name = get_last_string ().substring (1);
+			pattern_sql.append (get_sql_for_variable (variable_name));
+			break;
+		case SparqlTokenType.STR:
+		case SparqlTokenType.LANG:
+		case SparqlTokenType.LANGMATCHES:
+		case SparqlTokenType.DATATYPE:
+			next ();
+			break;
+		case SparqlTokenType.BOUND:
+			parse_bound_call ();
+			break;
+		case SparqlTokenType.SAMETERM:
+		case SparqlTokenType.ISIRI:
+		case SparqlTokenType.ISURI:
+		// case SparqlTokenType.ISBLANK:
+		case SparqlTokenType.ISLITERAL:
+			next ();
+			break;
+		case SparqlTokenType.REGEX:
+			parse_regex ();
+			break;
+		}
+	}
 
-				visit_triple (triple);
+	void parse_unary_expression () throws SparqlError {
+		if (accept (SparqlTokenType.OP_NEG)) {
+			pattern_sql.append ("NOT (");
+			parse_primary_expression ();
+			pattern_sql.append (")");
+			return;
+		}
+		parse_primary_expression ();
+	}
+
+	void parse_multiplicative_expression () throws SparqlError {
+		long begin = pattern_sql.len;
+		parse_unary_expression ();
+		while (true) {
+			if (accept (SparqlTokenType.STAR)) {
+				pattern_sql.insert (begin, "(");
+				pattern_sql.append (" * ");
+				parse_unary_expression ();
+				pattern_sql.append (")");
+			} else if (accept (SparqlTokenType.DIV)) {
+				pattern_sql.insert (begin, "(");
+				pattern_sql.append (" / ");
+				parse_unary_expression ();
+				pattern_sql.append (")");
+			} else {
+				break;
 			}
+		}
+	}
 
-			// remove last comma and space
-			pattern_sql.truncate (pattern_sql.len - 2);
+	void parse_additive_expression () throws SparqlError {
+		long begin = pattern_sql.len;
+		parse_multiplicative_expression ();
+		while (true) {
+			if (accept (SparqlTokenType.PLUS)) {
+				pattern_sql.insert (begin, "(");
+				pattern_sql.append (" + ");
+				parse_multiplicative_expression ();
+				pattern_sql.append (")");
+			} else if (accept (SparqlTokenType.MINUS)) {
+				pattern_sql.insert (begin, "(");
+				pattern_sql.append (" - ");
+				parse_multiplicative_expression ();
+				pattern_sql.append (")");
+			} else {
+				break;
+			}
+		}
+	}
 
-			pattern_sql.append (" FROM ");
-			bool first = true;
-			foreach (DataTable table in tables) {
-				if (!first) {
-					pattern_sql.append (", ");
-				} else {
-					first = false;
-				}
-				if (table.sql_db_tablename != null) {
-					pattern_sql.append_printf ("\"%s\"", table.sql_db_tablename);
-				} else {
-					pattern_sql.append_printf ("(%s)", table.predicate_variable.get_sql_query ());
-				}
-				pattern_sql.append_printf (" AS \"%s\"", table.sql_query_tablename);
+	void parse_numeric_expression () throws SparqlError {
+		parse_additive_expression ();
+	}
+
+	void parse_relational_expression () throws SparqlError {
+		long begin = pattern_sql.len;
+		parse_numeric_expression ();
+		if (accept (SparqlTokenType.OP_GE)) {
+			pattern_sql.insert (begin, "(");
+			pattern_sql.append (" >= ");
+			parse_numeric_expression ();
+			pattern_sql.append (")");
+		} else if (accept (SparqlTokenType.OP_EQ)) {
+			pattern_sql.insert (begin, "(");
+			pattern_sql.append (" = ");
+			parse_numeric_expression ();
+			pattern_sql.append (")");
+		} else if (accept (SparqlTokenType.OP_NE)) {
+			pattern_sql.insert (begin, "(");
+			pattern_sql.append (" <> ");
+			parse_numeric_expression ();
+			pattern_sql.append (")");
+		} else if (accept (SparqlTokenType.OP_LT)) {
+			pattern_sql.insert (begin, "(");
+			pattern_sql.append (" < ");
+			parse_numeric_expression ();
+			pattern_sql.append (")");
+		} else if (accept (SparqlTokenType.OP_LE)) {
+			pattern_sql.insert (begin, "(");
+			pattern_sql.append (" <= ");
+			parse_numeric_expression ();
+			pattern_sql.append (")");
+		} else if (accept (SparqlTokenType.OP_GT)) {
+			pattern_sql.insert (begin, "(");
+			pattern_sql.append (" > ");
+			parse_numeric_expression ();
+			pattern_sql.append (")");
+		}
+	}
+
+	void parse_value_logical () throws SparqlError {
+		parse_relational_expression ();
+	}
+
+	void parse_conditional_and_expression () throws SparqlError {
+		parse_value_logical ();
+		while (accept (SparqlTokenType.OP_AND)) {
+			parse_value_logical ();
+		}
+	}
+
+	void parse_conditional_or_expression () throws SparqlError {
+		parse_conditional_and_expression ();
+		while (accept (SparqlTokenType.OP_OR)) {
+			parse_conditional_and_expression ();
+		}
+	}
+
+	void parse_expression () throws SparqlError {
+		parse_conditional_or_expression ();
+	}
+
+	void parse_bracketted_expression () throws SparqlError {
+		expect (SparqlTokenType.OPEN_PARENS);
+		parse_expression ();
+		expect (SparqlTokenType.CLOSE_PARENS);
+	}
+
+	void parse_constraint () throws SparqlError {
+		switch (current ()) {
+		case SparqlTokenType.STR:
+		case SparqlTokenType.LANG:
+		case SparqlTokenType.LANGMATCHES:
+		case SparqlTokenType.DATATYPE:
+		case SparqlTokenType.BOUND:
+		case SparqlTokenType.SAMETERM:
+		case SparqlTokenType.ISIRI:
+		case SparqlTokenType.ISURI:
+		// case SparqlTokenType.ISBLANK:
+		case SparqlTokenType.ISLITERAL:
+		case SparqlTokenType.REGEX:
+			parse_primary_expression ();
+			return;
+		}
+		parse_bracketted_expression ();
+	}
+
+	void parse_filter () throws SparqlError {
+		expect (SparqlTokenType.FILTER);
+		parse_constraint ();
+	}
+
+	void skip_filter () throws SparqlError {
+		expect (SparqlTokenType.FILTER);
+
+		switch (current ()) {
+		case SparqlTokenType.STR:
+		case SparqlTokenType.LANG:
+		case SparqlTokenType.LANGMATCHES:
+		case SparqlTokenType.DATATYPE:
+		case SparqlTokenType.BOUND:
+		case SparqlTokenType.SAMETERM:
+		case SparqlTokenType.ISIRI:
+		case SparqlTokenType.ISURI:
+		// case SparqlTokenType.ISBLANK:
+		case SparqlTokenType.ISLITERAL:
+		case SparqlTokenType.REGEX:
+			next ();
+			break;
+		}
+
+		expect (SparqlTokenType.OPEN_PARENS);
+		int n_parens = 1;
+		while (n_parens > 0) {
+			if (accept (SparqlTokenType.OPEN_PARENS)) {
+				n_parens++;
+			} else if (accept (SparqlTokenType.CLOSE_PARENS)) {
+				n_parens--;
+			} else if (current () == SparqlTokenType.EOF) {
+				throw new SparqlError.PARSE ("unexpected end of query, expected )");
+			} else {
+				// ignore everything else
+				next ();
 			}
+		}
+	}
 
-			foreach (string variable in pattern_variables) {
-				bool maybe_null = true;
-				string last_name = null;
-				foreach (VariableBinding binding in pattern_var_map.lookup (variable).list) {
-					string name = "\"%s\".\"%s\"".printf (binding.table.sql_query_tablename, binding.sql_db_column_name);
-					if (last_name != null) {
-						if (!first_where) {
-							pattern_sql.append (" AND ");
-						} else {
-							pattern_sql.append (" WHERE ");
-							first_where = false;
-						}
-						pattern_sql.append (last_name);
-						pattern_sql.append (" = ");
-						pattern_sql.append (name);
-					}
-					last_name = name;
-					if (!binding.maybe_null) {
-						maybe_null = false;
-					}
+	void parse_triples_block () throws SparqlError {
+		while (true) {
+			current_subject = parse_var_or_term (out current_subject_is_var);
+			parse_property_list_not_empty ();
+
+			if (accept (SparqlTokenType.DOT)) {
+				if (current () == SparqlTokenType.VAR ||
+				    current () == SparqlTokenType.IRI_REF ||
+				    current () == SparqlTokenType.PN_PREFIX ||
+				    current () == SparqlTokenType.COLON ||
+				    current () == SparqlTokenType.OPEN_BRACKET) {
+					// optional TriplesBlock
+					continue;
 				}
+			}
+			break;
+		}
+
+		// remove last comma and space
+		pattern_sql.truncate (pattern_sql.len - 2);
+	}
+
+	void visit_group_graph_pattern () throws SparqlError {
+		SourceLocation[] filters = { };
+
+		bool first_where = true;
+
+		tables = new List<DataTable> ();
+		table_map = new HashTable<string,DataTable>.full (str_hash, str_equal, g_free, g_object_unref);
+
+		pattern_variables = new List<string> ();
+		pattern_var_map = new HashTable<string,VariableBindingList>.full (str_hash, str_equal, g_free, g_object_unref);
 
-				if (maybe_null) {
-					// ensure that variable is bound in case it could return NULL in SQL
-					// assuming SPARQL variable is not optional
+		pattern_bindings = new List<LiteralBinding> ();
+
+		int group_graph_pattern_start = (int) pattern_sql.len;
+
+		pattern_sql.append ("SELECT ");
+
+		expect (SparqlTokenType.OPEN_BRACE);
+
+		// optional TriplesBlock
+		if (current () == SparqlTokenType.VAR ||
+		    current () == SparqlTokenType.IRI_REF ||
+		    current () == SparqlTokenType.PN_PREFIX ||
+		    current () == SparqlTokenType.COLON ||
+		    current () == SparqlTokenType.OPEN_BRACKET) {
+			parse_triples_block ();
+		}
+		/* possibly
+		else {
+			pattern_sql.append ("1");
+		}
+		*/
+
+		pattern_sql.append (" FROM ");
+		bool first = true;
+		foreach (DataTable table in tables) {
+			if (!first) {
+				pattern_sql.append (", ");
+			} else {
+				first = false;
+			}
+			if (table.sql_db_tablename != null) {
+				pattern_sql.append_printf ("\"%s\"", table.sql_db_tablename);
+			} else {
+				pattern_sql.append_printf ("(%s)", table.predicate_variable.get_sql_query ());
+			}
+			pattern_sql.append_printf (" AS \"%s\"", table.sql_query_tablename);
+		}
+
+		foreach (string variable in pattern_variables) {
+			bool maybe_null = true;
+			string last_name = null;
+			foreach (VariableBinding binding in pattern_var_map.lookup (variable).list) {
+				string name = "\"%s\".\"%s\"".printf (binding.table.sql_query_tablename, binding.sql_db_column_name);
+				if (last_name != null) {
 					if (!first_where) {
 						pattern_sql.append (" AND ");
 					} else {
 						pattern_sql.append (" WHERE ");
 						first_where = false;
 					}
-					pattern_sql.append_printf ("\"%s_u\" IS NOT NULL", variable);
+					pattern_sql.append (last_name);
+					pattern_sql.append (" = ");
+					pattern_sql.append (name);
+				}
+				last_name = name;
+				if (!binding.maybe_null) {
+					maybe_null = false;
 				}
 			}
-			foreach (LiteralBinding binding in pattern_bindings) {
+
+			if (maybe_null) {
+				// ensure that variable is bound in case it could return NULL in SQL
+				// assuming SPARQL variable is not optional
 				if (!first_where) {
 					pattern_sql.append (" AND ");
 				} else {
 					pattern_sql.append (" WHERE ");
 					first_where = false;
 				}
-				pattern_sql.append ("\"");
-				pattern_sql.append (binding.table.sql_query_tablename);
-				pattern_sql.append ("\".\"");
-				pattern_sql.append (binding.sql_db_column_name);
-				pattern_sql.append ("\"");
-				if (binding.is_fts_match) {
-					// parameters do not work with fts MATCH
-					string escaped_literal = string.joinv ("''", binding.literal.split ("'"));
-					pattern_sql.append_printf (" IN (SELECT rowid FROM fts WHERE fts MATCH '%s')", escaped_literal);
+				pattern_sql.append_printf ("\"%s_u\" IS NOT NULL", variable);
+			}
+		}
+		foreach (LiteralBinding binding in pattern_bindings) {
+			if (!first_where) {
+				pattern_sql.append (" AND ");
+			} else {
+				pattern_sql.append (" WHERE ");
+				first_where = false;
+			}
+			pattern_sql.append ("\"");
+			pattern_sql.append (binding.table.sql_query_tablename);
+			pattern_sql.append ("\".\"");
+			pattern_sql.append (binding.sql_db_column_name);
+			pattern_sql.append ("\"");
+			if (binding.is_fts_match) {
+				// parameters do not work with fts MATCH
+				string escaped_literal = string.joinv ("''", binding.literal.split ("'"));
+				pattern_sql.append_printf (" IN (SELECT rowid FROM fts WHERE fts MATCH '%s')", escaped_literal);
+			} else {
+				pattern_sql.append (" = ");
+				if (binding.is_uri) {
+					pattern_sql.append ("(SELECT ID FROM \"rdfs:Resource\" WHERE Uri = ?)");
 				} else {
-					pattern_sql.append (" = ");
-					if (binding.is_uri) {
-						pattern_sql.append ("(SELECT ID FROM \"rdfs:Resource\" WHERE Uri = ?)");
-					} else {
-						pattern_sql.append ("?");
-					}
+					pattern_sql.append ("?");
 				}
 			}
+		}
 
-			tables = null;
-			table_map = null;
-			pattern_variables = null;
-			pattern_var_map = null;
-			pattern_bindings = null;
-		} else if (graph_pattern.get_operator () == Rasqal.GraphPattern.Operator.GROUP
-		           || graph_pattern.get_operator () == Rasqal.GraphPattern.Operator.OPTIONAL) {
-			pattern_sql.append ("SELECT * FROM (");
-			long subgraph_start = pattern_sql.len;
-			int subgraph_idx = 0;
-			for (int pattern_idx = 0; true; pattern_idx++) {
-				weak Rasqal.GraphPattern sub_graph_pattern = graph_pattern.get_sub_graph_pattern (pattern_idx);
-				if (sub_graph_pattern == null) {
-					break;
-				}
+		tables = null;
+		table_map = null;
+		pattern_variables = null;
+		pattern_var_map = null;
+		pattern_bindings = null;
+
+		while (true) {
+			// check whether we have GraphPatternNotTriples | Filter
+			if (current () == SparqlTokenType.OPTIONAL ||
+			    current () == SparqlTokenType.OPEN_BRACE ||
+			    current () == SparqlTokenType.GRAPH) {
+				parse_graph_pattern_not_triples (group_graph_pattern_start);
+			} else if (current () == SparqlTokenType.FILTER) {
+				filters += get_location ();
+				skip_filter ();
+			} else {
+				break;
+			}
 
-				if (sub_graph_pattern.get_operator () == Rasqal.GraphPattern.Operator.FILTER) {
-					// ignore filters, processed later
-					continue;
-				}
+			accept (SparqlTokenType.DOT);
 
-				if (subgraph_idx > 1) {
-					// additional (SELECT * FROM ...) necessary
-					// when using more than two subgraphs to
-					// work around SQLite bug with NATURAL JOINs
-					pattern_sql.insert (subgraph_start, "(SELECT * FROM ");
-					pattern_sql.append (")");
-				}
+			// optional TriplesBlock
+			if (current () == SparqlTokenType.VAR ||
+			    current () == SparqlTokenType.IRI_REF ||
+			    current () == SparqlTokenType.OPEN_BRACKET) {
+				parse_triples_block ();
+			}
+		}
 
-				if (subgraph_idx > 0) {
-					if (sub_graph_pattern.get_operator () == Rasqal.GraphPattern.Operator.OPTIONAL) {
-						pattern_sql.append (" NATURAL LEFT JOIN ");
-					} else {
-						pattern_sql.append (" NATURAL CROSS JOIN ");
-					}
+		expect (SparqlTokenType.CLOSE_BRACE);
+
+		// handle filters last, they apply to the pattern as a whole
+		if (filters.length > 0) {
+			var end = get_location ();
+
+			foreach (var filter_location in filters) {
+				if (!first_where) {
+					pattern_sql.append (" AND ");
+				} else {
+					pattern_sql.append (" WHERE ");
+					first_where = false;
 				}
-				pattern_sql.append ("(");
-				visit_graph_pattern (sub_graph_pattern);
-				pattern_sql.append (")");
 
-				// differs from pattern_idx due to filters
-				subgraph_idx++;
+				set_location (filter_location);
+				parse_filter ();
 			}
-			pattern_sql.append (")");
+
+			set_location (end);
+		}
+	}
+
+	void parse_optional_graph_pattern (int group_graph_pattern_start) {
+		expect (SparqlTokenType.OPTIONAL);
+		pattern_sql.insert (group_graph_pattern_start, "SELECT * FROM (");
+		pattern_sql.append (") NATURAL LEFT JOIN (SELECT * FROM (");
+		visit_group_graph_pattern ();
+		pattern_sql.append ("))");
+#if 0
+		if (graph_pattern.get_operator () == Rasqal.GraphPattern.Operator.BASIC) {
+		} else if (graph_pattern.get_operator () == Rasqal.GraphPattern.Operator.GROUP
+		           || graph_pattern.get_operator () == Rasqal.GraphPattern.Operator.OPTIONAL) {
 		} else if (graph_pattern.get_operator () == Rasqal.GraphPattern.Operator.UNION) {
 			for (int pattern_idx = 0; true; pattern_idx++) {
 				weak Rasqal.GraphPattern sub_graph_pattern = graph_pattern.get_sub_graph_pattern (pattern_idx);
@@ -804,32 +1398,10 @@ public class Tracker.SparqlQuery : Object {
 				visit_graph_pattern (sub_graph_pattern);
 			}
 		}
-
-		// process filters
-		for (int pattern_idx = 0; true; pattern_idx++) {
-			weak Rasqal.GraphPattern sub_graph_pattern = graph_pattern.get_sub_graph_pattern (pattern_idx);
-			if (sub_graph_pattern == null) {
-				break;
-			}
-
-			if (sub_graph_pattern.get_operator () != Rasqal.GraphPattern.Operator.FILTER) {
-				// ignore non-filter subgraphs
-				continue;
-			}
-
-			weak Rasqal.Expression filter = sub_graph_pattern.get_filter_expression ();
-
-			if (!first_where) {
-				pattern_sql.append (" AND ");
-			} else {
-				pattern_sql.append (" WHERE ");
-				first_where = false;
-			}
-
-			visit_filter (filter);
-		}
+#endif
 	}
 
+#if 0
 	string get_string_from_literal (Rasqal.Literal lit, bool is_subject = false) {
 		if (lit.type == Rasqal.Literal.Type.BLANK) {
 			if (!is_subject && lit.as_string ().has_prefix (":")) {
@@ -837,20 +1409,17 @@ public class Tracker.SparqlQuery : Object {
 				// generate appropriate uri
 				return lit.as_string ();
 			} else {
-				return generate_bnodeid_handler (null, lit.as_string ());
+				return generate_bnodeid (lit.as_string ());
 			}
 		} else {
 			return lit.as_string ();
 		}
 	}
+#endif
 
-	void visit_triple (Rasqal.Triple triple) throws SparqlError {
-		string subject;
-		if (triple.subject.type == Rasqal.Literal.Type.VARIABLE) {
-			subject = "?" + triple.subject.as_variable ().name;
-		} else {
-			subject = get_string_from_literal (triple.subject, true);
-		}
+	void parse_object () throws SparqlError {
+		bool object_is_var;
+		string object = parse_var_or_term (out object_is_var);
 
 		string db_table;
 		bool rdftype = false;
@@ -860,38 +1429,38 @@ public class Tracker.SparqlQuery : Object {
 		DataTable table;
 		Property prop = null;
 
-		if (triple.predicate.type == Rasqal.Literal.Type.URI) {
-			prop = Ontology.get_property_by_uri (triple.predicate.as_string ());
+		if (!current_predicate_is_var) {
+			prop = Ontology.get_property_by_uri (current_predicate);
 
-			if (triple.predicate.as_string () == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";
-			    && triple.object.type == Rasqal.Literal.Type.URI) {
+			if (current_predicate == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";
+			    && !object_is_var) {
 				// rdf:type query
 				rdftype = true;
-				var cl = Ontology.get_class_by_uri (triple.object.as_string ());
+				var cl = Ontology.get_class_by_uri (object);
 				if (cl == null) {
-					throw new SparqlError.UNKNOWN_CLASS ("Unknown class `%s'".printf (triple.object.as_string ()));
+					throw new SparqlError.UNKNOWN_CLASS ("Unknown class `%s'".printf (object));
 				}
 				db_table = cl.name;
 			} else if (prop == null) {
-				if (triple.predicate.as_string () == "http://www.tracker-project.org/ontologies/fts#match";) {
+				if (current_predicate == "http://www.tracker-project.org/ontologies/fts#match";) {
 					// fts:match
 					db_table = "rdfs:Resource";
 				} else {
-					throw new SparqlError.UNKNOWN_PROPERTY ("Unknown property `%s'".printf (triple.predicate.as_string ()));
+					throw new SparqlError.UNKNOWN_PROPERTY ("Unknown property `%s'".printf (current_predicate));
 				}
 			} else {
-				if (triple.predicate.as_string () == "http://www.w3.org/2000/01/rdf-schema#domain";
-				    && triple.subject.type == Rasqal.Literal.Type.VARIABLE
-				    && triple.object.type == Rasqal.Literal.Type.URI) {
+				if (current_predicate == "http://www.w3.org/2000/01/rdf-schema#domain";
+				    && current_subject_is_var
+				    && !object_is_var) {
 					// rdfs:domain
-					var domain = Ontology.get_class_by_uri (triple.object.as_string ());
+					var domain = Ontology.get_class_by_uri (object);
 					if (domain == null) {
-						throw new SparqlError.UNKNOWN_CLASS ("Unknown class `%s'".printf (triple.object.as_string ()));
+						throw new SparqlError.UNKNOWN_CLASS ("Unknown class `%s'".printf (object));
 					}
-					var pv = predicate_variable_map.lookup (triple.subject.as_variable ().name);
+					var pv = predicate_variable_map.lookup (current_subject);
 					if (pv == null) {
 						pv = new PredicateVariable ();
-						predicate_variable_map.insert (triple.subject.as_variable ().name, pv);
+						predicate_variable_map.insert (current_subject, pv);
 					}
 					pv.domain = domain;
 				}
@@ -905,31 +1474,31 @@ public class Tracker.SparqlQuery : Object {
 					db_table = prop.domain.name;
 				}
 			}
-			table = get_table (subject, db_table, share_table, out newtable);
+			table = get_table (current_subject, db_table, share_table, out newtable);
 		} else {
 			// variable in predicate
 			newtable = true;
 			table = new DataTable ();
-			table.predicate_variable = predicate_variable_map.lookup (triple.predicate.as_variable ().name);
+			table.predicate_variable = predicate_variable_map.lookup (current_predicate);
 			if (table.predicate_variable == null) {
 				table.predicate_variable = new PredicateVariable ();
-				predicate_variable_map.insert (triple.predicate.as_variable ().name, table.predicate_variable);
+				predicate_variable_map.insert (current_predicate, table.predicate_variable);
 			}
-			if (triple.subject.type == Rasqal.Literal.Type.URI) {
+			if (!current_subject_is_var) {
 				// single subject
-				table.predicate_variable.subject = subject;
+				table.predicate_variable.subject = current_subject;
 			}
-			if (triple.object.type == Rasqal.Literal.Type.URI) {
+			if (!current_subject_is_var) {
 				// single object
-				table.predicate_variable.object = get_string_from_literal (triple.object);
+				table.predicate_variable.object = object;
 			}
-			table.sql_query_tablename = triple.predicate.as_variable ().name + (++counter).to_string ();
+			table.sql_query_tablename = current_predicate + (++counter).to_string ();
 			tables.append (table);
 
 			// add to variable list
 			var binding = new VariableBinding ();
 			binding.is_uri = true;
-			binding.variable = triple.predicate.as_variable ().name;
+			binding.variable = current_predicate;
 			binding.table = table;
 			binding.sql_db_column_name = "predicate";
 			var binding_list = pattern_var_map.lookup (binding.variable);
@@ -950,10 +1519,10 @@ public class Tracker.SparqlQuery : Object {
 		}
 		
 		if (newtable) {
-			if (triple.subject.type == Rasqal.Literal.Type.VARIABLE) {
+			if (current_subject_is_var) {
 				var binding = new VariableBinding ();
 				binding.is_uri = true;
-				binding.variable = triple.subject.as_variable ().name;
+				binding.variable = current_subject;
 				binding.table = table;
 				binding.sql_db_column_name = "ID";
 				var binding_list = pattern_var_map.lookup (binding.variable);
@@ -974,8 +1543,8 @@ public class Tracker.SparqlQuery : Object {
 			} else {
 				var binding = new LiteralBinding ();
 				binding.is_uri = true;
-				binding.literal = get_string_from_literal (triple.subject);
-				binding.literal_type = triple.subject.type;
+				binding.literal = current_subject;
+				// binding.literal_type = triple.subject.type;
 				binding.table = table;
 				binding.sql_db_column_name = "ID";
 				pattern_bindings.append (binding);
@@ -984,9 +1553,9 @@ public class Tracker.SparqlQuery : Object {
 		}
 		
 		if (!rdftype) {
-			if (triple.object.type == Rasqal.Literal.Type.VARIABLE) {
+			if (object_is_var) {
 				var binding = new VariableBinding ();
-				binding.variable = triple.object.as_variable ().name;
+				binding.variable = object;
 				binding.table = table;
 				if (prop != null) {
 
@@ -1028,23 +1597,23 @@ public class Tracker.SparqlQuery : Object {
 				if (var_map.lookup (binding.variable) == null) {
 					var_map.insert (binding.variable, binding);
 				}
-			} else if (triple.predicate.as_string () == "http://www.tracker-project.org/ontologies/fts#match";) {
+			} else if (current_predicate == "http://www.tracker-project.org/ontologies/fts#match";) {
 				var binding = new LiteralBinding ();
 				binding.is_fts_match = true;
-				binding.literal = triple.object.as_string ();
-				binding.literal_type = triple.object.type;
+				binding.literal = object;
+				// binding.literal_type = triple.object.type;
 				binding.table = table;
 				binding.sql_db_column_name = "ID";
 				pattern_bindings.append (binding);
 			} else {
 				var binding = new LiteralBinding ();
-				binding.literal = triple.object.as_string ();
-				binding.literal_type = triple.object.type;
+				binding.literal = object;
+				// binding.literal_type = triple.object.type;
 				binding.table = table;
 				if (prop != null) {
 					if (prop.data_type == PropertyType.RESOURCE) {
 						binding.is_uri = true;
-						binding.literal = get_string_from_literal (triple.object);
+						binding.literal = object;
 					} else if (prop.data_type == PropertyType.BOOLEAN) {
 						binding.is_boolean = true;
 					} else if (prop.data_type == PropertyType.DATE) {
@@ -1062,9 +1631,9 @@ public class Tracker.SparqlQuery : Object {
 			}
 		}
 
-		if (triple.subject.type != Rasqal.Literal.Type.VARIABLE &&
-		    triple.predicate.type != Rasqal.Literal.Type.VARIABLE &&
-		    triple.object.type != Rasqal.Literal.Type.VARIABLE) {
+		if (!current_subject_is_var &&
+		    !current_predicate_is_var &&
+		    !object_is_var) {
 			// no variables involved, add dummy expression to SQL
 			pattern_sql.append ("1, ");
 		}
@@ -1088,6 +1657,7 @@ public class Tracker.SparqlQuery : Object {
 		return table;
 	}
 
+#if 0
 	string sql_operator (Rasqal.Op op) {
 		switch (op) {
 		case Rasqal.Op.AND:     return " AND ";
@@ -1257,6 +1827,7 @@ public class Tracker.SparqlQuery : Object {
 			throw new SparqlError.UNSUPPORTED ("Unsupported operation");
 		}
 	}
+#endif
 
 	static string? get_string_for_value (Value value)
 	{
diff --git a/src/libtracker-data/tracker-sparql-scanner.vala b/src/libtracker-data/tracker-sparql-scanner.vala
new file mode 100644
index 0000000..bf14afa
--- /dev/null
+++ b/src/libtracker-data/tracker-sparql-scanner.vala
@@ -0,0 +1,835 @@
+/*
+ * Copyright (C) 2009, Nokia
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+public class Tracker.SparqlScanner : Object {
+	char* current;
+	char* end;
+
+	int line;
+	int column;
+
+	public SparqlScanner (char* input, long len) {
+		char* begin = input;
+		end = begin + len;
+
+		current = begin;
+
+		line = 1;
+		column = 1;
+	}
+
+	public void seek (SourceLocation location) {
+		current = location.pos;
+		line = location.line;
+		column = location.column;
+	}
+
+	SparqlTokenType get_identifier_or_keyword (char* begin, int len) {
+		switch (len) {
+		case 1:
+			switch (begin[0]) {
+			case 'A':
+			case 'a':
+				return SparqlTokenType.A;
+			}
+			break;
+		case 2:
+			switch (begin[0]) {
+			case 'A':
+			case 'a':
+				if (matches (begin, "AS")) return SparqlTokenType.AS;
+				break;
+			case 'B':
+			case 'b':
+				if (matches (begin, "BY")) return SparqlTokenType.BY;
+				break;
+			}
+			break;
+		case 3:
+			switch (begin[0]) {
+			case 'A':
+			case 'a':
+				switch (begin[2]) {
+				case 'C':
+				case 'c':
+					if (matches (begin, "ASC")) return SparqlTokenType.ASC;
+					break;
+				case 'K':
+				case 'k':
+					if (matches (begin, "ASK")) return SparqlTokenType.ASK;
+					break;
+				}
+				break;
+			case 'S':
+			case 's':
+				if (matches (begin, "STR")) return SparqlTokenType.STR;
+				break;
+			}
+			break;
+		case 4:
+			switch (begin[0]) {
+			case 'B':
+			case 'b':
+				if (matches (begin, "BASE")) return SparqlTokenType.BASE;
+				break;
+			case 'D':
+			case 'd':
+				switch (begin[1]) {
+				case 'E':
+				case 'e':
+					if (matches (begin, "DESC")) return SparqlTokenType.DESC;
+					break;
+				case 'R':
+				case 'r':
+					if (matches (begin, "DROP")) return SparqlTokenType.DROP;
+					break;
+				}
+				break;
+			case 'F':
+			case 'f':
+				if (matches (begin, "FROM")) return SparqlTokenType.FROM;
+				break;
+			case 'I':
+			case 'i':
+				if (matches (begin, "INTO")) return SparqlTokenType.INTO;
+				break;
+			case 'L':
+			case 'l':
+				if (matches (begin, "LANG")) return SparqlTokenType.LANG;
+				break;
+			case 'T':
+			case 't':
+				if (matches (begin, "TRUE")) return SparqlTokenType.TRUE;
+				break;
+			}
+			break;
+		case 5:
+			switch (begin[0]) {
+			case 'B':
+			case 'b':
+				if (matches (begin, "BOUND")) return SparqlTokenType.BOUND;
+				break;
+			case 'G':
+			case 'g':
+				switch (begin[2]) {
+				case 'A':
+				case 'a':
+					if (matches (begin, "GRAPH")) return SparqlTokenType.GRAPH;
+					break;
+				case 'O':
+				case 'o':
+					if (matches (begin, "GROUP")) return SparqlTokenType.GROUP;
+					break;
+				}
+				break;
+			case 'L':
+			case 'l':
+				if (matches (begin, "LIMIT")) return SparqlTokenType.LIMIT;
+				break;
+			case 'N':
+			case 'n':
+				if (matches (begin, "NAMED")) return SparqlTokenType.NAMED;
+				break;
+			case 'O':
+			case 'o':
+				if (matches (begin, "ORDER")) return SparqlTokenType.ORDER;
+				break;
+			case 'R':
+			case 'r':
+				if (matches (begin, "REGEX")) return SparqlTokenType.REGEX;
+				break;
+			case 'U':
+			case 'u':
+				if (matches (begin, "UNION")) return SparqlTokenType.UNION;
+				break;
+			case 'W':
+			case 'w':
+				if (matches (begin, "WHERE")) return SparqlTokenType.WHERE;
+				break;
+			case 'F':
+			case 'f':
+				if (matches (begin, "FALSE")) return SparqlTokenType.FALSE;
+				break;
+			case 'I':
+			case 'i':
+				switch (begin[1]) {
+				case 'S':
+				case 's':
+					switch (begin[2]) {
+					case 'I':
+					case 'i':
+						if (matches (begin, "ISIRI")) return SparqlTokenType.ISIRI;
+						break;
+					case 'U':
+					case 'u':
+						if (matches (begin, "ISURI")) return SparqlTokenType.ISURI;
+						break;
+					}
+					break;
+				}
+				break;
+			}
+			break;
+		case 6:
+			switch (begin[0]) {
+			case 'D':
+			case 'd':
+				if (matches (begin, "DELETE")) return SparqlTokenType.DELETE;
+				break;
+			case 'F':
+			case 'f':
+				if (matches (begin, "FILTER")) return SparqlTokenType.FILTER;
+				break;
+			case 'I':
+			case 'i':
+				if (matches (begin, "INSERT")) return SparqlTokenType.INSERT;
+				break;
+			case 'O':
+			case 'o':
+				if (matches (begin, "OFFSET")) return SparqlTokenType.OFFSET;
+				break;
+			case 'P':
+			case 'p':
+				if (matches (begin, "PREFIX")) return SparqlTokenType.PREFIX;
+				break;
+			case 'S':
+			case 's':
+				if (matches (begin, "SELECT")) return SparqlTokenType.SELECT;
+				break;
+			}
+			break;
+		case 7:
+			switch (begin[0]) {
+			case 'R':
+			case 'r':
+				if (matches (begin, "REDUCED")) return SparqlTokenType.REDUCED;
+				break;
+			}
+			break;
+		case 8:
+			switch (begin[0]) {
+			case 'D':
+			case 'd':
+				switch (begin[1]) {
+				case 'A':
+				case 'a':
+					if (matches (begin, "DATATYPE")) return SparqlTokenType.DATATYPE;
+					break;
+				case 'E':
+				case 'e':
+					if (matches (begin, "DESCRIBE")) return SparqlTokenType.DESCRIBE;
+					break;
+				case 'I':
+				case 'i':
+					if (matches (begin, "DISTINCT")) return SparqlTokenType.DISTINCT;
+					break;
+				}
+				break;
+			case 'O':
+			case 'o':
+				if (matches (begin, "OPTIONAL")) return SparqlTokenType.OPTIONAL;
+				break;
+			case 'S':
+			case 's':
+				if (matches (begin, "SAMETERM")) return SparqlTokenType.SAMETERM;
+				break;
+			}
+			break;
+		case 9:
+			switch (begin[0]) {
+			case 'C':
+			case 'c':
+				if (matches (begin, "CONSTRUCT")) return SparqlTokenType.CONSTRUCT;
+				break;
+			case 'I':
+			case 'i':
+				if (matches (begin, "ISLITERAL")) return SparqlTokenType.ISLITERAL;
+				break;
+			}
+			break;
+		case 11:
+			if (matches (begin, "LANGMATCHES")) return SparqlTokenType.LANGMATCHES;
+			break;
+		}
+		return SparqlTokenType.PN_PREFIX;
+	}
+
+	SparqlTokenType read_number () {
+		var type = SparqlTokenType.INTEGER;
+
+		// integer part
+		if (current < end - 2 && current[0] == '0'
+		    && current[1] == 'x' && current[2].isxdigit ()) {
+			// hexadecimal integer literal
+			current += 2;
+			while (current < end && current[0].isxdigit ()) {
+				current++;
+			}
+		} else {
+			// decimal number
+			while (current < end && current[0].isdigit ()) {
+				current++;
+			}
+		}
+
+		// fractional part
+		if (current < end - 1 && current[0] == '.' && current[1].isdigit ()) {
+			type = SparqlTokenType.DOUBLE;
+			current++;
+			while (current < end && current[0].isdigit ()) {
+				current++;
+			}
+		}
+
+		// exponent part
+		if (current < end && current[0].tolower () == 'e') {
+			type = SparqlTokenType.DOUBLE;
+			current++;
+			if (current < end && (current[0] == '+' || current[0] == '-')) {
+				current++;
+			}
+			while (current < end && current[0].isdigit ()) {
+				current++;
+			}
+		}
+
+		// type suffix
+		if (current < end) {
+			switch (current[0]) {
+			case 'l':
+			case 'L':
+				if (type == SparqlTokenType.INTEGER) {
+					current++;
+					if (current < end && current[0].tolower () == 'l') {
+						current++;
+					}
+				}
+				break;
+			case 'u':
+			case 'U':
+				if (type == SparqlTokenType.INTEGER) {
+					current++;
+					if (current < end && current[0].tolower () == 'l') {
+						current++;
+						if (current < end && current[0].tolower () == 'l') {
+							current++;
+						}
+					}
+				}
+				break;
+			case 'f':
+			case 'F':
+			case 'd':
+			case 'D':
+				type = SparqlTokenType.DOUBLE;
+				current++;
+				break;
+			}
+		}
+
+		return type;
+	}
+
+	bool is_pn_char (char c) {
+		return (c.isalnum () || c == '_' || c == '-');
+	}
+
+	bool is_pn_local_char (char c) {
+		return (c.isalnum () || c == '_' || c == '-' || c == '.');
+	}
+
+	bool is_varname_char (char c) {
+		return (c.isalnum () || c == '_');
+	}
+
+	public SparqlTokenType read_token (out SourceLocation token_begin, out SourceLocation token_end) throws SparqlError {
+		space ();
+
+		SparqlTokenType type;
+		char* begin = current;
+		token_begin.pos = begin;
+		token_begin.line = line;
+		token_begin.column = column;
+
+		int token_length_in_chars = -1;
+
+		if (current >= end) {
+			type = SparqlTokenType.EOF;
+		} else if (current[0].isalpha () /* || current[0] == ':' */ ) {
+			// keyword or prefixed name
+			int len = 0;
+			while (current < end && is_pn_char (current[0])) {
+				current++;
+				len++;
+			}
+			type = get_identifier_or_keyword (begin, len);
+		} else if (current[0].isdigit ()) {
+			type = read_number ();
+		} else {
+			switch (current[0]) {
+			case '{':
+				type = SparqlTokenType.OPEN_BRACE;
+				current++;
+				break;
+			case '}':
+				type = SparqlTokenType.CLOSE_BRACE;
+				current++;
+				break;
+			case '(':
+				type = SparqlTokenType.OPEN_PARENS;
+				current++;
+				break;
+			case ')':
+				type = SparqlTokenType.CLOSE_PARENS;
+				current++;
+				break;
+			case '[':
+				type = SparqlTokenType.OPEN_BRACKET;
+				current++;
+				break;
+			case ']':
+				type = SparqlTokenType.CLOSE_BRACKET;
+				current++;
+				break;
+			case '.':
+				type = SparqlTokenType.DOT;
+				current++;
+				break;
+			case ':':
+				type = SparqlTokenType.COLON;
+				current++;
+				while (current < end && is_pn_local_char (current[0])) {
+					current++;
+				}
+				break;
+			case ',':
+				type = SparqlTokenType.COMMA;
+				current++;
+				break;
+			case ';':
+				type = SparqlTokenType.SEMICOLON;
+				current++;
+				break;
+			case '?':
+			case '$':
+				type = SparqlTokenType.NONE;
+				current++;
+				while (current < end && is_varname_char (current[0])) {
+					type = SparqlTokenType.VAR;
+					current++;
+				}
+				break;
+			case '|':
+				type = SparqlTokenType.NONE;
+				current++;
+				if (current < end) {
+					switch (current[0]) {
+					case '|':
+						type = SparqlTokenType.OP_OR;
+						current++;
+						break;
+					}
+				}
+				break;
+			case '&':
+				type = SparqlTokenType.NONE;
+				current++;
+				if (current < end) {
+					switch (current[0]) {
+					case '&':
+						type = SparqlTokenType.OP_AND;
+						current++;
+						break;
+					}
+				}
+				break;
+			case '=':
+				type = SparqlTokenType.OP_EQ;
+				current++;
+				break;
+			case '<':
+				type = SparqlTokenType.OP_LT;
+				current++;
+				if (current < end) {
+					// check whether token is an IRI
+					while (current < end && current[0] > ' ') {
+						switch (current[0]) {
+						case '<':
+						case '>':
+						case '"':
+						case '{':
+						case '}':
+						case '|':
+						case '^':
+						case '`':
+						case '\\':
+							// not an IRI
+							break;
+						default:
+							current++;
+							continue;
+						}
+						break;
+					}
+					if (current < end && current[0] == '>') {
+						type = SparqlTokenType.IRI_REF;
+						current++;
+						break;
+					} else {
+						current = begin + 1;
+					}
+					switch (current[0]) {
+					case '=':
+						type = SparqlTokenType.OP_LE;
+						current++;
+						break;
+					}
+				}
+				break;
+			case '>':
+				type = SparqlTokenType.OP_GT;
+				current++;
+				if (current < end && current[0] == '=') {
+					type = SparqlTokenType.OP_GE;
+					current++;
+				}
+				break;
+			case '!':
+				type = SparqlTokenType.OP_NEG;
+				current++;
+				if (current < end && current[0] == '=') {
+					type = SparqlTokenType.OP_NE;
+					current++;
+				}
+				break;
+			case '+':
+				type = SparqlTokenType.PLUS;
+				current++;
+				break;
+			case '-':
+				type = SparqlTokenType.MINUS;
+				current++;
+				break;
+			case '*':
+				type = SparqlTokenType.STAR;
+				current++;
+				break;
+			case '/':
+				type = SparqlTokenType.DIV;
+				current++;
+				break;
+			case '\'':
+			case '"':
+				if (current < end - 6 && begin[1] == begin[0] && begin[2] == begin[0]) {
+					if (begin[0] == '\'') {
+						type = SparqlTokenType.STRING_LITERAL_LONG1;
+					} else {
+						type = SparqlTokenType.STRING_LITERAL_LONG2;
+					}
+
+					token_length_in_chars = 6;
+					current += 3;
+					while (current < end - 4) {
+						if (current[0] == begin[0] && current[1] == begin[0] && current[2] == begin[0]) {
+							break;
+						} else if (current[0] == '\n') {
+							current++;
+							line++;
+							column = 1;
+							token_length_in_chars = 3;
+						} else {
+							unichar u = ((string) current).get_char_validated ((long) (end - current));
+							if (u != (unichar) (-1)) {
+								current += u.to_utf8 (null);
+								token_length_in_chars++;
+							} else {
+								throw new SparqlError.PARSE ("%d.%d: invalid UTF-8 character", line, column + token_length_in_chars);
+							}
+						}
+					}
+					if (current[0] == begin[0] && current[1] == begin[0] && current[2] == begin[0]) {
+						current += 3;
+					} else {
+						throw new SparqlError.PARSE ("%d.%d: syntax error, expected \"\"\"", line, column + token_length_in_chars);
+					}
+					break;
+				}
+
+				if (begin[0] == '\'') {
+					type = SparqlTokenType.STRING_LITERAL1;
+				} else {
+					type = SparqlTokenType.STRING_LITERAL2;
+				}
+
+				token_length_in_chars = 2;
+				current++;
+				while (current < end && current[0] != begin[0]) {
+					if (current[0] == '\\') {
+						current++;
+						token_length_in_chars++;
+						if (current >= end) {
+							break;
+						}
+
+						switch (current[0]) {
+						case '\'':
+						case '"':
+						case '\\':
+						case 'b':
+						case 'f':
+						case 'n':
+						case 'r':
+						case 't':
+							current++;
+							token_length_in_chars++;
+							break;
+						default:
+							throw new SparqlError.PARSE ("%d.%d: invalid escape sequence", line, column + token_length_in_chars);
+						}
+					} else if (current[0] == '\n') {
+						break;
+					} else {
+						unichar u = ((string) current).get_char_validated ((long) (end - current));
+						if (u != (unichar) (-1)) {
+							current += u.to_utf8 (null);
+							token_length_in_chars++;
+						} else {
+							current++;
+							throw new SparqlError.PARSE ("%d.%d: invalid UTF-8 character", line, column + token_length_in_chars);
+						}
+					}
+				}
+				if (current < end && current[0] != '\n') {
+					current++;
+				} else {
+					throw new SparqlError.PARSE ("%d.%d: syntax error, expected %c", line, column + token_length_in_chars, begin[0]);
+				}
+				break;
+			default:
+				unichar u = ((string) current).get_char_validated ((long) (end - current));
+				if (u != (unichar) (-1)) {
+					throw new SparqlError.PARSE ("%d.%d: syntax error, unexpected character", line, column);
+				} else {
+					throw new SparqlError.PARSE ("%d.%d: invalid UTF-8 character", line, column);
+				}
+			}
+		}
+
+		if (token_length_in_chars < 0) {
+			column += (int) (current - begin);
+		} else {
+			column += token_length_in_chars;
+		}
+
+		token_end.pos = current;
+		token_end.line = line;
+		token_end.column = column - 1;
+
+		return type;
+	}
+
+	bool matches (char* begin, string keyword) {
+		char* keyword_array = keyword;
+		long len = keyword.len ();
+		for (int i = 0; i < len; i++) {
+			if (begin[i].toupper () != keyword_array[i]) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	bool whitespace () {
+		bool found = false;
+		while (current < end && current[0].isspace ()) {
+			if (current[0] == '\n') {
+				line++;
+				column = 0;
+			}
+			found = true;
+			current++;
+			column++;
+		}
+		return found;
+	}
+
+	bool comment () {
+		if (current >= end || current[0] != '#') {
+			return false;
+		}
+
+		// single-line comment
+		// skip until end of line or end of file
+		while (current < end && current[0] != '\n') {
+			current++;
+		}
+
+		return true;
+	}
+
+	void space () {
+		while (whitespace () || comment ()) {
+		}
+	}
+}
+
+/**
+ * Represents a position in a source file.
+ */
+public struct Tracker.SourceLocation {
+	public char* pos;
+	public int line;
+	public int column;
+
+	public SourceLocation (char* _pos, int _line, int _column) {
+		pos = _pos;
+		line = _line;
+		column = _column;
+	}
+}
+
+public enum Tracker.SparqlTokenType {
+	NONE,
+	A,
+	AS,
+	ASC,
+	ASK,
+	BASE,
+	BOUND,
+	BY,
+	CLOSE_BRACE,
+	CLOSE_BRACKET,
+	CLOSE_PARENS,
+	COLON,
+	COMMA,
+	CONSTRUCT,
+	DATATYPE,
+	DECIMAL,
+	DELETE,
+	DESC,
+	DESCRIBE,
+	DISTINCT,
+	DIV,
+	DOT,
+	DOUBLE,
+	DROP,
+	EOF,
+	FALSE,
+	FILTER,
+	FROM,
+	GRAPH,
+	GROUP,
+	INSERT,
+	INTEGER,
+	INTO,
+	IRI_REF,
+	ISIRI,
+	ISLITERAL,
+	ISURI,
+	LANG,
+	LANGMATCHES,
+	LIMIT,
+	MINUS,
+	NAMED,
+	OFFSET,
+	OP_AND,
+	OP_EQ,
+	OP_GE,
+	OP_GT,
+	OP_LE,
+	OP_LT,
+	OP_NE,
+	OP_NEG,
+	OP_OR,
+	OPEN_BRACE,
+	OPEN_BRACKET,
+	OPEN_PARENS,
+	OPTIONAL,
+	ORDER,
+	PLUS,
+	PN_PREFIX,
+	PREFIX,
+	REDUCED,
+	REGEX,
+	SAMETERM,
+	SELECT,
+	SEMICOLON,
+	STAR,
+	STR,
+	STRING_LITERAL1,
+	STRING_LITERAL2,
+	STRING_LITERAL_LONG1,
+	STRING_LITERAL_LONG2,
+	TRUE,
+	UNION,
+	VAR,
+	WHERE;
+
+	public weak string to_string () {
+		switch (this) {
+		case A: return "`a'";
+		case AS: return "`AS'";
+		case ASC: return "`ASC'";
+		case ASK: return "`ASK'";
+		case BASE: return "`BASE'";
+		case BOUND: return "`BOUND'";
+		case BY: return "`BY'";
+		case CONSTRUCT: return "`CONSTRUCT'";
+		case DATATYPE: return "`DATATYPE'";
+		case DECIMAL: return "`DECIMAL'";
+		case DELETE: return "`DELETE'";
+		case DESC: return "`DESC'";
+		case DESCRIBE: return "`DESCRIBE'";
+		case DISTINCT: return "`DISTINCT'";
+		case DOUBLE: return "`DOUBLE'";
+		case DROP: return "`DROP'";
+		case EOF: return "`EOF'";
+		case FALSE: return "`FALSE'";
+		case FILTER: return "`FILTER'";
+		case FROM: return "`FROM'";
+		case GRAPH: return "`GRAPH'";
+		case GROUP: return "`GROUP'";
+		case INSERT: return "`INSERT'";
+		case INTEGER: return "`INTEGER'";
+		case INTO: return "`INTO'";
+		case ISIRI: return "`ISIRI'";
+		case ISLITERAL: return "`ISLITERAL'";
+		case ISURI: return "`ISURI'";
+		case LANG: return "`LANG'";
+		case LANGMATCHES: return "`LANGMATCHES'";
+		case LIMIT: return "`LIMIT'";
+		case NAMED: return "`NAMED'";
+		case OFFSET: return "`OFFSET'";
+		case OPTIONAL: return "`OPTIONAL'";
+		case ORDER: return "`ORDER'";
+		case PN_PREFIX: return "`PN_PREFIX'";
+		case PREFIX: return "`PREFIX'";
+		case REDUCED: return "`REDUCED'";
+		case REGEX: return "`REGEX'";
+		case SAMETERM: return "`SAMETERM'";
+		case SELECT: return "`SELECT'";
+		case STR: return "`STR'";
+		case TRUE: return "`TRUE'";
+		case UNION: return "`UNION'";
+		case WHERE: return "`WHERE'";
+		default: return "unknown token";
+		}
+	}
+}
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]