opal 0.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.
data/runtime/opal.js ADDED
@@ -0,0 +1,200 @@
1
+ /*
2
+ * opal.js
3
+ * opal
4
+ *
5
+ * Created by Adam Beynon.
6
+ * Copyright 2010 Adam Beynon.
7
+ *
8
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ * of this software and associated documentation files (the "Software"), to deal
10
+ * in the Software without restriction, including without limitation the rights
11
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ * copies of the Software, and to permit persons to whom the Software is
13
+ * furnished to do so, subject to the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be included in
16
+ * all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ * THE SOFTWARE.
25
+ */
26
+
27
+
28
+ // temp..
29
+ var nil;
30
+
31
+ /**
32
+ nodes etc
33
+ */
34
+
35
+ var NOEX_PUBLIC = 0,
36
+ NOEX_NOSUPER = 1,
37
+ NOEX_PRIVATE = 2,
38
+ NOEX_PROTECTED = 4,
39
+ NOEX_MASK = 6,
40
+ NOEX_BASIC = 8;
41
+
42
+
43
+ function require() {
44
+
45
+ };
46
+
47
+ // Boolean test. false if null, undefined, nil, or false
48
+ function RTEST(val) {
49
+ return (val != null && val != undefined && val != nil && val != false) ? true : false;
50
+ };
51
+
52
+ /**
53
+ Performs an 'or op' with lhs and rhs
54
+ */
55
+ function ORTEST(lhs, rhs) {
56
+ if (lhs == null || lhs == undefined) lhs = nil;
57
+ if (rhs == null || rhs == undefined) rhs = nil;
58
+
59
+ if (lhs == nil || lhs == false) {
60
+ return rhs;
61
+ }
62
+ return lhs;
63
+ };
64
+
65
+ /**
66
+ Performs an 'and op' with lhs and rhs
67
+ */
68
+ function ANDTEST(lhs, rhs) {
69
+ if (lhs == null || lhs == undefined) lhs = nil;
70
+ if (rhs == null || rhs == undefined) rhs = nil;
71
+
72
+ if (lhs == nil || lhs == false) {
73
+ return nil;
74
+ }
75
+ return rhs;
76
+ };
77
+
78
+ function NOTTEST(expr) {
79
+ if (expr == null || expr == undefined || expr == nil || expr == false) return true;
80
+ return false;
81
+ };
82
+
83
+ /**
84
+ Fix for browsers not having console
85
+ */
86
+ // if (typeof console === 'undefined') {
87
+ // var console = console || window.console || { };
88
+ // console.log = console.info = console.warn = console.error = function() { };
89
+ // }
90
+
91
+ function RObject(klass, type) {
92
+ this.klass = klass;
93
+ this.flags = type;
94
+ this.iv_tbl = { };
95
+ return this;
96
+ }
97
+
98
+ function RClass(klass, super_klass) {
99
+ this.klass = klass ;
100
+ this.sup = super_klass ;
101
+ this.flags = T_CLASS ;
102
+ this.m_tbl = { };
103
+ this.iv_tbl = { };
104
+ return this;
105
+ };
106
+
107
+ function RHash() {
108
+ this.klass = nil;
109
+ this.flags = nil;
110
+ this.ifnone = nil;
111
+ // ordered keys
112
+ this.keys = [];
113
+ // keys.to_s => values
114
+ this.dict = { };
115
+ return this;
116
+ }
117
+
118
+ // Types
119
+ var T_CLASS = 1,
120
+ T_MODULE = 2,
121
+ T_OBJECT = 4,
122
+ T_BOOLEAN = 8,
123
+ T_STRING = 16,
124
+ T_ARRAY = 32,
125
+ T_NUMBER = 64,
126
+ T_PROC = 128,
127
+ T_SYMBOL = 256,
128
+ T_HASH = 512,
129
+ T_ICLASS = 1024;
130
+
131
+ // Flags
132
+ var FL_SINGLETON = 2056;
133
+
134
+ function FL_TEST(x, f) {
135
+ return x.flags & f;
136
+ }
137
+
138
+ function FL_SET(x, f) {
139
+ x.flags |= f;
140
+ }
141
+
142
+ function FL_UNSET(x, f) {
143
+ x.flags &= (~f);
144
+ }
145
+
146
+ rb_class_tbl = { } ; // all classes are stored here
147
+ rb_global_tbl = { } ; // globals are stored here
148
+
149
+ function rb_gvar_get(id) {
150
+
151
+ };
152
+
153
+ function rb_gvar_set(id, val) {
154
+
155
+ };
156
+
157
+
158
+ function boot_defclass(id, super_class) {
159
+ var o = rb_class_boot(super_class);
160
+ rb_name_class(o, id);
161
+ rb_class_tbl[id] = o;
162
+ rb_const_set((rb_cObject ? rb_cObject : o), id, o);
163
+ return o;
164
+ };
165
+
166
+ boot_defmetametaclass = function(klass, metametaclass) {
167
+ klass.klass.klass = metametaclass;
168
+ };
169
+
170
+ obj_alloc = function(klass) {
171
+ // console.log('in base.js, obj_alloc ' + arguments.length);
172
+ // var obj = klass.$('allocate', []);
173
+ var obj = VN$(klass, 'allocate');
174
+ return obj;
175
+ };
176
+
177
+ class_allocate_instance = function() {
178
+ // console.log('doing VN.class_allocate_instance');
179
+ var obj = new RObject(this, T_OBJECT) ;
180
+ return obj;
181
+ };
182
+
183
+ obj_dummy = function() {
184
+ return nil ;
185
+ };
186
+
187
+ equal = function(obj) {
188
+ if (obj == this) return true ;
189
+ var result = this.$funcall('==', [obj]);
190
+ if (result) return true ;
191
+ return false ;
192
+ };
193
+
194
+ eql = function(obj) {
195
+ return this.$funcall('==', [obj]);
196
+ };
197
+
198
+ obj_equal = function(obj) {
199
+ return (obj == this) ? true : false ;
200
+ };
data/runtime/parse.js ADDED
@@ -0,0 +1,2218 @@
1
+ /*
2
+ * parse.js
3
+ * opal
4
+ *
5
+ * Created by Adam Beynon.
6
+ * Copyright 2010 Adam Beynon.
7
+ *
8
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ * of this software and associated documentation files (the "Software"), to deal
10
+ * in the Software without restriction, including without limitation the rights
11
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ * copies of the Software, and to permit persons to whom the Software is
13
+ * furnished to do so, subject to the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be included in
16
+ * all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ * THE SOFTWARE.
25
+ */
26
+
27
+
28
+ // lex states
29
+ var EXPR_BEG = 0, EXPR_END = 1, EXPR_ENDARG = 2, EXPR_ARG = 3,
30
+ EXPR_CMDARG = 4, EXPR_MID = 5, EXPR_FNAME = 6, EXPR_DOT = 7,
31
+ EXPR_CLASS = 8, EXPR_VALUE = 9;
32
+
33
+ // keywords
34
+ var kCLASS = 0, kMODULE = 1, kDEF = 2, kUNDEF = 3,
35
+ kBEGIN = 4, kRESCUE = 5, kENSURE = 6, kEND = 7,
36
+ kIF = 8, kUNLESS = 9, kTHEN = 10, kELSIF = 11,
37
+ kELSE = 12, kCASE = 13, kWHEN = 14, kWHILE = 15,
38
+ kUNTIL = 16, kFOR = 17, kBREAK = 18, kNEXT = 19,
39
+ kREDO = 20, kELSIF = 21, kELSE = 22, kCASE = 23,
40
+ kWHEN = 24, kWHILE = 25, kUNTIL = 26, kFOR = 27,
41
+ kBREAK = 28, kNEXT = 29, kREDO = 30, kRETRY = 31,
42
+ kIN = 32, kDO_COND = 33, kDO_BLOCK = 34, kDO_LAMBDA = 35,
43
+ kRETURN = 36, kYIELD = 37, kSUPER = 38, kSELF = 39,
44
+ kNIL = 40, kTRUE = 41, kFALSE = 42, kAND = 43,
45
+ kOR = 44, kNOT = 45, kIF_MOD = 46, kUNLESS_MOD= 47,
46
+ kWHILE_MOD = 48, kUNTIL_MOD = 49, kRESCUE_MOD = 50, kALIAS = 51,
47
+ kDEFINED = 52, klBEGIN = 53, klEND = 54, k__LINE__ = 55,
48
+ k__FILE__ = 56, kDO = 57, kDEFined = 58,
49
+ // tokens
50
+ tIDENTIFIER = 59, tFID = 60, tGVAR = 61, tIVAR = 62,
51
+ tCONSTANT = 63, tCVAR = 64, tLABEL = 65, tINTEGER = 66,
52
+ tFLOAT = 67, tSTR_CONTENT= 68, tCHAR = 69, tNTH_REF = 70,
53
+ tBACK_REF = 71, tREGEXP_END = 72, tUPLUS = 73, tUMINUS = 74,
54
+ tPOW = 75, tCMP = 76, tEQ = 77, tEQQ = 78,
55
+ tNEQ = 79, tGEQ = 80, tLEQ = 81, tANDOP = 82,
56
+ tOROP = 83, tMATCH = 84, tNMATCH = 85, tDOT2 = 86,
57
+ tDOT3 = 87, tAREF = 88, tASET = 89, tLSHFT = 90,
58
+ tRSHFT = 91, tCOLON2 = 92, tCOLON3 = 93, tOP_ASGN = 94,
59
+ tASSOC = 95, tLPAREN = 96, tLPAREN_ARG = 97, tRPAREN = 98,
60
+ tLBRACK = 99, tLBRACE = 100, tLBRACE_ARG = 101, tSTAR = 102,
61
+ tAMPER = 103, tLAMBDA = 104, tSYMBEG = 105, tSTRING_BEG= 106,
62
+ tXSTRING_BEG= 107, tREGEXP_BEG = 108, tWORDS_BEG = 109, tQWORDS_BEG= 110,
63
+ tSTRING_DBEG= 111, tSTRING_DVAR= 112, tSTRING_END = 113, tLAMBEG = 114,
64
+ tUMINUS_NUM = 115, tSTRING = 116, tXSTRING_END= 117,
65
+
66
+ tPLUS = 118, tMINUS = 119, tNL = 120, tSEMI = 121;
67
+
68
+ // special tokens (used for generator)
69
+ var tCALL = 150, tMLHS = 151, tOPT_PLUS = 152, tOPT_MINUS = 153,
70
+ tOPT_MULT = 154, tOPT_DIV = 155;
71
+
72
+ /**
73
+ Parse the given ruby code, str, with the given filename. This allows us to
74
+ dynamically set the filename, for example, with eval()'d code. This returns
75
+ an Instruction sequence, with all of its sub sequences, opcodes etc.
76
+ */
77
+ var vn_parser = function(filename, str) {
78
+
79
+ // current lex state
80
+ var lex_state = EXPR_BEG;
81
+ // last lexerparser state
82
+ var last_state;
83
+ // the scanner
84
+ var scanner; //= new vn_ruby_string_scanner(str);
85
+ // current token
86
+ var token = { type: false, value: false };
87
+ // last token
88
+ var last_token = { type: false, value: false };
89
+ //
90
+ var sym_tbl = { };
91
+ // eval string..
92
+ var eval_arr = [];
93
+ // valid types of stmt that are valid as the first cmd args (helps us identify if the
94
+ // next statemebnt should be appeneded to the current identifer as a cmd arg )
95
+ var valid_cmd_args = [tIDENTIFIER, tINTEGER, tCONSTANT, tSTRING_BEG, kDO, '{', tSYMBEG];
96
+ // start of command (not stmt), when on new line etc
97
+ var cmd_start = false;
98
+
99
+ // all contexts
100
+ var contexts = [];
101
+
102
+ // function push_context(c) {
103
+ // contexts.push(c);
104
+ // }
105
+
106
+
107
+ /**
108
+ String parsing
109
+ */
110
+ var string_parse_stack = [];
111
+
112
+ var push_string_parse = function(o) {
113
+ string_parse_stack.push(o);
114
+ };
115
+
116
+ var pop_string_parse = function() {
117
+ string_parse_stack.pop();
118
+ };
119
+
120
+ var current_string_parse = function() {
121
+ if (string_parse_stack.length == 0) {
122
+ return null;
123
+ }
124
+ return string_parse_stack[string_parse_stack.length - 1];
125
+ };
126
+
127
+
128
+ // create object dup
129
+ var object_create = function(obj) {
130
+ var targ = { };
131
+ for (var prop in obj) {
132
+ targ[prop] = obj[prop];
133
+ }
134
+
135
+ return targ;
136
+ };
137
+
138
+ var original_symbol = {
139
+ nud: function () {
140
+ return this;
141
+ },
142
+ led: function (left) {
143
+ throw 'led unimplemented';
144
+ }
145
+ };
146
+
147
+ var symbol = function(id, binding_power) {
148
+ var sym = sym_tbl[id];
149
+ binding_power = binding_power || 0;
150
+ if (sym) {
151
+ if (binding_power >= sym.lbp) {
152
+ sym.lbp = binding_power;
153
+ }
154
+ }
155
+ else {
156
+ sym = object_create(original_symbol);
157
+ sym.type = sym.value = id;
158
+ sym.lbp = binding_power;
159
+ sym_tbl[id] = sym;
160
+ }
161
+ return sym;
162
+ };
163
+
164
+ var sym_stmt = function (id, bp, block) {
165
+ if (!block) {
166
+ block = bp;
167
+ bp = 0;
168
+ }
169
+
170
+ var sym = symbol(id);
171
+ sym.std = block;
172
+ return sym;
173
+ };
174
+
175
+ var infixr = function (id, bp, led) {
176
+ var s = symbol(id, bp);
177
+ s.led = led || function (left) {
178
+ this.first = left;
179
+ this.second = expr(bp - 1);
180
+ this.arity = "binary";
181
+ return this;
182
+ };
183
+ return s;
184
+ };
185
+
186
+ // make a function for us that has a 'usual beahiour' (saves making a function
187
+ // over and over) - +/-/*// all do the same thing etc
188
+ var infix = function (id, bp, led) {
189
+ var s = symbol(id, bp);
190
+ s.led = led || function (left) {
191
+ this.$lhs = left;
192
+ this.$rhs = expr(bp);
193
+ this.type = id;
194
+ return this;
195
+ };
196
+ return s;
197
+ };
198
+
199
+ var prefix = function (id, nud) {
200
+ var s = symbol(id);
201
+ s.nud = nud || function () {
202
+ scope.reserve(this);
203
+ this.first = expression(70);
204
+ this.arity = "unary";
205
+ return this;
206
+ };
207
+ return s;
208
+ };
209
+
210
+ var assignment = function (id) {
211
+ return infixr(id, 10, function (left) {
212
+ if (left.type !== "." && left.type !== "[" && left.type !== tIDENTIFIER && left.type != tIVAR && left.type !== tMLHS && left.type !== tCONSTANT) {
213
+ throw 'bad lhs'
214
+ }
215
+ this.$lhs = left;
216
+ this.$rhs = stmt();
217
+ this.assignment = true;
218
+ // this.type = "assignment";
219
+ return this;
220
+ });
221
+ };
222
+
223
+ assignment("=");
224
+
225
+ symbol(kDO).nud = function() {
226
+ if (token.type == '|') {
227
+ var e;
228
+ this.$args = [];
229
+ // gather block params
230
+ next_token();
231
+ e = expr();
232
+ this.$args.push(e);
233
+ while (true) {
234
+ if (token.type == "|") {
235
+ next_token();
236
+ break;
237
+ }
238
+ else if (token.type == ",") {
239
+ next_token();
240
+ continue;
241
+ }
242
+ else {
243
+ this.$args.push(expr());
244
+ }
245
+ // throw "erm.."
246
+ }
247
+ }
248
+ // throw token.value
249
+ // next_token();
250
+ // throw token.type
251
+ this.$stmts = stmts([kEND]);
252
+ // read over kEND
253
+ next_token();
254
+ return this;
255
+ };
256
+
257
+ // alt block
258
+ symbol('{').nud = function() {
259
+ // read over {
260
+ this.$stmt = stmt();
261
+ // read over }
262
+ next_token('}');
263
+ return this;
264
+ };
265
+
266
+ // self... simple, just return
267
+ symbol(kSELF).nud = function() {
268
+ return this;
269
+ };
270
+
271
+ symbol(kRETURN).nud = function() {
272
+ return this;
273
+ };
274
+
275
+ symbol(kNIL).nud = function() {
276
+ return this;
277
+ };
278
+
279
+ symbol(kSUPER).nud = function() {
280
+ return this;
281
+ };
282
+
283
+ symbol(kTRUE).nud = function() {
284
+ return this;
285
+ };
286
+
287
+ symbol(kFALSE).nud = function() {
288
+ return this;
289
+ };
290
+
291
+ symbol(tSTRING_BEG).nud = function() {
292
+ // console.log('in hre..');
293
+ // these will be string_contents mixed with actual ruby parse trees
294
+ this.$parts = [];
295
+ // next_token();
296
+ // throw token.value
297
+ while (true) {
298
+ if (token.type === false) {
299
+ throw 'Parsing string error: not expecting EOF before end of string'
300
+ }
301
+ else {
302
+ // console.log(token.value);
303
+ if (token.type === tSTRING_END) {
304
+ next_token();
305
+ break;
306
+ }
307
+ else {
308
+ if (token.type === tSTR_CONTENT) {
309
+ this.$parts.push(token)
310
+ next_token();
311
+ }
312
+ else if (token.type === tSTRING_DBEG) {
313
+ var d = token;
314
+ // skip over dbeg
315
+ next_token();
316
+ d.$value = stmt();
317
+ this.$parts.push(d);
318
+ // skip over '}'
319
+ next_token();
320
+ }
321
+ // console.log('found a part');
322
+ // this.$parts.push(token);
323
+ // next_token();
324
+ }
325
+
326
+ }
327
+ }
328
+ return this;
329
+ };
330
+
331
+ // when we get identifier identifier (treat first like receiver, second like arg1)
332
+ symbol(tIDENTIFIER).nud = function() {
333
+ // we need to check last_state, as lex_state (current) is overridden when parsing current token
334
+ if ((valid_cmd_args.indexOf(token.type) != -1) && (last_state == EXPR_CMDARG)) {
335
+ // console.log("about to gather command args..");
336
+ gather_command_args(this);
337
+ this.type = tCALL;
338
+ this.$recv = null;
339
+ this.$meth = this.value;
340
+ // this.$meth = this;
341
+ }
342
+ return this;
343
+ };
344
+
345
+ symbol(tCONSTANT).nud = function() {
346
+ return this;
347
+ };
348
+
349
+ // the 'command' to apply the argumens to
350
+ // FIXME: rewrite to asume first arg is not neceserialy a arg, it could be start
351
+ // of assocs, or might be start of kDO
352
+ var gather_command_args = function(cmd) {
353
+ cmd.$call_args = {
354
+ args: []
355
+ };
356
+ // console.log('tIDENTIFIER "' + token.value + '" lex state: ' + lex_state + ' last state: ' + last_state + ' ,last token: ' + last_token.value);
357
+ if ((token.type !== kDO) && (token.type !== '{')) {
358
+ // dont add if next statement is kDO...
359
+ // console.log("getting exopr..");
360
+ cmd.$call_args.args.push(expr());
361
+ }
362
+
363
+ // collect remaining params
364
+ if (token.type === ',') {
365
+ // read over initial commar
366
+ next_token();
367
+ while (true) {
368
+ s = expr();
369
+ // s = expr(80);
370
+ // this.$args.push(stmt());
371
+ // check if tok is tASSOC.. if so, , then we
372
+ // are beginning a hash list, so dont add stmt to $args, but push it to
373
+ // the hash arg list instead
374
+ // console.log(token.type);
375
+ if (token.type === tASSOC) {
376
+ // console.log('found tassoc');
377
+ // should we check if we already have assoc list? having it more than once per cmd call
378
+ // might be an error
379
+ var a_keys = [], a_values = [];
380
+ cmd.$assocs = { '$keys': a_keys, '$values': a_values };
381
+ a_keys.push(s);
382
+ // read over tassoc
383
+ next_token();
384
+ a_values.push(expr());
385
+
386
+ while (true) {
387
+ if (token.type !== ',') {
388
+ // end of assoc list
389
+ break;
390
+ }
391
+ // read over commar
392
+ next_token(',');
393
+ a_keys.push(expr());
394
+ next_token(tASSOC);
395
+ a_values.push(expr());
396
+ }
397
+
398
+ // console.log(this);
399
+ // throw 'hash begin!'
400
+ }
401
+ else {
402
+ cmd.$call_args.args.push(s);
403
+ }
404
+ // CHECK HERE for do_block
405
+ // move this outside of loop? once we have do_block, command is over
406
+
407
+
408
+ if (token.type !== ',') {
409
+ break;
410
+ }
411
+ // any other case, add it as an arg
412
+
413
+ // console.log(token.type);
414
+ next_token(',');
415
+ // check for 'wrong' token types... a def, class, module etc are NOT valid tokens
416
+ if ([kDEF, kCLASS, kMODULE, kIF].indexOf(token.type) !== -1) {
417
+ throw 'Command Args: Not expecting token "' + token.type + '". Perhaps a trailing commar?'
418
+ }
419
+ }
420
+ }
421
+ if (token.type === kDO) {
422
+ // gather do block
423
+ cmd.$brace_block = stmt();
424
+ }
425
+ else if (token.type === '{') {
426
+ // gather rlcurly block
427
+ cmd.$brace_block = stmt();
428
+ }
429
+ };
430
+
431
+ // kDO opt_block_param compstmt kEND
432
+ var gather_do_block = function() {
433
+ var result = token;
434
+ // read over kDO
435
+ next_token();
436
+ // throw token.value
437
+ result.$stmts = stmts([kEND]);
438
+ // read over kEND
439
+ next_token();
440
+ return result;
441
+ }
442
+
443
+ symbol(tINTEGER).nud = function() {
444
+ return this;
445
+ };
446
+
447
+ symbol(tSYMBEG).nud = function() {
448
+ this.$name = stmt();
449
+ return this;
450
+ };
451
+
452
+ symbol(tIVAR).nud = function() {
453
+ return this;
454
+ };
455
+
456
+ symbol(tCVAR).nud = function() {
457
+ return this;
458
+ };
459
+
460
+ symbol(tGVAR).nud = function() {
461
+ return this;
462
+ };
463
+
464
+ // Catching block definitions in Def statements.
465
+ symbol("&").nud = function() {
466
+ this.$name = stmt();
467
+ return this;
468
+ }
469
+
470
+
471
+ infix(",", 80, function(left) {
472
+ this.type = tMLHS;
473
+ // check if already part of a mLHS chain
474
+ if (left.type == tMLHS) {
475
+ // add to current chain
476
+ throw "in here.." + token.value
477
+ }
478
+ else {
479
+ // start new chain
480
+ this.$parts = [];
481
+ this.$parts.push(left);
482
+ this.$parts.push(expr(10));
483
+ // dont get next_token. expr() gets it for us.
484
+ // next_token();
485
+ // this.$parts.push(next_token());
486
+ }
487
+ // throw token.value
488
+ // throw "in here.." + token.value
489
+
490
+
491
+ return this;
492
+ });
493
+
494
+
495
+ /**
496
+ Fixme!! this is going to break!!
497
+
498
+ Argh! not sure how we are going to do mlghs, mrhs
499
+ */
500
+ // infix(',', 80, function(left) {
501
+ // this.$lhs = left;
502
+ // if (token.type === tSYMBEG) {
503
+ // // throw 'we need to parse an assoc.'
504
+ // next_token();
505
+ // next_token();
506
+ // next_token();
507
+ // next_token();
508
+ // }
509
+ // else {
510
+ // this.$rhs = stmt();
511
+ // }
512
+ //
513
+ // return this;
514
+ // });
515
+
516
+ // Dot notation
517
+ infix(".", 80, function (left) {
518
+ // console.log('doing dot!')
519
+ this.$recv = left;
520
+ this.$meth = token;
521
+ this.type = tCALL;
522
+ // skip over dot
523
+ next_token();
524
+ if ((valid_cmd_args.indexOf(token.type) != -1) && (last_state === EXPR_CMDARG)) {
525
+ gather_command_args(this);
526
+ }
527
+ // else {
528
+ // if not, check if we just have a block...... no args, just block..
529
+ // e.g. my_array.each do ...
530
+ // could we just add kDO to the valid_cmd_args array...?
531
+ // throw token.value
532
+ // }
533
+
534
+ return this;
535
+ });
536
+
537
+ // m - method name
538
+ // b - binding power
539
+ // t is optinal type (instead of tCALL)
540
+ // used for a + a, a - a, a << a etc. (as m)
541
+ function meth_call(m, b, t) {
542
+ return infix(m, b, function(left) {
543
+ this.type = t || tCALL;
544
+ this.$recv = left;
545
+ this.$meth = this;
546
+ this.$call_args = {
547
+ args: [stmt()]
548
+ }
549
+ return this;
550
+ });
551
+ }
552
+
553
+ meth_call(tPLUS, 80, tOPT_PLUS);
554
+ meth_call(tMINUS, 80, tOPT_MINUS);
555
+ meth_call("*", 80, tOPT_MULT);
556
+ meth_call("/", 80, tOPT_DIV);
557
+
558
+
559
+ // method calls (with paranthesis)
560
+ infix("(", 80, function (left) {
561
+ var args = {
562
+ args: []
563
+ };
564
+ // valid left values
565
+ if (left.type === '.') {
566
+ // already a method call, so just set $args property
567
+ left.$call_args = args;
568
+ }
569
+ else if (left.type === tIDENTIFIER || left.type === tCONSTANT || left.type === tCALL) {
570
+ // identifier/constant - turn them into a method call, with args
571
+ // as the args (and no receiver!)
572
+ // will identifier already be a method call? unless an actual identifier
573
+ left.$call_args = args;
574
+ }
575
+ else {
576
+ throw left.value + ' is not a valid receiver'
577
+ }
578
+
579
+ if (token.type !== ')') {
580
+ while (true) {
581
+ // console.log("gaething..");
582
+ args.args.push(expr());
583
+ if (token.type !== ',') {
584
+ break;
585
+ }
586
+ next_token(',');
587
+ }
588
+ }
589
+ next_token(')');
590
+
591
+ if (token.type === kDO) {
592
+ // gather do block
593
+ left.$block = stmt();
594
+ }
595
+ else if (token.type === '{') {
596
+ // gather rlcurly block
597
+ left.$block = stmt();
598
+ }
599
+
600
+ return left;
601
+ });
602
+
603
+ // array declarations (explicit)
604
+ prefix(tLBRACK, function() {
605
+ var arr = [];
606
+ // throw token.value
607
+ if (token.type !== ']') {
608
+ while (true) {
609
+ arr.push(expr());
610
+ if (token.type !== ',') {
611
+ break;
612
+ }
613
+ next_token(',');
614
+ }
615
+ }
616
+ next_token(']');
617
+ this.$values = arr;
618
+ return this;
619
+ });
620
+
621
+ // hash literal
622
+ prefix(tLBRACE, function () {
623
+ this.$keys = [];
624
+ this.$values = [];
625
+ if (token.type !== '}') {
626
+ while (true) {
627
+ var t = token;
628
+ // check for valid key?
629
+ next_token();
630
+ // should this be a => ?? probbaly...
631
+ next_token();
632
+ this.$keys.push(t);
633
+ this.$values.push(stmt());
634
+ if (token.type !== ',') {
635
+ break;
636
+ }
637
+ next_token(',');
638
+ }
639
+ }
640
+ next_token('}');
641
+ return this;
642
+ });
643
+
644
+ prefix(kCASE, function() {
645
+ this.$expr = stmt();
646
+ this.$body = [];
647
+
648
+ if (token.type == tNL || token.type == tSEMI) next_token();
649
+
650
+ while (true) {
651
+ if (token.type == kEND) {
652
+ next_token();
653
+ break;
654
+ }
655
+ else if (token.type == kWHEN) {
656
+ var s, t = token;
657
+ t.$args = [];
658
+ next_token();
659
+ if ([tNL, tSEMI, ","].indexOf(token.type) != -1)
660
+ throw "kCASE: not expecting given token type"
661
+ while (true) {
662
+ s = stmt();
663
+ t.$args.push(s);
664
+ if (token.type == ",") next_token();
665
+ else break;
666
+ }
667
+ t.$stmts = stmts([kEND, kELSE, kWHEN]);
668
+ }
669
+ else if (token.type == kELSE) {
670
+ var t = token;
671
+ next_token();
672
+ // throw "jere"
673
+ t.$stmts = stmts([kEND]);
674
+ // throw "erm"
675
+ }
676
+ }
677
+ return this;
678
+ });
679
+
680
+ // if statment - expression, not really a statement.
681
+ prefix(kIF, function() {
682
+ this.$expr = stmt();
683
+ this.$tail = [];
684
+
685
+ if (token.type == tNL || token.type == tSEMI) {
686
+ next_token();
687
+ if (token.type == kTHEN) next_token();
688
+ }
689
+ else if (token.type == kTHEN) {
690
+ next_token();
691
+ }
692
+ else {
693
+ throw "kIF: expecting either term or kTHEN"
694
+ }
695
+
696
+ this.$stmts = stmts([kEND, kELSE, kELSIF]);
697
+
698
+ while (true) {
699
+ if (token.type == kEND) {
700
+ next_token();
701
+ break;
702
+ }
703
+ else if (token.type == kELSIF) {
704
+ var t = token;
705
+ next_token();
706
+ t.$expr = stmt();
707
+
708
+ if (token.type == tNL || token.type == tSEMI) {
709
+ next_token();
710
+ if (token.type == kTHEN) next_token();
711
+ }
712
+ else if (token.type == kTHEN) {
713
+ next_token();
714
+ }
715
+ else {
716
+ throw "kIF: expecting either term or kTHEN"
717
+ }
718
+
719
+ t.$stmts = stmts([kEND, kELSIF, kELSE]);
720
+ this.$tail.push(t);
721
+ }
722
+ else if (token.type == kELSE) {
723
+ var t = token;
724
+ next_token();
725
+ t.$stmts = stmts([kEND]);
726
+ this.$tail.push(t);
727
+ }
728
+ else {
729
+ throw "kIF: unexpected token: " + token.type + ", " + token.value
730
+ }
731
+ }
732
+
733
+ return this;
734
+ });
735
+
736
+ // method definitions
737
+ sym_stmt(kDEF, function () {
738
+
739
+ if (token.type === tIDENTIFIER || token.type === tCONSTANT || token.type === kSELF) {
740
+ this.$fname = token;
741
+ }
742
+ else {
743
+ throw 'Method Defintion: expected identifier or constant as def name.'
744
+ }
745
+ // reads over the fname
746
+ next_token();
747
+
748
+ // check if singleton definition
749
+ if (token.type === '.' || token.type === tCOLON2) {
750
+ // we have a singleton, so put old $fname as singleton name
751
+ this.$sname = this.$fname;
752
+ // stype is either '.' or tCOLON2 - might help code generation
753
+ this.$stype = token.type;
754
+ // now get real fname
755
+ next_token();
756
+ this.$fname = token;
757
+ // read over fname
758
+ next_token();
759
+ }
760
+ else {
761
+ // check we havent shot ourself in the foot
762
+ if (this.$fname.type === kSELF) {
763
+ throw "Cannot use keyword 'self' as method name"
764
+ }
765
+ }
766
+
767
+ // ignore arglist for the moment.
768
+ if (token.type === tNL || token.type === tSEMI) {
769
+ // we can ignore... nothing to do
770
+ }
771
+ else {
772
+
773
+ this.$arglist = {
774
+ arg: [],
775
+ rest_arg: [],
776
+ opt_arg: [],
777
+ opt_block_arg: null
778
+ };
779
+
780
+ if (token.type === '(') {
781
+ // params with paranthesis
782
+ this.$paran = true;
783
+ next_token();
784
+ }
785
+ while (true) {
786
+ if (token.type === ')') {
787
+ // end of params..check if we actually had start paran?
788
+ next_token();
789
+ break;
790
+ }
791
+ else {
792
+ // for now assume every stmt will be a regular arg. need to check actual types
793
+ // later
794
+ var s = stmt();
795
+ this.$arglist.arg.push(s);
796
+ if (token.type == ',') {
797
+ // read over commar
798
+ next_token();
799
+ }
800
+ else {
801
+
802
+ if (token.type === ')') continue;
803
+ else if (token.type == tNL || token.type == tSEMI) break;
804
+ else throw "Error: def, unsupported param type " + token.type
805
+ }
806
+ }
807
+ }
808
+ }
809
+
810
+ // read stmts.
811
+ this.$stmts = stmts([kEND]);
812
+ // read over kEND
813
+ next_token();
814
+ return this;
815
+ });
816
+
817
+ sym_stmt(kCLASS, function() {
818
+
819
+ if (token.type === tIDENTIFIER) {
820
+ throw 'Class defintion: cannot use tIDENTIFIER as a class name. Expected tCONSTANT'
821
+ }
822
+ else if(token.type === tCONSTANT) {
823
+ this.$kname = token;
824
+ }
825
+ else {
826
+ throw 'Class definition: expected constant as class name'
827
+ }
828
+ // read over kname
829
+ next_token();
830
+
831
+ if (token.type == '<') {
832
+ next_token();
833
+ // for now, only constant is valid superclass. we should allow other things..except new line.
834
+ if (token.type == tCONSTANT) {
835
+ this.$super = stmt();
836
+ next_token();
837
+ }
838
+ else {
839
+ throw "Class error: supername?"
840
+ }
841
+ }
842
+
843
+ this.$stmts = stmts([kEND]);
844
+ // read over kEND
845
+ next_token();
846
+ return this;
847
+ });
848
+
849
+ sym_stmt(kMODULE, function() {
850
+ if (token.type === tIDENTIFIER) {
851
+ throw "Module definition: cannot use tIDENTIFIER as a module name. Expected tCONSTANT"
852
+ }
853
+ else if (token.type === tCONSTANT) {
854
+ this.$kname = token;
855
+ }
856
+ else {
857
+ throw "Module definition: Expected tCONSTANT for module name"
858
+ }
859
+
860
+ // name
861
+ next_token();
862
+
863
+ this.$stmts = stmts([kEND]);
864
+ // kend
865
+ next_token();
866
+ return this;
867
+ });
868
+
869
+
870
+
871
+ var stmts = function(t) {
872
+ var s;
873
+ var r = [];
874
+ t = t || [];
875
+ while (true) {
876
+ if (token.type === false) {
877
+ if (t.indexOf(false) === -1) {
878
+ break;
879
+ }
880
+ else {
881
+ throw 'stmts: got to EOF before reaching end of statements'
882
+ }
883
+ }
884
+ else if (t.indexOf(token.type) != -1) {
885
+ break;
886
+ }
887
+ else {
888
+ if (token.type === tNL || token.type === tSEMI) {
889
+ next_token();
890
+ }
891
+ else {
892
+ s = stmt();
893
+ r.push(s);
894
+ }
895
+ }
896
+ }
897
+ return r;
898
+ };
899
+
900
+ var stmt = function() {
901
+ var c = token;
902
+ if (c.std) {
903
+ next_token();
904
+ return c.std();
905
+ }
906
+ var e = expr(0);
907
+ return e;
908
+ };
909
+
910
+ var expr = function(right_binding_power) {
911
+ var old = token;
912
+ next_token();
913
+ // console.log(old);
914
+ var left = old.nud();
915
+ while (right_binding_power < token.lbp) {
916
+ old = token;
917
+ next_token();
918
+ left = old.led(left);
919
+ }
920
+ return left;
921
+ };
922
+
923
+
924
+
925
+ var get_next_string_token = function() {
926
+ var str_parse = current_string_parse();
927
+
928
+ // see if we can read end of string/xstring/regexp markers
929
+ if (scanner.scan( new RegExp('^\\' + str_parse.beg))) {
930
+ pop_string_parse();
931
+ if (str_parse.beg == '"' || str_parse.beg == "'") {
932
+ lex_state = EXPR_END;
933
+ return [tSTRING_END, scanner.matched];
934
+ }
935
+ else {
936
+ // assume to be xstring
937
+ return [tXSTRING_END, scanner.matched]
938
+ }
939
+ }
940
+
941
+ // not end of string, so we must be parsing contents
942
+ var str_buffer = [];
943
+
944
+ if (scanner.scan(/^#(\$|\@)/)) {
945
+ return [tSTRING_DVAR, scanner.matched];
946
+ }
947
+ else if (scanner.scan(/^#\{/)) {
948
+ // we are into ruby code, so stop parsing content (for the moment)
949
+ str_parse.content = false;
950
+ return [tSTRING_DBEG, scanner.matched];
951
+ }
952
+ else if (scanner.scan(/^#/)) {
953
+ str_buffer.push('#');
954
+ }
955
+
956
+ // content regexp (what is valid content for strings..)
957
+ var reg_exp = (str_parse.beg == '`') ?
958
+ // xstring: CAN include new lines
959
+ new RegExp('[^\\' + str_parse.beg + '\#\0\\]+|.') :
960
+ // normal string: cannot include new lines
961
+ new RegExp('[^\\' + str_parse.beg + '\#\0\\\n]+|.');
962
+
963
+ scanner.scan(reg_exp);
964
+ str_buffer.push(scanner.matched);
965
+ return [tSTR_CONTENT, str_buffer.join('')];
966
+ };
967
+
968
+
969
+ // checks id of current token to make sure it matches, only if id is defined.
970
+ var next_token = function(id) {
971
+ // last token support
972
+ last_token = token;
973
+ // capture string stuff
974
+ if (current_string_parse() && current_string_parse().content) {
975
+ // console.log('geting str token');
976
+ var t = get_next_string_token();
977
+ // console.log('string token: (' + t[0] + ' : ' + t[1] + ') lex_state: (' + lex_state + ')');
978
+ // token = object_create(sym_tblt);
979
+ token = { };
980
+ token.type = t[0];
981
+ token.value = t[1];
982
+ return token;
983
+ }
984
+
985
+ var t = get_next_token();
986
+ if (id && (id !== token.type)) {
987
+ throw 'Unexpected value "' + token.value + '". Expecting: ' + id
988
+ }
989
+ // console.log('token: (' + t[0] + ' : ' + t[1] + ') lex_state: (' + lex_state + ')');
990
+ // token = { type: t[0], value:t[1] };
991
+ // token = {};
992
+ token = object_create(sym_tbl[t[0]]);
993
+ token.type = t[0];
994
+ token.value = t[1];
995
+ // console.log(token.value + ', ' + last_token.value);
996
+ return token;
997
+ };
998
+
999
+ // actually get the next token
1000
+ var get_next_token = function() {
1001
+ var c = '', space_seen = false;
1002
+
1003
+ last_state = lex_state;
1004
+ cmd_start = false;
1005
+
1006
+
1007
+ while (true) {
1008
+ // console.log(scanner.working_string);
1009
+ // if (scanner.scan(/\ |\t|\r/)) {
1010
+ if(scanner.scan(/^(\ |\t|\r)/)) {
1011
+ space_seen = true;
1012
+ // console.log('found space: "' + scanner.matched + '"');
1013
+ // console.log(scanner.working_string);
1014
+ continue;
1015
+ }
1016
+ else if (scanner.scan(/^(\n|#)/)) {
1017
+ // console.log('found: ' + scanner.matched);
1018
+ c = scanner.matched;
1019
+ if (c == '#') {
1020
+ scanner.scan(/^(.*\n)/);
1021
+ }
1022
+ // we can skip any more blank lines..(combine them into one..)
1023
+ scanner.scan(/^(\n+)/);
1024
+ // console.log('we scanned lots');
1025
+ // console.log(scanner.matched);
1026
+
1027
+ if (lex_state == EXPR_BEG) {
1028
+ continue;
1029
+ }
1030
+ cmd_start = true;
1031
+ lex_state = EXPR_BEG;
1032
+ return [tNL, '\n'];
1033
+ }
1034
+ else if (scanner.scan(/^[+-]/)) {
1035
+ var result = scanner.matched == '+' ? tPLUS : tMINUS;
1036
+ var sign = (result == tPLUS) ? tUPLUS : tUMINUS;
1037
+ // method name
1038
+ if (lex_state == EXPR_FNAME || lex_state == EXPR_DOT) {
1039
+ lex_state = EXPR_ARG;
1040
+ if (scanner.scan(/^@/)) {
1041
+ return [sign, result + '@'];
1042
+ }
1043
+ else {
1044
+ return [sign, result];
1045
+ }
1046
+ }
1047
+ // += or -=
1048
+ if (scanner.scan(/^\=/)) {
1049
+ lex_state = EXPR_BEG;
1050
+ return [tOP_ASGN, result];
1051
+ }
1052
+
1053
+ if (lex_state == EXPR_BEG || lex_state == EXPR_MID) {
1054
+ lex_state = EXPR_BEG;
1055
+ return [sign, result];
1056
+ }
1057
+
1058
+ lex_state = EXPR_BEG;
1059
+ return [result, scanner.matched];
1060
+ }
1061
+
1062
+
1063
+
1064
+
1065
+ else if (scanner.scan(/^\//)) {
1066
+ lex_state = EXPR_BEG;
1067
+ return ['/', scanner.matched];
1068
+ }
1069
+
1070
+ else if (scanner.scan(/^\*\*\=/)) {
1071
+ lex_state = EXPR_BEG;
1072
+ return [tOP_ASGN, "**"];
1073
+ }
1074
+ else if (scanner.scan(/^\*\*/)) {
1075
+ return [tPOW, "**"];
1076
+ }
1077
+ else if (scanner.scan(/^\*\=/)) {
1078
+ lex_state = EXPR_BEG;
1079
+ return [tOP_ASGN, "*"];
1080
+ }
1081
+ else if (scanner.scan(/^\*/)) {
1082
+ var r;
1083
+ if (lex_state == EXPR_FNAME) {
1084
+ lex_state = EXPR_BEG;
1085
+ r = "*";
1086
+ }
1087
+ else if (lex_state == EXPR_BEG || lex_state == EXPR_MID) {
1088
+ r = tSTAR;
1089
+ }
1090
+ else {
1091
+ lex_state = EXPR_BEG;
1092
+ r = "*"
1093
+ }
1094
+ return [r, scanner.matched];
1095
+ }
1096
+
1097
+
1098
+
1099
+
1100
+
1101
+
1102
+ else if (scanner.scan(/^\<\=\>/)) {
1103
+ return [tCMP, scanner.matched];
1104
+ }
1105
+ else if (scanner.scan(/^\<\=/)) {
1106
+ return [tLEQ, "<="];
1107
+ }
1108
+ else if (scanner.scan(/^\<\<\=/)) {
1109
+ lex_state = EXPR_BEG;
1110
+ return [tOP_ASGN, "<<"];
1111
+ }
1112
+ else if (scanner.scan(/^\<\</)) {
1113
+ if (([EXPR_END, EXPR_DOT, EXPR_ENDARG, EXPR_CLASS].indexOf(lex_state) != -1) && space_seen) {
1114
+ return [tLSHFT, "<<"];
1115
+ }
1116
+ lex_state = EXPR_BEG;
1117
+ return [tLSHFT, "<<"];
1118
+ }
1119
+ else if (scanner.scan(/^\</)) {
1120
+ lex_state = EXPR_BEG;
1121
+ return ["<", "<"];
1122
+ }
1123
+
1124
+
1125
+
1126
+
1127
+ else if (scanner.scan(/^\&\&\=/)) {
1128
+ lex_state = EXPR_BEG;
1129
+ return [tOP_ASGN, "&&"];
1130
+ }
1131
+ else if (scanner.scan(/^\&\&/)) {
1132
+ lex_state = EXPR_BEG;
1133
+ return [tANDOP, "&&"];
1134
+ }
1135
+ else if (scanner.scan(/^\&\=/)) {
1136
+ lex_state = EXPR_BEG;
1137
+ return [tOP_ASGN, "&"];
1138
+ }
1139
+ else if (scanner.scan(/^\&/)) {
1140
+ var r;
1141
+ if (space_seen && !scanner.check(/^\s/)) {
1142
+ if (lex_state == EXPR_CMDARG) r = tAMPER;
1143
+ else r = "&";
1144
+ }
1145
+ else if (lex_state == EXPR_BEG || lex_state == EXPR_MID) {
1146
+ r = tAMPER;
1147
+ }
1148
+ else {
1149
+ r = "&";
1150
+ }
1151
+ return [r, "&"];
1152
+ }
1153
+
1154
+
1155
+
1156
+
1157
+ // strings.. in order: double, single, xstring
1158
+ else if (scanner.scan(/^\"/)) {
1159
+ push_string_parse({ beg: '"', content: true });
1160
+ return [tSTRING_BEG, scanner.matched];
1161
+ }
1162
+ else if (scanner.scan(/^\'/)) {
1163
+ push_string_parse({ beg: "'", content: true });
1164
+ return [tSTRING_BEG, scanner.matched];
1165
+ }
1166
+ else if (scanner.scan(/^\`/)) {
1167
+ push_string_parse({ beg: "`", content: true });
1168
+ return [tXSTRING_BEG, scanner.matched];
1169
+ }
1170
+
1171
+ // numbers
1172
+ else if (scanner.check(/^[0-9]/)) {
1173
+ lex_state = EXPR_END;
1174
+ if (scanner.scan(/^[\d_]+\.[\d_]+\b/)) {
1175
+ return [tFLOAT, scanner.matched];
1176
+ }
1177
+ else if (scanner.scan(/^[\d_]+\b/)) {
1178
+ return [tINTEGER, scanner.matched];
1179
+ }
1180
+ else if (scanner.scan(/^0(x|X)(\d|[a-f]|[A-F])+/)) {
1181
+ return [tINTEGER, scanner.matched];
1182
+ }
1183
+ else {
1184
+ console.log('unexpected number type');
1185
+ return [false, false];
1186
+ }
1187
+ }
1188
+
1189
+
1190
+ else if (scanner.scan(/^\|\|\=/)) {
1191
+ lex_state = EXPR_BEG;
1192
+ return [tOP_ASGN, '||'];
1193
+ }
1194
+ else if (scanner.scan(/^\|\|/)) {
1195
+ lex_state = EXPR_BEG;
1196
+ return [tOROP, scanner.matched];
1197
+ }
1198
+ else if (scanner.scan(/^\|\=/)) {
1199
+ lex_state = EXPR_BEG;
1200
+ return [tOP_ASGN, '|'];
1201
+ }
1202
+ else if (scanner.scan(/^\|/)) {
1203
+ lex_state = EXPR_BEG;
1204
+ return ["|", scanner.matched];
1205
+ }
1206
+
1207
+ else if (scanner.scan(/^\:/)) {
1208
+ // console.log ("HERE " + lex_state);
1209
+ if (lex_state === EXPR_END || lex_state === EXPR_ENDARG || scanner.check(/^\s/)) {
1210
+ // FIXME: hack for tertiary statements
1211
+ if (!scanner.check(/^\w/)) {
1212
+ return [':', scanner.matched];
1213
+ }
1214
+
1215
+ lex_state = EXPR_BEG;
1216
+ return [tSYMBEG, scanner.matched];
1217
+ }
1218
+
1219
+ lex_state = EXPR_FNAME;
1220
+ return [tSYMBEG, ':'];
1221
+ }
1222
+
1223
+ else if (scanner.scan(/^\[/)) {
1224
+ result = scanner.matched;
1225
+
1226
+ if (lex_state == EXPR_FNAME || lex_state == EXPR_DOT) {
1227
+ lex_state = EXPR_ARG
1228
+ if (scanner.scan(/^\]\=/)) {
1229
+ return [tASET, '[]='];
1230
+ }
1231
+ else if (scanner.scan(/^\]/)) {
1232
+ return [tAREF, '[]'];
1233
+ }
1234
+ else {
1235
+ throw "error, unexpecrted '[]' token"
1236
+ }
1237
+ }
1238
+ // space seen allows for method calls with array as first param
1239
+ // otherwise it thinks its calling the '[]' method
1240
+ else if (lex_state == EXPR_BEG || lex_state == EXPR_MID || space_seen) {
1241
+ return [tLBRACK, scanner.matched]
1242
+ }
1243
+ // hmm?
1244
+ return ['[', scanner.matched]
1245
+ }
1246
+
1247
+ else if (scanner.scan(/^\{/)) {
1248
+ var result;
1249
+ if ([EXPR_END, EXPR_CMDARG].indexOf(lex_state) !== -1) {
1250
+ // primary block
1251
+ result = '{';
1252
+ }
1253
+ else if (lex_state == EXPR_ENDARG) {
1254
+ // expr block
1255
+ result = tLBRACE_ARG;
1256
+ }
1257
+ else {
1258
+ // hash
1259
+ result = tLBRACE;
1260
+ }
1261
+ return [result, scanner.matched];
1262
+ }
1263
+
1264
+ // ]
1265
+ else if (scanner.scan(/^\]/)) {
1266
+ lex_state = EXPR_END;
1267
+ return [']', scanner.matched];
1268
+ }
1269
+
1270
+ else if (scanner.scan(/^\;/)) {
1271
+ lex_state = EXPR_BEG;
1272
+ return [tSEMI, ';'];
1273
+ }
1274
+ // #
1275
+ else if (scanner.scan(/^\(/)) {
1276
+ var result = '(';
1277
+ if (lex_state == EXPR_BEG || lex_state == EXPR_MID) {
1278
+ result = tLPAREN;
1279
+ }
1280
+ else if (space_seen) {
1281
+ if (lex_state == EXPR_CMDARG) {
1282
+ result = tLPAREN_ARG;
1283
+ }
1284
+ else if(lex_state == EXPR_ARG) {
1285
+ // dont put space before arys
1286
+ result = tLPAREN2;
1287
+ }
1288
+ }
1289
+ lex_state = EXPR_BEG;
1290
+ return [result, scanner.matched];
1291
+ }
1292
+ // )
1293
+ else if (scanner.scan(/^\)/)) {
1294
+ lex_state = EXPR_END;
1295
+ return [')', scanner.matched];
1296
+ }
1297
+
1298
+ // }
1299
+ else if (scanner.scan(/^\}/)) {
1300
+ lex_state = EXPR_END;
1301
+ // throw 'got to end of string'
1302
+ if (current_string_parse()) {
1303
+ current_string_parse().content = true
1304
+ }
1305
+ // check if parsing string...
1306
+ return ['}', scanner.matched];
1307
+ }
1308
+
1309
+ // .
1310
+ else if (scanner.scan(/^\./)) {
1311
+ // should be EXPR_DOT in ALL cases?
1312
+ // if (lex_state == EXPR_FNAME) {
1313
+ lex_state = EXPR_DOT;
1314
+ // }
1315
+ return ['.', scanner.matched];
1316
+ }
1317
+
1318
+ // ,
1319
+ else if (scanner.scan(/^\,/)) {
1320
+ lex_state = EXPR_BEG;
1321
+ return [',', scanner.matched];
1322
+ }
1323
+
1324
+ // Class variabled
1325
+ else if (scanner.scan(/^\@\@\w*/)) {
1326
+ lex_state = EXPR_END;
1327
+ return [tCVAR, scanner.matched];
1328
+ }
1329
+ // Instance variables
1330
+ else if (scanner.scan(/^\@\w*/)) {
1331
+ lex_state = EXPR_END;
1332
+ return [tIVAR, scanner.matched];
1333
+ }
1334
+
1335
+ else if (scanner.scan(/^\=\>/)) {
1336
+ lex_state = EXPR_BEG;
1337
+ return [tASSOC, scanner.matched];
1338
+ }
1339
+
1340
+ else if (scanner.scan(/^\=/)) {
1341
+ lex_state = EXPR_BEG;
1342
+ return ['=', scanner.matched];
1343
+ }
1344
+
1345
+ else if (scanner.scan(/^\w+[\?\!]?/)) {
1346
+ switch (scanner.matched) {
1347
+ case 'def':
1348
+ lex_state = EXPR_FNAME;
1349
+ return [kDEF, scanner.matched];
1350
+ case 'end':
1351
+ lex_state = EXPR_END;
1352
+ return [kEND, scanner.matched];
1353
+ case 'class':
1354
+ // catch 'class' being used as a method name. This only works when class is used
1355
+ // like object.class .. you cannot just use 'class' to call class method on self
1356
+ // without explicitly stating self as the receiver.
1357
+ if (lex_state == EXPR_DOT) {
1358
+ return [tIDENTIFIER, scanner.matched];
1359
+ }
1360
+ lex_state = EXPR_CLASS;
1361
+ return [kCLASS, scanner.matched];
1362
+ case 'module':
1363
+ lex_state = EXPR_BEG;
1364
+ return [kMODULE, scanner.matched];
1365
+ case 'do':
1366
+ if (lex_state == EXPR_ENDARG) {
1367
+ lex_state = EXPR_BEG;
1368
+ return [kDO_BLOCK, scanner.matched];
1369
+ }
1370
+ return [kDO, scanner.matched];
1371
+ case 'if':
1372
+ if (lex_state == EXPR_BEG) {
1373
+ return [kIF, scanner.matched];
1374
+ }
1375
+ lex_state = EXPR_BEG;
1376
+ return [kIF_MOD, scanner.matched];
1377
+ case 'then':
1378
+ return [kTHEN, scanner.matched];
1379
+ case 'else':
1380
+ return [kELSE, scanner.matched];
1381
+ case 'elsif':
1382
+ return [kELSIF, scanner.matched];
1383
+ case 'unless':
1384
+ if (lex_state == EXPR_BEG) {
1385
+ return [kUNLESS, scanner.matched];
1386
+ }
1387
+ lex_state = EXPR_BEG;
1388
+ return [kUNLESS_MOD, scanner.matched];
1389
+ case 'self':
1390
+ if (lex_state != EXPR_FNAME) {
1391
+ lex_state = EXPR_END;
1392
+ }
1393
+ return [kSELF, scanner.matched];
1394
+ case 'super':
1395
+ lex_state = EXPR_ARG;
1396
+ return [kSUPER, scanner.matched];
1397
+ case 'true':
1398
+ lex_state = EXPR_END;
1399
+ return [kTRUE, scanner.matched];
1400
+ case 'false':
1401
+ lex_state = EXPR_END;
1402
+ return [kFALSE, scanner.matched];
1403
+ case 'nil':
1404
+ lex_state = EXPR_END;
1405
+ return [kNIL, scanner.matched];
1406
+ case 'return':
1407
+ lex_state = EXPR_MID;
1408
+ return [kRETURN, scanner.matched];
1409
+ case 'case':
1410
+ lex_state = EXPR_BEG;
1411
+ return [kCASE, scanner.matched];
1412
+ case 'when':
1413
+ lex_state = EXPR_BEG;
1414
+ return [kWHEN, scanner.matched];
1415
+ case 'yield':
1416
+ lex_state = EXPR_ARG;
1417
+ return [kYIELD, scanner.matched];
1418
+ }
1419
+
1420
+ var matched = scanner.matched;
1421
+
1422
+ // labels - avoid picking up a mod/class divide name
1423
+ if ((scanner.peek(2) != '::') && (scanner.scan(/^\:/))) {
1424
+ return [tLABEL, matched + scanner.matched];
1425
+ }
1426
+
1427
+ if (lex_state == EXPR_FNAME) {
1428
+ if (scanner.scan(/^=(?:(?![~>=])|(?==>))/)) {
1429
+ lex_state = EXPR_END;
1430
+ return [tIDENTIFIER, matched + scanner.matched];
1431
+ }
1432
+ }
1433
+
1434
+ // console.log('current state: ' + lex_state);
1435
+
1436
+ if ([EXPR_BEG, EXPR_DOT, EXPR_MID, EXPR_ARG, EXPR_CMDARG].indexOf(lex_state) !== -1) {
1437
+ lex_state = EXPR_CMDARG;
1438
+ }
1439
+ else {
1440
+ lex_state = EXPR_END;
1441
+ }
1442
+
1443
+ return [matched.match(/^[A-Z]/) ? tCONSTANT : tIDENTIFIER, matched];
1444
+ }
1445
+
1446
+ else {
1447
+ // false, false === end of stream
1448
+ return [false, false];
1449
+ }
1450
+ }
1451
+ };
1452
+
1453
+ var iseq_stack = [], iseq_stack_current = null;
1454
+ var iseq_locals_stack = [], iseq_locals_current = null;
1455
+ var iseq_jump_stack = [], iseq_jump_current = null;
1456
+
1457
+ function iseq_jump_idx() {
1458
+ return (iseq_jump_current++).toString();
1459
+ }
1460
+
1461
+ function iseq_stack_push(s) {
1462
+ iseq_jump_current = 0;
1463
+ iseq_jump_stack.push(iseq_jump_current);
1464
+
1465
+ iseq_locals_stack.push(iseq_locals_current = []);
1466
+ iseq_stack.push(s);
1467
+ iseq_stack_current = s;
1468
+ return s;
1469
+ }
1470
+
1471
+ function iseq_stack_pop() {
1472
+ // console.log(iseq_stack_current[7]);
1473
+ // throw "a"
1474
+ var f = iseq_stack_current[7].join("");
1475
+ console.log(f);
1476
+ var func = new Function(f);
1477
+ // console.log("here");
1478
+ // console.log(iseq_stack_current);
1479
+ iseq_stack_current[7] = func;
1480
+
1481
+
1482
+ iseq_jump_stack.pop();
1483
+ iseq_jump_current = iseq_jump_stack[iseq_jump_stack.length - 1];
1484
+
1485
+ iseq_locals_current = iseq_locals_stack[iseq_locals_stack.length - 2];
1486
+ iseq_locals_stack.pop();
1487
+
1488
+ iseq_stack_current = iseq_stack[iseq_stack.length - 2];
1489
+ return iseq_stack.pop();
1490
+ }
1491
+
1492
+ function write(str) {
1493
+ iseq_stack_current[7].push(str);
1494
+ }
1495
+
1496
+ // function iseq_opcode_push(opcode) {
1497
+ // iseq_stack_current[7].push(opcode);
1498
+ // return opcode;
1499
+ // }
1500
+
1501
+ /**
1502
+ checks the given name to see if its in the index. If the result is 0 or above,
1503
+ it is, and the idx is the index in the locals array. -1 means it is not in the
1504
+ array (so not a local)
1505
+ */
1506
+ function iseq_locals_idx(name) {
1507
+ return iseq_locals_current.indexOf(name);
1508
+ }
1509
+
1510
+ /**
1511
+ push locals name. the return value is the new index for the name
1512
+ */
1513
+ function iseq_locals_push(name) {
1514
+ var len = iseq_locals_current.length;
1515
+ iseq_locals_current.push(name);
1516
+ return len;
1517
+ }
1518
+
1519
+ function generate_tree(tree) {
1520
+ console.log("tree:");
1521
+ console.log(tree);
1522
+ var top_iseq = iseq_stack_push([0,0,"<compiled>",filename,ISEQ_TYPE_TOP,0,[],[]]);
1523
+
1524
+ var i;
1525
+ for (i = 0; i < tree.length; i++) {
1526
+ generate_stmt(tree[i], { instance: true, full_stmt: true, last_stmt:(tree.length - 1) == i, top_level: true} );
1527
+ }
1528
+ console.log(iseq_stack_pop());
1529
+
1530
+ return top_iseq;
1531
+ }
1532
+
1533
+ function generate_stmt(stmt, context) {
1534
+ switch (stmt.type) {
1535
+ case kCLASS:
1536
+ generate_class(stmt, context);
1537
+ break;
1538
+ case kMODULE:
1539
+ generate_module(stmt, context);
1540
+ break;
1541
+ case kDEF:
1542
+ generate_def(stmt, context);
1543
+ break;
1544
+ case tCALL:
1545
+ generate_call(stmt, context);
1546
+ break;
1547
+ case tSYMBEG:
1548
+ generate_symbol(stmt, context);
1549
+ break;
1550
+ case tCONSTANT:
1551
+ generate_constant(stmt, context);
1552
+ break;
1553
+ case tIDENTIFIER:
1554
+ generate_identifier(stmt, context);
1555
+ break;
1556
+ case tINTEGER:
1557
+ generate_integer(stmt, context);
1558
+ break;
1559
+ case tSTRING_BEG:
1560
+ generate_string(stmt, context);
1561
+ break;
1562
+ case kSELF:
1563
+ generate_self(stmt, context);
1564
+ break;
1565
+ case kIF:
1566
+ generate_if(stmt, context);
1567
+ break;
1568
+ case '=':
1569
+ generate_assign(stmt, context);
1570
+ break;
1571
+ case kFALSE:
1572
+ generate_false(stmt, context);
1573
+ break;
1574
+ case kTRUE:
1575
+ generate_true(stmt, context);
1576
+ break;
1577
+ case tLBRACK:
1578
+ generate_array(stmt, context);
1579
+ break;
1580
+ default:
1581
+ console.log("unknown generate_stmt type: " + stmt.type + ", " + stmt.value);
1582
+ }
1583
+ }
1584
+
1585
+ function generate_array(stmt, context) {
1586
+ write("[");
1587
+ if (stmt.$values) {
1588
+ var i;
1589
+ for (i = 0; i < stmt.$values.length; i++) {
1590
+ if (i > 0) write(",");
1591
+ generate_stmt(stmt.$values[i], {full_stmt:false, last_stmt:false});
1592
+ }
1593
+ }
1594
+ write("]");
1595
+ // iseq_opcode_push([iNEWARRAY, stmt.$values ? stmt.$values.length : 0]);
1596
+ }
1597
+
1598
+ function generate_assign(stmt, context) {
1599
+
1600
+ if (context.last_stmt && context.full_stmt) write("return ");
1601
+
1602
+
1603
+ if (stmt.$lhs.type == tIDENTIFIER) {
1604
+ var idx;
1605
+ // iseq_opcode_push([iSETLOCAL, 0]);
1606
+ if ((idx = iseq_locals_idx(stmt.$lhs.value)) == -1) {
1607
+ // doesnt exist, so we need a new local
1608
+ // iseq_opcode_push([iSETLOCAL, iseq_locals_push(stmt.$lhs.value)]);
1609
+ write('vm_setlocal(' + iseq_locals_push(stmt.$lhs.value) + ',');
1610
+ generate_stmt(stmt.$rhs, {full_stmt: false, last_stmt: false});
1611
+ write(')');
1612
+ }
1613
+ else {
1614
+ // already a local, so just get the index
1615
+ // iseq_opcode_push([iSETLOCAL, idx]);
1616
+ }
1617
+ }
1618
+ else {
1619
+ throw "unsupported lhs, for now"
1620
+ }
1621
+
1622
+ if (context.full_stmt) write(";");
1623
+ }
1624
+
1625
+ function generate_if(stmt, context) {
1626
+ // if expression..
1627
+ generate_stmt(stmt.$expr, {instance:context.instance, full_stmt:false, last_stmt:false});
1628
+ var jmp_label = iseq_jump_idx();
1629
+ iseq_opcode_push([iBRANCHUNLESS, jmp_label]);
1630
+
1631
+ // stmts
1632
+ if (stmt.$stmts) {
1633
+ var i, s = stmt.$stmts;
1634
+ for (i = 0; i < s.length; i++) {
1635
+ generate_stmt(s[i], {instance:context.instance, full_stmt:true, last_stmt:false});
1636
+ }
1637
+ }
1638
+
1639
+ iseq_opcode_push(jmp_label);
1640
+
1641
+ // if (context.last_stmt && context.full_stmt) write("return ");
1642
+ // write("(function(){");
1643
+ //
1644
+ // (stmt.type == kIF) ? write("if(RTEST(") : write("if(!RTEST(");
1645
+ //
1646
+ // // RTEST expression
1647
+ // generate_stmt(stmt.$expr, {instance:context.instance, full_stmt:false, last_stmt:false});
1648
+ // write(")){\n");
1649
+ //
1650
+ // if (stmt.$stmts) {
1651
+ // var i, s = stmt.$stmts;
1652
+ // for (i = 0; i < s.length; i++) {
1653
+ // generate_stmt(s[i], {instance:context.instance, full_stmt:true, last_stmt:(s[s.length -1] == s[i] ? true : false)});
1654
+ // }
1655
+ // }
1656
+ //
1657
+ // write("}\n");
1658
+ //
1659
+ // if (stmt.$tail) {
1660
+ // var i, t = stmt.$tail;
1661
+ // for (i = 0; i < t.length; i++) {
1662
+ // if (t[i].type == kELSIF) {
1663
+ // write("else if(RTEST(");
1664
+ // generate_stmt(t[i].$expr, {instance:context.instance, full_stmt:false, last_stmt:false});
1665
+ // write(")){\n");
1666
+ // }
1667
+ // else {
1668
+ // write("else{\n");
1669
+ // }
1670
+ //
1671
+ // if (t[i].$stmts) {
1672
+ // var j, k = t[i].$stmts;
1673
+ // for (j = 0; j < k.length; j++) {
1674
+ // // console.log("doing " + k[j].value);
1675
+ // generate_stmt(k[j], {instance:context.instance, full_stmt:true, last_stmt:(k[k.length - 1] == k[i] ? true : false)});
1676
+ // }
1677
+ // }
1678
+ //
1679
+ // write("}\n");
1680
+ // }
1681
+ // }
1682
+ //
1683
+ // write("})()");
1684
+ // if (context.full_stmt) write(";\n");
1685
+ }
1686
+
1687
+ function generate_false(stmt, context) {
1688
+ iseq_opcode_push([iPUTOBJECT, false]);
1689
+
1690
+ if (context.last_stmt && context.full_stmt) {
1691
+ iseq_opcode_push([iLEAVE]);
1692
+ }
1693
+ }
1694
+
1695
+ function generate_true(stmt, context) {
1696
+ iseq_opcode_push([iPUTOBJECT, true]);
1697
+
1698
+ if (context.last_stmt && context.full_stmt) {
1699
+ iseq_opcode_push([iLEAVE]);
1700
+ }
1701
+ }
1702
+
1703
+
1704
+ function generate_self(stmt, context) {
1705
+ if (context.last_stmt && context.full_stmt) write("return ");
1706
+ write(current_self());
1707
+ if (context.full_stmt) write(";\n");
1708
+ }
1709
+
1710
+ function generate_string(stmt, context) {
1711
+ // iseq_opcode_push([iPUTSTRING, stmt.$parts[0].value]);
1712
+
1713
+ // if (context.last_stmt && context.full_stmt) {
1714
+ // iseq_opcode_push([iLEAVE]);
1715
+ // }
1716
+
1717
+ write("'" + stmt.$parts[0].value + "'");
1718
+ }
1719
+
1720
+ function generate_integer(stmt, context) {
1721
+
1722
+ // iseq_opcode_push([iPUTOBJECT, parseInt(stmt.value)]);
1723
+ write(parseInt(stmt.value));
1724
+
1725
+ if (context.last_stmt && context.full_stmt) {
1726
+ iseq_opcode_push([iLEAVE]);
1727
+ }
1728
+ }
1729
+
1730
+ function generate_constant(stmt, context) {
1731
+ // iseq_opcode_push([iPUTNIL]);
1732
+ // iseq_opcode_push([iGETCONSTANT, stmt.value]);
1733
+ write("vm_getconstant(nil,'" + stmt.value + "')");
1734
+ }
1735
+
1736
+ function generate_identifier(identifier, context) {
1737
+ // for now, assumption is that they are all method calls. should check for local or dynamic
1738
+
1739
+ // no receiver.
1740
+ var idx;
1741
+ if ((idx = iseq_locals_idx(identifier.value)) == -1) {
1742
+ // not an identifier
1743
+ iseq_opcode_push([iPUTNIL]);
1744
+ iseq_opcode_push([iSEND, identifier.value, 0, null, 8, null]);
1745
+ }
1746
+ else {
1747
+ // its an identifier
1748
+ iseq_opcode_push([iGETLOCAL, idx]);
1749
+ }
1750
+
1751
+
1752
+ if (context.full_stmt && context.last_stmt) {
1753
+ iseq_opcode_push([iLEAVE]);
1754
+ }
1755
+ else if (context.full_stmt) {
1756
+ iseq_opcode_push([iPOP]);
1757
+ }
1758
+ }
1759
+
1760
+ function generate_symbol(sym, context) {
1761
+
1762
+ iseq_opcode_push([iPUTOBJECT, ID2SYM(sym.$name.value)]);
1763
+
1764
+ if (context.full_stmt && context.last_stmt) {
1765
+ iseq_opcode_push([iLEAVE]);
1766
+ }
1767
+ else if (context.full_stmt) {
1768
+ iseq_opcode_push([iPOP]);
1769
+ }
1770
+ }
1771
+
1772
+ function generate_call(call, context) {
1773
+
1774
+ write("vm_send(");
1775
+
1776
+ // receiver
1777
+ if (call.$recv) {
1778
+ generate_stmt(call.$recv, {instance:context.instance, full_stmt:false});
1779
+ // fix fcall bit..?
1780
+ }
1781
+ else {
1782
+ write("vm_putself()");
1783
+ }
1784
+
1785
+ // mid
1786
+ var mid = call.$meth;
1787
+ if (typeof mid === 'object') { mid = mid.value; }
1788
+ write(",'" + mid + "',");
1789
+
1790
+ // arguments (argv)
1791
+ if (call.$call_args && call.$call_args.args) {
1792
+ write("[");
1793
+ var i = 0, a = call.$call_args.args;
1794
+ for (i = 0; i < a.length; i++) {
1795
+ if (i > 0) write(",");
1796
+ generate_stmt(a[i], {instance:context.instance, full_stmt:false});
1797
+ }
1798
+ write("],");
1799
+ }
1800
+ else {
1801
+ write("[],");
1802
+ }
1803
+
1804
+ // block
1805
+ write("null");
1806
+
1807
+
1808
+ // end
1809
+ write(")");
1810
+
1811
+ if (context.full_stmt) write(";");
1812
+
1813
+
1814
+ // var mid = call.$meth;
1815
+ // if (typeof mid === 'object') {
1816
+ // mid = mid.value;
1817
+ // }
1818
+ //
1819
+ // var iseq = [iSEND, mid, 0, null, 8, null];
1820
+ //
1821
+ // // receiver
1822
+ // if (call.$recv) {
1823
+ // generate_stmt(call.$recv, {instance:context.instance, full_stmt:false});
1824
+ // // fix fcall bit
1825
+ // iseq[4] = 0;
1826
+ // }
1827
+ // else {
1828
+ // iseq_opcode_push([iPUTNIL]);
1829
+ // }
1830
+ //
1831
+ // // args..
1832
+ // if (call.$call_args && call.$call_args.args) {
1833
+ // var i, a = call.$call_args.args;
1834
+ // for (i = 0; i < a.length; i++) {
1835
+ // generate_stmt(a[i], { instance:context.instance, full_stmt:false });
1836
+ // }
1837
+ // iseq[2] = a.length;
1838
+ // }
1839
+ //
1840
+ // iseq_opcode_push(iseq);
1841
+ //
1842
+ // if (context.full_stmt && context.last_stmt) {
1843
+ // // if last stmt, we want to leave the context (with result of call on stack)
1844
+ // iseq_opcode_push([iLEAVE]);
1845
+ // }
1846
+ // else if (context.full_stmt) {
1847
+ // // if not last stmt, but a full stmt, remove result from stack. no-one wants it
1848
+ // iseq_opcode_push([iPOP]);
1849
+ // }
1850
+ //
1851
+ // // block
1852
+ // if (call.$brace_block) {
1853
+ // var b_seq = [0, 0, "block in <compiled>", filename, ISEQ_TYPE_BLOCK, 0, [], []];
1854
+ // iseq[3] = b_seq;
1855
+ //
1856
+ // if (call.$brace_block.$stmts) {
1857
+ // // generate stmts
1858
+ // iseq_stack_push(b_seq);
1859
+ //
1860
+ // var i, s = call.$brace_block.$stmts;
1861
+ // for (i = 0; i < s.length; i++) {
1862
+ // generate_stmt(s[i], {full_stmt:true, last_stmt:false});
1863
+ // }
1864
+ //
1865
+ // iseq_stack_pop();
1866
+ // }
1867
+ // }
1868
+
1869
+
1870
+
1871
+ // iseq_opcode_push([iPUTNIL]);
1872
+ // var iseq = [0, 0, definition.$fname.value, filename, ISEQ_TYPE_METHOD, 0, [], []];
1873
+ // var opcode = [iDEFINEMETHOD, definition.$fname.value, iseq, 0];
1874
+ // iseq_opcode_push(opcode);
1875
+ // iseq_stack_push(iseq);
1876
+ //
1877
+ // if (definition.$stmts) {
1878
+ // var i, s = definition.$stmts;
1879
+ // for (i = 0; i < s.length; i++) {
1880
+ // generate_stmt(s[i], {instance:(definition.$sname ? false : true), full_stmt:true, last_stmt:(s[s.length - 1] == s[i] ? true : false), name:definition.$fname});
1881
+ // }
1882
+ // }
1883
+
1884
+
1885
+
1886
+ // if (context.last_stmt && context.last_stmt) write("return ");
1887
+ //
1888
+ // if(call.value.match(/^[A-Z]/)) {
1889
+ // write(call.value);
1890
+ // write("(");
1891
+ // }
1892
+ // else {
1893
+ // // detect block..
1894
+ // if (call.$brace_block) {
1895
+ // write("rb_block_funcall(");
1896
+ // }
1897
+ // else {
1898
+ // write("rb_funcall(");
1899
+ // }
1900
+ //
1901
+ //
1902
+ // if (call.$recv) {
1903
+ // generate_stmt(call.$recv, {instance:context.instance, full_stmt:false, last_stmt:context.last_stmt, top_level:context.top_level});
1904
+ // }
1905
+ // else {
1906
+ // write(current_self());
1907
+ // }
1908
+ //
1909
+ // write(",'" + call.$meth.value + "'");
1910
+ // }
1911
+ //
1912
+ // // normal args
1913
+ // if (call.$call_args && call.$call_args.args) {
1914
+ // var i, a = call.$call_args.args;
1915
+ // for (i = 0; i < a.length; i++) {
1916
+ // write(",");
1917
+ // generate_stmt(a[i], {instance:context.instance, full_stmt:false});
1918
+ // }
1919
+ // }
1920
+ //
1921
+ // // assocs
1922
+ // if (call.$call_args && call.$call_args.assocs) {
1923
+ //
1924
+ // }
1925
+ //
1926
+ // // block
1927
+ // if (call.$brace_block) {
1928
+ //
1929
+ // }
1930
+ //
1931
+ // // sym block: &:upcase etc
1932
+ // if (call.$call_args && call.$call_args.block_arg) {
1933
+ // write(",rb_funcall(");
1934
+ // generate_stmt(call.$call_args.block_arg.arg, {instance:context.singleton, full_stmt:false, last_stmt:false, top_level:context.top_level});
1935
+ // write(",'to_proc')");
1936
+ // }
1937
+ //
1938
+ // write(")");
1939
+ // if (context.full_stmt) write(";\n");
1940
+ }
1941
+
1942
+ function generate_def(definition, context) {
1943
+ // assume not singleton for now, so define "on nil"
1944
+ iseq_opcode_push([iPUTNIL]);
1945
+ var iseq = [0, 0, definition.$fname.value, filename, ISEQ_TYPE_METHOD, 0, [], []];
1946
+ var opcode = [iDEFINEMETHOD, definition.$fname.value, iseq, 0];
1947
+ iseq_opcode_push(opcode);
1948
+ iseq_stack_push(iseq);
1949
+
1950
+ if (definition.$stmts) {
1951
+ var i, s = definition.$stmts;
1952
+ for (i = 0; i < s.length; i++) {
1953
+ generate_stmt(s[i], {instance:(definition.$sname ? false : true), full_stmt:true, last_stmt:(s[s.length - 1] == s[i] ? true : false), name:definition.$fname});
1954
+ }
1955
+ }
1956
+
1957
+
1958
+
1959
+ // if (definition.singleton) {
1960
+ // write("rb_define_singleton_method(");
1961
+ // generate_stmt(definition.singleton, {instance: context.instance, full_stmt:false, last_stmt:false});
1962
+ // write(",'" + definition.$fname + "',function(self,_cmd");
1963
+ // current_self_push("self");
1964
+ // }
1965
+ // else if (context.top_level) {
1966
+ // write("rb_define_singleton_method(rb_top_self, " + definition.$fname + "',function(self,_cmd");
1967
+ // current_self_push("self");
1968
+ // }
1969
+ // else {
1970
+ // write("rb_define_method(" + current_self() + ",'");
1971
+ // write(definition.$fname.value);
1972
+ // write("',function(self,_cmd");
1973
+ // current_self_push("self");
1974
+ // }
1975
+ //
1976
+ // // arglist
1977
+ // if (definition.$arglist && definition.$arglist.arg) {
1978
+ // var i, a = definition.$arglist.arg;
1979
+ // for (i = 0; i < a.length; i++) {
1980
+ // write(",");
1981
+ // write(a[i].value);
1982
+ // // add_to_nametable(a[i].value);
1983
+ // }
1984
+ // }
1985
+ //
1986
+ // // block arg support - every method potentialy might have a block.
1987
+ // write(",$b");
1988
+ //
1989
+ // write("){\n");
1990
+
1991
+ // block reference goes here (so if we say &block in params, map var block to $b)
1992
+ // if definition[:arglist] && definition[:arglist][:opt_block_arg]
1993
+ // write "var #{definition[:arglist][:opt_block_arg]} = $b;\n"
1994
+ // add_to_nametable definition[:arglist][:opt_block_arg]
1995
+ // end
1996
+
1997
+ // statements
1998
+ // push_string_buffer();
1999
+ // push_nametable();
2000
+
2001
+ // if (definition.$stmts) {
2002
+ // var i, s = definition.$stmts;
2003
+ // for (i = 0; i < s.length; i++) {
2004
+ // generate_stmt(s[i], {instance:(definition.$sname ? false : true), full_stmt:true, last_stmt:(s[s.length - 1] == s[i] ? true : false), name:definition.$fname});
2005
+ // }
2006
+ // }
2007
+
2008
+ // var body_contents = pop_string_buffer(), name_table = pop_nametable();
2009
+ // write each ivar statements..
2010
+ // if name_table.length > 0
2011
+ // write "var #{name_table.join(",")};\n"
2012
+ // end
2013
+
2014
+ // write(body_contents);
2015
+
2016
+ // current_self_pop();
2017
+ // pop_nametable();
2018
+ // write("});\n");
2019
+
2020
+ iseq_stack_pop();
2021
+ }
2022
+
2023
+ function generate_class(stmt, context) {
2024
+
2025
+ if (context.full_stmt && context.last_stmt) write("return ");
2026
+
2027
+ write("vm_defineclass(");
2028
+
2029
+ // base
2030
+ write("vm_putnil(),");
2031
+
2032
+ // superclass
2033
+ if (stmt.$super) {
2034
+ generate_stmt(stmt.$super, {full_stmt:false, last_stmt:false});
2035
+ }
2036
+ else {
2037
+ write("vm_putnil()");
2038
+ }
2039
+ write(",");
2040
+
2041
+ // class id
2042
+ write("'" + stmt.$kname.value + "'");
2043
+ write(",");
2044
+
2045
+ // iseq
2046
+ write("function(){},")
2047
+
2048
+ // op_flag
2049
+ write(0);
2050
+
2051
+ write(")");
2052
+
2053
+ if (context.full_stmt) write(";");
2054
+
2055
+ // base (for class << Ben; ...; end)
2056
+ // iseq_opcode_push([iPUTNIL]);
2057
+ // // superclass
2058
+ // if (stmt.$super) {
2059
+ // // console.log("super..");
2060
+ // // console.log(stmt.$super);
2061
+ // generate_stmt(stmt.$super, {full_stmt: false, last_stmt:false});
2062
+ // }
2063
+ // else {
2064
+ // iseq_opcode_push([iPUTNIL]);
2065
+ // }
2066
+ //
2067
+ // var iseq = [0, 0, "<class:" + stmt.$kname.value + ">", filename, ISEQ_TYPE_CLASS, 0, [], []];
2068
+ // var opcode = [iDEFINECLASS, stmt.$kname.value, iseq, 0];
2069
+ // iseq_opcode_push(opcode);
2070
+ // iseq_stack_push(iseq);
2071
+ //
2072
+ // // statements.
2073
+ // if (stmt.$stmts) {
2074
+ // var i, s = stmt.$stmts;
2075
+ // for (i = 0; i < s.length; i++) {
2076
+ // generate_stmt(s[i], {instance:false, full_stmt:true, last_stmt:(s[s.length - 1] == s[i] ? true : false), top_level:false});
2077
+ // }
2078
+ // }
2079
+ //
2080
+ // iseq_stack_pop();
2081
+
2082
+ // write("(function(self) {\n");
2083
+ // push_nametable();
2084
+ // current_self_push("self");
2085
+ //
2086
+ // if (stmt.$stmts) {
2087
+ // var i, m = stmt.$stmts;
2088
+ // for (i = 0; i < m.length; i++) {
2089
+ // generate_stmt(m[i], {instance: false, full_stmt: true, last_stmt: (m[m.length -1] == m[i] ? true : false), top_level: false});
2090
+ // }
2091
+ // }
2092
+ //
2093
+ // pop_nametable();
2094
+ // current_self_pop();
2095
+ //
2096
+ // write("})(");
2097
+ //
2098
+ // if (context.top_level) {
2099
+ // write("rb_define_class('")
2100
+ // write(stmt.$kname.value);
2101
+ // write("',");
2102
+ // }
2103
+ // else {
2104
+ // write("rb_define_class_under(" + current_self() + ",'");
2105
+ // write(stmt.$kname.value);
2106
+ // write("',");
2107
+ // }
2108
+ //
2109
+ // // superclass
2110
+ // if (stmt.$super) {
2111
+ // write("rb_const_get(self, '" + stmt.$super.value + "'))")
2112
+ // }
2113
+ // else {
2114
+ // write("rb_cObject)");
2115
+ // }
2116
+ //
2117
+ // write(");\n")
2118
+ }
2119
+
2120
+ function generate_module(mod, context) {
2121
+ write("(function(self) {\n");
2122
+ push_nametable();
2123
+ current_self_push("self");
2124
+
2125
+ if (mod.$stmts) {
2126
+ var i, m = mod.$stmts;
2127
+ for (i = 0; i < m.length; i++) {
2128
+ generate_stmt(m[i], {instance: false, full_stmt: false, last_stmt: (m[m.length -1] == m[i] ? true : false), nested: true});
2129
+ }
2130
+ }
2131
+
2132
+ pop_nametable();
2133
+ current_self_pop();
2134
+
2135
+ write("})(");
2136
+
2137
+ if (context.top_level) {
2138
+ write("rb_define_module('");
2139
+ write(mod.$kname.value);
2140
+ write("'));\n");
2141
+ }
2142
+ else {
2143
+ write("rb_define_module_under(" + current_self() + ",'");
2144
+ write(mod.$kname.value);
2145
+ write("'));\n")
2146
+ }
2147
+ }
2148
+
2149
+ this.parse = function(str) {
2150
+ scanner = new vn_ruby_string_scanner(str);
2151
+ next_token();
2152
+ var s = stmts();
2153
+ return generate_tree(s);
2154
+ }
2155
+
2156
+ this.contexts = function() {
2157
+ return contexts;
2158
+ }
2159
+
2160
+ // the parser - pass is the source to actually parse
2161
+ // return function(parse_text) {
2162
+ // scanner = new vn_ruby_string_scanner(parse_text);
2163
+ // next_token();
2164
+ // var s = stmts();
2165
+ // generate_tree(s);
2166
+ // return s;
2167
+ // }
2168
+ return this;
2169
+ };
2170
+
2171
+
2172
+ // String scanner
2173
+ var vn_ruby_string_scanner = function(str) {
2174
+ // whole string
2175
+ this.str = str;
2176
+ // current index
2177
+ this.at = 0;
2178
+ // last matched data
2179
+ this.matched = "";
2180
+ // working string (basically str substr'd from the 'at' index to the end)
2181
+ this.working_string = str;
2182
+ };
2183
+
2184
+ vn_ruby_string_scanner.prototype.scan = function(reg) {
2185
+ // reg = this._fix_regexp_to_match_beg(reg);
2186
+ var res = reg.exec(this.working_string);
2187
+ if (res == null) {
2188
+ return false;
2189
+ }
2190
+ else if (typeof res == "object") {
2191
+ // array.
2192
+ this.at += res[0].length;
2193
+ this.working_string = this.working_string.substr(res[0].length);
2194
+ this.matched = res[0];
2195
+ return res;
2196
+ }
2197
+ else if (typeof res == "string") {
2198
+ this.at += res.length;
2199
+ this.working_string = this.working_string.substr(res.length);
2200
+ return res;
2201
+ }
2202
+ return false;
2203
+ };
2204
+
2205
+ vn_ruby_string_scanner.prototype.check = function(reg) {
2206
+ // reg = this._fix_regexp_to_match_beg(reg);
2207
+ var res = reg.exec(this.working_string);
2208
+ return res;
2209
+ };
2210
+
2211
+ vn_ruby_string_scanner.prototype.matched = function() {
2212
+
2213
+ };
2214
+
2215
+ vn_ruby_string_scanner.prototype.peek = function(len) {
2216
+ return this.working_string.substr(0, len);
2217
+ };
2218
+