Добро пожаловать!

Я Дож, программирование — моё хобби. По мере того, как я осваиваю что-то новое, стараюсь об этом написать пост.
На текущий момент в блоге затронуты следующие темы: vim, free pascal, lisp, forth, m4
Занимаюсь разработкой своего языка под названием DEmbro, подбробней: wiki и svn
Для постов, не связанных с программированием, у меня есть отдельное жж.

Мышь для веселья, клавиатура для боя!

ноября 26, 2015

Анонс фичи: Обобщённые функции, процедуры и методы

Это мой перевод анонса так называемых generic routines. Оригинальный текст письма доступен здесь. Слова «generic routine» я перевожу напрямую как «обобщённая подпрограмма», т.к. пока ещё не знаю какой русский термин приживётся в паскаль-сообществе (возможны ещё варианты функция-дженерик, параметризованная функция или просто дженерик)


Всем привет!

Я рад наконец-то объявить о добавлении обобщённых функций, процедур и методов (вместе назваемых «подпрограммами») во Free Pascal, позволяющих писать типобезопасный код, который можно переиспользовать для многих типов.

Синтаксис:

Синтаксис объявления обобщённых подпрограмм схож с синтаксисом объявления обычнох подпрограмм, и тоже поддерживает классовые методы:

generic [class] (procedure|function) IDENTIFIER<TYPEARGLIST>[(PARAMETERLIST)][: RESULTTYPE]; MODIFIERS;

TYPEARGLIST пишется по тем же правилам, что и соответствующий список в обобщённых типах. Типы-параметры, объявленные в TYPEARGLIST, могут быть использованы в PARAMETERLIST, в RESULTTYPE и, конечно же, в теле подпрограммы.

Обобщённые подпрограммы могут быть перегружены и по TYPEARGLIST, и по PARAMETERLIST.

Для вызова обобщённой подпрограммы используется следующий синтаксис:

specialize IDENTIFIER<TYPELIST>[(PARAMETERS)]

TYPELIST пишется по тем же правилам, что и при специализации обобщённых типов. Если подпрограмма вызывается не из глобального пространства имён, а из типа, переменной, или через имя модуля, то нужно поместить соответствующее выражение перед словом «specialize»:

TYPENAME.specialize IDENTIFIER<TYPELIST>[(PARAMETERS)]
VARIABLE.specialize IDENTIFIER<TYPELIST>[(PARAMETERS)]
UNITNAME.specialize IDENTIFIER<TYPELIST>[(PARAMETERS)]

Вызовы обобщённых подпрограмм считаются обычным выражением, поэтому они могут быть использованы так:

=== начало примера ===

{$mode objfpc}

generic function Add<T>(aLeft, aRight: T): T;
begin
  Result := aLeft + aRight;
end;

begin
  Writeln(specialize Add<String>('Generic ', 'routines') + specialize Add<String>(' with ', 'Free Pascal'));
end.

=== конец примера ===

Совместимость с Delphi:

Конечно, эта возможность также реализована в режиме совместимости с Delphi. Синтаксис объявления обобщённых функций в этом случае будет выглядеть как-то так:

[class] (procedure|function) IDENTIFIER<TYPELIST>[(PARAMETERLIST)][: RETURNTYPE]; MODIFIERS;

Т.е. всего лишь опущено слово «generic». Аналогично и при вызове:

IDENTIFIER<TYPELIST>[(PARAMETERS)]

Из-за того, что нет ключевого слова «specialize», указывающего на специализацию, сложные выражения пока что не работают в Delphi режиме. Поэтому запись результата вызова функции работает хорошо, но с чем-то более сложным вы можете столкнуться с ошибкой компиляции.

Пожалуйста, обратите внимание, что в отличие от Delphi, мы поддерживаем глобальные обобщённые функции/процедуры даже в Delphi режиме.

Ограничения/Планы:

Разработка фичи ещё не окончена, что-то ещё требует серьёзных доработок, а какие-то части ещё совсем не работают. Вот список подобных моментов:

  • поддержка сложных выражений в Delphi режиме (относится также и к специализации типов)
  • поддержка указателей на обобщённые подпрограммы (сейчас это приведёт к ошибке компиляции в лучшем случае, и внутренней ошибке или исключению в худшем)
  • поддержка возвращаемого значения в режимах без поддержки слова «Result»
  • поддержка вложенных обобщённых функций, особенно важен случай обобщённых методов внутри обобщённых классов

