opal 0.3.11 → 0.3.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (282) hide show
  1. data/.gitignore +13 -0
  2. data/Gemfile +10 -0
  3. data/LICENSE +20 -0
  4. data/README.md +11 -116
  5. data/Rakefile +126 -0
  6. data/bin/opal +1 -2
  7. data/docs/spec_runner.html +16 -0
  8. data/index.html +434 -0
  9. data/lib/opal.rb +14 -15
  10. data/lib/opal/builder.rb +46 -148
  11. data/lib/opal/command.rb +45 -115
  12. data/lib/opal/context.rb +139 -78
  13. data/lib/opal/dependency_builder.rb +34 -0
  14. data/lib/opal/environment.rb +92 -0
  15. data/lib/opal/parser/grammar.rb +4915 -0
  16. data/lib/opal/{parser.y → parser/grammar.y} +430 -284
  17. data/lib/opal/parser/lexer.rb +1329 -0
  18. data/lib/opal/parser/parser.rb +1460 -0
  19. data/lib/opal/parser/scope.rb +140 -0
  20. data/lib/opal/parser/sexp.rb +17 -0
  21. data/lib/opal/version.rb +2 -1
  22. data/opal.gemspec +23 -0
  23. data/opal.js +3149 -4162
  24. data/runtime/README.md +25 -0
  25. data/runtime/corelib/alpha.rb +10 -0
  26. data/runtime/corelib/array.rb +962 -0
  27. data/runtime/corelib/basic_object.rb +66 -0
  28. data/runtime/corelib/boolean.rb +44 -0
  29. data/runtime/corelib/class.rb +43 -0
  30. data/runtime/corelib/comparable.rb +25 -0
  31. data/runtime/corelib/complex.rb +2 -0
  32. data/runtime/corelib/dir.rb +29 -0
  33. data/runtime/corelib/enumerable.rb +316 -0
  34. data/runtime/corelib/enumerator.rb +80 -0
  35. data/runtime/corelib/error.rb +25 -0
  36. data/runtime/corelib/file.rb +80 -0
  37. data/runtime/corelib/hash.rb +503 -0
  38. data/runtime/corelib/io.rb +44 -0
  39. data/runtime/corelib/kernel.rb +237 -0
  40. data/runtime/corelib/load_order +29 -0
  41. data/runtime/corelib/match_data.rb +37 -0
  42. data/runtime/corelib/module.rb +171 -0
  43. data/runtime/corelib/native.rb +50 -0
  44. data/runtime/corelib/nil_class.rb +47 -0
  45. data/runtime/corelib/numeric.rb +219 -0
  46. data/runtime/corelib/object.rb +21 -0
  47. data/runtime/corelib/proc.rb +42 -0
  48. data/runtime/corelib/range.rb +38 -0
  49. data/runtime/corelib/rational.rb +16 -0
  50. data/runtime/corelib/regexp.rb +63 -0
  51. data/runtime/corelib/string.rb +185 -0
  52. data/runtime/corelib/struct.rb +97 -0
  53. data/runtime/corelib/time.rb +196 -0
  54. data/runtime/corelib/top_self.rb +7 -0
  55. data/runtime/gemlib/alpha.rb +5 -0
  56. data/runtime/gemlib/kernel.rb +17 -0
  57. data/runtime/gemlib/load_order +2 -0
  58. data/runtime/kernel/class.js +256 -0
  59. data/runtime/kernel/debug.js +42 -0
  60. data/runtime/kernel/init.js +114 -0
  61. data/runtime/kernel/load_order +5 -0
  62. data/runtime/kernel/loader.js +151 -0
  63. data/runtime/kernel/runtime.js +414 -0
  64. data/runtime/spec/README.md +34 -0
  65. data/runtime/spec/core/array/allocate_spec.rb +15 -0
  66. data/runtime/spec/core/array/append_spec.rb +31 -0
  67. data/runtime/spec/core/array/assoc_spec.rb +29 -0
  68. data/runtime/spec/core/array/at_spec.rb +38 -0
  69. data/runtime/spec/core/array/clear_spec.rb +22 -0
  70. data/runtime/spec/core/array/collect_spec.rb +3 -0
  71. data/runtime/spec/core/array/compact_spec.rb +42 -0
  72. data/runtime/spec/core/array/concat_spec.rb +21 -0
  73. data/runtime/spec/core/array/constructor_spec.rb +24 -0
  74. data/runtime/spec/core/array/count_spec.rb +11 -0
  75. data/runtime/spec/core/array/delete_at_spec.rb +31 -0
  76. data/runtime/spec/core/array/delete_if_spec.rb +24 -0
  77. data/runtime/spec/core/array/delete_spec.rb +26 -0
  78. data/runtime/spec/core/array/each_index_spec.rb +33 -0
  79. data/runtime/spec/core/array/each_spec.rb +11 -0
  80. data/runtime/spec/core/array/element_reference_spec.rb +136 -0
  81. data/runtime/spec/core/array/element_set_spec.rb +7 -0
  82. data/runtime/spec/core/array/empty_spec.rb +10 -0
  83. data/runtime/spec/core/array/eql_spec.rb +3 -0
  84. data/runtime/spec/core/array/equal_value_spec.rb +3 -0
  85. data/runtime/spec/core/array/fetch_spec.rb +26 -0
  86. data/runtime/spec/core/array/first_spec.rb +54 -0
  87. data/runtime/spec/core/array/fixtures/classes.rb +8 -0
  88. data/runtime/spec/core/array/flatten_spec.rb +41 -0
  89. data/runtime/spec/core/array/include_spec.rb +20 -0
  90. data/runtime/spec/core/array/insert_spec.rb +59 -0
  91. data/runtime/spec/core/array/last_spec.rb +57 -0
  92. data/runtime/spec/core/array/length_spec.rb +3 -0
  93. data/runtime/spec/core/array/map_spec.rb +3 -0
  94. data/runtime/spec/core/array/plus_spec.rb +16 -0
  95. data/runtime/spec/core/array/pop_spec.rb +79 -0
  96. data/runtime/spec/core/array/push_spec.rb +19 -0
  97. data/runtime/spec/core/array/rassoc_spec.rb +12 -0
  98. data/runtime/spec/core/array/reject_spec.rb +54 -0
  99. data/runtime/spec/core/array/replace_spec.rb +3 -0
  100. data/runtime/spec/core/array/reverse_each_spec.rb +18 -0
  101. data/runtime/spec/core/array/reverse_spec.rb +9 -0
  102. data/runtime/spec/core/array/shared/collect.rb +53 -0
  103. data/runtime/spec/core/array/shared/eql.rb +19 -0
  104. data/runtime/spec/core/array/shared/length.rb +6 -0
  105. data/runtime/spec/core/array/shared/replace.rb +31 -0
  106. data/runtime/spec/core/class/new_spec.rb +19 -0
  107. data/runtime/spec/core/enumerable/all_spec.rb +102 -0
  108. data/runtime/spec/core/enumerable/any_spec.rb +115 -0
  109. data/runtime/spec/core/enumerable/collect_spec.rb +3 -0
  110. data/runtime/spec/core/enumerable/count_spec.rb +29 -0
  111. data/runtime/spec/core/enumerable/detect_spec.rb +3 -0
  112. data/runtime/spec/core/enumerable/find_spec.rb +3 -0
  113. data/runtime/spec/core/enumerable/fixtures/classes.rb +26 -0
  114. data/runtime/spec/core/enumerable/shared/collect.rb +12 -0
  115. data/runtime/spec/core/enumerable/shared/entries.rb +7 -0
  116. data/runtime/spec/core/enumerable/shared/find.rb +49 -0
  117. data/runtime/spec/core/enumerable/to_a_spec.rb +7 -0
  118. data/runtime/spec/core/false/and_spec.rb +11 -0
  119. data/runtime/spec/core/false/inspect_spec.rb +7 -0
  120. data/runtime/spec/core/false/or_spec.rb +11 -0
  121. data/runtime/spec/core/false/to_s_spec.rb +7 -0
  122. data/runtime/spec/core/false/xor_spec.rb +11 -0
  123. data/runtime/spec/core/hash/allocate_spec.rb +15 -0
  124. data/runtime/spec/core/hash/assoc_spec.rb +29 -0
  125. data/runtime/spec/core/hash/clear_spec.rb +21 -0
  126. data/runtime/spec/core/hash/clone_spec.rb +12 -0
  127. data/runtime/spec/core/hash/default_spec.rb +6 -0
  128. data/runtime/spec/core/hash/delete_if_spec.rb +15 -0
  129. data/runtime/spec/core/hash/element_reference_spec.rb +16 -0
  130. data/runtime/spec/core/hash/element_set_spec.rb +8 -0
  131. data/runtime/spec/core/hash/new_spec.rb +13 -0
  132. data/runtime/spec/core/matchdata/to_a_spec.rb +7 -0
  133. data/runtime/spec/core/nil/and_spec.rb +12 -0
  134. data/runtime/spec/core/nil/inspect_spec.rb +8 -0
  135. data/runtime/spec/core/nil/nil_spec.rb +8 -0
  136. data/runtime/spec/core/nil/or_spec.rb +12 -0
  137. data/runtime/spec/core/nil/to_a_spec.rb +8 -0
  138. data/runtime/spec/core/nil/to_f_spec.rb +12 -0
  139. data/runtime/spec/core/nil/to_i_spec.rb +12 -0
  140. data/runtime/spec/core/nil/to_s_spec.rb +8 -0
  141. data/runtime/spec/core/nil/xor_spec.rb +12 -0
  142. data/runtime/spec/core/numeric/equal_value_spec.rb +11 -0
  143. data/runtime/spec/core/object/is_a_spec.rb +2 -0
  144. data/runtime/spec/core/object/shared/kind_of.rb +0 -0
  145. data/runtime/spec/core/regexp/match_spec.rb +23 -0
  146. data/runtime/spec/core/regexp/shared/match.rb +11 -0
  147. data/runtime/spec/core/symbol/to_proc_spec.rb +8 -0
  148. data/runtime/spec/core/true/and_spec.rb +11 -0
  149. data/runtime/spec/core/true/inspect_spec.rb +7 -0
  150. data/runtime/spec/core/true/or_spec.rb +11 -0
  151. data/runtime/spec/core/true/to_s_spec.rb +7 -0
  152. data/runtime/spec/core/true/xor_spec.rb +11 -0
  153. data/runtime/spec/language/alias_spec.rb +25 -0
  154. data/runtime/spec/language/and_spec.rb +62 -0
  155. data/runtime/spec/language/array_spec.rb +68 -0
  156. data/runtime/spec/language/block_spec.rb +105 -0
  157. data/runtime/spec/language/break_spec.rb +49 -0
  158. data/runtime/spec/language/case_spec.rb +165 -0
  159. data/runtime/spec/language/defined_spec.rb +80 -0
  160. data/runtime/spec/language/ensure_spec.rb +82 -0
  161. data/runtime/spec/language/fixtures/block.rb +19 -0
  162. data/runtime/spec/language/fixtures/break.rb +39 -0
  163. data/runtime/spec/language/fixtures/defined.rb +9 -0
  164. data/runtime/spec/language/fixtures/ensure.rb +37 -0
  165. data/runtime/spec/language/fixtures/next.rb +46 -0
  166. data/runtime/spec/language/fixtures/send.rb +36 -0
  167. data/runtime/spec/language/fixtures/super.rb +43 -0
  168. data/runtime/spec/language/hash_spec.rb +43 -0
  169. data/runtime/spec/language/if_spec.rb +278 -0
  170. data/runtime/spec/language/loop_spec.rb +32 -0
  171. data/runtime/spec/language/next_spec.rb +128 -0
  172. data/runtime/spec/language/or_spec.rb +65 -0
  173. data/runtime/spec/language/predefined_spec.rb +21 -0
  174. data/runtime/spec/language/regexp/interpolation_spec.rb +9 -0
  175. data/runtime/spec/language/regexp_spec.rb +7 -0
  176. data/runtime/spec/language/send_spec.rb +105 -0
  177. data/runtime/spec/language/string_spec.rb +4 -0
  178. data/runtime/spec/language/super_spec.rb +18 -0
  179. data/runtime/spec/language/symbol_spec.rb +41 -0
  180. data/runtime/spec/language/undef_spec.rb +16 -0
  181. data/runtime/spec/language/unless_spec.rb +44 -0
  182. data/runtime/spec/language/until_spec.rb +137 -0
  183. data/runtime/spec/language/variables_spec.rb +28 -0
  184. data/runtime/spec/language/versions/hash_1.9.rb +20 -0
  185. data/runtime/spec/language/while_spec.rb +175 -0
  186. data/runtime/spec/library/stringscanner/scan_spec.rb +36 -0
  187. data/runtime/spec/opal/forwardable/def_instance_delegator_spec.rb +49 -0
  188. data/runtime/spec/opal/opal/defined_spec.rb +15 -0
  189. data/runtime/spec/opal/opal/function_spec.rb +11 -0
  190. data/runtime/spec/opal/opal/native_spec.rb +16 -0
  191. data/runtime/spec/opal/opal/null_spec.rb +10 -0
  192. data/runtime/spec/opal/opal/number_spec.rb +11 -0
  193. data/runtime/spec/opal/opal/object_spec.rb +16 -0
  194. data/runtime/spec/opal/opal/string_spec.rb +11 -0
  195. data/runtime/spec/opal/opal/typeof_spec.rb +9 -0
  196. data/runtime/spec/opal/opal/undefined_spec.rb +10 -0
  197. data/runtime/spec/opal/true/case_compare_spec.rb +12 -0
  198. data/runtime/spec/opal/true/class_spec.rb +10 -0
  199. data/runtime/spec/spec_helper.rb +25 -0
  200. data/runtime/stdlib/base64.rb +91 -0
  201. data/runtime/stdlib/date.rb +4 -0
  202. data/{stdlib → runtime/stdlib}/dev.rb +0 -0
  203. data/runtime/stdlib/forwardable.rb +33 -0
  204. data/runtime/stdlib/optparse.rb +0 -0
  205. data/runtime/stdlib/pp.rb +6 -0
  206. data/{stdlib → runtime/stdlib}/racc/parser.rb +0 -0
  207. data/runtime/stdlib/rbconfig.rb +0 -0
  208. data/runtime/stdlib/si.rb +17 -0
  209. data/runtime/stdlib/strscan.rb +53 -0
  210. data/runtime/stdlib/uri.rb +111 -0
  211. data/runtime/stdlib/uri/common.rb +1014 -0
  212. data/runtime/stdlib/uri/ftp.rb +261 -0
  213. data/runtime/stdlib/uri/generic.rb +1599 -0
  214. data/runtime/stdlib/uri/http.rb +106 -0
  215. data/runtime/stdlib/uri/https.rb +22 -0
  216. data/runtime/stdlib/uri/ldap.rb +260 -0
  217. data/runtime/stdlib/uri/ldaps.rb +20 -0
  218. data/runtime/stdlib/uri/mailto.rb +280 -0
  219. data/spec/builder/build_source_spec.rb +52 -0
  220. data/spec/builder/fixtures/build_source/adam.rb +0 -0
  221. data/spec/builder/fixtures/build_source/bar/a.rb +0 -0
  222. data/spec/builder/fixtures/build_source/bar/wow/b.rb +0 -0
  223. data/spec/builder/fixtures/build_source/bar/wow/cow/c.rb +0 -0
  224. data/spec/builder/fixtures/build_source/beynon.rb +0 -0
  225. data/spec/builder/fixtures/build_source/charles.js +0 -0
  226. data/spec/builder/fixtures/build_source/foo/a.rb +0 -0
  227. data/spec/builder/fixtures/build_source/foo/b.rb +0 -0
  228. data/spec/builder/fixtures/build_source/foo/x.js +0 -0
  229. data/spec/builder/fixtures/build_source/foo/y.js +0 -0
  230. data/spec/builder/output_path_spec.rb +50 -0
  231. data/spec/grammar/alias_spec.rb +26 -0
  232. data/spec/grammar/and_spec.rb +13 -0
  233. data/spec/grammar/array_spec.rb +22 -0
  234. data/spec/grammar/attrasgn_spec.rb +28 -0
  235. data/spec/grammar/begin_spec.rb +38 -0
  236. data/spec/grammar/block_spec.rb +12 -0
  237. data/spec/grammar/break_spec.rb +17 -0
  238. data/spec/grammar/call_spec.rb +58 -0
  239. data/spec/grammar/class_spec.rb +35 -0
  240. data/spec/grammar/const_spec.rb +13 -0
  241. data/spec/grammar/cvar_spec.rb +11 -0
  242. data/spec/grammar/def_spec.rb +60 -0
  243. data/spec/grammar/false_spec.rb +17 -0
  244. data/spec/grammar/file_spec.rb +7 -0
  245. data/spec/grammar/gvar_spec.rb +13 -0
  246. data/spec/grammar/hash_spec.rb +17 -0
  247. data/spec/grammar/iasgn_spec.rb +9 -0
  248. data/spec/grammar/if_spec.rb +26 -0
  249. data/spec/grammar/iter_spec.rb +59 -0
  250. data/spec/grammar/ivar_spec.rb +9 -0
  251. data/spec/grammar/lasgn_spec.rb +8 -0
  252. data/spec/grammar/line_spec.rb +8 -0
  253. data/spec/grammar/lvar_spec.rb +38 -0
  254. data/spec/grammar/module_spec.rb +27 -0
  255. data/spec/grammar/nil_spec.rb +17 -0
  256. data/spec/grammar/not_spec.rb +27 -0
  257. data/spec/grammar/op_asgn1_spec.rb +23 -0
  258. data/spec/grammar/op_asgn2_spec.rb +23 -0
  259. data/spec/grammar/or_spec.rb +13 -0
  260. data/spec/grammar/return_spec.rb +17 -0
  261. data/spec/grammar/sclass_spec.rb +20 -0
  262. data/spec/grammar/self_spec.rb +17 -0
  263. data/spec/grammar/str_spec.rb +96 -0
  264. data/spec/grammar/super_spec.rb +20 -0
  265. data/spec/grammar/true_spec.rb +17 -0
  266. data/spec/grammar/undef_spec.rb +15 -0
  267. data/spec/grammar/unless_spec.rb +13 -0
  268. data/spec/grammar/while_spec.rb +15 -0
  269. data/spec/grammar/xstr_spec.rb +116 -0
  270. data/spec/grammar/yield_spec.rb +20 -0
  271. data/spec/spec_helper.rb +9 -0
  272. metadata +346 -21
  273. data/lib/opal/bundle.rb +0 -34
  274. data/lib/opal/lexer.rb +0 -902
  275. data/lib/opal/nodes.rb +0 -2150
  276. data/lib/opal/parser.rb +0 -4894
  277. data/lib/opal/rake/bundle_task.rb +0 -63
  278. data/opal-parser.js +0 -8343
  279. data/stdlib/strscan.rb +0 -52
  280. data/templates/init/Rakefile +0 -7
  281. data/templates/init/index.html +0 -17
  282. data/templates/init/lib/__NAME__.rb +0 -2
