Просмотр исходного кода

[parser] adding hplsql statements parser

ayush.goyal 4 лет назад
Родитель
Сommit
818759c3a2

+ 789 - 0
desktop/core/src/desktop/js/parse/hplsqlStatementsParser.js

@@ -0,0 +1,789 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. 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.
+/* parser generated by jison 0.4.17 */
+/*
+  Returns a Parser object of the following structure:
+
+  Parser: {
+    yy: {}
+  }
+
+  Parser.prototype: {
+    yy: {},
+    trace: function(),
+    symbols_: {associative list: name ==> number},
+    terminals_: {associative list: number ==> name},
+    productions_: [...],
+    performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$),
+    table: [...],
+    defaultActions: {...},
+    parseError: function(str, hash),
+    parse: function(input),
+
+    lexer: {
+        EOF: 1,
+        parseError: function(str, hash),
+        setInput: function(input),
+        input: function(),
+        unput: function(str),
+        more: function(),
+        less: function(n),
+        pastInput: function(),
+        upcomingInput: function(),
+        showPosition: function(),
+        test_match: function(regex_match_array, rule_index),
+        next: function(),
+        lex: function(),
+        begin: function(condition),
+        popState: function(),
+        _currentRules: function(),
+        topState: function(),
+        pushState: function(condition),
+
+        options: {
+            ranges: boolean           (optional: true ==> token location info will include a .range[] member)
+            flex: boolean             (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match)
+            backtrack_lexer: boolean  (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code)
+        },
+
+        performAction: function(yy, yy_, $avoiding_name_collisions, YY_START),
+        rules: [...],
+        conditions: {associative list: name ==> set},
+    }
+  }
+
+
+  token location info (@$, _$, etc.): {
+    first_line: n,
+    last_line: n,
+    first_column: n,
+    last_column: n,
+    range: [start_number, end_number]       (where the numbers are indexes into the input string, regular zero-based)
+  }
+
+
+  the parseError function receives a 'hash' object with these members for lexer and parser errors: {
+    text:        (matched text)
+    token:       (the produced terminal token, if any)
+    line:        (yylineno)
+  }
+  while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: {
+    loc:         (yylloc)
+    expected:    (string describing the set of expected tokens)
+    recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error)
+  }
+*/
+var hplsqlStatementsParser = (function(){
+var o=function(k,v,o,l){for(o=o||{},l=k.length;l--;o[k[l]]=v);return o},$V0=[1,7],$V1=[1,6],$V2=[1,12],$V3=[5,9],$V4=[1,13],$V5=[5,8,9];
+var parser = {trace: function trace () { },
+yy: {},
+symbols_: {"error":2,"HplsqlStatementsParser":3,"Statements":4,"EOF":5,"OneOrMoreSeparators":6,"StatementParts":7,"PART_OF_STATEMENT":8,"/":9,"$accept":0,"$end":1},
+terminals_: {2:"error",5:"EOF",8:"PART_OF_STATEMENT",9:"/"},
+productions_: [0,[3,2],[3,3],[3,4],[3,3],[3,2],[3,1],[4,1],[4,3],[7,1],[7,2],[6,1],[6,2]],
+performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) {
+/* this == yyval */
+
+var $0 = $$.length - 1;
+switch (yystate) {
+case 1:
+
+     parser.removeTrailingWhiteSpace($$[$0-1]);
+     return $$[$0-1];
+   
+break;
+case 2:
+
+     parser.handleLeadingStatements($$[$0-2], $$[$0-1]);
+     parser.removeTrailingWhiteSpace($$[$0-1]);
+     return $$[$0-1];
+   
+break;
+case 3:
+
+     parser.handleLeadingStatements($$[$0-3], $$[$0-2]);
+     parser.handleTrailingStatements($$[$0-2], $$[$0-1]);
+     parser.removeTrailingWhiteSpace($$[$0-2]);
+     return $$[$0-2];
+   
+break;
+case 4:
+
+     parser.handleTrailingStatements($$[$0-2], $$[$0-1]);
+     parser.removeTrailingWhiteSpace($$[$0-2]);
+     return $$[$0-2];
+   
+break;
+case 5:
+
+     var result = [];
+     parser.handleLeadingStatements($$[$0-1], result);
+     return result;
+   
+break;
+case 6:
+
+     var result = [];
+     parser.addEntry(result, 'statement', $$[$0], _$[$0]);
+     return result;
+   
+break;
+case 7:
+
+     this.$ = [];
+     parser.addEntry(this.$, 'statement', $$[$0], _$[$0]);
+   
+break;
+case 8:
+
+     parser.handleTrailingStatements($$[$0-2], $$[$0-1]);
+     parser.addEntry($$[$0-2], 'statement', $$[$0], _$[$0]);
+   
+break;
+case 10:
+this.$ = $$[$0-1] + $$[$0];;
+break;
+case 11:
+this.$ = [_$[$0]];
+break;
+case 12:
+
+     $$[$0-1].push(_$[$0]);
+   
+break;
+}
+},
+table: [{3:1,4:2,5:[1,4],6:3,7:5,8:$V0,9:$V1},{1:[3]},{5:[1,8],6:9,9:$V1},{4:10,5:[1,11],7:5,8:$V0,9:$V2},{1:[2,6]},o($V3,[2,7],{8:$V4}),o($V5,[2,11]),o($V5,[2,9]),{1:[2,1]},{5:[1,14],7:15,8:$V0,9:$V2},{5:[1,16],6:17,9:$V1},{1:[2,5]},o($V5,[2,12]),o($V5,[2,10]),{1:[2,4]},o($V3,[2,8],{8:$V4}),{1:[2,2]},{5:[1,18],7:15,8:$V0,9:$V2},{1:[2,3]}],
+defaultActions: {4:[2,6],8:[2,1],11:[2,5],14:[2,4],16:[2,2],18:[2,3]},
+parseError: function parseError (str, hash) {
+    if (hash.recoverable) {
+        this.trace(str);
+    } else {
+        var error = new Error(str);
+        error.hash = hash;
+        throw error;
+    }
+},
+/**
+ * @param {string} input
+ *
+ * @return {SqlStatementsParserResult}
+ */
+parse: function parse(input) {
+    var self = this, stack = [0], tstack = [], vstack = [null], lstack = [], table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1;
+    var args = lstack.slice.call(arguments, 1);
+    var lexer = Object.create(this.lexer);
+    var sharedState = { yy: {} };
+    for (var k in this.yy) {
+        if (Object.prototype.hasOwnProperty.call(this.yy, k)) {
+            sharedState.yy[k] = this.yy[k];
+        }
+    }
+    lexer.setInput(input, sharedState.yy);
+    sharedState.yy.lexer = lexer;
+    sharedState.yy.parser = this;
+    if (typeof lexer.yylloc == 'undefined') {
+        lexer.yylloc = {};
+    }
+    var yyloc = lexer.yylloc;
+    lstack.push(yyloc);
+    var ranges = lexer.options && lexer.options.ranges;
+    if (typeof sharedState.yy.parseError === 'function') {
+        this.parseError = sharedState.yy.parseError;
+    } else {
+        this.parseError = Object.getPrototypeOf(this).parseError;
+    }
+    function popStack(n) {
+        stack.length = stack.length - 2 * n;
+        vstack.length = vstack.length - n;
+        lstack.length = lstack.length - n;
+    }
+    _token_stack:
+        var lex = function () {
+            var token;
+            token = lexer.lex() || EOF;
+            if (typeof token !== 'number') {
+                token = self.symbols_[token] || token;
+            }
+            return token;
+        };
+    var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected;
+    while (true) {
+        state = stack[stack.length - 1];
+        if (this.defaultActions[state]) {
+            action = this.defaultActions[state];
+        } else {
+            if (symbol === null || typeof symbol == 'undefined') {
+                symbol = lex();
+            }
+            action = table[state] && table[state][symbol];
+        }
+                    if (typeof action === 'undefined' || !action.length || !action[0]) {
+                var errStr = '';
+                expected = [];
+                for (p in table[state]) {
+                    if (this.terminals_[p] && p > TERROR) {
+                        expected.push('\'' + this.terminals_[p] + '\'');
+                    }
+                }
+                if (lexer.showPosition) {
+                    errStr = 'Parse error on line ' + (yylineno + 1) + ':\n' + lexer.showPosition() + '\nExpecting ' + expected.join(', ') + ', got \'' + (this.terminals_[symbol] || symbol) + '\'';
+                } else {
+                    errStr = 'Parse error on line ' + (yylineno + 1) + ': Unexpected ' + (symbol == EOF ? 'end of input' : '\'' + (this.terminals_[symbol] || symbol) + '\'');
+                }
+                this.parseError(errStr, {
+                    text: lexer.match,
+                    token: this.terminals_[symbol] || symbol,
+                    line: lexer.yylineno,
+                    loc: lexer.yylloc,
+                    ruleId: stack.slice(stack.length - 2, stack.length).join('_'),
+                    expected: expected
+                });
+            }
+        if (action[0] instanceof Array && action.length > 1) {
+            throw new Error('Parse Error: multiple actions possible at state: ' + state + ', token: ' + symbol);
+        }
+        switch (action[0]) {
+        case 1:
+            stack.push(symbol);
+            vstack.push(lexer.yytext);
+            lstack.push(lexer.yylloc);
+            stack.push(action[1]);
+            symbol = null;
+            if (!preErrorSymbol) {
+                yyleng = lexer.yyleng;
+                yytext = lexer.yytext;
+                yylineno = lexer.yylineno;
+                yyloc = lexer.yylloc;
+                if (recovering > 0) {
+                    recovering--;
+                }
+            } else {
+                symbol = preErrorSymbol;
+                preErrorSymbol = null;
+            }
+            break;
+        case 2:
+            len = this.productions_[action[1]][1];
+            yyval.$ = vstack[vstack.length - len];
+            yyval._$ = {
+                first_line: lstack[lstack.length - (len || 1)].first_line,
+                last_line: lstack[lstack.length - 1].last_line,
+                first_column: lstack[lstack.length - (len || 1)].first_column,
+                last_column: lstack[lstack.length - 1].last_column
+            };
+            if (ranges) {
+                yyval._$.range = [
+                    lstack[lstack.length - (len || 1)].range[0],
+                    lstack[lstack.length - 1].range[1]
+                ];
+            }
+            r = this.performAction.apply(yyval, [
+                yytext,
+                yyleng,
+                yylineno,
+                sharedState.yy,
+                action[1],
+                vstack,
+                lstack
+            ].concat(args));
+            if (typeof r !== 'undefined') {
+                return r;
+            }
+            if (len) {
+                stack = stack.slice(0, -1 * len * 2);
+                vstack = vstack.slice(0, -1 * len);
+                lstack = lstack.slice(0, -1 * len);
+            }
+            stack.push(this.productions_[action[1]][0]);
+            vstack.push(yyval.$);
+            lstack.push(yyval._$);
+            newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
+            stack.push(newState);
+            break;
+        case 3:
+            return true;
+        }
+    }
+    return true;
+}};
+
+
+parser.addEntry = function (statements, type, statement, location) {
+  if (parser.yy.firstToken) {
+    if (parser.yy.useDatabase) {
+      statements.push({ type: type, statement: statement, location: location, firstToken: parser.yy.firstToken, database: parser.yy.useDatabase });
+      delete parser.yy.useDatabase;
+    } else {
+      statements.push({ type: type, statement: statement, location: location, firstToken: parser.yy.firstToken });
+    }
+    delete parser.yy.firstToken;
+  } else {
+    statements.push({ type: type, statement: statement, location: location });
+  }
+}
+
+parser.handleLeadingStatements = function (emptyStatements, result) {
+  for (var i = emptyStatements.length - 1; i >= 0; i--) {
+    result.unshift({ type: 'statement', statement: ';', location: emptyStatements[i] });
+  }
+}
+
+parser.handleTrailingStatements = function (result, emptyStatements) {
+  var lastStatement = result[result.length - 1];
+  lastStatement.statement += ';'
+  lastStatement.location = {
+    first_line: lastStatement.location.first_line,
+    first_column: lastStatement.location.first_column,
+    last_line: emptyStatements[0].last_line,
+    last_column: emptyStatements[0].last_column
+  }
+  if (emptyStatements.length > 1) {
+    for (var i = 1; i < emptyStatements.length; i++) {
+      result.push({ type: 'statement', statement: ';', location: emptyStatements[i] });
+    }
+  }
+}
+
+parser.removeTrailingWhiteSpace = function (result) {
+  if (result.length > 1) {
+      var lastStatement = result[result.length - 1];
+      if (/^\s+$/.test(lastStatement.statement)) {
+        result.pop()
+      }
+  }
+}
+/* generated by jison-lex 0.3.4 */
+var lexer = (function(){
+var lexer = ({
+
+EOF:1,
+
+parseError:function parseError(str, hash) {
+        if (this.yy.parser) {
+            this.yy.parser.parseError(str, hash);
+        } else {
+            throw new Error(str);
+        }
+    },
+
+// resets the lexer, sets new input
+setInput:function (input, yy) {
+        this.yy = yy || this.yy || {};
+        this._input = input;
+        this._more = this._backtrack = this.done = false;
+        this.yylineno = this.yyleng = 0;
+        this.yytext = this.matched = this.match = '';
+        this.conditionStack = ['INITIAL'];
+        this.yylloc = {
+            first_line: 1,
+            first_column: 0,
+            last_line: 1,
+            last_column: 0
+        };
+        if (this.options.ranges) {
+            this.yylloc.range = [0,0];
+        }
+        this.offset = 0;
+        return this;
+    },
+
+// consumes and returns one char from the input
+input:function () {
+        var ch = this._input[0];
+        this.yytext += ch;
+        this.yyleng++;
+        this.offset++;
+        this.match += ch;
+        this.matched += ch;
+        var lines = ch.match(/(?:\r\n?|\n).*/g);
+        if (lines) {
+            this.yylineno++;
+            this.yylloc.last_line++;
+        } else {
+            this.yylloc.last_column++;
+        }
+        if (this.options.ranges) {
+            this.yylloc.range[1]++;
+        }
+
+        this._input = this._input.slice(1);
+        return ch;
+    },
+
+// unshifts one char (or a string) into the input
+unput:function (ch) {
+        var len = ch.length;
+        var lines = ch.split(/(?:\r\n?|\n)/g);
+
+        this._input = ch + this._input;
+        this.yytext = this.yytext.substr(0, this.yytext.length - len);
+        //this.yyleng -= len;
+        this.offset -= len;
+        var oldLines = this.match.split(/(?:\r\n?|\n)/g);
+        this.match = this.match.substr(0, this.match.length - 1);
+        this.matched = this.matched.substr(0, this.matched.length - 1);
+
+        if (lines.length - 1) {
+            this.yylineno -= lines.length - 1;
+        }
+        var r = this.yylloc.range;
+
+        this.yylloc = {
+            first_line: this.yylloc.first_line,
+            last_line: this.yylineno + 1,
+            first_column: this.yylloc.first_column,
+            last_column: lines ?
+                (lines.length === oldLines.length ? this.yylloc.first_column : 0)
+                 + oldLines[oldLines.length - lines.length].length - lines[0].length :
+              this.yylloc.first_column - len
+        };
+
+        if (this.options.ranges) {
+            this.yylloc.range = [r[0], r[0] + this.yyleng - len];
+        }
+        this.yyleng = this.yytext.length;
+        return this;
+    },
+
+// When called from action, caches matched text and appends it on next action
+more:function () {
+        this._more = true;
+        return this;
+    },
+
+// When called from action, signals the lexer that this rule fails to match the input, so the next matching rule (regex) should be tested instead.
+reject:function () {
+        if (this.options.backtrack_lexer) {
+            this._backtrack = true;
+        } else {
+            return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n' + this.showPosition(), {
+                text: "",
+                token: null,
+                line: this.yylineno
+            });
+
+        }
+        return this;
+    },
+
+// retain first n characters of the match
+less:function (n) {
+        this.unput(this.match.slice(n));
+    },
+
+// displays already matched input, i.e. for error messages
+pastInput:function () {
+        var past = this.matched.substr(0, this.matched.length - this.match.length);
+        return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
+    },
+
+// displays upcoming input, i.e. for error messages
+upcomingInput:function () {
+        var next = this.match;
+        if (next.length < 20) {
+            next += this._input.substr(0, 20-next.length);
+        }
+        return (next.substr(0,20) + (next.length > 20 ? '...' : '')).replace(/\n/g, "");
+    },
+
+// displays the character position where the lexing error occurred, i.e. for error messages
+showPosition:function () {
+        var pre = this.pastInput();
+        var c = new Array(pre.length + 1).join("-");
+        return pre + this.upcomingInput() + "\n" + c + "^";
+    },
+
+// test the lexed token: return FALSE when not a match, otherwise return token
+test_match:function(match, indexed_rule) {
+        var token,
+            lines,
+            backup;
+
+        if (this.options.backtrack_lexer) {
+            // save context
+            backup = {
+                yylineno: this.yylineno,
+                yylloc: {
+                    first_line: this.yylloc.first_line,
+                    last_line: this.last_line,
+                    first_column: this.yylloc.first_column,
+                    last_column: this.yylloc.last_column
+                },
+                yytext: this.yytext,
+                match: this.match,
+                matches: this.matches,
+                matched: this.matched,
+                yyleng: this.yyleng,
+                offset: this.offset,
+                _more: this._more,
+                _input: this._input,
+                yy: this.yy,
+                conditionStack: this.conditionStack.slice(0),
+                done: this.done
+            };
+            if (this.options.ranges) {
+                backup.yylloc.range = this.yylloc.range.slice(0);
+            }
+        }
+
+        lines = match[0].match(/(?:\r\n?|\n).*/g);
+        if (lines) {
+            this.yylineno += lines.length;
+        }
+        this.yylloc = {
+            first_line: this.yylloc.last_line,
+            last_line: this.yylineno + 1,
+            first_column: this.yylloc.last_column,
+            last_column: lines ?
+                         lines[lines.length - 1].length - lines[lines.length - 1].match(/\r?\n?/)[0].length :
+                         this.yylloc.last_column + match[0].length
+        };
+        this.yytext += match[0];
+        this.match += match[0];
+        this.matches = match;
+        this.yyleng = this.yytext.length;
+        if (this.options.ranges) {
+            this.yylloc.range = [this.offset, this.offset += this.yyleng];
+        }
+        this._more = false;
+        this._backtrack = false;
+        this._input = this._input.slice(match[0].length);
+        this.matched += match[0];
+        token = this.performAction.call(this, this.yy, this, indexed_rule, this.conditionStack[this.conditionStack.length - 1]);
+        if (this.done && this._input) {
+            this.done = false;
+        }
+        if (token) {
+            return token;
+        } else if (this._backtrack) {
+            // recover context
+            for (var k in backup) {
+                this[k] = backup[k];
+            }
+            return false; // rule action called reject() implying the next rule should be tested instead.
+        }
+        return false;
+    },
+
+// return next match in input
+next:function () {
+        if (this.done) {
+            return this.EOF;
+        }
+        if (!this._input) {
+            this.done = true;
+        }
+
+        var token,
+            match,
+            tempMatch,
+            index;
+        if (!this._more) {
+            this.yytext = '';
+            this.match = '';
+        }
+        var rules = this._currentRules();
+        for (var i = 0; i < rules.length; i++) {
+            tempMatch = this._input.match(this.rules[rules[i]]);
+            if (tempMatch && (!match || tempMatch[0].length > match[0].length)) {
+                match = tempMatch;
+                index = i;
+                if (this.options.backtrack_lexer) {
+                    token = this.test_match(tempMatch, rules[i]);
+                    if (token !== false) {
+                        return token;
+                    } else if (this._backtrack) {
+                        match = false;
+                        continue; // rule action called reject() implying a rule MISmatch.
+                    } else {
+                        // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
+                        return false;
+                    }
+                } else if (!this.options.flex) {
+                    break;
+                }
+            }
+        }
+        if (match) {
+            token = this.test_match(match, rules[index]);
+            if (token !== false) {
+                return token;
+            }
+            // else: this is a lexer rule which consumes input without producing a token (e.g. whitespace)
+            return false;
+        }
+        if (this._input === "") {
+            return this.EOF;
+        } else {
+            return this.parseError('Lexical error on line ' + (this.yylineno + 1) + '. Unrecognized text.\n' + this.showPosition(), {
+                text: "",
+                token: null,
+                line: this.yylineno
+            });
+        }
+    },
+
+// return next match that has a token
+lex:function lex () {
+        var r = this.next();
+        if (r) {
+            return r;
+        } else {
+            return this.lex();
+        }
+    },
+
+// activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
+begin:function begin (condition) {
+        this.conditionStack.push(condition);
+    },
+
+// pop the previously active lexer condition state off the condition stack
+popState:function popState () {
+        var n = this.conditionStack.length - 1;
+        if (n > 0) {
+            return this.conditionStack.pop();
+        } else {
+            return this.conditionStack[0];
+        }
+    },
+
+// produce the lexer rule set which is active for the currently active lexer condition state
+_currentRules:function _currentRules () {
+        if (this.conditionStack.length && this.conditionStack[this.conditionStack.length - 1]) {
+            return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules;
+        } else {
+            return this.conditions["INITIAL"].rules;
+        }
+    },
+
+// return the currently active lexer condition state; when an index argument is provided it produces the N-th previous condition state, if available
+topState:function topState (n) {
+        n = this.conditionStack.length - 1 - Math.abs(n || 0);
+        if (n >= 0) {
+            return this.conditionStack[n];
+        } else {
+            return "INITIAL";
+        }
+    },
+
+// alias for begin(condition)
+pushState:function pushState (condition) {
+        this.begin(condition);
+    },
+
+// return the number of states currently on the stack
+stateStackSize:function stateStackSize() {
+        return this.conditionStack.length;
+    },
+options: {"flex":true},
+performAction: function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
+var YYSTATE=YY_START;
+switch($avoiding_name_collisions) {
+case 0: this.begin("multiLineComment"); return 8; 
+break;
+case 1: return 8; 
+break;
+case 2: return 8; 
+break;
+case 3: this.popState(); return 8; 
+break;
+case 4: this.popState(); return 8; 
+break;
+case 5: this.begin("inLineComment"); return 8; 
+break;
+case 6: return 8; 
+break;
+case 7: this.popState(); return 5; 
+break;
+case 8: this.popState(); return 8; 
+break;
+case 9: this.begin("doubleQuote"); return 8; 
+break;
+case 10: return 8; 
+break;
+case 11: this.popState(); return 5; 
+break;
+case 12: this.popState(); return 8; 
+break;
+case 13: this.begin("singleQuote"); return 8; 
+break;
+case 14: return 8; 
+break;
+case 15: this.popState(); return 5; 
+break;
+case 16: this.popState(); return 8; 
+break;
+case 17: this.begin("backTick"); return 8; 
+break;
+case 18:
+                                                                        if (parser.yy.usePreceding) {
+                                                                          parser.yy.useDatabase = yy_.yytext;
+                                                                          parser.yy.usePreceding = false;
+                                                                        }
+                                                                        return 8;
+                                                                      
+break;
+case 19: this.popState(); return 5; 
+break;
+case 20: this.popState(); return 8; 
+break;
+case 21:
+                                                                        if (!parser.yy.firstToken) {
+                                                                          var firstWordMatch = yy_.yytext.match(/[a-zA-Z_]+/);
+                                                                          if (firstWordMatch) {
+                                                                            parser.yy.firstToken = firstWordMatch[0];
+                                                                          }
+                                                                          var useDatabaseMatch = yy_.yytext.match(/USE\s+(\S+)/i);
+                                                                          if (useDatabaseMatch) {
+                                                                            parser.yy.useDatabase = useDatabaseMatch[1];
+                                                                          } else {
+                                                                            // For backticked
+                                                                            parser.yy.usePreceding = /USE/i.test(yy_.yytext);
+                                                                          }
+                                                                        } else if (parser.yy.usePreceding) {
+                                                                          parser.yy.useDatabase = yy_.yytext;
+                                                                          parser.yy.usePreceding = false;
+                                                                        }
+                                                                        return 8;
+                                                                      
+break;
+case 22: return 8; 
+break;
+case 23: return 8; 
+break;
+case 24: return 9; 
+break;
+case 25: return 5; 
+break;
+case 26: /* To prevent console logging of unknown chars */ 
+break;
+case 27:console.log(yy_.yytext);
+break;
+}
+},
+rules: [/^(?:\/\*)/,/^(?:[^*]+)/,/^(?:[*][^/])/,/^(?:$)/,/^(?:\*\/)/,/^(?:--)/,/^(?:[^\n]+)/,/^(?:$)/,/^(?:[\n])/,/^(?:")/,/^(?:(?:\\\\|\\["]|[^"])+)/,/^(?:$)/,/^(?:")/,/^(?:')/,/^(?:(?:\\\\|\\[']|[^'])+)/,/^(?:$)/,/^(?:')/,/^(?:`)/,/^(?:[^`]+)/,/^(?:$)/,/^(?:`)/,/^(?:[^"\/;'`-]+)/,/^(?:[-][^-]?)/,/^(?:;)/,/^(?:\/)/,/^(?:$)/,/^(?:.)/,/^(?:.)/],
+conditions: {"multiLineComment":{"rules":[1,2,3,4],"inclusive":false},"inLineComment":{"rules":[6,7,8],"inclusive":false},"singleQuote":{"rules":[14,15,16],"inclusive":false},"doubleQuote":{"rules":[10,11,12],"inclusive":false},"backTick":{"rules":[18,19,20],"inclusive":false},"INITIAL":{"rules":[0,5,9,13,17,21,22,23,24,25,26,27],"inclusive":true}}
+});
+return lexer;
+})();
+parser.lexer = lexer;
+function Parser () {
+  this.yy = {};
+}
+Parser.prototype = parser;parser.Parser = Parser;
+return new Parser;
+})();export default hplsqlStatementsParser;

