Skip to content

Commit 93a271e

Browse files
committed
reimplement precompiler
1 parent 92c3ba5 commit 93a271e

File tree

2 files changed

+138
-2
lines changed

2 files changed

+138
-2
lines changed

server/src/antlr/vbapre.g4

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ compilerIfBlock
2525
;
2626

2727
compilerConditionalBlock
28-
: compilerConditionalStatement (anyOtherLine | endOfLine)*
28+
: compilerConditionalStatement compilerBlockContent?
2929
;
3030

3131
compilerDefaultBlock
32-
: compilerElseStatement (anyOtherLine | endOfLine)*
32+
: compilerElseStatement compilerBlockContent?
3333
;
3434

3535
compilerConditionalStatement
@@ -53,6 +53,10 @@ compilerEndIfStatement
5353
: ENDIF endOfStatement?
5454
;
5555

56+
compilerBlockContent
57+
: (anyOtherLine | endOfLine)+
58+
;
59+
5660
// *************************
5761

5862
booleanExpression
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// Core
2+
import * as ts from "typescript";
3+
import { Position, Range, SemanticTokenTypes } from 'vscode-languageserver';
4+
import { TextDocument } from 'vscode-languageserver-textdocument';
5+
6+
// Antlr
7+
import { ParserRuleContext } from 'antlr4ng';
8+
import { CompilerConditionalBlockContext, CompilerDefaultBlockContext, CompilerIfBlockContext } from '../../antlr/out/vbapreParser';
9+
10+
// Project
11+
import { SemanticTokenCapability } from '../../capabilities/capabilities';
12+
import { BaseContextSyntaxElement, HasSemanticTokenCapability } from '../elements/base';
13+
14+
15+
export class CompilerLogicalBlock extends BaseContextSyntaxElement<CompilerIfBlockContext> {
16+
conditionalBlocks: CompilerConditionBlock[] = [];
17+
inactiveBlocks: CompilerConditionBlock[] = [];
18+
19+
constructor(ctx: CompilerIfBlockContext, doc: TextDocument, env: {environment: { os: string, version: string }}) {
20+
super(ctx, doc);
21+
22+
// Create the block elements
23+
const blocks = [ctx.compilerConditionalBlock(), ctx.compilerDefaultBlock()].flat();
24+
blocks.map(x => { if(x) this.conditionalBlocks.push(new CompilerConditionBlock(x, doc, env)) });
25+
26+
// Create the comment elements.
27+
let resolved = false;
28+
for (const block of this.conditionalBlocks) {
29+
if (!resolved && block.conditionResult) {
30+
resolved = true;
31+
continue;
32+
}
33+
this.inactiveBlocks.push(block);
34+
}
35+
}
36+
}
37+
38+
39+
class CompilerConditionBlock extends BaseContextSyntaxElement<CompilerConditionalBlockContext | CompilerDefaultBlockContext> {
40+
readonly documentSettings: {environment: { os: string, version: string }};
41+
42+
constructor(ctx: CompilerConditionalBlockContext | CompilerDefaultBlockContext, doc: TextDocument, env: {environment: { os: string, version: string }}) {
43+
super(ctx, doc);
44+
this.documentSettings = env;
45+
}
46+
47+
get blockLines(): string[] {
48+
return this.context.rule.compilerBlockContent()?.getText().split('\n') ?? [];
49+
}
50+
51+
get linesToComments(): GenericCommentElement[] {
52+
const rowX = this.context.range.start.line;
53+
const rowY = this.context.range.end.line;
54+
55+
// Iterate the rows -- test what happens when you get to the end of the document.
56+
// May require a try catch where the default offset is the character count of the document.
57+
const result: GenericCommentElement[] = [];
58+
for (let i = rowX; i < rowY; i++) {
59+
const posX = this.context.document.offsetAt({ line: i, character: 0 });
60+
const posY = this.context.document.offsetAt({ line: i + 1, character: 0 }) - 1;
61+
62+
const lineRange = Range.create(
63+
this.context.document.positionAt(posX),
64+
this.context.document.positionAt(posY)
65+
);
66+
result.push(new GenericCommentElement(
67+
this.context.rule,
68+
this.context.document,
69+
lineRange)
70+
);
71+
}
72+
73+
return result;
74+
}
75+
76+
get conditionResult(): boolean {
77+
// Default "Else" block is always true.
78+
const ctx = this.context.rule;
79+
if(((o: any): o is CompilerDefaultBlockContext => 'compilerElseStatement' in o)(ctx)) return true;
80+
81+
const vbaExpression = ctx.compilerConditionalStatement().vbaExpression();
82+
const tsExpression = this.transpileVbaToTypescript(vbaExpression);
83+
84+
// Evaluate the expression and return the result.
85+
const result = eval(ts.transpile(tsExpression));
86+
if (!(typeof result === "boolean")) {
87+
// TODO: Return false here instead of throwing
88+
// and return an error diagnostic for the expression.
89+
throw new Error("Expected boolean result.");
90+
}
91+
return result;
92+
}
93+
94+
/** Transpiles a VBA expression into Typescript. */
95+
private transpileVbaToTypescript(exp: string): string {
96+
// Convert the environment constant to boolean.
97+
const envToBooleanText = (opt: string) => {
98+
const isOs = this.documentSettings.environment.os.toLowerCase() == opt;
99+
const isVer = this.documentSettings.environment.version.toLowerCase() == opt;
100+
return isOs || isVer ? 'true' : 'false';
101+
}
102+
103+
// Set up text replacements map.
104+
const constants = ['vba6', 'vba7', 'mac', 'win16', 'win32', 'win64']
105+
const replacements = new Map(constants.map(x => [x, envToBooleanText(x)]));
106+
replacements.set('or', '||');
107+
replacements.set('and', '&&');
108+
replacements.set('not ', '!');
109+
110+
// Perform text replacements.
111+
let result = exp;
112+
replacements.forEach((v, k) => {
113+
const regexp = RegExp(`${k}`, 'i')
114+
if (regexp.test(result)) {
115+
result = result.replace(regexp, v);
116+
}
117+
});
118+
119+
return result;
120+
}
121+
}
122+
123+
124+
export class GenericCommentElement extends BaseContextSyntaxElement<ParserRuleContext> implements HasSemanticTokenCapability {
125+
semanticTokenCapability: SemanticTokenCapability;
126+
127+
constructor(ctx: ParserRuleContext, doc: TextDocument, range?: Range) {
128+
super(ctx, doc);
129+
const textLen = range ? doc.offsetAt(range.end) - doc.offsetAt(range.start) + 1 : undefined;
130+
this.semanticTokenCapability = new SemanticTokenCapability(this, SemanticTokenTypes.comment, [], range, textLen)
131+
}
132+
}

0 commit comments

Comments
 (0)