mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-04-28 08:44:06 +02: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":
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
const pathRes = this.resolveInnerPath(kind.path);
|
const res = this.resolveValPath(kind.path);
|
||||||
switch (pathRes.kind.tag) {
|
this.exprResols.set(expr.id, res);
|
||||||
case "error":
|
return "stop";
|
||||||
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();
|
||||||
|
}
|
||||||
visitTypeAliasItem(item: ast.Item, kind: ast.TypeAliasItem): ast.VisitRes {
|
this.resolveValPath(kind.path);
|
||||||
todo();
|
return "stop";
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
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) {
|
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 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