Register only reachable core families in generated programs
4ae0db9 parent: 1119f58 modified
src/app_runtime.nim +14 -0 | @@ -10,6 +10,20 @@ proc mkFn*(name: string, | ||
| 10 | 10 | {.push cdecl.} |
| 11 | 11 | proc initClonimRuntime*() {.importc: "ClonimRuntimeNimMain".} |
| 12 | 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 | 27 | proc registerNamespaceCore*() {.importc: "clonim_register_namespace_core".} |
| 14 | 28 | |
| 15 | 29 | 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 = | ||
| 94 | 94 | "--nimcache:" & installed.parentDir / |
| 95 | 95 | (if release: "nimcache-release" else: "nimcache-debug"), |
| 96 | 96 | "-o:" & installed] |
| 97 | + args.add "--passC:-ffunction-sections" | |
| 97 | 98 | # The HTTP primitive speaks TLS; without this it can only reach http://. |
| 98 | 99 | args.add "-d:ssl" |
| 99 | 100 | if release: args.add "-d:release" |
| @@ -212,6 +213,7 @@ proc main() = | ||
| 212 | 213 | nimCmd.add "--passL:-Wl,--allow-multiple-definition" |
| 213 | 214 | nimCmd.add "--passL:" & rtLib |
| 214 | 215 | nimCmd.add "--passL:-lm" |
| 216 | + nimCmd.add "--passL:-Wl,--gc-sections" | |
| 215 | 217 | nimCmd.add "-d:ssl" |
| 216 | 218 | if release: |
| 217 | 219 | # `-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:" & rtLib | 214 | 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 symbol | 219 | # `-d:release` disables Nim runtime checks but keeps DWARF and symbol |
modified
src/compiler.nim +27 -6 | @@ -3,8 +3,8 @@ | ||
| 3 | 3 | ## Shape borrowed from jank: read to data, analyze into a small set of core |
| 4 | 4 | ## special forms (everything else is expanded), then emit host-language code |
| 5 | 5 | ## 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 | |
| 8 | 8 | |
| 9 | 9 | type |
| 10 | 10 | ## 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 | 569 | j += 2 |
| 570 | 570 | continue |
| 571 | 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 | 573 | $idx & "), NilV])") |
| 574 | 574 | bindPattern(p, id, lenv, c) |
| 575 | 575 | inc idx; inc j |
| @@ -584,7 +584,7 @@ proc bindPattern(target: Value, v: string, lenv: Env, c: Ctx) = | ||
| 584 | 584 | if defaults.kind == kMap: |
| 585 | 585 | for (dk, dv) in defaults.pairs: |
| 586 | 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 | 588 | ", mkKeyword(" & nimStr(key) & "), " & default & "])") |
| 589 | 589 | lenv.locals[nm] = id |
| 590 | 590 | for (k, valForm) in target.pairs: |
| @@ -602,7 +602,7 @@ proc bindPattern(target: Value, v: string, lenv: Env, c: Ctx) = | ||
| 602 | 602 | else: |
| 603 | 603 | let id = c.gensym("l" & mangle(symName(k))) |
| 604 | 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 | 606 | lenv.locals[symName(k)] = id |
| 607 | 607 | else: |
| 608 | 608 | err("Unsupported binding form: " & prStr(target)) |
| @@ -1321,7 +1321,28 @@ proc compileForms*(forms: seq[Value]): string = | ||
| 1321 | 1321 | c.push |
| 1322 | 1322 | c.line("discard call(getVar(" & nimStr(mainVar) & "), clonimArgv)") |
| 1323 | 1323 | 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 | 1346 | c.body.join("\n") & "\n\n" |
| 1326 | 1347 | src &= """ |
| 1327 | 1348 | when isMainModule: |
| @@ -3,8 +3,8 @@ | |||
| 3 | ## Shape borrowed from jank: read to data, analyze into a small set of core | 3 | ## 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 code | 4 | ## 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, namespaces | 7 | +import runtime, namespaces, core |
| 8 | 8 | ||
| 9 | type | 9 | type |
| 10 | ## A fn whose arity is known at the call site, so it can be reached as a | 10 | ## 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 += 2 | 569 | j += 2 |
| 570 | continue | 570 | 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 j | 575 | 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] = id | 589 | 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)] = id | 606 | 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.push | 1321 | c.push |
| 1322 | c.line("discard call(getVar(" & nimStr(mainVar) & "), clonimArgv)") | 1322 | c.line("discard call(getVar(" & nimStr(mainVar) & "), clonimArgv)") |
| 1323 | c.pop | 1323 | 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 @@ | ||
| 1 | 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 | 4 | import runtime |
| 4 | 5 | |
| 6 | +var selectedCore: HashSet[string] | |
| 7 | +var selectingCore = false | |
| 8 | + | |
| 5 | 9 | proc num(v: Value): float64 = |
| 6 | 10 | case v.kind |
| 7 | 11 | of kInt: float64(v.i) |
| @@ -336,10 +340,12 @@ proc lazyConcat(colls: seq[Value], i: int, c: Cursor): Value = | ||
| 336 | 340 | mkCons(x, lazyConcat(colls, k, cc))) |
| 337 | 341 | |
| 338 | 342 | proc def(name: string, f: proc (args: openArray[Value]): Value {.closure.}) = |
| 343 | + if selectingCore and name notin selectedCore: return | |
| 339 | 344 | setVar(name, mkFn(name, f)) |
| 340 | 345 | |
| 341 | 346 | proc def(name: string, v: Value) = |
| 342 | 347 | ## A var whose value is data rather than a function, such as *out*. |
| 348 | + if selectingCore and name notin selectedCore: return | |
| 343 | 349 | setVar(name, v) |
| 344 | 350 | |
| 345 | 351 | # ----------------------------------------------------------------- fusion |
| @@ -497,7 +503,7 @@ proc currentOut(): File = | ||
| 497 | 503 | else: |
| 498 | 504 | stdout |
| 499 | 505 | |
| 500 | -proc registerCore*() = | |
| 506 | +proc registerCoreArithmetic*() = | |
| 501 | 507 | # ---- arithmetic |
| 502 | 508 | def "+", proc (a: openArray[Value]): Value = |
| 503 | 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 | 603 | try: mkFloat(parseFloat(a[0].s.strip)) |
| 598 | 604 | except ValueError: err("Not a number: " & a[0].s) |
| 599 | 605 | |
| 606 | +proc registerCorePredicates*() = | |
| 600 | 607 | # ---- comparison / predicates |
| 601 | 608 | def "=", proc (a: openArray[Value]): Value = |
| 602 | 609 | for i in 0 ..< a.len - 1: |
| @@ -642,6 +649,7 @@ proc registerCore*() = | ||
| 642 | 649 | mkBool(a[1].kind == kInt and a[1].i >= 0 and a[1].i < c.vec.cnt) |
| 643 | 650 | else: FalseV |
| 644 | 651 | |
| 652 | +proc registerCoreStringsIo*() = | |
| 645 | 653 | # ---- strings / IO |
| 646 | 654 | def "str", proc (a: openArray[Value]): Value = |
| 647 | 655 | var s = "" |
| @@ -797,6 +805,7 @@ proc registerCore*() = | ||
| 797 | 805 | # Host primitive used by the source-level stdlib's now-ms wrapper. |
| 798 | 806 | def "*epoch-time-ms*", proc (a: openArray[Value]): Value = mkInt(int64(epochTime() * 1000)) |
| 799 | 807 | |
| 808 | +proc registerCoreCollections*() = | |
| 800 | 809 | # ---- collections |
| 801 | 810 | def "list", proc (a: openArray[Value]): Value = mkList(a) |
| 802 | 811 | def "vector", proc (a: openArray[Value]): Value = mkVector(a) |
| @@ -998,6 +1007,7 @@ proc registerCore*() = | ||
| 998 | 1007 | for x in elems(call(a[0], args)): r.add x |
| 999 | 1008 | mkList(r) |
| 1000 | 1009 | |
| 1010 | +proc registerCoreHigherOrder*() = | |
| 1001 | 1011 | # ---- higher order |
| 1002 | 1012 | def "apply", proc (a: openArray[Value]): Value = |
| 1003 | 1013 | var callArgs: seq[Value] = @[] |
| @@ -1081,6 +1091,7 @@ proc registerCore*() = | ||
| 1081 | 1091 | let v = a[0] |
| 1082 | 1092 | mkFn("constantly", proc (args: openArray[Value]): Value = v) |
| 1083 | 1093 | |
| 1094 | +proc registerCoreStateHost*() = | |
| 1084 | 1095 | # ---- atoms (mutable boxes, modelled as a 1-slot vector) |
| 1085 | 1096 | def "atom", proc (a: openArray[Value]): Value = |
| 1086 | 1097 | var cell = a[0] |
| @@ -1151,3 +1162,69 @@ proc registerCore*() = | ||
| 1151 | 1162 | def "throw", proc (a: openArray[Value]): Value = err(str(a[0])) |
| 1152 | 1163 | def "ex-info", proc (a: openArray[Value]): Value = mkStr(str(a[0])) |
| 1153 | 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 | |
| @@ -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 runtime | 4 | 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.kind | 10 | 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 | # ----------------------------------------------------------------- fusion | 351 | # ----------------------------------------------------------------- fusion |
| @@ -497,7 +503,7 @@ proc currentOut(): File = | |||
| 497 | else: | 503 | else: |
| 498 | stdout | 504 | stdout |
| 499 | 505 | ||
| 500 | -proc registerCore*() = | 506 | +proc registerCoreArithmetic*() = |
| 501 | # ---- arithmetic | 507 | # ---- 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 / predicates | 607 | # ---- 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: FalseV | 650 | else: FalseV |
| 644 | 651 | ||
| 652 | +proc registerCoreStringsIo*() = | ||
| 645 | # ---- strings / IO | 653 | # ---- 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 | # ---- collections | 809 | # ---- 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 x | 1007 | for x in elems(call(a[0], args)): r.add x |
| 999 | mkList(r) | 1008 | mkList(r) |
| 1000 | 1009 | ||
| 1010 | +proc registerCoreHigherOrder*() = | ||
| 1001 | # ---- higher order | 1011 | # ---- 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, | ||
| 8 | 8 | |
| 9 | 9 | {.push cdecl, exportc.} |
| 10 | 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 | 25 | proc clonim_register_namespace_core() = registerNamespaceCore() |
| 12 | 26 | proc clonim_err(msg: string) {.noreturn.} = err(msg) |
| 13 | 27 | 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 @@ | ||
| 1 | 1 | ## Run with: nim r --hints:off --path:src tests/test_namespaces.nim |
| 2 | 2 | import std/[unittest, os, tempfiles, strutils, tables] |
| 3 | -import runtime, reader, core, namespaces | |
| 3 | +import runtime, reader, core, namespaces, compiler | |
| 4 | 4 | |
| 5 | 5 | proc checkForms(src, expected: string, roots: seq[string] = @[]) = |
| 6 | 6 | let actual = resolveSource(src, roots) |
| @@ -22,6 +22,15 @@ proc rejects(src, message: string, roots: seq[string] = @[]) = | ||
| 22 | 22 | check rejected |
| 23 | 23 | |
| 24 | 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 | 34 | test "regex literals preserve their pattern source": |
| 26 | 35 | let forms = readAll("#\"(?i)^did:[a-z0-9]+:\"") |
| 27 | 36 | check forms.len == 1 |
| @@ -1,6 +1,6 @@ | |||
| 1 | ## Run with: nim r --hints:off --path:src tests/test_namespaces.nim | 1 | ## 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, namespaces | 3 | +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 rejected | 22 | 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 == 1 | 36 | check forms.len == 1 |