diff --git a/.gitignore b/.gitignore index ead3f1b5..28a91bcf 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,4 @@ fabric.properties \.idea/sonarlint-state\.xml \.idea/sonarlint\.xml +**/antlr/.antlr/** diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 4602afbd..4f38879f 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,7 +1,7 @@ - + diff --git a/.idea/misc.xml b/.idea/misc.xml index 84da703c..5bc2ae5a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/src/main/antlr/SDBLLexer.g4 b/src/main/antlr/SDBLLexer.g4 new file mode 100644 index 00000000..534370b1 --- /dev/null +++ b/src/main/antlr/SDBLLexer.g4 @@ -0,0 +1,320 @@ +/** + * This file is a part of BSL Parser. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov , Sergey Batanov + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Parser is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Parser is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Parser. + */ +lexer grammar SDBLLexer; + +@members { +public SDBLLexer(CharStream input, boolean crAwareCostructor) { + super(input); + _interp = new CRAwareLexerATNSimulator(this, _ATN); + validateInputStream(_ATN, input); +} +} + +// COMMONS +WHITE_SPACE: [ \t\f\r\n]+ -> channel(HIDDEN); +LINE_COMMENT: '//' ~[\r\n]* -> channel(HIDDEN); + +// SEPARATORS +DOT: '.' -> pushMode(DOT_MODE); +LPAREN: '('; +RPAREN: ')'; +SEMICOLON: ';'; +COMMA: ','; +ASSIGN: '='; +PLUS: '+'; +MINUS: '-'; +LESS_OR_EQUAL: '<='; +NOT_EQUAL: '<>'; +LESS: '<'; +GREATER_OR_EQUAL: '>='; +GREATER: '>'; +MUL: '*'; +QUOTIENT: '/'; +AMPERSAND: '&' -> pushMode(PARAMETER_MODE); +BAR: '|'; +BRACE: '{' -> pushMode(BRACE_MODE), channel(HIDDEN); + +// KEYWORDS RU EN +ALL: RU_V RU_S RU_E | A L L; +ALLOWED: RU_R RU_A RU_Z RU_R RU_E RU_SH RU_E RU_N RU_N RU_Y RU_E | A L L O W E D; +AND: RU_I | A N D; +AS: RU_K RU_A RU_K | A S; +ASC: RU_V RU_O RU_Z RU_R | A S C; +AUTOORDER: RU_A RU_V RU_T RU_O RU_U RU_P RU_O RU_R RU_YA RU_D RU_O + RU_CH RU_I RU_V RU_A RU_N RU_I RU_E | A U T O O R D E R; +BETWEEN: RU_M RU_E RU_ZH RU_D RU_U | B E T W E E N; +BY_EN: B Y; +CASE: RU_V RU_Y RU_B RU_O RU_R | C A S E; +CAST: RU_V RU_Y RU_R RU_A RU_Z RU_I RU_T RU_SOFT_SIGN | C A S T; +DESC: RU_U RU_B RU_Y RU_V | D E S C; +DISTINCT: RU_R RU_A RU_Z RU_L RU_I RU_CH RU_N RU_Y RU_E | D I S T I N C T; +DROP: RU_U RU_N RU_I RU_CH RU_T RU_O RU_ZH RU_I RU_T RU_SOFT_SIGN | D R O P; +ELSE: RU_I RU_N RU_A RU_CH RU_E | E L S E; +END: RU_K RU_O RU_N RU_E RU_C | E N D; +ESCAPE: RU_S RU_P RU_E RU_C RU_S RU_I RU_M RU_V RU_O RU_L | E S C A P E; +FALSE: RU_L RU_O RU_ZH RU_SOFT_SIGN | F A L S E; +FOR: RU_D RU_L RU_YA | F O R; +FROM: RU_I RU_Z | F R O M; +FULL: RU_P RU_O RU_L RU_N RU_O RU_E | F U L L; +GROUP: RU_S RU_G RU_R RU_U RU_P RU_P RU_I RU_R RU_O RU_V RU_A RU_T RU_SOFT_SIGN | G R O U P; +HAVING: RU_I RU_M RU_E RU_YU RU_SCH RU_I RU_E | H A V I N G; +HIERARCHY_EN: H I E R A R C H Y; +HIERARCHII_RU: RU_I RU_E RU_R RU_A RU_R RU_H RU_I RU_I; +HIERARCHYA_RU: RU_I RU_E RU_R RU_A RU_R RU_H RU_I RU_YA; +IN: RU_V | I N; +INDEX: RU_I RU_N RU_D RU_E RU_K RU_S RU_I RU_R RU_O RU_V RU_A RU_T RU_SOFT_SIGN | I N D E X; +INNER: RU_V RU_N RU_U RU_T RU_R RU_E RU_N RU_N RU_E RU_E | I N N E R; +INTO: RU_P RU_O RU_M RU_E RU_S RU_T RU_I RU_T RU_SOFT_SIGN | I N T O; +IS: RU_E RU_S RU_T RU_SOFT_SIGN | I S; +ISNULL: RU_E RU_S RU_T RU_SOFT_SIGN N U L L | I S N U L L; +JOIN: RU_S RU_O RU_E RU_D RU_I RU_N RU_E RU_N RU_I RU_E | J O I N; +LEFT: RU_L RU_E RU_V RU_O RU_E | L E F T; +LIKE: RU_P RU_O RU_D RU_O RU_B RU_N RU_O | L I K E; +NOT: RU_N RU_E | N O T; +NULL: N U L L; +OF: O F; +ON_EN: O N; +OR: RU_I RU_L RU_I | O R; +ORDER: RU_U RU_P RU_O RU_R RU_YA RU_D RU_O RU_CH RU_I RU_T RU_SOFT_SIGN | O R D E R; +OUTER: RU_V RU_N RU_E RU_SH RU_N RU_E RU_E | O U T E R; +OVERALL: RU_O RU_B RU_SCH RU_I RU_E | O V E R A L L; +PO_RU: RU_P RU_O; +RIGHT: RU_P RU_R RU_A RU_V RU_O RU_E | R I G H T; +SELECT: RU_V RU_Y RU_B RU_R RU_A RU_T RU_SOFT_SIGN | S E L E C T; +THEN: RU_T RU_O RU_G RU_D RU_A | T H E N; +TOP: RU_P RU_E RU_R RU_V RU_Y RU_E | T O P; +TOTALS: RU_I RU_T RU_O RU_G RU_I | T O T A L S; +TRUE: RU_I RU_S RU_T RU_I RU_N RU_A | T R U E; +UNDEFINED: RU_N RU_E RU_O RU_P RU_R RU_E RU_D RU_E RU_L RU_E RU_N RU_O | U N D E F I N E D; +UNION: RU_O RU_B RU_SOLID_SIGN RU_E RU_D RU_I RU_N RU_I RU_T RU_SOFT_SIGN | U N I O N; +WHEN: RU_K RU_O RU_G RU_D RU_A | W H E N; +WHERE: RU_G RU_D RU_E | W H E R E; + +// KEYWORDS RU EN +AVG: RU_S RU_R RU_E RU_D RU_N RU_E RU_E | A V G; +BEGINOFPERIOD: RU_N RU_A RU_CH RU_A RU_L RU_O RU_P RU_E RU_R RU_I RU_O RU_D RU_A | B E G I N O F P E R I O D; +BOOLEAN: RU_B RU_U RU_L RU_E RU_V RU_O | B O O L E A N; +COUNT: RU_K RU_O RU_L RU_I RU_CH RU_E RU_S RU_T RU_V RU_O | C O U N T; +DATE: RU_D RU_A RU_T RU_A | D A T E; +DATEADD: RU_D RU_O RU_B RU_A RU_V RU_I RU_T RU_SOFT_SIGN RU_K RU_D RU_A RU_T RU_E | D A T E A D D; +DATEDIFF: RU_R RU_A RU_Z RU_N RU_O RU_S RU_T RU_SOFT_SIGN RU_D RU_A RU_T | D A T E D I F F; +DATETIME: RU_D RU_A RU_T RU_A RU_V RU_R RU_E RU_M RU_YA | D A T E T I M E; +DAY: RU_D RU_E RU_N RU_SOFT_SIGN | D A Y; +DAYOFYEAR: RU_D RU_E RU_N RU_SOFT_SIGN RU_G RU_O RU_D RU_A | D A Y O F Y E A R; +EMPTYTABLE: RU_P RU_U RU_S RU_T RU_A RU_YA RU_T RU_A RU_B RU_L RU_I RU_C RU_A | E M P T Y T A B L E; +ENDOFPERIOD: RU_K RU_O RU_N RU_E RU_C RU_P RU_E RU_R RU_I RU_O RU_D RU_A | E N D O F P E R I O D; +HALFYEAR: RU_P RU_O RU_L RU_U RU_G RU_O RU_D RU_I RU_E | H A L F Y E A R; +HOUR: RU_CH RU_A RU_S | H O U R; +MAX: RU_M RU_A RU_K RU_S RU_I RU_M RU_U RU_M | M A X; +MIN: RU_M RU_I RU_N RU_I RU_M RU_U RU_M | M I N; +MINUTE: RU_M RU_I RU_N RU_U RU_T RU_A | M I N U T E; +MONTH: RU_M RU_E RU_S RU_YA RU_C | M O N T H; +NUMBER: RU_CH RU_I RU_S RU_L RU_O | N U M B E R; +QUARTER: RU_K RU_V RU_A RU_R RU_T RU_A RU_L | Q U A R T E R; +ONLY: RU_T RU_O RU_L RU_SOFT_SIGN RU_K RU_O | O N L Y; +PERIODS: RU_P RU_E RU_R RU_I RU_O RU_D RU_A RU_M RU_I | P E R I O D S; +REFS: RU_S RU_S RU_Y RU_L RU_K RU_A | R E F S; +PRESENTATION: RU_P RU_R RU_E RU_D RU_S RU_T RU_A RU_V RU_L RU_E RU_N RU_I RU_E | P R E S E N T A T I O N; +RECORDAUTONUMBER: + RU_A RU_V RU_T RU_O RU_N RU_O RU_M RU_E RU_R RU_Z RU_A RU_P RU_I RU_S RU_I | R E C O R D A U T O N U M B E R; +REFPRESENTATION: + RU_P RU_R RU_E RU_D RU_S RU_T RU_A RU_V RU_L RU_E RU_N RU_I RU_E RU_S RU_S RU_Y RU_L RU_K RU_I + | R E F P R E S E N T A T I O N; +SECOND: RU_S RU_E RU_K RU_U RU_N RU_D RU_A | S E C O N D; +STRING: RU_S RU_T RU_R RU_O RU_K RU_A | S T R I N G; +SUBSTRING: RU_P RU_O RU_D RU_S RU_T RU_R RU_O RU_K RU_A | S U B S T R I N G; +SUM: RU_S RU_U RU_M RU_M RU_A | S U M; +TENDAYS: RU_D RU_E RU_K RU_A RU_D RU_A | T E N D A Y S; +TYPE: RU_T RU_I RU_P | T Y P E; +UPDATE: RU_I RU_Z RU_M RU_E RU_N RU_E RU_N RU_I RU_YA | U P D A T E; +VALUE: RU_Z RU_N RU_A RU_CH RU_E RU_N RU_I RU_E | V A L U E; +VALUETYPE: RU_T RU_I RU_P RU_Z RU_N RU_A RU_CH RU_E RU_N RU_I RU_YA | V A L U E T Y P E; +WEEK: RU_N RU_E RU_D RU_E RU_L RU_YA | W E E K; +WEEKDAY: RU_D RU_E RU_N RU_SOFT_SIGN RU_N RU_E RU_D RU_E RU_L RU_I | W E E K D A Y; +YEAR: RU_G RU_O RU_D | Y E A R; + +// MDO TYPES RU EN +ACCOUNTING_REGISTER_TYPE: + REGISTER_RU RU_B RU_U RU_H RU_G RU_A RU_L RU_T RU_E RU_R RU_I RU_I | A C C O U N T I N G REGISTER_EN; +ACCUMULATION_REGISTER_TYPE: + REGISTER_RU RU_N RU_A RU_K RU_O RU_P RU_L RU_E RU_N RU_I RU_YA | A C C U M U L A T I O N REGISTER_EN; +BUSINESS_PROCESS_TYPE: + RU_B RU_I RU_Z RU_N RU_E RU_S RU_P RU_R RU_O RU_C RU_E RU_S RU_S | B U S I N E S S P R O C E S S; +CALCULATION_REGISTER_TYPE: + REGISTER_RU RU_R RU_A RU_S RU_CH RU_E RU_T RU_A | C A L C U L A T I O N REGISTER_EN; +CATALOG_TYPE: RU_S RU_P RU_R RU_A RU_V RU_O RU_CH RU_N RU_I RU_K | C A T A L O G; +CHART_OF_ACCOUNTS_TYPE: + PLAN_RU RU_S RU_CH RU_E RU_T RU_O RU_V | PLAN_EN A C C O U N T S; +CHART_OF_CALCULATION_TYPES_TYPE: + PLAN_RU RU_V RU_I RU_D RU_O RU_V RU_R RU_A RU_S RU_CH RU_E RU_T RU_A | PLAN_EN C A L C U L A T I O N T Y P E S; +CHART_OF_CHARACTERISTIC_TYPES_TYPE: + PLAN_RU RU_V RU_I RU_D RU_O RU_V RU_H RU_A RU_R RU_A RU_K RU_T RU_E RU_R RU_I RU_S RU_T RU_I RU_K + | PLAN_EN C H A R A C T E R I S T I C T Y P E S; +CONSTANT_TYPE: RU_K RU_O RU_N RU_S RU_T RU_A RU_N RU_T RU_A | C O N S T A N T; +DOCUMENT_TYPE: DOCUMENT_RU | DOCUMENT_EN; +DOCUMENT_JOURNAL_TYPE: + RU_ZH RU_U RU_R RU_N RU_A RU_L DOCUMENT_RU RU_O RU_V | DOCUMENT_EN J O U R N A L; +ENUM_TYPE: RU_P RU_E RU_R RU_E RU_CH RU_I RU_S RU_L RU_E RU_N RU_I RU_E | E N U M; +EXCHANGE_PLAN_TYPE: + PLAN_RU RU_O RU_B RU_M RU_E RU_N RU_A | E X C H A N G E P L A N; +EXTERNAL_DATA_SOURCE_TYPE: + RU_V RU_N RU_E RU_SH RU_N RU_I RU_J RU_I RU_S RU_T RU_O RU_CH RU_N RU_I RU_K RU_D RU_A RU_N RU_N RU_Y RU_H + | E X T E R N A L D A T A S O U R C E; +FILTER_CRITERION_TYPE: + RU_K RU_R RU_I RU_T RU_E RU_R RU_I RU_J RU_O RU_T RU_B RU_O RU_R RU_A | F I L T E R C R I T E R I O N; +INFORMATION_REGISTER_TYPE: + REGISTER_RU RU_S RU_V RU_E RU_D RU_E RU_N RU_I RU_J | I N F O R M A T I O N REGISTER_EN; +SEQUENCE_TYPE: + RU_P RU_O RU_S RU_L RU_E RU_D RU_O RU_V RU_A RU_T RU_E RU_L RU_SOFT_SIGN RU_N RU_O RU_S RU_T RU_SOFT_SIGN + | S E Q U E N C E; +TASK_TYPE: RU_Z RU_A RU_D RU_A RU_CH RU_A | T A S K; + +// FIELDS RU EN +ROUTEPOINT_FIELD: RU_T RU_O RU_CH RU_K RU_A RU_M RU_A RU_R RU_SH RU_R RU_U RU_T RU_A | R O U T E P O I N T; + +fragment BALANCE_RU: RU_O RU_S RU_T RU_A RU_T RU_K RU_I; +fragment BALANCE_EN: B A L A N C E; +fragment DOCUMENT_RU: RU_D RU_O RU_K RU_U RU_M RU_E RU_N RU_T; +fragment DOCUMENT_EN: D O C U M E N T; +fragment EXT_DIMENSIONS_RU: RU_S RU_U RU_B RU_K RU_O RU_N RU_T RU_O; +fragment EXT_DIMENSIONS_EN: E X T D I M E N S I O N S; +fragment PLAN_RU: RU_P RU_L RU_A RU_N; +fragment PLAN_EN: C H A R T O F; +fragment REGISTER_RU: RU_R RU_E RU_G RU_I RU_S RU_T RU_R; +fragment REGISTER_EN: R E G I S T E R; +fragment TURNOVERS_RU: RU_O RU_B RU_O RU_R RU_O RU_T RU_Y; +fragment TURNOVERS_EN: T U R N O V E R S; + +// LETTERS +fragment RU_A: 'А' | 'а'; +fragment RU_B: 'Б' | 'б'; +fragment RU_V: 'В' | 'в'; +fragment RU_G: 'Г' | 'г'; +fragment RU_D: 'Д' | 'д'; +fragment RU_YO: 'Ё' | 'ё'; +fragment RU_E: 'Е' | 'е'; +fragment RU_ZH: 'Ж' | 'ж'; +fragment RU_Z: 'З' | 'з'; +fragment RU_I: 'И' | 'и'; +fragment RU_J: 'Й' | 'й'; +fragment RU_K: 'К' | 'к'; +fragment RU_L: 'Л' | 'л'; +fragment RU_M: 'М' | 'м'; +fragment RU_N: 'Н' | 'н'; +fragment RU_O: 'О' | 'о'; +fragment RU_P: 'П' | 'п'; +fragment RU_R: 'Р' | 'р'; +fragment RU_S: 'С' | 'с'; +fragment RU_T: 'Т' | 'т'; +fragment RU_U: 'У' | 'у'; +fragment RU_F: 'Ф' | 'ф'; +fragment RU_H: 'Х' | 'х'; +fragment RU_C: 'Ц' | 'ц'; +fragment RU_CH: 'Ч' | 'ч'; +fragment RU_SH: 'Ш' | 'ш'; +fragment RU_SCH: 'Щ' | 'щ'; +fragment RU_SOLID_SIGN: 'Ъ' | 'ъ'; +fragment RU_Y: 'Ы' | 'ы'; +fragment RU_SOFT_SIGN: 'Ь' | 'ь'; +fragment RU_EH: 'Э' | 'э'; +fragment RU_YU: 'Ю' | 'ю'; +fragment RU_YA: 'Я' | 'я'; +fragment A: 'A' | 'a'; +fragment B: 'B' | 'b'; +fragment C: 'C' | 'c'; +fragment D: 'D' | 'd'; +fragment I: 'I' | 'i'; +fragment J: 'J' | 'j'; +fragment E: 'E' | 'e'; +fragment F: 'F' | 'f'; +fragment G: 'G' | 'g'; +fragment U: 'U' | 'u'; +fragment K: 'K' | 'k'; +fragment L: 'L' | 'l'; +fragment M: 'M' | 'm'; +fragment N: 'N' | 'n'; +fragment O: 'O' | 'o'; +fragment P: 'P' | 'p'; +fragment Q: 'Q' | 'q'; +fragment R: 'R' | 'r'; +fragment S: 'S' | 's'; +fragment T: 'T' | 't'; +fragment V: 'V' | 'v'; +fragment H: 'H' | 'h'; +fragment W: 'W' | 'w'; +fragment X: 'X' | 'x'; +fragment Y: 'Y' | 'y'; + +// LITERALS +fragment DIGIT: [0-9]; +fragment LETTER: [\p{Letter}] | '_'; + +DECIMAL : DIGIT+; +FLOAT : DIGIT+ '.' DIGIT*; +STR : '"' (~["] | '""')* '"'; +INCORRECT_IDENTIFIER : DIGIT+ LETTER (LETTER | DIGIT)*; +IDENTIFIER : LETTER (LETTER | DIGIT)*; +UNKNOWN: . -> channel(HIDDEN); + +// PARAMETERS +mode PARAMETER_MODE; +PARAMETER_WHITE_SPACE : [ \n\r\t\f]+ -> channel(HIDDEN), type(WHITE_SPACE); +PARAMETER_IDENTIFIER : LETTER (LETTER | DIGIT)* -> popMode; +PARAMETER_UKNOWN : . -> channel(HIDDEN), type(UNKNOWN); + +mode DOT_MODE; +DOT_WHITE_SPACE : [ \t\f]+ -> channel(HIDDEN), type(WHITE_SPACE); +DOT_MUL : MUL -> type(MUL), popMode; +DOT_LPAREN : LPAREN -> type(LPAREN), popMode; +DOT_RPAREN : RPAREN -> type(RPAREN), popMode; +DOT_ROUTEPOINT_FIELD : ROUTEPOINT_FIELD -> type(ROUTEPOINT_FIELD), popMode; +// VIRTUAL TABLES RU EN +ACTUAL_ACTION_PERIOD_VT: + (RU_F RU_A RU_K RU_T RU_I RU_CH RU_E RU_S RU_K RU_I RU_J RU_P RU_E RU_R RU_I RU_O RU_D RU_D RU_E RU_J RU_S RU_T RU_V RU_I RU_YA + | A C T U A L A C T I O N P E R I O D) -> popMode; +BALANCE_VT: (BALANCE_RU | BALANCE_EN) -> popMode; +BALANCE_AND_TURNOVERS_VT: + (BALANCE_RU RU_I TURNOVERS_RU | BALANCE_EN A N D TURNOVERS_EN) -> popMode; +BOUNDARIES_VT: (RU_G RU_R RU_A RU_N RU_I RU_C RU_Y | B O U N D A R I E S) -> popMode; +DR_CR_TURNOVERS_VT: + (TURNOVERS_RU RU_D RU_T RU_K RU_T | D R C R TURNOVERS_EN) -> popMode; +EXT_DIMENSIONS_VT: + (EXT_DIMENSIONS_RU | EXT_DIMENSIONS_EN) -> popMode; +RECORDS_WITH_EXT_DIMENSIONS_VT: + (RU_D RU_V RU_I RU_ZH RU_E RU_N RU_I RU_YA RU_S EXT_DIMENSIONS_RU | R E C O R D S W I T H EXT_DIMENSIONS_EN) -> popMode; +SCHEDULE_DATA_VT: + (RU_D RU_A RU_N RU_N RU_Y RU_E RU_G RU_R RU_A RU_F RU_I RU_K RU_A | S C H E D U L E D A T A) -> popMode; +SLICEFIRST_VT: (RU_S RU_R RU_E RU_Z RU_P RU_E RU_R RU_V RU_Y RU_H | S L I C E F I R S T) -> popMode; +SLICELAST_VT: (RU_S RU_R RU_E RU_Z RU_P RU_O RU_S RU_L RU_E RU_D RU_N RU_I RU_H | S L I C E L A S T) -> popMode; +TASK_BY_PERFORMER_VT: + (RU_Z RU_A RU_D RU_A RU_CH RU_I RU_P RU_O RU_I RU_S RU_P RU_O RU_L RU_N RU_I RU_T RU_E RU_L RU_YU + | T A S K B Y P E R F O R M E R) -> popMode; +TURNOVERS_VT: (TURNOVERS_RU | TURNOVERS_EN) -> popMode; + +DOT_IDENTIFIER : (LETTER ( LETTER | DIGIT )*) -> type(IDENTIFIER), popMode; + +mode BRACE_MODE; +BRACE_WHITE_SPACE : [ \n\r\t\f]+ -> channel(HIDDEN), type(WHITE_SPACE); +BRACE_IDENTIFIER : LETTER (LETTER | DIGIT)* -> channel(HIDDEN); +BRACE_START : '{' -> pushMode(BRACE_MODE), channel(HIDDEN); +BRACE_END : '}' -> channel(HIDDEN), type(UNKNOWN), popMode; +BRACE_UNKNOWN : . -> channel(HIDDEN), type(UNKNOWN); diff --git a/src/main/antlr/SDBLParser.g4 b/src/main/antlr/SDBLParser.g4 new file mode 100644 index 00000000..321cb636 --- /dev/null +++ b/src/main/antlr/SDBLParser.g4 @@ -0,0 +1,903 @@ +/** + * This file is a part of BSL Parser. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov , Sergey Batanov + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Parser is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Parser is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Parser. + */ +parser grammar SDBLParser; + +options { + tokenVocab = SDBLLexer; + contextSuperClass = 'BSLParserRuleContext'; +} + +// ROOT +// основная структура пакета запросов +queryPackage: queries (SEMICOLON queries)* SEMICOLON? EOF; + +// QUERY +// описание элемента пакета +queries: dropTableQuery | selectQuery; + +// DROP TABLE +// удаление временной таблицы, где temparyTableName идентификатор временной таблицы +dropTableQuery: DROP temparyTableName=identifier; + +// SELECT +// запрос на выборку данных, может состять из подзапроса или подзапроса с временной таблицей +selectQuery: (subquery ordersAndTotals) | (temparyTableSubquery orders? indexing); + +// SUBQUERIES +// различные виды подзапросов + +// простой подзапрос для выборки данных, состоит из первого запроса (main) и объединений с остальными +subquery: main=query union*; +// объединение запросов +union: UNION all=ALL? query; +// структура запроса +query: + SELECT limitations + selectedFields + from + where + groupBy + having + forUpdate + ; +// выбираемые поля +selectedFields: selectedField (COMMA selectedField)*; +selectedField: + ( + (emptyTable=EMPTYTABLE DOT LPAREN emptyTableFields RPAREN) + | ((tableName=identifier DOT)* inlineTable=identifier DOT LPAREN inlineTableFields RPAREN) + | ((tableName=identifier DOT)* MUL) + | selectExpression + ) alias + ; +// поле-пустая таблица +emptyTableFields: emptyTableField (COMMA emptyTableField)*; +// само поле состоит только из алиаса (который вообще может быть пустым) +emptyTableField: alias; +// поле-вложенная таблица (табличная часть) +inlineTableFields: inlineTableField (COMMA inlineTableField)*; +// поле вложенной таблицы +inlineTableField: inlineTableExpression alias; +// источник данных запроса +from: (FROM dataSources)?; + +// подзапрос с выборкой данных во временную таблицу, состоит из первого запроса с помещением во временню таблицу (main) +// и объединения с "обычными" запросами +temparyTableSubquery: main=temparyTableMainQuery temparyTableUnion*; +// объединение запросов +temparyTableUnion: UNION all=ALL? temparyTableQuery; +// структура запроса помещения во временную таблицу (основной) +temparyTableMainQuery: + SELECT limitations + temparyTableSelectedFields + into + temparyTableFrom + where + groupBy + having + forUpdate + ; +// структура запроса помещения во временную таблицу (объединение) +temparyTableQuery: + SELECT limitations + temparyTableSelectedFields + temparyTableFrom + where + groupBy + having + forUpdate + ; +// выбираемые поля временной таблицы +temparyTableSelectedFields: temparyTableSelectedField (COMMA temparyTableSelectedField)*; +temparyTableSelectedField: + ( + ((tableName=identifier DOT)* MUL) + | (doCall=RECORDAUTONUMBER LPAREN RPAREN) + | selectExpression + ) alias + ; +// помещение во временную таблицу +into: INTO temparyTableName=identifier; +// источники данных для временной таблицы +temparyTableFrom: (FROM temparyTableDataSources)?; +// перечень таблиц-источников данных для выборки +temparyTableDataSources: (dataSources | parameterTable) (COMMA dataSources | parameterTable)*; +// таблица как параметр, соединяться ни с чем не может +parameterTable: parameter alias; +// индексирование во временной таблице +indexing: (INDEX by=(BY_EN | PO_RU) indexingItem (COMMA indexingItem)*)?; +// поле индексирования, может быть колонкой или параметром +indexingItem: parameter | column; + +// вложенный подзапрос +inlineSubquery: LPAREN subquery orders? RPAREN; + +// COMMON FOR QUERIES + +// конструкция для изменения, может содержать перечень таблиц, которые необходимо заблокировать для изменения +forUpdate: (FOR UPDATE mdo?)?; + +// ограничения выборки, для ускорения анализа развернуты все варианты +limitations: + (ALLOWED DISTINCT top) + | (ALLOWED top DISTINCT) + | (top ALLOWED DISTINCT) + | (top DISTINCT ALLOWED) + | (DISTINCT ALLOWED top) + | (DISTINCT top ALLOWED) + | (ALLOWED DISTINCT) + | (ALLOWED top) + | (DISTINCT ALLOWED) + | (DISTINCT top) + | (top ALLOWED) + | (top DISTINCT) + | ((ALLOWED | DISTINCT | top)?) + ; +// выборка первых N элементов, где count - количество элементов +top: TOP count=DECIMAL+; + +// упорядочивание и итоги +ordersAndTotals: + ( + (AUTOORDER orders totals) + | (orders AUTOORDER totals) + | (orders totals AUTOORDER) + | (AUTOORDER (orders | totals)?) + | (orders (AUTOORDER | totals)?) + | (totals AUTOORDER?) + )? + ; +// итоги +totals: TOTALS totalsItems? by=(BY_EN | PO_RU) totalsGroups; +totalsItems: totalsItem (COMMA totalsItem)*; +totalsItem: totalsItemExpression alias; +totalsGroups: totalsGroup (COMMA totalsGroup)*; +totalsGroup: + ( + OVERALL + | totalsGroupExpression + ) alias + ; + +// только упорядочивание +orders: ORDER by=(BY_EN | PO_RU) ordersItems; +ordersItems: ordersItem (COMMA ordersItem)*; +ordersItem: ordersExpression orderDirection? alias; +orderDirection: ASC | DESC | (hierarhy=(HIERARCHY_EN | HIERARCHYA_RU) DESC?); + +// перечень таблиц-источников данных для выборки +dataSources: dataSource (COMMA dataSource)*; +// варианты источников данных +dataSource: + ( + (LPAREN dataSource RPAREN) + | inlineSubquery + | table + | virtualTable + ) alias joinPart* + ; + +// истоник-таблица, может быть временной таблице или таблицей объекта метаданных +table: + mdo + | (mdo (DOT identifierWithoutTT)+) + | tableName=identifier + ; +// виртуальная таблица объекта метаданных +virtualTable: + (mdo DOT virtualTableName (LPAREN virtualTableParameters RPAREN)) + | (mdo DOT virtualTableName) + | (FILTER_CRITERION_TYPE DOT identifier LPAREN parameter? RPAREN) // для критерия отбора имя ВТ не указывается + ; +// параметры виртуальной таблицы, могут отсутствовать, могут быть просто запятые, без значений +virtualTableParameters: virtualTableParameter (COMMA virtualTableParameter)*; +virtualTableParameter: virtualTableExpression?; + +// соединения таблиц +joinPart: + (INNER? | ((LEFT | RIGHT | FULL) OUTER?)) // тип соединения + JOIN dataSource on=(ON_EN | PO_RU) joinExpression // имя таблицы и соединение + ; + +// условия выборки +where: (WHERE whereExpression)?; + +// группировка данных +groupBy: (GROUP by=(BY_EN | PO_RU) groupByItems)?; +groupByItems: groupByExpression (COMMA groupByExpression)*; + +// условия на аггрегированные данные +having: (HAVING havingExpression)?; + +// EXPRESSIONS +// все виды выражений +selectExpression : selectMember (boolOperation selectMember)*; +inlineTableExpression : inlineTableMember (boolOperation inlineTableMember)*; +virtualTableExpression : virtualTableMember (boolOperation virtualTableMember)*; +joinExpression : joinMember (boolOperation joinMember)*; +whereExpression : whereMember (boolOperation whereMember)*; +groupByExpression : groupByMember (boolOperation groupByMember)*; +havingExpression : havingMember (boolOperation havingMember)*; +totalsItemExpression : totalsItemMember (boolOperation totalsItemMember)*; +totalsGroupExpression : totalsGroupMember (boolOperation totalsGroupMember)*; +ordersExpression : ordersMember (boolOperation ordersMember)*; + +// MEMBERS +// члены выражений +selectMember: + selectStatement + | selectBinaryStatement + | selectComparyStatement + | (selectStatement NOT? IN (hierarhy=(HIERARCHY_EN | HIERARCHII_RU))? (inlineSubquery | (LPAREN selectStatement (COMMA selectStatement)*) RPAREN)) + | (LPAREN selectStatement (COMMA selectStatement)+ RPAREN NOT? IN (inlineSubquery | ( LPAREN selectStatement (COMMA selectStatement)*) RPAREN)) + | (NOT+ selectStatement NOT? IN (hierarhy=(HIERARCHY_EN | HIERARCHII_RU))? (inlineSubquery | (LPAREN selectStatement (COMMA selectStatement)*) RPAREN)) + | (NOT+ LPAREN selectStatement (COMMA selectStatement)+ RPAREN NOT? IN (inlineSubquery | ( LPAREN selectStatement (COMMA selectStatement)*) RPAREN)) + | (selectStatement IS NOT? NULL) + | (selectStatement REFS mdo) + | (selectStatement NOT? BETWEEN selectBetweenStatement) + | (selectStatement NOT? LIKE selectStatement (ESCAPE escape=STR)?) + ; +inlineTableMember: + inlineTableStatement + | inlineTableBinaryStatement + | inlineTableComparyStatement + ; +virtualTableMember: + virtualTableStatement + | virtualTableBinaryStatement + | virtualTableComparyStatement + | (virtualTableStatement NOT? IN (hierarhy=(HIERARCHY_EN | HIERARCHII_RU))? (inlineSubquery | (LPAREN virtualTableStatement (COMMA virtualTableStatement)*) RPAREN)) + | (LPAREN virtualTableStatement (COMMA virtualTableStatement)+ RPAREN NOT? IN (inlineSubquery | (LPAREN virtualTableStatement (COMMA virtualTableStatement)*) RPAREN)) + | (NOT+ virtualTableStatement NOT? IN (hierarhy=(HIERARCHY_EN | HIERARCHII_RU))? (inlineSubquery | (LPAREN virtualTableStatement (COMMA virtualTableStatement)*) RPAREN)) + | (NOT+ LPAREN virtualTableStatement (COMMA virtualTableStatement)+ RPAREN NOT? IN (inlineSubquery | (LPAREN virtualTableStatement (COMMA virtualTableStatement)*) RPAREN)) + | (virtualTableStatement IS NOT? NULL) + | (virtualTableStatement REFS mdo) + | (virtualTableStatement NOT? BETWEEN virtualTableBetweenStatement) + | (virtualTableStatement NOT? LIKE virtualTableStatement (ESCAPE escape=STR)?) + ; +joinMember: + joinStatement + | joinBinaryStatement + | joinComparyStatement + | (joinStatement NOT? IN (hierarhy=(HIERARCHY_EN | HIERARCHII_RU))? (inlineSubquery | (LPAREN joinStatement (COMMA joinStatement)*) RPAREN)) + | (LPAREN joinStatement (COMMA joinStatement)+ RPAREN NOT? IN (inlineSubquery | (LPAREN joinStatement (COMMA joinStatement)*) RPAREN)) + | (NOT+ joinStatement NOT? IN (hierarhy=(HIERARCHY_EN | HIERARCHII_RU))? (inlineSubquery | (LPAREN joinStatement (COMMA joinStatement)*) RPAREN)) + | (NOT+ LPAREN joinStatement (COMMA joinStatement)+ RPAREN NOT? IN (inlineSubquery | (LPAREN joinStatement (COMMA joinStatement)*) RPAREN)) + | (joinStatement IS NOT? NULL) + | (joinStatement REFS mdo) + | (joinStatement NOT? BETWEEN joinBetweenStatement) + | (joinStatement NOT? LIKE joinStatement (ESCAPE escape=STR)?) + ; +whereMember: + whereStatement + | whereBinaryStatement + | whereComparyStatement + | (whereStatement NOT? IN (hierarhy=(HIERARCHY_EN | HIERARCHII_RU))? (inlineSubquery | (LPAREN whereStatement (COMMA whereStatement)*) RPAREN)) + | (LPAREN whereStatement (COMMA whereStatement)+ RPAREN NOT? IN (inlineSubquery | (LPAREN whereStatement (COMMA whereStatement)*) RPAREN)) + | (NOT+ whereStatement NOT? IN (hierarhy=(HIERARCHY_EN | HIERARCHII_RU))? (inlineSubquery | (LPAREN whereStatement (COMMA whereStatement)*) RPAREN)) + | (NOT+ LPAREN whereStatement (COMMA whereStatement)+ RPAREN NOT? IN (inlineSubquery | (LPAREN whereStatement (COMMA whereStatement)*) RPAREN)) + | (whereStatement IS NOT? NULL) + | (whereStatement REFS mdo) + | (whereStatement NOT? BETWEEN whereBetweenStatement) + | (whereStatement NOT? LIKE whereStatement (ESCAPE escape=STR)?) + ; +groupByMember: + groupByStatement + | groupByBinaryStatement + | groupByComparyStatement + | (groupByStatement NOT? IN (hierarhy=(HIERARCHY_EN | HIERARCHII_RU))? (inlineSubquery | (LPAREN groupByStatement (COMMA groupByStatement)*) RPAREN)) + | (LPAREN groupByStatement (COMMA groupByStatement)+ RPAREN NOT? IN (inlineSubquery | (LPAREN groupByStatement (COMMA groupByStatement)*) RPAREN)) + | (NOT+ groupByStatement NOT? IN (hierarhy=(HIERARCHY_EN | HIERARCHII_RU))? (inlineSubquery | (LPAREN groupByStatement (COMMA groupByStatement)*) RPAREN)) + | (NOT+ LPAREN groupByStatement (COMMA groupByStatement)+ RPAREN NOT? IN (inlineSubquery | (LPAREN groupByStatement (COMMA groupByStatement)*) RPAREN)) + | (groupByStatement IS NOT? NULL) + | (groupByStatement REFS mdo) + | (groupByStatement NOT? BETWEEN groupByBetweenStatement) + | (groupByStatement NOT? LIKE groupByStatement (ESCAPE escape=STR)?) + ; +havingMember: + havingStatement + | havingBinaryStatement + | havingComparyStatement + | (havingStatement NOT? IN (hierarhy=(HIERARCHY_EN | HIERARCHII_RU))? (inlineSubquery | (LPAREN havingStatement (COMMA havingStatement)*) RPAREN)) + | (LPAREN havingStatement (COMMA havingStatement)+ RPAREN NOT? IN (inlineSubquery | (LPAREN havingStatement (COMMA havingStatement)*) RPAREN)) + | (NOT+ havingStatement NOT? IN (hierarhy=(HIERARCHY_EN | HIERARCHII_RU))? (inlineSubquery | (LPAREN havingStatement (COMMA havingStatement)*) RPAREN)) + | (NOT+ LPAREN havingStatement (COMMA havingStatement)+ RPAREN NOT? IN (inlineSubquery | (LPAREN havingStatement (COMMA havingStatement)*) RPAREN)) + | (havingStatement IS NOT? NULL) + | (havingStatement REFS mdo) + ; +totalsItemMember: + totalsItemStatement + | totalsItemBinaryStatement + | totalsItemComparyStatement + | (totalsItemStatement NOT? IN (hierarhy=(HIERARCHY_EN | HIERARCHII_RU))? (inlineSubquery | (LPAREN totalsItemStatement (COMMA totalsItemStatement)*) RPAREN)) + | (NOT+ totalsItemStatement NOT? IN (hierarhy=(HIERARCHY_EN | HIERARCHII_RU))? (inlineSubquery | (LPAREN totalsItemStatement (COMMA totalsItemStatement)*) RPAREN)) + | (totalsItemStatement IS NOT? NULL) + ; +totalsGroupMember: + totalsGroupStatement + | totalsGroupBinaryStatement + | totalsGroupComparyStatement + ; +ordersMember: + ordersStatement + | ordersBinaryStatement + | ordersComparyStatement + | (ordersStatement IN (hierarhy=(HIERARCHY_EN | HIERARCHII_RU))? (inlineSubquery | (LPAREN ordersStatement (COMMA ordersStatement)*) RPAREN)) + | (LPAREN ordersStatement (COMMA ordersStatement)+ RPAREN IN (inlineSubquery | (LPAREN ordersStatement (COMMA ordersStatement)*) RPAREN)) + | (NOT+ ordersStatement IN (hierarhy=(HIERARCHY_EN | HIERARCHII_RU))? (inlineSubquery | (LPAREN ordersStatement (COMMA ordersStatement)*) RPAREN)) + | (NOT+ LPAREN ordersStatement (COMMA ordersStatement)+ RPAREN IN (inlineSubquery | (LPAREN ordersStatement (COMMA ordersStatement)*) RPAREN)) + ; + +// STATEMENTS +// части выражения +selectStatement: + (LPAREN selectExpression RPAREN) + | (NOT+ LPAREN selectExpression RPAREN) + | (MINUS+ LPAREN selectExpression RPAREN) + | statement + | selectAggrMathCallStatement + | selectAggrCountCallStatement + | selectCastStatement + | selectCaseStatement + | ((doCall=ISNULL | (NOT+ doCall=ISNULL) | (MINUS+ doCall=ISNULL)) LPAREN selectExpression COMMA selectExpression RPAREN) + | (doCall=DATEADD LPAREN selectExpression COMMA datePart COMMA selectExpression RPAREN) + | (doCall=DATEDIFF LPAREN selectExpression COMMA selectExpression COMMA datePart RPAREN) + | (doCall=(BEGINOFPERIOD | ENDOFPERIOD) LPAREN selectExpression COMMA datePart RPAREN) + | (MINUS* doCall=(YEAR | QUARTER | MONTH | DAYOFYEAR | DAY | WEEK | WEEKDAY | HOUR | MINUTE | SECOND) LPAREN selectExpression RPAREN) + | (doCall=SUBSTRING LPAREN selectExpression COMMA selectExpression COMMA selectExpression RPAREN) + | (doCall=(VALUETYPE | PRESENTATION | REFPRESENTATION) LPAREN selectExpression RPAREN) + ; +selectBinaryStatement: selectStatement (binaryOperation selectStatement)+; +selectComparyStatement: (selectBinaryStatement | selectStatement) compareOperation (selectBinaryStatement | selectStatement); +selectCaseStatement: (CASE | (MINUS+ CASE) | (NOT+ CASE)) selectExpression? selectWhenBranch+ selectElseBranch? END; +selectWhenBranch: WHEN selectExpression THEN selectExpression; +selectElseBranch: ELSE selectExpression; +selectAggrMathCallStatement: + ( + (doCall=(SUM | AVG | MIN | MAX)) + | (MINUS+ doCall=(SUM | AVG | MIN | MAX)) + | (NOT+ doCall=(SUM | AVG | MIN | MAX)) + ) LPAREN selectExpression RPAREN; +selectAggrCountCallStatement: ((doCall=COUNT) | (MINUS+ doCall=COUNT) | (NOT+ doCall=COUNT)) LPAREN (DISTINCT? selectExpression | MUL) RPAREN; +selectCastStatement: + (doCall=CAST | (NOT+ doCall=CAST) | (MINUS doCall=CAST)) LPAREN selectExpression AS ( + BOOLEAN + | (NUMBER (LPAREN DECIMAL (COMMA DECIMAL)? RPAREN)?) + | (STRING (LPAREN DECIMAL RPAREN)?) + | DATE + | mdo + ) RPAREN (DOT identifier)*; +selectBetweenStatement: selectExpression AND selectExpression; + +inlineTableStatement: + (LPAREN inlineTableExpression RPAREN) + | (NOT+ LPAREN inlineTableExpression RPAREN) + | (MINUS+ LPAREN inlineTableExpression RPAREN) + | inlineTableCaseStatement + | statement + | ((NOT* | MINUS*) doCall=DATEADD LPAREN inlineTableCaseStatement COMMA datePart COMMA inlineTableCaseStatement RPAREN) + | (doCall=DATEDIFF LPAREN inlineTableCaseStatement COMMA inlineTableCaseStatement COMMA datePart RPAREN) + | (doCall=(BEGINOFPERIOD | ENDOFPERIOD) LPAREN inlineTableCaseStatement COMMA datePart RPAREN) + | (MINUS* doCall=(YEAR | QUARTER | MONTH | DAYOFYEAR | DAY | WEEK | WEEKDAY | HOUR | MINUTE | SECOND) LPAREN inlineTableCaseStatement RPAREN) + | (doCall=SUBSTRING LPAREN inlineTableExpression COMMA inlineTableExpression COMMA inlineTableExpression RPAREN) + ; +inlineTableBinaryStatement: inlineTableStatement (binaryOperation inlineTableStatement)+; +inlineTableComparyStatement: (selectBinaryStatement | inlineTableBinaryStatement) compareOperation (inlineTableBinaryStatement | inlineTableStatement); +inlineTableCaseStatement: (CASE | (MINUS+ CASE) | (NOT+ CASE)) inlineTableExpression? inlineTableWhenBranch+ inlineTableElseBranch? END; +inlineTableWhenBranch: WHEN inlineTableExpression THEN inlineTableExpression; +inlineTableElseBranch: ELSE inlineTableExpression; + +virtualTableStatement: + (LPAREN virtualTableExpression RPAREN) + | (NOT+ LPAREN virtualTableExpression RPAREN) + | (MINUS+ LPAREN virtualTableExpression RPAREN) + | statement + | virtualTableCastStatement + | virtualTableCaseStatement + | ((doCall=ISNULL | (NOT+ doCall=ISNULL) | (MINUS+ doCall=ISNULL)) LPAREN virtualTableExpression COMMA virtualTableExpression RPAREN) + | (doCall=DATEADD LPAREN virtualTableExpression COMMA datePart COMMA virtualTableExpression RPAREN) + | (doCall=DATEDIFF LPAREN virtualTableExpression COMMA virtualTableExpression COMMA datePart RPAREN) + | (doCall=(BEGINOFPERIOD | ENDOFPERIOD) LPAREN virtualTableExpression COMMA datePart RPAREN) + | (MINUS* doCall=(YEAR | QUARTER | MONTH | DAYOFYEAR | DAY | WEEK | WEEKDAY | HOUR | MINUTE | SECOND) LPAREN virtualTableExpression RPAREN) + | (doCall=SUBSTRING LPAREN virtualTableExpression COMMA virtualTableExpression COMMA virtualTableExpression RPAREN) + ; +virtualTableBinaryStatement: virtualTableStatement (binaryOperation virtualTableStatement)+; +virtualTableComparyStatement: (virtualTableBinaryStatement | virtualTableStatement) compareOperation (virtualTableBinaryStatement | virtualTableStatement); +virtualTableCaseStatement: (CASE | (MINUS+ CASE) | (NOT+ CASE)) virtualTableExpression? virtualTableWhenBranch+ virtualTableElseBranch? END; +virtualTableWhenBranch: WHEN virtualTableExpression THEN virtualTableExpression; +virtualTableElseBranch: ELSE virtualTableExpression; +virtualTableCastStatement: + (doCall=CAST | (NOT+ doCall=CAST) | (MINUS doCall=CAST)) LPAREN virtualTableExpression AS ( + BOOLEAN + | (NUMBER (LPAREN DECIMAL (COMMA DECIMAL)? RPAREN)?) + | (STRING (LPAREN DECIMAL RPAREN)?) + | DATE + | mdo + ) RPAREN (DOT identifier)*; +virtualTableBetweenStatement: virtualTableExpression AND virtualTableExpression; + +joinStatement: + (LPAREN joinExpression RPAREN) + | (NOT+ LPAREN joinExpression RPAREN) + | (MINUS+ LPAREN joinExpression RPAREN) + | statement + | joinCastStatement + | joinCaseStatement + | ((doCall=ISNULL | (NOT+ doCall=ISNULL) | (MINUS+ doCall=ISNULL)) LPAREN joinExpression COMMA joinExpression RPAREN) + | (doCall=DATEADD LPAREN joinExpression COMMA datePart COMMA joinExpression RPAREN) + | (doCall=DATEDIFF LPAREN joinExpression COMMA joinExpression COMMA datePart RPAREN) + | (doCall=(BEGINOFPERIOD | ENDOFPERIOD) LPAREN joinExpression COMMA datePart RPAREN) + | (MINUS* doCall=(YEAR | QUARTER | MONTH | DAYOFYEAR | DAY | WEEK | WEEKDAY | HOUR | MINUTE | SECOND) LPAREN joinExpression RPAREN) + | (doCall=SUBSTRING LPAREN joinExpression COMMA joinExpression COMMA joinExpression RPAREN) + | (doCall=(VALUETYPE | PRESENTATION | REFPRESENTATION) LPAREN joinExpression RPAREN) + ; +joinBinaryStatement: joinStatement (binaryOperation joinStatement)+; +joinComparyStatement: (joinBinaryStatement | joinStatement) compareOperation (joinBinaryStatement | joinStatement); +joinCaseStatement: (CASE | (MINUS+ CASE) | (NOT+ CASE)) joinExpression? joinWhenBranch+ joinElseBranch? END; +joinWhenBranch: WHEN joinExpression THEN joinExpression; +joinElseBranch: ELSE joinExpression; +joinCastStatement: + (doCall=CAST | (NOT+ doCall=CAST) | (MINUS doCall=CAST)) LPAREN joinExpression AS ( + BOOLEAN + | (NUMBER (LPAREN DECIMAL (COMMA DECIMAL)? RPAREN)?) + | (STRING (LPAREN DECIMAL RPAREN)?) + | DATE + | mdo + ) RPAREN (DOT identifier)*; +joinBetweenStatement: joinExpression AND joinExpression; + +whereStatement: + (LPAREN whereExpression RPAREN) + | (NOT+ LPAREN whereExpression RPAREN) + | (MINUS+ LPAREN whereExpression RPAREN) + | statement + | whereCastStatement + | whereCaseStatement + | ((doCall=ISNULL | (NOT+ doCall=ISNULL) | (MINUS+ doCall=ISNULL)) LPAREN whereExpression COMMA whereExpression RPAREN) + | (doCall=DATEADD LPAREN whereExpression COMMA datePart COMMA whereExpression RPAREN) + | (doCall=DATEDIFF LPAREN whereExpression COMMA whereExpression COMMA datePart RPAREN) + | (doCall=(BEGINOFPERIOD | ENDOFPERIOD) LPAREN whereExpression COMMA datePart RPAREN) + | (MINUS* doCall=(YEAR | QUARTER | MONTH | DAYOFYEAR | DAY | WEEK | WEEKDAY | HOUR | MINUTE | SECOND) LPAREN whereExpression RPAREN) + | (doCall=SUBSTRING LPAREN whereExpression COMMA whereExpression COMMA whereExpression RPAREN) + ; +whereBinaryStatement: whereStatement (binaryOperation whereStatement)+; +whereComparyStatement: (whereBinaryStatement | whereStatement) compareOperation (whereBinaryStatement | whereStatement); +whereCaseStatement: (CASE | (MINUS+ CASE) | (NOT+ CASE)) whereExpression? whereWhenBranch+ whereElseBranch? END; +whereWhenBranch: WHEN whereExpression THEN whereExpression; +whereElseBranch: ELSE whereExpression; +whereCastStatement: + (doCall=CAST | (NOT+ doCall=CAST) | (MINUS doCall=CAST)) LPAREN whereExpression AS ( + BOOLEAN + | (NUMBER (LPAREN DECIMAL (COMMA DECIMAL)? RPAREN)?) + | (STRING (LPAREN DECIMAL RPAREN)?) + | DATE + | mdo + ) RPAREN (DOT identifier)*; +whereBetweenStatement: whereExpression AND whereExpression; + +groupByStatement: + (LPAREN groupByExpression RPAREN) + | (NOT+ LPAREN groupByExpression RPAREN) + | (MINUS+ LPAREN groupByExpression RPAREN) + | statement + | groupByCastStatement + | groupByCaseStatement + | ((doCall=ISNULL | (NOT+ doCall=ISNULL) | (MINUS+ doCall=ISNULL)) LPAREN groupByExpression COMMA groupByExpression RPAREN) + | (doCall=DATEADD LPAREN groupByExpression COMMA datePart COMMA groupByExpression RPAREN) + | (doCall=DATEDIFF LPAREN groupByExpression COMMA groupByExpression COMMA datePart RPAREN) + | (doCall=(BEGINOFPERIOD | ENDOFPERIOD) LPAREN groupByExpression COMMA datePart RPAREN) + | (MINUS* doCall=(YEAR | QUARTER | MONTH | DAYOFYEAR | DAY | WEEK | WEEKDAY | HOUR | MINUTE | SECOND) LPAREN groupByExpression RPAREN) + | (doCall=SUBSTRING LPAREN groupByExpression COMMA groupByExpression COMMA groupByExpression RPAREN) + | (doCall=(VALUETYPE | PRESENTATION | REFPRESENTATION) LPAREN groupByExpression RPAREN) + ; +groupByBinaryStatement: groupByStatement (binaryOperation groupByStatement)+; +groupByComparyStatement: (groupByBinaryStatement | groupByStatement) compareOperation (groupByBinaryStatement | groupByStatement); +groupByCaseStatement: (CASE | (MINUS+ CASE) | (NOT+ CASE)) groupByExpression? groupByWhenBranch+ groupByElseBranch? END; +groupByWhenBranch: WHEN groupByExpression THEN groupByExpression; +groupByElseBranch: ELSE groupByExpression; +groupByCastStatement: + (doCall=CAST | (NOT+ doCall=CAST) | (MINUS doCall=CAST)) LPAREN groupByExpression AS ( + BOOLEAN + | (NUMBER (LPAREN DECIMAL (COMMA DECIMAL)? RPAREN)?) + | (STRING (LPAREN DECIMAL RPAREN)?) + | DATE + | mdo + ) RPAREN (DOT identifier)*; +groupByBetweenStatement: groupByExpression AND groupByExpression; + +havingStatement: + (LPAREN havingExpression RPAREN) + | (NOT+ LPAREN havingExpression RPAREN) + | (MINUS+ LPAREN havingExpression RPAREN) + | statement + | havingAggrMathCallStatement + | havingAggrCountCallStatement + | havingCastStatement + | havingCaseStatement + | ((doCall=ISNULL | (NOT+ doCall=ISNULL) | (MINUS+ doCall=ISNULL)) LPAREN havingExpression COMMA havingExpression RPAREN) + | (doCall=DATEADD LPAREN havingExpression COMMA datePart COMMA havingExpression RPAREN) + | (doCall=DATEDIFF LPAREN havingExpression COMMA havingExpression COMMA datePart RPAREN) + | (doCall=(BEGINOFPERIOD | ENDOFPERIOD) LPAREN havingExpression COMMA datePart RPAREN) + | (MINUS* doCall=(YEAR | QUARTER | MONTH | DAYOFYEAR | DAY | WEEK | WEEKDAY | HOUR | MINUTE | SECOND) LPAREN havingExpression RPAREN) + | (doCall=SUBSTRING LPAREN havingExpression COMMA havingExpression COMMA havingExpression RPAREN) + ; +havingBinaryStatement: havingStatement (binaryOperation havingStatement)+; +havingComparyStatement: (havingBinaryStatement | havingStatement) compareOperation (havingBinaryStatement | havingStatement); +havingCaseStatement: (CASE | (MINUS+ CASE) | (NOT+ CASE)) havingExpression? havingWhenBranch+ havingElseBranch? END; +havingWhenBranch: WHEN havingExpression THEN havingExpression; +havingElseBranch: ELSE havingExpression; +havingAggrMathCallStatement: + ( + (doCall=(SUM | AVG | MIN | MAX)) + | (MINUS+ doCall=(SUM | AVG | MIN | MAX)) + | (NOT+ doCall=(SUM | AVG | MIN | MAX)) + ) LPAREN havingExpression RPAREN; +havingAggrCountCallStatement: ((doCall=COUNT) | (MINUS+ doCall=COUNT) | (NOT+ doCall=COUNT)) LPAREN (DISTINCT? havingExpression | MUL) RPAREN; +havingCastStatement: + (doCall=CAST | (NOT+ doCall=CAST) | (MINUS doCall=CAST)) LPAREN havingExpression AS ( + BOOLEAN + | (NUMBER (LPAREN DECIMAL (COMMA DECIMAL)? RPAREN)?) + | (STRING (LPAREN DECIMAL RPAREN)?) + | DATE + | mdo + ) RPAREN (DOT identifier)*; + +totalsItemStatement: + (LPAREN totalsItemExpression RPAREN) + | statement + | totalsItemAggrMathCallStatement + | totalsItemAggrCountCallStatement + | totalsItemCastStatement + | totalsItemCaseStatement + | ((doCall=ISNULL | (NOT+ doCall=ISNULL) | (MINUS+ doCall=ISNULL)) LPAREN totalsItemExpression COMMA totalsItemExpression RPAREN) + ; +totalsItemBinaryStatement: totalsItemStatement (binaryOperation totalsItemStatement)+; +totalsItemComparyStatement: (totalsItemBinaryStatement | totalsItemStatement) compareOperation (totalsItemBinaryStatement | totalsItemStatement); +totalsItemCaseStatement: (CASE | (MINUS+ CASE) | (NOT+ CASE)) totalsItemExpression? totalsItemWhenBranch+ totalsItemElseBranch? END; +totalsItemWhenBranch: WHEN totalsItemExpression THEN totalsItemExpression; +totalsItemElseBranch: ELSE totalsItemExpression; +totalsItemAggrMathCallStatement: + ( + (doCall=(SUM | AVG | MIN | MAX)) + | (MINUS+ doCall=(SUM | AVG | MIN | MAX)) + | (NOT+ doCall=(SUM | AVG | MIN | MAX)) + ) LPAREN totalsItemExpression RPAREN; +totalsItemAggrCountCallStatement: ((doCall=COUNT) | (MINUS+ doCall=COUNT) | (NOT+ doCall=COUNT)) LPAREN (DISTINCT? totalsItemExpression | MUL) RPAREN; +totalsItemCastStatement: + (doCall=CAST | (NOT+ doCall=CAST) | (MINUS doCall=CAST)) LPAREN totalsItemExpression AS ( + BOOLEAN + | (NUMBER (LPAREN DECIMAL (COMMA DECIMAL)? RPAREN)?) + | (STRING (LPAREN DECIMAL RPAREN)?) + | DATE + | mdo + ) RPAREN (DOT identifier)*; + +totalsGroupStatement: + (LPAREN totalsGroupExpression RPAREN) + | statement + | totalsGroupCaseStatement + ; +totalsGroupBinaryStatement: totalsGroupStatement (binaryOperation totalsGroupStatement)+; +totalsGroupComparyStatement: (totalsGroupBinaryStatement | totalsGroupStatement) compareOperation (totalsGroupBinaryStatement | totalsGroupStatement); +totalsGroupCaseStatement: (CASE | (MINUS+ CASE) | (NOT+ CASE)) totalsGroupExpression? totalsGroupWhenBranch+ totalsGroupElseBranch? END; +totalsGroupWhenBranch: WHEN totalsGroupExpression THEN totalsGroupExpression; +totalsGroupElseBranch: ELSE totalsGroupExpression; + +ordersStatement: + (LPAREN ordersExpression RPAREN) + | statement + | ordersAggrMathCallStatement + | ordersAggrCountCallStatement + | ordersItemCastStatement + | ordersCaseStatement + | ((doCall=ISNULL | (NOT+ doCall=ISNULL) | (MINUS+ doCall=ISNULL)) LPAREN ordersExpression COMMA ordersExpression RPAREN) + | (doCall=DATEADD LPAREN ordersExpression COMMA datePart COMMA ordersExpression RPAREN) + | (doCall=DATEDIFF LPAREN ordersExpression COMMA ordersExpression COMMA datePart RPAREN) + | (doCall=(BEGINOFPERIOD | ENDOFPERIOD) LPAREN ordersExpression COMMA datePart RPAREN) + | (doCall=SUBSTRING LPAREN ordersExpression COMMA ordersExpression COMMA ordersExpression RPAREN) + ; +ordersBinaryStatement: ordersStatement (binaryOperation ordersStatement)+; +ordersComparyStatement: (ordersBinaryStatement | ordersStatement) compareOperation (ordersBinaryStatement | ordersStatement); +ordersCaseStatement: (CASE | (MINUS+ CASE) | (NOT+ CASE)) ordersExpression? ordersWhenBranch+ ordersElseBranch? END; +ordersWhenBranch: WHEN ordersExpression THEN ordersExpression; +ordersElseBranch: ELSE ordersExpression; +ordersAggrMathCallStatement: + ( + (doCall=(SUM | AVG | MIN | MAX)) + | (MINUS+ doCall=(SUM | AVG | MIN | MAX)) + | (NOT+ doCall=(SUM | AVG | MIN | MAX)) + ) LPAREN ordersExpression RPAREN; +ordersAggrCountCallStatement: ((doCall=COUNT) | (MINUS+ doCall=COUNT) | (NOT+ doCall=COUNT)) LPAREN (DISTINCT? ordersExpression | MUL) RPAREN; +ordersItemCastStatement: + (doCall=CAST | (NOT+ doCall=CAST) | (MINUS doCall=CAST)) LPAREN ordersExpression AS ( + BOOLEAN + | (NUMBER (LPAREN DECIMAL (COMMA DECIMAL)? RPAREN)?) + | (STRING (LPAREN DECIMAL RPAREN)?) + | DATE + | mdo + ) RPAREN (DOT identifier)*; + +// COMMON +// Общие правила, без окраски + +// возможные идентификаторы +identifier: + IDENTIFIER // просто идентификатор объекта + // типы метаданных + | BUSINESS_PROCESS_TYPE + | CATALOG_TYPE + | DOCUMENT_TYPE + | INFORMATION_REGISTER_TYPE + | CONSTANT_TYPE + | FILTER_CRITERION_TYPE + | EXCHANGE_PLAN_TYPE + | SEQUENCE_TYPE + | DOCUMENT_JOURNAL_TYPE + | ENUM_TYPE + | CHART_OF_CHARACTERISTIC_TYPES_TYPE + | CHART_OF_ACCOUNTS_TYPE + | CHART_OF_CALCULATION_TYPES_TYPE + | ACCUMULATION_REGISTER_TYPE + | ACCOUNTING_REGISTER_TYPE + | CALCULATION_REGISTER_TYPE + | TASK_TYPE + | EXTERNAL_DATA_SOURCE_TYPE + // ключевые слова + | ALL + | DROP + | END + | FULL + | HIERARCHY_EN + | HIERARCHII_RU + | HIERARCHYA_RU + | INDEX + | ISNULL + | JOIN + | LEFT + | ORDER + | OUTER + | RIGHT + | SELECT + | TOTALS + | UNION + | AVG + | BEGINOFPERIOD + | BOOLEAN + | COUNT + | DATE + | DATEADD + | DATEDIFF + | DATETIME + | DAY + | DAYOFYEAR + | EMPTYTABLE + | ENDOFPERIOD + | HALFYEAR + | HOUR + | MAX + | MIN + | MINUTE + | MONTH + | NUMBER + | QUARTER + | ONLY + | PERIODS + | REFS + | PRESENTATION + | RECORDAUTONUMBER + | REFPRESENTATION + | SECOND + | STRING + | SUBSTRING + | SUM + | TENDAYS + | TYPE + | UPDATE + | VALUE + | VALUETYPE + | WEEK + | WEEKDAY + | YEAR + // виртуальные таблицы + | ACTUAL_ACTION_PERIOD_VT + | BALANCE_VT + | BALANCE_AND_TURNOVERS_VT + | BOUNDARIES_VT + | DR_CR_TURNOVERS_VT + | EXT_DIMENSIONS_VT + | RECORDS_WITH_EXT_DIMENSIONS_VT + | SCHEDULE_DATA_VT + | SLICEFIRST_VT + | SLICELAST_VT + | TASK_BY_PERFORMER_VT + | TURNOVERS_VT + // системные поля + | ROUTEPOINT_FIELD + ; +// для отзделения временных таблиц +identifierWithoutTT: + IDENTIFIER // просто идентификатор объекта + // типы метаданных + | BUSINESS_PROCESS_TYPE + | CATALOG_TYPE + | DOCUMENT_TYPE + | INFORMATION_REGISTER_TYPE + | CONSTANT_TYPE + | FILTER_CRITERION_TYPE + | EXCHANGE_PLAN_TYPE + | SEQUENCE_TYPE + | DOCUMENT_JOURNAL_TYPE + | ENUM_TYPE + | CHART_OF_CHARACTERISTIC_TYPES_TYPE + | CHART_OF_ACCOUNTS_TYPE + | CHART_OF_CALCULATION_TYPES_TYPE + | ACCUMULATION_REGISTER_TYPE + | ACCOUNTING_REGISTER_TYPE + | CALCULATION_REGISTER_TYPE + | TASK_TYPE + | EXTERNAL_DATA_SOURCE_TYPE + // ключевые слова + | ALL + | DROP + | END + | FULL + | HIERARCHY_EN + | HIERARCHII_RU + | HIERARCHYA_RU + | INDEX + | ISNULL + | JOIN + | LEFT + | ORDER + | OUTER + | RIGHT + | SELECT + | TOTALS + | UNION + | AVG + | BEGINOFPERIOD + | BOOLEAN + | COUNT + | DATE + | DATEADD + | DATEDIFF + | DATETIME + | DAY + | DAYOFYEAR + | EMPTYTABLE + | ENDOFPERIOD + | HALFYEAR + | HOUR + | MAX + | MIN + | MINUTE + | MONTH + | NUMBER + | QUARTER + | ONLY + | PERIODS + | REFS + | PRESENTATION + | RECORDAUTONUMBER + | REFPRESENTATION + | SECOND + | STRING + | SUBSTRING + | SUM + | TENDAYS + | TYPE + | UPDATE + | VALUE + | VALUETYPE + | WEEK + | WEEKDAY + | YEAR + // системные поля + | ROUTEPOINT_FIELD + ; +// полное имя объекта метаданных, где mdoName - имя прикладного объекта +mdo : mdoType DOT mdoName=identifier; +// алиас поля или таблицы, где name - собственно идентификатор +alias : (AS? name=identifier)?; +// колонка (поле) таблицы, где +// tableName - идетификатор таблицы (каждой вложенной таблицы), может отсутствовать +// name - собственно идентификатор колонки +column : (tableName=identifier DOT)* name=identifier; +// параметр, может быть и таблицей, где name - идентификатор параметра +parameter : AMPERSAND name=PARAMETER_IDENTIFIER; + +// имена виртуальных таблиц +virtualTableName: + SLICELAST_VT + | SLICEFIRST_VT + | BOUNDARIES_VT + | TURNOVERS_VT + | BALANCE_VT + | BALANCE_AND_TURNOVERS_VT + | EXT_DIMENSIONS_VT + | RECORDS_WITH_EXT_DIMENSIONS_VT + | DR_CR_TURNOVERS_VT + | ACTUAL_ACTION_PERIOD_VT + | SCHEDULE_DATA_VT + | TASK_BY_PERFORMER_VT + ; + +type : STRING | BOOLEAN | DATE | NUMBER; // встроенные типы данных +datePart: MINUTE | HOUR | DAY | WEEK | MONTH | QUARTER | YEAR | TENDAYS | HALFYEAR | SECOND; // составные части дат + +// имена типов метаданных +mdoType : + BUSINESS_PROCESS_TYPE + | CATALOG_TYPE + | DOCUMENT_TYPE + | INFORMATION_REGISTER_TYPE + | CONSTANT_TYPE + | FILTER_CRITERION_TYPE + | EXCHANGE_PLAN_TYPE + | SEQUENCE_TYPE + | DOCUMENT_JOURNAL_TYPE + | ENUM_TYPE + | CHART_OF_CHARACTERISTIC_TYPES_TYPE + | CHART_OF_ACCOUNTS_TYPE + | CHART_OF_CALCULATION_TYPES_TYPE + | ACCUMULATION_REGISTER_TYPE + | ACCOUNTING_REGISTER_TYPE + | CALCULATION_REGISTER_TYPE + | TASK_TYPE + | EXTERNAL_DATA_SOURCE_TYPE + ; + +boolOperation : OR | AND; // логические операторы +binaryOperation : PLUS | MINUS | MUL | QUOTIENT; // математические операторы +compareOperation : LESS | LESS_OR_EQUAL | GREATER | GREATER_OR_EQUAL | ASSIGN | NOT_EQUAL; // операторы сраневния + +// части выражения, используемые везде +statement: + column + | (NOT+ column) + | (MINUS+ column) + | parameter + | (NOT+ parameter) + | (MINUS+ parameter) + | (NOT* literal=(TRUE | FALSE | NULL)) + | (MINUS* literal=(DECIMAL | FLOAT)) + | (literal=(STR | UNDEFINED)) + | (doCall=DATETIME LPAREN + (parameter | DECIMAL) COMMA (parameter | DECIMAL) COMMA (parameter | DECIMAL) + /* эта часть может быть опущена */ (COMMA (parameter | DECIMAL) COMMA (parameter | DECIMAL) (COMMA (parameter | DECIMAL)))? + RPAREN + ) + | (doCall=VALUE LPAREN + ( + (mdo DOT ROUTEPOINT_FIELD DOT IDENTIFIER) // для точки маршрута бизнес процесса + | (identifier DOT identifier) // для системного перечисления + | (mdo DOT name=identifier?) // может быть просто точка - аналог пустой ссылки + ) RPAREN + ) + | (doCall=TYPE LPAREN (mdo | type) RPAREN) +; + +// todo +// [?] Добавить системные перечисления +// [?] Добавить сопоставление виртуальных таблиц MDO +// [ ] Оптимизировать скорость парсера +// [?] Комментарии в многострочной строке diff --git a/src/main/java/com/github/_1c_syntax/bsl/parser/BSLParserRuleContext.java b/src/main/java/com/github/_1c_syntax/bsl/parser/BSLParserRuleContext.java index bdb2309e..5c8cd48a 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/parser/BSLParserRuleContext.java +++ b/src/main/java/com/github/_1c_syntax/bsl/parser/BSLParserRuleContext.java @@ -23,9 +23,7 @@ import com.github._1c_syntax.utils.Lazy; import org.antlr.v4.runtime.ParserRuleContext; -import org.antlr.v4.runtime.RuleContext; import org.antlr.v4.runtime.Token; -import org.antlr.v4.runtime.tree.ErrorNode; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; @@ -56,7 +54,7 @@ public List getTokens() { } private List computeTokens() { - if ( children == null ) { + if (children == null) { return Collections.emptyList(); } diff --git a/src/main/java/com/github/_1c_syntax/bsl/parser/BSLTokenizer.java b/src/main/java/com/github/_1c_syntax/bsl/parser/BSLTokenizer.java new file mode 100644 index 00000000..ffbdd185 --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/parser/BSLTokenizer.java @@ -0,0 +1,36 @@ +/* + * This file is a part of BSL Parser. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov , Sergey Batanov + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Parser is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Parser is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Parser. + */ +package com.github._1c_syntax.bsl.parser; + +import org.antlr.v4.runtime.CharStreams; + +public class BSLTokenizer extends Tokenizer { + public BSLTokenizer(String content) { + super(content, new BSLLexer(CharStreams.fromString(""), true), BSLParser.class); + } + + @Override + protected BSLParser.FileContext rootAST() { + return parser.file(); + } + +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/parser/SDBLTokenizer.java b/src/main/java/com/github/_1c_syntax/bsl/parser/SDBLTokenizer.java new file mode 100644 index 00000000..89866ef8 --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/parser/SDBLTokenizer.java @@ -0,0 +1,35 @@ +/* + * This file is a part of BSL Parser. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov , Sergey Batanov + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Parser is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Parser is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Parser. + */ +package com.github._1c_syntax.bsl.parser; + +import org.antlr.v4.runtime.CharStreams; + +public class SDBLTokenizer extends Tokenizer { + public SDBLTokenizer(String content) { + super(content, new SDBLLexer(CharStreams.fromString(""), true), SDBLParser.class); + } + + @Override + protected SDBLParser.QueryPackageContext rootAST() { + return parser.queryPackage(); + } +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/parser/Tokenizer.java b/src/main/java/com/github/_1c_syntax/bsl/parser/Tokenizer.java index 99fc4f3e..a86724b5 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/parser/Tokenizer.java +++ b/src/main/java/com/github/_1c_syntax/bsl/parser/Tokenizer.java @@ -28,7 +28,9 @@ import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.ConsoleErrorListener; import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.Parser; import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.TokenStream; import org.antlr.v4.runtime.atn.PredictionMode; import org.apache.commons.io.IOUtils; @@ -36,38 +38,41 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; +import java.lang.reflect.InvocationTargetException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import static java.util.Objects.requireNonNull; import static org.antlr.v4.runtime.Token.EOF; -public class Tokenizer { +abstract public class Tokenizer { private final InputStream content; - private Lexer lexer; + private final Lexer lexer; private final Lazy tokenStream = new Lazy<>(this::computeTokenStream); private final Lazy> tokens = new Lazy<>(this::computeTokens); - private final Lazy ast = new Lazy<>(this::computeAST); + private final Lazy ast = new Lazy<>(this::computeAST); + private final Class

