compiler: types and strings

This commit is contained in:
SimonFJ20 2025-03-26 16:06:36 +01:00
parent 0b65a5841f
commit f291516c87
10 changed files with 306 additions and 82 deletions

View File

@ -1,3 +1,4 @@
import { AttrView } from "./attr.ts";
import * as lir from "./lir.ts"; import * as lir from "./lir.ts";
export class AsmGen { export class AsmGen {
@ -34,40 +35,57 @@ export class AsmGen {
} }
private generateFn(fn: lir.Fn) { private generateFn(fn: lir.Fn) {
const query = this.queryCFunction(fn); const cFunctionQuery = this.queryCFunction(fn);
if (query.found) { if (cFunctionQuery.found) {
const { label, args } = query; const { label, args } = cFunctionQuery;
this.generateCFunctionBody(fn, label, args); this.generateCFunctionBody(fn, label, args);
return; return;
} }
this.generateFnBody(fn); this.generateFnBody(fn);
const cExportQuery = this.queryCExport(fn);
if (cExportQuery.found) {
const { label } = cExportQuery;
this.generateCExporter(fn, label);
}
} }
private queryCFunction( private queryCFunction(
fn: lir.Fn, fn: lir.Fn,
): { found: false } | { found: true; label: string; args: number } { ): { found: false } | { found: true; label: string; args: number } {
const stmtKind = fn.mir.stmt.kind; const attrs = AttrView.fromStmt(fn.mir.stmt);
if (stmtKind.tag !== "fn") { if (attrs.has("c_function")) {
throw new Error(); const attr = attrs.get("c_function");
} if (attr.args !== 1 || !attr.isStr(0)) {
if (stmtKind.attrs.at(0)?.ident === "c_function") {
const arg = stmtKind.attrs.at(0)!.args.at(0);
if (!arg || arg.kind.tag !== "string") {
throw new Error("incorrect args for attribute"); throw new Error("incorrect args for attribute");
} }
return { return {
found: true, found: true,
label: arg.kind.val, label: attr.strVal(0),
args: fn.mir.paramLocals.size, args: fn.mir.paramLocals.size,
}; };
} }
return { found: false }; return { found: false };
} }
private queryCExport(
fn: lir.Fn,
): { found: false } | { found: true; label: string } {
const attrs = AttrView.fromStmt(fn.mir.stmt);
if (attrs.has("c_export")) {
const attr = attrs.get("c_export");
if (attr.args !== 1 || !attr.isStr(0)) {
throw new Error("incorrect args for attribute");
}
const label = attr.strVal(0);
return { found: true, label };
}
return { found: false };
}
private generateCFunctionBody(fn: lir.Fn, label: string, args: number) { private generateCFunctionBody(fn: lir.Fn, label: string, args: number) {
this.writeln(`extern ${label}`); this.writeln(`extern ${label}`);
this.writeln(`global ${fn.label}`);
this.writeln(`${fn.label}:`); this.writeln(`${fn.label}:`);
this.writeIns(`push rbp`); this.writeIns(`push rbp`);
@ -90,7 +108,6 @@ export class AsmGen {
} }
private generateFnBody(fn: lir.Fn) { private generateFnBody(fn: lir.Fn) {
this.writeln(`global ${fn.label}:`);
this.writeln(`${fn.label}:`); this.writeln(`${fn.label}:`);
this.writeIns(`push rbp`); this.writeIns(`push rbp`);
this.writeIns(`mov rbp, rsp`); this.writeIns(`mov rbp, rsp`);
@ -112,6 +129,28 @@ export class AsmGen {
this.writeIns(`ret`); this.writeIns(`ret`);
} }
private generateCExporter(fn: lir.Fn, label: string) {
this.writeln(`global ${label}`);
this.writeln(`${label}:`);
this.writeIns(`push rbp`);
this.writeIns(`mov rbp, rsp`);
this.writeIns(`sub rsp, 8`);
const args = fn.mir.paramLocals.size;
const argRegs = ["rdi", "rsi", "rdx", "rcx", "r8", "r9"];
for (const reg of argRegs.slice(0, args)) {
this.writeIns(`push ${reg}`);
}
this.writeIns(`call ${fn.label}`);
this.writeIns(`mov rsp, rbp`);
this.writeIns(`pop rbp`);
this.writeIns(`ret`);
}
private generateIns(ins: lir.Ins) { private generateIns(ins: lir.Ins) {
const r = (reg: lir.Reg) => this.reg(reg); const r = (reg: lir.Reg) => this.reg(reg);

View File

@ -14,7 +14,7 @@ export type Stmt = {
export type StmtKind = export type StmtKind =
| { tag: "error" } | { tag: "error" }
| { tag: "fn" } & FnStmt | { tag: "fn" } & FnStmt
| { tag: "let"; ident: string; expr?: Expr } | { tag: "let" } & LetStmt
| { tag: "loop"; body: Block } | { tag: "loop"; body: Block }
| { tag: "if"; expr: Expr; truthy: Block; falsy?: Block } | { tag: "if"; expr: Expr; truthy: Block; falsy?: Block }
| { tag: "return"; expr?: Expr } | { tag: "return"; expr?: Expr }
@ -25,10 +25,23 @@ export type StmtKind =
export type FnStmt = { export type FnStmt = {
ident: string; ident: string;
attrs: Attr[]; attrs: Attr[];
params: string[]; params: Param[];
returnTy: Ty;
body: Block; body: Block;
}; };
export type LetStmt = {
ident: string;
ty?: Ty;
expr?: Expr;
};
export type Param = {
ident: string;
line: number;
ty: Ty;
};
export type Expr = { export type Expr = {
id: number; id: number;
line: number; line: number;
@ -39,12 +52,23 @@ export type ExprKind =
| { tag: "error" } | { tag: "error" }
| { tag: "ident"; ident: string } | { tag: "ident"; ident: string }
| { tag: "int"; val: number } | { tag: "int"; val: number }
| { tag: "string"; val: string } | { tag: "str"; val: string }
| { tag: "call"; expr: Expr; args: Expr[] } | { tag: "call"; expr: Expr; args: Expr[] }
| { tag: "binary"; op: BinaryOp; left: Expr; right: Expr }; | { tag: "binary"; op: BinaryOp; left: Expr; right: Expr };
export type BinaryOp = "<" | "==" | "+" | "*"; export type BinaryOp = "<" | "==" | "+" | "*";
export type Ty = {
id: number;
line: number;
kind: TyKind;
};
export type TyKind =
| { tag: "error" }
| { tag: "ident"; ident: string }
| { tag: "ptr"; ty: Ty };
export type Attr = { export type Attr = {
ident: string; ident: string;
args: Expr[]; args: Expr[];

View File

@ -1,8 +1,8 @@
#include <stdint.h> #include <stdint.h>
extern int32_t sbc__main(void); extern int32_t sbc_main(void);
int main(void) int main(void)
{ {
sbc__main(); sbc_main();
} }

View File

@ -4,14 +4,18 @@ import {
Block, Block,
Expr, Expr,
ExprKind, ExprKind,
Param,
Stmt, Stmt,
StmtKind, StmtKind,
Ty as AstTy,
TyKind,
} from "./ast.ts"; } from "./ast.ts";
import { Ty, tyToString } from "./ty.ts"; import { Ty, tyToString } from "./ty.ts";
export class Checker { export class Checker {
private stmtTys = new Map<number, Ty>(); private stmtTys = new Map<number, Ty>();
private exprTys = new Map<number, Ty>(); private exprTys = new Map<number, Ty>();
private tyTys = new Map<number, Ty>();
public errorOccured = false; public errorOccured = false;
@ -27,8 +31,9 @@ export class Checker {
if (this.stmtTys.has(stmt.id)) { if (this.stmtTys.has(stmt.id)) {
return this.stmtTys.get(stmt.id)!; return this.stmtTys.get(stmt.id)!;
} }
const params = k.params.map((_): Ty => ({ tag: "int" })); const params = k.params
const returnTy: Ty = { tag: "int" }; .map((param): Ty => this.tyTy(param.ty));
const returnTy: Ty = this.tyTy(k.returnTy);
const ty: Ty = { tag: "fn", stmt, params, returnTy }; const ty: Ty = { tag: "fn", stmt, params, returnTy };
this.stmtTys.set(stmt.id, ty); this.stmtTys.set(stmt.id, ty);
return ty; return ty;
@ -50,7 +55,30 @@ export class Checker {
if (this.stmtTys.has(stmt.id)) { if (this.stmtTys.has(stmt.id)) {
return this.stmtTys.get(stmt.id)!; return this.stmtTys.get(stmt.id)!;
} }
const ty: Ty = k.expr ? this.exprTy(k.expr) : { tag: "int" }; const declTy = k.ty && this.tyTy(k.ty);
const exprTy = k.expr && this.exprTy(k.expr);
const tyRes = declTy && exprTy
? this.resolveTys(declTy, exprTy)
: declTy
? { ok: true, ty: declTy } as const
: exprTy
? { ok: true, ty: exprTy } as const
: { ok: true, ty: { tag: "unknown" } as Ty } as const;
if (!tyRes.ok) {
this.report(tyRes.msg, stmt.line);
const ty: Ty = { tag: "error" };
this.stmtTys.set(stmt.id, ty);
return ty;
}
const ty = tyRes.ty;
if (ty.tag === "unknown") {
this.report("could not infer type", stmt.line);
const ty: Ty = { tag: "error" };
this.stmtTys.set(stmt.id, ty);
return ty;
}
this.stmtTys.set(stmt.id, ty); this.stmtTys.set(stmt.id, ty);
return ty; return ty;
} }
@ -83,8 +111,8 @@ export class Checker {
} }
case "int": case "int":
return { tag: "int" }; return { tag: "int" };
case "string": case "str":
return { tag: "string" }; return { tag: "ptr", ty: { tag: "str" } };
case "call": { case "call": {
const callee = this.exprTy(k.expr); const callee = this.exprTy(k.expr);
if (callee.tag !== "fn") { if (callee.tag !== "fn") {
@ -98,13 +126,15 @@ export class Checker {
); );
return { tag: "error" }; return { tag: "error" };
} }
const args = k.args.map((arg) => this.exprTy(arg));
const argTys = k.args
.map((arg) => this.exprTy(arg));
for (const [i, param] of callee.params.entries()) { for (const [i, param] of callee.params.entries()) {
if (!this.assignable(args[i], param)) { console.log({ arg: argTys[i], param });
const tyRes = this.resolveTys(argTys[i], param);
if (!tyRes.ok) {
this.report( this.report(
`argument mismatch, type '${ `argument mismatch, ${tyRes.msg}`,
tyToString(args[i])
}' not assignable to '${tyToString(param)}'`,
expr.line, expr.line,
); );
} }
@ -147,6 +177,40 @@ export class Checker {
return ty; return ty;
} }
public tyTy(astTy: AstTy): Ty {
if (this.tyTys.has(astTy.id)) {
return this.tyTys.get(astTy.id)!;
}
const ty = ((): Ty => {
const k = astTy.kind;
switch (k.tag) {
case "error":
return { tag: "error" };
case "ident": {
switch (k.ident) {
case "int":
return { tag: "int" };
case "str":
return { tag: "str" };
default:
this.report(
`unknown type '${k.ident}'`,
astTy.line,
);
return { tag: "error" };
}
}
case "ptr": {
const ty = this.tyTy(k.ty);
return { tag: "ptr", ty };
}
}
const _: never = k;
})();
this.tyTys.set(astTy.id, ty);
return ty;
}
private assignable(a: Ty, b: Ty): boolean { private assignable(a: Ty, b: Ty): boolean {
if (a.tag !== b.tag) { if (a.tag !== b.tag) {
return false; return false;
@ -157,16 +221,52 @@ export class Checker {
return true; return true;
} }
private resolveTys(a: Ty, b: Ty):
| { ok: true; ty: Ty }
| { ok: false; msg: string } {
const ok = (ty: Ty) => ({ ok: true, ty } as const);
const both = (tag: Ty["tag"]): boolean =>
a.tag === tag && b.tag === tag;
if (a.tag === "error" || b.tag === "error") {
return ok(a);
} else if (both("int")) {
return ok(a);
} else if (both("str")) {
return ok(a);
} else if (
a.tag === "ptr" && b.tag === "ptr"
) {
const tyRes = this.resolveTys(a.ty, b.ty);
if (!tyRes.ok) {
return tyRes;
}
return ok(a);
} else if (
a.tag === "fn" && b.tag === "fn" &&
a.stmt.id !== b.stmt.id
) {
return ok(a);
} else {
return {
ok: false,
msg: `type '${tyToString(a)}' is not assignable to '${
tyToString(b)
}'`,
};
}
}
private report(msg: string, line: number) { private report(msg: string, line: number) {
this.errorOccured = true; this.errorOccured = true;
//console.error(`parser: ${msg} on line ${line}`); //console.error(`Checker: ${msg} on line ${line}`);
throw new Error(`parser: ${msg} on line ${line}`); throw new Error(`Checker: ${msg} on line ${line}`);
} }
} }
export type Resolve = export type Resolve =
| { tag: "fn"; stmt: Stmt } | { tag: "fn"; stmt: Stmt }
| { tag: "param"; stmt: Stmt; i: number } | { tag: "param"; stmt: Stmt; param: Param; i: number }
| { tag: "let"; stmt: Stmt } | { tag: "let"; stmt: Stmt }
| { tag: "loop"; stmt: Stmt }; | { tag: "loop"; stmt: Stmt };
@ -298,7 +398,12 @@ export class Resolver {
throw new Error(); throw new Error();
} }
for (const [i, param] of k.params.entries()) { for (const [i, param] of k.params.entries()) {
this.syms.defineVal(param, { tag: "param", stmt: fn, i }); this.syms.defineVal(param.ident, {
tag: "param",
stmt: fn,
param,
i,
});
} }
this.resolveBlock(k.body); this.resolveBlock(k.body);
@ -375,7 +480,7 @@ export class Resolver {
} }
case "int": case "int":
return; return;
case "string": case "str":
return; return;
case "call": case "call":
this.resolveExpr(k.expr); this.resolveExpr(k.expr);
@ -393,8 +498,8 @@ export class Resolver {
private report(msg: string, line: number) { private report(msg: string, line: number) {
this.errorOccured = true; this.errorOccured = true;
//console.error(`parser: ${msg} on line ${line}`); //console.error(`Resolver: ${msg} on line ${line}`);
throw new Error(`parser: ${msg} on line ${line}`); throw new Error(`Resolver: ${msg} on line ${line}`);
} }
} }
@ -405,6 +510,7 @@ export class Parser {
private blockIds = 0; private blockIds = 0;
private stmtIds = 0; private stmtIds = 0;
private exprIds = 0; private exprIds = 0;
private tyIds = 0;
private last: Tok; private last: Tok;
private eaten?: Tok; private eaten?: Tok;
@ -534,13 +640,13 @@ export class Parser {
this.report("expected '('"); this.report("expected '('");
return this.stmt({ tag: "error" }, line); return this.stmt({ tag: "error" }, line);
} }
const params: string[] = []; const params: Param[] = [];
if (!this.done() && !this.test(")")) { if (!this.done() && !this.test(")")) {
if (!this.eat("ident")) { const paramRes = this.parseParam();
this.report("expected 'ident'"); if (!paramRes.ok) {
return this.stmt({ tag: "error" }, line); return this.stmt({ tag: "error" }, line);
} }
params.push(this.eaten!.identVal!); params.push(paramRes.param);
while (!this.done() && !this.test(")")) { while (!this.done() && !this.test(")")) {
if (!this.eat(",")) { if (!this.eat(",")) {
this.report("expected ','"); this.report("expected ','");
@ -549,23 +655,48 @@ export class Parser {
if (this.test(")")) { if (this.test(")")) {
break; break;
} }
if (!this.eat("ident")) { const paramRes = this.parseParam();
this.report("expected 'ident'"); if (!paramRes.ok) {
return this.stmt({ tag: "error" }, line); return this.stmt({ tag: "error" }, line);
} }
params.push(this.eaten!.identVal!); params.push(paramRes.param);
} }
} }
if (!this.eat(")")) { if (!this.eat(")")) {
this.report("expected ')'"); this.report("expected ')'");
return this.stmt({ tag: "error" }, line); return this.stmt({ tag: "error" }, line);
} }
if (!this.eat("->")) {
this.report("expected '->'");
return this.stmt({ tag: "error" }, line);
}
const returnTy = this.parseTy();
if (!this.test("{")) { if (!this.test("{")) {
this.report("expected block"); this.report("expected block");
return this.stmt({ tag: "error" }, line); return this.stmt({ tag: "error" }, line);
} }
const body = this.parseBlock(); const body = this.parseBlock();
return this.stmt({ tag: "fn", ident, attrs, params, body }, line); return this.stmt(
{ tag: "fn", ident, attrs, params, returnTy, body },
line,
);
}
private parseParam():
| { ok: true; param: Param }
| { ok: false } {
const line = this.curr().line;
if (!this.eat("ident")) {
this.report("expected 'ident'");
return { ok: false };
}
const ident = this.eaten!.identVal!;
if (!this.eat(":")) {
this.report("expected ':'");
return { ok: false };
}
const ty = this.parseTy();
return { ok: true, param: { ident, line, ty } };
} }
private parseLetStmt(): Stmt { private parseLetStmt(): Stmt {
@ -722,17 +853,32 @@ export class Parser {
{ tag: "int", val: this.eaten!.intVal! }, { tag: "int", val: this.eaten!.intVal! },
this.eaten!.line, this.eaten!.line,
); );
} else if (this.eat("string")) { } else if (this.eat("str")) {
return this.expr( return this.expr(
{ tag: "string", val: this.eaten?.stringVal! }, { tag: "str", val: this.eaten?.stringVal! },
this.eaten!.line, this.eaten!.line,
); );
} else { } else {
this.report("expected expr"); this.report("expected expression");
return this.expr({ tag: "error" }, this.last!.line); return this.expr({ tag: "error" }, this.last!.line);
} }
} }
private parseTy(): AstTy {
if (this.eat("ident")) {
return this.ty(
{ tag: "ident", ident: this.eaten!.identVal! },
this.eaten!.line,
);
} else if (this.eat("*")) {
const ty = this.parseTy();
return this.ty({ tag: "ptr", ty }, this.eaten!.line);
} else {
this.report("expected type");
return this.ty({ tag: "error" }, this.last!.line);
}
}
private stmt(kind: StmtKind, line: number): Stmt { private stmt(kind: StmtKind, line: number): Stmt {
const id = this.stmtIds++; const id = this.stmtIds++;
return { id, line, kind }; return { id, line, kind };
@ -743,6 +889,11 @@ export class Parser {
return { id, line, kind }; return { id, line, kind };
} }
private ty(kind: TyKind, line: number): AstTy {
const id = this.tyIds++;
return { id, line, kind };
}
private eat(type: string): boolean { private eat(type: string): boolean {
if (this.test(type)) { if (this.test(type)) {
this.eaten = this.curr(); this.eaten = this.curr();
@ -769,8 +920,8 @@ export class Parser {
private report(msg: string, line = this.last.line) { private report(msg: string, line = this.last.line) {
this.errorOccured = true; this.errorOccured = true;
//console.error(`parser: ${msg} on line ${line}`); //console.error(`Parser: ${msg} on line ${line}`);
throw new Error(`parser: ${msg} on line ${line}`); throw new Error(`Parser: ${msg} on line ${line}`);
} }
} }
@ -783,7 +934,7 @@ export type Tok = {
}; };
export function lex(text: string): Tok[] { export function lex(text: string): Tok[] {
const ops = "(){}[]<>+*=,;#\n"; const ops = "(){}[]<>+-*=:,;#\n";
const kws = ["let", "fn", "return", "if", "else", "loop", "break"]; const kws = ["let", "fn", "return", "if", "else", "loop", "break"];
return ops return ops
@ -792,6 +943,7 @@ export function lex(text: string): Tok[] {
text text
.replaceAll(/\/\/.*?$/mg, "") .replaceAll(/\/\/.*?$/mg, "")
.replaceAll(op, ` ${op} `) .replaceAll(op, ` ${op} `)
.replaceAll(" - > ", " -> ")
.replaceAll(" = = ", " == ") .replaceAll(" = = ", " == ")
.replaceAll(/\\ /g, "\\SPACE"), text) .replaceAll(/\\ /g, "\\SPACE"), text)
.split(/[ \t\r]/) .split(/[ \t\r]/)
@ -814,7 +966,7 @@ export function lex(text: string): Tok[] {
: { type: "ident", line, identVal: val }; : { type: "ident", line, identVal: val };
} else if (/^".*?"$/.test(val)) { } else if (/^".*?"$/.test(val)) {
return { return {
type: "string", type: "str",
line, line,
stringVal: val stringVal: val
.slice(1, val.length - 1) .slice(1, val.length - 1)

View File

@ -1,6 +1,11 @@
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
typedef struct {
int64_t length;
char data[];
} Str;
int64_t notice(void) int64_t notice(void)
{ {
printf("NOTICE!\n"); printf("NOTICE!\n");
@ -12,3 +17,9 @@ int64_t print_int(int64_t value)
printf("%ld\n", value); printf("%ld\n", value);
return 0; return 0;
} }
int64_t println(const Str* value)
{
printf("%.*s\n", (int)value->length, value->data);
return 0;
}

View File

@ -141,7 +141,7 @@ class FnGen {
return; return;
case "push": { case "push": {
switch (k.val.tag) { switch (k.val.tag) {
case "string": { case "str": {
const reg = this.reg(); const reg = this.reg();
const stringId = this.strings.intern(k.val.val); const stringId = this.strings.intern(k.val.val);
this.pushIns({ tag: "mov_string", reg, stringId }); this.pushIns({ tag: "mov_string", reg, stringId });

View File

@ -58,7 +58,7 @@ export type TerKind =
export type Val = export type Val =
| { tag: "int"; val: number } | { tag: "int"; val: number }
| { tag: "string"; val: string } | { tag: "str"; val: string }
| { tag: "fn"; stmt: ast.Stmt }; | { tag: "fn"; stmt: ast.Stmt };
export class FnStringifyer { export class FnStringifyer {
@ -129,7 +129,7 @@ export class FnStringifyer {
private val(val: Val): string { private val(val: Val): string {
switch (val.tag) { switch (val.tag) {
case "string": case "str":
return JSON.stringify(val.val); return JSON.stringify(val.val);
case "int": case "int":
return `${val.val}`; return `${val.val}`;

View File

@ -45,7 +45,7 @@ export class FnMirGen {
this.returnLocal = this.local(fnTy.returnTy); this.returnLocal = this.local(fnTy.returnTy);
for (const [i, param] of this.stmtKind.params.entries()) { for (const [i, param] of this.stmtKind.params.entries()) {
const ty = this.ch.paramTy(this.stmt, i); const ty = this.ch.paramTy(this.stmt, i);
const local = this.local(ty, param); const local = this.local(ty, param.ident);
this.paramLocals.set(i, local); this.paramLocals.set(i, local);
} }
@ -182,6 +182,7 @@ export class FnMirGen {
} }
private lowerExpr(expr: ast.Expr) { private lowerExpr(expr: ast.Expr) {
const ty = this.ch.exprTy(expr);
const k = expr.kind; const k = expr.kind;
switch (k.tag) { switch (k.tag) {
case "error": case "error":
@ -191,7 +192,6 @@ export class FnMirGen {
if (!re) { if (!re) {
throw new Error(); throw new Error();
} }
const ty = this.ch.exprTy(expr);
switch (re.tag) { switch (re.tag) {
case "fn": { case "fn": {
this.pushStmt({ this.pushStmt({
@ -224,7 +224,6 @@ export class FnMirGen {
return; return;
} }
case "int": { case "int": {
const ty = this.ch.exprTy(expr);
this.pushStmt({ this.pushStmt({
tag: "push", tag: "push",
val: { tag: "int", val: k.val }, val: { tag: "int", val: k.val },
@ -232,11 +231,10 @@ export class FnMirGen {
}); });
return; return;
} }
case "string": { case "str": {
const ty = this.ch.exprTy(expr);
this.pushStmt({ this.pushStmt({
tag: "push", tag: "push",
val: { tag: "string", val: k.val }, val: { tag: "str", val: k.val },
ty, ty,
}); });
return; return;

View File

@ -1,28 +1,22 @@
#[c_function("notice")] #[c_function("notice")]
fn notice() {} fn notice() -> int {}
#[c_function("print_int")] #[c_function("print_int")]
fn print_int(value) {} fn print_int(value: int) -> int {}
fn inner(value) {
print_int(value);
#[c_function("println")]
fn println(value: *str) -> int {}
#[c_export("sbc_main")]
fn main() -> int {
println("hello_world");
return 0; return 0;
} }
fn main() { // vim: syntax=slige commentstring=//\ %s
let i = 0;
loop {
if 10 < i + 1 {
break;
}
let a = 4;
inner(a + 2);
i = i + 1;
}
return i;
}
// vim: syntax=rust commentstring=//\ %s

View File

@ -2,18 +2,24 @@ import * as ast from "./ast.ts";
export type Ty = export type Ty =
| { tag: "error" } | { tag: "error" }
| { tag: "unknown" }
| { tag: "int" } | { tag: "int" }
| { tag: "string" } | { tag: "str" }
| { tag: "ptr"; ty: Ty }
| { tag: "fn"; stmt: ast.Stmt; params: Ty[]; returnTy: Ty }; | { tag: "fn"; stmt: ast.Stmt; params: Ty[]; returnTy: Ty };
export function tyToString(ty: Ty): string { export function tyToString(ty: Ty): string {
switch (ty.tag) { switch (ty.tag) {
case "error": case "error":
return `<error>`; return `<error>`;
case "unknown":
return `<unknown>`;
case "int": case "int":
return `int`; return `int`;
case "string": case "str":
return `string`; return `str`;
case "ptr":
return `*${tyToString(ty.ty)}`;
case "fn": { case "fn": {
const k = ty.stmt.kind as ast.StmtKind & { tag: "fn" }; const k = ty.stmt.kind as ast.StmtKind & { tag: "fn" };
const params = ty.params const params = ty.params