+ 97 - 0
desktop/core/src/desktop/js/parse/hplsqlStatementsParser.test.js

@@ -0,0 +1,97 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. 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 hplsqlStatementsParser from './hplsqlStatementsParser';
+
+describe('hplsqlStatementsParser.js', () => {
+  const stringifySplitResult = function (result) {
+    let s = '[';
+    let first = true;
+    result.forEach(entry => {
+      s += first ? '{\n' : ', {\n';
+      s += "  statement: '" + entry.statement.replace(/\n/g, '\\n') + "',\n";
+      s +=
+        '  location: { first_line: ' +
+        entry.location.first_line +
+        ', first_column: ' +
+        entry.location.first_column +
+        ', last_line: ' +
+        entry.location.last_line +
+        ', last_column: ' +
+        entry.location.last_column +
+        ' }';
+      if (entry.firstToken) {
+        s += ",\n  firstToken: '" + entry.firstToken + "'";
+      }
+      if (entry.database) {
+        s += ",\n  database: '" + entry.database + "'";
+      }
+      s += '\n}';
+      first = false;
+    });
+    s += ']';
+    return s;
+  };
+
+  const testParser = function (input, expectedOutput) {
+    try {
+      expectedOutput.forEach(entry => {
+        entry.type = 'statement';
+      });
+      const result = hplsqlStatementsParser.parse(input);
+      const because =
+        '\n\nParser output: ' +
+        stringifySplitResult(result) +
+        '\nExpected output: ' +
+        stringifySplitResult(expectedOutput);
+
+      expect(result).toEqual(expectedOutput, because);
+    } catch (error) {
+      console.error(error);
+      console.warn(error.message);
+
+      fail('Got error');
+    }
+  };
+
+  it('should split "CREATE PROCEDURE greet(name STRING)\\nBEGIN\\n  PRINT \'Hello \' || name;END;" correctly', () => {
+    testParser("CREATE PROCEDURE greet(name STRING)\nBEGIN\n  PRINT 'Hello ' || name;END;", [
+      {
+        statement: "CREATE PROCEDURE greet(name STRING)\nBEGIN\n  PRINT 'Hello ' || name;END;",
+        location: { first_line: 1, first_column: 0, last_line: 3, last_column: 29 },
+        firstToken: 'CREATE'
+      }
+    ]);
+  });
+
+  it('should split "CREATE PROCEDURE greet(name STRING)\\nBEGIN\\n  PRINT \'Hello \' || name;\\nEND;/\\nprint "world";" correctly', () => {
+    testParser(
+      "CREATE PROCEDURE greet(name STRING)\nBEGIN\n  PRINT 'Hello ' || name;\nEND;/\nprint 'world';",
+      [
+        {
+          statement: "CREATE PROCEDURE greet(name STRING)\nBEGIN\n  PRINT 'Hello ' || name;\nEND;;",
+          location: { first_line: 1, first_column: 0, last_line: 4, last_column: 5 },
+          firstToken: 'CREATE'
+        },
+        {
+          statement: "\nprint 'world';",
+          location: { first_line: 4, first_column: 5, last_line: 5, last_column: 14 },
+          firstToken: 'print'
+        }
+      ]
+    );
+  });
+});

