Skip to content

Commit 1bb563a

Browse files
Merge pull request #74 from Mochitto/feat/better-issues
Improve parsing `Issue`s reporting
2 parents ad17a02 + 8308f8b commit 1bb563a

File tree

8 files changed

+72
-11
lines changed

8 files changed

+72
-11
lines changed

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
All notable changes to this project from version 1.2.0 upwards are documented in this file.
33
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
44

5+
## [Unreleased]
6+
7+
### Changed
8+
- `Position`'s method `isEmpty` is deprecated in favour of `isFlat` to ensure consistency across the [StarLasu](https://github.com/Strumenta/StarLasu) libraries collection.
9+
- `Issue`'s messages are capitalized.
10+
- Parsing `Issue`s' position uses the token's length.
11+
512
## [1.6.30] – 2024-09-30
613

714
### Added

src/model/position.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -131,10 +131,18 @@ export class Position {
131131

132132
/**
133133
* If start and end are the same,
134-
* then this Position is considered empty.
134+
* then this Position is considered flat.
135+
*/
136+
isFlat(): boolean {
137+
return this.start.equals(this.end);
138+
}
139+
140+
/**
141+
* @deprecated
142+
* Use `this.isFlat()` instead.
135143
*/
136144
isEmpty(): boolean {
137-
return this.start.equals(this.end)
145+
return this.isFlat();
138146
}
139147

140148
/**

src/parsing/tylasu-parser.ts

+18-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import {
1111
Recognizer,
1212
TerminalNode,
1313
Token,
14-
TokenStream
14+
TokenStream,
15+
CommonToken
1516
} from "antlr4ng";
1617
import {Issue, IssueSeverity} from "../validation";
1718
import {Point, Position, Source, StringSource} from "../model/position";
@@ -120,14 +121,20 @@ export abstract class TylasuANTLRLexer<T extends TylasuToken> implements TylasuL
120121
reportAttemptingFullContext() {},
121122
reportContextSensitivity() {},
122123
syntaxError<S extends Token, T extends ATNSimulator>(recognizer: Recognizer<T>, offendingSymbol: S | null, line: number, charPositionInLine: number, msg: string) {
124+
const startPoint = new Point(line, charPositionInLine);
125+
let endPoint = new Point(line, charPositionInLine);
126+
if (offendingSymbol instanceof CommonToken) {
127+
const tokenLength = offendingSymbol.stop - offendingSymbol.start + 1;
128+
endPoint = new Point(offendingSymbol.line, offendingSymbol.column + tokenLength);
129+
};
123130
const regex = /token recognition error at: '(.+)'/
124131
if (regex.test(msg)){
125132
const match = msg.match(regex) as string[];
126133
issues.push(
127134
Issue.lexical(
128135
msg || "unspecified",
129136
IssueSeverity.ERROR,
130-
Position.ofPoint(new Point(line, charPositionInLine)),
137+
new Position(startPoint, endPoint),
131138
undefined,
132139
TOKEN_RECOGNITION_ERROR,
133140
[
@@ -141,7 +148,7 @@ export abstract class TylasuANTLRLexer<T extends TylasuToken> implements TylasuL
141148
Issue.lexical(
142149
msg || "unspecified",
143150
IssueSeverity.ERROR,
144-
Position.ofPoint(new Point(line, charPositionInLine)),
151+
new Position(startPoint, endPoint),
145152
undefined,
146153
SYNTAX_ERROR));
147154
}
@@ -301,6 +308,12 @@ export abstract class TylasuParser<
301308
reportAttemptingFullContext() {},
302309
reportContextSensitivity() {},
303310
syntaxError<S extends Token, T extends ATNSimulator>(recognizer: Recognizer<T>, offendingSymbol: S | null, line: number, charPositionInLine: number, msg: string) {
311+
const startPoint = new Point(line, charPositionInLine);
312+
let endPoint = new Point(line, charPositionInLine);
313+
if (offendingSymbol instanceof CommonToken) {
314+
const tokenLength = offendingSymbol.stop - offendingSymbol.start + 1;
315+
endPoint = new Point(offendingSymbol.line, offendingSymbol.column + tokenLength);
316+
};
304317
const mismatchedRegex = /^mismatched input '(<EOF>|.+)' expecting {([a-zA-Z_]+(, [a-zA-Z_]+)*)}$/
305318
if (mismatchedRegex.test(msg)) {
306319
const match = msg.match(mismatchedRegex) as string[];
@@ -316,7 +329,7 @@ export abstract class TylasuParser<
316329
Issue.syntactic(
317330
msg,
318331
IssueSeverity.ERROR,
319-
Position.ofPoint(new Point(line, charPositionInLine)),
332+
new Position(startPoint, endPoint),
320333
undefined,
321334
MISMATCHED_INPUT,
322335
args));
@@ -325,7 +338,7 @@ export abstract class TylasuParser<
325338
Issue.syntactic(
326339
msg || "unspecified",
327340
IssueSeverity.ERROR,
328-
Position.ofPoint(new Point(line, charPositionInLine)),
341+
new Position(startPoint, endPoint),
329342
undefined,
330343
SYNTAX_ERROR));
331344
}

src/utils/capitalize.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* Capitalize the first letter of a string
3+
*/
4+
export function capitalize(str: string) {
5+
return str.charAt(0).toUpperCase() + str.slice(1);
6+
}

src/validation.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {Node} from "./model/model";
22
import {Position} from "./model/position";
3+
import { capitalize } from "./utils/capitalize";
34

45
export enum IssueType { LEXICAL, SYNTACTIC, SEMANTIC}
56

@@ -21,6 +22,8 @@ export class Issue {
2122
public readonly code?: string,
2223
public readonly args: IssueArg[] = []
2324
) {
25+
this.message = capitalize(message);
26+
2427
if (!position) {
2528
this.position = node?.position;
2629
}

tests/issues.test.ts

+6
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,10 @@ describe('Issues', function() {
3030
SOURCE_NODE_NOT_MAPPED, [{ name: "nodeType", value: "SomeNode" }]);
3131
expect(i18next.t(issue.code!, { type: issue.args[0].value })).to.equal("Source node not mapped: SomeNode");
3232
});
33+
34+
it("has capitalized messages",
35+
function () {
36+
let issue = Issue.syntactic("unexpected token: foo", IssueSeverity.ERROR, undefined, undefined, SYNTAX_ERROR);
37+
expect(issue.message).to.equal("Unexpected token: foo");
38+
});
3339
});

