-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparseJevkoWithHeredocs.js
More file actions
89 lines (86 loc) · 2.71 KB
/
parseJevkoWithHeredocs.js
File metadata and controls
89 lines (86 loc) · 2.71 KB
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
// todo: perhaps rename tag -> identifier/id
export const parseJevkoWithHeredocs = (str, {
opener = '[',
closer = ']',
escaper = '`',
quoter = "'",
} = {}) => {
const delimiters = [opener, closer, escaper, quoter]
const delimiterSetSize = new Set(delimiters).size
if (delimiterSetSize !== delimiters.length) {
throw Error(`Delimiters must be unique! ${delimiters.length - delimiterSetSize} of them are identical:\n${delimiters.join('\n')}`)
}
const parents = []
let parent = {subjevkos: []}, prefix = '', h = 0, mode = 'normal'
let line = 1, column = 1
let tag = '', t = 0
for (let i = 0; i < str.length; ++i) {
const c = str[i]
if (mode === 'escaped') {
if (c === escaper || c === opener || c === closer) mode = 'normal'
else if (c === quoter) {
mode = 'tag'
t = i + 1
} else throw SyntaxError(`Invalid digraph (${escaper}${c}) at ${line}:${column}!`)
} else if (mode === 'tag') {
if (c === quoter) {
tag = str.slice(t, i)
h = i + 1
t = h
mode = 'block'
}
} else if (mode === 'block') {
if (c === quoter) {
const found = str.slice(h, i)
if (found === tag) {
const jevko = {
subjevkos: [],
suffix: str.slice(t, h - 1),
tag
}
parent.subjevkos.push({prefix, jevko})
prefix = ''
h = i + 1
tag = ''
mode = 'normal'
} else {
h = i + 1
}
}
} else /*if (mode === 'normal')*/ if (c === escaper) {
prefix += str.slice(h, i)
h = i + 1
mode = 'escaped'
} else if (c === opener) {
const jevko = {subjevkos: []}
parent.subjevkos.push({prefix: prefix + str.slice(h, i), jevko})
prefix = ''
h = i + 1
parents.push(parent)
parent = jevko
} else if (c === closer) {
parent.suffix = prefix + str.slice(h, i)
prefix = ''
h = i + 1
if (parents.length < 1) throw SyntaxError(`Unexpected closer (${closer}) at ${line}:${column}!`)
parent = parents.pop()
}
if (c === '\n') {
++line
column = 1
} else {
++column
}
}
// todo: better error msgs
if (mode === 'escaped') throw SyntaxError(`Unexpected end after escaper (${escaper})!`)
if (mode === 'tag') throw SyntaxError(`Unexpected end after quoter (${quoter})!`)
if (mode === 'block') throw SyntaxError(`Unexpected end after quoter (${quoter})!`)
if (parents.length > 0) throw SyntaxError(`Unexpected end: missing ${parents.length} closer(s) (${closer})!`)
parent.suffix = prefix + str.slice(h)
parent.opener = opener
parent.closer = closer
parent.escaper = escaper
parent.quoter = quoter
return parent
}