rlang 0.4.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -3
  3. data/Gemfile.lock +4 -6
  4. data/README +11 -0
  5. data/README.md +18 -10
  6. data/bin/rlang +17 -5
  7. data/docs/RlangCompiler.md +5 -1
  8. data/docs/RlangManual.md +98 -20
  9. data/examples/fib/fib.rb +5 -1
  10. data/lib/builder/rlang/compiler.rb +2 -21
  11. data/lib/rlang/lib/array/array32.rb +59 -0
  12. data/lib/rlang/lib/array/array64.rb +56 -0
  13. data/lib/rlang/lib/array.rb +6 -0
  14. data/lib/rlang/lib/base64.rb +223 -0
  15. data/lib/rlang/lib/io.rb +75 -0
  16. data/lib/rlang/lib/kernel.rb +23 -0
  17. data/lib/rlang/lib/malloc.rb +12 -8
  18. data/lib/rlang/lib/memory.rb +102 -2
  19. data/lib/rlang/lib/object.rb +40 -4
  20. data/lib/rlang/lib/rlang.rb +29 -0
  21. data/lib/rlang/lib/rlang_core.rb +15 -0
  22. data/lib/rlang/lib/string.rb +106 -8
  23. data/lib/rlang/lib/type/i32.rb +43 -0
  24. data/lib/rlang/lib/type/i64.rb +2 -0
  25. data/lib/rlang/lib/unistd.rb +1 -2
  26. data/lib/rlang/lib/wasi.rb +184 -0
  27. data/lib/rlang/parser/attr.rb +9 -13
  28. data/lib/rlang/parser/const.rb +105 -1
  29. data/lib/rlang/parser/cvar.rb +11 -7
  30. data/lib/rlang/parser/data.rb +17 -6
  31. data/lib/rlang/parser/export.rb +4 -3
  32. data/lib/rlang/parser/ext/integer.rb +3 -1
  33. data/lib/rlang/parser/ext/type.rb +30 -1
  34. data/lib/rlang/parser/global.rb +10 -2
  35. data/lib/rlang/parser/ivar.rb +3 -7
  36. data/lib/rlang/parser/klass.rb +8 -35
  37. data/lib/rlang/parser/method.rb +36 -12
  38. data/lib/rlang/parser/module.rb +143 -0
  39. data/lib/rlang/parser/wgenerator.rb +462 -168
  40. data/lib/rlang/parser/wnode.rb +387 -142
  41. data/lib/rlang/parser/wtype.rb +30 -7
  42. data/lib/rlang/parser.rb +506 -231
  43. data/lib/rlang/version.rb +1 -1
  44. data/lib/rlang.rb +3 -0
  45. data/lib/ruby/mirror/rstring.rb +16 -0
  46. data/lib/utils/exceptions.rb +12 -0
  47. data/rlang.gemspec +4 -4
  48. metadata +25 -13
  49. data/lib/rlang/lib.rb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 45f6d0c347cfcf901dcd935e2b979098759f2c9b435ff5d900f77a830d5b2101
4
- data.tar.gz: 88c0e8f7b79f3cb784f0dd0c85fc10b68427958893f3401998f9866fa9a9b50a
3
+ metadata.gz: 853a10a09ac23cc4cedb2c2b5bc966258cfc858673bbecc7a7bb625c8ddf4b92
4
+ data.tar.gz: f27458788eb2cccaa49bb319436979a91e93de4e54fb3fd83c28fd67b481a631
5
5
  SHA512:
