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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2faf9d24b118829cdd2317c26e88bd720a97ccd04844469798640596284aad82
4
- data.tar.gz: 2e1cd589da638a7711022e08a6f4c7070ce26e36c0dd7dcaef4520e404f12cc4
3
+ metadata.gz: f0e1f5809295de94b29d997b98d712156654bc60621394d0476b5fc048ee70c0
4
+ data.tar.gz: d35a2adf54dc40514dfa2f966b1ff9d992a11495c3c2c13f927e380be7616fda
5
5
  SHA512:
6
- metadata.gz: e050812a81279acc76f15e191d40510e575e4f0dd935883b3292a3a2ca14b497b82b0d0185feccc79b4250f8e510c73c4c44c788a904e3083c28a5f14a213920
7
- data.tar.gz: c689cfb6e10ec680592629f7e4c71ec44d4abd1b4e8723284633676765d189e29d5c8dd3c81ee71bcfd02597173798550b87b9ec0a7d15623a7bc53c0493eeca
6
+ metadata.gz: fcb55360cc77db0bf05466a419f876274f210f3af0ce66af88210f08899e9f45b473efde8657914ea8eb0c1fc32e8816e3c0849b282b22807fce7b297319e5d0
7
+ data.tar.gz: 9021a72e563ba1608d3fd5208929d619f4324b5274d8506e42e7a8a29d397178dd58254ba78b096fc29b0c16d4acb0ff92343e6e2e49e16c617c959298104080
data/.gitignore CHANGED
@@ -9,4 +9,5 @@
9
9
  # added for rlang
10
10
  *.gem
11
11
  *~
12
- .rake_tasks
12
+ .rake_tasks
13
+ *.wasm
@@ -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
@@ -1,18 +1,23 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rlang (0.3.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[:load_path] ||= []
63
- options[:load_path] << dir
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[:level] = level || 'INFO'
96
- logger.level = Kernel.const_get("Logger::#{options[:level].upcase}")
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[:load_path] ||= []
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
- parser = Rlang::Parser::Parser.new(nil)
128
- parser.config[:LOAD_PATH] = options[:load_path]
129
- parser.config[:__FILE__] = File.expand_path(ARGV[0])
130
- parser.config[:export_all] = options[:export_all]
131
- wg = Rlang::Parser::WGenerator.new(parser)
132
- parser.wgenerator = wg
133
- parser.parse_file(File.expand_path(ARGV[0]))
134
-
135
- # Write generated WAT code in a temp file
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(tf.path, options[:output], options[:load_path])
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(tf.path, options[:output])
121
+ FileUtils.mv(wat_path, options[:output])
157
122
  else
158
- STDOUT.write(File.read(tf.path))
123
+ STDOUT.write(File.read(wat_path))
159
124
  end
160
125
  end
161
126
  end
@@ -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 but *only* statically not dynamically. Supporting dynamic object allocation would more or less mean that Rlang becomes a Ruby virtual machine and this is not the intent. What the intent is with Rlang is to provide you with a language that can assist you in developing such a Virtual Machine for instance ;-)
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 (only at compile time)
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
- Within a class you define methods and class variables. Actually all methods must be defined within 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.
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 initially receives the value 100.
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
- Since objects cannot be instantiated at runtime in Rlang, there is no such thing as instance variable. Rlang uses a special directive `wattr` to do 4 things at once:
77
- 1. Define the attributes of a class (see that as its instance variables somehow)
78
- 2. Define the type of the attributes (remember, Rlang is a compiler...)
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
- That's a lot with a single statement and it makes your code easy to read. Here is an example
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 would return 100.
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
- In the current version of Rlang objects can only be instantiated at compile time not at runtime. As a result of this, all object instantiation must happen in the body of a class not in the body of a method. You have already seen an example of such an object instantiation in the previous example with `Square.new` being instantiated and stored in the class variable `@@cvar`.
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
- Similarly you can also instantiate and store an object in a global variable.
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 self.m_two_args(arg1, arg2, arg3)
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 `return :I64` if your method is to return a double integer value or `return :Square` if you method returns an object.
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 some_expression` statement, a method returns the value of the last evaluated statement. In the example above the method `MyClass::take_one` returns the value of `@@cvar` after decreasing it by one and `MyClass::refill` returns 100.
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, :wasm_type
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
- If `:method_name` symbol starts with a `#` it refers to an instance method. Without it it refers to a class method.
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 only contains basic WebAssembly memory management functions like `Memory::size` and `Memory::grow` to mirror the WebAssembly functions of the same name.
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
- A basic dynamic memory allocator is also provided. It is shamelessly adapted from example provided in the famous Kernigan & Richie C book. 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 adapt [Malloc in Rlang](https://github.com/ljulliar/rlang/blob/master/lib/rlang/lib/malloc.rb).
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.
@@ -0,0 +1,11 @@
1
+ class Math
2
+ export
3
+ def self.fib(n)
4
+ if n <= 1
5
+ f = n
6
+ else
7
+ f = self.fib(n-1) + self.fib(n-2)
8
+ end
9
+ return f
10
+ end
11
+ end
@@ -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
@@ -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
- def initialize
16
- @wat_path = nil
17
- @target = nil
18
- end
19
-
20
- def compile(source, target, options='')
21
- @source = source # Path to Rlang file
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
- system("ruby -I#{LIB_DIR} -- #{RLANG} #{@options} --wasm -o #{target} #{@source}")
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
- File.unlink(@target) if @target
60
+ @compiler.cleanup
29
61
  end
30
62
  end
31
63
  end