nimeval.nim 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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, vm, vmdef,
  13. modulegraphs, idents, os, pathutils, passaux,
  14. 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 rountine (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. if nimexe.len == 0: return ""
  74. result = nimexe.splitPath()[0] /../ "lib"
  75. if not fileExists(result / "system.nim"):
  76. when defined(unix):
  77. result = nimexe.expandSymlink.splitPath()[0] /../ "lib"
  78. if not fileExists(result / "system.nim"): return ""
  79. except OSError, ValueError:
  80. return ""
  81. proc findNimStdLibCompileTime*(): string =
  82. ## Same as ``findNimStdLib`` but uses source files used at compile time,
  83. ## and asserts on error.
  84. const sourcePath = currentSourcePath()
  85. result = sourcePath.parentDir.parentDir / "lib"
  86. doAssert fileExists(result / "system.nim"), "result:" & result
  87. proc createInterpreter*(scriptName: string;
  88. searchPaths: openArray[string];
  89. flags: TSandboxFlags = {}): Interpreter =
  90. var conf = newConfigRef()
  91. var cache = newIdentCache()
  92. var graph = newModuleGraph(cache, conf)
  93. connectCallbacks(graph)
  94. initDefines(conf.symbols)
  95. defineSymbol(conf.symbols, "nimscript")
  96. defineSymbol(conf.symbols, "nimconfig")
  97. registerPass(graph, semPass)
  98. registerPass(graph, evalPass)
  99. for p in searchPaths:
  100. conf.searchPaths.add(AbsoluteDir p)
  101. if conf.libpath.isEmpty: conf.libpath = AbsoluteDir p
  102. var m = graph.makeModule(scriptName)
  103. incl(m.flags, sfMainModule)
  104. var vm = newCtx(m, cache, graph)
  105. vm.mode = emRepl
  106. vm.features = flags
  107. graph.vm = vm
  108. graph.compileSystemModule()
  109. result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName)
  110. proc destroyInterpreter*(i: Interpreter) =
  111. ## destructor.
  112. discard "currently nothing to do."
  113. proc runRepl*(r: TLLRepl;
  114. searchPaths: openArray[string];
  115. supportNimscript: bool) =
  116. var conf = newConfigRef()
  117. var cache = newIdentCache()
  118. var graph = newModuleGraph(cache, conf)
  119. for p in searchPaths:
  120. conf.searchPaths.add(AbsoluteDir p)
  121. if conf.libpath.isEmpty: conf.libpath = AbsoluteDir p
  122. conf.cmd = cmdInteractive
  123. conf.errorMax = high(int)
  124. initDefines(conf.symbols)
  125. defineSymbol(conf.symbols, "nimscript")
  126. if supportNimscript: defineSymbol(conf.symbols, "nimconfig")
  127. when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
  128. registerPass(graph, verbosePass)
  129. registerPass(graph, semPass)
  130. registerPass(graph, evalPass)
  131. var m = graph.makeStdinModule()
  132. incl(m.flags, sfMainModule)
  133. if supportNimscript: graph.vm = setupVM(m, cache, "stdin", graph)
  134. graph.compileSystemModule()
  135. processModule(graph, m, llStreamOpenStdIn(r))