6
- metadata.gz: d6f959bc61a5f4f9548455244662a8b3d9482d9e4670fdb66fd9d5ea1101e062bb93d5fbaffabac5e24852c85058130f92680e76f7d01bbe1745914bee25640b
7
- data.tar.gz: 30e4ecce3b01f850fd9515ab614e75d3cd915545b172bab7174872d0d5fab4875a96ade32dac2f1b7a95a3ca0f58ef0ef891544b926ba0e6c23fa38c4cb6fca2
6
+ metadata.gz: 78a5e6da19f10887854dd40fb85e4a48d2073c10ece4acc01c07651041110eceb4c13cb67fd7251b1e0abbc310bc25134ef32c27bd2bb4e6af3488eec3a2bdf5
7
+ data.tar.gz: 3bfc1ffe8b133f4b8b2621c810af522f51fa88d44ab5dcc00a679dd0bf3eab857321d311c9b156dcd63428e62222669d87a6c1f1a501203ba0e448b44300a60f
data/CHANGELOG.md CHANGED
@@ -1,6 +1,22 @@
1
- ## 0.4.1
2
- * Class attributes syntac now identical to plain Ruby
3
- * Very preliminary version of Rlang String class
1
+ ## 0.6.0
2
+ * Support for signed/unsigned integers (arithmetics, relational operatos, explicit and implicit type cast)
3
+ * More methods in String ([], []=, +, *, ==, !=, chr,...)
4
+ * More methods in Array ([], []=, ==, !=,...)
5
+ * More methods in I32 and I64 classes (to_s,...)
6
+ * Base 64 encoding and decidong module (same methods as in Ruby)
7
+ * Array literal ([]) supported as initializer
8
+ * Automatically adjust HEAP base address at compile time
9
+ * Various bug fixes and code cleanup
10
+
11
+ ## 0.5.1
12
+ * Imported WASM methods can now be defined
13
+ * Preliminary version of WASI interface and IO class added to Rlang library
14
+
15
+ ## 0.5.0
16
+ * Class attributes syntax now identical to plain Ruby
17
+ * Class in class definition and module supported
18
+ * Class inheritance
19
+ * Basic Array and String class in Rlang library
4
20
 
5
21
  ## 0.4.0
6
22
  * Object instances, instance methods and instance variables now supported
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rlang (0.4.0)
4
+ rlang (0.5.1)
5
5
  parser
6
6
 
7
7
  GEM
@@ -13,13 +13,11 @@ GEM
13
13
  parser (2.7.0.2)
14
14
  ast (~> 2.4.0)
15
15
  rake (12.3.3)
16
- rutie (0.0.4)
17
16
  simplecov (0.18.1)
18
17
  docile (~> 1.1)
19
18
  simplecov-html (~> 0.11.0)
20
19
  simplecov-html (0.11.0)
21
- wasmer (0.3.0)
22
- rutie (~> 0.0.3)
20
+ wasmer (1.0.0)
23
21
 
24
22
  PLATFORMS
25
23
  ruby
@@ -30,7 +28,7 @@ DEPENDENCIES
30
28
  rake (~> 12.0)
31
29
  rlang!
32
30
  simplecov (~> 0.18)
33
- wasmer (~> 0.3)
31
+ wasmer (~> 1.0)
34
32
 
35
33
  BUNDLED WITH
36
- 2.0.2
34
+ 2.3.13
data/README ADDED
@@ -0,0 +1,11 @@
1
+ All contents here come from http://www.wolczko.com/st80/
2
+
3
+ image.tar.gz:original Xerox virtual image (as provided on tape by Xerox PARC)
4
+ manual.pdf: the manual that came with the tape (incl. errata for the Blue Book implementation section)
5
+ st.tar.gz: C implementation of the Smalltalk VM from Mario Wolczko
6
+ VMsrc.shar: Smalltalk VM sourcesas re-keyed from the Blue Book by Mario Wolczko
7
+
8
+ Xerox Star
9
+ ----------
10
+ 8010_hd_images.zip: Xerox Star images. hard disk images for Viewpoint 2.0, XDE 5.0, and The Harmony release of Interlisp-D (Not Smalltalk)
11
+ See article at https://engblg.livingcomputers.org/index.php/2019/01/19/introducing-darkstar-a-xerox-star-emulator/
data/README.md CHANGED
@@ -1,12 +1,17 @@
1
- # Rlang : a (subset of) Ruby to WebAssembly compiler
1
+ # Rlang : a Ruby-like language compiled to WebAssembly
2
2
 
3
3
  Rlang is meant to generate fast and uncluttered [WebAssembly](https://webassembly.org) code from the comfort of the Ruby language.
4
4
 
5
- Rlang is actually two things: 1) a supported subset of the Ruby language and 2) a **compiler** transforming this Ruby subset in a valid, fully runnable and native WebAssembly module.
5
+ Rlang is actually two things: 1) a subset of the Ruby language and 2) a **compiler** transforming this Ruby subset in a native, fully runnable WebAssembly module.
6
+
7
+ So in summary what Rlang does is:
8
+ <p align="center"><b>
9
+ Rlang source code &rarr; Rlang compiler &rarr; WebAssembly bytecode
10
+ </b></p>
6
11
 
