bidir
This commit is contained in:
parent
dbad9eb094
commit
1b728c62ab
4 changed files with 175 additions and 8 deletions
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
@ -9,6 +9,9 @@
|
||||||
"typescript": "^5.0.0"
|
"typescript": "^5.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash.isequal": "^4.5.0"
|
"@lezer/generator": "^1.7.1",
|
||||||
|
"@lezer/lr": "^1.4.2",
|
||||||
|
"lodash.isequal": "^4.5.0",
|
||||||
|
"peggy": "^4.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
import { isEqual } from "lodash";
|
import { isEqual } from "lodash";
|
||||||
|
import { generate } from "peggy";
|
||||||
|
import { buildParser, buildParserFile } from "@lezer/generator";
|
||||||
|
import { Tree, TreeCursor } from "@lezer/common";
|
||||||
|
|
||||||
export type ty = { kind: "arr"; left: ty; right: ty } | { kind: "unit" };
|
export type ty = { kind: "arr"; left: ty; right: ty } | { kind: "unit" };
|
||||||
|
|
||||||
|
@ -116,3 +119,131 @@ export function infer(t: term2): ty {
|
||||||
|
|
||||||
return infer(t, []);
|
return infer(t, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const parserSource = (top: string) => `
|
||||||
|
@top result { ${top} }
|
||||||
|
|
||||||
|
@precedence {
|
||||||
|
arrow @right,
|
||||||
|
app @left
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr {
|
||||||
|
EUnit { "()" }
|
||||||
|
| Var { Ident }
|
||||||
|
| Lam { lam "(" Ident ":" Ty ")" "." Expr }
|
||||||
|
| App { Expr !app Expr }
|
||||||
|
| paren { "(" Expr ")" }
|
||||||
|
}
|
||||||
|
|
||||||
|
Ty {
|
||||||
|
TUnit { "()" }
|
||||||
|
| Arrow { Ty !arrow "->" Ty }
|
||||||
|
| paren { "(" Ty ")" }
|
||||||
|
}
|
||||||
|
|
||||||
|
@skip { space }
|
||||||
|
|
||||||
|
@tokens {
|
||||||
|
space { @whitespace+ }
|
||||||
|
lam { "lam" }
|
||||||
|
Ident { $[A-Za-z_]+ }
|
||||||
|
@precedence { lam, Ident }
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const buildApp = (s: string, c: TreeCursor): term => {
|
||||||
|
c.firstChild();
|
||||||
|
const left = buildExpr(s, c);
|
||||||
|
c.nextSibling();
|
||||||
|
const right = buildExpr(s, c);
|
||||||
|
c.parent();
|
||||||
|
return { kind: "app", left, right };
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildLam = (s: string, c: TreeCursor): term => {
|
||||||
|
c.firstChild();
|
||||||
|
const name = s.slice(c.from, c.to);
|
||||||
|
c.nextSibling();
|
||||||
|
const ty = buildTy(s, c);
|
||||||
|
c.nextSibling();
|
||||||
|
const body = buildExpr(s, c);
|
||||||
|
c.parent();
|
||||||
|
return { kind: "abs", arg: name, ty, body };
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildVar = (s: string, c: TreeCursor): term => {
|
||||||
|
c.firstChild();
|
||||||
|
const name = s.slice(c.from, c.to);
|
||||||
|
c.parent();
|
||||||
|
return { kind: "var", name };
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildArrow = (s: string, c: TreeCursor): ty => {
|
||||||
|
c.firstChild();
|
||||||
|
const left = buildTy(s, c);
|
||||||
|
c.nextSibling();
|
||||||
|
const right = buildTy(s, c);
|
||||||
|
c.parent();
|
||||||
|
return { kind: "arr", left, right };
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildTy = (s: string, c: TreeCursor): ty => {
|
||||||
|
let result: ty;
|
||||||
|
c.firstChild();
|
||||||
|
if (c.name === "TUnit") result = { kind: "unit" };
|
||||||
|
else if (c.name === "Arrow") result = buildArrow(s, c);
|
||||||
|
else if (c.name === "Ty") result = buildTy(s, c);
|
||||||
|
else throw new Error("unknown ty " + c.name);
|
||||||
|
c.parent();
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildExpr = (s: string, c: TreeCursor): term => {
|
||||||
|
let result;
|
||||||
|
c.firstChild();
|
||||||
|
switch (c.name) {
|
||||||
|
case "App":
|
||||||
|
result = buildApp(s, c);
|
||||||
|
break;
|
||||||
|
case "Lam":
|
||||||
|
result = buildLam(s, c);
|
||||||
|
break;
|
||||||
|
case "Var":
|
||||||
|
result = buildVar(s, c);
|
||||||
|
break;
|
||||||
|
case "Expr":
|
||||||
|
result = buildExpr(s, c);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(
|
||||||
|
`unknown ${c.name} (${c.from}:${c.to} = "${s.slice(c.from, c.to)}")`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
c.parent();
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const parseExpr = (s: string) => {
|
||||||
|
const tree = buildParser(parserSource("Expr"), {
|
||||||
|
typeScript: true,
|
||||||
|
warn: false,
|
||||||
|
}).parse(s);
|
||||||
|
|
||||||
|
const cursor = tree.cursor();
|
||||||
|
cursor.next();
|
||||||
|
return buildExpr(s, cursor);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const parseTy = (s: string) => {
|
||||||
|
const tree = buildParser(parserSource("Ty"), {
|
||||||
|
typeScript: true,
|
||||||
|
warn: false,
|
||||||
|
}).parse(s);
|
||||||
|
|
||||||
|
const cursor = tree.cursor();
|
||||||
|
cursor.next();
|
||||||
|
return buildTy(s, cursor);
|
||||||
|
};
|
||||||
|
// export const parseExpr = (s: string) => parser.parse(s, { startRule: "expr" });
|
||||||
|
// export const parseTy = (s: string) => parser.parse(s, { startRule: "ty" });
|
||||||
|
|
|
@ -7,14 +7,47 @@ import {
|
||||||
arr,
|
arr,
|
||||||
convertToDebruijn,
|
convertToDebruijn,
|
||||||
infer,
|
infer,
|
||||||
|
parseExpr,
|
||||||
|
parseTy,
|
||||||
unitTy,
|
unitTy,
|
||||||
var1,
|
var1,
|
||||||
var2,
|
var2,
|
||||||
} from ".";
|
} from ".";
|
||||||
|
|
||||||
|
const kCombinator = `lam (x : ()) . lam (y : ()) . x`;
|
||||||
|
const kCombinatorTy = `() -> (() -> ())`;
|
||||||
|
|
||||||
|
const sCombinator = `lam (x : () -> (() -> ())) . lam (y : () -> ()) . lam (z : ()) . (x z) (y z)`;
|
||||||
|
|
||||||
|
describe("parser", () => {
|
||||||
|
test("K combinator", () => {
|
||||||
|
const parsed = parseExpr(kCombinator);
|
||||||
|
const expr = abs1("x", unitTy(), abs1("y", unitTy(), var1("x")));
|
||||||
|
expect(parsed).toEqual(expr);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("S combinator", () => {
|
||||||
|
const parsed = parseExpr(sCombinator);
|
||||||
|
const expr = abs1(
|
||||||
|
"x",
|
||||||
|
arr(unitTy(), arr(unitTy(), unitTy())),
|
||||||
|
abs1(
|
||||||
|
"y",
|
||||||
|
arr(unitTy(), unitTy()),
|
||||||
|
abs1(
|
||||||
|
"z",
|
||||||
|
unitTy(),
|
||||||
|
app1(app1(var1("x"), var1("z")), app1(var1("y"), var1("z"))),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(parsed).toEqual(expr);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("convertToDeBruijn", () => {
|
describe("convertToDeBruijn", () => {
|
||||||
test("K combinator", () => {
|
test("K combinator", () => {
|
||||||
const expr = abs1("x", unitTy(), abs1("y", unitTy(), var1("x")));
|
const expr = parseExpr(kCombinator);
|
||||||
const expr2 = abs2(unitTy(), abs2(unitTy(), var2(1)));
|
const expr2 = abs2(unitTy(), abs2(unitTy(), var2(1)));
|
||||||
|
|
||||||
expect(convertToDebruijn(expr)).toEqual(expr2);
|
expect(convertToDebruijn(expr)).toEqual(expr2);
|
||||||
|
@ -48,17 +81,17 @@ describe("convertToDeBruijn", () => {
|
||||||
|
|
||||||
describe("infer", () => {
|
describe("infer", () => {
|
||||||
test("K combinator", () => {
|
test("K combinator", () => {
|
||||||
const expr = abs1("x", unitTy(), abs1("y", unitTy(), var1("x")));
|
const expr = parseExpr(kCombinator);
|
||||||
const expr2 = convertToDebruijn(expr);
|
const expr2 = convertToDebruijn(expr);
|
||||||
const ty = arr(unitTy(), arr(unitTy(), unitTy()));
|
const ty = parseTy(kCombinatorTy);
|
||||||
|
|
||||||
expect(infer(expr2)).toEqual(ty);
|
expect(infer(expr2)).toEqual(ty);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("K combinator", () => {
|
test("K combinator negative", () => {
|
||||||
const expr = abs1("x", unitTy(), abs1("y", unitTy(), var1("x")));
|
const expr = parseExpr(kCombinator);
|
||||||
const expr2 = convertToDebruijn(expr);
|
const expr2 = convertToDebruijn(expr);
|
||||||
const ty = arr(unitTy(), arr(unitTy(), arr(unitTy(), unitTy())));
|
const ty = parseTy(`() -> (() -> (() -> ()))`);
|
||||||
|
|
||||||
expect(infer(expr2)).not.toEqual(ty);
|
expect(infer(expr2)).not.toEqual(ty);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue