() {
- 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
-//
-// ::=