2

根据 Unix 哲学,我有一个项目,其中包含一堆使用 bash 脚本捆绑在一起的小程序。他们的交换格式最初是这样的:

meta1a:meta1b:meta1c AST1
meta2a:meta2b:meta2c AST2

其中-:分隔的字段是元数据,而ASTs 是脚本按原样传递的 s 表达式。这很好用,因为我可以用来cut -d ' '从 AST 中拆分元数据,并cut -d ':'深入研究元数据。但是,我随后需要添加一个包含空格的元数据字段,这会破坏这种格式。由于没有字段使用标签,我切换到以下内容:

meta1a:meta1b:meta1c:meta 1 d\tAST1
meta2a:meta2b:meta2c:meta 2 d\tAST2

由于我设想将来会添加更多元数据字段,因此我认为是时候切换到更结构化的格式而不是玩“猜标点符号”的游戏了。

而不是分隔符,cut我可以使用 JSON 和jq,或者我可以使用 XML 和xsltproc,但是由于我已经在 AST 中使用 s 表达式,我想知道是否有一种很好的方法可以在这里使用它们?

例如,看起来像这样的东西:

(echo '(("foo1" "bar1" "baz1" "quux 1") ast1)'
 echo '(("foo2" "bar2" "baz2" "quux 2") ast2)') | sexpr 'caar'

"foo1"
"foo2"

我的要求是:

  • 以最少的样板直接使用 stdio,因为这是我的程序读取/写入数据的地方
  • 可从 shell 脚本轻松调用,为bash 的进程调用和流水线提供非常引人注目的替代方案
  • 如果可能,流式 I/O;IE。我宁愿一次使用一个 AST,而不是消耗整个输入来寻找结束)
  • 快速且轻量级,尤其是在它被多次调用的情况下;每个 AST 只有几 KB,但它们加起来可以达到数百 MB
  • 至少应该在 Linux 上工作;跨平台就好了

显而易见的选择是使用 Lisp/Scheme 解释器,但我使用过的唯一一个是 Emacs,它太重量级了。也许另一个实现更轻量级并且适合这个?

在 Haskell 中,我使用过 shelly、turtle 和 atto-lisp,但我的大部分代码都用于在 String/Text/ByteString、包装/解包Lisp、实现我自己的carcdrcons等之间进行转换。

我已经阅读了一些关于 scsh 的内容,但也不知道这是否合适。

4

2 回答 2

1

你可以试试 Common Lisp。

以最少的样板直接使用 stdio,因为这是我的程序读取/写入数据的地方

(loop for (attributes ast) = (safe-read) do (print ...)
  • 从标准输入和输出读/写。
  • safe-read应该在读取时禁用代码的执行。至少有一种实现方式。eval除非您完全知道其中的内容,否则不要直接使用 AST。

可从 shell 脚本轻松调用,或为 bash 的进程调用和流水线提供非常引人注目的替代方案

本着与 相同的精神java -jar ...,您可以启动您的 Common Lisp 可执行文件,例如sbcl,在参数中使用脚本:sbcl --load file.lisp。您甚至可以转储应用程序的核心或可执行核心,并预先加载所有内容 ( save-lisp-and-die)。 或者,使用cl-launchwhich 自动且可移植地执行上述操作,并生成 shell 脚本和/或从您的代码生成可执行程序。

如果可能,流式 I/O;IE。我宁愿一次使用一个 AST 而不是消耗整个输入来寻找结束)

如果整个输入流以 a 开头(read则将读取到结束)字符,但实际上很少这样做:Common Lisp 中的源代码不是包含在每个文件的一对括号中,而是作为一系列形式. 如果您的流产生的不是一个而是多个 s-exp,读者将一次阅读一个。

快速且轻量级,尤其是在它被多次调用的情况下;每个 AST 只有几 KB,但它们加起来可以达到数百 MB

它会很快,特别是如果你保存一个核心。轻量级,众所周知,lisp 图像会占用一些磁盘空间(例如 46MB),但这很少成为问题。为什么重要?也许您对轻量级的含义有另一个定义,因为这与您将要解析的 AST 的大小无关。不过,阅读这些 AST 应该没有问题。

至少应该在 Linux 上工作;跨平台就好了

参见维基百科。例如,Clozure CL (CCL) 在 Mac OS X、FreeBSD、Linux、Solaris 和 Windows 上运行,32/64 位。

于 2015-07-30T15:04:32.253 回答
0

在处理一个稍微不同的任务时,我再次发现需要处理一堆 s 表达式。这次我需要对给定的 s 表达式执行一些重要的处理(提取使用的符号列表等),而不是选择将它们作为不透明的字符串传递。

我尝试了 Racket 并感到惊喜;它比我以前使用过的其他 Lisp(Emacs Lisp 和各种特定于应用程序的 Scheme 脚本)要好得多,因为它有很好的文档和包含电池的标准库。

此类任务的一些相关要点:

  • 用于读取和写入数据的“端口”。这些可以(动态地?)跨表达式限定范围,并且默认为stdio(即(current-input-port)默认为stdin,(current-output-port)默认为stdout)。端口使 stdio 和文件访问与 shell 一样好用:更冗长,但更少粗糙的边缘情况。
  • 各种转换函数,如port->stringfile->linesread等,可以很容易地以适当的粒度形式(字符、行、字符串、表达式等)获取数据。
  • 我找不到读取多个s 表达式的“标准”方法,因为read只返回一个,因此需要迭代/递归才能以流式方式执行此操作。
  • 如果不需要流式传输,我发现将整个输入读取为字符串、追加"(\n""\n)",然后用于(with-input-from-string my-modified-input read)获取一个大列表最容易。

我发现 Racket 的启动时间很慢,所以如果速度是一个问题,我不建议反复调用脚本作为循环的一部分。将我的循环移动到 Racket 并调用一次脚本很容易。

于 2016-05-17T17:48:24.650 回答