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

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

мая 31, 2010

dpp: лямбда-функции

Занялся написанием лямбда-функций.

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

Именно эту проблему и решают мои лямбда-функции:
FButton.OnClick := #l(Button: TButton) FWindow.Close;

Конструкция #l создает метод у класса, внутри которого она вызывается. Так записывается вызов процедуры и функции соответственно:
ObjectUpdate := #l(Obj: TMyObject) Obj.Update;
ObjectFilter := #l(Obj: TMyObject): Boolean = Obj is TSuperObject;

Внутри можно писать и несколько операторов, разделенных символом ;

Есть так же возможность создать лямбду на классовый метод или даже просто функцию, делается это заменой l на lsimple.

lsimple уже реализовано и работает.

При реализации сразу же я столкнулся с множеством проблем.

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

Во-вторых, есть более серьезная проблема при использовании локальных переменных. Например, сейчас такой код не заработает:
for I := 0 to 3 do
    FButtons[I].OnClick := #l(Button: TButton) FWindows[I].Show;
хотя очень хотелось бы.

Даже если отбросить тот факт, что это сложно реализовать технически (нужно создавать промежуточный класс, который запомнит значение переменной I в момент вызова, и у этого класса могут начаться трудности в получении доступа к приватному полю FWindows, да и вообще к FWindows нужно будет каким-то макаром приписать что-то типа FOwner.FWindows), даже отбросив - совершенно непонятно сколько этот класс должен жить в памяти. Узнать когда кнопка перестанет ссылаться на метод этого класса невозможно.

