mirror of
https://github.com/Noratrieb/riverdelta.git
synced 2026-01-16 17:35:02 +01:00
name resolution
This commit is contained in:
parent
cc2a9aeca8
commit
35f1c92e36
6 changed files with 449 additions and 60 deletions
245
src/ast.ts
245
src/ast.ts
|
|
@ -1,5 +1,13 @@
|
||||||
import { Span } from "./error";
|
import { Span } from "./error";
|
||||||
|
|
||||||
|
export type Ast = Item[];
|
||||||
|
|
||||||
|
export type Identifier = {
|
||||||
|
name: string;
|
||||||
|
span: Span;
|
||||||
|
res?: Resolution;
|
||||||
|
};
|
||||||
|
|
||||||
export type ItemKind = {
|
export type ItemKind = {
|
||||||
kind: "function";
|
kind: "function";
|
||||||
node: FunctionDef;
|
node: FunctionDef;
|
||||||
|
|
@ -22,40 +30,67 @@ export type FunctionArg = {
|
||||||
span: Span;
|
span: Span;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ExprKind =
|
export type ExprEmpty = { kind: "empty" };
|
||||||
| { kind: "empty" }
|
|
||||||
| { kind: "let"; name: string; type?: Type; rhs: Expr; after: Expr }
|
export type ExprLet = {
|
||||||
| { kind: "block"; exprs: Expr[] }
|
kind: "let";
|
||||||
| {
|
name: string;
|
||||||
|
type?: Type;
|
||||||
|
rhs: Expr;
|
||||||
|
after: Expr;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ExprBlock = {
|
||||||
|
kind: "block";
|
||||||
|
exprs: Expr[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ExprLiteral = {
|
||||||
kind: "literal";
|
kind: "literal";
|
||||||
value: Literal;
|
value: Literal;
|
||||||
}
|
};
|
||||||
| {
|
|
||||||
|
export type ExprIdent = {
|
||||||
kind: "ident";
|
kind: "ident";
|
||||||
value: string;
|
value: Identifier;
|
||||||
}
|
};
|
||||||
| {
|
|
||||||
|
export type ExprBinary = {
|
||||||
kind: "binary";
|
kind: "binary";
|
||||||
binaryKind: BinaryKind;
|
binaryKind: BinaryKind;
|
||||||
lhs: Expr;
|
lhs: Expr;
|
||||||
rhs: Expr;
|
rhs: Expr;
|
||||||
}
|
};
|
||||||
| {
|
|
||||||
|
export type ExprUnary = {
|
||||||
kind: "unary";
|
kind: "unary";
|
||||||
unaryKind: UnaryKind;
|
unaryKind: UnaryKind;
|
||||||
rhs: Expr;
|
rhs: Expr;
|
||||||
}
|
};
|
||||||
| {
|
|
||||||
|
export type ExprCall = {
|
||||||
kind: "call";
|
kind: "call";
|
||||||
lhs: Expr;
|
lhs: Expr;
|
||||||
args: Expr[];
|
args: Expr[];
|
||||||
}
|
};
|
||||||
| {
|
|
||||||
|
export type ExprIf = {
|
||||||
kind: "if";
|
kind: "if";
|
||||||
cond: Expr;
|
cond: Expr;
|
||||||
then: Expr;
|
then: Expr;
|
||||||
else?: Expr;
|
else?: Expr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ExprKind =
|
||||||
|
| ExprEmpty
|
||||||
|
| ExprLet
|
||||||
|
| ExprBlock
|
||||||
|
| ExprLiteral
|
||||||
|
| ExprIdent
|
||||||
|
| ExprBinary
|
||||||
|
| ExprUnary
|
||||||
|
| ExprCall
|
||||||
|
| ExprIf;
|
||||||
|
|
||||||
export type Expr = ExprKind & {
|
export type Expr = ExprKind & {
|
||||||
span: Span;
|
span: Span;
|
||||||
|
|
@ -126,7 +161,7 @@ export const UNARY_KINDS: UnaryKind[] = ["!", "-"];
|
||||||
export type TypeKind =
|
export type TypeKind =
|
||||||
| {
|
| {
|
||||||
kind: "ident";
|
kind: "ident";
|
||||||
value: string;
|
value: Identifier;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
kind: "list";
|
kind: "list";
|
||||||
|
|
@ -140,3 +175,177 @@ export type TypeKind =
|
||||||
export type Type = TypeKind & {
|
export type Type = TypeKind & {
|
||||||
span: Span;
|
span: Span;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Resolution =
|
||||||
|
| {
|
||||||
|
kind: "local";
|
||||||
|
/**
|
||||||
|
* The index of the local variable, from inside out.
|
||||||
|
* ```
|
||||||
|
* let a in let b in (a, b);
|
||||||
|
* ^ ^
|
||||||
|
* 1 0
|
||||||
|
* ```
|
||||||
|
* When traversing resolutions, a stack of locals has to be kept.
|
||||||
|
* It's similar to a De Bruijn index.
|
||||||
|
*/
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
kind: "item";
|
||||||
|
/**
|
||||||
|
* Items are numbered in the order they appear in.
|
||||||
|
* Right now we only have one scope of items (global)
|
||||||
|
* so this is enough.
|
||||||
|
*/
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
kind: "builtin";
|
||||||
|
};
|
||||||
|
|
||||||
|
// folders
|
||||||
|
|
||||||
|
export type FoldFn<T> = (value: T) => T;
|
||||||
|
|
||||||
|
export type Folder = {
|
||||||
|
item: FoldFn<Item>;
|
||||||
|
expr: FoldFn<Expr>;
|
||||||
|
ident: FoldFn<Identifier>;
|
||||||
|
type: FoldFn<Type>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DEFAULT_FOLDER: Folder = {
|
||||||
|
item(item) {
|
||||||
|
return super_fold_item(item, this);
|
||||||
|
},
|
||||||
|
expr(expr) {
|
||||||
|
return super_fold_expr(expr, this);
|
||||||
|
},
|
||||||
|
ident(ident) {
|
||||||
|
return ident;
|
||||||
|
},
|
||||||
|
type(type) {
|
||||||
|
return super_fold_type(type, this);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function fold_ast(ast: Ast, folder: Folder): Ast {
|
||||||
|
return ast.map((item) => folder.item(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function super_fold_item(item: Item, folder: Folder): Item {
|
||||||
|
switch (item.kind) {
|
||||||
|
case "function": {
|
||||||
|
const args = item.node.args.map(({ name, type, span }) => ({
|
||||||
|
name,
|
||||||
|
type: folder.type(type),
|
||||||
|
span,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
kind: "function",
|
||||||
|
span: item.span,
|
||||||
|
node: {
|
||||||
|
name: item.node.name,
|
||||||
|
args,
|
||||||
|
body: folder.expr(item.node.body),
|
||||||
|
returnType: item.node.returnType && folder.type(item.node.returnType),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function super_fold_expr(expr: Expr, folder: Folder): Expr {
|
||||||
|
const span = expr.span;
|
||||||
|
switch (expr.kind) {
|
||||||
|
case "empty": {
|
||||||
|
return { kind: "empty", span };
|
||||||
|
}
|
||||||
|
case "let": {
|
||||||
|
return {
|
||||||
|
kind: "let",
|
||||||
|
name: expr.name,
|
||||||
|
type: expr.type && folder.type(expr.type),
|
||||||
|
rhs: folder.expr(expr.rhs),
|
||||||
|
after: folder.expr(expr.after),
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "block": {
|
||||||
|
return {
|
||||||
|
kind: "block",
|
||||||
|
exprs: expr.exprs.map((expr) => folder.expr(expr)),
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "literal": {
|
||||||
|
return { kind: "literal", value: expr.value, span };
|
||||||
|
}
|
||||||
|
case "ident": {
|
||||||
|
return { kind: "ident", value: folder.ident(expr.value), span };
|
||||||
|
}
|
||||||
|
case "binary": {
|
||||||
|
return {
|
||||||
|
kind: "binary",
|
||||||
|
binaryKind: expr.binaryKind,
|
||||||
|
lhs: folder.expr(expr.lhs),
|
||||||
|
rhs: folder.expr(expr.rhs),
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "unary": {
|
||||||
|
return {
|
||||||
|
kind: "unary",
|
||||||
|
unaryKind: expr.unaryKind,
|
||||||
|
rhs: folder.expr(expr.rhs),
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "call": {
|
||||||
|
return {
|
||||||
|
kind: "call",
|
||||||
|
lhs: folder.expr(expr.lhs),
|
||||||
|
args: expr.args.map((expr) => folder.expr(expr)),
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "if": {
|
||||||
|
return {
|
||||||
|
kind: "if",
|
||||||
|
cond: folder.expr(expr.cond),
|
||||||
|
then: folder.expr(expr.then),
|
||||||
|
else: expr.else && folder.expr(expr.else),
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function super_fold_type(type: Type, folder: Folder): Type {
|
||||||
|
const span = type.span;
|
||||||
|
switch (type.kind) {
|
||||||
|
case "ident": {
|
||||||
|
return {
|
||||||
|
kind: "ident",
|
||||||
|
value: folder.ident(type.value),
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "list": {
|
||||||
|
return {
|
||||||
|
kind: "list",
|
||||||
|
elem: folder.type(type.elem),
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case "tuple": {
|
||||||
|
return {
|
||||||
|
kind: "tuple",
|
||||||
|
elems: type.elems.map((type) => folder.type(type)),
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,6 @@ export function lines(input: string): Span[] {
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function todo(msg: string, span: Span): never {
|
export function todo(msg: string): never {
|
||||||
throw new CompilerError(`TODO: ${msg}`, span);
|
throw new CompilerError(`TODO: ${msg}`, { start: 0, end: 0 });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
25
src/index.ts
25
src/index.ts
|
|
@ -2,15 +2,20 @@ import { withErrorHandler } from "./error";
|
||||||
import { tokenize } from "./lexer";
|
import { tokenize } from "./lexer";
|
||||||
import { parse } from "./parser";
|
import { parse } from "./parser";
|
||||||
import { printAst } from "./printer";
|
import { printAst } from "./printer";
|
||||||
|
import { resolve } from "./resolve";
|
||||||
|
|
||||||
const input = `
|
const input = `
|
||||||
function main(argv: [String]): uwu = (
|
function main(argv: [String]): () = (
|
||||||
print("Hello, world!");
|
print(argv);
|
||||||
let a: [String] = 0 in
|
if 1 then (
|
||||||
let b = 1 in
|
print("AAAAAAAAAAAAAAAAAAAA");
|
||||||
if 0 then 0 else (
|
let a = 0 in
|
||||||
if 1 == 1 then 1 else "what"
|
a;
|
||||||
;"meow")
|
) else (
|
||||||
|
print("AAAAAAAAAAAAAAAAAAAAAA");
|
||||||
|
let b = 0 in
|
||||||
|
b;
|
||||||
|
)
|
||||||
);
|
);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
@ -23,12 +28,16 @@ function main() {
|
||||||
const ast = parse(tokens);
|
const ast = parse(tokens);
|
||||||
console.log("-----AST------");
|
console.log("-----AST------");
|
||||||
|
|
||||||
console.dir(ast, { depth: 10 });
|
console.dir(ast, { depth: 50 });
|
||||||
|
|
||||||
const printed = printAst(ast);
|
const printed = printAst(ast);
|
||||||
console.log("-----AST pretty------");
|
console.log("-----AST pretty------");
|
||||||
console.log(printed);
|
console.log(printed);
|
||||||
|
|
||||||
|
const resolved = resolve(ast);
|
||||||
|
console.log("-----AST resolved------");
|
||||||
|
const resolvedPrinted = printAst(resolved);
|
||||||
|
console.log(resolvedPrinted);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import {
|
import {
|
||||||
ARITH_FACTOR_KINDS,
|
ARITH_FACTOR_KINDS,
|
||||||
ARITH_TERM_KINDS,
|
ARITH_TERM_KINDS,
|
||||||
|
Ast,
|
||||||
BinaryKind,
|
BinaryKind,
|
||||||
COMPARISON_KINDS,
|
COMPARISON_KINDS,
|
||||||
Expr,
|
Expr,
|
||||||
|
|
@ -17,7 +18,7 @@ import { BaseToken, Token, TokenIdent } from "./lexer";
|
||||||
|
|
||||||
type Parser<T> = (t: Token[]) => [Token[], T];
|
type Parser<T> = (t: Token[]) => [Token[], T];
|
||||||
|
|
||||||
export function parse(t: Token[]): Item[] {
|
export function parse(t: Token[]): Ast {
|
||||||
const items: Item[] = [];
|
const items: Item[] = [];
|
||||||
|
|
||||||
while (t.length > 0) {
|
while (t.length > 0) {
|
||||||
|
|
@ -242,6 +243,7 @@ function parseExprCall(t: Token[]): [Token[], Expr] {
|
||||||
|
|
||||||
function parseExprAtom(startT: Token[]): [Token[], Expr] {
|
function parseExprAtom(startT: Token[]): [Token[], Expr] {
|
||||||
let [t, tok] = next(startT);
|
let [t, tok] = next(startT);
|
||||||
|
const span = tok.span;
|
||||||
|
|
||||||
if (tok.kind === "(") {
|
if (tok.kind === "(") {
|
||||||
let expr: Expr;
|
let expr: Expr;
|
||||||
|
|
@ -255,7 +257,7 @@ function parseExprAtom(startT: Token[]): [Token[], Expr] {
|
||||||
}
|
}
|
||||||
[t] = expectNext(t, ")");
|
[t] = expectNext(t, ")");
|
||||||
|
|
||||||
return [t, { kind: "block", span: tok.span, exprs }];
|
return [t, { kind: "block", span, exprs }];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tok.kind === "lit_string") {
|
if (tok.kind === "lit_string") {
|
||||||
|
|
@ -263,7 +265,7 @@ function parseExprAtom(startT: Token[]): [Token[], Expr] {
|
||||||
t,
|
t,
|
||||||
{
|
{
|
||||||
kind: "literal",
|
kind: "literal",
|
||||||
span: tok.span,
|
span,
|
||||||
value: { kind: "str", value: tok.value },
|
value: { kind: "str", value: tok.value },
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
@ -274,33 +276,48 @@ function parseExprAtom(startT: Token[]): [Token[], Expr] {
|
||||||
t,
|
t,
|
||||||
{
|
{
|
||||||
kind: "literal",
|
kind: "literal",
|
||||||
span: tok.span,
|
span,
|
||||||
value: { kind: "int", value: tok.value },
|
value: { kind: "int", value: tok.value },
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tok.kind === "identifier") {
|
if (tok.kind === "identifier") {
|
||||||
return [t, { kind: "ident", span: tok.span, value: tok.ident }];
|
return [
|
||||||
|
t,
|
||||||
|
{
|
||||||
|
kind: "ident",
|
||||||
|
span,
|
||||||
|
value: { name: tok.ident, span },
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse nothing at all.
|
// Parse nothing at all.
|
||||||
return [startT, { kind: "empty", span: tok.span }];
|
return [startT, { kind: "empty", span }];
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseType(t: Token[]): [Token[], Type] {
|
function parseType(t: Token[]): [Token[], Type] {
|
||||||
let tok;
|
let tok;
|
||||||
[t, tok] = next(t);
|
[t, tok] = next(t);
|
||||||
|
const span = tok.span;
|
||||||
|
|
||||||
switch (tok.kind) {
|
switch (tok.kind) {
|
||||||
case "identifier": {
|
case "identifier": {
|
||||||
return [t, { kind: "ident", value: tok.ident, span: tok.span }];
|
return [
|
||||||
|
t,
|
||||||
|
{
|
||||||
|
kind: "ident",
|
||||||
|
value: { name: tok.ident, span },
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
case "[": {
|
case "[": {
|
||||||
let elem;
|
let elem;
|
||||||
[t, elem] = parseType(t);
|
[t, elem] = parseType(t);
|
||||||
[t] = expectNext(t, "]");
|
[t] = expectNext(t, "]");
|
||||||
return [t, { kind: "list", elem, span: tok.span }];
|
return [t, { kind: "list", elem, span }];
|
||||||
}
|
}
|
||||||
case "(": {
|
case "(": {
|
||||||
let first = true;
|
let first = true;
|
||||||
|
|
@ -316,12 +333,12 @@ function parseType(t: Token[]): [Token[], Type] {
|
||||||
}
|
}
|
||||||
[t] = expectNext(t, ")");
|
[t] = expectNext(t, ")");
|
||||||
|
|
||||||
return [t, { kind: "tuple", elems, span: tok.span }];
|
return [t, { kind: "tuple", elems, span }];
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
`unexpected token: \`${tok.kind}\`, expected type`,
|
`unexpected token: \`${tok.kind}\`, expected type`,
|
||||||
tok.span
|
span
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,14 @@
|
||||||
import { Expr, FunctionDef, Item, Type } from "./ast";
|
import {
|
||||||
|
Ast,
|
||||||
|
Expr,
|
||||||
|
FunctionDef,
|
||||||
|
Identifier,
|
||||||
|
Item,
|
||||||
|
Resolution,
|
||||||
|
Type,
|
||||||
|
} from "./ast";
|
||||||
|
|
||||||
export function printAst(ast: Item[]): string {
|
export function printAst(ast: Ast): string {
|
||||||
return ast.map(printItem).join("\n");
|
return ast.map(printItem).join("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,7 +75,7 @@ function printExpr(expr: Expr, indent: number): string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "ident": {
|
case "ident": {
|
||||||
return expr.value;
|
return printIdent(expr.value);
|
||||||
}
|
}
|
||||||
case "binary": {
|
case "binary": {
|
||||||
return `${printExpr(expr.lhs, indent)} ${expr.binaryKind} ${printExpr(
|
return `${printExpr(expr.lhs, indent)} ${expr.binaryKind} ${printExpr(
|
||||||
|
|
@ -107,7 +115,7 @@ function printExpr(expr: Expr, indent: number): string {
|
||||||
function printType(type: Type): string {
|
function printType(type: Type): string {
|
||||||
switch (type.kind) {
|
switch (type.kind) {
|
||||||
case "ident":
|
case "ident":
|
||||||
return type.value;
|
return printIdent(type.value);
|
||||||
case "list":
|
case "list":
|
||||||
return `[${printType(type.elem)}]`;
|
return `[${printType(type.elem)}]`;
|
||||||
case "tuple":
|
case "tuple":
|
||||||
|
|
@ -115,6 +123,22 @@ function printType(type: Type): string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function printIdent(ident: Identifier): string {
|
||||||
|
const printRes = (res: Resolution): string => {
|
||||||
|
switch (res.kind) {
|
||||||
|
case "local":
|
||||||
|
return `#${res.index}`;
|
||||||
|
case "item":
|
||||||
|
return `#G${res.index}`;
|
||||||
|
case "builtin": {
|
||||||
|
return `#B`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const res = ident.res ? printRes(ident.res) : "";
|
||||||
|
return `${ident.name}${res}`;
|
||||||
|
}
|
||||||
|
|
||||||
function linebreak(indent: number): string {
|
function linebreak(indent: number): string {
|
||||||
return `\n${ind(indent)}`;
|
return `\n${ind(indent)}`;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
130
src/resolve.ts
Normal file
130
src/resolve.ts
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
import {
|
||||||
|
Ast,
|
||||||
|
DEFAULT_FOLDER,
|
||||||
|
Folder,
|
||||||
|
Identifier,
|
||||||
|
Resolution,
|
||||||
|
fold_ast,
|
||||||
|
super_fold_expr,
|
||||||
|
super_fold_item,
|
||||||
|
} from "./ast";
|
||||||
|
import { CompilerError } from "./error";
|
||||||
|
|
||||||
|
const BUILTINS = new Set<string>(["print", "String"]);
|
||||||
|
|
||||||
|
export function resolve(ast: Ast): Ast {
|
||||||
|
const items = new Map<string, number>();
|
||||||
|
|
||||||
|
for (let i = 0; i < ast.length; i++) {
|
||||||
|
const item = ast[i];
|
||||||
|
const existing = items.get(item.node.name);
|
||||||
|
if (existing !== undefined) {
|
||||||
|
throw new CompilerError(
|
||||||
|
`item \`${item.node.name}\` has already been declared`,
|
||||||
|
item.span
|
||||||
|
);
|
||||||
|
}
|
||||||
|
items.set(item.node.name, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
const scopes: string[] = [];
|
||||||
|
|
||||||
|
const popScope = (expected: string) => {
|
||||||
|
const popped = scopes.pop();
|
||||||
|
if (popped !== expected) {
|
||||||
|
throw new Error(
|
||||||
|
`Scopes corrupted, wanted to pop ${name} but popped ${popped}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveIdent = (ident: Identifier): Resolution => {
|
||||||
|
const lastIdx = scopes.length - 1;
|
||||||
|
for (let i = lastIdx; i >= 0; i--) {
|
||||||
|
const candidate = scopes[i];
|
||||||
|
if (candidate === ident.name) {
|
||||||
|
const index = lastIdx - i;
|
||||||
|
return {
|
||||||
|
kind: "local",
|
||||||
|
index,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = items.get(ident.name);
|
||||||
|
if (item !== undefined) {
|
||||||
|
return {
|
||||||
|
kind: "item",
|
||||||
|
index: item,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BUILTINS.has(ident.name)) {
|
||||||
|
return { kind: "builtin" };
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new CompilerError(`cannot find ${ident.name}`, ident.span);
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolver: Folder = {
|
||||||
|
...DEFAULT_FOLDER,
|
||||||
|
item(item) {
|
||||||
|
switch (item.kind) {
|
||||||
|
case "function": {
|
||||||
|
const args = item.node.args.map(({ name, span, type }) => ({
|
||||||
|
name,
|
||||||
|
span,
|
||||||
|
type: this.type(type),
|
||||||
|
}));
|
||||||
|
const returnType =
|
||||||
|
item.node.returnType && this.type(item.node.returnType);
|
||||||
|
|
||||||
|
item.node.args.forEach(({ name }) => scopes.push(name));
|
||||||
|
const body = super_fold_expr(item.node.body, this);
|
||||||
|
item.node.args.forEach(({ name }) => popScope(name));
|
||||||
|
|
||||||
|
return {
|
||||||
|
kind: "function",
|
||||||
|
span: item.span,
|
||||||
|
node: {
|
||||||
|
name: item.node.name,
|
||||||
|
args,
|
||||||
|
returnType,
|
||||||
|
body,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return super_fold_item(item, this);
|
||||||
|
},
|
||||||
|
expr(expr) {
|
||||||
|
if (expr.kind === "let") {
|
||||||
|
const rhs = this.expr(expr.rhs);
|
||||||
|
const type = expr.type && this.type(expr.type);
|
||||||
|
scopes.push(expr.name);
|
||||||
|
const after = this.expr(expr.after);
|
||||||
|
popScope(expr.name);
|
||||||
|
|
||||||
|
return {
|
||||||
|
kind: "let",
|
||||||
|
name: expr.name,
|
||||||
|
rhs,
|
||||||
|
after,
|
||||||
|
type,
|
||||||
|
span: expr.span,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return super_fold_expr(expr, this);
|
||||||
|
},
|
||||||
|
ident(ident) {
|
||||||
|
const res = resolveIdent(ident);
|
||||||
|
return { name: ident.name, span: ident.span, res };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolved = fold_ast(ast, resolver);
|
||||||
|
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue