Compare commits

..

10 Commits

Author SHA1 Message Date
sfja
7ca864d5a9 define more ast stuff 2025-01-24 00:46:03 +01:00
SimonFJ20
ab5a830f66 add ast stuff 2025-01-23 14:19:40 +01:00
6a5fce18fe pretty report printing 2025-01-23 13:58:07 +01:00
SimonFJ20
7766c88512 test diagnostics 2025-01-23 10:28:48 +01:00
SimonFJ20
cd087392b9 rename diags to reports 2025-01-23 10:18:33 +01:00
sfja
93dd4c32c8 add stuff 2025-01-22 22:40:29 +01:00
SimonFJ20
e8cfd059cc new main 2025-01-22 14:09:27 +01:00
SimonFJ20
00298d6d83 move compiler/ to compiler/old/ 2025-01-22 13:53:05 +01:00
SimonFJ20
09c3c3277b delete dev-env 2025-01-22 12:47:42 +01:00
sfja
d83ade33d2 i forgor 2025-01-20 02:17:05 +01:00
43 changed files with 1080 additions and 59 deletions

View File

@ -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
View 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
View File

@ -0,0 +1,2 @@
export * from "./ast.ts";
export * from "./visitor.ts";

288
compiler/ast/visitor.ts Normal file
View 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
View 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
View 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,
);
}
}

View File

@ -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
View 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));

View File

@ -170,9 +170,6 @@ class LocalChecker {
}
private markSrc(src: RValue) {
if (src.type === "local") {
throw new Error("should be 'copy' or 'move'");
}
if (
(src.type !== "copy" && src.type !== "move") ||
src.id !== this.local.id

View File

@ -73,7 +73,7 @@ class EliminateUnusedLocalsFnPass {
}
private markUsed(local: RValue) {
if (local.type !== "local") {
if (local.type !== "copy" && local.type !== "move") {
return;
}
this.locals = this.locals.filter((lid) => lid !== local.id);

View File

@ -23,7 +23,7 @@ class LocalExpliciter {
}
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 this.copyable

View File

@ -509,5 +509,5 @@ class FnAstLowerer {
}
function local(id: LocalId): RValue {
return { type: "local", id };
return { type: "move", id };
}

View File

@ -45,7 +45,6 @@ export type OpKind =
| { type: "ref_mut"; dst: L; src: L }
| { type: "ptr"; dst: L; src: L }
| { type: "ptr_mut"; dst: L; src: L }
| { type: "drop"; src: L }
| { type: "deref"; dst: L; src: R }
| { type: "assign_deref"; subject: R; src: R }
| { type: "field"; dst: L; subject: R; ident: string }
@ -67,7 +66,6 @@ export type TerKind =
export type RValue =
| { type: "error" }
| { type: "local"; id: BlockId }
| { type: "copy"; id: BlockId }
| { type: "move"; id: BlockId }
| { type: "null" }
@ -123,7 +121,6 @@ export function replaceBlockSrcs(
case "ref_mut":
case "ptr":
case "ptr_mut":
case "drop":
break;
case "deref":
ok.src = replace(ok.src);
@ -190,7 +187,6 @@ export function visitBlockSrcs(
case "ref_mut":
case "ptr":
case "ptr_mut":
case "drop":
break;
case "deref":
visitor(ok.src, op, i);
@ -307,9 +303,6 @@ export function printMir(mir: Mir) {
case "ptr_mut":
l(`_${k.dst} = *mut _${k.src};`);
break;
case "drop":
l(`drop _${k.src};`);
break;
case "deref":
l(`_${k.dst} = *${r(k.src)};`);
break;
@ -372,8 +365,6 @@ export function rvalueToString(rvalue: RValue): string {
switch (rvalue.type) {
case "error":
return `<error>`;
case "local":
return `_${rvalue.id}`;
case "copy":
return `copy _${rvalue.id}`;
case "move":

14
compiler/parser/parser.ts Normal file
View 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();
}
}

View 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
View 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();
}

View File

@ -1,4 +0,0 @@
FROM archlinux
RUN pacman -Syu git base-devel deno --noconfirm
WORKDIR /workspace
ENTRYPOINT ["/bin/bash"]

View File

@ -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
View 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);
}
}