diff --git a/README.txt b/README.txt index 0633bd5..4963694 100644 --- a/README.txt +++ b/README.txt @@ -80,7 +80,6 @@ Dependencies olap4j requires the following libraries at run time: * lib/olap4j.jar (or lib/olap4j-jdk14.jar if you are running JDK 1.4) -* lib/javacup.jar If you use the JDK 1.4 compatible jar, you will also need: diff --git a/build.xml b/build.xml index f6c109f..67aacb2 100644 --- a/build.xml +++ b/build.xml @@ -30,16 +30,22 @@ - + + + +${src.dir}/org/olap4j/mdx/parser/impl/TokenMgrError.java, +${src.dir}/org/olap4j/driver/xmla/XmlaOlap4jDriverVersion.java" /> @@ -84,6 +90,14 @@ ${src.dir}/org/olap4j/mdx/parser/impl/TokenMgrError.java" /> + + + + + + + + - - - - - + - - - diff --git a/checkFile.sh b/checkFile.sh index 8e256f4..0741b70 100755 --- a/checkFile.sh +++ b/checkFile.sh @@ -135,8 +135,8 @@ doCheck() { fi ;; - # Only validate .java and .cup files at present. - *.java|*.cup|*.h|*.cpp) + # Only validate .java and .jj files at present. + *.java|*.jj|*.h|*.cpp) ;; *) diff --git a/ivy-jdk14.xml b/ivy-jdk14.xml index e9f6d1c..a850d93 100644 --- a/ivy-jdk14.xml +++ b/ivy-jdk14.xml @@ -47,7 +47,7 @@ - + diff --git a/ivy.xml b/ivy.xml index 09e5853..0028a07 100644 --- a/ivy.xml +++ b/ivy.xml @@ -49,9 +49,6 @@ - - diff --git a/src/org/olap4j/mdx/parser/impl/DefaultMdxParser.cup b/src/org/olap4j/mdx/parser/impl/DefaultMdxParser.cup deleted file mode 100644 index bed1daf..0000000 --- a/src/org/olap4j/mdx/parser/impl/DefaultMdxParser.cup +++ /dev/null @@ -1,1699 +0,0 @@ -/* -// $Id$ -// -// Licensed to Julian Hyde under one or more contributor license -// agreements. See the NOTICE file distributed with this work for -// additional information regarding copyright ownership. -// -// Julian Hyde licenses this file to you under the Apache License, -// Version 2.0 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at: -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ - -import java_cup.runtime.*; - -import java.math.BigDecimal; -import java.util.*; - -import org.olap4j.Axis; -import org.olap4j.mdx.*; -import org.olap4j.mdx.parser.MdxParseException; - -// Preliminaries to set up and use the scanner. -parser code {: - // Generated from $Id$ - Scanner scanner; - private String queryString; - private JavaCupMdxParserImpl.FunTable funTable; - - /** - * Recursively parses an expression. - */ - ParseTreeNode recursivelyParseExp(String s) - { - return new DefaultMdxParser().parseExpression( - s, false, funTable); - } - - /** - * Parses a string to create a {@link SelectNode}. - */ - SelectNode parseSelect( - String queryString, - boolean debug, - JavaCupMdxParserImpl.FunTable funTable) - { - Symbol parse_tree = null; - this.scanner = new StringScanner(queryString, debug); - this.queryString = queryString; - this.funTable = funTable; - try { - if (debug) { - parse_tree = debug_parse(); - } else { - parse_tree = parse(); - } - return (SelectNode) parse_tree.value; - } catch (Exception e) { - throw new RuntimeException( - "Error while parsing MDX statement '" + queryString + "'", - e); - } finally { - this.scanner = null; - this.queryString = null; - this.funTable = null; - } - } - - /** - * Parses a string to create an expression. - */ - ParseTreeNode parseExpression( - String queryString, - boolean debug, - JavaCupMdxParserImpl.FunTable funTable) - { - Symbol parse_tree = null; - this.scanner = new PrefixScanner( - debug, - new StringScanner(queryString, debug), - new int[] {DefaultMdxParserSym._VALUE_EXPRESSION}); - this.queryString = queryString; - this.funTable = funTable; - try { - if (debug) { - parse_tree = debug_parse(); - } else { - parse_tree = parse(); - } - return (ParseTreeNode) parse_tree.value; - } catch (Exception e) { - throw new RuntimeException( - "Syntax error while parsing MDX expression '" + queryString - + "'", - e); - } finally { - this.scanner = null; - this.queryString = null; - this.funTable = null; - } - } - - /** - * Scanner which returns a list of pre-programmed tokens, then switches - * to a parent scanner. - */ - private static class PrefixScanner extends Scanner { - private final Scanner parent; - private final int tokens[]; - private int i; - - PrefixScanner(boolean debug, Scanner parent, int[] tokens) { - super(debug); - this.parent = parent; - this.tokens = tokens; - } - - public void init() throws java.io.IOException { - i = 0; - parent.init(); - } - - public Symbol next_token() throws java.io.IOException { - if (i < tokens.length) { - return new Symbol(tokens[i++], 0, 0, null); - } - return parent.next_token(); - } - - ParseRegion createRegion(int left, int right) { - return parent.createRegion(left, right); - } - } - - /** - * Creates a {@link SelectNode} object. - * Override this function to make your kind of query. - */ - protected SelectNode newSelect( - ParseRegion region, - List withList, - List axisList, - ParseTreeNode from, - ParseTreeNode filter, - List cellProps) - { - final AxisNode filterAxis = - filter == null - ? null - : new AxisNode( - filter.getRegion(), false, Axis.FILTER, - Collections.emptyList(), filter); - // sort axes by ordinal - Collections.sort( - axisList, - new Comparator() { - public int compare(AxisNode o1, AxisNode o2) { - return o1.getAxis().axisOrdinal() - - o2.getAxis().axisOrdinal(); - } - }); - return new SelectNode( - region, withList, axisList, from, filterAxis, cellProps); - } - - // Override lr_parser methods for NLS. With this error handling scheme, - // all errors are fatal. - public void report_fatal_error( - String message, - Object info) - throws java.lang.Exception - { - done_parsing(); - try { - report_error(message, info); - } catch (Throwable e) { - throw new RuntimeException( - "MDX parser cannot recover from previous error(s)", - e); - } - } - - // override lr_parser method - public void report_error(String message, Object info) - { - // "Error: %1" - throw new RuntimeException("Error: " + message); - } - - // override lr_parser method - public void syntax_error(Symbol cur_token) - { - String s = cur_token.value.toString(); - if (cur_token.left != -1) { - final ParseRegion region = - scanner.createRegion(cur_token.left, cur_token.right); - throw new MdxParseException( - region, - "Syntax error at " + region - + ", token '" + s + "'"); - } else { - throw new RuntimeException( - "Syntax error at token '" + s + "'"); - } - } - - public void unrecovered_syntax_error(Symbol cur_token) - throws java.lang.Exception - { - report_fatal_error("Couldn't repair and continue parse", cur_token); - } - - /** - * Returns whether the given identifier can possibly the name of an operator - * with property syntax. - * - *

For example, isFunCall("ORDINAL") - * returns true because there is a "<Level>.Ordinal" property.

- */ - protected boolean isFunCall(String s) { - return funTable.isProperty(s); - } -:}; - -action code {: - ParseRegion createRegion(final int... ordinals) { - assert ordinals.length % 2 == 0; - return ParseRegion.sum( - new Iterable() { - public Iterator iterator() { - return new Iterator() { - int i = 0; - public boolean hasNext() { - return i < ordinals.length; - } - - public ParseRegion next() { - return parser.scanner.createRegion( - ordinals[i++], ordinals[i++]); - } - - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } - } - ); - } - - - static List emptyList(List list) { - if (list == null) { - return Collections.emptyList(); - } - return list; - } -:}; - -init with {: - scanner.init(); -:}; - -scan with {: - return scanner.next_token(); -:}; - -// Terminals (tokens returned by the scanner). -// a. Keywords. -terminal - AND, - AS, - AXIS, - CASE, - CAST, - CELL, - CHAPTERS, - COLUMNS, - DIMENSION, - ELSE, - EMPTY, - END, - FROM, - IN, - IS, - MATCHES, - MEMBER, - NON, - NOT, - NULL, - ON, - OR, - PAGES, - PROPERTIES, - ROWS, - SECTIONS, - SELECT, - SET, - THEN, - WHEN, - WHERE, - XOR, - WITH, - _VALUE_EXPRESSION; - -// b. Symbols -terminal - ASTERISK, // * - COLON, // : - COMMA, // , - CONCAT, // || - DOT, // . - EQ, // = - GE, // >= - GT, // > - LBRACE, // { - LE, // <= - LPAREN, // ( - LT, // < - MINUS, // - - NE, // <> - PLUS, // + - RBRACE, // } - RPAREN, // ) - SOLIDUS; // / - -// c. Typed terminals -terminal BigDecimal NUMBER; -terminal String ID; -terminal String QUOTED_ID; -terminal String AMP_QUOTED_ID; -terminal String AMP_UNQUOTED_ID; -terminal String STRING; -terminal String FORMULA_STRING; -terminal String UNKNOWN; // a token the lexer doesn't like! - -// Non terminals -non terminal AxisNode - axis_specification; -non terminal ParseTreeNode - case_expression, - cube_specification_or_select_statement, - else_clause_opt, - expression, - expression_or_empty, - factor, - filter_specification, - term, - term2, - term3, - term4, - term5, - value_expression, - value_expression_opt, - value_expression_primary, - where_clause_opt; -non terminal ParseTreeNode - select_statement, - single_formula_specification, - statement; -non terminal IdentifierNode - cell_property, - compound_id, - cube_name, - cube_specification, - member_name, - set_name; -non terminal Axis.Standard - axis_name; -non terminal String - comp_op, - keyword; -non terminal IdentifierSegment - identifier, - key_identifier, - quoted_identifier, - unquoted_identifier, - amp_identifier, - amp_quoted_identifier, - amp_unquoted_identifier; -non terminal List - amp_identifier_list; -non terminal WithMemberNode - member_specification; -non terminal WithSetNode - set_specification; -non terminal PropertyValueNode - member_property_definition; -non terminal - cell_opt, - dimension_opt, - property, - unsigned_integer; - -non terminal Boolean - non_empty_opt; - -non terminal List - axis_specification_list, - axis_specification_list_opt, - cell_props, - cell_props_opt, - comma_member_property_def_list_opt, - dim_props, - dim_props_opt, - exp_list, - exp_list_opt, - formula_specification, - member_property_def_list, - property_list, - cell_property_list, - when_list, - with_formula_specification_opt; - -non terminal ParseTreeNode[] - when_clause; - -non terminal BigDecimal - axis_number; - -// Start symbol -start with statement; - -// ---------------------------------------------------------------------------- -// Elements -// -// -// ::= | - -quoted_identifier ::= - QUOTED_ID:i {: - ParseRegion region = createRegion(ileft, iright); - RESULT = new NameSegment(region, i, Quoting.QUOTED); - :} - ; - -unquoted_identifier ::= - ID:i {: - ParseRegion region = createRegion(ileft, iright); - RESULT = new NameSegment(region, i, Quoting.UNQUOTED); - :} - | keyword:i {: - ParseRegion region = createRegion(ileft, iright); - RESULT = new NameSegment(region, i, Quoting.UNQUOTED); - :} - ; - -// for example '&foo&[1]&bar' in '[x].&foo&[1]&bar.[y]' -key_identifier ::= - amp_identifier_list:list {: - RESULT = new KeySegment(list); - :} - ; - -amp_identifier_list ::= - amp_identifier:i {: - RESULT = new ArrayList(); - RESULT.add(i); - :} - | - amp_identifier_list:list amp_identifier:i {: - list.add(i); - RESULT = list; - :} - ; - -amp_identifier ::= - amp_quoted_identifier - | - amp_unquoted_identifier - ; - -amp_quoted_identifier ::= - AMP_QUOTED_ID:i {: - ParseRegion region = createRegion(ileft, iright); - RESULT = new NameSegment(region, i, Quoting.QUOTED); - :} - ; - -amp_unquoted_identifier ::= - AMP_UNQUOTED_ID:i {: - ParseRegion region = createRegion(ileft, iright); - RESULT = new NameSegment(region, i, Quoting.UNQUOTED); - :} - ; - -identifier ::= - unquoted_identifier - | quoted_identifier - | key_identifier - ; - -// a keyword (unlike a reserved word) can be converted back into an -// identifier in some contexts -keyword ::= - DIMENSION {: - RESULT = "Dimension"; - :} - | PROPERTIES {: - RESULT = "Properties"; - :} - ; - -compound_id ::= - identifier:i {: - RESULT = new IdentifierNode(i); - :} - | compound_id:hd DOT identifier:tl {: - RESULT = hd.append(tl); - :} - ; - -// -// ::= [{ | -// | }...] -// -// ::= -// { | } -// [{ | }...] -// -// -// ::= -// -// ::= -// -// ::= end_delimiter> -// -// ::= !! -// -// ::= [ [ [ ] ] [].] -// -cube_name ::= compound_id ; -// -// ::= -// -// ::= -// -// ::= -// -// ::= [.] -// | [[.]< dimension_name>.] -// jhyde: Need more lookahead for this to work... just use id in place of -// dim_hier. -// dim_hier ::= id; -// -// ::= -// | .DIMENSION -// | .DIMENSION -// | .DIMENSION -// -// ::= -// | < member>.HIERARCHY -// | .HIERARCHY -// -// ::= [.]< identifier> -// | .LEVELS() -// | .LEVEL -// -// Note: The first production is for the case when named levels are -// supported. The second production is for the case when named levels are not -// supported. -// -// -// ::= [.] -// | . -// | . -// | -// -// Note: The . recognizes the fact that members may -// sometimes need to be qualified by their parent names. For example, -// "Portland" is a city in Oregon, and also in Maine. So a reference to -// Portland will be either Oregon.Portland or Maine.Portland. -// -// -// ::= | -// -// ::= CATALOG_NAME -// | SCHEMA_NAME -// | CUBE_NAME -// | DIMENSION_UNIQUE_NAME -// | HIERARCHY_UNIQUE_NAME -// | LEVEL_UNIQUE_NAME -// | LEVEL_NUMBER -// | MEMBER_UNIQUE_NAME -// | MEMBER_NAME -// | MEMBER_TYPE -// | MEMBER_GUID -// | MEMBER_CAPTION -// | MEMBER_ORDINAL -// | CHILDREN_CARDINALITY -// | PARENT_LEVEL -// | PARENT_UNIQUE_NAME -// | PARENT_COUNT -// | DESCRIPTION -// -// ::= . -// | . -// | . -// -// Note: The three productions recognize the fact that a property can apply to -// all the members of a dimension, or all the members of a level, or just to a -// member. -// -// -// ::= -// | ( [, ...]) -// | -// -// Note: Each member must be from a different dimension or from a different -// hierarchy. -// -// -// ::= : -// -// Note: Each member must be from the same hierarchy and the same level. -// -// -// | -// | [| [, |...]] -// -// Note: Duplicates (if any) are always retained when specifying sets in this -// fashion. -// -// -// | () -// -// ::= { -// -// ::= } -// -// ::= [ -// -// ::= ] -// -// ::= _ -// -// ::= a | b | c | ...| z | A | B | C | ... | Z -// -// ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 -// -// Leveling Rules for Elements -// -// The ability to qualify a cube name by one or more of , -// , or is optional. Consumers can check the value -// of the property MDPROP_MDX_OBJQUALIFICATION to see whether a provider -// supports cube qualification. -// -// -// The ability to qualify a dimension name by a cube name is -// optional. Consumers can check the value of the property -// MDPROP_MDX_OBJQUALIFICATION to see whether a provider supports dimension -// qualification. -// -// -// The ability to qualify a hierarchy name by a dimension name or by cube name -// and dimension name is optional. Consumers can check the value of the -// property MDPROP_MDX_OBJQUALIFICATION to see whether a provider supports -// hierarchy qualification. -// -// -// The provider need support only one of the two productions for . If it -// supports -// -// ::= [.] -// -// then the ability to qualify by is optional. -// -// Consumers can check the value of the property MDPROP_NAMED_LEVELS to see if -// the provider supports named levels. If it does, then the consumer can check -// MDPROP_MDX_OBJQUALIFICATION to see whether named levels can be qualified by -// . -// -// The ability to qualify a member by a level, a member, or is -// optional. Consumers can check the value of the property -// MDPROP_MDX_OBJQUALIFICATION to see whether a provider supports member -// qualification. -// -// Note: Several leveling rules above make it optional to qualify -// multidimensional schema object names. However, this does not imply that the -// ability to generate unique names for members, levels, dimensions, and -// hierarchies is optional. Providers are required to furnish unique names in -// the schema rowsets for these objects. If providers generate unique names by -// means other than qualification, then the ability to qualify is optional. For -// more information, see 'Provider Implementation Considerations for Unique -// Names' in Chapter 2. -// -// - -// ---------------------------------------------------------------------------- -// -// -// Expressions -// -// Note: The syntax of is generally the same as SQL-92, -// subclause 6.11, . Differences are: -// -// [.VALUE], [.VALUE], and are new -// values for . -// -// -// There are new values for , mainly for statistical -// analysis. -// -// -// The BNF for , , and -// have been shortened by eliminating several -// intermediate nonterminals. -// -// ::= -// | -// -// ::= -// | { | } -value_expression ::= - term5 - | value_expression:x OR term5:y {: - ParseRegion region = createRegion(xleft, yright); - RESULT = new CallNode(region, "OR", Syntax.Infix, x, y); - :} - | value_expression:x XOR term5:y {: - ParseRegion region = createRegion(xleft, yright); - RESULT = new CallNode(region, "XOR", Syntax.Infix, x, y); - :} - ; - -term5 ::= - term4 - | term5:x AND term4:y {: - ParseRegion region = createRegion(xleft, yright); - RESULT = new CallNode(region, "AND", Syntax.Infix, x, y); - :} - ; - -term4 ::= - term3 - | NOT:not term4:p {: - ParseRegion region = createRegion(notleft, pright); - RESULT = new CallNode(region, "NOT", Syntax.Prefix, p); - :} - ; - -term3 ::= - term2 - | term3:x comp_op:op term2:y {: // e.g. "1 < 5" - ParseRegion region = createRegion(xleft, yright); - RESULT = new CallNode(region, op, Syntax.Infix, x, y); - :} - | - // We expect a shift-reduce conflict here, because NULL is a literal and - // so is a valid argument to the IS operator. JavaCUP resolves the - // conflict by shifting, which is what we want. Compile with expect=61 - // to ignore the conflicts and continue. - term3:x IS NULL:n {: - ParseRegion region = createRegion(xleft, nright); - RESULT = new CallNode(region, "IS NULL", Syntax.Postfix, x); - :} - | term3:x IS term2:y {: - // e.g. "x IS y"; but "x IS NULL" is handled elsewhere - ParseRegion region = createRegion(xleft, yright); - RESULT = new CallNode(region, "IS", Syntax.Infix, x, y); - :} - | term3:x IS EMPTY:empty {: - ParseRegion region = createRegion(xleft, emptyright); - RESULT = new CallNode(region, "IS EMPTY", Syntax.Postfix, x); - :} - | term3:x MATCHES term2:y {: - ParseRegion region = createRegion(xleft, yright); - RESULT = new CallNode(region, "MATCHES", Syntax.Infix, x, y); - :} - | term3:x NOT MATCHES term2:y {: - ParseRegion region = createRegion(xleft, yright); - RESULT = - new CallNode( - region, "NOT", Syntax.Prefix, - new CallNode(region, "MATCHES", Syntax.Infix, x, y)); - :} - | term3:x IN term2:y {: - ParseRegion region = createRegion(xleft, yright); - RESULT = new CallNode(region, "IN", Syntax.Infix, x, y); - :} - | term3:x NOT IN term2:y {: - ParseRegion region = createRegion(xleft, yright); - RESULT = - new CallNode( - region, "NOT", Syntax.Prefix, - new CallNode(region, "IN", Syntax.Infix, x, y)); - :} - ; - -term2 ::= - term - | term2:x PLUS term:y {: - ParseRegion region = createRegion(xleft, yright); - RESULT = new CallNode(region, "+", Syntax.Infix, x, y); - :} - | term2:x MINUS term:y {: - ParseRegion region = createRegion(xleft, yright); - RESULT = new CallNode(region, "-", Syntax.Infix, x, y); - :} - | term2:x CONCAT term:y {: - ParseRegion region = createRegion(xleft, yright); - RESULT = new CallNode(region, "||", Syntax.Infix, x, y); - :} - ; - -// -// ::= | { | } -term ::= - factor - | term:x ASTERISK factor:y {: - ParseRegion region = createRegion(xleft, yright); - RESULT = new CallNode(region, "*", Syntax.Infix, x, y); - :} - | term:x SOLIDUS factor:y {: - ParseRegion region = createRegion(xleft, yright); - RESULT = new CallNode(region, "/", Syntax.Infix, x, y); - :} - ; -// -// ::= [] -// -factor ::= - value_expression_primary - | PLUS value_expression_primary:p {: - RESULT = p; - :} - | MINUS:minus value_expression_primary:p {: - ParseRegion region = createRegion(minusleft, pright); - RESULT = new CallNode(region, "-", Syntax.Prefix, p); - :} - ; - -// ::= + | - -// -// ::= + -// -// ::= - -// -// ::= * -// -// ::= / -// -// ::= -// | -// -// Note: The data type of in the above production -// shall be numeric. -// -// -// ::= -// | () -// | -// | [.][.VALUE] -// | [.VALUE] -// | -value_expression_primary ::= - STRING:s {: - ParseRegion region = createRegion(sleft, sright); - RESULT = LiteralNode.createString(region, s); - :} - | NUMBER:d {: - ParseRegion region = createRegion(dleft, dright); - RESULT = LiteralNode.createNumeric(region, d, false); - :} - | identifier:i {: - RESULT = new IdentifierNode(i); - :} - | value_expression_primary:i DOT unquoted_identifier:j {: - if (i instanceof IdentifierNode && !parser.isFunCall(j.getName())) { - RESULT = ((IdentifierNode) i).append(j); - } else { - ParseRegion region = createRegion(ileft, jright); - RESULT = new CallNode(region, j.getName(), Syntax.Property, i); - } - :} - | value_expression_primary:i DOT quoted_identifier:j {: - if (i instanceof IdentifierNode) { - RESULT = ((IdentifierNode) i).append(j); - } else { - ParseRegion region = createRegion(ileft, jright); - RESULT = new CallNode( - region, j.getName(), Syntax.QuotedProperty, i); - } - :} - | value_expression_primary:i DOT key_identifier:j {: - if (i instanceof IdentifierNode) { - RESULT = ((IdentifierNode) i).append(j); - } else { - ParseRegion region = createRegion(ileft, jright); - RESULT = new CallNode( - region, j.getName(), Syntax.AmpersandQuotedProperty, i); - } - :} - | value_expression_primary:i DOT identifier:j LPAREN exp_list_opt:lis - RPAREN:rparen {: - lis.add(0, i); - ParseRegion region = createRegion(ileft, rparenright); - RESULT = new CallNode(region, j.getName(), Syntax.Method, lis); - :} - | identifier:i LPAREN exp_list_opt:lis RPAREN:rparen {: - ParseRegion region = createRegion(ileft, rparenright); - RESULT = new CallNode(region, i.getName(), Syntax.Function, lis); - :} - | CAST:cast LPAREN expression:e AS identifier:t RPAREN:rparen {: - LiteralNode symbol = - LiteralNode.createSymbol(t.getRegion(), t.getName()); - ParseRegion region = createRegion(castleft, rparenright); - RESULT = new CallNode(region, "CAST", Syntax.Cast, e, symbol); - :} - | LPAREN:lparen exp_list:lis RPAREN:rparen {: - // Whereas ([Sales],[Time]) and () are tuples, ([Sales]) and (5) - // are just expressions. - ParseRegion region = createRegion(lparenleft, rparenright); - RESULT = new CallNode(region, "()", Syntax.Parentheses, lis); - :} - | LBRACE:lbrace exp_list_opt:lis RBRACE:rbrace {: - // set built from sets/tuples - ParseRegion region = createRegion(lbraceleft, rbraceright); - RESULT = new CallNode(region, "{}", Syntax.Braces, lis); - :} - | NULL:n {: - ParseRegion region = createRegion(nleft, nright); - RESULT = LiteralNode.createNull(region); - :} - | case_expression - ; - -case_expression ::= - CASE:kase value_expression_opt:x - when_list:y - else_clause_opt:z - END {: - List v = new ArrayList(); - if (x != null) { - v.add(x); - } - for (int i = 0; i < y.size(); i++) { - ParseTreeNode[] exps = (ParseTreeNode[]) y.get(i); - assert exps.length == 2; - v.add(exps[0]); - v.add(exps[1]); - } - if (z != null) { - v.add(z); - } - ParseRegion region = createRegion(kaseleft, zright); - if (x == null) { - RESULT = new CallNode(region, "_CaseTest", Syntax.Case, v); - } else { - RESULT = new CallNode(region, "_CaseMatch", Syntax.Case, v); - } - :} - ; - -value_expression_opt ::= - /* empty */ - | value_expression - ; - -when_list ::= - /* empty */ {: - RESULT = new ArrayList(); - :} - | when_list:x when_clause:y {: - RESULT = x; - x.add(y); - :} - ; - -when_clause ::= - WHEN value_expression:x THEN value_expression:y {: - RESULT = new ParseTreeNode[] {x, y}; - :} - ; - -else_clause_opt ::= - /* empty */ - | ELSE value_expression:x {: - RESULT = x; - :} - ; - -// -// ::= | -// -// ::= iif(, , ) -// -// ::= -// -// ::= -// -// ::= | | -// -// ::= CASE -// ... -// [] -// END -// -// ::= CASE -// ... -// [] -// END -// -// ::= WHEN THEN -// -// ::= WHEN THEN -// -// ::= ELSE -// -// ::= -// -// ::= -// -// ::= -// -// ::= COALESCEEMPTY ( -// {, }...) -// -// ::= [] -// -// ::= -// | -// -// ::= [.] -// | . -// | . -// -// ::= {}... -// -// ::= E -// -// ::= < exact_numeric_literal> -// -// ::= [] -// -// ::= -// | -// -// -// -// Note: The data type of in the above production -// shall be a character string. -// -// -// ::= [...] -// -// -// ::= | -// -// ::= !! -// -// -// ::= -// -// ::= ' -// -// ::= || -// -// Leveling Rules for Expressions -// -// The following productions for are optional: -// -// The ability to qualify [.VALUE] by in a value expression -// primary is optional. Consumers can check the value of the property -// MDPROP_MDX_OUTERREFERENCE to see whether a provider supports this feature. -// -// -// [.VALUE]. Consumers can check the value of the property -// MDPROP_MDX_QUERYBYPROPERTY to see whether a provider supports this feature. -// -// -// , . Consumers can check the value of the -// property MDPROP_MDX_CASESUPPORT to see whether a provider supports this -// feature. -// - -// ---------------------------------------------------------------------------- -// Search Condition -// -// ::= -// | {OR | XOR} -// -// ::= | AND -// -// ::= [NOT] -// -// ::= -// | ISEMPTY() -// | IS EMPTY -// | () -// ::= -// | -// | -// | -// | -// | -// "IS" is not a comp_op because of conflict with " IS EMPTY" -comp_op ::= - EQ {: - RESULT = "="; - :} - | NE {: - RESULT = "<>"; - :} - | LT {: - RESULT = "<"; - :} - | GT {: - RESULT = ">"; - :} - | LE {: - RESULT = "<="; - :} - | GE {: - RESULT = ">="; - :} - ; -// -// ::= = -// -// ::= <> -// -// ::= > -// -// ::= < -// -// ::= >= -// -// ::= <= -// -// Leveling Rules for Search Condition -// -// If in a value is a string value -// expression, then support for values other than -// and is optional. Consumers can check the value of the -// property MDPROP_MDX_STRING_COMPOP to see whether a provider supports this -// feature. - -// ---------------------------------------------------------------------------- -// Set Value Expression -// -// ::= -// -// Note: denotes an integer argument. If an arbitrary -// appears here, then it is truncated to the nearest -// integer. -// -// -// ::= -// -// ::= .MEMBERS -// | .MEMBERS -// | .CHILDREN -// | BOTTOMCOUNT(, -// [, ]) -// | BOTTOMPERCENT(, , -// ) -// | BOTTOMSUM(, , -// ) -// | CROSSJOIN(, ) -// | DESCENDANTS(, [,]) -// -// Note: In the absence of explicit specification, SELF is the -// default. -// -// | DISTINCT() -// | DRILLDOWNLEVEL( [, ]]) -// | DRILLDOWNLEVELBOTTOM(, -// [,[] [, ]]) -// | DRILLDOWNLEVELTOP(, [, [] -// [, ]]) -// | DRILLDOWNMEMBER(, [, RECURSIVE]) -// | DRILLDOWNMEMBERBOTTOM(, , -// [, ][, RECURSIVE]]) -// | DRILLDOWNMEMBERTOP(, , -// [, [][, RECURSIVE]]) -// | DRILLUPLEVEL([, ]]) -// | DRILLUPMEMBER(, ) -// | EXCEPT(, [, ALL]) -// | EXTRACT(, [, ...]) -// | FILTER(, ) -// | GENERATE(, [, ALL]) -// | HIERARCHIZE() -// | INTERSECT(, [, ALL]) -// | LASTPERIODS( [, ]) -// | MTD([]) -// | ORDER(, -// [, ASC | DESC | BASC | BDESC]) -// -// Note: In the absence of explicit specification, ASC is the default. -// -// -// | PERIODSTODATE([[, ]]) -// | QTD([]) -// | TOGGLEDRILLSTATE(, [, RECURSIVE]) -// -// Note: With the exception of CROSSJOIN, all set functions that take more than -// one argument require that the two set arguments have tuples of the -// same dimensionality. -// -// -// | TOPCOUNT(, -// [, ]) -// | TOPPERCENT(, , -// ) -// | TOPSUM(, , -// ) -// | UNION(, [, ALL]) -// | WTD([]) -// | YTD() -// -// ::= SELF -// | AFTER -// | BEFORE -// | BEFORE_AND_AFTER -// | SELF_AND_AFTER -// | SELF_AND_BEFORE -// | SELF_BEFORE_AFTER -// - -// ---------------------------------------------------------------------------- -// Member Value Expression -// -// ::= .{PARENT | FIRSTCHILD | LASTCHILD -// | PREVMEMBER | NEXTMEMBER} -// | .LEAD() -// | .LAG() -// -// Note: LAG() is the same as LEAD(-) -// -// -// | .{FIRSTSIBLING | LASTSIBLING} -// | .[CURRENTMEMBER] -// | .DEFAULTMEMBER -// | .DEFAULTMEMBER -// | ANCESTOR(, ) -// | CLOSINGPERIOD([[, ]) -// | COUSIN(, ) -// | OPENINGPERIOD([[, ]) -// | PARALLELPERIOD([[, -// [, ]]]) -expression ::= - expression:x COLON value_expression:y {: // range yields set - ParseRegion region = createRegion(xleft, yright); - RESULT = new CallNode(region, ":", Syntax.Infix, x, y); - :} - | value_expression - ; -expression_or_empty ::= - expression - | /* empty */ {: - final ParseRegion region = createRegion(0, 0); - RESULT = new CallNode(region, "", Syntax.Empty); - :} - ; -exp_list_opt ::= - /* empty */ {: - RESULT = new LinkedList(); - :} - | exp_list - ; -exp_list ::= - expression:e {: - RESULT = new LinkedList(); - RESULT.add(e); - :} - | expression_or_empty:e COMMA exp_list:list {: - list.add(0, e); RESULT = list; - :} - ; -// -// Leveling Rules for Member Value Expression -// -// The following member functions are optional: COUSIN, PARALLELPERIOD, -// OPENINGPERIOD, CLOSINGPERIOD. Consumers can check the value of the property -// MDPROP_MDX_MEMBER_FUNCTIONS to see whether a provider supports this feature. -// - -// -// * Tuple Value Expression -// -// ::= .CURRENTMEMBER -// | [.ITEM]( -// [, ...] | ) -// - -// -// * Numeric Value Function -// -// ::= -// AGGREGATE( [, ]) -// | AVG([, ]) -// | CORRELATION( [, ] -// [, ]) -// | COVARIANCE([, -// [, ]) -// | COUNT([, INCLUDEEMPTY]) -// | LINREGINTERCEPT([, -// -// -// Leveling Rules for Numeric Value Function -// -// The following numeric functions are optional: MEDIAN, VAR, STDEV, RANK, -// AGGREGATE, COVARIANCE, CORRELATION, LINREGSLOPE, LINREGVARIANCE, LINREGR2, -// LINREGPOINT. Consumers can check the value of the property -// MDPROP_MDX_NUMERIC_FUNCTIONS to see whether a provider supports this -// feature. -// - -// ---------------------------------------------------------------------------- -// MDX Statement -// -// ::= -// | -// | -// - -// ::= [WITH ] -// SELECT [ -// [, ...]] -// FROM [] -// WHERE [] -// [] -// | -// [WITH ] -// SELECT [ -// [, ...]] -// FROM ( select_statement ) -// WHERE [] -// [] -// jhyde: The above is wrong... you can omit 'WHERE'. - -statement ::= - select_statement - | _VALUE_EXPRESSION value_expression:e {: - RESULT = (ParseTreeNode) e; - :} - ; - -cube_specification_or_select_statement ::= - cube_specification - | LPAREN select_statement:s RPAREN {: - RESULT = s; - :} - ; - -select_statement ::= - with_formula_specification_opt:f - SELECT:select axis_specification_list_opt:a - FROM cube_specification_or_select_statement:c - where_clause_opt:w - cell_props_opt:cp {: - ParseRegion region = createRegion(selectleft, selectright); - RESULT = parser.newSelect(region, f, a, c, w, cp); - :}; - -with_formula_specification_opt ::= - /* empty */ {: - RESULT = new LinkedList(); - :} - | WITH formula_specification:f {: - RESULT = f; - :} - ; -axis_specification_list_opt ::= - /* empty */ {: - RESULT = new LinkedList(); - :} - | axis_specification_list - ; -axis_specification_list ::= - axis_specification:i {: - RESULT = new LinkedList(); - RESULT.add(i); - :} - | axis_specification:e COMMA axis_specification_list:list {: - list.add(0, e); - RESULT = list; - :} - ; -where_clause_opt ::= - /* empty */ - | WHERE filter_specification:s {: - RESULT = s; - :}; -cell_props_opt ::= - /* empty */ {: - RESULT = new LinkedList(); - :} - | cell_props; - -// -// ::= -// [...] -// -formula_specification ::= - single_formula_specification:e {: - RESULT = new LinkedList(); - RESULT.add(e); - :} - | single_formula_specification:hd formula_specification:tl {: - tl.add(0, hd); - RESULT = tl; - :} - ; - -// ::= -// | -// -single_formula_specification ::= - member_specification - | set_specification - ; -// -// ::= MEMBER AS -// [, ] -// [, ...] -member_specification ::= - MEMBER:member member_name:m AS FORMULA_STRING:s - comma_member_property_def_list_opt:l {: - ParseTreeNode e = parser.recursivelyParseExp(s); - ParseRegion region = createRegion(memberleft, sright); - RESULT = new WithMemberNode(region, m, e, l); - :} - | MEMBER:member member_name:m AS value_expression:e - comma_member_property_def_list_opt:l {: - ParseRegion region = createRegion(memberleft, eright); - RESULT = new WithMemberNode(region, m, e, l); - :} - ; -comma_member_property_def_list_opt ::= - /* empty */ {: - RESULT = new LinkedList(); - :} - | COMMA member_property_def_list:l {: - RESULT = l; - :} - ; -member_property_def_list ::= - member_property_definition:m {: - RESULT = new LinkedList(); - RESULT.add(m); - :} - | member_property_definition:hd COMMA member_property_def_list:tl {: - RESULT = tl; - RESULT.add(0, hd); - :} - ; -// -// ::= . -// | .. -// -member_name ::= compound_id; -// -// Note: -// -// The identifier defines a new member. The qualification member has enough -// information to specify the dimension, and the level in the dimension that -// this new member should be on. -// -// -// If is part of a member specification that appears in a create -// formula statement or is part of a drop formula statement, then it must be -// qualified by a cube name, as in the second production above. -// -// ::= SOLVE_ORDER = -// -// ::= = -member_property_definition ::= - identifier:id EQ value_expression:e {: - ParseRegion region = createRegion(idleft, eright); - RESULT = new PropertyValueNode(region, id.getName(), e); - :} - ; -// -// Note: Since the property definition appears in the context of a member -// definition, there is enough information to associate the identifier (which -// is the property name) in the above production with a member. -// -// -// ::= SET AS -set_specification ::= - SET:set set_name:n AS FORMULA_STRING:s {: - ParseTreeNode e = parser.recursivelyParseExp(s); - ParseRegion region = createRegion(setleft, sright); - RESULT = new WithSetNode(region, n, e); - :} - | SET:set set_name:n AS expression:e {: - ParseRegion region = createRegion(setleft, eright); - RESULT = new WithSetNode(region, n, e); - :} - ; -// -// ::= | . -set_name ::= compound_id ; -// -// Note: If is part of a set specification that appears in a create -// formula statement or is part of a drop formula statement, then it must be -// qualified by a cube name, as in the second production above. -// -// -// ::= [NON EMPTY] [] ON -axis_specification ::= - non_empty_opt:b expression:s dim_props_opt:dp ON axis_name:a {: - ParseRegion region = createRegion( - bleft, bright, sleft, sright, dpleft, dpright, aleft, aright); - RESULT = new AxisNode(region, b, a, emptyList(dp), s); - :} - | non_empty_opt:b expression:s dim_props_opt:dp ON axis_number:n {: - double d = n.doubleValue(); - int index = (int)d; - - // AxisOrdinal values go from -2 to 4 for standard axis, but higher - // ordinals are allowed. The negative values represent - // special cases, so are ignored. - if (index < 0 || index != d) { - throw new MdxParseException( - createRegion(nleft, nright), - "Invalid axis specification. The axis number must be a " - + "non-negative integer, but it was " + d + "."); - } - - Axis axis = Axis.Factory.forOrdinal(index); - ParseRegion region = createRegion( - bleft, bright, sleft, sright, dpleft, dpright, nleft, nright); - RESULT = new AxisNode(region, b, axis, emptyList(dp), s); - :} - ; - -non_empty_opt ::= - /* empty */ {: - RESULT = Boolean.FALSE; - :} - | NON EMPTY {: - RESULT = Boolean.TRUE; - :} - ; -dim_props_opt ::= - /* empty */ - | dim_props - ; -// -// ::= COLUMNS -// | ROWS -// | PAGES -// | CHAPTERS -// | SECTIONS -// | AXIS() -axis_name ::= - COLUMNS {: - RESULT = Axis.COLUMNS; - :} - | ROWS {: - RESULT = Axis.ROWS; - :} - | PAGES {: - RESULT = Axis.PAGES; - :} - | SECTIONS {: - RESULT = Axis.SECTIONS; - :} - | CHAPTERS {: - RESULT = Axis.CHAPTERS; - :} - ; - -axis_number ::= - NUMBER - | AXIS LPAREN NUMBER:n RPAREN {: - RESULT = n; - :} - ; -// -// ::= [DIMENSION] PROPERTIES [, ...] -dim_props ::= - dimension_opt PROPERTIES property_list:pl {: - RESULT = pl; - :} - ; -dimension_opt ::= - /* empty */ - | DIMENSION - ; -property_list ::= - property:p {: - RESULT = new LinkedList(); - RESULT.add(p); - :} - | property:p COMMA property_list:pl {: - pl.add(0, p); - RESULT = pl; - :} - ; -property ::= - compound_id - ; -// -// ::= [] [, ] -// jhyde: In this implementation, you must supply EXACTLY one cube. -cube_specification ::= - cube_name; - -// -// ::= { | } -filter_specification ::= - expression; -// -// ::= [CELL] PROPERTIES [, ...] -cell_props ::= - cell_opt PROPERTIES cell_property_list:p1 {: - RESULT = p1; - :} - ; -cell_opt ::= - /* empty */ - | CELL - ; -cell_property_list ::= - cell_property:p {: - RESULT = new LinkedList(); - RESULT.add(p); - :} - | cell_property:p COMMA cell_property_list:p1 {: - p1.add(0, p); - RESULT = p1; - :} -; - -// ::= FORMAT_STRING -// | FORMATTED_VALUE -// | FORE_COLOR -// | BACK_COLOR -// | FONT_NAME -// | FONT_SIZE -// | FONT_FLAGS -// | CELL_ORDINAL -// | VALUE -cell_property ::= compound_id; - -// -// ::= CREATE [] -// -// ::= -// | -// -// ::= DROP MEMBER -// [, ...] -// -// ::= DROP SET [, ...] -// -// := GLOBAL | SESSION -// -// Leveling Rules for MDX Statement -// -// Support for is optional. Consumers can check the -// value of the property MDPROP_MDX_FORMULAS to see whether a provider supports -// this feature. -// -// -// Support for in is optional. Consumers can check -// the value of the property MDPROP_MDX_SLICER to see whether a provider -// supports this feature. -// -// -// Support for more than one cube name in is -// optional. Support for having no cube name in the FROM clause (that is, the -// cube is implicitly defined by the axis and filter dimensions) is also -// optional. Consumers can check the value of the property MDPROP_MDX_JOINCUBES -// to see whether a provider supports this feature. -// -// -// The axis names CHAPTERS and SECTIONS are optional. Consumers can check the -// value of the property MDPROP_AXES to see whether a provider supports this -// feature. -// -// -// Support for > 2 in the AXIS() function is optional. Consumers -// can check the value of the property MDPROP_AXES to see whether a provider -// supports this feature. -// -// -// Support for is optional. Consumers can check the -// value of the property MDPROP_MDX_FORMULAS to see whether a provider supports -// this feature. -// -// -// Support for of GLOBAL is optional. Consumers can check the value of -// the property MDPROP_MDX_FORMULAS to see whether a provider supports this -// feature. -// - -// End DefaultMdxParser.cup diff --git a/src/org/olap4j/mdx/parser/impl/JavaCupMdxParserImpl.java b/src/org/olap4j/mdx/parser/impl/JavaCupMdxParserImpl.java deleted file mode 100644 index a575f26..0000000 --- a/src/org/olap4j/mdx/parser/impl/JavaCupMdxParserImpl.java +++ /dev/null @@ -1,67 +0,0 @@ -/* -// $Id$ -// -// Licensed to Julian Hyde under one or more contributor license -// agreements. See the NOTICE file distributed with this work for -// additional information regarding copyright ownership. -// -// Julian Hyde licenses this file to you under the Apache License, -// Version 2.0 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at: -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ -package org.olap4j.mdx.parser.impl; - -import org.olap4j.mdx.ParseTreeNode; -import org.olap4j.mdx.SelectNode; -import org.olap4j.mdx.parser.MdxParser; - -/** - * Default implementation of {@link org.olap4j.mdx.parser.MdxParser MDX Parser}. - * - * @author jhyde - * @version $Id$ - * @since Aug 22, 2006 - */ -public class JavaCupMdxParserImpl implements MdxParser { - private boolean debug = false; - private final FunTable funTable = new FunTable() { - public boolean isProperty(String s) { - return s.equals("CHILDREN"); - } - }; - - /** - * Creates a DefaultMdxParserImpl. - */ - public JavaCupMdxParserImpl() { - super(); - } - - public SelectNode parseSelect(String mdx) { - return new DefaultMdxParser().parseSelect( - mdx, - debug, - funTable); - } - - public ParseTreeNode parseExpression(String mdx) { - return new DefaultMdxParser().parseExpression( - mdx, - debug, - funTable); - } - - interface FunTable { - boolean isProperty(String s); - } -} - -// End DefaultMdxParserImpl.java diff --git a/src/org/olap4j/mdx/parser/impl/Scanner.java b/src/org/olap4j/mdx/parser/impl/Scanner.java deleted file mode 100644 index 0c65182..0000000 --- a/src/org/olap4j/mdx/parser/impl/Scanner.java +++ /dev/null @@ -1,889 +0,0 @@ -/* -// $Id$ -// -// Licensed to Julian Hyde under one or more contributor license -// agreements. See the NOTICE file distributed with this work for -// additional information regarding copyright ownership. -// -// Julian Hyde licenses this file to you under the Apache License, -// Version 2.0 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at: -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ -package org.olap4j.mdx.parser.impl; - -import org.olap4j.mdx.ParseRegion; -import org.olap4j.mdx.parser.MdxParseException; - -import java_cup.runtime.Symbol; - -import java.io.IOException; -import java.math.BigDecimal; -import java.util.*; - -/** - * Lexical analyzer for MDX. - * - *

NOTE: This class is not part of the public olap4j API. - * - * @version $Id$ - * @author jhyde - */ -class Scanner { - - /** single lookahead character */ - protected int nextChar; - /** next lookahead character */ - private int lookaheadChars[] = new int[16]; - private int firstLookaheadChar = 0; - private int lastLookaheadChar = 0; - private Map reservedWordIds; - private int iMaxResword; - private String[] reservedWords; - protected boolean debug; - - /** lines[x] is the start of the x'th line */ - private final List lines = new ArrayList(); - - /** number of times advance() has been called */ - private int iChar; - - /** end of previous token */ - private int iPrevChar; - - /** previous symbol returned */ - private int previousSymbol; - private boolean inFormula; - - /** - * Comment delimiters. Modify this list to support other comment styles. - */ - private static final String[][] commentDelim = { - {"//", null}, - {"--", null}, - {"/*", "*/"} - }; - - /** - * Whether to allow nested comments. - */ - private static final boolean allowNestedComments = true; - - /** - * The {@link java.math.BigDecimal} value 0. - * Note that BigDecimal.ZERO does not exist until JDK 1.5. - */ - private static final BigDecimal BigDecimalZero = BigDecimal.valueOf(0); - - /** - * Creates a Scanner. - * - * @param debug Whether to populate debug messages. - */ - Scanner(boolean debug) { - this.debug = debug; - } - - /** - * Returns the current nested comments state. - */ - public static boolean getNestedCommentsState() { - return allowNestedComments; - } - - /** - * Returns the list of comment delimiters. - */ - public static String[][] getCommentDelimiters() { - return commentDelim; - } - - /** - * Advance input by one character, setting {@link #nextChar}. - */ - private void advance() - throws java.io.IOException { - - if (firstLookaheadChar == lastLookaheadChar) { - // We have nothing in the lookahead buffer. - nextChar = getChar(); - } else { - // We have called lookahead(); advance to the next character it got. - nextChar = lookaheadChars[firstLookaheadChar++]; - if (firstLookaheadChar == lastLookaheadChar) { - firstLookaheadChar = 0; - lastLookaheadChar = 0; - } - } - if (nextChar == '\012') { - lines.add(iChar); - } - iChar++; - } - - /** Peek at the character after {@link #nextChar} without advancing. */ - private int lookahead() - throws java.io.IOException { - - return lookahead(1); - } - - /** - * Peeks at the character n after {@link #nextChar} without advancing. - * lookahead(0) returns the current char (nextChar). - * lookahead(1) returns the next char (was lookaheadChar, same as lookahead()); - */ - private int lookahead(int n) - throws java.io.IOException { - - if (n == 0) { - return nextChar; - } - else { - // if the desired character not in lookahead buffer, read it in - if (n > lastLookaheadChar - firstLookaheadChar) { - int len=lastLookaheadChar - firstLookaheadChar; - int t[]; - - // make sure we do not go off the end of the buffer - if (n + firstLookaheadChar > lookaheadChars.length) { - if (n > lookaheadChars.length) { - // the array is too small; make it bigger and shift - // everything to the beginning. - t=new int[n * 2]; - } - else { - // the array is big enough, so just shift everything - // to the beginning of it. - t = lookaheadChars; - } - - System.arraycopy( - lookaheadChars, firstLookaheadChar, t, 0, len); - lookaheadChars = t; - firstLookaheadChar = 0; - lastLookaheadChar = len; - } - - // read ahead enough - while (n > lastLookaheadChar - firstLookaheadChar) { - lookaheadChars[lastLookaheadChar++] = getChar(); - } - } - - return lookaheadChars[n - 1 + firstLookaheadChar]; - } - } - - /** Read a character from input, returning -1 if end of input. */ - protected int getChar() - throws java.io.IOException { - - return System.in.read(); - } - - /** Initialize the scanner */ - public void init() - throws java.io.IOException { - - initReswords(); - lines.clear(); - iChar = iPrevChar = 0; - advance(); - } - - private void initResword(int id, String s) { - reservedWordIds.put(s, id); - if (id > iMaxResword) { - iMaxResword = id; - } - } - - /** - * Initializes the table of reserved words. - */ - private void initReswords() { - // This list generated by piping the 'terminal' declaration in mdx.cup - // through: - // grep -list // | - // sed -e 's/,//' | - // awk '{printf "initResword(%20s,%c%s%c);",$1,34,$1,34}' - reservedWordIds = new HashMap(); - iMaxResword = 0; -// initResword(DefaultMdxParserSym.ALL ,"ALL"); - initResword(DefaultMdxParserSym.AND ,"AND"); - initResword(DefaultMdxParserSym.AS ,"AS"); -// initResword(DefaultMdxParserSym.ASC ,"ASC"); - initResword(DefaultMdxParserSym.AXIS ,"AXIS"); -// initResword(DefaultMdxParserSym.BACK_COLOR ,"BACK_COLOR"); -// initResword(DefaultMdxParserSym.BASC ,"BASC"); -// initResword(DefaultMdxParserSym.BDESC ,"BDESC"); - initResword(DefaultMdxParserSym.CAST ,"CAST"); // mondrian extension - initResword(DefaultMdxParserSym.CASE ,"CASE"); - initResword(DefaultMdxParserSym.CELL ,"CELL"); -// initResword(DefaultMdxParserSym.CELL_ORDINAL ,"CELL_ORDINAL"); - initResword(DefaultMdxParserSym.CHAPTERS ,"CHAPTERS"); -// initResword(DefaultMdxParserSym.CHILDREN ,"CHILDREN"); - initResword(DefaultMdxParserSym.COLUMNS ,"COLUMNS"); -// initResword(DefaultMdxParserSym.DESC ,"DESC"); - initResword(DefaultMdxParserSym.DIMENSION ,"DIMENSION"); - initResword(DefaultMdxParserSym.ELSE ,"ELSE"); - initResword(DefaultMdxParserSym.EMPTY ,"EMPTY"); - initResword(DefaultMdxParserSym.END ,"END"); -// initResword(DefaultMdxParserSym.FIRSTCHILD ,"FIRSTCHILD"); -// initResword(DefaultMdxParserSym.FIRSTSIBLING ,"FIRSTSIBLING"); -// initResword(DefaultMdxParserSym.FONT_FLAGS ,"FONT_FLAGS"); -// initResword(DefaultMdxParserSym.FONT_NAME ,"FONT_NAME"); -// initResword(DefaultMdxParserSym.FONT_SIZE ,"FONT_SIZE"); -// initResword(DefaultMdxParserSym.FORE_COLOR ,"FORE_COLOR"); -// initResword(DefaultMdxParserSym.FORMATTED_VALUE ,"FORMATTED_VALUE"); -// initResword(DefaultMdxParserSym.FORMAT_STRING ,"FORMAT_STRING"); - initResword(DefaultMdxParserSym.FROM ,"FROM"); - initResword(DefaultMdxParserSym.IS ,"IS"); - initResword(DefaultMdxParserSym.IN ,"IN"); -// initResword(DefaultMdxParserSym.LAG ,"LAG"); -// initResword(DefaultMdxParserSym.LASTCHILD ,"LASTCHILD"); -// initResword(DefaultMdxParserSym.LASTSIBLING ,"LASTSIBLING"); -// initResword(DefaultMdxParserSym.LEAD ,"LEAD"); - initResword(DefaultMdxParserSym.MATCHES ,"MATCHES"); - initResword(DefaultMdxParserSym.MEMBER ,"MEMBER"); -// initResword(DefaultMdxParserSym.MEMBERS ,"MEMBERS"); -// initResword(DefaultMdxParserSym.NEXTMEMBER ,"NEXTMEMBER"); - initResword(DefaultMdxParserSym.NON ,"NON"); - initResword(DefaultMdxParserSym.NOT ,"NOT"); - initResword(DefaultMdxParserSym.NULL ,"NULL"); - initResword(DefaultMdxParserSym.ON ,"ON"); - initResword(DefaultMdxParserSym.OR ,"OR"); - initResword(DefaultMdxParserSym.PAGES ,"PAGES"); -// initResword(DefaultMdxParserSym.PARENT ,"PARENT"); -// initResword(DefaultMdxParserSym.PREVMEMBER ,"PREVMEMBER"); - initResword(DefaultMdxParserSym.PROPERTIES ,"PROPERTIES"); -// initResword(DefaultMdxParserSym.RECURSIVE ,"RECURSIVE"); - initResword(DefaultMdxParserSym.ROWS ,"ROWS"); - initResword(DefaultMdxParserSym.SECTIONS ,"SECTIONS"); - initResword(DefaultMdxParserSym.SELECT ,"SELECT"); - initResword(DefaultMdxParserSym.SET ,"SET"); -// initResword(DefaultMdxParserSym.SOLVE_ORDER ,"SOLVE_ORDER"); - initResword(DefaultMdxParserSym.THEN ,"THEN"); -// initResword(DefaultMdxParserSym.VALUE ,"VALUE"); - initResword(DefaultMdxParserSym.WHEN ,"WHEN"); - initResword(DefaultMdxParserSym.WHERE ,"WHERE"); - initResword(DefaultMdxParserSym.WITH ,"WITH"); - initResword(DefaultMdxParserSym.XOR ,"XOR"); - - reservedWords = new String[iMaxResword + 1]; - for (Map.Entry entry : reservedWordIds.entrySet()) { - reservedWords[entry.getValue()] = entry.getKey(); - } - } - - /** return the name of the reserved word whose token code is "i" */ - public String lookupReserved(int i) { - return reservedWords[i]; - } - - private Symbol makeSymbol(int id, Object o) { - int iPrevPrevChar = iPrevChar; - this.iPrevChar = iChar; - this.previousSymbol = id; - return new Symbol(id, iPrevPrevChar, iChar, o); - } - - /** - * Creates a token representing a numeric literal. - * - * @param mantissa The digits of the number - * @param exponent The base-10 exponent of the number - * @return number literal token - */ - private Symbol makeNumber(BigDecimal mantissa, int exponent) { - BigDecimal d = mantissa.movePointRight(exponent); - return makeSymbol(DefaultMdxParserSym.NUMBER, d); - } - - private Symbol makeId(String s, boolean quoted, boolean ampersand) { - final int id; - if (quoted) { - if (ampersand) { - id = DefaultMdxParserSym.AMP_QUOTED_ID; - } else { - id = DefaultMdxParserSym.QUOTED_ID; - } - } else { - if (ampersand) { - id = DefaultMdxParserSym.AMP_UNQUOTED_ID; - } else { - id = DefaultMdxParserSym.ID; - } - } - return makeSymbol(id, s); - } - - /** - * Creates a token representing a reserved word. - * - * @param i Token code - * @return Token - */ - private Symbol makeRes(int i) { - return makeSymbol(i, reservedWords[i]); - } - - /** - * Creates a token. - * - * @param i Token code - * @param s Text of the token - * @return Token - */ - private Symbol makeToken(int i, String s) { - return makeSymbol(i, s); - } - - /** - * Creates a token representing a string literal. - * - * @param s String - * @return String token - */ - private Symbol makeString(String s) { - if (inFormula) { - inFormula = false; - return makeSymbol(DefaultMdxParserSym.FORMULA_STRING, s); - } else { - return makeSymbol(DefaultMdxParserSym.STRING, s); - } - } - - /** - * Discards all characters until the end of the current line. - */ - private void skipToEOL() throws IOException { - while (nextChar != -1 && nextChar != '\012') { - advance(); - } - } - - /** - * Eats a delimited comment. - * The type of delimiters are kept in commentDelim. The current - * comment type is indicated by commentType. - * end of file terminates a comment without error. - */ - private void skipComment( - final String startDelim, - final String endDelim) throws IOException { - - int depth = 1; - - // skip the starting delimiter - for (int x = 0; x < startDelim.length(); x++) { - advance(); - } - - for (;;) { - if (nextChar == -1) { - return; - } - else if (checkForSymbol(endDelim)) { - // eat the end delimiter - for (int x = 0; x < endDelim.length(); x++) { - advance(); - } - if (--depth == 0) { - return; - } - } - else if (allowNestedComments && checkForSymbol(startDelim)) { - // eat the nested start delimiter - for (int x = 0; x < startDelim.length(); x++) { - advance(); - } - depth++; - } - else { - advance(); - } - } - } - - /** - * If the next tokens are comments, skip over them. - */ - private void searchForComments() throws IOException { - - // eat all following comments - boolean foundComment; - do { - foundComment = false; - for (String[] aCommentDelim : commentDelim) { - if (checkForSymbol(aCommentDelim[0])) { - if (aCommentDelim[1] == null) { - foundComment = true; - skipToEOL(); - } else { - foundComment = true; - skipComment(aCommentDelim[0], aCommentDelim[1]); - } - } - } - } while (foundComment); - } - - /** - * Checks if the next symbol is the supplied string - */ - private boolean checkForSymbol(final String symb) throws IOException { - for (int x = 0; x < symb.length(); x++) { - if (symb.charAt(x) != lookahead(x)) { - return false; - } - } - return true; - } - - /** - * Recognizes and returns the next complete token. - */ - public Symbol next_token() throws IOException { - - StringBuilder id; - boolean ampersandId = false; - for (;;) { - searchForComments(); - mainSwitch: - switch (nextChar) { - case '.': - switch (lookahead()) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - // We're looking at the '.' on the start of a number, - // e.g. .1; fall through to parse a number. - break; - default: - advance(); - return makeToken(DefaultMdxParserSym.DOT, "."); - } - // fall through - - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - - // Parse a number. Valid examples include 1, 1.2, 0.1, .1, - // 1e2, 1E2, 1e-2, 1e+2. Invalid examples include e2, 1.2.3, - // 1e2e3, 1e2.3. - // - // Signs preceding numbers (e.g. -1, +1E-5) are valid, but are - // handled by the parser. - // - BigDecimal n = BigDecimalZero; - int digitCount = 0, exponent = 0; - boolean positive = true; - BigDecimal mantissa = BigDecimalZero; - State state = State.leftOfPoint; - - for (;;) { - switch (nextChar) { - case '.': - switch (state) { - case leftOfPoint: - state = State.rightOfPoint; - mantissa = n; - n = BigDecimalZero; - digitCount = 0; - positive = true; - advance(); - break; - // Error: we are seeing a point in the exponent - // (e.g. 1E2.3 or 1.2E3.4) or a second point in the - // mantissa (e.g. 1.2.3). Return what we've got - // and let the parser raise the error. - case rightOfPoint: - mantissa = - mantissa.add( - n.movePointRight(-digitCount)); - return makeNumber(mantissa, exponent); - case inExponent: - if (!positive) { - n = n.negate(); - } - exponent = n.intValue(); - return makeNumber(mantissa, exponent); - } - break; - - case 'E': - case 'e': - switch (state) { - case inExponent: - // Error: we are seeing an 'e' in the exponent - // (e.g. 1.2e3e4). Return what we've got and let - // the parser raise the error. - if (!positive) { - n = n.negate(); - } - exponent = n.intValue(); - return makeNumber(mantissa, exponent); - case leftOfPoint: - mantissa = n; - break; - default: - mantissa = - mantissa.add( - n.movePointRight(-digitCount)); - break; - } - - digitCount = 0; - n = BigDecimalZero; - positive = true; - advance(); - state = State.inExponent; - break; - - case'0': case'1': case'2': case'3': case'4': - case'5': case'6': case'7': case'8': case'9': - n = n.movePointRight(1); - n = n.add(BigDecimal.valueOf(nextChar - '0')); - digitCount++; - advance(); - break; - - case '+': - case '-': - if (state == State.inExponent && digitCount == 0) { - // We're looking at the sign after the 'e'. - positive = !positive; - advance(); - break; - } - // fall through - end of number - - default: - // Reached end of number. - switch (state) { - case leftOfPoint: - mantissa = n; - break; - case rightOfPoint: - mantissa = - mantissa.add( - n.movePointRight(-digitCount)); - break; - default: - if (!positive) { - n = n.negate(); - } - exponent = n.intValue(); - break; - } - return makeNumber(mantissa, exponent); - } - } - - case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': - case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': - case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': - case 's': case 't': case 'u': case 'v': case 'w': case 'x': - case 'y': case 'z': - case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': - case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': - case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': - case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': - case 'Y': case 'Z': - case '_': case '$': - /* parse an identifier */ - id = new StringBuilder(); - for (;;) { - id.append((char) nextChar); - advance(); - switch (nextChar) { - case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': - case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': - case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': - case 's': case 't': case 'u': case 'v': case 'w': case 'x': - case 'y': case 'z': - case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': - case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': - case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': - case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': - case 'Y': case 'Z': - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - case '_': case '$': - break; - default: - String strId = id.toString(); - Integer i = reservedWordIds.get( - strId.toUpperCase()); - if (i == null) { - // identifier - return makeId(strId, false, ampersandId); - } else { - // reserved word - return makeRes(i); - } - } - } - - case '&': - advance(); - switch (nextChar) { - case '[': - // fall through to parse a delimited identifier - ampersandId = true; - break; - case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': - case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': - case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': - case 's': case 't': case 'u': case 'v': case 'w': case 'x': - case 'y': case 'z': - case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': - case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': - case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': - case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': - case 'Y': case 'Z': - // fall into logic to create identifer - ampersandId = true; - break mainSwitch; - default: - // error - return makeToken(DefaultMdxParserSym.UNKNOWN, "&"); - } - - case '[': - /* parse a delimited identifier */ - id = new StringBuilder(); - for (;;) { - advance(); - switch (nextChar) { - case ']': - advance(); - if (nextChar == ']') { - // ] escaped with ] - just take one - id.append(']'); - break; - } else { - // end of identifier - if (ampersandId) { - ampersandId = false; - return makeId(id.toString(), true, true); - } else { - return makeId(id.toString(), true, false); - } - } - case -1: - if (ampersandId) { - ampersandId = false; - return makeId(id.toString(), true, true); - } else { - return makeId(id.toString(), true, false); - } - default: - id.append((char) nextChar); - } - } - - case ':': - advance(); - return makeToken(DefaultMdxParserSym.COLON, ":"); - case ',': - advance(); - return makeToken(DefaultMdxParserSym.COMMA, ","); - case '=': - advance(); - return makeToken(DefaultMdxParserSym.EQ, "="); - case '<': - advance(); - switch (nextChar) { - case '>': - advance(); - return makeToken(DefaultMdxParserSym.NE, "<>"); - case '=': - advance(); - return makeToken(DefaultMdxParserSym.LE, "<="); - default: - return makeToken(DefaultMdxParserSym.LT, "<"); - } - case '>': - advance(); - switch (nextChar) { - case '=': - advance(); - return makeToken(DefaultMdxParserSym.GE, ">="); - default: - return makeToken(DefaultMdxParserSym.GT, ">"); - } - case '{': - advance(); - return makeToken(DefaultMdxParserSym.LBRACE, "{"); - case '(': - advance(); - return makeToken(DefaultMdxParserSym.LPAREN, "("); - case '}': - advance(); - return makeToken(DefaultMdxParserSym.RBRACE, "}"); - case ')': - advance(); - return makeToken(DefaultMdxParserSym.RPAREN, ")"); - case '+': - advance(); - return makeToken(DefaultMdxParserSym.PLUS, "+"); - case '-': - advance(); - return makeToken(DefaultMdxParserSym.MINUS, "-"); - case '*': - advance(); - return makeToken(DefaultMdxParserSym.ASTERISK, "*"); - case '/': - advance(); - return makeToken(DefaultMdxParserSym.SOLIDUS, "/"); - case '|': - advance(); - switch (nextChar) { - case '|': - advance(); - return makeToken(DefaultMdxParserSym.CONCAT, "||"); - default: - return makeToken(DefaultMdxParserSym.UNKNOWN, "|"); - } - - case '"': - /* parse a double-quoted string */ - id = new StringBuilder(); - for (;;) { - advance(); - switch (nextChar) { - case '"': - advance(); - if (nextChar == '"') { - // " escaped with " - id.append('"'); - break; - } else { - // end of string - return makeString(id.toString()); - } - case -1: - return makeString(id.toString()); - default: - id.append((char) nextChar); - } - } - - case '\'': - if (previousSymbol == DefaultMdxParserSym.AS) { - inFormula = true; - } - - /* parse a single-quoted string */ - id = new StringBuilder(); - for (;;) { - advance(); - switch (nextChar) { - case '\'': - advance(); - if (nextChar == '\'') { - // " escaped with " - id.append('\''); - break; - } else { - // end of string - return makeString(id.toString()); - } - case -1: - return makeString(id.toString()); - default: - id.append((char) nextChar); - } - } - - case -1: - // we're done - return makeToken(DefaultMdxParserSym.EOF, "EOF"); - - default: - // If it's whitespace, skip over it. - if (nextChar <= Character.MAX_VALUE - && Character.isWhitespace(nextChar)) - { - // fall through - } else { - // everything else is an error - throw new MdxParseException( - createRegion(iPrevChar, iChar), - "Unexpected character '" + (char) nextChar + "'"); - } - - case ' ': - case '\t': - case '\n': - case '\r': - // whitespace can be ignored - iPrevChar = iChar; - advance(); - break; - } - } - } - - /** - * Creates a region from a start and end point. - * Called by {@link DefaultMdxParser#syntax_error}. - */ - ParseRegion createRegion(final int left, final int right) { - int target = left; - int line = -1; - int lineEnd = 0; - int lineStart; - do { - line++; - lineStart = lineEnd; - lineEnd = Integer.MAX_VALUE; - if (line < lines.size()) { - lineEnd = lines.get(line); - } - } while (lineEnd < target); - - int startLine = line; - int startColumn = target - lineStart; - - if (right == left) { - return new ParseRegion(startLine + 1, startColumn + 1); - } - - target = right - 1; - if (target > left) --target; // don't know why - line = -1; - lineEnd = 0; - do { - line++; - lineStart = lineEnd; - lineEnd = Integer.MAX_VALUE; - if (line < lines.size()) { - lineEnd = lines.get(line); - } - } while (lineEnd < target); - - int endLine = line; - int endColumn = target - lineStart; - - return new ParseRegion( - startLine + 1, startColumn + 1, endLine + 1, endColumn + 1); - } - - private enum State { - leftOfPoint, - rightOfPoint, - inExponent, - } -} - -// End Scanner.java diff --git a/src/org/olap4j/mdx/parser/impl/StringScanner.java b/src/org/olap4j/mdx/parser/impl/StringScanner.java deleted file mode 100644 index 2ab5530..0000000 --- a/src/org/olap4j/mdx/parser/impl/StringScanner.java +++ /dev/null @@ -1,53 +0,0 @@ -/* -// $Id$ -// -// Licensed to Julian Hyde under one or more contributor license -// agreements. See the NOTICE file distributed with this work for -// additional information regarding copyright ownership. -// -// Julian Hyde licenses this file to you under the Apache License, -// Version 2.0 (the "License"); you may not use this file except in -// compliance with the License. You may obtain a copy of the License at: -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -*/ -package org.olap4j.mdx.parser.impl; - -/** - * Lexical analyzer whose input is a string. - * - *

NOTE: This class is not part of the public olap4j API. - * - * @version $Id$ - * @author jhyde - */ -class StringScanner extends Scanner { - private final String s; - private int i; - - /** - * Creates a StringScanner. - * - * @param s Input string - * @param debug Whether to populate debug messages - */ - StringScanner(String s, boolean debug) { - super(debug); - this.s = s; - i = 0; - } - - protected int getChar() { - return (i >= s.length()) - ? -1 - : s.charAt(i++); - } -} - -// End StringScanner.java