pas2js/ru

From Lazarus wiki

English (en)русский (ru)




Pas2js: что это?

Компилятор

Pas2js - это транспилер(транскомпилятор) [кода] Pascal в JavaScript. Он парсит Object Pascal и выдает JavaScript. JavaScript в настоящее время имеет уровень ECMAScript 5 и должен запускаться в любом браузере или в Node.js (цель "nodejs"). Доступен в 3 формах:

  • как библиотека
  • как программа командной строки
  • как веб-сервер

Он транскомпилирует [код] из фактических исходников Pascal, у него нет промежуточных файлов .ppu. Это означает, что все исходники должны быть всегда доступны.

Через определения внешних классов компилятор может использовать классы JavaScript:

  • Все классы, доступные во время выполнения JavaScript и в браузере, доступны через модули импорта (сравнимо с модулями windows или unix для собственного компилятора).
  • Для Node.js доступна базовая поддержка среды выполнения nodejs.
  • Доступен модуль импорта для jQuery (libjquery)

RTL

Для работы сгенерированного кода требуется небольшой файл JavaScript: rtl.js. Он определяет объект RTL. Этот объект запустит код Object Pascal, если вы включите вызов rtl.run() на странице HTML.

<script type="application/javascript">
  rtl.run()
</script>

pas2js может автоматически включить этот файл в сгенерированный вывод, например так:

pas2js -Jc -Jirtl.js -Tbrowser hello.pas

Для nodejs компилятор вставит вызов rtl.run() автоматически в конец сгенерированного файла Javascript.

Существует базовый Object Pascal RTL, также доступны несколько модулей из пакетов FPC.

  • system
  • sysutils
  • Math
  • strutils
  • rtlconst
  • classes
  • contnrs
  • DB (да, TDataset)
  • fpcunit testsuite
  • custapp
  • restconnection
  • js (системные объекты javascript)
  • web (объекты, предоставляемые браузером)
  • libjquery (JQuery также доступен)
  • nodejs (среда выполнения базового узла)
  • typinfo
  • objpas
  • browserconsole (поддержка writeln)
  • dateutils
  • browserapp
  • nodejsapp

Где его взять

Компилятор pas2js и RTL - естественно - с открытым исходным кодом и могут быть загружены и использованы свободно.

Снапшоты

Снапшоты содержат двоичные файлы для Windows (32 и 64-разрядная версия), Linux (64-разрядная версия) и MacOS.

Снапшоты загружены в

Каждая версия имеет каталог с номером версии. Список изменений можно найти на странице журнала изменений Pas2JS Version Changes

SVN

svn co https://svn.freepascal.org/svn/projects/pas2js/trunk pas2js

Вам нужен FPC 3.0.4 или старше, чтобы скомпилировать его.

Перейдите в каталог и соберите его с помощью:

make clean all

Это создаст compiler/utils/pas2js/pas2js (Windows: compiler\utils\pas2js\pas2js.exe)

Как использовать pas2js

Аргументы командной строки в основном остаются такими же, как аргументы командной строки FPC. Сообщения об ошибках также в том же формате.

Компилятору необходим доступ ко всем источникам, поэтому вам необходимо указать путь к источникам всех используемых модулей.

Что касается компилятора FPC, поддерживается файл конфигурации, который имеет тот же синтаксис, что и файл конфигурации FPC. Обратите внимание, что снапшоты и версия svn уже содержат файл pas2js.cfg по умолчанию с путями поиска модулей (-Fu) для rtl и fcl.

По сути, команда такая же, как и в любой командной строке FPC. Единственное, что отличается - это цель: '-Tbrowser' или '-Tnodeejs'

Вот полный список аргументов командной строки: аргументы командной строки.

для браузера

Рассмотрим классику:

program hello;

begin
  Writeln('Hello, world!');
end.

Да, writeln поддерживается. Вот как это скомпилировать:

pas2js -Jc -Jirtl.js -Tbrowser hello.pas

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

<html>
  <head>
    <meta charset="utf-8"/>
    <script type="application/javascript" src="hello.js"></script>
  </head>
  <body>
    <script type="application/javascript">
     rtl.run();
    </script>
  </body>
</html>

Необходимые файлы:

  • hello.html
  • hello.js

