rlang 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rake_tasks~ +0 -0
  4. data/.travis.yml +7 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +373 -0
  8. data/README.md +61 -0
  9. data/Rakefile +10 -0
  10. data/bin/rlang +164 -0
  11. data/docs/RlangCompiler.md +37 -0
  12. data/docs/RlangManual.md +391 -0
  13. data/lib/builder/ext/tempfile.rb +7 -0
  14. data/lib/builder/ext.rb +5 -0
  15. data/lib/builder/rlang/builder.rb +31 -0
  16. data/lib/builder/rlang.rb +2 -0
  17. data/lib/builder/wat/builder.rb +52 -0
  18. data/lib/builder/wat/renderer.rb +28 -0
  19. data/lib/builder/wat.rb +3 -0
  20. data/lib/builder.rb +7 -0
  21. data/lib/rlang/lib/malloc.c +97 -0
  22. data/lib/rlang/lib/malloc.rb +169 -0
  23. data/lib/rlang/lib/memory.rb +11 -0
  24. data/lib/rlang/lib/type/i32.rb +7 -0
  25. data/lib/rlang/lib/type/i64.rb +7 -0
  26. data/lib/rlang/lib/type.rb +6 -0
  27. data/lib/rlang/lib/unistd.rb +47 -0
  28. data/lib/rlang/lib.rb +10 -0
  29. data/lib/rlang/parser/const.rb +15 -0
  30. data/lib/rlang/parser/cvar.rb +44 -0
  31. data/lib/rlang/parser/data.rb +105 -0
  32. data/lib/rlang/parser/export.rb +22 -0
  33. data/lib/rlang/parser/ext/integer.rb +5 -0
  34. data/lib/rlang/parser/ext/string.rb +5 -0
  35. data/lib/rlang/parser/ext/type.rb +64 -0
  36. data/lib/rlang/parser/global.rb +65 -0
  37. data/lib/rlang/parser/lvar.rb +29 -0
  38. data/lib/rlang/parser/marg.rb +30 -0
  39. data/lib/rlang/parser/method.rb +76 -0
  40. data/lib/rlang/parser/wattr.rb +65 -0
  41. data/lib/rlang/parser/wgenerator.rb +509 -0
  42. data/lib/rlang/parser/winstruction.rb +148 -0
  43. data/lib/rlang/parser/wnode.rb +455 -0
  44. data/lib/rlang/parser/wtree.rb +19 -0
  45. data/lib/rlang/parser/wtype.rb +116 -0
  46. data/lib/rlang/parser.rb +1842 -0
  47. data/lib/rlang/version.rb +3 -0
  48. data/lib/rlang.rb +4 -0
  49. data/lib/simul/classes/data.rb +80 -0
  50. data/lib/simul/classes/global.rb +38 -0
  51. data/lib/simul/classes/memory.rb +131 -0
  52. data/lib/utils/log.rb +32 -0
  53. data/rlang.gemspec +38 -0
  54. metadata +158 -0
