nimeval.nim 5.9 KB

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