Независимо от того, открыт ли hello.html двойным щелчком по нему в проводнике или помещен на сервер и открыт с помощью URL, это не имеет значения.

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

program hello;

uses browserconsole;

begin
  Writeln('Hello, world!');
end.

для NodeJS

pas2js -Tnodejs hello.pas

После успешной компиляции код можно запустить на узле с помощью следующей команды.

nodejs hello.js
Note-icon.png

Примечание: в MacOS это "node hello.js"

Supported syntax elements

В основном, поддерживается синтаксис Delphi 7. Это включает RTTI. Более подробный список можно найти в источниках файла translation.html.

  • режим Delphi и ObjFPC
  • Program, Units, пространства имен
  • инициализация модуля, но не финализация
  • Var, Const, Type
  • string (unicodestring), char (widechar), Boolean, Double, Byte, Shortint, Word, Smallint, longword, Longint, nativeint(int53), nativeuint(int52), currency
  • resourcestrings
  • Pointer (как ссылка на класс, массив, запись, указатель записи, интерфейс, но без арифметики указателя)
  • Record (но не variant records)
  • Functions, Procedures, вложенные, анонимные функции
  • типы функций: of object, reference to (закрытые)
  • аргументы функций: default, const, var, out
  • If-then-else
  • For-do
  • Repeat-until
  • While-do
  • With-do
  • try-finally
  • try-except
  • enums
  • sets
  • arrays static, dynamic, open, multi dimensionals
  • String like array operations: a:=[1,2,3]+[1,1];
  • class type, visibility, virtual, override, abstract, overload, properties, class properties, class var, class const, constructor, destructor
  • class-of
  • вложенные классы
  • интерфейсы: CORBA, COM, delegations, method resolution, reference counting, TVirtualInterface
  • внешние классы, переменные, константы
  • Энумератор for..in..do
  • Псевдоним типа, например, type TTranslateString = type string;
  • RTTI
  • asm блок для встраивания в JavaScript напрямую
  • директивы компилятора (e.g. $ifdef, $if, $define, $modeswitch, $R+)
  • проверка времени компиляции и времени выполнения

Есть некоторые конструкции, которые естественно не поддерживаются и никогда не будут поддерживаться:

  • Все, что связано с указателями памяти и арифметикой указателей.
  • Variant records

Подробно о поддерживаемых элементах и переходе с Pascal на JavaScript: translation.

Планируемые языковые особенности

По сути, идея состоит в том, чтобы вывести транспайлер pas2js на тот же уровень, что и FPC или Delphi. Это означает, что необходимо добавить следующее:

  • Продвинутые записи(records)
  • Проверка во время выполнения: переполнение(overflow) -Co, $Q
  • Обобщения(дженерики)
  • Тип помощников(helpers)
  • Массив констант

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

Другие не реализованные функции

  • Атрибуты
  • Перечисления с пользовательскими значениями
  • Глобальные свойства
  • Futures
  • Помощники(helpers) для типов, классов, записей
  • Inline
  • Библиотека
  • Объекты
  • Перегрузка оператора
  • Арифметика указателей
  • Ресурсы
  • Расширение RTTI, $RTTI
  • Вариантные записи(records)
  • Варианты(variants)

Интеграция pas2js в Lazarus

Lazarus понимает концепцию внешних классов, используемую pas2js, поэтому автозавершение кода будет работать.

Начиная с Lazarus 1.9, среда IDE может использовать pas2js.exe в качестве обычного компилятора.

Интеграция описана здесь: Интеграция pas2JS в Lazarus. Он все еще находится в стадии разработки, но планируется глубокая интеграция с Lazarus.

Импорт классов Javascript

Чтобы импортировать класс javascript, нужно написать обычное определение класса, которое имитирует класс Javascript. Можно использовать свойства. Многие примеры можно найти в модулях JS, web, nodejs и libjquery.

Вот простой пример:

  TJSFunction = class external name 'Function'(TJSObject)
  private
    Flength: NativeInt external name 'length';
    Fprototyp: TJSFunction external name 'prototyp';
  public
    name: String;
    property prototyp: TJSFunction read Fprototyp;
    property length: NativeInt read Flength;
    function apply(thisArg: TJSObject; const ArgArray: TJSValueDynArray): JSValue; varargs;
    function bind(thisArg: TJSObject): JSValue; varargs;
    function call(thisArg: TJSObject): JSValue; varargs;
  end;

