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

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

октября 27, 2009

Один из способов создания алиасов в windows

Пусть нам нужно при наборе какой-то команды, например do, перенаправлять вызов в какую-то другую программу. Например, в mysuperutil.exe.

Один из способов это сделать - воспользоваться реестром:
1) открываем реестр (командой regedit)
2) идем в HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
3) создаем новый раздел с именем do.exe и у переменной по умолчанию устанавливаем значением C:\myutils\mysuperutil.exe
4) закрываем реестр

Так же это отличный способ сократить путь к какой-то очередной утилите, когда не хочется засорять переменную %PATH%.


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

июля 15, 2009

Получаем стек в реальном времени

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

function BackTraceStrFunc(Addr: Pointer): ShortString;
Возвращает по адресу функции информацию о ней. В простейшем случае это будет просто адрес, а если скомпилировать программу с ключом -gl, то еще название этой функции и название файла, в котором она объявлена. В тот момент, когда программа падает и паскаль выводит стек вызовов - используется именно эта функция. Что забавно, это не функция, а переменная функционального типа, поэтому ее можно переопределить (BackTraceStrFunc := @MyBackTraceStrFunc;). Помимо адреса функции в нее можно передать информацию о вызове (см. get_caller_addr) и тогда будет выдан номер строки в исходнике, в которой был вызов.

function get_frame: pointer;
Возвращает указатель на фрейм вызова текущей функции. Сама по себе малополезна.

function get_caller_addr(framebp: pointer): pointer;
Возвращает указатель на место с информацией о том, откуда был вызван фрейм framebp. С помощью этого указателя уже можно получить при помощи BackTraceStrFunc информацию о вызывающей функции.

function get_caller_frame(framebp: pointer): pointer;
Возвращает фрейм, из которого был вызван фрейм framebp.

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

procedure PrintStack;
  var
    Frame: Pointer;
begin
  Frame := get_frame;
  while Frame <> nil do begin
    Writeln(BackTraceStrFunc(get_caller_addr(Frame)));
    Frame := get_caller_frame(Frame);
  end;
end;


Наслаждайтесь!


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

июня 06, 2009

Автоматическое автодополнение

В виме по умолчанию для автодополнения нужно нажимать ^N (т.е. Ctrl+N). Иногда, правда, хочется, чтобы редактор делал это автоматически по мере набора слова. Специально для этого есть плагин word_complete, который выполняет ремап в interst mode (т.е. делает imap) для каждой буквы, добавляя автодополнение.

Единственное неудобство скрипта - он использует стандартный вимовский буфер обмена.


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

мая 27, 2009

Проблема с property record'ом

Имеем код


type
TVec2f = packed record
  X, Y: Single;
end;

TClass = class
private
  FVec: TVec2f;
  procedure SetVec(V: TVec2f);
public
  property Vec: TVec2f read FVec write SetVec;
end;
  
procedure TClass.SetVec(V: TVec2f);
begin
  FVec := V;
  Writeln(V.X, ' ', V.Y);
end;

var
  Obj: TClass;
  V: TVec2f;
begin
  Obj := TClass.Create;
  V.X := 100;
  V.Y := 1000;
  Obj.Vec := V;
  Obj.Vec.Y := 666;
  Writeln('Last value: ', Obj.Vec.Y);
end.


Код прекрасно компилируется. Вся соль в предпоследней строке Obj.Vec.Y := 666. Возникает несколько вопросов
1) Будет ли вызван Writeln при выполнении этой строки?
2) Что будет напечатано в самом конце?

Ответы

1) Нет
2) "Last value: 6.660000000E+02"

Из-за этого совершенно неочевидного поведения я просидел в поисках ошибки в одной своей программе около часа.

Далее, если в этом же коде убрать "read FVec" и "Writeln('Last value: ', Obj.Vec.Y);", то компилятор будет ругаться на строку "Obj.Vec.Y := 666;".

От нас что-то скрывают! :)

Видимо, теперь record ведет себя как класс. В предыдущих версиях фпц такой код вообще не компилировался.


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

FPC 2.2.4

Полтора месяца прошло с появления FPC 2.2.4. С паскалем я, к сожалению, все это время дела не имел (использовал CodeGear Delphi 2009), а вот сегодня наконец пришлось.

Полный лог изменений в FPC 2.2.4.

