type alias

This commit is contained in:
sfja 2024-12-31 05:32:41 +01:00
parent f56df189c4
commit 26acdc10ca
9 changed files with 135 additions and 66 deletions

View File

@ -31,6 +31,7 @@ export type StmtKind =
vtype?: VType;
}
| { type: "let"; param: Param; value: Expr }
| { type: "type_alias"; param: Param }
| { type: "assign"; assignType: AssignType; subject: Expr; value: Expr }
| { type: "expr"; expr: Expr };
@ -124,6 +125,7 @@ export type Sym = {
export type SymKind =
| { type: "let"; stmt: Stmt; param: Param }
| { type: "let_static"; stmt: Stmt; param: Param }
| { type: "type_alias"; stmt: Stmt; param: Param }
| { type: "fn"; stmt: Stmt }
| { type: "fn_param"; param: Param }
| { type: "closure"; inner: Sym }

View File

@ -13,6 +13,7 @@ export interface AstVisitor<Args extends unknown[] = []> {
visitReturnStmt?(stmt: Stmt, ...args: Args): VisitRes;
visitFnStmt?(stmt: Stmt, ...args: Args): VisitRes;
visitLetStmt?(stmt: Stmt, ...args: Args): VisitRes;
visitTypeAliasStmt?(stmt: Stmt, ...args: Args): VisitRes;
visitAssignStmt?(stmt: Stmt, ...args: Args): VisitRes;
visitExprStmt?(stmt: Stmt, ...args: Args): VisitRes;
visitExpr?(expr: Expr, ...args: Args): VisitRes;
@ -106,6 +107,10 @@ export function visitStmt<Args extends unknown[] = []>(
visitParam(stmt.kind.param, v, ...args);
visitExpr(stmt.kind.value, v, ...args);
break;
case "type_alias":
if (v.visitTypeAliasStmt?.(stmt, ...args) == "stop") return;
visitParam(stmt.kind.param, v, ...args);
break;
case "assign":
if (v.visitAssignStmt?.(stmt, ...args) == "stop") return;
visitExpr(stmt.kind.subject, v, ...args);

View File

@ -20,17 +20,15 @@ export class Checker {
public constructor(private reporter: Reporter) {}
public check(stmts: Stmt[]) {
this.checkFnHeaders(stmts);
this.scout(stmts);
for (const stmt of stmts) {
this.checkStmt(stmt);
}
}
private checkFnHeaders(stmts: Stmt[]) {
private scout(stmts: Stmt[]) {
for (const stmt of stmts) {
if (stmt.kind.type !== "fn") {
continue;
}
if (stmt.kind.type === "fn") {
let genericParams: VTypeGenericParam[] | undefined;
if (stmt.kind.genericParams !== undefined) {
genericParams = [];
@ -45,7 +43,10 @@ export class Checker {
const params: VTypeParam[] = [];
for (const param of stmt.kind.params) {
if (param.etype === undefined) {
this.report("parameter types must be defined", param.pos);
this.report(
"parameter types must be defined",
param.pos,
);
stmt.kind.vtype = { type: "error" };
}
const vtype = this.checkEType(param.etype!);
@ -62,6 +63,13 @@ export class Checker {
returnType,
stmtId: stmt.id,
};
} else if (stmt.kind.type === "type_alias") {
if (!stmt.kind.param.etype) {
this.report("no type specified", stmt.pos);
return;
}
stmt.kind.param.vtype = this.checkEType(stmt.kind.param.etype);
}
}
}
@ -82,6 +90,8 @@ export class Checker {
return this.checkFnStmt(stmt);
case "let":
return this.checkLetStmt(stmt);
case "type_alias":
return this.checkTypeAliasStmt(stmt);
case "assign":
return this.checkAssignStmt(stmt);
case "expr":
@ -94,7 +104,7 @@ export class Checker {
throw new Error();
}
const { ast } = stmt.kind.mod;
this.checkFnHeaders(ast);
this.scout(ast);
for (const stmt of ast) {
this.checkStmt(stmt);
}
@ -146,8 +156,8 @@ export class Checker {
if (!vtypesEqual(exprType, returnType)) {
this.report(
`incompatible return type` +
`, got ${exprType}` +
`, expected ${returnType}`,
`, expected ${vtypeToString(returnType)}` +
`, got ${vtypeToString(exprType)}`,
pos,
);
}
@ -210,6 +220,18 @@ export class Checker {
stmt.kind.param.vtype = value;
}
public checkTypeAliasStmt(stmt: Stmt) {
if (stmt.kind.type !== "type_alias") {
throw new Error();
}
const pos = stmt.pos;
if (!stmt.kind.param.etype) {
this.report("no type specified", pos);
return;
}
stmt.kind.param.vtype = this.checkEType(stmt.kind.param.etype);
}
public checkAssignStmt(stmt: Stmt) {
if (stmt.kind.type !== "assign") {
throw new Error();
@ -373,6 +395,8 @@ export class Checker {
switch (sym.type) {
case "let":
return sym.param.vtype!;
case "type_alias":
return sym.param.vtype!;
case "fn": {
const fnStmt = sym.stmt!;
if (fnStmt.kind.type !== "fn") {
@ -923,7 +947,7 @@ export class Checker {
if (expr.kind.type !== "block") {
throw new Error();
}
this.checkFnHeaders(expr.kind.stmts);
this.scout(expr.kind.stmts);
for (const stmt of expr.kind.stmts) {
this.checkStmt(stmt);
}
@ -949,6 +973,9 @@ export class Checker {
return { type: "error" };
}
if (etype.kind.type === "sym") {
if (etype.kind.sym.type === "type_alias") {
return etype.kind.sym.param.vtype!;
}
if (etype.kind.sym.type === "generic") {
const { id: globalId, ident } = etype.kind.sym.genericParam;
if (!this.globalIdToGenericParamMap.has(globalId)) {

View File

@ -8,7 +8,6 @@ import {
} from "../ast.ts";
import {
AstVisitor,
visitExpr,
visitField,
VisitRes,
visitStmts,
@ -46,10 +45,6 @@ export class StructLiteralDesugarer implements AstVisitor {
ident,
});
// Yes, I know this isn't a deep clone,
// but I don't really need it to be.
const oldExpr = { ...expr };
const fields = expr.kind.fields;
expr.kind = {
@ -67,7 +62,10 @@ export class StructLiteralDesugarer implements AstVisitor {
type: "etype_args",
subject: std("struct_new"),
etypeArgs: [
EType({ type: "type_of", expr: oldExpr }),
EType({
type: "type_of",
expr: Expr({ ...expr.kind }),
}),
],
}),
args: [],

View File

@ -50,6 +50,7 @@ export class Lexer {
"mod",
"pub",
"use",
"type_alias",
];
if (keywords.includes(value)) {
return this.token(value, pos);

View File

@ -49,7 +49,7 @@ export class Parser {
) {
return this.parseItemStmt();
} else if (
["let", "return", "break"].some((tt) => this.test(tt))
["let", "type_alias", "return", "break"].some((tt) => this.test(tt))
) {
const expr = this.parseSingleLineBlockStmt();
this.eatSemicolon();
@ -92,6 +92,9 @@ 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();
}
@ -129,7 +132,8 @@ export class Parser {
) {
stmts.push(this.parseItemStmt());
} else if (
["let", "return", "break"].some((tt) => this.test(tt))
["let", "type_alias", "return", "break"]
.some((tt) => this.test(tt))
) {
stmts.push(this.parseSingleLineBlockStmt());
this.eatSemicolon();
@ -413,6 +417,17 @@ export class Parser {
return this.stmt({ type: "let", param, value }, pos);
}
private parseTypeAlias(): Stmt {
const pos = this.pos();
this.step();
const paramResult = this.parseParam();
if (!paramResult.ok) {
return this.stmt({ type: "error" }, pos);
}
const param = paramResult.value;
return this.stmt({ type: "type_alias", param }, pos);
}
private parseAssign(): Stmt {
const pos = this.pos();
const subject = this.parseExpr();

View File

@ -24,18 +24,49 @@ export class Resolver implements AstVisitor<[Syms]> {
public resolve(stmts: Stmt[]): VisitRes {
const syms = new EntryModSyms();
this.scoutFnStmts(stmts, syms);
this.scout(stmts, syms);
visitStmts(stmts, this, syms);
return "stop";
}
private scout(stmts: Stmt[], syms: Syms) {
for (const stmt of stmts) {
if (stmt.kind.type === "fn") {
if (syms.definedLocally(stmt.kind.ident)) {
this.reportAlreadyDefined(stmt.kind.ident, stmt.pos, syms);
return;
}
const ident = stmt.kind.ident;
syms.define(ident, {
ident: stmt.kind.ident,
type: "fn",
pos: stmt.pos,
stmt,
});
} else if (stmt.kind.type === "type_alias") {
const ident = stmt.kind.param.ident;
if (syms.definedLocally(ident)) {
this.reportAlreadyDefined(ident, stmt.pos, syms);
return;
}
syms.define(ident, {
ident,
type: "type_alias",
pos: stmt.kind.param.pos,
stmt,
param: stmt.kind.param,
});
}
}
}
visitModStmt(stmt: Stmt, syms: Syms): VisitRes {
if (stmt.kind.type !== "mod") {
throw new Error("expected let statement");
}
const modSyms = new ModSyms(syms);
const { mod, ident } = stmt.kind;
this.scoutFnStmts(mod.ast, modSyms);
this.scout(mod.ast, modSyms);
visitStmts(mod.ast, this, modSyms);
if (syms.definedLocally(ident)) {
@ -72,23 +103,11 @@ export class Resolver implements AstVisitor<[Syms]> {
return "stop";
}
private scoutFnStmts(stmts: Stmt[], syms: Syms) {
for (const stmt of stmts) {
if (stmt.kind.type !== "fn") {
continue;
}
if (syms.definedLocally(stmt.kind.ident)) {
this.reportAlreadyDefined(stmt.kind.ident, stmt.pos, syms);
return;
}
const ident = stmt.kind.ident;
syms.define(ident, {
ident: stmt.kind.ident,
type: "fn",
pos: stmt.pos,
stmt,
});
visitTypeAliasStmt(stmt: Stmt, syms: Syms): VisitRes {
if (stmt.kind.type !== "type_alias") {
throw new Error("expected type_alias statement");
}
// nothing to do here
}
visitFnStmt(stmt: Stmt, syms: Syms): VisitRes {
@ -186,7 +205,7 @@ export class Resolver implements AstVisitor<[Syms]> {
throw new Error();
}
const childSyms = new LeafSyms(syms);
this.scoutFnStmts(expr.kind.stmts, childSyms);
this.scout(expr.kind.stmts, childSyms);
visitStmts(expr.kind.stmts, this, childSyms);
if (expr.kind.expr) {
visitExpr(expr.kind.expr, this, childSyms);

View File

@ -205,7 +205,7 @@ private:
}
}
size_t max_size = 8;
size_t max_size = 512;
std::vector<AllocItem> heap_1;
std::vector<AllocItem> heap_2;

View File

@ -54,6 +54,8 @@ pub fn itos(number: int) -> string {}
#[builtin(StringToInt)]
pub fn stoi(str: string) -> int {}
pub fn ctos(ch: int) -> string { string_push_char("", ch) }
pub fn stdin() -> int { 0 }
pub fn stdout() -> int { 1 }
pub fn stderr() -> int { 2 }
@ -81,7 +83,7 @@ pub fn read_text_file(filename: string) -> string {
}
pub fn input(prompt: string) -> string {
print("> ");
print(prompt);
file_flush(stdout());
file_read_line(stdin())
}