summary refs log tree commit diff stats
path: root/compiler/nimeval.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/nimeval.nim')
-rw-r--r--compiler/nimeval.nim130
1 files changed, 108 insertions, 22 deletions
diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim
index aca03fc16..f20b5642c 100644
--- a/compiler/nimeval.nim
+++ b/compiler/nimeval.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nim Compiler
-#        (c) Copyright 2013 Andreas Rumpf
+#        (c) Copyright 2018 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -9,26 +9,112 @@
 
 ## exposes the Nim VM to clients.
 import
-  ast, modules, passes, passaux, condsyms,
-  options, nimconf, sem, semdata, llstream, vm, modulegraphs, idents
-
-proc execute*(program: string) =
-  passes.gIncludeFile = includeModule
-  passes.gImportModule = importModule
-  initDefines()
-  loadConfigs(DefaultConfig)
-
-  initDefines()
-  defineSymbol("nimrodvm")
-  when hasFFI: defineSymbol("nimffi")
-  registerPass(verbosePass)
-  registerPass(semPass)
-  registerPass(evalPass)
-
-  searchPaths.add options.libpath
-  var graph = newModuleGraph()
+  ast, astalgo, modules, passes, condsyms,
+  options, sem, semdata, llstream, vm, vmdef,
+  modulegraphs, idents, os
+
+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 rountine (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 createInterpreter*(scriptName: string;
+                        searchPaths: openArray[string];
+                        flags: TSandboxFlags = {}): Interpreter =
+  var conf = newConfigRef()
   var cache = newIdentCache()
-  var m = makeStdinModule(graph)
+  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(p)
+    if conf.libpath.len == 0: conf.libpath = p
+
+  var m = graph.makeModule(scriptName)
   incl(m.flags, sfMainModule)
-  compileSystemModule(graph,cache)
-  processModule(graph,m, llStreamOpen(program), nil, cache)
+  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."