Skip to content

Commit 4f08e58

Browse files
committed
add support for enums
1 parent 6c2121c commit 4f08e58

File tree

1 file changed

+88
-17
lines changed

1 file changed

+88
-17
lines changed

packages/core/src/types/schema-to-typedDict.ts

Lines changed: 88 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@ export interface UnknownT {
1111
node: "Unknown";
1212
}
1313

14-
export type TypeNode = Base | MappingType | ArrayT | Obj | UnknownT;
14+
export interface EnumT {
15+
node: 'Enum',
16+
options: string[]
17+
}
18+
19+
export type TypeNode = Base | MappingType | ArrayT | Obj | EnumT | UnknownT;
1520

1621
export interface MappingType {
1722
node: "MappingType";
@@ -37,17 +42,19 @@ export interface Obj {
3742
// ---------------- Tokenizer ----------------
3843

3944
type TokKind =
40-
| "SQBRACKETS"
45+
| "SQUARE_BRACKETS"
4146
| "LBRACE"
4247
| "RBRACE"
4348
| "COLON"
4449
| "SEMI"
45-
| "Q"
50+
| "QUESTION_MARK"
4651
| "COMMA"
47-
| "LT"
48-
| "GT"
52+
| "LESS_THAN"
53+
| "GREATER_THAN"
4954
| "IDENT"
5055
| "KW"
56+
| "PIPE"
57+
| "STRING_LITERAL"
5158
| "UNKNOWN";
5259

5360
export interface Tok {
@@ -66,16 +73,17 @@ export function tokenize(src: string): Tok[] {
6673
const c = s[i]!;
6774
if (/\s/.test(c)) { i++; continue; }
6875
if (c === "[" && i + 1 < s.length && s[i + 1] === "]") {
69-
out.push({ kind: "SQBRACKETS", value: "[]", pos: i }); i += 2; continue;
76+
out.push({ kind: "SQUARE_BRACKETS", value: "[]", pos: i }); i += 2; continue;
7077
}
7178
if (c === "{") { out.push({ kind: "LBRACE", value: c, pos: i }); i++; continue; }
7279
if (c === "}") { out.push({ kind: "RBRACE", value: c, pos: i }); i++; continue; }
7380
if (c === ":") { out.push({ kind: "COLON", value: c, pos: i }); i++; continue; }
7481
if (c === ";") { out.push({ kind: "SEMI", value: c, pos: i }); i++; continue; }
75-
if (c === "?") { out.push({ kind: "Q", value: c, pos: i }); i++; continue; }
82+
if (c === "?") { out.push({ kind: "QUESTION_MARK", value: c, pos: i }); i++; continue; }
7683
if (c === ",") { out.push({ kind: "COMMA", value: c, pos: i }); i++; continue; }
77-
if (c === "<") { out.push({ kind: "LT", value: c, pos: i }); i++; continue; }
78-
if (c === ">") { out.push({ kind: "GT", value: c, pos: i }); i++; continue; }
84+
if (c === "<") { out.push({ kind: "LESS_THAN", value: c, pos: i }); i++; continue; }
85+
if (c === ">") { out.push({ kind: "GREATER_THAN", value: c, pos: i }); i++; continue; }
86+
if (c === '|') { out.push({kind: "PIPE", value: c, pos: i}); i++; continue; }
7987

8088
if (/[A-Za-z_]/.test(c)) {
8189
let j = i + 1;
@@ -85,6 +93,35 @@ export function tokenize(src: string): Tok[] {
8593
out.push({ kind, value: word, pos: i });
8694
i = j;
8795
continue;
96+
}else if(c === "'" || c === '"'){
97+
const quote = c
98+
let j = i + 1
99+
let buf = ""
100+
while(j < s.length){
101+
const ch = s[j]
102+
103+
if(ch === "\\" && j + 1 < s.length){
104+
buf += s[j + 1]
105+
j += 2
106+
continue
107+
}
108+
109+
if(ch === quote){
110+
j++
111+
break
112+
}
113+
114+
buf += ch
115+
j++
116+
}
117+
118+
if (j > s.length) {
119+
throw new SyntaxError(`Unterminated string starting at ${i}`)
120+
}
121+
122+
out.push({ kind: "STRING_LITERAL", value: buf, pos: i })
123+
i = j
124+
continue
88125
}else{
89126

90127
// Scope for improvement
@@ -161,7 +198,7 @@ class Parser {
161198
// Parse the field name
162199
private parse_field(): Field {
163200
const name_tok = this.want("IDENT");
164-
const optional = !!this.accept("Q");
201+
const optional = !!this.accept("QUESTION_MARK");
165202
this.want("COLON");
166203
const typ = this.parse_type();
167204
return { name: name_tok.value, typ, optional };
@@ -170,7 +207,7 @@ class Parser {
170207
// Parse the field type
171208
private parse_type(): TypeNode {
172209
let t = this.parse_primary();
173-
while (this.accept("SQBRACKETS")) {
210+
while (this.accept("SQUARE_BRACKETS")) {
174211
t = { node: "Array", item: t };
175212
}
176213
return t;
@@ -180,6 +217,21 @@ class Parser {
180217
const t = this.peek();
181218
if (!t) throw new SyntaxError("Unexpected EOF while parsing Type");
182219

220+
if(t.kind === 'STRING_LITERAL'){
221+
222+
const options: string[] = []
223+
options.push(this.want("STRING_LITERAL").value)
224+
while(this.accept("PIPE")){
225+
226+
if(this.peek()?.kind !== 'STRING_LITERAL')
227+
throw new SyntaxError(`Exprected String Literal at position: ${this.peek()?.pos ?? "EOF"}`)
228+
229+
options.push(this.want("STRING_LITERAL").value)
230+
}
231+
232+
return {node: "Enum", options: options}
233+
}
234+
183235
// Object or '{}'
184236
if (t.kind === "LBRACE") {
185237
if (this.peek(1) && this.peek(1)!.kind === "RBRACE") {
@@ -192,13 +244,13 @@ class Parser {
192244
// Record<string, T>
193245
if (t.kind === "IDENT" && t.value === "Record") {
194246
this.want("IDENT"); // Record
195-
this.want("LT"); // <
247+
this.want("LESS_THAN"); // <
196248
const k = this.want("KW"); // expect 'string'
197249
if (k.value !== "string")
198250
throw new SyntaxError(`Only Record<string, ...> supported at pos ${k.pos}`);
199251
this.want("COMMA");
200252
const val = this.parse_type();
201-
this.want("GT");
253+
this.want("GREATER_THAN");
202254
return { node: "MappingType", value: val };
203255
}
204256

@@ -244,19 +296,33 @@ export type Sig =
244296
| ["array", Sig]
245297
| ["obj", ReadonlyArray<[string, boolean, Sig]>]
246298
| ["mapping", Sig]
299+
| ["enum", string[]]
247300
| ["unknown"];
248301

249302
export function type_signature(t: TypeNode): Sig {
250-
if (t.node === "Base") return ["base", t.kind];
251-
if (t.node === "Array") return ["array", type_signature(t.item)];
303+
if (t.node === "Base")
304+
return ["base", t.kind];
305+
306+
if (t.node === "Array")
307+
return ["array", type_signature(t.item)];
308+
252309
if (t.node === "Obj") {
253310
const items = [...t.fields]
254311
.map(f => [f.name, f.optional, type_signature(f.typ)] as [string, boolean, Sig])
255312
.sort((a, b) => a[0].localeCompare(b[0]));
256313
return ["obj", items];
257314
}
258-
if (t.node === "MappingType") return ["mapping", type_signature(t.value)];
259-
if(t.node == "Unknown") return ["unknown"]
315+
316+
if (t.node === "MappingType")
317+
return ["mapping", type_signature(t.value)];
318+
319+
if(t.node == "Unknown")
320+
return ["unknown"]
321+
322+
if (t.node === "Enum") {
323+
const opts = [...t.options].sort();
324+
return ["enum", opts];
325+
}
260326
throw new TypeError(`Unknown TypeNode: ${(t as any) && (t as any).node}`);
261327
}
262328

@@ -364,6 +430,11 @@ export function py_type(t: TypeNode, name_of: Map<string, string>): string {
364430
return `Mapping[str, ${inner}]`;
365431
}
366432

433+
if(t.node === "Enum"){
434+
const opts = t.options.map(o => JSON.stringify(o)).join(", ");
435+
return `Literal[${opts}]`;
436+
}
437+
367438
if (t.node === "Unknown") {
368439
return "Any";
369440
}

0 commit comments

Comments
 (0)