rlang 0.5.1 → 0.6.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: 38b11b7ed747a6a72f0a46a2c498efef419039a9bf85b5ba6442b7fd8f5300fc
4
- data.tar.gz: 2e1c220542d7b7356c6b963edb2d34e45cdbd0cd556dec6a718a4f663b5651b5
3
+ metadata.gz: 853a10a09ac23cc4cedb2c2b5bc966258cfc858673bbecc7a7bb625c8ddf4b92
4
+ data.tar.gz: f27458788eb2cccaa49bb319436979a91e93de4e54fb3fd83c28fd67b481a631
5
5
  SHA512:
6
- metadata.gz: 71fc2a6a83c414edbc2dbfca1b1e465a1d265ba80350101e4536178e1959ccf4a823478c7c139acde6d3e88d56d25d0a2fd9d4a781b2c1da919b4de0f5a4268a
7
- data.tar.gz: 65969682077beb48030e5f9ffff0f1d97aa2ee2ea48e2142feac74e3a687661aa5b7afcbb8db26b8b1da8936e7dbeca4d09e9a8be5108dda8dd6325fb15a3c99
6
+ metadata.gz: 78a5e6da19f10887854dd40fb85e4a48d2073c10ece4acc01c07651041110eceb4c13cb67fd7251b1e0abbc310bc25134ef32c27bd2bb4e6af3488eec3a2bdf5
7
+ data.tar.gz: 3bfc1ffe8b133f4b8b2621c810af522f51fa88d44ab5dcc00a679dd0bf3eab857321d311c9b156dcd63428e62222669d87a6c1f1a501203ba0e448b44300a60f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
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
+
1
11
  ## 0.5.1
2
12
  * Imported WASM methods can now be defined
3
13
  * Preliminary version of WASI interface and IO class added to Rlang library
data/Gemfile.lock CHANGED
@@ -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/bin/rlang CHANGED
@@ -70,6 +70,10 @@ options:
70
70
  options[:wasm] = v
71
71
  end
72
72
 
73
+ opts.on("-c", "--comments", "Include comments in WAT source file") do |v|
74
+ options[:comments] = v
75
+ end
76
+
73
77
  opts.on("-S", "--start FUNCTION", "Function name where execution starts (default '_start')") do |function|
74
78
  options[:start] = function
75
79
  end
@@ -19,6 +19,7 @@ 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
22
23
  -S, --start FUNCTION Function name where execution starts (default '_start')
23
24
  -o, --output FILE Write output to file
24
25
  -v, --verbose [LEVEL] Verbosity level (fatal, error, warn, info, debug)
@@ -32,6 +33,7 @@ Usage: rlang [options] rlang_file.rb
32
33
  * **-w, --wat**: parse Rlang file and generate WAT source code
33
34
  * **-a, --ast**: parse Rlang file and generate the Ruby abstract syntax tree
34
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.
35
37
  * **-S, --start**: function to use as the execution starting point of the WASM module (default is `_start`)
36
38
  * **-o, --output FILE**: send rlang output to FILE
37
39
  * **-v, --verbose [LEVEL]**: verbosity level (fatal, error, warn, info, debug). Default is warn
data/docs/RlangManual.md CHANGED
@@ -1,6 +1,6 @@
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
 
@@ -21,6 +21,7 @@ Rlang provides:
21
21
  * Arithmetic, relational and logical operators
22
22
  * WebAssembly source code (WAT) inlining
23
23
  * Requiring other Rlang or WAT files
24
+ * Importing and exporting WASM functions
24
25
  * A Rlang library (written in Rlang of course) that you can reuse in your own WebAssembly module
25
26
 
26
27
  Here is a sample of Rlang code to whet your appetite:
@@ -117,7 +118,7 @@ end
117
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.
118
119
 
119
120
  ### Class attribute type
120
- 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).
121
122
 
122
123
  ```ruby
123
124
  class Square
@@ -229,9 +230,9 @@ Rlang also gives you the ability to declare the return type of a method like thi
229
230
  result :class_name, :method_name, :your_type_here
230
231
  ```
231
232
 
