1+ /**
2+ * Direct ANTLR parser test for VBA preprocessor grammar.
3+ *
4+ * This test directly uses the ANTLR parser to catch syntax errors and undesired implicit tokens (T__1, T__2, etc.)
5+ * without going through the VS Code diagnostics layer.
6+ */
7+
8+ import { describe , it } from 'mocha' ;
9+ import * as assert from 'assert' ;
10+ import * as fs from 'fs' ;
11+ import * as path from 'path' ;
12+ import { VbaPreParser , VbaPreLexer } from '../project/parser/vbaAntlr' ;
13+ import { CharStream , CommonTokenStream } from 'antlr4ng' ;
14+
15+ describe ( 'ANTLR VBA Preprocessor Parser' , ( ) => {
16+
17+ /**
18+ * Helper function to check and report implicit tokens
19+ */
20+ function checkImplicitTokens ( result : ReturnType < typeof parseAndGetErrors > ) : Array < { type : number , text : string , typeName : string } > {
21+ const implicitTokens = result . tokenInfo . filter ( t => t . typeName . startsWith ( 'T__' ) ) ;
22+ if ( implicitTokens . length > 0 ) {
23+ console . log ( ` ❌ Found ${ implicitTokens . length } implicit token(s): ${ implicitTokens . map ( t => t . typeName ) . join ( ', ' ) } ` ) ;
24+ } else {
25+ console . log ( ' ✅ No implicit tokens found' ) ;
26+ }
27+ return implicitTokens ;
28+ }
29+
30+ /**
31+ * Helper function to log parsing results consistently
32+ */
33+ function logParsingResults ( input : string , result : ReturnType < typeof parseAndGetErrors > ) {
34+ console . log ( '\n 📝 Input:' ) ;
35+ const inputLines = input . split ( '\n' ) ;
36+ inputLines . forEach ( ( line , index ) => {
37+ // Show line numbers and preserve exact whitespace
38+ if ( line . trim ( ) || index < inputLines . length - 1 ) { // Show non-empty lines and all but the last empty line
39+ console . log ( ` ${ ( index + 1 ) . toString ( ) . padStart ( 2 ) } : ${ line } ` ) ;
40+ }
41+ } ) ;
42+ console . log ( ' 🔤 Tokens:' ) ;
43+ result . tokenInfo . forEach ( ( t , i ) => {
44+ const displayText = t . text . replace ( / \n / g, '\\n' ) . replace ( / \r / g, '\\r' ) ;
45+ console . log ( ` ${ i . toString ( ) . padStart ( 2 ) } : ${ t . typeName . padEnd ( 12 ) } = "${ displayText } "` ) ;
46+ } ) ;
47+ if ( result . lexerErrors . length > 0 ) {
48+ console . log ( ' ❌ Lexer errors:' , result . lexerErrors ) ;
49+ }
50+ if ( result . errors . length > 0 ) {
51+ console . log ( ' ❌ Parser errors:' , result . errors ) ;
52+ }
53+ console . log ( ` 📊 Syntax errors: ${ result . syntaxErrors } ` ) ;
54+ }
55+
56+ /**
57+ * Test helper to parse input and collect syntax errors
58+ */
59+ function parseAndGetErrors ( input : string ) {
60+ const lexer = VbaPreLexer . create ( input ) ;
61+ const tokens = new CommonTokenStream ( lexer ) ;
62+ const parser = new VbaPreParser ( tokens ) ;
63+
64+ // Collect all error information
65+ const errors : string [ ] = [ ] ;
66+ const lexerErrors : string [ ] = [ ] ;
67+ const tokenInfo : Array < { type : number , text : string , typeName : string } > = [ ] ;
68+
69+ lexer . removeErrorListeners ( ) ;
70+ parser . removeErrorListeners ( ) ;
71+
72+ // Get tokens for inspection
73+ tokens . fill ( ) ;
74+ const allTokens = tokens . getTokens ( ) ;
75+ for ( const token of allTokens ) {
76+ if ( token . type !== - 1 ) { // Skip EOF
77+ const typeName = lexer . vocabulary . getSymbolicName ( token . type ) || `T__${ token . type - 1 } ` ;
78+ tokenInfo . push ( {
79+ type : token . type ,
80+ text : token . text || '' ,
81+ typeName : typeName
82+ } ) ;
83+ }
84+ }
85+
86+ // Try to parse
87+ let parseTree = null ;
88+ try {
89+ parseTree = parser . startRule ( ) ;
90+ } catch ( error ) {
91+ errors . push ( `Parse exception: ${ error } ` ) ;
92+ }
93+
94+ return {
95+ errors,
96+ lexerErrors,
97+ tokenInfo,
98+ syntaxErrors : parser . numberOfSyntaxErrors ,
99+ parseTree
100+ } ;
101+ }
102+
103+ it ( 'should parse function call with string literal and parentheses' , ( ) => {
104+ const testFilePath = path . join ( __dirname , '../../../test/parser/pre/ParsingParentheses.bas' ) ;
105+ const input = fs . readFileSync ( testFilePath , 'utf8' ) ;
106+
107+ const result = parseAndGetErrors ( input ) ;
108+
109+ logParsingResults ( input , result ) ;
110+ const implicitTokens = checkImplicitTokens ( result ) ;
111+
112+ // The test should fail if there are implicit T__ tokens for parentheses
113+ assert . strictEqual ( result . syntaxErrors , 0 , `Expected no syntax errors, but found: ${ result . errors . join ( ', ' ) } ` ) ;
114+ assert . strictEqual ( implicitTokens . length , 0 , `Found implicit tokens: ${ implicitTokens . map ( t => t . typeName ) . join ( ', ' ) } ` ) ;
115+ } ) ;
116+
117+ it ( 'should parse multiple function calls correctly' , ( ) => {
118+ const testFilePath = path . join ( __dirname , '../../../test/parser/pre/TwoFunctionCalls.bas' ) ;
119+ const input = fs . readFileSync ( testFilePath , 'utf8' ) ;
120+
121+ const result = parseAndGetErrors ( input ) ;
122+
123+ logParsingResults ( input , result ) ;
124+ const implicitTokens = checkImplicitTokens ( result ) ;
125+
126+ assert . strictEqual ( result . syntaxErrors , 0 ) ;
127+ assert . strictEqual ( implicitTokens . length , 0 ) ;
128+ } ) ;
129+
130+
131+ } ) ;
0 commit comments