@tabnas/json 0.2.0
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 +179 -0
- package/bin/json +9 -0
- package/dist/json-cli.d.ts +2 -0
- package/dist/json-cli.js +43 -0
- package/dist/json-cli.js.map +1 -0
- package/dist/json.d.ts +9 -0
- package/dist/json.js +210 -0
- package/dist/json.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# @tabnas/json
|
|
2
|
+
|
|
3
|
+
A standard JSON parser (RFC 8259 / ECMA-404) for TypeScript and
|
|
4
|
+
JavaScript — the standard-JSON **grammar plugin** for the
|
|
5
|
+
[`tabnas`](https://github.com/tabnas/parser) parsing engine.
|
|
6
|
+
|
|
7
|
+
Available for [TypeScript/JavaScript](#install) and [Go](../go/).
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @tabnas/json
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`tabnas` (the engine) is a peer/dependency; see [Develop](#develop) for
|
|
16
|
+
the sibling-checkout setup used until it is published.
|
|
17
|
+
|
|
18
|
+
## Quick example
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { parse } from '@tabnas/json'
|
|
22
|
+
|
|
23
|
+
parse('{"a":1, "b":2}') // { a: 1, b: 2 }
|
|
24
|
+
parse('[1, 2, 3]') // [1, 2, 3]
|
|
25
|
+
parse('{"a": {"b": [true, null]}}') // { a: { b: [true, null] } }
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
const { parse } = require('@tabnas/json')
|
|
30
|
+
parse('"hello"') // => "hello"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`parse` is also the default export.
|
|
34
|
+
|
|
35
|
+
## Documentation
|
|
36
|
+
|
|
37
|
+
Full [Diátaxis](https://diataxis.fr) docs:
|
|
38
|
+
|
|
39
|
+
- [`doc/tutorial.md`](doc/tutorial.md) — learn it step by step.
|
|
40
|
+
- [`doc/guide.md`](doc/guide.md) — task-focused recipes.
|
|
41
|
+
- [`doc/reference.md`](doc/reference.md) — the exact API and CLI surface.
|
|
42
|
+
- [`doc/concepts.md`](doc/concepts.md) — how it works and why.
|
|
43
|
+
|
|
44
|
+
The Go port has the [equivalent docs](../go/doc/).
|
|
45
|
+
|
|
46
|
+
## Use it as a plugin
|
|
47
|
+
|
|
48
|
+
The package is a `tabnas` grammar plugin. Install it on your own engine
|
|
49
|
+
instance and layer further grammar on the shared rules:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { Tabnas } from '@tabnas/parser'
|
|
53
|
+
import { json } from '@tabnas/json'
|
|
54
|
+
|
|
55
|
+
const tn = new Tabnas({ plugins: [json] })
|
|
56
|
+
tn.parse('{"a":[1,2,3]}')
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`registerJsonGrammar(tn)` installs just the `val` / `map` / `list` /
|
|
60
|
+
`pair` / `elem` rules (jsonic's "Plain JSON" core) without the strict
|
|
61
|
+
options, for plugins that want to build on the JSON rule set.
|
|
62
|
+
|
|
63
|
+
## Reuse and options
|
|
64
|
+
|
|
65
|
+
`parse` reuses a single lazily-created instance, so you don't pay to
|
|
66
|
+
rebuild the grammar on every call. To customize, build your own instance
|
|
67
|
+
with `make(opts?)` — extra options (e.g. the `info` metadata options) are
|
|
68
|
+
applied on top of the strict JSON config:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import { make } from '@tabnas/json'
|
|
72
|
+
|
|
73
|
+
const p = make({ info: { map: true, list: true } })
|
|
74
|
+
p.parse('{"a":[1,2]}')
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`Version` is exported as the package version string.
|
|
78
|
+
|
|
79
|
+
## What it accepts
|
|
80
|
+
|
|
81
|
+
Exactly standard JSON:
|
|
82
|
+
|
|
83
|
+
- objects `{ "key": value, ... }` with double-quoted string keys
|
|
84
|
+
- arrays `[ value, ... ]`
|
|
85
|
+
- double-quoted strings with the JSON escapes
|
|
86
|
+
(`\" \\ \/ \b \f \n \r \t \uXXXX`, including surrogate pairs)
|
|
87
|
+
- numbers: optional `-`, integer (no leading zeros), optional fraction,
|
|
88
|
+
optional `e`/`E` exponent
|
|
89
|
+
- `true`, `false`, `null`
|
|
90
|
+
- insignificant whitespace: space, tab, line feed, carriage return
|
|
91
|
+
|
|
92
|
+
It **rejects** everything outside that grammar — comments, trailing
|
|
93
|
+
commas, unquoted keys, single-quoted or backtick strings, multiline
|
|
94
|
+
strings, implicit objects/arrays, hex/octal/binary numbers, leading
|
|
95
|
+
zeros, a leading `+`, a bare `.5` or trailing `1.`, and empty input.
|
|
96
|
+
This matches the platform `JSON.parse`.
|
|
97
|
+
|
|
98
|
+
Parsed objects use a **null prototype** (`Object.create(null)`): this is
|
|
99
|
+
deliberate and prototype-pollution-safe — a `"__proto__"` key becomes a
|
|
100
|
+
normal own property rather than mutating the prototype. The only visible
|
|
101
|
+
difference from `JSON.parse` is the missing `Object.prototype` (so e.g.
|
|
102
|
+
`obj.hasOwnProperty` is `undefined`; use `Object.hasOwn(obj, k)`).
|
|
103
|
+
|
|
104
|
+
## Extending the grammar
|
|
105
|
+
|
|
106
|
+
Because this is a plain grammar plugin on the shared engine, it is a
|
|
107
|
+
foundation to build other parsers on. Layer options or rules on top of
|
|
108
|
+
the `json` plugin. For example, a JSON-with-comments (JSONC) parser is
|
|
109
|
+
just the JSON grammar with comment lexing re-enabled:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { Tabnas } from '@tabnas/parser'
|
|
113
|
+
import { json } from '@tabnas/json'
|
|
114
|
+
|
|
115
|
+
const jsonc = new Tabnas({ plugins: [json] })
|
|
116
|
+
jsonc.options({ comment: { lex: true } })
|
|
117
|
+
jsonc.parse('{"a":1} // ok') // { a: 1 }
|
|
118
|
+
jsonc.parse('{"a":/* ok */1}') // { a: 1 }
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
For deeper changes, call `registerJsonGrammar(tn)` to install just the
|
|
122
|
+
rules, then use the engine's rule API (`tn.rule(...)`, and the
|
|
123
|
+
`clearOpen` / `clearClose` / `@<rule>-<phase>/replace` hooks) to replace
|
|
124
|
+
or extend the shared `val` / `map` / `list` / `pair` / `elem` rules
|
|
125
|
+
without re-declaring the JSON core.
|
|
126
|
+
|
|
127
|
+
## Errors
|
|
128
|
+
|
|
129
|
+
On invalid input, `parse` throws a `TabnasError` (also exported as
|
|
130
|
+
`JsonError`) carrying `code`, `lineNumber`, and `columnNumber`:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
import { parse, TabnasError } from '@tabnas/json'
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
parse('{a:1}')
|
|
137
|
+
} catch (err) {
|
|
138
|
+
if (err instanceof TabnasError) {
|
|
139
|
+
err.code // 'unexpected'
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## CLI
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
echo '{"a":1}' | npx tabnas-json
|
|
148
|
+
tabnas-json '{"a":1}'
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Prints the re-serialized (pretty) JSON, or the error on stderr with exit
|
|
152
|
+
code 1.
|
|
153
|
+
|
|
154
|
+
## Develop
|
|
155
|
+
|
|
156
|
+
This package depends on the engine as a sibling checkout:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
git clone https://github.com/tabnas/parser # sibling of this repo
|
|
160
|
+
( cd parser/ts && npm install && npm run build )
|
|
161
|
+
npm install # resolves "@tabnas/parser": "file:../../parser/ts"
|
|
162
|
+
npm test # tsc build + node --test
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
See [`AGENTS.md`](AGENTS.md) for layout and conventions.
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
## Grammar diagram
|
|
169
|
+
|
|
170
|
+
The installed grammar as a railroad/syntax diagram, generated from the live
|
|
171
|
+
grammar with [`@tabnas/railroad`](https://github.com/tabnas/railroad):
|
|
172
|
+
|
|
173
|
+

|
|
174
|
+
|
|
175
|
+
A vertical ASCII version is in [`doc/grammar.txt`](doc/grammar.txt).
|
|
176
|
+
|
|
177
|
+
## License
|
|
178
|
+
|
|
179
|
+
MIT.
|
package/bin/json
ADDED
package/dist/json-cli.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* Copyright (c) 2026 tabnas, MIT License */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.run = run;
|
|
5
|
+
exports.main = main;
|
|
6
|
+
/* json-cli.ts
|
|
7
|
+
*
|
|
8
|
+
* A tiny command-line front end: read JSON from the arguments or from
|
|
9
|
+
* stdin, parse it, and print the canonical re-serialized form. Exit code
|
|
10
|
+
* 1 (with the error on stderr) when the input is not valid JSON.
|
|
11
|
+
*
|
|
12
|
+
* The logic is split into `run` (pure: source in, exit code out, writes
|
|
13
|
+
* via injected sinks) and `main` (process wiring), so `run` and `main`
|
|
14
|
+
* are both testable in-process. `bin/json` calls `main`.
|
|
15
|
+
*/
|
|
16
|
+
const json_1 = require("./json");
|
|
17
|
+
// Parse `src`, writing the pretty-printed value to `out` on success or
|
|
18
|
+
// the error message to `errOut` on failure. Returns the process exit code.
|
|
19
|
+
function run(src, out, errOut) {
|
|
20
|
+
try {
|
|
21
|
+
out(JSON.stringify((0, json_1.parse)(src), null, 2) + '\n');
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
if (err instanceof json_1.TabnasError) {
|
|
26
|
+
errOut(err.message + '\n');
|
|
27
|
+
return 1;
|
|
28
|
+
}
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Wire `run` to a process-like environment. With arguments, parse them;
|
|
33
|
+
// otherwise read all of stdin. Calls `exit` with the resulting code.
|
|
34
|
+
function main(argv, stdin, out, errOut, exit) {
|
|
35
|
+
if (argv.length > 0) {
|
|
36
|
+
exit(run(argv.join(' '), out, errOut));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const chunks = [];
|
|
40
|
+
stdin.on('data', (c) => chunks.push(Buffer.from(c)));
|
|
41
|
+
stdin.on('end', () => exit(run(Buffer.concat(chunks).toString('utf8'), out, errOut)));
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=json-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-cli.js","sourceRoot":"","sources":["../src/json-cli.ts"],"names":[],"mappings":";AAAA,4CAA4C;;AAiB5C,kBAeC;AAID,oBAcC;AAhDD;;;;;;;;;GASG;AAEH,iCAA2C;AAE3C,uEAAuE;AACvE,2EAA2E;AAC3E,SAAgB,GAAG,CACjB,GAAW,EACX,GAAwB,EACxB,MAA2B;IAE3B,IAAI,CAAC;QACH,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAA,YAAK,EAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;QAC/C,OAAO,CAAC,CAAA;IACV,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,kBAAW,EAAE,CAAC;YAC/B,MAAM,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;YAC1B,OAAO,CAAC,CAAA;QACV,CAAC;QACD,MAAM,GAAG,CAAA;IACX,CAAC;AACH,CAAC;AAED,wEAAwE;AACxE,qEAAqE;AACrE,SAAgB,IAAI,CAClB,IAAc,EACd,KAA4B,EAC5B,GAAwB,EACxB,MAA2B,EAC3B,IAA4B;IAE5B,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC,CAAA;QACtC,OAAM;IACR,CAAC;IACD,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC5D,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;AACvF,CAAC"}
|
package/dist/json.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Tabnas, TabnasError, type Plugin } from '@tabnas/parser';
|
|
2
|
+
export declare const Version = "1.0.0";
|
|
3
|
+
export declare function registerJsonGrammar(tn: Tabnas): void;
|
|
4
|
+
export declare const json: Plugin;
|
|
5
|
+
export declare function make(opts?: Record<string, any>): Tabnas;
|
|
6
|
+
export declare function parse(src: string): any;
|
|
7
|
+
export { Tabnas, TabnasError };
|
|
8
|
+
export { TabnasError as JsonError };
|
|
9
|
+
export default parse;
|
package/dist/json.js
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* Copyright (c) 2026 tabnas, MIT License */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.JsonError = exports.TabnasError = exports.Tabnas = exports.json = exports.Version = void 0;
|
|
5
|
+
exports.registerJsonGrammar = registerJsonGrammar;
|
|
6
|
+
exports.make = make;
|
|
7
|
+
exports.parse = parse;
|
|
8
|
+
/* json.ts
|
|
9
|
+
*
|
|
10
|
+
* A standard JSON grammar plugin for the `tabnas` parsing engine.
|
|
11
|
+
*
|
|
12
|
+
* The engine ships no grammar of its own; this package supplies the
|
|
13
|
+
* strict, standard-JSON one. The rule set (val / map / list / pair /
|
|
14
|
+
* elem) is the "Plain JSON" grammar from jsonic's `grammar.ts` — the
|
|
15
|
+
* pure-JSON core jsonic defines before extending it for the relaxed
|
|
16
|
+
* jsonic format. Here that core is installed on its own, with the lexer
|
|
17
|
+
* restricted to strict JSON, and none of jsonic's extended grammar
|
|
18
|
+
* (comments, unquoted keys, implicit objects/arrays, trailing commas,
|
|
19
|
+
* single/backtick strings, path diving).
|
|
20
|
+
*
|
|
21
|
+
* This plugin is intended to be the foundation other tabnas grammar
|
|
22
|
+
* plugins build on: `use` it first, then layer additional rules on the
|
|
23
|
+
* shared val / map / list / pair / elem rules.
|
|
24
|
+
*/
|
|
25
|
+
const parser_1 = require("@tabnas/parser");
|
|
26
|
+
Object.defineProperty(exports, "Tabnas", { enumerable: true, get: function () { return parser_1.Tabnas; } });
|
|
27
|
+
Object.defineProperty(exports, "TabnasError", { enumerable: true, get: function () { return parser_1.TabnasError; } });
|
|
28
|
+
Object.defineProperty(exports, "JsonError", { enumerable: true, get: function () { return parser_1.TabnasError; } });
|
|
29
|
+
// Current package version.
|
|
30
|
+
exports.Version = '1.0.0';
|
|
31
|
+
// JSON-only lexer/parser options. Restrictive enough to mirror
|
|
32
|
+
// JSON.parse: double-quoted strings only, plain decimal numbers, quoted
|
|
33
|
+
// keys only, no comments, no trailing-comma map extension, no empty
|
|
34
|
+
// input, and the rule set restricted to the `json`-tagged alternates.
|
|
35
|
+
const JSON_OPTIONS = {
|
|
36
|
+
text: { lex: false },
|
|
37
|
+
number: {
|
|
38
|
+
hex: false,
|
|
39
|
+
oct: false,
|
|
40
|
+
bin: false,
|
|
41
|
+
sep: null,
|
|
42
|
+
// Reject any token the (lenient) number matcher accepts that is not a
|
|
43
|
+
// strict JSON number: leading `+`, a bare leading `.` (`.5`), a
|
|
44
|
+
// trailing `.` (`1.`), and leading zeros (`01`, `00`). The negative
|
|
45
|
+
// lookahead matches — and so excludes — anything not of the form
|
|
46
|
+
// -?(0|[1-9][0-9]*)(.[0-9]+)?([eE][+-]?[0-9]+)?.
|
|
47
|
+
exclude: /^(?!-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?$)/,
|
|
48
|
+
},
|
|
49
|
+
string: {
|
|
50
|
+
chars: '"',
|
|
51
|
+
multiChars: '',
|
|
52
|
+
// Standard JSON escape handling: allowUnknown:false rejects any
|
|
53
|
+
// unrecognized escape (\q, \z); escapeStrict disables the engine's
|
|
54
|
+
// non-standard \xHH and \u{...} structural escapes (plain \uXXXX
|
|
55
|
+
// stays); and dropping v / ' / ` from the escape map removes the
|
|
56
|
+
// remaining non-standard built-ins. Result: exactly the JSON.parse
|
|
57
|
+
// escape set, identical to the Go engine.
|
|
58
|
+
allowUnknown: false,
|
|
59
|
+
escapeStrict: true,
|
|
60
|
+
escape: { v: '', "'": '', '`': '' },
|
|
61
|
+
},
|
|
62
|
+
comment: { lex: false },
|
|
63
|
+
map: { extend: false },
|
|
64
|
+
lex: { empty: false },
|
|
65
|
+
rule: { finish: false, include: 'json' },
|
|
66
|
+
result: { fail: [undefined, NaN] },
|
|
67
|
+
tokenSet: { KEY: ['#ST', null, null, null] },
|
|
68
|
+
};
|
|
69
|
+
// Install the pure JSON rule set (val / map / list / pair / elem) on the
|
|
70
|
+
// given engine instance. Exposed separately from the options so other
|
|
71
|
+
// grammar plugins can layer their extensions on top without re-declaring
|
|
72
|
+
// the JSON core. This is jsonic's "Plain JSON" grammar.
|
|
73
|
+
function registerJsonGrammar(tn) {
|
|
74
|
+
tn.grammar({
|
|
75
|
+
// The schema version of the native-value builtins this grammar binds
|
|
76
|
+
// to (object/array/reset/key/setval/push/value).
|
|
77
|
+
v: 2,
|
|
78
|
+
// The value tree is built ENTIRELY by the engine's native-value
|
|
79
|
+
// `$`-builtins — referenced by name on the alts below; the engine
|
|
80
|
+
// merges them in at load. There are NO grammar-local closures: the
|
|
81
|
+
// builders are info-aware, so when info.map/list/text is enabled they
|
|
82
|
+
// attach the introspection marker themselves (the json plugin used to
|
|
83
|
+
// hand-write that as @jsonMapMark/@jsonListMark/@jsonText/@jsonSetval).
|
|
84
|
+
// Strict JSON containers are always explicit, so @object$/@array$ take
|
|
85
|
+
// the default implicit:false (no `k` config needed).
|
|
86
|
+
//
|
|
87
|
+
// The builtin actions used below, one line each:
|
|
88
|
+
// @reset$ — clear the parent-seeded node (so a value doesn't inherit
|
|
89
|
+
// the parent container).
|
|
90
|
+
// @object$ — allocate an empty object into the node (a MapRef under
|
|
91
|
+
// info.map).
|
|
92
|
+
// @array$ — allocate an empty array into the node (a ListRef under
|
|
93
|
+
// info.list).
|
|
94
|
+
// @key$ — capture the matched key token into a scratch slot for the
|
|
95
|
+
// pending @setval$.
|
|
96
|
+
// @setval$ — assign the just-built child value into the object under
|
|
97
|
+
// the captured key.
|
|
98
|
+
// @push$ — append the just-built child value to the array.
|
|
99
|
+
// @value$ — resolve the rule's value: a built child wins, else the
|
|
100
|
+
// scalar token (boxed with quote info under info.text).
|
|
101
|
+
rule: {
|
|
102
|
+
val: {
|
|
103
|
+
// Opening token alternates. @reset$ clears the parent-seeded node
|
|
104
|
+
// so a scalar doesn't inherit the parent container.
|
|
105
|
+
open: [
|
|
106
|
+
// A map: `{ ...`
|
|
107
|
+
{ s: '#OB', p: 'map', b: 1, a: '@reset$', g: 'map,json' },
|
|
108
|
+
// A list: `[ ...`
|
|
109
|
+
{ s: '#OS', p: 'list', b: 1, a: '@reset$', g: 'list,json' },
|
|
110
|
+
// A plain value: `"x"` `1` `true` ....
|
|
111
|
+
{ s: '#VAL', a: '@reset$', g: 'val,json' },
|
|
112
|
+
],
|
|
113
|
+
// Closing alternates. @value$ coalesces (child wins, else the
|
|
114
|
+
// scalar token) and, under info.text, boxes a string with its quote.
|
|
115
|
+
close: [
|
|
116
|
+
// End of input.
|
|
117
|
+
{ s: '#ZZ', a: '@value$', g: 'end,json' },
|
|
118
|
+
// There's more JSON.
|
|
119
|
+
{ b: 1, a: '@value$', g: 'more,json' },
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
map: {
|
|
123
|
+
open: [
|
|
124
|
+
// An empty map: {}.
|
|
125
|
+
{ s: '#OB #CB', b: 1, n: { pk: 0 }, a: '@object$', g: 'map,json' },
|
|
126
|
+
// Start matching map key-value pairs.
|
|
127
|
+
// Reset counter n.pk as new map (for extensions).
|
|
128
|
+
{ s: '#OB', p: 'pair', n: { pk: 0 }, a: '@object$', g: 'map,json,pair' },
|
|
129
|
+
],
|
|
130
|
+
close: [
|
|
131
|
+
// End of map.
|
|
132
|
+
{ s: '#CB', g: 'end,json' },
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
list: {
|
|
136
|
+
open: [
|
|
137
|
+
// An empty list: [].
|
|
138
|
+
{ s: '#OS #CS', b: 1, a: '@array$', g: 'list,json' },
|
|
139
|
+
// Start matching list elements.
|
|
140
|
+
{ s: '#OS', p: 'elem', a: '@array$', g: 'list,elem,json' },
|
|
141
|
+
],
|
|
142
|
+
close: [
|
|
143
|
+
// End of list.
|
|
144
|
+
{ s: '#CS', g: 'end,json' },
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
// sets key:val on node
|
|
148
|
+
pair: {
|
|
149
|
+
open: [
|
|
150
|
+
// Match key-colon start of pair. @key$ captures the key.
|
|
151
|
+
{
|
|
152
|
+
s: '#KEY #CL',
|
|
153
|
+
p: 'val',
|
|
154
|
+
u: { pair: true },
|
|
155
|
+
a: '@key$',
|
|
156
|
+
g: 'map,pair,key,json',
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
close: [
|
|
160
|
+
// Comma means a new pair at same pair-key level. @setval$
|
|
161
|
+
// assigns the pair; on every close alt (fan-out).
|
|
162
|
+
{ s: '#CA', r: 'pair', a: '@setval$', g: 'map,pair,json' },
|
|
163
|
+
// End of map.
|
|
164
|
+
{ s: '#CB', b: 1, a: '@setval$', g: 'map,pair,json' },
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
// push onto node
|
|
168
|
+
elem: {
|
|
169
|
+
open: [
|
|
170
|
+
// List elements are values.
|
|
171
|
+
{ p: 'val', g: 'list,elem,val,json' },
|
|
172
|
+
],
|
|
173
|
+
close: [
|
|
174
|
+
// Next element. @push$ appends the element.
|
|
175
|
+
{ s: '#CA', r: 'elem', a: '@push$', g: 'list,elem,json' },
|
|
176
|
+
// End of list.
|
|
177
|
+
{ s: '#CS', b: 1, a: '@push$', g: 'list,elem,json' },
|
|
178
|
+
],
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
// The standard plugin form: apply the strict JSON options, then register
|
|
184
|
+
// the JSON grammar. `use` this on a bare engine, or pass it to `make`.
|
|
185
|
+
const json = function json(tn, _options) {
|
|
186
|
+
tn.options(JSON_OPTIONS);
|
|
187
|
+
registerJsonGrammar(tn);
|
|
188
|
+
};
|
|
189
|
+
exports.json = json;
|
|
190
|
+
// Create a standard-JSON parser instance: a tabnas engine with the json
|
|
191
|
+
// plugin installed. Extra options (e.g. info.map/list/text) are applied
|
|
192
|
+
// after the grammar exists, mirroring the Go `Make`.
|
|
193
|
+
function make(opts) {
|
|
194
|
+
const tn = new parser_1.Tabnas({ plugins: [exports.json] });
|
|
195
|
+
if (opts) {
|
|
196
|
+
tn.options(opts);
|
|
197
|
+
}
|
|
198
|
+
return tn;
|
|
199
|
+
}
|
|
200
|
+
// A lazily-created default instance reused by `parse`, so repeated calls
|
|
201
|
+
// don't rebuild the engine and grammar each time. Parsing creates a fresh
|
|
202
|
+
// context per call, so reuse is safe.
|
|
203
|
+
let defaultParser;
|
|
204
|
+
// Parse a JSON source string with a default standard-JSON parser and
|
|
205
|
+
// return the resulting value. Throws a TabnasError on invalid input.
|
|
206
|
+
function parse(src) {
|
|
207
|
+
return (defaultParser ??= make()).parse(src);
|
|
208
|
+
}
|
|
209
|
+
exports.default = parse;
|
|
210
|
+
//# sourceMappingURL=json.js.map
|
package/dist/json.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../src/json-cli.ts","../src/json.ts"],"version":"5.9.3"}
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tabnas/json",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"main": "dist/json.js",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"types": "dist/json.d.ts",
|
|
7
|
+
"description": "A standard JSON parser (RFC 8259).",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"json",
|
|
10
|
+
"parser",
|
|
11
|
+
"rfc8259",
|
|
12
|
+
"ecma-404"
|
|
13
|
+
],
|
|
14
|
+
"author": "tabnas",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=24"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc --build src",
|
|
21
|
+
"watch": "tsc --build src -w",
|
|
22
|
+
"clean": "rm -rf node_modules dist package-lock.json",
|
|
23
|
+
"test": "npm run build && node --enable-source-maps --test test/**/*.test.js",
|
|
24
|
+
"coverage": "npm run build && node --test --experimental-test-coverage --test-coverage-include=dist/json.js --test-coverage-include=dist/json-cli.js --test-coverage-lines=0.95 test/**/*.test.js"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"bin",
|
|
29
|
+
"LICENSE"
|
|
30
|
+
],
|
|
31
|
+
"bin": {
|
|
32
|
+
"tabnas-json": "bin/json"
|
|
33
|
+
},
|
|
34
|
+
"exports": {
|
|
35
|
+
".": "./dist/json.js"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@tabnas/debug": "file:../../debug/ts",
|
|
39
|
+
"@tabnas/parser": "file:../../parser/ts",
|
|
40
|
+
"@tabnas/railroad": "file:../../railroad/ts",
|
|
41
|
+
"@types/node": "20.x",
|
|
42
|
+
"typescript": "5.x"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"@tabnas/parser": "^0.2.0"
|
|
46
|
+
}
|
|
47
|
+
}
|