7
12
  Rlang can be seen as a foundational language that can help you quickly develop and debug high performance WebAssembly modules. For the rationale behind the creation of Rlang see [below](#why-rlang).
8
13
 
9
- This is still a young project but Rlang has already been successfully tested with some real code such as the dynamic memory allocator provided with the Rlang library. It will keep mproving over time, always with the goal of generating crisp and uncluttered WAT code.
14
+ This is still a young project but Rlang has already been successfully tested with some real code such as the dynamic memory allocator provided with the Rlang library. It will keep improving over time, always with the goal of generating crisp and uncluttered WebAssembly code.
10
15
 
11
16
  If you want to help with Rlang see [How you can help](#how-you-can-help).
12
17
 
@@ -15,6 +20,8 @@ If you want to help with Rlang see [How you can help](#how-you-can-help).
15
20
  * **WABT toolkit**: the rlang compiler can generate both WebAssembly source code (WAT file) and WebAssembly bytecode (WASM file). To generate WASM bytecode the rlang compiler uses wat2wasm. This utility is part of the [WABT toolkit](https://github.com/WebAssembly/wabt)
16
21
  * **wasmer runtime** (optional): [Wasmer](https://wasmer.io/) is a fast WebAssembly runtime. You'll need it if you want to run the test suite from the source repo. You can also use it to run the compiled WASM code generated by the rlang compiler. You can get Wasmer at [wasmer.io](https://wasmer.io/)
17
22
 
23
+ Rlang has also been successfully tested with the [Wasmtime](https://wasmtime.dev/) WebAssembly runtime.
24
+
18
25
 
19
26
  ## Installing Rlang
20
27
  Rlang is available as a gem from rubygems.org. So simply run the following command to install it:
@@ -22,7 +29,7 @@ Rlang is available as a gem from rubygems.org. So simply run the following comma
22
29
  ```
23
30
  $ gem install rlang
24
31
  ```
25
- 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:
32
+ Alternatively, if you clone this git repo and play with the Rlang source code you can generate your own local gem and install it as follows:
26
33
 
27
34
  ```
28
35
  $ bundle install
@@ -33,7 +40,7 @@ $ gem install --local rlang-x.y.z.gem
33
40
  To check that the installation went well, run `rlang --help` and see if the help message displays correctly.
34
41
 
35
42
  ## A Quick example
36
- 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.
43
+ An example is always worth a thousand words, so here is a quick one showing you how to compile some Rang code and run it from your browser. Since Chrome, Chromium, Firefox and Safari embed a WebAssembly runtime it is the easiest way to test your compiled Rlang code.
37
44
 
38
45
  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.
39
46
 
@@ -50,15 +57,14 @@ Point your browser to [http://localhost:8080](http://localhost:8080), a simple p
50
57
 
51
58
  That was easy, right ?
52
59
 
53
-
54
60
  ## The Rlang language
55
61
  Ruby features supported by Rlang are detailed in the [Rlang Manual](https://github.com/ljulliar/rlang/blob/master/docs/RlangManual.md)
56
62
 
57
63
  You can look at the rlang test suite in [test/rlang_test_files](https://github.com/ljulliar/rlang/blob/master/test/rlang_test_files/) to get a flavor of the subset of Ruby currently supported.
58
64
 
59
- For a more involved example of Ruby code, I invite you to look at the [dynamic memory allocator](https://github.com/ljulliar/rlang/blob/master/lib/rlang/lib/malloc.rb) provided with the Rlang library.
65
+ For a more involved example of Rlang code, I invite you to look at the [dynamic memory allocator](https://github.com/ljulliar/rlang/blob/master/lib/rlang/lib/malloc.rb) provided with the Rlang library.
60
66
 
61
- ## rlang compiler
67
+ ## The rlang compiler
62
68
  The Rlang compiler can be invoked through the `rlang` command. See the [Rlang Compiler Documentation](https://github.com/ljulliar/rlang/blob/master/docs/RlangCompiler.md) for more details about the command line options.
63
69
 
64
70
  Keep in mind that Rlang is **NOT** a Ruby interpreter or a Ruby VM executing some kind of bytecode. It does actually statically **compile** the Rlang language to WebAssembly code pretty much like gcc or llvm compiles C/C++ code to machine assembly language.
@@ -70,9 +76,11 @@ One of the big benefits of Rlang being a subset of the Ruby language is that you
70
76
  ## Why Rlang?
71
77
  This project was created out of the need to develop a Virtual Machine written in WebAssembly capable of interpreting the [Rubinius](https://github.com/rubinius/rubinius) bytecode. And yes, ultimately run a native Ruby VM in a browser :-)
72
78
 
73
- After a first proof of concept written directly by hand in WebAssembly (WAT code) it became clear that writing a full fledged VM directly in WebAssembly was going to be tedious, complex and unnecessarily painful.
79
+ After a first proof of concept written directly by hand in WebAssembly (WAT source code) it became clear that writing a full fledged VM directly in WebAssembly was going to be tedious, complex and unnecessarily painful.
80
+
81
+ Sure I could have written this VM in any of the language that can already be compiled directly to WebAssembly (C, C++, Rust, Go,...) but being fond of Ruby since 2000 I decided that I would go for a compiler capable of transforming a subset of the Ruby language directly into WebAssembly with a minimum overhead.
74
82
 
75
- Sure I could have written this VM in any of the language that can already be compiled directly to WebAssembly (C, C++, Rust, Go,...) but being fond of Ruby since 2000 I decided that I would go for a compiler capable of transforming a subset of the Ruby language directly into WebAssembly with a minimum overhead. So in a nutshell: the goal of Rlang is to let you develop efficient WebAssembly code with a reasonably high level of abstraction while keeping the generated WebAssembly code straightforward and human readable.
83
+ So in a nutshell: the goal of Rlang is to let you develop efficient WebAssembly code with a reasonably high level of abstraction while keeping the generated WebAssembly code straightforward, human readable and slim.
76
84
 
77
85
  ## Why the name Rlang?
78
86
  Yes I hear you: Rlang is already the name of the R language so why use that name and aren't you introducing some confusion? Well for one I couldn't resist using that name to honor software engineering history (see below) and because, after all, the intersection between the Ruby/WebAssembly community and the R language community focused on data processing and machine learning must be quite small to say the least.
data/bin/rlang CHANGED
@@ -13,13 +13,16 @@
13
13
 
14
14
  require 'optparse'
15
15
  require 'fileutils'
16
- require 'rlang'
16
+ require 'rlang' # setup RLANG_BASE_DIR
17
17
  require 'builder'
18
18
 
19
+ RLANG_LIB_DIR = File.expand_path('./rlang/lib', RLANG_BASE_DIR)
20
+
19
21
  include Log
20
22
  logger.level = Logger::INFO
21
23
 
22
24
  options = {}
25
+ custom_load_path = []
23
26
  OptionParser.new do |opts|
24
27
  opts.banner = %q{Usage: rlang [options] filename
25
28
  read a Rlang file, check it for errors, and convert it
@@ -39,8 +42,8 @@ options:
39
42
  }
40
43
 
41
44
  opts.on("-I DIR", "--load_path DIRECTORY", "specify $LOAD_PATH directory (may be used more than once)") do |dir|
42
- options[:LOAD_PATH] ||= []
43
- options[:LOAD_PATH] << dir
45
+ custom_load_path ||= []
46
+ custom_load_path << dir
44
47
  end
45
48
 
46
49
  opts.on("-M", "--module NAME", "WASM module name") do |name|
@@ -67,6 +70,14 @@ options:
67
70
  options[:wasm] = v
68
71
  end
69
72
 
73
+ opts.on("-c", "--comments", "Include comments in WAT source file") do |v|
74
+ options[:comments] = v
75
+ end
76
+
77
+ opts.on("-S", "--start FUNCTION", "Function name where execution starts (default '_start')") do |function|
78
+ options[:start] = function
79
+ end
80
+
70
81
  opts.on("-o", "--output FILE", "Write output to file") do |file|
71
82
  options[:output] = file
72
83
  end
@@ -87,9 +98,10 @@ options:
87
98
  end
88
99
  end.parse!(ARGV)
89
100
 
90
- options[:module] ||= ''
101
+ options[:module] ||= ''
91
102
  options[:memory_min] ||= 4
92
- options[:LOAD_PATH] ||= []
103
+ options[:LOAD_PATH] = custom_load_path << RLANG_LIB_DIR
104
+ options[:start] ||= '_start'
93
105
 
94
106
  fh_out = options[:output] ? File.open(options[:output], 'w') : STDOUT
95
107
 
@@ -19,6 +19,8 @@ Usage: rlang [options] rlang_file.rb
19
19
  -w, --wat Generate WAT source file
20
20
  -a, --ast Generate Ruby AST file
21
21
  -s, --wasm Generate WASM bytecode file
22
+ -c, --comments Include comments in WAT source file
23
+ -S, --start FUNCTION Function name where execution starts (default '_start')
22
24
  -o, --output FILE Write output to file
23
25
  -v, --verbose [LEVEL] Verbosity level (fatal, error, warn, info, debug)
24
26
  -V, --version Displays Rlang version
@@ -30,7 +32,9 @@ Usage: rlang [options] rlang_file.rb
30
32
  * **-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
33
  * **-w, --wat**: parse Rlang file and generate WAT source code
32
34
  * **-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
35
+ * **-s, --wasm**: parse Rlang file, generate WAT source code and compile it to WASM bytecode
36
+ * **-c, --comments**: include the Rlang comments at the corresponding location in the generated WAT source file.
37
+ * **-S, --start**: function to use as the execution starting point of the WASM module (default is `_start`)
34
38
  * **-o, --output FILE**: send rlang output to FILE
35
39
  * **-v, --verbose [LEVEL]**: verbosity level (fatal, error, warn, info, debug). Default is warn
36
40
  * **-V, --version**: Displays Rlang version
data/docs/RlangManual.md CHANGED
@@ -1,19 +1,16 @@
1
1
  # The Rlang language
2
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.
3
+ Rlang is a subset of the Ruby language that is meant to provide a good level of abstraction and expressiveness while keeping its compilation to WebAssembly straightforward.
4
4
 
5
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
6
 
7
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
8
 
9
- ## The Rlang object model
10
-
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.
12
-
13
9
  ## What Rlang does
14
10
 
15
11
  Rlang provides:
16
12
  * Classes, class attributes and class variables
13
+ * Modules
17
14
  * Object instantiation, attribute accessors and instance variables
18
15
  * Method definition and method calls
19
16
  * Integers and booleans
@@ -24,6 +21,7 @@ Rlang provides:
24
21
  * Arithmetic, relational and logical operators
25
22
  * WebAssembly source code (WAT) inlining
26
23
  * Requiring other Rlang or WAT files
24
+ * Importing and exporting WASM functions
27
25
  * A Rlang library (written in Rlang of course) that you can reuse in your own WebAssembly module
28
26
 
29
27
  Here is a sample of Rlang code to whet your appetite:
@@ -46,8 +44,6 @@ calling that method later in your code is as simple as invoking `Math.fib(20)`
46
44
  ## Classes
47
45
  Classes are core constructs of Rlang and are very similar to Ruby classes.
48
46
 
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.
50
-
51
47
  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):
52
48
 
53
49
  ```ruby
@@ -70,6 +66,28 @@ This short piece of code shows several interesting points:
70
66
  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)
71
67
  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)
72
68
 
69
+ ### Class scope and inheritance
70
+ Rlang supports the definition of classes in class at any depth and class naming follows the exact same convention as in Ruby. In the example below the `B` class can be referred to as `A::B`.
71
+
72
+ ```ruby
73
+ class A
74
+ class B
75
+ end
76
+ end
77
+
78
+ def main
79
+ new_object = A::B.new
80
+ end
81
+ ```
82
+ A Class in RLang can also inherit from another class as in Ruby. Whan a superclass is not specified, a newly defined class automatically inherits from the Object class.
83
+
84
+ If you need to use a class name in your code before the class is actually processed by the compiler you can use the following empty class notation to declare that the corresponding constant is actually a class. You can later reopen the class definition and define methods.
85
+
86
+ ```ruby
87
+ class String; end
88
+ ```
89
+
90
+
73
91
  ### Class attributes and instance variables
74
92
  Rlang support both the use of class attributes and instance variables. Class attribute declaration is happening through the `attr_accessor`, `attr_reader` or `attr_writer` directives as in plain Ruby. It actually defines a couple of things for you:
75
93
 
@@ -100,7 +118,7 @@ end
100
118
  The code is pretty straightforward: a new square instance is created, its side attribute is set to 10. As you would expect, the call to the Square#area method returns 100 and the perimeter is 40.
101
119
 
102
120
  ### Class attribute type
103
- 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).
121
+ In the example above the `side` attribute is implicitely using the `:I32` (signed 32-bit 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).
104
122
 
105
123
  ```ruby
106
124
  class Square
@@ -113,6 +131,8 @@ class Square
113
131
  end
114
132
  end
115
133
  ```
134
+ ## Modules
135
+ Modules in Rlang behaves exactly like modules in Ruby. Modules can be included, extended or prepended in other classes and modules. .
116
136
 
117
137
  ## Object instantiation
118
138
  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.
@@ -130,8 +150,9 @@ class Test
130
150
  # ...
131
151
  end
132
152
  ```
153
+ The address in memory of both class variables and constants can be accessed by using the `addr` method as in `SQUARE.addr` for instance.
133
154
 
134
- **IMPORTANT NOTE**: in the current version of Rlang the new method call used to allocate the object sape doesn't do any initialization. That's why the new method in this context (class body or top level) doesn't accept any parameter
155
+ **IMPORTANT NOTE**: in the current version of Rlang the new method call used to allocate static objects doesn't do any initialization. That's why the new method in this context (class body or top level) doesn't accept any parameter.
135
156
 
136
157
  ### Dynamic objects
137
158
  At any point in the body of method you can dynamically instantiate a new object. Here is an exemple:
@@ -163,7 +184,7 @@ class Main
163
184
  cube = Cube.new(10, 20, 30)
164
185
  v = cube.volume
165
186
  # ... Do what ever you have to do...
166
- Object.free(cube)
187
+ cube.free
167
188
  end
168
189
  end
169
190
  ```
@@ -171,6 +192,11 @@ end
171
192
  ## Methods
172
193
  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.
173
194
 
195
+ ### Method definition
196
+ A method in Rlang can either be defined in a class, a module or at the top level. In that case the method is implicitely defined within the context of the Object class as Ruby does.
197
+
198
+ Class methods must be defined using the `def self.mymethod` syntax. The `class << self ... end` form is not supported.
199
+
174
200
  ### Method arguments
175
201
  Rlang method definition supports fixed name arguments in any number. The *args and **args notation are not supported.
176
202
 
@@ -204,9 +230,9 @@ Rlang also gives you the ability to declare the return type of a method like thi
204
230
  result :class_name, :method_name, :your_type_here
205
231
  ```
206
232
 
207
- 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`).
233
+ This result directive must be used to instruct the compiler about the return type of a method if the compiler has not yet compiled this method (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. Keep in mind that this is only needed if the method returns something different than the default type (`:I32`).
208
234
 
209
- 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.
235
+ Note: in this directive `:method_name` symbol starts with a `#` characters if it refers to an instance method. Without the `#` it refers to a class method.
210
236
 
211
237
  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.
212
238
 
@@ -224,7 +250,9 @@ end
224
250
  The `local` directive above instructs the compiler that `lvar` is of type `:I64` and the local variable mysquare is of type `Square`. Without it `lvar` would have been auto-vivified with the Wasm default type or `:I32`.
225
251
 
226
252
  ### Exporting a method
227
- 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.
253
+ 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 with an optional export name of your choice.
254
+
255
+ If no function name is specified, Rlang will build it for you. WASM exported functions are named after the class name (in lower case), the method type (class or instance) and the method name. As an example the exported method in the example above will be 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).
228
256
 
229
257
  ```ruby
230
258
  class MyClass
@@ -234,31 +262,53 @@ class MyClass
234
262
  # ...
235
263
  end
236
264
 
265
+ export :seeable
266
+ def self.visible_too(arg1)
267
+ # ...
268
+ end
269
+
237
270
  def self.not_visible
238
271
  # ...
239
272
  end
240
273
  end
241
274
  ```
242
275
 
243
- 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
276
+ Note that the `export` keyword only applies to the method definition that immediately follows. In the example above `MyClass::visible` and `MyClass::visible_too` will be exported by the generated WASM module whereas `MyClass::not_visible` will not.
277
+
278
+ ### Importing a method
279
+ An import statement in WebAssembly is a way to declare the signature of a method defined outside of the current WebAssembly module and then call it from your code.
280
+
281
+ Rlang has an equivalent import statement as shown in the example below:
282
+ ```ruby
283
+ import :wasi_unstable, :proc_exit
284
+ def self.proc_exit(exitcode)
285
+ arg exitcode: :I32
286
+ result :none
287
+ end
288
+ ```
289
+
290
+ The first 2 arguments of import are the imported module name and function name as in WebAssembly and then follows a regular method definition with arguments and possibly the `arg` directive to specify argument types and a `result` directive to indicate the nature of the returned value. Two points worth highlighting here:
291
+ 1. The Rlang method name doesn't have to be the same as the function name in the import statement.
292
+ 2. As imported function are external to your Rlang module they must be declared as class methods. This is bacause defining them as instance method would automatically pass `self` as the first argument in the method call therefore changing the signature of the method.
293
+
294
+ The example above is taken from the Rlang WASI class that defines the interface with [WASI (WebAssembly System Interface)](https::wasi.dev).
244
295
 
245
- 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)
246
296
 
247
297
  ## Rlang types
248
- 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).
298
+ The types currently supported by Rlang are integers either signed long (`:I32`), unsigned long (`:UI32`), signed double (`:I64`), unsigned double (`:UI64`) 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 declare it explicitely in the method body (see above) or cast it explicitely (see below).
249
299
 
