|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|||||||||||||||||||||||||||||||||||||||||||||||||||
Пишем свой интерпретатор языка BASIC20.06.2009, 20:03. Показов 251175. Ответов 464
Метки нет (Все метки)
Благодаря форуму и Evg в частности интерпретатор развивается, потихоньку превращаясь в простенький интерпретатор QBASIC.
Некоторые из самых старых версий сохранились в теме и ссылки на них будут добавлены в это сообщение,а также ссылки на другие темы,связанные с этой. Репозиторий с проектом находится тут, там же есть возможность в браузере посмотреть историю ревизий (английский в логах весьма примитивен,комментарии и рекомендации можете писать в личку),а также скачать самый последний архив репозитория в формате .tar.gz Если кто-то пользуется Subversion,скачать исходники можно так:
Технический приём для формирования согласованных данных https://www.cyberforum.ru/c-linux/thread46096.html Вопрос по svn (Subversion) Создание системы тестирования ПО. Вопрос про разные реализации бэйсиков Можно ли выразить порядковый номер элемента массива через индексы? [C++] Какие флаги указать линкеру для компиляции программы? Как можно определить переменную в файле configure.in,чтобы её можно было использовать в Makefile? Странный SIGSEGV, или что зависит от порядка написания интерфейса класса https://www.cyberforum.ru/c-linux/thread61324.html Альтернативная версия интерпретатора от Evg на C Это простая реализация разбора выражений, написанная Evg на C: Представление выражения в двоичном дереве ***************** Первое сообщение: ***************** Задание(Страуструп,из книги,по готовому коду): Введите программу калькулятора и заставьте её работать.Например,при вводе
LexicalAnalyzer.h
LexicalAnalyzer.cpp
main.cpp
Анализатор-то работает,но конечное значение не вычисляется.Более того,если вводим
Добавлено через 2 часа 5 минут 30 секунд Пришлось решать влоб с дебаггером.У Страуструпа опечатка (или намеренная ошибка,что более вероятно ) Вот в этом куске кода в функции get_token():
Добавлено через 16 минут 19 секунд И ещё опечатка была
31
|
|||||||||||||||||||||||||||||||||||||||||||||||||||
| 20.06.2009, 20:03 | |
|
Ответы с готовыми решениями:
464
Пишем свой интерпретатор языка BASIC
Пишем свой чекер |
|
Флудер
195 / 33 / 11
Регистрация: 23.03.2007
Сообщений: 334
|
|
| 20.06.2009, 20:06 | |
|
вот сейчас открыл книгу - нет там никакой опечатки, всё верно. это у вас была опечатка
а неявное преобразование для кода ошибки вполне приемлемо
10
|
|
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|
| 21.06.2009, 12:26 [ТС] | |
|
Значит,у вас версия книги новее))
7
|
|
|
2924 / 1274 / 114
Регистрация: 27.05.2008
Сообщений: 3,465
|
|
| 21.06.2009, 12:30 | |
|
Странный какой-то дебаггер..... Если условие (1) выполнилось, происходит не переход к switch, а выход из функции get_token() с возвратом значения END.
9
|
|
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|
| 21.06.2009, 12:32 [ТС] | |
|
Да,я уже понял,что ступил)
7
|
|
|
|
|
| 21.06.2009, 21:58 | |
|
#pragma, а ты все задания просто передираешь из книги или всё-таки что-то пытаешься сделать сам? Вопрос задаю НЕ с целью, чтобы сказать "ай-ай-ай". Просто ты уже кучу вопросов вских задавал. Если ты действительно решил серьёзно занаться программированием, то постараюсь отвечать ещё более развёрнуто. Если интерес несерьёзный - то как обычно
10
|
|
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
||
| 21.06.2009, 23:27 [ТС] | ||
|
P.S.Твоё "как обычно" тоже круто для меня )) но мотаю на ус всё сказанное и написанное)
4
|
||
|
|
|
| 22.06.2009, 12:06 | |
|
> но мотаю на ус всё сказанное и написанное)
Ну вот это самое главное. Остальное со временем придёт Добавлено через 12 часов 27 минут 44 секунды Наверное в страуструпе есть и дальнейшее развитие этой задачи. Со своей стороны могу дыть 3 предложения по дальнейшему апгрейду
4
|
|
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|
| 22.06.2009, 22:28 [ТС] | |
|
3
|
|
|
|
|
| 22.06.2009, 23:44 | |
|
По возрастающей сложности (как мне кажется)
1. Работать со входным файлом (т.е. фактически получается некий простенький интерпретатор). Выдавать пользовательские ошибки с прявязкой к исходнику: т.е. печатать имя файла, номер строки, номер позиции в строке (последнее некритично), суть ошибки. Использование неинициализированной переменной считать ошибкой. При этом добавить конструкцию "print <var_name>". На вход программы подаём имя файла с текстом программы, которую будем интерпретировать 2. Заменить плавающий типа (double) на целочисленный и добавить операции битовой арифметики: & | ~ << >>. Приоритет операций такой же, как и в си (можешь посмотреть в том страуструпе). 3. Сделать типизацию. Т.е. понимать как плавающие типы, так и целые. При первой записи в перменную, её тип вычисляется по типу выражения, стоящей в правой части присваивания. Т.е. для "a = 5" переменная a заводится как целочисленная, для "a = 5.0" - как плавающая. Все дальнейшие присваивания в переменную делаются с учётом типа. Т.е. если изначально переменная была как целочисленная, то операция "a = 23.2" означает запись целого значения 23 (потому как a целое). При вычислении двухоперандногй операции выражения, второй операнд должен приводиться к типу первого операнда. Т.е. "12 + 34.5" должно вычисляться как (пишу в терминах си) "12 + (int)34.5", а "12.1 + 7" должно вычисляться как "12.1 + (float)7". При попытке построения битовых операций над плавающим типом выдавать ошибку (ибо считаем, что битовые типы разрешены только для целых чисел) Может быть эти задания тебе покажутся сложными, но всё же рекомендую сделать все три. Потому что это тебе сильно поможет в освоении проектирования программ. Если от этого не устанешь, а меня не заломает - попробуем дальше это дело наворотить. Таким образом, писать программу следует с учётом того, что её функциональность будет расширяться - это ещё добавит экспы в части проектирования. Да и на своих же ошибках, возможно, начнёшь лучше понимать, как НЕ надо делать
5
|
|
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
||||||||||||||||||||||||||
| 25.06.2009, 19:10 [ТС] | ||||||||||||||||||||||||||
|
Итак,после длительного биения об стену
,можно подвести предварительный итог: 1)Программа читает входной файл,но как она это делает,я понятия не имею,потому что делал по описанию библиотеки из и-нета http://www.cplusplus.com 2)Есть некое подобие привязки к исходнику,есть сообщение об ошибке,но иногда работает некорректно из-за этих каскадных вызовов... 3)Я попытался добавить возможность программируемых пользователем функций,но в самом конце выяснилось,что это не работет,опять же из-за подобия рекурсии.Так как анализ посимвольный,после знака $ должно происходить вычисление,но при последующем вызове expr(true) происходит дальнейшее чтение строки и в итоге ошибка. 4)В функции get_resulting_expr (она ещё не доделана) ошибка в алгоритме(не заменяется последний параметр). 5)Задания твои я попытаюсь сделать,просто я ещё,например,понятия не имею о битовых операциях. 6)В-общем,пока не знаю,как всё это разгребать.Писалось всё это с целью просто сделать,хотя бы сделать,а не как сделать...На данный момент есть код: LexicalAnalyzer.h
LexicalAnalyzer.cpp
param_swapper.cpp
main.cpp
Входные данные в файле program.clc program.clc
0
|
||||||||||||||||||||||||||
|
|
||||||
| 25.06.2009, 19:55 | ||||||
|
А можешь скомпилить бинарник, а то в этих чёртовых билдарах я не пойму, как консольное приложение сделать. Чисто интересно посотмреть
Добавлено через 1 минуту 52 секунды Я собственно задание то со следующей целью дал. Вот ты сделал код по книге. А задание - это чтобы доработки сделать самому и без книги
2
|
||||||
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|
| 25.06.2009, 21:10 [ТС] | |
|
Ладно,функции пока оставлю,буду потихоньку дальше продвигаться.Бинарник прилагается.
0
|
|
|
|
||||||
| 25.06.2009, 21:43 | ||||||
|
А сам Exe'шник где? Если Calculator нужно просто переименовать в *.exe, то у меня он что-то завис
Добавлено через 1 минуту 0 секунд Пишет "Program too big to fit in memoty" Добавлено через 3 минуты 53 секунды Под линухом скомпилялось и запустилось. Щас вот только разберусь, как русские сообщения нормально печатать Добавлено через 10 минут 58 секунд Когда я делал поставновку задачи, для первого пункта я имел в виду следующее. Ты имеешь файл
А так для начала очень даже неплохо. Правда пока, насколько я понимаю, тут в основном чужой код, но, тем не менее, начало положено. Я исходники не смотрел, только запускал программу. Даже деление на ноль отловилось. Из ошибок отловил только неправильную реакцию на "a=2.2.2" Добавлено через 2 минуты 31 секунду Но использование неинициализированной переменной нужно запретить. Скорее для тренировки в отработке ошибочных ситуаций, чем для практики (т.к. большинство интерпретируемых языков позволяет использовать неинициализированные переменные)
1
|
||||||
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
|||||||||||||||||||||
| 28.06.2009, 02:43 [ТС] | |||||||||||||||||||||
|
1) Кое-что удалось сделать.Добавил битовые операции &,^,~,|,но способ,которым это сделано,мне не нравится.Вообще,вся эта программа-тихий ужас,не дай бог вот над такими программами придётся когда-нибудь сидеть и разбираться)) Кстати,результат проверить не мешало бы,но я поглядел по аналогии (в отдельной программе),вроде бы корректно.
2) Добавил вывод переменной на экран по желанию пользователя.Это делается командой #<var name> .Я до сих пор не додумался(мало думал?),как сделать интерпретацию нескольких символов подряд,"не теряя при этом нити беседы" - то есть чтобы предотвратить вызов посимвольно во время интерпретации зарезервированных слов.Как-то я поставил на все 300 с лишним строк программы точки разрыва для дебаггера,и у меня получилось,что,например,при попытке вычисления функции (case $: )после записи её тела в строку и возврате строка продолжает читаться,но в предыдущем вызове.Короче там такая путаница,ё-маё Аж дебаггер заглючило.3)Добавил возможность комментариев,это делается значком "(двойной апостроф),всё,что после значка,игнорируется. 4)Есть ограничение на битовые операции с числами с плавающей точкой.Типизацию пока не сделал.Просто хочется сделать наподобие "int x=1;" но всё упирается в проблему,описанную ранее. 5) Запретил использование неинициализированных переменных,а также программа прерывается после первой ошибки. 6)На данный момент программа такова: LexicalAnalyzer.h
LexicalAnalyzer.cpp
param_swapper.cpp
main.cpp
Входные данные: program.clc
#~0 "Печатаем дополнение нуля
a=3 b=4 "This is comment "Это комментарий #a "Ура!Каменты! : ) "Это тоже комментарий
0
|
|||||||||||||||||||||
|
|
|||||||||||||||||||
| 28.06.2009, 12:08 | |||||||||||||||||||
2. Когда пишешь "неинициализированная переменная", надо ещё указывать, какая переменная. Написано, что строка 6 "#e", я могу догадаться, что это e. Но если бы ошибка была на строке 5, а там выражение из тридцати слагаемых, то надо долго разбираться, что же там неинициализировано. 3. В случае ошибки программа по прежнему отрабатывает до конца. Советую этот вопрос долго не откладывать, а отработать для себя механизм выдачи пользовательской ошибки, потому как чем раньше ты это сделаешь, тем меньше в будущем надо будет переделывать И вообще, лучше пиши программу исходя из того, что в ней будет копаться кто-то ещё, кроме тебя (даже если в жизни этого не произойдёт) - приучай себя к этому.
Если вдруг ты ещё не понял, я хочу протолкнуть твою программу до интерпретатора бэйсика. Конечно же полноценный бэйсик не получится, но некое его примитивное подобие - а почему бы и нет? Поэтому я сначала хочу от тебя добиться стабильной работы твоей программы в простейших случаях (запись в переменные, их чтение и печать, выдача ошибок). Дальше попробуем навести некий структурный порядок в том, что ты сделал, а потом попробуем продвинуться дальше. Я специально продивгаю по чуть-чуть. Чтобы с появлением каждых новых фич тебе приходилось что-то в программе серьёзно переделывать. Только так можно научиться правильно строить программу с точки зрения её архитектуры. А мозг устроен так, что сначала нужно много раз сделать неправильно, чтобы чётко понимать в дальнейшем, как же нужно делать правильно Добавлено через 2 минуты 58 секунд А код по процедурным вызовам удали пока, чтобы не мешал. Ибо это большой паравоз, прицепленный к твоей программе, который ты пока не знаешь как работает - он только занимает место и мозолит глаза ![]() Добавлено через 10 минут 40 секунд Кстати, положи к себе в проект ещё и текстовый файл, в который будешь складывать все найденные ошибки. А исправленные ошибки переносить в другой файл (но не удалять - для истории полезно их сохранить)
1
|
|||||||||||||||||||
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
||||||||||||||||||||||||||||||||
| 29.06.2009, 01:02 [ТС] | ||||||||||||||||||||||||||||||||
|
Хотел уточнить свои слова насчёт проблемы с вызовом функций(ну очень хочется доделать)
Вот прилагаю исходник,если не лень поглядеть,конечно.А насчёт svn это в интернете репозиторий создается,навроде как в launchpad сделано?Бесспорно,это удобнее...есть ещё cvs какой-то. main.cpp
LexicalAnalyzer.h
LexicalAnalyzer.cpp
error_handler.h
error_handler.cpp
param_swapper.cpp
Входной файл: program.clc
a=10.6
b=2 @f(c,d){c*b} #$f(a,b)
0
|
||||||||||||||||||||||||||||||||
|
|
|||||||||||||||||||||||||||||
| 29.06.2009, 20:53 | |||||||||||||||||||||||||||||
|
Исходники погляжу дома. Но, вижу, уже обработка ошибок аккуратно выделена в отдельный модуль, т.е. процесс идёт и появляется понимание того, как надо делать аккуратную разбивку на отдельные независимые блоки Я поначалу делал просто на файловой системе и доступ через файловую систему. А потом переделал на сетевой вариант. В итоге у меня svn сервер запущен под виндой, под vmwar'ой запущен линух и через виртуальную сеть из-под линуха работаю с svn-репозиторием, который физически находится под виндойДобавлено через 10 часов 14 минут 45 секунд Глубоко не смотрел, но поглядел поверхностно исходники. По теущему положению тебе действительно будет сложно дальше развивать интерпретатор. Со своей стороны могу тебе предожить для начала навести порядок. Затем чётко разбить на компоненты (потому как у тебя пока мешанина): аккуратно сделать механизм обработки ошибок, грамматический анализатор, лексический анализатор, разбиение на операторы (statement), коих у тебя пока работающих только два (оператор присваивания и оператор печати), таблицу переменных. Затем добавлять поддержку новых конструкций. Ну либо у тебя есть какие-то свои пожелания. Я вижу ты никак не угомонишься с процедурными вызовами. Моё личное мнение - по текущему состоянию их надо выкидывать, а потом попробовать добавить по-человечески. Либо добавить сейчас в том же виде, в котором ты пытаешься, с целью понять, что в таком бардаке поддерживать это будет слишком проблематично. Если есть какие-то идеи, вопросы и т.п. - не стесняйся, спрашивай. Пока я добрый, помогу ![]() Добавлено через 12 минут 53 секунды Для такого примера не ловит использование неинициализированной переменной b во 2-й строке
Здесь в 3-й строке должна либо выдаваться ошибка, либо печататься все значения
Аналогично долдна быть синтаксическая ошибка (ну или любое другое сообщение об ошибке)
Добавлено через 3 минуты 58 секунд Неправильно учитывается приоритет операций. Следующий пример демонстрирует это. При вычислении b поставлены скобки (которые по большому счёту не нужны, только для показывания в каком порядке должны идти вычисления). Оба выражения должны дать один и тот же результат
1
|
|||||||||||||||||||||||||||||
|
Временно недоступен
957 / 228 / 14
Регистрация: 12.04.2009
Сообщений: 926
|
||||||
| 30.06.2009, 05:11 [ТС] | ||||||
|
Пытаюсь избавиться от этой проблемы
1)Почему? 2) Как её поймать? Я уже успел поискать про машинную запись чисел с плавающей точкой,я так понимаю,этот ньюанс требует пересмотра посимвольного разбора с помощью putback? Где-то там по дороге эта точка и теряется между цифрами...
0
|
||||||
|
|
|||||||||||||||||||||
| 30.06.2009, 12:23 | |||||||||||||||||||||
Сообщение было отмечено Памирыч как решение
Решение
Точка не должна отлавливаться. У тебя "10.0" должно идти как единая грамматическая единица (token). Собственно, потому я и предложил навести порядок, что логически у тебя некорректно написано.
Сейчас у тебя get_token выдирает число по одной циферке. Это не есть правильно. get_token за раз должен выдрать целиком грамматическую единицу. Т.е. если записано "123", то за раз будет выдрано "123", если "123.45", то "123.45". А вот если "123.45.67", то первая лексическая единица будет "123.45", а следующая "." (или ".67", если понимать дополнительный вариант записи плавающих чисел). Если это ключевое слово "print", то и будет "print" (с чем у тебя на текущий момент проблемы). Сейчас у меня под рукой нет формальных описаний, но если желаешь - ознакомлю тебя с формальными описаниями грамматики и лексики Добавлено через 1 минуту 40 секунд > Я уже успел поискать про машинную запись чисел с плавающей точкой,я так > понимаю,этот ньюанс требует пересмотра посимвольного разбора с помощью > putback? Нет. Как я уже писал, get_token за раз должен выдрать "123.45", котору затем стандартными функциями ты превратишь в плавающее число. Возможно, я пока объясняю слишком непонятно, но если ты морально готов к перелопачиванию своей программы, могу начать пояснять более подробно. Добавлено через 5 минут 0 секунд Хотя нашёл в инете пример формального описания грамматики. Так что если надо - могу вкратце пояснить суть работы грамматического анализатора Добавлено через 2 часа 51 минуту 6 секунд ======================================== ======================== В общем, появилось немного свободного времени на работе. Так что родил примерно следующее пояснение Есть две вещи разного уровня: грамматика и лексика. Грамматика - это по сути дела правила построения слов из отдельных букв. Лексика - построение предложений из слов (а последние построены по правилам грамматики) Давай рассмотрим простейший вариант того, какие грамматические единицы (token'ы) должны поддерживаться нашим интерпретатором:
Задача парсера, который по сути дела является грамматическим анализатором, является нарезка входного текста на слова (token'ы). При этом грамматический анализатор будет пропускать комментарии, ненужные пробелы и знаки табуляции. Мы будем считать, что один вызов GetToken (я всё-таки обзову именно так, чтобы не путать с тем, что сейчас есть у тебя) вынимает из входного потока одно слово (token). Как это будет представлено на уровне данных, пока не рассматриваем (чисто чтобы теорию понять) Вот для такого примера:
При этом комментарии за пределы парсера вообще не вылезают. Дабы остальным компонентам с ними не возиться Теперь, как это всё должно выглядеть технически. По результату вызова GetToken фактически должен возвращать в качестве результата некие два значения. Первым значением является непосредственное строковое представление слова, которое полезно для печати диагностики, отладки, а так же необходимо для разбора идентификаторов и констант. Т.е. для нашего примера этими строковыми значениями будут "LET", "A", "=", "5.123" и т.д. Вторым значением является значение некоего enum'а, которое удобно обрабатывать в виде целочисленного значения и которое является описанием того, что у нас записано в строке. Таким образом пара этих значений полностью описывает наш token Как конкретно сделать enum - зависит от того, как тебе удобно работать. Я бы сделал так:
В итоге интерфейс нашего грамматического анализатора будет примерно таким:
Ну и весь грамматический анализатор полезно выделить в отдельный файл Добавлено через 14 минут 34 секунды Вот примерное формальное описание грамматики:
Твой анализатор должен делать разбор, руководствуясь этими формальными правилами (глядя на них проще понмать, что в каком порядке должно разбираться). Исходя из этих правил, например, "12.ab" должно трактоваться как ошибка, потому как ни в одно правило такая конструкция не вписывается. Пробелы и знаки табуляции означают конец текущего слова. При этом получается, что "12. " опять-таки не вписывается, т.к. после десятичной точки мы требуем хотя бы одну цифру (хотя можем этого и не делать) Самая первая задача - научиться нарезать на слова в случае, когда нет грамматических ошибок. А уже потом пытаться отсекать ошибочные случаи
6
|
|||||||||||||||||||||
| 30.06.2009, 12:23 | |
|
Помогаю со студенческими работами здесь
20
пишем свой троян с нуля Пишем свой класс, спецификатор доступа protected Интерпретатор небольшого языка программирования на С++ Не удается откомпилировать интерпретатор М-языка
Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |
|
Новые блоги и статьи
|
||||
|
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта
Симптом:
После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
|
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
|
Новый ноутбук
volvo 07.12.2025
Всем привет.
По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне:
Ryzen 5 7533HS
64 Gb DDR5
1Tb NVMe
16" Full HD Display
Win11 Pro
|
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
|
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
|
|
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов
На странице:
https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/
нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
|
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов.
. . .
|
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
|
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
|
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут.
В век Веб все очень привыкли к дизайну Single-Page-Application .
Быстренько разберем подход "на фреймах".
Мы делаем одну. . .
|