Skip to content

Commit 0aeafa4

Browse files
committed
try to use recursive-lambda instead of defined-lambda
1 parent 7e8e02a commit 0aeafa4

File tree

6 files changed

+76
-25
lines changed

6 files changed

+76
-25
lines changed

TODO.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
use `RecursiveLambda` instead of `DefinedLambda`
2-
`markAllRecursiveFunctions`
3-
`sameInCtx` -- use head on `DelayedApply`, but only for `RecursiveLambda`
4-
51
# lazy evaluation
62

73
we still can bring back lazy evaluation by lazy + box,
@@ -18,6 +14,11 @@ maybe only way to understand a reduction strategy
1814
is to see a lot of reduction paths.
1915
we need to see why current strategy can handle `Y`.
2016

17+
# recursive lambda
18+
19+
use `RecursiveLambda` instead of `DefinedLambda` -- i tried, but it is to slow
20+
`sameInCtx` -- use head on `DelayedApply`, but only for `RecursiveLambda`
21+
2122
# later
2223

2324
`formatValue` -- use `let` to print closure
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[load] I find undefined name: g
22
defining: f
3-
body: (lambda (x) (g x))
3+
to: : (lambda (x) (g x))

src/lang/exp/expIndirectFreeNames.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import assert from "node:assert"
2+
import { setPop } from "../../utils/set/setPop.ts"
3+
import type { Mod } from "../mod/index.ts"
4+
import { modOwnDefs } from "../mod/index.ts"
5+
import type { Exp } from "./Exp.ts"
6+
import { expFreeNames } from "./expFreeNames.ts"
7+
8+
export function expIndirectFreeNames(mod: Mod, exp: Exp): Set<string> {
9+
const ownDefs = modOwnDefs(mod)
10+
const remainingNames = expFreeNames(new Set(), exp)
11+
const collectedNames = new Set<string>()
12+
13+
while (remainingNames.size > 0) {
14+
const name = setPop(remainingNames)
15+
if (collectedNames.has(name)) continue
16+
17+
collectedNames.add(name)
18+
19+
const def = ownDefs.get(name)
20+
if (def === undefined) continue
21+
22+
assert(def.freeNames)
23+
for (const freeName of def.freeNames) {
24+
if (!collectedNames.has(freeName)) {
25+
remainingNames.add(freeName)
26+
}
27+
}
28+
}
29+
30+
return collectedNames
31+
}

src/lang/exp/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from "./Bind.ts"
22
export * from "./Exp.ts"
33
export * from "./expFreeNames.ts"
4+
export * from "./expIndirectFreeNames.ts"

src/lang/load/load.ts

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
import { ParsingError } from "@xieyuheng/x-data.js"
2+
import dedent from "dedent"
3+
import assert from "node:assert"
24
import fs from "node:fs"
35
import { expFreeNames } from "../exp/expFreeNames.ts"
6+
import { expIndirectFreeNames } from "../exp/index.ts"
47
import { formatExp } from "../format/formatExp.ts"
5-
import {
6-
createMod,
7-
modFind,
8-
modOwnDefs,
9-
type Def,
10-
type Mod,
11-
} from "../mod/index.ts"
8+
import { createMod, modFind, modOwnDefs, type Mod } from "../mod/index.ts"
129
import { parseStmts } from "../parse/index.ts"
1310
import { globalLoadedMods } from "./globalLoadedMods.ts"
1411
import { handleDefine } from "./handleDefine.ts"
@@ -42,24 +39,34 @@ async function run(mod: Mod): Promise<void> {
4239
for (const stmt of mod.stmts) await handleDefine(mod, stmt)
4340
for (const stmt of mod.stmts) await handleImport(mod, stmt)
4441

45-
for (const def of modOwnDefs(mod).values()) postprocessDef(mod, def)
42+
postprocess(mod)
4643

4744
for (const stmt of mod.stmts) await handleEffect(mod, stmt)
4845

4946
mod.isFinished = true
5047
}
5148

52-
function postprocessDef(mod: Mod, def: Def): void {
53-
def.freeNames = expFreeNames(new Set(), def.exp)
54-
for (const name of def.freeNames) {
55-
if (modFind(mod, name) === undefined) {
56-
throw new Error(
57-
[
58-
`[load] I find undefined name: ${name}`,
59-
` defining: ${def.name}`,
60-
` body: ${formatExp(def.exp)}`,
61-
].join("\n"),
62-
)
49+
function postprocess(mod: Mod): void {
50+
for (const def of modOwnDefs(mod).values()) {
51+
def.freeNames = expFreeNames(new Set(), def.exp)
52+
}
53+
54+
for (const def of modOwnDefs(mod).values()) {
55+
assert(def.freeNames)
56+
for (const name of def.freeNames) {
57+
if (!modFind(mod, name)) {
58+
throw new Error(dedent`
59+
[load] I find undefined name: ${name}
60+
defining: ${def.name}
61+
to: : ${formatExp(def.exp)}
62+
`)
63+
}
64+
}
65+
}
66+
67+
for (const def of modOwnDefs(mod).values()) {
68+
if (expIndirectFreeNames(mod, def.exp).has(def.name)) {
69+
def.isRecursive = true
6370
}
6471
}
6572
}

src/lang/mod/Mod.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export type Def = {
1010
exp: Exp
1111
freeNames?: Set<string>
1212
value?: Value
13+
isRecursive?: boolean
1314
}
1415

1516
export type Mod = {
@@ -42,7 +43,17 @@ export function modFindValue(mod: Mod, name: string): Value | undefined {
4243
if (def.value) return def.value
4344

4445
const value = evaluate(def.mod, emptyEnv(), def.exp)
45-
if (value.kind === "Lambda" && value.definedName === undefined) {
46+
47+
// TODO Uncomment the following,
48+
// will only blaze recursive function,
49+
// but it will be to slow for `equalInCtx`.
50+
// I do not fully understand it yet.
51+
52+
if (
53+
// def.isRecursive &&
54+
value.kind === "Lambda" &&
55+
value.definedName === undefined
56+
) {
4657
value.definedName = def.name
4758
}
4859

0 commit comments

Comments
 (0)