-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathspanner.js
126 lines (108 loc) · 3.05 KB
/
spanner.js
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
const isNumber = n => n >= '0' && n <= '9'
const isAlpha = n => n >= 'a' && n <= 'z' || n >= 'A' && n <= 'Z'
const isAlphanumeric = n => isNumber(n) || isAlpha(n)
const isDef = n => isNumber(n) || isAlpha(n) || n == '_'
const isOperation = n => n == '/' || n == '+' || n == '-' || n == '('
const isTimezone = n => isAlpha(n) || n == '_' || n == '/'
const isWhitespace = n => n == ' ' || n == '\t' || n == '\n'
const isDecimal = n => n == '.'
export default (anchor, s, tz, vars) => {
let i = 0
const readwhitespace = () => {
while (i < s.length && isWhitespace(s[i])) i++
}
const readdef = () => {
let n = 0
while (i + n < s.length && isDef(s[i+n])) n++
const res = s.substr(i, n)
i += n
return res
}
const readalpha = () => {
let n = 0
while (i + n < s.length && isAlpha(s[i+n])) n++
const res = s.substr(i, n)
i += n
return res
}
const readnumber = () => {
let n = 0
while (i + n < s.length && isNumber(s[i+n])) n++
if (i + n < s.length && isDecimal(s[i+n])) {
n++
while (i + n < s.length && isNumber(s[i+n])) n++
}
const res = s.substr(i, n)
i += n
return res
}
const readtimezone = () => {
let n = 0
while (i + n < s.length && isTimezone(s[i+n])) n++
const res = s.substr(i, n)
i += n
return res
}
const readduration = () => {
readwhitespace()
let value = readnumber()
if (value == '') value = 1
readwhitespace()
const unit = readalpha()
readwhitespace()
return [+value, unit]
}
readwhitespace()
// could start with a timezone
if (i < s.length && s[i] == '(') {
i++
tz = readtimezone()
if (s[i] != ')') throw new Error('Expecting closing ) on timezone')
i++
}
readwhitespace()
anchor = anchor.clone()
if (tz) anchor = anchor.tz(tz)
// could start with 'now' or another variable
if (i < s.length && isAlpha(s[i])) {
const variable = readdef()
if (variable != 'now') { // now is the default
if (!vars || !vars[variable])
throw new Error(`Variable ${variable} not known`)
let f = vars[variable]
if (typeof f == 'function') f = f(tz)
anchor = f.clone()
}
}
else if (i < s.length && isNumber(s[i])) {
while (i < s.length && isAlphanumeric(s[i]))
anchor = anchor.add.apply(anchor, readduration())
}
readwhitespace()
while (i < s.length && isOperation(s[i])) {
if (s[i] == '/') {
i++
anchor = anchor.startOf(readalpha())
}
else if (s[i] == '+') {
i++
while (i < s.length && isAlphanumeric(s[i]))
anchor = anchor.add.apply(anchor, readduration())
}
else if (s[i] == '-') {
i++
while (i < s.length && isAlphanumeric(s[i]))
anchor = anchor.subtract.apply(anchor, readduration())
}
else if (s[i] == '(') {
i++
tz = readtimezone()
anchor = anchor.tz(tz)
if (s[i] != ')') throw new Error('Expecting closing ) on timezone')
i++
}
readwhitespace()
}
if (i < s.length) throw new Error(`unknown format ${i} < ${s.length}`)
return anchor
}