diff --git a/compiler/ast.ts b/compiler/ast.ts index 7119da5..c9d81f0 100644 --- a/compiler/ast.ts +++ b/compiler/ast.ts @@ -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 } diff --git a/compiler/ast_visitor.ts b/compiler/ast_visitor.ts index 62eb19b..ba6c8ac 100644 --- a/compiler/ast_visitor.ts +++ b/compiler/ast_visitor.ts @@ -13,6 +13,7 @@ export interface AstVisitor { 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( 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); diff --git a/compiler/checker.ts b/compiler/checker.ts index 7a42f4b..88b1c10 100644 --- a/compiler/checker.ts +++ b/compiler/checker.ts @@ -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)) { diff --git a/compiler/desugar/struct_literal.ts b/compiler/desugar/struct_literal.ts index 93405ad..0931f0d 100644 --- a/compiler/desugar/struct_literal.ts +++ b/compiler/desugar/struct_literal.ts @@ -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: [], diff --git a/compiler/lexer.ts b/compiler/lexer.ts index b7f34f1..a962341 100644 --- a/compiler/lexer.ts +++ b/compiler/lexer.ts @@ -50,6 +50,7 @@ export class Lexer { "mod", "pub", "use", + "type_alias", ]; if (keywords.includes(value)) { return this.token(value, pos); diff --git a/compiler/parser.ts b/compiler/parser.ts index d854faa..03f6fbd 100644 --- a/compiler/parser.ts +++ b/compiler/parser.ts @@ -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(); diff --git a/compiler/resolver.ts b/compiler/resolver.ts index 2d06de4..c811b28 100644 --- a/compiler/resolver.ts +++ b/compiler/resolver.ts @@ -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); diff --git a/runtime/alloc.hpp b/runtime/alloc.hpp index 21c4f96..05c34da 100644 --- a/runtime/alloc.hpp +++ b/runtime/alloc.hpp @@ -205,7 +205,7 @@ private: } } - size_t max_size = 8; + size_t max_size = 512; std::vector heap_1; std::vector heap_2; diff --git a/std/lib.slg b/std/lib.slg index b5c8d79..3c85937 100644 --- a/std/lib.slg +++ b/std/lib.slg @@ -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()) }