1 <!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
2 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6 <link rel=
"stylesheet" media=
"screen" type=
"text/css" href=
"./style.css" />
7 <link rel=
"stylesheet" media=
"screen" type=
"text/css" href=
"./design.css" />
8 <link rel=
"stylesheet" media=
"print" type=
"text/css" href=
"./print.css" />
10 <meta http-equiv=
"Content-Type" content=
"text/html; charset=utf-8" />
15 <h1 class=
"sectionedit1452"><a name=
"написание_скриптов_драйверов_gnetlist_на_scheme" id=
"написание_скриптов_драйверов_gnetlist_на_scheme">Написание скриптов драйверов gnetlist на Scheme
</a></h1>
19 <strong>Автор:
<em>John Doty
</em></strong>
23 (первоначально это было
24 <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> в
25 список рассылки gEDA-user в июле
2009 г.)
33 Если ты никогда не писал программы на
<strong>Lisp
</strong>, это выглядит страшновато. Но
34 это намного легче, чем кажется. Добавь в
<strong>Lisp
</strong> чуть-чуть синтаксического
35 сахара
<sup><a href=
"#fn__1" name=
"fnt__1" id=
"fnt__1" class=
"fn_top">1)
</a></sup> и он превращается в
36 <strong>Logo
</strong>, который могут изучить даже дети из начальной школы.
40 И просто для объяснения значения некоторых из этих странных слов:
41 <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> — компьютерный язык,
42 <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>'а,
43 и
<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>.
44 <strong>Guile
</strong> в gEDA используется для написания скриптов. В частности,
45 оболочка
<strong>gnetlist
</strong>, написанная на
<strong>C
</strong>, выделяет из схем информацию о
46 топологии и атрибутах, а затем отдаёт данные низкоуровневым скриптам
47 (драйверам) на
<strong>Guile
</strong> для обработки и вывода.
51 Это руководство именно по программированию драйверов
52 <strong>gnetlist
</strong> на
<strong>Scheme
</strong>. Если ты ещё не знаешь
<strong>Scheme
</strong>, тебе, наверно,
53 стоит взглянуть и на другие материалы, такие как:
57 <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>
61 Или поищи “Учебник по Scheme” в своём любимом поисковике: их много.
65 Также может пригодиться справочный документ по адресу:
69 <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>
73 Итак, начнём. Вот очень простой драйвер:
75 <pre class=
"code lisp"><span class=
"co1">;; gnetlist development playground
</span>
77 <span class=
"br0">(</span>use-modules
<span class=
"br0">(</span>ice-
<span class=
"nu0">9</span> readline
<span class=
"br0">)</span><span class=
"br0">)</span>
78 <span class=
"br0">(</span>activate-readline
<span class=
"br0">)</span>
80 <span class=
"br0">(</span>define
<span class=
"br0">(</span>devel output-filename
<span class=
"br0">)</span>
81 <span class=
"br0">(</span>scm-style-repl
<span class=
"br0">)</span>
82 <span class=
"br0">)</span></pre>
85 Чтобы это применить, сохрани всё в файле
<em><code>gnet-devel.scm
</code></em>. Скопируй этот
86 файл туда, где в твоей системе хранятся файлы
<strong>Scheme
</strong>. На машине, на
87 которой я сейчас работаю, команда такова:
89 <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>
92 <em><code>/sw/
</code></em> для многих устанавливаемых в Linux пакетов надо
93 заменить на
<em><code>/usr/
</code></em>, может быть на
<em><code>/usr/local
</code></em>, или — при
94 установке из tar-архива — на
<em><code>~/mygeda/
</code></em>. Это нужно выяснить. Если ты
95 можешь записывать в целевой каталог без прав суперпользователя,
<strong><code>sudo
</code></strong>
100 Теперь, изменив нужным образом
<em><code>/sw/
</code></em>, набери:
102 <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>
105 Ты должен увидеть обычный текст стандартного приглашения, за которым следует:
107 <pre class=
"code">guile
></pre>
112 <pre class=
"code">guile
> packages
</pre>
117 <pre class=
"code lisp"><span class=
"br0">(</span><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><span class=
"br0">)</span></pre>
120 <code>packages
</code> — удобная переменная, содержащая список всех уникальных
121 значений атрибутов
<code>refdes=
</code>. Набрав её, ты скормил её “REPL” — циклу
122 чтения, оценки, вывода (Read, Evaluate, Print Loop). Итак,
123 REPL считал её, оценил (создав список) и вывел.
129 <pre class=
"code">guile
> (length packages)
133 Что здесь произошло? Здесь REPL оценил список.
135 <pre class=
"code lisp"><span class=
"br0">(</span><span class=
"kw1">length
</span> packages
<span class=
"br0">)</span></pre>
138 В большинстве языков программирования ты бы написал это выражение в более
139 традиционной функциональной записи:
<code>length(packages)
</code>.
<code>length
</code> — это
140 функция, которая сообщит тебе длину списка.
144 Такая же запись используется для арифметических вычислений. Например, “
2+
3”
147 <pre class=
"code">guile
> (+
2 3)
151 Учти, что процедура
"+
" может использоваться для сложения любого
152 количества величин, в том числе и совсем ни одной:
154 <pre class=
"code">guile
> (+)
160 Это мы используем позже.
164 Строки про
<code>readline
</code> в нашем драйвере
<em><code>gnet-devel.scm
</code></em> позволят тебе
165 пользоваться стрелками на клавиатуре для перемещения по истории и для
166 редактирования вводимых строк. Очень удобно в интерактивном режиме. Попробуй.
170 Другая полезная переменная, определённая в
<strong>gnetlist
</strong>, это
171 <code>all-unique-nets
</code> (набери это). Точно так же как
<code>(length packages)
</code>
172 говорит тебе, сколько у тебя компонентов,
<code>(length all-unique-nets)
</code>
173 подскажет, сколько у тебя соединений.
177 Ещё есть
<code>all-pins
</code>:
179 <pre class=
"code">guile
> all-pins
180 ((
"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>
183 Заметь, это немного сложнее, чем в предыдущих примерах: это список списков, а
184 не просто список строк. Каждый из списков соответствует выводам компонента.
185 Есть одна штука, которую мы могли бы вытащить отсюда, — это подсчёт
186 количества выводов. Мы не можем просто взять длину
<code>all-pins
</code>, чтобы
187 получить его: это даст нам только количество списков, содержащихся там, равное
188 количеству компонентов:
190 <pre class=
"code">guile
> (length all-pins)
194 Чтобы посчитать количество выводов, сначала посчитаем их количество для
195 каждого из компонентов в отдельности:
197 <pre class=
"code">guile
> (map length all-pins)
198 (
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>
201 Это один из простых способов сделать “цикл” на
<strong>Scheme
</strong>;
<code>(map p x)
</code>
202 выдаёт список результатов вызываемой процедуры
<code>p
</code> отдельно для каждого
203 элемента из
<code>x
</code>. Затем мы можем их сложить с помощью “цикла” несколько иного
206 <pre class=
"code">guile
> (apply + (map length all-pins))
210 <code>(apply p x)
</code> вызывает процедуру
<code>p
</code> один раз, с аргументами из всех
211 элементов из
<code>x
</code>. Поэтому вышеуказанное выражение в конце концов посчитает
214 <pre class=
"code lisp"><span class=
"br0">(</span>+
<span class=
"nu0">3</span> <span class=
"nu0">3</span> <span class=
"nu0">2</span> <span class=
"nu0">2</span> <span class=
"nu0">2</span> <span class=
"nu0">2</span> <span class=
"nu0">2</span> <span class=
"nu0">2</span> <span class=
"nu0">2</span> <span class=
"nu0">2</span> <span class=
"nu0">2</span> <span class=
"nu0">2</span> <span class=
"nu0">2</span> <span class=
"nu0">2</span> <span class=
"nu0">1</span> <span class=
"nu0">1</span> <span class=
"nu0">2</span> <span class=
"nu0">3</span> <span class=
"nu0">3</span> <span class=
"nu0">1</span> <span class=
"nu0">2</span> <span class=
"nu0">3</span> <span class=
"nu0">2</span> <span class=
"nu0">1</span> <span class=
"nu0">1</span><span class=
"br0">)</span></pre>
217 До сих пор мы использовали предопределённые переменные и процедуры. Но мы бы
218 хотели иметь возможность определять свои. Это просто:
220 <pre class=
"code">guile
> (define the-answer
42)
225 Это определяет переменную
<code>the-answer
</code> и задаёт ей значение
42.
229 Можно также определять процедуры:
231 <pre class=
"code">guile
> (define add1 (lambda (x) (+ x
1)))
236 Когда видишь
<code>lambda
</code>, думай — “процедура”. Сразу следом за
<code>lambda
</code> идёт
237 первый элемент (технический термин — “выражение”
<sup><a href=
"#fn__2" name=
"fnt__2" id=
"fnt__2" class=
"fn_top">2)
</a></sup>) — список аргументов процедуры, в данном случае
238 <code>(x)
</code>. Когда вызывается процедура,
<strong>Guile
</strong> оценивает оставшиеся выражения,
239 в данном случае только одно,
<code>(+ x
1)
</code>, с подстановкой текущих аргументов.
240 Результат процедуры — это результат оценки последнего выражения. Так,
241 <code>(add1
100)
</code> становится
<code>(+
100 1)
</code>, что даёт
101.
245 Теперь мы можем объединить наш сбор статистики в драйвер.
246 Сначала определим процедуру для записи выходной строки:
248 <pre class=
"code lisp"><span class=
"br0">(</span>define format-line
249 <span class=
"br0">(</span><span class=
"kw1">lambda
</span> <span class=
"br0">(</span><span class=
"kw1">name
</span> <span class=
"kw1">value
</span><span class=
"br0">)</span>
250 <span class=
"br0">(</span>display
<span class=
"kw1">name
</span><span class=
"br0">)</span>
251 <span class=
"br0">(</span>display
<span class=
"kw1">value
</span><span class=
"br0">)</span>
252 <span class=
"br0">(</span>newline
<span class=
"br0">)</span>
253 <span class=
"br0">)</span>
254 <span class=
"br0">)</span></pre>
257 Здесь мы используем две новых встроенных процедуры,
<code>display
</code> и
<code>newline
</code>,
258 названия которых говорят сами за себя. Теперь:
260 <pre class=
"code lisp"><span class=
"br0">(</span>define display-stats
261 <span class=
"br0">(</span><span class=
"kw1">lambda
</span> <span class=
"br0">(</span><span class=
"br0">)</span> <span class=
"co1">; без аргументов
</span>
262 <span class=
"br0">(</span>format-line
<span class=
"st0">"pins:
"</span> <span class=
"br0">(</span><span class=
"kw1">apply
</span><span class=
"sy0"> +
</span><span class=
"br0">(</span>map
<span class=
"kw1">length
</span> all-pins
<span class=
"br0">)</span><span class=
"br0">)</span><span class=
"br0">)</span>
263 <span class=
"br0">(</span>format-line
<span class=
"st0">"packages:
"</span> <span class=
"br0">(</span><span class=
"kw1">length
</span> packages
<span class=
"br0">)</span><span class=
"br0">)</span>
264 <span class=
"br0">(</span>format-line
<span class=
"st0">"nets:
"</span> <span class=
"br0">(</span><span class=
"kw1">length
</span> all-unique-nets
<span class=
"br0">)</span><span class=
"br0">)</span>
265 <span class=
"br0">)</span>
266 <span class=
"br0">)</span></pre>
267 <pre class=
"code">guile
> (display-stats)
273 Чтобы завершить драйвер, нам нужна “основная программа”. По соглашению она
274 называется так же, как и сам драйвер. Также она отвечает за открывание выходного файла.
275 Итак, целиком файл драйвера сбора статистики “stats” будет выглядеть
278 <pre class=
"code lisp"><span class=
"co1">;; драйвер gnetlist для получения статистики по проекту
</span>
279 <span class=
"co1">;;
</span>
280 <span class=
"co1">;; Стандартный текст лицензии, как положено
</span>
282 <span class=
"br0">(</span>define stats
283 <span class=
"br0">(</span><span class=
"kw1">lambda
</span> <span class=
"br0">(</span>filename
<span class=
"br0">)</span>
284 <span class=
"br0">(</span>set-current-output-port
<span class=
"br0">(</span>open-output-file filename
<span class=
"br0">)</span><span class=
"br0">)</span>
285 <span class=
"br0">(</span>display-stats
<span class=
"br0">)</span>
286 <span class=
"br0">)</span>
287 <span class=
"br0">)</span>
289 <span class=
"co1">;; Сбор и вывод статистики
</span>
291 <span class=
"br0">(</span>define display-stats
292 <span class=
"br0">(</span><span class=
"kw1">lambda
</span> <span class=
"br0">(</span><span class=
"br0">)</span> <span class=
"co1">; без аргументов
</span>
293 <span class=
"br0">(</span>format-line
<span class=
"st0">"pins:
"</span> <span class=
"br0">(</span><span class=
"kw1">apply
</span><span class=
"sy0"> +
</span><span class=
"br0">(</span>map
<span class=
"kw1">length
</span> all-pins
<span class=
"br0">)</span><span class=
"br0">)</span><span class=
"br0">)</span>
294 <span class=
"br0">(</span>format-line
<span class=
"st0">"packages:
"</span> <span class=
"br0">(</span><span class=
"kw1">length
</span> packages
<span class=
"br0">)</span><span class=
"br0">)</span>
295 <span class=
"br0">(</span>format-line
<span class=
"st0">"nets:
"</span> <span class=
"br0">(</span><span class=
"kw1">length
</span> all-unique-nets
<span class=
"br0">)</span><span class=
"br0">)</span>
296 <span class=
"br0">)</span>
297 <span class=
"br0">)</span>
299 <span class=
"co1">;; Простой формат вывода
</span>
301 <span class=
"br0">(</span>define format-line
302 <span class=
"br0">(</span><span class=
"kw1">lambda
</span> <span class=
"br0">(</span><span class=
"kw1">name
</span> <span class=
"kw1">value
</span><span class=
"br0">)</span>
303 <span class=
"br0">(</span>display
<span class=
"kw1">name
</span><span class=
"br0">)</span>
304 <span class=
"br0">(</span>display
<span class=
"kw1">value
</span><span class=
"br0">)</span>
305 <span class=
"br0">(</span>newline
<span class=
"br0">)</span>
306 <span class=
"br0">)</span>
307 <span class=
"br0">)</span></pre>
310 Сохрани это в файле с именем
<code>gnet-stats.scm
</code>, скопируй его в надлежащее
313 <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>
316 и затем
<strong><code>gnetlist -g stats
</code></strong> с другими обычными аргументами и именами
317 схем выдаст статистику твоего проекта в выходной файл (по умолчанию
318 <em><code>output.net
</code></em>).
322 Довольно просто, а? А также полезно. Недавно я проектировал системы, состоящие
323 из множества плат: статистика, подобная этой, помогает мне выяснить, какие
324 подсистемы лучше скомбинировать на каждой из плат.
328 <div class=
"footnotes">
329 <div class=
"fn"><sup><a href=
"#fnt__1" id=
"fn__1" name=
"fn__1" class=
"fn_bot">1)
</a></sup>
330 “Синтаксический сахар” — конструкция языка программирования,
331 полностью эквивалентная другой его конструкции, но имеющая более естественную
332 запись (Компьютерный словарь). —
<em>Прим. перев.
</em></div>
333 <div class=
"fn"><sup><a href=
"#fnt__2" id=
"fn__2" name=
"fn__2" class=
"fn_bot">2)
</a></sup>
334 Англоязычный термин —
335 “form”. —
<em>Прим. перев.
</em></div>