Поэтому прошу вас тестировать и сообщать о любых багах в трекер. Вопросы могут быть также заданы здесь в рассылке (и нет, эта фича не будет добавлена в FPC 3.0.0).

С уважением,
Свен


сентября 01, 2014

Free Pascal: Трюки с объектами

Введение

Данный пост я решил написать после дискуссии на форуме freepascal.ru, из которой я для себя узнал много всего полезного. За ценные знания большое спасибо Vapaamies, runewalsh и Сергею Горелкину, также вклад в дискуссию внесли zub и stanilar.

В посте будет рассказано о читах разной степени приемлимости, но даже если у вас нет необходимости в их применении, пост поможет глубже понять устройство объектов в паскале. Под объектами здесь и далее подразумеваются типы, объявленные как object, а не class. С class'ом многие рассматриваемые тут задачи решаются легко, т.к. язык поддерживает и указатели на конструкторы, и виртуальные конструкторы, и классовые типы (т.е. class of ...).

Тем не менее, в последнее время я возвращаюсь к истокам и всё чаще использую именно object, т.к. в отличии от class, он (1) может быть создан на стеке или внутри какой-то внешней структуры, не затрачивая времени на аллокацию, что полезно для мелких и короткоживущих объектов, и (2) гораздо легковеснее, не несёт с собой в программу RTTI, что может быть полезно при приступах оптимизаторской паранойи.


Пост ненависти к GoogleCL

Недавно захотел опробовать отправку поста в линуксе при помощи GoogleCL. Из консоли, то есть, — потому что посты я набираю исключительно в Vim'е, да ещё и с собственной преобразовалкой Markup'а, и вместо копирования текста в браузер мне проще из консоли отправить пост.

Установил, набрал пример из документации:

google blogger post --tags "GoogleCL, awesome" "Here's a really short post. The next posts will be much longer\!"

Запросили логин и название блога — ввёл. А дальше… впервые в своей жизни увидел w3m — консольный браузер! У меня же нет графической оболочки. Оказывается, чтобы залогиниться в консольной программе, требуется браузер, да не просто браузер, а с поддержкой JavaScript! Т.к. w3m не поддерживает JavaScript, из коробки по крайней мере, я поставил lynx, попытался залогиниться через него, — а в нём не работает нажатие на кнопку «Разрешить» (пишет «ОТКЛЮЧЕННАЯ кнопка посылки формы»).

Ёлки-палки, о чём думали в гугле, когда делали это!? Получается, консольная программа не такая уж и консольная. Как с этим быть, кто знает?


августа 31, 2014

Free Pascal: Type Helpers

Это мой перевод анонса так называемых type helpers, сделанного в феврале 2013 года. На мой взгляд, он достаточно хорошо и подробно описывает эту фичу. Оригинальный текст письма доступен здесь.


Приветствую сообщество Free Pascal!

Я рад представить дополнение к хелперам, которое распространяет существующую концепцию хелперов на примитивные типы.

Мотивация

С хелперами для классов и записей появилась возможность расширять классы и записи без их наследования (что всё равно было бы невозможно для записей). К примеру, это позволяет добавлять методы типам из других модулей, на которые вы не можете влиять напрямую, или не можете повлиять на экземпляр (например, используемый в TMemo наследник TStrings). Сейчас логично расширить это также и на другие типы, поддерживаемые Паскалём, но теперь нами движет в большей степени возможность сгруппировать методы вместе для примитивных типов.

Здесь не будет идти речь об использовании в паскале концепции объекта-обёртки в управляемых языках/окружениях, таких как Java и .NET, но всё это можно рассматривать и с этой точки зрения.

Синтаксис

Объявление хелперов для типов выглядит следующим образом:

TYPENAME = type helper[(BASEHELPER)] for EXTENDEDTYPE
   DECLARATIONS
end;

Как и в случае классов и записей, они поддерживают все видимые секции, и вы можете определять методы, свойства и конструкторы. Внутри методов, объявленных в хелпере, «Self» будет иметь расширенный тип и его значение также может быть изменено.

