ejs-rcompiler 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Ben Curren
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,39 @@
1
+ = ejs-rcompiler
2
+
3
+ A simple ruby compiler that compiles an ejs file to js. Here is an example usage:
4
+
5
+ == In file named or.ui.Cities.ejs
6
+ <ul>
7
+ <%# Loop through each city %>
8
+ <% $.each(cities, function() { %>
9
+ <li><%= this.name %></li>
10
+ <% }) %>
11
+ </ul>
12
+
13
+ == Compile the file like so
14
+ compiler = Ejs::Compiler.new
15
+ compiler.compile(ejs_file)
16
+
17
+ This will create a .js file with a JavaScript class name that matches the file name.
18
+
19
+ == You can then require the output js file as such.
20
+ <script src="/javascripts/or.ui.Cities.js"></script>
21
+ <script>
22
+ document.body.innerHTML = or.ui.Cities.template({
23
+ cities: [{ name: "Paris" }, { name: "Mountain View" }, { name: "Las Vegas" }]
24
+ });
25
+ </script>
26
+
27
+ == Note on Patches/Pull Requests
28
+
29
+ * Fork the project.
30
+ * Make your feature addition or bug fix.
31
+ * Add tests for it. This is important so I don't break it in a
32
+ future version unintentionally.
33
+ * Commit, do not mess with rakefile, version, or history.
34
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
35
+ * Send me a pull request. Bonus points for topic branches.
36
+
37
+ == Copyright
38
+
39
+ Copyright (c) 2010 Ben Curren. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,60 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "ejs-rcompiler"
8
+ gem.summary = %Q{Ejs to Javascript compiler written in Ruby. This allows you to define HTML heavy Javascript components in EJS and compile them down to Javascript functions for later use.}
9
+ gem.description = %Q{Ejs to Javascript compiler.}
10
+ gem.email = "ben@outright.com"
11
+ gem.homepage = "http://github.com/bcurren/ejs-rcompiler"
12
+ gem.authors = ["Ben Curren"]
13
+ gem.add_dependency "treetop", ">= 1.4.0"
14
+ gem.add_development_dependency "rspec", ">= 1.2.9"
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
+ end
21
+
22
+ require 'spec/rake/spectask'
23
+ Spec::Rake::SpecTask.new(:spec) do |spec|
24
+ spec.libs << 'lib' << 'spec'
25
+ spec.spec_files = FileList['spec/**/*_spec.rb']
26
+ end
27
+
28
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.pattern = 'spec/**/*_spec.rb'
31
+ spec.rcov = true
32
+ end
33
+
34
+ task :spec => :check_dependencies
35
+
36
+ task :default => :spec
37
+
38
+ require 'rake/rdoctask'
39
+ Rake::RDocTask.new do |rdoc|
40
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
41
+
42
+ rdoc.rdoc_dir = 'rdoc'
43
+ rdoc.title = "ejs-rcompiler #{version}"
44
+ rdoc.rdoc_files.include('README*')
45
+ rdoc.rdoc_files.include('lib/**/*.rb')
46
+ end
47
+
48
+ PARSER_FILE="lib/ejs/grammar.rb"
49
+ GRAMMAR_FILE="lib/ejs/grammar.treetop"
50
+
51
+ namespace :treetop do
52
+ desc "Generate parser from treetop grammar."
53
+ task :generate => PARSER_FILE
54
+ end
55
+
56
+ file PARSER_FILE => GRAMMAR_FILE do |t|
57
+ require 'treetop'
58
+ compiler = Treetop::Compiler::GrammarCompiler.new
59
+ compiler.compile(t.prerequisites[0], t.name)
60
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
@@ -0,0 +1 @@
1
+ Autotest.add_discovery { "rspec" }
@@ -0,0 +1,68 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{ejs-rcompiler}
8
+ s.version = "0.1.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Ben Curren"]
12
+ s.date = %q{2010-09-27}
13
+ s.description = %q{Ejs to Javascript compiler.}
14
+ s.email = %q{ben@outright.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "autotest/discover.rb",
27
+ "ejs-rcompiler.gemspec",
28
+ "lib/ejs-rcompiler.rb",
29
+ "lib/ejs/compiler.rb",
30
+ "lib/ejs/grammar.rb",
31
+ "lib/ejs/grammar.treetop",
32
+ "lib/ejs/parse_error.rb",
33
+ "lib/ejs/parser.rb",
34
+ "spec/compiler_spec.rb",
35
+ "spec/fixtures/Cities.ejs",
36
+ "spec/fixtures/Cities.js",
37
+ "spec/parser_spec.rb",
38
+ "spec/spec.opts",
39
+ "spec/spec_helper.rb"
40
+ ]
41
+ s.homepage = %q{http://github.com/bcurren/ejs-rcompiler}
42
+ s.rdoc_options = ["--charset=UTF-8"]
43
+ s.require_paths = ["lib"]
44
+ s.rubygems_version = %q{1.3.6}
45
+ s.summary = %q{Ejs to Javascript compiler written in Ruby. This allows you to define HTML heavy Javascript components in EJS and compile them down to Javascript functions for later use.}
46
+ s.test_files = [
47
+ "spec/compiler_spec.rb",
48
+ "spec/parser_spec.rb",
49
+ "spec/spec_helper.rb"
50
+ ]
51
+
52
+ if s.respond_to? :specification_version then
53
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
54
+ s.specification_version = 3
55
+
56
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
57
+ s.add_runtime_dependency(%q<treetop>, [">= 1.4.0"])
58
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
59
+ else
60
+ s.add_dependency(%q<treetop>, [">= 1.4.0"])
61
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
62
+ end
63
+ else
64
+ s.add_dependency(%q<treetop>, [">= 1.4.0"])
65
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
66
+ end
67
+ end
68
+
@@ -0,0 +1,91 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/parser")
2
+ require File.expand_path("#{File.dirname(__FILE__)}/parse_error")
3
+
4
+ module Ejs
5
+ AUTOGENERATED = "// Autogenerated from an Ejs file. Edits may be lost.\n"
6
+
7
+ class Compiler
8
+ def initialize
9
+ @parser = Parser.new
10
+ end
11
+
12
+ def compile(source_path, namespace = nil, target_path = source_path.gsub(/\.ejs\Z/, '.js'))
13
+ File.open(target_path, 'w') do |target_file|
14
+ target_file.write(AUTOGENERATED+"\n\n")
15
+ target_file.write(js_source(source_path, namespace))
16
+ end
17
+ end
18
+
19
+ # compile a ejs file into javascript
20
+ def js_source(source_path, namespace = nil, output_as_array = false)
21
+ template_name = File.basename(source_path, ".ejs")
22
+ template_name = "#{namespace}.#{template_name}" if namespace
23
+
24
+ js_source_from_string(template_name, File.read(source_path), output_as_array)
25
+ end
26
+
27
+ # compile a string containing ejs source into javascript
28
+ def js_source_from_string(template_name, content, output_as_array = false)
29
+ buffer = []
30
+ parsed = @parser.parse(content)
31
+ if parsed.nil?
32
+ raise ParseError.new(@parser.failure_reason, @parser.failure_line, @parser.failure_column)
33
+ end
34
+
35
+ template_namespace(buffer, template_name)
36
+ template_header(buffer, template_name)
37
+ parsed.elements.each do |element|
38
+ push_content(buffer, element)
39
+ end
40
+ template_footer(buffer)
41
+
42
+ output_as_array ? buffer : buffer.join("\n")
43
+ end
44
+
45
+ private
46
+
47
+ def template_namespace(buffer, template_name)
48
+ ns = template_name.split(".")
49
+ ns_str = "window"
50
+ begin
51
+ ns_str = "#{ns_str}.#{ns.shift}"
52
+ buffer.push("#{ns_str} = #{ns_str} || {};")
53
+ end until ns.empty?
54
+ end
55
+
56
+ def template_header(buffer, template_name)
57
+ buffer.push("#{template_name}.template = function(options) {")
58
+ buffer.push(" var p = [];")
59
+ buffer.push(" with(options) {")
60
+ end
61
+
62
+ def push_content(buffer, element)
63
+ case element.node_type
64
+ when 'static_content'
65
+ buffer.push(" p.push('#{escape(element.text_value)}');")
66
+ when 'scriplet'
67
+ buffer.push(" #{element.text_value.strip}")
68
+ when 'expression'
69
+ buffer.push(" p.push(#{element.text_value.strip});")
70
+ when 'comment'
71
+ buffer.push(" /*#{element.text_value}*/")
72
+ else
73
+ raise "Unknown node_type encountered while parsing: #{element.node_type}"
74
+ end
75
+ end
76
+
77
+ def template_footer(buffer)
78
+ buffer.push(" }")
79
+ buffer.push(" return p.join('');")
80
+ buffer.push("}")
81
+ end
82
+
83
+ def escape(content)
84
+ content.
85
+ gsub(/\n/, '\n').
86
+ gsub(/\t/, '\t').
87
+ gsub(/"/, '\"').
88
+ gsub(/'/, "\\\\'") # yes, this is how you escape a single quote
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,658 @@
1
+ # Autogenerated from a Treetop grammar. Edits may be lost.
2
+
3
+
4
+ module Ejs
5
+ module Grammar
6
+ include Treetop::Runtime
7
+
8
+ def root
9
+ @root ||= :program
10
+ end
11
+
12
+ def _nt_program
13
+ start_index = index
14
+ if node_cache[:program].has_key?(index)
15
+ cached = node_cache[:program][index]
16
+ if cached
17
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
18
+ @index = cached.interval.end
19
+ end
20
+ return cached
21
+ end
22
+
23
+ s0, i0 = [], index
24
+ loop do
25
+ i1 = index
26
+ r2 = _nt_expression
27
+ if r2
28
+ r1 = r2
29
+ else
30
+ r3 = _nt_comment
31
+ if r3
32
+ r1 = r3
33
+ else
34
+ r4 = _nt_scriplet
35
+ if r4
36
+ r1 = r4
37
+ else
38
+ r5 = _nt_static_content
39
+ if r5
40
+ r1 = r5
41
+ else
42
+ @index = i1
43
+ r1 = nil
44
+ end
45
+ end
46
+ end
47
+ end
48
+ if r1
49
+ s0 << r1
50
+ else
51
+ break
52
+ end
53
+ end
54
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
55
+
56
+ node_cache[:program][start_index] = r0
57
+
58
+ r0
59
+ end
60
+
61
+ module Expression0
62
+ def script_content
63
+ elements[1]
64
+ end
65
+
66
+ end
67
+
68
+ module Expression1
69
+ def node_type
70
+ "expression"
71
+ end
72
+
73
+ def text_value
74
+ script_content.text_value
75
+ end
76
+ end
77
+
78
+ def _nt_expression
79
+ start_index = index
80
+ if node_cache[:expression].has_key?(index)
81
+ cached = node_cache[:expression][index]
82
+ if cached
83
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
84
+ @index = cached.interval.end
85
+ end
86
+ return cached
87
+ end
88
+
89
+ i0, s0 = index, []
90
+ if has_terminal?('<%=', false, index)
91
+ r1 = instantiate_node(SyntaxNode,input, index...(index + 3))
92
+ @index += 3
93
+ else
94
+ terminal_parse_failure('<%=')
95
+ r1 = nil
96
+ end
97
+ s0 << r1
98
+ if r1
99
+ r2 = _nt_script_content
100
+ s0 << r2
101
+ if r2
102
+ if has_terminal?('%>', false, index)
103
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 2))
104
+ @index += 2
105
+ else
106
+ terminal_parse_failure('%>')
107
+ r3 = nil
108
+ end
109
+ s0 << r3
110
+ end
111
+ end
112
+ if s0.last
113
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
114
+ r0.extend(Expression0)
115
+ r0.extend(Expression1)
116
+ else
117
+ @index = i0
118
+ r0 = nil
119
+ end
120
+
121
+ node_cache[:expression][start_index] = r0
122
+
123
+ r0
124
+ end
125
+
126
+ module Comment0
127
+ def script_content
128
+ elements[1]
129
+ end
130
+
131
+ end
132
+
133
+ module Comment1
134
+ def node_type
135
+ "comment"
136
+ end
137
+
138
+ def text_value
139
+ script_content.text_value
140
+ end
141
+ end
142
+
143
+ def _nt_comment
144
+ start_index = index
145
+ if node_cache[:comment].has_key?(index)
146
+ cached = node_cache[:comment][index]
147
+ if cached
148
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
149
+ @index = cached.interval.end
150
+ end
151
+ return cached
152
+ end
153
+
154
+ i0, s0 = index, []
155
+ if has_terminal?('<%#', false, index)
156
+ r1 = instantiate_node(SyntaxNode,input, index...(index + 3))
157
+ @index += 3
158
+ else
159
+ terminal_parse_failure('<%#')
160
+ r1 = nil
161
+ end
162
+ s0 << r1
163
+ if r1
164
+ r2 = _nt_script_content
165
+ s0 << r2
166
+ if r2
167
+ if has_terminal?('%>', false, index)
168
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 2))
169
+ @index += 2
170
+ else
171
+ terminal_parse_failure('%>')
172
+ r3 = nil
173
+ end
174
+ s0 << r3
175
+ end
176
+ end
177
+ if s0.last
178
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
179
+ r0.extend(Comment0)
180
+ r0.extend(Comment1)
181
+ else
182
+ @index = i0
183
+ r0 = nil
184
+ end
185
+
186
+ node_cache[:comment][start_index] = r0
187
+
188
+ r0
189
+ end
190
+
191
+ module Scriplet0
192
+ def script_content
193
+ elements[1]
194
+ end
195
+
196
+ end
197
+
198
+ module Scriplet1
199
+ def node_type
200
+ "scriplet"
201
+ end
202
+
203
+ def text_value
204
+ script_content.text_value
205
+ end
206
+ end
207
+
208
+ def _nt_scriplet
209
+ start_index = index
210
+ if node_cache[:scriplet].has_key?(index)
211
+ cached = node_cache[:scriplet][index]
212
+ if cached
213
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
214
+ @index = cached.interval.end
215
+ end
216
+ return cached
217
+ end
218
+
219
+ i0, s0 = index, []
220
+ if has_terminal?('<%', false, index)
221
+ r1 = instantiate_node(SyntaxNode,input, index...(index + 2))
222
+ @index += 2
223
+ else
224
+ terminal_parse_failure('<%')
225
+ r1 = nil
226
+ end
227
+ s0 << r1
228
+ if r1
229
+ r2 = _nt_script_content
230
+ s0 << r2
231
+ if r2
232
+ if has_terminal?('%>', false, index)
233
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 2))
234
+ @index += 2
235
+ else
236
+ terminal_parse_failure('%>')
237
+ r3 = nil
238
+ end
239
+ s0 << r3
240
+ end
241
+ end
242
+ if s0.last
243
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
244
+ r0.extend(Scriplet0)
245
+ r0.extend(Scriplet1)
246
+ else
247
+ @index = i0
248
+ r0 = nil
249
+ end
250
+
251
+ node_cache[:scriplet][start_index] = r0
252
+
253
+ r0
254
+ end
255
+
256
+ module StaticContent0
257
+ def node_type
258
+ "static_content"
259
+ end
260
+
261
+ def text_value
262
+ elements.map do |element|
263
+ element.text_value
264
+ end.join('')
265
+ end
266
+ end
267
+
268
+ def _nt_static_content
269
+ start_index = index
270
+ if node_cache[:static_content].has_key?(index)
271
+ cached = node_cache[:static_content][index]
272
+ if cached
273
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
274
+ @index = cached.interval.end
275
+ end
276
+ return cached
277
+ end
278
+
279
+ s0, i0 = [], index
280
+ loop do
281
+ i1 = index
282
+ r2 = _nt_right_delimeter_escaped
283
+ if r2
284
+ r1 = r2
285
+ else
286
+ r3 = _nt_left_delimeter_escaped
287
+ if r3
288
+ r1 = r3
289
+ else
290
+ r4 = _nt_unescaped_static_content
291
+ if r4
292
+ r1 = r4
293
+ else
294
+ @index = i1
295
+ r1 = nil
296
+ end
297
+ end
298
+ end
299
+ if r1
300
+ s0 << r1
301
+ else
302
+ break
303
+ end
304
+ end
305
+ if s0.empty?
306
+ @index = i0
307
+ r0 = nil
308
+ else
309
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
310
+ r0.extend(StaticContent0)
311
+ end
312
+
313
+ node_cache[:static_content][start_index] = r0
314
+
315
+ r0
316
+ end
317
+
318
+ module ScriptContent0
319
+ def text_value
320
+ elements.map do |element|
321
+ element.text_value
322
+ end.join('')
323
+ end
324
+ end
325
+
326
+ def _nt_script_content
327
+ start_index = index
328
+ if node_cache[:script_content].has_key?(index)
329
+ cached = node_cache[:script_content][index]
330
+ if cached
331
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
332
+ @index = cached.interval.end
333
+ end
334
+ return cached
335
+ end
336
+
337
+ s0, i0 = [], index
338
+ loop do
339
+ i1 = index
340
+ r2 = _nt_right_delimeter_escaped
341
+ if r2
342
+ r1 = r2
343
+ else
344
+ r3 = _nt_left_delimeter_escaped
345
+ if r3
346
+ r1 = r3
347
+ else
348
+ r4 = _nt_unescape_script_content
349
+ if r4
350
+ r1 = r4
351
+ else
352
+ @index = i1
353
+ r1 = nil
354
+ end
355
+ end
356
+ end
357
+ if r1
358
+ s0 << r1
359
+ else
360
+ break
361
+ end
362
+ end
363
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
364
+ r0.extend(ScriptContent0)
365
+
366
+ node_cache[:script_content][start_index] = r0
367
+
368
+ r0
369
+ end
370
+
371
+ module RightDelimeterEscaped0
372
+ def text_value
373
+ '%>'
374
+ end
375
+ end
376
+
377
+ def _nt_right_delimeter_escaped
378
+ start_index = index
379
+ if node_cache[:right_delimeter_escaped].has_key?(index)
380
+ cached = node_cache[:right_delimeter_escaped][index]
381
+ if cached
382
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
383
+ @index = cached.interval.end
384
+ end
385
+ return cached
386
+ end
387
+
388
+ if has_terminal?('%%>', false, index)
389
+ r0 = instantiate_node(SyntaxNode,input, index...(index + 3))
390
+ r0.extend(RightDelimeterEscaped0)
391
+ @index += 3
392
+ else
393
+ terminal_parse_failure('%%>')
394
+ r0 = nil
395
+ end
396
+
397
+ node_cache[:right_delimeter_escaped][start_index] = r0
398
+
399
+ r0
400
+ end
401
+
402
+ module LeftDelimeterEscaped0
403
+ def text_value
404
+ '<%'
405
+ end
406
+ end
407
+
408
+ def _nt_left_delimeter_escaped
409
+ start_index = index
410
+ if node_cache[:left_delimeter_escaped].has_key?(index)
411
+ cached = node_cache[:left_delimeter_escaped][index]
412
+ if cached
413
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
414
+ @index = cached.interval.end
415
+ end
416
+ return cached
417
+ end
418
+
419
+ if has_terminal?('<%%', false, index)
420
+ r0 = instantiate_node(SyntaxNode,input, index...(index + 3))
421
+ r0.extend(LeftDelimeterEscaped0)
422
+ @index += 3
423
+ else
424
+ terminal_parse_failure('<%%')
425
+ r0 = nil
426
+ end
427
+
428
+ node_cache[:left_delimeter_escaped][start_index] = r0
429
+
430
+ r0
431
+ end
432
+
433
+ module UnescapedStaticContent0
434
+ end
435
+
436
+ def _nt_unescaped_static_content
437
+ start_index = index
438
+ if node_cache[:unescaped_static_content].has_key?(index)
439
+ cached = node_cache[:unescaped_static_content][index]
440
+ if cached
441
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
442
+ @index = cached.interval.end
443
+ end
444
+ return cached
445
+ end
446
+
447
+ s0, i0 = [], index
448
+ loop do
449
+ i1, s1 = index, []
450
+ i2 = index
451
+ if has_terminal?('<%%', false, index)
452
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 3))
453
+ @index += 3
454
+ else
455
+ terminal_parse_failure('<%%')
456
+ r3 = nil
457
+ end
458
+ if r3
459
+ r2 = nil
460
+ else
461
+ @index = i2
462
+ r2 = instantiate_node(SyntaxNode,input, index...index)
463
+ end
464
+ s1 << r2
465
+ if r2
466
+ i4 = index
467
+ if has_terminal?('%%>', false, index)
468
+ r5 = instantiate_node(SyntaxNode,input, index...(index + 3))
469
+ @index += 3
470
+ else
471
+ terminal_parse_failure('%%>')
472
+ r5 = nil
473
+ end
474
+ if r5
475
+ r4 = nil
476
+ else
477
+ @index = i4
478
+ r4 = instantiate_node(SyntaxNode,input, index...index)
479
+ end
480
+ s1 << r4
481
+ if r4
482
+ i6 = index
483
+ if has_terminal?('<%', false, index)
484
+ r7 = instantiate_node(SyntaxNode,input, index...(index + 2))
485
+ @index += 2
486
+ else
487
+ terminal_parse_failure('<%')
488
+ r7 = nil
489
+ end
490
+ if r7
491
+ r6 = nil
492
+ else
493
+ @index = i6
494
+ r6 = instantiate_node(SyntaxNode,input, index...index)
495
+ end
496
+ s1 << r6
497
+ if r6
498
+ i8 = index
499
+ if has_terminal?('%>', false, index)
500
+ r9 = instantiate_node(SyntaxNode,input, index...(index + 2))
501
+ @index += 2
502
+ else
503
+ terminal_parse_failure('%>')
504
+ r9 = nil
505
+ end
506
+ if r9
507
+ r8 = nil
508
+ else
509
+ @index = i8
510
+ r8 = instantiate_node(SyntaxNode,input, index...index)
511
+ end
512
+ s1 << r8
513
+ if r8
514
+ if index < input_length
515
+ r10 = instantiate_node(SyntaxNode,input, index...(index + 1))
516
+ @index += 1
517
+ else
518
+ terminal_parse_failure("any character")
519
+ r10 = nil
520
+ end
521
+ s1 << r10
522
+ end
523
+ end
524
+ end
525
+ end
526
+ if s1.last
527
+ r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
528
+ r1.extend(UnescapedStaticContent0)
529
+ else
530
+ @index = i1
531
+ r1 = nil
532
+ end
533
+ if r1
534
+ s0 << r1
535
+ else
536
+ break
537
+ end
538
+ end
539
+ if s0.empty?
540
+ @index = i0
541
+ r0 = nil
542
+ else
543
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
544
+ end
545
+
546
+ node_cache[:unescaped_static_content][start_index] = r0
547
+
548
+ r0
549
+ end
550
+
551
+ module UnescapeScriptContent0
552
+ end
553
+
554
+ def _nt_unescape_script_content
555
+ start_index = index
556
+ if node_cache[:unescape_script_content].has_key?(index)
557
+ cached = node_cache[:unescape_script_content][index]
558
+ if cached
559
+ cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
560
+ @index = cached.interval.end
561
+ end
562
+ return cached
563
+ end
564
+
565
+ s0, i0 = [], index
566
+ loop do
567
+ i1, s1 = index, []
568
+ i2 = index
569
+ if has_terminal?('%>', false, index)
570
+ r3 = instantiate_node(SyntaxNode,input, index...(index + 2))
571
+ @index += 2
572
+ else
573
+ terminal_parse_failure('%>')
574
+ r3 = nil
575
+ end
576
+ if r3
577
+ r2 = nil
578
+ else
579
+ @index = i2
580
+ r2 = instantiate_node(SyntaxNode,input, index...index)
581
+ end
582
+ s1 << r2
583
+ if r2
584
+ i4 = index
585
+ if has_terminal?('%%>', false, index)
586
+ r5 = instantiate_node(SyntaxNode,input, index...(index + 3))
587
+ @index += 3
588
+ else
589
+ terminal_parse_failure('%%>')
590
+ r5 = nil
591
+ end
592
+ if r5
593
+ r4 = nil
594
+ else
595
+ @index = i4
596
+ r4 = instantiate_node(SyntaxNode,input, index...index)
597
+ end
598
+ s1 << r4
599
+ if r4
600
+ i6 = index
601
+ if has_terminal?('<%%', false, index)
602
+ r7 = instantiate_node(SyntaxNode,input, index...(index + 3))
603
+ @index += 3
604
+ else
605
+ terminal_parse_failure('<%%')
606
+ r7 = nil
607
+ end
608
+ if r7
609
+ r6 = nil
610
+ else
611
+ @index = i6
612
+ r6 = instantiate_node(SyntaxNode,input, index...index)
613
+ end
614
+ s1 << r6
615
+ if r6
616
+ if index < input_length
617
+ r8 = instantiate_node(SyntaxNode,input, index...(index + 1))
618
+ @index += 1
619
+ else
620
+ terminal_parse_failure("any character")
621
+ r8 = nil
622
+ end
623
+ s1 << r8
624
+ end
625
+ end
626
+ end
627
+ if s1.last
628
+ r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
629
+ r1.extend(UnescapeScriptContent0)
630
+ else
631
+ @index = i1
632
+ r1 = nil
633
+ end
634
+ if r1
635
+ s0 << r1
636
+ else
637
+ break
638
+ end
639
+ end
640
+ if s0.empty?
641
+ @index = i0
642
+ r0 = nil
643
+ else
644
+ r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
645
+ end
646
+
647
+ node_cache[:unescape_script_content][start_index] = r0
648
+
649
+ r0
650
+ end
651
+
652
+ end
653
+
654
+ class GrammarParser < Treetop::Runtime::CompiledParser
655
+ include Grammar
656
+ end
657
+
658
+ end
@@ -0,0 +1,91 @@
1
+ module Ejs
2
+ grammar Grammar
3
+ rule program
4
+ (expression / comment / scriplet / static_content)*
5
+ end
6
+
7
+ rule expression
8
+ '<%=' script_content '%>' {
9
+ def node_type
10
+ "expression"
11
+ end
12
+
13
+ def text_value
14
+ script_content.text_value
15
+ end
16
+ }
17
+ end
18
+
19
+ rule comment
20
+ '<%#' script_content '%>' {
21
+ def node_type
22
+ "comment"
23
+ end
24
+
25
+ def text_value
26
+ script_content.text_value
27
+ end
28
+ }
29
+ end
30
+
31
+ rule scriplet
32
+ '<%' script_content '%>' {
33
+ def node_type
34
+ "scriplet"
35
+ end
36
+
37
+ def text_value
38
+ script_content.text_value
39
+ end
40
+ }
41
+ end
42
+
43
+ rule static_content
44
+ (right_delimeter_escaped / left_delimeter_escaped / unescaped_static_content)+ {
45
+ def node_type
46
+ "static_content"
47
+ end
48
+
49
+ def text_value
50
+ elements.map do |element|
51
+ element.text_value
52
+ end.join('')
53
+ end
54
+ }
55
+ end
56
+
57
+ rule script_content
58
+ (right_delimeter_escaped / left_delimeter_escaped / unescape_script_content)* {
59
+ def text_value
60
+ elements.map do |element|
61
+ element.text_value
62
+ end.join('')
63
+ end
64
+ }
65
+ end
66
+
67
+ rule right_delimeter_escaped
68
+ '%%>' {
69
+ def text_value
70
+ '%>'
71
+ end
72
+ }
73
+ end
74
+
75
+ rule left_delimeter_escaped
76
+ '<%%' {
77
+ def text_value
78
+ '<%'
79
+ end
80
+ }
81
+ end
82
+
83
+ rule unescaped_static_content
84
+ (!'<%%' !'%%>' !'<%' !'%>' .)+
85
+ end
86
+
87
+ rule unescape_script_content
88
+ (!'%>' !'%%>' !'<%%' .)+
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,11 @@
1
+ module Ejs
2
+ class ParseError < StandardError
3
+ attr_reader :failure_reason, :failure_line, :failure_column
4
+ def initialize(failure_reason, failure_line, failure_column)
5
+ @failure_reason = failure_reason
6
+ @failure_line = failure_line
7
+ @failure_column = failure_column
8
+ super(@failure_reason)
9
+ end
10
+ end
11
+ end
data/lib/ejs/parser.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'treetop'
2
+ require File.expand_path("#{File.dirname(__FILE__)}/grammar")
3
+
4
+ module Ejs
5
+ class Parser < Treetop::Runtime::CompiledParser
6
+ include ::Ejs::Grammar
7
+ end
8
+ end
@@ -0,0 +1 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/ejs/compiler")
@@ -0,0 +1,70 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'tempfile'
3
+ require 'fileutils'
4
+
5
+ describe "Compiler" do
6
+ before(:each) do
7
+ @compiler = Ejs::Compiler.new
8
+ end
9
+
10
+ it "should js_source_from_string function header and footer" do
11
+ compiled = @compiler.js_source_from_string("TemplateName", "", true)
12
+
13
+ compiled[0].should == "window.TemplateName = window.TemplateName || {};"
14
+ compiled[1].should == "TemplateName.template = function(options) {"
15
+ compiled[2].should == " var p = [];"
16
+ compiled[3].should == " with(options) {"
17
+
18
+ compiled[-3].should == " }"
19
+ compiled[-2].should == " return p.join('');"
20
+ compiled[-1].should == "}"
21
+ end
22
+
23
+ it "should add namespace declaration" do
24
+ compiled = @compiler.js_source_from_string("my.namespace.MyClass", "", true)
25
+ compiled[0].should == "window.my = window.my || {};"
26
+ compiled[1].should == "window.my.namespace = window.my.namespace || {};"
27
+ compiled[2].should == "window.my.namespace.MyClass = window.my.namespace.MyClass || {};"
28
+ compiled[3].should == "my.namespace.MyClass.template = function(options) {"
29
+ end
30
+
31
+ it "should js_source_from_string static content" do
32
+ compiled = @compiler.js_source_from_string("TemplateName", "<p>Static output</p>", true)
33
+ compiled[4].should == " p.push('<p>Static output</p>');"
34
+ end
35
+
36
+ it "should escape static content" do
37
+ compiled = @compiler.js_source_from_string("TemplateName", "\"\'\n\t", true)
38
+ compiled[4].should == " p.push('\\\"\\\'\\n\\t');"
39
+ end
40
+
41
+ it "should js_source_from_string scriplet" do
42
+ compiled = @compiler.js_source_from_string("TemplateName", "<% console.log('test') %>", true)
43
+ compiled[4].should == " console.log('test')"
44
+ end
45
+
46
+ it "should js_source_from_string expression" do
47
+ compiled = @compiler.js_source_from_string("TemplateName", "<%= this.name %>", true)
48
+ compiled[4].should == " p.push(this.name);"
49
+ end
50
+
51
+ it "should js_source_from_string comments" do
52
+ compiled = @compiler.js_source_from_string("TemplateName", "<%# This is a comment %>", true)
53
+ compiled[4].should == " /* This is a comment */"
54
+ end
55
+
56
+ it "should raise exception when syntax is invalid" do
57
+ lambda{ @compiler.js_source_from_string("Name", "<%= this.name %> %>") }.should raise_error(::Ejs::ParseError)
58
+ end
59
+
60
+ it "should compile a source file to a target file" do
61
+ fixtures = "#{File.dirname(__FILE__)}/fixtures"
62
+ target_file = "/tmp/Cities.js"
63
+ expected_target_file = "#{fixtures}/Cities.js"
64
+
65
+ FileUtils.rm(target_file) if File.exist?(target_file)
66
+
67
+ @compiler.compile("#{fixtures}/Cities.ejs", "or.ui", target_file)
68
+ File.open(target_file).read.should == File.open(expected_target_file).read
69
+ end
70
+ end
@@ -0,0 +1,6 @@
1
+ <ul>
2
+ <%# Loop through each city %>
3
+ <% $.each(cities, function() { %>
4
+ <li><%= this.name %></li>
5
+ <% }) %>
6
+ </ul>
@@ -0,0 +1,21 @@
1
+ // Autogenerated from an Ejs file. Edits may be lost.
2
+
3
+
4
+ window.or = window.or || {};
5
+ window.or.ui = window.or.ui || {};
6
+ window.or.ui.Cities = window.or.ui.Cities || {};
7
+ or.ui.Cities.template = function(options) {
8
+ var p = [];
9
+ with(options) {
10
+ p.push('<ul>\n ');
11
+ /* Loop through each city */
12
+ p.push('\n ');
13
+ $.each(cities, function() {
14
+ p.push('\n <li>');
15
+ p.push(this.name);
16
+ p.push('</li>\n ');
17
+ })
18
+ p.push('\n</ul>\n');
19
+ }
20
+ return p.join('');
21
+ }
@@ -0,0 +1,57 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Parser" do
4
+ before(:each) do
5
+ @parser = Ejs::Parser.new
6
+ end
7
+
8
+ it "should parse static content" do
9
+ parsed = parse("<p>Static output</p>")
10
+ parsed.size.should == 1
11
+ parsed[0].node_type.should == 'static_content'
12
+ parsed[0].text_value.should == '<p>Static output</p>'
13
+ end
14
+
15
+ it "should parse scriplet" do
16
+ parsed = parse("<% console.log('test') %>")
17
+ parsed.size.should == 1
18
+ parsed[0].node_type.should == 'scriplet'
19
+ parsed[0].text_value.should == " console.log('test') "
20
+ end
21
+
22
+ it "should parse expression" do
23
+ parsed = parse("<%= this.name %>")
24
+ parsed.size.should == 1
25
+ parsed[0].node_type.should == 'expression'
26
+ parsed[0].text_value.should == " this.name "
27
+ end
28
+
29
+ it "should parse comment" do
30
+ parsed = parse("<%# This is a comment %>")
31
+ parsed.size.should == 1
32
+ parsed[0].node_type.should == 'comment'
33
+ parsed[0].text_value.should == " This is a comment "
34
+ end
35
+
36
+ it "should escape left and right delimter in script content" do
37
+ parsed = parse("<% console.log('Example: <%% puts 'test' %%>') %>")
38
+ parsed.size.should == 1
39
+ parsed[0].node_type.should == 'scriplet'
40
+ parsed[0].text_value.should == " console.log('Example: <% puts 'test' %>') "
41
+ end
42
+
43
+ it "should escape elft and right delimeter in staic content" do
44
+ parsed = parse("<%% this is a test %%>")
45
+ parsed.size.should == 1
46
+ parsed[0].node_type.should == 'static_content'
47
+ parsed[0].text_value.should == "<% this is a test %>"
48
+ end
49
+
50
+ private
51
+
52
+ def parse(ejs_conent)
53
+ result = @parser.parse(ejs_conent)
54
+ result.should_not be_nil
55
+ result.elements
56
+ end
57
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,25 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'rubygems'
4
+ require 'ejs-rcompiler'
5
+ require 'spec'
6
+ require 'spec/autorun'
7
+
8
+ Spec::Runner.configure do |config|
9
+
10
+ end
11
+
12
+ Spec::Matchers.define :equal_ignoring_spaces do |expected|
13
+ match do |actual|
14
+ clean_spaces(actual) == clean_spaces(expected)
15
+ end
16
+
17
+ def clean_spaces(text)
18
+ text = "" if text.nil?
19
+ text.
20
+ gsub(/\s+/, " ").
21
+ strip.
22
+ gsub(/' /, "'").
23
+ gsub(/ '/, "'")
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ejs-rcompiler
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 1
9
+ version: 0.1.1
10
+ platform: ruby
11
+ authors:
12
+ - Ben Curren
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-09-27 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: treetop
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 4
30
+ - 0
31
+ version: 1.4.0
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: rspec
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 1
43
+ - 2
44
+ - 9
45
+ version: 1.2.9
46
+ type: :development
47
+ version_requirements: *id002
48
+ description: Ejs to Javascript compiler.
49
+ email: ben@outright.com
50
+ executables: []
51
+
52
+ extensions: []
53
+
54
+ extra_rdoc_files:
55
+ - LICENSE
56
+ - README.rdoc
57
+ files:
58
+ - .document
59
+ - .gitignore
60
+ - LICENSE
61
+ - README.rdoc
62
+ - Rakefile
63
+ - VERSION
64
+ - autotest/discover.rb
65
+ - ejs-rcompiler.gemspec
66
+ - lib/ejs-rcompiler.rb
67
+ - lib/ejs/compiler.rb
68
+ - lib/ejs/grammar.rb
69
+ - lib/ejs/grammar.treetop
70
+ - lib/ejs/parse_error.rb
71
+ - lib/ejs/parser.rb
72
+ - spec/compiler_spec.rb
73
+ - spec/fixtures/Cities.ejs
74
+ - spec/fixtures/Cities.js
75
+ - spec/parser_spec.rb
76
+ - spec/spec.opts
77
+ - spec/spec_helper.rb
78
+ has_rdoc: true
79
+ homepage: http://github.com/bcurren/ejs-rcompiler
80
+ licenses: []
81
+
82
+ post_install_message:
83
+ rdoc_options:
84
+ - --charset=UTF-8
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ segments:
99
+ - 0
100
+ version: "0"
101
+ requirements: []
102
+
103
+ rubyforge_project:
104
+ rubygems_version: 1.3.6
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: Ejs to Javascript compiler written in Ruby. This allows you to define HTML heavy Javascript components in EJS and compile them down to Javascript functions for later use.
108
+ test_files:
109
+ - spec/compiler_spec.rb
110
+ - spec/parser_spec.rb
111
+ - spec/spec_helper.rb