nimeval.nim 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2018 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## exposes the Nim VM to clients.
  10. import
  11. ast, modules, condsyms,
  12. options, llstream, lineinfos, vm,
  13. vmdef, modulegraphs, idents, pathutils,
  14. scriptconfig, std/[compilesettings, tables, os]
  15. import pipelines
  16. when defined(nimPreviewSlimSystem):
  17. import std/[assertions, syncio]
  18. type
  19. Interpreter* = ref object ## Use Nim as an interpreter with this object
  20. mainModule: PSym
  21. graph: ModuleGraph
  22. scriptName: string
  23. idgen: IdGenerator
  24. iterator exportedSymbols*(i: Interpreter): PSym =
  25. assert i != nil
  26. assert i.mainModule != nil, "no main module selected"
  27. for s in modulegraphs.allSyms(i.graph, i.mainModule):
  28. yield s
  29. proc selectUniqueSymbol*(i: Interpreter; name: string;
  30. symKinds: set[TSymKind] = {skLet, skVar}): PSym =
  31. ## Can be used to access a unique symbol of ``name`` and
  32. ## the given ``symKinds`` filter.
  33. assert i != nil
  34. assert i.mainModule != nil, "no main module selected"
  35. let n = getIdent(i.graph.cache, name)
  36. var it: ModuleIter = default(ModuleIter)
  37. var s = initModuleIter(it, i.graph, i.mainModule, n)
  38. result = nil
  39. while s != nil:
  40. if s.kind in symKinds:
  41. if result == nil: result = s
  42. else: return nil # ambiguous
  43. s = nextModuleIter(it, i.graph)
  44. proc selectRoutine*(i: Interpreter; name: string): PSym =
  45. ## Selects a declared routine (proc/func/etc) from the main module.
  46. ## The routine needs to have the export marker ``*``. The only matching
  47. ## routine is returned and ``nil`` if it is overloaded.
  48. result = selectUniqueSymbol(i, name, {skTemplate, skMacro, skFunc,
  49. skMethod, skProc, skConverter})
  50. proc callRoutine*(i: Interpreter; routine: PSym; args: openArray[PNode]): PNode =
  51. assert i != nil
  52. result = vm.execProc(PCtx i.graph.vm, routine, args)
  53. proc getGlobalValue*(i: Interpreter; letOrVar: PSym): PNode =
  54. result = vm.getGlobalValue(PCtx i.graph.vm, letOrVar)
  55. proc setGlobalValue*(i: Interpreter; letOrVar: PSym, val: PNode) =
  56. ## Sets a global value to a given PNode, does not do any type checking.
  57. vm.setGlobalValue(PCtx i.graph.vm, letOrVar, val)
  58. proc implementRoutine*(i: Interpreter; pkg, module, name: string;
  59. impl: proc (a: VmArgs) {.closure, gcsafe.}) =
  60. assert i != nil
  61. let vm = PCtx(i.graph.vm)
  62. vm.registerCallback(pkg & "." & module & "." & name, impl)
  63. proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) =
  64. ## This can also be used to *reload* the script.
  65. assert i != nil
  66. assert i.mainModule != nil, "no main module selected"
  67. initStrTables(i.graph, i.mainModule)
  68. i.graph.cacheSeqs.clear()
  69. i.graph.cacheCounters.clear()
  70. i.graph.cacheTables.clear()
  71. i.mainModule.ast = nil
  72. let s = if scriptStream != nil: scriptStream
  73. else: llStreamOpen(findFile(i.graph.config, i.scriptName), fmRead)
  74. discard processPipelineModule(i.graph, i.mainModule, i.idgen, s)
  75. proc findNimStdLib*(): string =
  76. ## Tries to find a path to a valid "system.nim" file.
  77. ## Returns "" on failure.
  78. try:
  79. let nimexe = os.findExe("nim")
  80. # this can't work with choosenim shims, refs https://github.com/dom96/choosenim/issues/189
  81. # it'd need `nim dump --dump.format:json . | jq -r .libpath`
  82. # which we should simplify as `nim dump --key:libpath`
  83. if nimexe.len == 0: return ""
  84. result = nimexe.splitPath()[0] /../ "lib"
  85. if not fileExists(result / "system.nim"):
  86. when defined(unix):
  87. result = nimexe.expandSymlink.splitPath()[0] /../ "lib"
  88. if not fileExists(result / "system.nim"): return ""
  89. except OSError, ValueError:
  90. return ""
  91. proc findNimStdLibCompileTime*(): string =
  92. ## Same as `findNimStdLib` but uses source files used at compile time,
  93. ## and asserts on error.
  94. result = querySetting(libPath)
  95. doAssert fileExists(result / "system.nim"), "result:" & result
  96. proc createInterpreter*(scriptName: string;
  97. searchPaths: openArray[string];
  98. flags: TSandboxFlags = {},
  99. defines = @[("nimscript", "true")],
  100. registerOps = true): Interpreter =
  101. var conf = newConfigRef()
  102. var cache = newIdentCache()
  103. var graph = newModuleGraph(cache, conf)
  104. connectPipelineCallbacks(graph)
  105. initDefines(conf.symbols)
  106. for define in defines:
  107. defineSymbol(conf.symbols, define[0], define[1])
  108. for p in searchPaths:
  109. conf.searchPaths.add(AbsoluteDir p)
  110. if conf.libpath.isEmpty: conf.libpath = AbsoluteDir p
  111. var m = graph.makeModule(scriptName)
  112. incl(m.flags, sfMainModule)
  113. var idgen = idGeneratorFromModule(m)
  114. var vm = newCtx(m, cache, graph, idgen)
  115. vm.mode = emRepl
  116. vm.features = flags
  117. if registerOps:
  118. vm.registerAdditionalOps() # Required to register parts of stdlib modules
  119. graph.vm = vm
  120. setPipeLinePass(graph, EvalPass)
  121. graph.compilePipelineSystemModule()
  122. result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName, idgen: idgen)
  123. proc destroyInterpreter*(i: Interpreter) =
  124. ## destructor.
  125. discard "currently nothing to do."
  126. proc registerErrorHook*(i: Interpreter, hook:
  127. proc (config: ConfigRef; info: TLineInfo; msg: string;
  128. severity: Severity) {.gcsafe.}) =
  129. i.graph.config.structuredErrorHook = hook
  130. proc runRepl*(r: TLLRepl;
  131. searchPaths: openArray[string];
  132. supportNimscript: bool) =
  133. ## deadcode but please don't remove... might be revived
  134. var conf = newConfigRef()
  135. var cache = newIdentCache()
  136. var graph = newModuleGraph(cache, conf)
  137. for p in searchPaths:
  138. conf.searchPaths.add(AbsoluteDir p)
  139. if conf.libpath.isEmpty: conf.libpath = AbsoluteDir p
  140. conf.cmd = cmdInteractive # see also `setCmd`
  141. conf.setErrorMaxHighMaybe
  142. initDefines(conf.symbols)
  143. defineSymbol(conf.symbols, "nimscript")
  144. if supportNimscript: defineSymbol(conf.symbols, "nimconfig")
  145. when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
  146. var m = graph.makeStdinModule()
  147. incl(m.flags, sfMainModule)
  148. var idgen = idGeneratorFromModule(m)
  149. if supportNimscript: graph.vm = setupVM(m, cache, "stdin", graph, idgen)
  150. setPipeLinePass(graph, InterpreterPass)
  151. graph.compilePipelineSystemModule()
  152. discard processPipelineModule(graph, m, idgen, llStreamOpenStdIn(r))