1 // $Id: CQLParser.java,v 1.7 2002-10-25 16:56:43 mike Exp $
3 package org.z3950.zing.cql;
4 import java.util.Properties;
5 import java.io.InputStream;
6 import java.io.IOException;
7 import java.io.StringReader;
8 import java.io.StreamTokenizer;
12 * Compiles a CQL string into a parse tree ...
15 * @version $Id: CQLParser.java,v 1.7 2002-10-25 16:56:43 mike Exp $
16 * @see <A href="http://zing.z3950.org/cql/index.html"
17 * >http://zing.z3950.org/cql/index.html</A>
19 public class CQLParser {
21 private StreamTokenizer st;
23 private class CQLParseException extends Exception {
24 CQLParseException(String s) { super(s); }
28 // Nothing to do: do we need this constructor, then?
31 public CQLNode parse(String cql)
32 throws CQLParseException, IOException {
34 st = new StreamTokenizer(new StringReader(cql));
45 while ((token = st.nextToken()) != st.TT_EOF) {
46 System.out.println("token=" + token + ", " +
47 "nval=" + st.nval + ", " +
54 System.err.println("*about to parse_query()");
55 CQLNode root = parse_query();
56 if (st.ttype != st.TT_EOF)
57 throw new CQLParseException("junk after end: " + render(st));
62 private CQLNode parse_query()
63 throws CQLParseException, IOException {
64 System.err.println("*in parse_query()");
66 CQLNode term = parse_term();
67 while (st.ttype == st.TT_WORD) {
68 String op = st.sval.toLowerCase();
69 System.err.println("*checking op '" + op + "'");
70 if (st.sval.equals("and")) {
72 CQLNode term2 = parse_term();
73 term = new CQLAndNode(term, term2);
74 } else if (st.sval.equals("or")) {
76 CQLNode term2 = parse_term();
77 term = new CQLOrNode(term, term2);
78 } else if (st.sval.equals("not")) {
80 CQLNode term2 = parse_term();
81 term = new CQLNotNode(term, term2);
83 // ### Need to handle "prox"
86 System.err.println("*no more ops");
90 private CQLNode parse_term()
91 throws CQLParseException, IOException {
92 System.err.println("*in parse_term()");
93 if (st.ttype == '(') {
95 CQLNode expr = parse_query();
100 System.err.println("*not a parenthesised term");
101 // ### Need to parse qualifier-relation pairs
102 String word = st.sval;
104 CQLTermNode node = new CQLTermNode("x", "=", word);
105 System.err.println("*made term node " + node);
109 private void match(int token)
110 throws CQLParseException, IOException {
111 System.err.println("*in match(" + render(st, token, null) + ")");
112 if (st.ttype != token)
113 throw new CQLParseException("expected " + render(st, token, null) +
114 ", " + "got " + render(st));
118 // ### This utility should surely be a method of the StreamTokenizer class
119 private static String render(StreamTokenizer st) {
120 return render(st, st.ttype, null);
123 private static String render(StreamTokenizer st, int token, String str) {
126 if (token == st.TT_EOF) {
128 } else if (token == st.TT_EOL) {
130 } else if (token == st.TT_NUMBER) {
131 return "number: " + st.nval;
132 } else if (token == st.TT_WORD) {
133 return "word: \"" + st.sval + "\"";
134 } else if (token == '"' || token == '\'') {
135 return "string: \"" + st.sval + "\"";
138 return "'" + String.valueOf((char) token) + "'";
144 // e.g. echo '(au=Kerninghan or au=Ritchie) and ti=Unix' |
145 // java org.z3950.zing.cql.CQLParser
149 public static void main (String[] args) {
150 if (args.length != 0) {
151 System.err.println("Usage: " + args[0]);
155 byte[] bytes = new byte[10000];
157 // Read in the whole of standard input in one go
158 int nbytes = System.in.read(bytes);
159 } catch (java.io.IOException ex) {
160 System.err.println("Can't read query: " + ex);
163 String cql = new String(bytes);
164 CQLParser parser = new CQLParser();
167 root = parser.parse(cql);
168 System.err.println("root='" + root + "'");
169 System.out.println(root.toXCQL(0));
170 } catch (CQLParseException ex) {
171 System.err.println("Syntax error: " + ex);
173 } catch (java.io.IOException ex) {
174 System.err.println("Can't compile query: " + ex);