#
#
# The Nim Compiler
# (c) Copyright 2018 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## exposes the Nim VM to clients.
import
ast, astalgo, modules, passes, condsyms,
options, sem, semdata, llstream, vm, vmdef,
modulegraphs, idents, os, pathutils, passaux,
scriptconfig
type
Interpreter* = ref object ## Use Nim as an interpreter with this object
mainModule: PSym
graph: ModuleGraph
scriptName: string
iterator exportedSymbols*(i: Interpreter): PSym =
assert i != nil
assert i.mainModule != nil, "no main module selected"
var it: TTabIter
var s = initTabIter(it, i.mainModule.tab)
while s != nil:
yield s
s = nextIter(it, i.mainModule.tab)
proc selectUniqueSymbol*(i: Interpreter; name: string;
symKinds: set[TSymKind] = {skLet, skVar}): PSym =
## Can be used to access a unique symbol of ``name`` and
## the given ``symKinds`` filter.
assert i != nil
assert i.mainModule != nil, "no main module selected"
let n = getIdent(i.graph.cache, name)
var it: TIdentIter
var s = initIdentIter(it, i.mainModule.tab, n)
result = nil
while s != nil:
if s.kind in symKinds:
if result == nil: result = s
else: return nil # ambiguous
s = nextIdentIter(it, i.mainModule.tab)
proc selectRoutine*(i: Interpreter; name: string): PSym =
## Selects a declared routine (proc/func/etc) from the main module.
## The routine needs to have the export marker ``*``. The only matching
## routine is returned and ``nil`` if it is overloaded.
result = selectUniqueSymbol(i, name, {skTemplate, skMacro, skFunc,
skMethod, skProc, skConverter})
proc callRoutine*(i: Interpreter; routine: PSym; args: openArray[PNode]): PNode =
assert i != nil
result = vm.execProc(PCtx i.graph.vm, routine, args)
proc getGlobalValue*(i: Interpreter; letOrVar: PSym): PNode =
result = vm.getGlobalValue(PCtx i.graph.vm, letOrVar)
proc implementRoutine*(i: Interpreter; pkg, module, name: string;
impl: proc (a: VmArgs) {.closure, gcsafe.}) =
assert i != nil
let vm = PCtx(i.graph.vm)
vm.registerCallback(pkg & "." & module & "." & name, impl)
proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) =
## This can also be used to *reload* the script.
assert i != nil
assert i.mainModule != nil, "no main module selected"
initStrTable(i.mainModule.tab)
i.mainModule.ast = nil
let s = if scriptStream != nil: scriptStream
else: llStreamOpen(findFile(i.graph.config, i.scriptName), fmRead)
processModule(i.graph, i.mainModule, s)
proc findNimStdLib*(): string =
## Tries to find a path to a valid "system.nim" file.
## Returns "" on failure.
try:
let nimexe = os.findExe("nim")
if nimexe.len == 0: return ""
result = nimexe.splitPath()[0] /../ "lib"
if not fileExists(result / "system.nim"):
when defined(unix):
result = nimexe.expandSymlink.splitPath()[0] /../ "lib"
if not fileExists(result / "system.nim"): return ""
except OSError, ValueError:
return ""
proc findNimStdLibCompileTime*(): string =
## Same as ``findNimStdLib`` but uses source files used at compile time,
## and asserts on error.
const sourcePath = currentSourcePath()
result = sourcePath.parentDir.parentDir / "lib"
doAssert fileExists(result / "system.nim"), "result:" & result
proc createInterpreter*(scriptName: string;
searchPaths: openArray[string];
flags: TSandboxFlags = {}): Interpreter =
var conf = newConfigRef()
var cache = newIdentCache()
var graph = newModuleGraph(cache, conf)
connectCallbacks(graph)
initDefines(conf.symbols)
defineSymbol(conf.symbols, "nimscript")
defineSymbol(conf.symbols, "nimconfig")
registerPass(graph, semPass)
registerPass(graph, evalPass)
for p in searchPaths:
conf.searchPaths.add(AbsoluteDir p)
if conf.libpath.isEmpty: conf.libpath = AbsoluteDir p
var m = graph.makeModule(scriptName)
incl(m.flags, sfMainModule)
var vm = newCtx(m, cache, graph)
vm.mode = emRepl
vm.features = flags
graph.vm = vm
graph.compileSystemModule()
result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName)
proc destroyInterpreter*(i: Interpreter) =
## destructor.
discard "currently nothing to do."
proc runRepl*(r: TLLRepl;
searchPaths: openArray[string];
supportNimscript: bool) =
var conf = newConfigRef()
var cache = newIdentCache()
var graph = newModuleGraph(cache, conf)
for p in searchPaths:
conf.searchPaths.add(AbsoluteDir p)
if conf.libpath.isEmpty: conf.libpath = AbsoluteDir p
conf.cmd = cmdInteractive
conf.errorMax = high(int)
initDefines(conf.symbols)
defineSymbol(conf.symbols, "nimscript")
if supportNimscript: defineSymbol(conf.symbols, "nimconfig")
when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
registerPass(graph, verbosePass)
registerPass(graph, semPass)
registerPass(graph, evalPass)
var m = graph.makeStdinModule()
incl(m.flags, sfMainModule)
if supportNimscript: graph.vm = setupVM(m, cache, "stdin", graph)
graph.compileSystemModule()
processModule(graph, m, llStreamOpenStdIn(r))