6
6
module Opal
7
7
class Parser
8
8
9
+ # A fragment holds a string of generated javascript that will be written
10
+ # to the destination. It also keeps hold of the original sexp from which
11
+ # it was generated. Using this sexp, when writing fragments in order, a
12
+ # mapping can be created of the original location => target location,
13
+ # aka, source-maps!
9
14
class Fragment
10
-
15
+ # String of javascript this fragment holds
11
16
attr_reader :code
12
17
13
18
def initialize ( code , sexp = nil )
14
19
@code = code
15
20
@sexp = sexp
16
21
end
17
22
23
+ # In debug mode we may wish to include the original line as a comment
18
24
def to_code
19
25
if @sexp
20
26
"/*:#{ @sexp . line } */#{ @code } "
@@ -23,8 +29,9 @@ def to_code
23
29
end
24
30
end
25
31
32
+ # inspect the contents of this fragment, f("fooo")
26
33
def inspect
27
- "fragment (#{ @code . inspect } )"
34
+ "f (#{ @code . inspect } )"
28
35
end
29
36
end
30
37
@@ -51,8 +58,13 @@ def inspect
51
58
# Statements which should not have ';' added to them.
52
59
STATEMENTS = [ :xstr , :dxstr ]
53
60
61
+ # Final generated javascript for this parser
54
62
attr_reader :result
55
63
64
+ # Parse some ruby code to a string.
65
+ #
66
+ # Opal::Parser.new.parse("1 + 2")
67
+ # # => "(function() {....})()"
56
68
def parse ( source , options = { } )
57
69
@sexp = Grammar . new . parse ( source , options [ :file ] )
58
70
@line = 1
@@ -83,6 +95,7 @@ def parse(source, options = {})
83
95
@result = source_map_comment + version_comment + file_comment + code
84
96
end
85
97
98
+ # Always at top of generated file to show current opal version
86
99
def version_comment
87
100
"/* Generated by Opal #{ Opal ::VERSION } */\n "
88
101
end
@@ -168,6 +181,13 @@ def mid_to_jsid(mid)
168
181
end
169
182
end
170
183
184
+ # Converts a ruby lvar/arg name to a js identifier. Not all ruby names
185
+ # are valid in javascript. A $ suffix is added to non-valid names.
186
+ def lvar_to_js ( var )
187
+ var = "#{ var } $" if RESERVED . include? var . to_s
188
+ var . to_sym
189
+ end
190
+
171
191
# Used to generate a unique id name per file. These are used
172
192
# mainly to name method bodies for methods that use blocks.
173
193
#
@@ -747,10 +767,7 @@ def process_iter(sexp, level)
747
767
# s(:args, parts...) => ["a", "b", "break$"]
748
768
def js_block_args ( sexp )
749
769
sexp . map do |arg |
750
- a = arg [ 1 ] . to_sym
751
- a = "#{ a } $" . to_sym if RESERVED . include? a . to_s
752
- @scope . add_arg a
753
- a
770
+ @scope . add_arg lvar_to_js ( arg [ 1 ] )
754
771
end
755
772
end
756
773
@@ -1025,25 +1042,20 @@ def process_class(sexp, level)
1025
1042
code , fragment ( "\n #@indent })" , sexp ) , fragment ( "(" , sexp ) , base , fragment ( ", " , sexp ) , sup , fragment ( ")" , sexp ) ]
1026
1043
end
1027
1044
1028
- # s(:sclass, recv, body)
1045
+ # Singleton class syntax. Runs body in context of singleton_class.
1046
+ # s(:sclass, recv, body) => (function() { ... }).call(recv.$singleton_class())
1029
1047
def process_sclass ( sexp , level )
1030
- recv = sexp [ 0 ]
1031
- body = sexp [ 1 ]
1032
- code = [ ]
1048
+ recv , body , code = sexp [ 0 ] , sexp [ 1 ] , [ ]
1033
1049
1034
1050
in_scope ( :sclass ) do
1035
1051
@scope . add_temp "__scope = #{ current_self } ._scope"
1036
1052
@scope . add_temp "def = #{ current_self } ._proto"
1037
1053
1038
- body = process body , :stmt
1039
- code << @scope . to_vars << body
1054
+ code << @scope . to_vars << process ( body , :stmt )
1040
1055
end
1041
1056
1042
- result = [ ]
1043
- result << fragment ( "(function(){" , sexp ) << code
1044
- result << fragment ( "}).call(" , sexp )
1045
- result << process ( recv , :expr ) << fragment ( ".$singleton_class())" , sexp )
1046
- result
1057
+ [ f ( "(function(){" , sexp ) , code , f ( "}).call(" , sexp ) ,
1058
+ process ( recv ) , f ( ".$singleton_class())" , sexp ) ]
1047
1059
end
1048
1060
1049
1061
# s(:module, cid, body)
@@ -1095,6 +1107,7 @@ def process_module(sexp, level)
1095
1107
1096
1108
# undef :foo
1097
1109
# => delete MyClass.prototype.$foo
1110
+ # FIXME: we should be setting method to a stub method here
1098
1111
def process_undef ( sexp , level )
1099
1112
fragment ( "delete #{ @scope . proto } #{ mid_to_jsid sexp [ 0 ] [ 1 ] . to_s } " , sexp )
1100
1113
end
@@ -1276,6 +1289,7 @@ def process_self(sexp, level)
1276
1289
# Returns the current value for 'self'. This will be native
1277
1290
# 'this' for methods and blocks, and the class name for class
1278
1291
# and module bodies.
1292
+ # s(:self) => self or this or class name
1279
1293
def current_self
1280
1294
if @scope . class_scope?
1281
1295
@scope . name
@@ -1286,19 +1300,25 @@ def current_self
1286
1300
end
1287
1301
end
1288
1302
1303
+ # true literal
1304
+ # s(:true) => true
1289
1305
def process_true ( sexp , level )
1290
1306
fragment ( "true" , sexp )
1291
1307
end
1292
1308
1309
+ # false literal
1310
+ # s(:false) => false
1293
1311
def process_false ( sexp , level )
1294
1312
fragment ( "false" , sexp )
1295
1313
end
1296
1314
1315
+ # nil literal
1316
+ # s(:nil) => nil
1297
1317
def process_nil ( sexp , level )
1298
1318
fragment ( "nil" , sexp )
1299
1319
end
1300
1320
1301
- # s(:array [, sexp [, sexp]])
1321
+ # s(:array [, sexp [, sexp]]) => [...]
1302
1322
def process_array ( sexp , level )
1303
1323
return [ fragment ( "[]" , sexp ) ] if sexp . empty?
1304
1324
0 commit comments