js2 0.0.10 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. data/Manifest +42 -0
  2. data/README.md +65 -0
  3. data/Rakefile +19 -35
  4. data/bin/js2 +80 -66
  5. data/config/js2.yml +2 -0
  6. data/js2.gemspec +33 -0
  7. data/lib/js2/{haml_parser.rb → parser/haml.rb} +2 -2
  8. data/lib/js2/{haml_engine.rb → parser/haml_engine.rb} +1 -1
  9. data/lib/js2/parser/lexer.rb +37 -0
  10. data/lib/js2/{parser.rb → parser/tokenizer.rb} +157 -143
  11. data/lib/js2/{replace.rb → ragel/helper.rb} +16 -5
  12. data/lib/js2/ragel/tokenizer.rl +561 -0
  13. data/lib/js2/{tokenizer.rl.erb → ragel/tokenizer.rl.erb} +12 -19
  14. data/lib/js2/standard/factory.rb +289 -0
  15. data/lib/js2/standard/node.rb +75 -0
  16. data/lib/js2/util/compilation.rb +77 -0
  17. data/lib/js2/util/config.rb +84 -0
  18. data/lib/js2/util/exec.rb +34 -0
  19. data/lib/js2/util/file_handler.rb +73 -0
  20. data/lib/js2/{js2bootstrap.js2 → util/js2bootstrap.js2} +12 -68
  21. data/lib/js2/util/processor.rb +88 -0
  22. data/lib/js2/util/rdoc.rb +35 -0
  23. data/lib/js2/{sel_decorator.rb → util/sel_decorator.rb} +11 -1
  24. data/lib/js2.rb +22 -45
  25. data/test/compiled/bar.js +3 -0
  26. data/test/compiled/basic.comp.js +31 -0
  27. data/test/compiled/basic.js +27 -0
  28. data/test/compiled/foo.js +3 -0
  29. data/test/fixtures/bar.js2 +3 -0
  30. data/test/fixtures/basic.js2 +27 -0
  31. data/test/fixtures/basic.js2.haml +4 -0
  32. data/test/fixtures/basic.js2.yml +5 -0
  33. data/test/fixtures/curry.js2 +5 -0
  34. data/test/fixtures/foo.js2 +3 -0
  35. data/test/fixtures/member.js2 +14 -0
  36. data/test/fixtures/private.js2 +5 -0
  37. data/test/fixtures/property.js2 +4 -0
  38. data/test/test_helper.rb +25 -0
  39. data/test/test_js2.rb +43 -0
  40. data/wiki/features.md +106 -0
  41. data/wiki/installation.md +53 -0
  42. metadata +89 -83
  43. data/Changelog +0 -33
  44. data/History.txt +0 -4
  45. data/Manifest.txt +0 -35
  46. data/PostInstall.txt +0 -7
  47. data/README +0 -69
  48. data/README.rdoc +0 -69
  49. data/README.txt +0 -69
  50. data/examples/js2.yml +0 -8
  51. data/examples/test.yml +0 -5
  52. data/lib/javascript/sel_marker.js2 +0 -150
  53. data/lib/javascript/test.js2 +0 -73
  54. data/lib/js2/config.rb +0 -39
  55. data/lib/js2/daemon.rb +0 -35
  56. data/lib/js2/file_handler.rb +0 -91
  57. data/lib/js2/foo.js2.haml +0 -3
  58. data/lib/js2/js2.js +0 -110
  59. data/lib/js2/processor.rb +0 -112
  60. data/lib/js2/test/selenium.rb +0 -119
  61. data/lib/js2/test/selenium_element.rb +0 -234
  62. data/lib/js2/test/selenium_helper.rb +0 -27
  63. data/lib/js2/tree.rb +0 -351
  64. data/lib/js2/universe.rb +0 -123
  65. data/lib/tasks/js2.rake +0 -9
  66. data/website/index.txt +0 -86
  67. /data/{LICENSE → lib/js2/standard/class_node.rb} +0 -0
