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 id=
"написаниескриптовдрайверовgnetlistнаscheme">Написание скриптов драйверов gnetlist на Scheme
</h1>
22 <strong>Автор:
<em>John Doty
</em></strong>
26 (первоначально это было
28 список рассылки gEDA-user в июле
2009 г.)
36 Если ты никогда не писал программы на
<strong>Lisp
</strong>, это выглядит страшновато. Но
37 это намного легче, чем кажется. Добавь в
<strong>Lisp
</strong> чуть-чуть синтаксического
38 сахара
<sup><a href=
"#fn__25" id=
"fnt__25" class=
"fn_top">25)
</a></sup> и он
39 превращается в
<strong>Logo
</strong>, который могут изучить даже дети из начальной школы.
43 И просто для объяснения значения некоторых из этих странных слов:
44 <a href=
"https://www.google.com/search?q=Lisp_%28programming_language%29&btnI=lucky" class=
"interwiki iw_go" title=
"https://www.google.com/search?q=Lisp_%28programming_language%29&btnI=lucky">Lisp
</a> --- компьютерный язык,
45 <a href=
"https://www.google.com/search?q=Scheme_%28programming_language%29&btnI=lucky" class=
"interwiki iw_go" title=
"https://www.google.com/search?q=Scheme_%28programming_language%29&btnI=lucky">Scheme
</a> --- диалект
<strong>Lisp
</strong>'а, и
46 <a href=
"https://www.google.com/search?q=GNU_Guile&btnI=lucky" class=
"interwiki iw_go" title=
"https://www.google.com/search?q=GNU_Guile&btnI=lucky">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 http:
<em>www.ccs.neu.edu/home/dorai/t-y-scheme/t-y-scheme.html
62 Или поищи «Учебник по Scheme» в своём любимом поисковике: их много.
64 Также может пригодиться справочный документ по адресу:
66 http:
</em>www.gnu.org/software/guile/manual/html_node/index.html
70 Итак, начнём. Вот очень простой драйвер:
72 <pre class=
"code lisp"><span class=
"co1">;; gnetlist development playground
</span>
74 <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>
75 <span class=
"br0">(</span>activate-readline
<span class=
"br0">)</span>
77 <span class=
"br0">(</span>define
<span class=
"br0">(</span>devel output-filename
<span class=
"br0">)</span>
78 <span class=
"br0">(</span>scm-style-repl
<span class=
"br0">)</span>
79 <span class=
"br0">)</span></pre>
82 Чтобы это применить, сохрани всё в файле
<em><code>gnet-devel.scm
</code></em>. Скопируй
83 этот файл туда, где в твоей системе хранятся файлы
<strong>Scheme
</strong>. На машине, на
84 которой я сейчас работаю, команда такова:
86 <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>
89 <em><code>/sw/
</code></em> для многих устанавливаемых в Linux пакетов надо заменить на
90 <em><code>/usr/
</code></em>, может быть на
<em><code>/usr/local
</code></em>, или --- при установке из
91 tar-архива --- на
<em><code>~/mygeda/
</code></em>. Это нужно выяснить. Если ты можешь
92 записывать в целевой каталог без прав суперпользователя,
<strong><code>sudo
</code></strong> не
97 Теперь, изменив нужным образом
<em><code>/sw/
</code></em>, набери:
99 <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>
102 Ты должен увидеть обычный текст стандартного приглашения, за которым
105 <pre class=
"code">guile
></pre>
110 <pre class=
"code">guile
> packages
</pre>
115 <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>
118 <code>packages
</code> --- удобная переменная, содержащая список всех уникальных
119 значений атрибутов
<code>refdes=
</code>. Набрав её, ты скормил её «REPL» --- циклу
120 чтения, вычисления, вывода (Read, Evaluate, Print Loop). Итак, REPL считал
121 её, вычислил (получив список) и вывел.
127 <pre class=
"code">guile
> (length packages)
131 Что здесь произошло? Здесь REPL вычислил список.
133 <pre class=
"code lisp"><span class=
"br0">(</span><span class=
"kw1">length
</span> packages
<span class=
"br0">)</span></pre>
136 В большинстве языков программирования ты бы написал это выражение в более
137 традиционной функциональной записи:
<code>length(packages)
</code>.
<code>length
</code> --- это
138 функция, которая сообщит тебе длину списка.
142 Такая же запись используется для арифметических вычислений. Например, «
2+
3»
145 <pre class=
"code">guile
> (+
2 3)
149 Учти, что процедура
"+
" может использоваться для сложения любого
150 количества величин, в том числе и совсем ни одной:
152 <pre class=
"code">guile
> (+)
158 Это мы используем позже.
162 Строки про
<code>readline
</code> в нашем драйвере
<em><code>gnet-devel.scm
</code></em> позволят
163 тебе пользоваться стрелками на клавиатуре для перемещения по истории и для
164 редактирования вводимых строк. Очень удобно в интерактивном
169 Другая полезная переменная, определённая в
<strong>gnetlist
</strong>, это
170 <code>all-unique-nets
</code> (набери это). Точно так же как
<code>(length packages)
</code>
171 говорит тебе, сколько у тебя компонентов,
<code>(length all-unique-nets)
</code>
172 подскажет, сколько у тебя соединений.
176 Ещё есть
<code>all-pins
</code>:
178 <pre class=
"code">guile
> all-pins
179 ((
"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>
182 Заметь, это немного сложнее, чем в предыдущих примерах: это список списков,
183 а не просто список строк. Каждый из списков соответствует выводам
184 компонента. Есть одна штука, которую мы могли бы вытащить отсюда, --- это
185 подсчёт количества выводов. Мы не можем просто взять длину
<code>all-pins
</code>,
186 чтобы получить его: это даст нам только количество списков, содержащихся
187 там, равное количеству компонентов:
189 <pre class=
"code">guile
> (length all-pins)
193 Чтобы посчитать количество выводов, сначала посчитаем их количество для
194 каждого из компонентов в отдельности:
196 <pre class=
"code">guile
> (map length all-pins)
197 (
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>
200 Это один из простых способов сделать «цикл» на
<strong>Scheme
</strong>;
<code>(map p x)
</code>
201 выдаёт список результатов вызываемой процедуры
<code>p
</code> отдельно для каждого
202 элемента из
<code>x
</code>. Затем мы можем их сложить с помощью «цикла» несколько
205 <pre class=
"code">guile
> (apply + (map length all-pins))
209 <code>(apply p x)
</code> вызывает процедуру
<code>p
</code> один раз, с аргументами из всех
210 элементов из
<code>x
</code>. Поэтому вышеуказанное выражение в конце концов посчитает
213 <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>
216 До сих пор мы использовали предопределённые переменные и процедуры. Но мы бы
217 хотели иметь возможность определять свои. Это просто:
219 <pre class=
"code">guile
> (define the-answer
42)
224 Это определяет переменную
<code>the-answer
</code> и задаёт ей значение
42.
228 Можно также определять процедуры:
230 <pre class=
"code">guile
> (define add1 (lambda (x) (+ x
1)))
235 Когда видишь
<code>lambda
</code>, думай --- «процедура». Сразу следом за
<code>lambda
</code>
236 идёт первый элемент (технический термин --- «выражение»
<sup><a href=
"#fn__26" id=
"fnt__26" class=
"fn_top">26)
</a></sup>) --- список аргументов процедуры, в
237 данном случае
<code>(x)
</code>. Когда вызывается процедура,
<strong>Guile
</strong> вычисляет
238 оставшиеся выражения, в данном случае только одно,
<code>(+ x
1)
</code>, с
239 подстановкой текущих аргументов. Результат процедуры --- это результат
240 вычисления последнего выражения. Так,
<code>(add1
100)
</code> становится
<code>(+
100
241 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> и
258 <code>newline
</code>, названия которых говорят сами за себя. Теперь:
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» будет
276 выглядеть примерно так:
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=
"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>
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__25" id=
"fn__25" class=
"fn_bot">25)
</a></sup>
330 <div class=
"content">«Синтаксический сахар» --- конструкция языка программирования,
331 полностью эквивалентная другой его конструкции, но имеющая более
332 естественную запись (Компьютерный словарь). ---
<em>Прим. перев.
</em></div></div>
333 <div class=
"fn"><sup><a href=
"#fnt__26" id=
"fn__26" class=
"fn_bot">26)
</a></sup>
334 <div class=
"content">Англоязычный термин
335 --- «form». ---
<em>Прим. перев.
</em></div></div>