rVM 0.0.1
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.
- 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
|