bbcode-compiler 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -3
- package/dist/generateHtml.d.ts +1 -1
- package/dist/generator/Generator.d.ts +2 -1
- package/dist/generator/transforms/Transform.d.ts +2 -1
- package/dist/generator/transforms/htmlTransforms.d.ts +2 -1
- package/dist/generator/transforms/htmlTransforms.d.ts.map +1 -1
- package/dist/generator/utils/getTagImmediateAttrVal.d.ts +2 -1
- package/dist/generator/utils/getTagImmediateText.d.ts +1 -0
- package/dist/generator/utils/getWidthHeightAttr.d.ts +2 -1
- package/dist/generator/utils/isOrderedList.d.ts +2 -1
- package/dist/index.js +1061 -16
- package/dist/index.js.map +1 -1
- package/dist/index.umd.cjs +1065 -0
- package/dist/index.umd.cjs.map +1 -0
- package/dist/lexer/Lexer.d.ts +2 -1
- package/dist/lexer/Token.d.ts +1 -0
- package/dist/parser/AstNode.d.ts +27 -17
- package/dist/parser/AstNode.d.ts.map +1 -1
- package/dist/parser/Parser.d.ts +2 -1
- package/dist/parser/nodeIsType.d.ts +1 -0
- package/package.json +41 -20
- package/src/generator/transforms/htmlTransforms.ts +7 -0
- package/src/parser/AstNode.ts +162 -89
- package/src/parser/Parser.ts +103 -103
- package/dist/generateHtml.js +0 -13
- package/dist/generateHtml.js.map +0 -1
- package/dist/generator/Generator.js +0 -54
- package/dist/generator/Generator.js.map +0 -1
- package/dist/generator/transforms/Transform.js +0 -2
- package/dist/generator/transforms/Transform.js.map +0 -1
- package/dist/generator/transforms/htmlTransforms.js +0 -198
- package/dist/generator/transforms/htmlTransforms.js.map +0 -1
- package/dist/generator/utils/getTagImmediateAttrVal.js +0 -19
- package/dist/generator/utils/getTagImmediateAttrVal.js.map +0 -1
- package/dist/generator/utils/getTagImmediateText.js +0 -28
- package/dist/generator/utils/getTagImmediateText.js.map +0 -1
- package/dist/generator/utils/getWidthHeightAttr.js +0 -47
- package/dist/generator/utils/getWidthHeightAttr.js.map +0 -1
- package/dist/generator/utils/isDangerousUrl.js +0 -14
- package/dist/generator/utils/isDangerousUrl.js.map +0 -1
- package/dist/generator/utils/isOrderedList.js +0 -26
- package/dist/generator/utils/isOrderedList.js.map +0 -1
- package/dist/lexer/Lexer.js +0 -81
- package/dist/lexer/Lexer.js.map +0 -1
- package/dist/lexer/Token.js +0 -53
- package/dist/lexer/Token.js.map +0 -1
- package/dist/lexer/TokenType.js +0 -41
- package/dist/lexer/TokenType.js.map +0 -1
- package/dist/parser/AstNode.js +0 -263
- package/dist/parser/AstNode.js.map +0 -1
- package/dist/parser/Parser.js +0 -268
- package/dist/parser/Parser.js.map +0 -1
- package/dist/parser/nodeIsType.js +0 -4
- package/dist/parser/nodeIsType.js.map +0 -1
package/src/parser/AstNode.ts
CHANGED
|
@@ -36,39 +36,32 @@ Attr <-
|
|
|
36
36
|
import { nodeIsType } from './nodeIsType.js'
|
|
37
37
|
|
|
38
38
|
// ----------------------------------------------------------------------------
|
|
39
|
-
// AstNode
|
|
39
|
+
// MARK: AstNode
|
|
40
40
|
// ----------------------------------------------------------------------------
|
|
41
41
|
|
|
42
42
|
export const enum AstNodeType {
|
|
43
|
-
RootNode,
|
|
44
|
-
TextNode,
|
|
45
|
-
LinebreakNode,
|
|
46
|
-
TagNode,
|
|
47
|
-
StartTagNode,
|
|
48
|
-
EndTagNode,
|
|
49
|
-
AttrNode,
|
|
43
|
+
RootNode = 'RootNode',
|
|
44
|
+
TextNode = 'TextNode',
|
|
45
|
+
LinebreakNode = 'LinebreakNode',
|
|
46
|
+
TagNode = 'TagNode',
|
|
47
|
+
StartTagNode = 'StartTagNode',
|
|
48
|
+
EndTagNode = 'EndTagNode',
|
|
49
|
+
AttrNode = 'AttrNode',
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
export
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
case AstNodeType.LinebreakNode: return 'LinebreakNode'
|
|
57
|
-
case AstNodeType.TagNode: return 'TagNode'
|
|
58
|
-
case AstNodeType.StartTagNode: return 'StartTagNode'
|
|
59
|
-
case AstNodeType.EndTagNode: return 'EndTagNode'
|
|
60
|
-
case AstNodeType.AttrNode: return 'AttrNode'
|
|
61
|
-
}
|
|
52
|
+
export type AstNodeJson = {
|
|
53
|
+
type: AstNodeType
|
|
54
|
+
data?: Record<string, string | AstNodeJson>
|
|
55
|
+
children?: Array<AstNodeJson>
|
|
62
56
|
}
|
|
63
57
|
|
|
64
58
|
export abstract class AstNode {
|
|
65
59
|
readonly abstract nodeType: AstNodeType
|
|
66
60
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
this.children = children
|
|
61
|
+
constructor(
|
|
62
|
+
readonly children: Array<AstNode> = [],
|
|
63
|
+
) {
|
|
64
|
+
// nop
|
|
72
65
|
}
|
|
73
66
|
|
|
74
67
|
addChild(node: AstNode): void {
|
|
@@ -86,7 +79,7 @@ export abstract class AstNode {
|
|
|
86
79
|
}
|
|
87
80
|
|
|
88
81
|
toShortString(): string {
|
|
89
|
-
return
|
|
82
|
+
return this.nodeType
|
|
90
83
|
}
|
|
91
84
|
|
|
92
85
|
// For debugging purposes only
|
|
@@ -100,10 +93,22 @@ export abstract class AstNode {
|
|
|
100
93
|
|
|
101
94
|
return s
|
|
102
95
|
}
|
|
96
|
+
|
|
97
|
+
toJSON(): AstNodeJson {
|
|
98
|
+
const json: AstNodeJson = {
|
|
99
|
+
type: this.nodeType,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (this.children.length > 0) {
|
|
103
|
+
json.children = this.children.map((child) => child.toJSON())
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return json
|
|
107
|
+
}
|
|
103
108
|
}
|
|
104
109
|
|
|
105
110
|
// ----------------------------------------------------------------------------
|
|
106
|
-
// Root
|
|
111
|
+
// MARK: Root
|
|
107
112
|
// ----------------------------------------------------------------------------
|
|
108
113
|
|
|
109
114
|
export class RootNode extends AstNode {
|
|
@@ -123,7 +128,7 @@ export class RootNode extends AstNode {
|
|
|
123
128
|
}
|
|
124
129
|
|
|
125
130
|
// ----------------------------------------------------------------------------
|
|
126
|
-
// Text
|
|
131
|
+
// MARK: Text
|
|
127
132
|
// ----------------------------------------------------------------------------
|
|
128
133
|
|
|
129
134
|
export class TextNode extends AstNode {
|
|
@@ -142,6 +147,16 @@ export class TextNode extends AstNode {
|
|
|
142
147
|
override toShortString(): string {
|
|
143
148
|
return `${super.toShortString()} "${this.str}"`
|
|
144
149
|
}
|
|
150
|
+
|
|
151
|
+
override toJSON(): AstNodeJson {
|
|
152
|
+
const json = super.toJSON()
|
|
153
|
+
|
|
154
|
+
json.data = {
|
|
155
|
+
str: this.str,
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return json
|
|
159
|
+
}
|
|
145
160
|
}
|
|
146
161
|
|
|
147
162
|
export class LinebreakNode extends AstNode {
|
|
@@ -153,7 +168,100 @@ export class LinebreakNode extends AstNode {
|
|
|
153
168
|
}
|
|
154
169
|
|
|
155
170
|
// ----------------------------------------------------------------------------
|
|
156
|
-
//
|
|
171
|
+
// MARK: Attr
|
|
172
|
+
// ----------------------------------------------------------------------------
|
|
173
|
+
|
|
174
|
+
export class AttrNode extends AstNode {
|
|
175
|
+
readonly nodeType = AstNodeType.AttrNode
|
|
176
|
+
|
|
177
|
+
static readonly DEFAULT_KEY = 'default'
|
|
178
|
+
|
|
179
|
+
get key(): string {
|
|
180
|
+
switch (this.children.length) {
|
|
181
|
+
case 1: {
|
|
182
|
+
return AttrNode.DEFAULT_KEY
|
|
183
|
+
}
|
|
184
|
+
case 2: {
|
|
185
|
+
if (!nodeIsType(this.children[0], AstNodeType.TextNode)) {
|
|
186
|
+
throw new Error('Invalid TextNode')
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return this.children[0].str.trim()
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
throw new Error('Invalid AttrNode')
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
get val(): string {
|
|
197
|
+
switch (this.children.length) {
|
|
198
|
+
case 1: {
|
|
199
|
+
if (!nodeIsType(this.children[0], AstNodeType.TextNode)) {
|
|
200
|
+
throw new Error('Invalid TextNode')
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return this.children[0].str.trim()
|
|
204
|
+
}
|
|
205
|
+
case 2: {
|
|
206
|
+
if (!nodeIsType(this.children[1], AstNodeType.TextNode)) {
|
|
207
|
+
throw new Error('Invalid TextNode')
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return this.children[1].str.trim()
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
throw new Error('Invalid AttrNode')
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
override isValid(): boolean {
|
|
218
|
+
return super.isValid() && (this.children.length >= 1 && this.children.length <= 2)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
override toShortString(): string {
|
|
222
|
+
let s = super.toShortString()
|
|
223
|
+
|
|
224
|
+
switch (this.children.length) {
|
|
225
|
+
case 1: {
|
|
226
|
+
s += ` VAL="${this.val}"`
|
|
227
|
+
break
|
|
228
|
+
}
|
|
229
|
+
case 2: {
|
|
230
|
+
s += ` KEY="${this.key}" VAL="${this.val}"`
|
|
231
|
+
break
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return s
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
override toJSON(): AstNodeJson {
|
|
239
|
+
const json: AstNodeJson = {
|
|
240
|
+
type: this.nodeType,
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
switch (this.children.length) {
|
|
244
|
+
case 1: {
|
|
245
|
+
json.data = {
|
|
246
|
+
key: this.key,
|
|
247
|
+
}
|
|
248
|
+
break
|
|
249
|
+
}
|
|
250
|
+
case 2: {
|
|
251
|
+
json.data = {
|
|
252
|
+
key: this.key,
|
|
253
|
+
val: this.val,
|
|
254
|
+
}
|
|
255
|
+
break
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return json
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// ----------------------------------------------------------------------------
|
|
264
|
+
// MARK: Tag
|
|
157
265
|
// ----------------------------------------------------------------------------
|
|
158
266
|
|
|
159
267
|
export class StartTagNode extends AstNode {
|
|
@@ -180,6 +288,16 @@ export class StartTagNode extends AstNode {
|
|
|
180
288
|
override toShortString(): string {
|
|
181
289
|
return `${super.toShortString()} ${this.ogTag}`
|
|
182
290
|
}
|
|
291
|
+
|
|
292
|
+
override toJSON(): AstNodeJson {
|
|
293
|
+
const json = super.toJSON()
|
|
294
|
+
|
|
295
|
+
json.data = {
|
|
296
|
+
tag: this.tagName,
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return json
|
|
300
|
+
}
|
|
183
301
|
}
|
|
184
302
|
|
|
185
303
|
export class EndTagNode extends AstNode {
|
|
@@ -200,6 +318,16 @@ export class EndTagNode extends AstNode {
|
|
|
200
318
|
override toShortString(): string {
|
|
201
319
|
return `${super.toShortString()} ${this.ogTag}`
|
|
202
320
|
}
|
|
321
|
+
|
|
322
|
+
override toJSON(): AstNodeJson {
|
|
323
|
+
const json = super.toJSON()
|
|
324
|
+
|
|
325
|
+
json.data = {
|
|
326
|
+
tag: this.tagName,
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return json
|
|
330
|
+
}
|
|
203
331
|
}
|
|
204
332
|
|
|
205
333
|
export class TagNode extends AstNode {
|
|
@@ -266,73 +394,18 @@ export class TagNode extends AstNode {
|
|
|
266
394
|
|
|
267
395
|
return s
|
|
268
396
|
}
|
|
269
|
-
}
|
|
270
397
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
// ----------------------------------------------------------------------------
|
|
398
|
+
override toJSON(): AstNodeJson {
|
|
399
|
+
const json = super.toJSON()
|
|
274
400
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
static readonly DEFAULT_KEY = 'default'
|
|
279
|
-
|
|
280
|
-
get key(): string {
|
|
281
|
-
switch (this.children.length) {
|
|
282
|
-
case 1: {
|
|
283
|
-
return AttrNode.DEFAULT_KEY
|
|
284
|
-
}
|
|
285
|
-
case 2: {
|
|
286
|
-
if (!nodeIsType(this.children[0], AstNodeType.TextNode)) {
|
|
287
|
-
throw new Error('Invalid TextNode')
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
return this.children[0].str.trim()
|
|
291
|
-
}
|
|
401
|
+
json.data = {
|
|
402
|
+
startTag: this._startTag.toJSON(),
|
|
292
403
|
}
|
|
293
404
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
get val(): string {
|
|
298
|
-
switch (this.children.length) {
|
|
299
|
-
case 1: {
|
|
300
|
-
if (!nodeIsType(this.children[0], AstNodeType.TextNode)) {
|
|
301
|
-
throw new Error('Invalid TextNode')
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
return this.children[0].str.trim()
|
|
305
|
-
}
|
|
306
|
-
case 2: {
|
|
307
|
-
if (!nodeIsType(this.children[1], AstNodeType.TextNode)) {
|
|
308
|
-
throw new Error('Invalid TextNode')
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
return this.children[1].str.trim()
|
|
312
|
-
}
|
|
405
|
+
if (this._endTag) {
|
|
406
|
+
json.data.endTag = this._endTag.toJSON()
|
|
313
407
|
}
|
|
314
408
|
|
|
315
|
-
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
override isValid(): boolean {
|
|
319
|
-
return super.isValid() && (this.children.length >= 1 && this.children.length <= 2)
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
override toShortString(): string {
|
|
323
|
-
let s = super.toShortString()
|
|
324
|
-
|
|
325
|
-
switch (this.children.length) {
|
|
326
|
-
case 1: {
|
|
327
|
-
s += ` VAL="${this.val}"`
|
|
328
|
-
break
|
|
329
|
-
}
|
|
330
|
-
case 2: {
|
|
331
|
-
s += ` KEY="${this.key}" VAL="${this.val}"`
|
|
332
|
-
break
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
return s
|
|
409
|
+
return json
|
|
337
410
|
}
|
|
338
411
|
}
|
package/src/parser/Parser.ts
CHANGED
|
@@ -18,109 +18,6 @@ export class Parser {
|
|
|
18
18
|
parse(ogText: string, tokens: Array<Token>): RootNode {
|
|
19
19
|
let idx = 0
|
|
20
20
|
|
|
21
|
-
const parseRoot = (): RootNode => {
|
|
22
|
-
const root = new RootNode()
|
|
23
|
-
|
|
24
|
-
while (idx < tokens.length) {
|
|
25
|
-
if (tokens[idx].type === TokenType.L_BRACKET) {
|
|
26
|
-
const startIdx = idx
|
|
27
|
-
const tagNode = parseTag()
|
|
28
|
-
|
|
29
|
-
if (tagNode !== null) {
|
|
30
|
-
root.addChild(tagNode)
|
|
31
|
-
} else {
|
|
32
|
-
const invalidTokens = tokens.slice(startIdx, idx)
|
|
33
|
-
const str = stringifyTokens(ogText, invalidTokens)
|
|
34
|
-
const textNode = new TextNode(str)
|
|
35
|
-
root.addChild(textNode)
|
|
36
|
-
}
|
|
37
|
-
} else if (tokens[idx].type === TokenType.LINEBREAK) {
|
|
38
|
-
idx += 1 // Consume LINEBREAK
|
|
39
|
-
root.addChild(new LinebreakNode())
|
|
40
|
-
} else {
|
|
41
|
-
const startIdx = idx
|
|
42
|
-
|
|
43
|
-
// Advance until we see the start of another RootNode's child (TagNode or LinebreakNode)
|
|
44
|
-
while (idx < tokens.length && tokens[idx].type !== TokenType.L_BRACKET && tokens[idx].type !== TokenType.LINEBREAK) {
|
|
45
|
-
idx += 1
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const slice = tokens.slice(startIdx, idx)
|
|
49
|
-
const str = stringifyTokens(ogText, slice)
|
|
50
|
-
root.addChild(new TextNode(str))
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return root
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const parseTag = (): StartTagNode | EndTagNode | null => {
|
|
58
|
-
if (idx + 1 >= tokens.length) {
|
|
59
|
-
return null
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (tokens[idx].type !== TokenType.L_BRACKET) {
|
|
63
|
-
return null
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// If L_BRACKET is followed by text, then it must be StartTag or is invalid
|
|
67
|
-
if (isStringToken(tokens[idx + 1].type)) {
|
|
68
|
-
const startIdx = idx
|
|
69
|
-
idx += 1 // Consume L_BRACKET
|
|
70
|
-
|
|
71
|
-
const labelText = parseLabel()
|
|
72
|
-
if (!this.tags.has(labelText)) {
|
|
73
|
-
return null
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const attrNodes = new Array<AttrNode>()
|
|
77
|
-
while (true) {
|
|
78
|
-
const attrNode = parseAttr()
|
|
79
|
-
if (attrNode === null) {
|
|
80
|
-
break
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
attrNodes.push(attrNode)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (tokens[idx].type !== TokenType.R_BRACKET) {
|
|
87
|
-
return null
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
idx += 1 // Consume R_BRACKET
|
|
91
|
-
|
|
92
|
-
const slice = tokens.slice(startIdx, idx)
|
|
93
|
-
const ogTag = stringifyTokens(ogText, slice)
|
|
94
|
-
const startTagNode = new StartTagNode(labelText, ogTag, attrNodes)
|
|
95
|
-
return startTagNode
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// If L_BRACKET is followed by BACKSLASH, then it must be EndTag or is invalid
|
|
99
|
-
if (tokens[idx + 1].type === TokenType.BACKSLASH) {
|
|
100
|
-
const startIdx = idx
|
|
101
|
-
idx += 1 // Consume L_BRACKET
|
|
102
|
-
idx += 1 // Consume BACKSLASH
|
|
103
|
-
|
|
104
|
-
const labelText = parseLabel()
|
|
105
|
-
if (!this.tags.has(labelText)) {
|
|
106
|
-
return null
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (tokens[idx].type !== TokenType.R_BRACKET) {
|
|
110
|
-
return null
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
idx += 1 // Consume R_BRACKET
|
|
114
|
-
|
|
115
|
-
const slice = tokens.slice(startIdx, idx)
|
|
116
|
-
const ogTag = stringifyTokens(ogText, slice)
|
|
117
|
-
const endTagNode = new EndTagNode(labelText, ogTag)
|
|
118
|
-
return endTagNode
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return null
|
|
122
|
-
}
|
|
123
|
-
|
|
124
21
|
const parseLabel = (): string => {
|
|
125
22
|
const slice = tokens.slice(idx, idx + 1)
|
|
126
23
|
const label = stringifyTokens(ogText, slice)
|
|
@@ -243,6 +140,109 @@ export class Parser {
|
|
|
243
140
|
return attrNode
|
|
244
141
|
}
|
|
245
142
|
|
|
143
|
+
const parseTag = (): StartTagNode | EndTagNode | null => {
|
|
144
|
+
if (idx + 1 >= tokens.length) {
|
|
145
|
+
return null
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (tokens[idx].type !== TokenType.L_BRACKET) {
|
|
149
|
+
return null
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// If L_BRACKET is followed by text, then it must be StartTag or is invalid
|
|
153
|
+
if (isStringToken(tokens[idx + 1].type)) {
|
|
154
|
+
const startIdx = idx
|
|
155
|
+
idx += 1 // Consume L_BRACKET
|
|
156
|
+
|
|
157
|
+
const labelText = parseLabel()
|
|
158
|
+
if (!this.tags.has(labelText)) {
|
|
159
|
+
return null
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const attrNodes = new Array<AttrNode>()
|
|
163
|
+
while (true) {
|
|
164
|
+
const attrNode = parseAttr()
|
|
165
|
+
if (attrNode === null) {
|
|
166
|
+
break
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
attrNodes.push(attrNode)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (tokens[idx].type !== TokenType.R_BRACKET) {
|
|
173
|
+
return null
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
idx += 1 // Consume R_BRACKET
|
|
177
|
+
|
|
178
|
+
const slice = tokens.slice(startIdx, idx)
|
|
179
|
+
const ogTag = stringifyTokens(ogText, slice)
|
|
180
|
+
const startTagNode = new StartTagNode(labelText, ogTag, attrNodes)
|
|
181
|
+
return startTagNode
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// If L_BRACKET is followed by BACKSLASH, then it must be EndTag or is invalid
|
|
185
|
+
if (tokens[idx + 1].type === TokenType.BACKSLASH) {
|
|
186
|
+
const startIdx = idx
|
|
187
|
+
idx += 1 // Consume L_BRACKET
|
|
188
|
+
idx += 1 // Consume BACKSLASH
|
|
189
|
+
|
|
190
|
+
const labelText = parseLabel()
|
|
191
|
+
if (!this.tags.has(labelText)) {
|
|
192
|
+
return null
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (tokens[idx].type !== TokenType.R_BRACKET) {
|
|
196
|
+
return null
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
idx += 1 // Consume R_BRACKET
|
|
200
|
+
|
|
201
|
+
const slice = tokens.slice(startIdx, idx)
|
|
202
|
+
const ogTag = stringifyTokens(ogText, slice)
|
|
203
|
+
const endTagNode = new EndTagNode(labelText, ogTag)
|
|
204
|
+
return endTagNode
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return null
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const parseRoot = (): RootNode => {
|
|
211
|
+
const root = new RootNode()
|
|
212
|
+
|
|
213
|
+
while (idx < tokens.length) {
|
|
214
|
+
if (tokens[idx].type === TokenType.L_BRACKET) {
|
|
215
|
+
const startIdx = idx
|
|
216
|
+
const tagNode = parseTag()
|
|
217
|
+
|
|
218
|
+
if (tagNode !== null) {
|
|
219
|
+
root.addChild(tagNode)
|
|
220
|
+
} else {
|
|
221
|
+
const invalidTokens = tokens.slice(startIdx, idx)
|
|
222
|
+
const str = stringifyTokens(ogText, invalidTokens)
|
|
223
|
+
const textNode = new TextNode(str)
|
|
224
|
+
root.addChild(textNode)
|
|
225
|
+
}
|
|
226
|
+
} else if (tokens[idx].type === TokenType.LINEBREAK) {
|
|
227
|
+
idx += 1 // Consume LINEBREAK
|
|
228
|
+
root.addChild(new LinebreakNode())
|
|
229
|
+
} else {
|
|
230
|
+
const startIdx = idx
|
|
231
|
+
|
|
232
|
+
// Advance until we see the start of another RootNode's child (TagNode or LinebreakNode)
|
|
233
|
+
while (idx < tokens.length && tokens[idx].type !== TokenType.L_BRACKET && tokens[idx].type !== TokenType.LINEBREAK) {
|
|
234
|
+
idx += 1
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const slice = tokens.slice(startIdx, idx)
|
|
238
|
+
const str = stringifyTokens(ogText, slice)
|
|
239
|
+
root.addChild(new TextNode(str))
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return root
|
|
244
|
+
}
|
|
245
|
+
|
|
246
246
|
let root = parseRoot()
|
|
247
247
|
root = this.matchTagNodes(root)
|
|
248
248
|
return root
|
package/dist/generateHtml.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Generator } from './generator/Generator.js';
|
|
2
|
-
import { htmlTransforms } from './generator/transforms/htmlTransforms.js';
|
|
3
|
-
import { Lexer } from './lexer/Lexer.js';
|
|
4
|
-
import { Parser } from './parser/Parser.js';
|
|
5
|
-
export function generateHtml(input, transforms = htmlTransforms) {
|
|
6
|
-
const lexer = new Lexer();
|
|
7
|
-
const tokens = lexer.tokenize(input);
|
|
8
|
-
const parser = new Parser(transforms);
|
|
9
|
-
const root = parser.parse(input, tokens);
|
|
10
|
-
const generator = new Generator(transforms);
|
|
11
|
-
return generator.generate(root);
|
|
12
|
-
}
|
|
13
|
-
//# sourceMappingURL=generateHtml.js.map
|
package/dist/generateHtml.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"generateHtml.js","sourceRoot":"","sources":["../src/generateHtml.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAA;AACzE,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAE3C,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,UAAU,GAAG,cAAc;IACnE,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAA;IACzB,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAEpC,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,UAAU,CAAC,CAAA;IACrC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IAExC,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,UAAU,CAAC,CAAA;IAC3C,OAAO,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;AACnC,CAAC"}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { nodeIsType } from '../parser/nodeIsType.js';
|
|
2
|
-
import { htmlTransforms } from './transforms/htmlTransforms.js';
|
|
3
|
-
export class Generator {
|
|
4
|
-
transforms;
|
|
5
|
-
constructor(transforms = htmlTransforms) {
|
|
6
|
-
this.transforms = new Map(transforms.map((transform) => [transform.name, transform]));
|
|
7
|
-
}
|
|
8
|
-
generate(root) {
|
|
9
|
-
const stringify = (node) => {
|
|
10
|
-
let output = '';
|
|
11
|
-
if (nodeIsType(node, 3 /* AstNodeType.TagNode */)) {
|
|
12
|
-
const tagName = node.tagName;
|
|
13
|
-
const transform = this.transforms.get(tagName);
|
|
14
|
-
if (!transform) {
|
|
15
|
-
throw new Error(`Unrecognized bbcode ${node.tagName}`);
|
|
16
|
-
}
|
|
17
|
-
const renderedStartTag = transform.start(node);
|
|
18
|
-
const renderedEndTag = transform.end?.(node) ?? '';
|
|
19
|
-
const isInvalidTag = renderedStartTag === false;
|
|
20
|
-
if (isInvalidTag) {
|
|
21
|
-
output += node.ogStartTag;
|
|
22
|
-
}
|
|
23
|
-
else {
|
|
24
|
-
output += renderedStartTag;
|
|
25
|
-
}
|
|
26
|
-
if (!transform.skipChildren || isInvalidTag) {
|
|
27
|
-
for (const child of node.children) {
|
|
28
|
-
output += stringify(child);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
if (isInvalidTag) {
|
|
32
|
-
output += node.ogEndTag;
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
output += renderedEndTag;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
else if (nodeIsType(node, 1 /* AstNodeType.TextNode */)) {
|
|
39
|
-
output += node.str;
|
|
40
|
-
}
|
|
41
|
-
else if (nodeIsType(node, 2 /* AstNodeType.LinebreakNode */)) {
|
|
42
|
-
output += '\n';
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
for (const child of node.children) {
|
|
46
|
-
output += stringify(child);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
return output;
|
|
50
|
-
};
|
|
51
|
-
return stringify(root);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
//# sourceMappingURL=Generator.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Generator.js","sourceRoot":"","sources":["../../src/generator/Generator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAG/D,MAAM,OAAO,SAAS;IAClB,UAAU,CAAgC;IAE1C,YAAY,UAAU,GAAG,cAAc;QACnC,IAAI,CAAC,UAAU,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;IACzF,CAAC;IAED,QAAQ,CAAC,IAAc;QACnB,MAAM,SAAS,GAAG,CAAC,IAAa,EAAU,EAAE;YACxC,IAAI,MAAM,GAAG,EAAE,CAAA;YAEf,IAAI,UAAU,CAAC,IAAI,8BAAsB,EAAE,CAAC;gBACxC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;gBAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;gBAC9C,IAAI,CAAC,SAAS,EAAE,CAAC;oBACb,MAAM,IAAI,KAAK,CAAC,uBAAuB,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;gBAC1D,CAAC;gBAED,MAAM,gBAAgB,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;gBAC9C,MAAM,cAAc,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAA;gBAClD,MAAM,YAAY,GAAG,gBAAgB,KAAK,KAAK,CAAA;gBAE/C,IAAI,YAAY,EAAE,CAAC;oBACf,MAAM,IAAI,IAAI,CAAC,UAAU,CAAA;gBAC7B,CAAC;qBAAM,CAAC;oBACJ,MAAM,IAAI,gBAAgB,CAAA;gBAC9B,CAAC;gBAED,IAAI,CAAC,SAAS,CAAC,YAAY,IAAI,YAAY,EAAE,CAAC;oBAC1C,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;wBAChC,MAAM,IAAI,SAAS,CAAC,KAAK,CAAC,CAAA;oBAC9B,CAAC;gBACL,CAAC;gBAED,IAAI,YAAY,EAAE,CAAC;oBACf,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAA;gBAC3B,CAAC;qBAAM,CAAC;oBACJ,MAAM,IAAI,cAAc,CAAA;gBAC5B,CAAC;YACL,CAAC;iBAAM,IAAI,UAAU,CAAC,IAAI,+BAAuB,EAAE,CAAC;gBAChD,MAAM,IAAI,IAAI,CAAC,GAAG,CAAA;YACtB,CAAC;iBAAM,IAAI,UAAU,CAAC,IAAI,oCAA4B,EAAE,CAAC;gBACrD,MAAM,IAAI,IAAI,CAAA;YAClB,CAAC;iBAAM,CAAC;gBACJ,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;oBAChC,MAAM,IAAI,SAAS,CAAC,KAAK,CAAC,CAAA;gBAC9B,CAAC;YACL,CAAC;YAED,OAAO,MAAM,CAAA;QACjB,CAAC,CAAA;QAED,OAAO,SAAS,CAAC,IAAI,CAAC,CAAA;IAC1B,CAAC;CACJ"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Transform.js","sourceRoot":"","sources":["../../../src/generator/transforms/Transform.ts"],"names":[],"mappings":""}
|