@gmb/bitmark-parser 7.2.0 → 7.4.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.
Files changed (47) hide show
  1. package/README.md +163 -23
  2. package/config/bitmark-json.tsp +439 -0
  3. package/config/bitmark.json +85074 -0
  4. package/dist/browser/bitmark-parser.min.js +6 -4
  5. package/dist/browser/bitmark-parser.min.js.map +1 -1
  6. package/dist/browser/cjs/index.cjs +140 -72
  7. package/dist/browser/cjs/index.cjs.map +1 -1
  8. package/dist/browser/cjs/index.d.cts +6299 -6806
  9. package/dist/browser/esm/index.d.ts +6299 -6806
  10. package/dist/browser/esm/index.js +139 -72
  11. package/dist/browser/esm/index.js.map +1 -1
  12. package/dist/browser/esm/worker-entry.js +86 -57
  13. package/dist/browser/esm/worker-entry.js.map +1 -1
  14. package/dist/browser/wasm/bitmark_browser_full_wasm_bg.wasm +0 -0
  15. package/dist/browser/wasm/bitmark_json_wasm_bg.wasm +0 -0
  16. package/dist/browser/wasm/bitmark_wasm_bg.wasm +0 -0
  17. package/dist/index.cjs +76 -29
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +6299 -6806
  20. package/dist/index.d.ts +6299 -6806
  21. package/dist/index.js +75 -29
  22. package/dist/index.js.map +1 -1
  23. package/dist/legacy.cjs +4 -3
  24. package/dist/legacy.cjs.map +1 -1
  25. package/dist/legacy.d.cts +9 -1
  26. package/dist/legacy.d.ts +9 -1
  27. package/dist/legacy.js +4 -3
  28. package/dist/legacy.js.map +1 -1
  29. package/dist/worker-entry.cjs +23 -15
  30. package/dist/worker-entry.cjs.map +1 -1
  31. package/package.json +11 -7
  32. package/schema/bitmark.schema.json +10459 -14050
  33. package/wasm/bitmark_wasm.d.ts +12 -3
  34. package/wasm/bitmark_wasm.js +34 -15
  35. package/wasm/bitmark_wasm_bg.wasm +0 -0
  36. package/wasm/bitmark_wasm_bg.wasm.d.ts +2 -2
  37. package/wasm/package.json +1 -1
  38. package/wasm-bitmark-json/bitmark_json_wasm.d.ts +12 -3
  39. package/wasm-bitmark-json/bitmark_json_wasm.js +34 -15
  40. package/wasm-bitmark-json/bitmark_json_wasm_bg.wasm +0 -0
  41. package/wasm-bitmark-json/bitmark_json_wasm_bg.wasm.d.ts +2 -2
  42. package/wasm-bitmark-json/package.json +1 -1
  43. package/wasm-browser-full/bitmark_browser_full_wasm.d.ts +12 -3
  44. package/wasm-browser-full/bitmark_browser_full_wasm.js +34 -15
  45. package/wasm-browser-full/bitmark_browser_full_wasm_bg.wasm +0 -0
  46. package/wasm-browser-full/bitmark_browser_full_wasm_bg.wasm.d.ts +2 -2
  47. package/wasm-browser-full/package.json +1 -1
package/README.md CHANGED
@@ -69,9 +69,9 @@ runtime — the API surface never changes:
69
69
 
70
70
  | Feature | Contents | Size (approx.) |
71
71
  | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------- |
72
- | `full` (Node default) | everything: rich `info` metadata + built-in translations + the semantic `diff` | ~1025 KB (~395 KB gzip) |
73
- | `browser-full` (browser default) | the same conversions and `diff`, without either | ~818 KB (~333 KB gzip) |
74
- | `bitmark-json` | bitmark ↔ JSON only: `convert`/`canonicalize`/`transform` (formats `auto`/`bitmark`/`json`, plus the `text`, `semantic-tokens` and `diagnostics` outputs), the editor services, `info` (JSON only), breakscape, text fragments — no `diff` | ~599 KB (~239 KB gzip) |
72
+ | `full` (Node default) | everything: rich `info` metadata + built-in translations + the semantic `diff` | ~1044 KB (~410 KB gzip) |
73
+ | `browser-full` (browser default) | the same conversions and `diff`, without either | ~855 KB (~353 KB gzip) |
74
+ | `bitmark-json` | bitmark ↔ JSON only: `convert`/`canonicalize`/`transform` (formats `auto`/`bitmark`/`json`, plus the `text`, `semantic-tokens` and `diagnostics` outputs), the editor services, `info` (JSON only), breakscape, text fragments — no `diff` | ~629 KB (~256 KB gzip) |
75
75
 
