1 <!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Transitional//EN"
2 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
5 <link rel=
"stylesheet" media=
"screen" type=
"text/css" href=
"./style.css" />
6 <link rel=
"stylesheet" media=
"screen" type=
"text/css" href=
"./design.css" />
7 <link rel=
"stylesheet" media=
"print" type=
"text/css" href=
"./print.css" />
9 <meta http-equiv=
"Content-Type" content=
"text/html; charset=utf-8" />
14 <em>Это руководство доступно также на следующих языках:
</em>
15 <a href=
"geda-gnetlist_scheme_tutorial.html" class=
"wikilink1" title=
"geda-gnetlist_scheme_tutorial.html">English
</a>
18 <h1 class=
"sectionedit1" id=
"написание_скриптов_драйверов_gnetlist_на_scheme">Написание скриптов драйверов gnetlist на Scheme
</h1>
22 <strong>Автор:
<em>John Doty
</em></strong>
26 (первоначально это было
27 <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> в
28 список рассылки gEDA-user в июле
2009 г.)
36 Если ты никогда не писал программы на
<strong>Lisp
</strong>, это выглядит страшновато. Но
37 это намного легче, чем кажется. Добавь в
<strong>Lisp
</strong> чуть-чуть синтаксического
38 сахара
<sup><a href=
"#fn__26" id=
"fnt__26" class=
"fn_top">26)
</a></sup> и он
39 превращается в
<strong>Logo
</strong>, который могут изучить даже дети из начальной школы.
43 И просто для объяснения значения некоторых из этих странных слов:
44 <a href=
"https://en.wikipedia.org/wiki/Lisp_(programming_language)" class=
"interwiki iw_wp" title=
"https://en.wikipedia.org/wiki/Lisp_(programming_language)">Lisp
</a> — компьютерный язык,
45 <a href=
"https://en.wikipedia.org/wiki/Scheme_(programming_language)" class=
"interwiki iw_wp" title=
"https://en.wikipedia.org/wiki/Scheme_(programming_language)">Scheme
</a> — диалект
<strong>Lisp
</strong>'а, и
46 <a href=
"https://en.wikipedia.org/wiki/GNU_Guile" class=
"interwiki iw_wp" title=
"https://en.wikipedia.org/wiki/GNU_Guile">Guile
</a> — реализация
<strong>Scheme
</strong>.
<strong>Guile
</strong> в gEDA
47 используется для написания скриптов. В частности, оболочка
<strong>gnetlist
</strong>,
48 написанная на
<strong>C
</strong>, выделяет из схем информацию о топологии и атрибутах, а
49 затем отдаёт данные низкоуровневым скриптам (драйверам) на
<strong>Guile
</strong> для
54 Это руководство именно по программированию драйверов
<strong>gnetlist
</strong> на
55 <strong>Scheme
</strong>. Если ты ещё не знаешь
<strong>Scheme
</strong>, тебе, наверно, стоит взглянуть
56 и на другие материалы, такие как:
60 <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>
64 Или поищи «Учебник по Scheme» в своём любимом поисковике: их много.
68 Также может пригодиться справочный документ по адресу:
72 <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>
76 Итак, начнём. Вот очень простой драйвер:
78 <pre class=
"code lisp"><span class=
"co1">;; gnetlist development playground
</span>
80 <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>
81 <span class=
"br0">(</span>activate-readline
<span class=
"br0">)</span>
83 <span class=
"br0">(</span>define
<span class=
"br0">(</span>devel output-filename
<span class=
"br0">)</span>
84 <span class=
"br0">(</span>scm-style-repl
<span class=
"br0">)</span>
85 <span class=
"br0">)</span></pre>
88 Чтобы это применить, сохрани всё в файле
<em><code>gnet-devel.scm
</code></em>. Скопируй
89 этот файл туда, где в твоей системе хранятся файлы
<strong>Scheme
</strong>. На машине, на
90 которой я сейчас работаю, команда такова:
92 <pre class=
"code bash"><span class=
"co4">$
</span><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>
95 <em><code>/sw/
</code></em> для многих устанавливаемых в Linux пакетов надо заменить на
96 <em><code>/usr/
</code></em>, может быть на
<em><code>/usr/local
</code></em>, или — при установке из
97 tar-архива — на
<em><code>~/mygeda/
</code></em>. Это нужно выяснить. Если ты можешь
98 записывать в целевой каталог без прав суперпользователя,
<strong><code>sudo
</code></strong> не
103 Теперь, изменив нужным образом
<em><code>/sw/
</code></em>, набери:
105 <pre class=
"code bash"><span class=
"co4">$
</span>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>
108 Ты должен увидеть обычный текст стандартного приглашения, за которым
111 <pre class=
"code">guile
></pre>
116 <pre class=
"code">guile
> packages
</pre>
121 <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>
124 <code>packages
</code> — удобная переменная, содержащая список всех уникальных
125 значений атрибутов
<code>refdes=
</code>. Набрав её, ты скормил её «REPL» — циклу
126 чтения, вычисления, вывода (Read, Evaluate, Print Loop). Итак, REPL считал
127 её, вычислил (получив список) и вывел.
133 <pre class=
"code">guile
> (length packages)
137 Что здесь произошло? Здесь REPL вычислил список.
139 <pre class=
"code lisp"><span class=
"br0">(</span><span class=
"kw1">length
</span> packages
<span class=
"br0">)</span></pre>
142 В большинстве языков программирования ты бы написал это выражение в более
143 традиционной функциональной записи:
<code>length(packages)
</code>.
<code>length
</code> — это
144 функция, которая сообщит тебе длину списка.
148 Такая же запись используется для арифметических вычислений. Например, «
2+
3»
151 <pre class=
"code">guile
> (+
2 3)
155 Учти, что процедура
"+
" может использоваться для сложения любого
156 количества величин, в том числе и совсем ни одной:
158 <pre class=
"code">guile
> (+)
164 Это мы используем позже.
168 Строки про
<code>readline
</code> в нашем драйвере
<em><code>gnet-devel.scm
</code></em> позволят
169 тебе пользоваться стрелками на клавиатуре для перемещения по истории и для
170 редактирования вводимых строк. Очень удобно в интерактивном
175 Другая полезная переменная, определённая в
<strong>gnetlist
</strong>, это
176 <code>all-unique-nets
</code> (набери это). Точно так же как
<code>(length packages)
</code>
177 говорит тебе, сколько у тебя компонентов,
<code>(length all-unique-nets)
</code>
178 подскажет, сколько у тебя соединений.
182 Ещё есть
<code>all-pins
</code>:
184 <pre class=
"code">guile
> all-pins
185 ((
"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>
188 Заметь, это немного сложнее, чем в предыдущих примерах: это список списков,
189 а не просто список строк. Каждый из списков соответствует выводам
190 компонента. Есть одна штука, которую мы могли бы вытащить отсюда, — это
191 подсчёт количества выводов. Мы не можем просто взять длину
<code>all-pins
</code>,
192 чтобы получить его: это даст нам только количество списков, содержащихся
193 там, равное количеству компонентов:
195 <pre class=
"code">guile
> (length all-pins)
199 Чтобы посчитать количество выводов, сначала посчитаем их количество для
200 каждого из компонентов в отдельности:
202 <pre class=
"code">guile
> (map length all-pins)
203 (
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>
206 Это один из простых способов сделать «цикл» на
<strong>Scheme
</strong>;
<code>(map p x)
</code>
207 выдаёт список результатов вызываемой процедуры
<code>p
</code> отдельно для каждого
208 элемента из
<code>x
</code>. Затем мы можем их сложить с помощью «цикла» несколько
211 <pre class=
"code">guile
> (apply + (map length all-pins))
215 <code>(apply p x)
</code> вызывает процедуру
<code>p
</code> один раз, с аргументами из всех
216 элементов из
<code>x
</code>. Поэтому вышеуказанное выражение в конце концов посчитает
219 <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>
222 До сих пор мы использовали предопределённые переменные и процедуры. Но мы бы
223 хотели иметь возможность определять свои. Это просто:
225 <pre class=
"code">guile
> (define the-answer
42)
230 Это определяет переменную
<code>the-answer
</code> и задаёт ей значение
42.
234 Можно также определять процедуры:
236 <pre class=
"code">guile
> (define add1 (lambda (x) (+ x
1)))
241 Когда видишь
<code>lambda
</code>, думай — «процедура». Сразу следом за
<code>lambda
</code>
242 идёт первый элемент (технический термин — «выражение»
<sup><a href=
"#fn__27" id=
"fnt__27" class=
"fn_top">27)
</a></sup>) — список аргументов процедуры, в
243 данном случае
<code>(x)
</code>. Когда вызывается процедура,
<strong>Guile
</strong> вычисляет
244 оставшиеся выражения, в данном случае только одно,
<code>(+ x
1)
</code>, с
245 подстановкой текущих аргументов. Результат процедуры — это результат
246 вычисления последнего выражения. Так,
<code>(add1
100)
</code> становится
<code>(+
100
247 1)
</code>, что даёт
101.
251 Теперь мы можем объединить наш сбор статистики в драйвер. Сначала определим
252 процедуру для записи выходной строки:
254 <pre class=
"code lisp"><span class=
"br0">(</span>define format-line
255 <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>
256 <span class=
"br0">(</span>display
<span class=
"kw1">name
</span><span class=
"br0">)</span>
257 <span class=
"br0">(</span>display
<span class=
"kw1">value
</span><span class=
"br0">)</span>
258 <span class=
"br0">(</span>newline
<span class=
"br0">)</span>
259 <span class=
"br0">)</span>
260 <span class=
"br0">)</span></pre>
263 Здесь мы используем две новых встроенных процедуры,
<code>display
</code> и
264 <code>newline
</code>, названия которых говорят сами за себя. Теперь:
266 <pre class=
"code lisp"><span class=
"br0">(</span>define display-stats
267 <span class=
"br0">(</span><span class=
"kw1">lambda
</span> <span class=
"br0">(</span><span class=
"br0">)</span> <span class=
"co1">; без аргументов
</span>
268 <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>
269 <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>
270 <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>
271 <span class=
"br0">)</span>
272 <span class=
"br0">)</span></pre>
273 <pre class=
"code">guile
> (display-stats)
279 Чтобы завершить драйвер, нам нужна «основная программа». По соглашению она
280 называется так же, как и сам драйвер. Также она отвечает за открывание
281 выходного файла. Итак, целиком файл драйвера сбора статистики «stats» будет
282 выглядеть примерно так:
284 <pre class=
"code lisp"><span class=
"co1">;; драйвер gnetlist для получения статистики по проекту
</span>
285 <span class=
"co1">;;
</span>
286 <span class=
"co1">;; Стандартный текст лицензии, как положено
</span>
288 <span class=
"br0">(</span>define stats
289 <span class=
"br0">(</span><span class=
"kw1">lambda
</span> <span class=
"br0">(</span>filename
<span class=
"br0">)</span>
290 <span class=
"br0">(</span>set-current-output-port
<span class=
"br0">(</span>open-output-file filename
<span class=
"br0">)</span><span class=
"br0">)</span>
291 <span class=
"br0">(</span>display-stats
<span class=
"br0">)</span>
292 <span class=
"br0">)</span>
293 <span class=
"br0">)</span>
295 <span class=
"co1">;; Сбор и вывод статистики
</span>
297 <span class=
"br0">(</span>define display-stats
298 <span class=
"br0">(</span><span class=
"kw1">lambda
</span> <span class=
"br0">(</span><span class=
"br0">)</span> <span class=
"co1">; без аргументов
</span>
299 <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>
300 <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>
301 <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>
302 <span class=
"br0">)</span>
303 <span class=
"br0">)</span>
305 <span class=
"co1">;; Простой формат вывода
</span>
307 <span class=
"br0">(</span>define format-line
308 <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>
309 <span class=
"br0">(</span>display
<span class=
"kw1">name
</span><span class=
"br0">)</span>
310 <span class=
"br0">(</span>display
<span class=
"kw1">value
</span><span class=
"br0">)</span>
311 <span class=
"br0">(</span>newline
<span class=
"br0">)</span>
312 <span class=
"br0">)</span>
313 <span class=
"br0">)</span></pre>
316 Сохрани это в файле с именем
<code>gnet-stats.scm
</code>, скопируй его в надлежащее
319 <pre class=
"code bash"><span class=
"co4">$
</span><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>
322 и затем
<strong><code>gnetlist -g stats
</code></strong> с другими обычными аргументами и именами
323 схем выдаст статистику твоего проекта в выходной файл (по умолчанию
324 <em><code>output.net
</code></em>).
328 Довольно просто, а? А также полезно. Недавно я проектировал системы,
329 состоящие из множества плат: статистика, подобная этой, помогает мне
330 выяснить, какие подсистемы лучше скомбинировать на каждой из плат.
334 <!-- EDIT1 SECTION "Написание скриптов драйверов gnetlist на Scheme" [139-] --><div class=
"footnotes">
335 <div class=
"fn"><sup><a href=
"#fnt__26" id=
"fn__26" class=
"fn_bot">26)
</a></sup>
336 <div class=
"content">«Синтаксический сахар» — конструкция языка программирования,
337 полностью эквивалентная другой его конструкции, но имеющая более
338 естественную запись (Компьютерный словарь). —
<em>Прим. перев.
</em></div></div>
339 <div class=
"fn"><sup><a href=
"#fnt__27" id=
"fn__27" class=
"fn_bot">27)
</a></sup>
340 <div class=
"content">Англоязычный термин
341 — «form». —
<em>Прим. перев.
</em></div></div>