diff --git a/README.md b/README.md index cfc04a2..e2a88b8 100644 --- a/README.md +++ b/README.md @@ -91,3 +91,4 @@ hive,impala等语法解析文件来自[Hue](https://github.com/cloudera/hue) - 1.1.8 添加转义字符支持 - 1.1.9 添加函数的中括号语法支持( split(nameList)[0] ) +- 1.2.0 添加 ts,添加测试 \ No newline at end of file diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..f8b3b51 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,185 @@ +// For a detailed explanation regarding each configuration property, visit: +// https://jestjs.io/docs/en/configuration.html + +module.exports = { + // All imported modules in your tests should be mocked automatically + // automock: false, + + // Stop running tests after `n` failures + // bail: 0, + + // Respect "browser" field in package.json when resolving modules + // browser: false, + + // The directory where Jest should store its cached dependency information + // cacheDirectory: "/private/var/folders/xr/54w2mws93hj3p3_ysc347flc0000gn/T/jest_dx", + + // Automatically clear mock calls and instances between every test + // clearMocks: false, + + // Indicates whether the coverage information should be collected while executing the test + // collectCoverage: false, + + // An array of glob patterns indicating a set of files for which coverage information should be collected + // collectCoverageFrom: null, + + // The directory where Jest should output its coverage files + // coverageDirectory: null, + + // An array of regexp pattern strings used to skip coverage collection + coveragePathIgnorePatterns: [ + "/node_modules/" + ], + + // A list of reporter names that Jest uses when writing coverage reports + // coverageReporters: [ + // "json", + // "text", + // "lcov", + // "clover" + // ], + + // An object that configures minimum threshold enforcement for coverage results + // coverageThreshold: null, + + // A path to a custom dependency extractor + // dependencyExtractor: null, + + // Make calling deprecated APIs throw helpful error messages + // errorOnDeprecated: false, + + // Force coverage collection from ignored files using an array of glob patterns + // forceCoverageMatch: [], + + // A path to a module which exports an async function that is triggered once before all test suites + // globalSetup: null, + + // A path to a module which exports an async function that is triggered once after all test suites + // globalTeardown: null, + + // A set of global variables that need to be available in all test environments + // globals: {}, + + // An array of directory names to be searched recursively up from the requiring module's location + // moduleDirectories: [ + // "node_modules" + // ], + + // An array of file extensions your modules use + // moduleFileExtensions: [ + // "js", + // "json", + // "jsx", + // "ts", + // "tsx", + // "node" + // ], + + // A map from regular expressions to module names that allow to stub out resources with a single module + // moduleNameMapper: {}, + + // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader + // modulePathIgnorePatterns: [], + + // Activates notifications for test results + // notify: false, + + // An enum that specifies notification mode. Requires { notify: true } + // notifyMode: "failure-change", + + // A preset that is used as a base for Jest's configuration + // preset: null, + + // Run tests from one or more projects + // projects: null, + + // Use this configuration option to add custom reporters to Jest + // reporters: undefined, + + // Automatically reset mock state between every test + // resetMocks: false, + + // Reset the module registry before running each individual test + // resetModules: false, + + // A path to a custom resolver + // resolver: null, + + // Automatically restore mock state between every test + // restoreMocks: false, + + // The root directory that Jest should scan for tests and modules within + // rootDir: null, + + // A list of paths to directories that Jest should use to search for files in + // roots: [ + // "" + // ], + + // Allows you to use a custom runner instead of Jest's default test runner + // runner: "jest-runner", + + // The paths to modules that run some code to configure or set up the testing environment before each test + // setupFiles: [], + + // A list of paths to modules that run some code to configure or set up the testing framework before each test + // setupFilesAfterEnv: [], + + // A list of paths to snapshot serializer modules Jest should use for snapshot testing + // snapshotSerializers: [], + + // The test environment that will be used for testing + testEnvironment: "node", + + // Options that will be passed to the testEnvironment + // testEnvironmentOptions: {}, + + // Adds a location field to test results + // testLocationInResults: false, + + // The glob patterns Jest uses to detect test files + // testMatch: [ + // "**/__tests__/**/*.[jt]s?(x)", + // "**/?(*.)+(spec|test).[tj]s?(x)" + // ], + + // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped + testPathIgnorePatterns: [ + "/node_modules/" + ], + + // The regexp pattern or array of patterns that Jest uses to detect test files + // testRegex: [], + + // This option allows the use of a custom results processor + // testResultsProcessor: null, + + // This option allows use of a custom test runner + // testRunner: "jasmine2", + + // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href + // testURL: "http://localhost", + + // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" + // timers: "real", + + // A map from regular expressions to paths to transformers + // transform: null, + + // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation + transformIgnorePatterns: [ + "/node_modules/" + ], + + // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them + // unmockedModulePathPatterns: undefined, + + // Indicates whether each individual test should be reported during the run + // verbose: null, + + // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode + // watchPathIgnorePatterns: [], + + // Whether to use watchman for file crawling + // watchman: true, +}; diff --git a/lib/lib/parser.js b/lib/lib/parser.js index 0b98cc3..fa2d80c 100644 --- a/lib/lib/parser.js +++ b/lib/lib/parser.js @@ -3,40 +3,33 @@ Object.defineProperty(exports, "__esModule", { value: true }); // import * as sqlSyntaxParser from '../core/sqlSyntaxParser'; const sqlSyntaxParser = require("../core/sqlSyntaxParser"); const sqlAutoCompleteParser = require("../core/sqlAutoCompleteParser"); +var sqlType; +(function (sqlType) { + sqlType["Hive"] = "hive"; + sqlType["None"] = "sql"; + sqlType["Impala"] = "impala"; +})(sqlType || (sqlType = {})); +function sqlToParserArgs(sql) { + let preSql = '', sufSql = ''; + if (Object.prototype.toString.call(sql) == '[object Array]') { + preSql = sql[0]; + sufSql = sql[1]; + } + return [preSql, sufSql]; +} /** - * 自动补全提示 - * @param {(string | Array)} sql - * @param {string} [type="hive"] + * 校验语法 */ -function parseSyntax(sql, type) { - if (typeof type == "undefined") { - type = "hive"; - } - let sql1 = sql; - let sql2 = ''; - if (sql instanceof Array) { - sql1 = sql[0]; - sql2 = sql[1]; - } - // @ts-nocheck - return sqlSyntaxParser.parser.parseSyntax(sql1, sql2, type, false); +function parseSyntax(sql, type = sqlType.Hive) { + const parserArgs = sqlToParserArgs(sql); + return sqlSyntaxParser.parser.parseSyntax(parserArgs[0], parserArgs[1], type, false); } exports.parseSyntax = parseSyntax; /** * 自动补全提示 - * @param {(string | Array)} sql - * @param {string} [type="hive"] */ -function parserSql(sql, type) { - if (typeof type == "undefined") { - type = "hive"; - } - let sql1 = sql; - let sql2 = ''; - if (sql instanceof Array) { - sql1 = sql[0]; - sql2 = sql[1]; - } - return sqlAutoCompleteParser.parser.parseSql(sql1, sql2, type, false); +function parserSql(sql, type = sqlType.Hive) { + const parserArgs = sqlToParserArgs(sql); + return sqlAutoCompleteParser.parser.parseSql(parserArgs[0], parserArgs[1], type, false); } exports.parserSql = parserSql; diff --git a/package.json b/package.json index f0c4f0b..df0614a 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,29 @@ { "name": "dt-sql-parser", - "version": "1.1.10", + "version": "1.2.0", "description": "sql,hive,parser ", - "keywords":["hive","hql","sql","parser"], - "main": "index.js", + "keywords": [ + "hive", + "hql", + "sql", + "parser" + ], + "main": "lib/index.js", "scripts": { "build:parse": "pegjs -o core/astParser.js peg/nquery.pegjs ", "build:filter": "pegjs -o core/comment.js peg/comment.pegjs ", "build:syntax": "sh ./jison/hue-sql-syntax.sh ", - "build": "rm -rf lib && tsc" + "build": "jest && rm -rf lib && tsc", + "test": "jest" }, "author": "xiaokang", "license": "ISC", "devDependencies": { + "@types/jest": "^24.0.13", + "jest": "^24.8.0", "node-query": "^0.3.3", "pegjs": "^0.10.0" }, - "git repository":"https://github.com/HSunboy/dt-sql-parser", + "git repository": "https://github.com/HSunboy/dt-sql-parser", "dependencies": {} } diff --git a/src/core/sqlAutoCompleteParser.d.ts b/src/core/sqlAutoCompleteParser.d.ts index f61fb8e..511460e 100644 --- a/src/core/sqlAutoCompleteParser.d.ts +++ b/src/core/sqlAutoCompleteParser.d.ts @@ -1,4 +1,32 @@ +interface Location { + first_column: number, + first_line: number, + last_column: number, + last_line: number +} +declare enum sqlType { + Hive = 'hive', + None = 'sql', + Impala = 'impala' +} +interface Locations { + // 语句类型 + type: string; + // 语法是否缺失 + missing?: Boolean; + // 语句位置 + location: Location; +} -export const parser:{ - parseSql:Function -} \ No newline at end of file +interface SuggestKeyword { + value: string; + weight: number; +} +interface CompleteParser { + parseSql: (preSql: string, sufSql: string, type: sqlType, d: Boolean) => CompleteResult +} +export interface CompleteResult { + locations: Locations[]; + suggestKeywords: SuggestKeyword[]; +} +export const parser:CompleteParser; diff --git a/src/core/sqlSyntaxParser.d.ts b/src/core/sqlSyntaxParser.d.ts index ea5a024..38ab362 100644 --- a/src/core/sqlSyntaxParser.d.ts +++ b/src/core/sqlSyntaxParser.d.ts @@ -1,3 +1,29 @@ -export const parser:{ - parseSyntax:Function -} \ No newline at end of file +import { Locations, SuggestKeyword } from "./sqlAutoCompleteParser"; +declare enum sqlType { + Hive = 'hive', + None = 'sql', + Impala = 'impala' +} +interface SyntaxParser { + parseSyntax: (preSql: string, sufSql: string, type: sqlType, d: Boolean) => SyntaxResult | false +} + +interface ExpectedWord { + text: string; + distance: number; +} + +interface ErrorLoc { + first_column: number; + first_line: number; + last_column: number; + last_line: number; +} +export interface SyntaxResult { + text: string; + expected: ExpectedWord[]; + incompleteStatement: Boolean; + loc: ErrorLoc, + token: string +} +export const parser: SyntaxParser; diff --git a/src/lib/parser.ts b/src/lib/parser.ts index 086fbd1..041810d 100644 --- a/src/lib/parser.ts +++ b/src/lib/parser.ts @@ -1,41 +1,35 @@ // import * as sqlSyntaxParser from '../core/sqlSyntaxParser'; import * as sqlSyntaxParser from '../core/sqlSyntaxParser'; import * as sqlAutoCompleteParser from '../core/sqlAutoCompleteParser'; + +type sql = string | string []; +enum sqlType { + Hive = 'hive', + None = 'sql', + Impala = 'impala' +} +function sqlToParserArgs (sql: sql) { + let preSql = '', sufSql = ''; + if(Object.prototype.toString.call(sql) == '[object Array]'){ + preSql=sql[0]; + sufSql=sql[1]; + } + return [preSql, sufSql]; +} /** - * 自动补全提示 - * @param {(string | Array)} sql - * @param {string} [type="hive"] + * 校验语法 */ -function parseSyntax(sql, type) { - if (typeof type == "undefined") { - type = "hive" - } - let sql1=sql; - let sql2=''; - if(sql instanceof Array){ - sql1=sql[0]; - sql2=sql[1]; - } - // @ts-nocheck - return sqlSyntaxParser.parser.parseSyntax(sql1, sql2, type, false) +function parseSyntax(sql: sql, type:sqlType = sqlType.Hive): sqlSyntaxParser.SyntaxResult | false { + const parserArgs = sqlToParserArgs(sql); + return sqlSyntaxParser.parser.parseSyntax(parserArgs[0], parserArgs[1], type, false) } /** * 自动补全提示 - * @param {(string | Array)} sql - * @param {string} [type="hive"] */ -function parserSql(sql, type) { - if (typeof type == "undefined") { - type = "hive" - } - let sql1=sql; - let sql2=''; - if(sql instanceof Array){ - sql1=sql[0]; - sql2=sql[1]; - } - return sqlAutoCompleteParser.parser.parseSql(sql1, sql2, type, false) +function parserSql(sql: sql, type:sqlType = sqlType.Hive): sqlAutoCompleteParser.CompleteResult { + const parserArgs = sqlToParserArgs(sql); + return sqlAutoCompleteParser.parser.parseSql(parserArgs[0], parserArgs[1], type, false) } export { diff --git a/src/peg/comment.pegjs b/src/peg/comment.pegjs index c87fb06..a9261f2 100644 --- a/src/peg/comment.pegjs +++ b/src/peg/comment.pegjs @@ -13,10 +13,10 @@ start union_stmt -=stmt: +=single_stmt: ( - words:(!kw_start word:. {return word})* - stmt:(comment:comment {return ''}/quote:quote {return quote}/";" {isSplit=true;return ";"}) + words:text* + stmt:(comment:comment {return comment}/quote:quote {return quote}/";" {isSplit=true;return ";"}) { const text=words.join("")+stmt; let index=Math.max(lines.length-1,0); @@ -29,9 +29,9 @@ union_stmt } )* other:.* { - const text=stmt.join("")+other.join("") + const text=single_stmt.join("")+other.join("") let index=Math.max(lines.length-1,0); - lines[index]=lines[index]+other.join(""); + lines[index]=(lines[index]||'')+other.join(""); return text; } @@ -75,7 +75,7 @@ quote - +text = !kw_start word:. {return word} kw_start=KW_SINGLE_LINE_START/KW_MULTI_LINE_START/"\""/"'"/";" diff --git a/src/peg/nquery.pegjs b/src/peg/nquery.pegjs deleted file mode 100644 index 84a0f6a..0000000 --- a/src/peg/nquery.pegjs +++ /dev/null @@ -1,1043 +0,0 @@ -// (C) 2011-2012 Alibaba Group Holding Limited. -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// version 2 as published by the Free Software Foundation. - -// Author :windyrobin - -{ - var util = require('util'); - - function debug(str){ - console.log(str); - } - - function inspect(obj){ - console.log(util.inspect(obj, false, 10)); - } - - function createUnaryExpr(op, e) { - return { - type : 'unary_expr', - operator : op, - expr : e - } - } - - function createBinaryExpr(op, left, right) { - return { - type : 'binary_expr', - operator : op, - left : left, - right : right - } - } - - function createList(head, tail) { - var result = [head]; - for (var i = 0; i < tail.length; i++) { - result.push(tail[i][3]); - } - return result; - } - - function createExprList(head, tail, room) { - var epList = createList(head, tail); - var exprList = []; - var ep; - for (var i = 0; i < epList.length; i++) { - ep = epList[i]; - //the ep has already added to the global params - if (ep.type == 'param') { - ep.room = room; - ep.pos = i; - } else { - exprList.push(ep); - } - } - return exprList; - } - - function createBinaryExprChain(head, tail) { - var result = head; - for (var i = 0; i < tail.length; i++) { - result = createBinaryExpr(tail[i][1], result, tail[i][3]); - } - return result; - } - - var reservedMap = { - 'SHOW' : true, - 'DROP' : true, - 'SELECT' : true, - 'UPDATE' : true, - 'CREATE' : true, - 'DELETE' : true, - 'INSERT' : true, - 'REPLACE' : true, - 'EXPLAIN' : true, - 'ALL' : true, - 'DISTINCT': true, - 'AS' : true, - 'TABLE' : true, - 'INTO' : true, - 'FROM' : true, - 'SET' : true, - 'LEFT' : true, - 'ON' : true, - 'INNER' : true, - 'JOIN' : true, - 'UNION' : true, - 'VALUES' : true, - 'EXISTS' : true, - 'WHERE' : true, - 'GROUP' : true, - 'BY' : true, - 'HAVING' : true, - 'ORDER' : true, - 'ASC' : true, - 'DESC' : true, - 'LIMIT' : true, - 'BETWEEN' : true, - 'IN' : true, - 'IS' : true, - 'LIKE' : true, - 'CONTAINS': true, - 'NOT' : true, - 'AND' : true, - 'OR' : true, - - //literal - 'TRUE' : true, - 'FALSE' : true, - 'NULL' : true - } - - var cmpPrefixMap = { - '+' : true, - '-' : true, - '*' : true, - '/' : true, - '>' : true, - '<' : true, - '!' : true, - '=' : true, - - //between - 'B' : true, - 'b' : true, - //for is or in - 'I' : true, - 'i' : true, - //for like - 'L' : true, - 'l' : true, - //for not - 'N' : true, - 'n' : true, - //for contains - 'C' : true, - 'c' : true, - } - - //used for store refered parmas - var params = []; - - //used for dependency analysis - var varList = []; -} - -start - = &init __ ast:(union_stmt / update_stmt / replace_insert_stmt) { - return { - ast : ast, - param : params - } - } - /ast:proc_stmts { - return { - ast : ast - } - } - -init = & { params = []; return true; } - -union_stmt - = head:select_stmt tail:(__ KW_UNION __ select_stmt)* { - var cur = head; - for (var i = 0; i < tail.length; i++) { - cur._next = tail[i][3]; - cur = cur._next - } - return head; - } - -select_stmt - = select_stmt_nake - / s:('(' __ select_stmt __ ')') { - return s[2]; - } - -select_stmt_nake - = KW_SELECT __ - d:KW_DISTINCT? __ - c:column_clause __ - f:from_clause? __ - w:where_clause? __ - g:group_by_clause? __ - o:order_by_clause? __ - l:limit_clause? { - return { - type : 'select', - distinct : d, - columns : c, - from : f, - where : w, - groupby : g, - orderby : o, - limit : l - } - } - -column_clause - = (KW_ALL / (STAR !ident_start)) { - return '*'; - } - / head:column_list_item tail:(__ COMMA __ column_list_item)* { - return createList(head, tail); - } - -/** - * maybe you should use `expr` instead of `primary` or `additive_expr` - * to support complicated expression in column clause - */ -column_list_item - = e:additive_expr __ alias:alias_clause? { - return { - expr : e, - as : alias - }; - } - -alias_clause - = KW_AS? __ i:ident { return i; } - -from_clause - = KW_FROM __ l:table_ref_list { return l; } - -table_ref_list - = head:table_base - tail:table_ref* { - tail.unshift(head); - return tail; - } - -table_ref - = __ COMMA __ t:table_base { return t; } - / __ t:table_join { return t; } - - -table_join - = op:join_op __ t:table_base __ expr:on_clause? { - t.join = op; - t.on = expr; - return t; - /* - return { - db : t.db, - table : t.table, - as : t.as, - join : op, - on : expr - } - */ - } - -//NOTE that ,the table assigned to `var` shouldn't write in `table_join` -table_base - = t:table_name __ KW_AS? __ alias:ident? { - if (t.type == 'var') { - t.as = alias; - return t; - } else { - return { - db : t.db, - table : t.table, - as : alias - } - } - } - -join_op - = KW_LEFT __ KW_JOIN { return 'LEFT JOIN'; } - / (KW_INNER __)? KW_JOIN { return 'INNER JOIN'; } - -table_name - = dt:ident tail:(__ DOT __ ident_name)? { - var obj = { - db : '', - table : dt - } - if (tail) { - obj.db = dt; - obj.table = tail[3]; - } - return obj; - } - /v:var_decl { - v.db = ''; - v.table = v.name; - return v; - } - -on_clause - = KW_ON __ e:expr { return e; } - -where_clause - = KW_WHERE __ e:expr { return e; } - -group_by_clause - = KW_GROUP __ KW_BY __ l:column_ref_list { return l; } - -column_ref_list - = head:column_ref tail:(__ COMMA __ column_ref)* { - return createList(head, tail); - } - -having_clause - = KW_HAVING e:expr { return e; } - -order_by_clause - = KW_ORDER __ KW_BY __ l:order_by_list { return l; } - -order_by_list - = head:order_by_element tail:(__ COMMA __ order_by_element)* { - return createList(head, tail); - } - -order_by_element - = e:expr __ d:(KW_DESC / KW_ASC)? { - var obj = { - expr : e, - type : 'ASC' - } - if (d == 'DESC') { - obj.type = 'DESC'; - } - return obj; - } - -number_or_param - = literal_numeric - / param - -limit_clause - = KW_LIMIT __ i1:(number_or_param) __ tail:(COMMA __ number_or_param)? { - var res = [i1]; - if (tail == '') { - res.unshift({ - type : 'number', - value : 0 - }); - } else { - res.push(tail[2]); - } - return res; - } - -update_stmt - = KW_UPDATE __ - t:table_name __ - KW_SET __ - l:set_list __ - w:where_clause { - return { - type : 'update', - db : t.db, - table : t.table, - set : l, - where : w - } - } - -set_list - = head:set_item tail:(__ COMMA __ set_item)* { - return createList(head, tail); - } - -/** - * here only use `additive_expr` to support 'col1 = col1+2' - * if you want to use lower operator, please use '()' like below - * 'col1 = (col2 > 3)' - */ -set_item - = c:column_name __ '=' __ v:additive_expr { - return { - column: c, - value : v - } - } - -replace_insert_stmt - = ri:replace_insert __ - KW_INTO __ - t:table_name __ LPAREN __ - c:column_list __ RPAREN __ - v:value_clause { - return { - type : ri, - db : t.db, - table : t.table, - columns : c, - values : v - } - } - -replace_insert - = KW_INSERT { return 'insert'; } - / KW_REPLACE { return 'replace' } - -value_clause - = KW_VALUES __ l:value_list { return l; } - -value_list - = head:value_item tail:(__ COMMA __ value_item)* { - return createList(head, tail); - } - -value_item - = LPAREN __ l:expr_list __ RPAREN { - return l; - } - -//for template auto fill -expr_list - = head:expr tail:(__ COMMA __ expr)*{ - var el = { - type : 'expr_list' - } - var l = createExprList(head, tail, el); - - el.value = l; - return el; - } - -expr_list_or_empty - = l:expr_list - / & { - return { - type : 'expr_list', - value : [] - } - } - -/** - * Borrowed from PL/SQL ,the priority of below list IS ORDER BY DESC - * --------------------------------------------------------------------------------------------------- - * | +, - | identity, negation | - * | *, / | multiplication, division | - * | +, - | addition, subtraction, concatenation | - * | =, <, >, <=, >=, <>, !=, IS, LIKE, BETWEEN, IN, CONTAINS | comparion | - * | !, NOT | logical negation | - * | AND | conjunction | - * | OR | inclusion | - * --------------------------------------------------------------------------------------------------- - */ - -expr = or_expr - -or_expr - = head:and_expr tail:(__ KW_OR __ and_expr)* { - return createBinaryExprChain(head, tail); - } - -and_expr - = head:not_expr tail:(__ KW_AND __ not_expr)* { - return createBinaryExprChain(head, tail); - } - -//here we should use `NOT` instead of `comparision_expr` to support chain-expr -not_expr - = (KW_NOT / "!" !"=") __ expr:not_expr { - return createUnaryExpr('NOT', expr); - } - / comparison_expr - -comparison_expr - = left:additive_expr __ rh:comparison_op_right? { - if (rh == '') { - return left; - } else { - var res = null; - if (rh.type == 'arithmetic') { - res = createBinaryExprChain(left, rh.tail); - } else { - res = createBinaryExpr(rh.op, left, rh.right); - } - return res; - } - } - -/* -//optimization for comparison judge, bug because we in use `additive` expr -//in column clause now , it have little effect -cmp_prefix_char - = c:char &{ debug(c); return cmpPrefixMap[c]; } - -comparison_op_right - = &cmp_prefix_char body:( - arithmetic_op_right - / in_op_right - / between_op_right - / is_op_right - / like_op_right - / contains_op_right - ){ - return body; - } -*/ - -comparison_op_right - = arithmetic_op_right - / in_op_right - / between_op_right - / is_op_right - / like_op_right - / contains_op_right - -arithmetic_op_right - = l:(__ arithmetic_comparison_operator __ additive_expr)+ { - return { - type : 'arithmetic', - tail : l - } - } - -arithmetic_comparison_operator - = ">=" / ">" / "<=" / "<>" / "<" / "=" / "!=" - -is_op_right - = op:KW_IS __ right:additive_expr { - return { - op : op, - right : right - } - } - -between_op_right - = op:KW_BETWEEN __ begin:additive_expr __ KW_AND __ end:additive_expr { - return { - op : op, - right : { - type : 'expr_list', - value : [begin, end] - } - } - } - -like_op - = nk:(KW_NOT __ KW_LIKE) { return nk[0] + ' ' + nk[2]; } - / KW_LIKE - -in_op - = nk:(KW_NOT __ KW_IN) { return nk[0] + ' ' + nk[2]; } - / KW_IN - -contains_op - = nk:(KW_NOT __ KW_CONTAINS) { return nk[0] + ' ' + nk[2]; } - / KW_CONTAINS - -like_op_right - = op:like_op __ right:comparison_expr { - return { - op : op, - right : right - } - } - -in_op_right - = op:in_op __ LPAREN __ l:expr_list __ RPAREN { - return { - op : op, - right : l - } - } - / op:in_op __ e:var_decl { - return { - op : op, - right : e - } - } - -contains_op_right - = op:contains_op __ LPAREN __ l:expr_list __ RPAREN { - return { - op : op, - right : l - } - } - / op:contains_op __ e:var_decl { - return { - op : op, - right : e - } - } - -additive_expr - = head:multiplicative_expr - tail:(__ additive_operator __ multiplicative_expr)* { - return createBinaryExprChain(head, tail); - } - -additive_operator - = "+" / "-" - -multiplicative_expr - = head:primary - tail:(__ multiplicative_operator __ primary)* { - return createBinaryExprChain(head, tail) - } - -multiplicative_operator - = "*" / "/" / "%" - -primary - = literal - / aggr_func - / func_call - / column_ref - / param - / LPAREN __ e:expr __ RPAREN { - e.paren = true; - return e; - } - / var_decl - -column_ref - = tbl:ident __ DOT __ col:column { - return { - type : 'column_ref', - table : tbl, - column : col - }; - } - / col:column { - return { - type : 'column_ref', - table : '', - column: col - }; - } - -column_list - = head:column tail:(__ COMMA __ column)* { - return createList(head, tail); - } - -ident = - name:ident_name !{ return reservedMap[name.toUpperCase()] === true; } { - return name; - } - -column = - name:column_name !{ return reservedMap[name.toUpperCase()] === true; } { - return name; - } - /'`' chars:[^`]+ '`' { - return chars.join(''); - } - -column_name - = start:ident_start parts:column_part* { return start + parts.join(''); } - -ident_name - = start:ident_start parts:ident_part* { return start + parts.join(''); } - -ident_start = [A-Za-z_] - -ident_part = [A-Za-z0-9_] - -//to support column name like `cf1:name` in hbase -column_part = [A-Za-z0-9_:] - - -param - = l:(':' ident_name) { - var p = { - type : 'param', - value: l[1] - } - //var key = 'L' + line + 'C' + column; - //debug(key); - //params[key] = p; - params.push(p); - return p; - } - -aggr_func - = aggr_fun_count - / aggr_fun_smma - -aggr_fun_smma - = name:KW_SUM_MAX_MIN_AVG __ LPAREN __ e:additive_expr __ RPAREN { - return { - type : 'aggr_func', - name : name, - args : { - expr : e - } - } - } - -KW_SUM_MAX_MIN_AVG - = KW_SUM / KW_MAX / KW_MIN / KW_AVG - -aggr_fun_count - = name:KW_COUNT __ LPAREN __ arg:count_arg __ RPAREN { - return { - type : 'aggr_func', - name : name, - args : arg - } - } - -count_arg - = e:star_expr { - return { - expr : e - } - } - / d:KW_DISTINCT? __ c:column_ref { - return { - distinct : d, - expr : c - } - } - -star_expr - = "*" { - return { - type : 'star', - value : '*' - } - } - -func_call - = name:ident __ LPAREN __ l:expr_list_or_empty __ RPAREN { - return { - type : 'function', - name : name, - args : l - } - } - -literal - = literal_string / literal_numeric / literal_bool /literal_null - -literal_list - = head:literal tail:(__ COMMA __ literal)* { - return createList(head, tail); - } - -literal_null - = KW_NULL { - return { - type : 'null', - value : null - }; - } - -literal_bool - = KW_TRUE { - return { - type : 'bool', - value : true - }; - } - / KW_FALSE { - return { - type : 'bool', - value : false - }; - } - -literal_string - = ca:( ('"' double_char* '"') - /("'" single_char* "'")) { - return { - type : 'string', - value : ca[1].join('') - } - } - -single_char - = [^'\\\0-\x1F\x7f] - / escape_char - -double_char - = [^"\\\0-\x1F\x7f] - / escape_char - -escape_char - = "\\'" { return "'"; } - / '\\"' { return '"'; } - / "\\\\" { return "\\"; } - / "\\/" { return "/"; } - / "\\b" { return "\b"; } - / "\\f" { return "\f"; } - / "\\n" { return "\n"; } - / "\\r" { return "\r"; } - / "\\t" { return "\t"; } - / "\\u" h1:hexDigit h2:hexDigit h3:hexDigit h4:hexDigit { - return String.fromCharCode(parseInt("0x" + h1 + h2 + h3 + h4)); - } - -line_terminator - = [\n\r] - -literal_numeric - = n:number { - return { - type : 'number', - value : n - } - } - -number - = int_:int frac:frac exp:exp __ { return parseFloat(int_ + frac + exp); } - / int_:int frac:frac __ { return parseFloat(int_ + frac); } - / int_:int exp:exp __ { return parseFloat(int_ + exp); } - / int_:int __ { return parseFloat(int_); } - -int - = digit19:digit19 digits:digits { return digit19 + digits; } - / digit:digit - / op:("-" / "+" ) digit19:digit19 digits:digits { return "-" + digit19 + digits; } - / op:("-" / "+" ) digit:digit { return "-" + digit; } - -frac - = "." digits:digits { return "." + digits; } - -exp - = e:e digits:digits { return e + digits; } - -digits - = digits:digit+ { return digits.join(""); } - -digit = [0-9] -digit19 = [1-9] - -hexDigit - = [0-9a-fA-F] - -e - = e:[eE] sign:[+-]? { return e + sign; } - - -KW_NULL = "NULL"i !ident_start -KW_TRUE = "TRUE"i !ident_start -KW_FALSE = "FALSE"i !ident_start - -KW_SHOW = "SHOW"i !ident_start -KW_DROP = "DROP"i !ident_start -KW_SELECT = "SELECT"i !ident_start -KW_UPDATE = "UPDATE"i !ident_start -KW_CREATE = "CREATE"i !ident_start -KW_DELETE = "DELETE"i !ident_start -KW_INSERT = "INSERT"i !ident_start -KW_REPLACE = "REPLACE"i !ident_start -KW_EXPLAIN = "EXPLAIN"i !ident_start - -KW_INTO = "INTO"i !ident_start -KW_FROM = "FROM"i !ident_start -KW_SET = "SET"i !ident_start - -KW_AS = "AS"i !ident_start -KW_TABLE = "TABLE"i !ident_start - -KW_ON = "ON"i !ident_start -KW_LEFT = "LEFT"i !ident_start -KW_INNER = "INNER"i !ident_start -KW_JOIN = "JOIN"i !ident_start -KW_UNION = "UNION"i !ident_start -KW_VALUES = "VALUES"i !ident_start - -KW_EXISTS = "EXISTS"i !ident_start - -KW_WHERE = "WHERE"i !ident_start - -KW_GROUP = "GROUP"i !ident_start -KW_BY = "BY"i !ident_start -KW_ORDER = "ORDER"i !ident_start -KW_HAVING = "HAVING"i !ident_start - -KW_LIMIT = "LIMIT"i !ident_start - -KW_ASC = "ASC"i !ident_start { return 'ASC'; } -KW_DESC = "DESC"i !ident_start { return 'DESC'; } - -KW_ALL = "ALL"i !ident_start { return 'ALL'; } -KW_DISTINCT = "DISTINCT"i !ident_start { return 'DISTINCT';} - -KW_BETWEEN = "BETWEEN"i !ident_start { return 'BETWEEN'; } -KW_IN = "IN"i !ident_start { return 'IN'; } -KW_IS = "IS"i !ident_start { return 'IS'; } -KW_LIKE = "LIKE"i !ident_start { return 'LIKE'; } -KW_CONTAINS = "CONTAINS"i !ident_start { return 'CONTAINS';} - -KW_NOT = "NOT"i !ident_start { return 'NOT'; } -KW_AND = "AND"i !ident_start { return 'AND'; } -KW_OR = "OR"i !ident_start { return 'OR'; } - -KW_COUNT = "COUNT"i !ident_start { return 'COUNT'; } -KW_MAX = "MAX"i !ident_start { return 'MAX'; } -KW_MIN = "MIN"i !ident_start { return 'MIN'; } -KW_SUM = "SUM"i !ident_start { return 'SUM'; } -KW_AVG = "AVG"i !ident_start { return 'AVG'; } - -//specail character -DOT = '.' -COMMA = ',' -STAR = '*' -LPAREN = '(' -RPAREN = ')' - -LBRAKE = '[' -RBRAKE = ']' - -__ = - whitespace* - -char = . - -whitespace = - [ \t\n\r] - -EOL - = EOF - / [\n\r]+ - -EOF = !. - -//begin procedure extension -proc_stmts - = proc_stmt* - -proc_stmt - = &proc_init __ s:(assign_stmt / return_stmt) { - return { - stmt : s, - vars: varList - } - } - -proc_init = & { varList = []; return true; } - -assign_stmt - = va:var_decl __ KW_ASSIGN __ e:proc_expr { - return { - type : 'assign', - left : va, - right: e - } - } - -return_stmt - = KW_RETURN __ e:proc_expr { - return { - type : 'return', - expr: e - } -} - -proc_expr - = select_stmt - / proc_join - / proc_additive_expr - / proc_array - -proc_additive_expr - = head:proc_multiplicative_expr - tail:(__ additive_operator __ proc_multiplicative_expr)* { - return createBinaryExprChain(head, tail); - } - -proc_multiplicative_expr - = head:proc_primary - tail:(__ multiplicative_operator __ proc_primary)* { - return createBinaryExprChain(head, tail); - } - -proc_join - = lt:var_decl __ op:join_op __ rt:var_decl __ expr:on_clause { - return { - type : 'join', - ltable : lt, - rtable : rt, - op : op, - on : expr - } - } - -proc_primary - = literal - / var_decl - / proc_func_call - / param - / LPAREN __ e:proc_additive_expr __ RPAREN { - e.paren = true; - return e; - } - -proc_func_call - = name:ident __ LPAREN __ l:proc_primary_list __ RPAREN { - //compatible with original func_call - return { - type : 'function', - name : name, - args : { - type : 'expr_list', - value : l - } - } - } - -proc_primary_list - = head:proc_primary tail:(__ COMMA __ proc_primary)* { - return createList(head, tail); - } - -proc_array = - LBRAKE __ l:proc_primary_list __ RBRAKE { - return { - type : 'array', - value : l - } - } - - -var_decl - = KW_VAR_PRE name:ident_name m:mem_chain { - //push for analysis - varList.push(name); - return { - type : 'var', - name : name, - members : m - } - } - -mem_chain - = l:('.' ident_name)* { - var s = []; - for (var i = 0; i < l.length; i++) { - s.push(l[i][1]); - } - return s; - } - - KW_VAR_PRE = '$' - - KW_RETURN = 'return'i - - KW_ASSIGN = ':=' diff --git a/tsconfig.json b/tsconfig.json index bb658d3..413046f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,5 +12,10 @@ "exclude": [ "./src/peg/*", "./src/jison/*" - ] + ], + "typeAcquisition": { + "include": [ + "jest" + ] + } } \ No newline at end of file