tests/parsing.test.ts

+19-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ describe('Parsing', function() {
8282
IssueType.SYNTACTIC,
8383
"mismatched input '+' expecting {INT_LIT, DEC_LIT, STRING_LIT, BOOLEAN_LIT}",
8484
IssueSeverity.ERROR,
85-
new Position(new Point(1, 11), new Point(1, 11)),
85+
new Position(new Point(1, 11), new Point(1, 12)),
8686
undefined,
8787
"parser.mismatchedinput",
8888
[
@@ -225,4 +225,22 @@ describe('Parsing', function() {
225225
]
226226
)])
227227
})
228+
it("produces issues with non-flat positions",
229+
function() {
230+
const code =
231+
"set set a = 10\n" +
232+
"|display c\n";
233+
const parser = new SLParser(new ANTLRTokenFactory());
234+
const result = parser.parse(code);
235+
236+
expect(result.issues.length).to.not.eq(0)
237+
238+
const extraneousInput = result.issues.find(issue => issue.message.startsWith("Extraneous input 'set'"))
239+
expect(!(extraneousInput?.position?.isFlat()))
240+
expect(extraneousInput?.position).to.eql(new Position(new Point(1, 4), new Point(1, 7)))
241+
242+
const mismatchedInput = result.issues.find(issue => issue.message.startsWith("Mismatched input 'c'"))
243+
expect(!(mismatchedInput?.position?.isFlat()))
244+
expect(mismatchedInput?.position).to.eql(new Position(new Point(2, 9), new Point(2, 10)))
245+
})
228246
});

tests/transformation/transformation.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ describe("AST Transformers", function () {
101101
transformer.addIssue("warning", IssueSeverity.WARNING);
102102
transformer.addIssue("info", IssueSeverity.INFO, pos(1, 0, 1, 2));
103103

104-
expect(transformer.issues[0].message).to.be.equal("error");
105-
expect(transformer.issues[1].message).to.be.equal("warning");
106-
expect(transformer.issues[2].message).to.be.equal("info");
104+
expect(transformer.issues[0].message).to.be.equal("Error");
105+
expect(transformer.issues[1].message).to.be.equal("Warning");
106+
expect(transformer.issues[2].message).to.be.equal("Info");
107107
});
108108
it("transform function does not accept collections as source", function () {
109109
const transformer = new ASTTransformer();

0 commit comments

Comments
 (0)