Compare commits

...

2 Commits

Author SHA1 Message Date
SimonFJ20
493a11a93b lower ast 2025-01-28 15:15:30 +01:00
SimonFJ20
15c70e4840 fix adapted parser 2025-01-28 11:58:46 +01:00
12 changed files with 766 additions and 104 deletions

View File

@ -1,3 +1,4 @@
import { IdentId } from "../ctx.ts";
import { Span } from "../diagnostics.ts"; import { Span } from "../diagnostics.ts";
export type File = { export type File = {
@ -23,7 +24,7 @@ export type ItemStmt = { item: Item };
export type LetStmt = { export type LetStmt = {
pat: Pat; pat: Pat;
ty: Ty; ty?: Ty;
expr?: Expr; expr?: Expr;
}; };
@ -57,7 +58,7 @@ export type ItemKind =
| { tag: "use" } & UseItem | { tag: "use" } & UseItem
| { tag: "type_alias" } & TypeAliasItem; | { tag: "type_alias" } & TypeAliasItem;
export type ModBlockItem = { stmts: Stmt[] }; export type ModBlockItem = { block: Block };
export type ModFileItem = { filePath: string }; export type ModFileItem = { filePath: string };
export type EnumItem = { variants: Variant[] }; export type EnumItem = { variants: Variant[] };
export type StructItem = { data: VariantData }; export type StructItem = { data: VariantData };
@ -122,11 +123,11 @@ export type Expr = {
export type ExprKind = export type ExprKind =
| { tag: "error" } | { tag: "error" }
| { tag: "path" } & Path | { tag: "path" } & PathExpr
| { tag: "null" } | { tag: "null" }
| { tag: "int" } & IntExpr | { tag: "int" } & IntExpr
| { tag: "bool" } & BoolExpr | { tag: "bool" } & BoolExpr
| { tag: "string" } & StringExpr | { tag: "str" } & StringExpr
| { tag: "group" } & GroupExpr | { tag: "group" } & GroupExpr
| { tag: "array" } & ArrayExpr | { tag: "array" } & ArrayExpr
| { tag: "repeat" } & RepeatExpr | { tag: "repeat" } & RepeatExpr
@ -139,13 +140,14 @@ export type ExprKind =
| { tag: "call" } & CallExpr | { tag: "call" } & CallExpr
| { tag: "unary" } & UnaryExpr | { tag: "unary" } & UnaryExpr
| { tag: "binary" } & BinaryExpr | { tag: "binary" } & BinaryExpr
| { tag: "block" } & Block | { tag: "block" } & BlockExpr
| { tag: "if" } & IfExpr | { tag: "if" } & IfExpr
| { tag: "loop" } & LoopExpr | { tag: "loop" } & LoopExpr
| { tag: "while" } & WhileExpr | { tag: "while" } & WhileExpr
| { tag: "for" } & ForExpr | { tag: "for" } & ForExpr
| { tag: "c_for" } & CForExpr; | { tag: "c_for" } & CForExpr;
export type PathExpr = { path: Path };
export type IntExpr = { value: number }; export type IntExpr = { value: number };
export type BoolExpr = { value: boolean }; export type BoolExpr = { value: boolean };
export type StringExpr = { value: string }; export type StringExpr = { value: string };
@ -161,6 +163,7 @@ export type IndexExpr = { expr: Expr; index: Expr };
export type CallExpr = { expr: Expr; args: Expr[] }; export type CallExpr = { expr: Expr; args: Expr[] };
export type UnaryExpr = { unaryType: UnaryType; expr: Expr }; export type UnaryExpr = { unaryType: UnaryType; expr: Expr };
export type BinaryExpr = { binaryType: BinaryType; left: Expr; right: 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 IfExpr = { cond: Expr; truthy: Block; falsy?: Expr };
export type LoopExpr = { body: Block }; export type LoopExpr = { body: Block };
export type WhileExpr = { cond: Expr; body: Block }; export type WhileExpr = { cond: Expr; body: Block };
@ -216,13 +219,18 @@ export type Ty = {
export type TyKind = export type TyKind =
| { tag: "error" } | { tag: "error" }
| { tag: "path" } & Path | { tag: "null" }
| { tag: "int" }
| { tag: "bool" }
| { tag: "str" }
| { tag: "path" } & PathTy
| { tag: "ref" } & RefTy | { tag: "ref" } & RefTy
| { tag: "ptr" } & PtrTy | { tag: "ptr" } & PtrTy
| { tag: "slice" } & SliceTy | { tag: "slice" } & SliceTy
| { tag: "array" } & ArrayTy | { tag: "array" } & ArrayTy
| { tag: "anon_struct" } & AnonStructTy; | { tag: "anon_struct" } & AnonStructTy;
export type PathTy = { path: Path };
export type RefTy = { ty: Ty; mut: boolean }; export type RefTy = { ty: Ty; mut: boolean };
export type PtrTy = { ty: Ty; mut: boolean }; export type PtrTy = { ty: Ty; mut: boolean };
export type SliceTy = { ty: Ty }; export type SliceTy = { ty: Ty };
@ -248,6 +256,6 @@ export type PathSegment = {
}; };
export type Ident = { export type Ident = {
text: string; id: IdentId;
span: Span; span: Span;
}; };

View File

@ -1,5 +1,5 @@
import { exhausted } from "../util.ts"; import { exhausted } from "../util.ts";
import { Block } from "./ast.ts"; import { Block, BlockExpr, PathExpr, PathTy } from "./ast.ts";
import { import {
AnonStructTy, AnonStructTy,
ArrayExpr, ArrayExpr,
@ -83,7 +83,7 @@ export interface Visitor<
visitExpr?(expr: Expr, ...p: P): R; visitExpr?(expr: Expr, ...p: P): R;
visitErrorExpr?(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; visitNullExpr?(expr: Expr, ...p: P): R;
visitIntExpr?(expr: Expr, kind: IntExpr, ...p: P): R; visitIntExpr?(expr: Expr, kind: IntExpr, ...p: P): R;
visitBoolExpr?(expr: Expr, kind: BoolExpr, ...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; visitCallExpr?(expr: Expr, kind: CallExpr, ...p: P): R;
visitUnaryExpr?(expr: Expr, kind: UnaryExpr, ...p: P): R; visitUnaryExpr?(expr: Expr, kind: UnaryExpr, ...p: P): R;
visitBinaryExpr?(expr: Expr, kind: BinaryExpr, ...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; visitIfExpr?(expr: Expr, kind: IfExpr, ...p: P): R;
visitLoopExpr?(expr: Expr, kind: LoopExpr, ...p: P): R; visitLoopExpr?(expr: Expr, kind: LoopExpr, ...p: P): R;
visitWhileExpr?(expr: Expr, kind: WhileExpr, ...p: P): R; visitWhileExpr?(expr: Expr, kind: WhileExpr, ...p: P): R;
@ -113,7 +113,11 @@ export interface Visitor<
visitTy?(ty: Ty, ...p: P): R; visitTy?(ty: Ty, ...p: P): R;
visitErrorTy?(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;
visitStrTy?(ty: Ty, ...p: P): R;
visitPathTy?(ty: Ty, kind: PathTy, ...p: P): R;
visitRefTy?(ty: Ty, kind: RefTy, ...p: P): R; visitRefTy?(ty: Ty, kind: RefTy, ...p: P): R;
visitPtrTy?(ty: Ty, kind: PtrTy, ...p: P): R; visitPtrTy?(ty: Ty, kind: PtrTy, ...p: P): R;
visitSliceTy?(ty: Ty, kind: SliceTy, ...p: P): R; visitSliceTy?(ty: Ty, kind: SliceTy, ...p: P): R;
@ -163,24 +167,41 @@ export function visitStmt<
return; return;
case "item": case "item":
if (v.visitItemStmt?.(stmt, kind, ...p) === "stop") return; if (v.visitItemStmt?.(stmt, kind, ...p) === "stop") return;
visitItem(v, kind.item, ...p);
return; return;
case "let": case "let":
if (v.visitLetStmt?.(stmt, kind, ...p) === "stop") return; 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; return;
case "return": case "return":
if (v.visitReturnStmt?.(stmt, kind, ...p) === "stop") return; if (v.visitReturnStmt?.(stmt, kind, ...p) === "stop") return;
if (kind.expr) {
visitExpr(v, kind.expr, ...p);
}
return; return;
case "break": case "break":
if (v.visitBreakStmt?.(stmt, kind, ...p) === "stop") return; if (v.visitBreakStmt?.(stmt, kind, ...p) === "stop") return;
if (kind.expr) {
visitExpr(v, kind.expr, ...p);
}
return; return;
case "continue": case "continue":
if (v.visitContinueStmt?.(stmt, ...p) === "stop") return; if (v.visitContinueStmt?.(stmt, ...p) === "stop") return;
return; return;
case "assign": case "assign":
if (v.visitAssignStmt?.(stmt, kind, ...p) === "stop") return; if (v.visitAssignStmt?.(stmt, kind, ...p) === "stop") return;
visitExpr(v, kind.subject, ...p);
visitExpr(v, kind.value, ...p);
return; return;
case "expr": case "expr":
if (v.visitExprStmt?.(stmt, kind, ...p) === "stop") return; if (v.visitExprStmt?.(stmt, kind, ...p) === "stop") return;
visitExpr(v, kind.expr, ...p);
return; return;
} }
exhausted(kind); exhausted(kind);
@ -193,6 +214,7 @@ export function visitItem<
item: Item, item: Item,
...p: P ...p: P
) { ) {
visitIdent(v, item.ident, ...p);
const kind = item.kind; const kind = item.kind;
switch (kind.tag) { switch (kind.tag) {
case "error": case "error":
@ -237,7 +259,7 @@ export function visitExpr<
return; return;
case "path": case "path":
if (v.visitPathExpr?.(expr, kind, ...p) === "stop") return; if (v.visitPathExpr?.(expr, kind, ...p) === "stop") return;
visitPath(v, kind, ...p); visitPath(v, kind.path, ...p);
return; return;
case "null": case "null":
if (v.visitNullExpr?.(expr, ...p) === "stop") return; if (v.visitNullExpr?.(expr, ...p) === "stop") return;
@ -245,7 +267,7 @@ export function visitExpr<
case "int": case "int":
if (v.visitIntExpr?.(expr, kind, ...p) === "stop") return; if (v.visitIntExpr?.(expr, kind, ...p) === "stop") return;
return; return;
case "string": case "str":
if (v.visitStringExpr?.(expr, kind, ...p) === "stop") return; if (v.visitStringExpr?.(expr, kind, ...p) === "stop") return;
return; return;
case "bool": case "bool":
@ -271,7 +293,7 @@ export function visitExpr<
if (kind.path) { if (kind.path) {
visitPath(v, kind.path, ...p); visitPath(v, kind.path, ...p);
} }
for (const field of kind.field) { for (const field of kind.fields) {
visitIdent(v, field.ident, ...p); visitIdent(v, field.ident, ...p);
visitExpr(v, field.expr, ...p); visitExpr(v, field.expr, ...p);
} }
@ -316,14 +338,14 @@ export function visitExpr<
return; return;
case "block": case "block":
if (v.visitBlockExpr?.(expr, kind, ...p) === "stop") return; if (v.visitBlockExpr?.(expr, kind, ...p) === "stop") return;
visitBlock(v, kind, ...p); visitBlock(v, kind.block, ...p);
return; return;
case "if": case "if":
if (v.visitIfExpr?.(expr, kind, ...p) === "stop") return; if (v.visitIfExpr?.(expr, kind, ...p) === "stop") return;
visitExpr(v, kind.cond, ...p); visitExpr(v, kind.cond, ...p);
visitBlock(v, kind.truthy, ...p); visitBlock(v, kind.truthy, ...p);
if (kind.falsy) { if (kind.falsy) {
visitBlock(v, kind.falsy, ...p); visitExpr(v, kind.falsy, ...p);
} }
return; return;
case "loop": case "loop":
@ -388,9 +410,21 @@ export function visitTy<
case "error": case "error":
if (v.visitErrorTy?.(ty, ...p) === "stop") return; if (v.visitErrorTy?.(ty, ...p) === "stop") return;
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 "str":
if (v.visitStrTy?.(ty, ...p) === "stop") return;
return;
case "path": case "path":
if (v.visitPathTy?.(ty, kind, ...p) === "stop") return; if (v.visitPathTy?.(ty, kind, ...p) === "stop") return;
v.visitPath?.(kind, ...p); v.visitPath?.(kind.path, ...p);
return; return;
case "ref": case "ref":
if (v.visitRefTy?.(ty, kind, ...p) === "stop") return; if (v.visitRefTy?.(ty, kind, ...p) === "stop") return;

View File

@ -1,14 +1,21 @@
import * as ast from "./ast/mod.ts"; 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";
import * as hir from "./middle/hir.ts";
export class Ctx { export class Ctx {
private fileIds = new Ids(); private fileIds = new Ids<File>();
private files = new Map<Id<File>, FileInfo>(); private files = new Map<Key<File>, FileInfo>();
private reports: Report[] = []; private reports: Report[] = [];
public fileHasChildWithIdent(file: File, childIdent: string): boolean { public fileHasChildWithIdent(file: File, childIdent: string): boolean {
return this.files.get(id(file))! return this.files.get(idKey(file))!
.subFiles.has(childIdent); .subFiles.has(childIdent);
} }
@ -20,7 +27,7 @@ export class Ctx {
text: string, text: string,
): File { ): File {
const file = this.fileIds.nextThenStep(); const file = this.fileIds.nextThenStep();
this.files.set(id(file), { this.files.set(idKey(file), {
ident, ident,
absPath, absPath,
relPath, relPath,
@ -29,38 +36,74 @@ export class Ctx {
text, text,
}); });
if (superFile) { if (superFile) {
this.files.get(id(superFile))! this.files.get(idKey(superFile))!
.subFiles.set(ident, file); .subFiles.set(ident, file);
} }
return file; return file;
} }
public addFileAst(file: File, ast: ast.File) { public addFileAst(file: File, ast: ast.File) {
this.files.get(id(file))!.ast = ast; this.files.get(idKey(file))!.ast = ast;
} }
public fileInfo(file: File): FileInfo { public fileInfo(file: File): FileInfo {
return this.files.get(id(file))!; return this.files.get(idKey(file))!;
}
public entryFile(): File {
return keyId(0);
}
public iterFiles(): Iterator<File> {
return this.files.keys()
.map((key) => keyId(key));
}
private identIds = new Ids<IdentId>();
private identStringToId = new Map<string, IdentId>();
private identIdToString = new Map<Key<IdentId>, string>();
public internIdent(ident: string): IdentId {
if (this.identStringToId.has(ident)) {
return this.identStringToId.get(ident)!;
}
const id = this.identIds.nextThenStep();
this.identStringToId.set(ident, id);
this.identIdToString.set(idKey(id), ident);
return id;
}
public identText(ident: IdentId): string {
return this.identIdToString.get(idKey(ident))!;
}
private stmtIds = new Ids<hir.StmtId>();
private stmts = new Map<Key<hir.StmtId>, hir.Stmt>();
public internStmt(kind: hir.StmtKind, span: Span): hir.StmtId {
const id = this.stmtIds.nextThenStep();
this.stmts.set(idKey(id), { kind, span });
return id;
} }
public filePosLineText(file: File, pos: Pos): string { public filePosLineText(file: File, pos: Pos): string {
const fileTextLines = this.fileInfo(file).text.split("\n") const fileTextLines = this.fileInfo(file).text.split("\n");
return fileTextLines[pos.line-1] return fileTextLines[pos.line - 1];
} }
public fileSpanText(file: File, span: Span): string { public fileSpanText(file: File, span: Span): string {
let result = "" let result = "";
const fileTextLines = this.fileInfo(file).text.split("\n") const fileTextLines = this.fileInfo(file).text.split("\n");
for(let i = 0; i < fileTextLines.length; i++) { for (let i = 0; i < fileTextLines.length; i++) {
if (i > span.end.line-1) { if (i > span.end.line - 1) {
break; break;
} }
if (i >= span.begin.line-1) { if (i >= span.begin.line - 1) {
result += fileTextLines[i] + "\n"; result += fileTextLines[i] + "\n";
} }
} }
return result return result;
} }
public report(rep: Report) { public report(rep: Report) {
@ -78,9 +121,16 @@ 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; export type File = IdBase & { readonly _: unique symbol };
export type FileInfo = { export type FileInfo = {
ident: string; ident: string;
@ -92,16 +142,22 @@ export type FileInfo = {
ast?: ast.File; ast?: ast.File;
}; };
export type IdBase = { id: number }; export type IdentId = IdBase & { readonly _: unique symbol };
export type DefId = IdBase & { readonly _: unique symbol };
export type Id<IdType extends IdBase> = IdType["id"]; export type IdBase = { key: number };
export const id = <IdType extends IdBase>(id: IdType): Id<IdType> => id.id;
export type Key<IdType extends IdBase> = IdType["key"];
export const idKey = <IdType extends IdBase>(id: IdType): Key<IdType> => id.key;
export const keyId = <IdType extends IdBase>(
key: Key<IdType>,
): IdType => ({ key } as IdType);
export class Ids<IdType extends IdBase> { export class Ids<IdType extends IdBase> {
private next = 0; private next = 0;
public nextThenStep(): IdType { public nextThenStep(): IdType {
const id = this.next; const key = this.next;
this.next += 1; this.next += 1;
return { id } as IdType; return { key } as IdType;
} }
} }

View File

@ -4,6 +4,13 @@ import * as ast from "./ast/mod.ts";
import { Ctx } from "./ctx.ts"; import { Ctx } from "./ctx.ts";
import { File } 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 = { export type Pack = {
rootMod: Mod; rootMod: Mod;
}; };
@ -14,6 +21,11 @@ export interface PackEmitter {
emit(pack: Pack): void; emit(pack: Pack): void;
} }
export class NullEmitter implements PackEmitter {
emit(pack: Pack): void {
}
}
export class PackCompiler { export class PackCompiler {
private ctx = new Ctx(); private ctx = new Ctx();
@ -22,10 +34,16 @@ export class PackCompiler {
private emitter: PackEmitter, private emitter: PackEmitter,
) {} ) {}
public compile() { public async compile() {
FileTreeAstCollector await FileTreeAstCollector
.fromEntryFile(this.ctx, this.entryFilePath) .fromEntryFile(this.ctx, this.entryFilePath)
.collect(); .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, kind: ast.ModFileItem,
{ file }: _P, { file }: _P,
): ast.VisitRes { ): ast.VisitRes {
const { ident: { text: ident }, filePath: relPath } = kind; const ident = this.ctx.identText(item.ident.id);
const { filePath: relPath } = kind;
const absPath = path.join(path.dirname(this.absPath), relPath); const absPath = path.join(path.dirname(this.absPath), relPath);
this.subFilePromise = this.subFilePromise this.subFilePromise = this.subFilePromise
.then(() => { .then(() => {
@ -99,3 +118,5 @@ export class FileTreeAstCollector implements ast.Visitor<[_P]> {
return "stop"; return "stop";
} }
} }
main();

278
compiler/middle/hir.ts Normal file
View File

@ -0,0 +1,278 @@
import { Span } from "../diagnostics.ts";
export type StmtId = { key: number; readonly unique: unique symbol };
export type Stmt = {
kind: StmtKind;
span: Span;
};
export type StmtKind =
| { tag: "error" }
| { tag: "item" } & ItemStmt
| { tag: "let" } & LetStmt
| { tag: "return" } & ReturnStmt
| { tag: "break" } & BreakStmt
| { tag: "continue" }
| { tag: "assign" } & AssignStmt
| { tag: "expr" } & ExprStmt;
export type ItemStmt = { item: ItemId };
export type LetStmt = {
pat: PatId;
ty?: TyId;
expr?: ExprId;
};
export type ReturnStmt = { expr?: ExprId };
export type BreakStmt = { expr?: ExprId };
export type AssignStmt = {
assignType: AssignType;
subject: ExprId;
value: ExprId;
};
export type AssignType = "=" | "+=" | "-=";
export type ExprStmt = { expr: ExprId };
export type ItemId = { key: number; readonly unique: unique symbol };
export type Item = {
kind: ItemKind;
span: Span;
ident: Ident;
pub: boolean;
};
export type ItemKind =
| { tag: "error" }
| { tag: "mod_block" } & ModBlockItem
| { tag: "mod_file" } & ModFileItem
| { tag: "enum" } & EnumItem
| { tag: "struct" } & StructItem
| { tag: "fn" } & FnItem
| { tag: "use" } & UseItem
| { tag: "type_alias" } & TypeAliasItem;
export type ModBlockItem = { block: BlockId };
export type ModFileItem = { filePath: string };
export type EnumItem = { variants: Variant[] };
export type StructItem = { data: VariantData };
export type FnItem = {
generics?: Generics;
params: Param[];
returnTy?: TyId;
body: BlockId;
};
export type UseItem = { _: 0 };
export type TypeAliasItem = { ty: TyId };
export type Variant = {
ident: Ident;
data: VariantData;
pub: boolean;
span: Span;
};
export type VariantData = {
kind: VariantDataKind;
span: Span;
};
export type VariantDataKind =
| { tag: "error" }
| { tag: "unit" }
| { tag: "tuple" } & TupleVariantData
| { tag: "struct" } & StructVariantData;
export type TupleVariantData = { elems: VariantData[] };
export type StructVariantData = { fields: FieldDef[] };
export type FieldDef = {
ident: Ident;
ty: TyId;
pub: boolean;
span: Span;
};
export type Param = {
pat: PatId;
ty: TyId;
span: Span;
};
export type Generics = {
params: GenericParam[];
};
export type GenericParam = {
ident: Ident;
span: Span;
};
export type ExprId = { key: number; readonly unique: unique symbol };
export type Expr = {
kind: ExprKind;
span: Span;
};
export type ExprKind =
| { tag: "error" }
| { tag: "path" } & PathExpr
| { tag: "null" }
| { tag: "int" } & IntExpr
| { tag: "bool" } & BoolExpr
| { tag: "str" } & StringExpr
| { tag: "group" } & GroupExpr
| { tag: "array" } & ArrayExpr
| { tag: "repeat" } & RepeatExpr
| { tag: "struct" } & StructExpr
| { tag: "ref" } & RefExpr
| { tag: "deref" } & DerefExpr
| { tag: "elem" } & ElemExpr
| { tag: "field" } & FieldExpr
| { tag: "index" } & IndexExpr
| { tag: "call" } & CallExpr
| { tag: "unary" } & UnaryExpr
| { tag: "binary" } & BinaryExpr
| { 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 };
export type GroupExpr = { expr: ExprId };
export type ArrayExpr = { exprs: ExprId[] };
export type RepeatExpr = { expr: ExprId; length: ExprId };
export type StructExpr = { path?: Path; fields: ExprField[] };
export type RefExpr = { expr: ExprId; refType: RefType; mut: boolean };
export type DerefExpr = { expr: ExprId };
export type ElemExpr = { expr: ExprId; elem: number };
export type FieldExpr = { expr: ExprId; ident: Ident };
export type IndexExpr = { expr: ExprId; index: ExprId };
export type CallExpr = { expr: ExprId; args: ExprId[] };
export type UnaryExpr = { unaryType: UnaryType; expr: ExprId };
export type BinaryExpr = {
binaryType: BinaryType;
left: ExprId;
right: ExprId;
};
export type BlockExpr = { block: BlockId };
export type IfExpr = { cond: ExprId; truthy: BlockId; falsy?: ExprId };
export type LoopExpr = { body: BlockId };
export type WhileExpr = { cond: ExprId; body: BlockId };
export type ForExpr = { pat: PatId; expr: ExprId; body: BlockId };
export type CForExpr = {
decl?: StmtId;
cond?: ExprId;
incr?: StmtId;
body: BlockId;
};
export type RefType = "ref" | "ptr";
export type UnaryType = "not" | "-";
export type BinaryType =
| "+"
| "*"
| "=="
| "-"
| "/"
| "!="
| "<"
| ">"
| "<="
| ">="
| "or"
| "and";
export type ExprField = {
ident: Ident;
expr: ExprId;
span: Span;
};
export type BlockId = { key: number; readonly unique: unique symbol };
export type Block = {
stmts: StmtId[];
expr?: ExprId;
span: Span;
};
export type PatId = { key: number; readonly unique: unique symbol };
export type Pat = {
kind: PatKind;
span: Span;
};
export type PatKind =
| { tag: "error" }
| { tag: "bind" } & BindPat;
export type BindPat = {
ident: Ident;
mut: boolean;
};
export type TyId = { key: number; readonly unique: unique symbol };
export type Ty = {
kind: TyKind;
span: Span;
};
export type TyKind =
| { tag: "error" }
| { tag: "null" }
| { tag: "int" }
| { tag: "bool" }
| { tag: "str" }
| { 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: TyId; mut: boolean };
export type PtrTy = { ty: TyId; mut: boolean };
export type SliceTy = { ty: TyId };
export type ArrayTy = { ty: TyId; length: ExprId };
export type TupleTy = { elems: TyId[] };
export type AnonStructTy = { fields: AnonFieldDef[] };
export type AnonFieldDef = {
ident: Ident;
ty: TyId;
span: Span;
};
export type Path = {
segments: PathSegment[];
span: Span;
};
export type PathSegment = {
ident: Ident;
genericArgs?: TyId[];
span: Span;
};
export type Ident = {
internId: number;
span: Span;
};

View File

@ -0,0 +1,125 @@
import { Ctx, DefId, IdentId } from "../ctx.ts";
import * as ast from "../ast/ast.ts";
import { ExprId, Ident, ItemId, PatId, StmtId, TyId } from "./hir.ts";
import { exhausted, Res as Result, todo } from "../util.ts";
export class AstLowerer {
private ribs: Rib[] = [];
public constructor(
private ctx: Ctx,
) {}
public lower() {
const file = this.ctx.entryFile();
const ast = this.ctx.fileInfo(file).ast!;
this.lowerFile(ast);
}
private lowerFile(file: ast.File) {
this.lowerStmts(file.stmts);
}
private lowerStmts(stmts: ast.Stmt[]): StmtId[] {
return stmts.map((stmt) => this.lowerStmt(stmt));
}
private lowerStmt(stmt: ast.Stmt): StmtId {
const kind = stmt.kind;
switch (kind.tag) {
case "error":
return this.ctx.internStmt(kind, stmt.span);
case "item":
return this.ctx.internStmt({
tag: "item",
item: this.lowerItem(kind.item),
}, stmt.span);
case "let":
return this.lowerLetStmt(stmt, kind);
case "return":
return this.ctx.internStmt({
tag: "return",
expr: kind.expr && this.lowerExpr(kind.expr),
}, stmt.span);
case "break":
return this.ctx.internStmt({
tag: "break",
expr: kind.expr && this.lowerExpr(kind.expr),
}, stmt.span);
case "continue":
return this.ctx.internStmt({
tag: "continue",
}, stmt.span);
case "assign":
return this.ctx.internStmt({
tag: "assign",
assignType: kind.assignType,
subject: this.lowerExpr(kind.subject),
value: this.lowerExpr(kind.value),
}, stmt.span);
case "expr":
return this.ctx.internStmt({
tag: "expr",
expr: this.lowerExpr(kind.expr),
}, stmt.span);
}
exhausted(kind);
}
private lowerLetStmt(stmt: ast.Stmt, kind: ast.LetStmt): StmtId {
return this.ctx.internStmt({
tag: "let",
pat: this.lowerPat(kind.pat),
ty: kind.ty && this.lowerTy(kind.ty),
expr: kind.expr && this.lowerExpr(kind.expr),
}, stmt.span);
}
private lowerItem(item: ast.Item): ItemId {
return todo();
}
private lowerPat(pat: ast.Pat): PatId {
return todo();
}
private lowerTy(ty: ast.Ty): TyId {
return todo();
}
private lowerExpr(expr: ast.Expr): ExprId {
return todo();
}
}
// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/late/struct.Rib.html
type Rib = {
kind: RibKind;
bindings: Map<IdentId, Res>;
};
// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/late/enum.RibKind.html
type RibKind =
| { tag: "normal" }
| { tag: "fn" }
| { tag: "item"; defKind: DefKind }
| { tag: "mod" };
// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/late/enum.RibKind.html
type Res =
| { tag: "error" }
| { tag: "def"; kind: DefKind; id: DefId }
| { tag: "local"; id: DefId };
// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def/enum.DefKind.html
type DefKind =
| { type: "mod" }
| { type: "struct" }
| { type: "enum" }
| { type: "variant" }
| { type: "ty_alias" }
| { type: "ty_param" }
| { type: "fn" }
| { type: "ctor" }
| { type: "use" }
| { type: "field" };

View File

@ -56,7 +56,7 @@ export class Lexer {
if (keywords.includes(value)) { if (keywords.includes(value)) {
return this.token(value, pos); return this.token(value, pos);
} else { } else {
return { ...this.token("ident", pos), identValue: value }; return { ...this.token("ident", pos), identId: value };
} }
} }
if (this.test(/[1-9]/)) { if (this.test(/[1-9]/)) {

View File

@ -199,7 +199,7 @@ export class Parser {
this.report("expected 'ident'"); this.report("expected 'ident'");
return this.stmt({ type: "error" }, spos); return this.stmt({ type: "error" }, spos);
} }
const ident = this.current().identValue!; const ident = this.current().identId!;
this.step(); this.step();
const args: Expr[] = []; const args: Expr[] = [];
if (this.test("(")) { if (this.test("(")) {
@ -250,7 +250,7 @@ export class Parser {
this.report("expected 'ident'"); this.report("expected 'ident'");
return this.stmt({ type: "error" }, pos); return this.stmt({ type: "error" }, pos);
} }
const ident = this.current().identValue!; const ident = this.current().identId!;
this.step(); this.step();
if (this.test(";")) { if (this.test(";")) {
this.eatSemicolon(); this.eatSemicolon();
@ -290,7 +290,7 @@ export class Parser {
this.report("expected ident"); this.report("expected ident");
return this.stmt({ type: "error" }, pos); return this.stmt({ type: "error" }, pos);
} }
const ident = this.current().identValue!; const ident = this.current().identId!;
this.step(); this.step();
let genericParams: GenericParam[] | undefined; let genericParams: GenericParam[] | undefined;
if (this.test("<")) { if (this.test("<")) {
@ -333,7 +333,7 @@ export class Parser {
private parseETypeParam(index: number): Res<GenericParam> { private parseETypeParam(index: number): 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().identId!;
this.step(); this.step();
return { return {
ok: true, ok: true,
@ -394,7 +394,7 @@ export class Parser {
mut = true; mut = true;
this.step(); this.step();
} }
const ident = this.current().identValue!; const ident = this.current().identId!;
this.step(); this.step();
if (this.test(":")) { if (this.test(":")) {
this.step(); this.step();
@ -637,7 +637,7 @@ export class Parser {
this.report("expected 'ident'"); this.report("expected 'ident'");
return { ok: false, pos }; return { ok: false, pos };
} }
const ident = this.current().identValue!; const ident = this.current().identId!;
this.step(); this.step();
if (!this.test(":")) { if (!this.test(":")) {
this.report("expected ':'"); this.report("expected ':'");
@ -856,7 +856,7 @@ export class Parser {
this.report("expected ident"); this.report("expected ident");
return this.expr({ type: "error" }, pos); return this.expr({ type: "error" }, pos);
} }
const ident = this.current().identValue!; const ident = this.current().identId!;
this.step(); this.step();
return this.expr({ type: "field", subject, ident }, pos); return this.expr({ type: "field", subject, ident }, pos);
} }
@ -890,7 +890,7 @@ export class Parser {
this.report("expected ident"); this.report("expected ident");
return this.expr({ type: "error" }, pos); return this.expr({ type: "error" }, pos);
} }
const ident = this.current().identValue!; const ident = this.current().identId!;
this.step(); this.step();
return this.expr({ type: "path", subject, ident }, pos); return this.expr({ type: "path", subject, ident }, pos);
} }
@ -906,7 +906,7 @@ 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 ident = this.current().identValue!; const ident = this.current().identId!;
this.step(); this.step();
return this.expr({ type: "ident", ident }, pos); return this.expr({ type: "ident", ident }, pos);
} }
@ -975,7 +975,7 @@ export class Parser {
return this.etype({ type }, pos); return this.etype({ type }, pos);
} }
if (this.test("ident")) { if (this.test("ident")) {
const ident = this.current().identValue!; const ident = this.current().identId!;
this.step(); this.step();
return this.etype({ type: "ident", ident: ident }, pos); return this.etype({ type: "ident", ident: ident }, pos);
} }

View File

@ -37,7 +37,7 @@ export class Lexer implements TokenIter {
? this.token(val, span) ? this.token(val, span)
: this.token("ident", span, { : this.token("ident", span, {
type: "ident", type: "ident",
identValue: val, identId: this.ctx.internIdent(val),
}); });
}, },
/[a-zA-Z_]/, /[a-zA-Z_]/,
@ -127,8 +127,8 @@ export class Lexer implements TokenIter {
return this.token("error", { begin, end }); return this.token("error", { begin, end });
} }
this.step(); this.step();
return this.token("string", { begin, end }, { return this.token("str", { begin, end }, {
type: "string", type: "str",
stringValue: value, stringValue: value,
}); });
} }
@ -275,7 +275,7 @@ const keywords = new Set([
"null", "null",
"int", "int",
"bool", "bool",
"string", "str",
"return", "return",
"break", "break",
"continue", "continue",

View File

@ -1,4 +1,5 @@
import { import {
AnonFieldDef,
BinaryType, BinaryType,
ExprField, ExprField,
PathSegment, PathSegment,
@ -28,6 +29,8 @@ import { Ctx, File as CtxFile } from "../ctx.ts";
import { Pos, Span } from "../diagnostics.ts"; import { Pos, Span } from "../diagnostics.ts";
import { Res, todo } from "../util.ts"; import { Res, todo } from "../util.ts";
import { Lexer } from "./lexer.ts"; import { Lexer } from "./lexer.ts";
import { TokenIter } from "./token.ts";
import { SigFilter } from "./token.ts";
import { Token } from "./token.ts"; import { Token } from "./token.ts";
type ParseRes<V, E = undefined> = Res<V, E>; type ParseRes<V, E = undefined> = Res<V, E>;
@ -38,14 +41,14 @@ type StmtDetails = {
}; };
export class Parser { export class Parser {
private lexer: Lexer; private lexer: TokenIter;
private currentToken: Token | null; private currentToken: Token | null;
public constructor( public constructor(
private ctx: Ctx, private ctx: Ctx,
private file: CtxFile, 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(); this.currentToken = this.lexer.next();
} }
@ -203,7 +206,7 @@ export class Parser {
private parseBlockExpr(): Expr { private parseBlockExpr(): Expr {
const block = this.parseBlock(); const block = this.parseBlock();
return block.ok 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()); : this.expr({ tag: "error" }, this.span());
} }
@ -277,7 +280,7 @@ export class Parser {
return this.stmt({ tag: "error" }, pos); return this.stmt({ tag: "error" }, pos);
} }
const ident = this.parseIdent(); const ident = this.parseIdent();
if (this.test("string")) { if (this.test("str")) {
const filePath = this.current().stringValue!; const filePath = this.current().stringValue!;
this.step(); this.step();
this.eatSemicolon(); this.eatSemicolon();
@ -312,7 +315,13 @@ export class Parser {
return this.stmt({ return this.stmt({
tag: "item", tag: "item",
item: this.item( item: this.item(
{ tag: "mod_block", stmts }, {
tag: "mod_block",
block: {
stmts,
span: Span.fromto(pos, stmts.at(-1)?.span ?? pos),
},
},
pos, pos,
ident, ident,
details.pub, details.pub,
@ -443,18 +452,15 @@ export class Parser {
}); });
} }
private parsePat(): Pat {
return todo();
}
private parseLet(): Stmt { private parseLet(): Stmt {
const pos = this.span(); const pos = this.span();
this.step(); this.step();
const paramResult = this.parseParam(); const pat = this.parsePat();
if (!paramResult.ok) { let ty: Ty | undefined = undefined;
return this.stmt({ tag: "error" }, pos); if (this.test(":")) {
this.step();
ty = this.parseTy();
} }
const { pat, ty } = paramResult.val;
if (!this.test("=")) { if (!this.test("=")) {
this.report("expected '='"); this.report("expected '='");
return this.stmt({ tag: "error" }, pos); return this.stmt({ tag: "error" }, pos);
@ -936,19 +942,33 @@ export class Parser {
private parseOperand(): Expr { private parseOperand(): Expr {
const pos = this.span(); const pos = this.span();
if (this.test("ident")) { if (this.test("ident")) {
const ident = this.current().identValue!; const pathRes = this.parsePath();
this.step(); if (!pathRes.ok) {
return this.expr({ tag: "ident", ident }, pos); return this.expr({ tag: "error" }, pos);
}
if (this.test("{")) {
this.step();
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")) { if (this.test("int")) {
const value = this.current().intValue!; const value = this.current().intValue!;
this.step(); this.step();
return this.expr({ tag: "int", value }, pos); return this.expr({ tag: "int", value }, pos);
} }
if (this.test("string")) { if (this.test("str")) {
const value = this.current().stringValue!; const value = this.current().stringValue!;
this.step(); this.step();
return this.expr({ tag: "string", value }, pos); return this.expr({ tag: "str", value }, pos);
} }
if (this.test("false")) { if (this.test("false")) {
this.step(); this.step();
@ -993,31 +1013,82 @@ export class Parser {
return this.expr({ tag: "error" }, pos); 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 { private parseTy(): Ty {
const pos = this.span(); const pos = this.span();
if (["null", "int", "bool", "string"].includes(this.current().type)) { if (["null", "int", "bool", "str"].includes(this.current().type)) {
const tag = this.current().type as const tag = this.current().type as
| "null" | "null"
| "int" | "int"
| "bool" | "bool"
| "string"; | "str";
this.step(); this.step();
return this.ty({ tag }, pos); return this.ty({ tag }, pos);
} }
if (this.test("ident")) { if (this.test("ident")) {
const ident = this.current().identValue!; const pathRes = this.parsePath();
this.step(); if (!pathRes.ok) {
return this.ty({ tag: "ident", ident: ident }, pos); return this.ty({ tag: "error" }, pos);
}
return this.ty({ tag: "path", path: pathRes.val }, pos);
} }
if (this.test("[")) { if (this.test("[")) {
this.step(); 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", ty, length }, pos);
}
if (!this.test("]")) { if (!this.test("]")) {
this.report("expected ']'", pos); this.report("expected ']' or ';'", pos);
return this.ty({ tag: "error" }, pos); return this.ty({ tag: "error" }, pos);
} }
this.step(); this.step();
return this.ty({ tag: "array", subject }, pos); return this.ty({ tag: "slice", ty }, pos);
} }
if (this.test("struct")) { if (this.test("struct")) {
this.step(); this.step();
@ -1025,55 +1096,55 @@ export class Parser {
this.report("expected '{'"); this.report("expected '{'");
return this.ty({ tag: "error" }, pos); return this.ty({ tag: "error" }, pos);
} }
const fields = this.parseTyStructFields(); const fields = this.parseAnonFieldDefs();
return this.ty({ tag: "struct", fields }, pos); return this.ty({ tag: "anon_struct", fields }, pos);
} }
if (this.test("&")) { if (this.test("&")) {
this.step(); this.step();
let tag: "ref" | "ref_mut" = "ref"; let mut = false;
if (this.test("mut")) { if (this.test("mut")) {
this.step(); this.step();
tag = "ref_mut"; mut = true;
} }
const subject = this.parseTy(); const ty = this.parseTy();
return this.ty({ type, subject }, pos); return this.ty({ tag: "ref", ty, mut }, pos);
} }
if (this.test("*")) { if (this.test("*")) {
this.step(); this.step();
let tag: "ptr" | "ptr_mut" = "ptr"; let mut = false;
if (this.test("mut")) { if (this.test("mut")) {
this.step(); this.step();
tag = "ptr_mut"; mut = true;
} }
const subject = this.parseTy(); const ty = this.parseTy();
return this.ty({ type, subject }, pos); return this.ty({ tag: "ptr", ty, mut }, pos);
} }
this.report("expected type"); this.report("expected type");
return this.ty({ tag: "error" }, pos); return this.ty({ tag: "error" }, pos);
} }
private parseTyStructFields(): Param[] { private parseAnonFieldDefs(): AnonFieldDef[] {
this.step(); this.step();
if (this.test("}")) { if (this.test("}")) {
this.step(); this.step();
return []; return [];
} }
const params: Param[] = []; const params: AnonFieldDef[] = [];
const paramResult = this.parseParam(); const paramResult = this.parseAnonFieldDef();
if (!paramResult.ok) { if (!paramResult.ok) {
return []; return [];
} }
params.push(paramResult.value); params.push(paramResult.val);
while (this.test(",")) { while (this.test(",")) {
this.step(); this.step();
if (this.test("}")) { if (this.test("}")) {
break; break;
} }
const paramResult = this.parseParam(); const paramResult = this.parseAnonFieldDef();
if (!paramResult.ok) { if (!paramResult.ok) {
return []; return [];
} }
params.push(paramResult.value); params.push(paramResult.val);
} }
if (!this.test("}")) { if (!this.test("}")) {
this.report("expected '}'"); this.report("expected '}'");
@ -1083,16 +1154,78 @@ export class Parser {
return params; 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(); const begin = this.span();
let end = begin; let end = begin;
const segments: PathSegment[] = []; 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 { private parseIdent(): Ident {
const tok = this.current(); const tok = this.current();
this.step(); this.step();
return { text: tok.identValue!, span: tok.span }; return { id: tok.identId!, span: tok.span };
} }
private step() { private step() {
@ -1107,11 +1240,12 @@ export class Parser {
return this.currentToken!; return this.currentToken!;
} }
private lastSpan?: Span;
private span(): Span { private span(): Span {
if (this.done()) { if (this.done()) {
throw new Error(); return this.lastSpan!;
} }
return this.current().span; return this.lastSpan = this.current().span;
} }
private test(type: string): boolean { private test(type: string): boolean {

View File

@ -1,10 +1,11 @@
import { IdentId } from "../ctx.ts";
import { Span } from "../diagnostics.ts"; import { Span } from "../diagnostics.ts";
export type Token = { export type Token = {
type: string; type: string;
span: Span; span: Span;
length: number; length: number;
identValue?: string; identId?: IdentId;
intValue?: number; intValue?: number;
stringValue?: string; stringValue?: string;
}; };

5
compiler/program.slg Normal file
View File

@ -0,0 +1,5 @@
fn main() -> int {
let a = 5;
}