Самое значимое - это полноценная поддержка generic'ов. А этого я ждал долго :) Теоретически, они были и раньше, но по факту при попытке специализации какого-то дженерика вываливалась ошибка "202 runtime error".

В скором времени перепишу паскалевский синтаксис в виме, чтобы слова generic и specialize подсвечивались.

Код с тестовым примером:


generic TList<TItem> = class(TObject)
var private
  FItems: Array Of TItem;
public
  procedure SetLength(Len: Integer); inline;
  function High: Integer; inline;
  function Length: Integer; inline;
  procedure Add(Item: TItem);
  procedure Del(Item: TItem; All: Boolean = True);
  function Find(Item: TItem): Integer;
  function GetItem(Index: Integer): TItem; inline;
  procedure SetItem(Index: Integer; Item: TItem); inline;
  property Items[Index: Integer]: TItem read GetItem write SetItem; default;
end;
TListOfInteger = specialize TList<Integer>;
TListOfString = specialize TList<String>;



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

мая 03, 2009

Перерисовка окна

Иногда в виме окно оказывается замусоренным. Может помочь скроллинг вверх/вниз, но это не всегдая удобно. Более удобный способ - вызвать принудительную перерисовку:
      :redr!


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

апреля 13, 2009

Кат

Вроде научился делать кат.


Инфу взял отсюда http://seorussian.blogspot.com/2008/12/sdelat-kat-v-blogger-odin-iz-luchshih.html


[updated] Нашёл официальные каты.  


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

Синтаксис в html

Мне приходится время от времени постить исходные коды. Нужно, чтобы они имели подсветку - поэтому без html не обойтись. К счастью, vim умеет генерировать html версию редактируемого файла, для этого нужно набрать
          :TOhtml

Эта команда работает с диапазоном строк, поэтому ее можно использовать и для переконвертирования участка файла (в данном случае от строки 10 до строки 20):
          :10,20TOhtml
или, чтобы не запоминать номера строк, после выделения диапазона (Shift+v) можно набрать
          :'<,'>TOhtml

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

//  TEntity//{{{
//      Базовый класс для всех классов в программе
//
//        
TEntity = class
private
  FOwner: TEntity;
  FGrabber: Boolean;
  FHash: Cardinal;
protected
  FName: String;
public
  Constructor Create(Owner: TEntity); virtual;
  Destructor Destroy; override;
  Procedure DoString(S: String); virtual;
  Procedure AssignVar(Name, Value: String); virtual;
  Procedure Render; virtual;
  Procedure Update; virtual;
  Procedure Kill; virtual;
  Procedure OnEvent(Event: TEvent); virtual;
  Procedure SendEvent(Event: TEvent); virtual;
  Procedure SendEventAndFree(Event: TEvent);
  Property Grabber: Boolean read FGrabber;
  Property Owner: TEntity read FOwner;
  Property Hash: Cardinal read FHash;
  Property Name: String read FName;
end;
//}}}

И не забудьте про :help TOhtml


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

апреля 02, 2009

Дополнительные операторы для перегрузки

Не секрет, что во free pascal'е существует возможность перегрузки стандартных операторов. В документации говорится, что есть три типа таких операторов:
1) Арифметические бинарные (+, *, <>, /, **) - любая функция от двух параметров.
2) Операции сравнения (<, >, =, <=, >=) - функция от двух переменных, возвращающая Boolean, причем <> будет сгенерирован автоматически как отрицание к =.
3) Оператор присваивания (:=) - функция принимающая значение одного типа и возвращающая значение другого типа.

Работает это примерно так


Type
TVec3D = Array[0..2] Of Double;

// Скалярное произведение
operator * (A, B: TVec3D) Result: Double;
  Var
    I: Integer;
begin
  Result := 0;
  for I := 0 to 2 do
    Result += A[I] * B[I];
end;

// Угол от A к B в плоскости XY по часовой?
operator > (A, B: TVec3D) Result: Boolean;
begin
  Result := A[0]*B[1] - A[1]*B[0] >= 0;
end;

// Присвоение числа в вектор
operator := (V: Integer) Result: TVec3D;
  Var
    I: Integer;
begin
  for I := 0 to 2 do
    Result[I] := V;
end;


