22import { TextDocument } from 'vscode-languageserver-textdocument' ;
33
44// Antlr
5- import { CompilerConditionalBlockContext , CompilerDefaultBlockContext , CompilerIfBlockContext } from '../../antlr/out/vbapreParser' ;
5+ import { CompilerConditionalBlockContext , CompilerDefaultBlockContext , CompilerIfBlockContext , ConstDirectiveStatementContext } from '../../antlr/out/vbapreParser' ;
66
77// Project
8- import { DiagnosticCapability , FoldingRangeCapability } from '../../capabilities/capabilities' ;
8+ import { DiagnosticCapability , FoldingRangeCapability , IdentifierCapability } from '../../capabilities/capabilities' ;
99import { BaseRuleSyntaxElement } from '../elements/base' ;
1010import { UnreachableCodeDiagnostic } from '../../capabilities/diagnostics' ;
1111
1212
13+ type DocumentSettings = { environment : { os : string , version : string } } ;
14+
15+ export class CompilerDirectiveElement extends BaseRuleSyntaxElement < ConstDirectiveStatementContext > {
16+ identifierCapability : IdentifierCapability ;
17+
18+ constructor ( ctx : ConstDirectiveStatementContext ,
19+ doc : TextDocument ,
20+ private readonly documentSettings : DocumentSettings ,
21+ private readonly directiveConstants : Map < string , any > ) {
22+ super ( ctx , doc ) ;
23+
24+ const getNameCtx = ( ) => ctx . constDirectiveName ( ) ;
25+ this . identifierCapability = new IdentifierCapability ( this , getNameCtx ) ;
26+ }
27+
28+ evaluate ( ) : string {
29+ const vbaExpression = this . context . rule . directiveExpression ( ) . vbaExpression ( ) ;
30+ try {
31+ const tsExpression = transpileVbaToTypescript ( vbaExpression , this . documentSettings , this . directiveConstants ) ;
32+ const getExpressionResult = Function ( '"use strict"; return (' + tsExpression + ')' ) ;
33+ return getExpressionResult ( ) . toString ( ) ;
34+ } catch ( e ) {
35+ // FIXME Add a diagnostic for if this fails.
36+ return '0' ;
37+ }
38+ }
39+ }
40+
41+
1342export class CompilerLogicalBlock extends BaseRuleSyntaxElement < CompilerIfBlockContext > {
1443 conditionalBlocks : CompilerConditionBlock [ ] = [ ] ;
1544 inactiveBlocks : CompilerConditionBlock [ ] = [ ] ;
1645
17- constructor ( ctx : CompilerIfBlockContext , doc : TextDocument , env : { environment : { os : string , version : string } } ) {
46+ constructor (
47+ ctx : CompilerIfBlockContext ,
48+ doc : TextDocument ,
49+ documentSettings : DocumentSettings ,
50+ directiveConstants : Map < string , any > ) {
1851 super ( ctx , doc ) ;
1952 this . foldingRangeCapability = new FoldingRangeCapability ( this ) ;
2053 this . foldingRangeCapability . openWord = '#If' ;
2154 this . foldingRangeCapability . closeWord = '#End If' ;
2255
2356 // Create the block elements
2457 const blocks = [ ctx . compilerConditionalBlock ( ) , ctx . compilerDefaultBlock ( ) ] . flat ( ) ;
25- blocks . map ( x => { if ( x ) this . conditionalBlocks . push ( new CompilerConditionBlock ( x , doc , env ) ) ; } ) ;
58+ blocks . map ( x => {
59+ if ( x ) this . conditionalBlocks . push (
60+ new CompilerConditionBlock ( x , doc , documentSettings , directiveConstants )
61+ ) ;
62+ } ) ;
2663
2764 // Create the comment elements.
2865 let resolved = false ;
@@ -39,11 +76,12 @@ export class CompilerLogicalBlock extends BaseRuleSyntaxElement<CompilerIfBlockC
3976
4077
4178class CompilerConditionBlock extends BaseRuleSyntaxElement < CompilerConditionalBlockContext | CompilerDefaultBlockContext > {
42- readonly documentSettings : { environment : { os : string , version : string } } ;
43-
44- constructor ( ctx : CompilerConditionalBlockContext | CompilerDefaultBlockContext , doc : TextDocument , env : { environment : { os : string , version : string } } ) {
79+ constructor (
80+ ctx : CompilerConditionalBlockContext | CompilerDefaultBlockContext ,
81+ doc : TextDocument ,
82+ private readonly documentSettings : DocumentSettings ,
83+ private readonly directiveConstants : Map < string , any > ) {
4584 super ( ctx , doc ) ;
46- this . documentSettings = env ;
4785 }
4886
4987 get blockLines ( ) : string [ ] {
@@ -56,7 +94,7 @@ class CompilerConditionBlock extends BaseRuleSyntaxElement<CompilerConditionalBl
5694 if ( ( ( o : any ) : o is CompilerDefaultBlockContext => 'compilerElseStatement' in o ) ( ctx ) ) return true ;
5795
5896 const vbaExpression = ctx . compilerConditionalStatement ( ) . vbaExpression ( ) ;
59- const tsExpression = this . transpileVbaToTypescript ( vbaExpression ) ;
97+ const tsExpression = transpileVbaToTypescript ( vbaExpression , this . documentSettings , this . directiveConstants ) ;
6098
6199 // Evaluate the expression and return the result.
62100 const result : boolean = Function ( '"use strict"; return (' + tsExpression + ')' ) ( ) ;
@@ -72,32 +110,39 @@ class CompilerConditionBlock extends BaseRuleSyntaxElement<CompilerConditionalBl
72110 this . diagnosticCapability = new DiagnosticCapability ( this ) ;
73111 this . diagnosticCapability . diagnostics . push ( new UnreachableCodeDiagnostic ( this . context . range ) ) ;
74112 }
113+ }
75114
76- /** Transpiles a VBA expression into Typescript. */
77- private transpileVbaToTypescript ( exp : string ) : string {
78- // Convert the environment constant to boolean.
79- const envToBooleanText = ( opt : string ) => {
80- const isOs = this . documentSettings . environment . os . toLowerCase ( ) == opt ;
81- const isVer = this . documentSettings . environment . version . toLowerCase ( ) == opt ;
82- return isOs || isVer ? 'true' : 'false' ;
83- } ;
84-
85- // Set up text replacements map.
86- const constants = [ 'vba6' , 'vba7' , 'mac' , 'win16' , 'win32' , 'win64' ] ;
87- const replacements = new Map ( constants . map ( x => [ x , envToBooleanText ( x ) ] ) ) ;
88- replacements . set ( 'or' , '||' ) ;
89- replacements . set ( 'and' , '&&' ) ;
90- replacements . set ( 'not ' , '!' ) ;
91-
92- // Perform text replacements.
93- let result = exp ;
94- replacements . forEach ( ( v , k ) => {
95- const regexp = RegExp ( `${ k } ` , 'i' ) ;
96- if ( regexp . test ( result ) ) {
97- result = result . replace ( regexp , v ) ;
98- }
99- } ) ;
115+ function transpileVbaToTypescript ( exp : string , settings : DocumentSettings , directives : Map < string , any > ) : string {
116+ // Convert the environment constant to boolean.
117+ const envToBooleanText = ( opt : string ) => {
118+ const isOs = settings . environment . os . toLowerCase ( ) == opt ;
119+ const isVer = settings . environment . version . toLowerCase ( ) == opt ;
120+ return isOs || isVer ? 'true' : 'false' ;
121+ } ;
122+
123+ // Set up text replacements map.
124+ const constants = [ 'vba6' , 'vba7' , 'mac' , 'win16' , 'win32' , 'win64' ] ;
125+ const replacements = new Map ( constants . map ( x => [ x , envToBooleanText ( x ) ] ) ) ;
126+ replacements . set ( 'or' , '||' ) ;
127+ replacements . set ( 'and' , '&&' ) ;
128+ replacements . set ( 'not ' , '!' ) ;
129+
130+ // Perform language text replacements.
131+ let result = exp ;
132+ replacements . forEach ( ( v , k ) => {
133+ const regexp = RegExp ( `${ k } ` , 'i' ) ;
134+ if ( regexp . test ( result ) ) {
135+ result = result . replace ( regexp , v ) ;
136+ }
137+ } ) ;
100138
101- return result ;
102- }
103- }
139+ // Perform user directives text replacements.
140+ directives . forEach ( ( v , k ) => {
141+ const regexp = RegExp ( `${ k } ` , 'i' ) ;
142+ if ( regexp . test ( result ) ) {
143+ result = result . replace ( regexp , v ) ;
144+ }
145+ } ) ;
146+
147+ return result ;
148+ }
0 commit comments