parserClass; + protected P parser; - public Tokenizer(String content) { - this(content, null); + protected Tokenizer(String content, Lexer lexer, Class

parserClass) { + this(IOUtils.toInputStream(content, StandardCharsets.UTF_8), lexer, parserClass); } - protected Tokenizer(String content, Lexer lexer) { - this(IOUtils.toInputStream(content, StandardCharsets.UTF_8), lexer); - } - - protected Tokenizer(InputStream content, Lexer lexer) { + protected Tokenizer(InputStream content, Lexer lexer, Class

parserClass) { + requireNonNull(content); + requireNonNull(lexer); this.content = content; this.lexer = lexer; + this.parserClass = parserClass; } public List getTokens() { return tokens.getOrCompute(); } - public BSLParser.FileContext getAst() { + public T getAst() { return ast.getOrCompute(); } @@ -76,32 +81,34 @@ private List computeTokens() { Token lastToken = tokensTemp.get(tokensTemp.size() - 1); if (lastToken.getType() == EOF && lastToken instanceof CommonToken) { - ((CommonToken)lastToken).setChannel(Lexer.HIDDEN); + ((CommonToken) lastToken).setChannel(Lexer.HIDDEN); } return tokensTemp; } - private BSLParser.FileContext computeAST() { - BSLParser parser = new BSLParser(getTokenStream()); + private T computeAST() { + parser = createParser(getTokenStream()); parser.removeErrorListener(ConsoleErrorListener.INSTANCE); try { parser.getInterpreter().setPredictionMode(PredictionMode.SLL); - return parser.file(); + return rootAST(); } catch (Exception ex) { parser.reset(); // rewind input stream parser.getInterpreter().setPredictionMode(PredictionMode.LL); } - return parser.file(); + return rootAST(); } + abstract protected T rootAST(); + private CommonTokenStream computeTokenStream() { CharStream input; try ( UnicodeBOMInputStream ubis = new UnicodeBOMInputStream(content); - Reader inputStreamReader = new InputStreamReader(ubis, StandardCharsets.UTF_8); + Reader inputStreamReader = new InputStreamReader(ubis, StandardCharsets.UTF_8) ) { ubis.skipBOM(); input = CharStreams.fromReader(inputStreamReader); @@ -109,11 +116,7 @@ private CommonTokenStream computeTokenStream() { throw new RuntimeException(e); } - if (lexer == null) { - lexer = new BSLLexer(input, true); - } else { - lexer.setInputStream(input); - } + lexer.setInputStream(input); lexer.removeErrorListener(ConsoleErrorListener.INSTANCE); CommonTokenStream tempTokenStream = new CommonTokenStream(lexer); @@ -121,10 +124,18 @@ private CommonTokenStream computeTokenStream() { return tempTokenStream; } - private CommonTokenStream getTokenStream() { + protected CommonTokenStream getTokenStream() { final CommonTokenStream tokenStreamUnboxed = tokenStream.getOrCompute(); tokenStreamUnboxed.seek(0); return tokenStreamUnboxed; } + private P createParser(CommonTokenStream tokenStream) { + try { + return parserClass.getDeclaredConstructor(TokenStream.class) + .newInstance(tokenStream); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/com/github/_1c_syntax/bsl/parser/UnicodeBOMInputStream.java b/src/main/java/com/github/_1c_syntax/bsl/parser/UnicodeBOMInputStream.java index 82cfb67d..fad2afad 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/parser/UnicodeBOMInputStream.java +++ b/src/main/java/com/github/_1c_syntax/bsl/parser/UnicodeBOMInputStream.java @@ -57,8 +57,8 @@ public class UnicodeBOMInputStream extends InputStream { private final PushbackInputStream in; - private final BOM bom; - private boolean skipped; + private final BOM bom; + private boolean skipped; /** * Type safe enumeration class that describes the different types of Unicode @@ -66,8 +66,8 @@ public class UnicodeBOMInputStream extends InputStream { */ public static final class BOM { - private final byte[] bytes; - private final String description; + private final byte[] bytes; + private final String description; /** * NONE. @@ -77,50 +77,50 @@ public static final class BOM { /** * UTF-8 BOM (EF BB BF). */ - public static final BOM UTF_8 = new BOM(new byte[]{(byte)0xEF, - (byte)0xBB, - (byte)0xBF}, + public static final BOM UTF_8 = new BOM(new byte[]{(byte) 0xEF, + (byte) 0xBB, + (byte) 0xBF}, "UTF-8"); /** * UTF-16, little-endian (FF FE). */ - public static final BOM UTF_16_LE = new BOM(new byte[]{ (byte)0xFF, - (byte)0xFE}, + public static final BOM UTF_16_LE = new BOM(new byte[]{(byte) 0xFF, + (byte) 0xFE}, "UTF-16 little-endian"); /** * UTF-16, big-endian (FE FF). */ - public static final BOM UTF_16_BE = new BOM(new byte[]{ (byte)0xFE, - (byte)0xFF}, + public static final BOM UTF_16_BE = new BOM(new byte[]{(byte) 0xFE, + (byte) 0xFF}, "UTF-16 big-endian"); /** * UTF-32, little-endian (FF FE 00 00). */ - public static final BOM UTF_32_LE = new BOM(new byte[]{ (byte)0xFF, - (byte)0xFE, - (byte)0x00, - (byte)0x00}, + public static final BOM UTF_32_LE = new BOM(new byte[]{(byte) 0xFF, + (byte) 0xFE, + (byte) 0x00, + (byte) 0x00}, "UTF-32 little-endian"); /** * UTF-32, big-endian (00 00 FE FF). */ - public static final BOM UTF_32_BE = new BOM(new byte[]{ (byte)0x00, - (byte)0x00, - (byte)0xFE, - (byte)0xFF}, + public static final BOM UTF_32_BE = new BOM(new byte[]{(byte) 0x00, + (byte) 0x00, + (byte) 0xFE, + (byte) 0xFF}, "UTF-32 big-endian"); private BOM(final byte[] bom, final String description) { - assert(bom != null) : "invalid BOM: null is not allowed"; - assert(description != null) : "invalid description: null is not allowed"; - assert(description.length() != 0) : "invalid description: empty string is not allowed"; + assert (bom != null) : "invalid BOM: null is not allowed"; + assert (description != null) : "invalid description: null is not allowed"; + assert (description.length() != 0) : "invalid description: empty string is not allowed"; - this.bytes = bom.clone(); - this.description = description; + this.bytes = bom.clone(); + this.description = description; } /** @@ -135,8 +135,8 @@ public String toString() { * Returns the bytes corresponding to this BOM value. */ public byte[] getBytes() { - final int length = bytes.length; - final byte[] result = new byte[length]; + final int length = bytes.length; + final byte[] result = new byte[length]; // make a defensive copy System.arraycopy(bytes, 0, result, 0, length); @@ -151,13 +151,12 @@ public byte[] getBytes() { * specified InputStream. * * @param inputStream an InputStream. - * * @throws NullPointerException when inputStream is - * null. - * @throws IOException on reading from the specified InputStream - * when trying to detect the Unicode BOM. + * null. + * @throws IOException on reading from the specified InputStream + * when trying to detect the Unicode BOM. */ - public UnicodeBOMInputStream(final InputStream inputStream) throws NullPointerException, + public UnicodeBOMInputStream(final InputStream inputStream) throws NullPointerException, IOException { if (inputStream == null) { throw new NullPointerException("invalid input stream: null is not allowed"); @@ -165,40 +164,40 @@ public UnicodeBOMInputStream(final InputStream inputStream) throws NullPointerE in = new PushbackInputStream(inputStream, 4); - final byte[] bom = new byte[4]; - final int read = in.read(bom); + final byte[] bom = new byte[4]; + final int read = in.read(bom); - switch(read) { + switch (read) { case 4: - if ((bom[0] == (byte)0xFF) && - (bom[1] == (byte)0xFE) && - (bom[2] == (byte)0x00) && - (bom[3] == (byte)0x00)) { + if ((bom[0] == (byte) 0xFF) && + (bom[1] == (byte) 0xFE) && + (bom[2] == (byte) 0x00) && + (bom[3] == (byte) 0x00)) { this.bom = BOM.UTF_32_LE; break; - } else if ((bom[0] == (byte)0x00) && - (bom[1] == (byte)0x00) && - (bom[2] == (byte)0xFE) && - (bom[3] == (byte)0xFF)) { + } else if ((bom[0] == (byte) 0x00) && + (bom[1] == (byte) 0x00) && + (bom[2] == (byte) 0xFE) && + (bom[3] == (byte) 0xFF)) { this.bom = BOM.UTF_32_BE; break; } case 3: - if ((bom[0] == (byte)0xEF) && - (bom[1] == (byte)0xBB) && - (bom[2] == (byte)0xBF)) { + if ((bom[0] == (byte) 0xEF) && + (bom[1] == (byte) 0xBB) && + (bom[2] == (byte) 0xBF)) { this.bom = BOM.UTF_8; break; } case 2: - if ((bom[0] == (byte)0xFF) && - (bom[1] == (byte)0xFE)) { + if ((bom[0] == (byte) 0xFF) && + (bom[1] == (byte) 0xFE)) { this.bom = BOM.UTF_16_LE; break; - } else if ((bom[0] == (byte)0xFE) && - (bom[1] == (byte)0xFF)) { + } else if ((bom[0] == (byte) 0xFE) && + (bom[1] == (byte) 0xFF)) { this.bom = BOM.UTF_16_BE; break; } @@ -229,9 +228,8 @@ public final BOM getBOM() { * InputStream object. * * @return this UnicodeBOMInputStream. - * * @throws IOException when trying to skip the BOM from the wrapped - * InputStream object. + * InputStream object. */ public final synchronized UnicodeBOMInputStream skipBOM() throws IOException { if (!skipped) { @@ -250,7 +248,7 @@ public int read() throws IOException { } @Override - public int read(final byte[] b) throws IOException, NullPointerException { + public int read(final byte[] b) throws IOException, NullPointerException { return in.read(b, 0, b.length); } diff --git a/src/test/java/com/github/_1c_syntax/bsl/parser/AbstractLexerTest.java b/src/test/java/com/github/_1c_syntax/bsl/parser/AbstractLexerTest.java new file mode 100644 index 00000000..75ed08d2 --- /dev/null +++ b/src/test/java/com/github/_1c_syntax/bsl/parser/AbstractLexerTest.java @@ -0,0 +1,103 @@ +/* + * This file is a part of BSL Parser. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov , Sergey Batanov + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Parser is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Parser is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Parser. + */ +package com.github._1c_syntax.bsl.parser; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CodePointCharStream; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.ConsoleErrorListener; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.Token; +import org.apache.commons.io.IOUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +abstract class AbstractLexerTest { + + private final T lexer; + + protected AbstractLexerTest(Class lexerClass) { + this.lexer = createLexer(lexerClass); + } + + private T createLexer(Class lexerClass) { + try { + return lexerClass.getDeclaredConstructor(CharStream.class, boolean.class) + .newInstance(CharStreams.fromString(""), true); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + protected List getTokens(int mode, String inputString) { + CharStream input; + + try ( + InputStream inputStream = IOUtils.toInputStream(inputString, StandardCharsets.UTF_8); + UnicodeBOMInputStream ubis = new UnicodeBOMInputStream(inputStream); + Reader inputStreamReader = new InputStreamReader(ubis, StandardCharsets.UTF_8) + ) { + ubis.skipBOM(); + CodePointCharStream inputTemp = CharStreams.fromReader(inputStreamReader); + input = new CaseChangingCharStream(inputTemp); + } catch (IOException e) { + throw new RuntimeException(e); + } + + lexer.setInputStream(input); + lexer.removeErrorListener(ConsoleErrorListener.INSTANCE); + lexer.pushMode(mode); + + CommonTokenStream tempTokenStream = new CommonTokenStream(lexer); + tempTokenStream.fill(); + + return tempTokenStream.getTokens(); + } + + protected void assertMatch(String inputString, Integer... expectedTokens) { + assertMatch(T.DEFAULT_MODE, inputString, expectedTokens); + } + + protected void assertMatch(String inputStringRu, String inputStringEn, Integer... expectedTokens) { + assertMatch(T.DEFAULT_MODE, inputStringRu, expectedTokens); + assertMatch(T.DEFAULT_MODE, inputStringEn, expectedTokens); + } + + protected void assertMatch(int mode, String inputString, Integer... expectedTokens) { + List tokens = getTokens(mode, inputString); + Integer[] tokenTypes = tokens.stream() + .filter(token -> token.getChannel() == T.DEFAULT_TOKEN_CHANNEL) + .filter(token -> token.getType() != Token.EOF) + .map(Token::getType) + .toArray(Integer[]::new); + assertArrayEquals(expectedTokens, tokenTypes); + } +} diff --git a/src/test/java/com/github/_1c_syntax/bsl/parser/AbstractParserTest.java b/src/test/java/com/github/_1c_syntax/bsl/parser/AbstractParserTest.java new file mode 100644 index 00000000..cb4b974b --- /dev/null +++ b/src/test/java/com/github/_1c_syntax/bsl/parser/AbstractParserTest.java @@ -0,0 +1,147 @@ +/* + * This file is a part of BSL Parser. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov , Sergey Batanov + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Parser is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Parser is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Parser. + */ +package com.github._1c_syntax.bsl.parser; + +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CodePointCharStream; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.ConsoleErrorListener; +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.TokenStream; +import org.antlr.v4.runtime.tree.ParseTree; +import org.apache.commons.io.IOUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.StandardCharsets; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +abstract class AbstractParserTest

{ + protected P parser; + private final Class lexerClass; + private final Class

parserClass; + + protected AbstractParserTest(Class

parserClass, Class lexerClass) { + this.lexerClass = lexerClass; + this.parserClass = parserClass; + } + + protected void setInput(String inputString) { + setInput(inputString, Lexer.DEFAULT_MODE); + } + + protected void setInput(String inputString, int mode) { + CharStream input; + + try ( + InputStream inputStream = IOUtils.toInputStream(inputString, StandardCharsets.UTF_8); + UnicodeBOMInputStream ubis = new UnicodeBOMInputStream(inputStream); + Reader inputStreamReader = new InputStreamReader(ubis, StandardCharsets.UTF_8) + ) { + ubis.skipBOM(); + CodePointCharStream inputTemp = CharStreams.fromReader(inputStreamReader); + input = new CaseChangingCharStream(inputTemp); + } catch (IOException e) { + throw new RuntimeException(e); + } + + var lexer = createLexer(input); + lexer.removeErrorListener(ConsoleErrorListener.INSTANCE); + lexer.mode(mode); + + CommonTokenStream tokenStream = new CommonTokenStream(lexer); + tokenStream.fill(); + + parser = createParser(tokenStream); + parser.removeErrorListener(ConsoleErrorListener.INSTANCE); + } + + private L createLexer(CharStream inputStream) { + try { + return lexerClass.getDeclaredConstructor(CharStream.class, boolean.class) + .newInstance(inputStream, true); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + private P createParser(CommonTokenStream tokenStream) { + try { + return parserClass.getDeclaredConstructor(TokenStream.class) + .newInstance(tokenStream); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + protected void assertMatches(ParseTree tree) throws RecognitionException { + + if (parser.getNumberOfSyntaxErrors() != 0) { + throw new RecognitionException( + "Syntax error while parsing:\n" + parser.getInputStream().getText(), + parser, + parser.getInputStream(), + parser.getContext() + ); + } + + if (tree instanceof ParserRuleContext) { + ParserRuleContext ctx = (ParserRuleContext) tree; + if (ctx.exception != null) { + throw ctx.exception; + } + + if (((ParserRuleContext) tree).parent == null) { + boolean parseSuccess = lexerClass.cast(parser.getInputStream().getTokenSource())._hitEOF; + if (!parseSuccess) { + throw new RecognitionException( + "Parse error EOF don't hit\n" + parser.getInputStream().getText(), + parser, + parser.getInputStream(), + parser.getContext() + ); + } + } + } + + for (int i = 0; i < tree.getChildCount(); i++) { + ParseTree child = tree.getChild(i); + assertMatches(child); + } + } + + protected void assertNotMatches(ParseTree tree) { + assertThat(tree).satisfiesAnyOf( + (parseTree) -> assertThat(parseTree.getChildCount()).isEqualTo(0), + (parseTree) -> assertThrows(RecognitionException.class, () -> assertMatches(tree)) + ); + } +} diff --git a/src/test/java/com/github/_1c_syntax/bsl/parser/BSLLexerTest.java b/src/test/java/com/github/_1c_syntax/bsl/parser/BSLLexerTest.java index 1463f4c5..d2aa353b 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/parser/BSLLexerTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/parser/BSLLexerTest.java @@ -21,65 +21,17 @@ */ package com.github._1c_syntax.bsl.parser; -import org.antlr.v4.runtime.*; -import org.apache.commons.io.IOUtils; +import org.antlr.v4.runtime.Token; import org.junit.jupiter.api.Test; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.nio.charset.StandardCharsets; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; - -class BSLLexerTest { - - private BSLLexer lexer; - - private List getTokens(int mode, String inputString) { - CharStream input; - - try ( - InputStream inputStream = IOUtils.toInputStream(inputString, StandardCharsets.UTF_8); - UnicodeBOMInputStream ubis = new UnicodeBOMInputStream(inputStream); - Reader inputStreamReader = new InputStreamReader(ubis, StandardCharsets.UTF_8) - ) { - ubis.skipBOM(); - CodePointCharStream inputTemp = CharStreams.fromReader(inputStreamReader); - input = new CaseChangingCharStream(inputTemp); - } catch (IOException e) { - throw new RuntimeException(e); - } - - if (lexer == null) { - lexer = new BSLLexer(input, true); - } else { - lexer.setInputStream(input); - } - lexer.removeErrorListener(ConsoleErrorListener.INSTANCE); - lexer.pushMode(mode); - - CommonTokenStream tempTokenStream = new CommonTokenStream(lexer); - tempTokenStream.fill(); - - return tempTokenStream.getTokens(); - } - private void assertMatch(String inputString, Integer... expectedTokens) { - assertMatch(BSLLexer.DEFAULT_MODE, inputString, expectedTokens); - } +class BSLLexerTest extends AbstractLexerTest { - private void assertMatch(int mode, String inputString, Integer... expectedTokens) { - List tokens = getTokens(mode, inputString); - Integer[] tokenTypes = tokens.stream() - .filter(token -> token.getChannel() == BSLLexer.DEFAULT_TOKEN_CHANNEL) - .filter(token -> token.getType() != Token.EOF) - .map(Token::getType) - .toArray(Integer[]::new); - assertArrayEquals(expectedTokens, tokenTypes); + BSLLexerTest() { + super(BSLLexer.class); } @Test @@ -209,23 +161,23 @@ void testString() { assertMatch("|\"", BSLLexer.STRINGTAIL); assertMatch("|aaa\"", BSLLexer.STRINGTAIL); assertMatch("А = \"строка\" + \"строка\";", - BSLLexer.IDENTIFIER, - BSLLexer.ASSIGN, - BSLLexer.STRING, - BSLLexer.PLUS, - BSLLexer.STRING, - BSLLexer.SEMICOLON + BSLLexer.IDENTIFIER, + BSLLexer.ASSIGN, + BSLLexer.STRING, + BSLLexer.PLUS, + BSLLexer.STRING, + BSLLexer.SEMICOLON ); assertMatch("\"\"\"\"", BSLLexer.STRING); assertMatch("|СПЕЦСИМВОЛ \"\"~\"\"\"", BSLLexer.STRINGTAIL); assertMatch("\"Минимальная версия платформы \"\"1С:Предприятие 8\"\" указана выше рекомендуемой.", BSLLexer.STRINGSTART); assertMatch("А = \" \n | А \"\"\"\" + А \n |\";", - BSLLexer.IDENTIFIER, - BSLLexer.ASSIGN, - BSLLexer.STRINGSTART, - BSLLexer.STRINGPART, - BSLLexer.STRINGTAIL, - BSLLexer.SEMICOLON); + BSLLexer.IDENTIFIER, + BSLLexer.ASSIGN, + BSLLexer.STRINGSTART, + BSLLexer.STRINGPART, + BSLLexer.STRINGTAIL, + BSLLexer.SEMICOLON); } @Test diff --git a/src/test/java/com/github/_1c_syntax/bsl/parser/BSLParserTest.java b/src/test/java/com/github/_1c_syntax/bsl/parser/BSLParserTest.java index 063c841d..bd78fcbb 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/parser/BSLParserTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/parser/BSLParserTest.java @@ -21,110 +21,12 @@ */ package com.github._1c_syntax.bsl.parser; -import org.antlr.v4.runtime.CharStream; -import org.antlr.v4.runtime.CharStreams; -import org.antlr.v4.runtime.CodePointCharStream; -import org.antlr.v4.runtime.CommonTokenStream; -import org.antlr.v4.runtime.ConsoleErrorListener; -import org.antlr.v4.runtime.ParserRuleContext; -import org.antlr.v4.runtime.RecognitionException; -import org.antlr.v4.runtime.tree.ParseTree; -import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.nio.charset.StandardCharsets; +class BSLParserTest extends AbstractParserTest { -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -class BSLParserTest { - - private BSLParser parser; - - private void setInput(String inputString) { - setInput(inputString, BSLLexer.DEFAULT_MODE); - } - - private void setInput(String inputString, int mode) { - CharStream input; - - try ( - InputStream inputStream = IOUtils.toInputStream(inputString, StandardCharsets.UTF_8); - UnicodeBOMInputStream ubis = new UnicodeBOMInputStream(inputStream); - Reader inputStreamReader = new InputStreamReader(ubis, StandardCharsets.UTF_8) - ) { - ubis.skipBOM(); - CodePointCharStream inputTemp = CharStreams.fromReader(inputStreamReader); - input = new CaseChangingCharStream(inputTemp); - } catch (IOException e) { - throw new RuntimeException(e); - } - - BSLLexer lexer = new BSLLexer(input, true); - lexer.removeErrorListener(ConsoleErrorListener.INSTANCE); - lexer.mode(mode); - - CommonTokenStream tokenStream = new CommonTokenStream(lexer); - tokenStream.fill(); - - parser = new BSLParser(tokenStream); - parser.removeErrorListener(ConsoleErrorListener.INSTANCE); - } - - private void assertMatches(ParseTree tree) throws RecognitionException { - - if (parser.getNumberOfSyntaxErrors() != 0) { - throw new RecognitionException( - "Syntax error while parsing:\n" + parser.getInputStream().getText(), - parser, - parser.getInputStream(), - parser.getContext() - ); - } - - if (tree instanceof ParserRuleContext) { - ParserRuleContext ctx = (ParserRuleContext) tree; - if (ctx.exception != null) { - throw ctx.exception; - } - - if (((ParserRuleContext) tree).parent == null) { - boolean parseSuccess = ((BSLLexer) parser.getInputStream().getTokenSource())._hitEOF; - if (!parseSuccess) { - throw new RecognitionException( - "Parse error EOF don't hit\n" + parser.getInputStream().getText(), - parser, - parser.getInputStream(), - parser.getContext() - ); - } - } - - if (tree.getChildCount() == 0 && ((ParserRuleContext) tree).getStart() != null) { -// throw new RecognitionException( -// "Node without children and with filled start token\n" + parser.getInputStream().getText(), -// parser, -// parser.getInputStream(), -// parser.getContext() -// ); - } - } - - for (int i = 0; i < tree.getChildCount(); i++) { - ParseTree child = tree.getChild(i); - assertMatches(child); - } - } - - private void assertNotMatches(ParseTree tree) { - assertThat(tree).satisfiesAnyOf( - (parseTree) -> assertThat(parseTree.getChildCount()).isEqualTo(0), - (parseTree) -> assertThrows(RecognitionException.class, () -> assertMatches(tree)) - ); + protected BSLParserTest() { + super(BSLParser.class, BSLLexer.class); } @Test diff --git a/src/test/java/com/github/_1c_syntax/bsl/parser/TokenizerTest.java b/src/test/java/com/github/_1c_syntax/bsl/parser/BSLTokenizerTest.java similarity index 51% rename from src/test/java/com/github/_1c_syntax/bsl/parser/TokenizerTest.java rename to src/test/java/com/github/_1c_syntax/bsl/parser/BSLTokenizerTest.java index bf31c9c5..0b3027a3 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/parser/TokenizerTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/parser/BSLTokenizerTest.java @@ -23,42 +23,41 @@ import org.antlr.v4.runtime.Lexer; import org.antlr.v4.runtime.Token; -import org.antlr.v4.runtime.tree.ParseTree; import org.junit.jupiter.api.Test; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; -class TokenizerTest { +class BSLTokenizerTest { - @Test - void computeTokens() { - // given - Tokenizer tokenizer = new Tokenizer("Если Условие() Тогда КонецЕсли"); + @Test + void computeTokens() { + // given + BSLTokenizer tokenizer = new BSLTokenizer("Если Условие() Тогда КонецЕсли"); - // when - final List tokens = tokenizer.getTokens(); + // when + final List tokens = tokenizer.getTokens(); - // then - assertThat(tokens).hasSize(10); - assertThat(tokens.get(9).getType()).isEqualTo(Lexer.EOF); - assertThat(tokens.get(9).getChannel()).isEqualTo(Lexer.HIDDEN); - } + // then + assertThat(tokens).hasSize(10); + assertThat(tokens.get(9).getType()).isEqualTo(Lexer.EOF); + assertThat(tokens.get(9).getChannel()).isEqualTo(Lexer.HIDDEN); + } - @Test - void computeAST() { - // given - Tokenizer tokenizer = new Tokenizer("Если Условие() Тогда КонецЕсли"); + @Test + void computeAST() { + // given + BSLTokenizer tokenizer = new BSLTokenizer("Если Условие() Тогда КонецЕсли"); - // when - final BSLParser.FileContext ast = tokenizer.getAst(); + // when + final BSLParser.FileContext ast = tokenizer.getAst(); - // then - BSLParser.FileCodeBlockContext fileCodeBlock = ast.fileCodeBlock(); - assertThat(fileCodeBlock).isNotNull(); - assertThat(fileCodeBlock.getStart().getType()).isEqualTo(BSLParser.IF_KEYWORD); - assertThat(fileCodeBlock.getStop().getType()).isEqualTo(BSLParser.ENDIF_KEYWORD); - } + // then + BSLParser.FileCodeBlockContext fileCodeBlock = ast.fileCodeBlock(); + assertThat(fileCodeBlock).isNotNull(); + assertThat(fileCodeBlock.getStart().getType()).isEqualTo(BSLParser.IF_KEYWORD); + assertThat(fileCodeBlock.getStop().getType()).isEqualTo(BSLParser.ENDIF_KEYWORD); + } } \ No newline at end of file diff --git a/src/test/java/com/github/_1c_syntax/bsl/parser/SDBLLexerTest.java b/src/test/java/com/github/_1c_syntax/bsl/parser/SDBLLexerTest.java new file mode 100644 index 00000000..4482c4b4 --- /dev/null +++ b/src/test/java/com/github/_1c_syntax/bsl/parser/SDBLLexerTest.java @@ -0,0 +1,186 @@ +/* + * This file is a part of BSL Parser. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov , Sergey Batanov + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Parser is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Parser is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Parser. + */ +package com.github._1c_syntax.bsl.parser; + +import org.antlr.v4.runtime.Token; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SDBLLexerTest extends AbstractLexerTest { + protected SDBLLexerTest() { + super(SDBLLexer.class); + } + + @Test + void testWhitespaces() { + String inputString = " А"; + + List tokens = getTokens(SDBLLexer.DEFAULT_MODE, inputString); + + assertThat(tokens).extracting(Token::getType).containsExactly( + SDBLLexer.WHITE_SPACE, + SDBLLexer.IDENTIFIER, + SDBLLexer.EOF + ); + } + + @Test + void testKeyWords() { + assertMatch("ИСТиНА", "TRuE", SDBLLexer.TRUE); + assertMatch("ЛоЖЬ", "FaLSE", SDBLLexer.FALSE); + assertMatch("НеопределенО", "UNDEFINeD", SDBLLexer.UNDEFINED); + assertMatch("NUlL", SDBLLexer.NULL); + + assertMatch("Автоупорядочивание", "AUTOORDEr", SDBLLexer.AUTOORDER); + assertMatch("Булево", "Boolean", SDBLLexer.BOOLEAN); + assertMatch("В", "IN", SDBLLexer.IN); + assertMatch("ВНЕШНЕе", "OUTEr", SDBLLexer.OUTER); + assertMatch("ВНУТРЕННее", "INNeR", SDBLLexer.INNER); + assertMatch("ВОЗр", "aSC", SDBLLexer.ASC); + assertMatch("ВсЕ", "AlL", SDBLLexer.ALL); + assertMatch("ВыБОР", "CAsE", SDBLLexer.CASE); + assertMatch("ВЫБРАТь", "SELECt", SDBLLexer.SELECT); + assertMatch("ВЫРАзИТЬ", "CAST", SDBLLexer.CAST); + assertMatch("ГДЕ", "WHERE", SDBLLexer.WHERE); + assertMatch("ГОД", "YEAR", SDBLLexer.YEAR); + assertMatch("ДАТА", "DATE", SDBLLexer.DATE); + assertMatch("ДАТАВРЕМЯ", "DATETIME", SDBLLexer.DATETIME); + assertMatch("ДЕКАДА", "TENDAYS", SDBLLexer.TENDAYS); + assertMatch("ДЕНЬ", "DAY", SDBLLexer.DAY); + assertMatch("ДЕНЬГОДА", "DAYOFYEAR", SDBLLexer.DAYOFYEAR); + assertMatch("ДЕНЬНЕДЕЛИ", "WEEKDAY", SDBLLexer.WEEKDAY); + assertMatch("ДЛЯ ИЗМЕНЕНИЯ", "FOR UPDATE", SDBLLexer.FOR, SDBLLexer.UPDATE); + assertMatch("FOR UPDATE OF", SDBLLexer.FOR, SDBLLexer.UPDATE, SDBLLexer.OF); + assertMatch("ДОБАВИТЬКДАТЕ", "DATEADD", SDBLLexer.DATEADD); + assertMatch("ЕСТЬ", "IS", SDBLLexer.IS); + assertMatch("ЕСТЬNULL", "ISNULL", SDBLLexer.ISNULL); + assertMatch("Значение", "VALUE", SDBLLexer.VALUE); + assertMatch("И", "AND", SDBLLexer.AND); + assertMatch("HIERARCHY", SDBLLexer.HIERARCHY_EN); + assertMatch("ИЕРАРХИЯ", SDBLLexer.HIERARCHYA_RU); + assertMatch("ИЕРАРХИи", SDBLLexer.HIERARCHII_RU); + assertMatch("ИЗ", "FROM", SDBLLexer.FROM); + assertMatch("ИЛИ", "Or", SDBLLexer.OR); + assertMatch("ИМЕЮЩИЕ", "HAVING", SDBLLexer.HAVING); + assertMatch("ИНАЧЕ", "ELSE", SDBLLexer.ELSE); + assertMatch("ИНДЕКСИРОВАТЬ", "INDEX", SDBLLexer.INDEX); + assertMatch("ИТОГИ", "TOTALS", SDBLLexer.TOTALS); + assertMatch("КАК", "AS", SDBLLexer.AS); + assertMatch("КВАРТАЛ", "QUARTER", SDBLLexer.QUARTER); + assertMatch("КОГДА", "WHEN", SDBLLexer.WHEN); + assertMatch("КОЛИЧЕСТВО", "COUNT", SDBLLexer.COUNT); + assertMatch("КОНЕЦПЕРИОДА", "ENDOFPERIOD", SDBLLexer.ENDOFPERIOD); + assertMatch("КОНЕЦ", "END", SDBLLexer.END); + assertMatch("ЛЕВОЕ", "LEFT", SDBLLexer.LEFT); + assertMatch("МАКСИМУМ", "MAX", SDBLLexer.MAX); + assertMatch("МЕЖДУ", "BETWEEN", SDBLLexer.BETWEEN); + assertMatch("МЕСЯЦ", "MONTH", SDBLLexer.MONTH); + assertMatch("МИНИМУМ", "MIN", SDBLLexer.MIN); + assertMatch("МИНУТА", "MINUTE", SDBLLexer.MINUTE); + assertMatch("НАЧАЛОПЕРИОДА", "BEGINOFPERIOD", SDBLLexer.BEGINOFPERIOD); + assertMatch("НЕ", "Not", SDBLLexer.NOT); + assertMatch("НЕДЕЛЯ", "WEEK", SDBLLexer.WEEK); + assertMatch("ОБЩИЕ", "OVERALL", SDBLLexer.OVERALL); + assertMatch("ОБЪЕДИНИТЬ", "UNION", SDBLLexer.UNION); + assertMatch("ПЕРВЫЕ", "TOP", SDBLLexer.TOP); + assertMatch("ПЕРИОДАМИ", "PERIODS", SDBLLexer.PERIODS); + assertMatch("ПОДОБНО", "LIKE", SDBLLexer.LIKE); + assertMatch("ПОЛНОЕ", "FULL", SDBLLexer.FULL); + assertMatch("ПОЛУГОДИЕ", "HALFYEAR", SDBLLexer.HALFYEAR); + assertMatch("ПОМЕСТИТЬ", "INTO", SDBLLexer.INTO); + assertMatch("ПРАВОЕ", "RIGHT", SDBLLexer.RIGHT); + assertMatch("ПРЕДСТАВЛЕНИЕ", "PRESENTATION", SDBLLexer.PRESENTATION); + assertMatch("ПУСТАЯТАБЛИЦА", "EMPTYTABLE", SDBLLexer.EMPTYTABLE); + assertMatch("РАЗЛИЧНЫЕ", "DISTINCT", SDBLLexer.DISTINCT); + assertMatch("РАЗРЕШЕННЫЕ", "ALLOWED", SDBLLexer.ALLOWED); + assertMatch("Сгруппировать По", SDBLLexer.GROUP, SDBLLexer.PO_RU); + assertMatch("GROUP BY", SDBLLexer.GROUP, SDBLLexer.BY_EN); + assertMatch("СЕКУНДА", "SECOND", SDBLLexer.SECOND); + assertMatch("СОЕДИНЕНИЕ ПО", SDBLLexer.JOIN, SDBLLexer.PO_RU); + assertMatch("JOIN ON", SDBLLexer.JOIN, SDBLLexer.ON_EN); + assertMatch("СПЕЦСИМВОЛ", "ESCAPE", SDBLLexer.ESCAPE); + assertMatch("ПОДСТРОКА", "SUBSTRING", SDBLLexer.SUBSTRING); + assertMatch("СРЕДНЕЕ", "AVG", SDBLLexer.AVG); + assertMatch("ССЫЛКА", "REFS", SDBLLexer.REFS); + assertMatch("СТРОКА", "STRING", SDBLLexer.STRING); + assertMatch("СУММА", "SUM", SDBLLexer.SUM); + assertMatch("ТИП", "TYPE", SDBLLexer.TYPE); + assertMatch("ТИПЗНАЧЕНИЯ", "VALUETYPE", SDBLLexer.VALUETYPE); + assertMatch("ТОГДА", "THEN", SDBLLexer.THEN); + assertMatch("ТОЛЬКО", "ONLY", SDBLLexer.ONLY); + assertMatch("УБЫВ", "DESC", SDBLLexer.DESC); + assertMatch("УПОРЯДОЧИТЬ", "ORDER", SDBLLexer.ORDER); + assertMatch("ЧАС", "HOUR", SDBLLexer.HOUR); + assertMatch("ЧИСЛО", "NUMBER", SDBLLexer.NUMBER); + assertMatch("УНИЧТОЖИТЬ", "DROP", SDBLLexer.DROP); + + assertMatch("РазностьДат", "DateDiff", SDBLLexer.DATEDIFF); + assertMatch("автономерзаписи", "RECORDAUTONUMBER", SDBLLexer.RECORDAUTONUMBER); + + } + + @Test + void testStandardFields() { + assertMatch("ТочкаМаршрута", "RoutePoint", SDBLLexer.ROUTEPOINT_FIELD); + } + + @Test + void testMDOTypes() { + assertMatch("БизнесПроцесс", "BusinessProcess", SDBLLexer.BUSINESS_PROCESS_TYPE); + assertMatch("Справочник", "Catalog", SDBLLexer.CATALOG_TYPE); + assertMatch("ДОкумент", "Document", SDBLLexer.DOCUMENT_TYPE); + assertMatch("РегистрСведений", "InformationRegister", SDBLLexer.INFORMATION_REGISTER_TYPE); + assertMatch("Константа", "Constant", SDBLLexer.CONSTANT_TYPE); + assertMatch("КритерийОтбора", "FilterCriterion", SDBLLexer.FILTER_CRITERION_TYPE); + assertMatch("ПланОбмена", "ExchangePlan", SDBLLexer.EXCHANGE_PLAN_TYPE); + assertMatch("Последовательность", "SEQUENCE", SDBLLexer.SEQUENCE_TYPE); + assertMatch("ЖурналДокументов", "DocumentJournal", SDBLLexer.DOCUMENT_JOURNAL_TYPE); + assertMatch("Перечисление", "Enum", SDBLLexer.ENUM_TYPE); + assertMatch("ПланВидовХарактеристик", "ChartOfCharacteristicTypes", SDBLLexer.CHART_OF_CHARACTERISTIC_TYPES_TYPE); + assertMatch("ПланСчетов", "ChartOfAccounts", SDBLLexer.CHART_OF_ACCOUNTS_TYPE); + assertMatch("ПланВидоВРасчета", "ChartOfCalculationTypes", SDBLLexer.CHART_OF_CALCULATION_TYPES_TYPE); + assertMatch("РегистрНакопления", "AccumulationRegister", SDBLLexer.ACCUMULATION_REGISTER_TYPE); + assertMatch("РегистрБухгалтерии", "AccountingRegister", SDBLLexer.ACCOUNTING_REGISTER_TYPE); + assertMatch("РегистрРасчета", "CalculationRegister", SDBLLexer.CALCULATION_REGISTER_TYPE); + assertMatch("Задача", "Task", SDBLLexer.TASK_TYPE); + assertMatch("ВнешнийИсточникДанных", "ExternalDataSource", SDBLLexer.EXTERNAL_DATA_SOURCE_TYPE); + } + + @Test + void testMDOTT() { + assertMatch(".СрезПоследних", ".SLICELAST", SDBLLexer.DOT, SDBLLexer.SLICELAST_VT); + assertMatch(".СрезПервых", ".SLICEFIRST", SDBLLexer.DOT, SDBLLexer.SLICEFIRST_VT); + assertMatch(".Границы", ".BOUNDARIES", SDBLLexer.DOT, SDBLLexer.BOUNDARIES_VT); + assertMatch(".Обороты", ".TURNOVERS", SDBLLexer.DOT, SDBLLexer.TURNOVERS_VT); + assertMatch(".Остатки", ".BALANCE", SDBLLexer.DOT, SDBLLexer.BALANCE_VT); + assertMatch(".ОстаткиИОбороты", ".BALANCEANDTURNOVERS", SDBLLexer.DOT, SDBLLexer.BALANCE_AND_TURNOVERS_VT); + assertMatch(".Субконто", ".EXTDIMENSIONS", SDBLLexer.DOT, SDBLLexer.EXT_DIMENSIONS_VT); + assertMatch(".Движенияссубконто", ".RECORDSWITHEXTDIMENSIONS", SDBLLexer.DOT, SDBLLexer.RECORDS_WITH_EXT_DIMENSIONS_VT); + assertMatch(".ОборотыДтКт", ".DrCrTURNOVERS", SDBLLexer.DOT, SDBLLexer.DR_CR_TURNOVERS_VT); + assertMatch(".ФактическийПериодДействия", ".ACTUALACTIONPERIOD", SDBLLexer.DOT, SDBLLexer.ACTUAL_ACTION_PERIOD_VT); + assertMatch(".ДанныеГрафика", ".SCHEDULEDATA", SDBLLexer.DOT, SDBLLexer.SCHEDULE_DATA_VT); + assertMatch(".ЗадачиПоИсполнителю", ".TASKBYPERFORMER", SDBLLexer.DOT, SDBLLexer.TASK_BY_PERFORMER_VT); + } +} diff --git a/src/test/java/com/github/_1c_syntax/bsl/parser/SDBLParserTest.java b/src/test/java/com/github/_1c_syntax/bsl/parser/SDBLParserTest.java new file mode 100644 index 00000000..3f4d7e66 --- /dev/null +++ b/src/test/java/com/github/_1c_syntax/bsl/parser/SDBLParserTest.java @@ -0,0 +1,68 @@ +/* + * This file is a part of BSL Parser. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov , Sergey Batanov + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Parser is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Parser is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Parser. + */ +package com.github._1c_syntax.bsl.parser; + +import org.junit.jupiter.api.Test; + +public class SDBLParserTest extends AbstractParserTest { + + protected SDBLParserTest() { + super(SDBLParser.class, SDBLLexer.class); + } + + @Test + void testQueries() { + + setInput("А; Перем А;"); + assertNotMatches(parser.queries()); + + setInput("Выборка из таблицы"); + assertNotMatches(parser.queries()); + + setInput("Выбрать 1"); + assertMatches(parser.queries()); + + setInput("Выбрать 1; \n" + + "Уничтожить Б"); + assertMatches(parser.queries()); + + setInput("Выбрать 1, \n" + + "УничтожитьБорна как Б"); + assertMatches(parser.queries()); + + setInput("Выбрать 1, \n" + + "Уничтожить Б"); + assertMatches(parser.queries()); + } + + @Test + void testDropTable() { + + setInput("Уничтожить ИмяТаблицы"); + assertMatches(parser.queries()); + setInput("drop ИмяТаблицы;"); + assertMatches(parser.queries()); + setInput("drop"); + assertNotMatches(parser.queries()); + } + +} diff --git a/src/test/java/com/github/_1c_syntax/bsl/parser/SDBLTokenizerTest.java b/src/test/java/com/github/_1c_syntax/bsl/parser/SDBLTokenizerTest.java new file mode 100644 index 00000000..1a9b9945 --- /dev/null +++ b/src/test/java/com/github/_1c_syntax/bsl/parser/SDBLTokenizerTest.java @@ -0,0 +1,66 @@ +/* + * This file is a part of BSL Parser. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov , Sergey Batanov + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Parser is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Parser is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Parser. + */ +package com.github._1c_syntax.bsl.parser; + +import org.antlr.v4.runtime.Lexer; +import org.antlr.v4.runtime.Token; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class SDBLTokenizerTest { + + @Test + void computeTokens() { + // given + SDBLTokenizer tokenizer = new SDBLTokenizer("Выбрать Ссылка Из Справочник.Контрагенты"); + + // when + final List tokens = tokenizer.getTokens(); + + // then + assertThat(tokens).hasSize(10); + assertThat(tokens.get(9).getType()).isEqualTo(Lexer.EOF); + assertThat(tokens.get(9).getChannel()).isEqualTo(Lexer.HIDDEN); + } + + @Test + void computeAST() { + // given + SDBLTokenizer tokenizer = new SDBLTokenizer("Выбрать Ссылка Из Справочник.Контрагенты"); + + // when + final SDBLParser.QueryPackageContext ast = tokenizer.getAst(); + + // then + List queries = ast.queries(); + + assertThat(queries).isNotNull(); + assertThat(queries).hasSize(1); + SDBLParser.QueriesContext query = queries.get(0); + assertThat(query.getStart().getType()).isEqualTo(SDBLParser.SELECT); + assertThat(query.getStop().getType()).isEqualTo(SDBLParser.IDENTIFIER); + } + +} \ No newline at end of file