functionalscript 0.0.497 → 0.0.499
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/fsc/module.f.cjs +41 -37
- package/fsc/test.f.cjs +1 -1
- package/json/parser/module.f.cjs +235 -0
- package/json/parser/test.f.cjs +240 -0
- package/json/tokenizer/module.f.cjs +16 -9
- package/package.json +2 -2
package/fsc/module.f.cjs
CHANGED
|
@@ -12,7 +12,7 @@ const { toArray, map } = list
|
|
|
12
12
|
|
|
13
13
|
/** @typedef {readonly[readonly string[], ToResult]} Result */
|
|
14
14
|
|
|
15
|
-
/** @typedef {(
|
|
15
|
+
/** @typedef {(codePoint: number) => Result} ToResult */
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* @template T
|
|
@@ -25,10 +25,10 @@ const { toArray, map } = list
|
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
/** @type {ToResult} */
|
|
28
|
-
const
|
|
28
|
+
const unexpectedSymbol = codePoint => [[`unexpected symbol ${codePoint}`], unexpectedSymbol]
|
|
29
29
|
|
|
30
|
-
/** @type {<T>(
|
|
31
|
-
const def = () =>
|
|
30
|
+
/** @type {<T>(state: T) => ToResult} */
|
|
31
|
+
const def = () => unexpectedSymbol
|
|
32
32
|
|
|
33
33
|
/** @type {<T>(a: CreateToResult<T>) => (b: CreateToResult<T>) => CreateToResult<T>} */
|
|
34
34
|
const union = a => b => {
|
|
@@ -77,42 +77,46 @@ const create = a => {
|
|
|
77
77
|
|
|
78
78
|
const terminal = -1
|
|
79
79
|
|
|
80
|
+
/** @type {() => ToResult} */
|
|
81
|
+
const toInit = () => () => [[], init]
|
|
82
|
+
|
|
80
83
|
const init = create([
|
|
81
|
-
codePointRange(one(terminal))(
|
|
82
|
-
rangeSet(['\t', ' '
|
|
83
|
-
|
|
84
|
-
range('
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
range('
|
|
88
|
-
range('
|
|
89
|
-
range(
|
|
90
|
-
range('
|
|
91
|
-
range('
|
|
92
|
-
range('
|
|
93
|
-
range('
|
|
94
|
-
range('
|
|
95
|
-
range('
|
|
96
|
-
range('
|
|
97
|
-
range('
|
|
98
|
-
range('
|
|
99
|
-
range('
|
|
100
|
-
range('
|
|
101
|
-
range('
|
|
102
|
-
range('
|
|
103
|
-
range('
|
|
104
|
-
range('
|
|
105
|
-
range('
|
|
106
|
-
range('
|
|
107
|
-
range('
|
|
108
|
-
range('
|
|
109
|
-
range('
|
|
110
|
-
range('
|
|
111
|
-
range('
|
|
112
|
-
|
|
113
|
-
])
|
|
84
|
+
codePointRange(one(terminal))(toInit),
|
|
85
|
+
rangeSet(['\t', ' ', '\n', '\r'])(toInit),
|
|
86
|
+
range('!')(() => () => [['!'], unexpectedSymbol]),
|
|
87
|
+
range('"')(() => () => [['"'], unexpectedSymbol]),
|
|
88
|
+
rangeSet(['$', '_', 'AZ', 'az'])(() => c => [[fromCharCode(c)], unexpectedSymbol]),
|
|
89
|
+
range('%')(() => () => [['%'], unexpectedSymbol]),
|
|
90
|
+
range('&')(() => () => [['&'], unexpectedSymbol]),
|
|
91
|
+
range("'")(() => () => [["'"], unexpectedSymbol]),
|
|
92
|
+
range('(')(() => () => [['('], unexpectedSymbol]),
|
|
93
|
+
range(')')(() => () => [[')'], unexpectedSymbol]),
|
|
94
|
+
range('*')(() => () => [['*'], unexpectedSymbol]),
|
|
95
|
+
range('+')(() => () => [['+'], unexpectedSymbol]),
|
|
96
|
+
range(',')(() => () => [[','], unexpectedSymbol]),
|
|
97
|
+
range('-')(() => () => [['-'], unexpectedSymbol]),
|
|
98
|
+
range('.')(() => () => [['.'], unexpectedSymbol]),
|
|
99
|
+
range('/')(() => () => [['/'], unexpectedSymbol]),
|
|
100
|
+
range('09')(() => a => [[fromCharCode(a)], unexpectedSymbol]),
|
|
101
|
+
range(':')(() => () => [[':'], unexpectedSymbol]),
|
|
102
|
+
range(';')(() => () => [[';'], unexpectedSymbol]),
|
|
103
|
+
range('<')(() => () => [['<'], unexpectedSymbol]),
|
|
104
|
+
range('=')(() => () => [['='], unexpectedSymbol]),
|
|
105
|
+
range('>')(() => () => [['>'], unexpectedSymbol]),
|
|
106
|
+
range('?')(() => () => [['?'], unexpectedSymbol]),
|
|
107
|
+
range('[')(() => () => [['['], unexpectedSymbol]),
|
|
108
|
+
range(']')(() => () => [[']'], unexpectedSymbol]),
|
|
109
|
+
range('^')(() => () => [['^'], unexpectedSymbol]),
|
|
110
|
+
range('`')(() => () => [['`'], unexpectedSymbol]),
|
|
111
|
+
range('{')(() => () => [['{'], unexpectedSymbol]),
|
|
112
|
+
range('|')(() => () => [['|'], unexpectedSymbol]),
|
|
113
|
+
range('}')(() => () => [['}'], unexpectedSymbol]),
|
|
114
|
+
range('~')(() => () => [['~'], unexpectedSymbol]),
|
|
115
|
+
])(void 0)
|
|
114
116
|
|
|
115
117
|
module.exports = {
|
|
118
|
+
/** @readonly */
|
|
119
|
+
terminal,
|
|
116
120
|
/** @readonly */
|
|
117
121
|
init,
|
|
118
122
|
}
|
package/fsc/test.f.cjs
CHANGED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
const result = require('../../types/result/module.f.cjs')
|
|
2
|
+
const list = require('../../types/list/module.f.cjs')
|
|
3
|
+
const { fold, isEmpty, first, drop, toArray } = list
|
|
4
|
+
const operator = require('../../types/function/operator/module.f.cjs')
|
|
5
|
+
const tokenizer = require('../tokenizer/module.f.cjs')
|
|
6
|
+
const map = require('../../types/map/module.f.cjs')
|
|
7
|
+
const { entries, setReplace } = map
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {{
|
|
12
|
+
* readonly kind: 'object'
|
|
13
|
+
* readonly values: map.Map<any>
|
|
14
|
+
* readonly key: string
|
|
15
|
+
* }} JsonObject
|
|
16
|
+
* */
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {{
|
|
20
|
+
* readonly kind: 'array'
|
|
21
|
+
* readonly values: list.List<any>
|
|
22
|
+
* }} JsonArray
|
|
23
|
+
* */
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {|
|
|
27
|
+
* JsonObject |
|
|
28
|
+
* JsonArray
|
|
29
|
+
* } JsonStackElement
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/** @typedef {list.List<JsonStackElement>} JsonStack */
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {{
|
|
36
|
+
* readonly status: '' | '[' | '[v' | '[,' | '{' | '{k' | '{:' | '{v' | '{,'
|
|
37
|
+
* readonly top: JsonStackElement | null
|
|
38
|
+
* readonly stack: JsonStack
|
|
39
|
+
* }} StateParse
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @typedef {{
|
|
44
|
+
* readonly status: 'result'
|
|
45
|
+
* readonly value: any
|
|
46
|
+
* }} StateResult
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @typedef {{
|
|
51
|
+
* readonly status: 'error'
|
|
52
|
+
* readonly message: string
|
|
53
|
+
* }} StateError
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @typedef {|
|
|
58
|
+
* StateParse |
|
|
59
|
+
* StateResult |
|
|
60
|
+
* StateError
|
|
61
|
+
* } JsonState
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
/** @type {(obj: JsonObject) => (key: string) => JsonObject} */
|
|
65
|
+
const addKeyToObject = obj => key => ({ kind: 'object', values: obj.values, key: key })
|
|
66
|
+
|
|
67
|
+
/** @type {(obj: JsonObject) => (value: any) => JsonObject} */
|
|
68
|
+
const addValueToObject = obj => value => ({ kind: 'object', values: setReplace(obj.key)(value)(obj.values), key: '' })
|
|
69
|
+
|
|
70
|
+
/** @type {(array: JsonArray) => (value: any) => JsonArray} */
|
|
71
|
+
const addToArray = array => value => ({ kind: 'array', values: list.concat(array.values)([value]) })
|
|
72
|
+
|
|
73
|
+
/** @type {(state: StateParse) => (key: string) => JsonState} */
|
|
74
|
+
const pushKey = state => value => {
|
|
75
|
+
if (state.top?.kind === 'object') { return { status: '{k', top: addKeyToObject(state.top)(value), stack: state.stack } }
|
|
76
|
+
return { status: 'error', message: 'error' }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** @type {(state: StateParse) => (value: any) => JsonState} */
|
|
80
|
+
const pushValue = state => value => {
|
|
81
|
+
if (state.top === null) { return { status: 'result', value: value }}
|
|
82
|
+
if (state.top.kind === 'array') { return { status: '[v', top: addToArray(state.top)(value), stack: state.stack } }
|
|
83
|
+
return { status: '{v', top: addValueToObject(state.top)(value), stack: state.stack }
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** @type {(state: StateParse) => JsonState} */
|
|
87
|
+
const startArray = state => {
|
|
88
|
+
const newStack = state.top === null ? null : { first: state.top, tail: state.stack }
|
|
89
|
+
return { status: '[', top: { kind: 'array', values: null }, stack: newStack }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** @type {(state: StateParse) => JsonState} */
|
|
93
|
+
const endArray = state => {
|
|
94
|
+
const array = state.top !== null ? toArray(state.top.values) : null
|
|
95
|
+
/** @type {StateParse} */
|
|
96
|
+
const newState = { status: '', top: first(null)(state.stack), stack: drop(1)(state.stack) }
|
|
97
|
+
return pushValue(newState)(array)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** @type {(state: StateParse) => JsonState} */
|
|
101
|
+
const startObject = state => {
|
|
102
|
+
const newStack = state.top === null ? null : { first: state.top, tail: state.stack }
|
|
103
|
+
return { status: '{', top: { kind: 'object', values: null, key: '' }, stack: newStack }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** @type {operator.Fold<map.Entry<any>, any>} */
|
|
107
|
+
const foldToObjectOp = ([k, v]) => obj => { return { ...obj, [k] : v } }
|
|
108
|
+
|
|
109
|
+
/** @type {(m: map.Map<any>) => any} */
|
|
110
|
+
const toObject = m => {
|
|
111
|
+
const e = entries(m)
|
|
112
|
+
return fold(foldToObjectOp)({})(e)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** @type {(state: StateParse) => JsonState} */
|
|
116
|
+
const endObject = state => {
|
|
117
|
+
const obj = state.top?.kind === 'object' ? toObject(state.top.values) : null
|
|
118
|
+
/** @type {StateParse} */
|
|
119
|
+
const newState = { status: '', top: first(null)(state.stack), stack: drop(1)(state.stack) }
|
|
120
|
+
return pushValue(newState)(obj)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** @type {(token: tokenizer.JsonToken) => any} */
|
|
124
|
+
const tokenToValue = token => {
|
|
125
|
+
switch(token.kind) {
|
|
126
|
+
case 'null': return null
|
|
127
|
+
case 'false': return false
|
|
128
|
+
case 'true': return true
|
|
129
|
+
case 'number': return Number(token.value)
|
|
130
|
+
case 'string': return token.value
|
|
131
|
+
default: return null
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** @type {(token: tokenizer.JsonToken) => boolean} */
|
|
136
|
+
const isValueToken = token => {
|
|
137
|
+
switch(token.kind) {
|
|
138
|
+
case 'null':
|
|
139
|
+
case 'false':
|
|
140
|
+
case 'true':
|
|
141
|
+
case 'number':
|
|
142
|
+
case 'string': return true
|
|
143
|
+
default: return false
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** @type {(token: tokenizer.JsonToken) => (state: StateParse) => JsonState}} */
|
|
148
|
+
const parseValueOp = token => state => {
|
|
149
|
+
if (isValueToken(token)) { return pushValue(state)(tokenToValue(token)) }
|
|
150
|
+
if (token.kind === '[') { return startArray(state) }
|
|
151
|
+
if (token.kind === '{') { return startObject(state) }
|
|
152
|
+
return { status: 'error', message: 'unexpected token' }
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** @type {(token: tokenizer.JsonToken) => (state: StateParse) => JsonState}} */
|
|
156
|
+
const parseArrayStartOp = token => state => {
|
|
157
|
+
if (isValueToken(token)) { return pushValue(state)(tokenToValue(token)) }
|
|
158
|
+
if (token.kind === '[') { return startArray(state) }
|
|
159
|
+
if (token.kind === ']') { return endArray(state) }
|
|
160
|
+
if (token.kind === '{') { return startObject(state) }
|
|
161
|
+
return { status: 'error', message: 'unexpected token' }
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** @type {(token: tokenizer.JsonToken) => (state: StateParse) => JsonState}} */
|
|
165
|
+
const parseArrayValueOp = token => state => {
|
|
166
|
+
if (token.kind === ']') { return endArray(state) }
|
|
167
|
+
if (token.kind === ',') { return { status: '[,', top: state.top, stack: state.stack } }
|
|
168
|
+
return { status: 'error', message: 'unexpected token' }
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** @type {(token: tokenizer.JsonToken) => (state: StateParse) => JsonState}} */
|
|
172
|
+
const parseObjectStartOp = token => state => {
|
|
173
|
+
if (token.kind === 'string') { return pushKey(state)(token.value) }
|
|
174
|
+
if (token.kind === '}') { return endObject(state) }
|
|
175
|
+
return { status: 'error', message: 'unexpected token' }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** @type {(token: tokenizer.JsonToken) => (state: StateParse) => JsonState}} */
|
|
179
|
+
const parseObjectKeyOp = token => state => {
|
|
180
|
+
if (token.kind === ':') { return { status: '{:', top: state.top, stack: state.stack } }
|
|
181
|
+
return { status: 'error', message: 'unexpected token' }
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** @type {(token: tokenizer.JsonToken) => (state: StateParse) => JsonState}} */
|
|
185
|
+
const parseObjectColonOp = token => state => {
|
|
186
|
+
if (isValueToken(token)) { return pushValue(state)(tokenToValue(token)) }
|
|
187
|
+
if (token.kind === '[') { return startArray(state) }
|
|
188
|
+
if (token.kind === '{') { return startObject(state) }
|
|
189
|
+
return { status: 'error', message: 'unexpected token' }
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** @type {(token: tokenizer.JsonToken) => (state: StateParse) => JsonState}} */
|
|
193
|
+
const parseObjectNextOp = token => state => {
|
|
194
|
+
if (token.kind === '}') { return endObject(state) }
|
|
195
|
+
if (token.kind === ',') { return { status: '{,', top: state.top, stack: state.stack } }
|
|
196
|
+
return { status: 'error', message: 'unexpected token' }
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** @type {(token: tokenizer.JsonToken) => (state: StateParse) => JsonState}} */
|
|
200
|
+
const parseObjectCommaOp = token => state => {
|
|
201
|
+
if (token.kind === 'string') { return pushKey(state)(token.value) }
|
|
202
|
+
return { status: 'error', message: 'unexpected token' }
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** @type {operator.Fold<tokenizer.JsonToken, JsonState>} */
|
|
206
|
+
const foldOp = token => state => {
|
|
207
|
+
switch(state.status) {
|
|
208
|
+
case 'result': return { status: 'error', message: 'unexpected token' }
|
|
209
|
+
case 'error': return { status: 'error', message: state.message }
|
|
210
|
+
case '': return parseValueOp(token)(state)
|
|
211
|
+
case '[': return parseArrayStartOp(token)(state)
|
|
212
|
+
case '[v': return parseArrayValueOp(token)(state)
|
|
213
|
+
case '[,': return parseValueOp(token)(state)
|
|
214
|
+
case '{' : return parseObjectStartOp(token)(state)
|
|
215
|
+
case '{k' : return parseObjectKeyOp(token)(state)
|
|
216
|
+
case '{:' : return parseObjectColonOp(token)(state)
|
|
217
|
+
case '{v' : return parseObjectNextOp(token)(state)
|
|
218
|
+
case '{,' : return parseObjectCommaOp(token)(state)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** @type {(tokenList: list.List<tokenizer.JsonToken>) => result.Result<any, string>} */
|
|
223
|
+
const parse = tokenList => {
|
|
224
|
+
const state = fold(foldOp)({ status: '', top: null, stack: null })(tokenList)
|
|
225
|
+
switch(state.status) {
|
|
226
|
+
case 'result': return result.ok(state.value)
|
|
227
|
+
case 'error': return result.error(state.message)
|
|
228
|
+
default: return result.error('unexpected end')
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
module.exports = {
|
|
233
|
+
/** @readonly */
|
|
234
|
+
parse
|
|
235
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
const parser = require('./module.f.cjs')
|
|
2
|
+
const tokenizer = require('../tokenizer/module.f.cjs')
|
|
3
|
+
const { toArray } = require('../../types/list/module.f.cjs')
|
|
4
|
+
const json = require('../module.f.cjs')
|
|
5
|
+
const { sort } = require('../../types/object/module.f.cjs')
|
|
6
|
+
const encoding = require('../../text/utf16/module.f.cjs');
|
|
7
|
+
|
|
8
|
+
/** @type {(s: string) => readonly tokenizer.JsonToken[]} */
|
|
9
|
+
const tokenizeString = s => toArray(tokenizer.tokenize(encoding.stringToList(s)))
|
|
10
|
+
|
|
11
|
+
const stringify = json.stringify(sort)
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
valud: [
|
|
15
|
+
() => {
|
|
16
|
+
const tokenList = tokenizeString('null')
|
|
17
|
+
const obj = parser.parse(tokenList)
|
|
18
|
+
const result = stringify(obj)
|
|
19
|
+
if (result !== '["ok",null]') { throw result }
|
|
20
|
+
},
|
|
21
|
+
() => {
|
|
22
|
+
const tokenList = tokenizeString('true')
|
|
23
|
+
const obj = parser.parse(tokenList)
|
|
24
|
+
const result = stringify(obj)
|
|
25
|
+
if (result !== '["ok",true]') { throw result }
|
|
26
|
+
},
|
|
27
|
+
() => {
|
|
28
|
+
const tokenList = tokenizeString('false')
|
|
29
|
+
const obj = parser.parse(tokenList)
|
|
30
|
+
const result = stringify(obj)
|
|
31
|
+
if (result !== '["ok",false]') { throw result }
|
|
32
|
+
},
|
|
33
|
+
() => {
|
|
34
|
+
const tokenList = tokenizeString('0.1')
|
|
35
|
+
const obj = parser.parse(tokenList)
|
|
36
|
+
const result = stringify(obj)
|
|
37
|
+
if (result !== '["ok",0.1]') { throw result }
|
|
38
|
+
},
|
|
39
|
+
() => {
|
|
40
|
+
const tokenList = tokenizeString('1.1e+2')
|
|
41
|
+
const obj = parser.parse(tokenList)
|
|
42
|
+
const result = stringify(obj)
|
|
43
|
+
if (result !== '["ok",110]') { throw result }
|
|
44
|
+
},
|
|
45
|
+
() => {
|
|
46
|
+
const tokenList = tokenizeString('"abc"')
|
|
47
|
+
const obj = parser.parse(tokenList)
|
|
48
|
+
const result = stringify(obj)
|
|
49
|
+
if (result !== '["ok","abc"]') { throw result }
|
|
50
|
+
},
|
|
51
|
+
() => {
|
|
52
|
+
const tokenList = tokenizeString('[]')
|
|
53
|
+
const obj = parser.parse(tokenList)
|
|
54
|
+
const result = stringify(obj)
|
|
55
|
+
if (result !== '["ok",[]]') { throw result }
|
|
56
|
+
},
|
|
57
|
+
() => {
|
|
58
|
+
const tokenList = tokenizeString('[1]')
|
|
59
|
+
const obj = parser.parse(tokenList)
|
|
60
|
+
const result = stringify(obj)
|
|
61
|
+
if (result !== '["ok",[1]]') { throw result }
|
|
62
|
+
},
|
|
63
|
+
() => {
|
|
64
|
+
const tokenList = tokenizeString('[[]]')
|
|
65
|
+
const obj = parser.parse(tokenList)
|
|
66
|
+
const result = stringify(obj)
|
|
67
|
+
if (result !== '["ok",[[]]]') { throw result }
|
|
68
|
+
},
|
|
69
|
+
() => {
|
|
70
|
+
const tokenList = tokenizeString('[0,[1,[2,[]]],3]')
|
|
71
|
+
const obj = parser.parse(tokenList)
|
|
72
|
+
const result = stringify(obj)
|
|
73
|
+
if (result !== '["ok",[0,[1,[2,[]]],3]]') { throw result }
|
|
74
|
+
},
|
|
75
|
+
() => {
|
|
76
|
+
const tokenList = tokenizeString('{}')
|
|
77
|
+
const obj = parser.parse(tokenList)
|
|
78
|
+
const result = stringify(obj)
|
|
79
|
+
if (result !== '["ok",{}]') { throw result }
|
|
80
|
+
},
|
|
81
|
+
() => {
|
|
82
|
+
const tokenList = tokenizeString('[{}]')
|
|
83
|
+
const obj = parser.parse(tokenList)
|
|
84
|
+
const result = stringify(obj)
|
|
85
|
+
if (result !== '["ok",[{}]]') { throw result }
|
|
86
|
+
},
|
|
87
|
+
() => {
|
|
88
|
+
const tokenList = tokenizeString('{"a":true,"b":false,"c":null}')
|
|
89
|
+
const obj = parser.parse(tokenList)
|
|
90
|
+
const result = stringify(obj)
|
|
91
|
+
if (result !== '["ok",{"a":true,"b":false,"c":null}]') { throw result }
|
|
92
|
+
},
|
|
93
|
+
() => {
|
|
94
|
+
const tokenList = tokenizeString('{"a":{"b":{"c":["d"]}}}')
|
|
95
|
+
const obj = parser.parse(tokenList)
|
|
96
|
+
const result = stringify(obj)
|
|
97
|
+
if (result !== '["ok",{"a":{"b":{"c":["d"]}}}]') { throw result }
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
invalid: [
|
|
101
|
+
() => {
|
|
102
|
+
const tokenList = tokenizeString('')
|
|
103
|
+
const obj = parser.parse(tokenList)
|
|
104
|
+
const result = stringify(obj)
|
|
105
|
+
if (result !== '["error","unexpected end"]') { throw result }
|
|
106
|
+
},
|
|
107
|
+
() => {
|
|
108
|
+
const tokenList = tokenizeString('"123')
|
|
109
|
+
const obj = parser.parse(tokenList)
|
|
110
|
+
const result = stringify(obj)
|
|
111
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
112
|
+
},
|
|
113
|
+
() => {
|
|
114
|
+
const tokenList = tokenizeString('[,]')
|
|
115
|
+
const obj = parser.parse(tokenList)
|
|
116
|
+
const result = stringify(obj)
|
|
117
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
118
|
+
},
|
|
119
|
+
() => {
|
|
120
|
+
const tokenList = tokenizeString('[1 2]')
|
|
121
|
+
const obj = parser.parse(tokenList)
|
|
122
|
+
const result = stringify(obj)
|
|
123
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
124
|
+
},
|
|
125
|
+
() => {
|
|
126
|
+
const tokenList = tokenizeString('[1,,2]')
|
|
127
|
+
const obj = parser.parse(tokenList)
|
|
128
|
+
const result = stringify(obj)
|
|
129
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
130
|
+
},
|
|
131
|
+
() => {
|
|
132
|
+
const tokenList = tokenizeString('[]]')
|
|
133
|
+
const obj = parser.parse(tokenList)
|
|
134
|
+
const result = stringify(obj)
|
|
135
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
136
|
+
},
|
|
137
|
+
() => {
|
|
138
|
+
const tokenList = tokenizeString('["a"')
|
|
139
|
+
const obj = parser.parse(tokenList)
|
|
140
|
+
const result = stringify(obj)
|
|
141
|
+
if (result !== '["error","unexpected end"]') { throw result }
|
|
142
|
+
},
|
|
143
|
+
() => {
|
|
144
|
+
const tokenList = tokenizeString('[1,]')
|
|
145
|
+
const obj = parser.parse(tokenList)
|
|
146
|
+
const result = stringify(obj)
|
|
147
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
148
|
+
},
|
|
149
|
+
() => {
|
|
150
|
+
const tokenList = tokenizeString('[,1]')
|
|
151
|
+
const obj = parser.parse(tokenList)
|
|
152
|
+
const result = stringify(obj)
|
|
153
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
154
|
+
},
|
|
155
|
+
() => {
|
|
156
|
+
const tokenList = tokenizeString('[:]')
|
|
157
|
+
const obj = parser.parse(tokenList)
|
|
158
|
+
const result = stringify(obj)
|
|
159
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
160
|
+
},
|
|
161
|
+
() => {
|
|
162
|
+
const tokenList = tokenizeString(']')
|
|
163
|
+
const obj = parser.parse(tokenList)
|
|
164
|
+
const result = stringify(obj)
|
|
165
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
166
|
+
},
|
|
167
|
+
() => {
|
|
168
|
+
const tokenList = tokenizeString('{,}')
|
|
169
|
+
const obj = parser.parse(tokenList)
|
|
170
|
+
const result = stringify(obj)
|
|
171
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
172
|
+
},
|
|
173
|
+
() => {
|
|
174
|
+
const tokenList = tokenizeString('{1:2}')
|
|
175
|
+
const obj = parser.parse(tokenList)
|
|
176
|
+
const result = stringify(obj)
|
|
177
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
178
|
+
},
|
|
179
|
+
() => {
|
|
180
|
+
const tokenList = tokenizeString('{"1"2}')
|
|
181
|
+
const obj = parser.parse(tokenList)
|
|
182
|
+
const result = stringify(obj)
|
|
183
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
184
|
+
},
|
|
185
|
+
() => {
|
|
186
|
+
const tokenList = tokenizeString('{"1"::2}')
|
|
187
|
+
const obj = parser.parse(tokenList)
|
|
188
|
+
const result = stringify(obj)
|
|
189
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
190
|
+
},
|
|
191
|
+
() => {
|
|
192
|
+
const tokenList = tokenizeString('{"1":2,,"3":4')
|
|
193
|
+
const obj = parser.parse(tokenList)
|
|
194
|
+
const result = stringify(obj)
|
|
195
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
196
|
+
},
|
|
197
|
+
() => {
|
|
198
|
+
const tokenList = tokenizeString('{}}')
|
|
199
|
+
const obj = parser.parse(tokenList)
|
|
200
|
+
const result = stringify(obj)
|
|
201
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
202
|
+
},
|
|
203
|
+
() => {
|
|
204
|
+
const tokenList = tokenizeString('{"1":2')
|
|
205
|
+
const obj = parser.parse(tokenList)
|
|
206
|
+
const result = stringify(obj)
|
|
207
|
+
if (result !== '["error","unexpected end"]') { throw result }
|
|
208
|
+
},
|
|
209
|
+
() => {
|
|
210
|
+
const tokenList = tokenizeString('{"1":2,}')
|
|
211
|
+
const obj = parser.parse(tokenList)
|
|
212
|
+
const result = stringify(obj)
|
|
213
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
214
|
+
},
|
|
215
|
+
() => {
|
|
216
|
+
const tokenList = tokenizeString('{,"1":2}')
|
|
217
|
+
const obj = parser.parse(tokenList)
|
|
218
|
+
const result = stringify(obj)
|
|
219
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
220
|
+
},
|
|
221
|
+
() => {
|
|
222
|
+
const tokenList = tokenizeString('}')
|
|
223
|
+
const obj = parser.parse(tokenList)
|
|
224
|
+
const result = stringify(obj)
|
|
225
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
226
|
+
},
|
|
227
|
+
() => {
|
|
228
|
+
const tokenList = tokenizeString('[{]}')
|
|
229
|
+
const obj = parser.parse(tokenList)
|
|
230
|
+
const result = stringify(obj)
|
|
231
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
232
|
+
},
|
|
233
|
+
() => {
|
|
234
|
+
const tokenList = tokenizeString('{[}]')
|
|
235
|
+
const obj = parser.parse(tokenList)
|
|
236
|
+
const result = stringify(obj)
|
|
237
|
+
if (result !== '["error","unexpected token"]') { throw result }
|
|
238
|
+
},
|
|
239
|
+
]
|
|
240
|
+
}
|
|
@@ -84,7 +84,7 @@ const rangeSetWhiteSpace = [
|
|
|
84
84
|
one(space)
|
|
85
85
|
]
|
|
86
86
|
|
|
87
|
-
const
|
|
87
|
+
const rangeSetTerminalForNumber = [
|
|
88
88
|
one(ht),
|
|
89
89
|
one(lf),
|
|
90
90
|
one(cr),
|
|
@@ -134,15 +134,22 @@ const rangeCapitalAF = range('AF')
|
|
|
134
134
|
|
|
135
135
|
/** @typedef {{ readonly kind: 'escapeChar', readonly value: string}} ParseEscapeCharState */
|
|
136
136
|
|
|
137
|
-
/**
|
|
137
|
+
/**
|
|
138
|
+
* @typedef {{
|
|
139
|
+
* readonly kind: 'unicodeChar'
|
|
140
|
+
* readonly value: string
|
|
141
|
+
* readonly unicode: number
|
|
142
|
+
* readonly hexIndex: number
|
|
143
|
+
* }} ParseUnicodeCharState
|
|
144
|
+
*/
|
|
138
145
|
|
|
139
146
|
/**
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
147
|
+
* @typedef {{
|
|
148
|
+
* readonly kind: 'number',
|
|
149
|
+
* readonly numberKind: '0' | '-' | 'int' | '.' | 'fractional' | 'e' | 'e+' | 'e-' | 'expDigits'
|
|
150
|
+
* readonly value: string
|
|
144
151
|
* }} ParseNumberState
|
|
145
|
-
|
|
152
|
+
*/
|
|
146
153
|
|
|
147
154
|
/** @typedef {{ readonly kind: 'invalidNumber'}} InvalidNumberState */
|
|
148
155
|
|
|
@@ -323,12 +330,12 @@ const parseNumberStateOp = create(invalidNumberToToken)([
|
|
|
323
330
|
rangeSetFunc([one(latinSmallLetterE), one(latinCapitalLetterE)])(expToToken),
|
|
324
331
|
rangeFunc(one(hyphenMinus))(hyphenMinusToToken),
|
|
325
332
|
rangeFunc(one(plusSign))(plusSignToToken),
|
|
326
|
-
rangeSetFunc(
|
|
333
|
+
rangeSetFunc(rangeSetTerminalForNumber)(terminalToToken)
|
|
327
334
|
])
|
|
328
335
|
|
|
329
336
|
/** @type {(state: InvalidNumberState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
|
|
330
337
|
const invalidNumberStateOp = create(() => () => [empty, { kind: 'invalidNumber' }])([
|
|
331
|
-
rangeSetFunc(
|
|
338
|
+
rangeSetFunc(rangeSetTerminalForNumber)(() => input => {
|
|
332
339
|
const next = tokenizeOp({ kind: 'initial' })(input)
|
|
333
340
|
return [{ first: { kind: 'error', message: 'invalid number' }, tail: next[0] }, next[1]]
|
|
334
341
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "functionalscript",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.499",
|
|
4
4
|
"description": "FunctionalScript is a functional subset of JavaScript",
|
|
5
5
|
"main": "module.f.cjs",
|
|
6
6
|
"scripts": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"homepage": "https://github.com/functionalscript/functionalscript#readme",
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@types/node": "^18.11.
|
|
33
|
+
"@types/node": "^18.11.18",
|
|
34
34
|
"typescript": "^4.9.4"
|
|
35
35
|
}
|
|
36
36
|
}
|