rlang 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/CHANGELOG.md +5 -0
- data/Gemfile.lock +7 -1
- data/README.md +21 -1
- data/bin/rlang +18 -53
- data/docs/RlangManual.md +65 -27
- data/examples/fib/fib.rb +11 -0
- data/examples/fib/index.html +38 -0
- data/examples/fib/server.rb +16 -0
- data/lib/builder/rlang.rb +1 -1
- data/lib/builder/rlang/builder.rb +41 -9
- data/lib/builder/rlang/compiler.rb +83 -0
- data/lib/builder/wat/builder.rb +12 -21
- data/lib/rlang/lib.rb +1 -1
- data/lib/rlang/lib/malloc.rb +9 -19
- data/lib/rlang/lib/object.rb +16 -0
- data/lib/rlang/lib/unistd.rb +22 -0
- data/lib/rlang/parser.rb +176 -33
- data/lib/rlang/parser/data.rb +5 -0
- data/lib/rlang/parser/export.rb +4 -0
- data/lib/rlang/parser/global.rb +4 -0
- data/lib/rlang/parser/ivar.rb +36 -0
- data/lib/rlang/parser/klass.rb +49 -0
- data/lib/rlang/parser/marg.rb +4 -0
- data/lib/rlang/parser/method.rb +8 -4
- data/lib/rlang/parser/wattr.rb +19 -6
- data/lib/rlang/parser/wgenerator.rb +146 -47
- data/lib/rlang/parser/wnode.rb +108 -60
- data/lib/rlang/parser/wtype.rb +1 -0
- data/lib/rlang/version.rb +1 -1
- data/rlang.gemspec +1 -0
- metadata +23 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0e1f5809295de94b29d997b98d712156654bc60621394d0476b5fc048ee70c0
|
4
|
+
data.tar.gz: d35a2adf54dc40514dfa2f966b1ff9d992a11495c3c2c13f927e380be7616fda
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fcb55360cc77db0bf05466a419f876274f210f3af0ce66af88210f08899e9f45b473efde8657914ea8eb0c1fc32e8816e3c0849b282b22807fce7b297319e5d0
|
7
|
+
data.tar.gz: 9021a72e563ba1608d3fd5208929d619f4324b5274d8506e42e7a8a29d397178dd58254ba78b096fc29b0c16d4acb0ff92343e6e2e49e16c617c959298104080
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
## 0.4.0
|
2
|
+
* Object instances, instance methods and instance variables now supported
|
3
|
+
* Test suite using parser directly
|
4
|
+
* Code coverage is enabled in test suite
|
5
|
+
|
1
6
|
## 0.3.1
|
2
7
|
* Add ruby parser as runtime dependency in gemspec
|
3
8
|
* DAta directive documented in Rlang manual
|
data/Gemfile.lock
CHANGED
@@ -1,18 +1,23 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rlang (0.
|
4
|
+
rlang (0.4.0)
|
5
5
|
parser
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
10
|
ast (2.4.0)
|
11
|
+
docile (1.3.2)
|
11
12
|
minitest (5.14.0)
|
12
13
|
parser (2.7.0.2)
|
13
14
|
ast (~> 2.4.0)
|
14
15
|
rake (12.3.3)
|
15
16
|
rutie (0.0.4)
|
17
|
+
simplecov (0.18.1)
|
18
|
+
docile (~> 1.1)
|
19
|
+
simplecov-html (~> 0.11.0)
|
20
|
+
simplecov-html (0.11.0)
|
16
21
|
wasmer (0.3.0)
|
17
22
|
rutie (~> 0.0.3)
|
18
23
|
|
@@ -24,6 +29,7 @@ DEPENDENCIES
|
|
24
29
|
minitest (~> 5.0)
|
25
30
|
rake (~> 12.0)
|
26
31
|
rlang!
|
32
|
+
simplecov (~> 0.18)
|
27
33
|
wasmer (~> 0.3)
|
28
34
|
|
29
35
|
BUNDLED WITH
|
data/README.md
CHANGED
@@ -23,11 +23,31 @@ $ gem install rlang
|
|
23
23
|
Alternatively, if you clone this git repo and play with the Rlang source code you can generate your own local gem and install it like this:
|
24
24
|
|
25
25
|
```
|
26
|
+
$ bundle install
|
26
27
|
$ gem build rlang.gemspec
|
27
28
|
$ gem install --local rlang-x.y.z.gem
|
28
29
|
```
|
29
30
|
|
30
|
-
To check that the installation went well, run `rlang --help` and see if the help message displays correctly
|
31
|
+
To check that the installation went well, run `rlang --help` and see if the help message displays correctly.
|
32
|
+
|
33
|
+
## A Quick example
|
34
|
+
An example is always worth a thousand words, so here is a quick one that'll show you how to compile some Rang code and run it from your browser. Since Chrome, Chromium, Firefox and Safari all natively embed a WebAssembly runtime it is the easiest way to test compiled Rlang code.
|
35
|
+
|
36
|
+
Navigate to the Fibonacci directory in [examples/fib](https://github.com/ljulliar/rlang/blob/master/examples/fib/) and download the three files from this directory: fib.rb, index.html, server.rb. Alternatively you can git clone the repo and go directly in that directory.
|
37
|
+
|
38
|
+
Open [fib.rb](https://github.com/ljulliar/rlang/blob/master/examples/fib/fib.rb) with your favorite editor and see for yourself that it really looks like Ruby code :-). Then from a shell session:
|
39
|
+
|
40
|
+
```shell
|
41
|
+
# Compile the fib.rb file to Wasm bytecode
|
42
|
+
$ rlang --wasm -o ./fib.wasm ./fib.rb
|
43
|
+
# Launch the mini http server
|
44
|
+
$ ruby server.rb
|
45
|
+
|
46
|
+
```
|
47
|
+
Point your browser to [http://localhost:8080](http://localhost:8080), a simple page should display inviting you to type an integer value then click run to obtain the corresponding fibonacci term.
|
48
|
+
|
49
|
+
That was easy, right ?
|
50
|
+
|
31
51
|
|
32
52
|
## The Rlang language
|
33
53
|
Ruby features supported by Rlang are detailed in the [Rlang Manual](https://github.com/ljulliar/rlang/blob/master/docs/RlangManual.md)
|
data/bin/rlang
CHANGED
@@ -14,31 +14,11 @@
|
|
14
14
|
require 'optparse'
|
15
15
|
require 'fileutils'
|
16
16
|
require 'rlang'
|
17
|
+
require 'builder'
|
17
18
|
|
18
19
|
include Log
|
19
20
|
logger.level = Logger::INFO
|
20
21
|
|
21
|
-
# WAT source frame
|
22
|
-
WAT_FRAME = %q{
|
23
|
-
;; Generated by Rlang compiler version %{version} on %{time}\n"
|
24
|
-
(module %{module}
|
25
|
-
(memory $0 %{memory_min} %{memory_max})
|
26
|
-
|
27
|
-
;; ======= EXPORTS =======
|
28
|
-
(export "memory" (memory $0))
|
29
|
-
%{exports}
|
30
|
-
|
31
|
-
;; ======= GLOBAL VARIABLES =======
|
32
|
-
%{globals}
|
33
|
-
|
34
|
-
;; ======= STATIC DATA =======
|
35
|
-
%{data}
|
36
|
-
|
37
|
-
;; ======= CODE =======
|
38
|
-
%{code}
|
39
|
-
)
|
40
|
-
}
|
41
|
-
|
42
22
|
options = {}
|
43
23
|
OptionParser.new do |opts|
|
44
24
|
opts.banner = %q{Usage: rlang [options] filename
|
@@ -59,8 +39,8 @@ options:
|
|
59
39
|
}
|
60
40
|
|
61
41
|
opts.on("-I DIR", "--load_path DIRECTORY", "specify $LOAD_PATH directory (may be used more than once)") do |dir|
|
62
|
-
options[:
|
63
|
-
options[:
|
42
|
+
options[:LOAD_PATH] ||= []
|
43
|
+
options[:LOAD_PATH] << dir
|
64
44
|
end
|
65
45
|
|
66
46
|
opts.on("-M", "--module NAME", "WASM module name") do |name|
|
@@ -92,8 +72,8 @@ options:
|
|
92
72
|
end
|
93
73
|
|
94
74
|
opts.on("-v", "--verbose [LEVEL]", "Verbosity level (fatal, error, warn, info, debug)") do |level|
|
95
|
-
options[:
|
96
|
-
logger.level = Kernel.const_get("Logger::#{options[:
|
75
|
+
options[:log_level] = level || 'INFO'
|
76
|
+
logger.level = Kernel.const_get("Logger::#{options[:log_level].upcase}")
|
97
77
|
end
|
98
78
|
|
99
79
|
opts.on("-V", "--version", "Displays Rlang version") do |v|
|
@@ -109,7 +89,7 @@ end.parse!(ARGV)
|
|
109
89
|
|
110
90
|
options[:module] ||= ''
|
111
91
|
options[:memory_min] ||= 4
|
112
|
-
options[:
|
92
|
+
options[:LOAD_PATH] ||= []
|
113
93
|
|
114
94
|
fh_out = options[:output] ? File.open(options[:output], 'w') : STDOUT
|
115
95
|
|
@@ -124,38 +104,23 @@ if options[:ast]
|
|
124
104
|
end
|
125
105
|
|
126
106
|
if options[:wat] || options[:wasm]
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
#
|
136
|
-
# Do not delete temp file when closing
|
137
|
-
tf = Tempfile.new([File.basename(ARGV[0]), '.wat'])
|
138
|
-
tf.persist!
|
139
|
-
tf << WAT_FRAME % {version: Rlang::VERSION,
|
140
|
-
time: Time.now,
|
141
|
-
module: options[:module],
|
142
|
-
memory_min: options[:memory_min],
|
143
|
-
memory_max: options[:memory_max],
|
144
|
-
exports: Rlang::Parser::Export.transpile,
|
145
|
-
globals: Rlang::Parser::Global.transpile,
|
146
|
-
data: Rlang::Parser::DAta.transpile,
|
147
|
-
code: wg.root.transpile
|
148
|
-
}
|
149
|
-
tf.close
|
150
|
-
|
107
|
+
# Compile the Rlang file into a WAT file
|
108
|
+
source = File.expand_path(ARGV[0])
|
109
|
+
compiler = Builder::Rlang::Compiler.new(source, nil, options)
|
110
|
+
if compiler.compile
|
111
|
+
wat_path = compiler.target
|
112
|
+
else
|
113
|
+
exit 1
|
114
|
+
end
|
115
|
+
# Compile the WAt file to Wasm byte code
|
151
116
|
if options[:wasm]
|
152
|
-
builder = Builder::Wat::Builder.new(
|
117
|
+
builder = Builder::Wat::Builder.new(wat_path, options[:output])
|
153
118
|
builder.compile
|
154
119
|
elsif options[:wat]
|
155
120
|
if options[:output]
|
156
|
-
FileUtils.mv(
|
121
|
+
FileUtils.mv(wat_path, options[:output])
|
157
122
|
else
|
158
|
-
STDOUT.write(File.read(
|
123
|
+
STDOUT.write(File.read(wat_path))
|
159
124
|
end
|
160
125
|
end
|
161
126
|
end
|
data/docs/RlangManual.md
CHANGED
@@ -8,15 +8,13 @@ Still, to make this Ruby to WebAssembly compilation possible a number of trade-o
|
|
8
8
|
|
9
9
|
## The Rlang object model
|
10
10
|
|
11
|
-
In Rlang you can define classes and those classes can be instantiated
|
12
|
-
|
13
|
-
One of the consequence of this for instance is that you can statically instantiate a new object in the body of a class not in a method. In other words objects can be instantiated at compile time not at runtime (note: this may change in a future version)
|
11
|
+
In Rlang you can define classes and those classes can be instantiated either statically at compile time or dynamically at runtime. There is no inheritance mechanism today between classes in the current version. Classes cannot be nested.
|
14
12
|
|
15
13
|
## What Rlang does
|
16
14
|
|
17
15
|
Rlang provides:
|
18
16
|
* Classes, class attributes and class variables
|
19
|
-
* Object instantiation
|
17
|
+
* Object instantiation, attribute accessors and instance variables
|
20
18
|
* Method definition and method calls
|
21
19
|
* Integers and booleans
|
22
20
|
* Constants
|
@@ -48,9 +46,9 @@ calling that method later in your code is as simple as invoking `Math.fib(20)`
|
|
48
46
|
## Classes
|
49
47
|
Classes are core constructs of Rlang and are very similar to Ruby classes.
|
50
48
|
|
51
|
-
|
49
|
+
In Rlang, all methods must be defined within the scope of a class. In other words you can not define a method at the top level of your Rlang code (not a very good practice anyway, even in plain Ruby). Also there is no inheritance mechanism in Rlang in the current version.
|
52
50
|
|
53
|
-
Here is an example of a class definition and the initialization and use of a class variable written in Rlang:
|
51
|
+
Here is an example of a class definition and the initialization and use of a class variable written in Rlang (note: this example uses only class methods on purpose. Instance methods are covered later in this document):
|
54
52
|
|
55
53
|
```ruby
|
56
54
|
class MyClass
|
@@ -68,18 +66,16 @@ end
|
|
68
66
|
```
|
69
67
|
|
70
68
|
This short piece of code shows several interesting points:
|
71
|
-
1. A class variable can be statically initialized at the class level. Concretely this means that at compile time the memory location corresponding to the `@@cvar` class variable
|
69
|
+
1. A class variable can be statically initialized at the class level. Concretely this means that at compile time the memory location corresponding to the `@@cvar` class variable is statically initialized at 100.
|
72
70
|
1. Methods in this example are class methods, hence the use of `self.take_one` and `self.refill` in method definitions but instance methods are also supported (more on this later)
|
73
71
|
1. In `MyClass::take_one` you can see that Rlang also supports convenient syntactic sugar like `if` as a modifier or combined operation and assignment as in Ruby (here the `-=` operator)
|
74
72
|
|
75
|
-
### Class attributes
|
76
|
-
|
77
|
-
1. Define the
|
78
|
-
2. Define the
|
79
|
-
2. Define the corresponding accessors both getter and setter (like `attr_accessor` does in Ruby)
|
80
|
-
3. Compute the memory footprint needed when objects of this class are instantiated.
|
73
|
+
### Class attributes and instance variables
|
74
|
+
Rlang support both the use of class attributes and instance variables. Class attribute declaration is happening through the `wattr` directive. I actually defines a couple of things for you:
|
75
|
+
1. Define the corresponding accessors both getter and setter (like `attr_accessor` does in Ruby)
|
76
|
+
2. Define the corresponding instance variable
|
81
77
|
|
82
|
-
|
78
|
+
Here is an example showing the definition of a single attribute
|
83
79
|
```ruby
|
84
80
|
class Square
|
85
81
|
wattr :side
|
@@ -102,7 +98,7 @@ class Test
|
|
102
98
|
end
|
103
99
|
```
|
104
100
|
|
105
|
-
The code is pretty straightforward: a new square instance is created, its side is set to 10 and as you would expect the call to the Square#area method
|
101
|
+
The code is pretty straightforward: a new square instance is created (here it is created statically at compile time), its side attribute is set to 10 and, as you would expect, the call to the Square#area method returns 100.
|
106
102
|
|
107
103
|
### Class attribute type
|
108
104
|
In the example above the `side` attribute is implicitely using the `:I32` (long integer) WebAssembly type. It's the default Rlang type. Assuming you want to manage big squares, you'd have to use `:I64` (double integer) like this for the `side` attribute and also instruct Rlang that the return value of area is also `:I64` (more on this later).
|
@@ -120,19 +116,55 @@ end
|
|
120
116
|
```
|
121
117
|
|
122
118
|
## Object instantiation
|
123
|
-
|
119
|
+
Starting with version 0.4.0, Rlang is equipped with a dynamic memory allocator (see [Rlang library](#the-rlang-library) section). It is therefore capable of allocating objects in a dynamic way at *runtime*. Prior versions were only capable of allocating objects statically at *compile* time.
|
120
|
+
|
121
|
+
### Static objects
|
122
|
+
You have already seen how to perform static objects allocation in one of our previous example. A statement like `@@square = Square.new` appearing in the body of a class definition result in a portion of the WebAssembly memory being statically allocated and initialized at compile time by Rlang and the `@@square` variable points to that particular memory location. Similarly you can also statically instantiate and store an object in a global variable or in a constant like this:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
SQUARE = Square.new
|
126
|
+
$SQUARE = Square.new
|
127
|
+
```
|
128
|
+
|
129
|
+
### Dynamic objects
|
130
|
+
At any point in the body of method you can dynamically instantiate a new object. Here is an exemple:
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
class Cube
|
134
|
+
wattr :x, :y, :z
|
135
|
+
|
136
|
+
def initialize(x, y, z)
|
137
|
+
@x = x; @y = y; @z = z
|
138
|
+
end
|
124
139
|
|
125
|
-
|
140
|
+
def volume
|
141
|
+
@x * @y * @z
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
class Main
|
146
|
+
def self.run
|
147
|
+
cube = Cube.new(10, 20, 30)
|
148
|
+
v = cube.volume # would be 6000
|
149
|
+
# ... Do what eveer you have to do...
|
150
|
+
Object.free(cube)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
```
|
154
|
+
In this example the `Cube` method uses 3 instance variables `@x, @y, @z`. Those instance variables can be accessed through the usual attribute accessors like `Cube#x` and `Cube#x=`. In your code you can also use the `_size_` class method to know how much bytes an object consumes in meory. Here a call to `Cube._size_` would return 12 as each instance variable is an `I32` using 4 bytes each.
|
155
|
+
|
156
|
+
### (Lack of) Garbage collection
|
157
|
+
In its current version, Rlang doesn't come with a garbage collector. So all dynamically allocated objects must be eventually free'd explicitely using the `Object.free` method in your code when objects are no longer needed.
|
126
158
|
|
127
159
|
## Methods
|
128
|
-
Methods in Rlang are defined as you would normally do in Ruby by using. They can be either class or instance methods.
|
160
|
+
Methods in Rlang are defined as you would normally do in Ruby by using the `def` reserved keyword. They can be either class or instance methods.
|
129
161
|
|
130
162
|
### Method arguments
|
131
163
|
Rlang method definition supports fixed name arguments in any number. The *args and **args notation are not supported.
|
132
164
|
|
133
165
|
By default all arguments in Rlang are considered as being type `:I32` (a 32 bit integer). See the Type section below for more details. If your argument is of a different type you **must** explicitely state it.
|
134
166
|
```ruby
|
135
|
-
def
|
167
|
+
def m_three_args(arg1, arg2, arg3)
|
136
168
|
arg arg1: :Square, arg2: :I64
|
137
169
|
# your code here...
|
138
170
|
end
|
@@ -151,18 +183,18 @@ def self.m_no_return_value(arg1, arg2)
|
|
151
183
|
return
|
152
184
|
end
|
153
185
|
```
|
154
|
-
Similarly you can use `
|
186
|
+
Similarly you can use `result :I64` if your method is to return a double integer value or `return :Square` if you method returns an object.
|
155
187
|
|
156
|
-
With a few exceptions (see the Conditional and Iteration Structures sections below), each Rlang statements evaluate to a value. In the absence of an explicit `return
|
188
|
+
With a few exceptions (see the Conditional and Iteration Structures sections below), each Rlang statements evaluate to a value. In the absence of an explicit `return` statement, a method returns the value of the last evaluated statement. In the example above defining the `MyClass` class, the method `MyClass.take_one` returns the value of `@@cvar` after decreasing it by one and `MyClass.refill` returns 100.
|
157
189
|
|
158
|
-
Rlang also gives you the ability to declare the return type of a method like this
|
190
|
+
Rlang also gives you the ability to declare the return type of a method like this anywhere in your code.
|
159
191
|
```ruby
|
160
|
-
result :class_name, :method_name, :
|
192
|
+
result :class_name, :method_name, :your_type_here
|
161
193
|
```
|
162
194
|
|
163
|
-
This result directive must be used to instruct the compiler about the return type of a method if it has not seen it yet (e.g. the method definition is coming later in your source code). But keep in mind that this is only needed if the method returns something different than the default type (`:I32`).
|
195
|
+
This result directive must be used to instruct the compiler about the return type of a method if it has not seen it yet (e.g. the method definition is coming later in your source code) or when calling a method from another class defined in a different file not yet compiled. But keep in mind that this is only needed if the method returns something different than the default type (`:I32`).
|
164
196
|
|
165
|
-
|
197
|
+
Note: in this directive `:method_name` symbol starts with a `#` characters if it refers to an instance method. Without it it refers to a class method.
|
166
198
|
|
167
199
|
For an example see the [test_def_result_type_declaration.rb](https://github.com/ljulliar/rlang/blob/master/test/rlang_test_files/test_def_result_type_declaration.rb), a Rlang file that is part of the Rlang test suite.
|
168
200
|
|
@@ -410,8 +442,14 @@ Rlang comes with a library that provides a number of pre-defined classes and met
|
|
410
442
|
require 'rlang/lib'
|
411
443
|
```
|
412
444
|
|
413
|
-
For now, the Rlang library is very modest and
|
445
|
+
For now, the Rlang library is very modest and containoffers the following classes and methods
|
446
|
+
|
447
|
+
* **Memory** class: provides Rlang methods like `Memory::size` and `Memory::grow` mapping directly to the WebAssembly functions of the same name (see WebAssembly specficiations)
|
448
|
+
* **Malloc** class: provides a dynamic memory manager. It is used by Rlang to instantiate new objects but you can also use it in your own code if need be.
|
449
|
+
* Malloc.malloc(nbytes) : dynamically allocates nbytes of memory and return address of the allocated memory space or -1 if it fails
|
450
|
+
* Malloc.free(address) : frees the block of memory at the given address
|
451
|
+
* **Object** class: provides a couple of object management method. Use `Object.free` to free an object.
|
414
452
|
|
415
|
-
|
453
|
+
As a side note, the dynamic memory allocator provided with Rlang is quite simple. It is shamelessly adapted from example provided in the famous Kernigan & Richie C book 2nd edition. Take a look at the [C version](https://github.com/ljulliar/rlang/blob/master/lib/rlang/lib/malloc.c) and see how easy it is to rewrite it in [Rlang](https://github.com/ljulliar/rlang/blob/master/lib/rlang/lib/malloc.rb).
|
416
454
|
|
417
455
|
That's it! Enjoy Rlang and, as always, feedback and contributions are welcome.
|
data/examples/fib/fib.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
|
3
|
+
<html>
|
4
|
+
<head>
|
5
|
+
<script>
|
6
|
+
var importObject = {
|
7
|
+
host: {
|
8
|
+
print: function(arg) {
|
9
|
+
console.log(arg);
|
10
|
+
}
|
11
|
+
}
|
12
|
+
};
|
13
|
+
// Load the module using the newest Streaming API
|
14
|
+
WebAssembly.instantiateStreaming(fetch('/fib.wasm'), importObject)
|
15
|
+
.then(obj => {
|
16
|
+
// Add a listener to the button to execute the
|
17
|
+
// Wasm function when the button is pressed.
|
18
|
+
var button = document.getElementById('run');
|
19
|
+
button.addEventListener('click', function() {
|
20
|
+
var n = +document.getElementById('n').value;
|
21
|
+
console.log(n);
|
22
|
+
var fib_n = obj.instance.exports.math_c_fib(n);
|
23
|
+
console.log(fib_n);
|
24
|
+
document.getElementById('fib_n').innerHTML = fib_n;
|
25
|
+
}, false);
|
26
|
+
}
|
27
|
+
);
|
28
|
+
</script>
|
29
|
+
</head>
|
30
|
+
<body>
|
31
|
+
<h1>Rlang test</h1>
|
32
|
+
|
33
|
+
<p>Fibonacci terms</p>
|
34
|
+
n = <input type="text" size=6 id="n" value=16>
|
35
|
+
<input type="button" id="run" value="Run"/>
|
36
|
+
<p id="fib_n">-</p>
|
37
|
+
</body>
|
38
|
+
</html>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
3
|
+
Rack::Mime::MIME_TYPES.merge!({
|
4
|
+
".wasm" => "application/wasm",
|
5
|
+
})
|
6
|
+
|
7
|
+
builder = Rack::Builder.new do
|
8
|
+
map "/" do
|
9
|
+
use Rack::Static, urls: [""],
|
10
|
+
root: File.expand_path('./'),
|
11
|
+
index: 'index.html'
|
12
|
+
run lambda {}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
Rack::Handler::WEBrick.run builder
|
data/lib/builder/rlang.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
|
1
|
+
require_relative 'rlang/compiler'
|
2
2
|
require_relative 'rlang/builder'
|
@@ -1,8 +1,15 @@
|
|
1
1
|
# Rubinius WebAssembly VM
|
2
2
|
# Copyright (c) 2019, Laurent Julliard and contributors
|
3
3
|
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Turns an Rlang source file into a Wasm file
|
6
|
+
# either by executing the rlang command or by
|
7
|
+
# running the parser/compiler from within this
|
8
|
+
# class
|
4
9
|
|
5
10
|
require_relative '../ext/tempfile'
|
11
|
+
require_relative '../wat'
|
12
|
+
require_relative './compiler'
|
6
13
|
|
7
14
|
module Builder::Rlang
|
8
15
|
class Builder
|
@@ -11,21 +18,46 @@ module Builder::Rlang
|
|
11
18
|
RLANG = File.expand_path('../../../../bin/rlang', __FILE__)
|
12
19
|
|
13
20
|
attr_reader :source, :target, :wat_path
|
21
|
+
attr_accessor :use_rlang_command
|
14
22
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
23
|
+
# source: Path to Rlang file (.rb)
|
24
|
+
# target: Path to Wasm file (.wasm)
|
25
|
+
# options: Rlang parser options (parser.config)
|
26
|
+
#
|
27
|
+
# options is either a string when using rlang compiler
|
28
|
+
# or a hash of parser options when using the parser
|
29
|
+
def initialize(source, target, options=nil)
|
30
|
+
@source = source
|
22
31
|
@target = target
|
23
32
|
@options = options
|
24
|
-
|
33
|
+
@use_rlang_command = false
|
34
|
+
end
|
35
|
+
|
36
|
+
def use_rlang_command!
|
37
|
+
@use_rlang_command = true
|
38
|
+
end
|
39
|
+
|
40
|
+
# return true if everything went well, false otherwise
|
41
|
+
def compile
|
42
|
+
if @use_rlang_command
|
43
|
+
system("ruby -I#{LIB_DIR} -- #{RLANG} #{@options || ''} --wasm -o #{target} #{@source}")
|
44
|
+
else
|
45
|
+
# Compile the Rlang file into a WAT file
|
46
|
+
@compiler = Compiler.new(@source, nil, @options)
|
47
|
+
if @compiler.compile
|
48
|
+
tf_path = @compiler.target
|
49
|
+
else
|
50
|
+
return false
|
51
|
+
end
|
52
|
+
# turn the WAT file into Web Assembly code
|
53
|
+
wat_builder = ::Builder::Wat::Builder.new(tf_path, @target)
|
54
|
+
@target ||= wat_builder.target
|
55
|
+
return wat_builder.compile
|
56
|
+
end
|
25
57
|
end
|
26
58
|
|
27
59
|
def cleanup
|
28
|
-
|
60
|
+
@compiler.cleanup
|
29
61
|
end
|
30
62
|
end
|
31
63
|
end
|