76
76
  **The rule.** _Every build includes every feature, except the two
77
77
  browser-targeted variants, which each carry an explicit exclusion list._ That
@@ -82,7 +82,7 @@ below:
82
82
  | -------------------------------------------------- | ----------------------------------------------------------------------------------- |
83
83
  | native CLI, daemon, docgen, schemagen, wasm `full` | **all** |
84
84
  | wasm `browser-full` | all **except** `info-meta` (bit + tag prose) and `translations` (the baked table) |
85
- | wasm `bitmark-json` | all **except** the above and `markup`, `mapping-report`, `diff`, `lex`, `info-text` |
85
+ | wasm `bitmark-json` | all **except** the above and `markup`, `mapping-report`, `diff`, `lex` (with `ast`), `info-text` |
86
86
 
87
87
  So, not in `bitmark-json`: the markup formats (`html`, `xml`,
88
88
  `xml-niso-iec`, …) as input or output (they throw an `unsupported` error), the
@@ -192,9 +192,10 @@ Convert between bitmark, JSON, and the mapped markup formats (html/xml/…).
192
192
 
193
193
  A JSON input may be an array or a single value, each entry a `{ bit: {…} }`
194
194
  envelope (the forward output) or a bare bit object. An entry that is not a
195
- bit produces no bitmark: it is skipped and reported as an `unknown-bit-type`
196
- issue naming its JSON path (`$[2] is not a bit: no "type" — skipped`) on the
197
- warnings channel of the CLI (`--warnings`) and the daemon; the bits beside it
195
+ bit produces no bitmark: it is skipped and reported as a `bit-invalid` issue
196
+ (or `bit-type-invalid` when it has a `type` the registry does not know) naming
197
+ its JSON path (`$[2] is not a bit: expected a bit object with "type" —
198
+ ignored`) on the warnings channel of the CLI (`--warnings`) and the daemon; the bits beside it
198
199
  still convert, and a document in which nothing is a bit converts to empty
199
200
  output. This `convert` returns a bare string and so cannot show the skip.
200
201
  Only malformed JSON *text* throws (`invalid-json`).
@@ -202,7 +203,7 @@ Only malformed JSON *text* throws (`invalid-json`).
202
203
  Options:
203
204
 
204
205
  - `inputFormat` — `"auto"` (default), `"bitmark"`, `"json"`, or a mapping id (`"html"`, `"xml"`, …)
205
- - `outputFormat` — `"auto"` (default: the opposite direction), `"text"` (lossy plain-text extraction; output-only), `"semantic-tokens"` / `"diagnostics"` / `"lex"` (output-only, bitmark input only — see their sections), or as above
206
+ - `outputFormat` — `"auto"` (default: the opposite direction), `"text"` (lossy plain-text extraction; output-only), `"semantic-tokens"` / `"diagnostics"` / `"lex"` / `"ast"` (output-only, bitmark input only — see their sections), or as above
206
207
  - `mode` — `"optimized"` (default) or `"full"`
207
208
  - `warnings` — include validation warnings (default: `false`)
208
209
  - `plainText` — output text as plain text (default: `false`)
@@ -255,7 +256,7 @@ Options:
255
256
  For bitmark input, output is a JSON array of entries with:
256
257
 
257
258
  - `bit` — serialized bit payload