232
- 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`).
233
234
 
234
- 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.
235
236
 
236
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.
237
238
 
@@ -294,10 +295,10 @@ The example above is taken from the Rlang WASI class that defines the interface
294
295
 
295
296
 
296
297
  ## Rlang types
297
- 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).
298
299
 
299
300
  ### Implicit type cast
300
- 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.
301
302
 
302
303
  ```ruby
303
304
  def m_local_var(arg1)
@@ -307,7 +308,7 @@ def m_local_var(arg1)
307
308
  end
308
309
  ```
309
310
 
310
- 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.
311
312
 
312
313
  ```ruby
313
314
  def self.m_local_var(arg1)
@@ -318,6 +319,33 @@ def self.m_local_var(arg1)
318
319
  end
319
320
  ```
320
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
+
321
349
  ### Explicit type cast
322
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:
323
351
 
@@ -334,7 +362,7 @@ The first line will auto-vivify the `@@cvar` class variable as type `:I64`.
334
362
 
335
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.
336
364
 
337
- 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...
338
366
 
339
367
  Note that type cast can be used anywhere in the code whether in class body or method definition.
340
368
 
@@ -510,6 +538,7 @@ For now, the Rlang library is very modest and containoffers the following classe
510
538
  * Malloc.malloc(nbytes) : dynamically allocates nbytes of memory and return address of the allocated memory space or -1 if it fails
511
539
  * Malloc.free(address) : frees the block of memory at the given address
512
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]`)
513
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.
514
543
 
515
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).
@@ -5,6 +5,7 @@
5
5
  # 4 bytes object array class
6
6
  require_relative '../memory'
7
7
  require_relative '../kernel'
8
+ require_relative '../type'
8
9
  #require_relative '../string'
9
10
 
10
11
 
@@ -18,16 +19,23 @@ class Array32
18
19
  # pointers to objects
19
20
  # Arrays are fixed size for now
20
21
  def initialize(count)
21
- @ptr = Object.allocate(count * 4)
22
+ if count == 0
23
+ @ptr = 0
24
+ else
25
+ # Memory size is count * 4 bytes
26
+ @ptr = Object.allocate(count << 2)
27
+ end
22
28
  @count = count
23
29
  end
24
30
 
25
- def size; @count; end
31
+ def size; @count; end
26
32
  def length; @count; end
33
+ def empty?; @count == 0; end
27
34
 
28
35
  def [](idx)
29
36
  result :I32
30
- raise "Index out of bound" if idx >= @count
37
+ raise "Index out of bound" if idx >= @count || idx < -@count
38
+ idx = @count + idx if idx <0
31
39
  # offset in memory for elt #idx is idx * 4
32
40
  Memory.load32(@ptr + (idx << 2))
33
41
  end
@@ -35,7 +43,8 @@ class Array32
35
43
  def []=(idx, value)
36
44
  arg value: :I32
37
45
  result :I32
38
- raise "Index out of bound" if idx >= @count
46
+ raise "Index out of bound" if idx >= @count || idx < -@count
47
+ idx = @count + idx if idx <0
39
48
  # offset in memory for elt #idx is idx * 4
40
49
  Memory.store32(@ptr + (idx << 2), value)
41
50
  value
@@ -5,6 +5,7 @@
5
5
  # 4 bytes object array class
6
6
  require_relative '../memory'
7
7
  require_relative '../object'
8
+ require_relative '../type'
8
9
  require_relative '../string'
9
10
 
10
11
 
@@ -16,16 +17,23 @@ class Array64
16
17
  # pointers to objects
17
18
  # Arrays are fixed size for now
18
19
  def initialize(count)
19
- @ptr = Object.allocate(count * 8)
20
+ # Avoid allocating 0 bytes in memory
21
+ if count == 0
22
+ @ptr = 0
23
+ else
24
+ # Memory size is count * 8 bytes
25
+ @ptr = Object.allocate(count << 3)
26
+ end
20
27
  @count = count
21
28
  end
22
29
 
23
30
  def size; @count; end
24
31
  def length; @count; end
32
+ def empty?; self.size == 0; end
25
33
 