Однако, если покапаться в исходниках free pascal'я (я экспериментировал с 2.2.2, в более поздних версиях что-то может быть не так), то можно обнаружить, что перегрузке поддаются еще и операторы
1) Арифметические ><, or, and, div, mod, shl, shr, xor.
2) Унарные -, not.

Оператор >< в самом языке используется для нахождения симметрической разности двух множеств. Т.е. код


Var
  I: Integer;
  S: Set Of Byte;
...
S := [1, 2, 3, 4, 5] >< [3, 4, 5, 6, 7];
for I := 1 to 7 do
  Write(Ord(I in S));
Writeln;


напечатает 1100011, а в переменной S будет [1, 2, 6, 7].

Его очень удобно использовать для обозначения векторного произведения (чем, кстати и воспользовались разработчики free pascal'я в своем модуле matrix).

mod очень кстати заюзать для создания собственного удобного нахождения остатков.

Согласно комментариям в модуле tockens исходников компилятора перегрузке должны поддаваться еще и as, in, is. Однако, у меня этого сделать почему-то не вышло, наверно к ним нужен какой-то особый подход. Никто не знает какой именно? :)


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

марта 29, 2009

Вкладки

В виме можно в одном окне открыть несколько файлов на разные табы. Делается это просто:
        :tabe [FILENAME]
Переключение между табами - gt и gT. Рекомендую назначить эти команды на стрелочки влево и вправо (в обычном режиме вы ведь используете h/l, не так ли?):
        :map <LEFT>  gT
        :map <RIGHT> gT
Итак, у вас проект с несколькими модулями, вы их все открыли и успешно работаете. При каждом заходе в проект не хочется по новому открывать все файлы. Чтобы сохранить текущую конфигурацию достаточно выполнить команду
        :mksession MySession.vim
Тогда, чтобы потом снова открыть вкладки достаточно запустить вим так
        gvim -S MySession.vim
Для выполнения одной и той же команды на всех табах используется команда :tabdo, К примеру, сохранить открытые файлы
        :tabdo w
закрыть все табы
        :tabdo q

И кстати, не забудьте про :help tabpage. 


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

марта 14, 2009

Запаковка битов в free pascal.

Иногда, например при работе с файлами или написании низкоуровнего сетевого когда, может потребоваться работа с битами. Она без проблем реализуется с помощью побитовых операторов (and, not, shl, or), но хотелось бы более изящного решения.

По умолчанию конструкция вида
    TBits = packed record
       Carry: Boolean;
       Signed: Boolean;
       Zero: Boolean;
    end;
займет 3 байта. А если сверху прописать опцию

{$BITPACKING ON}

то эта запись будет занимать один байт, а работа с ней не будет отличаться от работы с обычной записью:
B: TBits;
...
B.Zero := True;
B.Signed := False;

К сожалению, fpc не поддерживает запаковку динамических массивов. Только константных, поэтому для реализации потока битов придется писать что-то типа
        Type
          TBits8 = packed array[0..7] Of Boolean;
        Var
          I: Integer;
          N: Integer; // размер
          Bits: Array Of TBits8; // поток битов
        ...
        N := 129; // хотим 129 бит
        SetLength(Bits, (N + 7) shr 3);
        for I := 0 to N - 1 do
          Bits[I shr 3][I and 7] := False; // бит с номером I устанавливаем в 0


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

марта 04, 2009

Как избавиться от гламурного розового окошка

Многих раздражает розовый цвет окошка автодополнения (то самое, которое по Ctrl-n вызывается). Самый простой способ это исправить - прописать в _vimrc такие строки:
function KillPink()
   hi Pmenu guibg=#2e3436 guifg=#eeeeec
   hi PmenuSel guibg=#8c8c8c guifg=#8c2426
   hi PmenuSbar guibg=#555753
   hi PmenuThumb guifg=#ffffff
endfunction
call KillPink()

Цвета можно переписать под собственный вкус. Если вы сменяете цветовую схему, то эта настройка будет сбита. Тогда нужно будет выполнить команду
   :call KillPink()

По умолчанию для автодополнения вим ищет слова только в текущем буфере. Часто этого недостаточно. Хотелось бы заранее обладать каким-то базовым комплектом слов (типа implementation).

Для этого создаем какой-нибудь файл, к примеру, C:\PascalKeywords.vim и в нем прописываем все необходимые слова:
   interface
   implementation
   initialization
   finalization
   inherited
   davidblain

