mirror of
https://github.com/Noratrieb/riverdelta.git
synced 2026-01-15 17:05:03 +01:00
Change let
This commit is contained in:
parent
40543b501c
commit
7c2faaecb8
9 changed files with 200 additions and 100 deletions
20
src/ast.ts
20
src/ast.ts
|
|
@ -54,15 +54,18 @@ export type ExprEmpty = { kind: "empty" };
|
||||||
|
|
||||||
export type ExprLet = {
|
export type ExprLet = {
|
||||||
kind: "let";
|
kind: "let";
|
||||||
name: string;
|
name: Identifier;
|
||||||
type?: Type;
|
type?: Type;
|
||||||
rhs: Expr;
|
rhs: Expr;
|
||||||
after: Expr;
|
// IMPORTANT: This is (sadly) shared with ExprBlock.
|
||||||
|
local?: LocalInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ExprBlock = {
|
export type ExprBlock = {
|
||||||
kind: "block";
|
kind: "block";
|
||||||
exprs: Expr[];
|
exprs: Expr[];
|
||||||
|
// IMPORTANT: This is (sadly) shared with ExprLet.
|
||||||
|
locals?: LocalInfo[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ExprLiteral = {
|
export type ExprLiteral = {
|
||||||
|
|
@ -206,13 +209,15 @@ export type Type = TypeKind & {
|
||||||
ty?: Ty;
|
ty?: Ty;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// name resolution stuff
|
||||||
|
|
||||||
export type Resolution =
|
export type Resolution =
|
||||||
| {
|
| {
|
||||||
kind: "local";
|
kind: "local";
|
||||||
/**
|
/**
|
||||||
* The index of the local variable, from inside out.
|
* The index of the local variable, from inside out.
|
||||||
* ```
|
* ```
|
||||||
* let a in let b in (a, b);
|
* let a = 0; let b; (a, b);
|
||||||
* ^ ^
|
* ^ ^
|
||||||
* 1 0
|
* 1 0
|
||||||
* ```
|
* ```
|
||||||
|
|
@ -246,6 +251,14 @@ export const BUILTINS = [
|
||||||
|
|
||||||
export type BuiltinName = (typeof BUILTINS)[number];
|
export type BuiltinName = (typeof BUILTINS)[number];
|
||||||
|
|
||||||
|
export type LocalInfo = {
|
||||||
|
name: string;
|
||||||
|
span: Span;
|
||||||
|
ty?: Ty;
|
||||||
|
};
|
||||||
|
|
||||||
|
// types
|
||||||
|
|
||||||
export type TyString = {
|
export type TyString = {
|
||||||
kind: "string";
|
kind: "string";
|
||||||
};
|
};
|
||||||
|
|
@ -397,7 +410,6 @@ export function superFoldExpr(expr: Expr, folder: Folder): Expr {
|
||||||
name: expr.name,
|
name: expr.name,
|
||||||
type: expr.type && folder.type(expr.type),
|
type: expr.type && folder.type(expr.type),
|
||||||
rhs: folder.expr(expr.rhs),
|
rhs: folder.expr(expr.rhs),
|
||||||
after: folder.expr(expr.after),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case "block": {
|
case "block": {
|
||||||
|
|
|
||||||
11
src/error.ts
11
src/error.ts
|
|
@ -52,8 +52,13 @@ function renderError(input: string, e: CompilerError) {
|
||||||
console.error(`${lineIdx} | ${spanToSnippet(input, line)}`);
|
console.error(`${lineIdx} | ${spanToSnippet(input, line)}`);
|
||||||
const startRelLine =
|
const startRelLine =
|
||||||
e.span.start === Number.MAX_SAFE_INTEGER ? 0 : e.span.start - line.start;
|
e.span.start === Number.MAX_SAFE_INTEGER ? 0 : e.span.start - line.start;
|
||||||
|
|
||||||
|
const spanLength = min(e.span.end, line.end) - e.span.start;
|
||||||
|
|
||||||
console.error(
|
console.error(
|
||||||
`${" ".repeat(String(lineIdx).length)} ${" ".repeat(startRelLine)}^`
|
`${" ".repeat(String(lineIdx).length)} ${" ".repeat(
|
||||||
|
startRelLine
|
||||||
|
)}${"^".repeat(spanLength)}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,3 +86,7 @@ export function lines(input: string): Span[] {
|
||||||
export function todo(msg: string): never {
|
export function todo(msg: string): never {
|
||||||
throw new CompilerError(`TODO: ${msg}`, { start: 0, end: 0 });
|
throw new CompilerError(`TODO: ${msg}`, { start: 0, end: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function min(a: number, b: number): number {
|
||||||
|
return a < b ? a : b;
|
||||||
|
}
|
||||||
|
|
|
||||||
17
src/index.ts
17
src/index.ts
|
|
@ -10,14 +10,13 @@ import fs from "fs";
|
||||||
import { exec } from "child_process";
|
import { exec } from "child_process";
|
||||||
|
|
||||||
const input = `
|
const input = `
|
||||||
type Uwu = (
|
function printInt(a: Int) = ;
|
||||||
meow: String,
|
|
||||||
oops: Int,
|
|
||||||
aaa: (),
|
|
||||||
);
|
|
||||||
|
|
||||||
function aa(a: Int, b: Uwu): Uwu = Uwu {meow: "",oops:0,aaa:()};
|
function main() = (
|
||||||
function main() = ();
|
let a = 0;
|
||||||
|
let b = 0;
|
||||||
|
printInt(a + b);
|
||||||
|
);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
|
|
@ -44,8 +43,8 @@ function main() {
|
||||||
|
|
||||||
console.log("-----AST typecked------");
|
console.log("-----AST typecked------");
|
||||||
const typecked = typeck(resolved);
|
const typecked = typeck(resolved);
|
||||||
|
console.dir(typecked, {depth: 8});
|
||||||
return;
|
|
||||||
|
|
||||||
console.log("-----wasm--------------");
|
console.log("-----wasm--------------");
|
||||||
const wasmModule = lowerToWasm(typecked);
|
const wasmModule = lowerToWasm(typecked);
|
||||||
|
|
|
||||||
18
src/lexer.ts
18
src/lexer.ts
|
|
@ -3,7 +3,6 @@ import { CompilerError, Span } from "./error";
|
||||||
export type DatalessToken =
|
export type DatalessToken =
|
||||||
| "function"
|
| "function"
|
||||||
| "let"
|
| "let"
|
||||||
| "in"
|
|
||||||
| "if"
|
| "if"
|
||||||
| "then"
|
| "then"
|
||||||
| "else"
|
| "else"
|
||||||
|
|
@ -79,6 +78,14 @@ export function tokenize(input: string): Token[] {
|
||||||
const next = input[i];
|
const next = input[i];
|
||||||
const span: Span = { start: i, end: i + 1 };
|
const span: Span = { start: i, end: i + 1 };
|
||||||
|
|
||||||
|
if (next === "/" && input[i + 1] === "/") {
|
||||||
|
while (input[i] !== "\n") {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (SINGLE_PUNCT.includes(next)) {
|
if (SINGLE_PUNCT.includes(next)) {
|
||||||
tokens.push({ kind: next as DatalessToken, span });
|
tokens.push({ kind: next as DatalessToken, span });
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -207,15 +214,16 @@ function isWhitespace(char: string): boolean {
|
||||||
return char === " " || char === "\t" || char === "\n" || char === "\r";
|
return char === " " || char === "\t" || char === "\n" || char === "\r";
|
||||||
}
|
}
|
||||||
|
|
||||||
const keywords = new Set<string>([
|
const KEYOWRDS: DatalessToken[] = [
|
||||||
"function",
|
"function",
|
||||||
"let",
|
"let",
|
||||||
"in",
|
|
||||||
"if",
|
"if",
|
||||||
"then",
|
"then",
|
||||||
"else",
|
"else",
|
||||||
"type",
|
"type",
|
||||||
]);
|
];
|
||||||
|
|
||||||
|
const KEYWORD_SET = new Set<string>(KEYOWRDS);
|
||||||
function isKeyword(kw: string): DatalessToken | undefined {
|
function isKeyword(kw: string): DatalessToken | undefined {
|
||||||
return keywords.has(kw) ? (kw as DatalessToken) : undefined;
|
return KEYWORD_SET.has(kw) ? (kw as DatalessToken) : undefined;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,8 @@ function lowerExpr(fcx: FuncContext, instrs: wasm.Instr[], expr: Expr) {
|
||||||
case "block":
|
case "block":
|
||||||
if (expr.exprs.length === 1) {
|
if (expr.exprs.length === 1) {
|
||||||
lowerExpr(fcx, instrs, expr.exprs[0]);
|
lowerExpr(fcx, instrs, expr.exprs[0]);
|
||||||
|
} else {
|
||||||
|
todo("complex blocks");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "literal":
|
case "literal":
|
||||||
|
|
|
||||||
146
src/parser.ts
146
src/parser.ts
|
|
@ -4,9 +4,11 @@ import {
|
||||||
Ast,
|
Ast,
|
||||||
BinaryKind,
|
BinaryKind,
|
||||||
COMPARISON_KINDS,
|
COMPARISON_KINDS,
|
||||||
|
DEFAULT_FOLDER,
|
||||||
Expr,
|
Expr,
|
||||||
ExprStructLiteral,
|
ExprStructLiteral,
|
||||||
FieldDef,
|
FieldDef,
|
||||||
|
Folder,
|
||||||
FunctionArg,
|
FunctionArg,
|
||||||
FunctionDef,
|
FunctionDef,
|
||||||
Identifier,
|
Identifier,
|
||||||
|
|
@ -16,9 +18,11 @@ import {
|
||||||
TypeDef,
|
TypeDef,
|
||||||
UNARY_KINDS,
|
UNARY_KINDS,
|
||||||
UnaryKind,
|
UnaryKind,
|
||||||
|
foldAst,
|
||||||
|
superFoldExpr,
|
||||||
} from "./ast";
|
} from "./ast";
|
||||||
import { CompilerError, Span, todo } from "./error";
|
import { CompilerError, Span, spanMerge } from "./error";
|
||||||
import { BaseToken, DatalessToken, Token, TokenIdent } from "./lexer";
|
import { BaseToken, Token, TokenIdent } from "./lexer";
|
||||||
|
|
||||||
type Parser<T> = (t: Token[]) => [Token[], T];
|
type Parser<T> = (t: Token[]) => [Token[], T];
|
||||||
|
|
||||||
|
|
@ -33,7 +37,11 @@ export function parse(t: Token[]): Ast {
|
||||||
|
|
||||||
const withIds = items.map((item, i) => ({ ...item, id: i }));
|
const withIds = items.map((item, i) => ({ ...item, id: i }));
|
||||||
|
|
||||||
return { items: withIds };
|
const ast = { items: withIds };
|
||||||
|
|
||||||
|
validateAst(ast);
|
||||||
|
|
||||||
|
return ast;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItem(t: Token[]): [Token[], Item] {
|
function parseItem(t: Token[]): [Token[], Item] {
|
||||||
|
|
@ -127,7 +135,7 @@ function parseItem(t: Token[]): [Token[], Item] {
|
||||||
|
|
||||||
function parseExpr(t: Token[]): [Token[], Expr] {
|
function parseExpr(t: Token[]): [Token[], Expr] {
|
||||||
/*
|
/*
|
||||||
EXPR = { LET | COMPARISON | IF }
|
EXPR = COMPARISON
|
||||||
|
|
||||||
LET = "let" NAME { ":" TYPE } "=" EXPR "in" EXPR
|
LET = "let" NAME { ":" TYPE } "=" EXPR "in" EXPR
|
||||||
IF = "if" EXPR "then" EXPR { "else" EXPR }
|
IF = "if" EXPR "then" EXPR { "else" EXPR }
|
||||||
|
|
@ -145,56 +153,11 @@ function parseExpr(t: Token[]): [Token[], Expr] {
|
||||||
|
|
||||||
CALL = ATOM { "(" EXPR_LIST ")" }
|
CALL = ATOM { "(" EXPR_LIST ")" }
|
||||||
|
|
||||||
ATOM = "(" { EXPR ";" } EXPR ")" | IDENT { STRUCT_INIT } | LITERAL | EMPTY
|
ATOM = "(" { EXPR ";" } EXPR ")" | IDENT { STRUCT_INIT } | LITERAL | EMPTY | LET | IF
|
||||||
EMPTY =
|
EMPTY =
|
||||||
STRUCT_INIT = "{" { NAME ":" EXPR } { "," NAME ":" EXPR } { "," } "}"
|
STRUCT_INIT = "{" { NAME ":" EXPR } { "," NAME ":" EXPR } { "," } "}"
|
||||||
EXPR_LIST = { EXPR { "," EXPR } { "," } }
|
EXPR_LIST = { EXPR { "," EXPR } { "," } }
|
||||||
*/
|
*/
|
||||||
const [, peak] = next(t);
|
|
||||||
|
|
||||||
if (peak.kind === "let") {
|
|
||||||
[t] = expectNext(t, "let");
|
|
||||||
let name;
|
|
||||||
[t, name] = expectNext<TokenIdent>(t, "identifier");
|
|
||||||
|
|
||||||
let type = undefined;
|
|
||||||
let colon;
|
|
||||||
[t, colon] = eat(t, ":");
|
|
||||||
if (colon) {
|
|
||||||
[t, type] = parseType(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
[t] = expectNext(t, "=");
|
|
||||||
let rhs;
|
|
||||||
[t, rhs] = parseExpr(t);
|
|
||||||
[t] = expectNext(t, "in");
|
|
||||||
let after;
|
|
||||||
[t, after] = parseExpr(t);
|
|
||||||
|
|
||||||
return [
|
|
||||||
t,
|
|
||||||
{ kind: "let", name: name.ident, type, rhs, after, span: name.span },
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (peak.kind === "if") {
|
|
||||||
[t] = expectNext(t, "if");
|
|
||||||
let cond;
|
|
||||||
[t, cond] = parseExpr(t);
|
|
||||||
[t] = expectNext(t, "then");
|
|
||||||
let then;
|
|
||||||
[t, then] = parseExpr(t);
|
|
||||||
|
|
||||||
let elseTok;
|
|
||||||
[t, elseTok] = eat(t, "else");
|
|
||||||
let elsePart = undefined;
|
|
||||||
if (elseTok) {
|
|
||||||
[t, elsePart] = parseExpr(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [t, { kind: "if", cond, then, else: elsePart, span: peak.span }];
|
|
||||||
}
|
|
||||||
|
|
||||||
return parseExprComparison(t);
|
return parseExprComparison(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,15 +169,15 @@ function mkParserExprBinary(
|
||||||
let lhs;
|
let lhs;
|
||||||
[t, lhs] = lower(t);
|
[t, lhs] = lower(t);
|
||||||
|
|
||||||
const [, peak] = next(t);
|
const [, peek] = next(t);
|
||||||
if (kinds.includes(peak.kind)) {
|
if (kinds.includes(peek.kind)) {
|
||||||
[t] = next(t);
|
[t] = next(t);
|
||||||
let rhs;
|
let rhs;
|
||||||
[t, rhs] = parser(t);
|
[t, rhs] = parser(t);
|
||||||
const span = peak.span;
|
const span = spanMerge(lhs.span, rhs.span);
|
||||||
return [
|
return [
|
||||||
t,
|
t,
|
||||||
{ kind: "binary", binaryKind: peak.kind as BinaryKind, lhs, rhs, span },
|
{ kind: "binary", binaryKind: peek.kind as BinaryKind, lhs, rhs, span },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -318,8 +281,6 @@ function parseExprAtom(startT: Token[]): [Token[], Expr] {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tok.kind === "identifier") {
|
if (tok.kind === "identifier") {
|
||||||
console.log(t);
|
|
||||||
|
|
||||||
if (maybeNextT(t)[1]?.kind === "{") {
|
if (maybeNextT(t)[1]?.kind === "{") {
|
||||||
let fields;
|
let fields;
|
||||||
[t, fields] = parseStructInit(t);
|
[t, fields] = parseStructInit(t);
|
||||||
|
|
@ -344,6 +305,52 @@ function parseExprAtom(startT: Token[]): [Token[], Expr] {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tok.kind === "let") {
|
||||||
|
let name;
|
||||||
|
[t, name] = expectNext<TokenIdent>(t, "identifier");
|
||||||
|
|
||||||
|
let type = undefined;
|
||||||
|
let colon;
|
||||||
|
[t, colon] = eat(t, ":");
|
||||||
|
if (colon) {
|
||||||
|
[t, type] = parseType(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
[t] = expectNext(t, "=");
|
||||||
|
let rhs;
|
||||||
|
[t, rhs] = parseExpr(t);
|
||||||
|
|
||||||
|
const nameIdent: Identifier = { name: name.ident, span: name.span };
|
||||||
|
|
||||||
|
return [
|
||||||
|
t,
|
||||||
|
{
|
||||||
|
kind: "let",
|
||||||
|
name: nameIdent,
|
||||||
|
type,
|
||||||
|
rhs,
|
||||||
|
span: name.span,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tok.kind === "if") {
|
||||||
|
let cond;
|
||||||
|
[t, cond] = parseExpr(t);
|
||||||
|
[t] = expectNext(t, "then");
|
||||||
|
let then;
|
||||||
|
[t, then] = parseExpr(t);
|
||||||
|
|
||||||
|
let elseTok;
|
||||||
|
[t, elseTok] = eat(t, "else");
|
||||||
|
let elsePart = undefined;
|
||||||
|
if (elseTok) {
|
||||||
|
[t, elsePart] = parseExpr(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [t, { kind: "if", cond, then, else: elsePart, span: tok.span }];
|
||||||
|
}
|
||||||
|
|
||||||
// Parse nothing at all.
|
// Parse nothing at all.
|
||||||
return [startT, { kind: "empty", span }];
|
return [startT, { kind: "empty", span }];
|
||||||
}
|
}
|
||||||
|
|
@ -496,3 +503,30 @@ function maybeNextT(t: Token[]): [Token[], Token | undefined] {
|
||||||
function unexpectedToken(token: Token): never {
|
function unexpectedToken(token: Token): never {
|
||||||
throw new CompilerError("unexpected token", token.span);
|
throw new CompilerError("unexpected token", token.span);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validateAst(ast: Ast) {
|
||||||
|
const validator: Folder = {
|
||||||
|
...DEFAULT_FOLDER,
|
||||||
|
expr(value: Expr): Expr {
|
||||||
|
if (value.kind === "block") {
|
||||||
|
value.exprs.forEach((expr) => {
|
||||||
|
if (expr.kind === "let") {
|
||||||
|
this.expr(expr.rhs);
|
||||||
|
if (expr.type) {
|
||||||
|
this.type(expr.type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.expr(expr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return value;
|
||||||
|
} else if (value.kind === "let") {
|
||||||
|
throw new CompilerError("let is only allowed in blocks", value.span);
|
||||||
|
} else {
|
||||||
|
return superFoldExpr(value, this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
foldAst(ast, validator);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,10 +53,10 @@ function printExpr(expr: Expr, indent: number): string {
|
||||||
case "let": {
|
case "let": {
|
||||||
const type = expr.type ? `: ${printType(expr.type)}` : "";
|
const type = expr.type ? `: ${printType(expr.type)}` : "";
|
||||||
|
|
||||||
return `let ${expr.name}${type} = ${printExpr(
|
return `let ${expr.name.name}${type} = ${printExpr(
|
||||||
expr.rhs,
|
expr.rhs,
|
||||||
indent + 1
|
indent + 1
|
||||||
)} in${linebreak(indent)}${printExpr(expr.after, indent)}`;
|
)}`;
|
||||||
}
|
}
|
||||||
case "block": {
|
case "block": {
|
||||||
const exprs = expr.exprs.map((expr) => printExpr(expr, indent + 1));
|
const exprs = expr.exprs.map((expr) => printExpr(expr, indent + 1));
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,10 @@ import {
|
||||||
BUILTINS,
|
BUILTINS,
|
||||||
BuiltinName,
|
BuiltinName,
|
||||||
DEFAULT_FOLDER,
|
DEFAULT_FOLDER,
|
||||||
|
Expr,
|
||||||
Folder,
|
Folder,
|
||||||
Identifier,
|
Identifier,
|
||||||
|
LocalInfo,
|
||||||
Resolution,
|
Resolution,
|
||||||
foldAst,
|
foldAst,
|
||||||
superFoldExpr,
|
superFoldExpr,
|
||||||
|
|
@ -68,6 +70,8 @@ export function resolve(ast: Ast): Ast {
|
||||||
throw new CompilerError(`cannot find ${ident.name}`, ident.span);
|
throw new CompilerError(`cannot find ${ident.name}`, ident.span);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const blockLocals: LocalInfo[][] = [];
|
||||||
|
|
||||||
const resolver: Folder = {
|
const resolver: Folder = {
|
||||||
...DEFAULT_FOLDER,
|
...DEFAULT_FOLDER,
|
||||||
item(item) {
|
item(item) {
|
||||||
|
|
@ -82,7 +86,7 @@ export function resolve(ast: Ast): Ast {
|
||||||
item.node.returnType && this.type(item.node.returnType);
|
item.node.returnType && this.type(item.node.returnType);
|
||||||
|
|
||||||
item.node.params.forEach(({ name }) => scopes.push(name));
|
item.node.params.forEach(({ name }) => scopes.push(name));
|
||||||
const body = superFoldExpr(item.node.body, this);
|
const body = this.expr(item.node.body);
|
||||||
const revParams = item.node.params.slice();
|
const revParams = item.node.params.slice();
|
||||||
revParams.reverse();
|
revParams.reverse();
|
||||||
revParams.forEach(({ name }) => popScope(name));
|
revParams.forEach(({ name }) => popScope(name));
|
||||||
|
|
@ -104,24 +108,39 @@ export function resolve(ast: Ast): Ast {
|
||||||
return superFoldItem(item, this);
|
return superFoldItem(item, this);
|
||||||
},
|
},
|
||||||
expr(expr) {
|
expr(expr) {
|
||||||
if (expr.kind === "let") {
|
if (expr.kind === "block") {
|
||||||
const rhs = this.expr(expr.rhs);
|
const prevScopeLength = scopes.length;
|
||||||
const type = expr.type && this.type(expr.type);
|
blockLocals.push([]);
|
||||||
scopes.push(expr.name);
|
|
||||||
const after = this.expr(expr.after);
|
const exprs = expr.exprs.map<Expr>((inner) => this.expr(inner));
|
||||||
popScope(expr.name);
|
|
||||||
|
scopes.length = prevScopeLength;
|
||||||
|
const locals = blockLocals.pop();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kind: "let",
|
kind: "block",
|
||||||
name: expr.name,
|
exprs,
|
||||||
rhs,
|
locals,
|
||||||
after,
|
|
||||||
type,
|
|
||||||
span: expr.span,
|
span: expr.span,
|
||||||
};
|
};
|
||||||
}
|
} else if (expr.kind === "let") {
|
||||||
|
let rhs = this.expr(expr.rhs);
|
||||||
|
let type = expr.type && this.type(expr.type);
|
||||||
|
|
||||||
return superFoldExpr(expr, this);
|
scopes.push(expr.name.name);
|
||||||
|
const local = { name: expr.name.name, span: expr.name.span };
|
||||||
|
blockLocals[blockLocals.length - 1].push(local);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...expr,
|
||||||
|
name: expr.name,
|
||||||
|
local,
|
||||||
|
type,
|
||||||
|
rhs,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return superFoldExpr(expr, this);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
ident(ident) {
|
ident(ident) {
|
||||||
const res = resolveIdent(ident);
|
const res = resolveIdent(ident);
|
||||||
|
|
|
||||||
|
|
@ -425,9 +425,11 @@ export function checkBody(
|
||||||
const rhs = this.expr(expr.rhs);
|
const rhs = this.expr(expr.rhs);
|
||||||
infcx.assign(bindingTy, rhs.ty!, expr.span);
|
infcx.assign(bindingTy, rhs.ty!, expr.span);
|
||||||
|
|
||||||
|
// AST validation ensures that lets can only be in blocks, where
|
||||||
|
// the types will be popped.
|
||||||
localTys.push(bindingTy);
|
localTys.push(bindingTy);
|
||||||
const after = this.expr(expr.after);
|
|
||||||
localTys.pop();
|
expr.local!.ty = bindingTy;
|
||||||
|
|
||||||
const type: Type | undefined = loweredBindingTy && {
|
const type: Type | undefined = loweredBindingTy && {
|
||||||
...expr.type!,
|
...expr.type!,
|
||||||
|
|
@ -439,20 +441,23 @@ export function checkBody(
|
||||||
name: expr.name,
|
name: expr.name,
|
||||||
type,
|
type,
|
||||||
rhs,
|
rhs,
|
||||||
after,
|
ty: TY_UNIT,
|
||||||
ty: after.ty!,
|
|
||||||
span: expr.span,
|
span: expr.span,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case "block": {
|
case "block": {
|
||||||
|
const prevLocalTysLen = localTys.length;
|
||||||
|
|
||||||
const exprs = expr.exprs.map((expr) => this.expr(expr));
|
const exprs = expr.exprs.map((expr) => this.expr(expr));
|
||||||
|
|
||||||
const ty = exprs.length > 0 ? exprs[exprs.length - 1].ty! : TY_UNIT;
|
const ty = exprs.length > 0 ? exprs[exprs.length - 1].ty! : TY_UNIT;
|
||||||
|
|
||||||
|
localTys.length = prevLocalTysLen;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
kind: "block",
|
...expr,
|
||||||
exprs,
|
exprs,
|
||||||
ty,
|
ty,
|
||||||
span: expr.span,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case "literal": {
|
case "literal": {
|
||||||
|
|
@ -592,13 +597,25 @@ export function checkBody(
|
||||||
|
|
||||||
infcx.assign(fnTy.returnTy, checked.ty!, body.span);
|
infcx.assign(fnTy.returnTy, checked.ty!, body.span);
|
||||||
|
|
||||||
|
const resolveTy = (ty: Ty, span: Span) => {
|
||||||
|
const resTy = infcx.resolveIfPossible(ty);
|
||||||
|
if (!resTy) {
|
||||||
|
throw new CompilerError("cannot infer type", span);
|
||||||
|
}
|
||||||
|
return resTy;
|
||||||
|
};
|
||||||
|
|
||||||
const resolver: Folder = {
|
const resolver: Folder = {
|
||||||
...DEFAULT_FOLDER,
|
...DEFAULT_FOLDER,
|
||||||
expr(expr) {
|
expr(expr) {
|
||||||
const ty = infcx.resolveIfPossible(expr.ty!);
|
const ty = resolveTy(expr.ty!, expr.span);
|
||||||
if (!ty) {
|
|
||||||
throw new CompilerError("cannot infer type", expr.span);
|
if (expr.kind === "block") {
|
||||||
|
expr.locals!.forEach((local) => {
|
||||||
|
local.ty = resolveTy(local.ty!, local.span);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return { ...expr, ty };
|
return { ...expr, ty };
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue