jq 或 xsltproc 替代 s 表达式?
jq or xsltproc alternative for s-expressions?
我有一个项目,其中包含一堆使用 bash 脚本捆绑在一起的小程序,按照 Unix 哲学。他们的交换格式原来是这样的:
meta1a:meta1b:meta1c AST1
meta2a:meta2b:meta2c AST2
其中 :
分隔的字段是元数据,AST
是脚本按原样传递的 s 表达式。这工作得很好,因为我可以使用 cut -d ' '
从 AST 中分离元数据,并使用 cut -d ':'
来挖掘元数据。但是,然后我需要添加一个包含空格的元数据字段,这会破坏这种格式。由于没有字段使用制表符,我切换到以下内容:
meta1a:meta1b:meta1c:meta 1 d\tAST1
meta2a:meta2b:meta2c:meta 2 d\tAST2
由于我设想将来会添加更多元数据字段,所以我认为是时候切换到更结构化的格式,而不是玩 "guess the punctuation".
的游戏了
我可以使用 JSON 和 jq
,而不是定界符和 cut
,或者我可以使用 XML 和 xsltproc
,但是因为我已经为 AST 使用了 s 表达式,我想知道这里是否有更好的方法来使用它们?
例如,看起来像这样的东西:
(echo '(("foo1" "bar1" "baz1" "quux 1") ast1)'
echo '(("foo2" "bar2" "baz2" "quux 2") ast2)') | sexpr 'caar'
"foo1"
"foo2"
我的要求是:
- 以最少的样板直接使用 stdio,因为那是我的程序 read/write 他们的数据
- 可以从 shell 脚本轻松调用 或 提供 非常 引人注目的替代 bash 进程调用和流水线
- 流式传输 I/O 如果可能; IE。我宁愿一次使用一个 AST 而不是消耗整个输入来寻找结束
)
- 快速且轻量级,尤其是在被调用几次的情况下;每个 AST 只有几 KB,但它们加起来可达数百 MB
- 至少应该在 Linux 上工作;跨平台就好了
显而易见的选择是使用 Lisp/Scheme 解释器,但我唯一使用过的是 Emacs,它太重量级了。也许另一个实现更轻量级并且适合这个?
在 Haskell 中,我玩过 shelly、turtle 和 atto-lisp,但我的大部分代码都花在了 String/Text/ByteString、wrapping/unwrapping 之间的转换 Lisp
s,实现我自己的 car
、cdr
、cons
等
我读过一些关于 scsh 的资料,但也不知道那是否合适。
你可以试试 Common Lisp。
Straightforward use of stdio with minimal boilerplate, since that's
where my programs read/write their data
(loop for (attributes ast) = (safe-read) do (print ...)
- Read/write 来自标准输入和输出。
safe-read
应该禁止在读取时执行代码。至少有one implementation个。不要直接 eval
你的 AST,除非你完全知道里面有什么。
Easily callable from shell scripts or provide a very compelling
alternative to bash's process invocation and pipelining
本着与 java -jar ...
相同的精神,您可以启动您的 Common Lisp 可执行文件,例如sbcl
,参数中有一个脚本:sbcl --load file.lisp
。您甚至可以转储应用程序的核心或可执行核心并预加载所有内容 (save-lisp-and-die
)。
或,使用cl-launch
自动、可移植地执行上述操作,并生成shell脚本and/or从您的代码生成可执行程序。
Streaming I/O if possible; ie. I'd rather work with one AST at a time
rather than consuming the whole input looking for a closing )
如果整个输入流以 (
开头,那么 read
将读取到结束的 )
字符,但实际上很少这样做:源代码在Common Lisp 不是包含在每个文件的一对括号中,而是作为一系列形式。如果您的流产生的不是一个而是多个 s-exp,reader 将一次读取一个。
Fast and lightweight, especially if it's being invoked a few times;
each AST is only a few KB, but they can add up to hundreds of MB
速度会很快,特别是如果你节省了一个核心。轻量级,众所周知,lisp 图像可以占用一些磁盘 space(例如 46MB),但这很少成为问题。为什么重要?也许您对轻量级的含义有另一种定义,因为这与您将要解析的 AST 的大小无关。不过,阅读那些 AST 应该没有问题。
Should work on Linux at least; cross-platform would be nice
参见 Wikipedia。例如,Clozure CL (CCL) 在 Mac OS X、FreeBSD、Linux、Solaris 和 Windows、32/64 位上运行。
在处理一个稍微不同的任务时,我再次发现需要处理一堆 s 表达式。这次我需要对给定的 s 表达式执行一些重要的处理(提取使用的符号列表等),而不是选择将它们作为不透明字符串传递。
我尝试了 Racket 并感到惊喜;它比我以前用过的其他 Lisp(Emacs Lisp 和各种特定于应用程序的 Scheme 脚本)要好得多,因为它有很好的文档和一个包含电池的标准库。
此类任务的一些相关要点:
- "Ports"用于读写数据。这些可以(动态地?)跨表达式限定范围,并且默认为 stdio(即
(current-input-port)
默认为 stdin,(current-output-port)
默认为 stdout)。端口使 stdio 和文件访问与 shell 一样好用:更冗长,但更少粗糙的边缘情况。
- 各种转换函数,如
port->string
、file->lines
、read
等,可以轻松获取合适粒度形式的数据(字符、行、字符串、表达式、等)。
- 我找不到 "standard" 方法来读取 多个 s 表达式,因为
read
只有 returns 一个,所以 iteration/recursion 需要以流方式执行此操作。
- 如果不需要流式传输,我发现最简单的方法是将整个输入作为字符串读取,附加
"(\n"
和 "\n)"
,然后使用 (with-input-from-string my-modified-input read)
获得一个大列表.
我发现 Racket 的启动时间非常慢,所以如果速度是一个问题,我不建议将脚本作为循环的一部分反复调用。很容易将我的循环移动到 Racket 中并调用一次脚本。
我有一个项目,其中包含一堆使用 bash 脚本捆绑在一起的小程序,按照 Unix 哲学。他们的交换格式原来是这样的:
meta1a:meta1b:meta1c AST1
meta2a:meta2b:meta2c AST2
其中 :
分隔的字段是元数据,AST
是脚本按原样传递的 s 表达式。这工作得很好,因为我可以使用 cut -d ' '
从 AST 中分离元数据,并使用 cut -d ':'
来挖掘元数据。但是,然后我需要添加一个包含空格的元数据字段,这会破坏这种格式。由于没有字段使用制表符,我切换到以下内容:
meta1a:meta1b:meta1c:meta 1 d\tAST1
meta2a:meta2b:meta2c:meta 2 d\tAST2
由于我设想将来会添加更多元数据字段,所以我认为是时候切换到更结构化的格式,而不是玩 "guess the punctuation".
的游戏了 我可以使用 JSON 和 jq
,而不是定界符和 cut
,或者我可以使用 XML 和 xsltproc
,但是因为我已经为 AST 使用了 s 表达式,我想知道这里是否有更好的方法来使用它们?
例如,看起来像这样的东西:
(echo '(("foo1" "bar1" "baz1" "quux 1") ast1)'
echo '(("foo2" "bar2" "baz2" "quux 2") ast2)') | sexpr 'caar'
"foo1"
"foo2"
我的要求是:
- 以最少的样板直接使用 stdio,因为那是我的程序 read/write 他们的数据
- 可以从 shell 脚本轻松调用 或 提供 非常 引人注目的替代 bash 进程调用和流水线
- 流式传输 I/O 如果可能; IE。我宁愿一次使用一个 AST 而不是消耗整个输入来寻找结束
)
- 快速且轻量级,尤其是在被调用几次的情况下;每个 AST 只有几 KB,但它们加起来可达数百 MB
- 至少应该在 Linux 上工作;跨平台就好了
显而易见的选择是使用 Lisp/Scheme 解释器,但我唯一使用过的是 Emacs,它太重量级了。也许另一个实现更轻量级并且适合这个?
在 Haskell 中,我玩过 shelly、turtle 和 atto-lisp,但我的大部分代码都花在了 String/Text/ByteString、wrapping/unwrapping 之间的转换 Lisp
s,实现我自己的 car
、cdr
、cons
等
我读过一些关于 scsh 的资料,但也不知道那是否合适。
你可以试试 Common Lisp。
Straightforward use of stdio with minimal boilerplate, since that's where my programs read/write their data
(loop for (attributes ast) = (safe-read) do (print ...)
- Read/write 来自标准输入和输出。
safe-read
应该禁止在读取时执行代码。至少有one implementation个。不要直接eval
你的 AST,除非你完全知道里面有什么。
Easily callable from shell scripts or provide a very compelling alternative to bash's process invocation and pipelining
本着与 java -jar ...
相同的精神,您可以启动您的 Common Lisp 可执行文件,例如sbcl
,参数中有一个脚本:sbcl --load file.lisp
。您甚至可以转储应用程序的核心或可执行核心并预加载所有内容 (save-lisp-and-die
)。
或,使用cl-launch
自动、可移植地执行上述操作,并生成shell脚本and/or从您的代码生成可执行程序。
Streaming I/O if possible; ie. I'd rather work with one AST at a time rather than consuming the whole input looking for a closing )
如果整个输入流以 (
开头,那么 read
将读取到结束的 )
字符,但实际上很少这样做:源代码在Common Lisp 不是包含在每个文件的一对括号中,而是作为一系列形式。如果您的流产生的不是一个而是多个 s-exp,reader 将一次读取一个。
Fast and lightweight, especially if it's being invoked a few times; each AST is only a few KB, but they can add up to hundreds of MB
速度会很快,特别是如果你节省了一个核心。轻量级,众所周知,lisp 图像可以占用一些磁盘 space(例如 46MB),但这很少成为问题。为什么重要?也许您对轻量级的含义有另一种定义,因为这与您将要解析的 AST 的大小无关。不过,阅读那些 AST 应该没有问题。
Should work on Linux at least; cross-platform would be nice
参见 Wikipedia。例如,Clozure CL (CCL) 在 Mac OS X、FreeBSD、Linux、Solaris 和 Windows、32/64 位上运行。
在处理一个稍微不同的任务时,我再次发现需要处理一堆 s 表达式。这次我需要对给定的 s 表达式执行一些重要的处理(提取使用的符号列表等),而不是选择将它们作为不透明字符串传递。
我尝试了 Racket 并感到惊喜;它比我以前用过的其他 Lisp(Emacs Lisp 和各种特定于应用程序的 Scheme 脚本)要好得多,因为它有很好的文档和一个包含电池的标准库。
此类任务的一些相关要点:
- "Ports"用于读写数据。这些可以(动态地?)跨表达式限定范围,并且默认为 stdio(即
(current-input-port)
默认为 stdin,(current-output-port)
默认为 stdout)。端口使 stdio 和文件访问与 shell 一样好用:更冗长,但更少粗糙的边缘情况。 - 各种转换函数,如
port->string
、file->lines
、read
等,可以轻松获取合适粒度形式的数据(字符、行、字符串、表达式、等)。 - 我找不到 "standard" 方法来读取 多个 s 表达式,因为
read
只有 returns 一个,所以 iteration/recursion 需要以流方式执行此操作。 - 如果不需要流式传输,我发现最简单的方法是将整个输入作为字符串读取,附加
"(\n"
和"\n)"
,然后使用(with-input-from-string my-modified-input read)
获得一个大列表.
我发现 Racket 的启动时间非常慢,所以如果速度是一个问题,我不建议将脚本作为循环的一部分反复调用。很容易将我的循环移动到 Racket 中并调用一次脚本。