Skip to content

Instantly share code, notes, and snippets.

@Vindaar
Created September 22, 2023 00:25
Show Gist options
  • Save Vindaar/036c9d50babff420455dc9eb25e86ade to your computer and use it in GitHub Desktop.
Save Vindaar/036c9d50babff420455dc9eb25e86ade to your computer and use it in GitHub Desktop.
Toy dynlib based Nim REPL
import noise, strutils, strformat, shell, tables, dynlib
proc printHelp() = echo ""
const procTmpl = """
{.push cdecl, exportc, dynlib.}
$#
{.pop.}
"""
const exprTmpl = """
$# # <- insert code to importc &
{.push cdecl, exportc, dynlib.}
proc tmp() =
$#
{.pop.}
"""
proc callTmp(fname, procName: string) =
## XXX: do not unload lib!
echo "Loading: ", fname, " name: ", procName
let lib = loadLib(fname)
doAssert lib != nil
let foo = cast[(proc() {.nimcall.})](lib.symAddr(procName))
doAssert foo != nil
foo()
unloadLib(lib)
var count = 0
# maps known functions to their dynlib
var fnTab = initTable[string, (string, string)]()
proc loadIt(fn, signature, lib: string): string =
result = signature & "{.importc: \"" & fn & "\", dynlib: \"" & lib & "\".}"
proc codeToLoad(): string =
for fn, (file, signature) in fnTab:
result.add loadIt(fn, signature, file) & "\n"
proc handleUserInput(line: string) =
## nim stuff!
# simple: if `proc` in input, just simple proc exported
# else: wrap in tmp proc
var content = ""
let loadCode = codeToLoad()
let isProc = "proc" in line
if isProc: content = procTmpl % line
else: content = exprTmpl % [loadCode, line]
# write file
let fname = "tmp_file_$#.nim" % $count
let file = "/t/$#" % fname
writeFile(file, content)
echo "\tWrote:\n", content
inc count
# compile as lib
shell:
nim c "--app:lib --verbosity:0" ($file)
let outfile = ("/t/lib" & fname).replace(".nim", ".so")
if not isProc:
# call tmp function
callTmp(outfile, "tmp")
else:
## XXX: save the procedures to a table, then
var fnName = line
fnName.removePrefix("proc ")
fnName = fnName.split("(")[0]
var signature = line
signature = signature.split("=")[0] ## XXX: Better extract real proc signature!!!
fnTab[fnName] = (outfile, signature)
proc main() =
var noise = Noise.init()
let prompt = Styler.init(fgRed, "Red ", fgGreen, "nim> ")
noise.setPrompt(prompt)
when promptPreloadBuffer:
noise.preloadBuffer("")
when promptHistory:
var file = "history"
discard noise.historyLoad(file)
when promptCompletion:
proc completionHook(noise: var Noise, text: string): int =
const words = ["apple", "diamond", "diadem", "diablo", "horse", "home", "quartz", "quit"]
for w in words:
if w.find(text) != -1:
noise.addCompletion w
noise.setCompletionHook(completionHook)
while true:
let ok = noise.readLine()
if not ok: break
## XXX: figure out how to input multiple lines
let line = noise.getLine
case line
of ".help": printHelp()
of ".quit": break
else:
if line.len > 0:
handleUserInput(line.strip)
when promptHistory:
if line.len > 0:
noise.historyAdd(line)
when promptHistory:
discard noise.historySave(file)
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment