nandi/clonimpublic Fork
⑂ 4ae0db9
Commits
⬇ Clone ▾
git clone https://git.rickub.com/nandi/clonim.git
git clone ssh://git@rickub.com/nandi/clonim.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

Register only reachable core families in generated programs

nandithebull committed 2026-09-22T14:48:34-07:00 Browse files
4ae0db9 parent: 1119f58
modified src/app_runtime.nim +14 -0
@@ -10,6 +10,20 @@ proc mkFn*(name: string,
1010 {.push cdecl.}
1111 proc initClonimRuntime*() {.importc: "ClonimRuntimeNimMain".}
1212 proc registerCore*() {.importc: "clonim_register_core".}
13+proc registerCoreSelected*(names: openArray[string])
14+ {.importc: "clonim_register_core_selected".}
15+proc registerCoreArithmetic*(names: openArray[string])
16+ {.importc: "clonim_register_core_arithmetic".}
17+proc registerCorePredicates*(names: openArray[string])
18+ {.importc: "clonim_register_core_predicates".}
19+proc registerCoreStringsIo*(names: openArray[string])
20+ {.importc: "clonim_register_core_strings_io".}
21+proc registerCoreCollections*(names: openArray[string])
22+ {.importc: "clonim_register_core_collections".}
23+proc registerCoreHigherOrder*(names: openArray[string])
24+ {.importc: "clonim_register_core_higher_order".}
25+proc registerCoreStateHost*(names: openArray[string])
26+ {.importc: "clonim_register_core_state_host".}
1327 proc registerNamespaceCore*() {.importc: "clonim_register_namespace_core".}
1428
1529 proc err*(msg: string) {.noreturn, importc: "clonim_err".}
@@ -10,6 +10,20 @@ proc mkFn*(name: string,
10 {.push cdecl.}10 {.push cdecl.}
11 proc initClonimRuntime*() {.importc: "ClonimRuntimeNimMain".}11 proc initClonimRuntime*() {.importc: "ClonimRuntimeNimMain".}
12 proc registerCore*() {.importc: "clonim_register_core".}12 proc registerCore*() {.importc: "clonim_register_core".}
13+proc registerCoreSelected*(names: openArray[string])
14+ {.importc: "clonim_register_core_selected".}
15+proc registerCoreArithmetic*(names: openArray[string])
16+ {.importc: "clonim_register_core_arithmetic".}
17+proc registerCorePredicates*(names: openArray[string])
18+ {.importc: "clonim_register_core_predicates".}
19+proc registerCoreStringsIo*(names: openArray[string])
20+ {.importc: "clonim_register_core_strings_io".}
21+proc registerCoreCollections*(names: openArray[string])
22+ {.importc: "clonim_register_core_collections".}
23+proc registerCoreHigherOrder*(names: openArray[string])
24+ {.importc: "clonim_register_core_higher_order".}
25+proc registerCoreStateHost*(names: openArray[string])
26+ {.importc: "clonim_register_core_state_host".}
13 proc registerNamespaceCore*() {.importc: "clonim_register_namespace_core".}27 proc registerNamespaceCore*() {.importc: "clonim_register_namespace_core".}
14 28
15 proc err*(msg: string) {.noreturn, importc: "clonim_err".}29 proc err*(msg: string) {.noreturn, importc: "clonim_err".}
modified src/clonim.nim +2 -0
@@ -94,6 +94,7 @@ proc runtimeLib(release: bool): string =
9494 "--nimcache:" & installed.parentDir /
9595 (if release: "nimcache-release" else: "nimcache-debug"),
9696 "-o:" & installed]
97+ args.add "--passC:-ffunction-sections"
9798 # The HTTP primitive speaks TLS; without this it can only reach http://.
9899 args.add "-d:ssl"
99100 if release: args.add "-d:release"
@@ -212,6 +213,7 @@ proc main() =
212213 nimCmd.add "--passL:-Wl,--allow-multiple-definition"
213214 nimCmd.add "--passL:" & rtLib
214215 nimCmd.add "--passL:-lm"
216+ nimCmd.add "--passL:-Wl,--gc-sections"
215217 nimCmd.add "-d:ssl"
216218 if release:
217219 # `-d:release` disables Nim runtime checks but keeps DWARF and symbol
@@ -94,6 +94,7 @@ proc runtimeLib(release: bool): string =
94 "--nimcache:" & installed.parentDir /94 "--nimcache:" & installed.parentDir /
95 (if release: "nimcache-release" else: "nimcache-debug"),95 (if release: "nimcache-release" else: "nimcache-debug"),
96 "-o:" & installed]96 "-o:" & installed]
97+ args.add "--passC:-ffunction-sections"
97 # The HTTP primitive speaks TLS; without this it can only reach http://.98 # The HTTP primitive speaks TLS; without this it can only reach http://.
98 args.add "-d:ssl"99 args.add "-d:ssl"
99 if release: args.add "-d:release"100 if release: args.add "-d:release"
@@ -212,6 +213,7 @@ proc main() =
212 nimCmd.add "--passL:-Wl,--allow-multiple-definition"213 nimCmd.add "--passL:-Wl,--allow-multiple-definition"
213 nimCmd.add "--passL:" & rtLib214 nimCmd.add "--passL:" & rtLib
214 nimCmd.add "--passL:-lm"215 nimCmd.add "--passL:-lm"
216+ nimCmd.add "--passL:-Wl,--gc-sections"
215 nimCmd.add "-d:ssl"217 nimCmd.add "-d:ssl"
216 if release:218 if release:
217 # `-d:release` disables Nim runtime checks but keeps DWARF and symbol219 # `-d:release` disables Nim runtime checks but keeps DWARF and symbol
modified src/compiler.nim +27 -6
@@ -3,8 +3,8 @@
33 ## Shape borrowed from jank: read to data, analyze into a small set of core
44 ## special forms (everything else is expanded), then emit host-language code
55 ## and let the host compiler do register allocation, inlining and codegen.
6-import std/[tables, strutils, sets]
7-import runtime, namespaces
6+import std/[tables, strutils, sets, algorithm]
7+import runtime, namespaces, core
88
99 type
1010 ## A fn whose arity is known at the call site, so it can be reached as a
@@ -569,7 +569,7 @@ proc bindPattern(target: Value, v: string, lenv: Env, c: Ctx) =
569569 j += 2
570570 continue
571571 let id = c.gensym("lnth")
572- c.line("var " & id & ": Value = call(getVar(\"clojure.core/nth\"), [" & v & ", mkInt(" &
572+ c.line("var " & id & ": Value = call(cellGet(" & c.cellFor("clojure.core/nth") & "), [" & v & ", mkInt(" &
573573 $idx & "), NilV])")
574574 bindPattern(p, id, lenv, c)
575575 inc idx; inc j
@@ -584,7 +584,7 @@ proc bindPattern(target: Value, v: string, lenv: Env, c: Ctx) =
584584 if defaults.kind == kMap:
585585 for (dk, dv) in defaults.pairs:
586586 if dk.kind == kSymbol and dk.s == nm: default = genExpr(dv, lenv, c)
587- c.line("var " & id & ": Value = call(getVar(\"clojure.core/get\"), [" & v &
587+ c.line("var " & id & ": Value = call(cellGet(" & c.cellFor("clojure.core/get") & "), [" & v &
588588 ", mkKeyword(" & nimStr(key) & "), " & default & "])")
589589 lenv.locals[nm] = id
590590 for (k, valForm) in target.pairs:
@@ -602,7 +602,7 @@ proc bindPattern(target: Value, v: string, lenv: Env, c: Ctx) =
602602 else:
603603 let id = c.gensym("l" & mangle(symName(k)))
604604 let kv = genExpr(valForm, lenv, c)
605- c.line("var " & id & ": Value = call(getVar(\"clojure.core/get\"), [" & v & ", " & kv & "])")
605+ c.line("var " & id & ": Value = call(cellGet(" & c.cellFor("clojure.core/get") & "), [" & v & ", " & kv & "])")
606606 lenv.locals[symName(k)] = id
607607 else:
608608 err("Unsupported binding form: " & prStr(target))
@@ -1321,7 +1321,28 @@ proc compileForms*(forms: seq[Value]): string =
13211321 c.push
13221322 c.line("discard call(getVar(" & nimStr(mainVar) & "), clonimArgv)")
13231323 c.pop
1324- var src = preamble & " initClonimRuntime()\n registerCore()\n registerNamespaceCore()\n" & c.prelude.join("\n") & "\n" &
1324+ var neededCore = initHashSet[string]()
1325+ neededCore.incl "*command-line-args*"
1326+ for name, _ in c.cells:
1327+ if name.startsWith("clojure.core/"):
1328+ neededCore.incl name[13 .. ^1]
1329+ elif '/' in name:
1330+ neededCore.incl name
1331+ var neededNames: seq[string] = @[]
1332+ for name in neededCore: neededNames.add name
1333+ neededNames.sort()
1334+ var needed: seq[string] = @[]
1335+ for name in neededNames: needed.add nimStr(name)
1336+ let manifest = "[" & needed.join(", ") & "]"
1337+ let groups = coreRegistrationGroups(neededNames)
1338+ const groupProcs = ["registerCoreArithmetic", "registerCorePredicates",
1339+ "registerCoreStringsIo", "registerCoreCollections",
1340+ "registerCoreHigherOrder", "registerCoreStateHost"]
1341+ var registration = ""
1342+ for group in 0 ..< groupProcs.len:
1343+ if group in groups:
1344+ registration.add " " & groupProcs[group] & "(" & manifest & ")\n"
1345+ var src = preamble & " initClonimRuntime()\n" & registration & c.prelude.join("\n") & "\n" &
13251346 c.body.join("\n") & "\n\n"
13261347 src &= """
13271348 when isMainModule:
@@ -3,8 +3,8 @@
3 ## Shape borrowed from jank: read to data, analyze into a small set of core3 ## Shape borrowed from jank: read to data, analyze into a small set of core
4 ## special forms (everything else is expanded), then emit host-language code4 ## special forms (everything else is expanded), then emit host-language code
5 ## and let the host compiler do register allocation, inlining and codegen.5 ## and let the host compiler do register allocation, inlining and codegen.
6-import std/[tables, strutils, sets]6+import std/[tables, strutils, sets, algorithm]
7-import runtime, namespaces7+import runtime, namespaces, core
8 8
9 type9 type
10 ## A fn whose arity is known at the call site, so it can be reached as a10 ## A fn whose arity is known at the call site, so it can be reached as a
@@ -569,7 +569,7 @@ proc bindPattern(target: Value, v: string, lenv: Env, c: Ctx) =
569 j += 2569 j += 2
570 continue570 continue
571 let id = c.gensym("lnth")571 let id = c.gensym("lnth")
572- c.line("var " & id & ": Value = call(getVar(\"clojure.core/nth\"), [" & v & ", mkInt(" &572+ c.line("var " & id & ": Value = call(cellGet(" & c.cellFor("clojure.core/nth") & "), [" & v & ", mkInt(" &
573 $idx & "), NilV])")573 $idx & "), NilV])")
574 bindPattern(p, id, lenv, c)574 bindPattern(p, id, lenv, c)
575 inc idx; inc j575 inc idx; inc j
@@ -584,7 +584,7 @@ proc bindPattern(target: Value, v: string, lenv: Env, c: Ctx) =
584 if defaults.kind == kMap:584 if defaults.kind == kMap:
585 for (dk, dv) in defaults.pairs:585 for (dk, dv) in defaults.pairs:
586 if dk.kind == kSymbol and dk.s == nm: default = genExpr(dv, lenv, c)586 if dk.kind == kSymbol and dk.s == nm: default = genExpr(dv, lenv, c)
587- c.line("var " & id & ": Value = call(getVar(\"clojure.core/get\"), [" & v &587+ c.line("var " & id & ": Value = call(cellGet(" & c.cellFor("clojure.core/get") & "), [" & v &
588 ", mkKeyword(" & nimStr(key) & "), " & default & "])")588 ", mkKeyword(" & nimStr(key) & "), " & default & "])")
589 lenv.locals[nm] = id589 lenv.locals[nm] = id
590 for (k, valForm) in target.pairs:590 for (k, valForm) in target.pairs:
@@ -602,7 +602,7 @@ proc bindPattern(target: Value, v: string, lenv: Env, c: Ctx) =
602 else:602 else:
603 let id = c.gensym("l" & mangle(symName(k)))603 let id = c.gensym("l" & mangle(symName(k)))
604 let kv = genExpr(valForm, lenv, c)604 let kv = genExpr(valForm, lenv, c)
605- c.line("var " & id & ": Value = call(getVar(\"clojure.core/get\"), [" & v & ", " & kv & "])")605+ c.line("var " & id & ": Value = call(cellGet(" & c.cellFor("clojure.core/get") & "), [" & v & ", " & kv & "])")
606 lenv.locals[symName(k)] = id606 lenv.locals[symName(k)] = id
607 else:607 else:
608 err("Unsupported binding form: " & prStr(target))608 err("Unsupported binding form: " & prStr(target))
@@ -1321,7 +1321,28 @@ proc compileForms*(forms: seq[Value]): string =
1321 c.push1321 c.push
1322 c.line("discard call(getVar(" & nimStr(mainVar) & "), clonimArgv)")1322 c.line("discard call(getVar(" & nimStr(mainVar) & "), clonimArgv)")
1323 c.pop1323 c.pop
1324- var src = preamble & " initClonimRuntime()\n registerCore()\n registerNamespaceCore()\n" & c.prelude.join("\n") & "\n" &1324+ var neededCore = initHashSet[string]()
1325+ neededCore.incl "*command-line-args*"
1326+ for name, _ in c.cells:
1327+ if name.startsWith("clojure.core/"):
1328+ neededCore.incl name[13 .. ^1]
1329+ elif '/' in name:
1330+ neededCore.incl name
1331+ var neededNames: seq[string] = @[]
1332+ for name in neededCore: neededNames.add name
1333+ neededNames.sort()
1334+ var needed: seq[string] = @[]
1335+ for name in neededNames: needed.add nimStr(name)
1336+ let manifest = "[" & needed.join(", ") & "]"
1337+ let groups = coreRegistrationGroups(neededNames)
1338+ const groupProcs = ["registerCoreArithmetic", "registerCorePredicates",
1339+ "registerCoreStringsIo", "registerCoreCollections",
1340+ "registerCoreHigherOrder", "registerCoreStateHost"]
1341+ var registration = ""
1342+ for group in 0 ..< groupProcs.len:
1343+ if group in groups:
1344+ registration.add " " & groupProcs[group] & "(" & manifest & ")\n"
1345+ var src = preamble & " initClonimRuntime()\n" & registration & c.prelude.join("\n") & "\n" &
1325 c.body.join("\n") & "\n\n"1346 c.body.join("\n") & "\n\n"
1326 src &= """1347 src &= """
1327 when isMainModule:1348 when isMainModule:
modified src/core.nim +79 -2
@@ -1,7 +1,11 @@
11 ## clonim core — clojure.core builtins, registered into the global var table.
2-import std/[strutils, math, times, random, re, os, httpclient, sequtils]
2+import std/[strutils, math, times, random, re, os, httpclient, sequtils, sets,
3+ tables]
34 import runtime
45
6+var selectedCore: HashSet[string]
7+var selectingCore = false
8+
59 proc num(v: Value): float64 =
610 case v.kind
711 of kInt: float64(v.i)
@@ -336,10 +340,12 @@ proc lazyConcat(colls: seq[Value], i: int, c: Cursor): Value =
336340 mkCons(x, lazyConcat(colls, k, cc)))
337341
338342 proc def(name: string, f: proc (args: openArray[Value]): Value {.closure.}) =
343+ if selectingCore and name notin selectedCore: return
339344 setVar(name, mkFn(name, f))
340345
341346 proc def(name: string, v: Value) =
342347 ## A var whose value is data rather than a function, such as *out*.
348+ if selectingCore and name notin selectedCore: return
343349 setVar(name, v)
344350
345351 # ----------------------------------------------------------------- fusion
@@ -497,7 +503,7 @@ proc currentOut(): File =
497503 else:
498504 stdout
499505
500-proc registerCore*() =
506+proc registerCoreArithmetic*() =
501507 # ---- arithmetic
502508 def "+", proc (a: openArray[Value]): Value =
503509 arith("+", a, 0, proc (x, y: int64): int64 = x + y, proc (x, y: float64): float64 = x + y)
@@ -597,6 +603,7 @@ proc registerCore*() =
597603 try: mkFloat(parseFloat(a[0].s.strip))
598604 except ValueError: err("Not a number: " & a[0].s)
599605
606+proc registerCorePredicates*() =
600607 # ---- comparison / predicates
601608 def "=", proc (a: openArray[Value]): Value =
602609 for i in 0 ..< a.len - 1:
@@ -642,6 +649,7 @@ proc registerCore*() =
642649 mkBool(a[1].kind == kInt and a[1].i >= 0 and a[1].i < c.vec.cnt)
643650 else: FalseV
644651
652+proc registerCoreStringsIo*() =
645653 # ---- strings / IO
646654 def "str", proc (a: openArray[Value]): Value =
647655 var s = ""
@@ -797,6 +805,7 @@ proc registerCore*() =
797805 # Host primitive used by the source-level stdlib's now-ms wrapper.
798806 def "*epoch-time-ms*", proc (a: openArray[Value]): Value = mkInt(int64(epochTime() * 1000))
799807
808+proc registerCoreCollections*() =
800809 # ---- collections
801810 def "list", proc (a: openArray[Value]): Value = mkList(a)
802811 def "vector", proc (a: openArray[Value]): Value = mkVector(a)
@@ -998,6 +1007,7 @@ proc registerCore*() =
9981007 for x in elems(call(a[0], args)): r.add x
9991008 mkList(r)
10001009
1010+proc registerCoreHigherOrder*() =
10011011 # ---- higher order
10021012 def "apply", proc (a: openArray[Value]): Value =
10031013 var callArgs: seq[Value] = @[]
@@ -1081,6 +1091,7 @@ proc registerCore*() =
10811091 let v = a[0]
10821092 mkFn("constantly", proc (args: openArray[Value]): Value = v)
10831093
1094+proc registerCoreStateHost*() =
10841095 # ---- atoms (mutable boxes, modelled as a 1-slot vector)
10851096 def "atom", proc (a: openArray[Value]): Value =
10861097 var cell = a[0]
@@ -1151,3 +1162,69 @@ proc registerCore*() =
11511162 def "throw", proc (a: openArray[Value]): Value = err(str(a[0]))
11521163 def "ex-info", proc (a: openArray[Value]): Value = mkStr(str(a[0]))
11531164 def "time-ms", proc (a: openArray[Value]): Value = mkInt(int64(epochTime() * 1000))
1165+
1166+proc aliasSelectedCore(names: openArray[string]) =
1167+ for name in names:
1168+ if (name == "/" or '/' notin name) and hasVar(name):
1169+ globals["clojure.core/" & name] = varCell(name)
1170+
1171+template registerSelected(names: openArray[string], body: untyped) =
1172+ selectedCore = initHashSet[string]()
1173+ for name in names: selectedCore.incl name
1174+ selectingCore = true
1175+ try:
1176+ body
1177+ finally:
1178+ selectingCore = false
1179+ aliasSelectedCore(names)
1180+
1181+proc registerCoreArithmeticSelected*(names: openArray[string]) =
1182+ registerSelected(names): registerCoreArithmetic()
1183+proc registerCorePredicatesSelected*(names: openArray[string]) =
1184+ registerSelected(names): registerCorePredicates()
1185+proc registerCoreStringsIoSelected*(names: openArray[string]) =
1186+ registerSelected(names): registerCoreStringsIo()
1187+proc registerCoreCollectionsSelected*(names: openArray[string]) =
1188+ registerSelected(names): registerCoreCollections()
1189+proc registerCoreHigherOrderSelected*(names: openArray[string]) =
1190+ registerSelected(names): registerCoreHigherOrder()
1191+proc registerCoreStateHostSelected*(names: openArray[string]) =
1192+ registerSelected(names): registerCoreStateHost()
1193+
1194+proc registerCore*() =
1195+ registerCoreArithmetic()
1196+ registerCorePredicates()
1197+ registerCoreStringsIo()
1198+ registerCoreCollections()
1199+ registerCoreHigherOrder()
1200+ registerCoreStateHost()
1201+
1202+proc registerCoreSelected*(names: openArray[string]) =
1203+ ## Register only the statically reachable core vars in a generated program.
1204+ ## Qualified clojure.core names share their unqualified cell, preserving the
1205+ ## rebinding semantics of the full registry.
1206+ selectedCore = initHashSet[string]()
1207+ for name in names: selectedCore.incl name
1208+ selectingCore = true
1209+ try:
1210+ registerCore()
1211+ finally:
1212+ selectingCore = false
1213+ aliasSelectedCore(names)
1214+
1215+proc coreRegistrationGroups*(names: openArray[string]): HashSet[int] =
1216+ ## Used by the compiler process to discover which independently linked core
1217+ ## registration families contain the resolved vars in a program.
1218+ let saved = globals
1219+ for group in 0 .. 5:
1220+ globals = initTable[string, VarCell]()
1221+ case group
1222+ of 0: registerCoreArithmetic()
1223+ of 1: registerCorePredicates()
1224+ of 2: registerCoreStringsIo()
1225+ of 3: registerCoreCollections()
1226+ of 4: registerCoreHigherOrder()
1227+ else: registerCoreStateHost()
1228+ for name in names:
1229+ if hasVar(name): result.incl group
1230+ globals = saved
@@ -1,7 +1,11 @@
1 ## clonim core — clojure.core builtins, registered into the global var table.1 ## clonim core — clojure.core builtins, registered into the global var table.
2-import std/[strutils, math, times, random, re, os, httpclient, sequtils]2+import std/[strutils, math, times, random, re, os, httpclient, sequtils, sets,
3+ tables]
3 import runtime4 import runtime
4 5
6+var selectedCore: HashSet[string]
7+var selectingCore = false
8+
5 proc num(v: Value): float64 =9 proc num(v: Value): float64 =
6 case v.kind10 case v.kind
7 of kInt: float64(v.i)11 of kInt: float64(v.i)
@@ -336,10 +340,12 @@ proc lazyConcat(colls: seq[Value], i: int, c: Cursor): Value =
336 mkCons(x, lazyConcat(colls, k, cc)))340 mkCons(x, lazyConcat(colls, k, cc)))
337 341
338 proc def(name: string, f: proc (args: openArray[Value]): Value {.closure.}) =342 proc def(name: string, f: proc (args: openArray[Value]): Value {.closure.}) =
343+ if selectingCore and name notin selectedCore: return
339 setVar(name, mkFn(name, f))344 setVar(name, mkFn(name, f))
340 345
341 proc def(name: string, v: Value) =346 proc def(name: string, v: Value) =
342 ## A var whose value is data rather than a function, such as *out*.347 ## A var whose value is data rather than a function, such as *out*.
348+ if selectingCore and name notin selectedCore: return
343 setVar(name, v)349 setVar(name, v)
344 350
345 # ----------------------------------------------------------------- fusion351 # ----------------------------------------------------------------- fusion
@@ -497,7 +503,7 @@ proc currentOut(): File =
497 else:503 else:
498 stdout504 stdout
499 505
500-proc registerCore*() =506+proc registerCoreArithmetic*() =
501 # ---- arithmetic507 # ---- arithmetic
502 def "+", proc (a: openArray[Value]): Value =508 def "+", proc (a: openArray[Value]): Value =
503 arith("+", a, 0, proc (x, y: int64): int64 = x + y, proc (x, y: float64): float64 = x + y)509 arith("+", a, 0, proc (x, y: int64): int64 = x + y, proc (x, y: float64): float64 = x + y)
@@ -597,6 +603,7 @@ proc registerCore*() =
597 try: mkFloat(parseFloat(a[0].s.strip))603 try: mkFloat(parseFloat(a[0].s.strip))
598 except ValueError: err("Not a number: " & a[0].s)604 except ValueError: err("Not a number: " & a[0].s)
599 605
606+proc registerCorePredicates*() =
600 # ---- comparison / predicates607 # ---- comparison / predicates
601 def "=", proc (a: openArray[Value]): Value =608 def "=", proc (a: openArray[Value]): Value =
602 for i in 0 ..< a.len - 1:609 for i in 0 ..< a.len - 1:
@@ -642,6 +649,7 @@ proc registerCore*() =
642 mkBool(a[1].kind == kInt and a[1].i >= 0 and a[1].i < c.vec.cnt)649 mkBool(a[1].kind == kInt and a[1].i >= 0 and a[1].i < c.vec.cnt)
643 else: FalseV650 else: FalseV
644 651
652+proc registerCoreStringsIo*() =
645 # ---- strings / IO653 # ---- strings / IO
646 def "str", proc (a: openArray[Value]): Value =654 def "str", proc (a: openArray[Value]): Value =
647 var s = ""655 var s = ""
@@ -797,6 +805,7 @@ proc registerCore*() =
797 # Host primitive used by the source-level stdlib's now-ms wrapper.805 # Host primitive used by the source-level stdlib's now-ms wrapper.
798 def "*epoch-time-ms*", proc (a: openArray[Value]): Value = mkInt(int64(epochTime() * 1000))806 def "*epoch-time-ms*", proc (a: openArray[Value]): Value = mkInt(int64(epochTime() * 1000))
799 807
808+proc registerCoreCollections*() =
800 # ---- collections809 # ---- collections
801 def "list", proc (a: openArray[Value]): Value = mkList(a)810 def "list", proc (a: openArray[Value]): Value = mkList(a)
802 def "vector", proc (a: openArray[Value]): Value = mkVector(a)811 def "vector", proc (a: openArray[Value]): Value = mkVector(a)
@@ -998,6 +1007,7 @@ proc registerCore*() =
998 for x in elems(call(a[0], args)): r.add x1007 for x in elems(call(a[0], args)): r.add x
999 mkList(r)1008 mkList(r)
1000 1009
1010+proc registerCoreHigherOrder*() =
1001 # ---- higher order1011 # ---- higher order
1002 def "apply", proc (a: openArray[Value]): Value =1012 def "apply", proc (a: openArray[Value]): Value =
1003 var callArgs: seq[Value] = @[]1013 var callArgs: seq[Value] = @[]
@@ -1081,6 +1091,7 @@ proc registerCore*() =
1081 let v = a[0]1091 let v = a[0]
1082 mkFn("constantly", proc (args: openArray[Value]): Value = v)1092 mkFn("constantly", proc (args: openArray[Value]): Value = v)
1083 1093
1094+proc registerCoreStateHost*() =
1084 # ---- atoms (mutable boxes, modelled as a 1-slot vector)1095 # ---- atoms (mutable boxes, modelled as a 1-slot vector)
1085 def "atom", proc (a: openArray[Value]): Value =1096 def "atom", proc (a: openArray[Value]): Value =
1086 var cell = a[0]1097 var cell = a[0]
@@ -1151,3 +1162,69 @@ proc registerCore*() =
1151 def "throw", proc (a: openArray[Value]): Value = err(str(a[0]))1162 def "throw", proc (a: openArray[Value]): Value = err(str(a[0]))
1152 def "ex-info", proc (a: openArray[Value]): Value = mkStr(str(a[0]))1163 def "ex-info", proc (a: openArray[Value]): Value = mkStr(str(a[0]))
1153 def "time-ms", proc (a: openArray[Value]): Value = mkInt(int64(epochTime() * 1000))1164 def "time-ms", proc (a: openArray[Value]): Value = mkInt(int64(epochTime() * 1000))
1165+
1166+proc aliasSelectedCore(names: openArray[string]) =
1167+ for name in names:
1168+ if (name == "/" or '/' notin name) and hasVar(name):
1169+ globals["clojure.core/" & name] = varCell(name)
1170+
1171+template registerSelected(names: openArray[string], body: untyped) =
1172+ selectedCore = initHashSet[string]()
1173+ for name in names: selectedCore.incl name
1174+ selectingCore = true
1175+ try:
1176+ body
1177+ finally:
1178+ selectingCore = false
1179+ aliasSelectedCore(names)
1180+
1181+proc registerCoreArithmeticSelected*(names: openArray[string]) =
1182+ registerSelected(names): registerCoreArithmetic()
1183+proc registerCorePredicatesSelected*(names: openArray[string]) =
1184+ registerSelected(names): registerCorePredicates()
1185+proc registerCoreStringsIoSelected*(names: openArray[string]) =
1186+ registerSelected(names): registerCoreStringsIo()
1187+proc registerCoreCollectionsSelected*(names: openArray[string]) =
1188+ registerSelected(names): registerCoreCollections()
1189+proc registerCoreHigherOrderSelected*(names: openArray[string]) =
1190+ registerSelected(names): registerCoreHigherOrder()
1191+proc registerCoreStateHostSelected*(names: openArray[string]) =
1192+ registerSelected(names): registerCoreStateHost()
1193+
1194+proc registerCore*() =
1195+ registerCoreArithmetic()
1196+ registerCorePredicates()
1197+ registerCoreStringsIo()
1198+ registerCoreCollections()
1199+ registerCoreHigherOrder()
1200+ registerCoreStateHost()
1201+
1202+proc registerCoreSelected*(names: openArray[string]) =
1203+ ## Register only the statically reachable core vars in a generated program.
1204+ ## Qualified clojure.core names share their unqualified cell, preserving the
1205+ ## rebinding semantics of the full registry.
1206+ selectedCore = initHashSet[string]()
1207+ for name in names: selectedCore.incl name
1208+ selectingCore = true
1209+ try:
1210+ registerCore()
1211+ finally:
1212+ selectingCore = false
1213+ aliasSelectedCore(names)
1214+
1215+proc coreRegistrationGroups*(names: openArray[string]): HashSet[int] =
1216+ ## Used by the compiler process to discover which independently linked core
1217+ ## registration families contain the resolved vars in a program.
1218+ let saved = globals
1219+ for group in 0 .. 5:
1220+ globals = initTable[string, VarCell]()
1221+ case group
1222+ of 0: registerCoreArithmetic()
1223+ of 1: registerCorePredicates()
1224+ of 2: registerCoreStringsIo()
1225+ of 3: registerCoreCollections()
1226+ of 4: registerCoreHigherOrder()
1227+ else: registerCoreStateHost()
1228+ for name in names:
1229+ if hasVar(name): result.incl group
1230+ globals = saved
modified src/runtime_lib.nim +14 -0
@@ -8,6 +8,20 @@ proc clonim_mk_fn(name: string,
88
99 {.push cdecl, exportc.}
1010 proc clonim_register_core() = registerCore()
11+proc clonim_register_core_selected(names: openArray[string]) =
12+ registerCoreSelected(names)
13+proc clonim_register_core_arithmetic(names: openArray[string]) =
14+ registerCoreArithmeticSelected(names)
15+proc clonim_register_core_predicates(names: openArray[string]) =
16+ registerCorePredicatesSelected(names)
17+proc clonim_register_core_strings_io(names: openArray[string]) =
18+ registerCoreStringsIoSelected(names)
19+proc clonim_register_core_collections(names: openArray[string]) =
20+ registerCoreCollectionsSelected(names)
21+proc clonim_register_core_higher_order(names: openArray[string]) =
22+ registerCoreHigherOrderSelected(names)
23+proc clonim_register_core_state_host(names: openArray[string]) =
24+ registerCoreStateHostSelected(names)
1125 proc clonim_register_namespace_core() = registerNamespaceCore()
1226 proc clonim_err(msg: string) {.noreturn.} = err(msg)
1327 proc clonim_var_cell(name: string): VarCell = varCell(name)
@@ -8,6 +8,20 @@ proc clonim_mk_fn(name: string,
8 8
9 {.push cdecl, exportc.}9 {.push cdecl, exportc.}
10 proc clonim_register_core() = registerCore()10 proc clonim_register_core() = registerCore()
11+proc clonim_register_core_selected(names: openArray[string]) =
12+ registerCoreSelected(names)
13+proc clonim_register_core_arithmetic(names: openArray[string]) =
14+ registerCoreArithmeticSelected(names)
15+proc clonim_register_core_predicates(names: openArray[string]) =
16+ registerCorePredicatesSelected(names)
17+proc clonim_register_core_strings_io(names: openArray[string]) =
18+ registerCoreStringsIoSelected(names)
19+proc clonim_register_core_collections(names: openArray[string]) =
20+ registerCoreCollectionsSelected(names)
21+proc clonim_register_core_higher_order(names: openArray[string]) =
22+ registerCoreHigherOrderSelected(names)
23+proc clonim_register_core_state_host(names: openArray[string]) =
24+ registerCoreStateHostSelected(names)
11 proc clonim_register_namespace_core() = registerNamespaceCore()25 proc clonim_register_namespace_core() = registerNamespaceCore()
12 proc clonim_err(msg: string) {.noreturn.} = err(msg)26 proc clonim_err(msg: string) {.noreturn.} = err(msg)
13 proc clonim_var_cell(name: string): VarCell = varCell(name)27 proc clonim_var_cell(name: string): VarCell = varCell(name)
modified tests/test_namespaces.nim +10 -1
@@ -1,6 +1,6 @@
11 ## Run with: nim r --hints:off --path:src tests/test_namespaces.nim
22 import std/[unittest, os, tempfiles, strutils, tables]
3-import runtime, reader, core, namespaces
3+import runtime, reader, core, namespaces, compiler
44
55 proc checkForms(src, expected: string, roots: seq[string] = @[]) =
66 let actual = resolveSource(src, roots)
@@ -22,6 +22,15 @@ proc rejects(src, message: string, roots: seq[string] = @[]) =
2222 check rejected
2323
2424 suite "static namespace resolution":
25+ test "generated programs register only reachable core families":
26+ let hello = compileSource("(println \"hi\")")
27+ check "registerCoreStringsIo" in hello
28+ check "registerCoreArithmetic" notin hello
29+ check "registerCore()" notin hello
30+ let destructured = compileSource("(let [[x] [1]] x)")
31+ check "registerCoreCollections" in destructured
32+ check "\"nth\"" in destructured
33+
2534 test "regex literals preserve their pattern source":
2635 let forms = readAll("#\"(?i)^did:[a-z0-9]+:\"")
2736 check forms.len == 1
@@ -1,6 +1,6 @@
1 ## Run with: nim r --hints:off --path:src tests/test_namespaces.nim1 ## Run with: nim r --hints:off --path:src tests/test_namespaces.nim
2 import std/[unittest, os, tempfiles, strutils, tables]2 import std/[unittest, os, tempfiles, strutils, tables]
3-import runtime, reader, core, namespaces3+import runtime, reader, core, namespaces, compiler
4 4
5 proc checkForms(src, expected: string, roots: seq[string] = @[]) =5 proc checkForms(src, expected: string, roots: seq[string] = @[]) =
6 let actual = resolveSource(src, roots)6 let actual = resolveSource(src, roots)
@@ -22,6 +22,15 @@ proc rejects(src, message: string, roots: seq[string] = @[]) =
22 check rejected22 check rejected
23 23
24 suite "static namespace resolution":24 suite "static namespace resolution":
25+ test "generated programs register only reachable core families":
26+ let hello = compileSource("(println \"hi\")")
27+ check "registerCoreStringsIo" in hello
28+ check "registerCoreArithmetic" notin hello
29+ check "registerCore()" notin hello
30+ let destructured = compileSource("(let [[x] [1]] x)")
31+ check "registerCoreCollections" in destructured
32+ check "\"nth\"" in destructured
33+
25 test "regex literals preserve their pattern source":34 test "regex literals preserve their pattern source":
26 let forms = readAll("#\"(?i)^did:[a-z0-9]+:\"")35 let forms = readAll("#\"(?i)^did:[a-z0-9]+:\"")
27 check forms.len == 136 check forms.len == 1