bakkdoor-blocktalk 0.1.1 → 0.1.2

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.
Files changed (64) hide show
  1. data/LICENSE +165 -0
  2. data/README.markdown +114 -0
  3. data/TODO +9 -0
  4. data/benchmark.bt +18 -0
  5. data/evaluator.rb +19 -0
  6. data/examples/chained_method_call.bt +15 -0
  7. data/examples/classes_modules.bt +68 -0
  8. data/examples/exceptions.bt +28 -0
  9. data/examples/fac.bt +23 -0
  10. data/examples/inline_ruby.bt +20 -0
  11. data/examples/linecounter.bt +8 -0
  12. data/examples/multiple_methodcall.bt +1 -0
  13. data/examples/portscan.bt +39 -0
  14. data/examples/require.bt +8 -0
  15. data/examples/ruby_methods.bt +20 -0
  16. data/examples/string_interpol.bt +10 -0
  17. data/examples/string_test.bt +13 -0
  18. data/examples/test.bt +45 -0
  19. data/examples/test2.bt +125 -0
  20. data/examples/test3.bt +9 -0
  21. data/grammar/blocktalk.rb +5030 -0
  22. data/grammar/blocktalk.tt +463 -0
  23. data/language-spec/blocktalk-example.bt +38 -0
  24. data/language-spec/blocktalk-lang-spec.bt +232 -0
  25. data/lib/blocktalk/array.bt +60 -0
  26. data/lib/blocktalk/string.bt +9 -0
  27. data/lib/blocktalk.bt +3 -0
  28. data/lib/core.rb +12 -0
  29. data/lib/kernel/array.rb +9 -0
  30. data/lib/kernel/class.rb +46 -0
  31. data/lib/kernel/codeblock.rb +57 -0
  32. data/lib/kernel/console.rb +40 -0
  33. data/lib/kernel/error.rb +11 -0
  34. data/lib/kernel/module.rb +18 -0
  35. data/lib/kernel/object.rb +66 -0
  36. data/lib/kernel/string.rb +5 -0
  37. data/lib/kernel/system.rb +5 -0
  38. data/parser/helpers/method_definitions.rb +31 -0
  39. data/parser/helpers/methodcalls.rb +56 -0
  40. data/parser/nodes/block_literal.rb +42 -0
  41. data/parser/nodes/catch.rb +22 -0
  42. data/parser/nodes/class_method_definition.rb +15 -0
  43. data/parser/nodes/comment.rb +7 -0
  44. data/parser/nodes/ensure.rb +7 -0
  45. data/parser/nodes/expression.rb +7 -0
  46. data/parser/nodes/identifier.rb +7 -0
  47. data/parser/nodes/integer_literal.rb +7 -0
  48. data/parser/nodes/message_receiver.rb +7 -0
  49. data/parser/nodes/message_with_params.rb +8 -0
  50. data/parser/nodes/message_without_params.rb +10 -0
  51. data/parser/nodes/method_definition.rb +31 -0
  52. data/parser/nodes/methodcall.rb +37 -0
  53. data/parser/nodes/multiple_methodcall.rb +28 -0
  54. data/parser/nodes/operator_message.rb +8 -0
  55. data/parser/nodes/require.rb +25 -0
  56. data/parser/nodes/return.rb +7 -0
  57. data/parser/nodes/root.rb +15 -0
  58. data/parser/nodes/string.rb +7 -0
  59. data/parser/nodes/subexpression.rb +7 -0
  60. data/parser/nodes/super_call.rb +12 -0
  61. data/parser/nodes/try.rb +7 -0
  62. data/parser/nodes/yield.rb +18 -0
  63. data/parser/nodes.rb +29 -0
  64. metadata +70 -3
