Skip to content

Commit 8a7c44e

Browse files
committed
#62 ParseTreeASTTransformer: propagate the Source as in Kolasu
1 parent 12d6688 commit 8a7c44e

File tree

5 files changed

+58
-16
lines changed

5 files changed

+58
-16
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
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+
## [1.6.19] – Not yet released
6+
7+
### Added
8+
- ParseTreeToASTTransformer: propagate the Source as in Kolasu
9+
10+
### Changed
11+
- Updated to antlr4ng 3
12+
513
## [1.6.18] – 2024-03-21
614

715
### Fixed

src/mapping.ts

+25
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import {ParserRuleContext, TerminalNode} from "antlr4ng";
22
import {Node, Origin} from "./model/model";
33
import {ASTTransformer} from "./transformation/transformation";
44
import {ParseTreeOrigin} from "./parsing";
5+
import {Issue} from "./validation";
6+
import {Source} from "./model/position";
57

68

79
/**
@@ -10,6 +12,10 @@ import {ParseTreeOrigin} from "./parsing";
1012
*/
1113
export class ParseTreeToASTTransformer extends ASTTransformer {
1214

15+
constructor(issues: Issue[] = [], allowGenericNode = true, public readonly source?: Source) {
16+
super(issues, allowGenericNode);
17+
}
18+
1319
/**
1420
* Performs the transformation of a node and, recursively, its descendants. In addition to the overridden method,
1521
* it also assigns the parseTreeNode to the AST node so that it can keep track of its position.
@@ -39,4 +45,23 @@ export class ParseTreeToASTTransformer extends ASTTransformer {
3945
return undefined;
4046
}
4147
}
48+
49+
/**
50+
* Performs the transformation of a node and, recursively, its descendants. In addition to the overridden method,
51+
* it also assigns the parseTreeNode to the AST node so that it can keep track of its position.
52+
* However, a node factory can override the parseTreeNode of the nodes it creates (but not the parent).
53+
*/
54+
transformIntoNodes(source?: any, parent?: Node) {
55+
const transformed = super.transformIntoNodes(source, parent);
56+
return transformed.map((node) => {
57+
if (source instanceof ParserRuleContext) {
58+
if (node.origin == null) {
59+
node.withParseTreeNode(source, this.source);
60+
} else if (node.position != null && node.source == null) {
61+
node.position.source = this.source;
62+
}
63+
}
64+
return node;
65+
});
66+
}
4267
}

src/model/model.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Position} from "./position";
1+
import {Position, Source} from "./position";
22
import "reflect-metadata";
33
import {PossiblyNamed, ReferenceByName} from "./naming";
44

@@ -90,6 +90,10 @@ export abstract class Origin {
9090
overlaps(position?: Position): boolean {
9191
return this.position?.overlaps(position) || false
9292
}
93+
94+
get source(): Source | undefined {
95+
return this.position?.source;
96+
}
9397
}
9498

9599
export class SimpleOrigin extends Origin {

src/model/position.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ export class Point {
5050
return other && this.compareTo(other) <= 0;
5151
}
5252

53-
asPosition(): Position {
54-
return new Position(this, this);
53+
asPosition(source?: Source): Position {
54+
return new Position(this, this, source);
5555
}
5656
}
5757

src/parsing/parse-tree.ts

+18-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Node, Origin, Point, Position} from "../";
1+
import {Node, Origin, Point, Position, Source} from "../";
22
import {ParserRuleContext, ParseTree, TerminalNode, Token} from "antlr4ng";
33

44
// Note: we cannot provide Kolasu-style extension methods on ParseTree because it's an interface.
@@ -12,27 +12,27 @@ declare module "../model/position" {
1212
}
1313
// eslint-disable-next-line @typescript-eslint/no-namespace
1414
export namespace Position {
15-
export function ofParseTree(parseTree: ParseTree): Position | undefined;
15+
export function ofParseTree(parseTree: ParseTree, source?: Source): Position | undefined;
1616
export function ofToken(token: Token): Position;
1717
export function ofTokenStart(token: Token): Position;
1818
export function ofTokenEnd(token: Token): Position;
1919
}
2020
}
2121

22-
export function positionOfParseTree(parseTree: ParseTree): Position | undefined {
22+
export function positionOfParseTree(parseTree: ParseTree, source?: Source): Position | undefined {
2323
if(parseTree instanceof ParserRuleContext) {
2424
const startToken = parseTree.start;
2525
const stopToken = parseTree.stop;
2626

2727
if (startToken) {
2828
if (stopToken) {
29-
return new Position(Point.ofTokenStart(startToken), Point.ofTokenEnd(stopToken));
29+
return new Position(Point.ofTokenStart(startToken), Point.ofTokenEnd(stopToken), source);
3030
} else {
31-
return Point.ofTokenStart(startToken).asPosition();
31+
return Point.ofTokenStart(startToken).asPosition(source);
3232
}
3333
}
3434
} else if(parseTree instanceof TerminalNode) {
35-
return new Position(Point.ofTokenStart(parseTree.symbol), Point.ofTokenEnd(parseTree.symbol));
35+
return new Position(Point.ofTokenStart(parseTree.symbol), Point.ofTokenEnd(parseTree.symbol), source);
3636
}
3737
}
3838

@@ -60,12 +60,12 @@ Position.ofToken = function (token: Token): Position {
6060
}
6161

6262
export class ParseTreeOrigin extends Origin {
63-
constructor(public parseTree?: ParseTree) {
63+
constructor(public parseTree?: ParseTree, protected readonly _source?: Source) {
6464
super();
6565
}
6666

6767
get position(): Position | undefined {
68-
return this.parseTree ? Position.ofParseTree(this.parseTree) : undefined;
68+
return this.parseTree ? Position.ofParseTree(this.parseTree, this._source) : undefined;
6969
}
7070

7171
get sourceText(): string | undefined {
@@ -77,12 +77,17 @@ export class ParseTreeOrigin extends Origin {
7777
return undefined;
7878
}
7979
}
80+
81+
82+
get source(): Source | undefined {
83+
return this._source;
84+
}
8085
}
8186

8287
declare module '../model/model' {
8388
export interface Node {
8489
parseTree?: ParseTree;
85-
withParseTreeNode(parseTree?: ParseTree | null): this;
90+
withParseTreeNode(parseTree?: ParseTree | null, source?: Source): this;
8691
}
8792
}
8893

@@ -92,15 +97,15 @@ declare module 'antlr4ng' {
9297
}
9398
}
9499

95-
export function withParseTreeNode(node: Node, parseTree?: ParseTree | null): Node {
100+
export function withParseTreeNode(node: Node, parseTree?: ParseTree | null, source?: Source): Node {
96101
if (parseTree) {
97-
node.origin = new ParseTreeOrigin(parseTree);
102+
node.origin = new ParseTreeOrigin(parseTree, source);
98103
}
99104
return node;
100105
}
101106

102-
Node.prototype.withParseTreeNode = function (parseTree) {
103-
return withParseTreeNode(this, parseTree);
107+
Node.prototype.withParseTreeNode = function (parseTree, source) {
108+
return withParseTreeNode(this, parseTree, source);
104109
}
105110

106111
Object.defineProperty(Node.prototype, "parseTree", {

0 commit comments

Comments
 (0)