Compare commits
10 Commits
9cb145d97e
...
7ca864d5a9
Author | SHA1 | Date | |
---|---|---|---|
|
7ca864d5a9 | ||
|
ab5a830f66 | ||
6a5fce18fe | |||
|
7766c88512 | ||
|
cd087392b9 | ||
|
93dd4c32c8 | ||
|
e8cfd059cc | ||
|
00298d6d83 | ||
|
09c3c3277b | ||
|
d83ade33d2 |
@ -1,34 +0,0 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Mnemonic Arg Arg description
|
|
||||||
----------------------------------------
|
|
||||||
Nop
|
|
||||||
PushNull
|
|
||||||
PushInt int initial value
|
|
||||||
PushString string initial value
|
|
||||||
PushArray
|
|
||||||
PushStruct
|
|
||||||
PushPtr ptr pointer value
|
|
||||||
Pop
|
|
||||||
LoadLocal int stack position
|
|
||||||
StoreLocal int stack position
|
|
||||||
Call int arg count
|
|
||||||
Return
|
|
||||||
Jump
|
|
||||||
JumpIfNotZero
|
|
||||||
Add
|
|
||||||
Subtract
|
|
||||||
Multiply
|
|
||||||
Divide
|
|
||||||
Remainder
|
|
||||||
Equal
|
|
||||||
LessThan
|
|
||||||
And
|
|
||||||
Or
|
|
||||||
Xor
|
|
||||||
Not
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
230
compiler/ast/ast.ts
Normal file
230
compiler/ast/ast.ts
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
import { Span } from "../diagnostics.ts";
|
||||||
|
|
||||||
|
export type File = {
|
||||||
|
stmts: Stmt[];
|
||||||
|
};
|
||||||
|
|
||||||
|
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: Item };
|
||||||
|
|
||||||
|
export type LetStmt = {
|
||||||
|
subject: Pat;
|
||||||
|
expr?: Expr;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ReturnStmt = { expr?: Expr };
|
||||||
|
export type BreakStmt = { expr?: Expr };
|
||||||
|
|
||||||
|
export type AssignStmt = {
|
||||||
|
assignType: AssignType;
|
||||||
|
subject: Expr;
|
||||||
|
value: Expr;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AssignType = "=" | "+=" | "-=";
|
||||||
|
|
||||||
|
export type ExprStmt = { expr: Expr };
|
||||||
|
|
||||||
|
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 = { ident: Ident; stmts: Stmt[] };
|
||||||
|
export type ModFileItem = { ident: Ident; filePath: string };
|
||||||
|
export type EnumItem = { variants: Variant[] };
|
||||||
|
export type StructItem = { data: VariantData };
|
||||||
|
export type FnItem = { _: 0 };
|
||||||
|
export type UseItem = { _: 0 };
|
||||||
|
export type TypeAliasItem = { ty: Ty };
|
||||||
|
|
||||||
|
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: Ty;
|
||||||
|
pub: boolean;
|
||||||
|
span: Span;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Expr = {
|
||||||
|
kind: ExprKind;
|
||||||
|
span: Span;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ExprKind =
|
||||||
|
| { tag: "error" }
|
||||||
|
| { tag: "path" } & Path
|
||||||
|
| { tag: "null" }
|
||||||
|
| { tag: "int" } & IntExpr
|
||||||
|
| { tag: "bool" } & BoolExpr
|
||||||
|
| { tag: "string" } & 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" } & Block
|
||||||
|
| { tag: "if" } & IfExpr
|
||||||
|
| { tag: "loop" } & LoopExpr
|
||||||
|
| { tag: "while" } & WhileExpr
|
||||||
|
| { tag: "for" } & ForExpr
|
||||||
|
| { tag: "c_for" } & CForExpr;
|
||||||
|
|
||||||
|
export type IntExpr = { value: number };
|
||||||
|
export type BoolExpr = { value: boolean };
|
||||||
|
export type StringExpr = { value: string };
|
||||||
|
export type GroupExpr = { expr: Expr };
|
||||||
|
export type ArrayExpr = { exprs: Expr[] };
|
||||||
|
export type RepeatExpr = { expr: Expr; length: Expr };
|
||||||
|
export type StructExpr = { path?: Path; field: ExprField[] };
|
||||||
|
export type RefExpr = { expr: Expr; refType: RefType; mut: boolean };
|
||||||
|
export type DerefExpr = { expr: Expr };
|
||||||
|
export type ElemExpr = { expr: Expr; elem: number };
|
||||||
|
export type FieldExpr = { expr: Expr; ident: Ident };
|
||||||
|
export type IndexExpr = { expr: Expr; index: Expr };
|
||||||
|
export type CallExpr = { expr: Expr; args: Expr };
|
||||||
|
export type UnaryExpr = { unaryType: UnaryType; expr: Expr };
|
||||||
|
export type BinaryExpr = { unaryType: UnaryType; left: Expr; right: Expr };
|
||||||
|
export type IfExpr = { cond: Expr; truthy: Block; falsy?: Block };
|
||||||
|
export type LoopExpr = { body: Block };
|
||||||
|
export type WhileExpr = { cond: Expr; body: Block };
|
||||||
|
export type ForExpr = { pat: Pat; expr: Expr; body: Block };
|
||||||
|
export type CForExpr = { decl?: Stmt; cond?: Expr; incr?: Stmt; body: Block };
|
||||||
|
|
||||||
|
export type RefType = "ref" | "ptr";
|
||||||
|
export type UnaryType = "not" | "-";
|
||||||
|
export type BinaryType =
|
||||||
|
| "+"
|
||||||
|
| "*"
|
||||||
|
| "=="
|
||||||
|
| "-"
|
||||||
|
| "/"
|
||||||
|
| "!="
|
||||||
|
| "<"
|
||||||
|
| ">"
|
||||||
|
| "<="
|
||||||
|
| ">="
|
||||||
|
| "or"
|
||||||
|
| "and";
|
||||||
|
|
||||||
|
export type ExprField = {
|
||||||
|
ident: Ident;
|
||||||
|
expr: Expr;
|
||||||
|
span: Span;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Block = {
|
||||||
|
stmts: Stmt[];
|
||||||
|
expr?: Expr;
|
||||||
|
span: Span;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Pat = {
|
||||||
|
kind: PatKind;
|
||||||
|
span: Span;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PatKind =
|
||||||
|
| { tag: "error" }
|
||||||
|
| { tag: "bind" } & BindPat;
|
||||||
|
|
||||||
|
export type BindPat = {
|
||||||
|
ident: Ident;
|
||||||
|
mut: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Ty = {
|
||||||
|
kind: TyKind;
|
||||||
|
span: Span;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TyKind =
|
||||||
|
| { tag: "error" }
|
||||||
|
| { tag: "path" } & Path
|
||||||
|
| { tag: "ref" } & RefTy
|
||||||
|
| { tag: "ptr" } & PtrTy
|
||||||
|
| { tag: "slice" } & SliceTy
|
||||||
|
| { tag: "array" } & ArrayTy
|
||||||
|
| { tag: "anon_struct" } & AnonStructTy;
|
||||||
|
|
||||||
|
export type RefTy = { ty: Ty; mut: boolean };
|
||||||
|
export type PtrTy = { ty: Ty; mut: boolean };
|
||||||
|
export type SliceTy = { ty: Ty };
|
||||||
|
export type ArrayTy = { ty: Ty; length: Expr };
|
||||||
|
export type TupleTy = { elems: Ty[] };
|
||||||
|
export type AnonStructTy = { fields: AnonFieldDef[] };
|
||||||
|
|
||||||
|
export type AnonFieldDef = {
|
||||||
|
ident: Ident;
|
||||||
|
ty: Ty;
|
||||||
|
span: Span;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Path = {
|
||||||
|
segments: PathSegment[];
|
||||||
|
span: Span;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PathSegment = {
|
||||||
|
ident: Ident;
|
||||||
|
genericArgs?: Ty[];
|
||||||
|
span: Span;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Ident = {
|
||||||
|
text: string;
|
||||||
|
span: Span;
|
||||||
|
};
|
2
compiler/ast/mod.ts
Normal file
2
compiler/ast/mod.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./ast.ts";
|
||||||
|
export * from "./visitor.ts";
|
288
compiler/ast/visitor.ts
Normal file
288
compiler/ast/visitor.ts
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
import { exhausted } from "../util.ts";
|
||||||
|
import {
|
||||||
|
AnonStructTy,
|
||||||
|
ArrayExpr,
|
||||||
|
ArrayTy,
|
||||||
|
AssignStmt,
|
||||||
|
BinaryExpr,
|
||||||
|
BindPat,
|
||||||
|
BoolExpr,
|
||||||
|
BreakStmt,
|
||||||
|
CallExpr,
|
||||||
|
CForExpr,
|
||||||
|
DerefExpr,
|
||||||
|
ElemExpr,
|
||||||
|
EnumItem,
|
||||||
|
Expr,
|
||||||
|
ExprStmt,
|
||||||
|
FieldExpr,
|
||||||
|
File,
|
||||||
|
FnItem,
|
||||||
|
ForExpr,
|
||||||
|
GroupExpr,
|
||||||
|
Ident,
|
||||||
|
IfExpr,
|
||||||
|
IndexExpr,
|
||||||
|
IntExpr,
|
||||||
|
Item,
|
||||||
|
ItemStmt,
|
||||||
|
LetStmt,
|
||||||
|
LoopExpr,
|
||||||
|
ModBlockItem,
|
||||||
|
ModFileItem,
|
||||||
|
Pat,
|
||||||
|
Path,
|
||||||
|
PtrTy,
|
||||||
|
RefExpr,
|
||||||
|
RefTy,
|
||||||
|
RepeatExpr,
|
||||||
|
ReturnStmt,
|
||||||
|
SliceTy,
|
||||||
|
Stmt,
|
||||||
|
StringExpr,
|
||||||
|
StructExpr,
|
||||||
|
StructItem,
|
||||||
|
TupleTy,
|
||||||
|
Ty,
|
||||||
|
TypeAliasItem,
|
||||||
|
UnaryExpr,
|
||||||
|
UseItem,
|
||||||
|
WhileExpr,
|
||||||
|
} from "./ast.ts";
|
||||||
|
|
||||||
|
export type VisitRes = "stop" | void;
|
||||||
|
|
||||||
|
type R = VisitRes;
|
||||||
|
type PM = unknown[];
|
||||||
|
|
||||||
|
export interface Visitor<
|
||||||
|
P extends PM = [],
|
||||||
|
> {
|
||||||
|
visitFile?(file: File, ...p: P): R;
|
||||||
|
|
||||||
|
visitStmt?(stmt: Stmt, ...p: P): R;
|
||||||
|
visitErrorStmt?(stmt: Stmt, ...p: P): R;
|
||||||
|
visitItemStmt?(stmt: Stmt, kind: ItemStmt, ...p: P): R;
|
||||||
|
visitLetStmt?(stmt: Stmt, kind: LetStmt, ...p: P): R;
|
||||||
|
visitReturnStmt?(stmt: Stmt, kind: ReturnStmt, ...p: P): R;
|
||||||
|
visitBreakStmt?(stmt: Stmt, kind: BreakStmt, ...p: P): R;
|
||||||
|
visitContinueStmt?(stmt: Stmt, ...p: P): R;
|
||||||
|
visitAssignStmt?(stmt: Stmt, kind: AssignStmt, ...p: P): R;
|
||||||
|
visitExprStmt?(stmt: Stmt, kind: ExprStmt, ...p: P): R;
|
||||||
|
|
||||||
|
visitItem?(item: Item, ...p: P): R;
|
||||||
|
visitErrorItem?(item: Item, ...p: P): R;
|
||||||
|
visitModBlockItem?(item: Item, kind: ModBlockItem, ...p: P): R;
|
||||||
|
visitModFileItem?(item: Item, kind: ModFileItem, ...p: P): R;
|
||||||
|
visitEnumItem?(item: Item, kind: EnumItem, ...p: P): R;
|
||||||
|
visitStructItem?(item: Item, kind: StructItem, ...p: P): R;
|
||||||
|
visitFnItem?(item: Item, kind: FnItem, ...p: P): R;
|
||||||
|
visitUseItem?(item: Item, kind: UseItem, ...p: P): R;
|
||||||
|
visitTypeAliasItem?(item: Item, kind: TypeAliasItem, ...p: P): R;
|
||||||
|
|
||||||
|
visitExpr?(expr: Expr, ...p: P): R;
|
||||||
|
visitErrorExpr?(expr: Expr, ...p: P): R;
|
||||||
|
visitPathExpr?(expr: Expr, kind: Path, ...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;
|
||||||
|
visitStringExpr?(expr: Expr, kind: StringExpr, ...p: P): R;
|
||||||
|
visitGroupExpr?(expr: Expr, kind: GroupExpr, ...p: P): R;
|
||||||
|
visitArrayExpr?(expr: Expr, kind: ArrayExpr, ...p: P): R;
|
||||||
|
visitRepeatExpr?(expr: Expr, kind: RepeatExpr, ...p: P): R;
|
||||||
|
visitStructExpr?(expr: Expr, kind: StructExpr, ...p: P): R;
|
||||||
|
visitRefExpr?(expr: Expr, kind: RefExpr, ...p: P): R;
|
||||||
|
visitDerefExpr?(expr: Expr, kind: DerefExpr, ...p: P): R;
|
||||||
|
visitElemExpr?(expr: Expr, kind: ElemExpr, ...p: P): R;
|
||||||
|
visitFieldExpr?(expr: Expr, kind: FieldExpr, ...p: P): R;
|
||||||
|
visitIndexExpr?(expr: Expr, kind: IndexExpr, ...p: P): R;
|
||||||
|
visitCallExpr?(expr: Expr, kind: CallExpr, ...p: P): R;
|
||||||
|
visitUnaryExpr?(expr: Expr, kind: UnaryExpr, ...p: P): R;
|
||||||
|
visitBinaryExpr?(expr: Expr, kind: BinaryExpr, ...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;
|
||||||
|
visitForExpr?(expr: Expr, kind: ForExpr, ...p: P): R;
|
||||||
|
visitCForExpr?(expr: Expr, kind: CForExpr, ...p: P): R;
|
||||||
|
|
||||||
|
visitPat?(pat: Pat, ...p: P): R;
|
||||||
|
visitErrorPat?(pat: Pat, ...p: P): R;
|
||||||
|
visitBindPat?(pat: Pat, kind: BindPat, ...p: P): R;
|
||||||
|
|
||||||
|
visitTy?(ty: Ty, ...p: P): R;
|
||||||
|
visitErrorTy?(ty: Ty, ...p: P): R;
|
||||||
|
visitPathTy?(ty: Ty, kind: Path, ...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;
|
||||||
|
visitArrayTy?(ty: Ty, kind: ArrayTy, ...p: P): R;
|
||||||
|
visitTupleTy?(ty: Ty, kind: TupleTy, ...p: P): R;
|
||||||
|
visitAnonStructTy?(ty: Ty, kind: AnonStructTy, ...p: P): R;
|
||||||
|
|
||||||
|
visitPath?(path: Path, ...p: P): R;
|
||||||
|
visitIdent?(ident: Ident, ...p: P): R;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function visitFile<
|
||||||
|
P extends PM = [],
|
||||||
|
>(
|
||||||
|
v: Visitor<P>,
|
||||||
|
file: File,
|
||||||
|
...p: P
|
||||||
|
) {
|
||||||
|
if (v.visitFile?.(file, ...p) === "stop") return;
|
||||||
|
visitStmts(v, file.stmts, ...p);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function visitStmts<
|
||||||
|
P extends PM = [],
|
||||||
|
>(
|
||||||
|
v: Visitor<P>,
|
||||||
|
stmts: Stmt[],
|
||||||
|
...p: P
|
||||||
|
) {
|
||||||
|
for (const stmt of stmts) {
|
||||||
|
visitStmt(v, stmt, ...p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function visitStmt<
|
||||||
|
P extends PM = [],
|
||||||
|
>(
|
||||||
|
v: Visitor<P>,
|
||||||
|
stmt: Stmt,
|
||||||
|
...p: P
|
||||||
|
) {
|
||||||
|
const kind = stmt.kind;
|
||||||
|
switch (kind.tag) {
|
||||||
|
case "error":
|
||||||
|
if (v.visitErrorStmt?.(stmt, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
case "item":
|
||||||
|
if (v.visitItemStmt?.(stmt, kind, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
case "let":
|
||||||
|
if (v.visitLetStmt?.(stmt, kind, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
case "return":
|
||||||
|
if (v.visitReturnStmt?.(stmt, kind, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
case "break":
|
||||||
|
if (v.visitBreakStmt?.(stmt, kind, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
case "continue":
|
||||||
|
if (v.visitContinueStmt?.(stmt, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
case "assign":
|
||||||
|
if (v.visitAssignStmt?.(stmt, kind, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
case "expr":
|
||||||
|
if (v.visitExprStmt?.(stmt, kind, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
exhausted(kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function visitItem<
|
||||||
|
P extends PM = [],
|
||||||
|
>(
|
||||||
|
v: Visitor<P>,
|
||||||
|
item: Item,
|
||||||
|
...p: P
|
||||||
|
) {
|
||||||
|
const kind = item.kind;
|
||||||
|
switch (kind.tag) {
|
||||||
|
case "error":
|
||||||
|
if (v.visitErrorItem?.(item, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
case "mod_block":
|
||||||
|
if (v.visitModBlockItem?.(item, kind, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
case "mod_file":
|
||||||
|
if (v.visitModFileItem?.(item, kind, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
case "enum":
|
||||||
|
if (v.visitEnumItem?.(item, kind, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
case "struct":
|
||||||
|
if (v.visitStructItem?.(item, kind, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
case "fn":
|
||||||
|
if (v.visitFnItem?.(item, kind, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
case "use":
|
||||||
|
if (v.visitUseItem?.(item, kind, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
case "type_alias":
|
||||||
|
if (v.visitTypeAliasItem?.(item, kind, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
exhausted(kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function visitExpr<
|
||||||
|
P extends PM = [],
|
||||||
|
>(
|
||||||
|
v: Visitor<P>,
|
||||||
|
expr: Expr,
|
||||||
|
...p: P
|
||||||
|
) {
|
||||||
|
const kind = expr.kind;
|
||||||
|
switch (kind.tag) {
|
||||||
|
case "error":
|
||||||
|
if (v.visitErrorExpr?.(expr, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
case "ident":
|
||||||
|
if (v.visitIdentExpr?.(expr, kind, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
exhausted(kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function visitPat<
|
||||||
|
P extends PM = [],
|
||||||
|
>(
|
||||||
|
v: Visitor<P>,
|
||||||
|
pat: Pat,
|
||||||
|
...p: P
|
||||||
|
) {
|
||||||
|
const kind = pat.kind;
|
||||||
|
switch (kind.tag) {
|
||||||
|
case "error":
|
||||||
|
if (v.visitErrorPat?.(pat, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
case "bind":
|
||||||
|
if (v.visitBindPat?.(pat, kind, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
exhausted(kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function visitTy<
|
||||||
|
P extends PM = [],
|
||||||
|
>(
|
||||||
|
v: Visitor<P>,
|
||||||
|
ty: Ty,
|
||||||
|
...p: P
|
||||||
|
) {
|
||||||
|
const kind = ty.kind;
|
||||||
|
switch (kind.tag) {
|
||||||
|
case "error":
|
||||||
|
if (v.visitErrorTy?.(ty, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
case "ident":
|
||||||
|
if (v.visitIdentTy?.(ty, kind, ...p) === "stop") return;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
exhausted(kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function visitIdent<
|
||||||
|
P extends PM = [],
|
||||||
|
>(
|
||||||
|
v: Visitor<P>,
|
||||||
|
ident: Ident,
|
||||||
|
...p: P
|
||||||
|
) {
|
||||||
|
v.visitIdent?.(ident, ...p);
|
||||||
|
}
|
107
compiler/ctx.ts
Normal file
107
compiler/ctx.ts
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import * as ast from "./ast/mod.ts";
|
||||||
|
import { Pos, prettyPrintReport, printStackTrace, Report, Span } from "./diagnostics.ts";
|
||||||
|
|
||||||
|
export class Ctx {
|
||||||
|
private fileIds = new Ids();
|
||||||
|
private files = new Map<Id<File>, FileInfo>();
|
||||||
|
|
||||||
|
private reports: Report[] = [];
|
||||||
|
|
||||||
|
public fileHasChildWithIdent(file: File, childIdent: string): boolean {
|
||||||
|
return this.files.get(id(file))!
|
||||||
|
.subFiles.has(childIdent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public addFile(
|
||||||
|
ident: string,
|
||||||
|
absPath: string,
|
||||||
|
relPath: string,
|
||||||
|
superFile: File | undefined,
|
||||||
|
text: string,
|
||||||
|
): File {
|
||||||
|
const file = this.fileIds.nextThenStep();
|
||||||
|
this.files.set(id(file), {
|
||||||
|
ident,
|
||||||
|
absPath,
|
||||||
|
relPath,
|
||||||
|
superFile,
|
||||||
|
subFiles: new Map(),
|
||||||
|
text,
|
||||||
|
});
|
||||||
|
if (superFile) {
|
||||||
|
this.files.get(id(superFile))!
|
||||||
|
.subFiles.set(ident, file);
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addFileAst(file: File, ast: ast.File) {
|
||||||
|
this.files.get(id(file))!.ast = ast;
|
||||||
|
}
|
||||||
|
|
||||||
|
public fileInfo(file: File): FileInfo {
|
||||||
|
return this.files.get(id(file))!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public filePosLineText(file: File, pos: Pos): string {
|
||||||
|
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")
|
||||||
|
|
||||||
|
for(let i = 0; i < fileTextLines.length; i++) {
|
||||||
|
if (i > span.end.line-1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i >= span.begin.line-1) {
|
||||||
|
result += fileTextLines[i] + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
public report(rep: Report) {
|
||||||
|
this.reports.push(rep);
|
||||||
|
this.reportImmediately(rep);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enableReportImmediately = false;
|
||||||
|
public enableStacktrace = false;
|
||||||
|
private reportImmediately(rep: Report) {
|
||||||
|
if (this.enableReportImmediately) {
|
||||||
|
prettyPrintReport(this, rep);
|
||||||
|
if (this.enableStacktrace) {
|
||||||
|
printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type File = IdBase;
|
||||||
|
|
||||||
|
export type FileInfo = {
|
||||||
|
ident: string;
|
||||||
|
absPath: string;
|
||||||
|
relPath: string;
|
||||||
|
superFile?: File;
|
||||||
|
subFiles: Map<string, File>;
|
||||||
|
text: string;
|
||||||
|
ast?: ast.File;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type IdBase = { id: number };
|
||||||
|
|
||||||
|
export type Id<IdType extends IdBase> = IdType["id"];
|
||||||
|
export const id = <IdType extends IdBase>(id: IdType): Id<IdType> => id.id;
|
||||||
|
|
||||||
|
export class Ids<IdType extends IdBase> {
|
||||||
|
private next = 0;
|
||||||
|
public nextThenStep(): IdType {
|
||||||
|
const id = this.next;
|
||||||
|
this.next += 1;
|
||||||
|
return { id } as IdType;
|
||||||
|
}
|
||||||
|
}
|
95
compiler/diagnostics.ts
Normal file
95
compiler/diagnostics.ts
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import { Ctx, File } from "./ctx.ts";
|
||||||
|
import { exhausted } from "./util.ts";
|
||||||
|
|
||||||
|
export type Span = {
|
||||||
|
begin: Pos;
|
||||||
|
end: Pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Pos = {
|
||||||
|
idx: number;
|
||||||
|
line: number;
|
||||||
|
col: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Report = {
|
||||||
|
severity: "fatal" | "error" | "warning" | "info";
|
||||||
|
origin?: string;
|
||||||
|
msg: string;
|
||||||
|
file?: File;
|
||||||
|
span?: Span;
|
||||||
|
pos?: Pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
function severityColor(severity: "fatal" | "error" | "warning" | "info") {
|
||||||
|
switch (severity) {
|
||||||
|
case "fatal":
|
||||||
|
return "\x1b[1m\x1b[31m";
|
||||||
|
case "error":
|
||||||
|
return "\x1b[1m\x1b[31m";
|
||||||
|
case "warning":
|
||||||
|
return "\x1b[1m\x1b[33m";
|
||||||
|
case "info":
|
||||||
|
return "\x1b[1m\x1b[34m";
|
||||||
|
}
|
||||||
|
exhausted(severity)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function prettyPrintReport(ctx: Ctx, rep: Report) {
|
||||||
|
const { severity, msg } = rep;
|
||||||
|
const origin = rep.origin ? `\x1b[1m${rep.origin}:\x1b[0m ` : "";
|
||||||
|
console.error(`${origin}${severityColor(severity)}${severity}:\x1b[0m \x1b[37m${msg}\x1b[0m`);
|
||||||
|
if (rep.file && (rep.span || rep.pos)) {
|
||||||
|
const errorLineOffset = 2
|
||||||
|
const { absPath: path } = ctx.fileInfo(rep.file);
|
||||||
|
const { line, col } = rep.span?.begin ?? rep.pos!;
|
||||||
|
console.error(` --> ./${path}:${line}:${col}`);
|
||||||
|
if (rep.span) {
|
||||||
|
const spanLines = ctx.fileSpanText(rep.file, rep.span).split("\n");
|
||||||
|
spanLines.pop()
|
||||||
|
if (spanLines.length == 1) {
|
||||||
|
console.error(`${rep.span.begin.line.toString().padStart(4, ' ')}| ${spanLines[0]}`);
|
||||||
|
console.error(` | ${severityColor(severity)}${" ".repeat(rep.span.begin.col)}${"~".repeat(rep.span.end.col-rep.span.begin.col)}\x1b[0m`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for (let i = 0; i < spanLines.length; i++) {
|
||||||
|
console.error(`${(rep.span.begin.line+i).toString().padStart(4, ' ')}| ${spanLines[i]}`);
|
||||||
|
if (i == 0) {
|
||||||
|
console.error(` | ${" ".repeat(rep.span.begin.col-1)}${severityColor(severity)}${"~".repeat(spanLines[i].length-(rep.span.begin.col-1))}\x1b[0m`)
|
||||||
|
}
|
||||||
|
else if (i == spanLines.length-1) {
|
||||||
|
console.error(` | ${severityColor(severity)}${"~".repeat(rep.span.end.col)}\x1b[0m`)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.error(` | ${severityColor(severity)}${"~".repeat(spanLines[i].length)}\x1b[0m`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (rep.pos) {
|
||||||
|
console.error(`${rep.pos.line.toString().padStart(4, ' ')}| ${ctx.filePosLineText(rep.file, rep.pos)}`);
|
||||||
|
console.error(` | ${severityColor(severity)}${" ".repeat(rep.pos.col)}^\x1b[0m`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function printStackTrace() {
|
||||||
|
class StackTracer extends Error {
|
||||||
|
constructor() {
|
||||||
|
super("StackTracer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
throw new StackTracer();
|
||||||
|
} catch (error) {
|
||||||
|
if (!(error instanceof StackTracer)) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
console.log(
|
||||||
|
error.stack?.replace(
|
||||||
|
"Error: StackTracer",
|
||||||
|
"Stack trace:",
|
||||||
|
) ??
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
102
compiler/main.ts
102
compiler/main.ts
@ -1,5 +1,101 @@
|
|||||||
import { Compiler } from "./compiler.ts";
|
import * as path from "jsr:@std/path";
|
||||||
|
import { Parser } from "./parser/parser.ts";
|
||||||
|
import * as ast from "./ast/mod.ts";
|
||||||
|
import { Ctx } from "./ctx.ts";
|
||||||
|
import { File } from "./ctx.ts";
|
||||||
|
|
||||||
const { program } = await new Compiler(Deno.args[0]).compile();
|
export type Pack = {
|
||||||
|
rootMod: Mod;
|
||||||
|
};
|
||||||
|
|
||||||
await Deno.writeTextFile("out.slgbc", JSON.stringify(program));
|
export type Mod = null;
|
||||||
|
|
||||||
|
export interface PackEmitter {
|
||||||
|
emit(pack: Pack): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PackCompiler {
|
||||||
|
private ctx = new Ctx();
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
private entryFilePath: string,
|
||||||
|
private emitter: PackEmitter,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public compile() {
|
||||||
|
FileTreeAstCollector
|
||||||
|
.fromEntryFile(this.ctx, this.entryFilePath)
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type _P = { file: File };
|
||||||
|
export class FileTreeAstCollector implements ast.Visitor<[_P]> {
|
||||||
|
private subFilePromise = Promise.resolve();
|
||||||
|
|
||||||
|
private constructor(
|
||||||
|
private ctx: Ctx,
|
||||||
|
private superFile: File | undefined,
|
||||||
|
private ident: string,
|
||||||
|
private absPath: string,
|
||||||
|
private relPath: string,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public static fromEntryFile(
|
||||||
|
ctx: Ctx,
|
||||||
|
entryFilePath: string,
|
||||||
|
): FileTreeAstCollector {
|
||||||
|
return new FileTreeAstCollector(
|
||||||
|
ctx,
|
||||||
|
undefined,
|
||||||
|
"root",
|
||||||
|
entryFilePath,
|
||||||
|
entryFilePath,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async collect(): Promise<void> {
|
||||||
|
const text = await Deno.readTextFile(this.absPath);
|
||||||
|
const file = this.ctx.addFile(
|
||||||
|
this.ident,
|
||||||
|
this.absPath,
|
||||||
|
this.relPath,
|
||||||
|
this.superFile,
|
||||||
|
text,
|
||||||
|
);
|
||||||
|
const fileAst = new Parser(file, text).parse();
|
||||||
|
this.ctx.addFileAst(file, fileAst);
|
||||||
|
ast.visitFile(this, fileAst, { file });
|
||||||
|
await this.subFilePromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
visitModFileItem(
|
||||||
|
item: ast.Item,
|
||||||
|
kind: ast.ModFileItem,
|
||||||
|
{ file }: _P,
|
||||||
|
): ast.VisitRes {
|
||||||
|
const { ident: { text: ident }, filePath: relPath } = kind;
|
||||||
|
const absPath = path.join(path.dirname(this.absPath), relPath);
|
||||||
|
this.subFilePromise = this.subFilePromise
|
||||||
|
.then(() => {
|
||||||
|
if (this.ctx.fileHasChildWithIdent(file, ident)) {
|
||||||
|
this.ctx.report({
|
||||||
|
severity: "fatal",
|
||||||
|
msg: `module '${ident}' already declared`,
|
||||||
|
file,
|
||||||
|
span: item.span,
|
||||||
|
});
|
||||||
|
Deno.exit(1);
|
||||||
|
}
|
||||||
|
return new FileTreeAstCollector(
|
||||||
|
this.ctx,
|
||||||
|
file,
|
||||||
|
ident,
|
||||||
|
absPath,
|
||||||
|
relPath,
|
||||||
|
)
|
||||||
|
.collect();
|
||||||
|
});
|
||||||
|
return "stop";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
5
compiler/old/main.ts
Normal file
5
compiler/old/main.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { Compiler } from "./compiler.ts";
|
||||||
|
|
||||||
|
const { program } = await new Compiler(Deno.args[0]).compile();
|
||||||
|
|
||||||
|
await Deno.writeTextFile("out.slgbc", JSON.stringify(program));
|
@ -170,9 +170,6 @@ class LocalChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private markSrc(src: RValue) {
|
private markSrc(src: RValue) {
|
||||||
if (src.type === "local") {
|
|
||||||
throw new Error("should be 'copy' or 'move'");
|
|
||||||
}
|
|
||||||
if (
|
if (
|
||||||
(src.type !== "copy" && src.type !== "move") ||
|
(src.type !== "copy" && src.type !== "move") ||
|
||||||
src.id !== this.local.id
|
src.id !== this.local.id
|
@ -73,7 +73,7 @@ class EliminateUnusedLocalsFnPass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private markUsed(local: RValue) {
|
private markUsed(local: RValue) {
|
||||||
if (local.type !== "local") {
|
if (local.type !== "copy" && local.type !== "move") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.locals = this.locals.filter((lid) => lid !== local.id);
|
this.locals = this.locals.filter((lid) => lid !== local.id);
|
@ -23,7 +23,7 @@ class LocalExpliciter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private explicitSrc(src: RValue): RValue {
|
private explicitSrc(src: RValue): RValue {
|
||||||
if (src.type !== "local" || src.id !== this.local.id) {
|
if (src.type !== "move" || src.id !== this.local.id) {
|
||||||
return src;
|
return src;
|
||||||
}
|
}
|
||||||
return this.copyable
|
return this.copyable
|
@ -509,5 +509,5 @@ class FnAstLowerer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function local(id: LocalId): RValue {
|
function local(id: LocalId): RValue {
|
||||||
return { type: "local", id };
|
return { type: "move", id };
|
||||||
}
|
}
|
@ -45,7 +45,6 @@ export type OpKind =
|
|||||||
| { type: "ref_mut"; dst: L; src: L }
|
| { type: "ref_mut"; dst: L; src: L }
|
||||||
| { type: "ptr"; dst: L; src: L }
|
| { type: "ptr"; dst: L; src: L }
|
||||||
| { type: "ptr_mut"; dst: L; src: L }
|
| { type: "ptr_mut"; dst: L; src: L }
|
||||||
| { type: "drop"; src: L }
|
|
||||||
| { type: "deref"; dst: L; src: R }
|
| { type: "deref"; dst: L; src: R }
|
||||||
| { type: "assign_deref"; subject: R; src: R }
|
| { type: "assign_deref"; subject: R; src: R }
|
||||||
| { type: "field"; dst: L; subject: R; ident: string }
|
| { type: "field"; dst: L; subject: R; ident: string }
|
||||||
@ -67,7 +66,6 @@ export type TerKind =
|
|||||||
|
|
||||||
export type RValue =
|
export type RValue =
|
||||||
| { type: "error" }
|
| { type: "error" }
|
||||||
| { type: "local"; id: BlockId }
|
|
||||||
| { type: "copy"; id: BlockId }
|
| { type: "copy"; id: BlockId }
|
||||||
| { type: "move"; id: BlockId }
|
| { type: "move"; id: BlockId }
|
||||||
| { type: "null" }
|
| { type: "null" }
|
||||||
@ -123,7 +121,6 @@ export function replaceBlockSrcs(
|
|||||||
case "ref_mut":
|
case "ref_mut":
|
||||||
case "ptr":
|
case "ptr":
|
||||||
case "ptr_mut":
|
case "ptr_mut":
|
||||||
case "drop":
|
|
||||||
break;
|
break;
|
||||||
case "deref":
|
case "deref":
|
||||||
ok.src = replace(ok.src);
|
ok.src = replace(ok.src);
|
||||||
@ -190,7 +187,6 @@ export function visitBlockSrcs(
|
|||||||
case "ref_mut":
|
case "ref_mut":
|
||||||
case "ptr":
|
case "ptr":
|
||||||
case "ptr_mut":
|
case "ptr_mut":
|
||||||
case "drop":
|
|
||||||
break;
|
break;
|
||||||
case "deref":
|
case "deref":
|
||||||
visitor(ok.src, op, i);
|
visitor(ok.src, op, i);
|
||||||
@ -307,9 +303,6 @@ export function printMir(mir: Mir) {
|
|||||||
case "ptr_mut":
|
case "ptr_mut":
|
||||||
l(`_${k.dst} = *mut _${k.src};`);
|
l(`_${k.dst} = *mut _${k.src};`);
|
||||||
break;
|
break;
|
||||||
case "drop":
|
|
||||||
l(`drop _${k.src};`);
|
|
||||||
break;
|
|
||||||
case "deref":
|
case "deref":
|
||||||
l(`_${k.dst} = *${r(k.src)};`);
|
l(`_${k.dst} = *${r(k.src)};`);
|
||||||
break;
|
break;
|
||||||
@ -372,8 +365,6 @@ export function rvalueToString(rvalue: RValue): string {
|
|||||||
switch (rvalue.type) {
|
switch (rvalue.type) {
|
||||||
case "error":
|
case "error":
|
||||||
return `<error>`;
|
return `<error>`;
|
||||||
case "local":
|
|
||||||
return `_${rvalue.id}`;
|
|
||||||
case "copy":
|
case "copy":
|
||||||
return `copy _${rvalue.id}`;
|
return `copy _${rvalue.id}`;
|
||||||
case "move":
|
case "move":
|
14
compiler/parser/parser.ts
Normal file
14
compiler/parser/parser.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { File } from "../ast/ast.ts";
|
||||||
|
import { File as CtxFile } from "../ctx.ts";
|
||||||
|
import { todo } from "../util.ts";
|
||||||
|
|
||||||
|
export class Parser {
|
||||||
|
public constructor(
|
||||||
|
private file: CtxFile,
|
||||||
|
private text: string,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public parse(): File {
|
||||||
|
return todo();
|
||||||
|
}
|
||||||
|
}
|
64
compiler/test_diagnostics.ts
Normal file
64
compiler/test_diagnostics.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import { Ctx } from "./ctx.ts";
|
||||||
|
import { prettyPrintReport } from "./diagnostics.ts";
|
||||||
|
|
||||||
|
const ctx = new Ctx();
|
||||||
|
|
||||||
|
const text = `
|
||||||
|
make an error here
|
||||||
|
`;
|
||||||
|
|
||||||
|
const biggerText = `
|
||||||
|
dont make error here
|
||||||
|
not here but start error here
|
||||||
|
and here
|
||||||
|
also here but not here
|
||||||
|
or here
|
||||||
|
`
|
||||||
|
|
||||||
|
const file = ctx.addFile(
|
||||||
|
"root",
|
||||||
|
"path/file.ts",
|
||||||
|
"path/file.ts",
|
||||||
|
undefined,
|
||||||
|
text,
|
||||||
|
);
|
||||||
|
|
||||||
|
const biggerFile = ctx.addFile(
|
||||||
|
"root",
|
||||||
|
"path/file.ts",
|
||||||
|
"path/file.ts",
|
||||||
|
undefined,
|
||||||
|
biggerText,
|
||||||
|
);
|
||||||
|
|
||||||
|
prettyPrintReport(ctx, {
|
||||||
|
file,
|
||||||
|
msg: "an error",
|
||||||
|
severity: "fatal",
|
||||||
|
origin: "compiler",
|
||||||
|
span: {
|
||||||
|
begin: { idx: 5, line: 2, col: 5 },
|
||||||
|
end: { idx: 13, line: 2, col: 13 },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
prettyPrintReport(ctx, {
|
||||||
|
file: biggerFile,
|
||||||
|
msg: "an error",
|
||||||
|
severity: "error",
|
||||||
|
origin: "compiler",
|
||||||
|
span: {
|
||||||
|
begin: { idx: 6, line: 3, col: 14 },
|
||||||
|
end: { idx: 13, line: 5, col: 13 },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
prettyPrintReport(ctx, {
|
||||||
|
file,
|
||||||
|
msg: "an error",
|
||||||
|
severity: "warning",
|
||||||
|
origin: "compiler",
|
||||||
|
pos: {
|
||||||
|
idx: 6, line: 2, col: 8
|
||||||
|
},
|
||||||
|
});
|
9
compiler/util.ts
Normal file
9
compiler/util.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export function todo<T>(msg?: string): T {
|
||||||
|
class NotImplemented extends Error {}
|
||||||
|
throw new NotImplemented(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function exhausted(_: never) {
|
||||||
|
class Unexhausted extends Error {}
|
||||||
|
throw new Unexhausted();
|
||||||
|
}
|
@ -1,4 +0,0 @@
|
|||||||
FROM archlinux
|
|
||||||
RUN pacman -Syu git base-devel deno --noconfirm
|
|
||||||
WORKDIR /workspace
|
|
||||||
ENTRYPOINT ["/bin/bash"]
|
|
@ -1,3 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
docker build -t slige-dev-env dev-env
|
|
||||||
docker run --name dev-env --rm -it --mount type=bind,source="$(pwd)"/,target=/workspace slige-dev-env
|
|
164
examples/calculator.slg
Normal file
164
examples/calculator.slg
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
mod std;
|
||||||
|
|
||||||
|
type_alias Tok: struct {
|
||||||
|
type: string,
|
||||||
|
value: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn lex(text: string) -> [Tok] {
|
||||||
|
let i = 0;
|
||||||
|
let len = std::string_length(text);
|
||||||
|
|
||||||
|
let toks = std::array_new::<Tok>();
|
||||||
|
|
||||||
|
while i < len {
|
||||||
|
if std::string_contains(" \t\n", text[i]) {
|
||||||
|
i += 1;
|
||||||
|
if i >= len {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if text[i] >= '1' and text[i] <= '9' {
|
||||||
|
let value = std::ctos(text[i]);
|
||||||
|
i += 1;
|
||||||
|
while i < len and text[i] >= '0' and text[i] <= '9' {
|
||||||
|
value = std::string_push_char(value, text[i]);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
let tok = struct { type: "int", value: value };
|
||||||
|
std::array_push(toks, tok);
|
||||||
|
} else if text[i] == '0' {
|
||||||
|
i += 1;
|
||||||
|
let tok = struct { type: "int", value: "0" };
|
||||||
|
std::array_push(toks, tok);
|
||||||
|
} else if text[i] == '+' {
|
||||||
|
i += 1;
|
||||||
|
let tok = struct { type: "+", value: "+" };
|
||||||
|
std::array_push(toks, tok);
|
||||||
|
} else if text[i] == '-' {
|
||||||
|
i += 1;
|
||||||
|
let tok = struct { type: "-", value: "-" };
|
||||||
|
std::array_push(toks, tok);
|
||||||
|
} else if text[i] == '*' {
|
||||||
|
i += 1;
|
||||||
|
let tok = struct { type: "*", value: "*" };
|
||||||
|
std::array_push(toks, tok);
|
||||||
|
} else if text[i] == '/' {
|
||||||
|
i += 1;
|
||||||
|
let tok = struct { type: "/", value: "/" };
|
||||||
|
std::array_push(toks, tok);
|
||||||
|
} else if text[i] == '(' {
|
||||||
|
i += 1;
|
||||||
|
let tok = struct { type: "(", value: "(" };
|
||||||
|
std::array_push(toks, tok);
|
||||||
|
} else if text[i] == ')' {
|
||||||
|
i += 1;
|
||||||
|
let tok = struct { type: ")", value: ")" };
|
||||||
|
std::array_push(toks, tok);
|
||||||
|
} else {
|
||||||
|
std::println("error: illegal character '" + std::ctos(text[i]) + "'");
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toks
|
||||||
|
}
|
||||||
|
|
||||||
|
type_alias Calc: struct {
|
||||||
|
toks: [Tok],
|
||||||
|
toks_len: int,
|
||||||
|
i: int,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn calc_new(text: string) -> Calc {
|
||||||
|
let toks = lex(text);
|
||||||
|
let toks_len = std::array_length(toks);
|
||||||
|
struct { toks: toks, toks_len: toks_len, i: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_expr(self: Calc) -> int {
|
||||||
|
calc_add_sub(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_add_sub(self: Calc) -> int {
|
||||||
|
let left = calc_mul_div(self);
|
||||||
|
loop {
|
||||||
|
if self.toks[self.i].type == "+" {
|
||||||
|
self.i += 1;
|
||||||
|
let right = calc_mul_div(self);
|
||||||
|
left = left + right;
|
||||||
|
} else if self.toks[self.i].type == "-" {
|
||||||
|
self.i += 1;
|
||||||
|
let right = calc_mul_div(self);
|
||||||
|
left = left - right;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
left
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_mul_div(self: Calc) -> int {
|
||||||
|
let left = calc_unary(self);
|
||||||
|
loop {
|
||||||
|
if self.toks[self.i].type == "*" {
|
||||||
|
self.i += 1;
|
||||||
|
let right = calc_unary(self);
|
||||||
|
left = left * right;
|
||||||
|
} else if self.toks[self.i].type == "/" {
|
||||||
|
self.i += 1;
|
||||||
|
let right = calc_unary(self);
|
||||||
|
left = left / right;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
left
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_unary(self: Calc) -> int {
|
||||||
|
if self.toks[self.i].type == "-" {
|
||||||
|
self.i += 1;
|
||||||
|
let subject = calc_unary(self);
|
||||||
|
-subject
|
||||||
|
} else {
|
||||||
|
calc_operand(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_operand(self: Calc) -> int {
|
||||||
|
if self.i >= self.toks_len {
|
||||||
|
std::println("error: expected expr");
|
||||||
|
0
|
||||||
|
} else if self.toks[self.i].type == "int" {
|
||||||
|
let val = std::stoi(self.toks[self.i].value);
|
||||||
|
self.i += 1;
|
||||||
|
val
|
||||||
|
} else if self.toks[self.i].type == "(" {
|
||||||
|
self.i += 1;
|
||||||
|
let val = calc_expr(self);
|
||||||
|
if self.i >= self.toks_len or self.toks[self.i].type != ")" {
|
||||||
|
std::println("error: missing ')'");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
self.i += 1;
|
||||||
|
val
|
||||||
|
} else {
|
||||||
|
std::println("error: expected expr");
|
||||||
|
self.i += 1;
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
loop {
|
||||||
|
let line = std::input("> ");
|
||||||
|
if line == "exit" {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let calc = calc_new(line);
|
||||||
|
let val = calc_expr(calc);
|
||||||
|
std::println(line);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user