compiler: more work along the lines of generics
This commit is contained in:
parent
bc82124601
commit
cd923450f5
@ -15,7 +15,7 @@ export type StmtKind =
|
||||
| {
|
||||
type: "fn";
|
||||
ident: string;
|
||||
etypeParams?: ETypeParam[];
|
||||
genericParams?: GenericParam[];
|
||||
params: Param[];
|
||||
returnType?: EType;
|
||||
body: Expr;
|
||||
@ -39,13 +39,18 @@ export type ExprKind =
|
||||
| { type: "error" }
|
||||
| { type: "int"; value: number }
|
||||
| { type: "string"; value: string }
|
||||
| { type: "ident"; value: string }
|
||||
| { type: "ident"; ident: string }
|
||||
| {
|
||||
type: "sym";
|
||||
ident: string;
|
||||
sym: Sym;
|
||||
}
|
||||
| { type: "group"; expr: Expr }
|
||||
| { type: "field"; subject: Expr; value: string }
|
||||
| { type: "field"; subject: Expr; ident: string }
|
||||
| { type: "index"; subject: Expr; value: Expr }
|
||||
| { type: "call"; subject: Expr; etypeArgs?: EType[]; args: Expr[] }
|
||||
| { type: "path"; subject: Expr; value: string }
|
||||
| { type: "etype_args"; subject: Expr; etypeArgs?: EType[] }
|
||||
| { type: "call"; subject: Expr; args: Expr[] }
|
||||
| { type: "path"; subject: Expr; ident: string }
|
||||
| { type: "etype_args"; subject: Expr; etypeArgs: EType[] }
|
||||
| { type: "unary"; unaryType: UnaryType; subject: Expr }
|
||||
| { type: "binary"; binaryType: BinaryType; left: Expr; right: Expr }
|
||||
| { type: "if"; cond: Expr; truthy: Expr; falsy?: Expr; elsePos?: Pos }
|
||||
@ -53,11 +58,6 @@ export type ExprKind =
|
||||
| { type: "null" }
|
||||
| { type: "loop"; body: Expr }
|
||||
| { type: "block"; stmts: Stmt[]; expr?: Expr }
|
||||
| {
|
||||
type: "sym";
|
||||
ident: string;
|
||||
sym: Sym;
|
||||
}
|
||||
| { type: "while"; cond: Expr; body: Expr }
|
||||
| { type: "for_in"; param: Param; value: Expr; body: Expr }
|
||||
| {
|
||||
@ -101,7 +101,7 @@ export type SymKind =
|
||||
| { type: "fn"; stmt: Stmt }
|
||||
| { type: "fn_param"; param: Param }
|
||||
| { type: "closure"; inner: Sym }
|
||||
| { type: "generic"; etypeParam: ETypeParam };
|
||||
| { type: "generic"; genericParam: GenericParam };
|
||||
|
||||
export type EType = {
|
||||
kind: ETypeKind;
|
||||
@ -111,11 +111,20 @@ export type EType = {
|
||||
|
||||
export type ETypeKind =
|
||||
| { type: "error" }
|
||||
| { type: "ident"; value: string }
|
||||
| { type: "null" }
|
||||
| { type: "int" }
|
||||
| { type: "bool" }
|
||||
| { type: "string" }
|
||||
| { type: "ident"; ident: string }
|
||||
| {
|
||||
type: "sym";
|
||||
ident: string;
|
||||
sym: Sym;
|
||||
}
|
||||
| { type: "array"; inner: EType }
|
||||
| { type: "struct"; fields: Param[] };
|
||||
|
||||
export type ETypeParam = {
|
||||
export type GenericParam = {
|
||||
ident: string;
|
||||
pos: Pos;
|
||||
vtype?: VType;
|
||||
|
@ -22,6 +22,8 @@ export interface AstVisitor<Args extends unknown[] = []> {
|
||||
visitFieldExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitIndexExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitCallExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitPathExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitETypeArgsExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitUnaryExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitBinaryExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
visitIfExpr?(expr: Expr, ...args: Args): VisitRes;
|
||||
@ -36,7 +38,12 @@ export interface AstVisitor<Args extends unknown[] = []> {
|
||||
visitParam?(param: Param, ...args: Args): VisitRes;
|
||||
visitEType?(etype: EType, ...args: Args): VisitRes;
|
||||
visitErrorEType?(etype: EType, ...args: Args): VisitRes;
|
||||
visitNullEType?(etype: EType, ...args: Args): VisitRes;
|
||||
visitIntEType?(etype: EType, ...args: Args): VisitRes;
|
||||
visitBoolEType?(etype: EType, ...args: Args): VisitRes;
|
||||
visitStringEType?(etype: EType, ...args: Args): VisitRes;
|
||||
visitIdentEType?(etype: EType, ...args: Args): VisitRes;
|
||||
visitSymEType?(etype: EType, ...args: Args): VisitRes;
|
||||
visitArrayEType?(etype: EType, ...args: Args): VisitRes;
|
||||
visitStructEType?(etype: EType, ...args: Args): VisitRes;
|
||||
visitAnno?(etype: EType, ...args: Args): VisitRes;
|
||||
@ -95,6 +102,12 @@ export function visitStmt<Args extends unknown[] = []>(
|
||||
if (v.visitExprStmt?.(stmt, ...args) == "stop") return;
|
||||
visitExpr(stmt.kind.expr, v, ...args);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`statement '${
|
||||
(stmt.kind as { type: string }).type
|
||||
}' not implemented`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,6 +148,15 @@ export function visitExpr<Args extends unknown[] = []>(
|
||||
visitExpr(expr.kind.subject, v, ...args);
|
||||
expr.kind.args.map((arg) => visitExpr(arg, v, ...args));
|
||||
break;
|
||||
case "path":
|
||||
if (v.visitPathExpr?.(expr, ...args) == "stop") return;
|
||||
visitExpr(expr.kind.subject, v, ...args);
|
||||
break;
|
||||
case "etype_args":
|
||||
if (v.visitETypeArgsExpr?.(expr, ...args) == "stop") return;
|
||||
visitExpr(expr.kind.subject, v, ...args);
|
||||
expr.kind.etypeArgs.map((arg) => visitEType(arg, v, ...args));
|
||||
break;
|
||||
case "unary":
|
||||
if (v.visitUnaryExpr?.(expr, ...args) == "stop") return;
|
||||
visitExpr(expr.kind.subject, v, ...args);
|
||||
@ -186,6 +208,12 @@ export function visitExpr<Args extends unknown[] = []>(
|
||||
case "sym":
|
||||
if (v.visitSymExpr?.(expr, ...args) == "stop") return;
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`expression '${
|
||||
(expr.kind as { type: string }).type
|
||||
}' not implemented`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -208,9 +236,24 @@ export function visitEType<Args extends unknown[] = []>(
|
||||
case "error":
|
||||
if (v.visitErrorEType?.(etype, ...args) == "stop") return;
|
||||
break;
|
||||
case "string":
|
||||
if (v.visitStringEType?.(etype, ...args) == "stop") return;
|
||||
break;
|
||||
case "null":
|
||||
if (v.visitNullEType?.(etype, ...args) == "stop") return;
|
||||
break;
|
||||
case "int":
|
||||
if (v.visitIntEType?.(etype, ...args) == "stop") return;
|
||||
break;
|
||||
case "bool":
|
||||
if (v.visitBoolEType?.(etype, ...args) == "stop") return;
|
||||
break;
|
||||
case "ident":
|
||||
if (v.visitIdentEType?.(etype, ...args) == "stop") return;
|
||||
break;
|
||||
case "sym":
|
||||
if (v.visitSymEType?.(etype, ...args) == "stop") return;
|
||||
break;
|
||||
case "array":
|
||||
if (v.visitArrayEType?.(etype, ...args) == "stop") return;
|
||||
if (etype.kind.inner) visitEType(etype.kind.inner, v, ...args);
|
||||
@ -219,6 +262,12 @@ export function visitEType<Args extends unknown[] = []>(
|
||||
if (v.visitStructEType?.(etype, ...args) == "stop") return;
|
||||
etype.kind.fields.map((field) => visitParam(field, v, ...args));
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`etype '${
|
||||
(etype.kind as { type: string }).type
|
||||
}' not implemented`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,13 @@
|
||||
import { EType, Expr, Stmt } from "./ast.ts";
|
||||
import { printStackTrace, Reporter } from "./info.ts";
|
||||
import { Pos } from "./token.ts";
|
||||
import { VType, VTypeParam, vtypesEqual, vtypeToString } from "./vtype.ts";
|
||||
import {
|
||||
VType,
|
||||
VTypeGenericParam,
|
||||
VTypeParam,
|
||||
vtypesEqual,
|
||||
vtypeToString,
|
||||
} from "./vtype.ts";
|
||||
|
||||
export class Checker {
|
||||
private fnReturnStack: VType[] = [];
|
||||
@ -24,6 +30,13 @@ export class Checker {
|
||||
const returnType: VType = stmt.kind.returnType
|
||||
? this.checkEType(stmt.kind.returnType)
|
||||
: { type: "null" };
|
||||
let genericParams: VTypeGenericParam[] | undefined;
|
||||
if (stmt.kind.genericParams !== undefined) {
|
||||
genericParams = [];
|
||||
for (const etypeParam of stmt.kind.genericParams) {
|
||||
genericParams.push({ ident: etypeParam.ident });
|
||||
}
|
||||
}
|
||||
const params: VTypeParam[] = [];
|
||||
for (const param of stmt.kind.params) {
|
||||
if (param.etype === undefined) {
|
||||
@ -34,7 +47,7 @@ export class Checker {
|
||||
param.vtype = vtype;
|
||||
params.push({ ident: param.ident, vtype });
|
||||
}
|
||||
stmt.kind.vtype = { type: "fn", params, returnType };
|
||||
stmt.kind.vtype = { type: "fn", genericParams, params, returnType };
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,13 +191,13 @@ export class Checker {
|
||||
this.report("cannot use field on non-struct", pos);
|
||||
return { type: "error" };
|
||||
}
|
||||
const fieldValue = stmt.kind.subject.kind.value;
|
||||
const fieldValue = stmt.kind.subject.kind.ident;
|
||||
const found = subject.fields.find((param) =>
|
||||
param.ident === fieldValue
|
||||
);
|
||||
if (!found) {
|
||||
this.report(
|
||||
`no field named '${stmt.kind.subject.kind.value}' on struct`,
|
||||
`no field named '${stmt.kind.subject.kind.ident}' on struct`,
|
||||
pos,
|
||||
);
|
||||
return { type: "error" };
|
||||
@ -281,6 +294,10 @@ export class Checker {
|
||||
return this.checkIndexExpr(expr);
|
||||
case "call":
|
||||
return this.checkCallExpr(expr);
|
||||
case "path":
|
||||
return this.checkPathExpr(expr);
|
||||
case "etype_args":
|
||||
return this.checkETypeArgsExpr(expr);
|
||||
case "unary":
|
||||
return this.checkUnaryExpr(expr);
|
||||
case "binary":
|
||||
@ -324,9 +341,9 @@ export class Checker {
|
||||
}
|
||||
case "fn_param":
|
||||
return expr.kind.sym.param.vtype!;
|
||||
case "builtin":
|
||||
case "let_static":
|
||||
case "closure":
|
||||
case "generic":
|
||||
throw new Error(
|
||||
`not implemented, sym type '${expr.kind.sym.type}'`,
|
||||
);
|
||||
@ -343,11 +360,11 @@ export class Checker {
|
||||
this.report("cannot use field on non-struct", pos);
|
||||
return { type: "error" };
|
||||
}
|
||||
const value = expr.kind.value;
|
||||
const value = expr.kind.ident;
|
||||
const found = subject.fields.find((param) => param.ident === value);
|
||||
if (!found) {
|
||||
this.report(
|
||||
`no field named '${expr.kind.value}' on struct`,
|
||||
`no field named '${expr.kind.ident}' on struct`,
|
||||
pos,
|
||||
);
|
||||
return { type: "error" };
|
||||
@ -408,6 +425,43 @@ export class Checker {
|
||||
return subject.returnType;
|
||||
}
|
||||
|
||||
public checkPathExpr(expr: Expr): VType {
|
||||
if (expr.kind.type !== "path") {
|
||||
throw new Error();
|
||||
}
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
public checkETypeArgsExpr(expr: Expr): VType {
|
||||
if (expr.kind.type !== "etype_args") {
|
||||
throw new Error();
|
||||
}
|
||||
const pos = expr.pos;
|
||||
const subject = this.checkExpr(expr.kind.subject);
|
||||
if (subject.type !== "fn" || subject.genericParams === undefined) {
|
||||
this.report(
|
||||
"etype arguments must only be applied to generic functions",
|
||||
expr.pos,
|
||||
);
|
||||
return { type: "error" };
|
||||
}
|
||||
const genericParams = expr.kind.etypeArgs.map((arg) =>
|
||||
this.checkEType(arg)
|
||||
);
|
||||
if (genericParams.length !== subject.params.length) {
|
||||
this.report(
|
||||
`incorrect number of arguments` +
|
||||
`, expected ${subject.params.length}`,
|
||||
pos,
|
||||
);
|
||||
}
|
||||
return {
|
||||
type: "generic_spec",
|
||||
subject,
|
||||
genericParams,
|
||||
};
|
||||
}
|
||||
|
||||
public checkUnaryExpr(expr: Expr): VType {
|
||||
if (expr.kind.type !== "unary") {
|
||||
throw new Error();
|
||||
@ -556,20 +610,25 @@ export class Checker {
|
||||
|
||||
public checkEType(etype: EType): VType {
|
||||
const pos = etype.pos;
|
||||
if (etype.kind.type === "ident") {
|
||||
if (etype.kind.value === "null") {
|
||||
switch (etype.kind.type) {
|
||||
case "null":
|
||||
return { type: "null" };
|
||||
}
|
||||
if (etype.kind.value === "int") {
|
||||
case "int":
|
||||
return { type: "int" };
|
||||
}
|
||||
if (etype.kind.value === "bool") {
|
||||
case "bool":
|
||||
return { type: "bool" };
|
||||
}
|
||||
if (etype.kind.value === "string") {
|
||||
case "string":
|
||||
return { type: "string" };
|
||||
}
|
||||
if (etype.kind.type === "ident") {
|
||||
this.report(`undefined type '${etype.kind.ident}'`, pos);
|
||||
return { type: "error" };
|
||||
}
|
||||
if (etype.kind.type === "sym") {
|
||||
if (etype.kind.sym.type === "generic") {
|
||||
return { type: "generic" };
|
||||
}
|
||||
this.report(`undefined type '${etype.kind.value}'`, pos);
|
||||
this.report(`sym type '${etype.kind.sym.type}' used as type`, pos);
|
||||
return { type: "error" };
|
||||
}
|
||||
if (etype.kind.type === "array") {
|
||||
|
@ -80,12 +80,12 @@ export class SpecialLoopDesugarer implements AstVisitor {
|
||||
type: "call",
|
||||
subject: Expr({
|
||||
type: "ident",
|
||||
value: "int_array_length",
|
||||
ident: "int_array_length",
|
||||
}),
|
||||
args: [
|
||||
Expr({
|
||||
type: "ident",
|
||||
value: "::values",
|
||||
ident: "::values",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
@ -114,11 +114,11 @@ export class SpecialLoopDesugarer implements AstVisitor {
|
||||
binaryType: "<",
|
||||
left: Expr({
|
||||
type: "ident",
|
||||
value: "::index",
|
||||
ident: "::index",
|
||||
}),
|
||||
right: Expr({
|
||||
type: "ident",
|
||||
value: "::length",
|
||||
ident: "::length",
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
@ -139,11 +139,11 @@ export class SpecialLoopDesugarer implements AstVisitor {
|
||||
type: "index",
|
||||
subject: Expr({
|
||||
type: "ident",
|
||||
value: "::values",
|
||||
ident: "::values",
|
||||
}),
|
||||
value: Expr({
|
||||
type: "ident",
|
||||
value: "::index",
|
||||
ident: "::index",
|
||||
}),
|
||||
}),
|
||||
}, expr.pos),
|
||||
@ -156,7 +156,7 @@ export class SpecialLoopDesugarer implements AstVisitor {
|
||||
assignType: "+=",
|
||||
subject: Expr({
|
||||
type: "ident",
|
||||
value: "::index",
|
||||
ident: "::index",
|
||||
}),
|
||||
value: Expr({
|
||||
type: "int",
|
||||
|
@ -36,15 +36,18 @@ export class Lexer {
|
||||
"else",
|
||||
"struct",
|
||||
"import",
|
||||
"false",
|
||||
"true",
|
||||
"null",
|
||||
"or",
|
||||
"and",
|
||||
"not",
|
||||
"while",
|
||||
"for",
|
||||
"in",
|
||||
"false",
|
||||
"true",
|
||||
"null",
|
||||
"int",
|
||||
"bool",
|
||||
"string",
|
||||
];
|
||||
if (keywords.includes(value)) {
|
||||
return this.token(value, pos);
|
||||
|
@ -107,7 +107,7 @@ export class Lowerer {
|
||||
switch (stmt.kind.subject.kind.type) {
|
||||
case "field": {
|
||||
this.lowerExpr(stmt.kind.subject.kind.subject);
|
||||
this.program.add(Ops.PushString, stmt.kind.subject.kind.value);
|
||||
this.program.add(Ops.PushString, stmt.kind.subject.kind.ident);
|
||||
this.program.add(Ops.Builtin, Builtins.StructSet);
|
||||
return;
|
||||
}
|
||||
@ -209,7 +209,7 @@ export class Lowerer {
|
||||
`unexpected argument type '${anno.kind.type}' expected 'ident'`,
|
||||
);
|
||||
}
|
||||
const value = anno.kind.value;
|
||||
const value = anno.kind.ident;
|
||||
const builtin = Object.entries(Builtins).find((entry) =>
|
||||
entry[0] === value
|
||||
)?.[1];
|
||||
|
@ -5,9 +5,9 @@ import {
|
||||
BinaryType,
|
||||
EType,
|
||||
ETypeKind,
|
||||
ETypeParam,
|
||||
Expr,
|
||||
ExprKind,
|
||||
GenericParam,
|
||||
Param,
|
||||
Stmt,
|
||||
StmtKind,
|
||||
@ -175,9 +175,9 @@ export class Parser {
|
||||
}
|
||||
const ident = this.current().identValue!;
|
||||
this.step();
|
||||
let etypeParams: ETypeParam[] | undefined;
|
||||
let genericParams: GenericParam[] | undefined;
|
||||
if (this.test("<")) {
|
||||
etypeParams = this.parseFnETypeParams();
|
||||
genericParams = this.parseFnETypeParams();
|
||||
}
|
||||
if (!this.test("(")) {
|
||||
this.report("expected '('");
|
||||
@ -207,7 +207,7 @@ export class Parser {
|
||||
{
|
||||
type: "fn",
|
||||
ident,
|
||||
etypeParams,
|
||||
genericParams,
|
||||
params,
|
||||
returnType,
|
||||
body,
|
||||
@ -265,11 +265,11 @@ export class Parser {
|
||||
return { ok: true, value: { ident, pos, values } };
|
||||
}
|
||||
|
||||
private parseFnETypeParams(): ETypeParam[] {
|
||||
private parseFnETypeParams(): GenericParam[] {
|
||||
return this.parseDelimitedList(this.parseETypeParam, ">", ",");
|
||||
}
|
||||
|
||||
private parseETypeParam(): Res<ETypeParam> {
|
||||
private parseETypeParam(): Res<GenericParam> {
|
||||
const pos = this.pos();
|
||||
if (this.test("ident")) {
|
||||
const ident = this.current().identValue!;
|
||||
@ -627,71 +627,24 @@ export class Parser {
|
||||
private parsePostfix(): Expr {
|
||||
let subject = this.parseOperand();
|
||||
while (true) {
|
||||
const pos = this.pos();
|
||||
if (this.test(".")) {
|
||||
this.step();
|
||||
if (!this.test("ident")) {
|
||||
this.report("expected ident");
|
||||
return this.expr({ type: "error" }, pos);
|
||||
}
|
||||
const value = this.current().identValue!;
|
||||
this.step();
|
||||
subject = this.expr({ type: "field", subject, value }, pos);
|
||||
subject = this.parseFieldTail(subject);
|
||||
continue;
|
||||
}
|
||||
if (this.test("[")) {
|
||||
this.step();
|
||||
const value = this.parseExpr();
|
||||
if (!this.test("]")) {
|
||||
this.report("expected ']'");
|
||||
return this.expr({ type: "error" }, pos);
|
||||
}
|
||||
this.step();
|
||||
subject = this.expr({ type: "index", subject, value }, pos);
|
||||
subject = this.parseIndexTail(subject);
|
||||
continue;
|
||||
}
|
||||
if (this.test("(")) {
|
||||
const args = this.parseDelimitedList(
|
||||
this.parseExprArg,
|
||||
")",
|
||||
",",
|
||||
);
|
||||
subject = this.expr({ type: "call", subject, args }, pos);
|
||||
subject = this.parseCallTail(subject);
|
||||
continue;
|
||||
}
|
||||
if (this.test("::")) {
|
||||
this.step();
|
||||
if (!this.test("ident")) {
|
||||
this.report("expected ident");
|
||||
return this.expr({ type: "error" }, pos);
|
||||
}
|
||||
const value = this.current().identValue!;
|
||||
this.step();
|
||||
subject = this.expr({ type: "path", subject, value }, pos);
|
||||
subject = this.parsePathTail(subject);
|
||||
continue;
|
||||
}
|
||||
if (this.test("::<")) {
|
||||
const etypeArgs = this.parseDelimitedList(
|
||||
this.parseETypeArg,
|
||||
">",
|
||||
",",
|
||||
);
|
||||
if (this.test("(")) {
|
||||
subject = this.expr(
|
||||
{ type: "etype_args", subject, etypeArgs },
|
||||
pos,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const args = this.parseDelimitedList(
|
||||
this.parseExprArg,
|
||||
")",
|
||||
",",
|
||||
);
|
||||
subject = this.expr(
|
||||
{ type: "call", subject, etypeArgs, args },
|
||||
pos,
|
||||
);
|
||||
subject = this.parseETypeArgsTail(subject);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
@ -699,6 +652,65 @@ export class Parser {
|
||||
return subject;
|
||||
}
|
||||
|
||||
private parseETypeArgsTail(subject: Expr): Expr {
|
||||
const pos = this.pos();
|
||||
const etypeArgs = this.parseDelimitedList(
|
||||
this.parseETypeArg,
|
||||
">",
|
||||
",",
|
||||
);
|
||||
return this.expr(
|
||||
{ type: "etype_args", subject, etypeArgs },
|
||||
pos,
|
||||
);
|
||||
}
|
||||
|
||||
private parseFieldTail(subject: Expr): Expr {
|
||||
const pos = this.pos();
|
||||
this.step();
|
||||
if (!this.test("ident")) {
|
||||
this.report("expected ident");
|
||||
return this.expr({ type: "error" }, pos);
|
||||
}
|
||||
const ident = this.current().identValue!;
|
||||
this.step();
|
||||
return this.expr({ type: "field", subject, ident }, pos);
|
||||
}
|
||||
|
||||
private parseIndexTail(subject: Expr): Expr {
|
||||
const pos = this.pos();
|
||||
this.step();
|
||||
const value = this.parseExpr();
|
||||
if (!this.test("]")) {
|
||||
this.report("expected ']'");
|
||||
return this.expr({ type: "error" }, pos);
|
||||
}
|
||||
this.step();
|
||||
return this.expr({ type: "index", subject, value }, pos);
|
||||
}
|
||||
|
||||
private parseCallTail(subject: Expr): Expr {
|
||||
const pos = this.pos();
|
||||
const args = this.parseDelimitedList(
|
||||
this.parseExprArg,
|
||||
")",
|
||||
",",
|
||||
);
|
||||
return this.expr({ type: "call", subject, args }, pos);
|
||||
}
|
||||
|
||||
private parsePathTail(subject: Expr): Expr {
|
||||
const pos = this.pos();
|
||||
this.step();
|
||||
if (!this.test("ident")) {
|
||||
this.report("expected ident");
|
||||
return this.expr({ type: "error" }, pos);
|
||||
}
|
||||
const ident = this.current().identValue!;
|
||||
this.step();
|
||||
return this.expr({ type: "path", subject, ident }, pos);
|
||||
}
|
||||
|
||||
private parseExprArg(): Res<Expr> {
|
||||
return { ok: true, value: this.parseExpr() };
|
||||
}
|
||||
@ -710,9 +722,9 @@ export class Parser {
|
||||
private parseOperand(): Expr {
|
||||
const pos = this.pos();
|
||||
if (this.test("ident")) {
|
||||
const value = this.current().identValue!;
|
||||
const ident = this.current().identValue!;
|
||||
this.step();
|
||||
return this.expr({ type: "ident", value }, pos);
|
||||
return this.expr({ type: "ident", ident }, pos);
|
||||
}
|
||||
if (this.test("int")) {
|
||||
const value = this.current().intValue!;
|
||||
@ -763,10 +775,19 @@ export class Parser {
|
||||
|
||||
private parseEType(): EType {
|
||||
const pos = this.pos();
|
||||
if (["null", "int", "bool", "string"].includes(this.current().type)) {
|
||||
const type = this.current().type as
|
||||
| "null"
|
||||
| "int"
|
||||
| "bool"
|
||||
| "string";
|
||||
this.step();
|
||||
return this.etype({ type }, pos);
|
||||
}
|
||||
if (this.test("ident")) {
|
||||
const ident = this.current().identValue!;
|
||||
this.step();
|
||||
return this.etype({ type: "ident", value: ident }, pos);
|
||||
return this.etype({ type: "ident", ident: ident }, pos);
|
||||
}
|
||||
if (this.test("[")) {
|
||||
this.step();
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { Expr, Stmt } from "./ast.ts";
|
||||
import { EType, Expr, Stmt } from "./ast.ts";
|
||||
import {
|
||||
AstVisitor,
|
||||
visitEType,
|
||||
visitExpr,
|
||||
visitParam,
|
||||
VisitRes,
|
||||
visitStmt,
|
||||
visitStmts,
|
||||
@ -73,7 +75,7 @@ export class Resolver implements AstVisitor<[Syms]> {
|
||||
throw new Error("expected fn statement");
|
||||
}
|
||||
const fnScopeSyms = new FnSyms(syms);
|
||||
for (const param of stmt.kind.etypeParams ?? []) {
|
||||
for (const param of stmt.kind.genericParams ?? []) {
|
||||
if (fnScopeSyms.definedLocally(param.ident)) {
|
||||
this.reportAlreadyDefined(param.ident, param.pos, syms);
|
||||
continue;
|
||||
@ -82,7 +84,7 @@ export class Resolver implements AstVisitor<[Syms]> {
|
||||
ident: param.ident,
|
||||
type: "generic",
|
||||
pos: param.pos,
|
||||
etypeParam: param,
|
||||
genericParam: param,
|
||||
});
|
||||
}
|
||||
for (const param of stmt.kind.params) {
|
||||
@ -90,6 +92,7 @@ export class Resolver implements AstVisitor<[Syms]> {
|
||||
this.reportAlreadyDefined(param.ident, param.pos, syms);
|
||||
continue;
|
||||
}
|
||||
visitParam(param, this, fnScopeSyms);
|
||||
fnScopeSyms.define(param.ident, {
|
||||
ident: param.ident,
|
||||
type: "fn_param",
|
||||
@ -97,6 +100,9 @@ export class Resolver implements AstVisitor<[Syms]> {
|
||||
param,
|
||||
});
|
||||
}
|
||||
if (stmt.kind.returnType) {
|
||||
visitEType(stmt.kind.returnType, this, fnScopeSyms);
|
||||
}
|
||||
visitExpr(stmt.kind.body, this, fnScopeSyms);
|
||||
return "stop";
|
||||
}
|
||||
@ -105,18 +111,14 @@ export class Resolver implements AstVisitor<[Syms]> {
|
||||
if (expr.kind.type !== "ident") {
|
||||
throw new Error("expected ident");
|
||||
}
|
||||
const ident = expr.kind;
|
||||
const symResult = syms.get(ident.value);
|
||||
const ident = expr.kind.ident;
|
||||
const symResult = syms.get(ident);
|
||||
if (!symResult.ok) {
|
||||
this.reportUseOfUndefined(ident.value, expr.pos, syms);
|
||||
this.reportUseOfUndefined(ident, expr.pos, syms);
|
||||
return;
|
||||
}
|
||||
const sym = symResult.sym;
|
||||
expr.kind = {
|
||||
type: "sym",
|
||||
ident: ident.value,
|
||||
sym,
|
||||
};
|
||||
expr.kind = { type: "sym", ident, sym };
|
||||
return "stop";
|
||||
}
|
||||
|
||||
@ -146,6 +148,21 @@ export class Resolver implements AstVisitor<[Syms]> {
|
||||
return "stop";
|
||||
}
|
||||
|
||||
visitIdentEType(etype: EType, syms: Syms): VisitRes {
|
||||
if (etype.kind.type !== "ident") {
|
||||
throw new Error();
|
||||
}
|
||||
const ident = etype.kind.ident;
|
||||
const symResult = syms.get(ident);
|
||||
if (!symResult.ok) {
|
||||
this.reportUseOfUndefined(ident, etype.pos, syms);
|
||||
return;
|
||||
}
|
||||
const sym = symResult.sym;
|
||||
etype.kind = { type: "sym", ident, sym };
|
||||
return "stop";
|
||||
}
|
||||
|
||||
private reportUseOfUndefined(ident: string, pos: Pos, _syms: Syms) {
|
||||
this.reporter.reportError({
|
||||
reporter: "Resolver",
|
||||
|
@ -7,13 +7,28 @@ export type VType =
|
||||
| { type: "bool" }
|
||||
| { type: "array"; inner: VType }
|
||||
| { type: "struct"; fields: VTypeParam[] }
|
||||
| { type: "fn"; params: VTypeParam[]; returnType: VType };
|
||||
| {
|
||||
type: "fn";
|
||||
genericParams?: VTypeGenericParam[];
|
||||
params: VTypeParam[];
|
||||
returnType: VType;
|
||||
}
|
||||
| { type: "generic" }
|
||||
| {
|
||||
type: "generic_spec";
|
||||
subject: VType;
|
||||
genericParams: VType[];
|
||||
};
|
||||
|
||||
export type VTypeParam = {
|
||||
ident: string;
|
||||
vtype: VType;
|
||||
};
|
||||
|
||||
export type VTypeGenericParam = {
|
||||
ident: string;
|
||||
};
|
||||
|
||||
export function vtypesEqual(a: VType, b: VType): boolean {
|
||||
if (a.type !== b.type) {
|
||||
return false;
|
||||
@ -58,5 +73,8 @@ export function vtypeToString(vtype: VType): string {
|
||||
.join(", ");
|
||||
return `fn (${paramString}) -> ${vtypeToString(vtype.returnType)}`;
|
||||
}
|
||||
if (vtype.type === "generic") {
|
||||
return `generic`;
|
||||
}
|
||||
throw new Error(`unhandled vtype '${vtype.type}'`);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user