public void Completion_Grammar_Antlr() { var cwd = System.IO.Directory.GetCurrentDirectory(); // arrange var input = System.IO.File.ReadAllText("../../../Grammar/Expr.g4"); var inputStream = new AntlrInputStream(input); var lexer = new ANTLRv4Lexer(inputStream); var tokenStream = new CommonTokenStream(lexer); var parser = new ANTLRv4Parser(tokenStream); lexer.RemoveErrorListeners(); parser.RemoveErrorListeners(); var errorListener = new CountingErrorListener(); parser.AddErrorListener(errorListener); // act // assert // Specify our entry point var tree = parser.grammarSpec(); Check.That(errorListener.ErrorCount).IsEqualTo(0); var core = new CodeCompletionCore(parser, null, null); // 1) At the input start. var candidates = core.CollectCandidates(0, null); Check.That(candidates.Tokens).HasSize(4); Check.That(candidates.Tokens).ContainsKey(ANTLRv4Lexer.DOC_COMMENT); Check.That(candidates.Tokens).ContainsKey(ANTLRv4Lexer.LEXER); Check.That(candidates.Tokens).ContainsKey(ANTLRv4Lexer.PARSER); Check.That(candidates.Tokens).ContainsKey(ANTLRv4Lexer.GRAMMAR); Check.That(candidates.Tokens[ANTLRv4Lexer.LEXER]).IsEqualTo(new[] { ANTLRv4Lexer.GRAMMAR }); Check.That(candidates.Tokens[ANTLRv4Lexer.PARSER]).IsEqualTo(new[] { ANTLRv4Lexer.GRAMMAR }); Check.That(candidates.Tokens[ANTLRv4Lexer.DOC_COMMENT]).HasSize(0); Check.That(candidates.Tokens[ANTLRv4Lexer.GRAMMAR]).HasSize(0); Check.That(candidates.Rules.Count == 0); // 2) Go to token index = 3 => ";" candidates = core.CollectCandidates(3, null); Check.That(candidates.Tokens).HasSize(1); Check.That(candidates.Tokens).ContainsKey(ANTLRv4Lexer.SEMI); Check.That(candidates.Rules.Count == 0); // 3) Go to token index = 14 => just after the ";" of the rule for "expression". candidates = core.CollectCandidates(14, null); Check.That(candidates.Tokens).HasSize(3); Check.That(candidates.Tokens).ContainsKey(ANTLRv4Lexer.CATCH); Check.That(candidates.Tokens).ContainsKey(ANTLRv4Lexer.FINALLY); Check.That(candidates.Tokens).ContainsKey(ANTLRv4Lexer.RULE_REF); // CRASH because -2 is not a token, it is epsilon! }
private void ProcessGrammarFile(Grammar grammar, string grammarFileName, AntlrErrorListener antlrErrorListener, CancellationToken cancellationToken) { string code = File.ReadAllText(Path.Combine(grammar.Directory, grammarFileName)); var inputStream = new AntlrInputStream(code); var codeSource = new CodeSource(grammarFileName, inputStream.ToString()); _result.GrammarFilesData.Add(grammarFileName, codeSource); string extension = Path.GetExtension(grammarFileName); if (extension != Grammar.AntlrDotExt) { return; } antlrErrorListener.CodeSource = codeSource; var antlr4Lexer = new ANTLRv4Lexer(inputStream); antlr4Lexer.RemoveErrorListeners(); antlr4Lexer.AddErrorListener(antlrErrorListener); var tokens = antlr4Lexer.GetAllTokens(); var codeTokenSource = new ListTokenSource(tokens); cancellationToken.ThrowIfCancellationRequested(); var codeTokenStream = new CommonTokenStream(codeTokenSource); var antlr4Parser = new ANTLRv4Parser(codeTokenStream); antlr4Parser.RemoveErrorListeners(); antlr4Parser.AddErrorListener(antlrErrorListener); var tree = antlr4Parser.grammarSpec(); var grammarInfoCollectorListener = new GrammarInfoCollectorListener(); grammarInfoCollectorListener.CollectInfo(antlrErrorListener.CodeSource, tree); var shortFileName = Path.GetFileNameWithoutExtension(grammarFileName); _result.GrammarActionsTextSpan[grammarFileName] = grammarInfoCollectorListener.CodeInsertions; var grammarType = grammarInfoCollectorListener.GrammarType; if (grammarType == GrammarType.Lexer || grammarType == GrammarType.Combined) { _result.LexerSuperClass = grammarInfoCollectorListener.SuperClass; } if (grammarType == GrammarType.Separated || grammarType == GrammarType.Combined) { _result.ParserSuperClass = grammarInfoCollectorListener.SuperClass; _result.Rules = grammarInfoCollectorListener.Rules; } void ErrorAction(ParsingError parsingError) { ErrorEvent?.Invoke(this, parsingError); _result.Errors.Add(parsingError); } var caseInsensitiveTypeOptionMatcher = new CaseInsensitiveTypeOptionMatcher(codeSource, grammarType, ErrorAction); var runtimeOptionMatcher = new RuntimeOptionMatcher(codeSource, grammarType, ErrorAction); var visitorOptionMatcher = new VisitorOptionMatcher(codeSource, grammarType, ErrorAction); var listenerOptionMatcher = new ListenerOptionMatcher(codeSource, grammarType, ErrorAction); var packageOptionMatcher = new PackageOptionMatcher(codeSource, grammarType, ErrorAction); var rootOptionMatcher = new RootOptionMatcher(codeSource, grammarType, ErrorAction, _result.Rules); var predictionOptionMatcher = new PredictionModeOptionMatcher(codeSource, grammarType, ErrorAction); foreach (IToken token in tokens) { if (token.Type == ANTLRv4Lexer.LINE_COMMENT || token.Type == ANTLRv4Lexer.BLOCK_COMMENT) { if (caseInsensitiveTypeOptionMatcher.Match(token, out var caseInsensitiveType)) { _result.CaseInsensitiveType = caseInsensitiveType; continue; } if (runtimeOptionMatcher.Match(token, out Runtime runtime)) { _result.Runtime = runtime; continue; } if (packageOptionMatcher.Match(token, out string package)) { _result.Package = package; continue; } if (visitorOptionMatcher.Match(token, out bool generateVisitor)) { _result.Visitor = generateVisitor; continue; } if (listenerOptionMatcher.Match(token, out bool generateListener)) { _result.Listener = generateListener; continue; } if (rootOptionMatcher.Match(token, out string root)) { _result.Root = root; continue; } if (predictionOptionMatcher.Match(token, out PredictionMode predictionMode)) { _result.PredictionMode = predictionMode; continue; } } } }
public void Test1() { { var input = @"grammar Expr; expression: assignment | simpleExpression; assignment : (VAR | LET) ID EQUAL simpleExpression ; simpleExpression : simpleExpression (PLUS | MINUS) simpleExpression | simpleExpression (MULTIPLY | DIVIDE) simpleExpression | variableRef | functionRef ; variableRef : ID ; functionRef : ID OPEN_PAR CLOSE_PAR ; VAR: [vV] [aA] [rR]; LET: [lL] [eE] [tT]; PLUS: '+'; MINUS: '-'; MULTIPLY: '*'; DIVIDE: '/'; EQUAL: '='; OPEN_PAR: '('; CLOSE_PAR: ')'; ID: [a-zA-Z] [a-zA-Z0-9_]*; WS: [ \n\r\t] -> channel(HIDDEN); "; var inputStream = new AntlrInputStream(input); var lexer = new ANTLRv4Lexer(inputStream); var tokenStream = new CommonTokenStream(lexer); var parser = new ANTLRv4Parser(tokenStream); lexer.RemoveErrorListeners(); parser.RemoveErrorListeners(); var errorListener = new CountingErrorListener(); parser.AddErrorListener(errorListener); var tree = parser.grammarSpec(); Assert.True(errorListener.ErrorCount == 0); var core = new CodeCompletionCore(parser, null, null); { var candidates = core.CollectCandidates(0, null); var t1 = candidates.Tokens.Count == 4; Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.DOC_COMMENT)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.GRAMMAR)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.PARSER)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.LEXER)); } { int index = 0; foreach (var t in tokenStream.GetTokens()) { if (t.Text == "assignment") // Stop on first "assignment" { index = t.TokenIndex; break; } } var candidates = core.CollectCandidates(index, null); Assert.True(index == 8); Assert.True(candidates.Tokens.Count == 9); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.TOKEN_REF)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.RULE_REF)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.STRING_LITERAL)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.BEGIN_ACTION)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.LPAREN)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.LT)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.DOT)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.NOT)); Assert.True(candidates.Tokens.ContainsKey(-2)); } { int index = 0; foreach (var t in tokenStream.GetTokens()) { if (t.Text == ":") // Stop on first ":" { index = t.TokenIndex; break; } } var candidates = core.CollectCandidates(index, null); Assert.True(index == 6); Assert.True(candidates.Tokens.Count == 7); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.BEGIN_ARGUMENT)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.OPTIONS)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.RETURNS)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.LOCALS)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.THROWS)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.COLON)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.AT)); } } { var input = @"grammar Expr; expression: assignment | simpleExpression; assignment : (VAR | LET) ID EQUAL simpleExpression ; "; var inputStream = new AntlrInputStream(input); var lexer = new ANTLRv4Lexer(inputStream); var tokenStream = new CommonTokenStream(lexer); var parser = new ANTLRv4Parser(tokenStream); lexer.RemoveErrorListeners(); parser.RemoveErrorListeners(); var errorListener = new CountingErrorListener(); parser.AddErrorListener(errorListener); var tree = parser.grammarSpec(); Assert.True(errorListener.ErrorCount == 0); var core = new CodeCompletionCore(parser, null, null); { var candidates = core.CollectCandidates(0, null); var t1 = candidates.Tokens.Count == 4; Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.DOC_COMMENT)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.GRAMMAR)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.PARSER)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.LEXER)); } { int index = 0; foreach (var t in tokenStream.GetTokens()) { if (t.Text == "assignment") // Stop on first "assignment" { index = t.TokenIndex; break; } } var candidates = core.CollectCandidates(index, null); Assert.True(index == 8); Assert.True(candidates.Tokens.Count == 9); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.TOKEN_REF)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.RULE_REF)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.STRING_LITERAL)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.BEGIN_ACTION)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.LPAREN)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.LT)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.DOT)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.NOT)); Assert.True(candidates.Tokens.ContainsKey(-2)); } { int index = 0; foreach (var t in tokenStream.GetTokens()) { if (t.Text == ":") // Stop on first ":" { index = t.TokenIndex; break; } } var candidates = core.CollectCandidates(index, null); Assert.True(index == 6); Assert.True(candidates.Tokens.Count == 7); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.BEGIN_ARGUMENT)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.OPTIONS)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.RETURNS)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.LOCALS)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.THROWS)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.COLON)); Assert.True(candidates.Tokens.ContainsKey(ANTLRv4Parser.AT)); } { int index = 0; int times = 0; foreach (var t in tokenStream.GetTokens()) { if (t.Text == ";") // Stop on ";" { if (++times == 3) { index = t.TokenIndex + 1; break; } } } var candidates = core.CollectCandidates(index, null); // candidates includes CATCH, FINALLY, -2. Why? Why not include // DOC_COMMENT, RULE_REF, ...? } } }
public GrammarCheckedState Check(InputState inputState, CancellationToken cancellationToken = default) { var grammar = inputState.Grammar; var result = new GrammarCheckedState(inputState); try { var antlrErrorListener = new AntlrErrorListener(); antlrErrorListener.ErrorEvent += ErrorEvent; antlrErrorListener.ErrorEvent += (sender, error) => { lock (result.Errors) { result.Errors.Add(error); } }; foreach (string grammarFileName in grammar.Files) { string code = File.ReadAllText(Path.Combine(grammar.Directory, grammarFileName)); var inputStream = new AntlrInputStream(code); var codeSource = new CodeSource(grammarFileName, inputStream.ToString()); result.GrammarFilesData.Add(grammarFileName, codeSource); string extension = Path.GetExtension(grammarFileName); if (extension != Grammar.AntlrDotExt) { continue; } antlrErrorListener.CodeSource = codeSource; var antlr4Lexer = new ANTLRv4Lexer(inputStream); antlr4Lexer.RemoveErrorListeners(); antlr4Lexer.AddErrorListener(antlrErrorListener); var codeTokenSource = new ListTokenSource(antlr4Lexer.GetAllTokens()); cancellationToken.ThrowIfCancellationRequested(); var codeTokenStream = new CommonTokenStream(codeTokenSource); var antlr4Parser = new ANTLRv4Parser(codeTokenStream); antlr4Parser.RemoveErrorListeners(); antlr4Parser.AddErrorListener(antlrErrorListener); var tree = antlr4Parser.grammarSpec(); var grammarInfoCollectorListener = new GrammarInfoCollectorListener(); grammarInfoCollectorListener.CollectInfo(antlrErrorListener.CodeSource, tree); var shortFileName = Path.GetFileNameWithoutExtension(grammarFileName); result.GrammarActionsTextSpan[grammarFileName] = grammarInfoCollectorListener.CodeInsertions; if (grammarFileName.Contains(Grammar.LexerPostfix)) { result.LexerSuperClass = grammarInfoCollectorListener.SuperClass; } if (grammarFileName.Contains(Grammar.ParserPostfix)) { result.ParserSuperClass = grammarInfoCollectorListener.SuperClass; } if (!shortFileName.Contains(Grammar.LexerPostfix)) { result.Rules = grammarInfoCollectorListener.Rules; cancellationToken.ThrowIfCancellationRequested(); } } } catch (Exception ex) { result.Exception = ex; if (!(ex is OperationCanceledException)) { ErrorEvent?.Invoke(this, new ParsingError(ex, WorkflowStage.GrammarChecked)); } } return(result); }