258
- - `parser` — parser metadata, `errors` / `warnings`, and `infos` (informational notices for recoveries that are not necessarily wrong: text kept as text because it only looks like an unclosed tag, `unclosed-tag`, or a formatting mark with no partner, `unclosed-formatting` — in bitmark malformed markup is text, not an error). Each issue carries a stable machine-readable `code` (`"unknown-property"`, `"missing-required-tag"`, …) beside its human-readable `message`, plus `text` and `location`. **Branch on `code`** — a shipped code never changes meaning, whereas the message text is not contractual and may be reworded in any release
259
+ - `parser` — parser metadata, `errors` / `warnings`, and `infos` (informational notices for recoveries that are not necessarily wrong: text kept as text because it only looks like an unclosed tag, `tag-unclosed`, or a formatting mark with no partner, `formatting-unclosed` — in bitmark malformed markup is text, not an error). Each issue carries a stable machine-readable `code` (`"tag-invalid"`, `"tag-missing"`, …) and a `scope` (`"bit"`, `"card"`, `"bit-chain"` or `"card-chain"` — where the issue sits) beside its human-readable `message`, plus `text` and `location`. **Branch on `code` and `scope`** — a shipped code never changes meaning, whereas the message text is not contractual and may be reworded in any release. `bitmark info error-codes` lists every code with its severity and message template (see `info` below)
259
260
  - `bitmark` — source bitmark text (a convenience echo; every bit is fully described by `bit` alone)
260
261
 
261
262
  A region the parser could not read — an unknown bit type, or non-blank text
@@ -266,6 +267,25 @@ commented-out bit and any `:format` / `&resource` suffix; absent when there
266
267
  was no header) and `body` (the raw text after it, as a plain string), so
267
268
  generating bitmark from it reproduces the original region.
268
269
 
270
+ The other direction reports too. Going **to** bitmark (from JSON or from
271
+ markup), an inline mark that has no bitmark form is dropped — the forward
272
+ parser would not read it back as that mark, so writing it would corrupt the
273
+ surrounding text. The text it decorated is always kept, and the loss is
274
+ reported: `mark-dropped` when the whole mark goes, `mark-reduced` when it
275
+ survives in a lesser form (its value cleared, or one of its segments dropped).
276
+ Both arrive on the conversion's warnings, like every other issue. In practice
277
+ they are rare — a `href` holding a line terminator, a mark kind the grammar has
278
+ no tag for — so treat either as a signal that the JSON holds something bitmark
279
+ cannot express, not as routine noise. Likewise `list-number-invalid`: a list
280
+ `start` or item `value` that is not a valid number for its list (`4000` on a
281
+ Roman list), or a `value` on a bullet or task item, is ignored and reported.
282
+
283
+ Reading bitmark, an inline property list (`==text==|link:…|?hint|`) reports two
284
+ things (PLAN-217): `text-tag-cardinality-exceeded` when a child is given more
285
+ often than it may be (`|?a|?b|` — the later value wins), and
286
+ `text-tag-value-invalid` when a value does not fit its format but is kept
287
+ (`|symbol:…|width:wide|`).
288
+
269
289
  #### canonicalize(input: string, options?: CanonicalizeOptions): string
270
290
 
271
291
  Re-emit input in its own format in canonical form — the single same-format surface. `mode`: `"optimized"` (default, omit natural defaults) or `"full"` (all keys).
@@ -277,7 +297,17 @@ Apply a patch document to a document, bit by bit, and re-emit it.
277
297
 
278
298
  - `patch` — a `PatchDocument`, or the shorthand: a bare array of entries
279
299
  applied to every bit (build entries with `patchEntry`). Either form may be
280
- a JSON string
300
+ a JSON string. An entry's `path` is in one of two languages, told apart by
301
+ its first character (one language per call; a mixed call is refused):
302
+ - a **JSON path** addresses the bit JSON (`title`,
303
+ `quizzes[0].choices[1].choice`, `table.data[1][1]`, `id[-1]`) and the
304
+ value is a JSON value;
305
+ - a **bitmark path** addresses the bit as an author writes it and the value
306
+ is bitmark source (see *Bitmark paths* below). Bitmark paths need
307
+ bitmark input
308
+ - a skipped entry — a bad path, an index out of range, a value the engine
309
+ refuses — **throws `PatchError`** (`diagnostics: { path, kind, message }[]`)
310
+ rather than returning a document with the edit silently missing
281
311
  - `preHook` / `postHook` — per-bit hooks; the pre-hook may return patches, an
282
312
  output-format override or `drop`. Hooks see the original bits, in order,
283
313
  before the document reshapes anything; a hook's patches apply after the
@@ -286,13 +316,14 @@ Apply a patch document to a document, bit by bit, and re-emit it.
286
316
  `spacesAroundValues` — as for `convert`. `outputFormat` (default `"json"`)
