В наше время в кармане обычного человека лежит мощный персональный компьютер, о котором 10-20 лет назад можно было только мечтать. И если у вас километры отлаженного Windows-кода и отлично работающие приложения и утилиты, написанные на Delphi, вы наверняка хотели бы задействовать это богатство для мобильной разработки. А также опыт, накопленный за время программирования под Windows. PAS2JS поможет вам совместить два мира: разработку под Windows и создание Web-приложений и Node.js модулей.
О некоторых обнаруженных трудностях из личного опыта идёт речь в этой статье.
Почему просто не изучить JavaScript и писать Web-приложения на нём?
Я изучил язык JavaScript в достаточной мере. Но во-первых, программирование для Web — это больше чем просто знание языка. Во-вторых, возможность писать один код под разные платформы бесценна. Можно отладить модули приложения в Delphi IDE, используя его мощный отладчик и редактор, и затем, добавив необходимую обвязку, получить готовое работающее приложение для сайта. А когда вы исправите ошибку или добавите новую функциональность в приложение для Windows, достаточно будет только перекомпилировать JavaScript-модули в PAS2JS.
Нужно заметить, что пока PAS2JS поддерживает не все возможности языка Delphi, они указаны на сайте. Также некоторые фрагменты, казалось бы, простого кода PAS2JS не может транспилировать в JavaScript.
Трудности перевода
Итак, свежий пакет PAS2JS скачан с FTP, пробуем перекомпилировать простой «Привет, мир», и сразу же остановка на:
uses
System.SysUtils;
Error: can't find unit "System.SysUtils"
Готовые пакеты PAS2JS, которые можно найти в папке packages, частично дублируют системные юниты Delphi. Но у них нет префикса в имени. Решение простое: удаляем префикс «System.» из названия юнита. Программа в Delphi компилируется (если нет — проверьте наличие префикса «System» в Unit Scope Names, в меню Delphi Project | Options | Delphi Compiler).
Приведение типа в константах
PAS2JS не поддерживает приведение типа в константных выражениях:
const
CODE_A = Word('a');
Error: Constant expression expected
В случае перечислимых типов можно попробовать поменять так, это проходит:
const
CODE_A = Ord('a');
Также PAS2JS не понимает встроенные функции языка Lo и Hi. В определении констант их можно заменить так:
const
LO_BYTE = $1234 and $FF; // Lo($1234);
HI_BYTE = $1234 shr 8;// Hi($1234);
Символы и строки ANSI
Я надеюсь, вы уже перешли на Unicode-строки в своих Delphi-проектах? Если же вы оставили часть строк в формате ANSI в целях экономии памяти, они в JavaScript не сконвертируются: PAS2JS не знает типы AnsiChar, AnsiString, Utf8String и RawByteString. Рассмотрите возможность заменить их на Unicode-типы, либо на Byte и Array of Byte.
Вот пример замены AnsiChar на Byte:
// Было
procedure TestAnsiCharAndByte1;
const
SMALL_ENG_LETTERS = ['a'..'z'];
CAPITAL_ENG_LETTERS = ['A'..'Z'];
var
ch: AnsiChar;
engs: set of AnsiChar;
begin
engs := SMALL_ENG_LETTERS + CAPITAL_ENG_LETTERS;
ch := 'Z';
if ch in engs then
Writeln('It''s an English letter');
end;
// Стало
procedure TestAnsiCharAndByte2;
const
SMALL_ENG_LETTERS = [Ord('a')..Ord('z')];
CAPITAL_ENG_LETTERS = [Ord('A')..Ord('Z')];
var
ch: Byte;
engs: set of Byte;
begin
engs := SMALL_ENG_LETTERS + CAPITAL_ENG_LETTERS;
ch := Ord('Z');
if ch in engs then
Writeln('It''s an English letter');
end;
Неуживчивая буква o
Как курьёз: в польском языке есть буква o — O kreskowane, Unicode #$00F3. По каким-то причинам PAS2JS её невзлюбил, и в некоторых случаях не может воспринять строку, если в неё входит эта буква:
var
s: string;
begin
s := #$00F3'abdef'; // Компилируется
s := 'abdef'#$017C; // Компилируется
s := #$00F3'abdef'#$017C; // Error: Illegal character
s := #$00F3; s := s + 'abdef'#$017C; // Так снова компилируется
end;
Оператор Case
Внезапная неожиданность подстерегла в операторе case, в котором PAS2JS отказался принимать русские буквы в качестве вариантов выбора:
ch := 'Я';
case ch of
'А': Writeln('Это "А"'); // Error: Incompatible types: got "Char" expected "Char" (???)
'Б'..'Я': Writeln('Это другая русская буква'); // Error: char expected, but string found
end;
Помогло определение констант для нужных русских букв:
const
ckbA = #$410; // А
ckbB = #$411; // Б
ckbYa = #$42F; // Я
var
ch: Char;
begin
ch := 'Я';
case ch of
ckbA: Writeln('Это "А"');
ckbB..ckbYa: Writeln('Это другая русская буква');
end;
Выводы
Мне удалось скомпилировать для Web небольшой проект Delphi, внеся сравнительно небольшие изменения в исходный текст программы, а на сэкономленное время я написал эту статью. Тестирование показало, что обе версии программы: для Windows и для Web, работают абсолютно одинаково. Это несомненно успех: теперь я могу развивать этот проект, дорабатывая программу на Delphi, и транспилируя её в JavaScript с помощью PAS2JS.
Что касается выявленных небольших недочётов, я уверен они будут быстро устранены. Поскольку проект PAS2JS — открытый и свободный, активно развиваемый силами сообщества Free Pascal.
Комментарии (16)
lair
21.01.2019 17:49+1Можно отладить модули приложения в Delphi IDE, используя его мощный отладчик и редактор, и затем, добавив необходимую обвязку, получить готовое работающее приложение для сайта.
Как вы гарантируете идентичность поведения исходного приложения и сконвертированного варианта?
(я, если честно, не видел еще ни одного трансформера windows-web, который бы переживал хоть сколько-нибудь сложные сценарии)
kryvichh Автор
21.01.2019 18:01На вход подаём одинаковые тексты, на выходе получаем другие тексты, сравниваем. Совпадает — значит, OK.
lair
21.01.2019 18:03Это вы так сравниваете простой алгоритм. А как гарантировать коректность приложения?
kryvichh Автор
21.01.2019 18:21Если под приложением понимать 1) GUI часть + 2) внутреннюю обработку, то для 1) с PAS2JS нет решения. У нас транспилируются из Delphi только модули, отвечающие за обработку информации.
Чтобы получить одинаковый код и для интерфейса пользователя, придётся воспользоваться другими решениями. Например, TMS Web Core, у которого под капотом тот же PAS2JS.lair
21.01.2019 18:25Если под приложением понимать 1) GUI часть + 2) внутреннюю обработку,
Ну… да. Это же приложение, а не модуль.
с PAS2JS нет решения
Понятно, ничего нового опять не произошло.
kryvichh Автор
21.01.2019 18:31Я понимаю, что не открыл своей статьёй Америку, но может кто-то попадал на такие же грабли. Или у кого-то были свои проблемы с PAS2JS, которые удалось решить. Очень интересно было бы почитать.
Keremet_2030
22.01.2019 07:56Я использовал UniGUI. Для небольших проектов вполне работает, но чем больше проект, тем больше неожиданных и непредсказуемых багов появляется.
Delphi хорош для нативного ПО, но не для WEB.
В последних RAD Studio с использованием FireMonkey можно написать отлично работающие мульти-платформенные приложения (win32, win64, linux, android, OSx, iOS). Было бы классно, если бы они придумали какую-нибудь HTML5 обёртку для WEB…a-tk
22.01.2019 20:35Помнится было время, когда Delphi for web появился. Правда, не знаю чем эта история закончилась.
CoolCmd
22.01.2019 20:56если не нужно работать с DOM, то не логичнее было сделать транспиляцию в WASM, а не JS? или PAS2JS — проект старый и тогда WASM ещё не было?
kryvichh Автор
21.01.2019 23:32-1Судя по багтрекеру Free Pascal, баги на PAS2JS начали сабмитить с лета 2017 года. А «Вики» для него была создана в декабре 2017 г. Но не исключено, что этот проект был создан на основе ещё более ранних разработок.
Транспиляция в JavaScript более удобная и гибкая, если нужно что-то проверить на готовой HTML-странице, поправить руками. Это удобство для разработчика. Хотя WASM должен быть быстрее, что выгоднее пользователям. Компилятор из Delphi-подобного языка в байткод WebAssembly тоже существует, с января 2018 года, но он не бесплатен.
a-tk
21.01.2019 18:06Чтобы сконвертировать проект из Delphi в JS надо переписать половину исходного проекта, чтобы конвертер это понял? Вы серьёзно?
А сколько ещё интересного Вам предстоит! Вы знаете про var-аргументы функций, например?kryvichh Автор
21.01.2019 18:28Ну, полпроекта — это Вы с плеча рубанули. :) Если бы проще было писать и тестировать параллельно на Delphi под Windows и JavaScript под Web, мы бы так и сделали.
А что не так с var-переменными? Вроде компилируются и работают. Или есть случаи, когда не компилируется код?
kryvichh Автор
22.01.2019 01:11-1Обнаруженные в статье баги я отправил в багтрекер проекта Pas2JS, и на данный момент они уже взяты в работу!
- https://bugs.freepascal.org/view.php?id=34923
- https://bugs.freepascal.org/view.php?id=34924
- https://bugs.freepascal.org/view.php?id=34925
Thank you, Mattias Gaertner!
KYuri
kryvichh Автор
Проект касается лингвистического программирования и обработки текстов. Что-то вроде «Транскриптора» Артемия Лебедева, но с белорусской спецификой. То есть там в основном текстовая обработка, символы… Delphi-модуль задействован как часть большей системы, а Web-модуль после доработки функционала будет позже где-то доступен. Пока не решено, тестируем небольшой комьюнити.
Но тут вопрос не в конкретной программе, а в возможности задействовать свои знания Delphi для Web-разработки. И это здорово!