diff --git a/slige/compiler/check/checker.ts b/slige/compiler/check/checker.ts index 51209c7..eb67b33 100644 --- a/slige/compiler/check/checker.ts +++ b/slige/compiler/check/checker.ts @@ -5,24 +5,28 @@ import { exhausted, File, IdMap, + IdSet, + Ok, Res, Span, todo, } from "@slige/common"; -import { Resols } from "@slige/resolve"; +import * as resolve from "@slige/resolve"; import { Ty, tyToString } from "@slige/ty"; export class Checker { + private stmtChecked = new IdSet(); private itemTys = new IdMap(); private exprTys = new IdMap(); private tyTys = new IdMap(); + private patTys = new IdMap(); private currentFile: File; public constructor( private ctx: Ctx, private entryFileAst: ast.File, - private resols: Resols, + private resols: resolve.Resols, ) { this.currentFile = ctx.entryFile(); } @@ -37,8 +41,51 @@ export class Checker { private checkStmts(stmts: ast.Stmt[]) { } + private checkLetStmtTy(stmt: ast.Stmt, kind: ast.LetStmt) { + if (this.stmtChecked.has(stmt.id)) { + return; + } + + const exprTy = kind.expr && Ok(this.exprTy(kind.expr)); + const tyTy = kind.ty && Ok(this.tyTy(kind.ty)); + const ty = exprTy && tyTy && this.resolveTys(exprTy.val, tyTy.val); + + if (ty === undefined) { + this.report("type amfibious, specify type or value", stmt.span); + return Ty({ tag: "error" }); + } + + if (!ty.ok) { + this.report(ty.val, stmt.span); + return Ty({ tag: "error" }); + } + + const res = this.assignPatTy(kind.pat, ty.val); + if (!res.ok) { + this.report(res.val, stmt.span); + return Ty({ tag: "error" }); + } + + this.stmtChecked.add(stmt.id); + } + + private assignPatTy(pat: ast.Pat, ty: Ty): Res { + const k = pat.kind; + switch (k.tag) { + case "error": + // don't report, already reported + return Res.Ok(undefined); + case "bind": + this.patTys.set(pat.id, ty); + return Ok(undefined); + case "path": + return todo(); + } + exhausted(k); + } + 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); } private checkFnItem(item: ast.Item, kind: ast.FnItem): Ty { @@ -128,7 +175,8 @@ export class Checker { return resu.val; } case "local": { - const patResu = this.resols.patRes(); + const patRes = this.resols.patRes(res.kind.id); + const ty = this.patTy(patRes.pat); const resu = this.resolveTys(ty, expected); if (!resu.ok) { this.report(resu.val, expr.span); @@ -166,13 +214,23 @@ export class Checker { exhausted(k); } - private report(msg: string, span: Span) { - this.ctx.report({ - severity: "error", - file: this.currentFile, - span, - msg, - }); + public patTy(pat: ast.Pat): Ty { + return this.patTys.get(pat.id) || + this.checkPat(pat); + } + + private checkPat(pat: ast.Pat): Ty { + const patRes = this.resols.patRes(pat.id); + const k = pat.kind; + switch (k.tag) { + case "error": + return todo(); + case "bind": + return todo(); + case "path": + return todo(); + } + exhausted(k); } private resolveTys(a: Ty, b: Ty): Res { @@ -211,4 +269,13 @@ export class Checker { } exhausted(a.kind); } + + private report(msg: string, span: Span) { + this.ctx.report({ + severity: "error", + file: this.currentFile, + span, + msg, + }); + } } diff --git a/slige/compiler/common/ids.ts b/slige/compiler/common/ids.ts index abcaade..f17fcec 100644 --- a/slige/compiler/common/ids.ts +++ b/slige/compiler/common/ids.ts @@ -1,3 +1,5 @@ +import { todo } from "./util.ts"; + // export type File = IdBase & { readonly _: unique symbol }; @@ -27,6 +29,74 @@ export class Ids { return idFromRaw(rawId); } } +export class IdSet implements Set { + private set = new Set>(); + + add(value: Id): this { + this.set.add(idRaw(value)); + return this; + } + clear(): void { + this.set.clear(); + } + delete(value: Id): boolean { + return this.set.delete(idRaw(value)); + } + forEach( + callbackfn: (value: Id, value2: Id, set: Set) => void, + thisArg?: unknown, + ): void { + this.set.forEach( + (v1, v2) => callbackfn(idFromRaw(v1), idFromRaw(v2), this), + thisArg, + ); + } + has(value: Id): boolean { + return this.set.has(idRaw(value)); + } + get size(): number { + return this.set.size; + } + entries(): SetIterator<[Id, Id]> { + return this.set.entries() + .map(([v1, v2]) => [idFromRaw(v1), idFromRaw(v2)]); + } + keys(): SetIterator { + return this.set.keys() + .map((v) => idFromRaw(v)); + } + values(): SetIterator { + return this.set.values() + .map((v) => idFromRaw(v)); + } + union(_other: ReadonlySetLike): Set { + return todo(); + } + intersection(_other: ReadonlySetLike): Set { + return todo(); + } + difference(_other: ReadonlySetLike): Set { + return todo(); + } + symmetricDifference(_other: ReadonlySetLike): Set { + return todo(); + } + isSubsetOf(_other: ReadonlySetLike): boolean { + return todo(); + } + isSupersetOf(_other: ReadonlySetLike): boolean { + return todo(); + } + isDisjointFrom(_other: ReadonlySetLike): boolean { + return todo(); + } + [Symbol.iterator](): SetIterator { + return this.set[Symbol.iterator]().map((v) => idFromRaw(v)); + } + get [Symbol.toStringTag](): string { + return this.set[Symbol.toStringTag]; + } +} export class IdMap implements Map { private map = new Map, V>(); diff --git a/slige/compiler/middle/ast_lower.ts b/slige/compiler/middle/ast_lower.ts index 38824e9..2c970e1 100644 --- a/slige/compiler/middle/ast_lower.ts +++ b/slige/compiler/middle/ast_lower.ts @@ -1,7 +1,7 @@ import * as ast from "@slige/ast"; import { Checker } from "@slige/check"; -import { Ctx, exhausted, IdMap, Ids, Res, todo } from "@slige/common"; -import { LocalId as ReLocalId, Resols } from "@slige/resolve"; +import { AstId, Ctx, exhausted, IdMap, Ids, Res, todo } from "@slige/common"; +import { Resols } from "@slige/resolve"; import { Ty } from "@slige/ty"; import { BinaryType, Operand, StmtKind, TerKind } from "./mir.ts"; import { Block, BlockId, Fn, Local, LocalId, RVal, Stmt, Ter } from "./mir.ts"; @@ -32,7 +32,7 @@ export class FnLowerer { private currentBlock?: Block; - private reLocals = new IdMap(); + private reLocals = new IdMap(); public constructor( private ctx: Ctx, diff --git a/slige/compiler/resolve/cx.ts b/slige/compiler/resolve/cx.ts index d02a240..e782633 100644 --- a/slige/compiler/resolve/cx.ts +++ b/slige/compiler/resolve/cx.ts @@ -17,16 +17,15 @@ export type Resolve = { export type ResolveKind = | { tag: "error" } | { tag: "fn"; item: ast.Item; kind: ast.FnItem } - | { tag: "local"; id: LocalId }; + | { tag: "local"; id: AstId }; -export type PatResolve = - | { tag: "param"; paramIdx: number } - | { tag: "let"; stmt: ast.Stmt; kind: ast.LetStmt }; +export type PatResolve = { + pat: ast.Pat; + kind: PatResolveKind; +}; -export type LocalId = IdBase & { readonly _: unique symbol }; - -export type Local = - | { tag: "param"; paramIdx: number } +export type PatResolveKind = + | { tag: "fn_param"; paramIdx: number } | { tag: "let"; stmt: ast.Stmt; kind: ast.LetStmt }; export const ResolveError = (ident: ast.Ident): Resolve => ({ diff --git a/slige/compiler/resolve/resolver.ts b/slige/compiler/resolve/resolver.ts index 588267e..ecc0e71 100644 --- a/slige/compiler/resolve/resolver.ts +++ b/slige/compiler/resolve/resolver.ts @@ -1,16 +1,26 @@ import * as ast from "@slige/ast"; -import { AstId, Ctx, exhausted, File, IdMap, Ids, todo } from "@slige/common"; +import { + AstId, + Ctx, + exhausted, + File, + IdMap, + Ids, + Res, + Span, + todo, +} from "@slige/common"; import { FnSyms, - LocalId, LocalSyms, PatResolve, + PatResolveKind, + Redef, Resolve, ResolveError, RootSyms, Syms, } from "./cx.ts"; -export { type LocalId } from "./cx.ts"; export class Resols { public constructor( @@ -41,9 +51,7 @@ export class Resolver implements ast.Visitor { private exprResols = new IdMap(); private patResols = new IdMap(); - private patResolveStack: PatResolve[] = []; - - private localIds = new Ids(); + private patResolveStack: PatResolveKind[] = []; public constructor( private ctx: Ctx, @@ -115,7 +123,7 @@ export class Resolver implements ast.Visitor { this.syms = new FnSyms(this.syms); this.syms = new LocalSyms(this.syms); for (const [paramIdx, param] of kind.params.entries()) { - this.patResolveStack.push({ tag: "param", paramIdx }); + this.patResolveStack.push({ tag: "fn_param", paramIdx }); ast.visitParam(this, param); this.patResolveStack.pop(); } @@ -164,19 +172,14 @@ export class Resolver implements ast.Visitor { } visitBindPat(pat: ast.Pat, kind: ast.BindPat): ast.VisitRes { - this.patResols.set(pat.id, this.patResolveStack.at(-1)!); + this.patResols.set(pat.id, { pat, kind: this.patResolveStack.at(-1)! }); const res = this.syms.defVal(kind.ident, { tag: "local", - id: this.localIds.nextThenStep(), + id: pat.id, }); if (!res.ok) { const text = this.ctx.identText(kind.ident.id); - this.ctx.report({ - severity: "error", - file: this.currentFile, - span: kind.ident.span, - msg: `redefinition of value '${text}'`, - }); + this.report(`redefinition of value '${text}'`, kind.ident.span); } return "stop"; } @@ -221,4 +224,13 @@ export class Resolver implements ast.Visitor { }, this.syms.getTy(path.segments[0].ident)); return res; } + + private report(msg: string, span: Span) { + this.ctx.report({ + severity: "error", + file: this.currentFile, + span, + msg, + }); + } }