287
317
  takes every per-bit format: `"json"`, `"bitmark"`, `"text"` or a mapping id
288
318
  such as `"html"` (per-bit text outputs are joined with newlines). The
289
- whole-document outputs `"lex"`, `"semantic-tokens"` and `"diagnostics"` are
290
- refused with an `unsupported` error in every driver — use `convert`. An
291
- explicit `inputFormat` is honoured as given, patch or hooks or not; it is
292
- never re-detected from the content
319
+ whole-document outputs `"lex"`, `"ast"`, `"semantic-tokens"` and
320
+ `"diagnostics"` are refused with an `unsupported` error in every driver —
321
+ use `convert`. An explicit `inputFormat` is honoured as given, patch or
322
+ hooks or not; it is never re-detected from the content
293
323
 
294
324
  A patch document addresses bits by their position (or id) in the input
295
- **before** any entry is applied, so it reads like a diff:
325
+ **before** any entry is applied, so it reads like a diff. An inserted `bit`
326
+ is a JSON bit, or a string holding one bit of bitmark source:
296
327
 
297
328
  ```json
298
329
  {
@@ -319,6 +350,53 @@ or `-1` for the document start. A bit may be the target of at most one
319
350
  and moving it are all rejected, naming the second of the conflicting entries.
320
351
  `diff` produces such a document from two versions of a file (below).
321
352
 
353
+ #### Bitmark paths
354
+
355
+ A bitmark path spells the thing it names the way an author writes it, so the
356
+ same path works for every bit type and no JSON key name is needed:
357
+
358
+ | Path | Addresses |
359
+ | --- | --- |
360
+ | `[@id]`, `[!]`, `[#]`, `[&image]` | the bit's first such tag; `[@id][-1]` the last, `[-][1]` the second `[-…]` |
361
+ | `[&image][@width]` | `[@width]` in the run after `[&image]` — a chain is spelled like the chain |
362
+ | `$body` | the bit's own text (tags, cards and footer untouched) |
363
+ | `$raw` | everything after the bit header |
364
+ | `$card[1]` | the whole second card; `$card[1][0]` its first side, `$card[1][0][1]` a variant |
365
+ | `$card[1][!]`, `$card[1][-][1]` | tags inside a card, by occurrence |
366
+ | `$card[1]$body`, `$card[1]$raw` | a card's own text / whole content |
367
+ | `$footer` | the `==== footer ====` section |
368
+
369
+ Values are bitmark source, breakscaped as an author would write it (a bare
370
+ `]` or a trailing `^` in a tag value is refused — use `breakscapeText` with
371
+ `location: "tag"` for user data, and add `subLocation: "attrValue"` when the
372
+ value is an inline-attr chain value such as a `link:` href); `null` is a
373
+ valueless tag (`[@x]`). The
374
+ four ops are the JSON engine's: `set` replaces a tag's value, a section's
375
+ content or a scope's text; `create` sets only when absent; `delete` removes
376
+ a tag (**a run head takes its whole run** — `[&image:u][@width:1]` minus
377
+ `[&image]` leaves nothing behind), a section, a body; `append` adds another
378
+ occurrence after the last one on its own line (`$card[1][-]` twice gives
379
+ white, green, blue, red), a sibling section after `$card[c]`, or a new last
380
+ card for a bare `$card`.
381
+
382
+ Every edit is a splice of the bit's own source: the parser's tree is the
383
+ address map, the rest of the bit keeps its formatting, and an edit that
384
+ would change the structure outside the addressed scope — a `==== text ====`
385
+ or `==== footer ====` that would swallow the cards after it, a bit header in
386
+ a value — is refused with the bit untouched. `convert(…, { outputFormat:
387
+ "ast" })` shows the tree a path resolves against.
388
+
389
+ ```js
390
+ transform(doc, {
391
+ outputFormat: "bitmark",
392
+ patch: [
393
+ { path: "$card[1][-]", op: "append", value: "green" },
394
+ { path: "$card[0][!]", op: "set", value: "Milk (cold)" },
395
+ { path: "$body", op: "set", value: "Think **twice**." },
396
+ ],
397
+ });
398
+ ```
399
+
322
400
  #### diff(a: string, b: string, options?: DiffOptions): string
323
401
 
324
402
  Semantic diff of two documents. Both sides are re-canonicalized and
@@ -458,10 +536,10 @@ const { positionEncoding, diagnostics: list } = diagnostics(source);
458
536
  // list[i] = {
459
537
  // range: { start: { line, character }, end: { line, character } }, // 0-based, UTF-16 units
460
538
  // severity: 2, // DiagnosticSeverity.Warning
461
- // code: "unknown-property", // the contract — branch on this
539
+ // code: "tag-invalid", // the contract — branch on this
462
540
  // source: "bitmark",
463
- // message: "[@nope] is an unknown property …", // prose, may change
464
- // data: { bit: 0 }, // the bit's index in the JSON array
541
+ // message: "[@nope] is not valid for [.article]", // prose, may change
542
+ // data: { bit: 0, scope: "bit" }, // the bit's index in the JSON array; the issue's scope
465
543
  // }
466
544
  ```
467
545
 
@@ -552,7 +630,7 @@ offers the bit types). The CLI has the same three services under one
552
630
  command: `bitmark editor diagnostics|complete|resolve|hover`. The contract is
553
631
  `.zen/specs/API-EDT-editor-services.tsp`.
554
632
 
555
- #### The `lex` output format
633
+ #### The `lex` and `ast` output formats
556
634
 
557
635
  `convert(input, { inputFormat: "bitmark", outputFormat: "lex" })` returns the
558
636
  lexer's token stream as a JSON array: one `{ kind, span, text }` per token
@@ -563,19 +641,59 @@ so a highlighter should use `semantic-tokens` instead. Bitmark input only; a
563
641
  JSON or markup document has no source text to lex. The former `lex()` export
564
642
  and `bitmark lex` command were removed in 7.0 (see the migration guide).
565
643
 
644
+ `convert(input, { inputFormat: "bitmark", outputFormat: "ast" })` returns the
645
+ parser's tree as JSON — bits, their blocks (tag runs, body text, dividers)
646
+ and inlines, every node with its byte `span`. Debug tooling like `lex`: the
647
+ shape follows the parser and is not a contract. It shows what a bitmark
648
+ patch path addresses (a run is one `TagChain`; a card is what sits between
649
+ divider blocks). Not to be confused with the legacy entry point's "ast",
650
+ which is bit JSON. Bitmark input only; in the same builds as `lex`.
651
+
566
652
  #### breakscapeText(input: string, options?: BreakscapeOptions): string
567
653
 
568
654
  Breakscape text (escape bitmark special characters).
569
655
 
570
656
  - `format` — `"bitmark++"` (default) or `"plainText"`
571
- - `location` — `"body"` (default) or `"tag"`
657
+ - `location` — `"body"` (default), `"cardBody"` or `"tag"`
658
+ - `subLocation` — `"text"` (default) or `"attrValue"`
659
+
660
+ `subLocation` REFINES `location`, it does not replace it. `"attrValue"` means
661
+ the string is the value half of an inline-attr chain segment
662
+ (`==t==|link:VALUE|`): it adds the chain delimiter `|` to the escaped set and
663
+ drops the running-text rules (inline doubles, block markers), while `location`
664
+ keeps deciding the tag rules — a `[`+trigger is escaped in `body` and inert in
665
+ `tag`, a `]` is inert in `body` and escaped in `tag`.
666
+
667
+ `"cardBody"` is a card side or variant body. It has the `body` rules plus the
668
+ card dividers `--` and `++`, which are markup only inside a card set and only
669
+ at the start of a line, where they break to `-^-` and `+^+`.
670
+
671
+ Reach for `subLocation: "attrValue"` when building a chain value for the patch
672
+ API by hand; without it, an href containing `[@` cannot be written correctly.
673
+
674
+ ```ts
675
+ breakscapeText("http://x/a[@b]", { location: "body", subLocation: "attrValue" });
676
+ // → "http://x/a[^@b]"
677
+ ```
678
+
679
+ Carets themselves follow one rule: **a literal `^` is written `^^` except with
680
+ `format: "plainText"` and `location: "body"`, where it is written `^`.** Three
681
+ cases land on the `^^` side although they read as though they would not — a tag
682
+ value in a plain-format bit (the `:text` suffix switches the body only), an
683
+ inline `code` mark (ordinary bitmark text carrying a mark), and an attr value
684
+ or bare URL. A `|code` *block* body is raw, but it has no public `format`: the
685
+ parser and generator apply that rule themselves at the block boundary.
572
686
 
573
687
  #### unbreakscapeText(input: string, options?: BreakscapeOptions): string
574
688
 
575
689
  Unbreakscape text (unescape bitmark special characters).
576
690
 
577
691
  - `format` — `"bitmark++"` (default) or `"plainText"`
578
- - `location` — `"body"` (default) or `"tag"`
692
+ - `location` — `"body"` (default), `"cardBody"` or `"tag"`
693
+ - `subLocation` — accepted and **ignored**
694
+
695
+ For `bitmark++` the rule is "remove one caret from each run" whatever the
696
+ context, so a single inverse undoes every breakscape mode.
579
697
 
580
698
  #### info(options?: InfoOptions): string
581
699
 
@@ -588,7 +706,10 @@ defaults), JSON keys, per-context overrides, and the card set structure. `"depre
588
706
  version and (separately) any migration target.
589
707
 
590
708
  - `infoType` — `"list"` (default), `"bit"`, `"all"`, `"deprecated"`,
591
- `"bit-groups"`, `"resource-groups"`, or `"languages"`
709
+ `"bit-groups"`, `"resource-groups"`, `"languages"`, or `"error-codes"`
710
+ (every issue code the parser can emit, with its severity and its message
711
+ templates per scope — `ErrorCodesInfo`; read from the validator's own
712
+ message table, so it is what the envelope emits, in every variant)
592
713
  - `format` — `"text"` (default) or `"json"`
593
714
  - `bit` — filter to a specific bit type (when `infoType` is `"bit"`)
594
715
  - `pretty` — prettify JSON output (default: `false`)
@@ -791,6 +912,21 @@ with each release (the site root hosts the bitmark language docs), or build
791
912
  it locally with `npm run docs` (→ `docs/api/`) and preview it with
792
913
  `npm run docs:serve` (http://localhost:8080, `--port` to change).
793
914
 
915
+ ### Resolved config document
916
+
917
+ The package ships the resolved bitmark configuration its parser was compiled
918
+ against — every bit type, tag group and card set with descriptions, formats,
919
+ defaults and mapping keys — as `bitmark.json`, with its TypeSpec contract
920
+ beside it:
921
+
922
+ ```ts
923
+ import config from "@gmb/bitmark-parser/bitmark.json" with { type: "json" };
924
+ // contract: node_modules/@gmb/bitmark-parser/config/bitmark-json.tsp
925
+ ```
926
+
927
+ It is generated by the build (`bitmark-confgen`) from the repo's jsonc config
928
+ tree, never hand-edited, and its `label` is a content hash of that tree.
929
+
794
930
  ### JSON Schema
795
931
 
796
932
  The package also ships a JSON Schema (Draft 2020-12) describing the parser's
@@ -827,7 +963,7 @@ A tag whose configured format is `number` accepts what JavaScript's
827
963
 
828
964
  `NaN`, `Infinity`, an exponent beyond ±4000, and a radix literal past 64 bits
829
965
  are **rejected**: the tag is dropped from the output and a
830
- `property-format-mismatch` warning names it, exactly like any other value that
966
+ `tag-value-invalid` warning names it, exactly like any other value that
831
967
  does not fit its format.
832
968
 
833
969
  Accepted values print in JavaScript's `JSON.stringify` form — `0.5`, `1.5`,
@@ -953,6 +1089,10 @@ bitmark convert input.bitmark --input-format bitmark --output-format lex --prett
953
1089
  # Breakscape / unbreakscape text
954
1090
  bitmark breakscape input.txt
955
1091
  bitmark breakscape input.txt --format plainText --location tag
1092
+ # a card side / variant body, where the card dividers are live
1093
+ bitmark breakscape input.txt --location cardBody
1094
+ # a value for an inline-attr chain segment (==t==|link:VALUE|)
1095
+ bitmark breakscape input.txt --location body --sub-location attrValue
956
1096
  bitmark unbreakscape input.txt
957
1097
 
958
1098
  # Query bit type information