-// $Id: CQLParser.java,v 1.10 2002-10-30 09:19:26 mike Exp $
+// $Id: CQLParser.java,v 1.12 2002-11-01 23:45:28 mike Exp $
package org.z3950.zing.cql;
import java.io.IOException;
+import java.util.Vector;
/**
* Compiles a CQL string into a parse tree.
- * ###
+ * ##
*
- * @version $Id: CQLParser.java,v 1.10 2002-10-30 09:19:26 mike Exp $
+ * @version $Id: CQLParser.java,v 1.12 2002-11-01 23:45:28 mike Exp $
* @see <A href="http://zing.z3950.org/cql/index.html"
* >http://zing.z3950.org/cql/index.html</A>
*/
CQLNode term2 = parse_term(qualifier, relation);
term = new CQLNotNode(term, term2);
} else if (lexer.ttype == lexer.TT_PROX) {
- // ### Handle "prox"
+ match(lexer.TT_PROX);
+ CQLProxNode proxnode = new CQLProxNode(term);
+ gatherProxParameters(proxnode);
+ CQLNode term2 = parse_term(qualifier, relation);
+ proxnode.addSecondSubterm(term2);
+ term = (CQLNode) proxnode;
} else {
throw new CQLParseException("expected boolean, got " +
lexer.render());
while (lexer.ttype == '/') {
match('/');
- // ### could insist on known modifiers only
- if (lexer.ttype != lexer.TT_WORD)
+ if (lexer.ttype != lexer.TT_RELEVANT &&
+ lexer.ttype != lexer.TT_FUZZY &&
+ lexer.ttype != lexer.TT_STEM)
throw new CQLParseException("expected relation modifier, "
+ "got " + lexer.render());
relation.addModifier(lexer.sval);
- match(lexer.TT_WORD);
+ match(lexer.ttype);
}
debug("qualifier='" + qualifier + ", " +
return node;
}
+ private void gatherProxParameters(CQLProxNode node)
+ throws CQLParseException, IOException {
+ for (int i = 0; i < 4; i++) {
+ if (lexer.ttype != '/')
+ return; // end of proximity parameters
+
+ match('/');
+ if (lexer.ttype != '/') {
+ // not an omitted default
+ switch (i) {
+ // Assumes order is: relation/distance/unit/ordering
+ case 0: gatherProxRelation(node); break;
+ case 1: gatherProxDistance(node); break;
+ case 2: gatherProxUnit(node); break;
+ case 3: gatherProxOrdering(node); break;
+ }
+ }
+ }
+ }
+
+ private void gatherProxRelation(CQLProxNode node)
+ throws CQLParseException, IOException {
+ if (!isProxRelation())
+ throw new CQLParseException("expected proximity relation, got " +
+ lexer.render());
+ node.addModifier("relation", lexer.render(lexer.ttype, false));
+ match(lexer.ttype);
+ debug("gPR matched " + lexer.render(lexer.ttype, false));
+ }
+
+ private void gatherProxDistance(CQLProxNode node)
+ throws CQLParseException, IOException {
+ if (lexer.ttype != lexer.TT_NUMBER)
+ throw new CQLParseException("expected proximity distance, got " +
+ lexer.render());
+ node.addModifier("distance", lexer.render(lexer.ttype, false));
+ match(lexer.ttype);
+ debug("gPD matched " + lexer.render(lexer.ttype, false));
+ }
+
+ private void gatherProxUnit(CQLProxNode node)
+ throws CQLParseException, IOException {
+ if (lexer.ttype != lexer.TT_pWORD &&
+ lexer.ttype != lexer.TT_SENTENCE &&
+ lexer.ttype != lexer.TT_PARAGRAPH &&
+ lexer.ttype != lexer.TT_ELEMENT)
+ throw new CQLParseException("expected proximity unit, got " +
+ lexer.render());
+ node.addModifier("unit", lexer.render());
+ match(lexer.ttype);
+ }
+
+ private void gatherProxOrdering(CQLProxNode node)
+ throws CQLParseException, IOException {
+ if (lexer.ttype != lexer.TT_ORDERED &&
+ lexer.ttype != lexer.TT_UNORDERED)
+ throw new CQLParseException("expected proximity ordering, got " +
+ lexer.render());
+ node.addModifier("ordering", lexer.render());
+ match(lexer.ttype);
+ }
+
boolean isBaseRelation() {
debug("isBaseRelation: checking ttype=" + lexer.ttype +
" (" + lexer.render() + ")");
+ return (isProxRelation() ||
+ lexer.ttype == lexer.TT_ANY ||
+ lexer.ttype == lexer.TT_ALL ||
+ lexer.ttype == lexer.TT_EXACT);
+ }
+
+ boolean isProxRelation() {
+ debug("isProxRelation: checking ttype=" + lexer.ttype +
+ " (" + lexer.render() + ")");
return (lexer.ttype == '<' ||
lexer.ttype == '>' ||
lexer.ttype == '=' ||
lexer.ttype == lexer.TT_LE ||
lexer.ttype == lexer.TT_GE ||
- lexer.ttype == lexer.TT_NE ||
- lexer.ttype == lexer.TT_ANY ||
- lexer.ttype == lexer.TT_ALL ||
- lexer.ttype == lexer.TT_EXACT);
+ lexer.ttype == lexer.TT_NE);
}
private void match(int token)
// </triple>
//
public static void main (String[] args) {
- if (args.length != 0) {
- System.err.println("Usage: " + args[0]);
+ boolean canonicalise = false;
+ Vector argv = new Vector();
+ for (int i = 0; i < args.length; i++) {
+ argv.add(args[i]);
+ }
+
+ if (argv.size() > 0 && argv.get(0).equals("-c")) {
+ canonicalise = true;
+ argv.remove(0);
+ }
+
+ if (argv.size() > 1) {
+ System.err.println("Usage: CQLParser [-c] [<CQL-query>]");
+ System.err.println("If unspecified, query is read from stdin");
System.exit(1);
}
- byte[] bytes = new byte[10000];
- try {
- // Read in the whole of standard input in one go
- int nbytes = System.in.read(bytes);
- } catch (java.io.IOException ex) {
- System.err.println("Can't read query: " + ex.getMessage());
- System.exit(2);
+ String cql;
+ if (argv.size() == 1) {
+ cql = (String) argv.get(0);
+ } else {
+ byte[] bytes = new byte[10000];
+ try {
+ // Read in the whole of standard input in one go
+ int nbytes = System.in.read(bytes);
+ } catch (java.io.IOException ex) {
+ System.err.println("Can't read query: " + ex.getMessage());
+ System.exit(2);
+ }
+ cql = new String(bytes);
}
- String cql = new String(bytes);
+
CQLParser parser = new CQLParser();
CQLNode root;
try {
root = parser.parse(cql);
debug("root='" + root + "'");
- System.out.println(root.toXCQL(0));
+ if (canonicalise) {
+ System.out.println(root.toCQL());
+ } else {
+ System.out.println(root.toXCQL(0));
+ }
} catch (CQLParseException ex) {
System.err.println("Syntax error: " + ex.getMessage());
System.exit(3);