rubex 0.0.1 → 0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23e2d866a0c76bc981182646dbb7c459eddc15c5
|
4
|
+
data.tar.gz: f648dcf434fb3f57deba0f2950a47b15cef095e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 05ae4e8cadf51c767c005cb03f2e0bf3b164903ad39d371560b5388f99ca9036cf2aaf21d15e10d3f732ed43bc2b951ec87c0283917a3404704225304a7b52ef
|
7
|
+
data.tar.gz: 34236b528e312e5b0617cc0d5a6b3d76a29215fb244b11294850baa0c65189c090640bdbeeaa5491102ee050a689ef98fe73e873539d4493c1fdb254605296a1
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/CONTRIBUTING.md
CHANGED
@@ -0,0 +1,101 @@
|
|
1
|
+
# Table of Contents
|
2
|
+
|
3
|
+
<!-- MarkdownTOC autolink="true" bracket="round" -->
|
4
|
+
|
5
|
+
- [Setup](#setup)
|
6
|
+
- [Development notes](#development-notes)
|
7
|
+
- [Important data representations](#important-data-representations)
|
8
|
+
- [Internals](#internals)
|
9
|
+
- [Attach classes](#attach-classes)
|
10
|
+
- [Future work](#future-work)
|
11
|
+
|
12
|
+
<!-- /MarkdownTOC -->
|
13
|
+
|
14
|
+
# Setup
|
15
|
+
|
16
|
+
If you wish to contribute to Rubex, you can setup rubex on your system with the following commands:
|
17
|
+
```
|
18
|
+
bundle install
|
19
|
+
rake install
|
20
|
+
```
|
21
|
+
|
22
|
+
Then you can compile a Rubex file with this:
|
23
|
+
```
|
24
|
+
rubex file_name.rubex
|
25
|
+
```
|
26
|
+
|
27
|
+
# Development notes
|
28
|
+
|
29
|
+
Couple of conventions that I followed when writing the code:
|
30
|
+
* All the statements contain methods:
|
31
|
+
- analyse_statement(local_scope)
|
32
|
+
- generate_code(code, local_scope)
|
33
|
+
* Expressions contain methods:
|
34
|
+
- analyse_statement(local_scope)
|
35
|
+
- c_code(local_scope)
|
36
|
+
|
37
|
+
Sometimes it can so happen that an expression can consist simply of a single variable (like `if (a)`) or be a full expression (like `if (a == b)`). In the first case, the `a` is just read as an `IDENTIFIER` by the parser. In the second case, `a == b` is read as an `expr` and is stored in the AST as `Rubex::AST::Expression`.
|
38
|
+
|
39
|
+
Now you might be thinking why expressions should also have a `analyse_statement` method, that's just for maintaining uniformity between exprs and stmts (maybe that should be just `analyse`?).
|
40
|
+
|
41
|
+
When writing the `<=>` operator under Rubex::DataType classes, I have been forced to assume that `Int` is 32 bits long since I have not yet incorporated a way to figure out the number of bits used by a particular machine for representing an `int`. It will be changed later to make it machine-dependent.
|
42
|
+
|
43
|
+
# Important data representations
|
44
|
+
|
45
|
+
### Data format for holding parsed variables in declarations and arguments
|
46
|
+
|
47
|
+
This consists of a hash that looks like this:
|
48
|
+
```
|
49
|
+
{
|
50
|
+
dtype: ,
|
51
|
+
variables: [{}]
|
52
|
+
}
|
53
|
+
```
|
54
|
+
|
55
|
+
The `:variables` field might be `nil` in case of a function declaration in which case it is not necessary to specify the name of the variable.
|
56
|
+
|
57
|
+
The `:variables` key maps to a value that is an Array of Hashes that contains a single Hash:
|
58
|
+
```
|
59
|
+
{
|
60
|
+
ptr_level:,
|
61
|
+
value:,
|
62
|
+
ident: identity
|
63
|
+
}
|
64
|
+
```
|
65
|
+
|
66
|
+
`identity` can be a Hash in case of a function pointer argument, or a simple String in case its an identifier, or an `ElementRef` if specifying an array of elements.
|
67
|
+
|
68
|
+
If Hash, it will look like this:
|
69
|
+
```
|
70
|
+
{
|
71
|
+
name:,
|
72
|
+
return_ptr_level:,
|
73
|
+
arg_list:
|
74
|
+
}
|
75
|
+
```
|
76
|
+
|
77
|
+
# Internals
|
78
|
+
|
79
|
+
## Attach classes
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
# Future work
|
84
|
+
|
85
|
+
The following features in Rubex need to be implemented or can be made better:
|
86
|
+
|
87
|
+
* Ability to have Ruby-style method arguments without parenthesis.
|
88
|
+
* Multiline conditionals in the condition of if-elsif statements.
|
89
|
+
* Special treatment for VALUE C arrays by marking each element with GC.
|
90
|
+
* Checks for return statement. No return statement or wrong return type should raise error/warning.
|
91
|
+
* Compile time checking of types passed into functions and methods.
|
92
|
+
* Perform type checks in C before implicit conversion between Ruby and C types.
|
93
|
+
* Ability to define function at any location in the file witout caring for when it will be actually used.
|
94
|
+
* Error checking for dereferencing of void pointer.
|
95
|
+
* Prohibit structs (vector types) in if statements via compile time checks.
|
96
|
+
* Ability to provide default values to method arguments.
|
97
|
+
* Ability to write Rubex programs spanning multiple files.
|
98
|
+
* If a Ruby method and extern C function have the same name the program malfunctions. Either namespace C functions or disallow Ruby methods with same names as extern C functions.
|
99
|
+
* Clean up classes under Statement such that they don't have attr_reader's like `:name` and `:type` which are really not a part of statement attributes.
|
100
|
+
* Support for multi-file programs (maybe using `require`?).
|
101
|
+
* Refactor classes to cleaner support for compound types like CFunctions and simple struct pointers that have the base type nested just one level deep.
|
data/HISTORY.md
CHANGED
data/README.md
CHANGED
@@ -1,343 +1,158 @@
|
|
1
|
-
#
|
2
|
-
rubex - A Crystal-inspired language for writing Ruby extensions.
|
1
|
+
# Rubex
|
3
2
|
|
4
|
-
|
3
|
+
Rubex is a Ruby-like language for writing Ruby C extensions.
|
5
4
|
|
6
|
-
Rubex
|
7
|
-
``` ruby
|
8
|
-
class Fact
|
9
|
-
def factorial(i64 n)
|
10
|
-
return (n > 1 ? n*factorial(n-1) : 1)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
```
|
5
|
+
Rubex is a language that makes writing CRuby C extensions as simple as writing Ruby. It does this by providing a syntax that is the perfect blend of the elegance of Ruby and the power of C. Rubex compiles to C and implicitly interfaces with the Ruby VM in a manner that is completely transparent to the programmer.
|
14
6
|
|
15
|
-
|
7
|
+
Rubex keeps you happy even when writing C extensions.
|
16
8
|
|
17
|
-
|
18
|
-
``` c
|
19
|
-
#include <ruby.h>
|
9
|
+
# Status
|
20
10
|
|
21
|
-
|
22
|
-
calc_factorial(int n)
|
23
|
-
{
|
24
|
-
return (n > 1 ? n*calc_factorial(n-1) : 1);
|
25
|
-
}
|
26
|
-
|
27
|
-
static VALUE
|
28
|
-
cfactorial(VALUE self, VALUE n)
|
29
|
-
{
|
30
|
-
return INT2NUM(calc_factorial(NUM2INT(n)));
|
31
|
-
}
|
32
|
-
|
33
|
-
void Init_factorial()
|
34
|
-
{
|
35
|
-
VALUE cFact = rb_define_class("Fact", rb_cObject);
|
36
|
-
rb_define_method(cFact, "factorial", cfactorial, 1);
|
37
|
-
}
|
38
|
-
```
|
11
|
+
[![Gem Version](https://badge.fury.io/rb/rubex.svg)](https://badge.fury.io/rb/rubex)
|
39
12
|
|
40
|
-
|
13
|
+
# Table of Contents
|
41
14
|
|
42
|
-
|
15
|
+
<!-- MarkdownTOC autolink="true" bracket="round"-->
|
43
16
|
|
44
|
-
|
17
|
+
- [Quick Introduction](#quick-introduction)
|
18
|
+
- [Installation](#installation)
|
19
|
+
- [Usage](#usage)
|
20
|
+
- [Tutorial](#tutorial)
|
21
|
+
- [Syntax](#syntax)
|
22
|
+
- [Roadmap](#roadmap)
|
23
|
+
- [Acknowledgements](#acknowledgements)
|
45
24
|
|
46
|
-
|
25
|
+
<!-- /MarkdownTOC -->
|
47
26
|
|
48
|
-
|
27
|
+
# Quick Introduction
|
49
28
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|i32 |int32_t |32 bit integer |
|
68
|
-
|i64 |int64_t |64 bit integer |
|
69
|
-
|u8 |uint8_t |8 bit unsigned integer |
|
70
|
-
|u16 |uint16_t |16 bit unsigned integer |
|
71
|
-
|u32 |uint32_t |32 bit unsigned integer |
|
72
|
-
|u64 |uint64_t |64 bit unsigned integer |
|
73
|
-
|int |int | Integer >= 16 bits. |
|
74
|
-
|unsigned int |unsigned int| Unsigned integer >= 16 bits. |
|
75
|
-
|long int |long int| Integer >= 32 bits.|
|
76
|
-
|unsigned long int |unsigned long int|Unsigned Integer >= 32 bits. |
|
77
|
-
|long long int |long long int|Integer >= 64 bits.|
|
78
|
-
|unsigned long long int|unsigned long long int|Unsigned Integer >= 64 bits.|
|
79
|
-
|f32/float |float |32 bit floating point |
|
80
|
-
|f64/double |double |64 bit floating point |
|
81
|
-
|long f64/long double |long double|Long double >= 96 bits. |
|
82
|
-
|object |VALUE |Ruby object |
|
83
|
-
|
84
|
-
Variables with these data types can be declared by writing the data type keyword before the variable name, and will not follow the Crystal convention. So for example, to declare some integers and floats in rubex, you would do this:
|
85
|
-
```
|
86
|
-
i32 int_number
|
87
|
-
f64 float_number
|
88
|
-
i8 u, i = 33
|
89
|
-
```
|
90
|
-
I will use the `stdint.h` header file that provides support for declaring integer types of precise bit length.
|
91
|
-
|
92
|
-
#### Structs
|
93
|
-
|
94
|
-
You can define your own C structures using the `struct` keyword. It can contain any sort of data type inside it, just like structs in C. It can also contain references and pointers to itself. To create a struct called 'Node', you can use the following syntax:
|
95
|
-
```
|
96
|
-
struct Node do
|
97
|
-
int data
|
98
|
-
struct Node* next
|
29
|
+
Consider this Ruby code for computing a fibonnaci series and returning it in an Array:
|
30
|
+
``` ruby
|
31
|
+
class Fibonnaci
|
32
|
+
def compute(n)
|
33
|
+
i = 1, prev = 1, current = 1, temp
|
34
|
+
arr = []
|
35
|
+
|
36
|
+
while i < n do
|
37
|
+
temp = current
|
38
|
+
current = current + prev
|
39
|
+
prev = temp
|
40
|
+
arr.push(prev)
|
41
|
+
i += 1
|
42
|
+
end
|
43
|
+
|
44
|
+
arr
|
45
|
+
end
|
99
46
|
end
|
100
|
-
# C equivalent:
|
101
|
-
# struct Node {
|
102
|
-
# int data;
|
103
|
-
# struct Node* next;
|
104
|
-
# };
|
105
47
|
```
|
106
|
-
Varibles of type `Node` can be declared by using `struct Node`, like `struct Node foo`, or pointers with `Pointer(struct Node)` or `struct Node*`.
|
107
48
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
#### Unions
|
49
|
+
If you decide to port this to a C extension, the code will look like so:
|
50
|
+
``` c
|
51
|
+
#include <ruby.h>
|
52
|
+
#include <stdint.h>
|
113
53
|
|
114
|
-
|
115
|
-
|
116
|
-
union IntAndFloat do
|
117
|
-
i32 a
|
118
|
-
f32 b
|
119
|
-
end
|
120
|
-
```
|
54
|
+
void Init_a ();
|
55
|
+
static VALUE Fibonnaci_compute (int argc,VALUE* argv,VALUE self);
|
121
56
|
|
122
|
-
|
57
|
+
static VALUE Fibonnaci_compute (int argc,VALUE* argv,VALUE self)
|
58
|
+
{
|
59
|
+
int n,i,prev,current,temp;
|
60
|
+
VALUE arr;
|
61
|
+
|
62
|
+
if (argc < 1) {
|
63
|
+
rb_raise(rb_eArgError, "Need 1 args, not %d", argc);
|
64
|
+
}
|
65
|
+
|
66
|
+
n = NUM2INT(argv[0]);
|
67
|
+
i = 1;
|
68
|
+
prev = 1;
|
69
|
+
current = 1;
|
70
|
+
arr = rb_ary_new2(0);
|
71
|
+
|
72
|
+
while (i < n)
|
73
|
+
{
|
74
|
+
temp = current;
|
75
|
+
current = current + prev;
|
76
|
+
prev = temp;
|
77
|
+
rb_funcall(arr, rb_intern("push"), 1 ,INT2NUM(prev));
|
78
|
+
i = i + 1;
|
79
|
+
}
|
80
|
+
|
81
|
+
return arr;
|
82
|
+
}
|
123
83
|
|
124
|
-
|
84
|
+
void Init_a ()
|
85
|
+
{
|
86
|
+
VALUE cls_Fibonnaci;
|
125
87
|
|
126
|
-
|
127
|
-
```
|
128
|
-
enum Week do
|
129
|
-
monday
|
130
|
-
tuesday
|
131
|
-
wednesday
|
132
|
-
thursday
|
133
|
-
friday
|
134
|
-
saturday
|
135
|
-
sunday
|
136
|
-
end
|
137
|
-
```
|
88
|
+
cls_Fibonnaci = rb_define_class("Fibonnaci", rb_cObject);
|
138
89
|
|
139
|
-
|
140
|
-
|
141
|
-
enum SomeEnum do
|
142
|
-
one = 3,
|
143
|
-
two = 5,
|
144
|
-
end
|
90
|
+
rb_define_method(cls_Fibonnaci ,"compute", Fibonnaci_compute, -1);
|
91
|
+
}
|
145
92
|
```
|
146
93
|
|
147
|
-
|
148
|
-
|
149
|
-
There can basically be two types of functions that will need to be defined in rubex:
|
150
|
-
|
151
|
-
* _Pure Ruby methods_ that take a Ruby object as the first argument (the `VALUE` in C extensions, see the `cfactorial` method in the above example) and return a Ruby object.
|
152
|
-
* _C methods_ that purely take C data types as input and return a C data type (for example the `calc_factorial` function in the above example).
|
153
|
-
|
154
|
-
These two kinds of methods will have slightly different syntax when it comes to defining methods. Let me elaborate on them both:
|
155
|
-
|
156
|
-
**Pure Ruby Methods**
|
157
|
-
|
158
|
-
These will be defined just like normal Ruby methods, but will support typed formal parameters. Internally they will be translated to functions that accept `VALUE` as the first argument and return `VALUE`.
|
159
|
-
|
160
|
-
To define a method of this kind, the user can use syntax that looks like this:
|
94
|
+
However, if you decide to write a C extension using Rubex, the code will look like this!:
|
161
95
|
``` ruby
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
cdef f32 plus_one(f32 n)
|
178
|
-
n + 1
|
96
|
+
class Fibonnaci
|
97
|
+
def compute(int n)
|
98
|
+
int i = 1, prev = 1, current = 1, temp
|
99
|
+
array = []
|
100
|
+
|
101
|
+
while i < n do
|
102
|
+
temp = current
|
103
|
+
current = current + prev
|
104
|
+
prev = temp
|
105
|
+
array.push(prev)
|
106
|
+
i += 1
|
107
|
+
end
|
108
|
+
|
109
|
+
return array
|
110
|
+
end
|
179
111
|
end
|
180
112
|
```
|
181
|
-
In above example, the function `plus_one` takes a 32 bit floating point number `n` as an argument and returns a 32 bit floating point number after adding `1` to it. If a return type is not specified for a C function, it is assumed to be `VALUE` (Ruby object).
|
182
|
-
|
183
|
-
These functions will also be compiled such that the last line is actually the return statement, as is the case in Ruby. Pure C methods will only be callable from rubex and cannot be called by external interpreted Ruby code.
|
184
|
-
|
185
|
-
**Passing functions to other functions**
|
186
|
-
|
187
|
-
C functions (i.e. those defined with the `cdef` keyword) can be passed to other functions, just like C function pointers. If a C method accepts a function of a particular type signature it can be specified with the `Function()` keyword. The last argument to the `Function()` keyword is the return type of that function. The functionality can also be realized by specifying the function as `input parameters type -> return type`.
|
188
|
-
|
189
|
-
For example, a function that accepts two arguments, one of type `i32` and the other of type `f64` and returns a varible of type `i32` can be specified by either `Function(i32,f64,i32)` or `i32, f64 -> i32`.
|
190
|
-
|
191
|
-
#### Variables
|
192
|
-
|
193
|
-
Any variable can have the `extern` or `static` keyword associated with it in order to declare it so.
|
194
|
-
|
195
|
-
#### Pointers
|
196
|
-
|
197
|
-
Rubex will allow declaring pointers with either the `*` operator or the `Pointer()` keyword. For example, an `i32` pointer can be declared as either `*i32` or `Pointer(i32)`. Notice that the 'P' of `Pointer()` is in capitals. This notation will be followed for all compiler directives that take arguments.
|
198
|
-
|
199
|
-
#### Literals
|
200
|
-
|
201
|
-
Several literals will be available for creating many basic types in the language. I have listed some of the major ones below:
|
202
|
-
|
203
|
-
* **nil** - This (`nil`) represent's Ruby's NilClass object, and will be translated to the `Qnil` Ruby constant according to the C API.
|
204
|
-
* **Boolean values** - `true` and `false` are the literals for boolean values and will be translated to `Qtrue` and `Qfalse` by the compiler.
|
205
|
-
* **Integers** - Rubex will not make any assumptions about variables without an associated type assigned to integers. Thus, in order to create C integers, users must specify the data type of the variable. Therefore, `i = 3` will lead to `i` being compiled as a Ruby `Fixnum` object, and `i32 i = 3` will compile to a C 32 bit integer as `int32_t i = 3`.
|
206
|
-
* **Floats** - The assumptions made for integers will apply to floats as well.
|
207
|
-
* **Character** - Character `char` literals can be specified by enclosing the character in single quotes (`'` and `'`). These are equivalent to character literals in C.
|
208
|
-
* **Strings** - String literals are enclosed inside double quotes (`"` and `"`). When assigning a string literal to a variable, the variable must be of type `char*`. If the type is not specified, it will be treated as a Ruby string.
|
209
|
-
* **Symbol** - Rubex symbols use the exact same syntax as Ruby symbols, and will be directly translated to the relevant C API function for creating symbols.
|
210
|
-
|
211
|
-
#### Arrays
|
212
|
-
|
213
|
-
C arrays of a definite size (static arrays) can be specified using square brackets after the variable name. For example, to declare an array of 10, 16-bit integers, the syntax would be `i16 arr[10]`. Static arrays can also be declared with the `StaticArray()` keyword. Thus, to declare a C static array of `i8` of size `8`, you can either use`StaticArray(i8, 8)` or `i8[8]`.
|
214
|
-
|
215
|
-
If a Ruby array is specified using Ruby's Array literal syntax (`[]`), it will be directly translated into a Ruby Array object. For example the statement `a = []` will store a Ruby Array object in the variable 'a'.
|
216
|
-
|
217
|
-
Static arrays can be initialized with Ruby's array literal syntax. So for example, you can initiazile an array of 10 `i16` integers like this:
|
218
|
-
```
|
219
|
-
i16 a[10] = [1,2,3,4,5,6,7,8,9,10]
|
220
|
-
# C equivalent -> int16_t a[10] = {1,2,3,4,5,6,7,8,9,10};
|
221
|
-
```
|
222
113
|
|
223
|
-
|
114
|
+
Notice the only difference between the above Rubex code and Ruby is the specification of explicit `int` types for the variables. Above Rubex code will automatically compile into C code and will also implicitly interface with the Ruby VM without you having to remember any of the APIs.
|
224
115
|
|
225
|
-
Rubex
|
226
|
-
```
|
227
|
-
i32 i, s = 0, m = 10
|
228
|
-
for s < i <= m do
|
229
|
-
# code
|
230
|
-
end
|
231
|
-
```
|
116
|
+
Rubex also takes care of the initial setup and compilation of the C files, so all you need to do is execute a bunch of commands and your extension is up and running!
|
232
117
|
|
233
|
-
|
118
|
+
# Installation
|
234
119
|
|
235
|
-
|
120
|
+
The gem as of now has not reached v0.1. However, you can try it out with:
|
236
121
|
```
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
end
|
242
|
-
|
243
|
-
# C equivalent:
|
244
|
-
# int size = 10
|
245
|
-
# for(int i = 0; i < size; i++) {
|
246
|
-
# i16 x = a[i]
|
247
|
-
# // do something....
|
248
|
-
# }
|
122
|
+
git clone https://github.com/v0dro/rubex.git
|
123
|
+
cd rubex
|
124
|
+
bundle install
|
125
|
+
rake install
|
249
126
|
```
|
250
127
|
|
251
|
-
|
252
|
-
|
253
|
-
This is the most important functionality of rubex. A lot of it will be borrowed from Crystal since Crystal's API for creating C bindings is very well thought out and simple.
|
254
|
-
|
255
|
-
**lib**
|
128
|
+
# Usage
|
256
129
|
|
257
|
-
|
130
|
+
Installing the gem will also install the `rubex` binary. You can now write a Rubex file (with a `.rubex` file extension) and compile it into C code with:
|
258
131
|
```
|
259
|
-
|
260
|
-
lib LibPCRE
|
261
|
-
end
|
132
|
+
rubex file_name.rubex
|
262
133
|
```
|
263
134
|
|
264
|
-
|
265
|
-
|
266
|
-
The `Link` keyword inside the `@[...]` syntax of the magic comment will ensure that appropriate flags are passed to the compiler to find the external libraries. So for example, it the above case, the `Link("pcre")` directive will `-lpcre` to the linker.
|
267
|
-
|
268
|
-
If `Link(ldflags: "...")` is passed into the magic comment, those flags will be passed directly to the linker, without any modification, for example `Link(ldflags: "-lpcre")`. Enclosing those commands inside backticks will execute those commands, for example `Link(ldflags: "`pkg-config libpcre --libs`")`.
|
269
|
-
|
270
|
-
**require_header**
|
271
|
-
|
272
|
-
The `require_header` keyword will include C headers in the generated C code. For example `require_header 'math'` will put a statement `#include <math.h>`.
|
273
|
-
|
274
|
-
**fun**
|
275
|
-
|
276
|
-
A `fun` declaration will bind a C function.
|
135
|
+
This will produce the translated C code and an `extconf.rb` file inside a directory called `file_name`. CD into the directory, and run the `extconf.rb` file with:
|
277
136
|
```
|
278
|
-
|
279
|
-
|
280
|
-
class Maths
|
281
|
-
def cos(f32 v)
|
282
|
-
CMath.cos(v)
|
283
|
-
end
|
284
|
-
end
|
285
|
-
|
286
|
-
lib CMath
|
287
|
-
fun f32 cos(f32 value)
|
288
|
-
end
|
137
|
+
ruby extconf.rb
|
289
138
|
```
|
290
|
-
The user can the call the `cos` function from Ruby with `Maths.new.cos(0.7)`. Calls to `fun` must be inside a `lib` block. This facilitates easy linking and namespacing of C bindings.
|
291
139
|
|
292
|
-
|
293
|
-
```
|
294
|
-
# In rubex
|
295
|
-
lib C
|
296
|
-
fun i32 getch
|
297
|
-
end
|
140
|
+
This will produce a `Makefile`. Run `make` to compile the generated C file and generate a `.so` shared object file that can be used in any Ruby script.
|
298
141
|
|
299
|
-
#
|
300
|
-
C.getch
|
301
|
-
```
|
302
|
-
If the return type is `void` you can omit it:
|
303
|
-
```
|
304
|
-
# In Rubex
|
305
|
-
lib C
|
306
|
-
fun srand(u32 seed)
|
307
|
-
end
|
142
|
+
# Tutorial
|
308
143
|
|
309
|
-
|
310
|
-
C.srand(1)
|
311
|
-
```
|
144
|
+
Give yourself 5 min and go through the [TUTORIAL](TUTORIAL.md). Convert a part of your C extension to Rubex and see the jump in cleanliness and productivity for yourself.
|
312
145
|
|
313
|
-
|
314
|
-
```
|
315
|
-
# In Rubex
|
316
|
-
lib X
|
317
|
-
fun i32 variadic(i32 value, ...)
|
318
|
-
end
|
146
|
+
# Syntax
|
319
147
|
|
320
|
-
|
321
|
-
X.variadic(1, 2, 3, 4)
|
322
|
-
```
|
148
|
+
Read the full Rubex reference in [REFERENCE](REFERENCE.md).
|
323
149
|
|
324
|
-
|
325
|
-
```
|
326
|
-
lib C
|
327
|
-
fun cosine = f32 cos(f32 value)
|
328
|
-
end
|
329
|
-
```
|
330
|
-
Or say the function starts with a capital letter (which is a constant in Ruby and is confusing to use as a method name) or contains an invalid character like `.`, in which case you can wrap it in a string:
|
331
|
-
```
|
332
|
-
lib LibSDL
|
333
|
-
fun init = u32 SDL_Init(u32 flags)
|
334
|
-
end
|
150
|
+
# Roadmap
|
335
151
|
|
336
|
-
|
337
|
-
fun ceil_f32 = f32 "llvm.ceil.f32"(f32 value)
|
338
|
-
end
|
339
|
-
```
|
152
|
+
See the [CONTRIBUTING](CONTRIBUTING.md) and the GitHub issue tracker for future features.
|
340
153
|
|
341
|
-
|
154
|
+
# Acknowledgements
|
342
155
|
|
343
|
-
|
156
|
+
* The Ruby Association (Japan) for providing the initial funding for this project through the Ruby Association Grant 2016.
|
157
|
+
* Koichi Sasada (@ko1) and Kenta Murata (@mrkn) for their support and mentorship throughout this project.
|
158
|
+
* Fukuoka Ruby Award 2017.
|