mirror of
https://git.sfja.dk/Mikkel/slige.git
synced 2025-01-18 10:56:30 +00:00
type alias
This commit is contained in:
parent
f56df189c4
commit
26acdc10ca
@ -31,6 +31,7 @@ export type StmtKind =
|
|||||||
vtype?: VType;
|
vtype?: VType;
|
||||||
}
|
}
|
||||||
| { type: "let"; param: Param; value: Expr }
|
| { type: "let"; param: Param; value: Expr }
|
||||||
|
| { type: "type_alias"; param: Param }
|
||||||
| { type: "assign"; assignType: AssignType; subject: Expr; value: Expr }
|
| { type: "assign"; assignType: AssignType; subject: Expr; value: Expr }
|
||||||
| { type: "expr"; expr: Expr };
|
| { type: "expr"; expr: Expr };
|
||||||
|
|
||||||
@ -124,6 +125,7 @@ export type Sym = {
|
|||||||
export type SymKind =
|
export type SymKind =
|
||||||
| { type: "let"; stmt: Stmt; param: Param }
|
| { type: "let"; stmt: Stmt; param: Param }
|
||||||
| { type: "let_static"; stmt: Stmt; param: Param }
|
| { type: "let_static"; stmt: Stmt; param: Param }
|
||||||
|
| { type: "type_alias"; stmt: Stmt; param: Param }
|
||||||
| { type: "fn"; stmt: Stmt }
|
| { type: "fn"; stmt: Stmt }
|
||||||
| { type: "fn_param"; param: Param }
|
| { type: "fn_param"; param: Param }
|
||||||
| { type: "closure"; inner: Sym }
|
| { type: "closure"; inner: Sym }
|
||||||
|
@ -13,6 +13,7 @@ export interface AstVisitor<Args extends unknown[] = []> {
|
|||||||
visitReturnStmt?(stmt: Stmt, ...args: Args): VisitRes;
|
visitReturnStmt?(stmt: Stmt, ...args: Args): VisitRes;
|
||||||
visitFnStmt?(stmt: Stmt, ...args: Args): VisitRes;
|
visitFnStmt?(stmt: Stmt, ...args: Args): VisitRes;
|
||||||
visitLetStmt?(stmt: Stmt, ...args: Args): VisitRes;
|
visitLetStmt?(stmt: Stmt, ...args: Args): VisitRes;
|
||||||
|
visitTypeAliasStmt?(stmt: Stmt, ...args: Args): VisitRes;
|
||||||
visitAssignStmt?(stmt: Stmt, ...args: Args): VisitRes;
|
visitAssignStmt?(stmt: Stmt, ...args: Args): VisitRes;
|
||||||
visitExprStmt?(stmt: Stmt, ...args: Args): VisitRes;
|
visitExprStmt?(stmt: Stmt, ...args: Args): VisitRes;
|
||||||
visitExpr?(expr: Expr, ...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);
|
visitParam(stmt.kind.param, v, ...args);
|
||||||
visitExpr(stmt.kind.value, v, ...args);
|
visitExpr(stmt.kind.value, v, ...args);
|
||||||
break;
|
break;
|
||||||
|
case "type_alias":
|
||||||
|
if (v.visitTypeAliasStmt?.(stmt, ...args) == "stop") return;
|
||||||
|
visitParam(stmt.kind.param, v, ...args);
|
||||||
|
break;
|
||||||
case "assign":
|
case "assign":
|
||||||
if (v.visitAssignStmt?.(stmt, ...args) == "stop") return;
|
if (v.visitAssignStmt?.(stmt, ...args) == "stop") return;
|
||||||
visitExpr(stmt.kind.subject, v, ...args);
|
visitExpr(stmt.kind.subject, v, ...args);
|
||||||
|
@ -20,48 +20,56 @@ export class Checker {
|
|||||||
public constructor(private reporter: Reporter) {}
|
public constructor(private reporter: Reporter) {}
|
||||||
|
|
||||||
public check(stmts: Stmt[]) {
|
public check(stmts: Stmt[]) {
|
||||||
this.checkFnHeaders(stmts);
|
this.scout(stmts);
|
||||||
for (const stmt of stmts) {
|
for (const stmt of stmts) {
|
||||||
this.checkStmt(stmt);
|
this.checkStmt(stmt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkFnHeaders(stmts: Stmt[]) {
|
private scout(stmts: Stmt[]) {
|
||||||
for (const stmt of stmts) {
|
for (const stmt of stmts) {
|
||||||
if (stmt.kind.type !== "fn") {
|
if (stmt.kind.type === "fn") {
|
||||||
continue;
|
let genericParams: VTypeGenericParam[] | undefined;
|
||||||
}
|
if (stmt.kind.genericParams !== undefined) {
|
||||||
let genericParams: VTypeGenericParam[] | undefined;
|
genericParams = [];
|
||||||
if (stmt.kind.genericParams !== undefined) {
|
for (const etypeParam of stmt.kind.genericParams) {
|
||||||
genericParams = [];
|
const id = genericParams.length;
|
||||||
for (const etypeParam of stmt.kind.genericParams) {
|
const globalId = etypeParam.id;
|
||||||
const id = genericParams.length;
|
const param = { id, ident: etypeParam.ident };
|
||||||
const globalId = etypeParam.id;
|
genericParams.push(param);
|
||||||
const param = { id, ident: etypeParam.ident };
|
this.globalIdToGenericParamMap.set(globalId, param);
|
||||||
genericParams.push(param);
|
}
|
||||||
this.globalIdToGenericParamMap.set(globalId, param);
|
|
||||||
}
|
}
|
||||||
}
|
const params: VTypeParam[] = [];
|
||||||
const params: VTypeParam[] = [];
|
for (const param of stmt.kind.params) {
|
||||||
for (const param of stmt.kind.params) {
|
if (param.etype === undefined) {
|
||||||
if (param.etype === undefined) {
|
this.report(
|
||||||
this.report("parameter types must be defined", param.pos);
|
"parameter types must be defined",
|
||||||
stmt.kind.vtype = { type: "error" };
|
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!);
|
const returnType: VType = stmt.kind.returnType
|
||||||
param.vtype = vtype;
|
? this.checkEType(stmt.kind.returnType)
|
||||||
params.push({ ident: param.ident, vtype });
|
: { 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);
|
return this.checkFnStmt(stmt);
|
||||||
case "let":
|
case "let":
|
||||||
return this.checkLetStmt(stmt);
|
return this.checkLetStmt(stmt);
|
||||||
|
case "type_alias":
|
||||||
|
return this.checkTypeAliasStmt(stmt);
|
||||||
case "assign":
|
case "assign":
|
||||||
return this.checkAssignStmt(stmt);
|
return this.checkAssignStmt(stmt);
|
||||||
case "expr":
|
case "expr":
|
||||||
@ -94,7 +104,7 @@ export class Checker {
|
|||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
const { ast } = stmt.kind.mod;
|
const { ast } = stmt.kind.mod;
|
||||||
this.checkFnHeaders(ast);
|
this.scout(ast);
|
||||||
for (const stmt of ast) {
|
for (const stmt of ast) {
|
||||||
this.checkStmt(stmt);
|
this.checkStmt(stmt);
|
||||||
}
|
}
|
||||||
@ -146,8 +156,8 @@ export class Checker {
|
|||||||
if (!vtypesEqual(exprType, returnType)) {
|
if (!vtypesEqual(exprType, returnType)) {
|
||||||
this.report(
|
this.report(
|
||||||
`incompatible return type` +
|
`incompatible return type` +
|
||||||
`, got ${exprType}` +
|
`, expected ${vtypeToString(returnType)}` +
|
||||||
`, expected ${returnType}`,
|
`, got ${vtypeToString(exprType)}`,
|
||||||
pos,
|
pos,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -210,6 +220,18 @@ export class Checker {
|
|||||||
stmt.kind.param.vtype = value;
|
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) {
|
public checkAssignStmt(stmt: Stmt) {
|
||||||
if (stmt.kind.type !== "assign") {
|
if (stmt.kind.type !== "assign") {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
@ -373,6 +395,8 @@ export class Checker {
|
|||||||
switch (sym.type) {
|
switch (sym.type) {
|
||||||
case "let":
|
case "let":
|
||||||
return sym.param.vtype!;
|
return sym.param.vtype!;
|
||||||
|
case "type_alias":
|
||||||
|
return sym.param.vtype!;
|
||||||
case "fn": {
|
case "fn": {
|
||||||
const fnStmt = sym.stmt!;
|
const fnStmt = sym.stmt!;
|
||||||
if (fnStmt.kind.type !== "fn") {
|
if (fnStmt.kind.type !== "fn") {
|
||||||
@ -923,7 +947,7 @@ export class Checker {
|
|||||||
if (expr.kind.type !== "block") {
|
if (expr.kind.type !== "block") {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
this.checkFnHeaders(expr.kind.stmts);
|
this.scout(expr.kind.stmts);
|
||||||
for (const stmt of expr.kind.stmts) {
|
for (const stmt of expr.kind.stmts) {
|
||||||
this.checkStmt(stmt);
|
this.checkStmt(stmt);
|
||||||
}
|
}
|
||||||
@ -949,6 +973,9 @@ export class Checker {
|
|||||||
return { type: "error" };
|
return { type: "error" };
|
||||||
}
|
}
|
||||||
if (etype.kind.type === "sym") {
|
if (etype.kind.type === "sym") {
|
||||||
|
if (etype.kind.sym.type === "type_alias") {
|
||||||
|
return etype.kind.sym.param.vtype!;
|
||||||
|
}
|
||||||
if (etype.kind.sym.type === "generic") {
|
if (etype.kind.sym.type === "generic") {
|
||||||
const { id: globalId, ident } = etype.kind.sym.genericParam;
|
const { id: globalId, ident } = etype.kind.sym.genericParam;
|
||||||
if (!this.globalIdToGenericParamMap.has(globalId)) {
|
if (!this.globalIdToGenericParamMap.has(globalId)) {
|
||||||
|
@ -8,7 +8,6 @@ import {
|
|||||||
} from "../ast.ts";
|
} from "../ast.ts";
|
||||||
import {
|
import {
|
||||||
AstVisitor,
|
AstVisitor,
|
||||||
visitExpr,
|
|
||||||
visitField,
|
visitField,
|
||||||
VisitRes,
|
VisitRes,
|
||||||
visitStmts,
|
visitStmts,
|
||||||
@ -46,10 +45,6 @@ export class StructLiteralDesugarer implements AstVisitor {
|
|||||||
ident,
|
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;
|
const fields = expr.kind.fields;
|
||||||
|
|
||||||
expr.kind = {
|
expr.kind = {
|
||||||
@ -67,7 +62,10 @@ export class StructLiteralDesugarer implements AstVisitor {
|
|||||||
type: "etype_args",
|
type: "etype_args",
|
||||||
subject: std("struct_new"),
|
subject: std("struct_new"),
|
||||||
etypeArgs: [
|
etypeArgs: [
|
||||||
EType({ type: "type_of", expr: oldExpr }),
|
EType({
|
||||||
|
type: "type_of",
|
||||||
|
expr: Expr({ ...expr.kind }),
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
args: [],
|
args: [],
|
||||||
|
@ -50,6 +50,7 @@ export class Lexer {
|
|||||||
"mod",
|
"mod",
|
||||||
"pub",
|
"pub",
|
||||||
"use",
|
"use",
|
||||||
|
"type_alias",
|
||||||
];
|
];
|
||||||
if (keywords.includes(value)) {
|
if (keywords.includes(value)) {
|
||||||
return this.token(value, pos);
|
return this.token(value, pos);
|
||||||
|
@ -49,7 +49,7 @@ export class Parser {
|
|||||||
) {
|
) {
|
||||||
return this.parseItemStmt();
|
return this.parseItemStmt();
|
||||||
} else if (
|
} else if (
|
||||||
["let", "return", "break"].some((tt) => this.test(tt))
|
["let", "type_alias", "return", "break"].some((tt) => this.test(tt))
|
||||||
) {
|
) {
|
||||||
const expr = this.parseSingleLineBlockStmt();
|
const expr = this.parseSingleLineBlockStmt();
|
||||||
this.eatSemicolon();
|
this.eatSemicolon();
|
||||||
@ -92,6 +92,9 @@ 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();
|
||||||
}
|
}
|
||||||
@ -129,7 +132,8 @@ export class Parser {
|
|||||||
) {
|
) {
|
||||||
stmts.push(this.parseItemStmt());
|
stmts.push(this.parseItemStmt());
|
||||||
} else if (
|
} else if (
|
||||||
["let", "return", "break"].some((tt) => this.test(tt))
|
["let", "type_alias", "return", "break"]
|
||||||
|
.some((tt) => this.test(tt))
|
||||||
) {
|
) {
|
||||||
stmts.push(this.parseSingleLineBlockStmt());
|
stmts.push(this.parseSingleLineBlockStmt());
|
||||||
this.eatSemicolon();
|
this.eatSemicolon();
|
||||||
@ -413,6 +417,17 @@ export class Parser {
|
|||||||
return this.stmt({ type: "let", param, value }, pos);
|
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 {
|
private parseAssign(): Stmt {
|
||||||
const pos = this.pos();
|
const pos = this.pos();
|
||||||
const subject = this.parseExpr();
|
const subject = this.parseExpr();
|
||||||
|
@ -24,18 +24,49 @@ export class Resolver implements AstVisitor<[Syms]> {
|
|||||||
|
|
||||||
public resolve(stmts: Stmt[]): VisitRes {
|
public resolve(stmts: Stmt[]): VisitRes {
|
||||||
const syms = new EntryModSyms();
|
const syms = new EntryModSyms();
|
||||||
this.scoutFnStmts(stmts, syms);
|
this.scout(stmts, syms);
|
||||||
visitStmts(stmts, this, syms);
|
visitStmts(stmts, this, syms);
|
||||||
return "stop";
|
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 {
|
visitModStmt(stmt: Stmt, syms: Syms): VisitRes {
|
||||||
if (stmt.kind.type !== "mod") {
|
if (stmt.kind.type !== "mod") {
|
||||||
throw new Error("expected let statement");
|
throw new Error("expected let statement");
|
||||||
}
|
}
|
||||||
const modSyms = new ModSyms(syms);
|
const modSyms = new ModSyms(syms);
|
||||||
const { mod, ident } = stmt.kind;
|
const { mod, ident } = stmt.kind;
|
||||||
this.scoutFnStmts(mod.ast, modSyms);
|
this.scout(mod.ast, modSyms);
|
||||||
visitStmts(mod.ast, this, modSyms);
|
visitStmts(mod.ast, this, modSyms);
|
||||||
|
|
||||||
if (syms.definedLocally(ident)) {
|
if (syms.definedLocally(ident)) {
|
||||||
@ -72,23 +103,11 @@ export class Resolver implements AstVisitor<[Syms]> {
|
|||||||
return "stop";
|
return "stop";
|
||||||
}
|
}
|
||||||
|
|
||||||
private scoutFnStmts(stmts: Stmt[], syms: Syms) {
|
visitTypeAliasStmt(stmt: Stmt, syms: Syms): VisitRes {
|
||||||
for (const stmt of stmts) {
|
if (stmt.kind.type !== "type_alias") {
|
||||||
if (stmt.kind.type !== "fn") {
|
throw new Error("expected type_alias statement");
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
// nothing to do here
|
||||||
}
|
}
|
||||||
|
|
||||||
visitFnStmt(stmt: Stmt, syms: Syms): VisitRes {
|
visitFnStmt(stmt: Stmt, syms: Syms): VisitRes {
|
||||||
@ -186,7 +205,7 @@ export class Resolver implements AstVisitor<[Syms]> {
|
|||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
const childSyms = new LeafSyms(syms);
|
const childSyms = new LeafSyms(syms);
|
||||||
this.scoutFnStmts(expr.kind.stmts, childSyms);
|
this.scout(expr.kind.stmts, childSyms);
|
||||||
visitStmts(expr.kind.stmts, this, childSyms);
|
visitStmts(expr.kind.stmts, this, childSyms);
|
||||||
if (expr.kind.expr) {
|
if (expr.kind.expr) {
|
||||||
visitExpr(expr.kind.expr, this, childSyms);
|
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_1;
|
||||||
std::vector<AllocItem> heap_2;
|
std::vector<AllocItem> heap_2;
|
||||||
|
@ -54,6 +54,8 @@ pub fn itos(number: int) -> string {}
|
|||||||
#[builtin(StringToInt)]
|
#[builtin(StringToInt)]
|
||||||
pub fn stoi(str: string) -> int {}
|
pub fn stoi(str: string) -> int {}
|
||||||
|
|
||||||
|
pub fn ctos(ch: int) -> string { string_push_char("", ch) }
|
||||||
|
|
||||||
pub fn stdin() -> int { 0 }
|
pub fn stdin() -> int { 0 }
|
||||||
pub fn stdout() -> int { 1 }
|
pub fn stdout() -> int { 1 }
|
||||||
pub fn stderr() -> int { 2 }
|
pub fn stderr() -> int { 2 }
|
||||||
@ -81,7 +83,7 @@ pub fn read_text_file(filename: string) -> string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn input(prompt: string) -> string {
|
pub fn input(prompt: string) -> string {
|
||||||
print("> ");
|
print(prompt);
|
||||||
file_flush(stdout());
|
file_flush(stdout());
|
||||||
file_read_line(stdin())
|
file_read_line(stdin())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user