docs: private void parseIndexColumnExpressionList( final String columnExpressionList,
                                                   final AstNode indexNode ) {
          final DdlTokenStream tokens = new DdlTokenStream(columnExpressionList, DdlTokenStream.ddlTokenizer(false), false);
          tokens.start();
  
          tokens.consume(L_PAREN); // must have opening paren
          int numLeft = 1;
          int numRight = 0;
  
          // must have content between the parens
          if (!tokens.matches(R_PAREN)) {
              final List<String> possibleColumns = new ArrayList<String>(); // dimension table columns
              final List<String> functions = new ArrayList<String>(); // functions, constants
              final StringBuilder text = new StringBuilder();
              boolean isFunction = false;
  
              while (tokens.hasNext()) {
                  if (tokens.canConsume(COMMA)) {
                      if (isFunction) {
                          functions.add(text.toString());
                      } else {
                          possibleColumns.add(text.toString());
                      }
  
                      text.setLength(0); // clear out
                      isFunction = false;
                      continue;
                  }
  
                  if (tokens.matches(L_PAREN)) {
                      isFunction = true;
                      ++numLeft;
                  } else if (tokens.matches("ASC") || tokens.matches("DESC")) {
                      text.append(SPACE);
                  } else if (tokens.matches(R_PAREN)) {
                      if (numLeft == ++numRight) {
                          if (isFunction) {
                              functions.add(text.toString());
                          } else {
                              possibleColumns.add(text.toString());
                          }
  
                          break;
                      }
                  }
  
                  text.append(tokens.consume());
              }
  
              if (!possibleColumns.isEmpty()) {
                  List<AstNode> tableNodes = null;
                  final boolean tableIndex = indexNode.hasMixin(OracleDdlLexicon.TYPE_CREATE_TABLE_INDEX_STATEMENT);
  
                  // find appropriate table nodes
                  if (tableIndex) {
                      // table index so find table node
                      final String tableName = (String)indexNode.getProperty(OracleDdlLexicon.TABLE_NAME);
                      final AstNode parent = indexNode.getParent();
                      final List<AstNode> nodes = parent.childrenWithName(tableName);
  
                      if (!nodes.isEmpty()) {
                          if (nodes.size() == 1) {
                              tableNodes = nodes;
                          } else {
                              // this should not be possible but check none the less
                              for (final AstNode node : nodes) {
                                  if (node.hasMixin(StandardDdlLexicon.TYPE_CREATE_TABLE_STATEMENT)) {
                                      tableNodes = new ArrayList<AstNode>(1);
                                      tableNodes.add(node);
                                      break;
                                  }
                              }
                          }
                      }
                  } else {
                      // must be bitmap-join
                      tableNodes = indexNode.getChildren(StandardDdlLexicon.TYPE_TABLE_REFERENCE);
                  }
  
                  if ((tableNodes != null) && !tableNodes.isEmpty()) {
                      boolean processed = false;
  
                      for (String possibleColumn : possibleColumns) {
                          // first determine any ordering
                          final int ascIndex = possibleColumn.toUpperCase().indexOf(" ASC");
                          final boolean asc = (ascIndex != -1);
                          final int descIndex = possibleColumn.toUpperCase().indexOf(" DESC");
                          boolean desc = (descIndex != -1);
  
                          // adjust column name if there is ordering
                          if (asc) {
                              possibleColumn = possibleColumn.substring(0, ascIndex);
                          } else if (desc) {
                              possibleColumn = possibleColumn.substring(0, descIndex);
                          }
  
                          if (tableIndex) {
                              if (tableNodes.isEmpty()) {
                                  if (asc) {
                                      functions.add(possibleColumn + SPACE + "ASC");
                                  } else if (desc) {
                                      functions.add(possibleColumn + SPACE + "DESC");
                                  } else {
                                      functions.add(possibleColumn);
                                  }
                              } else {
                                  // only one table reference. need to find column.
                                  final AstNode tableNode = tableNodes.get(0);
                                  final List<AstNode> columnNodes = tableNode.getChildren(StandardDdlLexicon.TYPE_COLUMN_DEFINITION);
  
                                  if (!columnNodes.isEmpty()) {
                                      // find column
                                      for (final AstNode colNode : columnNodes) {
                                          if (colNode.getName().equalsIgnoreCase(possibleColumn)) {
                                              final AstNode colRef = nodeFactory().node(possibleColumn,
                                                                                        indexNode,
                                                                                        TYPE_COLUMN_REFERENCE);
  
                                              if (asc || desc) {
                                                  colRef.addMixin(OracleDdlLexicon.TYPE_INDEX_ORDERABLE);
  
                                                  if (asc) {
                                                      colRef.setProperty(OracleDdlLexicon.INDEX_ORDER, "ASC");
                                                  } else {
                                                      colRef.setProperty(OracleDdlLexicon.INDEX_ORDER, "DESC");
                                                  }
                                              }
  
                                              processed = true;
                                              break;
                                          }
                                      }
                                  }
  
                                  if (!processed) {
                                      if (asc) {
                                          functions.add(possibleColumn + SPACE + "ASC");
                                      } else if (desc) {
                                          functions.add(possibleColumn + SPACE + "DESC");
                                      } else {
                                          functions.add(possibleColumn);
                                      }
  
                                      processed = true;
                                  }
                              }
                          } else {
                              // bitmap-join
                              for (final AstNode dimensionTableNode : tableNodes) {
                                  if (possibleColumn.toUpperCase(Locale.ROOT).startsWith(dimensionTableNode.getName().toUpperCase(Locale.ROOT) + PERIOD)) {
                                      final AstNode colRef = nodeFactory().node(possibleColumn, indexNode, TYPE_COLUMN_REFERENCE);
  
                                      if (asc || desc) {
                                          colRef.addMixin(OracleDdlLexicon.TYPE_INDEX_ORDERABLE);
  
                                          if (asc) {
                                              colRef.setProperty(OracleDdlLexicon.INDEX_ORDER, "ASC");
                                          } else {
                                              colRef.setProperty(OracleDdlLexicon.INDEX_ORDER, "DESC");
                                          }
                                      }
  
                                      processed = true;
                                      break;
                                  }
                              }
  
                              // probably a constant or function
                              if (!processed) {
                                  if (asc) {
                                      functions.add(possibleColumn + SPACE + "ASC");
                                  } else if (desc) {
                                      functions.add(possibleColumn + SPACE + "DESC");
                                  } else {
                                      functions.add(possibleColumn);
                                  }
  
                                  processed = true;
                              }
                          }
                      }
                  }
              }
  
              if (!functions.isEmpty()) {
                  indexNode.setProperty(OracleDdlLexicon.OTHER_INDEX_REFS, functions);
              }
          }
  
          if (numLeft != numRight) {
              throw new ParsingException(tokens.nextPosition());
          }
  
          tokens.consume(R_PAREN); // must have closing paren
      }