Difference between revisions of "pas2js"

From Lazarus wiki
Jump to navigationJump to search
(159 intermediate revisions by 15 users not shown)
Line 1: Line 1:
= Pas2js : What is it ? =
+
{{LanguageBar}}
  
== compiler ==
+
== Pas2js : What is it ? ==
Pas2js is a pascal to javascript transpiler. It parses Object Pascal and emits Javascript.
+
 
The javascript is currently of level ECMAScript 5 and should run in any browser or in nodejs.
+
=== Compiler ===
it is available in 3 forms:
+
 
 +
Pas2js is an open source [[Pascal]] to [[JavaScript]] transpiler. It parses Object Pascal and emits JavaScript.
 +
The JavaScript is currently of level ECMAScript 5 and should run in any browser or in Node.js (target "nodejs").
 +
It is available in 3 forms:
 
* as a library  
 
* as a library  
 
* as a command-line program
 
* as a command-line program
 
* as a webserver
 
* as a webserver
  
It transpiles from actual pascal source, it has no intermediate .ppu file.  
+
It transpiles from actual Pascal source, it has no intermediate .ppu files.  
 
That means all sources must always be available.
 
That means all sources must always be available.
  
Through external class definitions, the compiler can use Javascript classes:
+
Through external class definitions, the compiler can use JavaScript classes:
* All classes available in the Javascript runtime, and in the browser are available <br/>through import units (comparable to the windows or unix units for the native compiler).
+
* All classes available in the JavaScript runtime, and in the browser are available <br/>through import units (comparable to the windows or unix units for the native compiler).
* For nodejs, basic support for the nodejs runtime environment is available.
+
* For Node.js, basic support for the nodejs runtime environment is available.
 
* An import unit for jQuery is available (libjquery)
 
* An import unit for jQuery is available (libjquery)
  
== RTL ==
+
As a non commercial open source project we are always searching for helping hands. If you want to contribute see [[Pas2js How to contribute|here]].
For the generated code to work, a small file is needed: rtl.js. It defines an object rtl.
 
This file will start the pascal code if you include a call rtl.run().
 
  
<pre>
+
This project is '''not''' related to a similar named project on github.
 +
 
 +
=== RTL ===
 +
 
 +
For the generated code to work, a small JavaScript file is needed: rtl.js. It defines an object rtl.
 +
This object will start the Object Pascal code if you include a call to rtl.run() in the [[HTML]] page.
 +
 
 +
<syntaxhighlight lang="html4strict">
 
<script type="application/javascript">
 
<script type="application/javascript">
 
   rtl.run()
 
   rtl.run()
 
</script>
 
</script>
</pre>
+
</syntaxhighlight>
 
pas2js can automatically include this file in the generated output, like this:
 
pas2js can automatically include this file in the generated output, like this:
 
<pre>
 
<pre>
 
pas2js -Jc -Jirtl.js -Tbrowser hello.pas
 
pas2js -Jc -Jirtl.js -Tbrowser hello.pas
 
</pre>
 
</pre>
 +
For nodejs, the compiler will insert the call to rtl.run() automatically at the end of the generated Javascript file.
  
 
There is a basic Object Pascal RTL, several units from the FPC Packages are also available
 
There is a basic Object Pascal RTL, several units from the FPC Packages are also available
Line 47: Line 56:
 
* libjquery (jquery is available too)
 
* libjquery (jquery is available too)
 
* nodejs (basic node runtime environment)
 
* nodejs (basic node runtime environment)
* typeinfo
+
* typinfo
 
* objpas
 
* objpas
 
* browserconsole (support writeln)
 
* browserconsole (support writeln)
Line 54: Line 63:
 
* nodejsapp
 
* nodejsapp
  
= Where to get it =
+
== Where to get it ==
The pas2js compiler is - naturally - open source and can be downloaded and used freely.
+
 
The sources are currently the primary means to get it.
+
The pas2js compiler and RTL are - naturally - open source and can be downloaded and used freely.
 +
 
 +
=== Daily Snapshots ===
 +
 
 +
The daily snapshot directory contains binaries for Linux (64 bit) for trunk and latest fixes branch.
 +
Other OSes (macOS, Linux) will be made available as time permits.
  
== Compiler ==
+
The snapshots are uploaded to
 +
* ftp://ftpmaster.freepascal.org/fpc/contrib/pas2js/snapshot
  
