Skip to content

Commit 45f6f6a

Browse files
committed
Fixes #87
1 parent cf9204b commit 45f6f6a

File tree

3 files changed

+112
-41
lines changed

3 files changed

+112
-41
lines changed

server/src/extensions/antlrVbaPreParserExtensions.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
// Project
2-
import { CompilerConditionalStatementContext } from '../antlr/out/vbapreParser';
2+
import { CompilerConditionalStatementContext, DirectiveExpressionContext } from '../antlr/out/vbapreParser';
33

44

55
declare module '../antlr/out/vbapreParser' {
66
interface CompilerConditionalStatementContext {
77
vbaExpression(): string;
88
}
9+
10+
interface DirectiveExpressionContext {
11+
vbaExpression(): string;
12+
}
913
}
1014

1115

@@ -15,3 +19,7 @@ CompilerConditionalStatementContext.prototype.vbaExpression = function (): strin
1519
.getText()
1620
.toLowerCase();
1721
};
22+
23+
DirectiveExpressionContext.prototype.vbaExpression = function (): string {
24+
return this.getText().toLowerCase();
25+
};

server/src/project/elements/precompiled.ts

Lines changed: 81 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,64 @@
22
import { 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';
99
import { BaseRuleSyntaxElement } from '../elements/base';
1010
import { 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+
1342
export 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

4178
class 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+
}

server/src/project/parser/vbaListener.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { ErrorNode, ParserRuleContext } from 'antlr4ng';
66
import { vbaListener } from '../../antlr/out/vbaListener';
77
import { vbapreListener } from '../../antlr/out/vbapreListener';
88
import { vbafmtListener } from '../../antlr/out/vbafmtListener';
9-
import { CompilerIfBlockContext } from '../../antlr/out/vbapreParser';
9+
import { CompilerIfBlockContext, ConstDirectiveStatementContext} from '../../antlr/out/vbapreParser';
1010
import {
1111
AmbiguousIdentifierContext,
1212
AnyOperatorContext,
@@ -67,7 +67,7 @@ import {
6767
import { Services } from '../../injection/services';
6868
import { ExtensionConfiguration } from '../workspace';
6969
import { ErrorRuleElement } from '../elements/generic';
70-
import { CompilerLogicalBlock } from '../elements/precompiled';
70+
import { CompilerDirectiveElement, CompilerLogicalBlock } from '../elements/precompiled';
7171
import { UnexpectedEndOfLineElement } from '../elements/utils';
7272
import { VbaClassDocument, VbaModuleDocument } from '../document';
7373
import { DuplicateOperatorElement, IfElseBlockElement, WhileLoopElement } from '../elements/flow';
@@ -489,6 +489,7 @@ export class VbaListener extends vbaListener {
489489

490490
export class VbaPreListener extends vbapreListener {
491491
common: CommonParserCapability;
492+
directives: Map<string, any> = new Map();
492493

493494
get text(): string {
494495
return this.common.document.redactedText;
@@ -505,10 +506,27 @@ export class VbaPreListener extends vbapreListener {
505506
return result;
506507
}
507508

509+
enterConstDirectiveStatement = (ctx: ConstDirectiveStatementContext) => {
510+
const doc = this.common.document;
511+
const element = new CompilerDirectiveElement(
512+
ctx,
513+
doc.textDocument,
514+
this.common.documentSettings,
515+
this.directives
516+
);
517+
this.directives.set(element.identifierCapability.name, element.evaluate());
518+
doc.registerElement(element)
519+
.registerSubtractElement(element);
520+
};
521+
508522
enterCompilerIfBlock = (ctx: CompilerIfBlockContext) => {
509523
const doc = this.common.document;
510-
const docprops = this.common.documentSettings;
511-
const element = new CompilerLogicalBlock(ctx, doc.textDocument, docprops);
524+
const element = new CompilerLogicalBlock(
525+
ctx,
526+
doc.textDocument,
527+
this.common.documentSettings,
528+
this.directives
529+
);
512530
element.inactiveBlocks.forEach(
513531
b => doc.registerElement(b)
514532
.registerSubtractElement(b)

0 commit comments

Comments
 (0)