250
300
  ### Implicit type cast
251
- 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.
301
+ 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 an `:I64` local variable too.
252
302
 
253
303
  ```ruby
254
- def self.m_local_var(arg1)
304
+ def m_local_var(arg1)
255
305
  arg :arg1, :I64
256
306
  lvar = arg1 * 100
257
307
  # ....
258
308
  end
259
309
  ```
260
310
 
261
- 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.
311
+ 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.
262
312
 
263
313
  ```ruby
264
314
  def self.m_local_var(arg1)
@@ -269,6 +319,33 @@ def self.m_local_var(arg1)
269
319
  end
270
320
  ```
271
321
 
322
+ ### Implicit type cast precedence
323
+
324
+ Rlang follows the C IEC/ISO Standard to implicitely convert types. When operands of different types are mixed in arithmetic or relational operators, Rlang will conver them according to the following priority order from the highest priority to the lowest:
325
+
326
+ `:F64`, `:F32`, `:UI64`, `:I64`, `:Class`, `:UI32`, `:I32`
327
+
328
+ Let's take a few examples:
329
+ * If you use 32 bit floating point numbers and 64 bit floating point numbers in the same expression all `:F32` values will be converted to `:F64`
330
+ * Similarly if you mix `:F32` values and `:I64` values, all 64-bit integers will be converted to 32-bit floating point values before evaluating the expression
331
+ * **IMPORTANT** note that unsigned integers `:UI64` and `:UI32`have a higher priority than their signed counter part `:I64` and `:I32`. This means that when mixing signed and unsigned integer values in the same expression, the signed integers will be reinterpreted as its unsigned equivalent bitwise. As an example in this context an `:I32` with a value of -6 will be reinterpreted as an`:UI32` with a value of 4294967290. So be very cautious when mixing signed and unsigned integers as this may be give somewhat suprising results especially when using relational operators such >, <, >=,...
332
+
333
+ ### Pointer arithmetics
334
+
335
+ In the types priority order mentioned above, the `:Class` type designate any value pointing to a class instance, in other word an object. This means that object pointers are values that you can process through arithmetic and relational operators like in C. A pointer to an object is a 32 bit memory address that has type casting precedence over `:UI32` and `:I32` types.
336
+
337
+ Consequently when doing some arithmetics on object pointers like an addition or a substraction the integer vale will be interpreted as a multiple of the object size as if you were moving an index over objects of the same type in memory. Again this behavior is similar to the C language.
338
+
339
+
340
+ ```ruby
341
+ class MyClass
342
+ up = Header.new
343
+ next_hdr = up + 1
344
+ #...
345
+ end
346
+ ```
347
+ In the example above, a new header object is created and it meory location is stored in the up variable. When adding 1 to the up variable, Rlang knows that up is of type Header and will therefore increase up not by 1 but by the size in byted of the Header Object in memory. You can see examples of pointer arithmetics in the memory allocator of Rlang in lib/malloc.rb.
348
+
272
349
  ### Explicit type cast