26
34
  def [](idx)
27
35
  result :I64
28
- raise "Index out of bound" if idx >= @count
36
+ raise "Index out of bound" if idx >= @count || idx < -@count
29
37
  # offset in memory for elt #idx is idx * 8
30
38
  Memory.load64(@ptr + (idx << 3))
31
39
  end
@@ -33,7 +41,7 @@ class Array64
33
41
  def []=(idx, value)
34
42
  arg value: :I64
35
43
  result :I64
36
- raise "Index out of bound" if idx >= @count
44
+ raise "Index out of bound" if idx >= @count || idx < -@count
37
45
  # offset in memory for elt #idx is idx * 8
38
46
  Memory.store64(@ptr + (idx << 3), value)
39
47
  value
@@ -0,0 +1,223 @@
1
+ # Rlang WebAssembly compiler
2
+ # Copyright (c) 2019-2022, Laurent Julliard and contributors
3
+ # All rights reserved.
4
+
5
+ require 'kernel'
6
+ require 'string'
7
+ require 'array'
8
+
9
+ module Base64
10
+
11
+ include Kernel
12
+
13
+ BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
14
+ BASE64_CHARS_URLSAFE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
15
+ PAD = "="
16
+ LINE_SEP = "\n" # Would be "\r\n" on Windows
17
+
18
+ # Tables of valid ASCII char maps for Base64 decoding.
19
+ # -1 : the char is invalid
20
+ # >= 0 : the binary value to use for decoding (note: the '=' sign is also 0)
21
+ #
22
+ BASE64_CHAR_MAP =
23
+ [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
24
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
25
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8,
26
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26,
27
+ 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
28
+ 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
29
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
30
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
31
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
32
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
33
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
34
+
35
+ BASE64_CHAR_MAP_URLSAFE =
36
+ [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
37
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
38
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8,
39
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, -1, 26,
40
+ 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
41
+ 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
42
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
43
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
44
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
45
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
46
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
47
+
48
+
49
+ # Generic base 64 encoding
50
+ # Derived from
51
+ # http://www.java2s.com/Code/Java/Development-Class/AfastandmemoryefficientclasstoencodeanddecodetoandfromBASE64infullaccordancewithRFC2045.htm)
52
+ def self._encode64(bin, line_sep, base64_chars)
53
+ arg bin: :String, base64_chars: :String
54
+ local str: :String
55
+ result :String
56
+
57
+ # Check special case
58
+ return "" if bin.empty?
59
+ slen = bin.length
60
+
61
+ # Number of even 24 bits (groups of 3 bytes)
62
+ elen = (slen / 3) * 3
63
+ # Encoded character count
64
+ ccnt = ((slen - 1) / 3 + 1) << 2
65
+ # Size of encoded string (incl. line separators)
66
+ # Note : Ruby implementation always add a \n at the end
67
+ # hence the +1
68
+ dlen = ccnt
69
+ dlen += ((ccnt - 1) / 60 + 1) * LINE_SEP.length if line_sep
70
+
71
+ # Allocate the proper size for encoded string
72
+ # to avoid allocating many string objects during
73
+ # concatenation
74
+ str = String.new(0, dlen)
75
+
76
+ # Encode even 24 bits
77
+ s=0; d=0; cc=0
78
+ while s < elen
79
+ # Copy next three bytes into lower 24 bits of int,
80
+ # paying attention to sign.
81
+ i = bin[s].ord << 16 | bin[s+1].ord << 8 | bin[s+2].ord
82
+ s += 3
83
+
84
+ # Encode the int into four chars
85
+ str[d] = base64_chars[(i >> 18) & 0x3f]; d += 1
86
+ str[d] = base64_chars[(i >> 12) & 0x3f]; d += 1
87
+ str[d] = base64_chars[(i >> 6) & 0x3f]; d += 1
88
+ str[d] = base64_chars[i & 0x3f]; d += 1
89
+ cc += 1
90
+
91
+ # Add optional line separator each 60 chars in Ruby
92
+ if (line_sep && cc == 15 && d < dlen - 1)
93
+ str[d] = LINE_SEP; d += LINE_SEP.length
94
+ cc = 0
95
+ end
96
+ end
97
+
98
+ # Pad and encode last bits if source isn't even 24 bits.
99
+ left = slen - elen # 0 - 2
100
+ if left > 0
101
+ # Prepare the last int
102
+ i = bin[elen].ord << 10
103
+ i |= (bin[slen - 1].ord << 2) if left == 2
104
+
105
+ # Set last four chars
106
+ str[d] = base64_chars[i >> 12]; d += 1
107
+ str[d] = base64_chars[(i >> 6) & 0x3f]; d += 1
108
+ if left == 2
109
+ str[d] = base64_chars[i & 0x3f]
110
+ else
111
+ str[d] = PAD
112
+ end
113
+ d += 1
114
+ str[d] = PAD; d += 1
115
+ end
116
+
117
+ str[d] = LINE_SEP if line_sep
118
+ return str
119
+ end
120
+
121
+
122
+ # Decodes a BASE64 encoded String. All illegal characters will be
123
+ # ignored and can handle both arrays with and without line separators.
124
+ #
125
+ # Derived from
126
+ # http://www.java2s.com/Code/Java/Development-Class/AfastandmemoryefficientclasstoencodeanddecodetoandfromBASE64infullaccordancewithRFC2045.htm)
127
+
128
+ def self._decode64(str, base64_chars, base64_char_map)
129
+ arg str: :String, base64_chars: :String, base64_char_map: :Array32
130
+ local bin: :String
131
+ result :String
132
+
133
+ # Check special case
134
+ return "" if (slen = str.length) == 0
135
+
136
+ # Count illegal characters (including '\r', '\n') to know what
137
+ # size the returned array will be, so we don't have to reallocate
138
+ # & copy it later.
139
+ # Number of separator characters. (Actually illegal characters,
140
+ # but that's a bonus...)
141
+ sepcnt = 0; i = 0
142
+ while i < slen
143
+ sepcnt += 1 if base64_char_map[str[i].ord] < 0
144
+ i += 1
145
+ end
146
+
147
+ # Check that legal chars (including '=') are evenly divideable
148
+ # by 4 as specified in RFC 2045.
149
+ return "" if (slen - sepcnt) % 4 != 0
150
+
151
+ # Count padding chars from the end
152
+ pad = 0; i = slen-1
153
+ while i > 0 && base64_char_map[str[i].ord] <= 0
154
+ pad += 1 if str[i] == PAD
155
+ i -= 1
156
+ end
157
+
158
+ # Preallocate String of exact length
159
+ len = ((slen - sepcnt) * 6 >> 3) - pad
160
+ bin = String.new(0, len)
161
+
162
+ # Go on decoding
163
+ s = 0; d = 0
164
+ while d < len
165
+ # Assemble 3 bytes into an int from 4 valid characters.
166
+ i = 0; j = 0
167
+ while j < 4
168
+ c = base64_char_map[str[s].ord]; s += 1
169
+ # j increases only if a valid char is found
170
+ if c != -1
171
+ i |= c << (18 - j * 6)
172
+ j += 1
173
+ end
174
+ end
175
+
176
+ # Now decode the int and add 3 chars to the bin string
177
+ bin[d] = (i >> 16 & 0xff).chr; d += 1
178
+ if d < len
179
+ bin[d] = (i >> 8 & 0xff).chr; d += 1
180
+ if d < len
181
+ bin[d] = (i & 0xff).chr; d += 1
182
+ end
183
+ end
184
+ end
185
+
186
+ return bin
187
+ end
188
+
189
+ def self.decode64(str)
190
+ arg str: :String
191
+ result :String
192
+ self._decode64(str, BASE64_CHARS, BASE64_CHAR_MAP)
193
+ end
194
+
195
+ def self.encode64(bin)
196
+ arg bin: :String
197
+ result :String
198
+ self._encode64(bin, 1, BASE64_CHARS)
199
+ end
200
+
201
+ def self.strict_decode64(str)
202
+ arg str: :String
203
+ self._decode64(str, BASE64_CHARS, BASE64_CHAR_MAP)
204
+ end
205
+
206
+ def self.strict_encode64(bin)
207
+ arg bin: :String
208
+ result :String
209
+ self._encode64(bin, 0, BASE64_CHARS)
210
+ end
211
+
212
+ def self.urlsafe_decode64(str)
213
+ arg str: :String
214
+ self._decode64(str, BASE64_CHARS_URLSAFE, BASE64_CHAR_MAP_URLSAFE)
215
+ end
216
+
217
+ def self.urlsafe_encode64(bin)
218
+ arg bin: :String
219
+ result :String
220
+ self._encode64(bin, 0, BASE64_CHARS_URLSAFE)
221
+ end
222
+
223
+ end
data/lib/rlang/lib/io.rb CHANGED
@@ -40,11 +40,16 @@ class IO
40
40
  end
41
41
 
42
42
  def read
43
- result :I32 #:String
43
+ result :String
44
+ local stg: :String
44
45
  iovec = WASI::IOVec.new(1)
45
46
  errno = WASI.fd_read(@fd, iovec.iovs.ptr, 1, @@num_bytes_read.addr)
46
47
  # -1 below because of \0 terminated string
47
- String.new(iovec.iovs[0], @@num_bytes_read-1)
48
+ stg = String.new(iovec.iovs[0], @@num_bytes_read-1)
49
+ # Nullify the iovs entry used by the String object so it is not freed
50
+ iovec.iovs[0] = 0
51
+ iovec.free
52
+ stg
48
53
  end
49
54
 
50
55
  end
@@ -37,7 +37,7 @@ DAta[:dummy_malloc_data] = [0, 0, 0, 0]
37
37
 
38
38
  class Header
39
39
  attr_accessor :ptr, :size
40
- attr_type ptr: :Header, size: :I32
40
+ attr_type ptr: :Header, size: :UI32
41
41
  end
42
42
 
43
43
  class Malloc
@@ -55,7 +55,8 @@ class Malloc
55
55
  # malloc: allocate n bytes of memory and return pointer
56
56
  # to data block
57
57
  def self.malloc(nbytes)
58
- local p: :Header, prevp: :Header
58
+ arg nbytes: :UI32
59
+ local p: :Header, prevp: :Header, nunits: :UI32
59
60
 
60
61
  # allocate memory by chunk of units (the unit is
61
62
  # the size of a Header object here)
@@ -107,6 +108,7 @@ class Malloc
107
108
 
108
109
  # morecore: ask system for more memory
109
110
  def self.morecore(nu)
111
+ arg nu: :UI32
110
112
  result :Header
111
113
  local up: :Header
112
114
 
@@ -122,7 +124,7 @@ class Malloc
122
124
 
123
125
  # Free memory block
124
126
  def self.free(ap)
125
- arg ap: :I32
127
+ arg ap: :UI32
126
128
  result :none
127
129
  local bp: :Header, p: :Header
128
130
 
@@ -26,4 +26,27 @@ class Object
26
26
  result :String
27
27
  "Object <addr>"
28
28
  end
29
+
30
+ def object_id
31
+ result :I32
32
+ self
33
+ end
34
+
35
+ def eql?(object)
36
+ result :I32
37
+ inline wat: '(i32.eq
38
+ (local.get $_self_)
39
+ (local.get $object))',
40
+ wtype: :I32,
41
+ ruby: 'self.object_id == object.object_id'
42
+ end
43
+
44
+ def ==(object)
45
+ self.eql?(object)
46
+ end
47
+
48
+ def !=(object)
49
+ !(self == object)
50
+ end
51
+
29
52
  end
@@ -4,7 +4,7 @@
4
4
  #
5
5
  # Rlang core library classes and modules
6
6
  #
7
- require_relative './type'
7
+
8
8
  require_relative './memory'
9
9
  require_relative './unistd'
10
10
  require_relative './malloc'
@@ -12,3 +12,4 @@ require_relative './object'
12
12
  require_relative './kernel'
13
13
  require_relative './string'
14
14
  require_relative './array'
15
+ require_relative './type'