rubex 0.0.1 → 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/.travis.yml +14 -0
- data/CONTRIBUTING.md +101 -0
- data/HISTORY.md +3 -0
- data/README.md +112 -297
- data/REFERENCE.md +753 -0
- data/Rakefile +4 -1
- data/TUTORIAL.md +234 -0
- data/bin/rubex +1 -1
- data/docs/_config.yml +1 -0
- data/docs/index.html +1 -0
- data/examples/c_struct_interface/c_struct_interface.rb +6 -0
- data/examples/c_struct_interface/c_struct_interface.rubex +47 -0
- data/examples/linked_list/linked_list.rubex +39 -0
- data/examples/linked_list/rb_linked_list.rb +8 -0
- data/examples/rcsv wrapper/rcsv/README.md +1 -0
- data/examples/rcsv wrapper/rcsv/Rakefile +7 -0
- data/examples/rcsv wrapper/rcsv/ext/rcsv/extconf.rb +3 -0
- data/examples/rcsv wrapper/rcsv/ext/rcsv/rcsv.c +302 -0
- data/examples/rcsv wrapper/rcsv/ext/rcsv/rcsv.rubex +124 -0
- data/examples/rcsv wrapper/rcsv/lib/rcsv.rb +8 -0
- data/examples/rcsv wrapper/rcsv/lib/rcsv.so +0 -0
- data/examples/rcsv wrapper/rcsv/lib/rcsv/version.rb +1 -0
- data/examples/rcsv wrapper/rcsv/rcsv.gemspec +27 -0
- data/examples/rcsv wrapper/rcsv/spec/rcsv.csv +5 -0
- data/examples/rcsv wrapper/rcsv/spec/rcsv_spec.rb +17 -0
- data/examples/rcsv wrapper/rcsv/spec/spec_helper.rb +6 -0
- data/{spec/fixtures/basic_ruby_method/Makefile → examples/rcsv wrapper/rcsv/tmp/x86_64-linux/rcsv/2.3.3/Makefile } +20 -20
- data/examples/rcsv wrapper/rcsv/tmp/x86_64-linux/rcsv/2.3.3/rcsv.o +0 -0
- data/examples/rcsv wrapper/rcsv/tmp/x86_64-linux/rcsv/2.3.3/rcsv.so +0 -0
- data/examples/rcsv wrapper/rcsv/tmp/x86_64-linux/stage/lib/rcsv.so +0 -0
- data/lib/rubex.rb +6 -50
- data/lib/rubex/ast.rb +1 -3
- data/lib/rubex/ast/expression.rb +1257 -8
- data/lib/rubex/ast/node.rb +226 -28
- data/lib/rubex/ast/statement.rb +1162 -35
- data/lib/rubex/ast/top_statement.rb +815 -0
- data/lib/rubex/code_writer.rb +103 -26
- data/lib/rubex/compiler.rb +72 -0
- data/lib/rubex/compiler_config.rb +19 -0
- data/lib/rubex/constants.rb +145 -8
- data/lib/rubex/data_type.rb +667 -4
- data/lib/rubex/error.rb +15 -0
- data/lib/rubex/helpers.rb +154 -0
- data/lib/rubex/lexer.rex +186 -22
- data/lib/rubex/lexer.rex.rb +261 -35
- data/lib/rubex/parser.racc +876 -28
- data/lib/rubex/parser.racc.rb +2845 -90
- data/lib/rubex/rake_task.rb +34 -0
- data/lib/rubex/symbol_table/entry.rb +17 -3
- data/lib/rubex/symbol_table/scope.rb +298 -25
- data/lib/rubex/version.rb +1 -1
- data/rubex.gemspec +11 -3
- data/spec/basic_ruby_method_spec.rb +15 -21
- data/spec/binding_ptr_args_spec.rb +33 -0
- data/spec/bitwise_operators_spec.rb +40 -0
- data/spec/blocks_spec.rb +35 -0
- data/spec/c_bindings_spec.rb +36 -0
- data/spec/c_constants_spec.rb +33 -0
- data/spec/c_function_ptrs_spec.rb +38 -0
- data/spec/c_functions_spec.rb +35 -0
- data/spec/c_struct_interface_spec.rb +38 -0
- data/spec/call_by_reference_spec.rb +33 -0
- data/spec/class_methods_spec.rb +33 -0
- data/spec/class_spec.rb +40 -0
- data/spec/comments_spec.rb +33 -0
- data/spec/default_args_spec.rb +37 -0
- data/spec/error_handling_spec.rb +42 -0
- data/spec/examples_spec.rb +52 -0
- data/spec/expressions_spec.rb +33 -0
- data/spec/fixtures/basic_ruby_method/basic_ruby_method.rubex +2 -0
- data/spec/fixtures/binding_ptr_args/binding_ptr_args.rubex +30 -0
- data/spec/fixtures/bitwise_operators/bitwise_operators.rubex +40 -0
- data/spec/fixtures/blocks/blocks.rubex +11 -0
- data/spec/fixtures/c_bindings/c_bindings.rubex +58 -0
- data/spec/fixtures/c_constants/c_constants.rubex +7 -0
- data/spec/fixtures/c_function_ptrs/c_function_ptrs.rubex +52 -0
- data/spec/fixtures/c_functions/c_functions.rubex +25 -0
- data/spec/fixtures/c_struct_interface/c_struct_interface.rubex +34 -0
- data/spec/fixtures/call_by_reference/call_by_reference.rubex +30 -0
- data/spec/fixtures/class/class.rubex +20 -0
- data/spec/fixtures/class_methods/class_methods.rubex +12 -0
- data/spec/fixtures/comments/comments.rubex +9 -0
- data/spec/fixtures/default_args/default_args.rubex +11 -0
- data/spec/fixtures/error_handling/error_handling.rubex +54 -0
- data/spec/fixtures/examples/array_to_hash.rubex +14 -0
- data/spec/fixtures/examples/rcsv.csv +5 -0
- data/spec/fixtures/examples/rcsv.rubex +329 -0
- data/spec/fixtures/expressions/expressions.rubex +10 -0
- data/spec/fixtures/if_else/if_else.rubex +77 -0
- data/spec/fixtures/implicit_lib_include/implicit_lib_include.rubex +15 -0
- data/spec/fixtures/init_ruby_objects_with_literal_syntax/init_ruby_objects_with_literal_syntax.rubex +17 -0
- data/spec/fixtures/loops/loops.rubex +33 -0
- data/spec/fixtures/recursion/recursion.rubex +9 -0
- data/spec/fixtures/ruby_constant_method_calls/ruby_constant_method_calls.rubex +17 -0
- data/spec/fixtures/ruby_operators/ruby_operators.rubex +29 -0
- data/spec/fixtures/ruby_raise/ruby_raise.rubex +13 -0
- data/spec/fixtures/ruby_strings/ruby_strings.rubex +19 -0
- data/spec/fixtures/ruby_strings/string_blank_bm.rb +37 -0
- data/spec/fixtures/ruby_symbols/ruby_symbols.rubex +12 -0
- data/spec/fixtures/ruby_types/ruby_types.rubex +15 -0
- data/spec/fixtures/statement_expression/statement_expression.rubex +23 -0
- data/spec/fixtures/static_array/static_array.rubex +20 -0
- data/spec/fixtures/string_literals/string_literals.rubex +15 -0
- data/spec/fixtures/struct/struct.rubex +82 -0
- data/spec/fixtures/typecasting/typecasting.rubex +23 -0
- data/spec/fixtures/var_declarations/var_declarations.rubex +39 -0
- data/spec/if_else_spec.rb +39 -0
- data/spec/implicit_lib_include_spec.rb +33 -0
- data/spec/init_ruby_objects_with_literal_syntax_spec.rb +39 -0
- data/spec/loops_spec.rb +34 -0
- data/spec/recursion_spec.rb +35 -0
- data/spec/ruby_constant_method_calls_spec.rb +35 -0
- data/spec/ruby_operators_spec.rb +40 -0
- data/spec/ruby_raise_spec.rb +35 -0
- data/spec/ruby_strings_spec.rb +33 -0
- data/spec/ruby_symbols_spec.rb +37 -0
- data/spec/ruby_types_spec.rb +35 -0
- data/spec/spec_helper.rb +54 -1
- data/spec/statement_expression_spec.rb +34 -0
- data/spec/static_array_spec.rb +33 -0
- data/spec/string_literals_spec.rb +34 -0
- data/spec/struct_spec.rb +36 -0
- data/spec/typecasting_spec.rb +38 -0
- data/spec/var_declarions_spec.rb +35 -0
- metadata +255 -29
- data/lib/rubex/ast/argument_list.rb +0 -20
- data/lib/rubex/ast/c_base_type.rb +0 -11
- data/lib/rubex/ast/ruby_method_def.rb +0 -84
- data/spec/fixtures/basic_ruby_method/basic.rb +0 -3
- data/spec/fixtures/basic_ruby_method/basic_ruby_method.c +0 -16
- data/spec/fixtures/basic_ruby_method/basic_ruby_method.o +0 -0
- data/spec/fixtures/basic_ruby_method/basic_ruby_method.so +0 -0
- data/spec/fixtures/basic_ruby_method/extconf.rb +0 -3
data/REFERENCE.md
ADDED
@@ -0,0 +1,753 @@
|
|
1
|
+
# Rubex (RUBy EXtension Language)
|
2
|
+
|
3
|
+
Rubex is a language designed to keep you happy even when writing C extension.
|
4
|
+
|
5
|
+
# Table of Contents
|
6
|
+
<!-- MarkdownTOC autolink="true" bracket="round" depth="3"-->
|
7
|
+
|
8
|
+
- [Comments](#comments)
|
9
|
+
- [C data types](#c-data-types)
|
10
|
+
- [Primitive data types](#primitive-data-types)
|
11
|
+
- [C struct, union and enum](#c-struct-union-and-enum)
|
12
|
+
- [Forward declarations](#forward-declarations)
|
13
|
+
- [Pointers](#pointers)
|
14
|
+
- [Ruby Objects](#ruby-objects)
|
15
|
+
- [Literals](#literals)
|
16
|
+
- [Integer](#integer)
|
17
|
+
- [Float](#float)
|
18
|
+
- [Character](#character)
|
19
|
+
- [String](#string)
|
20
|
+
- [Ruby Literals](#ruby-literals)
|
21
|
+
- [Ruby Symbol](#ruby-symbol)
|
22
|
+
- [Ruby Array](#ruby-array)
|
23
|
+
- [Ruby Hash](#ruby-hash)
|
24
|
+
- [C Functions and Ruby Methods](#c-functions-and-ruby-methods)
|
25
|
+
- [Ruby Constants](#ruby-constants)
|
26
|
+
- [The print statement](#the-print-statement)
|
27
|
+
- [Loops](#loops)
|
28
|
+
- [The while loop](#the-while-loop)
|
29
|
+
- [The for loop](#the-for-loop)
|
30
|
+
- [Conditionals](#conditionals)
|
31
|
+
- [Important Note](#important-note)
|
32
|
+
- [Interfacing C libraries with lib](#interfacing-c-libraries-with-lib)
|
33
|
+
- [Basic Usage](#basic-usage)
|
34
|
+
- [Supported declarations](#supported-declarations)
|
35
|
+
- [Functions](#functions)
|
36
|
+
- [Variables](#variables)
|
37
|
+
- [Macros](#macros)
|
38
|
+
- [Types](#types)
|
39
|
+
- [Typedefs](#typedefs)
|
40
|
+
- [Linking Libraries](#linking-libraries)
|
41
|
+
- [Ready-to-use C functions](#ready-to-use-c-functions)
|
42
|
+
- [Filename: "rubex/ruby"](#filename-rubexruby)
|
43
|
+
- [Filename: "rubex/ruby/encoding"](#filename-rubexrubyencoding)
|
44
|
+
- [Filename: "rubex/stdlib"](#filename-rubexstdlib)
|
45
|
+
- [Exception Handling](#exception-handling)
|
46
|
+
- ['Attach' Classes](#attach-classes)
|
47
|
+
- [The attach keyword](#the-attach-keyword)
|
48
|
+
- [The data$ variable](#the-data-variable)
|
49
|
+
- [Special C functions in attach classes](#special-c-functions-in-attach-classes)
|
50
|
+
- [deallocate](#deallocate)
|
51
|
+
- [allocate](#allocate)
|
52
|
+
- [memcount](#memcount)
|
53
|
+
- [get_struct](#getstruct)
|
54
|
+
- [gc_mark](#gcmark)
|
55
|
+
- [Typecast](#typecast)
|
56
|
+
- [Alias](#alias)
|
57
|
+
- [Conversions between Ruby and C data](#conversions-between-ruby-and-c-data)
|
58
|
+
- [C callbacks](#c-callbacks)
|
59
|
+
- [Inline C](#inline-c)
|
60
|
+
- [Handling Strings](#handling-strings)
|
61
|
+
- [Differences from C](#differences-from-c)
|
62
|
+
- [Differences from Ruby](#differences-from-ruby)
|
63
|
+
- [Limitations](#limitations)
|
64
|
+
|
65
|
+
<!-- /MarkdownTOC -->
|
66
|
+
|
67
|
+
# Comments
|
68
|
+
|
69
|
+
Similar to Ruby, comments start with `#`.
|
70
|
+
|
71
|
+
Example:
|
72
|
+
```
|
73
|
+
# This is a comment.
|
74
|
+
def funky(int j) # ...so is this.
|
75
|
+
return j
|
76
|
+
end
|
77
|
+
```
|
78
|
+
|
79
|
+
# C data types
|
80
|
+
|
81
|
+
## Primitive data types
|
82
|
+
|
83
|
+
Following are the Rubex keywords for data types and their corresponding C types and description.
|
84
|
+
|
85
|
+
|rubex keyword | C type | Description |
|
86
|
+
|:--- |:--- |:--- |
|
87
|
+
|char |char |Character |
|
88
|
+
|i8 |int8_t |8 bit integer |
|
89
|
+
|i16 |int16_t |16 bit integer |
|
90
|
+
|i32 |int32_t |32 bit integer |
|
91
|
+
|i64 |int64_t |64 bit integer |
|
92
|
+
|u8 |uint8_t |8 bit unsigned integer |
|
93
|
+
|u16 |uint16_t |16 bit unsigned integer |
|
94
|
+
|u32 |uint32_t |32 bit unsigned integer |
|
95
|
+
|u64 |uint64_t |64 bit unsigned integer |
|
96
|
+
|int |int | Integer >= 16 bits. |
|
97
|
+
|unsigned int |unsigned int | Unsigned integer >= 16 bits. |
|
98
|
+
|long int |long int | Integer >= 32 bits.|
|
99
|
+
|unsigned long int |unsigned long int|Unsigned Integer >= 32 bits. |
|
100
|
+
|long long int |long long int |Integer >= 64 bits.|
|
101
|
+
|unsigned long long int|unsigned long long int|Unsigned Integer >= 64 bits.|
|
102
|
+
|f32/float |float |32 bit floating point |
|
103
|
+
|f64/double |double |64 bit floating point |
|
104
|
+
|long f64/long double |long double |Long double >= 96 bits. |
|
105
|
+
|object |VALUE |Ruby object |
|
106
|
+
|
107
|
+
## C struct, union and enum
|
108
|
+
|
109
|
+
C structs can defined using the `struct` keyword. For example:
|
110
|
+
``` ruby
|
111
|
+
struct node
|
112
|
+
int a
|
113
|
+
end
|
114
|
+
|
115
|
+
def foo
|
116
|
+
node a
|
117
|
+
a.a = 3
|
118
|
+
|
119
|
+
return a.a
|
120
|
+
end
|
121
|
+
```
|
122
|
+
|
123
|
+
Structs can either be at the global, class or method scope. Their accessibility will differ according to the scope they are defined in. Take note that once you define a `struct node`, the only way to define a variable of type `struct node` is just use `node` for the variable definition, not `struct node`. So `node a` will work but `struct node a` will not work.
|
124
|
+
|
125
|
+
In case you declare a pointer to a struct, the elements of the struct should still be accessed with `.` since the `->` operator is not supported in Rubex.
|
126
|
+
Example:
|
127
|
+
``` ruby
|
128
|
+
def foo
|
129
|
+
struct bar
|
130
|
+
int a
|
131
|
+
end
|
132
|
+
|
133
|
+
bar *n
|
134
|
+
n.a = 3
|
135
|
+
|
136
|
+
return n.a
|
137
|
+
end
|
138
|
+
```
|
139
|
+
|
140
|
+
### Forward declarations
|
141
|
+
|
142
|
+
In case your struct has a member of a type whose definition has to be after your struct, you can use a forward declaration with the `fwd` keyword.
|
143
|
+
``` ruby
|
144
|
+
fwd struct other_node
|
145
|
+
struct node
|
146
|
+
i32 a, b
|
147
|
+
other_node *hello
|
148
|
+
end
|
149
|
+
|
150
|
+
struct other_node
|
151
|
+
i64 a, b
|
152
|
+
end
|
153
|
+
```
|
154
|
+
|
155
|
+
## Pointers
|
156
|
+
|
157
|
+
You can define pointers and pass them to functions just like you do in C. Pointers can be specified using the `*` operator and addresses of variables can be accessed using the `&` (address-of) operator.
|
158
|
+
|
159
|
+
Keep in mind that Rubex does not support the `*` operator for dereferencing a pointer. You will need to use the `[]` operator and access the value of pointer with `[0]` (since accessing the value of a pointer is analogous to accessing the value of an array in C).
|
160
|
+
|
161
|
+
For example:
|
162
|
+
``` ruby
|
163
|
+
class CPointersDemo
|
164
|
+
def foo
|
165
|
+
int *i
|
166
|
+
int *j
|
167
|
+
|
168
|
+
i[0] = 5
|
169
|
+
j = i
|
170
|
+
|
171
|
+
return j[0]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
```
|
175
|
+
|
176
|
+
# Ruby Objects
|
177
|
+
|
178
|
+
Any variable of type `object` is a Ruby object. You must take care to not manually free objects as it will inevitably lead to memory leaks. Let the GC take care of them.
|
179
|
+
|
180
|
+
Variables that are assigned values without actually specifying the data type are also assumed to be objects.
|
181
|
+
|
182
|
+
For example:
|
183
|
+
``` ruby
|
184
|
+
def ruby_obj_demo
|
185
|
+
object a = "string!"
|
186
|
+
b = "string!"
|
187
|
+
|
188
|
+
return a == b
|
189
|
+
end
|
190
|
+
```
|
191
|
+
|
192
|
+
Above function will return `true`.
|
193
|
+
|
194
|
+
# Literals
|
195
|
+
|
196
|
+
Literals in Rubex can either be represented as C data or Ruby objects depending on the type of the variable that they are assigned to. Therefore, something like `a = 1` will cause `a` to be a Ruby object with an `Integer` value `1` and something like `int a = 1` will cause `a` to be of C type `int`.
|
197
|
+
|
198
|
+
## Integer
|
199
|
+
|
200
|
+
Integers are supported. Assigning an integer to a C type has the same effect as the assignment would in C code.
|
201
|
+
``` ruby
|
202
|
+
def foo
|
203
|
+
float a = 3
|
204
|
+
int b = -2
|
205
|
+
char c = 34
|
206
|
+
end
|
207
|
+
```
|
208
|
+
|
209
|
+
## Float
|
210
|
+
|
211
|
+
Similar to integers.
|
212
|
+
``` ruby
|
213
|
+
def foo
|
214
|
+
float i = 4.5
|
215
|
+
double j = -32.66
|
216
|
+
int c = 6.9
|
217
|
+
end
|
218
|
+
```
|
219
|
+
|
220
|
+
## Character
|
221
|
+
|
222
|
+
Characters can be specified with a single quote. If assigning to a Ruby object, `char` will be implicitly converted to a Ruby `String`.
|
223
|
+
``` ruby
|
224
|
+
def foo
|
225
|
+
char c = 'a'
|
226
|
+
|
227
|
+
return c
|
228
|
+
end
|
229
|
+
```
|
230
|
+
|
231
|
+
## String
|
232
|
+
|
233
|
+
Strings are specified with double quotes. Note that Ruby-like single quoted strings are _not_ supported in Rubex. String literals are C strings (`char*` arrays) by default but if you assign them to a Ruby object they are implicitly converted into Ruby strings.
|
234
|
+
|
235
|
+
For example:
|
236
|
+
``` ruby
|
237
|
+
def foo
|
238
|
+
s = "hello world!"
|
239
|
+
char *cs = "hello world!"
|
240
|
+
|
241
|
+
return cs
|
242
|
+
end
|
243
|
+
```
|
244
|
+
The `char*` can be implicitly converted to Ruby object. Read the [Handling Strings](#handling-strings) section to know more about string inter-conversion.
|
245
|
+
|
246
|
+
## Ruby Literals
|
247
|
+
|
248
|
+
Apart from `String`, Rubex also supports creating basic Ruby objects like `Array`, `Hash` and `Symbol` from literals using the same familiar Ruby syntax.
|
249
|
+
|
250
|
+
### Ruby Symbol
|
251
|
+
|
252
|
+
Symbols are specified with `:` before the identifier.
|
253
|
+
``` ruby
|
254
|
+
def foo
|
255
|
+
a = :hello
|
256
|
+
return a
|
257
|
+
end
|
258
|
+
```
|
259
|
+
|
260
|
+
### Ruby Array
|
261
|
+
|
262
|
+
Can be specified using `[]`. All members of the Array should be implicitly convertible to Ruby objects (like primitive C types or object). Putting instances of `struct` will lead to errors.
|
263
|
+
``` ruby
|
264
|
+
def foo
|
265
|
+
a = [1,2,3,"hello", "world", :symbol]
|
266
|
+
return a
|
267
|
+
end
|
268
|
+
```
|
269
|
+
|
270
|
+
### Ruby Hash
|
271
|
+
|
272
|
+
Hashes can be specified with `{}`.
|
273
|
+
|
274
|
+
Example:
|
275
|
+
``` ruby
|
276
|
+
def foo
|
277
|
+
a = {
|
278
|
+
:hello => "world",
|
279
|
+
3 => 4,
|
280
|
+
"foo" => :bar
|
281
|
+
}
|
282
|
+
|
283
|
+
return a
|
284
|
+
end
|
285
|
+
```
|
286
|
+
|
287
|
+
# C Functions and Ruby Methods
|
288
|
+
|
289
|
+
Apart from Ruby class methods and instance methods, Rubex allows you to define 'C functions' that are only accessible inside classes from within Rubex. These functions cannot be accessed from an external Ruby script.
|
290
|
+
|
291
|
+
C functions are defined used the `cfunc` keyword. You also need to specify the return type of the function along with its definition. For example:
|
292
|
+
``` ruby
|
293
|
+
class CFunctionTest
|
294
|
+
def foo(int n)
|
295
|
+
return bar(n)
|
296
|
+
end
|
297
|
+
|
298
|
+
cfunc int bar(int n)
|
299
|
+
return n + 5
|
300
|
+
end
|
301
|
+
end
|
302
|
+
```
|
303
|
+
|
304
|
+
C functions are 'lexically scoped' and are available inside both Ruby instance and class methods of the class and its hierarchy.
|
305
|
+
|
306
|
+
# Ruby Constants
|
307
|
+
|
308
|
+
Ruby constants can be specified using identifiers that start with capital letters. Since many C libraries contain functions or macros that start with capital letters, the Rubex compiler will first search for such identifiers, and if it does not find any, will assume that the identifier is a Ruby constant.
|
309
|
+
``` ruby
|
310
|
+
def foo
|
311
|
+
a = String.new("foo bar")
|
312
|
+
return a
|
313
|
+
end
|
314
|
+
```
|
315
|
+
|
316
|
+
# The print statement
|
317
|
+
|
318
|
+
The `print` statement makes it easy to print something to the console using C's `printf` function underneath. If it is passed a Ruby object instead of a C data type, the `#inspect` method will be called on the object and the resultant string will be printed. `print` can accept multiple comma-separated arguments and will concatenate them into a single string.
|
319
|
+
``` ruby
|
320
|
+
def foo(a, b)
|
321
|
+
int i = 5
|
322
|
+
|
323
|
+
print "Obj a is : ", a, ".", " b is: ", b, "... and i is: ", i
|
324
|
+
end
|
325
|
+
```
|
326
|
+
|
327
|
+
# Loops
|
328
|
+
|
329
|
+
Loops in Rubex are directly translated to C loops, thus giving massive gains in speed by just porting Ruby loops to Rubex. Rubex supports two kinds of loops: `while` loops and `for` loops.
|
330
|
+
|
331
|
+
## The while loop
|
332
|
+
|
333
|
+
The `while` loop in Rubex looks exactly like that in Ruby. Keep in mind that using C types for the conditional will save you the additional burden of a Ruby method call (which is takes non-trivial time for a long running loop).
|
334
|
+
``` ruby
|
335
|
+
def while_loop
|
336
|
+
int i = 0, j
|
337
|
+
|
338
|
+
while i < 100 do
|
339
|
+
j += 1
|
340
|
+
i += 1
|
341
|
+
end
|
342
|
+
|
343
|
+
return j
|
344
|
+
end
|
345
|
+
```
|
346
|
+
|
347
|
+
## The for loop
|
348
|
+
|
349
|
+
For loops look slightly different in Rubex than they do in Ruby. This has mainly been done to accommodate for the fact that Ruby for loops normally work with Ranges, but since that would make the code slow, Rubex `for` loops are directly translated into C `for` loops and thus follow a slightly different syntax. For example:
|
350
|
+
``` ruby
|
351
|
+
def for_loop_demo
|
352
|
+
int i, j = 0
|
353
|
+
|
354
|
+
for 0 <= i < 100 do
|
355
|
+
j += 1
|
356
|
+
end
|
357
|
+
|
358
|
+
return j
|
359
|
+
end
|
360
|
+
```
|
361
|
+
Above code will initialize `i` to `0` and iterate through it until it does not satisfy the second conditional. The inequalities must belong to the set `{<|<=}` OR `{>|>=}`.
|
362
|
+
|
363
|
+
# Conditionals
|
364
|
+
|
365
|
+
Similar to Ruby, conditionals in Rubex can be written as follows:
|
366
|
+
``` ruby
|
367
|
+
def foo(a)
|
368
|
+
int i = 3
|
369
|
+
|
370
|
+
return true if a == i
|
371
|
+
return false
|
372
|
+
end
|
373
|
+
```
|
374
|
+
|
375
|
+
In the above case, since `a` is a Ruby object and `i` is an `int`, `i` will be implicitly converted into a Ruby `Integer` and the comparison with the `==` operator will take place as though the two variables are Ruby objects (using `.send(:==)`).
|
376
|
+
|
377
|
+
## Important Note
|
378
|
+
|
379
|
+
If the expression in an `if` statements consists only of C variables, the values `0` and `NULL` (null pointer) are treated as _falsey_ and everything else is _truthy_ (exactly like C).
|
380
|
+
|
381
|
+
However, in case you intermingle Ruby and C types, Rubex will use the convention used in Ruby, i.e. `nil` (NilClass) and `false` (FalseClass) are _falsey_ whereas everything else (including zero, i.e. a Ruby `Integer` with value `0`) is _truthy_.
|
382
|
+
|
383
|
+
`0` and `NULL` will NOT be treated as _falsey_ if the expression type evaluates to a Ruby object.
|
384
|
+
|
385
|
+
# Interfacing C libraries with lib
|
386
|
+
|
387
|
+
The `lib` directive is used for interfacing external C libraries with Rubex.
|
388
|
+
|
389
|
+
## Basic Usage
|
390
|
+
|
391
|
+
Say you want to interface the `cos` and `sin` functions from the `math.h` C header file.
|
392
|
+
|
393
|
+
It is best demonstrated by this example:
|
394
|
+
``` ruby
|
395
|
+
lib "<math.h>"
|
396
|
+
double sin(double)
|
397
|
+
double cos(double)
|
398
|
+
end
|
399
|
+
|
400
|
+
class CMath
|
401
|
+
def f_sin(double n)
|
402
|
+
return sin(n)
|
403
|
+
end
|
404
|
+
|
405
|
+
def f_cos(double n)
|
406
|
+
return cos(n)
|
407
|
+
end
|
408
|
+
end
|
409
|
+
```
|
410
|
+
|
411
|
+
The string arugument (`"<math.h>"`) supplied to `lib` is the C header file that you want to interface your code with. You first need to tell Rubex that you will be using the functions `sin` and `cos` in your program, and that they accept a `double`as an argument and also return a `double`.
|
412
|
+
|
413
|
+
These can then be directly called from the Rubex program like any other function. Keep in mind that any method/function name in your Rubex program cannot have the same names as the external C functions since they are not namespaced. This will probably be fixed in a later version.
|
414
|
+
|
415
|
+
## Supported declarations
|
416
|
+
|
417
|
+
Following statements can be used inside the `lib` directive for telling rubex about functions/variables to be used from an external C library:
|
418
|
+
|
419
|
+
### Functions
|
420
|
+
|
421
|
+
Specify the return type and arguments of the functions that you will be using in your program:
|
422
|
+
``` ruby
|
423
|
+
lib "<math.h>"
|
424
|
+
double cos(double)
|
425
|
+
end
|
426
|
+
```
|
427
|
+
|
428
|
+
### Variables
|
429
|
+
|
430
|
+
You can specify to the Rubex compiler that you will be using constants or error values defined in C header files by specifying the name and type of the variable just like a normal variable declaration. For example:
|
431
|
+
|
432
|
+
``` ruby
|
433
|
+
lib "<ruby.h>"
|
434
|
+
int HAVE_RUBY_DEFINES_H
|
435
|
+
end
|
436
|
+
|
437
|
+
def have_ruby_h
|
438
|
+
return HAVE_RUBY_DEFINES_H
|
439
|
+
end
|
440
|
+
```
|
441
|
+
|
442
|
+
### Macros
|
443
|
+
|
444
|
+
Macros that work like functions, i.e. accept values and can be said to have 'return values' can be declared like any other C function:
|
445
|
+
``` ruby
|
446
|
+
lib "<ruby.h>"
|
447
|
+
object INT2FIX(int)
|
448
|
+
end
|
449
|
+
```
|
450
|
+
|
451
|
+
### Types
|
452
|
+
|
453
|
+
If the C library uses structs or unions, you need to specify the exact name of the type and its members that you will be using so that Rubex knows what to expect from the type that you will be using. If you will not be using a certain member of the struct, there is no need to specify it. If the struct is just an argument to a function or no members are being accessed, just leave the definition empty.
|
454
|
+
``` ruby
|
455
|
+
lib "<math.h>"
|
456
|
+
struct exception
|
457
|
+
int type
|
458
|
+
char *name
|
459
|
+
end
|
460
|
+
end
|
461
|
+
```
|
462
|
+
|
463
|
+
### Typedefs
|
464
|
+
|
465
|
+
## Linking Libraries
|
466
|
+
|
467
|
+
Frequently it is necessary to link external C binaries with the compiler in order to effectively compile the C file. This is normally done by passing `-l` flags to the compiler.
|
468
|
+
|
469
|
+
Rubex allows you to do this directly inside the compiler using the `link:` option supplied to the `lib` directive. This is best demonstrated with the following example:
|
470
|
+
``` ruby
|
471
|
+
lib "csv.h", link: "csv"
|
472
|
+
# some functions ...
|
473
|
+
end
|
474
|
+
|
475
|
+
def foo
|
476
|
+
# some code ...
|
477
|
+
end
|
478
|
+
```
|
479
|
+
|
480
|
+
The above code will add `-lcsv` option the compiler during the compilation phase.
|
481
|
+
|
482
|
+
## Ready-to-use C functions
|
483
|
+
|
484
|
+
In order to simplify interfacing with the standard library and to call some specific functions from the Ruby C API (if you must), Rubex provides a whole bunch of ready-to-use C functions from various header files that are available by default in most systems.
|
485
|
+
|
486
|
+
For example:
|
487
|
+
``` ruby
|
488
|
+
lib "rubex/ruby"; end
|
489
|
+
|
490
|
+
def foo(a)
|
491
|
+
return true if TYPE(a) == T_INT
|
492
|
+
return false
|
493
|
+
end
|
494
|
+
```
|
495
|
+
|
496
|
+
Above code will use the `TYPE()` macro from the `ruby.h` header file and check if the type is `Integer`, which is denoted by the `T_INT` macro, which is another macro from `ruby.h`. Many such libraries and their enclosing functions come as default with Rubex. Here is a complete list and the also the library names that you need to pass in order to make said functions visible to your program:
|
497
|
+
|
498
|
+
### Filename: "rubex/ruby"
|
499
|
+
|
500
|
+
Functions:
|
501
|
+
|
502
|
+
|Name and prototype| Description|
|
503
|
+
|:--- |:--- |
|
504
|
+
|`void* xmalloc(void*)` | |
|
505
|
+
|`void xfree(void*)` | |
|
506
|
+
|`int TYPE(object)`||
|
507
|
+
|`object rb_str_new(char* string, long lenghth)`||
|
508
|
+
|`object rb_ary_includes(object array, object item)`||
|
509
|
+
|
510
|
+
Variables:
|
511
|
+
|
512
|
+
|Type|Name|Description|
|
513
|
+
|:---|:---|:--- |
|
514
|
+
|`int`|`T_ARRAY`||
|
515
|
+
|`int`|`T_NIL`||
|
516
|
+
|`int`|`T_TRUE`||
|
517
|
+
|`int`|`T_FALSE`||
|
518
|
+
|`int`|`T_FLOAT`||
|
519
|
+
|`int`|`T_FIXNUM`||
|
520
|
+
|`int`|`T_BIGNUM`||
|
521
|
+
|`int`|`T_REGEXP`||
|
522
|
+
|`int`|`T_STRING`||
|
523
|
+
|
524
|
+
### Filename: "rubex/ruby/encoding"
|
525
|
+
|
526
|
+
Functions:
|
527
|
+
|
528
|
+
|Name and prototype| Description|
|
529
|
+
|:--- |:--- |
|
530
|
+
|`int rb_enc_find_index(char* encoding)`||
|
531
|
+
|`object rb_enc_associate_index(object string, int encoding)`||
|
532
|
+
|
533
|
+
### Filename: "rubex/stdlib"
|
534
|
+
|
535
|
+
Functions:
|
536
|
+
|
537
|
+
|Name and prototype|Description|
|
538
|
+
|:--- |:--- |
|
539
|
+
| `int atoi(char *string)`||
|
540
|
+
| `long atol(char *string)`||
|
541
|
+
| `long long atoll(char *string)`||
|
542
|
+
| `double atof(char *string)`||
|
543
|
+
|
544
|
+
# Exception Handling
|
545
|
+
|
546
|
+
Exception handling in Rubex can be done exactly like that in Ruby. No more dealing with `rb_protect()` or [complex tutorials](https://silverhammermba.github.io/emberb/c/#exceptions) on error handling in C extensions.
|
547
|
+
|
548
|
+
Just simply use `begin-rescue-else-ensure` blocks the way you would in Ruby. You can also make variable declarations inside these blocks and any value you define inside can be used outside too, just like in Ruby.
|
549
|
+
|
550
|
+
Example:
|
551
|
+
``` ruby
|
552
|
+
def foo(int n)
|
553
|
+
begin
|
554
|
+
raise ArgumentError if n == 1
|
555
|
+
raise SyntaxError if n == 2
|
556
|
+
rescue ArgumentError
|
557
|
+
n += 1
|
558
|
+
rescue SyntaxError
|
559
|
+
n += 2
|
560
|
+
else
|
561
|
+
n += 5
|
562
|
+
ensure
|
563
|
+
n += 10
|
564
|
+
end
|
565
|
+
|
566
|
+
return n
|
567
|
+
end
|
568
|
+
```
|
569
|
+
|
570
|
+
# 'Attach' Classes
|
571
|
+
|
572
|
+
Rubex introduces a special syntax that allows you to directly interface Ruby with C structs using some special language constructs, called 'attach' classes. These are normal Ruby classes and can be instantiated and used just like any other Ruby class, but with one caveat - they are permanently attached to a C struct and implicitly interface this struct with the Ruby VM.
|
573
|
+
|
574
|
+
Let me demonstrate with an example:
|
575
|
+
``` ruby
|
576
|
+
# file: structs.rubex
|
577
|
+
lib "rubex/ruby"; end
|
578
|
+
|
579
|
+
struct mp3info
|
580
|
+
int id
|
581
|
+
char* title
|
582
|
+
end
|
583
|
+
|
584
|
+
class Music attach mp3info
|
585
|
+
def initialize(id, title)
|
586
|
+
mp3info* mp3 = data$.mp3info
|
587
|
+
|
588
|
+
mp3.id = id
|
589
|
+
mp3.title = title
|
590
|
+
end
|
591
|
+
|
592
|
+
def id
|
593
|
+
return data$.id
|
594
|
+
end
|
595
|
+
|
596
|
+
def title
|
597
|
+
return data$.title
|
598
|
+
end
|
599
|
+
|
600
|
+
cfunc void deallocate
|
601
|
+
xfree(data$.mp3info)
|
602
|
+
end
|
603
|
+
end
|
604
|
+
```
|
605
|
+
This program can be used in a Ruby script like this:
|
606
|
+
``` ruby
|
607
|
+
require 'structs.so'
|
608
|
+
|
609
|
+
id = 1
|
610
|
+
title = "CAFO"
|
611
|
+
m = Music.new id, title
|
612
|
+
puts m.id, m.title
|
613
|
+
```
|
614
|
+
|
615
|
+
The above example has some notable Rubex constructs:
|
616
|
+
|
617
|
+
## The attach keyword
|
618
|
+
|
619
|
+
The 'attach' keyword is a special keyword that is used for associating a particular struct with a Ruby class. Once this keyword is used, the Rubex compiler will take care of allocation, deallocation and fetching of the struct, i.e. it will add calls to various functions essential for interfacing the struct with Ruby VM and the Garbage Collector. It will also create the `ruby_data_type_t` struct that [holds all the information](https://ruby-doc.org/core-2.3.0/doc/extension_rdoc.html#label-C+struct+to+Ruby+object)for interfacing structs with the Ruby VM, for example pointers to the marking and freeing functions.
|
620
|
+
|
621
|
+
In the above case, `attach` creates tells the class `Music` that it will be associated with a C struct of type `mp3info`.
|
622
|
+
|
623
|
+
## The data$ variable
|
624
|
+
|
625
|
+
The `data$` variable is a special variable keyword that is available _only_ inside attach classes. The `data$` variable allows access to the `mp3info` struct. In order to do this, it makes available a **pointer** to the struct that is of the same name as the struct (i.e. `mp3info` for `struct mp3info` or `foo` for `struct foo`). This pointer to the struct can then be used for reading or writing elements in the struct.
|
626
|
+
|
627
|
+
Read the 'Internals' section in [CONTRIBUTING](CONTRIBUTING.md) if you want to know more about the `data$` variable.
|
628
|
+
|
629
|
+
## Special C functions in attach classes
|
630
|
+
|
631
|
+
In most cases, the default configuration of the attach classes will suffice and you will not need to write any of the functions listed below by yourself (Rubex will write those functions in C for you), but if in some special cases where customization by the user is necessary, it can be done using some special functions that are translated directly into the relevant functions that need to interface with the Ruby VM for successfully interfacing a struct.
|
632
|
+
|
633
|
+
### deallocate
|
634
|
+
|
635
|
+
This is the only function that you must write by yourself, since a lot of GC deallocation depends upon specific data inside the struct and it is best if specified by the user.
|
636
|
+
|
637
|
+
Once you are done using an instance of your newly created attach class, Ruby's GC will want to clean up the memory used by it so that it can be used by other objects. In order to not have any memory leaks later, it is important to tell the GC that the memory that was used up by the `mp3info` struct needs to be freed. This freeing up of memory should be done inside the `deallocate` function.
|
638
|
+
|
639
|
+
The `xfree` function, which is the standard memory freeing function provided by the Ruby interpreter is used for this purpose.
|
640
|
+
|
641
|
+
### allocate
|
642
|
+
|
643
|
+
### memcount
|
644
|
+
|
645
|
+
### get_struct
|
646
|
+
|
647
|
+
### gc_mark
|
648
|
+
|
649
|
+
# Typecast
|
650
|
+
|
651
|
+
You can use a C typecast using `<>`. For example:
|
652
|
+
``` ruby
|
653
|
+
def foo
|
654
|
+
float a = 4.5
|
655
|
+
int b = <int>a
|
656
|
+
|
657
|
+
return b
|
658
|
+
end
|
659
|
+
```
|
660
|
+
|
661
|
+
Do not attempt typecasting between C and Ruby types. It will lead to problems. Let Rubex do that for you.
|
662
|
+
|
663
|
+
# Alias
|
664
|
+
|
665
|
+
The `alias` keyword can be used for aliasing data types. It is akin to `typedef` in C. Once the alias is declared, you can use it in your program just like any other data type.
|
666
|
+
|
667
|
+
``` ruby
|
668
|
+
def foo
|
669
|
+
struct node
|
670
|
+
int a, b
|
671
|
+
end
|
672
|
+
|
673
|
+
alias node_69 = node
|
674
|
+
|
675
|
+
node_69 n
|
676
|
+
n.a = 4
|
677
|
+
|
678
|
+
return n.a
|
679
|
+
end
|
680
|
+
```
|
681
|
+
|
682
|
+
# Conversions between Ruby and C data
|
683
|
+
|
684
|
+
Rubex will implicitly convert most primitive C types like `char`, `int` and `float` to their equivalent Ruby types and vice versa. However, types conversions for user defined types like structs and unions are not supported.
|
685
|
+
|
686
|
+
# C callbacks
|
687
|
+
|
688
|
+
Frequently, C libraries use C function pointers to pass functions to other functions as callbacks. Rubex supports this behaviour too.
|
689
|
+
|
690
|
+
A C function pointer for a function that returns an `int` and accepts two `int`s as arguments can be declared as:
|
691
|
+
```
|
692
|
+
int (*foo)(int, int)
|
693
|
+
```
|
694
|
+
|
695
|
+
You can also alias function pointers with `alias`:
|
696
|
+
```
|
697
|
+
alias foo = int (*)(int, int)
|
698
|
+
```
|
699
|
+
|
700
|
+
A sample program using C function pointers is demonstrated below:
|
701
|
+
``` ruby
|
702
|
+
cfunc int foo1(int a)
|
703
|
+
return a + 1
|
704
|
+
end
|
705
|
+
|
706
|
+
cfunc int baz1(int a, int b)
|
707
|
+
return a + b + 1
|
708
|
+
end
|
709
|
+
|
710
|
+
cfunc int bar(int (*func1)(int), int (*func2)(int, int), int a, int b)
|
711
|
+
return func1(a) + func2(a, b)
|
712
|
+
end
|
713
|
+
|
714
|
+
def foo
|
715
|
+
alias goswim = int (*ernub)(int, int)
|
716
|
+
int (*func_ptr1)(int)
|
717
|
+
goswim func_ptr2
|
718
|
+
int a = 1
|
719
|
+
int b = 1
|
720
|
+
|
721
|
+
func_ptr1 = foo1
|
722
|
+
func_ptr2 = baz1
|
723
|
+
|
724
|
+
return bar(func_ptr1, func_ptr2, a, b)
|
725
|
+
end
|
726
|
+
```
|
727
|
+
|
728
|
+
# Inline C
|
729
|
+
|
730
|
+
# Handling Strings
|
731
|
+
|
732
|
+
For purposes of optimization and compatibility with C, Rubex makes certain assumptions about strings. When you assign a Ruby object to a `char*`, Rubex will automatically pass a pointer to the C string contained inside the object to the `char*`, thereby increasing the efficiency of operations on the string. The resulting string is a regular `\0` delimited C string.
|
733
|
+
|
734
|
+
It should be noted that strings MUST use the double quotation syntax (`""`) and not single quotes in all cases. Single quote is reserved for use by C `char` data. For example, to assign a literal value to a C char, you can write `char a = 'b'`, to assign a literal C string to a `char*` array, use `char* a = "hello"`.
|
735
|
+
|
736
|
+
# Differences from C
|
737
|
+
|
738
|
+
* Rubex does not have dereferencing operator (`*`). Instead use `[0]` to access values pointed to by pointers.
|
739
|
+
* There is no `->` operator for accessing struct elements from a pointer to a struct. Use the `.` operator directly.
|
740
|
+
|
741
|
+
# Differences from Ruby
|
742
|
+
|
743
|
+
* The `return` statement must be specified to return a value. Unlike Ruby, Rubex does not return the last expression in a method.
|
744
|
+
* Specify arguments for method definitions inside round brackets.
|
745
|
+
* As of now, there is very limited support for metaprogramming.
|
746
|
+
* Blocks are not supported yet, though this will change by v0.2.
|
747
|
+
|
748
|
+
# Limitations
|
749
|
+
|
750
|
+
Most of the below limitations are temporary and will be taken care of in future versions:
|
751
|
+
|
752
|
+
* The `require` statement is currently not supported.
|
753
|
+
* Multi-file Rubex programs are not yet possible.
|