+ 198 - 0
desktop/core/src/desktop/js/parse/jison/hplsqlStatementsParser.jison

@@ -0,0 +1,198 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. 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.
+
+%lex
+%options flex
+%x multiLineComment inLineComment singleQuote doubleQuote backTick
+%%
+
+'/*'                                                                  { this.begin("multiLineComment"); return 'PART_OF_STATEMENT'; }
+<multiLineComment>[^*]+                                               { return 'PART_OF_STATEMENT'; }
+<multiLineComment>[*][^/]                                             { return 'PART_OF_STATEMENT'; }
+<multiLineComment><<EOF>>                                             { this.popState(); return 'PART_OF_STATEMENT'; }
+<multiLineComment>'*/'                                                { this.popState(); return 'PART_OF_STATEMENT'; }
+
+'--'                                                                  { this.begin("inLineComment"); return 'PART_OF_STATEMENT'; }
+<inLineComment>[^\n]+                                                 { return 'PART_OF_STATEMENT'; }
+<inLineComment><<EOF>>                                                { this.popState(); return 'EOF'; }
+<inLineComment>[\n]                                                   { this.popState(); return 'PART_OF_STATEMENT'; }
+
+'"'                                                                   { this.begin("doubleQuote"); return 'PART_OF_STATEMENT'; }
+<doubleQuote>(?:\\\\|\\["]|[^"])+                                     { return 'PART_OF_STATEMENT'; }
+<doubleQuote><<EOF>>                                                  { this.popState(); return 'EOF'; }
+<doubleQuote>'"'                                                      { this.popState(); return 'PART_OF_STATEMENT'; }
+
+'\''                                                                  { this.begin("singleQuote"); return 'PART_OF_STATEMENT'; }
+<singleQuote>(?:\\\\|\\[']|[^'])+                                     { return 'PART_OF_STATEMENT'; }
+<singleQuote><<EOF>>                                                  { this.popState(); return 'EOF'; }
+<singleQuote>'\''                                                     { this.popState(); return 'PART_OF_STATEMENT'; }
+
+'`'                                                                   { this.begin("backTick"); return 'PART_OF_STATEMENT'; }
+<backTick>[^`]+                                                       {
+                                                                        if (parser.yy.usePreceding) {
+                                                                          parser.yy.useDatabase = yytext;
+                                                                          parser.yy.usePreceding = false;
+                                                                        }
+                                                                        return 'PART_OF_STATEMENT';
+                                                                      }
+<backTick><<EOF>>                                                     { this.popState(); return 'EOF'; }
+<backTick>'`'                                                         { this.popState(); return 'PART_OF_STATEMENT'; }
+
+[^"\/;'`-]+                                                           {
+                                                                        if (!parser.yy.firstToken) {
+                                                                          var firstWordMatch = yytext.match(/[a-zA-Z_]+/);
+                                                                          if (firstWordMatch) {
+                                                                            parser.yy.firstToken = firstWordMatch[0];
+                                                                          }
+                                                                          var useDatabaseMatch = yytext.match(/USE\s+(\S+)/i);
+                                                                          if (useDatabaseMatch) {
+                                                                            parser.yy.useDatabase = useDatabaseMatch[1];
+                                                                          } else {
+                                                                            // For backticked
+                                                                            parser.yy.usePreceding = /USE/i.test(yytext);
+                                                                          }
+                                                                        } else if (parser.yy.usePreceding) {
+                                                                          parser.yy.useDatabase = yytext;
+                                                                          parser.yy.usePreceding = false;
+                                                                        }
+                                                                        return 'PART_OF_STATEMENT';
+                                                                      }
+[-][^-]?                                                             { return 'PART_OF_STATEMENT'; }
+
+';'                                                                   { return 'PART_OF_STATEMENT'; }
+
+'/'                                                                   { return '/'; }
+
+<<EOF>>                                                               { return 'EOF'; }
+
+.                                                                     { /* To prevent console logging of unknown chars */ }
+/lex
+
+%start HplsqlStatementsParser
+
+%%
+
+HplsqlStatementsParser
+ : Statements 'EOF'
+   {
+     parser.removeTrailingWhiteSpace($1);
+     return $1;
+   }
+ | OneOrMoreSeparators Statements 'EOF'
+   {
+     parser.handleLeadingStatements($1, $2);
+     parser.removeTrailingWhiteSpace($2);
+     return $2;
+   }
+ | OneOrMoreSeparators Statements OneOrMoreSeparators 'EOF'
+   {
+     parser.handleLeadingStatements($1, $2);
+     parser.handleTrailingStatements($2, $3);
+     parser.removeTrailingWhiteSpace($2);
+     return $2;
+   }
+ | Statements OneOrMoreSeparators 'EOF'
+   {
+     parser.handleTrailingStatements($1, $2);
+     parser.removeTrailingWhiteSpace($1);
+     return $1;
+   }
+ | OneOrMoreSeparators 'EOF'
+   {
+     var result = [];
+     parser.handleLeadingStatements($1, result);
+     return result;
+   }
+ | 'EOF'
+   {
+     var result = [];
+     parser.addEntry(result, 'statement', $1, @1);
+     return result;
+   }
+ ;
+
+Statements
+ : StatementParts
+   {
+     $$ = [];
+     parser.addEntry($$, 'statement', $1, @1);
+   }
+ | Statements OneOrMoreSeparators StatementParts
+   {
+     parser.handleTrailingStatements($1, $2);
+     parser.addEntry($1, 'statement', $3, @3);
+   }
+ ;
+
+StatementParts
+ : 'PART_OF_STATEMENT'
+ | StatementParts 'PART_OF_STATEMENT'                              -> $1 + $2;
+ ;
+
+OneOrMoreSeparators
+ : '/'                                                             -> [@1]
+ | OneOrMoreSeparators '/'
+   {
+     $1.push(@2);
+   }
+ ;
+
+%%
+
+parser.addEntry = function (statements, type, statement, location) {
+  if (parser.yy.firstToken) {
+    if (parser.yy.useDatabase) {
+      statements.push({ type: type, statement: statement, location: location, firstToken: parser.yy.firstToken, database: parser.yy.useDatabase });
+      delete parser.yy.useDatabase;
+    } else {
+      statements.push({ type: type, statement: statement, location: location, firstToken: parser.yy.firstToken });
+    }
+    delete parser.yy.firstToken;
+  } else {
+    statements.push({ type: type, statement: statement, location: location });
+  }
+}
+
+parser.handleLeadingStatements = function (emptyStatements, result) {
+  for (var i = emptyStatements.length - 1; i >= 0; i--) {
+    result.unshift({ type: 'statement', statement: ';', location: emptyStatements[i] });
+  }
+}
+
+parser.handleTrailingStatements = function (result, emptyStatements) {
+  var lastStatement = result[result.length - 1];
+  lastStatement.statement += ';'
+  lastStatement.location = {
+    first_line: lastStatement.location.first_line,
+    first_column: lastStatement.location.first_column,
+    last_line: emptyStatements[0].last_line,
+    last_column: emptyStatements[0].last_column
+  }
+  if (emptyStatements.length > 1) {
+    for (var i = 1; i < emptyStatements.length; i++) {
+      result.push({ type: 'statement', statement: ';', location: emptyStatements[i] });
+    }
+  }
+}
+
+parser.removeTrailingWhiteSpace = function (result) {
+  if (result.length > 1) {
+      var lastStatement = result[result.length - 1];
+      if (/^\s+$/.test(lastStatement.statement)) {
+        result.pop()
+      }
+  }
+}

+ 16 - 0
tools/jison/generateParsers.js

@@ -103,6 +103,22 @@ const parserDefinitions = {
             'export default sqlStatementsParser;\n'
         );
       })
+  },
+  hplsqlStatementsParser: {
+    sources: ['hplsqlStatementsParser.jison'],
+    target: 'hplsqlStatementsParser.jison',
+    outputFolder: OUTPUT_FOLDER,
+    afterParse: contents =>
+      new Promise(resolve => {
+        resolve(
+          LICENSE +
+            contents.replace(
+              'parse: function parse',
+              SQL_STATEMENTS_PARSER_JSDOC + 'parse: function parse'
+            ) +
+            'export default hplsqlStatementsParser;\n'
+        );
+      })
   }
 };