-
Notifications
You must be signed in to change notification settings - Fork 3
/
LatexAST.ts
116 lines (90 loc) · 3.67 KB
/
LatexAST.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import * as vscode from "vscode";
import { SourceFile } from "../source-files/SourceFile";
import { SourceFileChange } from "../source-files/SourceFileChange";
import { ASTParser, ASTParsingError } from "./ASTParser";
import { ASTNode } from "./nodes/ASTNode";
import { LatexNode } from "./nodes/LatexNode";
import { ASTFormatter } from "./visitors/ASTFormatter";
import { ASTNodeCollecter } from "./visitors/ASTNodeCollecter";
import { ASTSyncVisitor, ASTAsyncVisitor } from "./visitors/visitors";
/** Type of the root node of an AST. */
export type ASTRootNode = LatexNode;
export class NoRootNodeError {}
/** Class of an AST for a simple subset of Latex. */
export class LatexAST {
private readonly parser: ASTParser;
private readonly sourceFile: SourceFile;
private rootNode: ASTRootNode | null;
readonly parsingErrorEventEmitter: vscode.EventEmitter<ASTParsingError>;
private rootNodeObserverDisposable: vscode.Disposable | null;
constructor(sourceFile: SourceFile) {
this.parser = new ASTParser(sourceFile);
this.sourceFile = sourceFile;
this.rootNode = null;
this.parsingErrorEventEmitter = new vscode.EventEmitter();
this.rootNodeObserverDisposable = null;
}
async init(): Promise<void> {
await this.parseNewRoot();
}
get hasRoot(): boolean {
return !!this.rootNode;
}
get root(): ASTRootNode {
if (!this.rootNode) {
throw new NoRootNodeError();
}
return this.rootNode;
}
get nodes(): ASTNode[] {
const nodeCollecter = new ASTNodeCollecter();
this.root.syncVisitWith(nodeCollecter);
return nodeCollecter.nodes;
}
private changeRootNode(newRootNode: ASTRootNode): void {
this.stopObservingRootNode();
this.rootNode = newRootNode;
this.startObservingRootNode();
}
async parseNewRoot(): Promise<boolean> {
try {
const newRootNode = await this.parser.parse();
this.changeRootNode(newRootNode);
// console.info(`Root AST node of ${this.sourceFile.name} changed:`);
// const astFormatter = new ASTFormatter();
// await this.syncVisitWith(astFormatter);
// console.log(astFormatter.formattedAST);
return true;
}
catch (parsingError: any) {
console.warn(`The parsing of the AST of ${this.sourceFile.name} failed:`, parsingError);
this.parsingErrorEventEmitter.fire(parsingError);
return false;
}
}
protected startObservingRootNode(): void {
// // Observe reparsing completions (both for successes and failures)
// this.rootNodeObserverDisposable = this.root.reparsingEndEventEmitter.event(async ({node, result}) => {
// if (result.status) {
// console.info("The reparsing of root node of the AST suceeded: the root node should be changed.");
// }
// else {
// await this.tryToParseNewRootNode();
// }
// });
}
protected stopObservingRootNode(): void {
// this.rootNodeObserverDisposable?.dispose();
}
async processSourceFileChange(change: SourceFileChange): Promise<void> {
if (this.hasRoot) {
await this.root.dispatchAndProcessChange(change);
}
}
async syncVisitWith(visitor: ASTSyncVisitor, maxDepth: number = Number.MAX_SAFE_INTEGER): Promise<void> {
this.root.syncVisitWith(visitor, 0, maxDepth);
}
async asyncVisitWith(visitor: ASTAsyncVisitor, maxDepth: number = Number.MAX_SAFE_INTEGER): Promise<void> {
await this.root.asyncVisitWith(visitor, 0, maxDepth);
}
}