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,232 @@
1
+ # 'blocktalk' language specification
2
+ # comments work like this
3
+ # or like this:
4
+
5
+ #-#
6
+ multiline comments look like this
7
+ #-#
8
+
9
+ # simple anonymous function definition.
10
+ # functions / procedures / methods in general are simply names
11
+ # to which anonymous methods / codeblocks are bound.
12
+ # in contrast to ruby, there is no difference between lambdas, procs & blocks.
13
+ foo = do |bar baz|
14
+ bar puts # sending message puts to object bar
15
+ end
16
+
17
+ # square is a function, that takes one argument (x)
18
+ # and returns the square of it.
19
+ # note: as in ruby, message is a expression-based language,
20
+ # so no explicit return statement is needed.
21
+ # the last expression gets returned implicitly.
22
+ square = do |x|
23
+ x * x
24
+ end
25
+
26
+
27
+ # creating a subclass of Object called Integer.
28
+ # if no super class specified to constructor, Object is taken.
29
+ Class in: Integer do
30
+ # include Numerical module (as in ruby):
31
+ include Numerical
32
+
33
+ # operators are simply methods / messages that a class can define however it wants.
34
+ # in this case, the * operator is defined to be a message with one parameter (y).
35
+ def * = do |y|
36
+
37
+ # |> is a special operator that allows the last expression in a line above this one to be used
38
+ # as the argument to a method call.
39
+ # this makes it easier to handle several methodcalls to an object, retrieved in a more complex expression
40
+ # without needing to save it to a local variable or whatever.
41
+
42
+ (y is_a?: Number)
43
+ |> if_true do
44
+ # this gets executed, if the upper condition (boolean) object is true
45
+ (y is_a?: Integer) if_true do
46
+ Math base_int_mult: self with: y # some base function defined in the vm or whatever (in this case in the Math module)
47
+ end
48
+
49
+ (y is_a?: Float) if_true do
50
+ Math base_float_mult: self with: y
51
+ end
52
+ end
53
+
54
+ |> if_false do
55
+ # raise an exception:
56
+ Error raise: (WrongArgumentError new: "Expected a number, but got #{y class}")
57
+ end
58
+
59
+ end
60
+ end
61
+
62
+
63
+ Class in: File deriving: Stream do
64
+ # constructor that takes a file handle given by the operating system or so.
65
+ def new = do |file_handle|
66
+ File new: (file_handle file_name) mode: (file_handle access_mode)
67
+ end
68
+
69
+ # constructor to File class
70
+ def new = do |file_name mode|
71
+ @file_name = file_name
72
+ @access_mode = mode
73
+
74
+ block_given?
75
+ |> if_true do
76
+ File open: file_name mode: mode do |f|
77
+ yield f
78
+ end
79
+ end
80
+ end
81
+
82
+ # class method with named parameter.
83
+ # gets calles like this:
84
+ # File open "foo.txt" with:"r" do |f|
85
+ # Console puts : f readlines
86
+ # end
87
+ self open = do |file_name mode=nil|
88
+ mode ||= "r"
89
+ block_given? if_true do
90
+ # trying method takes a block (between { and }).
91
+ # this would also work: trying do ... end (same as in ruby).
92
+ # in ruby: trying = begin.
93
+ try {
94
+ # open file etc.
95
+ file = File new: (IO base_file_open: file_name mode)
96
+ # yield to block
97
+ yield: [file]
98
+
99
+ # catching errors is easy.
100
+ # simply use catch method with a given block to execute
101
+ # if the specified class (as the parameter) is caugth.
102
+ catch: IOError do |ex|
103
+ Console puts: "Error while trying to open file #{file_name} with access mode: #{mode}"
104
+ end
105
+
106
+ # Error is baseclass of all exceptions -> this will get called when all the others didn't match
107
+ catch: Error do |ex|
108
+ Console puts: "Error: #{ex}"
109
+ end
110
+
111
+ # blocks to methodcalls can also be done via curly-braces syntax {} (instead of do ... end)
112
+ ensure {
113
+ file close # always close the file
114
+ }
115
+ }
116
+ # and finally close again
117
+ end
118
+ end
119
+
120
+ # ... more methods for File class go here ...
121
+ end
122
+
123
+
124
+
125
+ # Numerical - Module.
126
+ # Holds some basic mathematical messages that can be used by different numerical types.
127
+ Module in: Numerical do
128
+
129
+ def abs = {
130
+ (self > 0)
131
+ |> if_true { self }
132
+ |> if_false { self * -1 }
133
+ }
134
+
135
+ def ** = { |power|
136
+ ((abs power) == 0) if_true { return 1 }
137
+
138
+ return self * (self ** (power - 1))
139
+ }
140
+
141
+ def expt = (**) # define expt to be the same as **
142
+
143
+ def - = {
144
+ self * -1
145
+ }
146
+
147
+ def negate = (-)
148
+
149
+ def fac = {
150
+ (self <= 1) if_true { return 1 }
151
+ self * ((self - 1) fac)
152
+ }
153
+
154
+ # maybe some more ...
155
+
156
+ def incr = { self += 1 }
157
+ def decr = { self -= 1 }
158
+
159
+ def ++ = { self incr }
160
+
161
+ def -- = { self decr }
162
+
163
+ def to = do |n|
164
+ block_given? if_true {
165
+ i = self
166
+ msg = (incr)
167
+
168
+ (i > n) if_true {
169
+ msg = (decr)
170
+ }
171
+
172
+ (i != n) while_true {
173
+ yield i
174
+ i msg # increment or decrement i, based on value of msg
175
+ }
176
+ }
177
+ end
178
+
179
+ def times = {
180
+ i = self
181
+ (i > 0) while_true {
182
+ i decr
183
+ yield i
184
+ }
185
+ }
186
+ end
187
+
188
+
189
+
190
+ Class in: Boolean do
191
+
192
+ def if_true = {
193
+ # check (via some core routine in the vm) if self == true.
194
+ # if so: yield to block.
195
+ # finally, return self for easy chaining.
196
+ yield
197
+ self
198
+ }
199
+
200
+ def if_false = {
201
+ # similar to if_true, but only yield, if self == false.
202
+ yield
203
+ self
204
+ }
205
+
206
+ def while_true = {
207
+ self if_true {
208
+ yield
209
+ self while_true
210
+ }
211
+ }
212
+
213
+ def while_true = {
214
+ self if_false {
215
+ yield
216
+ self while_false
217
+ }
218
+ }
219
+
220
+ end
221
+
222
+
223
+ Module in: MyModule {
224
+ def self some_static_method = do |param1 and: param2|
225
+ return "bla_blubb"
226
+ end
227
+ }
228
+
229
+
230
+ Class in: Foo deriving: [Bar] {
231
+ self mixin: [Enumerable, Weirdo]
232
+ }
@@ -0,0 +1,60 @@
1
+ Class >> :Array do
2
+ %ruby{
3
+ alias_method :insert_original, :insert
4
+ alias_method :push_original, :push
5
+ alias_method :unshift_original, :unshift
6
+ alias_method :values_at_original, :values_at
7
+ alias_method :zip_original, :zip
8
+ }%
9
+
10
+ def rest = do
11
+ %ruby{
12
+ self[0..-1]
13
+ }%
14
+ end
15
+
16
+ def at = do |index put: value|
17
+ %ruby{
18
+ self[index] = value
19
+ }%
20
+ end
21
+
22
+ def insert = do |index_obj_arr|
23
+ %ruby{
24
+ self.insert_original(*index_obj_arr.flatten)
25
+ }%
26
+ end
27
+
28
+ def push = do |obj_arr|
29
+ %ruby{
30
+ if obj_arr.is_a?(Array)
31
+ self.push_original(*obj_arr)
32
+ else
33
+ self.push_original(obj_arr)
34
+ end
35
+ }%
36
+ end
37
+
38
+ def unshift = do |obj_arr|
39
+ %ruby{
40
+ if obj_arr.is_a?(Array)
41
+ self.unshift_original(*obj_arr)
42
+ else
43
+ self.unshift_original(obj_arr)
44
+ end
45
+ }%
46
+ end
47
+
48
+ def values_at = do |selector_arr|
49
+ %ruby{
50
+ self.values_at_original(*selector_arr)
51
+ }%
52
+ end
53
+
54
+ def zip = do |arg_arr|
55
+ %ruby{
56
+ self.zip_original(*arg_arr)
57
+ }%
58
+ end
59
+
60
+ end
@@ -0,0 +1,9 @@
1
+ Class >> :String do
2
+ def substitute = do |string with: subst_string|
3
+ self gsub: string with: subst_string
4
+ end
5
+
6
+ def substitute! = do |string with: subst_string|
7
+ self gsub!: string with: subst_string
8
+ end
9
+ end
data/lib/blocktalk.bt ADDED
@@ -0,0 +1,3 @@
1
+ # blocktalk standard library files
2
+
3
+ require: "blocktalk/array"
data/lib/core.rb ADDED
@@ -0,0 +1,12 @@
1
+ # expand search path correct subdir.
2
+ $: << File.expand_path(File.dirname(__FILE__))
3
+
4
+ require "kernel/array"
5
+ require "kernel/class"
6
+ require "kernel/codeblock"
7
+ require "kernel/console"
8
+ require "kernel/error"
9
+ require "kernel/module"
10
+ require "kernel/object"
11
+ require "kernel/string"
12
+ require "kernel/system"
@@ -0,0 +1,9 @@
1
+ class Array
2
+ def rest
3
+ self[0..-1]
4
+ end
5
+
6
+ def at__set(index, value)
7
+ self[index] = value
8
+ end
9
+ end
@@ -0,0 +1,46 @@
1
+ class Class
2
+ def self.in(class_name_sym, &block)
3
+ self.in__subclassing(class_name_sym, Object, &block)
4
+ end
5
+
6
+ def self.in__subclassing(class_name_sym, superclass = Object, &block)
7
+ # try to find class via const_get
8
+ # if error occurs (not found), define a new class with the given name
9
+ begin
10
+ classobj = Kernel::const_get(class_name_sym)
11
+ classobj.class_eval(&block)
12
+ rescue
13
+ Kernel::const_set(class_name_sym, Class.new(superclass, &block))
14
+ end
15
+ end
16
+
17
+ def self.>>(class_name_sym, &block)
18
+ class_name = class_name_sym
19
+ superclass = Object # default
20
+
21
+ if class_name_sym.is_a?(Hash)
22
+ class_name = class_name_sym.keys.first
23
+ superclass = class_name_sym.values.first
24
+ end
25
+
26
+ self.in__subclassing(class_name, superclass, &block)
27
+ end
28
+
29
+ def extend(&block)
30
+ self.class_eval(&block)
31
+ end
32
+
33
+ def mixin(modules = [])
34
+ modules.each do |m|
35
+ self.class_eval do
36
+ include m
37
+ end
38
+ end
39
+ end
40
+
41
+ def meta_class
42
+ class << self
43
+ self
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,57 @@
1
+ module Kernel
2
+ class Codeblock < Proc
3
+
4
+ attr_reader :params, :block
5
+
6
+ def initialize(params = [], &block)
7
+ if params.size > 0 && (params.size != block.arity)
8
+ raise "Error: Codeblock's arity doesn't match given argument list."
9
+ else
10
+ super(&block)
11
+ @params = params
12
+ @block = block
13
+ end
14
+ end
15
+
16
+ def call(*args)
17
+ calling_args = []
18
+ args.rest.each do |arg_name_with_val|
19
+ name, val = arg_name_with_val
20
+ if name && val
21
+ pos = param_pos(name)
22
+ puts "pos: #{pos}"
23
+ calling_args[pos] = val
24
+ end
25
+ end
26
+ if calling_args.size > 0
27
+ super(*([args.first] + calling_args))
28
+ else
29
+ super(*args)
30
+ end
31
+ end
32
+
33
+ def param_pos(param_name)
34
+ if @params.include?(param_name)
35
+ @params.index(param_name)
36
+ else
37
+ nil
38
+ end
39
+ end
40
+
41
+ def while_true(&block)
42
+ while self.call
43
+ block.call
44
+ end
45
+ end
46
+
47
+ def while_false(&block)
48
+ while not self.call
49
+ block.call
50
+ end
51
+ end
52
+
53
+ def until(&block)
54
+ self.while_false(&block)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,40 @@
1
+ class Console
2
+ def self.puts(*args)
3
+ Kernel::puts *args
4
+ end
5
+
6
+ def self.print(*args)
7
+ Kernel::print *args
8
+ end
9
+
10
+ def self.putc(*args)
11
+ Kernel::putc(*args)
12
+ end
13
+
14
+ def self.printf(arg_array)
15
+ Kernel::printf(*arg_array)
16
+ end
17
+
18
+ def self.write(*args)
19
+ Kernel::print(*args)
20
+ end
21
+
22
+ def self.writeln(*args)
23
+ Kernel::puts(*args)
24
+ end
25
+
26
+ def self.gets(prompt_str = nil)
27
+ if prompt_str
28
+ self.print(prompt_str.to_s + " ")
29
+ end
30
+ Kernel::gets
31
+ end
32
+
33
+ def self.readln(prompt_str = nil)
34
+ gets(prompt_str)
35
+ end
36
+
37
+ def self.clear
38
+ puts "\e[H\e[2J"
39
+ end
40
+ end
@@ -0,0 +1,11 @@
1
+ class Error < StandardError
2
+ def self.raise(error_obj)
3
+ Kernel::raise error_obj
4
+ end
5
+ end
6
+
7
+ class RuntimeError
8
+ def self.raise(message)
9
+ Kernel::raise(message)
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ class Module
2
+ def self.in(module_name_sym, &block)
3
+ if Kernel::constants.include?(module_name_sym.to_s)
4
+ moduleobj = Kernel::const_get(module_name_sym)
5
+ moduleobj.module_eval(&block)
6
+ else
7
+ Kernel::const_set(module_name_sym, Module.new(&block))
8
+ end
9
+ end
10
+
11
+ def self.>>(module_name_sym, &block)
12
+ self.in(module_name_sym, &block)
13
+ end
14
+
15
+ def extend(&block)
16
+ self.module_eval(&block)
17
+ end
18
+ end
@@ -0,0 +1,66 @@
1
+ class Object
2
+
3
+ def if_true(true_block = nil)
4
+ if self == true
5
+ if true_block
6
+ true_block.call
7
+ elsif block_given?
8
+ yield
9
+ end
10
+ end
11
+ end
12
+
13
+ def if_true__if_false(true_block = nil, false_block = nil)
14
+ if self == true
15
+ if true_block
16
+ true_block.call
17
+ elsif block_given?
18
+ yield
19
+ end
20
+ else
21
+ if false_block
22
+ false_block.call
23
+ end
24
+ end
25
+ end
26
+
27
+ def if_false(&block)
28
+ if self == false
29
+ block.call
30
+ end
31
+ end
32
+
33
+ def if(&block)
34
+ if self
35
+ block.call
36
+ end
37
+ end
38
+
39
+ def unless(&block)
40
+ unless self
41
+ block.call
42
+ end
43
+ end
44
+
45
+ # this should get called, if we try to call a method on objects of
46
+ # ruby classes.
47
+ # it will try to find the correct ruby method-name & clall it.
48
+ def method_missing(name, *args, &block)
49
+ # probably a blocktalk_like method was called, when we actually
50
+ # ment a ruby method
51
+ splitted = name.to_s.split("__")
52
+ if splitted.size == 2
53
+ ruby_name = splitted.first
54
+
55
+ # hopefully, we got it right
56
+ # to make us not do the work again simply create the method for
57
+ # later use and then call it
58
+ arg_names = 0.upto(args.size - 1).collect{|i| "arg_#{i}"}
59
+ self.instance_eval "def #{name}(#{arg_names.join(',')}, &block); self.send(:#{ruby_name}, #{arg_names.join(',')}, &block); end;"
60
+ # now call!
61
+ self.send(ruby_name, *args, &block)
62
+ else
63
+ raise "Unknown method, don't know what to do: #{self.class}##{name}"
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,5 @@
1
+ class String
2
+ def range_from__to(start_index, end_index)
3
+ self[start_index, end_index]
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class System
2
+ def self.require(file)
3
+ Kernel::require(file)
4
+ end
5
+ end
@@ -0,0 +1,31 @@
1
+ module Blocktalk
2
+ module ASTHelpers
3
+ module MethodDefinitions
4
+ def ruby_method(method_name, params)
5
+ params_val = params.value
6
+ method_name_val = method_name.value
7
+
8
+ bang_or_question = nil
9
+
10
+ # check for bang or boolean methods (!/? at the end)
11
+ if method_name.text_value =~ /(!|\?)/
12
+ method_name_val = method_name_val[0..-2]
13
+ bang_or_question = method_name.text_value[-1].chr.to_s
14
+ end
15
+
16
+ param_names = params_val.collect{|p| p[:name].to_s[1..-1] if p[:name]}.reject{|p| p.nil?}
17
+ # methods with more than one parameter have names including
18
+ # the additional param-names, e.g.:
19
+ # def goto = do |place with: vehicle| ... end
20
+ # becomes: def goto_with(place, vehicle) ... end
21
+ if param_names.size > 0
22
+ method_name_val += "__" + param_names.join("__")
23
+ end
24
+
25
+ method_name_val += bang_or_question if bang_or_question
26
+
27
+ return method_name_val
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,56 @@
1
+ module Blocktalk
2
+ module ASTHelpers
3
+ module Methodcalls
4
+ def generate_methodcall(receiver, message, passed_block = nil)
5
+ message_hash = message.value
6
+ message = message_hash[:message]
7
+ param_names = message_hash[:params].collect{|p| p.is_a?(Hash) ? p[:name] : nil}
8
+ param_values = message_hash[:params].collect{|p| p.is_a?(Hash) ? p[:value] : p.value}
9
+ message += param_names.join("__")
10
+
11
+ if message =~ /new_\S*/
12
+ message = "new"
13
+ end
14
+
15
+ # this happens, when we pass in a chained method call
16
+ # the string then is the already evaluated part of the chain...
17
+ eval_str = ""
18
+ if receiver.is_a?(String)
19
+ eval_str += "#{receiver}.#{message}("
20
+ else
21
+ eval_str += "#{receiver.value}.#{message}("
22
+ end
23
+ eval_str += "#{param_values.join(', ')}"
24
+
25
+ if passed_block && passed_block.class == Blocktalk::BlockLiteralNode
26
+ eval_str += "){" # start block
27
+
28
+ # check for block_params
29
+ if passed_block.params.respond_to?(:value)
30
+ eval_str += "|"
31
+ eval_str +=
32
+ passed_block.params.value.collect{|p| p[:identifier]}.join(",")
33
+ eval_str += "| "
34
+ end
35
+
36
+ eval_str += passed_block.body.value # insert block-body
37
+ eval_str += "}" # end block
38
+
39
+ elsif passed_block && passed_block.text_value =~ /&\S+/
40
+ # check for Proc-Objects passed to method as block
41
+ if params.size > 0
42
+ eval_str += ", "
43
+ end
44
+ eval_str += "&"
45
+ eval_str += passed_block.block_var_name.value
46
+ eval_str += ")"
47
+ else
48
+ # no block given -> method call is finished
49
+ eval_str += ")"
50
+ end
51
+
52
+ return eval_str
53
+ end
54
+ end
55
+ end
56
+ end