首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >有没有一种类似Awk或Lisp的编程语言可以处理s-expression流?

有没有一种类似Awk或Lisp的编程语言可以处理s-expression流?
EN

Stack Overflow用户
提问于 2019-01-17 13:57:51
回答 2查看 331关注 0票数 6

我最近在KiCad中创建了一些印刷电路板封装外形,这些封装外形存储在s-expression文件中,数据如下:

代码语言:javascript
复制
(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-expression的递归性质使得它不适合这个任务。我想知道是否有一种带有解释器的编程语言可以处理管道数据,并且可以本机处理s表达式。也许数据驱动的Lisp方言可以做到这一点,但我不确定从哪里可以看到。

总之,我希望能够以类似于Awk允许我逐行处理数据列的方式对s-expression文件进行快速编辑;只有在s-expression的情况下,才会逐级执行处理。

示例:使用(size 0.29972 1.45034)查找所有类型为smdpad表达式,并根据其位置对每个表达式重新编号。

EN

回答 2

Stack Overflow用户

发布于 2019-01-17 16:59:16

简单的脚本

下面是一个用Common Lisp编写的示例,假设您的输入在文件"/tmp/ex.cad"中(也可以通过读取进程的输出流来获得)。

主处理循环包括打开文件以获得输入流in (在with-open-file结束时自动关闭),循环遍历文件中的所有表单,处理它们并可能将它们输出到标准输出。您可以根据需要将流程复杂化,但以下内容就足够好了:

代码语言:javascript
复制
(with-open-file (in #"/tmp/ex.cad")
  (let ((*read-eval* nil))
     (ignore-errors
       (loop (process-form (read in))))))

假设您想要增加fp_line条目的宽度,忽略fp_text,或者原封不动地打印表单,您可以按如下方式定义process-form

代码语言:javascript
复制
(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)))))

然后,运行前一个循环将输出:

代码语言:javascript
复制
(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并按照tfb的建议将*print-circte*绑定到T,禁止完全限定的符号(通过让#\:发出错误信号)等。最终,像Shell脚本一行程序一样,您添加的预防措施的数量取决于您对输入的信任程度:

代码语言:javascript
复制
;; 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符号专用包(导出是可选的):

代码语言:javascript
复制
(defpackage :kicad
  (:use)
  (:export #:fp_text
           #:fp_line
           #:pad
           #:size))

循环遍历窗体:

代码语言:javascript
复制
(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)))))))

示例:

代码语言:javascript
复制
;; 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))))

输出

代码语言:javascript
复制
(:LINE 2 :SIZE 3.175) 
(:LINE 3 :SIZE 0.29972)
票数 4
EN

Stack Overflow用户

发布于 2019-01-17 14:11:10

只需编写一个简单的Lisp或Scheme脚本,它会在读取时循环,并根据需要递归处理s-expr。在Linux上,我推荐使用Guile (一个很好的Scheme解释器),或者Clisp (一个简单的Common Lisp实现),甚至SBCL (一个非常强大的Common Lisp)。

(您可能会考虑使用DSSSL,但在您的情况下,使用它太过分了)

注意,您的示例输入不是一个S-expression,因为(layer F.Fab)不是一个表达式(因为在点后面应该有另一个s表达式,而不是像Fab这样的原子)。我猜这是一个拼写错误,应该是(layer "F.Fab");或者也许您的KiCad软件不处理S-expressions,而是受S-expressions启发的其他输入语言(应该指定,可能是用EBNF表示法)。

还要注意的是,KiCad是一个自由软件,并且有一个forums社区和一个邮件列表。也许你应该在那里问一下你的实际问题?

PS。我们不知道您的想法是什么转换,但是Scheme和Common Lisp非常适合这样的任务。在大多数情况下,它们的代码非常简单(可能只有几行代码)。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54229826

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档