bakkdoor-blocktalk 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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}