2 using System
.Collections
;
10 namespace SemWeb
.Query
{
12 public class SparqlParser
{
15 NamespaceManager nsmgr
= new NamespaceManager();
17 string baseuri
= null;
18 SparqlQuestion question
;
20 ArrayList variables
= new ArrayList();
21 ArrayList selectvariables
= new ArrayList();
22 bool selectdistinct
= false;
23 bool selectall
= false;
25 MemoryStore graph
= new MemoryStore();
27 public SparqlParser(TextReader reader
) {
28 this.reader
= new MyReader(reader
);
33 public string BaseUri { get { return baseuri; }
}
35 public SparqlQuestion Question { get { return question; }
}
37 public bool Distinct { get { return selectdistinct; }
}
39 public bool All { get { return selectall; }
}
41 public Store Graph { get { return graph; }
}
43 public IList Variables { get { return ArrayList.ReadOnly(variables); }
}
45 public IList Select { get { return ArrayList.ReadOnly(selectvariables); }
}
47 public enum SparqlQuestion
{
54 public QueryEngine
CreateQuery() {
55 QueryEngine query
= new QueryEngine();
57 foreach (string var in variables
) {
58 query
.Select(new Entity(var));
61 foreach (Statement s
in graph
)
64 //graph.Select(new N3Writer(Console.Out));
69 private void ReadWhitespace() {
71 while (char.IsWhiteSpace((char)reader
.Peek()))
74 if (reader
.Peek() == '#') {
76 int c
= reader
.Read();
77 if (c
== -1 || c
== 10 || c
== 13) break;
86 private string ReadToken() {
89 StringBuilder b
= new StringBuilder();
90 while (reader
.Peek() != -1 && char.IsLetter((char)reader
.Peek()))
91 b
.Append((char)reader
.Read());
96 private string ReadName() {
99 StringBuilder b
= new StringBuilder();
101 int c
= reader
.Peek();
102 if (c
== -1 || char.IsWhiteSpace((char)c
)
103 || (!char.IsLetterOrDigit((char)c
) && c
!= '?' && c
!= '$' && c
!= '-' && c
!= '_')) break;
110 private string ReadQuotedUri() {
112 Location loc
= reader
.Location
;
114 int open
= reader
.Read();
116 OnError("Expecting a quoted URI starting with a '<'", loc
);
118 StringBuilder b
= new StringBuilder();
120 int c
= reader
.Read();
123 OnError("End of file while reading a URI", loc
);
126 if (char.IsWhiteSpace((char)c
))
127 OnError("White space cannot appear in a URI", loc
);
135 private string ReadQNamePrefix() {
137 Location loc
= reader
.Location
;
139 StringBuilder b
= new StringBuilder();
141 int c
= reader
.Read();
144 OnError("End of file while reading a QName prefix", loc
);
145 if (char.IsWhiteSpace((char)c
))
146 OnError("Expecting a colon, " + b
, loc
);
157 private string ReadUri() {
159 if (reader
.Peek() == '<')
160 return ReadQuotedUri();
162 Location loc
= reader
.Location
;
163 string prefix
= ReadQNamePrefix();
164 string localname
= ReadName();
168 OnError("No BASE URI was specified", loc
);
169 return baseuri
+ localname
;
172 return nsmgr
.Resolve(prefix
+ localname
);
175 private string ReadVarOrUri() {
178 if (reader
.Peek() == '?' || reader
.Peek() == '$')
184 private object ReadNumber() {
186 Location loc
= reader
.Location
;
187 string num
= ReadName();
189 if (num
.StartsWith("0x") || num
.StartsWith("0X"))
190 return int.Parse(num
.Substring(2), System
.Globalization
.NumberStyles
.AllowHexSpecifier
);
191 if (num
.IndexOf(".") >= 0 || num
.IndexOf("e") >= 0 || num
.IndexOf("E") >= 0)
192 return decimal.Parse(num
);
193 return long.Parse(num
);
194 } catch (Exception e
) {
195 OnError("Invalid number: " + num
, loc
);
200 private string ReadLiteralText() {
201 char quotechar
= (char)reader
.Read();
203 StringBuilder b
= new StringBuilder();
205 bool escaped
= false;
207 Location loc
= reader
.Location
;
208 int c
= reader
.Read();
210 OnError("End of file while reading a text literal", loc
);
214 case 'n': b
.Append('\n'); break;
215 case 'r': b
.Append('\r'); break;
216 default: b
.Append(c
); break;
219 } else if (c
== '\\') {
221 } else if (c
== quotechar
) {
231 private object ReadLiteral() {
233 int firstchar
= reader
.Peek();
234 if (firstchar
== -1) OnError("End of file expecting a literal", reader
.Location
);
236 if (char.IsDigit((char)firstchar
) || firstchar
== '.')
237 return new Literal(ReadNumber().ToString(), null, null);
239 if (firstchar
== '\'' || firstchar
== '\"') {
240 string text
= ReadLiteralText();
241 string lang
= null, datatype
= null;
242 if (reader
.Peek() == '@') {
246 if (reader
.Peek() == '^') {
249 datatype
= ReadUri();
251 return new Literal(text
, lang
, datatype
);
254 return ReadVarOrUri();
257 private Entity
GetEntity(string name
) {
258 if (name
[0] == '?' || name
[0] == '$') {
259 if (!variables
.Contains(name
))
263 return new Entity(name
);
266 private int ReadPatternGroup() {
267 //bool nextoptional = false;
271 Location loc
= reader
.Location
;
273 int next
= reader
.Peek();
278 string union
= ReadToken();
279 if (union
!= "UNION")
280 OnError("Expecting UNION", loc
);
282 OnError("UNION is not supported", loc
);
284 } else if (next
== 'G') {
285 string graph
= ReadToken();
286 if (graph
!= "GRAPH")
287 OnError("Expecting GRAPH", loc
);
289 OnError("GRAPH is not supported", loc
);
291 } else if (next
== 'O') {
292 string optional
= ReadToken();
293 if (optional
!= "OPTIONAL")
294 OnError("Expecting OPTIONAL", loc
);
296 OnError("OPTIONAL is not supported", loc
);
298 //nextoptional = true;
301 } else if (next
== 'A') {
302 string and
= ReadToken();
304 OnError("Expecting AND", loc
);
306 loc
= reader
.Location
;
307 OnError("Expressions are not supported", loc
);
309 } else if (next
== '(') {
311 reader
.Read(); // open paren
312 string subj
= ReadVarOrUri();
313 string pred
= ReadVarOrUri();
314 object obj
= ReadLiteral();
317 loc
= reader
.Location
;
318 int close
= reader
.Read();
320 OnError("Expecting close parenthesis: " + (char)close
, loc
);
322 graph
.Add( new Statement (
325 obj
is string ? (Resource
)GetEntity((string)obj
) : (Resource
)obj
328 } else if (next
== '{') {
331 } else if (next
== '}') {
338 //nextoptional = false;
349 private void Parse() {
350 ReadState state
= ReadState
.StartOfProlog
;
353 Location loc
= reader
.Location
;
354 string clause
= ReadToken();
358 if (state
<= ReadState
.Prolog
)
359 OnError("No query was given", loc
);
363 if (state
!= ReadState
.StartOfProlog
)
364 OnError("BASE must be the first clause", loc
);
365 baseuri
= ReadQuotedUri();
366 state
= ReadState
.Prolog
;
370 if (state
> ReadState
.Prolog
)
371 OnError("PREFIX must occur in the prolog", loc
);
372 string prefix
= ReadQNamePrefix();
373 string uri
= ReadQuotedUri();
374 nsmgr
.AddNamespace(uri
, prefix
.Substring(0, prefix
.Length
-1)); // strip trailing ':'
375 state
= ReadState
.Prolog
;
379 if (state
> ReadState
.Prolog
)
380 OnError("SELECT cannot occur here", loc
);
381 state
= ReadState
.Limits
;
383 question
= SparqlQuestion
.Select
;
386 loc
= reader
.Location
;
387 int next
= reader
.Peek();
388 if (next
== ',') { reader.Read(); continue; }
389 if (next
!= 'D' && next
!= '?' && next
!= '$' && next
!= '*')
391 string var = ReadName();
392 if (var == "DISTINCT") {
393 selectdistinct
= true;
394 } else if (var[0] == '?' || var[0] == '$') {
396 OnError("Cannot select * and also name other variables", loc
);
397 if (!selectvariables
.Contains(var))
398 selectvariables
.Add(var);
399 } else if (var == "*") {
400 if (selectvariables
.Count
> 0)
401 OnError("Cannot select * and also name other variables", loc
);
405 OnError("Invalid variable: " + var, loc
);
412 if (state
> ReadState
.Prolog
)
413 OnError("DESCRIBE cannot occur here", loc
);
414 state
= ReadState
.Limits
;
415 OnError("DESCRIBE is not supported", loc
);
417 question
= SparqlQuestion
.Describe
;
421 if (state
> ReadState
.Prolog
)
422 OnError("CONSTRUCT cannot occur here", loc
);
423 state
= ReadState
.Limits
;
424 OnError("CONSTRUCT is not supported", loc
);
426 question
= SparqlQuestion
.Construct
;
430 if (state
> ReadState
.Prolog
)
431 OnError("ASK cannot occur here", loc
);
432 state
= ReadState
.Limits
;
434 question
= SparqlQuestion
.Ask
;
439 if (state
!= ReadState
.Limits
)
440 OnError("WITH cannot occur here", loc
);
441 OnError("WITH is not supported", loc
);
445 if (state
!= ReadState
.Limits
)
446 OnError("WHERE cannot occur here", loc
);
453 private void OnError(string message
, Location position
) {
454 throw new ParserException(message
+ ", line " + position
.Line
+ " col " + position
.Col
);