superchris-rubyjs 0.8.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.
- data/README +131 -0
- data/Rakefile +65 -0
- data/bin/rubyjs +144 -0
- data/rubyjs.gemspec +112 -0
- data/src/rubyjs.rb +3 -0
- data/src/rubyjs/code_generator.rb +474 -0
- data/src/rubyjs/compiler.rb +2061 -0
- data/src/rubyjs/debug_name_generator.rb +95 -0
- data/src/rubyjs/encoder.rb +171 -0
- data/src/rubyjs/eval_into.rb +59 -0
- data/src/rubyjs/lib/core.rb +1016 -0
- data/src/rubyjs/lib/dom_element.rb +66 -0
- data/src/rubyjs/lib/json.rb +101 -0
- data/src/rubyjs/lib/microunit.rb +188 -0
- data/src/rubyjs/model.rb +293 -0
- data/src/rubyjs/name_generator.rb +71 -0
- data/src/rwt/AbsolutePanel.rb +161 -0
- data/src/rwt/DOM.Konqueror.rb +89 -0
- data/src/rwt/DOM.Opera.rb +65 -0
- data/src/rwt/DOM.rb +1044 -0
- data/src/rwt/Event.Opera.rb +35 -0
- data/src/rwt/Event.rb +429 -0
- data/src/rwt/HTTPRequest.IE6.rb +5 -0
- data/src/rwt/HTTPRequest.rb +74 -0
- data/src/rwt/Label.rb +164 -0
- data/src/rwt/Panel.rb +90 -0
- data/src/rwt/RootPanel.rb +16 -0
- data/src/rwt/UIObject.rb +495 -0
- data/src/rwt/Widget.rb +193 -0
- data/src/rwt/ported-from/AbsolutePanel.java +158 -0
- data/src/rwt/ported-from/DOM.java +571 -0
- data/src/rwt/ported-from/DOMImpl.java +426 -0
- data/src/rwt/ported-from/DOMImplOpera.java +82 -0
- data/src/rwt/ported-from/DOMImplStandard.java +234 -0
- data/src/rwt/ported-from/HTTPRequest.java +81 -0
- data/src/rwt/ported-from/HTTPRequestImpl.java +103 -0
- data/src/rwt/ported-from/Label.java +163 -0
- data/src/rwt/ported-from/Panel.java +99 -0
- data/src/rwt/ported-from/UIObject.java +614 -0
- data/src/rwt/ported-from/Widget.java +221 -0
- data/test/benchmark/bm_vm1_block.rb +15 -0
- data/test/benchmark/bm_vm1_const.rb +13 -0
- data/test/benchmark/bm_vm1_ensure.rb +15 -0
- data/test/benchmark/common.rb +5 -0
- data/test/benchmark/params.yaml +7 -0
- data/test/common.Browser.rb +13 -0
- data/test/common.rb +8 -0
- data/test/gen_browser_test_suite.rb +129 -0
- data/test/gen_test_suite.rb +41 -0
- data/test/run_benchs.rb +58 -0
- data/test/run_tests.rb +22 -0
- data/test/test_args.rb +24 -0
- data/test/test_array.rb +22 -0
- data/test/test_case.rb +35 -0
- data/test/test_class.rb +55 -0
- data/test/test_eql.rb +9 -0
- data/test/test_exception.rb +61 -0
- data/test/test_expr.rb +12 -0
- data/test/test_hash.rb +29 -0
- data/test/test_hot_ruby.rb +146 -0
- data/test/test_if.rb +28 -0
- data/test/test_insertion_sort.rb +25 -0
- data/test/test_inspect.rb +10 -0
- data/test/test_lebewesen.rb +39 -0
- data/test/test_massign.rb +66 -0
- data/test/test_new.rb +12 -0
- data/test/test_range.rb +70 -0
- data/test/test_regexp.rb +22 -0
- data/test/test_send.rb +65 -0
- data/test/test_simple_output.rb +5 -0
- data/test/test_splat.rb +21 -0
- data/test/test_string.rb +51 -0
- data/test/test_test.rb +17 -0
- data/test/test_yield.rb +154 -0
- data/utils/js/Makefile +9 -0
- data/utils/js/RunScript.class +0 -0
- data/utils/js/RunScript.java +73 -0
- data/utils/js/js.jar +0 -0
- data/utils/js/run.sh +3 -0
- data/utils/jsc/Makefile +7 -0
- data/utils/jsc/README +3 -0
- data/utils/jsc/RunScript.c +93 -0
- data/utils/jsc/run.sh +15 -0
- data/utils/yuicompressor/README +1 -0
- data/utils/yuicompressor/yuicompressor-2.2.5.jar +0 -0
- metadata +157 -0
@@ -0,0 +1,95 @@
|
|
1
|
+
#
|
2
|
+
# A NameGenerator that outputs readable, non-obfuscated names.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2007 by Michael Neumann (mneumann@ntecs.de).
|
5
|
+
# All rights reserved.
|
6
|
+
#
|
7
|
+
|
8
|
+
class DebugNameGenerator
|
9
|
+
|
10
|
+
PREFIX = "___"
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@tmp_counter = 0
|
14
|
+
@cache = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def fresh
|
18
|
+
name = "tmp_" + @tmp_counter.to_s
|
19
|
+
@tmp_counter += 1
|
20
|
+
return name
|
21
|
+
end
|
22
|
+
|
23
|
+
MAP = [
|
24
|
+
%w($ dollar),
|
25
|
+
%w(? quest),
|
26
|
+
%w(+ plus),
|
27
|
+
%w(+@ uplus),
|
28
|
+
%w(- minus),
|
29
|
+
%w(-@ uminus),
|
30
|
+
%w(@ ivar),
|
31
|
+
%w(/ div),
|
32
|
+
%w(% mod),
|
33
|
+
%w(* mul),
|
34
|
+
%w(~ neg),
|
35
|
+
%w(^ xor),
|
36
|
+
%w(&& and),
|
37
|
+
%w(& band),
|
38
|
+
%w(|| or),
|
39
|
+
%w(| bor),
|
40
|
+
%w(! not),
|
41
|
+
%w(=== eqq),
|
42
|
+
%w(== eq),
|
43
|
+
%w(!== neqq),
|
44
|
+
%w(!= neq),
|
45
|
+
%w(=~ match),
|
46
|
+
%w(!~ nmatch),
|
47
|
+
%w(<<< lshift3),
|
48
|
+
%w(>>> rshift3),
|
49
|
+
%w(<< lshift),
|
50
|
+
%w(>> rshift),
|
51
|
+
%w(<= le),
|
52
|
+
%w(>= ge),
|
53
|
+
%w(> gt),
|
54
|
+
%w(< lt),
|
55
|
+
%w([]= set),
|
56
|
+
%w([] at),
|
57
|
+
%w(= eq),
|
58
|
+
%w(:: doublecolon)
|
59
|
+
]
|
60
|
+
|
61
|
+
#
|
62
|
+
# Generate a name for +name+. Return the same name for the same
|
63
|
+
# +name+.
|
64
|
+
#
|
65
|
+
|
66
|
+
def get(name)
|
67
|
+
raise unless name.is_a?(String)
|
68
|
+
|
69
|
+
if @cache[name]
|
70
|
+
return @cache[name]
|
71
|
+
else
|
72
|
+
name2 = name
|
73
|
+
MAP.each do |pat, rep|
|
74
|
+
name2 = name2.gsub(pat, PREFIX + rep)
|
75
|
+
end
|
76
|
+
|
77
|
+
if name2 !~ /^[A-Za-z_\$0-9]+$/
|
78
|
+
STDERR.puts name2
|
79
|
+
end
|
80
|
+
|
81
|
+
@cache[name] = name2
|
82
|
+
return name2
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def cache
|
87
|
+
@cache
|
88
|
+
end
|
89
|
+
|
90
|
+
def reverse_lookup(encoded_name)
|
91
|
+
@cache.index(encoded_name)
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
#
|
2
|
+
# Encode all kind of variables or symbols for Javascript generation.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2007 by Michael Neumann (mneumann@ntecs.de).
|
5
|
+
# All rights reserved.
|
6
|
+
#
|
7
|
+
|
8
|
+
if $RUBYJS__DEBUG
|
9
|
+
require 'rubyjs/debug_name_generator'
|
10
|
+
NameGenerator = DebugNameGenerator
|
11
|
+
else
|
12
|
+
require 'rubyjs/name_generator'
|
13
|
+
end
|
14
|
+
|
15
|
+
class Encoder
|
16
|
+
def initialize
|
17
|
+
@method_and_ivar_name_generator = NameGenerator.new
|
18
|
+
@attribute_name_generator = NameGenerator.new
|
19
|
+
@global_attribute_name_generator = NameGenerator.new
|
20
|
+
@global_name_generator = NameGenerator.new
|
21
|
+
reset_local_name_generator!
|
22
|
+
|
23
|
+
@unique_scope_ids = 0
|
24
|
+
end
|
25
|
+
|
26
|
+
def next_unique_scope_id()
|
27
|
+
n = @unique_scope_ids
|
28
|
+
@unique_scope_ids += 1
|
29
|
+
n
|
30
|
+
end
|
31
|
+
|
32
|
+
def reset_local_name_generator!
|
33
|
+
@local_name_generator = NameGenerator.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def with_local
|
37
|
+
reset_local_name_generator!
|
38
|
+
res = yield
|
39
|
+
reset_local_name_generator!
|
40
|
+
return res
|
41
|
+
end
|
42
|
+
|
43
|
+
def encode_nil
|
44
|
+
'nil'
|
45
|
+
end
|
46
|
+
|
47
|
+
def encode_self
|
48
|
+
'self'
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# Constants are encoded with a preceding "$". Doesn't conflict with
|
53
|
+
# methods or instance variables, as they are always used in dot
|
54
|
+
# notation while constants not.
|
55
|
+
#
|
56
|
+
# Constants use the global_name_generator. They use the same namespace
|
57
|
+
# as global variables do. There is no nameclash, as global variables
|
58
|
+
# are prefixed in Ruby with a $ character, so the generated name will
|
59
|
+
# always differ (e.g. Constant vs. $Constant leads two two different
|
60
|
+
# names).
|
61
|
+
#
|
62
|
+
def encode_constant(name)
|
63
|
+
"$" + @global_name_generator.get(name.to_s)
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Encode global variable.
|
68
|
+
#
|
69
|
+
# See encode_constant().
|
70
|
+
#
|
71
|
+
def encode_global_variable(name)
|
72
|
+
raise if name.to_s[0,1] != '$'
|
73
|
+
"$" + @global_name_generator.get(name.to_s)
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Encodes a Javascript attribute name.
|
78
|
+
# This is used in the runtime environment of RubyJS to obfuscate
|
79
|
+
# attribute names. They have their own namespace and don't clash
|
80
|
+
# with method names or instance variable names!
|
81
|
+
#
|
82
|
+
# Attributes are encoded with a preceding "a$". This doesn't conflict
|
83
|
+
# with methods or instance variables, as they use "$".
|
84
|
+
#
|
85
|
+
def encode_attr(name)
|
86
|
+
"a$" + @attribute_name_generator.get(name.to_s)
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# A global attribute. Similar to encode_attr(), but for the global
|
91
|
+
# scope (used by the def_class() Javascript function).
|
92
|
+
#
|
93
|
+
# Global attributes are encoded with a "a$" prefix. They don't
|
94
|
+
# conflict with attributes, because attributes are always used in dot
|
95
|
+
# notation, wheres global attributes not.
|
96
|
+
#
|
97
|
+
def encode_globalattr(name)
|
98
|
+
"a$" + @global_attribute_name_generator.get(name.to_s)
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
# Method names and instance variable names use the same generator.
|
103
|
+
# They don't clash, as instance variables are prefixed with '@' (not
|
104
|
+
# in the Javascript code, but in the Parse tree from Ruby). That's why
|
105
|
+
# a different symbol name is generated.
|
106
|
+
#
|
107
|
+
def encode_method(name)
|
108
|
+
"$" + @method_and_ivar_name_generator.get(name.to_s)
|
109
|
+
end
|
110
|
+
|
111
|
+
def reverse_lookup_method_name(encoded_name)
|
112
|
+
raise unless encoded_name[0,1] == "$"
|
113
|
+
@method_and_ivar_name_generator.reverse_lookup(encoded_name[1..-1])
|
114
|
+
end
|
115
|
+
|
116
|
+
def all_method_names
|
117
|
+
@method_and_ivar_name_generator.cache.each_pair do |k,v|
|
118
|
+
yield(k, "$" + v) if k[0,1] != '@'
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def encode_instance_variable(name)
|
123
|
+
raise unless name.to_s[0,1] == '@'
|
124
|
+
"$" + @method_and_ivar_name_generator.get(name.to_s)
|
125
|
+
end
|
126
|
+
|
127
|
+
def encode_local_variable(name)
|
128
|
+
"_" + @local_name_generator.get(name.to_s)
|
129
|
+
end
|
130
|
+
|
131
|
+
def encode_fresh_local_variable
|
132
|
+
"_" + @local_name_generator.fresh()
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
# Interpolate a string that contains #<...> expressions.
|
137
|
+
#
|
138
|
+
# Supported expressions:
|
139
|
+
#
|
140
|
+
# #<method()> # => encode_method("method")
|
141
|
+
# #<self> # => encode_self()
|
142
|
+
# #<nil> # => encode_nil()
|
143
|
+
# #<@ivar> # => encode_instance_variable("@ivar")
|
144
|
+
# #<Constant> # => encode_constant("Constant")
|
145
|
+
# #<attr:name> # => encode_attr("name")
|
146
|
+
# #<globalattr:name> # => encode_globalattr("name")
|
147
|
+
# #<variable> # => encode_local_variable("variable")
|
148
|
+
#
|
149
|
+
# FIXME: You can't interpolate methods like ">" yet.
|
150
|
+
#
|
151
|
+
def interpolate(str)
|
152
|
+
str = str.gsub(/#<(.*?)>/) {
|
153
|
+
case s = $1.strip
|
154
|
+
when /^(\w+)\(\)$/ then encode_method($1)
|
155
|
+
when /^m:(.+)$/ then encode_method($1)
|
156
|
+
when /^self$/ then encode_self()
|
157
|
+
when /^nil$/ then encode_nil()
|
158
|
+
when /^(@\w+)$/ then encode_instance_variable($1)
|
159
|
+
when /^(\$.+)$/ then encode_global_variable($1)
|
160
|
+
when /^([A-Z]\w*)$/ then encode_constant($1)
|
161
|
+
when /^attr:(\w+)$/ then encode_attr($1)
|
162
|
+
when /^globalattr:(\w+)$/ then encode_globalattr($1)
|
163
|
+
when /^([a-z_]\w*)$/ then encode_local_variable($1)
|
164
|
+
else
|
165
|
+
raise s
|
166
|
+
end
|
167
|
+
}
|
168
|
+
return str
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
#
|
2
|
+
# Evals into the given module_scope.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2007 by Michael Neumann (mneumann@ntecs.de).
|
5
|
+
# All rights reserved.
|
6
|
+
#
|
7
|
+
|
8
|
+
def eval_into(module_scope, &block)
|
9
|
+
$RUBYJS__MODULE_SCOPE = module_scope
|
10
|
+
$RUBYJS__LOADED ||= [] # avoids recursive loads
|
11
|
+
|
12
|
+
$RUBYJS__EVAL = proc {|str|
|
13
|
+
$RUBYJS__MODULE_SCOPE.module_eval(str)
|
14
|
+
}
|
15
|
+
|
16
|
+
# install "require" handler
|
17
|
+
alias old_require require
|
18
|
+
def require(file)
|
19
|
+
($RUBYJS__LOAD_PATH||['.']).each do |path|
|
20
|
+
name = File.expand_path(File.join(path, file + ".rb"))
|
21
|
+
if File.exists?(name)
|
22
|
+
if $RUBYJS__LOADED.include?(name)
|
23
|
+
return false
|
24
|
+
else
|
25
|
+
$RUBYJS__LOADED << name
|
26
|
+
STDERR.puts "loading file: #{name}" if $DEBUG
|
27
|
+
$RUBYJS__EVAL.call(File.read(name))
|
28
|
+
|
29
|
+
#
|
30
|
+
# load also platform specific file
|
31
|
+
# load first matching platform
|
32
|
+
#
|
33
|
+
|
34
|
+
($RUBYJS__PLATFORM||[]).each do |plat|
|
35
|
+
plat_name = File.expand_path(File.join(path, file + "." + plat + ".rb"))
|
36
|
+
next unless File.exists?(plat_name)
|
37
|
+
unless $RUBYJS__LOADED.include?(plat_name)
|
38
|
+
$RUBYJS__LOADED << plat_name
|
39
|
+
STDERR.puts "loading platform specific file: #{plat_name}" if $DEBUG
|
40
|
+
$RUBYJS__EVAL.call(File.read(plat_name))
|
41
|
+
break
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
return true
|
46
|
+
end
|
47
|
+
else
|
48
|
+
next
|
49
|
+
end
|
50
|
+
end
|
51
|
+
raise ::RuntimeError, "require: #{file} not found"
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
block.call($RUBYJS__EVAL)
|
56
|
+
|
57
|
+
# change "require" handler back to original
|
58
|
+
alias require old_require
|
59
|
+
end
|
@@ -0,0 +1,1016 @@
|
|
1
|
+
#
|
2
|
+
# Core classes for the Javascript side
|
3
|
+
#
|
4
|
+
# Copyright (c) 2007 by Michael Neumann (mneumann@ntecs.de).
|
5
|
+
# All rights reserved.
|
6
|
+
#
|
7
|
+
|
8
|
+
class Proc
|
9
|
+
OBJECT_CONSTRUCTOR__ = "Function"
|
10
|
+
|
11
|
+
def self.new(&block)
|
12
|
+
raise ArgumentError, "tried to create Proc object without a block" unless block
|
13
|
+
#
|
14
|
+
# wrap block inside another function, that catches iter_break returns.
|
15
|
+
#
|
16
|
+
`return (function() {
|
17
|
+
try {
|
18
|
+
return #<block>.#<m:call>.apply(#<block>, arguments);
|
19
|
+
} catch(e)
|
20
|
+
{
|
21
|
+
if (e instanceof #<globalattr:iter_jump>)
|
22
|
+
{
|
23
|
+
if (e.#<attr:scope> == null)
|
24
|
+
{`
|
25
|
+
raise LocalJumpError, "break from proc-closure"
|
26
|
+
`}
|
27
|
+
return e.#<attr:return_value>;
|
28
|
+
}
|
29
|
+
else throw(e);
|
30
|
+
}
|
31
|
+
})`
|
32
|
+
end
|
33
|
+
|
34
|
+
def call(*args) `
|
35
|
+
// TODO: use switch/case
|
36
|
+
if (#<args>.length == 0) return #<self>();
|
37
|
+
else if (#<args>.length == 1) return #<self>(#<args>[0]);
|
38
|
+
else return #<self>(#<args>);`
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class Boolean
|
43
|
+
OBJECT_CONSTRUCTOR__ = "Boolean"
|
44
|
+
|
45
|
+
class << self
|
46
|
+
undef_method :new
|
47
|
+
undef_method :allocate
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_s
|
51
|
+
`return (#<self> == true ? 'true' : 'false')`
|
52
|
+
end
|
53
|
+
|
54
|
+
def ==(obj)
|
55
|
+
`return (#<self> == #<obj>)`
|
56
|
+
end
|
57
|
+
|
58
|
+
=begin
|
59
|
+
def &(other)
|
60
|
+
`return (#<self> == true ? (#<other>!==#<nil> ...`
|
61
|
+
end
|
62
|
+
=end
|
63
|
+
|
64
|
+
alias inspect to_s
|
65
|
+
end
|
66
|
+
|
67
|
+
class NilClass
|
68
|
+
OBJECT_CONSTRUCTOR__ = "NilClass"
|
69
|
+
|
70
|
+
class << self
|
71
|
+
undef_method :new
|
72
|
+
undef_method :allocate
|
73
|
+
end
|
74
|
+
|
75
|
+
def nil?
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_s
|
80
|
+
""
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_i
|
84
|
+
0
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_f
|
88
|
+
0.0
|
89
|
+
end
|
90
|
+
|
91
|
+
def to_a
|
92
|
+
[]
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_splat
|
96
|
+
[]
|
97
|
+
end
|
98
|
+
|
99
|
+
def inspect
|
100
|
+
"nil"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class Class
|
105
|
+
def allocate
|
106
|
+
`return (new #<self>.#<attr:object_constructor>())`
|
107
|
+
end
|
108
|
+
|
109
|
+
def new(*args, &block)
|
110
|
+
obj = allocate()
|
111
|
+
obj.initialize(*args, &block)
|
112
|
+
obj
|
113
|
+
end
|
114
|
+
|
115
|
+
def ===(other)
|
116
|
+
eql?(other) or other.kind_of?(self)
|
117
|
+
end
|
118
|
+
|
119
|
+
def name
|
120
|
+
`return #<self>.#<attr:classname>`
|
121
|
+
end
|
122
|
+
|
123
|
+
def instance_methods
|
124
|
+
allocate.methods
|
125
|
+
end
|
126
|
+
|
127
|
+
alias inspect name
|
128
|
+
|
129
|
+
def self.new(superclass, classname, object_constructor=nil)
|
130
|
+
unless object_constructor
|
131
|
+
object_constructor = `(function() {})`
|
132
|
+
end
|
133
|
+
`return new #<self>.#<attr:object_constructor>(#<Class>, #<superclass>, #<classname>, #<object_constructor>);`
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
module Kernel
|
138
|
+
def nil?
|
139
|
+
false
|
140
|
+
end
|
141
|
+
|
142
|
+
def loop
|
143
|
+
while true
|
144
|
+
yield
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def puts(str)
|
149
|
+
str = str.to_s
|
150
|
+
`alert(#<str>); return #<nil>`
|
151
|
+
end
|
152
|
+
|
153
|
+
def p(*args)
|
154
|
+
args.each do |arg|
|
155
|
+
puts arg.inspect
|
156
|
+
end
|
157
|
+
nil
|
158
|
+
end
|
159
|
+
|
160
|
+
def method_missing(id, *args, &block)
|
161
|
+
raise NoMethodError, "undefined method `#{id}' for #{self.inspect}"
|
162
|
+
end
|
163
|
+
|
164
|
+
# id is a Javascript name, e.g. $aaa
|
165
|
+
def __invoke(id, args, &block) `
|
166
|
+
var m = #<self>[#<id>];
|
167
|
+
if (m)
|
168
|
+
return m.apply(#<self>, [#<block>].concat(#<args>));
|
169
|
+
else
|
170
|
+
return #<self>.#<m:method_missing>.apply(#<self>,
|
171
|
+
[#<block>].concat([#<globalattr:mm>[#<id>]]).concat(#<args>));`
|
172
|
+
end
|
173
|
+
|
174
|
+
# NOTE: In Ruby __send is __send__
|
175
|
+
def __send(id, *args, &block) `
|
176
|
+
var m = #<self>[#<globalattr:mm_reverse>[#<id>]];
|
177
|
+
if (m)
|
178
|
+
return m.apply(#<self>, [#<block>].concat(#<args>));
|
179
|
+
else
|
180
|
+
return #<self>.#<m:method_missing>.apply(#<self>, [#<block>].concat([#<id>]).concat(#<args>));`
|
181
|
+
end
|
182
|
+
alias send __send
|
183
|
+
|
184
|
+
def respond_to?(id) `
|
185
|
+
var m = #<globalattr:mm_reverse>[#<id>];
|
186
|
+
return (m !== undefined && #<self>[m] !== undefined && !#<self>[m].#<attr:_mm>)`
|
187
|
+
end
|
188
|
+
|
189
|
+
def proc(&block)
|
190
|
+
Proc.new(&block)
|
191
|
+
end
|
192
|
+
|
193
|
+
def raise(*args)
|
194
|
+
ex =
|
195
|
+
if args.empty?
|
196
|
+
RuntimeError.new("")
|
197
|
+
else
|
198
|
+
first = args.shift
|
199
|
+
if first.kind_of?(Class) # FIXME: subclass of Exception
|
200
|
+
first.new(*args)
|
201
|
+
elsif first.instance_of?(Exception)
|
202
|
+
if args.empty?
|
203
|
+
first
|
204
|
+
else
|
205
|
+
ArgumentError.new("to many arguments given to raise")
|
206
|
+
end
|
207
|
+
elsif first.instance_of?(String)
|
208
|
+
if args.empty?
|
209
|
+
RuntimeError.new(first)
|
210
|
+
else
|
211
|
+
ArgumentError.new("to many arguments given to raise")
|
212
|
+
end
|
213
|
+
else
|
214
|
+
TypeError.new("exception class/object expected")
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
`throw(#<ex>)`
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
class Object
|
223
|
+
include Kernel
|
224
|
+
|
225
|
+
def eql?(other)
|
226
|
+
`return (#<self>.constructor == #<other>.constructor && #<self> == #<other>)`
|
227
|
+
end
|
228
|
+
|
229
|
+
def ===(other)
|
230
|
+
eql?(other) or kind_of?(other)
|
231
|
+
end
|
232
|
+
|
233
|
+
def instance_of?(klass)
|
234
|
+
`return (#<self>.#<attr:_class> === #<klass>)`
|
235
|
+
end
|
236
|
+
|
237
|
+
def kind_of?(klass)
|
238
|
+
`return #<globalattr:kind_of>(#<self>, #<klass>)`
|
239
|
+
end
|
240
|
+
alias is_a? kind_of?
|
241
|
+
|
242
|
+
# Ruby 1.9
|
243
|
+
def tap
|
244
|
+
yield self
|
245
|
+
self
|
246
|
+
end
|
247
|
+
|
248
|
+
def initialize
|
249
|
+
end
|
250
|
+
|
251
|
+
def class
|
252
|
+
`return #<self>.#<attr:_class>`
|
253
|
+
end
|
254
|
+
|
255
|
+
def to_s
|
256
|
+
`return #<self>.toString()`
|
257
|
+
end
|
258
|
+
|
259
|
+
alias inspect to_s
|
260
|
+
alias hash to_s
|
261
|
+
|
262
|
+
def methods
|
263
|
+
methods = [];
|
264
|
+
`for (meth in #<self>) {
|
265
|
+
if (typeof #<self>[meth] == "function") {
|
266
|
+
#<methods>.push(#<globalattr:mm>[meth]);
|
267
|
+
}
|
268
|
+
}`
|
269
|
+
methods
|
270
|
+
end
|
271
|
+
|
272
|
+
def method(id)
|
273
|
+
Method.new(self, id)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
class Method
|
278
|
+
def initialize(object, method_id)
|
279
|
+
@object, @method_id = object, method_id
|
280
|
+
|
281
|
+
m = nil
|
282
|
+
`#<m> = #<object>[#<globalattr:mm_reverse>[#<method_id>]];
|
283
|
+
if (#<m>==null) #<m> = #<nil>;`
|
284
|
+
if m
|
285
|
+
@method = m
|
286
|
+
else
|
287
|
+
raise NameError, "undefined method `#{method_id}' for class `#{object.class.name}'"
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def call(*args, &block)
|
292
|
+
`return #<self>.#<@method>.apply(#<self>.#<@object>, [#<block>].concat(#<args>))`
|
293
|
+
end
|
294
|
+
|
295
|
+
def inspect
|
296
|
+
"#<Method: #{@object.class.name}##{@method_id}>"
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
module Enumerable
|
301
|
+
def map(&block)
|
302
|
+
result = []
|
303
|
+
each {|elem| result << (block ? block.call(elem) : elem) }
|
304
|
+
result
|
305
|
+
end
|
306
|
+
alias collect map
|
307
|
+
|
308
|
+
def select
|
309
|
+
result = []
|
310
|
+
each {|elem|
|
311
|
+
if yield(elem)
|
312
|
+
result << elem
|
313
|
+
end
|
314
|
+
}
|
315
|
+
result
|
316
|
+
end
|
317
|
+
alias find_all select
|
318
|
+
|
319
|
+
def reject
|
320
|
+
result = []
|
321
|
+
each {|elem|
|
322
|
+
unless yield(elem)
|
323
|
+
result << elem
|
324
|
+
end
|
325
|
+
}
|
326
|
+
result
|
327
|
+
end
|
328
|
+
|
329
|
+
def to_a
|
330
|
+
result = []
|
331
|
+
each {|elem| result << elem}
|
332
|
+
result
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
class Range
|
337
|
+
def initialize(first, last, exclude_last=false)
|
338
|
+
@first, @last = first, last
|
339
|
+
@exclude_last = exclude_last ? true : false
|
340
|
+
end
|
341
|
+
|
342
|
+
def exclude_end?
|
343
|
+
@exclude_last
|
344
|
+
end
|
345
|
+
|
346
|
+
def first
|
347
|
+
@first
|
348
|
+
end
|
349
|
+
alias begin first
|
350
|
+
|
351
|
+
def last
|
352
|
+
@last
|
353
|
+
end
|
354
|
+
alias end last
|
355
|
+
|
356
|
+
def ==(obj)
|
357
|
+
`if (#<self>.constructor != #<obj>.constructor) return false;`
|
358
|
+
@first == obj.first and @last == obj.last and @exclude_last == obj.exclude_end?
|
359
|
+
end
|
360
|
+
|
361
|
+
def eql?(obj)
|
362
|
+
`if (#<self>.constructor != #<obj>.constructor) return false;`
|
363
|
+
@first.eql?(obj.first) and @last.eql?(obj.last) and @exclude_last == obj.exclude_end?
|
364
|
+
end
|
365
|
+
|
366
|
+
def include?(obj)
|
367
|
+
return false if obj < @first
|
368
|
+
if @exclude_last
|
369
|
+
obj < @last
|
370
|
+
else
|
371
|
+
obj <= @last
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
alias member? include?
|
376
|
+
alias === include?
|
377
|
+
|
378
|
+
def each
|
379
|
+
current = @first
|
380
|
+
return if @first > @last
|
381
|
+
if @exclude_last
|
382
|
+
while current < @last
|
383
|
+
yield current
|
384
|
+
current = current.succ
|
385
|
+
end
|
386
|
+
else
|
387
|
+
while current <= @last
|
388
|
+
yield current
|
389
|
+
current = current.succ
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
def to_a
|
395
|
+
arr = []
|
396
|
+
return arr if @first > @last
|
397
|
+
current = @first
|
398
|
+
if @exclude_last
|
399
|
+
while current < @last
|
400
|
+
arr << current
|
401
|
+
current = current.succ
|
402
|
+
end
|
403
|
+
else
|
404
|
+
while current <= @last
|
405
|
+
arr << current
|
406
|
+
current = current.succ
|
407
|
+
end
|
408
|
+
end
|
409
|
+
return arr
|
410
|
+
end
|
411
|
+
|
412
|
+
def to_s
|
413
|
+
if @exclude_last
|
414
|
+
"#{@first}...#{@last}"
|
415
|
+
else
|
416
|
+
"#{@first}..#{@last}"
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
def inspect
|
421
|
+
if @exclude_last
|
422
|
+
"#{@first.inspect}...#{@last.inspect}"
|
423
|
+
else
|
424
|
+
"#{@first.inspect}..#{@last.inspect}"
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
end
|
429
|
+
|
430
|
+
class Exception
|
431
|
+
attr_reader :message
|
432
|
+
def initialize(message=nil)
|
433
|
+
if message.nil?
|
434
|
+
@message = self.class.name
|
435
|
+
else
|
436
|
+
@message = message
|
437
|
+
end
|
438
|
+
end
|
439
|
+
alias to_s message
|
440
|
+
|
441
|
+
def inspect
|
442
|
+
"#<#{self.class.name}: #{@message}>"
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
class StandardError < Exception; end
|
447
|
+
class NameError < StandardError; end
|
448
|
+
class NoMethodError < NameError; end
|
449
|
+
class RuntimeError < StandardError; end
|
450
|
+
class ArgumentError < StandardError; end
|
451
|
+
class TypeError < StandardError; end
|
452
|
+
class LocalJumpError < StandardError; end
|
453
|
+
|
454
|
+
#
|
455
|
+
# NOTE: Strings in RubyJS are immutable!!!
|
456
|
+
#
|
457
|
+
class String
|
458
|
+
OBJECT_CONSTRUCTOR__ = "String"
|
459
|
+
|
460
|
+
def +(str)
|
461
|
+
`return(#<self> + #<str>)`
|
462
|
+
end
|
463
|
+
|
464
|
+
def empty?
|
465
|
+
`return(#<self> === "")`
|
466
|
+
end
|
467
|
+
|
468
|
+
def rjust(len, pad=" ")
|
469
|
+
raise ArgumentError, "zero width padding" if pad.empty?
|
470
|
+
|
471
|
+
n = len - self.length
|
472
|
+
return self if n <= 0
|
473
|
+
|
474
|
+
fillstr = ""
|
475
|
+
`while(#<fillstr>.length < #<n>) #<fillstr> += #<pad>;`
|
476
|
+
|
477
|
+
return fillstr[0,n] + self
|
478
|
+
end
|
479
|
+
|
480
|
+
def ljust(len, pad=" ")
|
481
|
+
raise ArgumentError, "zero width padding" if pad.empty?
|
482
|
+
|
483
|
+
n = len - self.length
|
484
|
+
return self if n <= 0
|
485
|
+
|
486
|
+
fillstr = ""
|
487
|
+
`while(#<fillstr>.length < #<n>) #<fillstr> += #<pad>;`
|
488
|
+
|
489
|
+
return self + fillstr[0,n]
|
490
|
+
end
|
491
|
+
|
492
|
+
def inspect
|
493
|
+
# prototype.js
|
494
|
+
specialChar = `{
|
495
|
+
'\\b': '\\\\b',
|
496
|
+
'\\t': '\\\\t',
|
497
|
+
'\\n': '\\\\n',
|
498
|
+
'\\f': '\\\\f',
|
499
|
+
'\\r': '\\\\r',
|
500
|
+
'\\\\': '\\\\\\\\'
|
501
|
+
};`
|
502
|
+
|
503
|
+
escapedString = self.gsub(/[\x00-\x1f\\]/) {|match|
|
504
|
+
character = `#<specialChar>[#<match>]`
|
505
|
+
`return #<character> ? #<character> :
|
506
|
+
'\\\\u00' + ("0" + #<match>.charCodeAt().toString(16)).substring(0,2);`
|
507
|
+
}
|
508
|
+
|
509
|
+
`return ('"' + #<escapedString>.replace(/"/g, '\\\\"') + '"');`
|
510
|
+
end
|
511
|
+
|
512
|
+
def to_s
|
513
|
+
self
|
514
|
+
end
|
515
|
+
|
516
|
+
def strip
|
517
|
+
`return #<self>.replace(/^\\s+/, '').replace(/\\s+$/, '')`
|
518
|
+
end
|
519
|
+
|
520
|
+
def split(str)
|
521
|
+
`return #<self>.split(#<str>)`
|
522
|
+
end
|
523
|
+
|
524
|
+
def length
|
525
|
+
`return #<self>.length`
|
526
|
+
end
|
527
|
+
alias size length
|
528
|
+
|
529
|
+
def index(substring, offset=0) `
|
530
|
+
var i = #<self>.indexOf(#<substring>, #<offset>);
|
531
|
+
return (i == -1) ? #<nil> : i`
|
532
|
+
end
|
533
|
+
|
534
|
+
def =~(pattern) `
|
535
|
+
var i = #<self>.search(#<pattern>);
|
536
|
+
return (i == -1 ? #<nil> : i)`
|
537
|
+
end
|
538
|
+
|
539
|
+
def gsub(pattern, replacement=nil)
|
540
|
+
# from prototype.js
|
541
|
+
result, source, match = "", self, nil
|
542
|
+
`while(#<source>.length > 0) {
|
543
|
+
if (#<match> = #<source>.match(#<pattern>)) {
|
544
|
+
#<result> += #<source>.slice(0, #<match>.index);`
|
545
|
+
if replacement
|
546
|
+
result += replacement
|
547
|
+
else
|
548
|
+
result += yield(match.first).to_s
|
549
|
+
end
|
550
|
+
`#<source> = #<source>.slice(#<match>.index + #<match>[0].length);
|
551
|
+
} else {
|
552
|
+
#<result> += #<source>; #<source> = '';
|
553
|
+
}
|
554
|
+
} return #<result>`
|
555
|
+
end
|
556
|
+
|
557
|
+
def sub(pattern, replacement)
|
558
|
+
# FIXME: block
|
559
|
+
`#<self>.replace(pattern, replacement)`
|
560
|
+
end
|
561
|
+
|
562
|
+
def [](index, len=nil)
|
563
|
+
if len.nil?
|
564
|
+
# single character access
|
565
|
+
# FIXME: returns a string and not a Fixnum!
|
566
|
+
# But: Ruby 1.9+ has this behaviour!!!
|
567
|
+
`return #<self>.charAt(#<index>) || #<nil>`
|
568
|
+
else
|
569
|
+
# substring access
|
570
|
+
return nil if len < 0
|
571
|
+
`return #<self>.substring(#<index>, #<index>+#<len>)`
|
572
|
+
end
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
class Number
|
577
|
+
OBJECT_CONSTRUCTOR__ = "Number"
|
578
|
+
|
579
|
+
class << self
|
580
|
+
undef_method :new
|
581
|
+
undef_method :allocate
|
582
|
+
end
|
583
|
+
|
584
|
+
def to_s(base=10)
|
585
|
+
`return #<self>.toString(#<base>)`
|
586
|
+
end
|
587
|
+
|
588
|
+
def inspect
|
589
|
+
`return #<self>.toString()`
|
590
|
+
end
|
591
|
+
|
592
|
+
def +(x) `return #<self> + #<x>` end
|
593
|
+
def -(x) `return #<self> - #<x>` end
|
594
|
+
def -@() `return -#<self>` end
|
595
|
+
def +@() `return #<self>` end
|
596
|
+
def *(x) `return #<self> * #<x>` end
|
597
|
+
def /(x) `return #<self> / #<x>` end
|
598
|
+
def <(x) `return #<self> < #<x>` end
|
599
|
+
def <=(x) `return #<self> <= #<x>` end
|
600
|
+
def >(x) `return #<self> > #<x>` end
|
601
|
+
def >=(x) `return #<self> >= #<x>` end
|
602
|
+
def ==(x) `return #<self> == #<x>` end
|
603
|
+
def %(x) `return #<self> % #<x>` end
|
604
|
+
def |(x) `return #<self> | #<x>` end
|
605
|
+
def &(x) `return #<self> & #<x>` end
|
606
|
+
def ^(x) `return #<self> ^ #<x>` end
|
607
|
+
def ~() `return ~#<self>` end
|
608
|
+
|
609
|
+
def succ() `return #<self>+1` end
|
610
|
+
|
611
|
+
def times
|
612
|
+
i = 0
|
613
|
+
while i < self
|
614
|
+
yield i
|
615
|
+
i += 1
|
616
|
+
end
|
617
|
+
return self
|
618
|
+
end
|
619
|
+
|
620
|
+
def downto(x)
|
621
|
+
i = self
|
622
|
+
while i >= x
|
623
|
+
yield i
|
624
|
+
i -= 1
|
625
|
+
end
|
626
|
+
return self
|
627
|
+
end
|
628
|
+
|
629
|
+
def upto(x)
|
630
|
+
i = self
|
631
|
+
while i <= x
|
632
|
+
yield i
|
633
|
+
i += 1
|
634
|
+
end
|
635
|
+
return self
|
636
|
+
end
|
637
|
+
|
638
|
+
end
|
639
|
+
|
640
|
+
# for compatibility
|
641
|
+
class Fixnum < Number; end
|
642
|
+
class Bignum < Number; end
|
643
|
+
class Float < Number; end
|
644
|
+
|
645
|
+
#
|
646
|
+
# Every method that returns an element has to
|
647
|
+
# check this element for +null+. This is required
|
648
|
+
# to seamlessly use Javascript data without needing
|
649
|
+
# to convert it before usage.
|
650
|
+
#
|
651
|
+
# The reverse, passing a RubyJS Array to Javascript
|
652
|
+
# without conversion of +nil+ to +null+ is of course
|
653
|
+
# not possible!
|
654
|
+
#
|
655
|
+
# NOTE: Following condition holds true:
|
656
|
+
# v == null <=> v=null || v=undefined
|
657
|
+
#
|
658
|
+
class Array
|
659
|
+
OBJECT_CONSTRUCTOR__ = "Array"
|
660
|
+
|
661
|
+
include Enumerable
|
662
|
+
|
663
|
+
def each() `
|
664
|
+
var elem;
|
665
|
+
for (var i=0; i < #<self>.length; i++) {
|
666
|
+
elem = #<self>[i];`
|
667
|
+
yield `(elem == null ? #<nil> : elem)`
|
668
|
+
`}
|
669
|
+
return #<self>`
|
670
|
+
end
|
671
|
+
|
672
|
+
def each_with_index() `
|
673
|
+
var elem;
|
674
|
+
for (var i=0; i < #<self>.length; i++) {
|
675
|
+
elem = #<self>[i];`
|
676
|
+
yield `(elem == null ? #<nil> : elem)`, `i`
|
677
|
+
`}
|
678
|
+
return #<self>`
|
679
|
+
end
|
680
|
+
|
681
|
+
def join(sep="")
|
682
|
+
str = ""
|
683
|
+
self.each_with_index {|elem, i|
|
684
|
+
str += elem.to_s
|
685
|
+
str += sep if i != self.length-1
|
686
|
+
}
|
687
|
+
str
|
688
|
+
end
|
689
|
+
|
690
|
+
def to_a
|
691
|
+
self
|
692
|
+
end
|
693
|
+
|
694
|
+
def to_ary
|
695
|
+
self
|
696
|
+
end
|
697
|
+
|
698
|
+
def self.new
|
699
|
+
`return []`
|
700
|
+
end
|
701
|
+
|
702
|
+
# TODO: test that otherArray is array
|
703
|
+
def +(otherArray)
|
704
|
+
`return #<self>.concat(#<otherArray>)`
|
705
|
+
end
|
706
|
+
|
707
|
+
def dup
|
708
|
+
`return #<self>.concat()`
|
709
|
+
end
|
710
|
+
|
711
|
+
def reverse
|
712
|
+
`return #<self>.concat().reverse()`
|
713
|
+
end
|
714
|
+
|
715
|
+
def reverse!
|
716
|
+
`#<self>.reverse(); return #<self>`
|
717
|
+
end
|
718
|
+
|
719
|
+
def length
|
720
|
+
`return #<self>.length`
|
721
|
+
end
|
722
|
+
|
723
|
+
alias size length
|
724
|
+
|
725
|
+
def first
|
726
|
+
`var v = #<self>[0]; return (v == null ? #<nil> : v)`
|
727
|
+
end
|
728
|
+
|
729
|
+
def last
|
730
|
+
`var v = #<self>[#<self>.length - 1]; return (v == null ? #<nil> : v)`
|
731
|
+
end
|
732
|
+
|
733
|
+
def clear
|
734
|
+
`#<self>.length=0; return #<self>`
|
735
|
+
end
|
736
|
+
|
737
|
+
# TODO: check arrary bounds
|
738
|
+
def [](i)
|
739
|
+
`var v = #<self>[#<i>]; return (v == null ? #<nil> : v)`
|
740
|
+
end
|
741
|
+
|
742
|
+
def []=(i, val)
|
743
|
+
`return (#<self>[#<i>] = #<val>)`
|
744
|
+
end
|
745
|
+
|
746
|
+
def push(*args)
|
747
|
+
`#<self>.push.apply(#<self>, #<args>); return #<self>`
|
748
|
+
end
|
749
|
+
|
750
|
+
def <<(arg)
|
751
|
+
`#<self>.push(#<arg>); return #<self>`
|
752
|
+
end
|
753
|
+
|
754
|
+
def pop() `
|
755
|
+
var elem = #<self>.pop();
|
756
|
+
return (elem == null ? #<nil> : elem)`
|
757
|
+
end
|
758
|
+
|
759
|
+
def shift() `
|
760
|
+
var elem = #<self>.shift();
|
761
|
+
return (elem == null ? #<nil> : elem)`
|
762
|
+
end
|
763
|
+
|
764
|
+
def delete(obj) `
|
765
|
+
var del = false;
|
766
|
+
for (var i=0; i < #<self>.length; i++)
|
767
|
+
{
|
768
|
+
if (#<obj>.#<m:eql?>(#<nil>, #<self>[i]))
|
769
|
+
{
|
770
|
+
#<self>.splice(i,1);
|
771
|
+
del = true;
|
772
|
+
// stay at the current index unless we are at the last element!
|
773
|
+
if (i < #<self>.length-1) --i;
|
774
|
+
}
|
775
|
+
}
|
776
|
+
return del ? #<obj> : #<nil>`
|
777
|
+
end
|
778
|
+
|
779
|
+
def unshift(*args)
|
780
|
+
`#<self>.unshift.apply(#<self>, #<args>); return #<self>`
|
781
|
+
end
|
782
|
+
|
783
|
+
def empty?
|
784
|
+
`return (#<self>.length == 0)`
|
785
|
+
end
|
786
|
+
|
787
|
+
def to_s
|
788
|
+
map {|i| i.to_s}.join
|
789
|
+
end
|
790
|
+
|
791
|
+
def ==(obj)
|
792
|
+
self.eql?(obj)
|
793
|
+
end
|
794
|
+
|
795
|
+
def inspect
|
796
|
+
str = "["
|
797
|
+
str += self.map {|elem| elem.inspect}.join(", ")
|
798
|
+
str += "]"
|
799
|
+
str
|
800
|
+
end
|
801
|
+
|
802
|
+
def eql?(other)
|
803
|
+
each_with_index do |elem, i|
|
804
|
+
return false unless elem.eql? other[i]
|
805
|
+
end
|
806
|
+
end
|
807
|
+
|
808
|
+
def include?(candidate)
|
809
|
+
each do |elem|
|
810
|
+
return true if elem.eql?(candidate)
|
811
|
+
end
|
812
|
+
false
|
813
|
+
end
|
814
|
+
end
|
815
|
+
|
816
|
+
class Regexp
|
817
|
+
OBJECT_CONSTRUCTOR__ = "RegExp"
|
818
|
+
end
|
819
|
+
|
820
|
+
class MatchData
|
821
|
+
def initialize(match)
|
822
|
+
@match = match
|
823
|
+
end
|
824
|
+
end
|
825
|
+
|
826
|
+
#
|
827
|
+
# We prefix every element by a ":"
|
828
|
+
#
|
829
|
+
class Hash
|
830
|
+
include Enumerable
|
831
|
+
|
832
|
+
#
|
833
|
+
# Construct an empty Hash
|
834
|
+
#
|
835
|
+
def initialize() `
|
836
|
+
#<self>.#<attr:items> = {};
|
837
|
+
#<self>.#<attr:default_value> = #<nil>;
|
838
|
+
return #<nil>`
|
839
|
+
end
|
840
|
+
|
841
|
+
#
|
842
|
+
# Construct a Hash from key, value pairs, e.g.
|
843
|
+
#
|
844
|
+
# Hash.new_from_key_value_list(1,2, 3,4, 5,6)
|
845
|
+
#
|
846
|
+
# will result in
|
847
|
+
#
|
848
|
+
# {1 => 2, 3 => 4, 5 => 6}
|
849
|
+
#
|
850
|
+
def self.new_from_key_value_list(*list)
|
851
|
+
raise ArgumentError if list.length % 2 != 0
|
852
|
+
obj = allocate()
|
853
|
+
`
|
854
|
+
//
|
855
|
+
// we use an associate array to store the items. But unlike
|
856
|
+
// Javascript, the entries are arrays which contain the collisions.
|
857
|
+
// NOTE that we have to prefix the hash code with a prefix so that
|
858
|
+
// there are no collisions with methods etc.
|
859
|
+
// I prefix it for now with ":".
|
860
|
+
//
|
861
|
+
var items = {};
|
862
|
+
var hashed_key, current_key, current_val;
|
863
|
+
|
864
|
+
for (var i = 0; i < #<list>.length; i += 2)
|
865
|
+
{
|
866
|
+
current_key = #<list>[i];
|
867
|
+
current_val = #<list>[i+1];
|
868
|
+
hashed_key = ":" + current_key.#<m:hash>();
|
869
|
+
|
870
|
+
// make sure that a bucket exists
|
871
|
+
if (!items[hashed_key]) items[hashed_key] = [];
|
872
|
+
|
873
|
+
items[hashed_key].push(current_key, current_val);
|
874
|
+
}
|
875
|
+
|
876
|
+
#<obj>.#<attr:items> = items;
|
877
|
+
#<obj>.#<attr:default_value> = #<nil>;
|
878
|
+
return #<obj>;
|
879
|
+
`
|
880
|
+
end
|
881
|
+
|
882
|
+
def self.new_from_jsobject(jsobj)
|
883
|
+
obj = new()
|
884
|
+
end
|
885
|
+
|
886
|
+
def [](key) `
|
887
|
+
if (!#<self>.#<attr:items>)
|
888
|
+
{
|
889
|
+
// this is a Javascript Object, not a RubyJS Hash object.
|
890
|
+
// we directly look the key up. it's fast but not Ruby-like,
|
891
|
+
// so be careful!
|
892
|
+
|
893
|
+
var elem = #<self>[#<key>];
|
894
|
+
return (elem == null ? #<nil> : elem);
|
895
|
+
}
|
896
|
+
|
897
|
+
var hashed_key = ":" + #<key>.#<m:hash>();
|
898
|
+
var bucket = #<self>.#<attr:items>[hashed_key];
|
899
|
+
|
900
|
+
if (bucket)
|
901
|
+
{
|
902
|
+
//
|
903
|
+
// find the matching element inside the bucket
|
904
|
+
//
|
905
|
+
|
906
|
+
for (var i = 0; i < bucket.length; i += 2)
|
907
|
+
{
|
908
|
+
if (bucket[i].#<m:eql?>(#<nil>,#<key>))
|
909
|
+
return bucket[i+1];
|
910
|
+
}
|
911
|
+
}
|
912
|
+
|
913
|
+
// no matching key found -> return default value
|
914
|
+
return #<self>.#<attr:default_value>;
|
915
|
+
`
|
916
|
+
end
|
917
|
+
|
918
|
+
def []=(key, value) `
|
919
|
+
if (!#<self>.#<attr:items>)
|
920
|
+
{
|
921
|
+
// this is a Javascript Object, not a RubyJS Hash object.
|
922
|
+
// we directly look the key up. it's fast but not Ruby-like,
|
923
|
+
// so be careful!
|
924
|
+
|
925
|
+
#<self>[#<key>] = #<value>;
|
926
|
+
return #<value>;
|
927
|
+
}
|
928
|
+
|
929
|
+
var hashed_key = ":" + #<key>.#<m:hash>();
|
930
|
+
var bucket = #<self>.#<attr:items>[hashed_key];
|
931
|
+
|
932
|
+
if (bucket)
|
933
|
+
{
|
934
|
+
//
|
935
|
+
// find the matching element inside the bucket
|
936
|
+
//
|
937
|
+
|
938
|
+
for (var i = 0; i < bucket.length; i += 2)
|
939
|
+
{
|
940
|
+
if (bucket[i].#<m:eql?>(#<nil>,#<key>))
|
941
|
+
{
|
942
|
+
// overwrite value
|
943
|
+
bucket[i+1] = #<value>;
|
944
|
+
return #<value>;
|
945
|
+
}
|
946
|
+
}
|
947
|
+
// key not found in this bucket. append key, value pair to bucket
|
948
|
+
bucket.push(#<key>, #<value>);
|
949
|
+
}
|
950
|
+
else
|
951
|
+
{
|
952
|
+
//
|
953
|
+
// create new bucket
|
954
|
+
//
|
955
|
+
#<self>.#<attr:items>[hashed_key] = [#<key>, #<value>];
|
956
|
+
}
|
957
|
+
return #<value>;
|
958
|
+
`
|
959
|
+
end
|
960
|
+
|
961
|
+
def keys
|
962
|
+
map {|k,v| k}
|
963
|
+
end
|
964
|
+
|
965
|
+
def values
|
966
|
+
map {|k,v| v}
|
967
|
+
end
|
968
|
+
|
969
|
+
def each() `
|
970
|
+
if (!#<self>.#<attr:items>)
|
971
|
+
{
|
972
|
+
// this is a Javascript Object, not a RubyJS Hash object.
|
973
|
+
// we directly look the key up. it's fast but not Ruby-like,
|
974
|
+
// so be careful!
|
975
|
+
var key, value;
|
976
|
+
for (key in #<self>)
|
977
|
+
{
|
978
|
+
value = #<self>[key];`
|
979
|
+
yield `(key == null ? #<nil> : key)`, `(value == null ? #<nil> : value)`;
|
980
|
+
`
|
981
|
+
}
|
982
|
+
|
983
|
+
return #<nil>;
|
984
|
+
}
|
985
|
+
|
986
|
+
var key, bucket, i;
|
987
|
+
for (key in #<self>.#<attr:items>)
|
988
|
+
{
|
989
|
+
if (key.charAt(0) == ":")
|
990
|
+
{
|
991
|
+
bucket = #<self>.#<attr:items>[key];
|
992
|
+
for (i=0; i<bucket.length; i+=2)
|
993
|
+
{`
|
994
|
+
yield `bucket[i]`, `bucket[i+1]`
|
995
|
+
`
|
996
|
+
}
|
997
|
+
}
|
998
|
+
}
|
999
|
+
return #<nil>;
|
1000
|
+
`
|
1001
|
+
end
|
1002
|
+
|
1003
|
+
def inspect
|
1004
|
+
str = "{"
|
1005
|
+
str += map {|k, v| (k.inspect + "=>" + v.inspect) }.join(", ")
|
1006
|
+
str += "}"
|
1007
|
+
str
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
def to_s
|
1011
|
+
strs = []
|
1012
|
+
each {|k, v| strs << k; strs << v}
|
1013
|
+
strs.join("")
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
end
|