Кроме того, хочется в дальнейшем делать встроенные лямбы, а не только в конце строк:
RegistHandler($10, $32, #l(Packet: TPacket) DoSomething#);
А в этом плане символ # очень неудачен, трудно определить кто из них открывающий, а кто закрывающий, и при вложенностях получится путаница.

Поэтому, наверно, будет введена конструкция [# ... #] для встроенных в строки команд.


Читать дальше......

мая 19, 2010

dpp: линейные команды #assert и #return

Только что написал две новые команды, которые может обработать dpp:

#assert булево выражение
Пример вызова:
          #assert (2 + 2 = 4) and (0 = Cardinal(nil))
Проверяет истинность утверждения, если оно не выполняется - выводит в лог информацию о том в каком файле, в какой строке, когда, и какое утверждение не верно.

#return результат
Пример:
          #return ERROR_CANNOT_DO_ANYTHING
Возвращает из функции указанный результат, и выходит из функции

Это весьма простые конструкции, и добавление их поддержки требует написания парочки методов. В коде dpp есть глобальный класс PreProcessor, в котором нужно зарегистрировать необходимые обработчики. В настоящий момент поддерживаются только построчные обработчики:


  // Line - текущая обрабатываемая строка в файле
  // Fragment - часть строки, идущая после приставки
  //            (см. StartWord в RegistLineProcessor)
  // через S возвращается то, что нужно вставить
  // вместо приставки и Fragment'а
  TLineProcessor = procedure (
                         PreProcessor: TPreProcessor;
                         const Line, Fragment: TString;
                         out S: TString
                       ) of object;


К примеру, обработчик конструкции #assert выглядит так:


procedure TDebugProcessor.AssertProcess(
      PreProcessor: TPreProcessor;
      const Line, Fragment: TString;
      out S: TString
    );
begin
  S := 'Debug.Assert(' + Fragment + ', ''' +
        PreProcessor.CurrentFileName +
        '(' + IntToStr(PreProcessor.CurrentLine) + ') ' +
        Stringalize(Fragment) + ''');';
end;


Единственное, что я прокомментирую в этом коде, - функция Stringalize преобразует строку к печатному виду. А именно, если написать
          #assert S = 'hello'
то чтобы сконструировать строку, содержащую "S = 'hello'" для печати в лог, мы должны удвоить кавычки по правилам паскаля. Именно это и делает Stringalize.

Для полного счастья осталось зарегистрировать функцию в обработчике:


PreProcessor.RegistLineProcessor('#assert', '*', AssertProcess);


Первый параметр (я его называю приставкой или StartWord) - с чего должна начинаться конструкция.

Второй параметр - паттерн, задающий как должна выглядет строка после приставки. В данном случае может быть что угодно, но, например, конструкция
          #(6, 7, 2, 'hello')-> Notifier
не имеет опознавательных знаков в приставке, и потому задается паттерном
          (*)->*
(на самом деле он другой, но это не важно).

Совершенно аналогично написан #return.


Читать дальше......

мая 18, 2010

Математические символы

Сегодня искал коды математических символов для HTML. Чтобы хранить их всех где-то в одном месте, буду складывать их в этот пост.

символописаниеhtmlunicode
стрелка влево ←
стрелка вверх ↑
стрелка вправо →
стрелка вниз ↓
стрелка влево и вправо ↔
¬ Знак отрицания ¬
логическое И∧U+2227 ISOtech
логическое ИЛИ∨U+2228 ISOtech
для всех∀U+2200 ISOtech
существует∃U+2203 ISOtech
пустое множество/диаметр
U+2205 ISOamso
не равно≠U+2260 ISOtech
совпадает с
U+2261 ISOtech
перпендикулярно⊥U+22A5 ISOtech
истина ⊤
выводимо ⊦
не выводимо ⊬
двойное выводимо ⊩
двойное не выводимо ⊮
тройное выводимо ⊪
⊨ ⊧ верно ⊨
не верно ⊭
двойное верно ⊫
двойное не верно ⊯
входит∈U+2208 ISOtech
не входит∉U+2209 ISOtech
является подмножеством⊂U+2282 ISOtech
является надмножеством⊃U+2283 ISOtech
является подмножеством или совпадает ⊆U+2286 ISOtech
является надмножеством или совпадает ⊇U+2287 ISOtech
двойная стрелка вправо⇒ U+21D2 ISOtech
двойная стрелка влево-вправо⇔U+21D4 ISOamsa
n-арное произведение = знак произведения∏ U+220F ISOamsb prod
n-арное суммироание∑U+2211 ISOamsb sum
бесконечность∞U+221E ISOtech
вертикальная палка ∣
незакрашенный маленький квадрат□
закрашенный маленький квадрат ■
закрашенный квадрат
квадрат с точкой ⊡
квадрат с квадратом ▣
квадрат с горизонтальной полосой ⊟
квадрат с вертикальной полоской ◫
квадрат с плюсом ⊞
квадрат с горизонтальными полосами ▤
квадрат с вертикальными полосами ▥
лево полузакрашенный квадрат ◧
право полузакрашенный квадрат ◨
закрашенный квадрат с откусанным правым нижним уголком ◩
закрашенный квадрат с откусанным левым верхним уголком ◪
интеграл ∫
μ мю μ

◊ вытянутый ромб (◊) ◊
◇ ромб ◇
◆ закрашенный ромб ◆
◈ ромб с точкой ◈

○ маленький кружочек ○
● маленький закрашенный кружочек ●
◌ маленький пунктирный кружочек ◌
▢ ?кружочек ▢
⊝ кружочек с минусом ⊝
⊖ кружочек с горизонтальной чертой ⊖
⊕ кружочек с плюсом ⊕
⊜ кружочек с двумя полосами ⊜
◍ кружочек с вертикальной полосатостью ◍
⊘ кружочек с косой чертой ⊘
⊗ кружочек с крестом ⊗
⊙ кружочек с точкой ⊙
◉ кружочек с жирной точкой ◉
⊚ кружочек с кружочком ⊚
◎ кружочек с большим кружочком ◎
⊛ кружочек со звездочкой ⊛
◐ кружочек левозакрашенный ◐
◑ кружочек правозакрашенный ◑
◒ кружочек нижнезакрашенный ◒
◓ кружочек верхнезакрашенный ◓
◔ кружочек с закрашенным верхним правым углом ◔
◕ закрашенный кружочек с незакрашенным верхним левым углом ◕


Читать дальше......

мая 10, 2010

Собственный препроцессор (aka dpp)

Все больше и больше утыкаюсь в ограничения возможностей Free Pascal'я. Сегодня решил начать осуществлять свою давнюю задумку - препроцессор.

Итак, в чем заключается задумка. Есть файлы, написанные на моем расширенном паскале, имеют расширение dpp. Препроцессор просматривает папки, ищет такие файлы, и на их основе генерирует pas-файлы, которые уже можно просто скомпилировать free pascal'ем.

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

Сделано это странное ограничение для того, чтобы компилятор Free Pascal когда сообщал об ошибках, указывал бы номера строки и столбца ошибки совпадающие с соответствующими строками и столбцами в dpp-файла.

Моя утилитка dpp будет open source и ей сможет пользоваться любой желающий.

Это все была теория использования. О том, как утилитка действует, я расскажу в следующий раз, а сейчас перечислю некоторые фишки, которые хочу с помощью нее реализовать:

1) Событийную систему. К сожалению, мне не удалось на чистом fpc написать красиво отправку сообщения, поэтому ввожу такую конструкцию
#(5, 3, 'hello', Obj)-> Notifier
Она отправляет сообщение в Notifier с параметрами 5, 3, 'hello', Obj.

2) Автоматические создание и освобождение классов. Две конструкции

#autoclass(FMyClass: TMyClass) TMyExtendedClass.Create('hello')
Описывается внутри описания класса. Создает у класса поле FMyClass, которое будет автоматически создаваться во всех конструкторах вызовом TMyExtendedClass.Create('hello') и автоматически освобождаться в каждом деструкторе. По сути, время жизни поля привязывается к времени жизни хозяина - частоиспользуемая схема.

#stackclass(MyClass: TMyClass) TMyExtendedClass.Create('hello', Param1)
Описывается внутри описания метода. Автоматически создает в начале метода, автоматически уничтожает в конце

3) Система сериализации классов (для генерации кода по загрузке/сохранению). Тут немного сложнее, основные конструкции примерно такие:

#streaminterface
#streamimplementation(TMyClass)
Создает все необходимые функции по сохранению/загрузке класса TMyClass

#streamfield(FImportantValue: Integer)
Создает поле у класса, которое необходимо сохранять

4) Продвинутое RTTI.
#RTTIclass(MyRTTIVar) from TMyClass
Записывает в MyRTTIVar RTTI-информацию о классе TMyClass

5) Вставка в код название текущего файла/текущего метода/содержимого текущей строки/номера текущей строки. Очень удобно для логов.
#currentfilename_______________
#currentmethodname_____________
#currentline___________________
#currentlinenumber_____________

6) Собственно, печать в лог с указанием всей нужной инфы из пункта 5
#log 'error ' + IntToStr(ErrorId) + '!'

Пока что готов только пункт 1 :)


Читать дальше......

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

Обо мне

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