mirror of
				https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
				synced 2025-10-31 15:47:00 +01:00 
			
		
		
		
	compiler: add struct types
This commit is contained in:
		
							parent
							
								
									a97a128336
								
							
						
					
					
						commit
						53d3777ee0
					
				| @ -48,6 +48,7 @@ export type Item = { | ||||
|     span: Span; | ||||
|     ident: Ident; | ||||
|     pub: boolean; | ||||
|     attrs: Attr[]; | ||||
| }; | ||||
| 
 | ||||
| export type ItemKind = | ||||
| @ -95,11 +96,11 @@ export type VariantDataKind = | ||||
|     | { tag: "tuple" } & TupleVariantData | ||||
|     | { tag: "struct" } & StructVariantData; | ||||
| 
 | ||||
| export type TupleVariantData = { elems: VariantData[] }; | ||||
| export type TupleVariantData = { elems: FieldDef[] }; | ||||
| export type StructVariantData = { fields: FieldDef[] }; | ||||
| 
 | ||||
| export type FieldDef = { | ||||
|     ident: Ident; | ||||
|     ident?: Ident; | ||||
|     ty: Ty; | ||||
|     pub: boolean; | ||||
|     span: Span; | ||||
| @ -251,6 +252,7 @@ export type AnonFieldDef = { | ||||
| }; | ||||
| 
 | ||||
| export type Path = { | ||||
|     id: AstId; | ||||
|     segments: PathSegment[]; | ||||
|     span: Span; | ||||
| }; | ||||
| @ -266,3 +268,9 @@ export type Ident = { | ||||
|     text: string; | ||||
|     span: Span; | ||||
| }; | ||||
| 
 | ||||
| export type Attr = { | ||||
|     ident: Ident; | ||||
|     args?: Expr[]; | ||||
|     span: Span; | ||||
| }; | ||||
|  | ||||
| @ -1,11 +1,14 @@ | ||||
| import { AstId, Ids, Span } from "@slige/common"; | ||||
| import { | ||||
|     Attr, | ||||
|     Expr, | ||||
|     ExprKind, | ||||
|     Ident, | ||||
|     Item, | ||||
|     ItemKind, | ||||
|     Pat, | ||||
|     Path, | ||||
|     PathSegment, | ||||
|     PatKind, | ||||
|     Stmt, | ||||
|     StmtKind, | ||||
| @ -30,9 +33,10 @@ export class Cx { | ||||
|         span: Span, | ||||
|         ident: Ident, | ||||
|         pub: boolean, | ||||
|         attrs: Attr[], | ||||
|     ): Item { | ||||
|         const id = this.id(); | ||||
|         return { id, kind, span, ident, pub }; | ||||
|         return { id, kind, span, ident, pub, attrs }; | ||||
|     } | ||||
| 
 | ||||
|     public expr(kind: ExprKind, span: Span): Expr { | ||||
| @ -49,4 +53,9 @@ export class Cx { | ||||
|         const id = this.id(); | ||||
|         return { id, kind, span }; | ||||
|     } | ||||
| 
 | ||||
|     public path(segments: PathSegment[], span: Span): Path { | ||||
|         const id = this.id(); | ||||
|         return { id, segments, span }; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -18,6 +18,7 @@ import { | ||||
|     EnumItem, | ||||
|     Expr, | ||||
|     ExprStmt, | ||||
|     FieldDef, | ||||
|     FieldExpr, | ||||
|     File, | ||||
|     FnItem, | ||||
| @ -91,6 +92,8 @@ export interface Visitor< | ||||
|     visitTypeAliasItem?(item: Item, kind: TypeAliasItem, ...p: P): R; | ||||
| 
 | ||||
|     visitVariant?(variant: Variant, ...p: P): R; | ||||
|     visitVariantData?(data: VariantData, ...p: P): R; | ||||
|     visitFieldDef?(field: FieldDef, ...p: P): R; | ||||
| 
 | ||||
|     visitExpr?(expr: Expr, ...p: P): R; | ||||
|     visitErrorExpr?(expr: Expr, ...p: P): R; | ||||
| @ -291,6 +294,7 @@ export function visitVariantData< | ||||
|     data: VariantData, | ||||
|     ...p: P | ||||
| ) { | ||||
|     if (v.visitVariantData?.(data, ...p) === "stop") return; | ||||
|     const dk = data.kind; | ||||
|     switch (dk.tag) { | ||||
|         case "error": | ||||
| @ -299,19 +303,30 @@ export function visitVariantData< | ||||
|             return; | ||||
|         case "tuple": | ||||
|             for (const elem of dk.elems) { | ||||
|                 visitVariantData(v, elem, ...p); | ||||
|                 visitFieldDef(v, elem, ...p); | ||||
|             } | ||||
|             return; | ||||
|         case "struct": | ||||
|             for (const field of dk.fields) { | ||||
|                 visitIdent(v, field.ident, ...p); | ||||
|                 visitTy(v, field.ty, ...p); | ||||
|                 visitFieldDef(v, field, ...p); | ||||
|             } | ||||
|             return; | ||||
|     } | ||||
|     exhausted(dk); | ||||
| } | ||||
| 
 | ||||
| export function visitFieldDef< | ||||
|     P extends PM = [], | ||||
| >( | ||||
|     v: Visitor<P>, | ||||
|     field: FieldDef, | ||||
|     ...p: P | ||||
| ) { | ||||
|     if (v.visitFieldDef?.(field, ...p) === "stop") return; | ||||
|     field.ident && visitIdent(v, field.ident, ...p); | ||||
|     visitTy(v, field.ty, ...p); | ||||
| } | ||||
| 
 | ||||
| export function visitGenerics< | ||||
|     P extends PM = [], | ||||
| >( | ||||
|  | ||||
| @ -12,7 +12,7 @@ import { | ||||
|     todo, | ||||
| } from "@slige/common"; | ||||
| import * as resolve from "@slige/resolve"; | ||||
| import { Ty, tyToString } from "@slige/ty"; | ||||
| import { ElemDef, FieldDef, Ty, tyToString, VariantData } from "@slige/ty"; | ||||
| 
 | ||||
| export class Checker { | ||||
|     private stmtChecked = new IdSet<AstId>(); | ||||
| @ -232,6 +232,44 @@ export class Checker { | ||||
|         exhausted(k); | ||||
|     } | ||||
| 
 | ||||
|     public structItemTy(item: ast.Item, kind: ast.StructItem): Ty { | ||||
|         return this.itemTys.get(item.id) ?? this.checkStructItem(item, kind); | ||||
|     } | ||||
| 
 | ||||
|     private checkStructItem(item: ast.Item, kind: ast.StructItem): Ty { | ||||
|         const data = this.checkVariantData(kind.data); | ||||
|         return Ty({ tag: "struct", item, kind, data }); | ||||
|     } | ||||
| 
 | ||||
|     private checkVariantData(data: ast.VariantData): VariantData { | ||||
|         const k = data.kind; | ||||
|         switch (k.tag) { | ||||
|             case "error": | ||||
|                 return { tag: "error" }; | ||||
|             case "unit": | ||||
|                 return { tag: "unit" }; | ||||
|             case "tuple": { | ||||
|                 const elems = k.elems | ||||
|                     .map(({ ty, pub }): ElemDef => ({ | ||||
|                         ty: this.tyTy(ty), | ||||
|                         pub, | ||||
|                     })); | ||||
|                 return { tag: "tuple", elems }; | ||||
|             } | ||||
|             case "struct": { | ||||
|                 const fields = k.fields | ||||
|                     .map(({ ident, ty, pub }): FieldDef => { | ||||
|                         if (!ident) { | ||||
|                             throw new Error(); | ||||
|                         } | ||||
|                         return { ident: ident.id, ty: this.tyTy(ty), pub }; | ||||
|                     }); | ||||
|                 return { tag: "struct", fields }; | ||||
|             } | ||||
|         } | ||||
|         exhausted(k); | ||||
|     } | ||||
| 
 | ||||
|     public fnItemTy(item: ast.Item, kind: ast.FnItem): Ty { | ||||
|         return this.itemTys.get(item.id) ?? this.checkFnItem(item, kind); | ||||
|     } | ||||
| @ -270,7 +308,7 @@ export class Checker { | ||||
|             case "repeat": | ||||
|                 return todo(); | ||||
|             case "struct": | ||||
|                 return todo(); | ||||
|                 return this.checkStructExpr(expr, k, expected); | ||||
|             case "ref": | ||||
|                 return todo(); | ||||
|             case "deref": | ||||
| @ -312,6 +350,13 @@ export class Checker { | ||||
|         switch (res.kind.tag) { | ||||
|             case "error": | ||||
|                 return Ty({ tag: "error" }); | ||||
|             case "enum": | ||||
|             case "struct": | ||||
|                 return todo("return a ctor here"); | ||||
|             case "variant": | ||||
|                 return todo("return a ctor here"); | ||||
|             case "field": | ||||
|                 throw new Error(); | ||||
|             case "fn": { | ||||
|                 const fn = res.kind.item; | ||||
|                 const ty = this.fnItemTy(fn, res.kind.kind); | ||||
| @ -336,6 +381,70 @@ export class Checker { | ||||
|         exhausted(res.kind); | ||||
|     } | ||||
| 
 | ||||
|     private checkStructExpr( | ||||
|         expr: ast.Expr, | ||||
|         kind: ast.StructExpr, | ||||
|         expected: Ty, | ||||
|     ): Ty { | ||||
|         if (!kind.path) { | ||||
|             return todo(); | ||||
|         } | ||||
|         const res = this.re.pathRes(kind.path.id); | ||||
|         if (res.kind.tag !== "struct") { | ||||
|             this.report("type is not a struct", kind.path.span); | ||||
|             const ty = Ty({ tag: "error" }); | ||||
|             this.exprTys.set(expr.id, ty); | ||||
|             return ty; | ||||
|         } | ||||
|         const ty = this.structItemTy(res.kind.item, res.kind.kind); | ||||
|         this.exprTys.set(expr.id, ty); | ||||
|         if (ty.kind.tag === "error") { | ||||
|             return ty; | ||||
|         } | ||||
|         if (ty.kind.tag !== "struct") { | ||||
|             throw new Error(); | ||||
|         } | ||||
|         const data = ty.kind.data; | ||||
|         if (data.tag !== "struct") { | ||||
|             this.report("struct data not a struct", kind.path.span); | ||||
|             const ty = Ty({ tag: "error" }); | ||||
|             this.exprTys.set(expr.id, ty); | ||||
|             return ty; | ||||
|         } | ||||
|         const notCovered = new Set<FieldDef>(); | ||||
|         for (const field of data.fields) { | ||||
|             notCovered.add(field); | ||||
|         } | ||||
|         for (const field of kind.fields) { | ||||
|             const found = data.fields | ||||
|                 .find((f) => f.ident === field.ident.id); | ||||
|             if (!found) { | ||||
|                 this.report(`no field named '${field.ident.text}'`, field.span); | ||||
|                 return ty; | ||||
|             } | ||||
|             const fieldTy = this.exprTy(field.expr); | ||||
|             const tyRes = this.resolveTys(fieldTy, found.ty); | ||||
|             if (!tyRes.ok) { | ||||
|                 this.report(tyRes.val, field.span); | ||||
|                 return ty; | ||||
|             } | ||||
|             notCovered.delete(found); | ||||
|         } | ||||
|         if (notCovered.size !== 0) { | ||||
|             this.report( | ||||
|                 `fields ${ | ||||
|                     notCovered | ||||
|                         .keys() | ||||
|                         .toArray() | ||||
|                         .map((field) => `'${field}'`) | ||||
|                         .join(", ") | ||||
|                 } not covered`,
 | ||||
|                 expr.span, | ||||
|             ); | ||||
|         } | ||||
|         return ty; | ||||
|     } | ||||
| 
 | ||||
|     private checkCallExpr( | ||||
|         expr: ast.Expr, | ||||
|         kind: ast.CallExpr, | ||||
| @ -552,13 +661,32 @@ export class Checker { | ||||
|                 return Ty({ tag: "int" }); | ||||
|             case "bool": | ||||
|             case "str": | ||||
|             case "path": | ||||
|                 return todo(k.tag); | ||||
|             case "path": { | ||||
|                 const re = this.re.tyRes(ty.id); | ||||
|                 const k = re.kind; | ||||
|                 switch (k.tag) { | ||||
|                     case "error": | ||||
|                         return Ty({ tag: "error" }); | ||||
|                     case "enum": | ||||
|                         return todo(); | ||||
|                     case "struct": | ||||
|                         return this.structItemTy(k.item, k.kind); | ||||
|                     case "fn": | ||||
|                     case "variant": | ||||
|                     case "field": | ||||
|                     case "local": | ||||
|                         return todo(); | ||||
|                 } | ||||
|                 exhausted(k); | ||||
|                 return todo(); | ||||
|             } | ||||
|             case "ref": | ||||
|             case "ptr": | ||||
|             case "slice": | ||||
|             case "array": | ||||
|             case "anon_struct": | ||||
|                 return todo(k); | ||||
|                 return todo(k.tag); | ||||
|         } | ||||
|         exhausted(k); | ||||
|     } | ||||
| @ -611,7 +739,7 @@ export class Checker { | ||||
| 
 | ||||
|     private resolveTys(a: Ty, b: Ty): Res<Ty, string> { | ||||
|         if (a.kind.tag === "error" || b.kind.tag === "error") { | ||||
|             return Res.Ok(a); | ||||
|             return Res.Ok(Ty({ tag: "error" })); | ||||
|         } | ||||
|         if (b.kind.tag === "unknown") { | ||||
|             return Res.Ok(a); | ||||
| @ -653,6 +781,15 @@ export class Checker { | ||||
|                 } | ||||
|                 return Res.Ok(a); | ||||
|             } | ||||
|             case "struct": { | ||||
|                 if (b.kind.tag !== "struct") { | ||||
|                     return incompat(); | ||||
|                 } | ||||
|                 if (a.kind.item.id !== b.kind.item.id) { | ||||
|                     return incompat(); | ||||
|                 } | ||||
|                 return Res.Ok(a); | ||||
|             } | ||||
|         } | ||||
|         exhausted(a.kind); | ||||
|     } | ||||
|  | ||||
| @ -4,6 +4,7 @@ | ||||
|         "./check", | ||||
|         "./middle", | ||||
|         "./parse", | ||||
|         "./pat", | ||||
|         "./resolve", | ||||
|         "./ty", | ||||
|         "./common", | ||||
|  | ||||
| @ -307,6 +307,11 @@ export class FnLowerer { | ||||
|         switch (re.kind.tag) { | ||||
|             case "error": | ||||
|                 return { tag: "error" }; | ||||
|             case "enum": | ||||
|             case "struct": | ||||
|             case "variant": | ||||
|             case "field": | ||||
|                 return todo(); | ||||
|             case "fn": | ||||
|             case "local": | ||||
|                 return { | ||||
| @ -617,6 +622,11 @@ export class FnLowerer { | ||||
|         switch (re.kind.tag) { | ||||
|             case "error": | ||||
|                 return { tag: "error" }; | ||||
|             case "enum": | ||||
|             case "struct": | ||||
|             case "variant": | ||||
|             case "field": | ||||
|                 return todo(); | ||||
|             case "local": { | ||||
|                 const patRes = this.re.patRes(re.kind.id); | ||||
|                 const ty = this.ch.exprTy(expr); | ||||
| @ -651,6 +661,7 @@ export class FnLowerer { | ||||
|                 case "bool": | ||||
|                     return true; | ||||
|                 case "fn": | ||||
|                 case "struct": | ||||
|                     return false; | ||||
|             } | ||||
|             exhausted(ty.kind); | ||||
|  | ||||
| @ -1,12 +1,14 @@ | ||||
| import { | ||||
|     AnonFieldDef, | ||||
|     AssignType, | ||||
|     Attr, | ||||
|     BinaryType, | ||||
|     Block, | ||||
|     Cx, | ||||
|     Expr, | ||||
|     ExprField, | ||||
|     ExprKind, | ||||
|     FieldDef, | ||||
|     File, | ||||
|     GenericParam, | ||||
|     Ident, | ||||
| @ -23,8 +25,10 @@ import { | ||||
|     Ty, | ||||
|     TyKind, | ||||
|     UnaryType, | ||||
|     Variant, | ||||
|     VariantData, | ||||
| } from "@slige/ast"; | ||||
| import { Ctx, File as CtxFile, Res, Span } from "@slige/common"; | ||||
| import { Ctx, File as CtxFile, Res, Span, todo } from "@slige/common"; | ||||
| import { Lexer } from "./lexer.ts"; | ||||
| import { TokenIter } from "./token.ts"; | ||||
| import { SigFilter } from "./token.ts"; | ||||
| @ -63,12 +67,15 @@ export class Parser { | ||||
|     } | ||||
| 
 | ||||
|     private parseStmt(): Stmt { | ||||
|         const attrs = this.parseAttrs(); | ||||
|         if ( | ||||
|             ["#", "pub", "mod", "fn"].some((tt) => this.test(tt)) | ||||
|             ["pub", "mod", "enum", "struct", "fn", "type_alias"].some((tt) => | ||||
|                 this.test(tt) | ||||
|             ) | ||||
|         ) { | ||||
|             return this.parseItemStmt(); | ||||
|             return this.parseItemStmt(undefined, false, attrs); | ||||
|         } else if ( | ||||
|             ["let", "type_alias", "return", "break"].some((tt) => this.test(tt)) | ||||
|             ["let", "return", "break"].some((tt) => this.test(tt)) | ||||
|         ) { | ||||
|             const expr = this.parseSingleLineBlockStmt(); | ||||
|             this.eatSemicolon(); | ||||
| @ -85,6 +92,45 @@ export class Parser { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private parseAttrs(): Attr[] { | ||||
|         const attrs: Attr[] = []; | ||||
|         while (this.test("#")) { | ||||
|             const begin = this.span(); | ||||
|             this.step(); | ||||
|             if (!this.test("[")) { | ||||
|                 this.report("expected '['"); | ||||
|                 return attrs; | ||||
|             } | ||||
|             this.step(); | ||||
|             if (!this.test("ident")) { | ||||
|                 this.report("expected 'ident'"); | ||||
|                 return attrs; | ||||
|             } | ||||
|             const ident = this.parseIdent(); | ||||
|             let args: Expr[] | undefined = undefined; | ||||
|             if (this.test("(")) { | ||||
|                 this.step(); | ||||
|                 args = this.parseDelimitedList( | ||||
|                     this.parseAttrArg, | ||||
|                     ")", | ||||
|                     ",", | ||||
|                 ); | ||||
|             } | ||||
|             if (!this.test("]")) { | ||||
|                 this.report("expected ']'"); | ||||
|                 return attrs; | ||||
|             } | ||||
|             const end = this.span(); | ||||
|             this.step(); | ||||
|             attrs.push({ ident, args, span: Span.fromto(begin, end) }); | ||||
|         } | ||||
|         return attrs; | ||||
|     } | ||||
| 
 | ||||
|     private parseAttrArg(): Res<Expr, undefined> { | ||||
|         return Res.Ok(this.parseExpr()); | ||||
|     } | ||||
| 
 | ||||
|     private parseMultiLineBlockExpr(): Expr { | ||||
|         const begin = this.span(); | ||||
|         if (this.test("{")) { | ||||
| @ -111,9 +157,6 @@ export class Parser { | ||||
|         if (this.test("let")) { | ||||
|             return this.parseLet(); | ||||
|         } | ||||
|         if (this.test("type_alias")) { | ||||
|             return this.parseTypeAlias(); | ||||
|         } | ||||
|         if (this.test("return")) { | ||||
|             return this.parseReturn(); | ||||
|         } | ||||
| @ -143,16 +186,19 @@ export class Parser { | ||||
|         this.step(); | ||||
|         const stmts: Stmt[] = []; | ||||
|         while (!this.done()) { | ||||
|             const attrs = this.parseAttrs(); | ||||
|             if (this.test("}")) { | ||||
|                 const span = Span.fromto(begin, this.span()); | ||||
|                 this.step(); | ||||
|                 return Res.Ok({ stmts, span }); | ||||
|             } else if ( | ||||
|                 ["#", "pub", "mod", "fn"].some((tt) => this.test(tt)) | ||||
|                 ["pub", "mod", "enum", "struct", "fn", "type_alias"].some(( | ||||
|                     tt, | ||||
|                 ) => this.test(tt)) | ||||
|             ) { | ||||
|                 stmts.push(this.parseItemStmt()); | ||||
|                 stmts.push(this.parseItemStmt(undefined, false, attrs)); | ||||
|             } else if ( | ||||
|                 ["let", "type_alias", "return", "break"] | ||||
|                 ["let", "return", "break"] | ||||
|                     .some((tt) => this.test(tt)) | ||||
|             ) { | ||||
|                 stmts.push(this.parseSingleLineBlockStmt()); | ||||
| @ -210,67 +256,29 @@ export class Parser { | ||||
| 
 | ||||
|     private parseItemStmt( | ||||
|         pos = this.span(), | ||||
|         details: StmtDetails = { | ||||
|             pub: false, | ||||
|             annos: [], | ||||
|         }, | ||||
|         pub: boolean, | ||||
|         attrs: Attr[], | ||||
|     ): Stmt { | ||||
|         const sbegin = this.span(); | ||||
|         if (this.test("#") && !details.pub) { | ||||
|         if (this.test("pub") && !pub) { | ||||
|             this.step(); | ||||
|             if (!this.test("[")) { | ||||
|                 this.report("expected '['"); | ||||
|                 return this.stmt({ tag: "error" }, sbegin); | ||||
|             } | ||||
|             this.step(); | ||||
|             if (!this.test("ident")) { | ||||
|                 this.report("expected 'ident'"); | ||||
|                 return this.stmt({ tag: "error" }, sbegin); | ||||
|             } | ||||
|             const ident = this.parseIdent(); | ||||
|             const args: Expr[] = []; | ||||
|             if (this.test("(")) { | ||||
|                 this.step(); | ||||
|                 if (!this.done() && !this.test(")")) { | ||||
|                     args.push(this.parseExpr()); | ||||
|                     while (this.test(",")) { | ||||
|                         this.step(); | ||||
|                         if (this.done() || this.test(")")) { | ||||
|                             break; | ||||
|                         } | ||||
|                         args.push(this.parseExpr()); | ||||
|                     } | ||||
|                 } | ||||
|                 if (!this.test(")")) { | ||||
|                     this.report("expected ')'"); | ||||
|                     return this.stmt({ tag: "error" }, sbegin); | ||||
|                 } | ||||
|                 this.step(); | ||||
|             } | ||||
|             if (!this.test("]")) { | ||||
|                 this.report("expected ']'"); | ||||
|                 return this.stmt({ tag: "error" }, sbegin); | ||||
|             } | ||||
|             this.step(); | ||||
|             const anno = { ident, args, pos: sbegin }; | ||||
|             return this.parseItemStmt(pos, { | ||||
|                 ...details, | ||||
|                 annos: [...details.annos, anno], | ||||
|             }); | ||||
|         } else if (this.test("pub") && !details.pub) { | ||||
|             this.step(); | ||||
|             return this.parseItemStmt(pos, { ...details, pub: true }); | ||||
|             return this.parseItemStmt(pos, pub, attrs); | ||||
|         } else if (this.test("mod")) { | ||||
|             return this.parseMod(details); | ||||
|             return this.parseModItem(pub, attrs); | ||||
|         } else if (this.test("enum")) { | ||||
|             return this.parseEnumItem(pub, attrs); | ||||
|         } else if (this.test("struct")) { | ||||
|             return this.parseStructItem(pub, attrs); | ||||
|         } else if (this.test("fn")) { | ||||
|             return this.parseFn(details); | ||||
|             return this.parseFnItem(pub, attrs); | ||||
|         } else if (this.test("type_alias")) { | ||||
|             return this.parseTypeAliasItem(pub, attrs); | ||||
|         } else { | ||||
|             this.report("expected item statement"); | ||||
|             return this.stmt({ tag: "error" }, pos); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private parseMod(details: StmtDetails): Stmt { | ||||
|     private parseModItem(pub: boolean, attrs: Attr[]): Stmt { | ||||
|         const pos = this.span(); | ||||
|         this.step(); | ||||
|         if (!this.test("ident")) { | ||||
| @ -288,7 +296,8 @@ export class Parser { | ||||
|                     { tag: "mod_file", filePath }, | ||||
|                     pos, | ||||
|                     ident, | ||||
|                     details.pub, | ||||
|                     pub, | ||||
|                     attrs, | ||||
|                 ), | ||||
|             }, pos); | ||||
|         } | ||||
| @ -322,12 +331,123 @@ export class Parser { | ||||
|                 }, | ||||
|                 pos, | ||||
|                 ident, | ||||
|                 details.pub, | ||||
|                 pub, | ||||
|                 attrs, | ||||
|             ), | ||||
|         }, pos); | ||||
|     } | ||||
| 
 | ||||
|     private parseFn(details: StmtDetails): Stmt { | ||||
|     private parseEnumItem(pub: boolean, attrs: Attr[]): Stmt { | ||||
|         const begin = this.span(); | ||||
|         this.step(); | ||||
|         if (!this.test("ident")) { | ||||
|             this.report("expected ident"); | ||||
|             return this.stmt({ tag: "error" }, begin); | ||||
|         } | ||||
|         const ident = this.parseIdent(); | ||||
|         if (!this.test("{")) { | ||||
|             this.report("expected '{'"); | ||||
|             return this.stmt({ tag: "error" }, begin); | ||||
|         } | ||||
|         this.step(); | ||||
|         const endSpan: [Span] = [begin]; | ||||
|         const variants = this.parseDelimitedList( | ||||
|             this.parseVariant, | ||||
|             "}", | ||||
|             ",", | ||||
|             endSpan, | ||||
|         ); | ||||
|         const span = Span.fromto(begin, endSpan[0]); | ||||
|         return this.stmt({ | ||||
|             tag: "item", | ||||
|             item: this.item({ tag: "enum", variants }, span, ident, pub, attrs), | ||||
|         }, span); | ||||
|     } | ||||
| 
 | ||||
|     private parseVariant(): Res<Variant, undefined> { | ||||
|         const begin = this.span(); | ||||
|         let pub = false; | ||||
|         if (this.test("pub")) { | ||||
|             this.step(); | ||||
|             pub = true; | ||||
|         } | ||||
|         if (!this.test("ident")) { | ||||
|             this.report("expected 'ident'"); | ||||
|             return Res.Err(undefined); | ||||
|         } | ||||
|         const ident = this.parseIdent(); | ||||
|         const data = this.parseVariantData(); | ||||
|         return Res.Ok({ | ||||
|             ident, | ||||
|             data, | ||||
|             pub, | ||||
|             span: Span.fromto(begin, data.span), | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     private parseStructItem(pub: boolean, attrs: Attr[]): Stmt { | ||||
|         const begin = this.span(); | ||||
|         this.step(); | ||||
|         if (!this.test("ident")) { | ||||
|             this.report("expected ident"); | ||||
|             return this.stmt({ tag: "error" }, begin); | ||||
|         } | ||||
|         const ident = this.parseIdent(); | ||||
|         const data = this.parseVariantData(); | ||||
|         const span = Span.fromto(begin, data.span); | ||||
|         return this.stmt({ | ||||
|             tag: "item", | ||||
|             item: this.item({ tag: "struct", data }, span, ident, pub, attrs), | ||||
|         }, span); | ||||
|     } | ||||
| 
 | ||||
|     private parseVariantData(): VariantData { | ||||
|         const begin = this.span(); | ||||
|         if (this.test("(")) { | ||||
|             const elems = this.parseDelimitedList(this.parseFieldDef, ")", ","); | ||||
|             return { | ||||
|                 kind: { tag: "tuple", elems }, | ||||
|                 span: Span.fromto(begin, elems.at(-1)?.span ?? begin), | ||||
|             }; | ||||
|         } else if (this.test("{")) { | ||||
|             const fields = this.parseDelimitedList( | ||||
|                 this.parseFieldDef, | ||||
|                 "}", | ||||
|                 ",", | ||||
|             ); | ||||
|             return { | ||||
|                 kind: { tag: "struct", fields }, | ||||
|                 span: Span.fromto(begin, fields.at(-1)?.span ?? begin), | ||||
|             }; | ||||
|         } else { | ||||
|             return { | ||||
|                 kind: { tag: "unit" }, | ||||
|                 span: { begin: begin.end, end: begin.end }, | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private parseFieldDef(): Res<FieldDef, undefined> { | ||||
|         const begin = this.span(); | ||||
|         let pub = false; | ||||
|         if (this.test("pub")) { | ||||
|             this.step(); | ||||
|             pub = true; | ||||
|         } | ||||
|         let ident: Ident | undefined = undefined; | ||||
|         if (this.test("ident")) { | ||||
|             ident = this.parseIdent(); | ||||
|             if (!this.test(":")) { | ||||
|                 this.report("expected ':'"); | ||||
|                 return Res.Err(undefined); | ||||
|             } | ||||
|             this.step(); | ||||
|         } | ||||
|         const ty = this.parseTy(); | ||||
|         return Res.Ok({ ident, ty, pub, span: Span.fromto(begin, ty.span) }); | ||||
|     } | ||||
| 
 | ||||
|     private parseFnItem(pub: boolean, attrs: Attr[]): Stmt { | ||||
|         const pos = this.span(); | ||||
|         this.step(); | ||||
|         if (!this.test("ident")) { | ||||
| @ -373,7 +493,8 @@ export class Parser { | ||||
|                 }, | ||||
|                 pos, | ||||
|                 ident, | ||||
|                 details.pub, | ||||
|                 pub, | ||||
|                 attrs, | ||||
|             ), | ||||
|         }, pos); | ||||
|     } | ||||
| @ -400,6 +521,7 @@ export class Parser { | ||||
|         parseElem: (this: Parser, index: number) => ParseRes<T>, | ||||
|         endToken: string, | ||||
|         delimiter: string, | ||||
|         outEndSpan?: [Span], | ||||
|     ): T[] { | ||||
|         this.step(); | ||||
|         if (this.test(endToken)) { | ||||
| @ -430,6 +552,7 @@ export class Parser { | ||||
|             this.report(`expected '${endToken}'`); | ||||
|             return elems; | ||||
|         } | ||||
|         outEndSpan && (outEndSpan[0] = this.span()); | ||||
|         this.step(); | ||||
|         return elems; | ||||
|     } | ||||
| @ -468,7 +591,7 @@ export class Parser { | ||||
|         return this.stmt({ tag: "let", pat, ty, expr }, pos); | ||||
|     } | ||||
| 
 | ||||
|     private parseTypeAlias(): Stmt { | ||||
|     private parseTypeAliasItem(pub: boolean, attrs: Attr[]): Stmt { | ||||
|         const begin = this.span(); | ||||
|         this.step(); | ||||
|         if (!this.test("ident")) { | ||||
| @ -491,7 +614,8 @@ export class Parser { | ||||
|                 }, | ||||
|                 Span.fromto(begin, ty.span), | ||||
|                 ident, | ||||
|                 false, | ||||
|                 pub, | ||||
|                 attrs, | ||||
|             ), | ||||
|         }, begin); | ||||
|     } | ||||
| @ -644,7 +768,7 @@ export class Parser { | ||||
|         return this.expr({ tag: "array", exprs }, pos); | ||||
|     } | ||||
| 
 | ||||
|     private parseStruct(): Expr { | ||||
|     private parseAnonStructExpr(): Expr { | ||||
|         const pos = this.span(); | ||||
|         this.step(); | ||||
|         if (!this.test("{")) { | ||||
| @ -906,7 +1030,6 @@ export class Parser { | ||||
|                 return this.expr({ tag: "error" }, pos); | ||||
|             } | ||||
|             if (this.test("{") && !(rs & ExprRestricts.NoStructs)) { | ||||
|                 this.step(); | ||||
|                 const fields = this.parseDelimitedList( | ||||
|                     this.parseExprField, | ||||
|                     "}", | ||||
| @ -955,7 +1078,7 @@ export class Parser { | ||||
|             return this.parseArray(); | ||||
|         } | ||||
|         if (this.test("struct")) { | ||||
|             return this.parseStruct(); | ||||
|             return this.parseAnonStructExpr(); | ||||
|         } | ||||
|         if (this.test("{")) { | ||||
|             return this.parseBlockExpr(); | ||||
| @ -1166,7 +1289,7 @@ export class Parser { | ||||
|                 span: Span.fromto(begin, end), | ||||
|             }); | ||||
|         } | ||||
|         return Res.Ok({ segments, span: Span.fromto(begin, end) }); | ||||
|         return Res.Ok(this.path(segments, Span.fromto(begin, end))); | ||||
|     } | ||||
| 
 | ||||
|     private parseTyRes(): ParseRes<Ty> { | ||||
| @ -1229,8 +1352,9 @@ export class Parser { | ||||
|         span: Span, | ||||
|         ident: Ident, | ||||
|         pub: boolean, | ||||
|         attrs: Attr[], | ||||
|     ): Item { | ||||
|         return this.cx.item(kind, span, ident, pub); | ||||
|         return this.cx.item(kind, span, ident, pub, attrs); | ||||
|     } | ||||
| 
 | ||||
|     private expr(kind: ExprKind, span: Span): Expr { | ||||
| @ -1244,6 +1368,10 @@ export class Parser { | ||||
|     private ty(kind: TyKind, span: Span): Ty { | ||||
|         return this.cx.ty(kind, span); | ||||
|     } | ||||
| 
 | ||||
|     private path(segments: PathSegment[], span: Span): Path { | ||||
|         return this.cx.path(segments, span); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| const ExprRestricts = { | ||||
|  | ||||
							
								
								
									
										7
									
								
								slige/compiler/pat/ctor.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								slige/compiler/pat/ctor.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| export type Ctor = { | ||||
|     kind: CtorKind; | ||||
| }; | ||||
| 
 | ||||
| export type CtorKind = | ||||
|     | { tag: "error" } | ||||
|     | { tag: "struct" }; | ||||
							
								
								
									
										4
									
								
								slige/compiler/pat/deno.jsonc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								slige/compiler/pat/deno.jsonc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | ||||
| { | ||||
|     "name": "@slige/pat", | ||||
|     "exports": "./mod.ts", | ||||
| } | ||||
							
								
								
									
										2
									
								
								slige/compiler/pat/mod.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								slige/compiler/pat/mod.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| export * as ctor from "./ctor.ts"; | ||||
| export * from "./ctor.ts"; | ||||
| @ -1,6 +1,10 @@ | ||||
| 
 | ||||
| struct A { | ||||
|     v: int, | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
|     for (let mut i = 0; i < 10; i = i + 1) {} | ||||
|     let a: A = A { v: 123 }; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import * as ast from "@slige/ast"; | ||||
| import { AstId, IdBase, IdentId, IdMap, Res } from "@slige/common"; | ||||
| import { AstId, IdentId, IdMap, Res } from "@slige/common"; | ||||
| 
 | ||||
| export interface Syms { | ||||
|     getVal(ident: ast.Ident): Resolve; | ||||
| @ -16,7 +16,17 @@ export type Resolve = { | ||||
| 
 | ||||
| export type ResolveKind = | ||||
|     | { tag: "error" } | ||||
|     | { tag: "enum"; item: ast.Item; kind: ast.EnumItem } | ||||
|     | { tag: "struct"; item: ast.Item; kind: ast.StructItem } | ||||
|     | { tag: "fn"; item: ast.Item; kind: ast.FnItem } | ||||
|     | { | ||||
|         tag: "variant"; | ||||
|         item: ast.Item; | ||||
|         kind: ast.EnumItem; | ||||
|         variant: ast.Variant; | ||||
|         variantIdx: number; | ||||
|     } | ||||
|     | { tag: "field"; item: ast.Item; field: ast.FieldDef } | ||||
|     | { tag: "local"; id: AstId }; | ||||
| 
 | ||||
| export type PatResolve = { | ||||
| @ -127,6 +137,31 @@ export class FnSyms implements Syms { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class ItemSyms implements Syms { | ||||
|     private syms = new SymsNsTab(); | ||||
| 
 | ||||
|     public constructor( | ||||
|         private parent: Syms, | ||||
|     ) {} | ||||
| 
 | ||||
|     getVal(ident: ast.Ident): Resolve { | ||||
|         const res = this.syms.getVal(ident) || this.parent.getVal(ident); | ||||
|         if (res.kind.tag === "local") { | ||||
|             return ResolveError(ident); | ||||
|         } | ||||
|         return res; | ||||
|     } | ||||
|     getTy(ident: ast.Ident): Resolve { | ||||
|         return this.syms.getTy(ident) || this.parent.getTy(ident); | ||||
|     } | ||||
|     defVal(ident: ast.Ident, kind: ResolveKind): Res<void, Redef> { | ||||
|         return this.syms.defVal(ident, kind); | ||||
|     } | ||||
|     defTy(ident: ast.Ident, kind: ResolveKind): Res<void, Redef> { | ||||
|         return this.syms.defTy(ident, kind); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export class LocalSyms implements Syms { | ||||
|     private syms = new SymsNsTab(); | ||||
| 
 | ||||
|  | ||||
| @ -12,6 +12,7 @@ import { | ||||
| } from "@slige/common"; | ||||
| import { | ||||
|     FnSyms, | ||||
|     ItemSyms, | ||||
|     LocalSyms, | ||||
|     LoopBreakResolve, | ||||
|     LoopResolve, | ||||
| @ -27,6 +28,8 @@ import { | ||||
| export class Resols { | ||||
|     public constructor( | ||||
|         private exprResols: IdMap<AstId, Resolve>, | ||||
|         private tyResols: IdMap<AstId, Resolve>, | ||||
|         private pathResols: IdMap<AstId, Resolve>, | ||||
|         private patResols: IdMap<AstId, PatResolve>, | ||||
|         private loopsResols: IdMap<AstId, LoopResolve>, | ||||
|         private loopBreakResols: IdMap<AstId, LoopBreakResolve[]>, | ||||
| @ -39,6 +42,20 @@ export class Resols { | ||||
|         return this.exprResols.get(id)!; | ||||
|     } | ||||
| 
 | ||||
|     public tyRes(id: AstId): Resolve { | ||||
|         if (!this.tyResols.has(id)) { | ||||
|             throw new Error(); | ||||
|         } | ||||
|         return this.tyResols.get(id)!; | ||||
|     } | ||||
| 
 | ||||
|     public pathRes(id: AstId): Resolve { | ||||
|         if (!this.pathResols.has(id)) { | ||||
|             throw new Error(); | ||||
|         } | ||||
|         return this.pathResols.get(id)!; | ||||
|     } | ||||
| 
 | ||||
|     public patRes(id: AstId): PatResolve { | ||||
|         if (!this.patResols.has(id)) { | ||||
|             throw new Error(); | ||||
| @ -67,6 +84,8 @@ export class Resolver implements ast.Visitor { | ||||
|     private syms: Syms = this.rootSyms; | ||||
| 
 | ||||
|     private exprResols = new IdMap<AstId, Resolve>(); | ||||
|     private tyResols = new IdMap<AstId, Resolve>(); | ||||
|     private pathResols = new IdMap<AstId, Resolve>(); | ||||
| 
 | ||||
|     private patResols = new IdMap<AstId, PatResolve>(); | ||||
|     private patResolveStack: PatResolveKind[] = []; | ||||
| @ -84,6 +103,8 @@ export class Resolver implements ast.Visitor { | ||||
|         ast.visitFile(this, this.entryFileAst); | ||||
|         return new Resols( | ||||
|             this.exprResols, | ||||
|             this.tyResols, | ||||
|             this.pathResols, | ||||
|             this.patResols, | ||||
|             this.loopsResols, | ||||
|             this.loopBreakResols, | ||||
| @ -145,11 +166,33 @@ export class Resolver implements ast.Visitor { | ||||
|     } | ||||
| 
 | ||||
|     visitEnumItem(item: ast.Item, kind: ast.EnumItem): ast.VisitRes { | ||||
|         todo(); | ||||
|         this.syms.defTy(item.ident, { tag: "enum", item, kind }); | ||||
|         const outerSyms = this.syms; | ||||
|         this.syms = new ItemSyms(this.syms); | ||||
|         for (const variant of kind.variants) { | ||||
|             ast.visitVariant(this, variant); | ||||
|         } | ||||
|         this.syms = outerSyms; | ||||
|         return "stop"; | ||||
|     } | ||||
| 
 | ||||
|     visitStructItem(item: ast.Item, kind: ast.StructItem): ast.VisitRes { | ||||
|         todo(); | ||||
|         this.syms.defTy(item.ident, { tag: "struct", item, kind }); | ||||
|         const outerSyms = this.syms; | ||||
|         this.syms = new ItemSyms(this.syms); | ||||
|         ast.visitVariantData(this, kind.data); | ||||
|         this.syms = outerSyms; | ||||
|         return "stop"; | ||||
|     } | ||||
| 
 | ||||
|     visitVariant(variant: ast.Variant): ast.VisitRes { | ||||
|         ast.visitVariantData(this, variant.data); | ||||
|         return "stop"; | ||||
|     } | ||||
| 
 | ||||
|     visitFieldDef(field: ast.FieldDef): ast.VisitRes { | ||||
|         ast.visitTy(this, field.ty); | ||||
|         return "stop"; | ||||
|     } | ||||
| 
 | ||||
|     private fnBodiesToCheck: [ast.Item, ast.FnItem][][] = []; | ||||
| @ -160,6 +203,14 @@ export class Resolver implements ast.Visitor { | ||||
|         return "stop"; | ||||
|     } | ||||
| 
 | ||||
|     visitUseItem(item: ast.Item, kind: ast.UseItem): ast.VisitRes { | ||||
|         todo(); | ||||
|     } | ||||
| 
 | ||||
|     visitTypeAliasItem(item: ast.Item, kind: ast.TypeAliasItem): ast.VisitRes { | ||||
|         todo(); | ||||
|     } | ||||
| 
 | ||||
|     private popAndVisitFnBodies() { | ||||
|         for (const [item, kind] of this.fnBodiesToCheck.at(-1)!) { | ||||
|             const outerSyms = this.syms; | ||||
| @ -182,41 +233,20 @@ export class Resolver implements ast.Visitor { | ||||
|     } | ||||
| 
 | ||||
|     visitPathExpr(expr: ast.Expr, kind: ast.PathExpr): ast.VisitRes { | ||||
|         if (kind.path.segments.length === 1) { | ||||
|             const res = this.syms.getVal(kind.path.segments[0].ident); | ||||
|             switch (res.kind.tag) { | ||||
|                 case "error": | ||||
|                     return "stop"; | ||||
|                 case "fn": | ||||
|                     this.exprResols.set(expr.id, res); | ||||
|                     return "stop"; | ||||
|                 case "local": | ||||
|                     this.exprResols.set(expr.id, res); | ||||
|                     return "stop"; | ||||
|             } | ||||
|             exhausted(res.kind); | ||||
|         if (kind.qty) { | ||||
|             return todo(); | ||||
|         } | ||||
|         const pathRes = this.resolveInnerPath(kind.path); | ||||
|         switch (pathRes.kind.tag) { | ||||
|             case "error": | ||||
|                 todo(); | ||||
|                 return "stop"; | ||||
|             case "fn": | ||||
|                 todo(); | ||||
|                 return "stop"; | ||||
|             case "local": | ||||
|                 todo(); | ||||
|                 return "stop"; | ||||
|         } | ||||
|         exhausted(pathRes.kind); | ||||
|         const res = this.resolveValPath(kind.path); | ||||
|         this.exprResols.set(expr.id, res); | ||||
|         return "stop"; | ||||
|     } | ||||
| 
 | ||||
|     visitUseItem(item: ast.Item, kind: ast.UseItem): ast.VisitRes { | ||||
|         todo(); | ||||
|     } | ||||
| 
 | ||||
|     visitTypeAliasItem(item: ast.Item, kind: ast.TypeAliasItem): ast.VisitRes { | ||||
|         todo(); | ||||
|     visitStructExpr(expr: ast.Expr, kind: ast.StructExpr): ast.VisitRes { | ||||
|         if (!kind.path) { | ||||
|             return todo(); | ||||
|         } | ||||
|         this.resolveValPath(kind.path); | ||||
|         return "stop"; | ||||
|     } | ||||
| 
 | ||||
|     visitLoopExpr(expr: ast.Expr, kind: ast.LoopExpr): ast.VisitRes { | ||||
| @ -272,6 +302,15 @@ export class Resolver implements ast.Visitor { | ||||
|         todo(pat, kind); | ||||
|     } | ||||
| 
 | ||||
|     visitPathTy(ty: ast.Ty, kind: ast.PathTy): ast.VisitRes { | ||||
|         if (kind.qty) { | ||||
|             return todo(); | ||||
|         } | ||||
|         const res = this.resolveTyPath(kind.path); | ||||
|         this.tyResols.set(ty.id, res); | ||||
|         return "stop"; | ||||
|     } | ||||
| 
 | ||||
|     visitPath(_path: ast.Path): ast.VisitRes { | ||||
|         throw new Error("should not be reached"); | ||||
|     } | ||||
| @ -280,35 +319,80 @@ export class Resolver implements ast.Visitor { | ||||
|         throw new Error("should not be reached"); | ||||
|     } | ||||
| 
 | ||||
|     private resolveInnerPath(path: ast.Path): Resolve { | ||||
|         const res = path.segments.slice(1, path.segments.length) | ||||
|             .reduce((innerRes, seg) => { | ||||
|                 const k = innerRes.kind; | ||||
|     private resolveValPath(path: ast.Path): Resolve { | ||||
|         let res: Resolve; | ||||
|         if (path.segments.length === 0) { | ||||
|             res = this.syms.getVal(path.segments[0].ident); | ||||
|         } else { | ||||
|             res = path.segments | ||||
|                 .slice(1) | ||||
|                 .reduce((inner, seg): Resolve => { | ||||
|                     const k = inner.kind; | ||||
|                     switch (k.tag) { | ||||
|                         case "error": | ||||
|                             return inner; | ||||
|                         case "enum": | ||||
|                             return this.resolveEnumVariant(k.item, k.kind, seg); | ||||
|                         case "struct": | ||||
|                         case "fn": | ||||
|                         case "variant": | ||||
|                         case "field": | ||||
|                         case "local": | ||||
|                             return todo(); | ||||
|                     } | ||||
|                     exhausted(); | ||||
|                 }, this.syms.getTy(path.segments[0].ident)); | ||||
|         } | ||||
|         this.pathResols.set(path.id, res); | ||||
|         return res; | ||||
|     } | ||||
| 
 | ||||
|     private resolveTyPath(path: ast.Path): Resolve { | ||||
|         const res = path.segments | ||||
|             .slice(1) | ||||
|             .reduce((inner, seg): Resolve => { | ||||
|                 const k = inner.kind; | ||||
|                 switch (k.tag) { | ||||
|                     case "error": | ||||
|                         return innerRes; | ||||
|                         return inner; | ||||
|                     case "enum": | ||||
|                         return this.resolveEnumVariant(k.item, k.kind, seg); | ||||
|                     case "struct": | ||||
|                     case "fn": | ||||
|                         this.ctx.report({ | ||||
|                             severity: "error", | ||||
|                             file: this.currentFile, | ||||
|                             span: seg.ident.span, | ||||
|                             msg: "function, not pathable", | ||||
|                         }); | ||||
|                         return ResolveError(seg.ident); | ||||
|                     case "variant": | ||||
|                     case "field": | ||||
|                     case "local": | ||||
|                         this.ctx.report({ | ||||
|                             severity: "error", | ||||
|                             file: this.currentFile, | ||||
|                             span: seg.ident.span, | ||||
|                             msg: "local variable, not pathable", | ||||
|                         }); | ||||
|                         return ResolveError(seg.ident); | ||||
|                         return todo(); | ||||
|                 } | ||||
|                 exhausted(k); | ||||
|                 exhausted(); | ||||
|             }, this.syms.getTy(path.segments[0].ident)); | ||||
|         this.pathResols.set(path.id, res); | ||||
|         return res; | ||||
|     } | ||||
| 
 | ||||
|     private resolveEnumVariant( | ||||
|         item: ast.Item, | ||||
|         kind: ast.EnumItem, | ||||
|         seg: ast.PathSegment, | ||||
|     ): Resolve { | ||||
|         const { ident } = seg; | ||||
|         const found = kind.variants | ||||
|             .map((v, idx) => [v, idx] as const) | ||||
|             .find(([variant]) => variant.ident.id === ident.id); | ||||
|         if (!found) { | ||||
|             this.report( | ||||
|                 `enum ${item.ident.text} has no member '${ident.text}'`, | ||||
|                 seg.span, | ||||
|             ); | ||||
|             return ResolveError(ident); | ||||
|         } | ||||
|         const [variant, variantIdx] = found; | ||||
|         return { | ||||
|             ident, | ||||
|             kind: { tag: "variant", item, kind, variant, variantIdx }, | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     private report(msg: string, span: Span) { | ||||
|         this.ctx.report({ | ||||
|             severity: "error", | ||||
|  | ||||
| @ -59,7 +59,9 @@ export class HirStringifyer { | ||||
|             case "enum": | ||||
|                 return todo(); | ||||
|             case "struct": | ||||
|                 return todo(); | ||||
|                 return `struct ${ident}: ${ | ||||
|                     this.ty(this.ch.structItemTy(item, k)) | ||||
|                 };`;
 | ||||
|             case "fn": { | ||||
|                 const ty = this.ch.fnItemTy(item, k); | ||||
|                 if (ty.kind.tag !== "fn") { | ||||
| @ -98,7 +100,19 @@ export class HirStringifyer { | ||||
|             case "group": | ||||
|             case "array": | ||||
|             case "repeat": | ||||
|                 return todo(k.tag); | ||||
|             case "struct": | ||||
|                 return `${k.path ? `${this.path(k.path)}` : "struct "} {${ | ||||
|                     [ | ||||
|                         k.fields | ||||
|                             .map((field) => | ||||
|                                 `${indent(d + 1)}${field.ident.text}: ${ | ||||
|                                     this.expr(field.expr, d + 1) | ||||
|                                 },` | ||||
|                             ) | ||||
|                             .join("\n"), | ||||
|                     ].map((s) => `\n${s}\n${indent(d)}`) | ||||
|                 }}`;
 | ||||
|             case "ref": | ||||
|             case "deref": | ||||
|             case "elem": | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| import { Ctx, exhausted } from "@slige/common"; | ||||
| import { Ty } from "./ty.ts"; | ||||
| import { Ty, VariantData } from "./ty.ts"; | ||||
| 
 | ||||
| export function tyToString(ctx: Ctx, ty: Ty): string { | ||||
|     const k = ty.kind; | ||||
| @ -22,6 +22,10 @@ export function tyToString(ctx: Ctx, ty: Ty): string { | ||||
|             const reTy = tyToString(ctx, k.returnTy); | ||||
|             return `fn ${identText}(${params}) -> ${reTy}`; | ||||
|         } | ||||
|         case "struct": { | ||||
|             const identText = ctx.identText(k.item.ident.id); | ||||
|             return identText; | ||||
|         } | ||||
|     } | ||||
|     exhausted(k); | ||||
| } | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| import * as ast from "@slige/ast"; | ||||
| import { IdentId } from "@slige/common"; | ||||
| 
 | ||||
| export type Ty = { | ||||
|     kind: TyKind; | ||||
| @ -18,4 +19,27 @@ export type TyKind = | ||||
|         kind: ast.FnItem; | ||||
|         params: Ty[]; | ||||
|         returnTy: Ty; | ||||
|     } | ||||
|     | { | ||||
|         tag: "struct"; | ||||
|         item: ast.Item; | ||||
|         kind: ast.StructItem; | ||||
|         data: VariantData; | ||||
|     }; | ||||
| 
 | ||||
| export type VariantData = | ||||
|     | { tag: "error" } | ||||
|     | { tag: "unit" } | ||||
|     | { tag: "tuple"; elems: ElemDef[] } | ||||
|     | { tag: "struct"; fields: FieldDef[] }; | ||||
| 
 | ||||
| export type ElemDef = { | ||||
|     ty: Ty; | ||||
|     pub: boolean; | ||||
| }; | ||||
| 
 | ||||
| export type FieldDef = { | ||||
|     ident: IdentId; | ||||
|     ty: Ty; | ||||
|     pub: boolean; | ||||
| }; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user