如何在呼叫站点的目录中使用slurp / gorge / staticRead / staticExec?

时间:2019-04-28 15:13:42

标签: nim

编译时间函数slurp / gorge / staticRead / staticExec似乎使用源文件的目录作为工作目录。在大多数情况下,这是所需的行为,因为源代码和编译时间资源之间的关系是固定的。但是,如何在库中使用这些功能,以便它们引用用户提供的资源?

示例结构:

.
├── client
│   ├── client.nim
│   └── resource.data
└── library
    └── library.nim

我想在库中提供一个bundle函数,该函数允许客户端调用bundle("resource.data")之类的东西。图书馆内部可能会使用例如slurp(givenResourcePath)。但是,这将失败,因为相对于resource.data,slurp查找library.nim。有没有办法使用这些功能并引用相对于呼叫站点的文件?

注意:我尝试生成使用模板/宏执行slurp的AST,但即使查找相对于library.nim

2 个答案:

答案 0 :(得分:3)

处理此问题的最简单方法是依靠使用instantiationInfo的帮助程序模板来获取宏调用者的源路径。

您可以创建一个名为bundles.nim的模块:

import os

macro bundleImpl(userPath, resource: static string): untyped =
  let resourcePath = splitFile(userPath).dir / resource 
  echo "FULL RESOURCE PATH ", resourcePath
  echo "FILE CONTENTS:"
  echo staticRead(resourcePath)

template bundle*(resource: static string) =
  bundleImpl(instantiationInfo(-1, fullPaths = true).filename, resource)

然后,您可以按预期方式在任何模块中使用它:

import
  bundles

bundle "test.txt"

我的系统上的结果类似于:

FULL RESOURCE PATH /Users/zahary/nim/scratch/test.txt
FILE CONTENTS:
<test.txt contents>

答案 1 :(得分:2)

这可以通过一个宏使用一个小技巧来解决:查找slurp的实现表明它使用slurp AST节点的lineinfo来确定其工作目录。默认情况下,使用宏构造AST会附加指向library.nim的lineinfo,因此,slurp使用库路径。要修改行为,我们可以从呼叫站点读取lineinfo并将其附加到slurp节点:

macro bundle*(resource: string): untyped =
  # create slurp call node
  var slurpCall = newCall(ident "slurp", newStrLitNode resource.strVal)

  # forward callsite lineinfo to affect working directory behavior
  slurpCall.copyLineInfo(resource)

  # embed slurpCall somewhere in output AST
  # ...