d7f3b77606b5f102a95915da95f4935c95657432
[cql-java-moved-to-github.git] / src / main / java / org / z3950 / zing / cql / CQLLexer.java
1 /*
2  * Copyright (c) 1995-2014, Index Datassss
3  * All rights reserved.
4  * See the file LICENSE for details.
5  */
6 package org.z3950.zing.cql;
7
8 /**
9  * Implementation of the CQL lexical syntax analyzer
10  * @author jakub
11  */
12 public class CQLLexer implements CQLTokenizer {
13   private String qs;
14   private int qi;
15   private int ql;
16   private int what = TT_NOTHING;
17   private String val;
18   private String lval;
19   private StringBuilder buf = new StringBuilder();
20
21   public CQLLexer(String cql, boolean debug) {
22     qs = cql;
23     ql = cql.length();
24   }
25
26   @Override
27   public void move() {
28     //eat whitespace
29     while (qi < ql && strchr(" \t\r\n\0", qs.charAt(qi)))
30       qi++;
31     //eof
32     if (qi == ql) {
33       what = TT_EOF;
34       return;
35     }
36     //current char
37     char c = qs.charAt(qi);
38     //separators
39     if (strchr("()/", c)) {
40       what = c;
41       qi++;
42     //comparitor
43     } else if (strchr("<>=", c)) {
44       what = c;
45       qi++;
46       //two-char comparitor
47       if (qi < ql) {
48         char d = qs.charAt(qi);
49         String comp = String.valueOf((char) c) + String.valueOf((char) d);
50         if (comp.equals("==")) {
51           what = TT_EQEQ;
52           qi++;
53         }
54         else if (comp.equals("<=")) {
55           what = TT_LE;
56           qi++;
57         }
58         else if (comp.equals(">=")) {
59           what = TT_GE;
60           qi++;
61         }
62         else if (comp.equals("<>")) {
63           what = TT_NE;
64           qi++;
65         }
66       }
67     //quoted string
68     } else if (strchr("\"", c)) { //no single-quotes
69       what = '"';
70       //remember quote char
71       char mark = c;
72       qi++;
73       boolean escaped = false;
74       buf.setLength(0); //reset buffer
75       while (qi < ql) {
76         if (!escaped && qs.charAt(qi) == mark) //terminator
77           break;
78         if (escaped && strchr("*?^\\", qs.charAt(qi))) //no escaping for d-quote
79           buf.append("\\");
80         if (!escaped && qs.charAt(qi) == '\\') { //escape-char
81           escaped = true;
82           qi++;
83           continue;
84         }
85         escaped = false; //reset escape
86         buf.append(qs.charAt(qi));
87         qi++;
88       }
89       val = buf.toString();
90       lval = val.toLowerCase();
91       if (qi < ql)
92         qi++;
93       else //unterminated
94         what = TT_EOF; //notify error
95       //unquoted string
96     } else {
97       what = TT_WORD;
98       buf.setLength(0); //reset buffer
99       while (qi < ql
100         && !strchr("()/<>= \t\r\n", qs.charAt(qi))) {
101         if (qs.charAt(qi) != '\0')
102           buf.append(qs.charAt(qi));
103         qi++;
104       }
105       val = buf.toString();
106       lval = val.toLowerCase();
107       if (lval.equals("or")) what = TT_OR;
108       else if (lval.equals("and")) what = TT_AND;
109       else if (lval.equals("not")) what = TT_NOT;
110       else if (lval.equals("prox")) what = TT_PROX;
111       else if (lval.equals("sortby")) what = TT_SORTBY;
112     }
113   }
114
115   private boolean strchr(String s, char ch) {
116     return s.indexOf(ch) >= 0;
117   }
118
119   @Override
120   public String value() {
121     return val;
122   }
123
124   @Override
125   public int what() {
126     return what;
127   }
128
129   @Override
130   public String render() {
131     return render(what, true);
132   }
133
134   @Override
135   public String render(int token, boolean quoteChars) {
136     switch (token) {
137       case TT_EOF:
138         return "EOF";
139       case TT_WORD:
140         return "word: '" + val + "'";
141       case '"':
142         return "string: \"" + val + "\"";
143       case TT_LE:
144         return "<=";
145       case TT_GE:
146         return ">=";
147       case TT_NE:
148         return "<>";
149       case TT_EQEQ:
150         return "==";
151       case TT_AND:
152         return "and";
153       case TT_NOT:
154         return "not";
155       case TT_OR:
156         return "or";
157       case TT_PROX:
158         return "prox";
159       case TT_SORTBY:
160         return "sortby";
161       default:
162         //a single character, such as '(' or '/' or relation
163         String res = String.valueOf((char) token);
164         if (quoteChars)
165           res = "'" + res + "'";
166         return res;
167     }
168   }
169
170   @Override
171   public int pos() {
172     return qi;
173   }
174   
175   public static void main(String[] args) throws Exception {
176     if (args.length > 1) {
177       System.err.println("Usage: CQLLexer [<CQL-query>]");
178       System.err.println("If unspecified, query is read from stdin");
179       System.exit(1);
180     }
181
182     String cql;
183     if (args.length == 1) {
184       cql = args[0];
185     } else {
186       byte[] bytes = new byte[10000];
187       try {
188         // Read in the whole of standard input in one go
189         int nbytes = System.in.read(bytes);
190       } catch (java.io.IOException ex) {
191         System.err.println("Can't read query: " + ex.getMessage());
192         System.exit(2);
193       }
194       cql = new String(bytes);
195     }
196
197     CQLTokenizer lexer = new CQLLexer(cql, true);
198     while ((lexer.what()) != TT_EOF) {
199       lexer.move();
200       System.out.println(lexer.render());
201     }
202   }
203 }