И добавляем в _vimrc такую строчку:
   set cpt=.,w,b,u,t,i,kC:\PascalKeywords.vim

Так же можно добавить и другие необходимые файлы, просто приписав ",k[Путь к файлу]".

Полный список зарегестрированных слов можно найти тут.


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

февраля 18, 2009

Сворачиание текста. (Folding)

В современных Delphi IDE есть такая возможность как сворачивание участка кода при помощи {$REGION '<Name>'} ... {$ENDREGION}. Это очень удобная фича и вим ее поддерживает.

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

В простейшем случае делаем следующее
1) Переходим в line visual-mode (Shift-v).
2) Выделяем нужный участок кода (при помощи j/k)
3) Набираем zf

Сладка создана. Раскрыть ее - zo, закрыть - zc. Если набираете za, то состояние сладки меняется на противоположное. zd - удалить складку. zj/zk - переместиться до первой вкладки вниз/вверх.

Еще создать складку можно командой zf{motion}. К примеру, zf} свернет абзац.

В общем-то, вот и вся теория, остались некоторые мелочи.

Маркеры
Недостаток того, что мы только что сделали - складка не сохраняется при закрытии файла. Поэтому, перед тем, как создавать вкладки, нужно включить опцию
:set fdm=marker
Эта опция включает создание маркеров в начале и конце складок. По умолчанию маркеры имеют вид
/* {{{ */
blablabla
/* }}} */
Что для паскалиста не есть гуд. Для установки правильных комментариев нужно набрать что-нибудь из
:set commentstring={%s}
:set commentstring=(*%s*)
:set commentstring=//%s
на собственный вкус. Далее, {{{ и }}} выглядят очень неуклюже, исправляется это командой
:set foldmarker=<<<,>>>
Если вам нравится дельфи-стиль, то наберите
:set foldmarker=$REGION\ ',$ENDREGION
На самом деле, маркеры должны настраиваться из файла синтаксиса (во всяком случае, для многих языков настраиваются автоматически), и видна очередная недоработка касательно паскаля в виме.

Можно создавать вложенные друг в друга сладки.

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

:set foldtext=...

И не забывайте про :help fold.

PS
Кстати, кто знает как фолдинг делается в блоггере? (Типа как lj-cut в жж.)


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

февраля 05, 2009

Snippets

Иногда бывает очень полезной автоматическая вставка некоторого частовстречающегося кода, так называемых Snippet'ов. К примеру, это могут быть языковые конструкции (if ... then, while ... do, begin ... end. Такая фитча есть в Delphi IDE).

Для настройки ее в виме, потребуется скачать плагин Snippets_plugin. Устанавливается он очень просто - нужно открыть его в виме и набрать :UseVimball.

Теперь можно перейти к использованию. Нужна, скажем, конструкция if ... then ...;. Набираем:
:Snippet if if <{Cond}> then <{Body}>;

Если в режиме вставки набить if<TAB>, то сработает этот сниппет. Далее курсор будет установлен на Cond, необходимо набить конкретный Cond и нажать TAB. Курсор будет перевден на {Body} (а в общем случае - на первую необработаную шнягу вида <{что-то}>).

Вот, собственно, и вся теория. Теперь по мелочам.

Вместо if можно набирать любую последовательность символов без пробелов. Т.е., скажем, такой сниппет :Snippet i,. if (i < 0)Or(i >= Width) then Exit; будет корректен.

Одна из самых полезных фич в плагине - это теги. Допустим, что в предыдущем примере мы хотим иметь возможность вставлять вместо i другие переменные. Тогда:

:Snippet i,. if (<{Name}> < 0)Or(<{Name}> >= Width) then Exit;

Теперь, если вы наберете первое Name, второе будет заполнено автоматически.

Более хитрый способ использования сниппетов - значение, введенное ранее подставлять не сразу, а использовать для вычисления того, что нужно подставить. Записывается это как <{Name:Expression}>, где Name - имя тега, а Expression - выражение, которое будет подставлено. Значение, введенное пользователем в Name ранее заносится в регистр @z.

Для вставки Enter'ов используется <CR>.

