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" ;
2
9
import { createMarkdownFromHelp } from "../markdown.ts" ;
3
10
import { regexIndexOf , trimLines } from "../utils.ts" ;
4
11
@@ -29,7 +36,12 @@ export function parse(content: string) {
29
36
30
37
const options : Option [ ] = [ ] ;
31
38
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
+ } [ ] = [ ] ;
33
45
let last = - 1 ;
34
46
for ( const match of content . matchAll ( / \* ' ( \w + ) ' \* / g) ) {
35
47
const name = match [ 1 ] ;
@@ -39,22 +51,22 @@ export function parse(content: string) {
39
51
continue ;
40
52
}
41
53
const { block, start, end } = extractBlock ( content , index ) ;
42
- const option = parseBlock ( name , block ) ;
43
- if ( option ) {
54
+ try {
55
+ const option = parseBlock ( name , block ) ;
44
56
options . push ( option ) ;
45
57
succeeds . add ( start ) ;
46
58
last = end ;
47
- } else {
48
- errors . push ( { name, start, block } ) ;
59
+ } catch ( err ) {
60
+ errors . push ( { name, start, block, err } ) ;
49
61
}
50
62
}
51
63
52
64
if ( errors . length ) {
53
- for ( const { name, start, block } of errors ) {
65
+ for ( const { name, start, block, err } of errors ) {
54
66
if ( ! succeeds . has ( start ) ) {
55
67
const line = content . substring ( 0 , start + 1 ) . split ( "\n" ) . length ;
56
68
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 } ` ,
58
70
) ;
59
71
console . error ( "----- block start -----" ) ;
60
72
console . error ( block ) ;
@@ -90,6 +102,7 @@ function extractBlock(content: string, index: number): {
90
102
* - {name} : Required.
91
103
* - {type} : Required. But some have fallbacks.
92
104
* - {scope} : Optional. If not present, assume "global".
105
+ * - {localscope}: Required if {scope} is "local".
93
106
* - {defaults} : Optional. Appended to {document}.
94
107
* - {attention} : Optional. Appended to {document}.
95
108
* - {document} : Optional.
@@ -98,14 +111,14 @@ function extractBlock(content: string, index: number): {
98
111
* name type defaults
99
112
* ~~~~~ ~~~~~~ ~~~~~~~~~~~~~
100
113
* 'aleph' 'al' number (default 224) *E123*
101
- * global <- scope
114
+ * global <- scope, localscope
102
115
* {only available when compiled ... <- attention
103
116
* feature} :
104
117
* The ASCII code for the first letter of the ... <- document
105
118
* routine that maps the keyboard in Hebrew mode ... :
106
119
* ```
107
120
*/
108
- function parseBlock ( name : string , body : string ) : Option | undefined {
121
+ function parseBlock ( name : string , body : string ) : Option {
109
122
// Extract definition line
110
123
const reTags = / (?: [ \t ] + \* [ ^ * \s ] + \* ) + [ \t ] * $ / . source ;
111
124
const reShortNames = / (?: [ \t ] + ' \w + ' ) * / . source ;
@@ -117,17 +130,26 @@ function parseBlock(name: string, body: string): Option | undefined {
117
130
const m1 = body . match ( new RegExp ( reDefinition , "dm" ) ) ;
118
131
const type = m1 ?. groups ?. type ?? fallbackTypes [ name ] ;
119
132
if ( ! m1 || ! isOptionType ( type ) ) {
120
- // {name} not found, or {type} is invalid
121
- return ;
133
+ throw new TypeError ( "Failed to parse name or type" ) ;
122
134
}
123
135
const defaults = m1 . groups ! . defaults ?. replaceAll ( / ^ \s + / gm, " " ) . trim ( ) ;
124
136
body = trimLines ( body . substring ( m1 . indices ! [ 0 ] [ 1 ] ) ) + "\n" ;
125
137
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
+ }
131
153
body = trimLines ( body . substring ( m2 ?. indices ?. at ( 0 ) ?. at ( 1 ) ?? 0 ) ) + "\n" ;
132
154
133
155
// Extract {attention}
@@ -140,5 +162,5 @@ function parseBlock(name: string, body: string): Option | undefined {
140
162
141
163
const docs = createMarkdownFromHelp ( body ) ;
142
164
143
- return { name, type, scope, docs } ;
165
+ return { name, type, scope, localScope , docs } ;
144
166
}
0 commit comments