Initial code commit
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user