Для создания сниппетов специально для паскаля лучше создать pascal_snippets.vim в папке Vim\vimfiles\ftplugin (сразу после установки vim'а она обычно пустая), в котором и прописать все необходимые сниппеты.

Таб может показаться неудобным, тогда можно добавить в _vimrc что-то вроде

let g:snippetsEmu_key = "<S-Tab>"

И, конечно же, не забудьте про :help Snippet.

ПС.
Ценный совет. Не ставьте в качестве названия сниппета просто i или j.


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

января 21, 2009

Простейшие настройки Vim'а под FPC

Итак, вы программируете под Free Pascal и хотите использовать Vim в качестве редактора.

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

Первое, на что стоит обратить внимание, в меню (которое Синтаксис) есть только стандартный pascal. А в нем не выделяются многие ключевые слова (class, initialization, finalization, interface и прочие).

Есть специальная опция, включающая Delphi слова:

:let pascal_delphi=1


+ Free Pascal опция:
:let pascal_fpc=1


И после этого нужно заново включить синтаксис (:set syn=pascal). Помимо ключевых слов будут подсвечены (другим цветом) многие стандартные функции. Если вам это не нравится, перед загрузкой синтаксиса можно набрать
:let pascal_no_functions=1

Но мне нравится с подсветкой :)

Кроме того можно выставить еще парачку крайне полезных опций:

:let pascal_one_line_string=1 (однострочные строковые константы)
:let pascal_no_tabs=1 (выделять TAB'ы красным)


Для того, чтобы не набирать все эти команды каждый раз заново можно их поместить в конец файла _vimrc (или .vimrc для Linux) в папке с вимом.

Если вам что-то не нравится в стандартном синтаксисе, это можно исправить, поправив файл syntax\pascal.vim под свой вкус.

На самом деле, для паскаля вимовские файлы не обновляются аж с 2001 года, поэтому многого в них нет.

Определение синтаксиса по расширению

При запуске вим умеет определяеть по расширению какой синтаксис загружать. Он знает про pas и dpr, но по умолчанию он не знает про *.pp.

Исправить это можно, добавив в _vimrc строку:
au BufRead,BufNewFile *.pp set filetype=pascal


Компиляция

В vim'е также есть встроенные инструменты для компиляции и отображения сообщений компилятора. К примеру, есть команда
:make [target]

которая
1) осуществляет вызов утилитки make
2) выводит сообщения от компилятора

Чтобы все заработало нужно, чтобы
1) в переменной окружения %PATH% был прописан путь ко всем бинарникам freepascal'я.
Вы этого еще не сделали? Можно пойти двумя путями 1) если у вас есть FAR MANAGER, то в нем достаточно набрать set PATH=%PATH%+;C:\fpc\2.2.x\bin\i386-win32; 2) если же нет, но вы работаете в Windows XP, то Панель управения -> Система -> Дополнительно -> Переменные среды, там находим переменную Path и добавляем в нее C:\fpc\2.x.x\bin\i368-win32, т.е. путь до всех бинарников freepascal'я
2) в папке с исходником лежал Makefile.
Вы не работали никогда с Makefile'ами? В простейшем случае Makefile имеет вид
.PHONY all
all: hello.pp
fpc hello.pp -Sd -gl
# (перед fpc табуляция принципиальна!)
Подробнее рекомендую почитать тут.
3) в vim'е был правильно выбран компилятор. Делается это вызовом
:comp fpc


После того, как компиляция произошла, полный лог компиляции можно посмотреть вызовом
:cl

Можно просмотреть лог и в отдельном буфере:
:cope


Прочее
Для того, чтобы vim вместо TAB'ов ставил пробелы
:set et!

Нужное кол-во пробелов для одного отступа настраивается при помощи
:set shiftwidth=2

Для включения нумерации строк
:set nu!

Если вы привыкли к компиляции по F9 и компиляции с исполнением по Ctrl+F9, то пропишите в _vimrc
" -Sd включает режим компиляции, совместимый с Delphi 7
let ckeys=' -Sd'
map <F9> :execute '!fpc %' . ckeys<CR><CR>
map <C-F9> :execute '!fpc %' . ckeys<CR><CR>:execute '!%:r.exe'<CR><CR>

В переменной ckeys хранятся ключи компиляции, при неоходимости их можно изменить прямо из редактора
:let ckeys='<NEWKEYS>'


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

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

Архив блога

Обо мне

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