@@ -0,0 +1,289 @@
1
+ # TODO: break this up into multiple files
2
+ class JS2::Standard::PageNode < JS2::Standard::Node
3
+ attr_accessor :klasses, :file
4
+
5
+ def initialize (idx, string, factory)
6
+ @klasses = []
7
+ super(idx, string, factory)
8
+ end
9
+
10
+ def stop (idx)
11
+ JS2::Standard::ForeachNode.reset!
12
+ super(idx)
13
+ end
14
+ end
15
+
16
+ class JS2::Standard::CommentNode < JS2::Standard::Node
17
+ def clean
18
+ return to_s.gsub(%r|^\s*\/?\*+\s?\/?|, '')
19
+ end
20
+ end
21
+
22
+ class JS2::Standard::ClassNode < JS2::Standard::Node
23
+ REGEX = /^(\s*class|module)\s+([\w\.]+)\s+(extends\s+([\w\.]+))?\s*\{(.*)/m
24
+
25
+ def handle_first_string (str)
26
+ if m = str.match(REGEX)
27
+ @name = m[2]
28
+ @extends = m[4]
29
+ @ending = m[5]
30
+
31
+ if @extends
32
+ @extends = "#{@name}.oo('extends', #{@extends});"
33
+ end
34
+
35
+ return %|JS2.OO.#{create_name}("#{@name}"); #{@extends} (function (K,Package) {var self=K; var _super=JS2.OO['super']; #{@ending}|
36
+ end
37
+ end
38
+
39
+ def methods
40
+ ret = []
41
+
42
+ @children.each do |c|
43
+ if c.is_a?(JS2::Standard::MethodNode)
44
+ ret << c
45
+ end
46
+ end
47
+
48
+ return ret
49
+ end
50
+
51
+ def name
52
+ if m = @string[@start_idx .. @stop_idx].match(REGEX)
53
+ return m[2]
54
+ else
55
+ return ''
56
+ end
57
+ end
58
+
59
+ def create_name
60
+ return 'createClass'
61
+ end
62
+
63
+ def handle_ending (str)
64
+ pkg = @name.split(/\./)
65
+ pkg.pop()
66
+
67
+ if pkg.empty?
68
+ pkg = 'null'
69
+ else
70
+ pkg = pkg.join('.')
71
+ end
72
+
73
+ return str.sub(/\s*\z/, ")(#{@name}, #{pkg});")
74
+ end
75
+ end
76
+
77
+ class JS2::Standard::ModuleNode < JS2::Standard::ClassNode
78
+ def createName
79
+ return 'createModule'
80
+ end
81
+ end
82
+
83
+ class JS2::Standard::PrivateNode < JS2::Standard::Node
84
+ def handle_first_string (str)
85
+ return str.sub(/private/m, '// private')
86
+ end
87
+ end
88
+
89
+ class JS2::Standard::IncludeNode < JS2::Standard::Node
90
+ def handle_first_string (s)
91
+ return s.sub(/include\s*/, "K.oo('include', ").sub(/;$/, ');');
92
+ end
93
+ end
94
+
95
+
96
+ class JS2::Standard::MemberNode < JS2::Standard::Node
97
+ def handle_first_string (s)
98
+ method = 'member'
99
+
100
+ if s.match(/^(\s*)(static)\s*/)
101
+ method = 'staticMember'
102
+ s = s.sub(/static\s*/, '')
103
+ end
104
+
105
+ return s.sub(/var\s*([\$\w]+)/) { "K.oo('#{method}', '" + $1 + "'" }.sub(/;(\s*)$/) { ");#{$1}" }.sub(/\s*=/, ', ');
106
+ end
107
+
108
+ end
109
+
110
+ class JS2::Standard::CurryNode < JS2::Standard::Node
111
+ REGEX = %r|^curry\s*([^\{]*)?\{(.*)$|m
112
+ REGEX_WITH = %r|with\s+\(([^)]*)\)|
113
+ REGEX_ARGS = %r|^\s*\(([^)]*)\)|
114
+ REGEX_ENDING = %r|\}([^\}]*)\z|
115
+
116
+ def handle_first_string (str)
117
+ m = str.match(REGEX)
118
+ @decl = m[1].strip
119
+ @stop = m[2]
120
+
121
+ @args = ''
122
+ @in_scoped = ''
123
+ @scoped = ''
124
+
125
+ if m = @decl.match(REGEX_WITH)
126
+ in_scoped = m[1].split(',').collect { |v| v.strip }
127
+ scoped = in_scoped.collect { |v| v == 'this' ? 'self' : v }
128
+ if in_scoped.length
129
+ @scoped = scoped.join(', ')
130
+ @in_scoped = in_scoped.join(', ')
131
+ end
132
+ end
133
+
134
+ if m = @decl.match(%r|^\s*\(([^)]*)\)|)
135
+ @args = m[1].strip
136
+ end
137
+
138
+ return %|(function (#{@scoped}) { return function (#{@args}) {#{@stop}|
139
+ end
140
+
141
+ def handle_ending (str)
142
+ if m = str.match(REGEX_ENDING)
143
+ return str.sub(REGEX_ENDING) { |str| "}})(#{@in_scoped})#{m[1]}" }
144
+ end
145
+
146
+ return str
147
+ end
148
+ end
149
+
150
+
151
+ class JS2::Standard::MethodNode < JS2::Standard::Node
152
+ attr_accessor :name, :args, :static
153
+
154
+ REGEX = /^(\s*)(static\s+)?function\s+([\$\w\.]+)\s*\(([^)]*)\)\s*\{(.*)/m
155
+
156
+ def handle_first_string (s)
157
+ m = s.match(REGEX)
158
+ space = m[1]
159
+ @static = m[2]
160
+ @name = m[3]
161
+ @args = m[4]
162
+ @start = m[5]
163
+
164
+ m = static ? 'staticMember' : 'method'
165
+ return %|#{space}K.oo('#{m}', "#{@name}", function (#{@args}) {#{@start}|
166
+ end
167
+
168
+ def handle_ending (str)
169
+ return str.sub!(/(\s*)\z/) { |m| ");#{m}" }
170
+ end
171
+
172
+ end
173
+
174
+ class JS2::Standard::AccessorNode < JS2::Standard::Node
175
+ REGEX = /(\s*)accessor(\s+)([\w+,\s]+\w)(\s*);(\s*)/
176
+
177
+ def handle_first_string (str)
178
+ m = str.match(REGEX)
179
+ space = m[1]
180
+ mid_space = m[2]
181
+ list = m[3].split(/,/).collect { |i| i.strip }
182
+ end_space = m[4]
183
+ trailing = m[5]
184
+
185
+ return %|#{space}K.oo('accessor',#{mid_space}[ #{list.collect { |i| "'#{i}'" }.join(',')} ])#{end_space};#{trailing}|
186
+ end
187
+ end
188
+
189
+ class JS2::Standard::ForeachNode < JS2::Standard::Node
190
+ # start var iterator array
191
+ REGEX = /(\s*)foreach\s*\(\s*var\s*([\$\w]+)(\s*:\s*([\$\w]))?\s*in\s*([^\s]+)\s*\)/
192
+ attr_accessor :iterator, :item, :array
193
+ @@inc = 0
194
+
195
+ def handle_first_string (s)
196
+ m = s.match(REGEX)
197
+ start = m[1]
198
+ item = m[2]
199
+ iterator = m[4]
200
+ array = m[5]
201
+
202
+ it = iterator
203
+
204
+ unless iterator
205
+ it = 'it' + @@inc.to_s
206
+ @@inc += 1
207
+ end
208
+
209
+ len = it + '__len'
210
+ arr = it + '__arr'
211
+
212
+ return %(#{start}for (var #{it}=0,#{item},#{arr}=#{array},#{len}=#{arr}.length; (#{item}=#{arr}[#{it}]) || #{it}<#{len}; #{it}++))
213
+ end
214
+
215
+ def self.reset!
216
+ @@inc = 0
217
+ end
218
+ end
219
+
220
+ class JS2::Standard::PropertyNode < JS2::Standard::Node
221
+ REGEX = /(\s*)property(\s+)([\w+,\s]+\w)(\s*);(\s*)/
222
+ def handle_first_string (s)
223
+ m = s.match(REGEX)
224
+ space = m[1]
225
+ mid_space = m[2]
226
+ list = m[3].split(/,/).collect { |i| i.strip }
227
+ end_space = m[4]
228
+ trailing = m[5]
229
+
230
+ return %|#{space}K.oo('property',#{mid_space}[ #{list.collect { |i| "'#{i}'" }.join(',')} ])#{end_space};#{trailing}|
231
+ end
232
+ end
233
+
234
+
235
+ class JS2::Standard::StuffNode < JS2::Standard::Node
236
+ end
237
+
238
+
239
+ class JS2::Standard::Factory
240
+ attr_accessor :decorators
241
+
242
+ @@supports = [ :CLASS, :MEMBER, :METHOD, :ACCESSOR, :FOREACH, :PROPERTY, :INCLUDE, :CURRY, :PAGE, :COMMENT, :STUFF, :MODULE, :PRIVATE ]
243
+ @@lookup = Hash.new
244
+
245
+ @@supports.each do |v|
246
+ name = v.to_s.downcase.sub(/(\w)/) { |m| m.upcase }
247
+ @@lookup[v] = eval "JS2::Standard::#{name}Node"
248
+ end
249
+
250
+ def initialize
251
+ @decorators = []
252
+ end
253
+
254
+ def get_class (type)
255
+ klass = @@lookup[type] || JS2::Standard::Node
256
+ end
257
+
258
+ def page_node (string, file = nil)
259
+ @page = new_node(:PAGE, 0, string)
260
+ @page.file = file
261
+ @comment = nil
262
+ return @page
263
+ end
264
+
265
+ def new_node (type, idx, string)
266
+ klass = get_class(type)
267
+ node = klass.new(idx, string, self)
268
+
269
+ if type == :CLASS || type == :MODULE
270
+ @page.klasses << node
271
+ end
272
+
273
+ if type == :COMMENT
274
+ @comment = node
275
+ elsif @comment
276
+ node.comment = @comment
277
+ @comment = nil
278
+ end
279
+
280
+ setup_node(node)
281
+
282
+ return node
283
+ end
284
+
285
+ def setup_node (node)
286
+ # virtual
287
+ end
288
+ end
289
+
@@ -0,0 +1,75 @@
1
+ class JS2::Standard::Node
2
+ attr_accessor :type, :start_idx, :stop_idx, :children, :starting, :factory, :comment
3
+
4
+ def initialize (start_idx, str, factory)
5
+ @start_idx = start_idx
6
+ @string = str
7
+ @children = []
8
+ @factory = factory
9
+ @output = ''
10
+ end
11
+
12
+ def add_child (type, start_idx)
13
+ child = factory.new_node(type, start_idx, @string)
14
+ @children.push(child)
15
+ return child
16
+ end
17
+
18
+ def stop (idx)
19
+ @stop_idx = idx
20
+ process!
21
+ end
22
+
23
+ def to_s
24
+ return @output
25
+ end
26
+
27
+ def process! ()
28
+ last_idx = @start_idx
29
+ str = ''
30
+
31
+ first = true
32
+ @children.each do |c|
33
+ if c.start_idx > last_idx
34
+ str << handle_string(@string[last_idx .. c.start_idx-1], first)
35
+ first = false
36
+ end
37
+
38
+ result = c.to_s()
39
+
40
+ @factory.decorators.each do |d|
41
+ d.decorate(result, c)
42
+ end
43
+
44
+ str << result
45
+ last_idx = c.stop_idx + 1
46
+ end
47
+
48
+ if last_idx < @stop_idx
49
+ str << handle_string(@string[last_idx .. @stop_idx], first)
50
+ end
51
+
52
+ str = handle_ending(str)
53
+
54
+ @output = str
55
+ end
56
+
57
+ private
58
+
59
+ def handle_string (str, first)
60
+ if first
61
+ return handle_first_string(str)
62
+ else
63
+ return str
64
+ end
65
+ end
66
+
67
+ def handle_ending (str)
68
+ return str
69
+ end
70
+
71
+ def handle_first_string (str)
72
+ return str
73
+ end
74
+
75
+ end
@@ -0,0 +1,77 @@
1
+ class JS2::Util::Compilation
2
+ attr_accessor :klass_name, :require, :include, :template, :file
3
+ @@already = Hash.new
4
+
5
+ def initialize (klass_name, config, yml_file, file_handler)
6
+ @require = config['require'] || []
7
+ @include = config['include'] || []
8
+ @template = config['template'] || []
9
+ @klass_name = klass_name
10
+ @file = yml_file
11
+ @file_handler = file_handler
12
+
13
+ @make_compilation = config['make_compilation']
14
+ end
15
+
16
+ def self.parse (yml_file, file_handler)
17
+ klasses = YAML.load_file(yml_file)
18
+
19
+ comps = []
20
+ klasses.each_pair do |k, config|
21
+ comps << self.new(k, config, yml_file, file_handler)
22
+ end
23
+
24
+ return comps
25
+ end
26
+
27
+ def self.reset!
28
+ @@already = Hash.new
29
+ end
30
+
31
+ def compile (klasses, errors = [])
32
+ return unless @make_compilation
33
+
34
+ main_file = nil
35
+ if files = klasses[@klass_name]
36
+ main_file = @file_handler.outfile(files.first)
37
+ else
38
+ return
39
+ end
40
+
41
+ before = []
42
+ @require.each do |file|
43
+ before += get_files(file, klasses)
44
+ end
45
+
46
+ after = []
47
+ @include.each do |file|
48
+ after += get_files(file, klasses)
49
+ end
50
+
51
+ @template.each do |item|
52
+ after += get_files(item['class'], klasses)
53
+ end
54
+
55
+ file = main_file.sub(/\.js$/, '.comp.js')
56
+ str = ''
57
+ (before + [ main_file ] + after).uniq.each do |f|
58
+ if File.exist?(f)
59
+ str << File.read(f)
60
+ else
61
+ errors << [ "#{file} references #{f}, but js2 can't find it" ]
62
+ end
63
+ end
64
+
65
+ str << "#{@klass_name}.oo('method', 'getTemplate', function () { return #{@template.to_json}; });"
66
+ File.open(file, 'w') { |f| f << str }
67
+ end
68
+
69
+ def get_files (klass_name_or_file, klasses)
70
+ ret = nil
71
+ if files = klasses[klass_name_or_file]
72
+ ret = files.collect { |f| @file_handler.outfile(f) }
73
+ else
74
+ ret = @file_handler.outfile(klass_name_or_file)
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,84 @@
1
+ class JS2::Util::Config
2
+ attr_accessor :node_factory, :lexer, :file_handler, :lexer, :haml_engine, :haml_vars, :asset_dir, :decorators
3
+
4
+ def initialize
5
+ @lexer = JS2::Parser::Lexer.new
6
+ @node_factory = JS2::Standard::Factory.new
7
+ @file_handler = JS2::Util::FileHandler.new
8
+ @haml_engine = JS2::Parser::HamlEngine.new
9
+ @haml_vars = {}
10
+ end
11
+
12
+ def add_decorator (decorator)
13
+ @node_factory.decorators << decorator
14
+ end
15
+
16
+ def self.from_yml(yml, env = nil)
17
+ hash = YAML.load(yml)
18
+ hash = hash[env] if env
19
+ return self.from_hash(hash)
20
+ end
21
+
22
+ def load_hash (hash)
23
+ self.file_handler.js2_dir = hash['js2_dir'] if hash['js2_dir']
24
+ self.haml_engine = eval('::' + hash['haml_engine_class'] + '.new') if hash['haml_engine_class']
25
+
26
+ write_dir = hash['write_dir'] || hash['out_dir']
27
+ self.file_handler.out_dir = write_dir if write_dir
28
+ self.file_handler.haml_dir = hash['haml_dir'] || self.file_handler.js2_dir
29
+ end
30
+
31
+ def load_yml (yml, env = nil)
32
+ hash = YAML.load_file(yml)
33
+ hash = hash[env] if env
34
+ self.load_hash(hash)
35
+ end
36
+
37
+ def rails! (env = nil)
38
+ self.out_dir = './public/javascripts'
39
+ self.js2_dir = './app/js2'
40
+ self.haml_dir = './app/js2'
41
+ end
42
+
43
+
44
+ def out_dir= (dir)
45
+ @file_handler.out_dir = dir
46
+ end
47
+
48
+ def js2_dir= (dir)
49
+ @file_handler.js2_dir = dir
50
+ end
51
+
52
+ def haml_dir= (dir)
53
+ @file_handler.haml_dir = dir
54
+ end
55
+
56
+ def to_s
57
+ return <<-END
58
+ js2_dir: #{@file_handler.js2_dir}
59
+ out_dir: #{@file_handler.out_dir}
60
+ haml_dir: #{@file_handler.haml_dir}
61
+ END
62
+ end
63
+
64
+ def from_yml (yml, env = nil)
65
+ hash = YAML.load(yml)
66
+ hash = hash[env] if env
67
+ return self.from_hash(hash)
68
+ end
69
+
70
+ def self.from_hash(hash)
71
+ config = self.new
72
+ config.load_hash(hash)
73
+
74
+ return config
75
+ end
76
+
77
+ def example_yml
78
+ return <<-END
79
+ js2_dir: './app/js2'
80
+ haml_dir: './app/js2'
81
+ out_dir: './public/javascripts'
82
+ END
83
+ end
84
+ end
@@ -0,0 +1,34 @@
1
+ class JS2::Util::Exec
2
+ def initialize (config, options)
3
+ @config = config
4
+ end
5
+
6
+ def rails!
7
+ @config.out_dir = './public/javascripts'
8
+ @config.js2_dir = './app/js2'
9
+ @config.asset_dir = './app/js2'
10
+ end
11
+
12
+ def normal!
13
+ @config.out_dir = '.'
14
+ @config.js2_dir = '.'
15
+ end
16
+
17
+ def daemonize (timeout = 1)
18
+ get_processor!
19
+ while 1
20
+ @processor.process!
21
+ sleep timeout
22
+ end
23
+ end
24
+
25
+ def process
26
+ get_processor!
27
+ @processor.process!
28
+ end
29
+
30
+ def get_processor!
31
+ @processor = JS2::Util::Processor.new(@config)
32
+ end
33
+
34
+ end
@@ -0,0 +1,73 @@
1
+ class JS2::Util::FileHandler
2
+ attr_accessor :js2_dir, :out_dir, :haml_dir, :doc_dir
3
+
4
+ def initialize
5
+ @js2_dir = '.'
6
+ @out_dir = '.'
7
+ @haml_dir = '.'
8
+ @doc_dir = '.'
9
+
10
+ @lookup = {
11
+ :js2 => :js2_dir,
12
+ :haml => :haml_dir,
13
+ :yml => :js2_dir
14
+ }
15
+
16
+ @mtimes = Hash.new
17
+ end
18
+
19
+ def docfile (file)
20
+ return file.sub(/^#{@js2_dir}/, @doc_dir).sub(/\.js2$/, '.js')
21
+ end
22
+
23
+ def needs_update
24
+ @found = Hash.new
25
+
26
+ files = []
27
+ files += get_changed_files(:js2)
28
+ files += get_changed_files(:haml)
29
+ files += get_changed_files(:yml)
30
+
31
+ missing = false
32
+ @mtimes.keys.each do |f|
33
+ unless @found[f]
34
+ @mtimes.delete(f)
35
+ missing = true
36
+ end
37
+ end
38
+
39
+ return files
40
+ end
41
+
42
+ def outfile (file)
43
+ return file.sub(/^#{@js2_dir}/, @out_dir).sub(/\.js2$/, '.js')
44
+ end
45
+
46
+ def get_files (ext)
47
+ ext = ext.to_sym
48
+
49
+ my_ext = :js2 == ext ? 'js2' : "js2.#{ext}"
50
+ my_dir = self.send(@lookup[ext])
51
+ return Dir.glob(my_dir + "/**/*/*.#{my_ext}") + Dir.glob(my_dir + "/*.#{my_ext}")
52
+ end
53
+
54
+ def reset!
55
+ @mtimes = Hash.new
56
+ end
57
+
58
+ def get_changed_files (ext)
59
+ ret = []
60
+ get_files(ext).each do |f|
61
+ @found[f] = true
62
+ mtime = File.mtime(f)
63
+ if ! @mtimes[f] || @mtimes[f] < mtime
64
+ @mtimes[f] = mtime
65
+ ret << f
66
+ end
67
+ end
68
+
69
+ return ret
70
+ end
71
+
72
+
73
+ end