nimeval.nim 6.3 KB

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