* The Pas2js compiler is part of Free Pascal. As such, the compiler is present in the utils/pas2js directory of the
+
=== Releases ===
[https://svn.freepascal.org/cgi-bin/viewvc.cgi/trunk/utils/pas2js/ FPC SVN repo]
 
  
== RTL ==
+
The releases contain binaries for '''Windows (32 and 64bit), Linux (64 bit) and macOS'''.
 +
 
 +
Installation procedure:
 +
 
 +
1. Download pas2js from:
 +
* ftp://ftpmaster.freepascal.org/fpc/contrib/pas2js
 +
: Every version has a directory with the version number.
 +
: A list of changes can be found on the changelog page [[Pas2JS Version Changes]]
 +
 
 +
2. Unpack it in folder of your choice. The example below uses '''C:\lazarus\pas2js\'''. The release contains three folders:
 +
* bin - contains the compiler as executable (pas2js or pas2js.exe) and library and some utilities.
 +
* demo - lots of examples
 +
* packages - the Pascal units of the RTL and other packages.
 +
 
 +
:[[File:Pas2js-folder.png|Pas2js Folder]]
 +
 
 +
3. You can create a simple config to let the compiler find the RTL and packages. Edit '''bin/pas2js.cfg''':
  
* The pas2js RTL is still in flux. To enable faster releases, the RTL and demos are in a separate repository in SVN:
 
[https://svn.freepascal.org/svn/projects/pas2js/trunk https://svn.freepascal.org/svn/projects/pas2js/trunk]
 
 
<pre>
 
<pre>
svn co https://svn.freepascal.org/svn/projects/pas2js/trunk pas2js
+
#
 +
# Minimal config file for pas2js compiler
 +
#
 +
# -d is the same as #DEFINE
 +
# -u is the same as #UNDEF
 +
#
 +
# Write always a nice logo ;)
 +
-l
 +
 
 +
# Display Hints, Warnings and Notes
 +
-vwnh
 +
# If you don't want so much verbosity use
 +
#-vw
 +
 
 +
# Allow C-operators
 +
-Sc
 +
 
 +
-Fu$CfgDir\..\packages\*
 +
-Fu$CfgDir\..\compiler\utils\pas2js\dist
 +
 
 +
#IFDEF nodejs
 +
-Jirtl.js
 +
#ENDIF
 +
 
 +
# end.
 
</pre>
 
</pre>
  
== Snapshots ==  
+
4. To use pas2js into Lazarus IDE see: https://wiki.freepascal.org/lazarus_pas2js_integration
Till pas2js is integrated in the main FPC distribution, snapshots will be made available as time permits.  
+
 
They will contain binaries for Windows, Linux (64 bit) and Macos.
+
=== GIT ===
 +
 
 +
The sources are available on gitlab, and a read-only mirror exists on github.
 +
 
 +
<syntaxhighlight lang="bash">
 +
git clone https://gitlab.com/freepascal.org/fpc/pas2js.git
 +
</syntaxhighlight>
 +
 
 +
or
 +
<syntaxhighlight lang="bash">
 +
git clone https://github.com/fpc/pas2js.git
 +
</syntaxhighlight>
 +
 
 +
You need [[FPC]] 3.2.0 or better to compile it.
 +
 
 +
You will also need to check out the FPC repository inside the pas2js repository:
 +
 
 +
 
 +
<syntaxhighlight lang="bash">
 +
cd pas2js
 +
git clone https://gitlab.com/freepascal.org/fpc/fpc.git  compiler
 +
</syntaxhighlight>
 +
 
 +
Alternatively, set an environment variable FPCDIR to point to a checked-out version of FPC.
 +
 
 +
Change to the directory and build it with:
 +
 
 +
<syntaxhighlight lang="bash">
 +
make clean all
 +
</syntaxhighlight>
 +
 
 +
This creates ''bin/$(TargetCPU)-$(TargetOS)/pas2js'' (Windows: pas2js.exe). For example on Linux 64bit it creates ''bin/x86_64-linux/pas2js'', while under Windows 64bit it creates ''bin\x86_64-win\pas2js.exe''.
 +
 
 +
And create a text file pas2js.cfg in the folder where pas2js.exe is:
 +
<pre>
 +
# Write always a nice logo ;)
 +
-l
 +
 
 +
# Display Warnings, Notes and Hints
 +
-vwnh
 +
# If you don't want so much verbosity use
 +
#-vw
 +
 
 +
-Fu$CfgDir/../../packages/*
 +
-Fu$CfgDir/../../compiler/utils/pas2js/dist/
 +
 
 +
#IFDEF nodejs
 +
-Jirtl.js
 +
#ENDIF
  
A beta snapshot can be found for the following versions:
+
# Put all generated JavaScript into one file
* [http://www.freepascal.org/~michael/pas2js/pas2js-demo-0.8.39.zip Version 0.8.39]
+
-Jc
  
= How to use pas2js =
+
# end.
 +
</pre>
 +
 
 +
== How to use pas2js ==
  
 
The command-line arguments are kept mostly the same as the FPC command-line arguments.
 
The command-line arguments are kept mostly the same as the FPC command-line arguments.
Line 84: Line 187:
  
 
The compiler needs access to all sources, and so you need to specify the path to the sources of  
 
The compiler needs access to all sources, and so you need to specify the path to the sources of  
all used units.
+
all used units.  
 +
 
 +
As for the FPC compiler, a configuration file is supported, which has the same syntax as the FPC config file. Note that the snapshots and svn version already contains a default pas2js.cfg with unit search paths (-Fu) for the rtl and fcl. See here how for details about the [[pas2js.cfg]].
 +
 
 +
Basically, the command is the same as any FPC command line. The only thing that is different is the target: ''-Tbrowser'' or ''-Tnodeejs''
 +
 
 +
Here is the complete list of [[pas2js command line arguments|command line arguments]].
 +
 
 +
=== for the browser ===
 +
 
 +
Consider the classical:
 +
<syntaxhighlight lang="pascal">
 +
program hello;
 +
 
 +
begin
 +
  Writeln('Hello, world!');
 +
end.
 +
</syntaxhighlight>
 +
Yes, writeln is supported. Here is how to compile it:
 +
 
 +
<pre>
 +
pas2js -Jc -Jirtl.js -Tbrowser hello.pas
 +
</pre>
 +
When compiled succesfully, the code can be run in the browser by opening a html file in the browser with the following content:
 +
<syntaxhighlight lang="html4strict">
 +
<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>
 +
</syntaxhighlight>
 +
 
 +
The files that are needed are:
 +
* hello.html
 +
* hello.js
 +
Whether hello.html is opened by double-clicking it in the explorer or put on a server and opened with an URL, is not relevant for the functioning.
 +
 
 +
The output is visible in the browser's web developer console.
 +
By including the browserconsole unit, it will be visible in the browser page:
 +
 
 +
<syntaxhighlight lang="pascal">
 +
program hello;
 +
 
 +
uses browserconsole;
 +
 
 +
begin
 +
  Writeln('Hello, world!');
 +
end.
 +
</syntaxhighlight>
  
As for the FPC compiler, a configuration file is supported, which has the same syntax as the FPC config file.
+
=== for NodeJS ===
  
= Supported syntax elements =
+
<pre>
 +
pas2js -Tnodejs hello.pas
 +
</pre>
 +
When compiled succesfully, the code can be run in node using the following command.
 +
<pre>
 +
nodejs hello.js
 +
</pre>
  
Basically, Delphi 7 syntax minus interfaces is supported.
+
Note: on macOS it is "node hello.js"
This includes RTTI.
 
A more detailed list can be found in the translation.html file in the sources.
 
  
 +
== Supported syntax elements ==
 +
 +
A significant amount of Object Pascal syntax is supported, including RTTI.
 +
A more detailed list can be found in the [https://svn.freepascal.org/cgi-bin/viewvc.cgi/trunk/utils/pas2js/docs/translation.html?view=co translation.html].file in the sources.
 +
 +
*[[Mode_Delphi|Delphi]] and [[Mode_ObjFPC|ObjFPC]] mode
 +
*[[Program]], [[Unit]]s, namespaces
 +
*unit initialization, but not finalization
 +
*[[Var]], [[Const]], [[Type]]
 +
*string (unicodestring), char (widechar), [[Boolean]], [[Double]], [[Byte]], [[Shortint]], [[Word]], [[Smallint]], longword, [[Longint]], nativeint(int53), nativeuint(int52), currency
 +
*resourcestrings
 +
*[[Pointer]] (as a reference to a class, array, record, pointer of record, interface, but no pointer arithmetic)
 +
*[[Record]] (but no variant records), advanced records (since 1.3)
 +
*[[Function]]s, [[Procedure]]s, nested, anonymous functions (since 1.1)
 +
*function types, of object, reference to (closures)
 +
*function arguments: 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, array of const
 +
*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 constructor (since 1.5)
 +
*class-of
 +
*nested classes
 +
*interfaces: CORBA, COM, delegations, method resolution, reference counting, TVirtualInterface
 +
*external classes, vars, const
 +
*class helpers, record helpers, type helpers (since 1.3)
 +
*Enumeration for..in..do
 +
*Type alias, e.g. type TTranslateString = type string;
 +
*RTTI
 +
*asm block for embedding JavaScript directly
 +
*compiler directives (e.g. $ifdef, $if, $define, $modeswitch, $R+)
 +
*compile time and run time range checking. Overflow checking since 1.5.
 +
*attributes (since 1.5)
 +
*[[pas2js_Generics|generics]] (since 1.5)
 +
* [[pas2js_resources|Resources]] (since 1.5)
  
 
There are some constructs that are naturally not supported and will never be supported:
 
There are some constructs that are naturally not supported and will never be supported:
Anything involving memory pointers.
+
*Anything involving memory pointers and pointer arithmetic.
 +
*Variant records
 +
 
 +
And there are some extra constructs unique to pas2js:
 +
* '''JSValue''' - a base type representing a JavaScript value.
 +
* function Str(X[,Y,Z...]): string - same as procedure Str, except as a function
 +
*[[pas2js_AsyncAWait|async/await]]
 +
 
 +
Details about supported elements and the conversion from Pascal to JavaScript: [https://svn.freepascal.org/cgi-bin/viewvc.cgi/trunk/utils/pas2js/docs/translation.html?view=co translation].
 +
 
 +
== Planned language features ==
  
= Planned language features =
 
 
Basically, the idea is to get the pas2js transpiler up to the same level as FPC or Delphi.  
 
Basically, the idea is to get the pas2js transpiler up to the same level as FPC or Delphi.  
 
That means the following needs to be added:
 
That means the following needs to be added:
* More runtime/compile time checks
+
* Extended RTTI
* Interfaces
+
* Operator Overloading
* Generics
+
* anonymous records, e.g. ''var a: array of record w: word; end;''
* Type helpers
 
* Anonymous functions
 
  
 
Needless to say, anything requiring direct memory access is not going to be supported.
 
Needless to say, anything requiring direct memory access is not going to be supported.
  
= Lazarus integration of pas2js =
+
== Other unimplemented features ==
 +
 
 +
*Array of interface
 +
*Enums with custom values
 +
*Finalization sections, class destructors
 +
*Futures
 +
*Global properties
 +
*Inline
 +
*Library
 +
*Objects
 +
*Pointer arithmetic
 +
*Record field interface
 +
*Variants
 +
 
 +
== Lazarus integration of pas2js ==
  
 
Lazarus understands the concept of external classes as used by pas2js, so code completion will work.
 
Lazarus understands the concept of external classes as used by pas2js, so code completion will work.
  
pas2js comes with several lazarus packages that 'import' the pas2js rtl units:
+
Since Lazarus 1.9 the IDE can use pas2js.exe as a normal compiler.
by doing so, the IDE will know which units to use.
 
* pas2js_rtl : the basic RTL
 
* fcl_base_pas2js:  The custapp and other units
 
* fpcunit_pas2js:  the fpcunit units
 
* pas2js_fcldb: the database units
 
  
 +
The integration is described here: [[lazarus pas2js integration]].
 +
It is still under construction, but deep integration with lazarus is planned.
  
A new project wizard is under construction, but deeper integration with lazarus is planned.
+
== Importing Javascript classes ==
 
 
= Importing Javascript classes =
 
  
 
To import a javascript class, one writes a normal class definition that mimics the Javascript class.
 
To import a javascript class, one writes a normal class definition that mimics the Javascript class.
 
It is possible to use properties. Many examples can be found in the JS, web, nodejs and libjquery units.
 
It is possible to use properties. Many examples can be found in the JS, web, nodejs and libjquery units.
 +
 +
Here is a simple example:
 +
 +
<syntaxhighlight lang="pascal">
 +
  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;
 +
</syntaxhighlight>
 +
 +
This declares the <code>TJSFunction</code> object : in Javascript, functions are objects.
 +
* The "<code>external name 'Function'</code>" means that you declare a Javascript class where the Javascript name of the class is 'Function'.
 +
* The <code>(TJSObject)</code> means it descends from <code>TJSObject</code> also an external class. There does not need to be an ancestor type.
 +
* Fields are declared just as in Pascal.
 +
* To declare read-only fields, a trick can be used:</b> declare the field using an <var>external name "thename"</code> modifier, and declare a read-only property with the same name.<br>(see the length declaration)
 +
* <code>Varargs</code> can be used to indicate that a function accepts any number of arguments.
 +
* <code>JSValue</code> can be used to indicate an unknown type. <br/>It is more or less equivalent to a Variant.
 +
 +
== Create simple JS objects with the new function ==
 +
 +
Some JS-framework functions expect an JS object as parameter. Here is how to do that in Pascal using the ''new'' function from unit ''JS'':
 +
 +
<syntaxhighlight lang="javascript">
 +
// JavaScript:
 +
DoIt({name:"Fred", id:3, size:4.3});
 +
</syntaxhighlight>
 +
 +
<syntaxhighlight lang="pascal">
 +
// Pascal;
 +
DoIt(new(['name','Fred', 'id',3, 'size',4.3]));
 +
</syntaxhighlight>
 +
 +
You can nest it to create sub objects:
 +
 +
<syntaxhighlight lang="javascript">
 +
// JavaScript:
 +
DoIt({name:"Fred", size:{width:3,height:2}});
 +
</syntaxhighlight>
 +
 +
<syntaxhighlight lang="pascal">
 +
// Pascal;
 +
DoIt(new(['name','Fred', 'size',new(['width',3, 'height',2])]));
 +
</syntaxhighlight>
 +
 +
You can use TJSArray._of to create JS arrays on the fly:
 +
 +
<syntaxhighlight lang="javascript">
 +
// JavaScript:
 +
DoIt({numbers:[1,2,3]});
 +
</syntaxhighlight>
 +
 +
<syntaxhighlight lang="pascal">
 +
// Pascal;
 +
DoIt(new(['numbers',TJSArray._of(1,2,3)]));
 +
</syntaxhighlight>
 +
 +
== Resource strings ==
 +
 +
The pas2js transpiler can generate a JSON file (extension ''.jrs'') with all the resource strings in your program.
 +
 +
This is a quite simple file. A JSON object exists for every unit, with each json property a resource string.
 +
 +
<pre>
 +
{
 +
  "trs2" : {
 +
    "ResUsed" : "This resourcestring is used",
 +
    "ResUnUsed" : "This resourcestring is not used",
 +
    "ImplResUsed" : "This implementation resourcestring is used"
 +
  },
 +
  "trs1" : {
 +
    "MyString" : "The very nice string we will need to translate"
 +
  }
 +
}
 +
</pre>
 +
 +
This file can be translated, and the translation file can be loaded using the '''rstranslate''' unit, part of the rtl.
 +
There are demo programs which demonstrate the use of this feature.
 +
 +
The generating of this file is controlled by the '''-Jr''' option. It can take 3 possible arguments:
 +
# ''none'' This is the default, no file is generated.
 +
# ''unit'' one file per compiled unit will be generated. This file will contain all resource strings of the unit.
 +
# ''program'' one file is generated for the main file. This fill will contain all used resource strings for the main file and all the units it uses.
 +
 +
If you compile a program, then the ''program'' option will generate a file with all the used resource strings in your program.
 +
 +
The above example was generated using the command:
 +
 +
<pre>
 +
pas2js -Jrprogram trs1.pp -B
 +
</pre>
 +
 +
Note that the format is different from the format used by FPC:
 +
* Identifiers in the file are case sensitive: the names must be typed as they appear in the source file.
 +
* The strings are grouped per unit, this allows to load them fasters
 +
* The hash and bytes parts are missing, they make little sense in a Javascript context.
 +
 +
== Exceptions ==
 +
 +
Exceptions are translated to actual Javascript exceptions. The rtl.js has several mechanisms to deal with uncaught exceptions.
 +
The basic mechanism is setting the ''showUncaughtExceptions'' to true before calling rtl.run() in your html file:
 +
 +
<pre>
 +
<script type="application/javascript">
 +
  rtl.showUncaughtExceptions=true;
 +
  rtl.run();
 +
</script>
 +
</pre>
 +
 +
the browser will then use a ''window.alert()'' to show uncaught exceptions.
 +
 +
More explanations can be found in [[pas2js_exceptions]]
 +
 +
== Debugging ==
 +
 +
The generated Javascript source code is of course visible and debuggable in the browser.
 +
 +
Moreover, the transpiler can generate a source map, which means that you will be able to see and debug the Pascal code in the browser.
 +
(not everything will work, but many things do. This depends on the browser too.)
 +
 +
A source map can be generated using the command-line parameter:
 +
 +
<pre>
 +
-Jm
 +
</pre>
 +
 +
The easiest is to include the Pascal sources in the source map:
 +
 +
<pre>
 +
-Jminclude
 +
</pre>
 +
 +
By default all source filenames are relative to .js.map. You can tell the compiler to store all file names relative to a specific local base directory:
 +
 +
<pre>
 +
-Jmbasedir=DirName
 +
</pre>
 +
 +
And you can store an URL in the map, so the browser will use URL/above-relative-file-name to get the source:
 +
 +
<pre>
 +
-Jmsourceroot=URL
 +
</pre>
 +
 +
== Porting from FPC/Delphi ==
 +
 +
See [[Porting from FPC/Delphi to pas2js|here]] for tips and traps porting code from FPC and Delphi.
 +
 +
Delphi cannot parse some of the constructs that exist in ''pas2js'' (namely: external classes).
 +
You can create stub declarations suitable for the Delphi parser with the [[pas2js makestub|stub creator]].
 +
 +
== Bugs ==
 +
 +
Please report bugs in the [https://bugs.freepascal.org FPC Bug Tracker] with category ''pas2js''.
 +
 +
== Examples ==
 +
 +
*Time Tracking Application: https://www.devstructor.com/demos/pas2js-time/source.zip  (sources: https://www.devstructor.com/demos/pas2js-time/source.zip)
 +
*Drawing and animation on canvas: http://ragnemalm.se/images/santa/santa.html (sources: http://ragnemalm.se/images/santa/)
 +
*WebGL: https://github.com/genericptr/Pas2JS-WebGL#pas2js-webgl
 +
*Allegro Web Game: https://lainz.github.io/AllegroPas2JS-Demo-Game/index.html (sources: https://github.com/lainz/AllegroPas2JS-Demo-Game)
 +
 +
== Lazarus Widgetset ==
 +
 +
The ultimate goal is of course to have the LCL running in the web. Discussions on this topic are  delegated to a separate page. [[pas2js_widgetsets]]
 +
 +
== FAQ ==
 +
 +
=== Why is a simple hello world program so big? ===
 +
 +
This is mainly due to the used rtl.js. The rtl.js contains code for Pascal modules, classes, RTTI, sets, range checks, etc and is written with big WebApps in mind, not for scripts with a few lines of code.
 +
 +
# You can use a Javascript [[pas2js minifier|minifier]] to reduce the created Javascript
 +
# You can create your own minified rtl.js by removing all functions you don't need. Eventually this will be done automatically by pas2js.
 +
 +
=== Why are asm blocks bad? ===
 +
 +
Asm blocks are useful for things you cannot do with pas2js. But there are some downsides:
 +
pas2js does not parse the JS. Neither does it check the syntax, nor does it know what Pascal identifiers the code is referencing. That means any identifier only accessed by the asm block will be removed by the pas2js' optimizer.
 +
 +
Therefore always try to do it in Pascal. Remember you can typecast values to JSValue, objects to TJSObject, arrays to TJSArray, strings to TJSString, etc to use almost all JS features.
 +
 +
=== Why not parse asm blocks? ===
 +
 +
Any compiletime JS parser can only do a syntax check and parse only simple JS. But since simple JS can be better written in Pascal, it is somewhat pointless and has therefore low priority.
 +
 +
=== What about optimization X? ===
 +
 +
See here for [[Pas2js optimizations]]
 +
 +
== See also ==
 +
 +
* [[pas2js html2form]] - a small tool that converts an HTML file to a Pascal class.
 +
 +
[[Category:Pas2js]]

Revision as of 08:59, 26 July 2021

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

Pas2js : What is it ?

Compiler

Pas2js is an open source Pascal to JavaScript transpiler. It parses Object Pascal and emits JavaScript. The JavaScript is currently of level ECMAScript 5 and should run in any browser or in Node.js (target "nodejs"). It is available in 3 forms:

  • as a library
  • as a command-line program
  • as a webserver

It transpiles from actual Pascal source, it has no intermediate .ppu files. That means all sources must always be available.

Through external class definitions, the compiler can use JavaScript classes:

  • All classes available in the JavaScript runtime, and in the browser are available
    through import units (comparable to the windows or unix units for the native compiler).
  • For Node.js, basic support for the nodejs runtime environment is available.
  • An import unit for jQuery is available (libjquery)

As a non commercial open source project we are always searching for helping hands. If you want to contribute see here.

This project is not related to a similar named project on github.

RTL

For the generated code to work, a small JavaScript file is needed: rtl.js. It defines an object rtl. This object will start the Object Pascal code if you include a call to rtl.run() in the HTML page.

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

pas2js can automatically include this file in the generated output, like this:

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

For nodejs, the compiler will insert the call to rtl.run() automatically at the end of the generated Javascript file.

There is a basic Object Pascal RTL, several units from the FPC Packages are also available

  • system
  • sysutils
  • Math
  • strutils
  • rtlconst
  • classes
  • contnrs
  • DB (yes, TDataset)
  • fpcunit testsuite
  • custapp
  • restconnection
  • js (javascript system objects)
  • web (browser provided objects)
  • libjquery (jquery is available too)
  • nodejs (basic node runtime environment)
  • typinfo
  • objpas
  • browserconsole (support writeln)
  • dateutils
  • browserapp
  • nodejsapp

Where to get it

The pas2js compiler and RTL are - naturally - open source and can be downloaded and used freely.

Daily Snapshots

The daily snapshot directory contains binaries for Linux (64 bit) for trunk and latest fixes branch. Other OSes (macOS, Linux) will be made available as time permits.

The snapshots are uploaded to

Releases

The releases contain binaries for Windows (32 and 64bit), Linux (64 bit) and macOS.

Installation procedure:

1. Download pas2js from:

Every version has a directory with the version number.
A list of changes can be found on the changelog page Pas2JS Version Changes

2. Unpack it in folder of your choice. The example below uses C:\lazarus\pas2js\. The release contains three folders:

  • bin - contains the compiler as executable (pas2js or pas2js.exe) and library and some utilities.
  • demo - lots of examples
  • packages - the Pascal units of the RTL and other packages.
Pas2js Folder

3. You can create a simple config to let the compiler find the RTL and packages. Edit bin/pas2js.cfg:

#
# Minimal config file for pas2js compiler
#
# -d is the same as #DEFINE
# -u is the same as #UNDEF
#
# Write always a nice logo ;)
-l

# Display Hints, Warnings and Notes
-vwnh
# If you don't want so much verbosity use
#-vw

# Allow C-operators
-Sc

-Fu$CfgDir\..\packages\*
-Fu$CfgDir\..\compiler\utils\pas2js\dist

#IFDEF nodejs
-Jirtl.js
#ENDIF

# end.

4. To use pas2js into Lazarus IDE see: https://wiki.freepascal.org/lazarus_pas2js_integration

GIT

The sources are available on gitlab, and a read-only mirror exists on github.

git clone https://gitlab.com/freepascal.org/fpc/pas2js.git

or

git clone https://github.com/fpc/pas2js.git

You need FPC 3.2.0 or better to compile it.

You will also need to check out the FPC repository inside the pas2js repository:


cd pas2js
git clone https://gitlab.com/freepascal.org/fpc/fpc.git  compiler

Alternatively, set an environment variable FPCDIR to point to a checked-out version of FPC.

Change to the directory and build it with:

make clean all

This creates bin/$(TargetCPU)-$(TargetOS)/pas2js (Windows: pas2js.exe). For example on Linux 64bit it creates bin/x86_64-linux/pas2js, while under Windows 64bit it creates bin\x86_64-win\pas2js.exe.

And create a text file pas2js.cfg in the folder where pas2js.exe is:

# Write always a nice logo ;)
-l

# Display Warnings, Notes and Hints
-vwnh
# If you don't want so much verbosity use
#-vw

-Fu$CfgDir/../../packages/*
-Fu$CfgDir/../../compiler/utils/pas2js/dist/

#IFDEF nodejs
-Jirtl.js
#ENDIF

# Put all generated JavaScript into one file
-Jc

# end.

How to use pas2js

The command-line arguments are kept mostly the same as the FPC command-line arguments. Error messages are also in the same format.

The compiler needs access to all sources, and so you need to specify the path to the sources of all used units.

As for the FPC compiler, a configuration file is supported, which has the same syntax as the FPC config file. Note that the snapshots and svn version already contains a default pas2js.cfg with unit search paths (-Fu) for the rtl and fcl. See here how for details about the pas2js.cfg.

Basically, the command is the same as any FPC command line. The only thing that is different is the target: -Tbrowser or -Tnodeejs

Here is the complete list of command line arguments.

for the browser

Consider the classical:

program hello;

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

Yes, writeln is supported. Here is how to compile it:

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

When compiled succesfully, the code can be run in the browser by opening a html file in the browser with the following content:

<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>

The files that are needed are:

  • hello.html
  • hello.js

Whether hello.html is opened by double-clicking it in the explorer or put on a server and opened with an URL, is not relevant for the functioning.

The output is visible in the browser's web developer console. By including the browserconsole unit, it will be visible in the browser page:

program hello;

uses browserconsole;

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

for NodeJS

pas2js -Tnodejs hello.pas

When compiled succesfully, the code can be run in node using the following command.

nodejs hello.js

Note: on macOS it is "node hello.js"

Supported syntax elements

A significant amount of Object Pascal syntax is supported, including RTTI. A more detailed list can be found in the translation.html.file in the sources.

  • Delphi and ObjFPC mode
  • Program, Units, namespaces
  • unit initialization, but not finalization
  • Var, Const, Type
  • string (unicodestring), char (widechar), Boolean, Double, Byte, Shortint, Word, Smallint, longword, Longint, nativeint(int53), nativeuint(int52), currency
  • resourcestrings
  • Pointer (as a reference to a class, array, record, pointer of record, interface, but no pointer arithmetic)
  • Record (but no variant records), advanced records (since 1.3)
  • Functions, Procedures, nested, anonymous functions (since 1.1)
  • function types, of object, reference to (closures)
  • function arguments: 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, array of const
  • 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 constructor (since 1.5)
  • class-of
  • nested classes
  • interfaces: CORBA, COM, delegations, method resolution, reference counting, TVirtualInterface
  • external classes, vars, const
  • class helpers, record helpers, type helpers (since 1.3)
  • Enumeration for..in..do
  • Type alias, e.g. type TTranslateString = type string;
  • RTTI
  • asm block for embedding JavaScript directly
  • compiler directives (e.g. $ifdef, $if, $define, $modeswitch, $R+)
  • compile time and run time range checking. Overflow checking since 1.5.
  • attributes (since 1.5)
  • generics (since 1.5)
  • Resources (since 1.5)

There are some constructs that are naturally not supported and will never be supported:

  • Anything involving memory pointers and pointer arithmetic.
  • Variant records

And there are some extra constructs unique to pas2js:

  • JSValue - a base type representing a JavaScript value.
  • function Str(X[,Y,Z...]): string - same as procedure Str, except as a function
  • async/await

Details about supported elements and the conversion from Pascal to JavaScript: translation.

Planned language features

Basically, the idea is to get the pas2js transpiler up to the same level as FPC or Delphi. That means the following needs to be added:

  • Extended RTTI
  • Operator Overloading
  • anonymous records, e.g. var a: array of record w: word; end;

Needless to say, anything requiring direct memory access is not going to be supported.

Other unimplemented features

  • Array of interface
  • Enums with custom values
  • Finalization sections, class destructors
  • Futures
  • Global properties
  • Inline
  • Library
  • Objects
  • Pointer arithmetic
  • Record field interface
  • Variants

Lazarus integration of pas2js

Lazarus understands the concept of external classes as used by pas2js, so code completion will work.

Since Lazarus 1.9 the IDE can use pas2js.exe as a normal compiler.

The integration is described here: lazarus pas2js integration. It is still under construction, but deep integration with lazarus is planned.

Importing Javascript classes

To import a javascript class, one writes a normal class definition that mimics the Javascript class. It is possible to use properties. Many examples can be found in the JS, web, nodejs and libjquery units.

Here is a simple example:

  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;

This declares the TJSFunction object : in Javascript, functions are objects.

  • The "external name 'Function'" means that you declare a Javascript class where the Javascript name of the class is 'Function'.
  • The (TJSObject) means it descends from TJSObject also an external class. There does not need to be an ancestor type.
  • Fields are declared just as in Pascal.
  • To declare read-only fields, a trick can be used: declare the field using an external name "thename" modifier, and declare a read-only property with the same name.
    (see the length declaration)
  • Varargs can be used to indicate that a function accepts any number of arguments.
  • JSValue can be used to indicate an unknown type.
    It is more or less equivalent to a Variant.

Create simple JS objects with the new function

Some JS-framework functions expect an JS object as parameter. Here is how to do that in Pascal using the new function from unit JS:

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

You can nest it to create sub objects:

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

You can use TJSArray._of to create JS arrays on the fly:

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

Resource strings

The pas2js transpiler can generate a JSON file (extension .jrs) with all the resource strings in your program.

This is a quite simple file. A JSON object exists for every unit, with each json property a resource string.

{
  "trs2" : {
    "ResUsed" : "This resourcestring is used",
    "ResUnUsed" : "This resourcestring is not used",
    "ImplResUsed" : "This implementation resourcestring is used"
  },
  "trs1" : {
    "MyString" : "The very nice string we will need to translate"
  }
}

This file can be translated, and the translation file can be loaded using the rstranslate unit, part of the rtl. There are demo programs which demonstrate the use of this feature.

The generating of this file is controlled by the -Jr option. It can take 3 possible arguments:

  1. none This is the default, no file is generated.
  2. unit one file per compiled unit will be generated. This file will contain all resource strings of the unit.
  3. program one file is generated for the main file. This fill will contain all used resource strings for the main file and all the units it uses.

If you compile a program, then the program option will generate a file with all the used resource strings in your program.

The above example was generated using the command:

pas2js -Jrprogram trs1.pp -B

Note that the format is different from the format used by FPC:

  • Identifiers in the file are case sensitive: the names must be typed as they appear in the source file.
  • The strings are grouped per unit, this allows to load them fasters
  • The hash and bytes parts are missing, they make little sense in a Javascript context.

Exceptions

Exceptions are translated to actual Javascript exceptions. The rtl.js has several mechanisms to deal with uncaught exceptions. The basic mechanism is setting the showUncaughtExceptions to true before calling rtl.run() in your html file:

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

the browser will then use a window.alert() to show uncaught exceptions.

More explanations can be found in pas2js_exceptions

Debugging

The generated Javascript source code is of course visible and debuggable in the browser.

Moreover, the transpiler can generate a source map, which means that you will be able to see and debug the Pascal code in the browser. (not everything will work, but many things do. This depends on the browser too.)

A source map can be generated using the command-line parameter:

-Jm

The easiest is to include the Pascal sources in the source map:

-Jminclude

By default all source filenames are relative to .js.map. You can tell the compiler to store all file names relative to a specific local base directory:

-Jmbasedir=DirName

And you can store an URL in the map, so the browser will use URL/above-relative-file-name to get the source:

-Jmsourceroot=URL

Porting from FPC/Delphi

See here for tips and traps porting code from FPC and Delphi.

Delphi cannot parse some of the constructs that exist in pas2js (namely: external classes). You can create stub declarations suitable for the Delphi parser with the stub creator.

Bugs

Please report bugs in the FPC Bug Tracker with category pas2js.

Examples

Lazarus Widgetset

The ultimate goal is of course to have the LCL running in the web. Discussions on this topic are delegated to a separate page. pas2js_widgetsets

FAQ

Why is a simple hello world program so big?

This is mainly due to the used rtl.js. The rtl.js contains code for Pascal modules, classes, RTTI, sets, range checks, etc and is written with big WebApps in mind, not for scripts with a few lines of code.

  1. You can use a Javascript minifier to reduce the created Javascript
  2. You can create your own minified rtl.js by removing all functions you don't need. Eventually this will be done automatically by pas2js.

Why are asm blocks bad?

Asm blocks are useful for things you cannot do with pas2js. But there are some downsides: pas2js does not parse the JS. Neither does it check the syntax, nor does it know what Pascal identifiers the code is referencing. That means any identifier only accessed by the asm block will be removed by the pas2js' optimizer.

Therefore always try to do it in Pascal. Remember you can typecast values to JSValue, objects to TJSObject, arrays to TJSArray, strings to TJSString, etc to use almost all JS features.

Why not parse asm blocks?

Any compiletime JS parser can only do a syntax check and parse only simple JS. But since simple JS can be better written in Pascal, it is somewhat pointless and has therefore low priority.

What about optimization X?

See here for Pas2js optimizations

See also