awk - 是否有可以处理 s 表达式流的类似 Awk 或 Lisp 的编程语言?
问题描述
我最近在 KiCad 中创建了一些 PCB 封装,它们存储在 s-expression 文件中,数据如下所示:
(fp_text user %R (at 0 5.08) (layer F.Fab)
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_line (start -27.04996 -3.986) (end -27.24996 -3.786) (layer F.Fab) (width 0.1))
(pad "" np_thru_hole circle (at 35.56 0) (size 3.175 3.175) (drill 3.175) (layers *.Cu *.Mask)
(clearance 1.5875))
(pad 96 smd rect (at 1.25 3.08473) (size 0.29972 1.45034) (layers F.Cu F.Paste F.Mask)
(clearance 0.09906))
我希望能够编写 shell 单行代码来有效地编辑多个参数。我通常会使用 Awk 来完成类似的事情,但是 s 表达式的递归性质使其不适合该任务。我想知道是否有一种带有解释器的编程语言,用于处理管道数据并可以本地处理 s 表达式。也许 Lisp 的数据驱动方言可以做到这一点,但我不知道去哪里找。
总之,我希望能够以类似于 Awk 让我逐行处理数据列的方式对 s-expression 文件进行快速编辑;只有在 s 表达式的情况下,才会逐级执行处理。
示例:找到所有类型为 的表达式pad
,并根据每个表达式的位置重新编号。smd
(size 0.29972 1.45034)
解决方案
简单的脚本
这是 Common Lisp 中的一个示例,假设您的输入在文件中"/tmp/ex.cad"
(也可以通过读取进程的输出流来获得)。
主要处理循环包括打开文件以获得输入流in
(在结束时自动关闭with-open-file
),循环文件中的所有表单,处理它们并可能将它们输出到标准输出。您可以根据需要尽可能地复杂化该过程,但以下内容就足够了:
(with-open-file (in #"/tmp/ex.cad")
(let ((*read-eval* nil))
(ignore-errors
(loop (process-form (read in))))))
假设您想增加fp_line
条目的宽度,忽略fp_text
并打印未修改的表单,您可以定义process-form
如下:
(defun process-form (form)
(destructuring-bind (header . args) form
(print
(case header
(fp_line (let ((width (assoc 'width args)))
(when width (incf (second width) 3)))
form)
(fp_text (return-from process-form))
(t form)))))
运行前一个循环将输出:
(FP_LINE (START -27.04996 -3.986) (END -27.24996 -3.786) (LAYER F.FAB) (WIDTH 3.1))
(PAD "" NP_THRU_HOLE CIRCLE (AT 35.56 0) (SIZE 3.175 3.175) (DRILL 3.175) (LAYERS *.CU *.MASK) (CLEARANCE 1.5875))
(PAD 96 SMD RECT (AT 1.25 3.08473) (SIZE 0.29972 1.45034) (LAYERS F.CU F.PASTE F.MASK) (CLEARANCE 0.09906))
更安全
从那里,您可以根据需要在模式匹配或宏的帮助下构建更精细的管道。您必须考虑一些安全措施,例如绑定*read-eval*
到 nil、使用with-standard-io-syntax
和绑定*print-circte*
到 T 按照tfb的建议、不允许完全限定的符号(通过#\:
发出错误信号)等。最终,就像 Shell 脚本单行一样,金额您添加的预防措施取决于您对输入的信任程度:
;; Load libraries
(ql:quickload '(:alexandria :optima))
;; Import symbols in current package
(use-package :optima)
(use-package :alexandria)
;; Transform source into a stream
(defgeneric ensure-stream (source)
(:method ((source pathname)) (open source))
(:method ((source string)) (make-string-input-stream source))
(:method ((source stream)) source))
;; make reader stop on illegal characters
(defun abort-reader (&rest values)
(error "Aborting reader: ~s" values))
KiCad 符号的专用包(导出是可选的):
(defpackage :kicad
(:use)
(:export #:fp_text
#:fp_line
#:pad
#:size))
循环表单:
(defmacro do-forms ((form source &optional result) &body body)
"Loop over forms from source, eventually return result"
(with-gensyms (in form%)
`(with-open-stream (,in (ensure-stream ,source))
(with-standard-io-syntax
(let ((*read-eval* nil)
(*print-circle* t)
(*package* (find-package :kicad))
(*readtable* (copy-readtable)))
(set-macro-character #\: #'abort-reader nil)
(loop
:for ,form% := (read ,in nil ,in)
:until (eq ,form% ,in)
:do (let ((,form ,form%)) ,@body)
:finally (return ,result)))))))
例子:
;; Print lines at which there is a size parameter, and its value
(let ((line 0))
(labels ((size (alist) (second (assoc 'kicad:size alist)))
(emit (size) (when size (print `(:line ,line :size ,size))))
(process (options) (emit (size options))))
(do-forms (form #P"/tmp/ex.cad")
(match form
((list* 'kicad:fp_text _ _ options) (process options))
((list* 'kicad:fp_line options) (process options))
((list* 'kicad:pad _ _ _ options) (process options)))
(incf line))))
输出
(:LINE 2 :SIZE 3.175)
(:LINE 3 :SIZE 0.29972)
推荐阅读
- python-3.x - 此转换中要使用的日期时间格式是什么?
- python - 带有 Tensorflow 后端的 Keras --- 带有检查点回调的 model.fit() 中的内存错误
- java - Hibernate 省略 JsonManagedReference 以保持
- php - 如何从同一个 HTML 表中的第一行减去第二行值
- c# - 在 Visual Studio 中使用 Python for .NET for Python3.4
- jsf - 在 JSF 中从不同的站点执行搜索
- c++ - 运行 shell 命令并获取 PID 创建的进程
- javascript - 如何在点击时获取 wms 图层名称。我以不同的方式尝试了它,但我没有得到。我以这种方式有 3 个 wms 层
- autohotkey - AutoHotKey - 点击图片
- ios - 如何使用 setMethodCallHandler