@@ -0,0 +1,463 @@
1
+ grammar Blocktalk do
2
+ # starting point for the parser.
3
+ # a blocktalk programm consists of expressions & comments.
4
+ rule blocktalk_programm do
5
+ ws* exprs:(expression)* ws* <RootNode>
6
+ end
7
+
8
+ # comments start with '#' and end at the end of a line, as in ruby
9
+ rule comment do
10
+ '#' (![\n] .)* [\n]? <CommentNode>
11
+ end
12
+
13
+ # expressions start here
14
+
15
+ # an expression is one of the following:
16
+ # assignment, method_call, literal or subexpression
17
+ rule expression do
18
+ (subexpr:(method_definition / require_statement / return_statement / yield_statement
19
+ /
20
+ try_expression / catch_expression / ensure_expression
21
+ /
22
+ super_call / multiple_method_call / method_call / assignment / literal
23
+ /
24
+ subexpression / inline_ruby / comment)
25
+ ('$' / (ws* newline?)))
26
+ <ExpressionNode>
27
+ end
28
+
29
+ rule method_definition do
30
+ spaces* 'def' spaces+ method_name:(message_name) ws* '=' ws* method_body:(block_literal)
31
+ <MethodDefinitionNode>
32
+ /
33
+ spaces* 'def' spaces+ 'self ' method_name:(message_name) ws* '=' ws* method_body:(block_literal)
34
+ <ClassMethodDefinitionNode>
35
+ end
36
+
37
+ rule require_statement do
38
+ 'require:' spaces+ parse_file:string_literal <RequireNode>
39
+ end
40
+
41
+ rule return_statement do
42
+ 'return' ':'? ' ' ret_val:(subexpression / identifier / literal) <ReturnNode>
43
+ end
44
+
45
+ rule yield_statement do
46
+ 'yield' ':'? ' ' yieldval:(array_literal) <YieldNode>
47
+ end
48
+
49
+ rule try_expression do
50
+ 'try' ' '? try_block:(block_literal) <TryNode>
51
+ end
52
+
53
+ rule catch_expression do
54
+ 'catch' error_class:(': ' identifier ' ')? ' '? catch_block:(block_literal) <CatchNode>
55
+ end
56
+
57
+ rule ensure_expression do
58
+ 'ensure' ' '? ensure_block:(block_literal) <EnsureNode>
59
+ end
60
+
61
+ # a subexpression is an expression surrounded by parenthesis for grouping.
62
+ # e.g.: (gets to_i) + 3
63
+ rule subexpression do
64
+ '(' ws* subexpr:expression ws* ')' <ExpressionNode>
65
+ end
66
+
67
+ # an assignment typically looks like this:
68
+ # identifier = value (value is either an (sub)expression, an identifier or a literal
69
+ rule assignment do
70
+ target:(identifier) ws* '=' ws* val:(expression / identifier / literal) {
71
+ def value
72
+ if val.respond_to?(:subexpr)
73
+ "#{target.value} = #{val.subexpr.value}"
74
+ else
75
+ "#{target.value} = #{val.value}"
76
+ end
77
+ end
78
+ }
79
+ end
80
+
81
+
82
+ # call method of superclass (as in ruby)
83
+ rule super_call do
84
+ 'super' params:(message_params)? <SuperCallNode>
85
+ end
86
+
87
+ # calling multiple methods on the same object in a row, seperated by
88
+ # semicolons (as in smalltalk)
89
+ rule multiple_method_call do
90
+ receiver:(message_receiver) ' '
91
+ messages:(message:(message_with_params / message_without_params) spaces* ';' ws*)+
92
+ final_message:(message_with_params / message_without_params) ws*
93
+ <MultipleMethodcallNode>
94
+ end
95
+
96
+ # a method_call is characterized by a receiver, a message and optional parameters:
97
+ # e.g.: receiver message
98
+ # receiver message: param_value
99
+ # receiver message: param1_value param2: param2_value ...
100
+ # additionally, a method call can be chained as in:
101
+ # obj message1 message2 message3: param1 ...
102
+ # equivalent to this ruby code:
103
+ # obj.message1().message2().message3(param1) ...
104
+ rule method_call do
105
+ receiver:(message_receiver) ' '
106
+ first_message:(operator_message / message_with_params / message_without_params)
107
+ first_passed_block_with_ws:(' '? passed_block:(block_literal_do_end / block_literal_curly_braces / '&' block_var_name:identifier))?
108
+ messages:(' ' message:(message_with_params / message_without_params)
109
+ passed_block_with_ws:(' '?
110
+ passed_block:(block_literal_do_end / block_literal_curly_braces / '&' block_var_name:identifier))?)*
111
+ <MethodcallNode>
112
+ end
113
+
114
+ # a message receiver is one of the following:
115
+ # - message_without_params, e.g.: "42" to_i to_f # -> 42.0
116
+ # - literals, e.g. 123, 123.456, [1,2,3], {:a => 1, :b => 2}, "hello world", ?C etc.
117
+ # subexpressions, e.g. (3 + 4), ("foo" to_sym) etc.
118
+ rule message_receiver do
119
+ (literal / identifier) / subexpression
120
+ end
121
+
122
+ # a message with parameters passed to it should look like this:
123
+ # receiver message: param1_value param2: param2_value param3: param3_value etc.
124
+ rule message_with_params do
125
+ message:(message_name) params:(message_params) <MessageWithParamsNode>
126
+ end
127
+
128
+ # a message without parameters passed to it looks like this:
129
+ # receiver message
130
+ rule message_without_params do
131
+ message:(message_name) !message_params <MessageWithoutParamsNode>
132
+ end
133
+
134
+ rule operator_message do
135
+ operator_name:(operator) ' ' param_value !message_params <OperatorMessageNode>
136
+ end
137
+
138
+ # message parameters look like this:
139
+ # first_param param2_name: param2_value param3_name: param3_value
140
+ # but the additional parameters beyond the first one are optional.
141
+ rule message_params do
142
+ first_param:(first_param) rest_params:(' ' parameter:param)* {
143
+ def value
144
+ [first_param.value] + rest_params.elements.map do |space_and_param|
145
+ space_and_param.parameter.value
146
+ end
147
+ end
148
+ }
149
+ end
150
+
151
+ # the first_param is the one that gets passed to a message in the first position.
152
+ # it's param_name is the message_name, so to speak.
153
+ rule first_param do
154
+ ':' ' ' first_param_value:(param_value) {
155
+ def value
156
+ {:name => nil, :value => first_param_value.value}
157
+ end
158
+ }
159
+ end
160
+
161
+ rule param do
162
+ param_name ' ' param_value:(param_value) {
163
+ def value
164
+ #[param_name.value, param_value.value]
165
+ #param_value
166
+ {:name => param_name.value, :value => param_value.value}
167
+ end
168
+ }
169
+ end
170
+
171
+ rule param_name do
172
+ identifier ':' {
173
+ def value
174
+ identifier.value
175
+ end
176
+ }
177
+ end
178
+
179
+ rule param_value do
180
+ literal
181
+ /
182
+ identifier
183
+ /
184
+ subexpression
185
+ end
186
+
187
+ # literals
188
+
189
+ rule literal do
190
+ string_literal / symbol_literal / float_literal /int_literal / char_literal / array_literal / hash_literal / block_literal / regex_literal
191
+ end
192
+
193
+ rule string_literal do
194
+ '"' string_val:(string_interpolation / string_char)* '"' <StringLiteralNode>
195
+ / "'" string_val:(string_interpolation / string_char_single_quote)* "'" <StringLiteralNode>
196
+ end
197
+
198
+ rule string_char do
199
+ !'"' char:(.) {
200
+ def value
201
+ self.char.text_value
202
+ end
203
+ }
204
+ end
205
+
206
+ rule string_char_single_quote do
207
+ !"'" char:(.) {
208
+ def value
209
+ self.char.text_value
210
+ end
211
+ }
212
+ end
213
+
214
+ rule string_interpolation do
215
+ '#{' spaces* expr:(expression / identifier / literal) spaces* '}' {
216
+ def value
217
+ val = "\#\{"
218
+ val += expr.respond_to?(:value) ? expr.value : expr.subexpr.value
219
+ val += "\}"
220
+ end
221
+ }
222
+ end
223
+
224
+ rule symbol_literal do
225
+ ':' symbol_name:(!':' identifier) {
226
+ def value
227
+ (':' + symbol_name.text_value).to_sym
228
+ end
229
+ }
230
+ end
231
+
232
+ rule int_literal do
233
+ sign? non_zero_digit digit* <IntegerLiteralNode> / [0] <IntegerLiteralNode>
234
+ end
235
+
236
+ rule float_literal do
237
+ sign? digit+ '.' digit+
238
+ {
239
+ def value
240
+ self.text_value.to_f
241
+ end
242
+ }
243
+ end
244
+
245
+ rule char_literal do
246
+ '?' val:[a-zA-Z0-9_] {
247
+ def value
248
+ val.each_byte{|b| b}
249
+ end
250
+ }
251
+ end
252
+
253
+ rule array_literal do
254
+ '[' ws* first_item:(literal / identifier)? ws* rest_items:(',' ws* item:(literal / identifier))* ws* ']' {
255
+ def value
256
+ if self.text_value =~ /\[\s*\]/
257
+ "[]"
258
+ else
259
+ #[first_item.value] + rest_items.elements.collect{ |ws_and_items| ws_and_items.item.value }
260
+ array_str = "[#{first_item.value},"
261
+ rest_items.elements.each do |ws_and_items|
262
+ array_str += "#{ws_and_items.item.value},"
263
+ end
264
+ array_str += "]"
265
+ return array_str
266
+ end
267
+ end
268
+ }
269
+ end
270
+
271
+ rule hash_literal do
272
+ # '{' ws* '}' /
273
+ '{' ws* first_entry:(hash_entry)? ws* rest_pairs:(',' ws* pair:(hash_entry))* ws* '}' {
274
+ def value
275
+ if self.text_value =~ /\{\s*\}/
276
+ return "{}"
277
+ else
278
+ hash_str = "{"
279
+ first_pair = first_entry.value
280
+ hash_str += "#{first_pair[0]} => #{first_pair[1]},"
281
+ rest_pairs.elements.each do |ws_and_pair|
282
+ pair = ws_and_pair.pair
283
+ hash_str += "#{pair[0]} => #{pair[1]},"
284
+ end
285
+ hash_str += "}"
286
+
287
+ return hash_str
288
+ end
289
+ end
290
+ }
291
+ end
292
+
293
+ # hash entry: 'key => value'
294
+ rule hash_entry do
295
+ key:(literal / identifier) ws* '=>' ws* val:(literal / identifier) {
296
+ def value
297
+ [key.value, val.value]
298
+ end
299
+ }
300
+ end
301
+
302
+ rule block_literal do
303
+ (block_literal_do_end / block_literal_curly_braces)
304
+ end
305
+
306
+ rule block_literal_do_end do
307
+ do_keyword ws* !hash_entry ws* params:(block_params)? ws*
308
+ body:(block_body) ws*
309
+ end_keyword
310
+ <BlockLiteralNode>
311
+
312
+ end
313
+
314
+ rule block_literal_curly_braces do
315
+ '{' ws* !hash_entry ws* params:(block_params)? ws*
316
+ body:(block_body) ws*
317
+ '}'
318
+ <BlockLiteralNode>
319
+ end
320
+
321
+ rule block_body do
322
+ ws* exprs:(expression)+ ws* {
323
+ def value
324
+ body_str = ""
325
+ body_str += exprs.elements.collect{|e| e.elements[0].value}.join(";")
326
+ return body_str
327
+ end
328
+ }
329
+ end
330
+
331
+ # block parameters (e.g. |a,b,c|)
332
+ rule block_params do
333
+ '|' params:(block_param)+ '|' {
334
+ def value
335
+ # block_param_str = "|"
336
+ # block_param_str += params.elements.collect{|param| param.value}.join(",")
337
+ # block_param_str += "| "
338
+ # return block_param_str
339
+ params.elements.collect{|param| param.value}
340
+ end
341
+ }
342
+ end
343
+
344
+ rule block_param do
345
+ (ws* param_name:(identifier ':')? ' '* param_name_val:(identifier) (ws*) / (ws+ param_name_val:(identifier))) {
346
+ def value
347
+ # holds param name (if given) with param value (the identifier in the body of the block)
348
+ name_identifier_pair = {}
349
+ if param_name.text_value =~ /\S+\:/
350
+ # return the param-name as a symbol (start with a colon)
351
+ # without the final colon & space, since it's not part of the name
352
+ name_identifier_pair[:name] = ":#{param_name.text_value[0..-2]}"
353
+ end
354
+ name_identifier_pair[:identifier] = param_name_val.value
355
+ return name_identifier_pair
356
+ end
357
+ }
358
+ end
359
+
360
+ rule regex_literal do
361
+ '/' (!'/' .)* '/' {
362
+ def value
363
+ self.text_value
364
+ end
365
+ }
366
+ end
367
+
368
+ # we allow some inline ruby in blocktalk
369
+ # (mainly as a help for defining some standard library methods etc.)
370
+ # syntax: %ruby{ ... }%
371
+ rule inline_ruby do
372
+ '%ruby' ws* '{' ws* ruby_code:(comment / (!'}%' .))+ '}%' {
373
+ def value
374
+ return_val = ""
375
+ ruby_code.elements.each do |e|
376
+ unless e.respond_to?(:value) # when NOT a comment
377
+ return_val += e.text_value.gsub(/[\n\t]/, ";").gsub(/\s+/, " ")
378
+ else
379
+ # check if its actually a comment, or just a string
380
+ # interpolation - I know, it's ugly :(
381
+ if e.text_value =~ /#\{\S*\}/
382
+ # if it's just a string interpolation, also return it
383
+ return_val += e.text_value + ";"
384
+ end
385
+ end
386
+ end
387
+ return_val
388
+ end
389
+ }
390
+ end
391
+
392
+
393
+ # other rules
394
+
395
+ # identifiers are variable & possible methodnames, class & module
396
+ # names - basically anything, that you can store something in or
397
+ # assign to.
398
+ rule identifier do
399
+ ('@' / '@@')? ([a-zA-Z]+ / '$' [0-9]) [a-zA-Z0-9_]* <IdentifierNode>
400
+ end
401
+
402
+ # possible names for messages / methods
403
+ rule message_name do
404
+ [a-zA-Z]+ [a-zA-Z0-9_]* ('?' / '!' / '=')? {
405
+ def value
406
+ self.text_value
407
+ end
408
+ }
409
+ end
410
+
411
+ # operator name combinations
412
+ rule operator do
413
+ ('=' operator_symbol+) / (operator_symbol '=' operator_symbol*) / operator_symbol+ {
414
+ def value
415
+ self.text_value
416
+ end
417
+ }
418
+ end
419
+
420
+ rule operator_symbol do
421
+ ('<=' / '>='/ '<' / '>' / '==' / '!=' / '+=' / '-=' / '&' / '.' / '+' / '-' / '/' / '*' / '|' / '=~')
422
+ end
423
+
424
+ # whitespace (with newline)
425
+ rule ws do
426
+ [\n\s\t]+
427
+ end
428
+
429
+ # whitespace (without newline)
430
+ rule spaces do
431
+ [\s\t]+
432
+ end
433
+
434
+ # newline character
435
+ rule newline do
436
+ [\n]+
437
+ end
438
+
439
+ # the 'do' keyword.
440
+ rule do_keyword do
441
+ 'do' ![a-zA-Z0-9_] &ws
442
+ end
443
+
444
+ # the 'end' keyword
445
+ rule end_keyword do
446
+ 'end' ![a-zA-Z0-9_]
447
+ end
448
+
449
+ # digits between 1 and 9
450
+ rule non_zero_digit do
451
+ [1-9]
452
+ end
453
+
454
+ # digits
455
+ rule digit do
456
+ [0-9]
457
+ end
458
+
459
+ # prefix sign for number literals
460
+ rule sign do
461
+ ('+' / '-')
462
+ end
463
+ end
@@ -0,0 +1,38 @@
1
+ System require: "message-lang-spec"
2
+
3
+ File open: "test.txt" mode: "w" do |f|
4
+ # write three lines to 'test.txt' and close afterwards (done in File#open)
5
+ f puts: "what's up, dog?!"
6
+ f puts: "crazy shit, yo!"
7
+ f puts: "hahaha!!"
8
+ end
9
+
10
+ # print:
11
+ # 10
12
+ # 9
13
+ # 8
14
+ # ..
15
+ # 0
16
+ # to Console:
17
+ 10 to: 0 do |i|
18
+ Console puts: i
19
+ end
20
+
21
+ # same again
22
+ i = 0
23
+ (i < 10) while_true: do |i|
24
+ Console puts: i
25
+ i incr # increment i
26
+ # or:
27
+ #i ++
28
+ end
29
+
30
+
31
+ numbers = [1,2,3,4,5] select: {|i| i < 3} # will return [1,2]
32
+ numbers each: do |i|
33
+ Console puts: i
34
+ end
35
+
36
+ (1 .. 100) each: {|i| Console puts: i}
37
+
38
+ squares = (1 .. 100) collect: {|i| i * i}