Аналогично хелперам для записей, классовые методы ДОЛЖНЫ быть объявлены статичными. (Прим. пер.: т.е. использована директива static.)

Использование

Хелперы для типов активны, если они видимы. Это означает, что либо они должны быть объявлены в том же модуле до использующего их кода, либо они должны быть объявлены в используемом модуле. Как и в случае с хелперами для классов и записей, только один типовой хелпер может быть активен для одного заданного типа и вы должны следить за видимостью, когда используете хелперы (к примеру, в текущем модуле сперва проверяется implementation секция, затем interface (если код в implementation секции) и затем используемые модули просматриваются справа налево и каждый модуль сверху вниз).

В некоторых случаях смысл типа зависит от настроек компилятора, с которыми хелпер был скомпилирован. К примеру, тип Integer может означать либо ShortInt, либо LongInt в зависимости от текущего режима (fpc/tp и objfpc/delphi соответственно) и тип String отличается в зависимости от переключателей {$H+/-} и {$modeswitch unicodestring}. Это нужно помнить при использовании их в «generic» типах. Другой специфичный случай — тип Extended на платформах, которые не поддерживают этот тип (и поэтому могут быть определены как Double). Кроме того, тип, объявленный как «NewType = type OldType», считается полностью независимым типом, к примеру в случае перегрузки операторов.

Если хелпер для типа находится в области видимости, вы можете без проблем вызывать его методы и свойства как будто вы работаете с классами или записями. Давайте предположим, что у нас есть хелпер с методом «ToString: String» для типа LongInt в области видимости, тогда выглядеть это будет как-то так:

var
    i: LongInt;
begin
    Writeln(i.ToString);
end.

Кроме вызова типовых хелперов на переменных, их можно также вызвать на константах, но нужно позаботиться о том, что будет использован корректный тип. К примеру, константа «200» будет иметь типа «Byte», тогда как «20» — тип «SmallInt». Кроме того, тип строковых констант зависит от текущего используемого режима (в особеноости от {$H+/-} и {$modeswitch unicodestring}) и содержимого строки. К примеру, в случае "{$mode objfpc}{$H+}" строка, содержащая юникодные символы, будет рассматриваться как константа типа UnicodeString и поэтому только хелперы для UnicodeString будут применены.

Для следующего примера предположим, что хелпер из предыдущего примера опять находится в области видимости:

begin
   Writeln($12345678.ToString);
end.

Также допустимы указатели (к примеру, "@i") (тип: Pointer) и встроенные константы: "True" (тип: Boolean), "False" (тип: Boolean) и "Nil" (тип: Pointer). Пожалуйста, обратите внимание, что в случае типизированных указателей будет использован нетипизированный указатель, потому что соответствующее имя типа не может быть доступно в текущем модуле (а значит, хелпер не может быть найден). Константы, которые не могут быть использованы с типовыми хелперами — множества и конструкторы массивов.

Классовые методы (и свойства) могут быть применены не только к переменным и константам, но и к самим типам:

type
   TLongIntHelper = type helper for LongInt
     class procedure Test; static;
   end;

class procedure TLongIntHelper.Test;
begin
   Writeln('Test');
end;

var
   i: LongInt;
begin
   i.Test;
   $12345678.Test;
   LongInt.Test;
end.

Поддерживаемые типы

Имеются ограничения на типы, которые могут быть расширены. Любой примитивный тип может быть расширен, за исключением:

  • файловых типов (TextFile, file of ...)
  • процедурных переменных

Конечно, также запрещены записи, классы, Objective C классы, C++ классы, объекты и интерфейсы.

Если смотреть с другой стороны, то это значит, что следующие типы разрешены:

  • порядковые типы
  • сроковые типы (включая символьные типы)
  • множества и перечислимые типы
  • массивы
  • булевские типы
  • Variant
  • типы чисел с плавающей точкой
  • указатели

Совместимость с Delphi

Эта возможность введена в Delphi XE3, но вместо использования более логичного синтаксиса «type helper» была добавлена поддержка для примитивных типов «record helper». Поэтому для поддержания совместимости с Delphi такой синтаксис также используется в Delphi режиме. Кроме того, в не Delphi режимах поддерживается наследование для типовых хелперов.

