rescript-lang / rescript-compiler Goto Github PK
View Code? Open in Web Editor NEWThe compiler for ReScript.
Home Page: https://rescript-lang.org
License: Other
The compiler for ReScript.
Home Page: https://rescript-lang.org
License: Other
var x = require('x')
var y = require( 'y')
var y = require('y')
var x = require('x')
shall we clarify the order of importing module does not matter, user should not expect it?
so that the user does not need rebuild the runtime and standard library
It would be useful if we had external declarations to generate property access (obj.foo
, obj.foo = bar
) and also dynamic property access (obj[foo]
).
define(['exports', ...],function(exports,...){
}
instead of using return
, the benefit is that the code between commonjs
and amdjs
will be more similar
other cases
let v = Not_found
relevant #4129
do we need support do_at_exit
, it seems in js browser, there is no exit? but in server side, it is perfectly fine
for example
let rec f b x = b && f b x
and
let rec f b x = b || f b x
should be compiled into a loop
cases like
if (p0){
if(p1){
branch_code0
}else{
branch_code1
}
}else{
branch_code1
}
if(p0 && p1){
branch_code0
} else {
branch_code1
}
test cases:
let rec fib = function
| 1 | 23 -> 11111123
| n -> fib (n - 1 ) + fib (n - 2)
there are serveral built in exceptions
var Out_of_memory = [248, "Out_of_memory", 0];
var Sys_error = [248, "Sys_error", -1]; var Failure = [248, "Failure", -2];
var Invalid_argument = [248, "Invalid_argument", -3]; var End_of_file = [248, "End_of_file",-4];
var Division_by_zero = [248, "Division_by_zero", -5];
var Not_found = [248, "Not_found", -6];
var Match_failure = [248, "Match_failure", -7];
var Stack_overflow = [248, "Stack_overflow",-8];
var Sys_blocked_io = [248, "Sys_blocked_io", -9];
var Assert_failure = [248, "Assert_failure", -10];
var Undefined_recursive_module = [248, "Undefined_recursive_module", -11];
These are specially handled by the type checker, do they really need encode it that way, how about
var Out_of_memory = "Out_of_memory";
var Sys_error = "Sys_error"; var Failure = "Failure";
var Invalid_argument = "Invalid_argument"; var End_of_file = "End_of_file";
var Division_by_zero = "Division_by_zero";
var Not_found = "Not_found";
var Match_failure = "Match_failure";
var Stack_overflow = "Stack_overflow";
var Sys_blocked_io = "Sys_blocked_io";
var Assert_failure = "Assert_failure";
var Undefined_recursive_module = "Undefined_recursive_module";
I'm pleased that you list "Smaller code than hand written JavaScript" as a goal, as well as "dead code elimination". But I'm a little confused at the extent of this. As far as I can see, you're targeting separate module compilation which means that if I wanted to run this code in a browser, I'd need to combine my tiny hello.js with the 60k+ contents of stdlib/pervasives, caml_io, caml_string, caml_exceptions, etc.
So, can you shed some light on the plan? I'm sure there'splenty of stuff you just haven't got to yet, but I'm interested in your intent - are you planning to make ocamlscript able to produce a single browser-ready .js file including (dead-code-eliminated-version-of) the stdlib? Or are you planning to stick to 1:1 ocaml->js module symbols and have JS tooling take care of the dead code elimination, concatenation, etc?
for example
var print_endline = console.log
will have a ReferenceError
The following trivial example:
type point = {
x: string;
y: string;
}
let pmap fn p = { x = fn p.x; y = fn p.y }
produces
function pmap(fn, p) {
return [
/* record */0,
Caml_curry.app1(fn, p[1]),
Caml_curry.app1(fn, p[2])
];
}
It used to (and still does in the demo) produce
function pmap(fn, p) {
return [
/* record */0,
fn(p[1]),
fn(p[2])
];
}
Note that it also doesn't seem to get inlined:
let () = print_endline (pmap (fun x -> x) { x = "a"; y = "b" }).x
console.log(pmap(function (x) {
return x;
}, [
/* record */0,
"a",
"b"
])[1]);
there is an edge case, when none of the arguments are changed, our optimization will take it as an expression and generate a return statment
let v = ref 0
let acc = ref 0
let rec loop n : int =
if !v > n then !acc else begin incr v ; loop n end
A quick fix is to use continue
to mark it as a statement, so our engine will not return
it, in the future we should have a way to explitly mark Js_output.t
terminating...
for example:
let rec fib = function
| 1 | 2 | 5 | 33 -> 1
| n -> fib (n - 1 ) + fib (n - 2)
function fib(n) {
var exit = 0;
var switcher = n - 1;
if (switcher > 4 || switcher < 0) {
if (switcher !== 32) {
exit = 1;
}
else {
return 1;
}
}
else if (switcher === 3 || switcher === 2) {
exit = 1;
}
else {
return 1;
}
if (exit === 1) {
return fib(n - 1) + fib(n - 2);
}
}
this should be simplified
we need make sure int32 int64 semantics correct for sure.
for int (ocaml int is 31/63 bits), do we really need wrap those int everywhere, like
a+b
--> a+b|0
, the chance of overflow in ocaml often result in incorrect programs
OCaml objects are not recommended in JS backend, JS objects are recommended instead
we need support bigarray since lots of existing library depends on it like omd
etc
see #64
In current master the following code:
type p = { x: float }
let p = { x = 3. }
let map fn p = { x = fn p.x }
Generates:
function map(fn, p) {
return /* array */[fn(p[1])];
}
var p = /* float array */[3];
Note that the index in map
is 1. The issue doesn't appear in the browser demo (maybe it's fixed already?).
note that call ocaml from js is very easy, and there is not much work to do. (see the runtime representation #24), we also plan to use some ppx_derivings
to hide the internal representation.
calling js from ocaml is a bit hard, since you have to write type declarations, for binding simple functions
external array_map : 'a array -> ('a -> 'b) -> 'b array = "" [@@js.call "Array.prototype.map.call"]
for binding js objects we can do this
class type dom = object
method getElementById : string -> node
end
external document : dom = ""[@@js.global "document"]
let f v = document#getElementById v
To make it more efficient user can write bindings
class type dom = object
method getElementById : string -> node
method getElementById_js_01 : string -> node
method properties_js_set : string -> node
end
external document : dom = ""[@@js.global "document"]
let f v = document#getElementById_js_01 v
Note that document#getElementById_js_01
and document#getElementByID
compiles to js code which behaves exactly except that the former will be as efficient as handwritten js code
to make it easier to use js tools like rollup bundler or google closure compiler
I'm not an expert in OCaml or JavaScript, just someone who's exploring the various functional languages that compile to JavaScript for use in a long-term hobby project.
Your README says:
Since our work is derivative work of js_of_ocaml, the license of the OCamlScript components is GPLv2, the same as js_of_ocaml.
But the js_of_ocaml license is LGPL, not GPL. In https://github.com/ocsigen/js_of_ocaml/blob/master/LICENSE, the relevant statement is:
The Compiler and Library are distributed under the terms of the GNU
Library General Public License version 2 (included below).
[emphasis mine]
As I understand it (I'm not an expert by any means), using GPL instead of LGPL (for library/runtime files; for the compiler, it doesn't matter) prevents combining with commercial software, or with software under a license that's incompatible with the GPL, such as the Eclipse Public License. (Suppose someone wants to port something from the Clojure(Script) ecosystem, which is largely EPL.)
Secondly, the js_of_ocaml license also includes the following useful exception:
As a special exception to the GNU Library General Public License, you
may link, statically or dynamically, a "work that uses the Library"
with a publicly distributed version of the Library to produce an
executable file containing portions of the Library, and distribute
that executable file under terms of your choice, without any of the
additional requirements listed in clause 6 of the GNU Library General
Public License.
If I understand correctly, this exception is useful for (among other things) distributing open-source software via the Apple app store, whose terms are otherwise incompatible with the GPL or LGPL. While much software compiled to JavaScript is just distributed via a web server, where no app store licenses apply, one might also want to distribute compiled-to-JavaScript code (and the accompanying runtime and library) in a Cordova or React Native app.
I think it's safe to use js version for string_of_int
, we might be careful for string_of_float
, do people really rely on precisely how string_of_float
work? the documentation of string_of_float
is unclear to me http://caml.inria.fr/pub/docs/manual-ocaml/libref/Pervasives.html
One of the design goals in the readme says
Straight-forward FFI, generate tds file to target Typescript for better tooling.
Does this mean that typescript *.d.ts definition files can be automatically generated for the compiled JS?
The following code:
let x = mod_float
Generates:
function x(_, _$1) {
return /* Missing primitve */"caml_fmod_float";
}
so that the third party tool can load js directly follow the order, this is helpful, for example, if we use requirejs as module loader, we can still keep commonjs syntax, but pre-load files instead, so we don't need generate amdjs and commonjs when debugging at the same time
I'm curious, what are the odds of not requiring a compiler patch? I believe that compiler-libs
lets you compose your own compiler pipeline as long as you don't need to change any of the fundamental data structures. Are there changes that could be submitted upstream to enable building OcamlScript
purely on top of compiler-libs
?
when calculate Lambda.free_variables
also return the occurence
, suppose parameter expression has no side effect, if it is 0, don't do anything, if it is 1 (common cases for small function), do the substitution directly
also check:
If there's a PR#, I believe it's possible to use OPAM to quickly try a compiler patch and have it become a proper OPAM switch.
Goal:
Some documentation about the current encoding is [https://github.com/bloomberg/ocamlscript/blob/master/docs%2Fffi.md] here, there is a problem with this encoding is that Pfield i
is no longer the same as Parrayref
, while the internal OCaml compiler think it is the same, for example stdlib/camlinternalOO.ml
, bytecomp/translobj.ml
, bytecomp/translclass.ml
there might be some other files I am missing. (Recent changes in the trunk of stdlib/camlinternalOO
requires us to sync up the change)
So I am thinking of that Obj.tag
is not used in too much in the compiler itself(except the GC, which is not relevant in js backend)
So I am proposing that blocks with tag zero
is encoded as array, blocks with tag non zero (mostly normal variants) will be encoded as array plus an extra property via Object.defineProperty
so that they are polymorphic. and Pfield
, Parrayref
will behave the same.
for example
type u =
| B of int * int
|A of int * int * int
A (1,2,3)
will be encoded as Object.defineProperty([1,2,3], {'t', {'value', 1}}
B(1,2)
will be encoded as [1,2]
You can see here that I've found an issue in the String.set
implementation (in the interactive web version of the compiler).
https://news.ycombinator.com/item?id=10866113
JS Strings
are immutable so using regular JS strings as OCaml strings will not work. However, it might be worth simplifying the problem by saying "OCamlScript is intended for use with newer versions of OCaml where Strings are not mutable". This observation then led to a larger general discussion about compatibility and how adhering to strictly compatible compilation might open up opportunities to push performance concerns onto other toolchains like js_of_ocaml
(or even yet another JS compilation mode that makes use of the upcoming f-lambda optimizations), which I suggested could free up OCamlScript
to focus on creating a world-class developer experience.
I'm excited that there's a fresh take on JS compilation and learning a ton from the discussions about this.
let f : 'a -> 'b = raise Not_found
let f : 'a -> 'b -> c = fun _ -> raise Not_found
Hi when I try to compile in the jscomp
folder I got this error
ocamlopt.opt -I +compiler-libs -I bin -c bin/compiler.mli bin/compiler.ml
File "bin/compiler.ml", line 1009, characters 32-48:
Error: This variant pattern is expected to have type Lambda.primitive
The constructor Pbytes_to_string does not belong to type Lambda.primitive
generator in es6 is a pretty useful feature, it servers something like BatEnum.t
in ocaml
I could be wrong, but I believe that OCaml's variable scoping more closely aligns with javascript's, let
than var
. I noticed that the code generated by the compiler in the comments uses var exclusively. I'd love to hear other people's thoughts on the matter.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.