compiler: add struct types

This commit is contained in:
SimonFJ20 2025-02-12 23:53:33 +01:00
parent a97a128336
commit 53d3777ee0
16 changed files with 628 additions and 141 deletions

View File

@ -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;
};

View File

@ -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 };
}
}

View File

@ -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 = [],
>(

View File

@ -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);
}

View File

@ -4,6 +4,7 @@
"./check",
"./middle",
"./parse",
"./pat",
"./resolve",
"./ty",
"./common",

View File

@ -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);

View File

@ -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 = {

View File

@ -0,0 +1,7 @@
export type Ctor = {
kind: CtorKind;
};
export type CtorKind =
| { tag: "error" }
| { tag: "struct" };

View File

@ -0,0 +1,4 @@
{
"name": "@slige/pat",
"exports": "./mod.ts",
}

View File

@ -0,0 +1,2 @@
export * as ctor from "./ctor.ts";
export * from "./ctor.ts";

View File

@ -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 };
}

View File

@ -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();

View File

@ -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",

View File

@ -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":

View File

@ -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);
}

View File

@ -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;
};