-
-
Notifications
You must be signed in to change notification settings - Fork 52
/
Copy pathAst.ts
143 lines (131 loc) · 3.54 KB
/
Ast.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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
const escapeCharacter = '\\'
const alternationCharacter = '/'
const beginParameterCharacter = '{'
const endParameterCharacter = '}'
const beginOptionalCharacter = '('
const endOptionalCharacter = ')'
export function symbolOf(token: TokenType): string {
switch (token) {
case TokenType.beginOptional:
return beginOptionalCharacter
case TokenType.endOptional:
return endOptionalCharacter
case TokenType.beginParameter:
return beginParameterCharacter
case TokenType.endParameter:
return endParameterCharacter
case TokenType.alternation:
return alternationCharacter
}
return ''
}
export function purposeOf(token: TokenType): string {
switch (token) {
case TokenType.beginOptional:
case TokenType.endOptional:
return 'optional text'
case TokenType.beginParameter:
case TokenType.endParameter:
return 'a parameter'
case TokenType.alternation:
return 'alternation'
}
return ''
}
export interface Located {
readonly start: number
readonly end: number
}
export class Node implements Located {
constructor(
public readonly type: NodeType,
public readonly nodes: readonly Node[] | undefined,
private readonly token: string | undefined,
public readonly start: number,
public readonly end: number
) {
if (nodes === undefined && token === undefined) {
throw new Error('Either nodes or token must be defined')
}
}
text(): string {
if (this.nodes && this.nodes.length > 0) {
return this.nodes.map((value) => value.text()).join('')
}
return this.token || ''
}
}
export enum NodeType {
text = 'TEXT_NODE',
optional = 'OPTIONAL_NODE',
alternation = 'ALTERNATION_NODE',
alternative = 'ALTERNATIVE_NODE',
parameter = 'PARAMETER_NODE',
expression = 'EXPRESSION_NODE',
}
export class Token implements Located {
readonly type: TokenType
readonly text: string
readonly start: number
readonly end: number
constructor(type: TokenType, text: string, start: number, end: number) {
this.type = type
this.text = text
this.start = start
this.end = end
}
static isEscapeCharacter(codePoint: string): boolean {
return codePoint == escapeCharacter
}
static canEscape(codePoint: string): boolean {
if (codePoint == ' ') {
// TODO: Unicode whitespace?
return true
}
switch (codePoint) {
case escapeCharacter:
return true
case alternationCharacter:
return true
case beginParameterCharacter:
return true
case endParameterCharacter:
return true
case beginOptionalCharacter:
return true
case endOptionalCharacter:
return true
}
return false
}
static typeOf(codePoint: string): TokenType {
if (codePoint == ' ') {
// TODO: Unicode whitespace?
return TokenType.whiteSpace
}
switch (codePoint) {
case alternationCharacter:
return TokenType.alternation
case beginParameterCharacter:
return TokenType.beginParameter
case endParameterCharacter:
return TokenType.endParameter
case beginOptionalCharacter:
return TokenType.beginOptional
case endOptionalCharacter:
return TokenType.endOptional
}
return TokenType.text
}
}
export enum TokenType {
startOfLine = 'START_OF_LINE',
endOfLine = 'END_OF_LINE',
whiteSpace = 'WHITE_SPACE',
beginOptional = 'BEGIN_OPTIONAL',
endOptional = 'END_OPTIONAL',
beginParameter = 'BEGIN_PARAMETER',
endParameter = 'END_PARAMETER',
alternation = 'ALTERNATION',
text = 'TEXT',
}