Будущее развитие

  • Добавить поддержку для большего числа типов, особенно для интерфейсов и объектов. Они могут быть добавлены либо как классовые хелперы, либо как «object helper» и «interface helper» (т.к. объекты и интерфейсы поддерживают наследование правил видимостей, внутри они напоминают хелперы для классов больше, чем хелперы для записей/типов).
  • Позволить нескольким хелперам быть активными для одного типа в одно и то же время. Это одно из основных ограничений текущей реализации хелперов. Для этого нужно продумать соответствующие правила поиска, а возможность добавить как modeswitch (быть может, по умолчанию включив её в objfpc).
  • Добавить поддержку хелперов в JVM версии. Наиболее разумным подходом в реализации этого кажется передавать переменную «Self» как дополнительный параметр и рассматривать методы как статичные (как это сделано в .NET для методов в хелперах).

С уважением,
Свен


января 11, 2014

DEmbro: что с ним было в 2013

Хочется вкратце рассказать о том, в каком состоянии сейчас находится DEmbro и что было сделано за прошедший 2013 год.

Начну с некоторых проблем, с которыми я столкнулся при разработке. Одна из них, как ни странно, — компиляция. Для сборки dembro использовалась довольно сложная система, которая использовала M4 для препроцессинга команд на специальном языке, описывающем встроенные команды, с последующей генерацией кода, заголовков, документации и т.д. Эта система сборки была написана плохо, не учитывала многих зависимостей (и потому практически при каждом запуске требовала полной перекомпиляции), засоряла всё лишними файлами, частенько ломалась на какой-нибудь из платформ (Windows/Linux), в общем требовала полного переосмысления и переработки.

Сам M4 оказался не так хорош на практике. Необходимость постоянно следить за вложенностью цитат при активном использовании кода, сложности с лишними пробелами и переносами строк, коллизии синтаксиса M4 с программой делают почти непригодной M4 для чего-то сложнее, чем просто макросы. Поэтому было принято решение полностью отказаться от M4 как языка обработки командных файлов DEmbro и почти отказаться от использования в программах на паскале (но не полностью, иногда макросы мне всё же нужны). В частности, я закрываю разработку своего побочного проекта metapascal.

Ещё одна проблема — моё паскальное наследие. Пришло понимание, что, даже несмотря на то, что на DEmbro я успешно написал две игры и свой HTTP-сервер (который уже полтора года используется), полностью перейти на него удастся не так быстро, как хотелось бы. Как следствие, мне захотелось привести свои модули на паскале в порядок, а это не так-то просто — в моих разных проектах модули в чём-то отличаются и потому не совместимы друг с другом, от этого тормозится параллельное развитие нескольких проектов. Код самого DEmbro плох и требует написания некоторых фундаментальных вещей для дальнейшего развития, на данном этапе дело практически зашло в тупик.

Наконец, были проблемы с тестированием. При разработке DEmbro добавление фичи может сломать десять других как нечего делать, и уследить за этим вручную очень трудно. Требовалось регрессионное тестирование.  И хоть модуль для этого датирован аж июнем 2011 года, тесты для него практически не написаны, на регулярной основе запускать их не представлялось возможным.

Мутным текстом выше я обозначил проблемы, теперь же не менее мутным текстом опишу как я эти проблемы решил.

Вместо нескольких свн-серверов под каждый проект я завёл для себя один свн-сервер, в котором держу все свои проекты. В дальнейшем напишу автоматическую отгрузку из него файлов в специализированные свн-сервера.

Полностью переработал систему сборки, и не только для конкретно DEmbro, но и вообще для всех своих проектов. Она учитывает и регрессионное тестирование, и несколько режимов сборки, и аккуратное складывание файлов, с нормальным учётом всех зависимостей. Теперь у меня очень гибкая и сделанная дешёвыми средствами система сборки, о которой я планирую написать цикл статей.

Я долго думал чем заменить M4 при обработке языка команд. Рассматривал много вариантов, но в конце концов остановился на том, чтобы написать этот обработчик на самом DEmbro. Плюс очевиден: DEmbro, как и большинство форт-языков, предназначен для создания проблемно-ориентированных языков, и поэтому написание такого обработчика занимает пару часов времени и 50 строк кода. Минус: для использования этого обработчика нужен уже скомпилированный DEmbro, что усложнаяет развёртывание на новой платформе, но препроцессинг можно сделать и на другой платформе, и потом просто перенести сгенерированные файлики, поэтому этот минус не так страшен.

