type alias
This commit is contained in:
parent
f56df189c4
commit
26acdc10ca
@ -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 }
|
||||
|
@ -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);
|
||||
|
@ -20,48 +20,56 @@ 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;
|
||||
}
|
||||
let genericParams: VTypeGenericParam[] | undefined;
|
||||
if (stmt.kind.genericParams !== undefined) {
|
||||
genericParams = [];
|
||||
for (const etypeParam of stmt.kind.genericParams) {
|
||||
const id = genericParams.length;
|
||||
const globalId = etypeParam.id;
|
||||
const param = { id, ident: etypeParam.ident };
|
||||
genericParams.push(param);
|
||||
this.globalIdToGenericParamMap.set(globalId, param);
|
||||
if (stmt.kind.type === "fn") {
|
||||
let genericParams: VTypeGenericParam[] | undefined;
|
||||
if (stmt.kind.genericParams !== undefined) {
|
||||
genericParams = [];
|
||||
for (const etypeParam of stmt.kind.genericParams) {
|
||||
const id = genericParams.length;
|
||||
const globalId = etypeParam.id;
|
||||
const param = { id, ident: etypeParam.ident };
|
||||
genericParams.push(param);
|
||||
this.globalIdToGenericParamMap.set(globalId, param);
|
||||
}
|
||||
}
|
||||
}
|
||||
const params: VTypeParam[] = [];
|
||||
for (const param of stmt.kind.params) {
|
||||
if (param.etype === undefined) {
|
||||
this.report("parameter types must be defined", param.pos);
|
||||
stmt.kind.vtype = { type: "error" };
|
||||
const params: VTypeParam[] = [];
|
||||
for (const param of stmt.kind.params) {
|
||||
if (param.etype === undefined) {
|
||||
this.report(
|
||||
"parameter types must be defined",
|
||||
param.pos,
|
||||
);
|
||||
stmt.kind.vtype = { type: "error" };
|
||||
}
|
||||
const vtype = this.checkEType(param.etype!);
|
||||
param.vtype = vtype;
|
||||
params.push({ ident: param.ident, vtype });
|
||||
}
|
||||
const vtype = this.checkEType(param.etype!);
|
||||
param.vtype = vtype;
|
||||
params.push({ ident: param.ident, vtype });
|
||||
const returnType: VType = stmt.kind.returnType
|
||||
? this.checkEType(stmt.kind.returnType)
|
||||
: { type: "null" };
|
||||
stmt.kind.vtype = {
|
||||
type: "fn",
|
||||
genericParams,
|
||||
params,
|
||||
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);
|
||||
}
|
||||
const returnType: VType = stmt.kind.returnType
|
||||
? this.checkEType(stmt.kind.returnType)
|
||||
: { type: "null" };
|
||||
stmt.kind.vtype = {
|
||||
type: "fn",
|
||||
genericParams,
|
||||
params,
|
||||
returnType,
|
||||
stmtId: stmt.id,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)) {
|
||||
|
@ -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: [],
|
||||
|
@ -50,6 +50,7 @@ export class Lexer {
|
||||
"mod",
|
||||
"pub",
|
||||
"use",
|
||||
"type_alias",
|
||||
];
|
||||
if (keywords.includes(value)) {
|
||||
return this.token(value, pos);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user