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,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