Под этот новый обработчик нужно переписать все командные файлы, этого я до сих пор не сделал (их много), поэтому уже почти год команды не перегенерировались. Можно даже сказать, что DEmbro более года находится в некомпилирующемся состоянии. Но переписывание команд планируется осуществить в ближайшие несколько недель.

Свои библиотеки на паскале приведены в порядок, структуризированы, и готовы к развитию. Я уже начал использовать новые фичи из нестабильного fpc 2.7.1, о которых может быть напишу позже.

Почти весь прошлый год ушёл на разработку своей системы сборки, разработку нескольких других (тоже амбициозных) проектов, и вноса тяжёлых изменений в код DEmbro. Сам DEmbro был в анабиозном состоянии, а за последнюю неделю из него начал выходить. Ещё несколько дней активной разработки и все обозначенные выше проблемы окажутся решёнными.

О том, что с DEmbro планируется в дальнейшем, какие глобальные решения были приняты, я напишу позже.


августа 11, 2012

DEmbro портирован в FreeBSD

[doj@korica ~/dembro/trunk/release]$ ./de
Loaded default system file
!  Runtime(, 0, 0): cannot open file 'docs/rus/all.utf8.de'
DEmbro v0.11 built Sat Aug 11 01:30 2012
Type "quit" to exit
dembro> ." Hello FreeBSD!!!!!!!!!!!!!!!!!1111111111111111"
Hello FreeBSD!!!!!!!!!!!!!!!!!1111111111111111 ok
dembro>


Благополучно скомпилировал и запустил DEmbro в FreeBSD. Для этого потребовалось заменить в некоторых местах IFDEF LINUX на IFDEF LINUX_or_BSD, подправить вызов парочки утилит и собрать свежий Free Pascal с свна (в пакете fpc для FreeBSD не работает модуль DynLib).

Потихоньку настраиваю себе домашний сервер, на нём будут регулярно запускаться автоматические тесты функциональности DEmbro.


августа 10, 2012

DEmbro v0.11 -> v0.12

Планируется серьёзное переписывание ядра и системы команд DEmbro, а именно

  1. Расширенные возможности работы с шитым кодом: несколько отдельных буферов, возможность стирать часть кода.
  2. Расширенные возможности работы со словарём: возможность менять поведение словаря в некоторых случаях (например, когда происходит создание слова с именем, которое уже есть в словаре: сгенерировать исключение, создать новое, заменить старое, сделать что-то ещё), возможность удалять созданные ранее команды, поддержка механизма локальных слов.
  3. Упразднить универсальный стек W, который может работать с любым типом данных, до стека, умеющего работать только с ячейками. Добавить команды по работе с byte, word, dword в памяти по указателям.
  4. Стабилизировать систему команд для чисел с плавающей точкой.
  5. Вынести команды по работе со стеками R и L, унифицировать хранение traceback данных (точки возврата из команд) в R, унифицировать работу с исключениями.
  6. Пронести работу с исключениями в ядро, вынести возможность их обработки вне de32.dll/libde32.so, без «undefined behavior» как сейчас.
  7. Отщепить от «дембро-машины» так называемое «дембро-состояние», сделать возможность одной дембро-машине иметь несколько дембро-состояний. Это нужно для реализации многопоточности: сейчас все стеки и данные привязаны к глобальной дембро-машине и потому ни о какой многопоточности не может быть и речи.
  8. Собственно, реализовать многопоточность.
  9. Перевести основную часть функций по работе со строками и исходным кодом на «классические фортовские строки», т.е. на пару ячеек: размер строки и указатель на начало строки. В DEmbro сейчас основным типом строк является str — строки со счётчиком ссылок, но во многих случаях можно обойтись без них, а работают они медленно.
  10. Унифицировать шитый код, написать нормальную дампилку, может быть добавить новый «меташитый код». :)
  11. Исправить устаревшие названия на новые.
В связи с этим, готовлюсь повысить версию до v0.12. Сейчас выбираю стабильную ревизию, чтобы выделить её в отдельный бранч на свне.


Постоянные читатели

Обо мне

Моя фотография
Мой e-mail: vitek_03(at)mail(dot)ru