273
350
  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:
274
351
 
@@ -285,7 +362,7 @@ The first line will auto-vivify the `@@cvar` class variable as type `:I64`.
285
362
 
286
363
  The second example turns the value `123876` into a pointer to a `Square` object. It's pretty much like turning an integer into a pointer to a memory address in C.
287
364
 
288
- For `:I32` and `:I64` type cast you can also use the following shortcuts `100.to_I64` or `100.to_I32`
365
+ For type cast a number or an expression to native type you can also use shortcuts like `100.to_I64` or `100.to_UI32`, etc...
289
366
 
290
367
  Note that type cast can be used anywhere in the code whether in class body or method definition.
291
368
 
@@ -461,6 +538,7 @@ For now, the Rlang library is very modest and containoffers the following classe
461
538
  * Malloc.malloc(nbytes) : dynamically allocates nbytes of memory and return address of the allocated memory space or -1 if it fails
462
539
  * Malloc.free(address) : frees the block of memory at the given address
463
540
  * **Object** class: provides a couple of object management method. Use `Object.free` to free an object.
541
+ * **Array32**, **Array64** : Arrays can be initialized with the usual bracket operator '[]'. When the array is initialized at compile time (statically) such as for constants or class variables you can only use integers in the array initializers (e.g.`[0, 100, 200]`). When the array is initialized at runtime (dynamically) such as in the body of a method definition you can either use integers or any Rlang object (e.g. `[ 1, 2, "A string", my_object]`)
464
542
  * **String** class: string are initialized in Rlang by using a string literal like `"This is my string"`. String methods supported are very minimal for the moment. See [rlang/lib/String.rb](https://github.com/ljulliar/rlang/blob/master/lib/rlang/lib/string.rb). Feel free to improve.
465
543
 
466
544
  As a side note, the dynamic memory allocator currently used in Rlang is shamelessly adapted from the 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).
