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