Initial code commit

This commit is contained in:
Steve Faulkner
2018-11-26 10:46:04 -05:00
parent cec84d950e
commit 63070531cd
48 changed files with 14579 additions and 10 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,124 @@
export class CosmosDBSqlKeywords{
private static readonly KeywordTypeHintPrefix : string = "KeywordTypeHint:";
public static keywordsRegisteredForCompletion : { [key : string] : string } =
{
"AND": "AND",
"ARRAY": "ARRAY",
"AS": "AS",
"ASC": "ASC",
"BETWEEN": "BETWEEN",
"BY": "BY",
"CASE": "CASE",
"CAST": "CAST",
"CONVERT": "CONVERT",
"CROSS": "CROSS",
"DESC": "DESC",
"DISTINCT": "DISTINCT",
"ELSE": "ELSE",
"END": "END",
"ESCAPE": "ESCAPE",
"EXISTS": "EXISTS",
"K_false": "false",
"FOR": "FOR",
"FROM": "FROM",
"GROUP": "GROUP",
"HAVING": "HAVING",
"IN": "IN",
"INNER": "INNER",
"INSERT": "INSERT",
"INTO": "INTO",
"IS": "IS",
"JOIN": "JOIN",
"LEFT": "LEFT",
"LIKE": "LIKE",
"LIMIT": "LIMIT",
"NOT": "NOT",
"K_null": "null",
"OFFSET": "OFFSET",
"ON": "ON",
"OR": "OR",
"ORDER": "ORDER",
"OUTER": "OUTER",
"OVER": "OVER",
"RIGHT": "RIGHT",
"SELECT": "SELECT",
"SET": "SET",
"THEN": "THEN",
"TOP": "TOP",
"K_true": "true",
"K_udf": "udf",
"K_undefined": "undefined",
"UPDATE": "UPDATE",
"VALUE": "VALUE",
"WHEN": "WHEN",
"WHERE": "WHERE",
"WITH": "WITH",
"Infinity": "Infinity",
"NaN": "NaN",
"ABS": "ABS",
"ACOS": "ACOS",
"ARRAY_CONCAT": "ARRAY_CONCAT",
"ARRAY_CONTAINS": "ARRAY_CONTAINS",
"ARRAY_LENGTH": "ARRAY_LENGTH",
"ARRAY_SLICE": "ARRAY_SLICE",
"ASIN": "ASIN",
"ATAN": "ATAN",
"ATN2": "ATN2",
"AVG": "AVG",
"CEILING": "CEILING",
"CONCAT": "CONCAT",
"CONTAINS": "CONTAINS",
"COS": "COS",
"COT": "COT",
"COUNT": "COUNT",
"DEGREES": "DEGREES",
"ENDSWITH": "ENDSWITH",
"EXP": "EXP",
"FLOOR": "FLOOR",
"INDEX_OF": "INDEX_OF",
"S_ARRAY": "S_ARRAY",
"IS_BOOL": "IS_BOOL",
"IS_DEFINED": "IS_DEFINED",
"IS_FINITE_NUMBER": "IS_FINITE_NUMBER",
"IS_NULL": "IS_NULL",
"IS_NUMBER": "IS_NUMBER",
"IS_OBJECT": "IS_OBJECT",
"IS_PRIMITIVE": "IS_PRIMITIVE",
"IS_STRING": "IS_STRING",
"LENGTH": "LENGTH",
"LOG10": "LOG10",
"LOWER": "LOWER",
"LTRIM": "LTRIM",
"MAX": "MAX",
"MIN": "MIN",
"PI": "PI",
"POWER": "POWER",
"RADIANS": "RADIANS",
"RAND": "RAND",
"REPLACE": "REPLACE",
"REPLICATE": "REPLICATE",
"REVERSE": "REVERSE",
"ROUND": "ROUND",
"RTRIM": "RTRIM",
"SIGN": "SIGN",
"SIN": "SIN",
"SQRT": "SQRT",
"SQUARE": "SQUARE",
"ST_DISTANCE": "ST_DISTANCE",
"ST_INTERSECTS": "ST_INTERSECTS",
"ST_ISVALID": "ST_ISVALID",
"ST_ISVALIDDETAILED": "ST_ISVALIDDETAILED",
"ST_WITHIN": "ST_WITHIN",
"STARTSWITH": "STARTSWITH",
"SUBSTRING": "SUBSTRING",
"SUM": "SUM",
"TAN": "TAN",
"TRUNC": "TRUNC",
"UPPER": "UPPER",
"ID": CosmosDBSqlKeywords.KeywordTypeHintPrefix + "ID",
"NUMBER": CosmosDBSqlKeywords.KeywordTypeHintPrefix + "NUMBER"
};
}
@@ -0,0 +1,184 @@
lexer grammar CosmosDBSqlLexer;
// keywords
AND: A N D;
ARRAY: A R R A Y;
AS: A S;
ASC: A S C;
BETWEEN: B E T W E E N;
BY: B Y;
CASE: C A S E;
CAST: C A S T;
CONVERT: C O N V E R T;
CROSS: C R O S S;
DESC: D E S C;
DISTINCT: D I S T I N C T;
ELSE: E L S E;
END: E N D;
ESCAPE: E S C A P E;
EXISTS: E X I S T S;
K_false: 'false'; // case sensitive
FOR: F O R;
FROM: F R O M ;
GROUP: G R O U P;
HAVING: H A V I N G;
IN: I N;
INNER: I N N E R;
INSERT: I N S E R T;
INTO: I N T O;
IS: I S;
JOIN: J O I N;
LEFT: L E F T;
LIKE: L I K E;
LIMIT: L I M I T;
NOT: N O T;
K_null: 'null'; // case sensitive
OFFSET: O F F S E T;
ON: O N;
OR: O R;
ORDER: O R D E R;
OUTER: O U T E R;
OVER: O V E R;
RIGHT: R I G H T;
SELECT: S E L E C T;
SET: S E T;
THEN: T H E N;
TOP: T O P;
K_true: 'true'; // case sensitive
K_udf: 'udf'; // case sensitive
K_undefined: 'undefined'; // case sensitive
UPDATE: U P D A T E;
VALUE: V A L U E;
WHEN: W H E N;
WHERE: W H E R E;
WITH: W I T H;
Infinity: 'Infinity'; // case sensitive
NaN: 'NaN'; // case sensitive
// build-in functions
ABS: A B S;
ACOS: A C O S;
ARRAY_CONCAT: A R R A Y '_' C O N C A T;
ARRAY_CONTAINS: A R R A Y '_' C O N T A I N S;
ARRAY_LENGTH: A R R A Y '_' L E N G T H;
ARRAY_SLICE: A R R A Y '_' S L I C E;
ASIN: A S I N;
ATAN: A T A N;
ATN2: A T N '2';
AVG: A V G;
CEILING: C E I L I N G;
CONCAT: C O N C A T;
CONTAINS: C O N T A I N S;
COS: C O S;
COT: C O T;
COUNT: C O U N T;
DEGREES: D E G R E E S;
ENDSWITH: E N D S W I T H;
EXP: E X P;
FLOOR: F L O O R;
INDEX_OF: I N D E X '_' O F;
S_ARRAY: I S '_' A R R A Y;
IS_BOOL: I S '_' B O O L;
IS_DEFINED: I S '_' D E F I N E D;
IS_FINITE_NUMBER: I S '_' F I N I T E '_' N U M B E R;
IS_NULL: I S '_' N U L L;
IS_NUMBER: I S '_' N U M B E R;
IS_OBJECT: I S '_' O B J E C T;
IS_PRIMITIVE: I S '_' P R I M I T I V E;
IS_STRING: I S '_' S T R I N G;
LENGTH: L E N G T H;
LOG: L O G;
LOG10: L O G '1' '0';
LOWER: L O W E R;
LTRIM: L T R I M;
MAX: M A X;
MIN: M I N;
PI: P I;
POWER: P O W E R;
RADIANS: R A D I A N S;
RAND: R A N D;
REPLACE: R E P L A C E;
REPLICATE: R E P L I C A T E;
REVERSE: R E V E R S E;
ROUND: R O U N D;
RTRIM: R T R I M;
SIGN: S I G N;
SIN: S I N;
SQRT: S Q R T;
SQUARE: S Q U A R E;
ST_DISTANCE: S T '_' D I S T A N C E;
ST_INTERSECTS: S T '_' I N T E R S E C T S;
ST_ISVALID: S T '_' I S V A L I D;
ST_ISVALIDDETAILED: S T '_' I S V A L I D D E T A I L E D;
ST_WITHIN: S T '_' W I T H I N;
STARTSWITH: S T A R T S W I T H;
SUBSTRING: S U B S T R I N G;
SUM: S U M;
TAN: T A N;
TRUNC: T R U N C;
UPPER: U P P E R;
// others
SPACE: [ \t\r\n]+ -> skip;
COMMENTS: '-' '-' ~[\t\r\n]+ [\t\r\n] -> skip;
// keywords type groups
ID: [a-zA-Z_][a-zA-Z_0-9]*;
NUMBER: [1-9][0-9]*;
// operators
COL: 'C';
COMMA: ',';
DOT: '.';
ADD: '+';
SUB: '-';
MUL: '*';
DIV: '/';
MOD: '%';
COLON: ':';
EQUAL: '=';
GREATER: '>';
LESS: '<';
BIT_NOT_OP: '~';
BIT_OR_OP: '|';
BIT_AND_OP: '&';
BIT_XOR_OP: '^';
QUEST: '?';
LEFT_BRACE: '{';
RIGHT_BRACE: '}';
LEFT_BRACKET: '[';
RIGHT_BRACKET: ']';
LEFT_PARENTHESIS: '(';
RIGHT_PARENTHESIS: ')';
QUOTE: '"'|'\'';
fragment A : [aA];
fragment B : [bB];
fragment C : [cC];
fragment D : [dD];
fragment E : [eE];
fragment F : [fF];
fragment G : [gG];
fragment H : [hH];
fragment I : [iI];
fragment J : [jJ];
fragment K : [kK];
fragment L : [lL];
fragment M : [mM];
fragment N : [nN];
fragment O : [oO];
fragment P : [pP];
fragment Q : [qQ];
fragment R : [rR];
fragment S : [sS];
fragment T : [tT];
fragment U : [uU];
fragment V : [vV];
fragment W : [wW];
fragment X : [xX];
fragment Y : [yY];
fragment Z : [zZ];
@@ -0,0 +1,250 @@
parser grammar CosmosDBSqlParser;
options {
tokenVocab = CosmosDBSqlLexer;
}
root
: sql_query
;
sql_query
: select_clause from_clause? where_clause? orderby_clause?
;
select_clause
: SELECT top_spec? selection
;
top_spec
: TOP NUMBER
;
from_clause
: FROM from_specification
;
where_clause
: WHERE scalar_expression
;
orderby_clause
: ORDER BY orderby_item_list
;
selection
: select_list
| select_value_spec
| MUL // FIXME 'SELECT *<EOF>' is not supported actually
;
select_value_spec
: VALUE scalar_expression
;
select_list
: select_item
| select_list COMMA select_item
;
select_item
: scalar_expression
| scalar_expression select_alias
;
select_alias
: ID
| AS ID
;
orderby_item_list
: orderby_item
| orderby_item_list COMMA orderby_item
;
orderby_item:
scalar_expression
| scalar_expression ASC
| scalar_expression DESC
;
from_specification
: primary_from_specification
| from_specification JOIN primary_from_specification
;
primary_from_specification
: input_collection
| input_collection input_alias
| ID IN input_collection
;
input_alias
: ID
| AS ID
;
input_collection
: relative_path
| LEFT_PARENTHESIS sql_query RIGHT_PARENTHESIS
;
relative_path
: relative_path_segment
| relative_path DOT relative_path_segment
| relative_path LEFT_BRACKET NUMBER RIGHT_BRACKET
| relative_path LEFT_BRACKET QUOTE relative_path_segment QUOTE RIGHT_BRACKET
;
relative_path_segment
: ID
;
array_item_list
: scalar_expression
| array_item_list COMMA scalar_expression
;
array_create_expression
: LEFT_BRACKET array_item_list? RIGHT_BRACKET
;
property_name
: ID
;
object_property
: property_name COLON scalar_expression
;
object_property_list:
object_property
| object_property_list COMMA object_property
;
object_create_expression
: LEFT_BRACE object_property_list? RIGHT_BRACE
;
function_arg_list:
scalar_expression
| function_arg_list COMMA scalar_expression
;
sys_function_name
: ID
;
udf_function_name
: ID
;
function_call_expression
: sys_function_name LEFT_PARENTHESIS function_arg_list? RIGHT_PARENTHESIS
| K_udf DOT udf_function_name LEFT_PARENTHESIS function_arg_list? RIGHT_PARENTHESIS
;
scalar_expression
: logical_scalar_expression
| between_scalar_expression
;
logical_scalar_expression
: binary_expression
| in_scalar_expression
| logical_scalar_expression AND logical_scalar_expression
| logical_scalar_expression OR logical_scalar_expression
;
between_scalar_expression
: binary_expression BETWEEN binary_expression AND binary_expression
| binary_expression NOT BETWEEN binary_expression AND binary_expression
;
in_scalar_expression
: binary_expression IN LEFT_PARENTHESIS in_scalar_expression_item_list RIGHT_PARENTHESIS
| binary_expression NOT IN LEFT_PARENTHESIS in_scalar_expression_item_list RIGHT_PARENTHESIS
;
exists_scalar_expression
: EXISTS LEFT_PARENTHESIS sql_query RIGHT_PARENTHESIS
;
array_scalar_expression
: ARRAY LEFT_PARENTHESIS sql_query RIGHT_PARENTHESIS
;
in_scalar_expression_item_list
: scalar_expression
| in_scalar_expression_item_list COMMA scalar_expression
;
binary_expression:
unary_expression
| binary_expression ADD binary_expression
| binary_expression SUB binary_expression
| binary_expression MUL binary_expression
| binary_expression DIV binary_expression
| binary_expression MOD binary_expression
| binary_expression EQUAL binary_expression
| binary_expression LESS binary_expression
| binary_expression GREATER binary_expression
| binary_expression BIT_AND_OP binary_expression
| binary_expression BIT_OR_OP binary_expression
| binary_expression BIT_XOR_OP binary_expression
;
unary_expression:
primary_expression
| SUB unary_expression
| ADD unary_expression
| BIT_NOT_OP unary_expression
| NOT unary_expression;
primary_expression
: constant
| input_alias
// parameter_name: Represents a value of the specified parameter name. Parameter names must have a single @ as the first character.
| array_create_expression
| object_create_expression
| function_call_expression
| LEFT_PARENTHESIS scalar_expression RIGHT_PARENTHESIS
| LEFT_PARENTHESIS sql_query RIGHT_PARENTHESIS
| primary_expression DOT property_name
| primary_expression LEFT_BRACKET scalar_expression RIGHT_BRACKET
| exists_scalar_expression
| array_scalar_expression
;
constant
: K_undefined
| K_null
| K_true
| K_false
| NUMBER
| QUOTE ID QUOTE
| array_constant
| object_constant
;
array_constant
: LEFT_BRACKET array_constant_list? RIGHT_BRACKET
;
array_constant_list
: constant
| array_constant_list COMMA constant
;
object_constant
: LEFT_BRACE object_constant_items? RIGHT_BRACE
;
object_constant_item
: property_name COLON constant
;
object_constant_items
: object_constant_item
| object_constant_items COMMA object_constant_item
;
+79
View File
@@ -0,0 +1,79 @@
import * as Q from "q";
import {editor, languages, MarkerSeverity} from "monaco-editor";
export enum ParseReason {
GetCompletionWords = 1,
GetErrors = 2
}
export class LanguageServiceFacade {
private static readonly timeout : number = 2000;
private static workingWorker : Worker = null;
public static GetLanguageServiceParseResult(str : string, parseReason : ParseReason) : Q.Promise<any[]> {
const timeExceeded = Q.Promise<any[]>((resolve : any, reject : any) => {
const wait = setTimeout(() => {
const words : any = [];
resolve(words);
}, LanguageServiceFacade.timeout);
});
const result = LanguageServiceFacade.GetParseResult(str, parseReason);
return Q.race([timeExceeded, result]).then(function(words) {
LanguageServiceFacade.workingWorker.terminate();
return words;
});
}
private static GetParseResult = (str : string, parseReason : ParseReason) : Q.Promise<any[]> => {
return Q.Promise((resolve : any) => {
if (LanguageServiceFacade.workingWorker != null) {
LanguageServiceFacade.workingWorker.terminate();
}
const currentUrlWithoutQueryParamsAndHashRoute: string = `${window.location.protocol}//${window.location.host}${window.location.pathname}`;
let url = currentUrlWithoutQueryParamsAndHashRoute.replace(/\/[^\/]*$/, '/node_modules/cosmosdb-language-service/dist/worker/dist/LanguageServiceWorker.js');
LanguageServiceFacade.workingWorker = new Worker(url);
LanguageServiceFacade.workingWorker.onmessage = (ev : MessageEvent) => {
var processedResults: any = [];
var results : any[] = ev.data;
if (parseReason === ParseReason.GetCompletionWords) {
results.forEach((label: string) => {
if (!!label) {
processedResults.push({
label: label,
kind: languages.CompletionItemKind.Keyword
});
}
});
} else if (parseReason === ParseReason.GetErrors) {
results.forEach((err: any) => {
const mark: editor.IMarkerData = {
severity: MarkerSeverity.Error,
message: err.Message,
startLineNumber: err.line,
startColumn: err.column,
endLineNumber: err.line,
endColumn: err.column
};
processedResults.push(mark)
});
}
resolve(processedResults);
}
const source = {
code : str,
reason : parseReason
};
LanguageServiceFacade.workingWorker.postMessage(source);
});
}
}
@@ -0,0 +1,33 @@
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
import { CommonTokenStream } from "antlr4/CommonTokenStream";
import { Lexer } from "antlr4/Lexer";
import { Token } from "antlr4/Token";
export class LSCommonTokenStream extends CommonTokenStream {
public EofListener;
constructor(tokenSource : Lexer) {
super(tokenSource);
}
public LA(i : number) : number {
let token : number = super.LA(i);
if (token != null && token == Token.EOF && this.EofListener != undefined) {
this.EofListener();
}
return token;
}
public LT(i : number) : any {
let token = super.LT(i);
if (token != null && token.type == Token.EOF && this.EofListener != undefined) {
this.EofListener();
}
return token;
}
}
+18
View File
@@ -0,0 +1,18 @@
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
import { ErrorListener } from "antlr4/error/ErrorListener";
export class LSErrorListener extends ErrorListener {
private AddSyntaxError : (msg : string, line : number, column : number) => any;
constructor(AddSyntaxError : (msg : string, line : number, column : number) => any) {
super();
this.AddSyntaxError = AddSyntaxError;
}
public syntaxError(recognizer: any, offendingSymbol: any,line: number, column: number, msg: string, e: any): void {
this.AddSyntaxError(msg, line, column);
}
}
@@ -0,0 +1,299 @@
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
import * as ATNState from "antlr4/atn/ATNState";
import * as Transition from "antlr4/atn/Transition";
import { ATN } from "antlr4/atn/ATN";
import { CommonTokenStream } from "antlr4/CommonTokenStream";
import { DFA } from "antlr4/dfa/DFA";
import { LanguageService } from "./LanguageService";
import { NoViableAltException } from "antlr4/error/Errors";
import { Parser } from "antlr4/Parser";
import { ParserATNSimulator } from "antlr4/atn/ParserATNSimulator";
import { PredictionContextCache } from "antlr4/PredictionContext";
import { PredictionMode } from "antlr4/atn/PredictionMode";
import { RuleContext } from "antlr4/RuleContext";
import { Token } from "antlr4/Token";
import { Utils } from "./Utils";
interface StateWithTransitionPath {
state : ATNState.ATNState,
transitionStates : ATNState.ATNState[]
}
export class LSParserATNSimulator extends ParserATNSimulator {
private predictionMode = PredictionMode.LL;
private parser : Parser;
private atn : ATN;
private languageService : LanguageService;
constructor(parser : Parser, atn : ATN, decisionToDFA : Array<DFA>, sharedContextCache : PredictionContextCache, languageService : LanguageService) {
super(parser, atn, decisionToDFA, sharedContextCache);
this.parser = parser;
this.atn = atn;
this.languageService = languageService;
}
public adaptivePredict(input : CommonTokenStream, decision : number, outerContext : RuleContext) {
let tokensLeft : number = -1;
try {
this.languageService.IsInPredict = true;
this.languageService.EofReachedInPredict = false;
if (decision >= 0) {
return super.adaptivePredict(input, decision, outerContext);
}
}
catch(error) {
if (error instanceof NoViableAltException && error.offendingToken.type === Token.EOF) {
tokensLeft = error.offendingToken.tokenIndex - this.parser.getCurrentToken().tokenIndex;
return 1;
} else {
throw error;
}
}
finally {
if (this.languageService.EofReachedInPredict) {
if (tokensLeft < 0) {
tokensLeft = 0;
while (input.LA(tokensLeft + 1) != Token.EOF) {
tokensLeft++;
}
}
if (tokensLeft > 0) {
let states = this.CalculateValidStates(input, tokensLeft);
this.languageService.RecordErrorStatesBeforeEof(states);
}
}
this.languageService.IsInPredict = false;
}
}
private CalculateValidStates(input : CommonTokenStream, tokensLeft : number): ATNState.ATNState[] {
let state = this.atn.states[this.parser.state];
let states : StateWithTransitionPath[] = [ {
state : state,
transitionStates : []
} ];
let validStates : StateWithTransitionPath[] = [];
// one step each time. Consume a single token each time.
for (let index = 1; index <= tokensLeft; index++) {
let _states : StateWithTransitionPath[] = [];
let nextToken : number = input.LA(index);
states.forEach(s => { _states = _states.concat(this.ConsumeSingleTokenAhead(s, nextToken)).filter(Utils.notDuplicate); });
states = _states.filter(Utils.notDuplicate);
}
states.forEach(s => { validStates = validStates.concat(this.SearchValidStates(s)); });
return validStates.map(s => s.state).filter(Utils.notDuplicate);
}
private ConsumeSingleTokenAhead(stateWithTransitionPath : StateWithTransitionPath, matchToken : Token) : StateWithTransitionPath[] {
let validStates : StateWithTransitionPath[] = [];
let currentState = stateWithTransitionPath.state;
let nextStateWithTransitionPath : StateWithTransitionPath = {
state : null, // Temporary null
transitionStates : stateWithTransitionPath.transitionStates.slice()
};
if(nextStateWithTransitionPath.transitionStates.length > 0 &&
nextStateWithTransitionPath.transitionStates[nextStateWithTransitionPath.transitionStates.length - 1].ruleIndex === currentState.ruleIndex) {
nextStateWithTransitionPath.transitionStates.pop();
}
nextStateWithTransitionPath.transitionStates.push(currentState);
if (!(currentState instanceof ATNState.RuleStopState)) {
for (let index = 0;index < currentState.transitions.length;index++) {
let transition = currentState.transitions[index];
let destinationChildState = transition.target;
nextStateWithTransitionPath.state = destinationChildState;
if (!transition.isEpsilon) {
if (transition.label != null && transition.label.contains(matchToken)) {
validStates = validStates.concat(this.SearchValidStates(nextStateWithTransitionPath));
}
} else {
validStates = validStates.concat(this.ConsumeSingleTokenAhead(nextStateWithTransitionPath, matchToken)).filter(Utils.notDuplicate);
}
}
}
return validStates.filter(Utils.notEmpty);
}
private SearchValidStates(stateWithTransitionPath : StateWithTransitionPath) : StateWithTransitionPath[] {
let validStates : StateWithTransitionPath[] = [];
if (!this.IsLastStateBeforeRuleStopState(stateWithTransitionPath.state)) {
validStates.push(stateWithTransitionPath);
} else {
validStates = this.BackTracingAndFindActiveStates(stateWithTransitionPath).filter(Utils.notDuplicate);
if (this.HasActiveChildrenState(stateWithTransitionPath.state)) {
validStates.push(stateWithTransitionPath);
}
}
return validStates;
}
private BackTracingAndFindActiveStates(stateWithTransitionPath : StateWithTransitionPath) : StateWithTransitionPath[] {
let validStates : StateWithTransitionPath[] = [];
let completedRuleIndex = stateWithTransitionPath.state.ruleIndex;
let statesStack = this.GetLastStateInDifferentRulesFomStatesStack(stateWithTransitionPath.transitionStates, completedRuleIndex);
let currentStateIndex = statesStack.length - 1;
let keepBackTracing : boolean = true;
while (keepBackTracing && currentStateIndex >= 0) {
let currentState = statesStack[currentStateIndex];
keepBackTracing = false;
let followingStates = this.GetRuleFollowingState(currentState, completedRuleIndex);
for (let index = 0;index < followingStates.length; index++) {
let lastStateBeforeRuleStopState : boolean = false;
let haveActiveChildrenStatesInCurrentRule : boolean = false;
let transitions = followingStates[index].transitions;
while (transitions.length > 0){
let epsilonTrans = [];
for (let tIndex = 0;tIndex < transitions.length; tIndex++) {
if (transitions[tIndex].isEpsilon) {
if (transitions[tIndex] instanceof Transition.RuleTransition) {
haveActiveChildrenStatesInCurrentRule = true;
} else if (transitions[tIndex].target instanceof ATNState.RuleStopState) {
lastStateBeforeRuleStopState = true;
} else {
epsilonTrans = epsilonTrans.concat(transitions[tIndex].target.transitions);
}
} else {
haveActiveChildrenStatesInCurrentRule = true;
}
}
transitions = epsilonTrans;
if (lastStateBeforeRuleStopState && haveActiveChildrenStatesInCurrentRule) {
// We can jump out of loop ahead of schedule.
break;
}
}
if (lastStateBeforeRuleStopState) {
keepBackTracing = true;
}
if (haveActiveChildrenStatesInCurrentRule) {
//validStates.push(followingStates[index]);
let newValidState : StateWithTransitionPath = {
state : followingStates[index],
transitionStates : statesStack.slice(0, currentStateIndex + 1)
};
validStates.push(newValidState);
}
}
currentStateIndex--;
if(keepBackTracing) {
completedRuleIndex = followingStates[0].ruleIndex;
}
}
return validStates.filter(Utils.notEmpty);
}
private GetLastStateInDifferentRulesFomStatesStack(statesStack : ATNState.ATNState[], lastMatchedRuleIndex : number) : ATNState.ATNState[] {
let lastStates : ATNState.ATNState[] = [];
let matchedRuleIndex = lastMatchedRuleIndex;
for(let currentStateIndex = statesStack.length - 1; currentStateIndex >= 0; currentStateIndex--) {
if(statesStack[currentStateIndex].ruleIndex === matchedRuleIndex) {
continue;
} else {
lastStates.push(statesStack[currentStateIndex]);
matchedRuleIndex = statesStack[currentStateIndex].ruleIndex;
}
}
lastStates.reverse();
return lastStates.filter(Utils.notEmpty);
}
private GetRuleFollowingState(state : ATNState.ATNState, ruleIndex : number) : ATNState.ATNState[] {
let followingStates : ATNState.ATNState[] = [];
if(state instanceof ATNState.RuleStopState) {
return followingStates;
}
let transitions = state.transitions;
while(transitions.length > 0) {
let epsilonTrans = [];
for (let index = 0;index < transitions.length;index++) {
if (transitions[index].isEpsilon) {
if (transitions[index] instanceof Transition.RuleTransition) {
if (transitions[index].ruleIndex === ruleIndex) {
followingStates.push(transitions[index].followState);
}
} else if (!(transitions[index].target instanceof ATNState.RuleStopState)) {
epsilonTrans = epsilonTrans.concat(transitions[index].target.transitions);
}
}
}
transitions = epsilonTrans;
}
return followingStates.filter(Utils.notEmpty);
}
// Means with this state, parser can make up a complete rule.
private IsLastStateBeforeRuleStopState(state : ATNState.ATNState) {
let transitions = state.transitions;
while(transitions.length > 0) {
let epsilonTrans = [];
for (let index = 0;index < transitions.length;index++) {
if (transitions[index].isEpsilon) {
if (transitions[index].target instanceof ATNState.RuleStopState) {
return true;
} else if (!(transitions[index] instanceof Transition.RuleTransition)) {
epsilonTrans = epsilonTrans.concat(transitions[index].target.transitions);
}
}
}
transitions = epsilonTrans;
}
return false;
}
private HasActiveChildrenState(state : ATNState.ATNState) : boolean {
let transitions = state.transitions;
while(transitions.length > 0) {
let epsilonTrans = [];
for (let index = 0;index < transitions.length;index++) {
if (transitions[index].isEpsilon) {
if (transitions[index] instanceof Transition.RuleTransition) {
return true;
} else if (!(transitions[index].target instanceof ATNState.RuleStopState)) {
epsilonTrans = epsilonTrans.concat(transitions[index].target.transitions);
}
} else {
return true;
}
}
transitions = epsilonTrans;
}
return false;
}
}
+179
View File
@@ -0,0 +1,179 @@
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
import * as antlr4 from "antlr4";
import * as ATNState from "antlr4/atn/ATNState";
import { InputStream } from "antlr4/InputStream";
import { IntervalSet } from "antlr4/IntervalSet";
import { Lexer } from "antlr4/Lexer";
import { LSCommonTokenStream } from "./LSCommonTokenStream";
import { LSErrorListener } from "./LSErrorListener";
import { LSParserATNSimulator } from "./LSParserATNSimulator";
import { Parser } from "antlr4/Parser";
import { Utils } from "./Utils";
interface ErrorMarkItem {
line: number;
column: number;
Message: string;
}
interface StateContextDict {
[key : number] : StateContext
}
class StateContext {
public State : number;
public ExpectedTokens : IntervalSet;
public RuleIndex : number;
public RuleStack : string[];
constructor(state : number, ruleIndex : number, expectedTokens : IntervalSet, ruleStack : string[]) {
this.State = state;
this.RuleIndex = ruleIndex;
this.ExpectedTokens = expectedTokens;
this.RuleStack = ruleStack;
}
}
export class LanguageService {
private _lexerCtr : any;
private _parserCtr : any;
private _lexer : Lexer = null;
private _parser : Parser = null;
private _keywordsDict: { [key : string] : string } = null;
public StatesBeforeEof : StateContextDict = {};
public SyntaxErrors : ErrorMarkItem[] = [];
private EofReached : boolean = false;
public EofReachedInPredict : boolean = false;
private ExThrownAfterEofReached : boolean = false;
public IsInPredict : boolean = false;
constructor(lexerCtr : Lexer, parserCtr : Parser, keywordsDict : { [key : string] : string }) {
this._lexerCtr = lexerCtr;
this._parserCtr = parserCtr;
this._keywordsDict = keywordsDict;
}
private _parse(input : string) {
this.PrepareParse();
this._lexer = new this._lexerCtr(new InputStream(input));
this._parser = new this._parserCtr(new LSCommonTokenStream(this._lexer));
this._parser.getTokenStream().EofListener = () => {
this.RecordStateBeforeEof();
};
this._parser.removeErrorListeners();
this._parser.addErrorListener(new LSErrorListener(
(msg, line, column) => {
this.AddSyntaxError(msg, line, column);
}
));
let decisionsToDFA = this._parser.atn.decisionToState.map((ds, index) => { return new antlr4.dfa.DFA(ds, index);});
this._parser._interp = new LSParserATNSimulator(this._parser, this._parser.atn, decisionsToDFA, new antlr4.PredictionContextCache(), this);
this._parser.root();
}
public GetExpectedTokenStrs = function() : string[] {
let intervalSets = new IntervalSet();
for (var key in this.StatesBeforeEof) {
if (this.StatesBeforeEof.hasOwnProperty(key)) {
intervalSets.addSet(this.StatesBeforeEof[key].ExpectedTokens);
}
}
var expectedStrings = [];
if (intervalSets.intervals === null) {
return expectedStrings;
}
for (var i = 0; i < intervalSets.intervals.length; i++) {
var v = intervalSets.intervals[i];
if (v.start < 0) {
continue;
}
for (var j = v.start; j < v.stop; j++) {
var tokenString = this._parser._input.tokenSource.symbolicNames[j];
if (tokenString != null) {
let keyword = this._keywordsDict[tokenString.replace(/^\'|\'$/gi, "")];
if (keyword != null) {
expectedStrings.push(keyword);
}
}
}
}
return expectedStrings.filter(Utils.notDuplicate);
}
public RecordStateBeforeEof = function() {
if (!this.IsInPredict) {
this.EofReached = true;
if (!this.ExThrownAfterEofReached) {
if (this.StatesBeforeEof[this._parser.state] == undefined || this.StatesBeforeEof[this._parser.state] == null) {
this.StatesBeforeEof[this._parser.state] = new StateContext(this._parser.state, this._parser._ctx.ruleIndex, this._parser.getExpectedTokens(), this._parser.getRuleInvocationStack());
}
}
} else {
this.EofReachedInPredict = true;
}
}
public RecordErrorStatesBeforeEof = function(states : ATNState.ATNState[]) {
if (states.length > 0) {
states.forEach(state => {
if (state != null) {
if (this.StatesBeforeEof[state.stateNumber] == undefined || this.StatesBeforeEof[state.stateNumber] == null) {
this.StatesBeforeEof[state.stateNumber] = new StateContext(state.stateNumber, state.ruleIndex, this._parser._interp.atn.nextTokens(state), this._parser.getRuleInvocationStack());
}
}
});
}
}
public AddSyntaxError = (msg : string, line : number, column : number) : any => {
let error : ErrorMarkItem = {
line : line,
column : column,
Message : msg
};
this.SyntaxErrors.push(error);
if (this.EofReached) {
this.ExThrownAfterEofReached = true;
}
}
public PrepareParse() : any {
this.EofReached = false;
this.EofReachedInPredict = false;
this.ExThrownAfterEofReached = false;
this.StatesBeforeEof = {};
this.SyntaxErrors = [];
}
public getCompletionWords(input : string) : string[] {
this._parse(input);
return this.GetExpectedTokenStrs();
}
public getSyntaxErrors(input : string) : ErrorMarkItem[] {
this._parse(input);
return this.SyntaxErrors;
}
}
+13
View File
@@ -0,0 +1,13 @@
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
export class Utils {
public static notEmpty<TValue>(value : TValue | null | undefined) : value is TValue {
return value !== null && value !== undefined;
}
public static notDuplicate(item, pos, self) {
return self.indexOf(item) == pos;
}
}
+9
View File
@@ -0,0 +1,9 @@
import * as Q from "q";
import {editor} from "monaco-editor"
import {LanguageServiceFacade, ParseReason} from "../facade/LanguageServiceFacade";
export class ErrorMarkProvider {
public static getErrorMark(input: string): Q.Promise<editor.IMarkerData[]> {
return LanguageServiceFacade.GetLanguageServiceParseResult(input, ParseReason.GetErrors);
}
}
@@ -0,0 +1,19 @@
import {LanguageServiceFacade, ParseReason} from "../facade/LanguageServiceFacade";
import {editor, Position, CancellationToken} from "monaco-editor";
export class SqlCompletionItemProvider {
public triggerCharacters: string[] = [" ","."];
provideCompletionItems(model: editor.IReadOnlyModel, position: Position, token: CancellationToken) {
const range = {
startLineNumber: 1,
startColumn: 1,
endLineNumber: position.lineNumber,
endColumn: position.column
}
let text = model.getValueInRange(range);
text = this.triggerCharacters.indexOf(text.charAt(text.length - 1)) < 0 ? text.substring(0, text.length - 1) : text;
return LanguageServiceFacade.GetLanguageServiceParseResult(text, ParseReason.GetCompletionWords);
}
}
+32
View File
@@ -0,0 +1,32 @@
import { LanguageService } from "../language-service/LanguageService";
import { CosmosDBSqlLexer } from "../cosmosdb-sql/generated/CosmosDBSqlLexer";
import { CosmosDBSqlParser } from "../cosmosdb-sql/generated/CosmosDBSqlParser";
import { CosmosDBSqlKeywords } from "../cosmosdb-sql/grammar/CosmosDBSqlKeywords";
enum ParseReason {
GetCompletionWords = 1,
GetErrors = 2
}
export module LanguageServiceWorker {
// Respond to message from parent thread
onmessage = (event: MessageEvent) => {
const code: string = event.data.code;
const reason : number = event.data.reason;
let parseResults = [];
let languageService = new LanguageService(CosmosDBSqlLexer, CosmosDBSqlParser, CosmosDBSqlKeywords.keywordsRegisteredForCompletion);
if (reason == ParseReason.GetCompletionWords) {
parseResults = languageService.getCompletionWords(code);
} else if (reason == ParseReason.GetErrors) {
parseResults = languageService.getSyntaxErrors(code);
}
postMessage(parseResults, undefined);
close();
};
}
+29
View File
@@ -0,0 +1,29 @@
"use strict";
module.exports = {
entry: {
LanguageServiceWorker: './LanguageServiceWorker.ts'
},
mode: 'production',
target: 'web',
module: {
rules: [
{
test: /\.ts$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.ts', '.js']
},
output: {
globalObject: 'this',
path: __dirname + "/dist",
filename: '[name].js',
library: '[name]',
libraryTarget: 'umd'
},
node: { fs: "empty" }
};