data/examples/fib/fib.rb CHANGED
@@ -8,4 +8,8 @@ class Math
8
8
  end
9
9
  return f
10
10
  end
11
- end
11
+ end
12
+
13
+ def self.main
14
+ Math.fib(12)
15
+ end
@@ -17,23 +17,10 @@ module Builder::Rlang
17
17
  # WAT source frame
18
18
  WAT_FRAME = %q{
19
19
  ;; Generated by Rlang compiler version %{version} on %{time}\n"
20
- (module %{module}
21
- (memory $0 %{memory_min} %{memory_max})
22
20
 
23
- ;; ======= EXPORTS =======
24
- (export "memory" (memory $0))
25
- %{exports}
21
+ %{code}
26
22
 
27
- ;; ======= GLOBAL VARIABLES =======
28
- %{globals}
29
-
30
- ;; ======= STATIC DATA =======
31
- %{data}
32
-
33
- ;; ======= CODE =======
34
- %{code}
35
- )
36
- }
23
+ }
37
24
 
38
25
  # source: Path to Rlang file (.rb)
39
26
  # target: Path to Wat file (.wat)
@@ -64,12 +51,6 @@ module Builder::Rlang
64
51
  end
65
52
  @tf.write(WAT_FRAME % {version: Rlang::VERSION,
66
53
  time: Time.now,
67
- module: @options[:module],
68
- memory_min: @options[:memory_min],
69
- memory_max: @options[:memory_max],
70
- exports: Rlang::Parser::Export.transpile,
71
- globals: Rlang::Parser::Global.transpile,
72
- data: Rlang::Parser::DAta.transpile,
73
54
  code: @wgenerator.root.transpile
74
55
  })
