<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title></title> <link rel="stylesheet" media="screen" type="text/css" href="./style.css" /> <link rel="stylesheet" media="screen" type="text/css" href="./design.css" /> <link rel="stylesheet" media="print" type="text/css" href="./print.css" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body> <h1 class="sectionedit1426"><a name="написание_скриптов_драйверов_gnetlist_на_scheme" id="написание_скриптов_драйверов_gnetlist_на_scheme">Написание скриптов драйверов gnetlist на Scheme</a></h1> <div class="level1"> <p> <strong>Автор: <em>John Doty</em></strong> </p> <p> (первоначально это было <a href="http://archives.seul.org/geda/user/Jul-2009/msg00235.html" class="urlextern" title="http://archives.seul.org/geda/user/Jul-2009/msg00235.html" rel="nofollow">отправлено</a> в список рассылки gEDA-user в июле 2009 г.) </p> <p> Не паникуй! </p> <p> Если ты никогда не писал программы на <strong>Lisp</strong>, это выглядит страшновато. Но это намного легче, чем кажется. Добавь в <strong>Lisp</strong> чуть-чуть синтаксического сахара<sup><a href="#fn__1" name="fnt__1" id="fnt__1" class="fn_top">1)</a></sup> и он превращается в <strong>Logo</strong>, который могут изучить даже дети из начальной школы. </p> <p> И просто для объяснения значения некоторых из этих странных слов: <a href="http://en.wikipedia.org/wiki/Lisp_(programming_language)" class="interwiki iw_wp" title="http://en.wikipedia.org/wiki/Lisp_(programming_language)">Lisp</a> — компьютерный язык, <a href="http://en.wikipedia.org/wiki/Scheme_(programming_language)" class="interwiki iw_wp" title="http://en.wikipedia.org/wiki/Scheme_(programming_language)">Scheme</a> — диалект <strong>Lisp</strong>'а, и <a href="http://en.wikipedia.org/wiki/GNU_Guile" class="interwiki iw_wp" title="http://en.wikipedia.org/wiki/GNU_Guile">Guile</a> — реализация <strong>Scheme</strong>. <strong>Guile</strong> в gEDA используется для написания скриптов. В частности, оболочка <strong>gnetlist</strong>, написанная на <strong>C</strong>, выделяет из схем информацию о топологии и атрибутах, а затем отдаёт данные низкоуровневым скриптам (драйверам) на <strong>Guile</strong> для обработки и вывода. </p> <p> Это руководство именно по программированию драйверов <strong>gnetlist</strong> на <strong>Scheme</strong>. Если ты ещё не знаешь <strong>Scheme</strong>, тебе, наверно, стоит взглянуть и на другие материалы, такие как: </p> <p> <a href="http://www.ccs.neu.edu/home/dorai/t-y-scheme/t-y-scheme.html" class="urlextern" title="http://www.ccs.neu.edu/home/dorai/t-y-scheme/t-y-scheme.html" rel="nofollow">http://www.ccs.neu.edu/home/dorai/t-y-scheme/t-y-scheme.html</a> </p> <p> Или поищи “Учебник по Scheme” в своём любимом поисковике: их много. </p> <p> Также может пригодиться справочный документ по адресу: </p> <p> <a href="http://www.gnu.org/software/guile/manual/html_node/index.html" class="urlextern" title="http://www.gnu.org/software/guile/manual/html_node/index.html" rel="nofollow">http://www.gnu.org/software/guile/manual/html_node/index.html</a> </p> <p> Итак, начнём. Вот очень простой драйвер: </p> <pre class="code scheme"><span class="co1">;; gnetlist development playground</span> </pre> <p> Чтобы это применить, сохрани всё в файле <em><code>gnet-devel.scm</code></em>. Скопируй этот файл туда, где в твоей системе хранятся файлы <strong>Scheme</strong>. На машине, на которой я сейчас работаю, команда такова: </p> <pre class="code bash">$ <span class="kw2">sudo</span> <span class="kw2">cp</span> gnet-devel.scm <span class="sy0">/</span>sw<span class="sy0">/</span>share<span class="sy0">/</span>gEDA<span class="sy0">/</span>scheme<span class="sy0">/</span></pre> <p> <em><code>/sw/</code></em> для многих устанавливаемых в Linux пакетов надо заменить на <em><code>/usr/</code></em>, может быть на <em><code>/usr/local</code></em>, или — при установке из tar-архива — на <em><code>~/mygeda/</code></em>. Это нужно выяснить. Если ты можешь записывать в целевой каталог без прав суперпользователя, <strong><code>sudo</code></strong> не нужно. </p> <p> Теперь, изменив нужным образом <em><code>/sw/</code></em>, набери: </p> <pre class="code bash">$ gnetlist <span class="re5">-g</span> devel <span class="sy0">/</span>sw<span class="sy0">/</span>share<span class="sy0">/</span>gEDA<span class="sy0">/</span>examples<span class="sy0">/</span>lightning_detector<span class="sy0">/</span>lightning.sch</pre> <p> Ты должен увидеть обычный текст стандартного приглашения, за которым следует: </p> <pre class="code">guile></pre> <p> Попробуй: </p> <pre class="code">guile> packages</pre> <p> Ты должен увидеть: </p> <pre class="code scheme"><span class="st0">"Q3"</span><span class="st0">"R5"</span><span class="st0">"Q2"</span><span class="st0">"R4"</span><span class="st0">"Q1"</span><span class="st0">"C6"</span><span class="st0">"R3"</span><span class="st0">"L2"</span><span class="st0">"A1"</span><span class="st0">"bat(+3v)"</span><span class="st0">"lamp(1)"</span><span class="st0">"R2"</span><span class="st0">"C5"</span><span class="st0">"L1"</span><span class="st0">"R1"</span><span class="st0">"C4"</span><span class="st0">"lamp(2)"</span><span class="st0">"C3"</span><span class="st0">"C2"</span><span class="st0">"C1"</span><span class="st0">"D1"</span><span class="st0">"bat(0v)"</span><span class="st0">"R7"</span><span class="st0">"Q4"</span><span class="st0">"R6"</span></pre> <p> <code>packages</code> — удобная переменная, содержащая список всех уникальных значений атрибутов <code>refdes=</code>. Набрав её, ты скормил её “REPL” — циклу чтения, оценки, вывода (Read, Evaluate, Print Loop). Итак, REPL считал её, оценил (создав список) и вывел. </p> <p> Теперь попробуй: </p> <pre class="code">guile> (length packages) 25</pre> <p> Что здесь произошло? Здесь REPL оценил список. </p> <pre class="code scheme"> </pre> <p> В большинстве языков программирования ты бы написал это выражение в более традиционной функциональной записи: <code>length(packages)</code>. <code>length</code> — это функция, которая сообщит тебе длину списка. </p> <p> Такая же запись используется для арифметических вычислений. Например, “2+3” вычисляется так: </p> <pre class="code">guile> (+ 2 3) 5</pre> <p> Учти, что процедура "+" может использоваться для сложения любого количества величин, в том числе и совсем ни одной: </p> <pre class="code">guile> (+) 0 guile> (+ 1 2 3) 6</pre> <p> Это мы используем позже. </p> <p> Строки про <code>readline</code> в нашем драйвере <em><code>gnet-devel.scm</code></em> позволят тебе пользоваться стрелками на клавиатуре для перемещения по истории и для редактирования вводимых строк. Очень удобно в интерактивном режиме. Попробуй. </p> <p> Другая полезная переменная, определённая в <strong>gnetlist</strong>, это <code>all-unique-nets</code> (набери это). Точно так же как <code>(length packages)</code> говорит тебе, сколько у тебя компонентов, <code>(length all-unique-nets)</code> подскажет, сколько у тебя соединений. </p> <p> Ещё есть <code>all-pins</code>: </p> <pre class="code">guile> all-pins (("1" "2" "3") ("2" "3" "1") ("2" "1") ("1" "2") ("1" "2") ("1" "2") ("1" "2") ("1" "2") ("1" "2") ("2" "1") ("2" "1") ("2" "1") ("1" "2") ("2" "1") ("1") ("1") ("2" "1") ("2" "3" "1") ("2" "3" "1") ("1") ("2" "1") ("2" "3" "1") ("1" "2") ("1") ("1"))</pre> <p> Заметь, это немного сложнее, чем в предыдущих примерах: это список списков, а не просто список строк. Каждый из списков соответствует выводам компонента. Есть одна штука, которую мы могли бы вытащить отсюда, — это подсчёт количества выводов. Мы не можем просто взять длину <code>all-pins</code>, чтобы получить его: это даст нам только количество списков, содержащихся там, равное количеству компонентов: </p> <pre class="code">guile> (length all-pins) 25</pre> <p> Чтобы посчитать количество выводов, сначала посчитаем их количество для каждого из компонентов в отдельности: </p> <pre class="code">guile> (map length all-pins) (3 3 2 2 2 2 2 2 2 2 2 2 2 2 1 1 2 3 3 1 2 3 2 1 1)</pre> <p> Это один из простых способов сделать “цикл” на <strong>Scheme</strong>; <code>(map p x)</code> выдаёт список результатов вызываемой процедуры <code>p</code> отдельно для каждого элемента из <code>x</code>. Затем мы можем их сложить с помощью “цикла” несколько иного типа: </p> <pre class="code">guile> (apply + (map length all-pins)) 50</pre> <p> <code>(apply p x)</code> вызывает процедуру <code>p</code> один раз, с аргументами из всех элементов из <code>x</code>. Поэтому вышеуказанное выражение в конце концов посчитает следующее: </p> <pre class="code scheme"> </pre> <p> До сих пор мы использовали предопределённые переменные и процедуры. Но мы бы хотели иметь возможность определять свои. Это просто: </p> <pre class="code">guile> (define the-answer 42) guile> the-answer 42</pre> <p> Это определяет переменную <code>the-answer</code> и задаёт ей значение 42. </p> <p> Можно также определять процедуры: </p> <pre class="code">guile> (define add1 (lambda (x) (+ x 1))) guile> (add1 100) 101</pre> <p> Когда видишь <code>lambda</code>, думай — “процедура”. Сразу следом за <code>lambda</code> идёт первый элемент (технический термин — “выражение”<sup><a href="#fn__2" name="fnt__2" id="fnt__2" class="fn_top">2)</a></sup>) — список аргументов процедуры, в данном случае <code>(x)</code>. Когда вызывается процедура, <strong>Guile</strong> оценивает оставшиеся выражения, в данном случае только одно, <code>(+ x 1)</code>, с подстановкой текущих аргументов. Результат процедуры — это результат оценки последнего выражения. Так, <code>(add1 100)</code> становится <code>(+ 100 1)</code>, что даёт 101. </p> <p> Теперь мы можем объединить наш сбор статистики в драйвер. Сначала определим процедуру для записи выходной строки: </p> <pre class="code scheme"> </pre> <p> Здесь мы используем две новых встроенных процедуры, <code>display</code> и <code>newline</code>, названия которых говорят сами за себя. Теперь: </p> <pre class="code scheme"><span class="co1">; без аргументов</span> <span class="st0">"pins: "</span><span class="st0">"packages: "</span><span class="st0">"nets: "</span></pre> <pre class="code">guile> (display-stats) pins: 50 packages: 25 nets: 13</pre> <p> Чтобы завершить драйвер, нам нужна “основная программа”. По соглашению она называется так же, как и сам драйвер. Также она отвечает за открывание выходного файла. Итак, целиком файл драйвера сбора статистики “stats” будет выглядеть примерно так: </p> <pre class="code scheme"><span class="co1">;; драйвер gnetlist для получения статистики по проекту</span> <span class="co1">;;</span> <span class="co1">;; Стандартный текст лицензии, как положено</span> <span class="co1">;; Сбор и вывод статистики</span> <span class="co1">; без аргументов</span> <span class="st0">"pins: "</span><span class="st0">"packages: "</span><span class="st0">"nets: "</span><span class="co1">;; Простой формат вывода</span> </pre> <p> Сохрани это в файле с именем <code>gnet-stats.scm</code>, скопируй его в надлежащее место, например так: </p> <pre class="code bash">$ <span class="kw2">sudo</span> <span class="kw2">cp</span> gnet-stats.scm <span class="sy0">/</span>sw<span class="sy0">/</span>share<span class="sy0">/</span>gEDA<span class="sy0">/</span>scheme<span class="sy0">/</span></pre> <p> и затем <strong><code>gnetlist -g stats</code></strong> с другими обычными аргументами и именами схем выдаст статистику твоего проекта в выходной файл (по умолчанию <em><code>output.net</code></em>). </p> <p> Довольно просто, а? А также полезно. Недавно я проектировал системы, состоящие из множества плат: статистика, подобная этой, помогает мне выяснить, какие подсистемы лучше скомбинировать на каждой из плат. </p> </div> <div class="footnotes"> <div class="fn"><sup><a href="#fnt__1" id="fn__1" name="fn__1" class="fn_bot">1)</a></sup> “Синтаксический сахар” — конструкция языка программирования, полностью эквивалентная другой его конструкции, но имеющая более естественную запись (Компьютерный словарь). — <em>Прим. перев.</em></div> <div class="fn"><sup><a href="#fnt__2" id="fn__2" name="fn__2" class="fn_bot">2)</a></sup> Англоязычный термин — “form”. — <em>Прим. перев.</em></div> </div> </body> </html>