Skip to content

Commit 27caf08

Browse files
committed
Convert AstNode to a tagged union
1 parent b996e77 commit 27caf08

1 file changed

Lines changed: 50 additions & 40 deletions

File tree

libraries/rushell/src/AstNode.ts

Lines changed: 50 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import { Token } from './Tokenizer';
55

66
export enum AstKind {
7+
None,
78
Script,
89
AndIf,
910
Command,
@@ -15,8 +16,8 @@ export enum AstKind {
1516
/**
1617
* Base class for all AST nodes.
1718
*/
18-
export abstract class AstNode {
19-
public abstract get kind(): AstKind;
19+
export abstract class AstBaseNode {
20+
public readonly kind: AstKind = AstKind.None;
2021

2122
/**
2223
* Returns a diagnostic dump of the tree, showing the prefix/suffix/separator for
@@ -30,7 +31,7 @@ export abstract class AstNode {
3031
result += '=' + JSON.stringify(dumpText);
3132
}
3233

33-
const childNodes: AstNode[] = this.getChildNodes();
34+
const childNodes: AstBaseNode[] = this.getChildNodes();
3435
if (childNodes.length === 0) {
3536
result += '\n';
3637
} else {
@@ -43,35 +44,41 @@ export abstract class AstNode {
4344
return result;
4445
}
4546

46-
public getChildNodes(): AstNode[] {
47-
const nodes: AstNode[] = [];
47+
public getChildNodes(): AstBaseNode[] {
48+
const nodes: AstBaseNode[] = [];
4849
this.collectChildNodesInto(nodes);
4950
return nodes;
5051
}
5152

52-
protected abstract collectChildNodesInto(nodes: AstNode[]): void;
53+
protected abstract collectChildNodesInto(nodes: AstBaseNode[]): void;
5354

5455
protected getDumpText(): string | undefined {
5556
return undefined;
5657
}
5758
}
5859

59-
export class AstScript extends AstNode {
60-
public body: AstNode | undefined;
60+
/**
61+
* Represents a complete script that can be executed.
62+
*/
63+
export class AstScript extends AstBaseNode {
64+
public readonly kind: AstKind.Script = AstKind.Script;
6165

62-
public get kind(): AstKind {
63-
return AstKind.Script;
64-
}
66+
public body: AstNode | undefined;
6567

6668
/** @override */
67-
protected collectChildNodesInto(nodes: AstNode[]): void {
69+
protected collectChildNodesInto(nodes: AstBaseNode[]): void {
6870
if (this.body) {
6971
nodes.push(this.body);
7072
}
7173
}
7274
}
7375

74-
export class AstAndIf extends AstNode {
76+
/**
77+
* Represents the "&&" operator, which is used to join two individual commands.
78+
*/
79+
export class AstAndIf extends AstBaseNode {
80+
public readonly kind: AstKind.AndIf = AstKind.AndIf;
81+
7582
/**
7683
* The command that executes first, and always.
7784
*/
@@ -82,12 +89,8 @@ export class AstAndIf extends AstNode {
8289
*/
8390
public secondCommand: AstCommand | undefined;
8491

85-
public get kind(): AstKind {
86-
return AstKind.AndIf;
87-
}
88-
8992
/** @override */
90-
protected collectChildNodesInto(nodes: AstNode[]): void {
93+
protected collectChildNodesInto(nodes: AstBaseNode[]): void {
9194
if (this.firstCommand) {
9295
nodes.push(this.firstCommand);
9396
}
@@ -97,55 +100,60 @@ export class AstAndIf extends AstNode {
97100
}
98101
}
99102

100-
export class AstCommand extends AstNode {
103+
/**
104+
* Represents a command. For example, the name of an executable to be started.
105+
*/
106+
export class AstCommand extends AstBaseNode {
107+
public readonly kind: AstKind.Command = AstKind.Command;
108+
101109
public commandPath: AstCompoundWord | undefined;
102110
public arguments: AstCompoundWord[] = [];
103111

104-
public get kind(): AstKind {
105-
return AstKind.Command;
106-
}
107-
108112
/** @override */
109-
protected collectChildNodesInto(nodes: AstNode[]): void {
113+
protected collectChildNodesInto(nodes: AstBaseNode[]): void {
110114
if (this.commandPath) {
111115
nodes.push(this.commandPath);
112116
}
113117
nodes.push(...this.arguments);
114118
}
115119
}
116120

117-
export class AstCompoundWord extends AstNode {
118-
public readonly parts: AstNode[] = [];
121+
/**
122+
* Represents a compound word, e.g. "--the-thing" or "./the/thing".
123+
*/
124+
export class AstCompoundWord extends AstBaseNode {
125+
public readonly kind: AstKind.CompoundWord = AstKind.CompoundWord;
119126

120-
public get kind(): AstKind {
121-
return AstKind.CompoundWord;
122-
}
127+
public readonly parts: AstBaseNode[] = [];
123128

124129
/** @override */
125-
protected collectChildNodesInto(nodes: AstNode[]): void {
130+
protected collectChildNodesInto(nodes: AstBaseNode[]): void {
126131
nodes.push(...this.parts);
127132
}
128133
}
129134

130-
export class AstVariableExpansion extends AstNode {
131-
public get kind(): AstKind {
132-
return AstKind.VariableExpansion;
133-
}
135+
/**
136+
* Represents an environment variable expansion expression, e.g. "${VARIABLE}"
137+
*/
138+
export class AstVariableExpansion extends AstBaseNode {
139+
public readonly kind: AstKind.VariableExpansion = AstKind.VariableExpansion;
134140

135141
/** @override */
136-
protected collectChildNodesInto(nodes: AstNode[]): void {
142+
protected collectChildNodesInto(nodes: AstBaseNode[]): void {
137143
// no children
138144
}
139145
}
140146

141-
export class AstText extends AstNode {
147+
/**
148+
* Represents some plain text.
149+
*/
150+
export class AstText extends AstBaseNode {
151+
public readonly kind: AstKind.Text = AstKind.Text;
152+
142153
public token: Token | undefined;
143-
public get kind(): AstKind {
144-
return AstKind.Text;
145-
}
146154

147155
/** @override */
148-
protected collectChildNodesInto(nodes: AstNode[]): void {
156+
protected collectChildNodesInto(nodes: AstBaseNode[]): void {
149157
// no children
150158
}
151159

@@ -157,3 +165,5 @@ export class AstText extends AstNode {
157165
return undefined;
158166
}
159167
}
168+
169+
export type AstNode = AstScript | AstAndIf | AstCommand | AstCompoundWord | AstVariableExpansion | AstText;

0 commit comments

Comments
 (0)