75
56
  @tf.close
@@ -0,0 +1,59 @@
1
+ # Rubinius WebAssembly VM
2
+ # Copyright (c) 2019-2020, Laurent Julliard and contributors
3
+ # All rights reserved.
4
+ #
5
+ # 4 bytes object array class
6
+ require_relative '../memory'
7
+ require_relative '../kernel'
8
+ require_relative '../type'
9
+ #require_relative '../string'
10
+
11
+
12
+ class Array32
13
+ attr_reader :count, :ptr
14
+
15
+ result :Kernel, :raise, :none
16
+
17
+ # count: number of elements in Array
18
+ # Array elements are native types or
19
+ # pointers to objects
20
+ # Arrays are fixed size for now
21
+ def initialize(count)
22
+ if count == 0
23
+ @ptr = 0
24
+ else
25
+ # Memory size is count * 4 bytes
26
+ @ptr = Object.allocate(count << 2)
27
+ end
28
+ @count = count
29
+ end
30
+
31
+ def size; @count; end
32
+ def length; @count; end
33
+ def empty?; @count == 0; end
34
+
35
+ def [](idx)
36
+ result :I32
37
+ raise "Index out of bound" if idx >= @count || idx < -@count
38
+ idx = @count + idx if idx <0
39
+ # offset in memory for elt #idx is idx * 4
40
+ Memory.load32(@ptr + (idx << 2))
41
+ end
42
+
43
+ def []=(idx, value)
44
+ arg value: :I32
45
+ result :I32
46
+ raise "Index out of bound" if idx >= @count || idx < -@count
47
+ idx = @count + idx if idx <0
48
+ # offset in memory for elt #idx is idx * 4
49
+ Memory.store32(@ptr + (idx << 2), value)
50
+ value
51
+ end
52
+
53
+ def free
54
+ result :none
55
+ Object.free(@ptr)
56
+ Object.free(self)
57
+ end
58
+
59
+ end