ruby-ll 1.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
data/doc/changelog.md ADDED
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ This document contains details of the various releases and their release dates.
4
+ Dates are in the format `yyyy-mm-dd`.
5
+
6
+ ## 1.0.0 - 2015-02-13
7
+
8
+ The first public release of ruby-ll!
@@ -0,0 +1,77 @@
1
+ body
2
+ {
3
+ font-size: 14px;
4
+ line-height: 1.6;
5
+ margin: 0 auto;
6
+ max-width: 960px;
7
+ }
8
+
9
+ p code, dd code, li code
10
+ {
11
+ background: #f9f2f4;
12
+ color: #c7254e;
13
+ border-radius: 4px;
14
+ padding: 2px 4px;
15
+ }
16
+
17
+ pre.code
18
+ {
19
+ font-size: 13px;
20
+ line-height: 1.4;
21
+ overflow: auto;
22
+ }
23
+
24
+ blockquote
25
+ {
26
+ border-left: 5px solid #eee;
27
+ margin: 0px;
28
+ padding-left: 15px;
29
+ }
30
+
31
+ /**
32
+ * YARD uses generic table styles, using a special class means those tables
33
+ * don't get messed up.
34
+ */
35
+ .table
36
+ {
37
+ border: 1px solid #ccc;
38
+ border-right: none;
39
+ border-collapse: separate;
40
+ border-spacing: 0;
41
+ text-align: left;
42
+ }
43
+
44
+ .table.full
45
+ {
46
+ width: 100%;
47
+ }
48
+
49
+ .table .field_name
50
+ {
51
+ min-width: 160px;
52
+ }
53
+
54
+ .table thead tr th.no_sort:first-child
55
+ {
56
+ width: 25px;
57
+ }
58
+
59
+ .table thead tr th, .table tbody tr td
60
+ {
61
+ border-bottom: 1px solid #ccc;
62
+ border-right: 1px solid #ccc;
63
+ min-width: 20px;
64
+ padding: 8px 5px;
65
+ text-align: left;
66
+ vertical-align: top;
67
+ }
68
+
69
+ .table tbody tr:last-child td
70
+ {
71
+ border-bottom: none;
72
+ }
73
+
74
+ .table tr:nth-child(odd) td
75
+ {
76
+ background: #f9f9f9;
77
+ }
data/ext/c/driver.c ADDED
@@ -0,0 +1,258 @@
1
+ #include "driver.h"
2
+
3
+ #define T_EOF -1
4
+ #define T_RULE 0
5
+ #define T_TERMINAL 1
6
+ #define T_EPSILON 2
7
+ #define T_ACTION 3
8
+
9
+ ID id_config_const;
10
+ ID id_each_token;
11
+ ID id_send;
12
+
13
+ ID id_stack_input_error;
14
+ ID id_unexpected_input_error;
15
+ ID id_invalid_terminal_error;
16
+
17
+ /**
18
+ * Releases the memory of the driver's internal state and associated objects.
19
+ * This function is called automatically when a Driver instance is garbage
20
+ * collected.
21
+ */
22
+ void ll_driver_free(DriverState *state)
23
+ {
24
+ kv_destroy(state->stack);
25
+ kv_destroy(state->value_stack);
26
+
27
+ free(state);
28
+ }
29
+
30
+ /**
31
+ * Marks the objects stored in the driver's internal state, preventing them from
32
+ * being garbage collected until the next GC run.
33
+ */
34
+ void ll_driver_mark(DriverState *state)
35
+ {
36
+ size_t index;
37
+
38
+ FOR(index, kv_size(state->value_stack))
39
+ {
40
+ rb_gc_mark(kv_A(state->value_stack, index));
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Allocates a new instance of the Driver class and prepares its internal state.
46
+ */
47
+ VALUE ll_driver_allocate(VALUE klass)
48
+ {
49
+ DriverState *state = ALLOC(DriverState);
50
+ VALUE config = rb_const_get(klass, id_config_const);
51
+
52
+ Data_Get_Struct(config, DriverConfig, state->config);
53
+
54
+ kv_init(state->stack);
55
+ kv_init(state->value_stack);
56
+
57
+ return Data_Wrap_Struct(klass, ll_driver_mark, ll_driver_free, state);
58
+ }
59
+
60
+ /**
61
+ * Callback function for iterating over every input token and actually parsing
62
+ * said input.
63
+ *
64
+ * @param token An Array containing the token type as a Symbol and its value.
65
+ * @param self The Driver instance currently in use.
66
+ */
67
+ VALUE ll_driver_each_token(VALUE token, VALUE self)
68
+ {
69
+ VALUE method;
70
+ VALUE action_args;
71
+ VALUE action_retval;
72
+ long num_args;
73
+ long args_i;
74
+
75
+ long token_id;
76
+
77
+ long rule_i;
78
+ long production_i;
79
+
80
+ long stack_type;
81
+ long stack_value;
82
+
83
+ DriverState *state;
84
+
85
+ VALUE type = rb_ary_entry(token, 0);
86
+ VALUE value = rb_ary_entry(token, 1);
87
+
88
+ Data_Get_Struct(self, DriverState, state);
89
+
90
+ while ( 1 )
91
+ {
92
+ if ( kv_size(state->stack) == 0 )
93
+ {
94
+ rb_funcall(self, id_unexpected_input_error, 1, token);
95
+ }
96
+
97
+ stack_value = kv_pop(state->stack);
98
+ stack_type = kv_pop(state->stack);
99
+ token_id = 0;
100
+
101
+ if ( TYPE(type) == T_SYMBOL )
102
+ {
103
+ khint64_t found = kh_get(int64_map, state->config->terminals, type);
104
+
105
+ if ( found != kh_end(state->config->terminals) )
106
+ {
107
+ token_id = kh_value(state->config->terminals, found);
108
+ }
109
+ }
110
+
111
+ /* Rule */
112
+ if ( stack_type == T_RULE )
113
+ {
114
+ production_i = state->config->table[stack_value][token_id];
115
+
116
+ if ( production_i == T_EOF )
117
+ {
118
+ rb_funcall(
119
+ self,
120
+ id_stack_input_error,
121
+ 2,
122
+ INT2NUM(stack_value),
123
+ token
124
+ );
125
+ }
126
+ else
127
+ {
128
+ FOR(rule_i, state->config->rule_lengths[production_i])
129
+ {
130
+ kv_push(
131
+ long,
132
+ state->stack,
133
+ state->config->rules[production_i][rule_i]
134
+ );
135
+ }
136
+ }
137
+ }
138
+ /* Terminal */
139
+ else if ( stack_type == T_TERMINAL )
140
+ {
141
+ if ( stack_value == token_id )
142
+ {
143
+ kv_push(VALUE, state->value_stack, value);
144
+
145
+ RB_GC_GUARD(value);
146
+
147
+ break;
148
+ }
149
+ else
150
+ {
151
+ rb_funcall(
152
+ self,
153
+ id_invalid_terminal_error,
154
+ 2,
155
+ INT2NUM(token_id),
156
+ INT2NUM(stack_value)
157
+ );
158
+ }
159
+ }
160
+ /* Action */
161
+ else if ( stack_type == T_ACTION )
162
+ {
163
+ method = state->config->action_names[stack_value];
164
+ num_args = state->config->action_arg_amounts[stack_value];
165
+ action_args = rb_ary_new2(num_args);
166
+ args_i = num_args;
167
+
168
+ if ( args_i > (long) kv_size(state->value_stack) )
169
+ {
170
+ args_i = (long) kv_size(state->value_stack);
171
+ }
172
+
173
+ while ( args_i-- )
174
+ {
175
+ if ( kv_size(state->value_stack) > 0 )
176
+ {
177
+ rb_ary_store(
178
+ action_args,
179
+ args_i,
180
+ kv_pop(state->value_stack)
181
+ );
182
+ }
183
+ }
184
+
185
+ action_retval = rb_funcall(self, id_send, 2, method, action_args);
186
+
187
+ kv_push(VALUE, state->value_stack, action_retval);
188
+
189
+ RB_GC_GUARD(action_retval);
190
+ }
191
+ /* EOF */
192
+ else if ( stack_type == T_EOF )
193
+ {
194
+ break;
195
+ }
196
+ }
197
+
198
+ return Qnil;
199
+ }
200
+
201
+ /**
202
+ * Starts the parser.
203
+ *
204
+ * @param self The Driver instance the "parse" method was called on.
205
+ */
206
+ VALUE ll_driver_parse(VALUE self)
207
+ {
208
+ long index;
209
+
210
+ DriverState *state;
211
+
212
+ Data_Get_Struct(self, DriverState, state);
213
+
214
+ /* EOF rule */
215
+ kv_push(long, state->stack, T_EOF);
216
+ kv_push(long, state->stack, T_EOF);
217
+
218
+ /* Start rule */
219
+ FOR(index, state->config->rule_lengths[0])
220
+ {
221
+ kv_push(long, state->stack, state->config->rules[0][index]);
222
+ }
223
+
224
+ rb_block_call(
225
+ self,
226
+ id_each_token,
227
+ 0,
228
+ NULL,
229
+ RUBY_METHOD_FUNC(ll_driver_each_token),
230
+ self
231
+ );
232
+
233
+ if ( kv_size(state->value_stack) == 0 )
234
+ {
235
+ return Qnil;
236
+ }
237
+ else
238
+ {
239
+ return kv_pop(state->value_stack);
240
+ }
241
+ }
242
+
243
+ void Init_ll_driver()
244
+ {
245
+ VALUE mLL = rb_const_get(rb_cObject, rb_intern("LL"));
246
+ VALUE cDriver = rb_const_get(mLL, rb_intern("Driver"));
247
+
248
+ rb_define_method(cDriver, "parse", ll_driver_parse, 0);
249
+
250
+ rb_define_alloc_func(cDriver, ll_driver_allocate);
251
+
252
+ id_send = rb_intern("send");
253
+ id_config_const = rb_intern("CONFIG");
254
+ id_each_token = rb_intern("each_token");
255
+ id_stack_input_error = rb_intern("stack_input_error");
256
+ id_invalid_terminal_error = rb_intern("invalid_terminal_error");
257
+ id_unexpected_input_error = rb_intern("unexpected_input_error");
258
+ }
data/ext/c/driver.h ADDED
@@ -0,0 +1,28 @@
1
+ #ifndef LIBLL_DRIVER_H
2
+ #define LIBLL_DRIVER_H
3
+
4
+ #include <ruby.h>
5
+ #include "driver_config.h"
6
+ #include "macros.h"
7
+ #include "kvec.h"
8
+
9
+ /**
10
+ * Struct containing the internal state of a Driver instance. This struct is
11
+ * sadly required as rb_block_call() doesn't take any extra, custom arguments
12
+ * (other than a single VALUE). This means we have to use
13
+ * Data_Wrap_Struct/Data_Get_Struct instead :<
14
+ */
15
+ typedef struct
16
+ {
17
+ DriverConfig *config;
18
+
19
+ /* Stack for storing the rules/actions/etc to process */
20
+ kvec_t(long) stack;
21
+
22
+ /* Stack for action return values */
23
+ kvec_t(VALUE) value_stack;
24
+ } DriverState;
25
+
26
+ extern void Init_ll_driver();
27
+
28
+ #endif
@@ -0,0 +1,209 @@
1
+ #include "driver_config.h"
2
+
3
+ /**
4
+ * Releases memory of the DriverConfig struct and its members.
5
+ */
6
+ void ll_driver_config_free(DriverConfig *config)
7
+ {
8
+ long rindex;
9
+
10
+ FOR(rindex, config->rules_count)
11
+ {
12
+ free(config->rules[rindex]);
13
+ }
14
+
15
+ FOR(rindex, config->table_count)
16
+ {
17
+ free(config->table[rindex]);
18
+ }
19
+
20
+ free(config->rules);
21
+ free(config->rule_lengths);
22
+ free(config->table);
23
+ free(config->action_names);
24
+ free(config->action_arg_amounts);
25
+
26
+ kh_destroy(int64_map, config->terminals);
27
+
28
+ free(config);
29
+ }
30
+
31
+ /**
32
+ * Marks various members of the DriverConfig to ensure they are not garbage
33
+ * collected until at least the next GC run.
34
+ */
35
+ void ll_driver_config_mark(DriverConfig *config)
36
+ {
37
+ long index;
38
+
39
+ FOR(index, config->actions_count)
40
+ {
41
+ rb_gc_mark(config->action_names[index]);
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Allocates a new DriverConfig.
47
+ */
48
+ VALUE ll_driver_config_allocate(VALUE klass)
49
+ {
50
+ DriverConfig *config = ALLOC(DriverConfig);
51
+
52
+ return Data_Wrap_Struct(
53
+ klass,
54
+ ll_driver_config_mark,
55
+ ll_driver_config_free, config
56
+ );
57
+ }
58
+
59
+ /**
60
+ * Stores the terminals of the parser in the DriverConfig struct.
61
+ *
62
+ * @param self The current DriverConfig instance.
63
+ * @param array The terminals to store in the struct.
64
+ */
65
+ VALUE ll_driver_config_set_terminals(VALUE self, VALUE array)
66
+ {
67
+ long index;
68
+
69
+ int key_ret;
70
+ khint64_t key;
71
+ VALUE token;
72
+ DriverConfig *config;
73
+ long count = RARRAY_LEN(array);
74
+
75
+ Data_Get_Struct(self, DriverConfig, config);
76
+
77
+ config->terminals = kh_init(int64_map);
78
+
79
+ FOR(index, count)
80
+ {
81
+ token = rb_ary_entry(array, index);
82
+ key = kh_put(int64_map, config->terminals, token, &key_ret);
83
+
84
+ kh_value(config->terminals, key) = index;
85
+ }
86
+
87
+ return Qnil;
88
+ }
89
+
90
+ /**
91
+ * Stores the rules in the DriverConfig struct.
92
+ *
93
+ * @param self The DriverConfig instance.
94
+ * @param array The rules to store.
95
+ */
96
+ VALUE ll_driver_config_set_rules(VALUE self, VALUE array)
97
+ {
98
+ long rindex;
99
+ long cindex;
100
+ long col_count;
101
+ DriverConfig *config;
102
+ VALUE row;
103
+ long row_count = RARRAY_LEN(array);
104
+
105
+ Data_Get_Struct(self, DriverConfig, config);
106
+
107
+ config->rules = ALLOC_N(long*, row_count);
108
+ config->rule_lengths = ALLOC_N(long, row_count);
109
+
110
+ FOR(rindex, row_count)
111
+ {
112
+ row = rb_ary_entry(array, rindex);
113
+ col_count = RARRAY_LEN(row);
114
+
115
+ config->rules[rindex] = ALLOC_N(long, col_count);
116
+
117
+ FOR(cindex, col_count)
118
+ {
119
+ config->rules[rindex][cindex] = NUM2INT(rb_ary_entry(row, cindex));
120
+ }
121
+
122
+ config->rule_lengths[rindex] = col_count;
123
+ }
124
+
125
+ config->rules_count = row_count;
126
+
127
+ return Qnil;
128
+ }
129
+
130
+ /**
131
+ * Stores the lookup table in the DriverConfig struct.
132
+ *
133
+ * @param self The DriverConfig instance.
134
+ * @param array The lookup table.
135
+ */
136
+ VALUE ll_driver_config_set_table(VALUE self, VALUE array)
137
+ {
138
+ long rindex;
139
+ long cindex;
140
+ long col_count;
141
+ VALUE row;
142
+ DriverConfig *config;
143
+ long row_count = RARRAY_LEN(array);
144
+
145
+ Data_Get_Struct(self, DriverConfig, config);
146
+
147
+ config->table = ALLOC_N(long*, row_count);
148
+
149
+ FOR(rindex, row_count)
150
+ {
151
+ row = rb_ary_entry(array, rindex);
152
+ col_count = RARRAY_LEN(row);
153
+
154
+ config->table[rindex] = ALLOC_N(long, col_count);
155
+
156
+ FOR(cindex, col_count)
157
+ {
158
+ config->table[rindex][cindex] = NUM2INT(rb_ary_entry(row, cindex));
159
+ }
160
+ }
161
+
162
+ config->table_count = row_count;
163
+
164
+ return Qnil;
165
+ }
166
+
167
+ /**
168
+ * Stores the callback actions in the DriverConfig struct.
169
+ *
170
+ * @param self The DriverConfig instance.
171
+ * @param array The callback actions and their arities.
172
+ */
173
+ VALUE ll_driver_config_set_actions(VALUE self, VALUE array)
174
+ {
175
+ long rindex;
176
+ VALUE row;
177
+ DriverConfig *config;
178
+ long row_count = RARRAY_LEN(array);
179
+
180
+ Data_Get_Struct(self, DriverConfig, config);
181
+
182
+ config->action_names = ALLOC_N(ID, row_count);
183
+ config->action_arg_amounts = ALLOC_N(long, row_count);
184
+
185
+ FOR(rindex, row_count)
186
+ {
187
+ row = rb_ary_entry(array, rindex);
188
+
189
+ config->action_names[rindex] = rb_ary_entry(row, 0);
190
+ config->action_arg_amounts[rindex] = NUM2INT(rb_ary_entry(row, 1));
191
+ }
192
+
193
+ config->actions_count = row_count;
194
+
195
+ return Qnil;
196
+ }
197
+
198
+ void Init_ll_driver_config()
199
+ {
200
+ VALUE mLL = rb_const_get(rb_cObject, rb_intern("LL"));
201
+ VALUE klass = rb_const_get(mLL, rb_intern("DriverConfig"));
202
+
203
+ rb_define_alloc_func(klass, ll_driver_config_allocate);
204
+
205
+ rb_define_method(klass, "terminals_native=", ll_driver_config_set_terminals, 1);
206
+ rb_define_method(klass, "rules_native=", ll_driver_config_set_rules, 1);
207
+ rb_define_method(klass, "table_native=", ll_driver_config_set_table, 1);
208
+ rb_define_method(klass, "actions_native=", ll_driver_config_set_actions, 1);
209
+ }