1- import { isOptionType , type Option , type OptionType } from "./types.ts" ;
1+ import { isArrayOf , isUndefined , isUnionOf } from "@core/unknownutil/is" ;
2+ import {
3+ isOptionLocalScope ,
4+ isOptionScope ,
5+ isOptionType ,
6+ type Option ,
7+ type OptionType ,
8+ } from "./types.ts" ;
29import { createMarkdownFromHelp } from "../markdown.ts" ;
310import { regexIndexOf , trimLines } from "../utils.ts" ;
411
@@ -29,7 +36,12 @@ export function parse(content: string) {
2936
3037 const options : Option [ ] = [ ] ;
3138 const succeeds = new Set < number > ( ) ;
32- const errors : Array < { name : string ; start : number ; block : string } > = [ ] ;
39+ const errors : {
40+ name : string ;
41+ start : number ;
42+ block : string ;
43+ err : Error ;
44+ } [ ] = [ ] ;
3345 let last = - 1 ;
3446 for ( const match of content . matchAll ( / \* ' ( \w + ) ' \* / g) ) {
3547 const name = match [ 1 ] ;
@@ -39,22 +51,22 @@ export function parse(content: string) {
3951 continue ;
4052 }
4153 const { block, start, end } = extractBlock ( content , index ) ;
42- const option = parseBlock ( name , block ) ;
43- if ( option ) {
54+ try {
55+ const option = parseBlock ( name , block ) ;
4456 options . push ( option ) ;
4557 succeeds . add ( start ) ;
4658 last = end ;
47- } else {
48- errors . push ( { name, start, block } ) ;
59+ } catch ( err ) {
60+ errors . push ( { name, start, block, err } ) ;
4961 }
5062 }
5163
5264 if ( errors . length ) {
53- for ( const { name, start, block } of errors ) {
65+ for ( const { name, start, block, err } of errors ) {
5466 if ( ! succeeds . has ( start ) ) {
5567 const line = content . substring ( 0 , start + 1 ) . split ( "\n" ) . length ;
5668 console . error (
57- `Failed to parse option definition for '${ name } ' at line ${ line } :` ,
69+ `Failed to parse option definition for '${ name } ' at line ${ line } : ${ err } ` ,
5870 ) ;
5971 console . error ( "----- block start -----" ) ;
6072 console . error ( block ) ;
@@ -90,6 +102,7 @@ function extractBlock(content: string, index: number): {
90102 * - {name} : Required.
91103 * - {type} : Required. But some have fallbacks.
92104 * - {scope} : Optional. If not present, assume "global".
105+ * - {localscope}: Required if {scope} is "local".
93106 * - {defaults} : Optional. Appended to {document}.
94107 * - {attention} : Optional. Appended to {document}.
95108 * - {document} : Optional.
@@ -98,14 +111,14 @@ function extractBlock(content: string, index: number): {
98111 * name type defaults
99112 * ~~~~~ ~~~~~~ ~~~~~~~~~~~~~
100113 * 'aleph' 'al' number (default 224) *E123*
101- * global <- scope
114+ * global <- scope, localscope
102115 * {only available when compiled ... <- attention
103116 * feature} :
104117 * The ASCII code for the first letter of the ... <- document
105118 * routine that maps the keyboard in Hebrew mode ... :
106119 * ```
107120 */
108- function parseBlock ( name : string , body : string ) : Option | undefined {
121+ function parseBlock ( name : string , body : string ) : Option {
109122 // Extract definition line
110123 const reTags = / (?: [ \t ] + \* [ ^ * \s ] + \* ) + [ \t ] * $ / . source ;
111124 const reShortNames = / (?: [ \t ] + ' \w + ' ) * / . source ;
@@ -117,17 +130,26 @@ function parseBlock(name: string, body: string): Option | undefined {
117130 const m1 = body . match ( new RegExp ( reDefinition , "dm" ) ) ;
118131 const type = m1 ?. groups ?. type ?? fallbackTypes [ name ] ;
119132 if ( ! m1 || ! isOptionType ( type ) ) {
120- // {name} not found, or {type} is invalid
121- return ;
133+ throw new TypeError ( "Failed to parse name or type" ) ;
122134 }
123135 const defaults = m1 . groups ! . defaults ?. replaceAll ( / ^ \s + / gm, " " ) . trim ( ) ;
124136 body = trimLines ( body . substring ( m1 . indices ! [ 0 ] [ 1 ] ) ) + "\n" ;
125137
126- // Extract {scope}
127- const m2 = body . match ( / ^ \t { 3 , } ( g l o b a l o r l o c a l | g l o b a l | l o c a l ) (?: [ \t ] .* ) ? \n / d) ;
128- const scope = (
129- m2 ?. [ 1 ] . split ( " or " ) ?? [ "global" ]
130- ) as Array < "global" | "local" > ;
138+ // Extract {scope}, {localscope}
139+ const m2 = body . match (
140+ / ^ \t { 3 , } (?< scope > g l o b a l o r l o c a l | g l o b a l | l o c a l ) (?: t o (?< localscope > b u f f e r | t a b | w i n d o w ) ) ? (?: [ \t ] .* ) ? \n / d,
141+ ) ;
142+ const scope = m2 ?. groups ?. scope . split ( " or " ) ?? [ "global" ] ;
143+ if ( ! isArrayOf ( isOptionScope ) ( scope ) ) {
144+ throw new TypeError ( "Failed to parse scope" ) ;
145+ }
146+ const localScope = m2 ?. groups ?. localscope ;
147+ if ( ! isUnionOf ( [ isOptionLocalScope , isUndefined ] ) ( localScope ) ) {
148+ throw new TypeError ( "Failed to parse local scope" ) ;
149+ }
150+ if ( scope . includes ( "local" ) && localScope === undefined ) {
151+ throw new TypeError ( "Invalid scope and local scope" ) ;
152+ }
131153 body = trimLines ( body . substring ( m2 ?. indices ?. at ( 0 ) ?. at ( 1 ) ?? 0 ) ) + "\n" ;
132154
133155 // Extract {attention}
@@ -140,5 +162,5 @@ function parseBlock(name: string, body: string): Option | undefined {
140162
141163 const docs = createMarkdownFromHelp ( body ) ;
142164
143- return { name, type, scope, docs } ;
165+ return { name, type, scope, localScope , docs } ;
144166}
0 commit comments