mirror of
https://git.sfja.dk/Mikkel/slige.git
synced 2025-01-18 23:06:32 +00:00
243 lines
9.5 KiB
TypeScript
243 lines
9.5 KiB
TypeScript
import { AstCreator, Expr, ExprKind, Stmt, StmtKind } from "../ast.ts";
|
|
import { AstVisitor, visitExpr, VisitRes, visitStmts } from "../ast_visitor.ts";
|
|
import { Pos } from "../token.ts";
|
|
|
|
export class SpecialLoopDesugarer implements AstVisitor {
|
|
public constructor(
|
|
private astCreator: AstCreator,
|
|
) {}
|
|
|
|
public desugar(stmts: Stmt[]) {
|
|
visitStmts(stmts, this);
|
|
}
|
|
|
|
visitWhileExpr(expr: Expr): VisitRes {
|
|
if (expr.kind.type !== "while") {
|
|
throw new Error();
|
|
}
|
|
const npos: Pos = { index: 0, line: 1, col: 1 };
|
|
const Expr = (kind: ExprKind, pos = npos) =>
|
|
this.astCreator.expr(kind, pos);
|
|
const Stmt = (kind: StmtKind, pos = npos) =>
|
|
this.astCreator.stmt(kind, pos);
|
|
|
|
expr.kind = {
|
|
type: "loop",
|
|
body: Expr({
|
|
type: "block",
|
|
stmts: [
|
|
Stmt({
|
|
type: "expr",
|
|
expr: Expr({
|
|
type: "if",
|
|
cond: Expr({
|
|
type: "unary",
|
|
unaryType: "not",
|
|
subject: expr.kind.cond,
|
|
}),
|
|
truthy: Expr({
|
|
type: "block",
|
|
stmts: [
|
|
Stmt({ type: "break" }),
|
|
],
|
|
}),
|
|
}),
|
|
}),
|
|
Stmt({
|
|
type: "expr",
|
|
expr: expr.kind.body,
|
|
}),
|
|
],
|
|
}),
|
|
};
|
|
|
|
visitExpr(expr, this);
|
|
return "stop";
|
|
}
|
|
|
|
visitForInExpr(expr: Expr): VisitRes {
|
|
if (expr.kind.type !== "for_in") {
|
|
throw new Error();
|
|
}
|
|
const npos: Pos = { index: 0, line: 1, col: 1 };
|
|
const Expr = (kind: ExprKind, pos = npos) =>
|
|
this.astCreator.expr(kind, pos);
|
|
const Stmt = (kind: StmtKind, pos = npos) =>
|
|
this.astCreator.stmt(kind, pos);
|
|
|
|
expr.kind = {
|
|
type: "block",
|
|
stmts: [
|
|
Stmt({
|
|
type: "let",
|
|
param: { ident: "::values", pos: npos },
|
|
value: expr.kind.value,
|
|
}),
|
|
Stmt({
|
|
type: "let",
|
|
param: { ident: "::length", pos: npos },
|
|
value: Expr({
|
|
type: "call",
|
|
subject: Expr({
|
|
type: "ident",
|
|
value: "int_array_length",
|
|
}),
|
|
args: [
|
|
Expr({
|
|
type: "ident",
|
|
value: "::values",
|
|
}),
|
|
],
|
|
}),
|
|
}),
|
|
Stmt({
|
|
type: "let",
|
|
param: { ident: "::index", pos: npos },
|
|
value: Expr({ type: "int", value: 0 }),
|
|
}, expr.pos),
|
|
Stmt({
|
|
type: "expr",
|
|
expr: Expr({
|
|
type: "loop",
|
|
body: Expr({
|
|
type: "block",
|
|
stmts: [
|
|
Stmt({
|
|
type: "expr",
|
|
expr: Expr({
|
|
type: "if",
|
|
cond: Expr({
|
|
type: "unary",
|
|
unaryType: "not",
|
|
subject: Expr({
|
|
type: "binary",
|
|
binaryType: "<",
|
|
left: Expr({
|
|
type: "ident",
|
|
value: "::index",
|
|
}),
|
|
right: Expr({
|
|
type: "ident",
|
|
value: "::length",
|
|
}),
|
|
}),
|
|
}),
|
|
truthy: Expr({
|
|
type: "block",
|
|
stmts: [
|
|
Stmt({
|
|
type: "break",
|
|
}),
|
|
],
|
|
}),
|
|
}),
|
|
}),
|
|
Stmt({
|
|
type: "let",
|
|
param: expr.kind.param,
|
|
value: Expr({
|
|
type: "index",
|
|
subject: Expr({
|
|
type: "ident",
|
|
value: "::values",
|
|
}),
|
|
value: Expr({
|
|
type: "ident",
|
|
value: "::index",
|
|
}),
|
|
}),
|
|
}, expr.pos),
|
|
Stmt({
|
|
type: "expr",
|
|
expr: expr.kind.body,
|
|
}),
|
|
Stmt({
|
|
type: "assign",
|
|
assignType: "+=",
|
|
subject: Expr({
|
|
type: "ident",
|
|
value: "::index",
|
|
}),
|
|
value: Expr({
|
|
type: "int",
|
|
value: 1,
|
|
}),
|
|
}),
|
|
],
|
|
}),
|
|
}),
|
|
}),
|
|
],
|
|
};
|
|
|
|
visitExpr(expr, this);
|
|
return "stop";
|
|
}
|
|
|
|
visitForExpr(expr: Expr): VisitRes {
|
|
if (expr.kind.type !== "for") {
|
|
throw new Error();
|
|
}
|
|
const Expr = (
|
|
kind: ExprKind,
|
|
pos: Pos = { index: 0, line: 1, col: 1 },
|
|
) => this.astCreator.expr(kind, pos);
|
|
const Stmt = (
|
|
kind: StmtKind,
|
|
pos: Pos = { index: 0, line: 1, col: 1 },
|
|
) => this.astCreator.stmt(kind, pos);
|
|
|
|
expr.kind = {
|
|
type: "block",
|
|
stmts: [
|
|
...[expr.kind.decl]
|
|
.filter((v) => v !== undefined),
|
|
Stmt({
|
|
type: "expr",
|
|
expr: Expr({
|
|
type: "loop",
|
|
body: Expr({
|
|
type: "block",
|
|
stmts: [
|
|
...[expr.kind.cond]
|
|
.filter((v) => v !== undefined)
|
|
.map(
|
|
(cond) =>
|
|
Stmt({
|
|
type: "expr",
|
|
expr: Expr({
|
|
type: "if",
|
|
cond: Expr({
|
|
type: "unary",
|
|
unaryType: "not",
|
|
subject: cond,
|
|
}),
|
|
truthy: Expr({
|
|
type: "block",
|
|
stmts: [
|
|
Stmt({
|
|
type: "break",
|
|
}),
|
|
],
|
|
}),
|
|
}),
|
|
}, cond.pos),
|
|
),
|
|
Stmt({
|
|
type: "expr",
|
|
expr: expr.kind.body,
|
|
}),
|
|
...[expr.kind.incr]
|
|
.filter((v) => v !== undefined),
|
|
],
|
|
}),
|
|
}),
|
|
}),
|
|
],
|
|
};
|
|
|
|
visitExpr(expr, this);
|
|
return "stop";
|
|
}
|
|
}
|