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