Skip to content

Commit d52d5b8

Browse files
authored
Feature semantic errors (#8)
1 parent 5941014 commit d52d5b8

File tree

10 files changed

+155
-61
lines changed

10 files changed

+155
-61
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to the "bitloops-language" extension will be documented in t
44

55
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
66

7+
### 0.2.0
8+
9+
Added validator for semantic and syntactic errors
10+
711
### 0.1.1
812

913
Added transpiler package dependency

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
},
1010
"icon": "assets/images/bitloops-language-logo-256x256.png",
1111
"license": "MIT",
12-
"version": "0.1.1",
12+
"version": "0.2.0",
1313
"scripts": {
1414
"vs:package": "vsce package",
1515
"vs:publish": "vsce publish",

server/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "bitloops-lsp-server",
33
"description": "BitLoops Language Server",
4-
"version": "1.0.1",
4+
"version": "0.2.0",
55
"publisher": "Bitloops",
66
"license": "MIT",
77
"engines": {
@@ -10,7 +10,7 @@
1010
"type": "module",
1111
"scripts": {},
1212
"dependencies": {
13-
"@bitloops/bl-transpiler": "^0.1.0",
13+
"@bitloops/bl-transpiler": "^0.2.0",
1414
"vscode-languageserver": "^7.0.0",
1515
"vscode-languageserver-textdocument": "^1.0.4"
1616
},

server/src/analyzer.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,20 @@ export interface IAnalyzer {
99
/**
1010
* It analyzes the document and returns a list of diagnostics.
1111
*/
12-
analyze(document: TextDocument): Diagnostic[];
12+
analyze(document: TextDocument): Record<string, Diagnostic[]>;
1313
}
1414

1515
let problems = 0;
1616

1717
export class RegexAnalyzer implements IAnalyzer {
18-
public analyze(textDocument: TextDocument): Diagnostic[] {
19-
let diagnostics: Diagnostic[] = [];
20-
diagnostics = diagnostics.concat(this.validateComponents(textDocument));
18+
public analyze(textDocument: TextDocument): Record<string, Diagnostic[]> {
19+
let diagnostics: Record<string, Diagnostic[]> = {};
20+
diagnostics[textDocument.uri] = [];
21+
diagnostics[textDocument.uri] = diagnostics[textDocument.uri].concat(
22+
this.validateComponents(textDocument),
23+
);
2124
// connection.console.log(`[validate] ${textDocument.uri} has ${diagnostics.length} for ${text}`);
22-
problems = diagnostics.length;
25+
problems = diagnostics[textDocument.uri].length;
2326
return diagnostics;
2427
}
2528

server/src/diagnostic.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export class DiagnosticFactory {
1212
severity: LogLevel,
1313
range: Range,
1414
message: string,
15+
uri?: string,
1516
addRelatedInformation?: string[],
1617
): Diagnostic {
1718
message = this.prefixMessage(message, severity);
@@ -27,7 +28,7 @@ export class DiagnosticFactory {
2728
diagnostic.relatedInformation.push({
2829
location: {
2930
// TODO? add TextDocument uri?
30-
uri: '',
31+
uri: uri,
3132
range: range,
3233
},
3334
message: related,

server/src/parser/index.ts

Lines changed: 95 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,69 +2,128 @@ import {
22
OriginalParserError,
33
TParserInputData,
44
isParserErrors,
5+
isIntermediateASTValidationErrors,
56
transpiler,
7+
OriginalValidatorError,
68
} from '@bitloops/bl-transpiler';
79
import { Diagnostic } from 'vscode-languageserver/node.js';
810
import { TextDocument } from 'vscode-languageserver-textdocument';
911
import { IAnalyzer } from '../analyzer.js';
1012
import { DiagnosticFactory } from '../diagnostic.js';
13+
import { TParserCoreInputData, TParserSetupInputData } from '../types.js';
1114

1215
export class BitloopsAnalyzer implements IAnalyzer {
13-
static diagnostics: Diagnostic[] = [];
14-
analyze(document: TextDocument): Diagnostic[] {
16+
private diagnostics: Record<string, Diagnostic[]> = {};
17+
private res: Partial<TParserInputData> = {};
18+
private setup: Record<string, TParserSetupInputData> = {};
19+
private core: Record<string, TParserCoreInputData> = {};
20+
analyze(document: TextDocument): Record<string, Diagnostic[]> {
1521
try {
1622
const transpileInputData = this.documentToParserInputData(document);
1723
const intermediateModel = transpiler.bitloopsCodeToIntermediateModel(transpileInputData);
1824
if (isParserErrors(intermediateModel)) {
19-
const diagnostics = this.mapParserErrorsToLSPDiagnostics(intermediateModel, document);
20-
return diagnostics;
25+
this.mapParserErrorsToLSPDiagnostics(intermediateModel, document);
26+
return this.diagnostics;
2127
}
22-
return [];
28+
if (isIntermediateASTValidationErrors(intermediateModel)) {
29+
this.mapValidatorErrorsToLSPDiagnostics(intermediateModel, document);
30+
return this.diagnostics;
31+
}
32+
return this.diagnostics;
2333
} catch (e) {
2434
console.log('error', e);
25-
return [];
35+
return {};
2636
}
2737
}
2838

2939
private documentToParserInputData(document: TextDocument): TParserInputData {
30-
const res: Partial<TParserInputData> = {};
31-
32-
if (document.uri.endsWith('setup.bl')) {
33-
res.setup = [
40+
//giving file id as uri for now
41+
if (!(document.uri in this.diagnostics)) this.diagnostics[document.uri] = [];
42+
if (document.uri.endsWith('setup.bl'))
43+
this.setup[document.uri] = [
3444
{
35-
fileId: document.uri.split('/').slice(-1)[0],
45+
// fileId: document.uri.split('/').slice(-1)[0],
46+
fileId: document.uri,
3647
fileContents: document.getText(),
3748
},
3849
];
39-
return res as TParserInputData;
40-
}
4150
// Handle possibly unknown bounded context and module
42-
const boundedContext = document.uri.split('/')?.slice(-3)?.[0] ?? 'unknown';
43-
const module = document.uri.split('/')?.slice(-2)?.[0] ?? 'unknown';
44-
res.core = [
45-
{
46-
boundedContext,
47-
module,
48-
fileId: document.uri.split('/').slice(-1)[0],
49-
fileContents: document.getText(),
50-
},
51-
];
52-
return res as TParserInputData;
53-
}
54-
55-
mapParserErrorsToLSPDiagnostics(
56-
parserErrors: OriginalParserError,
57-
document: TextDocument,
58-
): Diagnostic[] {
59-
return parserErrors.map((e) =>
60-
DiagnosticFactory.create(
61-
1,
51+
else if (document.uri.endsWith('.bl')) {
52+
const boundedContext = document.uri.split('/')?.slice(-3)?.[0] ?? 'unknown';
53+
const module = document.uri.split('/')?.slice(-2)?.[0] ?? 'unknown';
54+
this.core[document.uri] = [
6255
{
63-
start: document.positionAt(e.start),
64-
end: document.positionAt(e.stop),
56+
boundedContext,
57+
module,
58+
// fileId: document.uri.split('/').slice(-1)[0],
59+
fileId: document.uri,
60+
fileContents: document.getText(),
6561
},
66-
`line: ${e.line}:${e.column}, offendingSymbol: ${e.offendingToken.text}, msg: ${e.message}`,
62+
];
63+
}
64+
this.res.core = [];
65+
this.res.setup = [];
66+
for (const core of Object.values(this.core)) {
67+
this.res.core.push(...core);
68+
}
69+
for (const setup of Object.values(this.setup)) {
70+
this.res.setup.push(...setup);
71+
}
72+
for (const key in this.diagnostics) {
73+
this.diagnostics[key] = [];
74+
}
75+
return this.res as TParserInputData;
76+
}
77+
mapParserErrorsToLSPDiagnostics(parserErrors: OriginalParserError, document: TextDocument): void {
78+
parserErrors.forEach((e) => {
79+
this.diagnostics[e.fileId] = [];
80+
});
81+
for (const key in this.diagnostics) {
82+
this.diagnostics[key] = [];
83+
}
84+
parserErrors.map((e) =>
85+
this.diagnostics[e.fileId].push(
86+
DiagnosticFactory.create(
87+
1,
88+
{
89+
start: document.positionAt(e.start),
90+
end: document.positionAt(e.stop),
91+
},
92+
`line: ${e.line}:${e.column}, offendingSymbol: ${e.offendingToken.text}, msg: ${e.message}`,
93+
e.fileId,
94+
),
6795
),
6896
);
6997
}
98+
99+
mapValidatorErrorsToLSPDiagnostics(
100+
validatorErrors: OriginalValidatorError,
101+
document: TextDocument,
102+
): void {
103+
validatorErrors.forEach((e) => {
104+
this.diagnostics[e.metadata.fileId] = [];
105+
});
106+
for (const key in this.diagnostics) {
107+
this.diagnostics[key] = [];
108+
}
109+
validatorErrors.map((e) => {
110+
this.diagnostics[e.metadata.fileId].push(
111+
DiagnosticFactory.create(
112+
1,
113+
{
114+
start: {
115+
line: e.metadata.start.line - 1,
116+
character: e.metadata.start.column - 1,
117+
},
118+
end: {
119+
line: e.metadata.end.line - 1,
120+
character: e.metadata.end.column - 1,
121+
},
122+
},
123+
`line: ${e.metadata.start.line}:${e.metadata.start.column} , msg: ${e.message}`,
124+
e.metadata.fileId,
125+
),
126+
);
127+
});
128+
}
70129
}

server/src/server.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
InitializeResult,
77
TextDocumentSyncKind,
88
DidChangeConfigurationNotification,
9+
Diagnostic,
910
} from 'vscode-languageserver/node.js';
1011

1112
import { TextDocument } from 'vscode-languageserver-textdocument';
@@ -41,17 +42,13 @@ export class BitloopsServer {
4142
}
4243

4344
public async onDidChangeContent(change: TextDocumentChangeEvent<TextDocument>): Promise<void> {
44-
const settings = await this.settingsManger.getDocumentSettings(
45-
change.document.uri,
46-
this.hasConfigurationCapability,
47-
this.connection,
48-
);
49-
// We could use retrieved settings here to change the way we parse the document
50-
51-
const diagnostics = this.analyzer.analyze(change.document);
52-
this.lspClient.publishDiagnostics({ uri: change.document.uri, diagnostics });
45+
this.createDiagnostics(change.document);
5346
}
5447

48+
// public async onDidOpen(change: TextDocumentChangeEvent<TextDocument>): Promise<void> {
49+
// this.createDiagnostics(change.document);
50+
// }
51+
5552
public onInitialize(params: InitializeParams): InitializeResult {
5653
const capabilities = params.capabilities;
5754
this.hasConfigurationCapability = !!(
@@ -98,4 +95,18 @@ export class BitloopsServer {
9895

9996
public completion = CompletionItemProvider.onCompletion;
10097
public completionResolve = CompletionItemProvider.onCompletionResolve;
98+
99+
private async createDiagnostics(document: TextDocument): Promise<void> {
100+
const settings = await this.settingsManger.getDocumentSettings(
101+
document.uri,
102+
this.hasConfigurationCapability,
103+
this.connection,
104+
);
105+
// We could use retrieved settings here to change the way we parse the document
106+
107+
const diagnostics = this.analyzer.analyze(document);
108+
for (const [uri, diagnostic] of Object.entries(diagnostics)) {
109+
this.lspClient.publishDiagnostics({ uri: uri, diagnostics: diagnostic });
110+
}
111+
}
101112
}

server/src/types.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ export type TParserCoreInputData = {
8787
fileContents: TFileContents;
8888
}[];
8989

90+
export type TParserSetupInputData = {
91+
fileId: TFileId;
92+
fileContents: TFileContents;
93+
}[];
94+
9095
export type TASTCoreInputData = {
9196
boundedContext: string;
9297
classes: Record<TClassType, Record<TClassName, TClassInformation>>;
@@ -126,7 +131,14 @@ export type TBitloopsTargetGeneratorParams = {
126131
sourceDirPath?: string; // TODO remove this after making the package files injectable in the setup
127132
};
128133

129-
export type TBitloopsClasses = TProps | TValueObjects | TRESTController | TUseCase | TDomainErrors | TDTO | TStructs;
134+
export type TBitloopsClasses =
135+
| TProps
136+
| TValueObjects
137+
| TRESTController
138+
| TUseCase
139+
| TDomainErrors
140+
| TDTO
141+
| TStructs;
130142

131143
export type TModuleName = string;
132144
export type TBoundedContext = Record<TModuleName, TModule>;
@@ -932,7 +944,11 @@ export type TIdentifierExpression = {
932944
};
933945

934946
export type TLogicalSingleExpression = {
935-
logicalExpression: TNotSingleExpression | TAndSingleExpression | TOrSingleExpression | TXorSingleExpression;
947+
logicalExpression:
948+
| TNotSingleExpression
949+
| TAndSingleExpression
950+
| TOrSingleExpression
951+
| TXorSingleExpression;
936952
};
937953

938954
export type TNotSingleExpression = {

server/yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
# yarn lockfile v1
33

44

5-
"@bitloops/bl-transpiler@^0.1.0":
6-
version "0.1.0"
7-
resolved "https://registry.yarnpkg.com/@bitloops/bl-transpiler/-/bl-transpiler-0.1.0.tgz#babf5f8ab4c02b23c75c1dca53ebb94d02f33d57"
8-
integrity sha512-HMsU+6ggV/+sRQRbDbzpgZhm7fE06YORfW207ayJYlotVuGhvefyWRqtWjE2da5W37Ora+a+kQwdGW9ArP05ow==
5+
"@bitloops/bl-transpiler@^0.2.0":
6+
version "0.2.0"
7+
resolved "https://registry.yarnpkg.com/@bitloops/bl-transpiler/-/bl-transpiler-0.2.0.tgz#0829384701af3086737e9c30ac724c19dd624e74"
8+
integrity sha512-PYcg08m0YCwWzLkufgB3ULgFncSW1BE50qdp5DiMFyRT/5mioDT9hiFcl0cRZJDY8s5hMeWkDRogSI17SOSCtw==
99
dependencies:
1010
antlr4 "^4.11.0"
1111
lodash "^4.17.21"

vsc-extension-quickstart.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
## Get up and running straight away
1111

12-
- `npm install`
12+
- `yarn`
1313
- `npm run compile`
1414
- Make sure the language configuration settings in `language-configuration.json` are accurate.
1515
- Press `F5` to open a new window with your extension loaded.

0 commit comments

Comments
 (0)