1
1
// Generates Python TypedDict code from a concise schema language.
2
2
3
- import { unknown } from "zod" ;
4
-
5
3
// ---------------- AST ----------------
6
4
7
- export type TypeNode = Base | MappingType | ArrayT | Obj | UnknownT ;
8
-
9
5
export interface Base {
10
6
node : "Base" ;
11
- kind : string ; // "string" | "number" | "int" | "boolean" | "emptyobj" (unused)
7
+ kind : string ;
8
+ }
9
+
10
+ export interface UnknownT {
11
+ node : "Unknown" ;
12
12
}
13
13
14
+ export type TypeNode = Base | MappingType | ArrayT | Obj | UnknownT ;
15
+
14
16
export interface MappingType {
15
17
node : "MappingType" ;
16
- value : TypeNode ; // Record<string, Value>
18
+ value : TypeNode ;
17
19
}
18
20
19
21
export interface ArrayT {
@@ -32,10 +34,6 @@ export interface Obj {
32
34
fields : ReadonlyArray < Field > ;
33
35
}
34
36
35
- export interface UnknownT {
36
- node : "Unknown" ;
37
- }
38
-
39
37
// ---------------- Tokenizer ----------------
40
38
41
39
type TokKind =
@@ -88,6 +86,8 @@ export function tokenize(src: string): Tok[] {
88
86
i = j ;
89
87
continue ;
90
88
} else {
89
+
90
+ // Scope for improvement
91
91
// catch-all: consume until whitespace or a stop char
92
92
const STOP = new Set ( [ "{" , "}" , "[" , "]" , ":" , ";" , "," , "<" , ">" , "?" ] ) ;
93
93
let j = i ;
@@ -146,6 +146,8 @@ class Parser {
146
146
}
147
147
148
148
private parse_object ( ) : Obj {
149
+
150
+ // Scope for change - What about Record<> Type ??
149
151
this . want ( "LBRACE" ) ;
150
152
const fields : Field [ ] = [ ] ;
151
153
while ( this . peek ( ) && this . peek ( ) ! . kind !== "RBRACE" ) {
@@ -226,7 +228,7 @@ export function parse_schema(src: string): Obj {
226
228
return root ;
227
229
}
228
230
229
- // ---------------- Shape signatures & naming ----------------
231
+ // ---------------- RENDERING THE TYPES ----------------
230
232
231
233
const PYTHON_KEYWORDS = new Set ( [
232
234
"False" , "None" , "True" , "and" , "as" , "assert" , "async" , "await" , "break" , "class" , "continue" , "def" , "del" , "elif" , "else" , "except" , "finally" , "for" , "from" , "global" , "if" , "import" , "in" , "is" , "lambda" , "nonlocal" , "not" , "or" , "pass" , "raise" , "return" , "try" , "while" , "with" , "yield"
@@ -338,7 +340,9 @@ export function py_type(t: TypeNode, name_of: Map<string, string>): string {
338
340
if ( t . kind === "number" ) return "float" ;
339
341
if ( t . kind === "int" ) return "int" ;
340
342
if ( t . kind === "boolean" ) return "bool" ;
341
- if ( t . kind === "emptyobj" ) return "Mapping[str, object]" ; // (unused here)
343
+
344
+ // Check this
345
+ if ( t . kind === "emptyobj" ) return "Mapping[str, object]" ;
342
346
return "Any" ;
343
347
344
348
// throw new Error(`Unknown base ${t.kind}`);
@@ -375,7 +379,7 @@ export function render_typeddicts(root: Obj, rootName: string = "Root"): string
375
379
376
380
// map signatures to names for type resolution
377
381
const name_of = new Map < string , string > ( ) ;
378
- for ( const cls of classes ) {
382
+ for ( const cls of classes ) {
379
383
const sig = type_signature ( { node : "Obj" , fields : cls . fields } ) ;
380
384
name_of . set ( JSON . stringify ( sig ) , cls . name ) ;
381
385
}
@@ -385,13 +389,19 @@ export function render_typeddicts(root: Obj, rootName: string = "Root"): string
385
389
386
390
const field_type_str = ( t : TypeNode ) : string => {
387
391
const s = py_type ( t , name_of ) ;
388
- if ( s . includes ( "Mapping[" ) ) needed . add ( "Mapping" ) ;
392
+ if ( s . includes ( "Mapping[" ) )
393
+ needed . add ( "Mapping" ) ;
389
394
// list[...] needs no extra import in Python 3.9+
390
395
return s ;
391
396
} ;
392
397
393
398
const opt_wrap = ( s : string , optional : boolean ) : string => {
394
- if ( optional ) { needed . add ( "NotRequired" ) ; return `NotRequired[${ s } ]` ; }
399
+
400
+ if ( optional ) {
401
+ needed . add ( "NotRequired" ) ;
402
+ return `NotRequired[${ s } ]` ;
403
+ }
404
+
395
405
return s ;
396
406
} ;
397
407
0 commit comments