Compare commits
No commits in common. "7ca864d5a92d39d84b7e99da7c738b2ae3c78ccf" and "9cb145d97ed0e7ecfd8fcd988b915a74f7de1ed5" have entirely different histories.
7ca864d5a9
...
9cb145d97e
34
compiler/architecture.txt
Normal file
34
compiler/architecture.txt
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,230 +0,0 @@
|
|||||||
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;
|
|
||||||
};
|
|
@ -1,2 +0,0 @@
|
|||||||
export * from "./ast.ts";
|
|
||||||
export * from "./visitor.ts";
|
|
@ -1,288 +0,0 @@
|
|||||||
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
107
compiler/ctx.ts
@ -1,107 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
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,101 +1,5 @@
|
|||||||
import * as path from "jsr:@std/path";
|
import { Compiler } from "./compiler.ts";
|
||||||
import { Parser } from "./parser/parser.ts";
|
|
||||||
import * as ast from "./ast/mod.ts";
|
|
||||||
import { Ctx } from "./ctx.ts";
|
|
||||||
import { File } from "./ctx.ts";
|
|
||||||
|
|
||||||
export type Pack = {
|
const { program } = await new Compiler(Deno.args[0]).compile();
|
||||||
rootMod: Mod;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Mod = null;
|
await Deno.writeTextFile("out.slgbc", JSON.stringify(program));
|
||||||
|
|
||||||
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";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -170,6 +170,9 @@ 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 !== "copy" && local.type !== "move") {
|
if (local.type !== "local") {
|
||||||
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 !== "move" || src.id !== this.local.id) {
|
if (src.type !== "local" || 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: "move", id };
|
return { type: "local", id };
|
||||||
}
|
}
|
@ -45,6 +45,7 @@ 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 }
|
||||||
@ -66,6 +67,7 @@ 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" }
|
||||||
@ -121,6 +123,7 @@ 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);
|
||||||
@ -187,6 +190,7 @@ 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);
|
||||||
@ -303,6 +307,9 @@ 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;
|
||||||
@ -365,6 +372,8 @@ 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":
|
@ -1,5 +0,0 @@
|
|||||||
import { Compiler } from "./compiler.ts";
|
|
||||||
|
|
||||||
const { program } = await new Compiler(Deno.args[0]).compile();
|
|
||||||
|
|
||||||
await Deno.writeTextFile("out.slgbc", JSON.stringify(program));
|
|
@ -1,14 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
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
|
|
||||||
},
|
|
||||||
});
|
|
@ -1,9 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
4
dev-env/Dockerfile
Normal file
4
dev-env/Dockerfile
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
FROM archlinux
|
||||||
|
RUN pacman -Syu git base-devel deno --noconfirm
|
||||||
|
WORKDIR /workspace
|
||||||
|
ENTRYPOINT ["/bin/bash"]
|
3
dev-env/run.sh
Normal file
3
dev-env/run.sh
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#!/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
|
@ -1,164 +0,0 @@
|
|||||||
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