Существует ли Awk- или Lisp-подобный язык программирования, который может обрабатывать поток s-выражений? - PullRequest
0 голосов
/ 17 января 2019

Недавно я создавал несколько следов печатной платы в KiCad, которые хранятся в файлах s-выражения с данными, которые выглядят следующим образом:

(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))

Я хотел бы иметь возможность писать однострочные оболочки для эффективного редактирования нескольких параметров. Я бы обычно использовал Awk для чего-то подобного, но рекурсивная природа s-выражений делает его неподходящим для этой задачи. Я хотел бы знать, существует ли язык программирования с интерпретатором, предназначенным для обработки переданных по трубопроводу данных и способным обрабатывать s-выражения самостоятельно. Возможно, управляемый данными диалект Lisp сделал бы это, но я не уверен, где искать.

Таким образом, я хотел бы иметь возможность быстро редактировать файл s-выражения аналогично тому, как Awk позволяет мне построчно обрабатывать столбцы данных; только в случае s-выражений обработка будет выполняться поэтапно.

Пример: найти все pad выражения типа smd с (size 0.29972 1.45034) и перенумеровать каждое из них на основе своей позиции.

Ответы [ 2 ]

0 голосов
/ 17 января 2019

Простой скрипт

Вот пример в 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* к нулю, используя with-standard-io-syntax и привязка *print-circte* к T, как предложено tfb , запрещение полностью определенных символов (с помощью #\:, сигнализирующих об ошибке), и т. д. В конечном счете, как и в случае однострочных сценариев сценариев оболочки, количество мер предосторожности, которые вы добавляете зависит от того, насколько вы доверяете своим данным:

;; 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)
0 голосов
/ 17 января 2019

Просто напишите простой скрипт на Лиспе или Схеме, который зацикливается на чтении и рекурсивно обрабатывает ваш s-expr по мере необходимости. В Linux я бы рекомендовал использовать Guile (хороший интерпретатор Scheme) или, возможно, Clisp (простая реализация Common Lisp) или даже SBCL (очень мощный Common Lisp ).

(Вы можете рассмотреть DSSSL , но в вашем случае это излишне)

Обратите внимание, что ваш пример ввода не и S-выражение , потому что (layer F.Fab) не одно (поскольку после точки у вас должно быть другое s-выражение, а не атом как Fab). Я думаю, что это опечатка и должно быть (layer "F.Fab"); или, может быть, ваше программное обеспечение KiCad не обрабатывает S-выражения, но какой-то другой язык ввода (который должен быть указан, вероятно, в EBNF нотации) вдохновлен S-выражениями.

Также обратите внимание, что KiCad является свободным программным обеспечением и имеет сообщество с форумами и списком рассылки. Возможно, вам стоит спросить о вашей настоящей проблеме?

PS. Мы не знаем, какую трансформацию вы имеете в виду, но Scheme и Common Lisp действительно подходят для таких задач. В большинстве случаев они чрезвычайно просты для кодирования (возможно, всего несколько строк).

...