我最近在KiCad中创建了一些PCB封装,它们存储在s-expression文件中,其数据如下所示:
(fp_text user%R(at 0 5.08)(layer F.Fab) (效果(字体(大小1 ……)
这是Common Lisp中的一个示例,假设您的输入在文件中 "/tmp/ex.cad" (也可以通过读取进程的输出流来获得)。
"/tmp/ex.cad"
主处理循环包括打开文件以获得输入流 in (在结束时自动关闭 with-open-file ),遍历文件中的所有表单,处理它们并可能将它们输出到标准输出。您可以根据需要复杂化流程,但以下内容足够好:
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 如下:
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* 没有,使用 with-standard-io-syntax 和绑定 *print-circte* 如建议的那样 TFB ,禁止完全合格的符号(通过 #\: 最后,就像Shell脚本单行一样,您添加的预防措施的数量取决于您对输入的信任程度:
*read-eval*
with-standard-io-syntax
*print-circte*
#\:
;; 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)