data/bin/rlang ADDED
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env ruby
2
+ # Rubinius WebAssembly VM
3
+ # Copyright (c) 2019-2020, Laurent Julliard and contributors
4
+ # All rights reserved.
5
+ #
6
+ # Rlang compiler
7
+ # Rlang is a subset of the Ruby language that can be transpiled
8
+ # to WAT and then compiled to WASM. The Rubinius WASM virtual
9
+ # machine is written in Rlang.
10
+ #
11
+ # Compile a Rlang source file to WAT file
12
+
13
+
14
+ require 'optparse'
15
+ require 'fileutils'
16
+ require 'rlang'
17
+
18
+ include Log
19
+ logger.level = Logger::INFO
20
+
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
+ options = {}
43
+ OptionParser.new do |opts|
44
+ opts.banner = %q{Usage: rlang [options] filename
45
+ read a Rlang file, check it for errors, and convert it
46
+ to either Ruby AST, WAT source code or WASM bytecode
47
+
48
+ examples:
49
+ # Parse Rlang file and display the Ruby abstract syntax tree
50
+ rlang --ast test.rb
51
+
52
+ # Parse Rlang file and generate WAT code in test.wat file
53
+ rlang --wat -o /tmp/test.wat test.rb
54
+
55
+ # Parse Rlang file and generate WASM bytecodein test.wasm file
56
+ rlang --wasm -o /tmp/test.wasm test.rb
57
+
58
+ options:
59
+ }
60
+
61
+ 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
64
+ end
65
+
66
+ opts.on("-M", "--module NAME", "WASM module name") do |name|
67
+ options[:module] = name
68
+ end
69
+
70
+ opts.on("-x", "--export-all", "Export all Web Assembly functions") do |v|
71
+ options[:export_all] = true
72
+ end
73
+
74
+ opts.on("-m", "--memory MIN[,MAX]", "WASM Memory size allocated in pages (MIN default is 4)") do |sizes|
75
+ options[:memory_min], options[:memory_max] = sizes.split(',')
76
+ end
77
+
78
+ opts.on("-w", "--wat", "Generate WAT source file") do |v|
79
+ options[:wat] = v
80
+ end
81
+
82
+ opts.on("-a", "--ast", "Generate Ruby AST file") do |v|
83
+ options[:ast] = v
84
+ end
85
+
86
+ opts.on("-s", "--wasm", "Generate WASM bytecode file") do |v|
87
+ options[:wasm] = v
88
+ end
89
+
90
+ opts.on("-o", "--output FILE", "Write output to file") do |file|
91
+ options[:output] = file
92
+ end
93
+
94
+ 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}")
97
+ end
98
+
99
+ opts.on("-V", "--version", "Displays Rlang version") do |v|
100
+ puts Rlang::VERSION
101
+ exit
102
+ end
103
+
104
+ opts.on("-h", "--help", "Prints this help") do
105
+ puts opts
106
+ exit
107
+ end
108
+ end.parse!(ARGV)
109
+
110
+ options[:module] ||= ''
111
+ options[:memory_min] ||= 4
112
+ options[:load_path] ||= []
113
+
114
+ fh_out = options[:output] ? File.open(options[:output], 'w') : STDOUT
115
+
116
+ logger.formatter = proc do |severity, datetime, progname, msg|
117
+ loc = caller_locations[3] # skip over the logger call itself
118
+ "#{severity[0]}: #{File.basename(loc.path)}:#{loc.lineno}##{loc.label} > #{msg}\n"
119
+ end
120
+
121
+ if options[:ast]
122
+ fh_in = ARGV.empty? ? STDIN : File.open(ARGV[0])
123
+ fh_out.write(Parser::CurrentRuby.parse(File.read(fh_in)))
124
+ end
125
+
126
+ 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
+
151
+ if options[:wasm]
152
+ builder = Builder::Wat::Builder.new(tf.path, options[:output], options[:load_path])
153
+ builder.compile
154
+ elsif options[:wat]
155
+ if options[:output]
156
+ FileUtils.mv(tf.path, options[:output])
157
+ else
158
+ STDOUT.write(File.read(tf.path))
159
+ end
160
+ end
161
+ end
162
+
163
+ exit 0
164
+
@@ -0,0 +1,37 @@
1
+ # The Rlang compiler
2
+
3
+ The Rlang compiler can be invoked with the `rlang` command. It takes a Rlang source file as an argument and can generate three types of output:
4
+ * Ruby AST: the `--ast` option generates an abstract syntax tree of your Rlang code
5
+ * WAT code: the `--wat` option turns your Rlang code into WebAssembly source code
6
+ * WASM bytecode: the `--wasm` first compile your Rlang code to WAT code and then turns it into WebAssembly bytecode that can be executed from within a WebAssembly runtime of your choice.
7
+
8
+ Make sure that the [WABT toolkit](https://github.com/WebAssembly/wabt) is installed before using the `--wasm` option.
9
+
10
+ ### Rlang compiler options
11
+ Here is the output of `rlang --help` command:
12
+
13
+ ```
14
+ Usage: rlang [options] rlang_file.rb
15
+ -I, --load_path DIRECTORY specify $LOAD_PATH directory (may be used more than once)
16
+ -x, --export-all Export all Web Assembly functions and globals
17
+ -M, --module NAME WASM module name
18
+ -m, --memory MIN[,MAX] WASM Memory size allocated in pages (MIN default is 4)
19
+ -w, --wat Generate WAT source file
20
+ -a, --ast Generate Ruby AST file
21
+ -s, --wasm Generate WASM bytecode file
22
+ -o, --output FILE Write output to file
23
+ -v, --verbose [LEVEL] Verbosity level (fatal, error, warn, info, debug)
24
+ -V, --version Displays Rlang version
25
+ -h, --help Prints this help
26
+ ```
27
+ * **-I, --load_path DIRECTORY**: this option can be used several time to append several directories to the Rlang path. Please note that the Rlang load path doesn't inherit from the regular Ruby load path
28
+ * **-x, --export-all**: systematically export all Web Assembly functions. This option is useful for test and debug purposes as it allows you to call any functions from outside the Web Assembly runtime
29
+ * **-M, --module**: allows to specify a name for the generated WebAssembly module. By default it doesn't have any.
30
+ * **-m, --memory MIN[,MAX]**: size of the WASM memory allocated at run time. The first argument is the initial amount of memory allocated and the second one (optional) is the maximum memory that your WASM module can allocate while running. The unit of both arguments is in number of WASM pages (4 KBytes)
31
+ * **-w, --wat**: parse Rlang file and generate WAT source code
32
+ * **-a, --ast**: parse Rlang file and generate the Ruby abstract syntax tree
33
+ * **-w, --wat**: parse Rlang file, generate WAT source code and compile it to WASM bytecode
34
+ * **-o, --output FILE**: send rlang output to FILE
35
+ * **-v, --verbose [LEVEL]**: verbosity level (fatal, error, warn, info, debug). Default is warn
36
+ * **-V, --version**: Displays Rlang version
37
+ * **-h, --help**: Prints help message
@@ -0,0 +1,391 @@
1
+ # The Rlang language
2
+
3
+ Rlang is a subset of the Ruby language that is meant to provide a certain level of abstration and expressiveness while keeping its translation to WebAssembly straightforward.
4
+
5
+ Ruby programmers will feel at home with Rlang and non Ruby programmers will find it useful to generate efficient WebAssembly code from a language that is much easier to use.
6
+
7
+ Still, to make this Ruby to WebAssembly compilation possible a number of trade-offs had to be made. The goal of this document is to explain the features of Rlang are how it differs from plain Ruby.
8
+
9
+ ## The Rlang object model
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)
14
+
15
+ ## What Rlang does
16
+
17
+ Rlang provides:
18
+ * Classes, class attributes and class variables
19
+ * Object instantiation (only at compile time)
20
+ * Method definition and method calls
21
+ * Integers and booleans
22
+ * Constants
23
+ * Global variables
24
+ * Control constructs (if, while, until, break, next,...)
25
+ * Arithmetic, relational and logical operators
26
+ * WebAssembly source code (WAT) inlining
27
+ * Requiring other Rlang or WAT files
28
+ * A Rlang library (written in Rlang of course) that you can reuse in your own WebAssembly module
29
+
30
+ Here is a sample of Rlang code to whet your appetite:
31
+
32
+ ```ruby
33
+ class Math
34
+ def self.fib(n)
35
+ if n <= 1
36
+ f = n
37
+ else
38
+ f = self.fib(n-1) + self.fib(n-2)
39
+ end
40
+ return f
41
+ end
42
+ end
43
+ ```
44
+
45
+ calling that method later in your code is as simple as invoking `Math.fib(20)`
46
+
47
+ ## Classes
48
+ Classes are core constructs of Rlang and are very similar to Ruby classes.
49
+
50
+ 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.
51
+
52
+ Here is an example of a class definition and the initialization and use of a class variable written in Rlang:
53
+
54
+ ```ruby
55
+ class MyClass
56
+ @@cvar = 100
57
+
58
+ def self.take_one
59
+ self.refill if @@cvar == 0
60
+ @@cvar -= 1
61
+ end
62
+
63
+ def self.refill
64
+ @@cvar = 100
65
+ end
66
+ end
67
+ ```
68
+
69
+ This short piece of code shows several interesting points:
70
+ 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.
71
+ 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)
72
+ 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)
73
+
74
+ ### Class attributes
75
+ 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:
76
+ 1. Define the attributes of a class (see that as its instance variables somehow)
77
+ 2. Define the type of the attributes (remember, Rlang is a compiler...)
78
+ 2. Define the corresponding accessors both getter and setter (like `attr_accessor` does in Ruby)
79
+ 3. Compute the memory footprint needed when objects of this class are instantiated.
80
+
81
+ That's a lot with a single statement and it makes your code easy to read. Here is an example
82
+ ```ruby
83
+ class Square
84
+ wattr :side
85
+
86
+ def area
87
+ self.side * self.side
88
+ end
89
+ end
90
+ ```
91
+ Later in your code you could use this class in your code as follows
92
+
93
+ ```ruby
94
+ class Test
95
+ @@square = Square.new
96
+
97
+ def self.my_method
98
+ @@square.side = 10
99
+ @@square.area
100
+ end
101
+ end
102
+ ```
103
+
104
+ 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.
105
+
106
+ ### Class attribute type
107
+ 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).
108
+
109
+ ```ruby
110
+ class Square
111
+ wattr :side
112
+ wattr_type side: :I64
113
+
114
+ def area
115
+ result :I64
116
+ self.side * self.side
117
+ end
118
+ end
119
+ ```
120
+
121
+ ## Object instantiation
122
+ 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`.
123
+
124
+ Similarly you can also instantiate and store an object in a global variable.
125
+
126
+ ## Methods
127
+ Methods in Rlang are defined as you would normally do in Ruby by using. They can be either class or instance methods.
128
+
129
+ ### Method arguments
130
+ Rlang method definition supports fixed name arguments in any number. The *args and **args notation are not supported.
131
+
132
+ 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.
133
+ ```ruby
134
+ def self.m_two_args(arg1, arg2, arg3)
135
+ arg arg1: :Square, arg2: :I64
136
+ # your code here...
137
+ end
138
+ ```
139
+ In the example above arg1 is of type Square (the class we defined earlier), arg2 is of type `:I64` and arg3 not being mention in the arg list of of default type (`:I32`)
140
+
141
+ ### Return result
142
+ Unless otherwise stated, a method must return a value of type `:I32` (the default type in Rlang). If your method returns nothing or a value of a different type you have to say so with the `result` directive.
143
+
144
+ ```ruby
145
+ def self.m_no_return_value(arg1, arg2)
146
+ result :none
147
+ # your code here
148
+ # ...
149
+ # and return nothing
150
+ return
151
+ end
152
+ ```
153
+ 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.
154
+
155
+ 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.
156
+
157
+ Rlang also gives you the ability to declare the return type of a method like this
158
+ ```ruby
159
+ result :class_name, :method_name, :wasm_type
160
+ ```
161
+
162
+ 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`).
163
+
164
+ If `:method_name` symbol starts with a `#` it refers to an instance method. Without it it refers to a class method.
165
+
166
+ 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.
167
+
168
+ ### Local variables
169
+ Local variable used in a method body doesn't have to be declared. They are auto-vivified the first time you assign a value to it. In some cases though, you may have to use the `local` directive as in the example below to explicitely state the type of a local variable.
170
+
171
+ ```ruby
172
+ def self.m_local_var(arg1)
173
+ local lvar: :I64, mysquare: :Square
174
+ lvar = 10
175
+ mysquare = @@square
176
+ # ....
177
+ end
178
+ ```
179
+ In this example, the `local` directive instructs the compiler that `lvar` is of type `:I64` and the local variable mysquare is of type `Square`.
180
+
181
+ ### Exporting a method
182
+ In WebAssembly, you can make functions visible to the outside world by declaring them in the export section. To achieve a similar result in Rlang, you can use the `export` keyword right before a method definition.
183
+
184
+ ```ruby
185
+ class MyClass
186
+
187
+ export
188
+ def self.visible(arg1)
189
+ # ...
190
+ end
191
+
192
+ def self.not_visible
193
+ # ...
194
+ end
195
+ end
196
+ ```
197
+
198
+ Note that the `export` keyword only applies to the method definition that immediately follows. In the example above `MyClass::m_visible` will be exported by the generated WASM module whereas `MyClass::m_not_visible` will not
199
+
200
+ WASM exported functions are named after the class name (in lower case) followed by an underscore and the method name. So the exported method in the example above is known to the WASM runtime as the `myclass_c_visible` function (where the `_c_` means it's a class function and `_i_` an instance method)
201
+
202
+ ## Rlang types
203
+ The types currently supported by Rlang are integers either long (`:I32`) or double (`:I64`) or a class type. Float types (:F32, :F64) may follow in a future version. By default Rlang assumes that any integer literal, variable, argument,... is of type `:I32`. If you need it to be of a different type you must state it explicitely in the method body (see above).
204
+
205
+ ### Implicit type cast
206
+ Only in rare cases will you use the `local` directive in methods as Rlang does its best to infer the type of a variable from its first assigned value. As an example, in the code below, the fact that `arg1` is known to be an `:I64` type of argument is enough to auto-magically create lvar as in `:I64` local variable too.
207
+
208
+ ```ruby
209
+ def self.m_local_var(arg1)
210
+ arg :arg1, :I64
211
+ lvar = arg1 * 100
212
+ # ....
213
+ end
214
+ ```
215
+
216
+ Conversely in the method below the first statement `lvar = 10` auto-vivifies `lvar` as a variable of type `:I32` (the default Rlang type). On the next line, Rlang evaluates `arg1 * 100` as an `:I64` result because `arg1` is declared as being of type `:I64`. Similarly as the type of `lvar` local variable was auto-vivified as `:I32`, the result of the expression `arg1 * 100` will be type cast from `:I64` to `:I32`. Note that Such a type cast may of course result in the value being truncated and the Rlang compiler will emit a warning accordingly.
217
+
218
+ ```ruby
219
+ def self.m_local_var(arg1)
220
+ arg :arg1, :I64
221
+ lvar = 10 # lvar is auto-vivified as :I32
222
+ lvar = arg1 * 100
223
+ # ....
224
+ end
225
+ ```
226
+
227
+ ### Explicit type cast
228
+ If Rlang is not capable of guessing the proper type of an expression or variable, you can explicitely cast it to any known type. Look at this example:
229
+
230
+ ```ruby
231
+ class MyClass
232
+ @@cvar = 100.cast_to(:I64)
233
+ @@square = 123876.cast_to(:Square)
234
+ # your code here
235
+ #...
236
+ end
237
+ ```
238
+
239
+ The first line will auto-vivify the `@@cvar` class variable as type `:I64`.
240
+
241
+ The second example turns the value `123876` into a pointer to a `Square` object. In the absence of dynamic object instantiation this allows you to create your own object at runtime by allocating WebAssembly memory and pointing to it as if it was an object of you choice (see Rlang library below for memory management)
242
+
243
+ For `:I32` and `:I64` type cast you can also use the following shortcuts `100.to_I64` or `100.to_I32`
244
+
245
+ Note that type cast can be used anywhere in the code whether in class body or method definition.
246
+
247
+ ## Constants
248
+ Rlang supports constants too and you can invoke constants from different classes as you would in Ruby. In the example below the `TestB::m_constants` returns 1001 as a result
249
+
250
+ ```ruby
251
+ class TestA
252
+ CONST = 1000
253
+ # your code here
254
+ end
255
+
256
+ class TestB
257
+ CONST = 1
258
+
259
+ export
260
+ def self.m_constants
261
+ CONST + TestA::CONST
262
+ end
263
+ end
264
+ ```
265
+
266
+ ## Booleans
267
+ Rlang the booleans `true` and `false`. You can use them in conditional statements like in the example below. As expected the `x` local variable equals would equal 10 when breaking from the loop
268
+
269
+ ```ruby
270
+ x = 0
271
+ while true
272
+ x += 1
273
+ break if x == 10
274
+ end
275
+ ```
276
+ As opposed to Ruby though, `true` and `false`are not instances of TrueClass and FalseClass but are internally represented respectively as `1` and `0`. More generally in Rlang (pretty much like in C) any non zero integer value will be considered true. However we strongly advise against mixing boolean condition and integer arithmetics. Like in C this can lead to **very** nasty bugs that will be hard to debunk.
277
+
278
+ ## Global variables
279
+ Rlang provides global variable as well. Whereas a constant can only be defined within the scope of a class definition, a global variable can be defined anywhere. When defined at the top level one can only assign a literal value like 100 (or 100.to_I64). Assigning an expression can only be done within the scope of a method.
280
+
281
+ ```ruby
282
+ $MYGLOBAL = 100
283
+
284
+ class TestB
285
+ CONST = 1
286
+
287
+ export
288
+ def self.m_constants
289
+ CONST + $MYGLOBAL
290
+ end
291
+ end
292
+ ```
293
+
294
+ ## Conditional statements
295
+ Rlang supports the following Ruby conditional statements:
296
+ * if/unless-end
297
+ * if/unless-else-end
298
+ * if-elsif-....- elsif-end
299
+ * as well as if and unless use as modifiers
300
+
301
+ **IMPORTANT REMARK** As opposed to Ruby, Rlang conditional statements never evaluates to a value. This is not much of a problem as even in regular Ruby code this feature is rarely used. However you might want to pay attention to some corner cases like in the fibonacci method shown at the beginning of this document. In regular Ruby you would use the following code:
302
+
303
+ ```ruby
304
+ def self.fib(n)
305
+ if n <= 1
306
+ n
307
+ else
308
+ fib(n-1) + fib(n-2)
309
+ end
310
+ end
311
+ ```
312
+
313
+ but as the if and else clauses doesn't return any value in Rlang you must collect the value in a local variable and return that local variable with an explicit return statement.
314
+
315
+ ## Iteration statements
316
+ Rlang supports the following Ruby iteration statements:
317
+ * while do-end
318
+ * until do-end
319
+
320
+ `break` and `next` statements are also supported
321
+
322
+ **IMPORTANT REMARK** As opposed to Ruby, Rlang iteration statements never evaluates to a value.
323
+
324
+ ## Operators
325
+
326
+ Here is the list of operators supported by Rlang:
327
+
328
+ * Arithmetic operators: `+`, `-`, `*`, `/`, `%`, `&`, `|`, `^`, `>>`, `<<`
329
+ * Relational operators: `==`, `!=`, `<`, `>`, `<=`, `>=`
330
+ * Logical operators: `&&`, `||`, `!`
331
+
332
+ ### Arithmetics operators
333
+ Arithmetics operators does the same as in plain Ruby. They apply to `:I32` and `:I64` types. They will apply equally to `:F32` and `:F64` when supported by Rlang
334
+
335
+ ### Relational operators
336
+ Relational operators does the same as in plain Ruby. They apply to `:I32` and `:I64` types. They will apply equally to `:F32` and `:F64` when supported by Rlang
337
+
338
+ All relational operators evaluate to a boolean value (see above) either `true` (value 1) or `false` (value 0)
339
+
340
+ ### Logical operators
341
+ Logical (aka Boolean) operators `&&` (logical AND), `||` (logical OR) and `!` (logical NOT) acts as in plain Ruby.
342
+
343
+ It's a Rlang best practice to apply logical operators to boolean values only (e.g. `true`, `false` or boolean values resulting from comparisons). However in Rlang all non zero value is equivalent to true so, like in C, you can mix and match both booleans and integer values although it is not recommended as it typically leads to very nasty bugs that are hard to spot.
344
+
345
+ ### Pointer Arithmetics
346
+ With the ability to define classes with attributes and instantiate objects from those classes, comes the notion of pointer arithmetics. When a new object is instantiated in Ruby, it is assigned a unique object ID. Similaly in Rlang the statement `@@cvar = Square.new`, will instatiate a new object, allocate the space needed in the WebAssembly memory, return the address of this memory space and, in this particular case, store the address i a class variable. In other words, `@@cvar` is a pointer to the new object.
347
+
348
+ With this we can start using pointer arithmetics like you would do in C. Supported operators are `+`, `-` as well as all relational operators `==`, `!=`, `<`, `>`, `<=`, `>=`. As an example the statement `@@cvar += 1` would result in @@cvar pointing to a memory address increased by the size of the Square object (here 8 bytes as Square has one `I64` attribute)
349
+
350
+ You can see examples of pointer arithmetics are work in the memory allocator class (Malloc) provide in the Rlang library.
351
+
352
+ ## Requiring files
353
+ `require` and `require_relative` are supported by Rlang. It means that, like in plain Ruby, you can split your Rlang classes in distinct files, require them in a master file and compile this single master file.
354
+
355
+ `require` looks for file in the Rlang load path that you can define by using the `-I` command line option of the rlang compiler (See the [Rlang Compiler Documentation](https://github.com/ljulliar/rlang/blob/master/docs/RlangCompiler.md). However for your Rlang projects, we strongly suggest using `require_relative` rather than `require` as all your Rlang files most likely form a single entity that you'll end up compiling into a single WebAssembly module.
356
+
357
+ If no extension is specified for the required file, Rlang will start looking for a matching `.wat` file first (which means you can include hand written WAT files in your Rlang projects) and second for a matching `.rb` file.
358
+
359
+ ## Code inlining
360
+ There are two ways to use WAT code directly in your Rlang projects:
361
+ * The first one is to require a `.wat` file (see previous section)
362
+ * The second is to use the `inline` directive directly in your code.
363
+
364
+ Here is an example:
365
+ ```ruby
366
+ class MyOtherClass
367
+ def self.x10_square(arg1)
368
+ arg1 *= 10
369
+ inline wat: '(i32.mul
370
+ (local.get $arg1)
371
+ (local.get $arg1))',
372
+ ruby: 'arg1 ** 2'
373
+ end
374
+ end
375
+ ```
376
+ What this code sample does is to multiply the method argument by 10 (in Ruby) and then inline some WAT code that squares this argument. The reason for the `ruby:` keyword argument is to give the equivalent Ruby code that will be used when you run your Rlang code in the Rlang simulator (still in development).
377
+
378
+ A third keyword argument `wtype:` also allows to specifiy the WebAssembly type produced by the fragment of inlined WAT code. By default it is assumed to produce an `:I32`. If not you can either specify `wtype: :I64` or `wtype: :none`
379
+
380
+ ## The Rlang library
381
+ Rlang comes with a library that provides a number of pre-defined classes and methods (written in Rlang of course) that you can use by adding the following statement in your Rlang files
382
+
383
+ ```ruby
384
+ require 'rlang/lib'
385
+ ```
386
+
387
+ 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.
388
+
389
+ 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).
390
+
391
+ That's it! Enjoy Rlang and, as always, feedback and contributions are welcome.
@@ -0,0 +1,7 @@
1
+ require 'tempfile'
2
+
3
+ class Tempfile
4
+ def persist!
5
+ ObjectSpace.undefine_finalizer(self)
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ # Rubinius WebAssembly VM
2
+ # Copyright (c) 2019, Laurent Julliard and contributors
3
+ # All rights reserved.
4
+
5
+ require_relative './ext/tempfile.rb'
@@ -0,0 +1,31 @@
1
+ # Rubinius WebAssembly VM
2
+ # Copyright (c) 2019, Laurent Julliard and contributors
3
+ # All rights reserved.
4
+
5
+ require_relative '../ext/tempfile'
6
+
7
+ module Builder::Rlang
8
+ class Builder
9
+
10
+ LIB_DIR = File.expand_path('../../../../lib', __FILE__)
11
+ RLANG = File.expand_path('../../../../bin/rlang', __FILE__)
12
+
13
+ attr_reader :source, :target, :wat_path
14
+
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
22
+ @target = target
23
+ @options = options
24
+ system("ruby -I#{LIB_DIR} -- #{RLANG} #{@options} --wasm -o #{target} #{@source}")
25
+ end
26
+
27
+ def cleanup
28
+ File.unlink(@target) if @target
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,2 @@
1
+
2
+ require_relative 'rlang/builder'