[Приведенный выше код] объявляет объект TJSFunction: в Javascript функции являются объектами.

  • "external name 'Function'" означает, что вы объявляете класс Javascript, где именем класса Javascript является 'Function'.
  • (TJSObject) означает, что он наследуется от TJSObject и внешнего класса. Там не должно быть типа предка.
  • Поля объявлены так же, как в Паскале.
  • Чтобы объявить поля только для чтения, можно использовать хитрость: объявите поле с использованием модификатора external name "thename" и объявите свойство только для чтения с тем же именем (см. length declaration)
  • Varargs может использоваться, чтобы указать, что функция принимает любое количество аргументов.
  • JSValue может использоваться для указания неизвестного типа. Это более или менее эквивалентно типу Variant.

Создание простых объектов JS с новой функцией

Некоторые функции JS-фреймворка ожидают JS-объект в качестве параметра. Вот как это сделать в Pascal, используя функцию new из модуля JS:

// JavaScript:
DoIt({name:"Fred", id:3, size:4.3});
// Pascal;
DoIt(new(['name','Fred', 'id',3, 'size',4.3]));

Вы можете вложить его для создания субобъектов:

// JavaScript:
DoIt({name:"Fred", size:{width:3,height:2}});
// Pascal;
DoIt(new(['name','Fred', 'size',new(['width',3, 'height',2])]));

Вы можете использовать TJSArray._of для создания JS-массивов "на лету":

// JavaScript:
DoIt({numbers:[1,2,3]});
// Pascal;
DoIt(new(['numbers',TJSArray._of(1,2,3)]));

Отладка

Сгенерированный исходный код Javascript, конечно, видим и доступен отладке в браузере.

Более того, транспайлер может генерировать файл-маппер (прим.перев: это минифицированный/объединённый файл, связанный с файлами, из которых он получился), что означает, что вы сможете увидеть и отладить код Pascal в браузере (будет работать не [абсолютно] все, но многое. Это зависит [еще] и от браузера.)

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

-Jm

Проще всего включать исходники Pascal в файл-маппер

-Jminclude

You can tell the compiler to store all file names relative to a local base directory: Вы можете указать компилятору хранить все имена файлов в [путях] относительно локального базового каталога:

-Jmbasedir=DirName

И вы можете сохранять URL-адрес на карте, поэтому браузер будет использовать URL/вышеуказанное-относительное-имя-файла, чтобы получать исходный код:

-Jmsourceroot=URL

Баги

Пожалуйста, сообщайте об ошибках в багтрекере FPC в категории pas2js: http://bugs.freepascal.org

Примеры

Виджеты Lazarus

Конечная цель, конечно, - запустить LCL в Всемирной сети. Обсуждения по этой теме делегированы на отдельной странице. pas2js_widgetsets

ЧаВо

Почему простая программа "hello world" такая большая?

Это в основном связано с использованием rtl.js. Файл rtl.js содержит код для паскалевских модулей, классов, RTTI, наборов, проверок диапазона и т.д., и написан с учетом больших WebApp, а не для сценариев с несколькими строками кода.

  1. Вы можете использовать Javascript minifier ("минимизатор"), чтобы уменьшить созданный Javascript
  2. Вы можете создать свой собственный "минимизатор" rtl.js, удалив все ненужные вам функции. В конечном итоге это будет сделано автоматически pas2js.

Почему [нежелательно] применение asm-вставок?

Asm-вставки полезны для вещей, которые вы не можете сделать с [помощью] pas2js. Но есть некоторые недостатки: pas2js не разбирает JS. Он также не проверяет синтаксис и не знает, на какие идентификаторы Паскаля ссылается код. Это означает, что любой идентификатор, доступный только блоку asm, будет удален оптимизатором pas2js.

Поэтому всегда старайтесь делать это на Паскале. Помните, что вы можете типизировать значения в JSValue, объекты в TJSObject, массивы в TJSArray, строки в TJSString и т.д., чтобы использовать почти все функции JS.

Почему не парсятся(разбираются) asm-вставки?

Любой анализатор JS времени компиляции может выполнять только проверку синтаксиса и анализировать только простые JS. Но так как простой JS может быть лучше написан на Паскале, это несколько бессмысленно и поэтому имеет низкий приоритет.