rVM 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +1 -0
- data/lib/rvm/base/classes.rb +85 -0
- data/lib/rvm/base/functions.rb +59 -0
- data/lib/rvm/base/interpreter.rb +342 -0
- data/lib/rvm/base/languages.rb +47 -0
- data/lib/rvm/base/plugin.rb +332 -0
- data/lib/rvm/classes/block.rb +26 -0
- data/lib/rvm/classes/boolean.rb +27 -0
- data/lib/rvm/classes/error.rb +22 -0
- data/lib/rvm/classes/list.rb +48 -0
- data/lib/rvm/classes/number.rb +70 -0
- data/lib/rvm/classes/string.rb +73 -0
- data/lib/rvm/functions/list/align.rb +49 -0
- data/lib/rvm/functions/list/join.rb +18 -0
- data/lib/rvm/functions/list/map.rb +24 -0
- data/lib/rvm/functions/list/split.rb +24 -0
- data/lib/rvm/functions/logic/and.rb +25 -0
- data/lib/rvm/functions/math/add.rb +18 -0
- data/lib/rvm/functions/math/div.rb +21 -0
- data/lib/rvm/functions/math/mul.rb +18 -0
- data/lib/rvm/functions/math/neg.rb +18 -0
- data/lib/rvm/functions/math/power.rb +18 -0
- data/lib/rvm/functions/math/sub.rb +18 -0
- data/lib/rvm/functions/string/ansi.rb +24 -0
- data/lib/rvm/functions/string/capstr.rb +23 -0
- data/lib/rvm/functions/string/center.rb +25 -0
- data/lib/rvm/functions/string/ljust.rb +26 -0
- data/lib/rvm/functions/string/regmatch.rb +34 -0
- data/lib/rvm/functions/string/rjust.rb +25 -0
- data/lib/rvm/languages/math/compiler.rb +36 -0
- data/lib/rvm/languages/math/tokenizer.rb +47 -0
- data/lib/rvm/languages/math/tree.rb +100 -0
- data/lib/rvm/languages/math.rb +14 -0
- data/lib/rvm.rb +33 -0
- metadata +96 -0
data/README
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# = classes.rb - Class Plugin Library
|
2
|
+
#
|
3
|
+
# Copyright (C) 2008 Heinz N. Gies (heinz@licenser.net)
|
4
|
+
#
|
5
|
+
# This file is published under the MIT Licenser, see LICENSE for details.
|
6
|
+
#
|
7
|
+
require File.dirname(__FILE__) + '/plugin'
|
8
|
+
module RVM
|
9
|
+
# This module is the basic class container, classes are supposed to be
|
10
|
+
# used by this as in: RVM::Classes[<class id>] this guarnatees that,
|
11
|
+
# in the case of overwriting a standard class the code still works.
|
12
|
+
#
|
13
|
+
# == Example
|
14
|
+
#
|
15
|
+
# === Creating a string calss
|
16
|
+
#
|
17
|
+
# require 'lib/base/classes'
|
18
|
+
# string = RVM::Classes[:string].new
|
19
|
+
module Classes
|
20
|
+
|
21
|
+
extend PluginHost
|
22
|
+
plugin_path File.dirname(__FILE__), '../classes/'
|
23
|
+
default :string
|
24
|
+
# The Parent for new classes, meant never to be used alone.
|
25
|
+
# It takes care of registering the calss to the PluginHost,
|
26
|
+
# as well of basic functionality.
|
27
|
+
#
|
28
|
+
# Also it offers access to the function plugins to be used
|
29
|
+
# as functions within the commands.
|
30
|
+
#
|
31
|
+
# == Examples
|
32
|
+
#
|
33
|
+
# === COMPLES NUMBERS
|
34
|
+
#
|
35
|
+
# # file: lib/classes/math/complex.rb
|
36
|
+
# require 'lib/base/classes'
|
37
|
+
# module RVM
|
38
|
+
# module Classes
|
39
|
+
# class Complex < RVM::Classes::Class
|
40
|
+
# def initialize i, n
|
41
|
+
# @i = i
|
42
|
+
# @j = j
|
43
|
+
# end
|
44
|
+
# #...
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
class Class
|
49
|
+
|
50
|
+
extend Plugin
|
51
|
+
plugin_host Classes
|
52
|
+
|
53
|
+
# This defines the type of the class, it defaults to :any
|
54
|
+
# it is important for tying and type conversion, as long as it
|
55
|
+
# behaves like a string, it can look like a sting ;)
|
56
|
+
def data_type
|
57
|
+
:any
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns if the objects is true or not,
|
61
|
+
# this is interesting for things like errors overwrite it if your
|
62
|
+
# class is not always true.
|
63
|
+
def is_true?
|
64
|
+
true
|
65
|
+
end
|
66
|
+
|
67
|
+
alias method_missing_old method_missing
|
68
|
+
alias respond_to_old respond_to?
|
69
|
+
|
70
|
+
# Implements a fallcback to allow use of function plugins within classes.
|
71
|
+
def respond_to?(symbol,include_private = false)
|
72
|
+
respond_to_old(symbol,include_private)
|
73
|
+
end
|
74
|
+
|
75
|
+
def method_missing(m, *args)
|
76
|
+
if (RVM::Functions.has? m)
|
77
|
+
RVM::Functions[m].execute args, @env
|
78
|
+
else
|
79
|
+
method_missing_old m, *args
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/plugin'
|
2
|
+
require File.dirname(__FILE__) + '/interpreter'
|
3
|
+
module RVM
|
4
|
+
module Functions
|
5
|
+
extend PluginHost
|
6
|
+
plugin_path File.dirname(__FILE__), '../functions/*/'
|
7
|
+
class Function
|
8
|
+
extend Plugin
|
9
|
+
plugin_host Functions
|
10
|
+
class << self
|
11
|
+
|
12
|
+
def data_type
|
13
|
+
:any
|
14
|
+
end
|
15
|
+
|
16
|
+
alias method_missing_old method_missing
|
17
|
+
alias respond_to_old respond_to?
|
18
|
+
def respond_to?(symbol,include_private = false)
|
19
|
+
respond_to_old(symbol,include_private)
|
20
|
+
end
|
21
|
+
|
22
|
+
def method_missing(m, *args)
|
23
|
+
if (RVM::Functions.has? m)
|
24
|
+
RVM::Functions[m].execute args, @env
|
25
|
+
else
|
26
|
+
method_missing_old m, *args
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
#This is for letting he itnerpreter know that it shall interprete the arguments
|
31
|
+
#and not let the function do it itself (this is needed for logical functions)
|
32
|
+
def execargs
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
def call params, env
|
37
|
+
i = 0
|
38
|
+
sig = :any
|
39
|
+
params.map! do |p|
|
40
|
+
sig = signature[i] || sig
|
41
|
+
if sig != :any and p.class != RVM::Classes[sig]
|
42
|
+
raise "TYPE MISMATCH" if RVM::strict
|
43
|
+
p = RVM::Classes[sig].new(p)
|
44
|
+
end
|
45
|
+
i+=1
|
46
|
+
p
|
47
|
+
end if not signature.empty?
|
48
|
+
r = execute(params, env)
|
49
|
+
RVM::debug "#{self.inspect}: #{signature.inspect} = '#{r}'"
|
50
|
+
r
|
51
|
+
end
|
52
|
+
|
53
|
+
def execute params, env
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,342 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/functions'
|
2
|
+
require File.dirname(__FILE__) + '/classes'
|
3
|
+
module RVM
|
4
|
+
# This Module hold all the VM Classes, so it is quite important to read
|
5
|
+
# and understand if you feel like making a own compiler.
|
6
|
+
#
|
7
|
+
# The basic idea is to translate your own code into a tree of objects
|
8
|
+
# from this.
|
9
|
+
#
|
10
|
+
module Interpreter
|
11
|
+
|
12
|
+
# This is a helper fuctio to quickly generate a empty enviorment.
|
13
|
+
def Interpreter.env aenv = {}
|
14
|
+
RVM::debug "Interpreter.env"
|
15
|
+
Enviroment.new aenv
|
16
|
+
end
|
17
|
+
|
18
|
+
# This is a helper function that generates a constat of a simple constant,
|
19
|
+
# for example if you've a lot of string constants it is way quickly to call
|
20
|
+
# this then writing it out the whole time.
|
21
|
+
def Interpreter.const type,value
|
22
|
+
RVM::Interpreter::Constant.new(RVM::Classes[type].new(value))
|
23
|
+
end
|
24
|
+
|
25
|
+
# This class represents the enviroment, the memory so to say for the VM.
|
26
|
+
# The enviroment holds enviromental variables like who executes the
|
27
|
+
# code, on what object it runs, what parameters where passed to the call
|
28
|
+
# and as well, and perhaps most importantly the local variables.
|
29
|
+
#
|
30
|
+
# Some of the functions called will initialize new enviroments, as
|
31
|
+
# for example function calls.
|
32
|
+
class Enviroment
|
33
|
+
# This creates a new enviroment enviroment, it generates a new env
|
34
|
+
# default variables are set if not defined in *data*.
|
35
|
+
#
|
36
|
+
# If *oldenv* is provided it will also fall back to see if not
|
37
|
+
# existing variables
|
38
|
+
def initialize data = {}, oldenv=nil
|
39
|
+
RVM::debug "data: #{data}\noldenv:#{oldenv}"
|
40
|
+
@data = {
|
41
|
+
:locals => {},
|
42
|
+
:caller => nil,
|
43
|
+
:self => nil,
|
44
|
+
:evaldeepth => 0,
|
45
|
+
:params => []
|
46
|
+
}.merge(data)
|
47
|
+
@prev = oldenv || {}
|
48
|
+
RVM::debug "Creating new enviroment with: #{@data.inspect} as child of #{@prev}"
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
# returns the object that issued the VM call - so to say it's owner.
|
53
|
+
#
|
54
|
+
# If the current enviroment has no caller it tries it's 'oldenv' attrib
|
55
|
+
# to see if that had a owner.
|
56
|
+
def caller
|
57
|
+
r = @data[:caller] || (@prev.is_a?(Enviroment) ? @prev.caller : nil)
|
58
|
+
RVM::debug "Getting caller: #{r}"
|
59
|
+
r
|
60
|
+
end
|
61
|
+
|
62
|
+
# returns the object that the code is run as, so it has the rights of
|
63
|
+
# this object.
|
64
|
+
#
|
65
|
+
# If the current enviroment has no object it tries it's 'oldenv' attrib
|
66
|
+
# to see if that had a object.
|
67
|
+
def object
|
68
|
+
r = @data[:self] || (@prev.is_a?(Enviroment) ? @prev.object : nil)
|
69
|
+
RVM::debug "Getting object: #{r}"
|
70
|
+
r
|
71
|
+
end
|
72
|
+
|
73
|
+
# returns a parameter that was passed to the call. For function calls
|
74
|
+
# this is especially important, it beginns with 0.
|
75
|
+
#
|
76
|
+
# If the current enviroment does not have the given paramenter it tries
|
77
|
+
# it's 'oldenv' attrib to see if that had a object.
|
78
|
+
def param i
|
79
|
+
RVM::debug "Getting param #{i} (#{@data[:params][i]})"
|
80
|
+
@data[:params][i]
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns a local variable with the given name.
|
84
|
+
#
|
85
|
+
# If the current enviroment does not have any local variables with
|
86
|
+
# the given name it tries the privious enviroments.
|
87
|
+
def [] k
|
88
|
+
r = @data[:locals][k] || @prev[k]
|
89
|
+
RVM::debug "Getting ariable #{k} (#{r})"
|
90
|
+
r
|
91
|
+
end
|
92
|
+
|
93
|
+
#Sets a local variable witin the enviroment.
|
94
|
+
def []= k, v
|
95
|
+
#TODO: add capability to have the privious env variables changed
|
96
|
+
RVM::debug "Setting variable #{k} to #{v}"
|
97
|
+
@data[:locals][k] = v
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
# A constant, it evaluates to thevalue given and end the evaluation.
|
103
|
+
# Meaning that no further execution is done in this tree branch, so the
|
104
|
+
# value isn't evaluated.
|
105
|
+
class Constant
|
106
|
+
attr_reader :value
|
107
|
+
def initialize value
|
108
|
+
@value = value
|
109
|
+
end
|
110
|
+
|
111
|
+
def data_type
|
112
|
+
@value.data_type
|
113
|
+
end
|
114
|
+
def == v
|
115
|
+
if v.is_a? Constant
|
116
|
+
@value == v.value
|
117
|
+
else
|
118
|
+
false
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def execute env
|
123
|
+
RVM::debug "Executing Constant: #{@value}"
|
124
|
+
@value
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# The condition is most widely known as 'if' statement, also the tertier
|
129
|
+
# opperator is a condition.
|
130
|
+
#
|
131
|
+
# Conditions are quite important, elseif can be implemented by chaining
|
132
|
+
# if statements.
|
133
|
+
#
|
134
|
+
# == Example
|
135
|
+
#
|
136
|
+
# === TRANSLATING IF
|
137
|
+
#
|
138
|
+
# cond = Interpreter::Condition.new(
|
139
|
+
# <... thing for the condition ...>,
|
140
|
+
# <... what to do if condition is true ...>
|
141
|
+
# [<... what to do if condition is false ...>])
|
142
|
+
#
|
143
|
+
# if now cond.execute is called the following happens:
|
144
|
+
# * execute is called for the condition
|
145
|
+
# * if the result of it true and is_true? is also true:
|
146
|
+
# * execute for the true case is called and it's result returned
|
147
|
+
# * if the result is false or is_true? is false:
|
148
|
+
# * execute is called for the false case and it's result is returned
|
149
|
+
class Condition
|
150
|
+
# Creates a new condition with the given parameters. The false_case can
|
151
|
+
# be ommitted.
|
152
|
+
#
|
153
|
+
# * value - is executed, and depandant of the result either true_case or
|
154
|
+
# false casae s executed.
|
155
|
+
# * true_case - is only executed if value evalutes to true (is_true?)
|
156
|
+
# * false_case - is only executed if value evalutes to false (!is_true?)
|
157
|
+
def initialize value, true_case, false_case = nil
|
158
|
+
@value = value
|
159
|
+
@true_case = true_case
|
160
|
+
@false_case = false_case
|
161
|
+
end
|
162
|
+
|
163
|
+
def data_type
|
164
|
+
if @true_case.data_type == @false_case.data_type
|
165
|
+
@false_case.data_type
|
166
|
+
else
|
167
|
+
:any
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def execute env
|
172
|
+
if @value.execute(env).is_true?
|
173
|
+
RVM::debug "Executing Condition... (true)"
|
174
|
+
@true_case.execute env
|
175
|
+
elsif @false_case
|
176
|
+
RVM::debug "Executing Condition... (false)"
|
177
|
+
@false_case.execute env
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# A variable assignment that sets a local variable, a declaration
|
183
|
+
# is not required before the assignment can be done.
|
184
|
+
#
|
185
|
+
# Both the *name* and the *value* are evaluated before the assignment
|
186
|
+
# is done.
|
187
|
+
class Assignment
|
188
|
+
def initialize name, value
|
189
|
+
@name = name
|
190
|
+
@value = value
|
191
|
+
end
|
192
|
+
|
193
|
+
def data_type
|
194
|
+
@value.data_type
|
195
|
+
end
|
196
|
+
|
197
|
+
def execute env
|
198
|
+
RVM::debug "Executing Assignment..."
|
199
|
+
env[@name.execute(env).to_s] = @value.execute env
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Reads the value of a variable in the enviroment.
|
204
|
+
#
|
205
|
+
# The *name* is evaluated before the variable is retrieved.
|
206
|
+
class Variable
|
207
|
+
def initialize name
|
208
|
+
@name = name
|
209
|
+
@type = :any
|
210
|
+
end
|
211
|
+
|
212
|
+
# The type can only be tretrieved when the name is aconstant
|
213
|
+
# as it can be evaluated without sideffect.
|
214
|
+
def data_type
|
215
|
+
@type
|
216
|
+
end
|
217
|
+
|
218
|
+
def execute env
|
219
|
+
RVM::debug "Executing Variable..."
|
220
|
+
r = env[@name.execute(env).to_s]
|
221
|
+
@type = r.data_type
|
222
|
+
r
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
|
227
|
+
# Returns a parameter of the enviroment. This is needed for things like
|
228
|
+
# *self* or *caller*.
|
229
|
+
class EnvGetter
|
230
|
+
|
231
|
+
def initialize what
|
232
|
+
@envvar = what
|
233
|
+
end
|
234
|
+
|
235
|
+
def data_type
|
236
|
+
if @envvar == :self or @envvar == :caller
|
237
|
+
:object
|
238
|
+
else
|
239
|
+
:any
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def execute env
|
244
|
+
case @envvar
|
245
|
+
when :self
|
246
|
+
env.object
|
247
|
+
when :caller
|
248
|
+
env.caller
|
249
|
+
else
|
250
|
+
RVM::Classes[:sting].new('')
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
# This evauates to the parameter passed to the function call.
|
256
|
+
# The number of it is evaluated and typecasted to an interger.
|
257
|
+
class Parameter
|
258
|
+
def initialize num
|
259
|
+
@num = num
|
260
|
+
@type = :any
|
261
|
+
end
|
262
|
+
|
263
|
+
# The type can only be tretrieved when the num is aconstant
|
264
|
+
# as it can be evaluated without sideffect.
|
265
|
+
def data_type
|
266
|
+
@type
|
267
|
+
end
|
268
|
+
|
269
|
+
def execute env
|
270
|
+
RVM::debug "Executing Parameter..."
|
271
|
+
r = env.param(@num.execute(env).to_i)
|
272
|
+
@type = r.data_type
|
273
|
+
r
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
# A sequence is a list of commands that are executed one after
|
278
|
+
# another.
|
279
|
+
# The type of the squence is equal to the last element of the sequence.
|
280
|
+
class Sequence < Array
|
281
|
+
def execute env
|
282
|
+
RVM::debug "Executing Sequence... #{inspect}"
|
283
|
+
r = nil
|
284
|
+
self.map { |seq|
|
285
|
+
RVM::debug "Executing Sequence step..."
|
286
|
+
r = seq.execute env
|
287
|
+
}
|
288
|
+
r
|
289
|
+
end
|
290
|
+
|
291
|
+
def data_type
|
292
|
+
:any
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
# A function call or a function or a block. The initialization of a new
|
297
|
+
# enviroment is done by the function Class as it also sorts the arguments
|
298
|
+
# into it.
|
299
|
+
#
|
300
|
+
# Arguments are only executed when the function does return true for
|
301
|
+
# execargs.
|
302
|
+
class FunctionCall
|
303
|
+
attr_reader :arguments
|
304
|
+
attr_reader :function
|
305
|
+
# The constructor. *function* can either be a block object or a function
|
306
|
+
# class.
|
307
|
+
#
|
308
|
+
# Arguments is a list of the arguments to the function.
|
309
|
+
def initialize function, arguments
|
310
|
+
if function.is_a? RVM::Classes[:block]
|
311
|
+
@function = function
|
312
|
+
else
|
313
|
+
@function = RVM::Functions[function]
|
314
|
+
end
|
315
|
+
@arguments = arguments
|
316
|
+
end
|
317
|
+
|
318
|
+
def data_type
|
319
|
+
@function.data_type
|
320
|
+
end
|
321
|
+
|
322
|
+
def == v
|
323
|
+
if v.is_a? FunctionCall
|
324
|
+
(@arguments == v.arguments) && (@function == v.function)
|
325
|
+
else
|
326
|
+
false
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
def execute env
|
331
|
+
RVM::debug "Executing FunctionCall..."
|
332
|
+
args = @arguments
|
333
|
+
if @function.execargs
|
334
|
+
args = @arguments.map do |arg|
|
335
|
+
arg.execute env
|
336
|
+
end
|
337
|
+
end
|
338
|
+
@function.call(args, env)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# = languages.rb - Class Plugin Library
|
2
|
+
#
|
3
|
+
# Copyright (C) 2008 Heinz N. Gies (heinz@licenser.net)
|
4
|
+
#
|
5
|
+
# This file is published under the MIT Licenser, see LICENSE for details.
|
6
|
+
require File.dirname(__FILE__) + '/plugin'
|
7
|
+
module RVM
|
8
|
+
# This module is the basic language container, classes are supposed to be
|
9
|
+
# used by this as in: RVM::Language[<class id>] this guarnatees that,
|
10
|
+
# in the case of overwriting a standard compilers the code still works.
|
11
|
+
#
|
12
|
+
# This are *COMPILERS* not *INTERPRETERS* they compile source code to a
|
13
|
+
# VM bytecode to be executed later
|
14
|
+
#
|
15
|
+
# Have a look at RVM::Interpreter for details
|
16
|
+
#
|
17
|
+
# == Example
|
18
|
+
#
|
19
|
+
# === COMPILING A MATHEMATICAL EQUILATION
|
20
|
+
#
|
21
|
+
# require 'lib/base/languages'
|
22
|
+
# code = RVM::Classes[:math].compile '(1 + 1) * 3^2'
|
23
|
+
module Languages
|
24
|
+
extend PluginHost
|
25
|
+
plugin_path File.dirname(__FILE__), '../languages'
|
26
|
+
|
27
|
+
# The Compiler class, it holds the compiler. As those grow big I suggest
|
28
|
+
# to split the actuall code into multiple fils. Have a look at the MUSHCode
|
29
|
+
# compile for an example.
|
30
|
+
class Language
|
31
|
+
extend Plugin
|
32
|
+
plugin_host Languages
|
33
|
+
class << self
|
34
|
+
|
35
|
+
# Method placeholder, your compiler /needs/ to implement this
|
36
|
+
# or it won't work (of cause).
|
37
|
+
def compile code
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def register_for *ids
|
42
|
+
plugin_host.register self.new, *ids
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|