@@ -0,0 +1,1460 @@
1
+ require 'opal/parser/lexer'
2
+ require 'opal/parser/grammar'
3
+ require 'opal/parser/scope'
4
+
5
+ module Opal
6
+ class OpalParseError < Exception; end
7
+
8
+ class Parser
9
+ def self.to_syms(ary)
10
+ ary.map &:to_sym
11
+ end
12
+
13
+ INDENT = ' '
14
+
15
+ LEVEL = to_syms(%w[statement statement_closure list expression receiver])
16
+
17
+ # Maths operators
18
+ MATH = %w(+ - / * %)
19
+
20
+ # Comparison operators
21
+ COMPARE = %w(< <= > >=)
22
+
23
+ # All operators that can be optimized in method calls
24
+ CALL_OPERATORS = MATH + COMPARE
25
+
26
+ # Reserved javascript keywords - we cannot create variables with the
27
+ # same name
28
+ RESERVED = %w(
29
+ break case catch continue debugger default delete do else finally for
30
+ function if in instanceof new return switch this throw try typeof var let
31
+ void while with class enum export extends import super true false native
32
+ const
33
+ )
34
+
35
+ METHOD_NAMES = {
36
+ :== => 'eq',
37
+ :=== => 'eqq',
38
+ :[] => 'aref',
39
+ :[]= => 'aset',
40
+ :~ => 'tild',
41
+ :<=> => 'cmp',
42
+ :=~ => 'match',
43
+ :+ => 'plus',
44
+ :- => 'minus',
45
+ :/ => 'div',
46
+ :* => 'mul',
47
+ :< => 'lt',
48
+ :<= => 'le',
49
+ :> => 'gt',
50
+ :>= => 'ge',
51
+ :<< => 'lshft',
52
+ :>> => 'rshft',
53
+ :| => 'or',
54
+ :& => 'and',
55
+ :^ => 'xor',
56
+ :+@ => 'uplus',
57
+ :-@ => 'uminus',
58
+ :% => 'mod',
59
+ :** => 'pow'
60
+ }
61
+
62
+ # Type info for flags of objects. This helps identify the type of object
63
+ # being dealt with
64
+ TYPES = {
65
+ class: 0x0001,
66
+ module: 0x0002,
67
+ object: 0x0004,
68
+ boolean: 0x0008,
69
+ string: 0x0010,
70
+ array: 0x0020,
71
+ number: 0x0040,
72
+ proc: 0x0080,
73
+ hash: 0x0100,
74
+ range: 0x0200,
75
+ iclass: 0x0400,
76
+ singleton: 0x0800
77
+ }
78
+
79
+ STATEMENTS = to_syms(%w[xstr dxstr])
80
+
81
+ def initialize(opts = {})
82
+ @debug = opts[:debug] or false
83
+ end
84
+
85
+ def parse(source, file = '(file)')
86
+ @file = file
87
+ @helpers = {
88
+ :breaker => true, :no_proc => true, :klass => true, :defn => true, :defs => true, :const_get => true,
89
+ :slice => true
90
+ }
91
+
92
+ parser = Grammar.new
93
+ reset
94
+
95
+ begin
96
+ top parser.parse(source, file)
97
+ rescue Exception => e
98
+ raise OpalParseError.new("#{e.message}\nfrom parsing #{file}:#{parser.line}")
99
+ end
100
+ end
101
+
102
+ def s(*parts)
103
+ sexp = Sexp.new *parts
104
+ sexp.line = @line
105
+ sexp
106
+ end
107
+
108
+ def reset
109
+ @line = 1
110
+ @indent = ''
111
+ @unique = 0
112
+ end
113
+
114
+ def mid_to_jsid(mid)
115
+ 'm$' + if name = METHOD_NAMES[mid.to_sym]
116
+ name + '$'
117
+ else
118
+ mid.sub('!', '$b').sub('?', '$p').sub('=', '$e')
119
+ end
120
+ end
121
+
122
+ # guaranteed unique id per file..
123
+ def unique_temp
124
+ "$TMP_#{@unique += 1}"
125
+ end
126
+
127
+ def top(sexp, options = {})
128
+ code = nil
129
+ vars = []
130
+
131
+ in_scope(:top) do
132
+ code = process s(:scope, sexp), :statement
133
+
134
+ vars << "FILE = $opal.FILE" if @uses_file
135
+ vars << "nil = $opal.nil"
136
+ vars << "$const = $opal.constants"
137
+ vars.concat @scope.locals.map { |t| "#{t}" }
138
+ vars.concat @scope.temps.map { |t| t }
139
+ vars.concat @helpers.keys.map { |h| "$#{h} = $opal.#{h}" }
140
+
141
+ code = "var #{vars.join ', '};" + code unless vars.empty?
142
+ end
143
+
144
+ pre = "(function($opal) {"
145
+ post = ""
146
+
147
+ uniques = []
148
+
149
+ @unique.times { |i| uniques << "$TMP_#{i+1}" }
150
+
151
+ unless uniques.empty?
152
+ post += ";var #{uniques.join ', '};"
153
+ end
154
+
155
+ post += "\n}).call(opal.top, opal);"
156
+
157
+ pre + code + post
158
+ end
159
+
160
+ def in_scope(type)
161
+ return unless block_given?
162
+
163
+ parent = @scope
164
+ @scope = Scope.new(type).tap { |s| s.parent = parent }
165
+ yield @scope
166
+
167
+ @scope = parent
168
+ end
169
+
170
+ def indent(&block)
171
+ indent = @indent
172
+ @indent += INDENT
173
+ res = yield
174
+ @indent = indent
175
+ res
176
+ end
177
+
178
+ # Used when we enter a while statement. This pushes onto the current
179
+ # scope's while stack so we know how to handle break, next etc.
180
+ #
181
+ # Usage:
182
+ #
183
+ # in_while do
184
+ # # generate while body here.
185
+ # end
186
+ def in_while
187
+ return unless block_given?
188
+ @while_loop = @scope.push_while
189
+ result = yield
190
+ @scope.pop_while
191
+
192
+ result
193
+ end
194
+
195
+ def in_while?
196
+ @scope.in_while?
197
+ end
198
+
199
+ def process(sexp, level)
200
+ type = sexp.shift
201
+
202
+ raise "Unsupported sexp: #{type}" unless respond_to? type
203
+
204
+ line = fix_line sexp.line
205
+ code = __send__ type, sexp, level
206
+ line + code
207
+ end
208
+
209
+ def fix_line(line)
210
+ res = ""
211
+
212
+ if @line < line
213
+ res = "\n" * (line - @line)
214
+ res += @indent
215
+ @line = line
216
+ end
217
+ res
218
+ end
219
+
220
+ def returns(sexp)
221
+ return returns s(:nil) unless sexp
222
+
223
+ case sexp.first
224
+ when :break, :next
225
+ sexp
226
+ when :scope
227
+ sexp
228
+ when :block
229
+ if sexp.length > 1
230
+ sexp[-1] = returns sexp[-1]
231
+ else
232
+ sexp << returns(s(:nil))
233
+ end
234
+ sexp
235
+ when :when
236
+ sexp[2] = returns(sexp[2])
237
+ sexp
238
+ when :ensure
239
+ sexp[1] = returns sexp[1]
240
+ sexp
241
+ when :while
242
+ sexp[2] = returns(sexp[2])
243
+ sexp
244
+ when :return
245
+ sexp
246
+ when :xstr
247
+ sexp[1] = "return #{sexp[1]};" unless /return|;/ =~ sexp[1]
248
+ sexp
249
+ when :dxstr
250
+ sexp[1] = "return #{sexp[1]}" unless /return|;|\n/ =~ sexp[1]
251
+ sexp
252
+ when :if
253
+ sexp[2] = returns(sexp[2] || s(:nil))
254
+ sexp[3] = returns(sexp[3] || s(:nil))
255
+ sexp
256
+ else
257
+ s(:js_return, sexp).tap { |s|
258
+ s.line = sexp.line
259
+ }
260
+ end
261
+ end
262
+
263
+ def expression?(sexp)
264
+ !STATEMENTS.include?(sexp.first)
265
+ end
266
+
267
+ def block(sexp, level)
268
+ result = []
269
+ sexp << s(:nil) if sexp.empty?
270
+
271
+ until sexp.empty?
272
+ stmt = sexp.shift
273
+ expr = expression?(stmt) and LEVEL.index(level) < LEVEL.index(:list)
274
+ result << process(stmt, level)
275
+ result << ";" if expr
276
+ end
277
+
278
+ result.join
279
+ end
280
+
281
+ def scope(sexp, level)
282
+ stmt = sexp.shift
283
+ stmt = returns stmt unless @scope.donates_methods
284
+ code = process stmt, :statement
285
+
286
+ code
287
+ end
288
+
289
+ # s(:js_return, sexp)
290
+ def js_return(sexp, level)
291
+ "return #{process sexp.shift, :expression}"
292
+ end
293
+
294
+ # s(:js_tmp, str)
295
+ def js_tmp(sexp, level)
296
+ sexp.shift.to_s
297
+ end
298
+
299
+ # s(:js_block_given)
300
+ def js_block_given(sexp, level)
301
+ @scope.uses_block!
302
+ "$block_given"
303
+ end
304
+
305
+ def js_operator_call(sexp, level)
306
+ recv = sexp[0]
307
+ meth = sexp[1]
308
+ arglist = sexp[2]
309
+ mid = mid_to_jsid meth.to_s
310
+
311
+ a = @scope.new_temp
312
+ b = @scope.new_temp
313
+ l = process recv, :expression
314
+ r = process arglist[1], :expression
315
+
316
+ res = "(#{a} = #{l}, #{b} = #{r}, typeof(#{a}) === "
317
+ res += "'number' ? #{a} #{meth} #{b} : #{a}.#{mid}"
318
+ res += "(null, #{b}))"
319
+
320
+ @scope.queue_temp a
321
+ @scope.queue_temp b
322
+
323
+ res
324
+ end
325
+
326
+ def js_compile_time_helpers(exp, level)
327
+ recv = exp[0]
328
+ meth = exp[1]
329
+ args = exp[2]
330
+
331
+ arg = args[1] || raise("No argument given to compile helper: #{meth}")
332
+ arg = process arg, :expression
333
+ tmp = @scope.new_temp
334
+
335
+ res = case meth
336
+ when :object?
337
+ "(!!(#{tmp} = #{arg}, #{tmp} != null && #{tmp}.$klass))"
338
+ when :native?
339
+ "(!!(#{tmp} = #{arg}, #{tmp} == null || !#{tmp}.$klass))"
340
+ when :string?
341
+ "(typeof #{arg} === 'string')"
342
+ when :number?
343
+ "(typeof #{arg} === 'number')"
344
+ when :function?
345
+ "(typeof #{arg} === 'function')"
346
+ when :defined?
347
+ "((#{tmp} = typeof(#{arg})) === 'undefined' ? nil : #{tmp})"
348
+ when :undefined?
349
+ "(typeof(#{arg}) === 'undefined')"
350
+ when :null?
351
+ "(#{arg} === null)"
352
+ when :typeof
353
+ "(typeof(#{arg}))"
354
+ else
355
+ raise "Bad compile time helper: #{meth}"
356
+ end
357
+
358
+ @scope.queue_temp tmp
359
+
360
+ res
361
+ end
362
+
363
+ # s(:lit, 1)
364
+ # s(:lit, :foo)
365
+ def lit(sexp, level)
366
+ val = sexp.shift
367
+ case val
368
+ when Numeric
369
+ level == :receiver ? "(#{val.inspect})" : val.inspect
370
+ when Symbol
371
+ val.to_s.inspect
372
+ when Regexp
373
+ val == // ? /^/.inspect : val.inspect
374
+ when Range
375
+ "$opal.range(#{val.begin}, #{val.end}, #{val.exclude_end?})"
376
+ else
377
+ raise "Bad lit: #{val.inspect}"
378
+ end
379
+ end
380
+
381
+ def dregx(sexp, level)
382
+ parts = sexp.map do |part|
383
+ if String === part
384
+ part.inspect
385
+ elsif part[0] == :str
386
+ process part, :expression
387
+ else
388
+ process part[1], :expression
389
+ end
390
+ end
391
+
392
+ "(new RegExp(#{parts.join ' + '}))"
393
+ end
394
+
395
+ def dot2 exp, level
396
+ "$opal.range(#{process exp[0], :expression}, #{process exp[1], :expression}, false)"
397
+ end
398
+
399
+ # s(:str, "string")
400
+ def str(sexp, level)
401
+ str = sexp.shift
402
+ if str == @file
403
+ @uses_file = true
404
+ "FILE"
405
+ else
406
+ str.inspect
407
+ end
408
+ end
409
+
410
+ def defined(sexp, level)
411
+ part = sexp[0]
412
+ case part[0]
413
+ when :self
414
+ "self".inspect
415
+ when :nil
416
+ "nil".inspect
417
+ when :true
418
+ "true".inspect
419
+ when :false
420
+ "false".inspect
421
+ when :call
422
+ mid = mid_to_jsid part[2].to_s
423
+ recv = part[1] ? process(part[1], :expression) : 'this'
424
+ "(#{recv}.#{mid} ? 'method' : nil)"
425
+ else
426
+ raise "bad defined? part: #{part[0]}"
427
+ end
428
+ end
429
+
430
+ # s(:not, sexp)
431
+ def not(sexp, level)
432
+ tmp = @scope.new_temp
433
+ code = "((#{tmp} = #{process sexp.shift, :expression}) === false || #{tmp} === nil)"
434
+ @scope.queue_temp tmp
435
+ code
436
+ end
437
+
438
+ def block_pass(exp, level)
439
+ pass = process exp.shift, level
440
+
441
+ tmp = @scope.new_temp
442
+
443
+ to_proc = process(s(:call, s(:js_tmp, tmp), :to_proc, s(:arglist)), :expression)
444
+
445
+ code = "(#{tmp} = #{pass}, (typeof(#{tmp}) === 'function' || #{tmp} == nil ? #{tmp} : #{to_proc}))"
446
+
447
+ @scope.queue_temp tmp
448
+
449
+ code
450
+ end
451
+
452
+ # s(:iter, call, block_args [, body)
453
+ def iter(sexp, level)
454
+ call = sexp[0]
455
+ args = sexp[1]
456
+ body = sexp[2]
457
+
458
+ body ||= s(:nil)
459
+ body = returns body
460
+ code = ""
461
+ params = nil
462
+
463
+ args = nil if Fixnum === args # argh
464
+ args ||= s(:masgn, s(:array))
465
+ args = args.first == :lasgn ? s(:array, args) : args[1]
466
+
467
+ if args.last[0] == :splat
468
+ splat = args[-1][1][1]
469
+ args[-1] = s(:lasgn, splat)
470
+ len = args.length
471
+ end
472
+
473
+ indent do
474
+ in_scope(:iter) do
475
+ args[1..-1].each do |arg|
476
+ arg = arg[1]
477
+ arg = "#{arg}$" if RESERVED.include? arg.to_s
478
+ code += "if (#{arg} === undefined) {#{arg} = nil; }"
479
+ end
480
+
481
+ params = js_block_args(args[1..-1])
482
+ params.unshift '_$'
483
+ code += "#{splat} = $slice.call(arguments, #{len - 1});" if splat
484
+ code += process body, :statement
485
+
486
+ code = @scope.to_vars + code
487
+ end
488
+ end
489
+
490
+ call << "function(#{params.join ', '}) {#{code}#{fix_line sexp.end_line}}"
491
+ process call, level
492
+ end
493
+
494
+ def js_block_args(sexp)
495
+ sexp.map do |arg|
496
+ a = arg[1].intern
497
+ a = "#{a}$".intern if RESERVED.include? a.to_s
498
+ @scope.add_arg a
499
+ a
500
+ end
501
+ end
502
+
503
+ ##
504
+ # recv.mid = rhs
505
+ #
506
+ # s(recv, :mid=, s(:arglist, rhs))
507
+
508
+ def attrasgn(exp, level)
509
+ recv = exp[0]
510
+ mid = exp[1]
511
+ arglist = exp[2]
512
+
513
+ return process(s(:call, recv, mid, arglist), level)
514
+ end
515
+
516
+ # s(:call, recv, :mid, s(:arglist))
517
+ # s(:call, nil, :mid, s(:arglist))
518
+ def call(sexp, level)
519
+ recv = sexp[0]
520
+ meth = sexp[1]
521
+ arglist = sexp[2]
522
+ iter = sexp[3]
523
+
524
+ mid = mid_to_jsid meth.to_s
525
+
526
+ return js_operator_call(sexp, level) if CALL_OPERATORS.include? meth.to_s
527
+ return js_compile_time_helpers(sexp, level) if recv && recv == [:const, :Opal]
528
+ return js_block_given(sexp, level) if meth == :block_given?
529
+ return "undefined" if meth == :undefined
530
+
531
+ splat = arglist[1..-1].any? { |a| a.first == :splat }
532
+
533
+ if Sexp === arglist.last and arglist.last.first == :block_pass
534
+ tmpproc = @scope.new_temp
535
+ arglist.insert 1, s(:js_tmp, process(arglist.pop, :expression))
536
+ elsif iter
537
+ tmpproc = @scope.new_temp
538
+ arglist.insert 1, s(:js_tmp, "(#{tmpproc}=#{iter},#{tmpproc}.$S=this,#{tmpproc})")
539
+ else
540
+ arglist.insert 1, s(:js_tmp, 'null') unless arglist.length == 1
541
+ end
542
+
543
+ tmprecv = @scope.new_temp if splat or @debug
544
+ args = ""
545
+
546
+ recv_code = recv.nil? ? 'this' : process(recv, :receiver)
547
+
548
+ args = process arglist, :expression
549
+
550
+ @scope.queue_temp tmprecv if tmprecv
551
+ @scope.queue_temp tmpproc if tmpproc
552
+
553
+ if @debug
554
+ pre = "((#{tmprecv}=#{recv_code}).#{mid} || $opal.mm('#{mid}'))."
555
+ splat ? "#{pre}apply(#{tmprecv}, #{args})" : "#{pre}call(#{tmprecv}#{args == '' ? '' : ", #{args}"})"
556
+ else
557
+ splat ? "(#{tmprecv}=#{recv_code}).#{mid}.apply(#{tmprecv}, #{args})" : "#{recv_code}.#{mid}(#{args})"
558
+ end
559
+ end
560
+
561
+ # s(:arglist, [arg [, arg ..]])
562
+ def arglist(sexp, level)
563
+ code = ''
564
+ work = []
565
+
566
+ until sexp.empty?
567
+ splat = sexp.first.first == :splat
568
+ arg = process sexp.shift, :expression
569
+
570
+ if splat
571
+ if work.empty?
572
+ if code.empty?
573
+ code += arg
574
+ else
575
+ code += ".concat(#{arg})"
576
+ end
577
+ else
578
+ join = "[#{work.join ', '}]"
579
+ code += (code.empty? ? join : ".concat(#{join})")
580
+ code += ".concat(#{arg})"
581
+ end
582
+
583
+ work = []
584
+ else
585
+ work.push arg
586
+ end
587
+ end
588
+
589
+ unless work.empty?
590
+ join = work.join ', '
591
+ code += (code.empty? ? join : ".concat([#{work}])")
592
+ end
593
+
594
+ code
595
+ end
596
+
597
+ # s(:splat, sexp)
598
+ def splat(sexp, level)
599
+ return "[]" if sexp.first == [:nil]
600
+ return "[#{process sexp.first, :expression}]" if sexp.first.first == :lit
601
+ process sexp.first, :receiver
602
+ end
603
+
604
+ # s(:class, cid, super, body)
605
+ def class(sexp, level)
606
+ cid = sexp[0]
607
+ sup = sexp[1]
608
+ body = sexp[2]
609
+ code = nil
610
+
611
+ if Symbol === cid or String === cid
612
+ donates_methods = (cid === :Object || cid === :BasicObject)
613
+ base = 'this'
614
+ name = cid.to_s.inspect
615
+ elsif cid[0] == :colon2
616
+ base = process(cid[1], :expression)
617
+ name = cid[2].to_s.inspect
618
+ elsif cid[0] == :colon3
619
+ donates_methods = (cid[1] === :Object || cid[1] === :BasicObject)
620
+ base = '$opal.Object'
621
+ name = cid[1].to_s.inspect
622
+ else
623
+ raise "Bad receiver in class"
624
+ end
625
+
626
+ sup = sup ? process(sup, :expression) : 'nil'
627
+
628
+ indent do
629
+ in_scope(:class) do
630
+ @scope.donates_methods = donates_methods
631
+ code = @scope.to_vars + process(body, :statement)
632
+ code += @scope.to_donate_methods if @scope.donates_methods
633
+ end
634
+ end
635
+
636
+ "$klass(#{base}, #{sup}, #{name}, function() {#{code}#{fix_line sexp.end_line}}, 0)"
637
+ end
638
+
639
+ # s(:sclass, recv, body)
640
+ def sclass(sexp, level)
641
+ recv = sexp[0]
642
+ body = sexp[1]
643
+ code = nil
644
+ base = process recv, :expression
645
+
646
+ in_scope(:sclass) do
647
+ code = @scope.to_vars + process(body, :statement)
648
+ end
649
+
650
+ "$klass(#{base}, nil, nil, function() {#{code}}, 2)"
651
+ end
652
+
653
+ # s(:module, cid, body)
654
+ def module(sexp, level)
655
+ cid = sexp[0]
656
+ body = sexp[1]
657
+ code = nil
658
+
659
+ if Symbol === cid or String === cid
660
+ base = 'this'
661
+ name = cid.to_s.inspect
662
+ elsif cid[0] == :colon2
663
+ base = process(cid[1], :expression)
664
+ name = cid[2].to_s.inspect
665
+ elsif cid[0] == :colon3
666
+ base = '$opal.Object'
667
+ name = cid[1].to_s.inspect
668
+ else
669
+ raise "Bad receiver in class"
670
+ end
671
+
672
+ indent do
673
+ in_scope(:module) do
674
+ @scope.donates_methods = true
675
+ code = @scope.to_vars + process(body, :statement) + @scope.to_donate_methods
676
+ end
677
+ end
678
+
679
+ "$klass(#{base}, nil, #{name}, function() {#{code}#{fix_line sexp.end_line}}, 1)"
680
+ end
681
+
682
+ def undef(exp, level)
683
+ "$opal.undef(this, #{process exp.shift, :expression})"
684
+ end
685
+
686
+ # s(:defn, mid, s(:args), s(:scope))
687
+ def defn(sexp, level)
688
+ mid = sexp[0]
689
+ args = sexp[1]
690
+ stmts = sexp[2]
691
+ js_def nil, mid, args, stmts, sexp.line, sexp.end_line
692
+ end
693
+
694
+ # s(:defs, recv, mid, s(:args), s(:scope))
695
+ def defs(sexp, level)
696
+ recv = sexp[0]
697
+ mid = sexp[1]
698
+ args = sexp[2]
699
+ stmts = sexp[3]
700
+ js_def recv, mid, args, stmts, sexp.line, sexp.end_line
701
+ end
702
+
703
+ def js_def(recvr, mid, args, stmts, line, end_line)
704
+ mid = mid_to_jsid mid.to_s
705
+
706
+ if recvr
707
+ type = '$defs'
708
+ recv = process(recvr, :expression)
709
+ else
710
+ type = '$defn'
711
+ recv = 'this'
712
+ end
713
+
714
+ code = ''
715
+ params = nil
716
+ scope_name = @scope.name
717
+
718
+ # opt args if last arg is sexp
719
+ opt = args.pop if Sexp === args.last
720
+
721
+ # block name &block
722
+ if args.last.to_s[0] == '&'
723
+ block_name = args.pop[1..-1].intern
724
+ end
725
+
726
+ # splat args *splat
727
+ if args.last.to_s[0] == '*'
728
+ if args.last == :*
729
+ args.pop
730
+ else
731
+ splat = args[-1].to_s[1..-1].intern
732
+ args[-1] = splat
733
+ len = args.length - 2
734
+ end
735
+ end
736
+
737
+ aritycode = arity_check(args, opt, splat) if @debug
738
+
739
+ indent do
740
+ in_scope(:def) do
741
+ args.insert 1, '$yield'
742
+ params = process args, :expression
743
+
744
+ if block_name
745
+ @scope.add_arg block_name
746
+ @scope.uses_block!
747
+ end
748
+
749
+ opt[1..-1].each do |o|
750
+ id = process s(:lvar, o[1]), :expression
751
+ code += "if (#{id} === undefined) { #{process o, :expression}; }"
752
+ end if opt
753
+
754
+ code += "#{splat} = $slice.call(arguments, #{len + 1});" if splat
755
+ code += process(stmts, :statement)
756
+
757
+ if @scope.uses_block?
758
+ blk = "$yield || ($yield = $no_proc);"
759
+ blk = "var #{block_name} = $yield || ($yield = $no_proc, nil);" if block_name
760
+ blk += "var $context = $yield.$S;"
761
+ blk = "var $block_given = ($yield != null); #{blk}"
762
+ code = blk + code
763
+ end
764
+
765
+ code = aritycode.to_s + code
766
+
767
+ if @scope.catches_break?
768
+ code = "try {#{code}} catch (e) { if (e === $breaker) { return e.$v; }; throw e;}"
769
+ end
770
+
771
+ code = @scope.to_vars + code
772
+ end
773
+ end
774
+
775
+ defcode = "#{"#{scope_name} = " if scope_name}function(#{params}) {#{code}#{fix_line end_line}}"
776
+
777
+ if recvr
778
+ "#{type}(#{recv}, '#{mid}', #{defcode})"
779
+ elsif @scope.type == :class
780
+ @scope.methods << mid if @scope.donates_methods
781
+ "$proto.#{mid} = #{defcode}"
782
+ elsif @scope.type == :module
783
+ @scope.methods << mid
784
+ "$proto.#{mid} = #{defcode}"
785
+ else
786
+ "#{type}(#{recv}, '#{mid}', #{defcode})"
787
+ end
788
+ end
789
+
790
+ ##
791
+ # Returns code used in debug mode to check arity of method call
792
+ def arity_check(args, opt, splat)
793
+ arity = args.size - 1
794
+ arity -= (opt.size - 1) if opt
795
+ arity -= 1 if splat
796
+ arity = -arity - 1 if opt or splat
797
+
798
+ aritycode = "var $arity = arguments.length; if ($arity !== 0) { $arity -= 1; }"
799
+ if arity < 0 # splat or opt args
800
+ aritycode + "if ($arity < #{-(arity + 1)}) { $opal.arg_error($arity, #{arity}); }"
801
+ else
802
+ aritycode + "if ($arity !== #{arity}) { $opal.arg_error($arity, #{arity}); }"
803
+ end
804
+ end
805
+
806
+ def args(exp, level)
807
+ args = []
808
+
809
+ until exp.empty?
810
+ a = exp.shift.intern
811
+ a = "#{a}$".intern if RESERVED.include? a.to_s
812
+ @scope.add_arg a
813
+ args << a
814
+ end
815
+
816
+ args.join ', '
817
+ end
818
+
819
+ # s(:self) # => this
820
+ def self(sexp, level)
821
+ 'this'
822
+ end
823
+
824
+ # s(:true) # => true
825
+ # s(:false) # => false
826
+ # s(:nil) # => nil
827
+ %w(true false nil).each do |name|
828
+ define_method name do |exp, level|
829
+ name
830
+ end
831
+ end
832
+
833
+ # s(:array [, sexp [, sexp]])
834
+ def array(sexp, level)
835
+ return '[]' if sexp.empty?
836
+
837
+ code = ''
838
+ work = []
839
+
840
+ until sexp.empty?
841
+ splat = sexp.first.first == :splat
842
+ part = process sexp.shift, :expression
843
+
844
+ if splat
845
+ if work.empty?
846
+ code += (code.empty? ? part : ".concat(#{part})")
847
+ else
848
+ join = "[#{work.join ', '}]"
849
+ code += (code.empty? ? join : ".concat(#{join})")
850
+ code += ".concat(#{part})"
851
+ end
852
+ work = []
853
+ else
854
+ work << part
855
+ end
856
+ end
857
+
858
+ unless work.empty?
859
+ join = "[#{work.join ', '}]"
860
+ code += (code.empty? ? join : ".concat(#{join})")
861
+ end
862
+
863
+ code
864
+ end
865
+
866
+ # s(:hash, key1, val1, key2, val2...)
867
+ def hash(sexp, level)
868
+ "(new $opal.hash(#{sexp.map { |p| process p, :expression }.join ', '}))"
869
+ end
870
+
871
+ # s(:while, exp, block, true)
872
+ def while(sexp, level)
873
+ expr = sexp[0]
874
+ stmt = sexp[1]
875
+ redo_var = @scope.new_temp
876
+ stmt_level = if level == :expression or level == :receiver
877
+ :statement_closure
878
+ else
879
+ :statement
880
+ end
881
+ pre = "while ("
882
+ code = "#{js_truthy expr}){"
883
+
884
+ in_while do
885
+ @while_loop[:closure] = true if stmt_level == :statement_closure
886
+ @while_loop[:redo_var] = redo_var
887
+ body = process(stmt, :statement)
888
+
889
+ if @while_loop[:use_redo]
890
+ pre = "#{redo_var}=false;" + pre + "#{redo_var} || "
891
+ code += "#{redo_var}=false;"
892
+ end
893
+
894
+ code += body
895
+ end
896
+
897
+ code += "}"
898
+ code = pre + code
899
+ @scope.queue_temp redo_var
900
+
901
+ if stmt_level == :statement_closure
902
+ code = "(function() {#{code}; return nil;}).call(this)"
903
+ end
904
+
905
+ code
906
+ end
907
+
908
+ def until(exp, level)
909
+ expr = exp[0]
910
+ stmt = exp[1]
911
+ redo_var = @scope.new_temp
912
+ stmt_level = if level == :expression or level == :receiver
913
+ :statement_closure
914
+ else
915
+ :statement
916
+ end
917
+ pre = "while (!("
918
+ code = "#{js_truthy expr})) {"
919
+
920
+ in_while do
921
+ @while_loop[:closure] = true if stmt_level == :statement_closure
922
+ @while_loop[:redo_var] = redo_var
923
+ body = process(stmt, :statement)
924
+
925
+ if @while_loop[:use_redo]
926
+ pre = "#{redo_var}=false;" + pre + "#{redo_var} || "
927
+ code += "#{redo_var}=false;"
928
+ end
929
+
930
+ code += body
931
+ end
932
+
933
+ code += "}"
934
+ code = pre + code
935
+ @scope.queue_temp redo_var
936
+
937
+ if stmt_level == :statement_closure
938
+ code = "(function() {#{code}; return nil;}).call(this)"
939
+ end
940
+
941
+ code
942
+ end
943
+
944
+ ##
945
+ # alias foo bar
946
+ #
947
+ # s(:alias, s(:lit, :foo), s(:lit, :bar))
948
+ def alias(exp, level)
949
+ new = exp[0]
950
+ old = exp[1]
951
+ "$opal.alias(this, #{process new, :expression}, #{process old, :expression})"
952
+ end
953
+
954
+ def svalue(sexp, level)
955
+ process sexp.shift, level
956
+ end
957
+
958
+ # s(:lasgn, :lvar, rhs)
959
+ def lasgn(sexp, level)
960
+ lvar = sexp[0]
961
+ rhs = sexp[1]
962
+ lvar = "#{lvar}$".intern if RESERVED.include? lvar.to_s
963
+ @scope.add_local lvar
964
+ "#{lvar} = #{process rhs, :expression}"
965
+ end
966
+
967
+ # s(:lvar, :lvar)
968
+ def lvar(exp, level)
969
+ lvar = exp.shift.to_s
970
+ lvar = "#{lvar}$" if RESERVED.include? lvar
971
+ lvar
972
+ end
973
+
974
+ # s(:iasgn, :ivar, rhs)
975
+ def iasgn(exp, level)
976
+ ivar = exp[0]
977
+ rhs = exp[1]
978
+ ivar = ivar.to_s[1..-1]
979
+ lhs = RESERVED.include?(ivar) ? "this['#{ivar}']" : "this.#{ivar}"
980
+ "#{lhs} = #{process rhs, :expression}"
981
+ end
982
+
983
+ # s(:ivar, :ivar)
984
+ def ivar(exp, level)
985
+ ivar = exp.shift.to_s[1..-1]
986
+ part = RESERVED.include?(ivar) ? "['#{ivar}']" : ".#{ivar}"
987
+ @scope.add_ivar part
988
+ "this#{part}"
989
+ end
990
+
991
+ # s(:gvar, gvar)
992
+ def gvar(sexp, level)
993
+ gvar = sexp.shift.to_s
994
+ tmp = @scope.new_temp
995
+ code = "((#{tmp} = $opal.gvars[#{gvar.inspect}]) == null ? nil : #{tmp})"
996
+ @scope.queue_temp tmp
997
+ code
998
+ end
999
+
1000
+ # s(:gasgn, :gvar, rhs)
1001
+ def gasgn(sexp, level)
1002
+ gvar = sexp[0]
1003
+ rhs = sexp[1]
1004
+ "($opal.gvars[#{gvar.to_s.inspect}] = #{process rhs, :expression})"
1005
+ end
1006
+
1007
+ # s(:const, :const)
1008
+ def const(sexp, level)
1009
+ if @debug
1010
+ "$opal.const_get($const, #{sexp.shift.to_s.inspect})"
1011
+ else
1012
+ "$const.#{sexp.shift}"
1013
+ end
1014
+ end
1015
+
1016
+ # s(:cdecl, :const, rhs)
1017
+ def cdecl(sexp, level)
1018
+ const = sexp[0]
1019
+ rhs = sexp[1]
1020
+ "$const.#{const} = #{process rhs, :expression}"
1021
+ end
1022
+
1023
+ # s(:return [val])
1024
+ def return(sexp, level)
1025
+ val = process(sexp.shift || s(:nil), :expression)
1026
+
1027
+ if level == :statement
1028
+ "return #{val}"
1029
+ else
1030
+ "$return(#{val})"
1031
+ end
1032
+ end
1033
+
1034
+ # s(:xstr, content)
1035
+ def xstr(sexp, level)
1036
+ code = sexp.first.to_s
1037
+ code += ";" if level == :statement and !code.include?(';')
1038
+ code = "(#{code})" if level == :receiver
1039
+
1040
+ code
1041
+ end
1042
+
1043
+ # s(:dxstr, parts...)
1044
+ def dxstr(sexp, level)
1045
+ code = sexp.map do |p|
1046
+ if String === p
1047
+ p.to_s
1048
+ elsif p.first == :evstr
1049
+ process p.last, :expression
1050
+ elsif p.first == :str
1051
+ p.last.to_s
1052
+ else
1053
+ raise "Bad dxstr part"
1054
+ end
1055
+ end.join
1056
+
1057
+ code += ";" if level == :statement and !code.include?(';')
1058
+ code = "(#{code})" if level == :receiver
1059
+ code
1060
+ end
1061
+
1062
+ # s(:dstr, parts..)
1063
+ def dstr(sexp, level)
1064
+ parts = sexp.map do |p|
1065
+ if String === p
1066
+ p.inspect
1067
+ elsif p.first == :evstr
1068
+ process(s(:call, p.last, :to_s, s(:arglist)), :expression)
1069
+ elsif p.first == :str
1070
+ p.last.inspect
1071
+ else
1072
+ raise "Bad dstr part"
1073
+ end
1074
+ end
1075
+
1076
+ "(#{parts.join ' + '})"
1077
+ end
1078
+
1079
+ def dsym(sexp, level)
1080
+ parts = sexp.map do |p|
1081
+ if String === p
1082
+ p.inspect
1083
+ elsif p.first == :evstr
1084
+ process(s(:call, p.last, :to_s, s(:arglist)), :expression)
1085
+ elsif p.first == :str
1086
+ p.last.inspect
1087
+ else
1088
+ raise "Bad dsym part"
1089
+ end
1090
+ end
1091
+
1092
+ "(#{parts.join '+'})"
1093
+ end
1094
+
1095
+ # s(:if, test, truthy, falsy)
1096
+ def if(sexp, level)
1097
+ test = sexp[0]
1098
+ truthy = sexp[1]
1099
+ falsy = sexp[2]
1100
+
1101
+ if level == :expression or level == :receiver
1102
+ truthy = returns(truthy || s(:nil))
1103
+ falsy = returns(falsy || s(:nil))
1104
+ end
1105
+
1106
+ code = "if (#{js_truthy test}) {"
1107
+ indent { code += process(truthy, :statement) } if truthy
1108
+ indent { code += "} else {#{process falsy, :statement}" } if falsy
1109
+ code += "#{fix_line sexp.end_line}}"
1110
+
1111
+ code = "(function() { #{code}; return nil; }).call(this)" if level == :expression or level == :receiver
1112
+
1113
+ code
1114
+ end
1115
+
1116
+ def js_truthy_optimize(sexp)
1117
+ if sexp.first == :call
1118
+ mid = sexp[2]
1119
+ if mid == :block_given?
1120
+ return process sexp, :expression
1121
+ elsif COMPARE.include? mid.to_s
1122
+ return process sexp, :expression
1123
+ end
1124
+ end
1125
+ end
1126
+
1127
+ def js_truthy(sexp)
1128
+ if optimized = js_truthy_optimize(sexp)
1129
+ return optimized
1130
+ end
1131
+
1132
+ tmp = @scope.new_temp
1133
+ code = "(#{tmp} = #{process sexp, :expression}) !== false && #{tmp} !== nil"
1134
+ @scope.queue_temp tmp
1135
+
1136
+ code
1137
+ end
1138
+
1139
+ # s(:and, lhs, rhs)
1140
+ def and(sexp, level)
1141
+ lhs = sexp[0]
1142
+ rhs = sexp[1]
1143
+ t = nil
1144
+ tmp = @scope.new_temp
1145
+
1146
+ if t = js_truthy_optimize(lhs)
1147
+ return "(#{tmp} = #{t} ? #{process rhs, :expression} : #{tmp})".tap {
1148
+ @scope.queue_temp tmp
1149
+ }
1150
+ end
1151
+
1152
+ code = "(#{tmp} = #{process lhs, :expression}, #{tmp} !== false && "
1153
+ code += "#{tmp} != nil ? #{process rhs, :expression} : #{tmp})"
1154
+ @scope.queue_temp tmp
1155
+
1156
+ code
1157
+ end
1158
+
1159
+ # s(:or, lhs, rhs)
1160
+ def or(sexp, level)
1161
+ lhs = sexp[0]
1162
+ rhs = sexp[1]
1163
+ t = nil
1164
+ tmp = @scope.new_temp
1165
+
1166
+ if t = js_truthy_optimize(lhs)
1167
+ return "(#{tmp} = #{t} ? #{tmp} : #{process rhs, :expression})".tap {
1168
+ @scope.queue_temp tmp
1169
+ }
1170
+ end
1171
+
1172
+ code = "(#{tmp} = #{process lhs, :expression}, #{tmp} !== false && "
1173
+ code += "#{tmp} != nil ? #{tmp} : #{process rhs, :expression})"
1174
+ @scope.queue_temp tmp
1175
+
1176
+ code
1177
+ end
1178
+
1179
+ # s(:yield, arg1, arg2)
1180
+ def yield(sexp, level)
1181
+ @scope.uses_block!
1182
+ splat = sexp.any? { |s| s.first == :splat }
1183
+ sexp.unshift s(:js_tmp, 'null')
1184
+ sexp.unshift s(:js_tmp, '$context') unless splat
1185
+ args = arglist(sexp, level)
1186
+
1187
+ call = if splat
1188
+ "$yield.apply($context, #{args})"
1189
+ else
1190
+ "$yield.call(#{args})"
1191
+ end
1192
+
1193
+ if level == :receiver or level == :expression
1194
+ tmp = @scope.new_temp
1195
+ @scope.catches_break!
1196
+ code = "((#{tmp} = #{call}) === $breaker ? #{tmp}.$t() : #{tmp})"
1197
+ @scope.queue_temp tmp
1198
+ else
1199
+ code = call
1200
+ end
1201
+
1202
+ code
1203
+ end
1204
+
1205
+ def break(exp, level)
1206
+ val = exp.empty? ? 'nil' : process(exp.shift, :expression)
1207
+ if in_while?
1208
+ if @while_loop[:closure]
1209
+ "return #{val};"
1210
+ else
1211
+ "break;"
1212
+ end
1213
+ else
1214
+ "return ($breaker.$v = #{val}, $breaker)"
1215
+ end
1216
+ end
1217
+
1218
+ # s(:case, expr, when1, when2, ..)
1219
+ def case(exp, level)
1220
+ code = []
1221
+ @scope.add_local "$case"
1222
+ expr = process exp.shift, :expression
1223
+ # are we inside a statement_closure
1224
+ returnable = level != :statement
1225
+ done_else = false
1226
+
1227
+ until exp.empty?
1228
+ wen = exp.shift
1229
+ if wen and wen.first == :when
1230
+ returns(wen) if returnable
1231
+ wen = process(wen, :statement)
1232
+ wen = "else #{wen}" unless code.empty?
1233
+ code << wen
1234
+ elsif wen # s(:else)
1235
+ done_else = true
1236
+ wen = returns(wen) if returnable
1237
+ code << "else {#{process wen, :statement}}"
1238
+ end
1239
+ end
1240
+
1241
+ code << "else {return nil}" if returnable and !done_else
1242
+
1243
+ code = "$case = #{expr};#{code.join "\n"}"
1244
+ code = "(function() { #{code} }).call(this)" if returnable
1245
+ code
1246
+ end
1247
+
1248
+ # when foo
1249
+ # bar
1250
+ #
1251
+ # s(:when, s(:array, foo), bar)
1252
+ def when(exp, level)
1253
+ arg = exp.shift[1..-1]
1254
+ body = exp.shift
1255
+ body = process body, level if body
1256
+
1257
+ test = []
1258
+ until arg.empty?
1259
+ a = arg.shift
1260
+
1261
+ if a.first == :when # when inside another when means a splat of values
1262
+ call = s(:call, s(:js_tmp, "$splt[i]"), :===, s(:arglist, s(:js_tmp, "$case")))
1263
+ splt = "(function($splt) {for(var i = 0; i < $splt.length; i++) {"
1264
+ splt += "if (#{process call, :expression}) { return true; }"
1265
+ splt += "} return false; }).call(this, #{process a[1], :expression})"
1266
+
1267
+ test << splt
1268
+ else
1269
+ call = s(:call, a, :===, s(:arglist, s(:js_tmp, "$case")))
1270
+ call = process call, :expression
1271
+ # call = "else " unless test.empty?
1272
+
1273
+ test << call
1274
+ end
1275
+ end
1276
+
1277
+ "if (#{test.join " || "}) {\n#{body}\n}"
1278
+ end
1279
+
1280
+ # lhs =~ rhs
1281
+ #
1282
+ # s(:match3, lhs, rhs)
1283
+ def match3(sexp, level)
1284
+ lhs = sexp[0]
1285
+ rhs = sexp[1]
1286
+ call = s(:call, lhs, :=~, s(:arglist, rhs))
1287
+ process call, level
1288
+ end
1289
+
1290
+ # @@class_variable
1291
+ #
1292
+ # s(:cvar, name)
1293
+ def cvar(exp, level)
1294
+ tmp = @scope.new_temp
1295
+ code = "((#{tmp} = $opal.cvars[#{exp.shift.to_s.inspect}]) == null ? nil : #{tmp})"
1296
+ @scope.queue_temp tmp
1297
+ code
1298
+ end
1299
+
1300
+ # @@name = rhs
1301
+ #
1302
+ # s(:cvasgn, :@@name, rhs)
1303
+ def cvasgn(exp, level)
1304
+ "($opal.cvars[#{exp.shift.to_s.inspect}] = #{process exp.shift, :expression})"
1305
+ end
1306
+
1307
+ def cvdecl(exp, level)
1308
+ "($opal.cvars[#{exp.shift.to_s.inspect}] = #{process exp.shift, :expression})"
1309
+ end
1310
+
1311
+ # BASE::NAME
1312
+ #
1313
+ # s(:colon2, base, :NAME)
1314
+ def colon2(sexp, level)
1315
+ base = sexp[0]
1316
+ name = sexp[1]
1317
+ "$opal.const_get((#{process base, :expression}).$const, #{name.to_s.inspect})"
1318
+ end
1319
+
1320
+ def colon3(exp, level)
1321
+ "$opal.const_get($opal.Object, #{exp.shift.to_s.inspect})"
1322
+ end
1323
+
1324
+ # super a, b, c
1325
+ #
1326
+ # s(:super, arg1, arg2, ...)
1327
+ def super(sexp, level)
1328
+ args = []
1329
+ until sexp.empty?
1330
+ args << process(sexp.shift, :expression)
1331
+ end
1332
+ "$opal.zuper(arguments.callee, this, [#{args.join ', '}])"
1333
+ end
1334
+
1335
+ # super
1336
+ #
1337
+ # s(:zsuper)
1338
+ def zsuper(exp, level)
1339
+ "$opal.zuper(arguments.callee, this, [])"
1340
+ end
1341
+
1342
+ # a ||= rhs
1343
+ #
1344
+ # s(:op_asgn_or, s(:lvar, :a), s(:lasgn, :a, rhs))
1345
+ def op_asgn_or(exp, level)
1346
+ process s(:or, exp.shift, exp.shift), :expression
1347
+ end
1348
+
1349
+ def op_asgn1(sexp, level)
1350
+ "'FIXME(op_asgn1)'"
1351
+ end
1352
+
1353
+ # lhs.b += rhs
1354
+ #
1355
+ # s(:op_asgn2, lhs, :b=, :+, rhs)
1356
+ def op_asgn2(exp, level)
1357
+ lhs = process exp.shift, :expression
1358
+ mid = exp.shift.to_s[0..-2]
1359
+ op = exp.shift
1360
+ rhs = exp.shift
1361
+
1362
+ if op.to_s == "||"
1363
+ raise "op_asgn2 for ||"
1364
+ else
1365
+ temp = @scope.new_temp
1366
+ getr = s(:call, s(:js_tmp, temp), mid, s(:arglist))
1367
+ oper = s(:call, getr, op, s(:arglist, rhs))
1368
+ asgn = s(:call, s(:js_tmp, temp), "#{mid}=", s(:arglist, oper))
1369
+
1370
+ "(#{temp} = #{lhs}, #{process asgn, :expression})".tap {
1371
+ @scope.queue_temp temp
1372
+ }
1373
+ end
1374
+ end
1375
+
1376
+ # s(:ensure, body, ensure)
1377
+ def ensure(exp, level)
1378
+ begn = exp.shift
1379
+ if level == :receiver || level == :expression
1380
+ retn = true
1381
+ begn = returns begn
1382
+ end
1383
+
1384
+ body = process begn, level
1385
+ ensr = exp.shift || s(:nil)
1386
+ ensr = process ensr, level
1387
+ body = "try {\n#{body}}" unless body =~ /^try \{/
1388
+
1389
+ res = "#{body}\n finally {\n#{ensr}}"
1390
+ res = "(function() { #{res}; }).call(this)" if retn
1391
+ res
1392
+ end
1393
+
1394
+ def rescue(exp, level)
1395
+ body = exp.first.first == :resbody ? s(:nil) : exp.shift
1396
+ body = process body, level
1397
+
1398
+ parts = []
1399
+ until exp.empty?
1400
+ part = process exp.shift, level
1401
+ part = "else " + part unless parts.empty?
1402
+ parts << part
1403
+ end
1404
+ # if no rescue statement captures our error, we should rethrow
1405
+ parts << "else { throw $err; }"
1406
+
1407
+ code = "try {\n#{body}\n} catch ($err) {\n#{parts.join "\n"}\n}"
1408
+ code = "(function() { #{code} }).call(this)" if level == :expression
1409
+
1410
+ code
1411
+ end
1412
+
1413
+ def resbody(exp, level)
1414
+ args = exp[0]
1415
+ body = exp[1]
1416
+ body = process(body || s(:nil), level)
1417
+ types = args[1..-2]
1418
+
1419
+ err = types.map { |t|
1420
+ call = s(:call, t, :===, s(:arglist, s(:js_tmp, "$err")))
1421
+ a = process call, :expression
1422
+ #puts a
1423
+ a
1424
+ }.join ', '
1425
+ err = "true" if err.empty?
1426
+
1427
+ if Sexp === args.last and [:lasgn, :iasgn].include? args.last.first
1428
+ val = args.last
1429
+ val[2] = s(:js_tmp, "$err")
1430
+ val = process(val, :expression) + ";"
1431
+ end
1432
+
1433
+ "if (#{err}) {\n#{val}#{body}}"
1434
+ # raise exp.inspect
1435
+ end
1436
+
1437
+ # FIXME: Hack.. grammar should remove top level begin.
1438
+ def begin(exp, level)
1439
+ process exp[0], level
1440
+ end
1441
+
1442
+ def next(exp, level)
1443
+ val = exp.empty? ? 'nil' : process(exp.shift, :expression)
1444
+ if in_while?
1445
+ "continue;"
1446
+ else
1447
+ "return #{val};"
1448
+ end
1449
+ end
1450
+
1451
+ def redo(exp, level)
1452
+ if in_while?
1453
+ @while_loop[:use_redo] = true
1454
+ "#{@while_loop[:redo_var]} = true"
1455
+ else
1456
+ "REDO()"
1457
+ end
1458
+ end
1459
+ end
1460
+ end