fix adapted parser
This commit is contained in:
		
							parent
							
								
									c92fce8ccb
								
							
						
					
					
						commit
						15c70e4840
					
				@ -23,7 +23,7 @@ export type ItemStmt = { item: Item };
 | 
			
		||||
 | 
			
		||||
export type LetStmt = {
 | 
			
		||||
    pat: Pat;
 | 
			
		||||
    ty: Ty;
 | 
			
		||||
    ty?: Ty;
 | 
			
		||||
    expr?: Expr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -57,7 +57,7 @@ export type ItemKind =
 | 
			
		||||
    | { tag: "use" } & UseItem
 | 
			
		||||
    | { tag: "type_alias" } & TypeAliasItem;
 | 
			
		||||
 | 
			
		||||
export type ModBlockItem = { stmts: Stmt[] };
 | 
			
		||||
export type ModBlockItem = { block: Block };
 | 
			
		||||
export type ModFileItem = { filePath: string };
 | 
			
		||||
export type EnumItem = { variants: Variant[] };
 | 
			
		||||
export type StructItem = { data: VariantData };
 | 
			
		||||
@ -122,7 +122,7 @@ export type Expr = {
 | 
			
		||||
 | 
			
		||||
export type ExprKind =
 | 
			
		||||
    | { tag: "error" }
 | 
			
		||||
    | { tag: "path" } & Path
 | 
			
		||||
    | { tag: "path" } & PathExpr
 | 
			
		||||
    | { tag: "null" }
 | 
			
		||||
    | { tag: "int" } & IntExpr
 | 
			
		||||
    | { tag: "bool" } & BoolExpr
 | 
			
		||||
@ -139,13 +139,14 @@ export type ExprKind =
 | 
			
		||||
    | { tag: "call" } & CallExpr
 | 
			
		||||
    | { tag: "unary" } & UnaryExpr
 | 
			
		||||
    | { tag: "binary" } & BinaryExpr
 | 
			
		||||
    | { tag: "block" } & Block
 | 
			
		||||
    | { tag: "block" } & BlockExpr
 | 
			
		||||
    | { tag: "if" } & IfExpr
 | 
			
		||||
    | { tag: "loop" } & LoopExpr
 | 
			
		||||
    | { tag: "while" } & WhileExpr
 | 
			
		||||
    | { tag: "for" } & ForExpr
 | 
			
		||||
    | { tag: "c_for" } & CForExpr;
 | 
			
		||||
 | 
			
		||||
export type PathExpr = { path: Path };
 | 
			
		||||
export type IntExpr = { value: number };
 | 
			
		||||
export type BoolExpr = { value: boolean };
 | 
			
		||||
export type StringExpr = { value: string };
 | 
			
		||||
@ -161,6 +162,7 @@ export type IndexExpr = { expr: Expr; index: Expr };
 | 
			
		||||
export type CallExpr = { expr: Expr; args: Expr[] };
 | 
			
		||||
export type UnaryExpr = { unaryType: UnaryType; expr: Expr };
 | 
			
		||||
export type BinaryExpr = { binaryType: BinaryType; left: Expr; right: Expr };
 | 
			
		||||
export type BlockExpr = { block: Block };
 | 
			
		||||
export type IfExpr = { cond: Expr; truthy: Block; falsy?: Expr };
 | 
			
		||||
export type LoopExpr = { body: Block };
 | 
			
		||||
export type WhileExpr = { cond: Expr; body: Block };
 | 
			
		||||
@ -216,13 +218,18 @@ export type Ty = {
 | 
			
		||||
 | 
			
		||||
export type TyKind =
 | 
			
		||||
    | { tag: "error" }
 | 
			
		||||
    | { tag: "path" } & Path
 | 
			
		||||
    | { tag: "null" }
 | 
			
		||||
    | { tag: "int" }
 | 
			
		||||
    | { tag: "bool" }
 | 
			
		||||
    | { tag: "string" }
 | 
			
		||||
    | { tag: "path" } & PathTy
 | 
			
		||||
    | { tag: "ref" } & RefTy
 | 
			
		||||
    | { tag: "ptr" } & PtrTy
 | 
			
		||||
    | { tag: "slice" } & SliceTy
 | 
			
		||||
    | { tag: "array" } & ArrayTy
 | 
			
		||||
    | { tag: "anon_struct" } & AnonStructTy;
 | 
			
		||||
 | 
			
		||||
export type PathTy = { path: Path };
 | 
			
		||||
export type RefTy = { ty: Ty; mut: boolean };
 | 
			
		||||
export type PtrTy = { ty: Ty; mut: boolean };
 | 
			
		||||
export type SliceTy = { ty: Ty };
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { exhausted } from "../util.ts";
 | 
			
		||||
import { Block } from "./ast.ts";
 | 
			
		||||
import { Block, BlockExpr, PathExpr, PathTy } from "./ast.ts";
 | 
			
		||||
import {
 | 
			
		||||
    AnonStructTy,
 | 
			
		||||
    ArrayExpr,
 | 
			
		||||
@ -83,7 +83,7 @@ export interface Visitor<
 | 
			
		||||
 | 
			
		||||
    visitExpr?(expr: Expr, ...p: P): R;
 | 
			
		||||
    visitErrorExpr?(expr: Expr, ...p: P): R;
 | 
			
		||||
    visitPathExpr?(expr: Expr, kind: Path, ...p: P): R;
 | 
			
		||||
    visitPathExpr?(expr: Expr, kind: PathExpr, ...p: P): R;
 | 
			
		||||
    visitNullExpr?(expr: Expr, ...p: P): R;
 | 
			
		||||
    visitIntExpr?(expr: Expr, kind: IntExpr, ...p: P): R;
 | 
			
		||||
    visitBoolExpr?(expr: Expr, kind: BoolExpr, ...p: P): R;
 | 
			
		||||
@ -100,7 +100,7 @@ export interface Visitor<
 | 
			
		||||
    visitCallExpr?(expr: Expr, kind: CallExpr, ...p: P): R;
 | 
			
		||||
    visitUnaryExpr?(expr: Expr, kind: UnaryExpr, ...p: P): R;
 | 
			
		||||
    visitBinaryExpr?(expr: Expr, kind: BinaryExpr, ...p: P): R;
 | 
			
		||||
    visitBlockExpr?(expr: Expr, kind: Block, ...p: P): R;
 | 
			
		||||
    visitBlockExpr?(expr: Expr, kind: BlockExpr, ...p: P): R;
 | 
			
		||||
    visitIfExpr?(expr: Expr, kind: IfExpr, ...p: P): R;
 | 
			
		||||
    visitLoopExpr?(expr: Expr, kind: LoopExpr, ...p: P): R;
 | 
			
		||||
    visitWhileExpr?(expr: Expr, kind: WhileExpr, ...p: P): R;
 | 
			
		||||
@ -113,7 +113,11 @@ export interface Visitor<
 | 
			
		||||
 | 
			
		||||
    visitTy?(ty: Ty, ...p: P): R;
 | 
			
		||||
    visitErrorTy?(ty: Ty, ...p: P): R;
 | 
			
		||||
    visitPathTy?(ty: Ty, kind: Path, ...p: P): R;
 | 
			
		||||
    visitNullTy?(ty: Ty, ...p: P): R;
 | 
			
		||||
    visitIntTy?(ty: Ty, ...p: P): R;
 | 
			
		||||
    visitBoolTy?(ty: Ty, ...p: P): R;
 | 
			
		||||
    visitStringTy?(ty: Ty, ...p: P): R;
 | 
			
		||||
    visitPathTy?(ty: Ty, kind: PathTy, ...p: P): R;
 | 
			
		||||
    visitRefTy?(ty: Ty, kind: RefTy, ...p: P): R;
 | 
			
		||||
    visitPtrTy?(ty: Ty, kind: PtrTy, ...p: P): R;
 | 
			
		||||
    visitSliceTy?(ty: Ty, kind: SliceTy, ...p: P): R;
 | 
			
		||||
@ -163,24 +167,41 @@ export function visitStmt<
 | 
			
		||||
            return;
 | 
			
		||||
        case "item":
 | 
			
		||||
            if (v.visitItemStmt?.(stmt, kind, ...p) === "stop") return;
 | 
			
		||||
            visitItem(v, kind.item, ...p);
 | 
			
		||||
            return;
 | 
			
		||||
        case "let":
 | 
			
		||||
            if (v.visitLetStmt?.(stmt, kind, ...p) === "stop") return;
 | 
			
		||||
            visitPat(v, kind.pat, ...p);
 | 
			
		||||
            if (kind.ty) {
 | 
			
		||||
                visitTy(v, kind.ty, ...p);
 | 
			
		||||
            }
 | 
			
		||||
            if (kind.expr) {
 | 
			
		||||
                visitExpr(v, kind.expr, ...p);
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        case "return":
 | 
			
		||||
            if (v.visitReturnStmt?.(stmt, kind, ...p) === "stop") return;
 | 
			
		||||
            if (kind.expr) {
 | 
			
		||||
                visitExpr(v, kind.expr, ...p);
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        case "break":
 | 
			
		||||
            if (v.visitBreakStmt?.(stmt, kind, ...p) === "stop") return;
 | 
			
		||||
            if (kind.expr) {
 | 
			
		||||
                visitExpr(v, kind.expr, ...p);
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        case "continue":
 | 
			
		||||
            if (v.visitContinueStmt?.(stmt, ...p) === "stop") return;
 | 
			
		||||
            return;
 | 
			
		||||
        case "assign":
 | 
			
		||||
            if (v.visitAssignStmt?.(stmt, kind, ...p) === "stop") return;
 | 
			
		||||
            visitExpr(v, kind.subject, ...p);
 | 
			
		||||
            visitExpr(v, kind.value, ...p);
 | 
			
		||||
            return;
 | 
			
		||||
        case "expr":
 | 
			
		||||
            if (v.visitExprStmt?.(stmt, kind, ...p) === "stop") return;
 | 
			
		||||
            visitExpr(v, kind.expr, ...p);
 | 
			
		||||
            return;
 | 
			
		||||
    }
 | 
			
		||||
    exhausted(kind);
 | 
			
		||||
@ -193,6 +214,7 @@ export function visitItem<
 | 
			
		||||
    item: Item,
 | 
			
		||||
    ...p: P
 | 
			
		||||
) {
 | 
			
		||||
    visitIdent(v, item.ident, ...p);
 | 
			
		||||
    const kind = item.kind;
 | 
			
		||||
    switch (kind.tag) {
 | 
			
		||||
        case "error":
 | 
			
		||||
@ -237,7 +259,7 @@ export function visitExpr<
 | 
			
		||||
            return;
 | 
			
		||||
        case "path":
 | 
			
		||||
            if (v.visitPathExpr?.(expr, kind, ...p) === "stop") return;
 | 
			
		||||
            visitPath(v, kind, ...p);
 | 
			
		||||
            visitPath(v, kind.path, ...p);
 | 
			
		||||
            return;
 | 
			
		||||
        case "null":
 | 
			
		||||
            if (v.visitNullExpr?.(expr, ...p) === "stop") return;
 | 
			
		||||
@ -271,7 +293,7 @@ export function visitExpr<
 | 
			
		||||
            if (kind.path) {
 | 
			
		||||
                visitPath(v, kind.path, ...p);
 | 
			
		||||
            }
 | 
			
		||||
            for (const field of kind.field) {
 | 
			
		||||
            for (const field of kind.fields) {
 | 
			
		||||
                visitIdent(v, field.ident, ...p);
 | 
			
		||||
                visitExpr(v, field.expr, ...p);
 | 
			
		||||
            }
 | 
			
		||||
@ -316,14 +338,14 @@ export function visitExpr<
 | 
			
		||||
            return;
 | 
			
		||||
        case "block":
 | 
			
		||||
            if (v.visitBlockExpr?.(expr, kind, ...p) === "stop") return;
 | 
			
		||||
            visitBlock(v, kind, ...p);
 | 
			
		||||
            visitBlock(v, kind.block, ...p);
 | 
			
		||||
            return;
 | 
			
		||||
        case "if":
 | 
			
		||||
            if (v.visitIfExpr?.(expr, kind, ...p) === "stop") return;
 | 
			
		||||
            visitExpr(v, kind.cond, ...p);
 | 
			
		||||
            visitBlock(v, kind.truthy, ...p);
 | 
			
		||||
            if (kind.falsy) {
 | 
			
		||||
                visitBlock(v, kind.falsy, ...p);
 | 
			
		||||
                visitExpr(v, kind.falsy, ...p);
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
        case "loop":
 | 
			
		||||
@ -388,9 +410,21 @@ export function visitTy<
 | 
			
		||||
        case "error":
 | 
			
		||||
            if (v.visitErrorTy?.(ty, ...p) === "stop") return;
 | 
			
		||||
            return;
 | 
			
		||||
        case "null":
 | 
			
		||||
            if (v.visitNullTy?.(ty, ...p) === "stop") return;
 | 
			
		||||
            return;
 | 
			
		||||
        case "int":
 | 
			
		||||
            if (v.visitIntTy?.(ty, ...p) === "stop") return;
 | 
			
		||||
            return;
 | 
			
		||||
        case "bool":
 | 
			
		||||
            if (v.visitBoolTy?.(ty, ...p) === "stop") return;
 | 
			
		||||
            return;
 | 
			
		||||
        case "string":
 | 
			
		||||
            if (v.visitStringTy?.(ty, ...p) === "stop") return;
 | 
			
		||||
            return;
 | 
			
		||||
        case "path":
 | 
			
		||||
            if (v.visitPathTy?.(ty, kind, ...p) === "stop") return;
 | 
			
		||||
            v.visitPath?.(kind, ...p);
 | 
			
		||||
            v.visitPath?.(kind.path, ...p);
 | 
			
		||||
            return;
 | 
			
		||||
        case "ref":
 | 
			
		||||
            if (v.visitRefTy?.(ty, kind, ...p) === "stop") return;
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,11 @@
 | 
			
		||||
import * as ast from "./ast/mod.ts";
 | 
			
		||||
import { Pos, prettyPrintReport, printStackTrace, Report, Span } from "./diagnostics.ts";
 | 
			
		||||
import {
 | 
			
		||||
    Pos,
 | 
			
		||||
    prettyPrintReport,
 | 
			
		||||
    printStackTrace,
 | 
			
		||||
    Report,
 | 
			
		||||
    Span,
 | 
			
		||||
} from "./diagnostics.ts";
 | 
			
		||||
 | 
			
		||||
export class Ctx {
 | 
			
		||||
    private fileIds = new Ids();
 | 
			
		||||
@ -44,23 +50,23 @@ export class Ctx {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public filePosLineText(file: File, pos: Pos): string {
 | 
			
		||||
        const fileTextLines = this.fileInfo(file).text.split("\n")
 | 
			
		||||
        return fileTextLines[pos.line-1]
 | 
			
		||||
        const fileTextLines = this.fileInfo(file).text.split("\n");
 | 
			
		||||
        return fileTextLines[pos.line - 1];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public fileSpanText(file: File, span: Span): string {
 | 
			
		||||
        let result = ""
 | 
			
		||||
        const fileTextLines = this.fileInfo(file).text.split("\n")
 | 
			
		||||
        let result = "";
 | 
			
		||||
        const fileTextLines = this.fileInfo(file).text.split("\n");
 | 
			
		||||
 | 
			
		||||
        for(let i = 0; i < fileTextLines.length; i++) {
 | 
			
		||||
            if (i > span.end.line-1) {
 | 
			
		||||
        for (let i = 0; i < fileTextLines.length; i++) {
 | 
			
		||||
            if (i > span.end.line - 1) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            if (i >= span.begin.line-1) {
 | 
			
		||||
            if (i >= span.begin.line - 1) {
 | 
			
		||||
                result += fileTextLines[i] + "\n";
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return result
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public report(rep: Report) {
 | 
			
		||||
@ -78,6 +84,13 @@ export class Ctx {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public printAsts() {
 | 
			
		||||
        for (const [_, info] of this.files) {
 | 
			
		||||
            console.log(`${info.absPath}:`);
 | 
			
		||||
            console.log(JSON.stringify(info.ast!, null, 2));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type File = IdBase;
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,13 @@ import * as ast from "./ast/mod.ts";
 | 
			
		||||
import { Ctx } from "./ctx.ts";
 | 
			
		||||
import { File } from "./ctx.ts";
 | 
			
		||||
 | 
			
		||||
async function main() {
 | 
			
		||||
    const filePath = Deno.args[0];
 | 
			
		||||
    const compiler = new PackCompiler(filePath, new NullEmitter());
 | 
			
		||||
    compiler.enableDebug();
 | 
			
		||||
    await compiler.compile();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type Pack = {
 | 
			
		||||
    rootMod: Mod;
 | 
			
		||||
};
 | 
			
		||||
@ -14,6 +21,11 @@ export interface PackEmitter {
 | 
			
		||||
    emit(pack: Pack): void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class NullEmitter implements PackEmitter {
 | 
			
		||||
    emit(pack: Pack): void {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class PackCompiler {
 | 
			
		||||
    private ctx = new Ctx();
 | 
			
		||||
 | 
			
		||||
@ -22,10 +34,16 @@ export class PackCompiler {
 | 
			
		||||
        private emitter: PackEmitter,
 | 
			
		||||
    ) {}
 | 
			
		||||
 | 
			
		||||
    public compile() {
 | 
			
		||||
        FileTreeAstCollector
 | 
			
		||||
    public async compile() {
 | 
			
		||||
        await FileTreeAstCollector
 | 
			
		||||
            .fromEntryFile(this.ctx, this.entryFilePath)
 | 
			
		||||
            .collect();
 | 
			
		||||
        this.ctx.printAsts();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public enableDebug() {
 | 
			
		||||
        this.ctx.enableReportImmediately = true;
 | 
			
		||||
        this.ctx.enableStacktrace = true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -74,7 +92,8 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> {
 | 
			
		||||
        kind: ast.ModFileItem,
 | 
			
		||||
        { file }: _P,
 | 
			
		||||
    ): ast.VisitRes {
 | 
			
		||||
        const { ident: { text: ident }, filePath: relPath } = kind;
 | 
			
		||||
        const { ident: { text: ident } } = item;
 | 
			
		||||
        const { filePath: relPath } = kind;
 | 
			
		||||
        const absPath = path.join(path.dirname(this.absPath), relPath);
 | 
			
		||||
        this.subFilePromise = this.subFilePromise
 | 
			
		||||
            .then(() => {
 | 
			
		||||
@ -99,3 +118,5 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> {
 | 
			
		||||
        return "stop";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
main();
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
import {
 | 
			
		||||
    AnonFieldDef,
 | 
			
		||||
    BinaryType,
 | 
			
		||||
    ExprField,
 | 
			
		||||
    PathSegment,
 | 
			
		||||
@ -28,6 +29,8 @@ import { Ctx, File as CtxFile } from "../ctx.ts";
 | 
			
		||||
import { Pos, Span } from "../diagnostics.ts";
 | 
			
		||||
import { Res, todo } from "../util.ts";
 | 
			
		||||
import { Lexer } from "./lexer.ts";
 | 
			
		||||
import { TokenIter } from "./token.ts";
 | 
			
		||||
import { SigFilter } from "./token.ts";
 | 
			
		||||
import { Token } from "./token.ts";
 | 
			
		||||
 | 
			
		||||
type ParseRes<V, E = undefined> = Res<V, E>;
 | 
			
		||||
@ -38,14 +41,14 @@ type StmtDetails = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export class Parser {
 | 
			
		||||
    private lexer: Lexer;
 | 
			
		||||
    private lexer: TokenIter;
 | 
			
		||||
    private currentToken: Token | null;
 | 
			
		||||
 | 
			
		||||
    public constructor(
 | 
			
		||||
        private ctx: Ctx,
 | 
			
		||||
        private file: CtxFile,
 | 
			
		||||
    ) {
 | 
			
		||||
        this.lexer = new Lexer(this.ctx, this.file);
 | 
			
		||||
        this.lexer = new SigFilter(new Lexer(this.ctx, this.file));
 | 
			
		||||
        this.currentToken = this.lexer.next();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -203,7 +206,7 @@ export class Parser {
 | 
			
		||||
    private parseBlockExpr(): Expr {
 | 
			
		||||
        const block = this.parseBlock();
 | 
			
		||||
        return block.ok
 | 
			
		||||
            ? this.expr({ tag: "block", ...block.val }, this.span())
 | 
			
		||||
            ? this.expr({ tag: "block", block: block.val }, this.span())
 | 
			
		||||
            : this.expr({ tag: "error" }, this.span());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -312,7 +315,13 @@ export class Parser {
 | 
			
		||||
        return this.stmt({
 | 
			
		||||
            tag: "item",
 | 
			
		||||
            item: this.item(
 | 
			
		||||
                { tag: "mod_block", stmts },
 | 
			
		||||
                {
 | 
			
		||||
                    tag: "mod_block",
 | 
			
		||||
                    block: {
 | 
			
		||||
                        stmts,
 | 
			
		||||
                        span: Span.fromto(pos, stmts.at(-1)?.span ?? pos),
 | 
			
		||||
                    },
 | 
			
		||||
                },
 | 
			
		||||
                pos,
 | 
			
		||||
                ident,
 | 
			
		||||
                details.pub,
 | 
			
		||||
@ -443,18 +452,15 @@ export class Parser {
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parsePat(): Pat {
 | 
			
		||||
        return todo();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parseLet(): Stmt {
 | 
			
		||||
        const pos = this.span();
 | 
			
		||||
        this.step();
 | 
			
		||||
        const paramResult = this.parseParam();
 | 
			
		||||
        if (!paramResult.ok) {
 | 
			
		||||
            return this.stmt({ tag: "error" }, pos);
 | 
			
		||||
        const pat = this.parsePat();
 | 
			
		||||
        let ty: Ty | undefined = undefined;
 | 
			
		||||
        if (this.test(":")) {
 | 
			
		||||
            this.step();
 | 
			
		||||
            ty = this.parseTy();
 | 
			
		||||
        }
 | 
			
		||||
        const { pat, ty } = paramResult.val;
 | 
			
		||||
        if (!this.test("=")) {
 | 
			
		||||
            this.report("expected '='");
 | 
			
		||||
            return this.stmt({ tag: "error" }, pos);
 | 
			
		||||
@ -936,9 +942,23 @@ export class Parser {
 | 
			
		||||
    private parseOperand(): Expr {
 | 
			
		||||
        const pos = this.span();
 | 
			
		||||
        if (this.test("ident")) {
 | 
			
		||||
            const ident = this.current().identValue!;
 | 
			
		||||
            const pathRes = this.parsePath();
 | 
			
		||||
            if (!pathRes.ok) {
 | 
			
		||||
                return this.expr({ tag: "error" }, pos);
 | 
			
		||||
            }
 | 
			
		||||
            if (this.test("{")) {
 | 
			
		||||
                this.step();
 | 
			
		||||
            return this.expr({ tag: "ident", ident }, pos);
 | 
			
		||||
                const fields = this.parseDelimitedList(
 | 
			
		||||
                    this.parseExprField,
 | 
			
		||||
                    "}",
 | 
			
		||||
                    ",",
 | 
			
		||||
                );
 | 
			
		||||
                return this.expr(
 | 
			
		||||
                    { tag: "struct", path: pathRes.val, fields },
 | 
			
		||||
                    pathRes.val.span,
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            return this.expr({ tag: "path", path: pathRes.val }, pos);
 | 
			
		||||
        }
 | 
			
		||||
        if (this.test("int")) {
 | 
			
		||||
            const value = this.current().intValue!;
 | 
			
		||||
@ -993,6 +1013,45 @@ export class Parser {
 | 
			
		||||
        return this.expr({ tag: "error" }, pos);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parseExprField(): ParseRes<ExprField> {
 | 
			
		||||
        if (!this.test("ident")) {
 | 
			
		||||
            this.report("expected 'ident'");
 | 
			
		||||
            return Res.Err(undefined);
 | 
			
		||||
        }
 | 
			
		||||
        const ident = this.parseIdent();
 | 
			
		||||
        if (!this.test(":")) {
 | 
			
		||||
            this.report("expected ':'");
 | 
			
		||||
            return Res.Err(undefined);
 | 
			
		||||
        }
 | 
			
		||||
        this.step();
 | 
			
		||||
        const expr = this.parseExpr();
 | 
			
		||||
        return Res.Ok({
 | 
			
		||||
            ident,
 | 
			
		||||
            expr,
 | 
			
		||||
            span: Span.fromto(ident.span, expr.span),
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parsePat(): Pat {
 | 
			
		||||
        const pos = this.span();
 | 
			
		||||
        if (this.test("ident")) {
 | 
			
		||||
            const ident = this.parseIdent();
 | 
			
		||||
            return this.pat({ tag: "bind", ident, mut: false }, ident.span);
 | 
			
		||||
        }
 | 
			
		||||
        if (this.test("mut")) {
 | 
			
		||||
            this.step();
 | 
			
		||||
            if (!this.test("ident")) {
 | 
			
		||||
                this.report("expected 'ident'");
 | 
			
		||||
                return this.pat({ tag: "error" }, pos);
 | 
			
		||||
            }
 | 
			
		||||
            const ident = this.parseIdent();
 | 
			
		||||
            return this.pat({ tag: "bind", ident, mut: false }, pos);
 | 
			
		||||
        }
 | 
			
		||||
        this.report(`expected pattern, got '${this.current().type}'`, pos);
 | 
			
		||||
        this.step();
 | 
			
		||||
        return this.pat({ tag: "error" }, pos);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parseTy(): Ty {
 | 
			
		||||
        const pos = this.span();
 | 
			
		||||
        if (["null", "int", "bool", "string"].includes(this.current().type)) {
 | 
			
		||||
@ -1005,19 +1064,31 @@ export class Parser {
 | 
			
		||||
            return this.ty({ tag }, pos);
 | 
			
		||||
        }
 | 
			
		||||
        if (this.test("ident")) {
 | 
			
		||||
            const ident = this.current().identValue!;
 | 
			
		||||
            this.step();
 | 
			
		||||
            return this.ty({ tag: "ident", ident: ident }, pos);
 | 
			
		||||
            const pathRes = this.parsePath();
 | 
			
		||||
            if (!pathRes.ok) {
 | 
			
		||||
                return this.ty({ tag: "error" }, pos);
 | 
			
		||||
            }
 | 
			
		||||
            return this.ty({ tag: "path", path: pathRes.val }, pos);
 | 
			
		||||
        }
 | 
			
		||||
        if (this.test("[")) {
 | 
			
		||||
            this.step();
 | 
			
		||||
            const subject = this.parseTy();
 | 
			
		||||
            const ty = this.parseTy();
 | 
			
		||||
            if (this.test(";")) {
 | 
			
		||||
                this.step();
 | 
			
		||||
                const length = this.parseExpr();
 | 
			
		||||
                if (!this.test("]")) {
 | 
			
		||||
                    this.report("expected ']'", pos);
 | 
			
		||||
                    return this.ty({ tag: "error" }, pos);
 | 
			
		||||
                }
 | 
			
		||||
                this.step();
 | 
			
		||||
            return this.ty({ tag: "array", subject }, pos);
 | 
			
		||||
                return this.ty({ tag: "array", ty, length }, pos);
 | 
			
		||||
            }
 | 
			
		||||
            if (!this.test("]")) {
 | 
			
		||||
                this.report("expected ']' or ';'", pos);
 | 
			
		||||
                return this.ty({ tag: "error" }, pos);
 | 
			
		||||
            }
 | 
			
		||||
            this.step();
 | 
			
		||||
            return this.ty({ tag: "slice", ty }, pos);
 | 
			
		||||
        }
 | 
			
		||||
        if (this.test("struct")) {
 | 
			
		||||
            this.step();
 | 
			
		||||
@ -1025,55 +1096,55 @@ export class Parser {
 | 
			
		||||
                this.report("expected '{'");
 | 
			
		||||
                return this.ty({ tag: "error" }, pos);
 | 
			
		||||
            }
 | 
			
		||||
            const fields = this.parseTyStructFields();
 | 
			
		||||
            return this.ty({ tag: "struct", fields }, pos);
 | 
			
		||||
            const fields = this.parseAnonFieldDefs();
 | 
			
		||||
            return this.ty({ tag: "anon_struct", fields }, pos);
 | 
			
		||||
        }
 | 
			
		||||
        if (this.test("&")) {
 | 
			
		||||
            this.step();
 | 
			
		||||
            let tag: "ref" | "ref_mut" = "ref";
 | 
			
		||||
            let mut = false;
 | 
			
		||||
            if (this.test("mut")) {
 | 
			
		||||
                this.step();
 | 
			
		||||
                tag = "ref_mut";
 | 
			
		||||
                mut = true;
 | 
			
		||||
            }
 | 
			
		||||
            const subject = this.parseTy();
 | 
			
		||||
            return this.ty({ type, subject }, pos);
 | 
			
		||||
            const ty = this.parseTy();
 | 
			
		||||
            return this.ty({ tag: "ref", ty, mut }, pos);
 | 
			
		||||
        }
 | 
			
		||||
        if (this.test("*")) {
 | 
			
		||||
            this.step();
 | 
			
		||||
            let tag: "ptr" | "ptr_mut" = "ptr";
 | 
			
		||||
            let mut = false;
 | 
			
		||||
            if (this.test("mut")) {
 | 
			
		||||
                this.step();
 | 
			
		||||
                tag = "ptr_mut";
 | 
			
		||||
                mut = true;
 | 
			
		||||
            }
 | 
			
		||||
            const subject = this.parseTy();
 | 
			
		||||
            return this.ty({ type, subject }, pos);
 | 
			
		||||
            const ty = this.parseTy();
 | 
			
		||||
            return this.ty({ tag: "ptr", ty, mut }, pos);
 | 
			
		||||
        }
 | 
			
		||||
        this.report("expected type");
 | 
			
		||||
        return this.ty({ tag: "error" }, pos);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parseTyStructFields(): Param[] {
 | 
			
		||||
    private parseAnonFieldDefs(): AnonFieldDef[] {
 | 
			
		||||
        this.step();
 | 
			
		||||
        if (this.test("}")) {
 | 
			
		||||
            this.step();
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
        const params: Param[] = [];
 | 
			
		||||
        const paramResult = this.parseParam();
 | 
			
		||||
        const params: AnonFieldDef[] = [];
 | 
			
		||||
        const paramResult = this.parseAnonFieldDef();
 | 
			
		||||
        if (!paramResult.ok) {
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
        params.push(paramResult.value);
 | 
			
		||||
        params.push(paramResult.val);
 | 
			
		||||
        while (this.test(",")) {
 | 
			
		||||
            this.step();
 | 
			
		||||
            if (this.test("}")) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            const paramResult = this.parseParam();
 | 
			
		||||
            const paramResult = this.parseAnonFieldDef();
 | 
			
		||||
            if (!paramResult.ok) {
 | 
			
		||||
                return [];
 | 
			
		||||
            }
 | 
			
		||||
            params.push(paramResult.value);
 | 
			
		||||
            params.push(paramResult.val);
 | 
			
		||||
        }
 | 
			
		||||
        if (!this.test("}")) {
 | 
			
		||||
            this.report("expected '}'");
 | 
			
		||||
@ -1083,10 +1154,72 @@ export class Parser {
 | 
			
		||||
        return params;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parsePath(): Path {
 | 
			
		||||
    private parseAnonFieldDef(): ParseRes<AnonFieldDef> {
 | 
			
		||||
        const begin = this.span();
 | 
			
		||||
        const identRes = this.eatIdent();
 | 
			
		||||
        if (!identRes.ok) return Res.Err(undefined);
 | 
			
		||||
        const ident = identRes.val;
 | 
			
		||||
        if (!this.test(":")) {
 | 
			
		||||
            this.report("expected ':'");
 | 
			
		||||
            return Res.Err(undefined);
 | 
			
		||||
        }
 | 
			
		||||
        this.step();
 | 
			
		||||
        const ty = this.parseTy();
 | 
			
		||||
        return Res.Ok({
 | 
			
		||||
            ident,
 | 
			
		||||
            ty,
 | 
			
		||||
            span: Span.fromto(begin, ty.span),
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parsePath(): ParseRes<Path> {
 | 
			
		||||
        const begin = this.span();
 | 
			
		||||
        let end = begin;
 | 
			
		||||
        const segments: PathSegment[] = [];
 | 
			
		||||
        const identRes = this.eatIdent();
 | 
			
		||||
        if (!identRes.ok) return Res.Err(undefined);
 | 
			
		||||
        const ident = identRes.val;
 | 
			
		||||
        segments.push({ ident, span: Span.fromto(begin, end) });
 | 
			
		||||
        while (this.test("::")) {
 | 
			
		||||
            this.step();
 | 
			
		||||
            if (!this.test("ident")) {
 | 
			
		||||
                this.report("expected 'ident'");
 | 
			
		||||
                return Res.Err(undefined);
 | 
			
		||||
            }
 | 
			
		||||
            end = this.span();
 | 
			
		||||
            const ident = this.parseIdent();
 | 
			
		||||
            let genericArgs: Ty[] | undefined = undefined;
 | 
			
		||||
            if (this.test("::")) {
 | 
			
		||||
                this.step();
 | 
			
		||||
                if (!this.test("<")) {
 | 
			
		||||
                    this.report("expected '<'");
 | 
			
		||||
                    return Res.Err(undefined);
 | 
			
		||||
                }
 | 
			
		||||
                genericArgs = this.parseDelimitedList(
 | 
			
		||||
                    this.parseTyRes,
 | 
			
		||||
                    ">",
 | 
			
		||||
                    ",",
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            segments.push({
 | 
			
		||||
                ident,
 | 
			
		||||
                genericArgs,
 | 
			
		||||
                span: Span.fromto(begin, end),
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return Res.Ok({ segments, span: Span.fromto(begin, end) });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parseTyRes(): ParseRes<Ty> {
 | 
			
		||||
        return Res.Ok(this.parseTy());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private eatIdent(): ParseRes<Ident> {
 | 
			
		||||
        if (!this.test("ident")) {
 | 
			
		||||
            this.report("expected 'ident'");
 | 
			
		||||
            return Res.Err(undefined);
 | 
			
		||||
        }
 | 
			
		||||
        return Res.Ok(this.parseIdent());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parseIdent(): Ident {
 | 
			
		||||
@ -1107,11 +1240,12 @@ export class Parser {
 | 
			
		||||
        return this.currentToken!;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private lastSpan?: Span;
 | 
			
		||||
    private span(): Span {
 | 
			
		||||
        if (this.done()) {
 | 
			
		||||
            throw new Error();
 | 
			
		||||
            return this.lastSpan!;
 | 
			
		||||
        }
 | 
			
		||||
        return this.current().span;
 | 
			
		||||
        return this.lastSpan = this.current().span;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private test(type: string): boolean {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user