jejune 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gemtest +0 -0
- data/History.txt +12 -0
- data/Manifest.txt +39 -0
- data/README.txt +55 -0
- data/Rakefile +75 -0
- data/bin/jjs +173 -0
- data/lib/jejune.rb +78 -0
- data/lib/jejune/boot.rb +107 -0
- data/lib/jejune/constants.rb +105 -0
- data/lib/jejune/data-extension.rb +144 -0
- data/lib/jejune/dependency-scanner.rb +69 -0
- data/lib/jejune/ejjs.rb +178 -0
- data/lib/jejune/errors.rb +53 -0
- data/lib/jejune/grammar.rb +32 -0
- data/lib/jejune/grammar/JavaScript.g +668 -0
- data/lib/jejune/grammar/Jejune.g +1029 -0
- data/lib/jejune/grammar/Jejune.tokens +241 -0
- data/lib/jejune/grammar/lexer.rb +6504 -0
- data/lib/jejune/grammar/parser.rb +17378 -0
- data/lib/jejune/grammar/rakefile +29 -0
- data/lib/jejune/grammar/tree.rb +6737 -0
- data/lib/jejune/input.rb +124 -0
- data/lib/jejune/jstring.rb +163 -0
- data/lib/jejune/lo-fi-lexer.rb +633 -0
- data/lib/jejune/macro.rb +78 -0
- data/lib/jejune/main.rb +289 -0
- data/lib/jejune/manager.rb +333 -0
- data/lib/jejune/node-test.rb +71 -0
- data/lib/jejune/parameters.rb +83 -0
- data/lib/jejune/rewrite-debug.rb +61 -0
- data/lib/jejune/rewrite.rb +125 -0
- data/lib/jejune/scanner.rb +201 -0
- data/lib/jejune/translator.rb +710 -0
- data/lib/jejune/tree-walker.rb +81 -0
- data/lib/jejune/utils.rb +81 -0
- data/lib/jejune/version.rb +38 -0
- data/spec/samples.txt +51 -0
- data/spec/translation.rb +69 -0
- data/spec/utils.rb +63 -0
- data/tools/env.fish +2 -0
- metadata +147 -0
data/lib/jejune/boot.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
#--
|
4
|
+
# Copyright (c) 2010-2011 Kyle C. Yetter
|
5
|
+
#
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
# a copy of this software and associated documentation files (the
|
8
|
+
# "Software"), to deal in the Software without restriction, including
|
9
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
# the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be
|
15
|
+
# included in all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
24
|
+
#++
|
25
|
+
|
26
|
+
|
27
|
+
module Jejune::Data
|
28
|
+
define_string DOC, :outdent
|
29
|
+
define_string DDOC, :interpolate, :outdent
|
30
|
+
define_string STRING
|
31
|
+
define_string DSTRING, :interpolate
|
32
|
+
|
33
|
+
define_string( 'Q', :interpolate, :outdent )
|
34
|
+
|
35
|
+
define( 'q', :outdent => true ) do | manager, blob |
|
36
|
+
manager.jstring( blob.data, true )
|
37
|
+
end
|
38
|
+
|
39
|
+
define( 'w' ) do | manager, blob |
|
40
|
+
manager.split_words( blob.data ).to_json
|
41
|
+
end
|
42
|
+
|
43
|
+
define( 'embed' ) do | manager, blob |
|
44
|
+
lib = manager.string_value( blob.data )
|
45
|
+
path = manager.find_resource( lib, '', blob.delimiter == '<' ) ||
|
46
|
+
manager.find_library( lib, blob.delimiter == '<' )
|
47
|
+
|
48
|
+
manager.add_dependency( path )
|
49
|
+
content = path =~ /\.jjs$/i ? manager.load_file( path ).translate : File.read( path )
|
50
|
+
content.to_json
|
51
|
+
end
|
52
|
+
|
53
|
+
define( 'r' ) do | manager, blob |
|
54
|
+
data = blob.data
|
55
|
+
data.gsub!( /(\\*)(\s)/ ) { $1.length.even? ? $1 : $1[ 1, $1.length ] << $2 }
|
56
|
+
data.gsub!( %r(/), '\/' )
|
57
|
+
'/' << data << '/' << blob.flags
|
58
|
+
end
|
59
|
+
|
60
|
+
define( 'sass', :outdent => true ) do | manager, blob |
|
61
|
+
defined?( gem ) or require 'rubygems'
|
62
|
+
begin
|
63
|
+
gem 'sass'
|
64
|
+
rescue LoadError
|
65
|
+
end
|
66
|
+
require 'sass'
|
67
|
+
|
68
|
+
sass_source = manager.string_value( blob.data )
|
69
|
+
css = Sass.compile( sass_source )
|
70
|
+
blob.flags.include?( 'c' ) and css = manager.compress( css, 'css' )
|
71
|
+
manager.jstring( css )
|
72
|
+
end
|
73
|
+
|
74
|
+
define( 'loadSass' ) do | manager, blob |
|
75
|
+
lib = manager.string_value( blob.data )
|
76
|
+
path = manager.find_resource( lib, 'sass|scss|css|', blob.delimiter == '<' )
|
77
|
+
source = File.read( path )
|
78
|
+
manager.add_dependency( path )
|
79
|
+
|
80
|
+
defined?( gem ) or require 'rubygems'
|
81
|
+
begin
|
82
|
+
gem 'sass'
|
83
|
+
rescue LoadError
|
84
|
+
end
|
85
|
+
require 'sass'
|
86
|
+
|
87
|
+
css = Sass.compile( source )
|
88
|
+
blob.flags.include?( 'c' ) and css = manager.compress( css, 'css' )
|
89
|
+
manager.jstring( css )
|
90
|
+
end
|
91
|
+
|
92
|
+
define( 'y', :outdent => true ) do | manager, blob |
|
93
|
+
::JSON.dump( ::YAML.load( blob.data ) )
|
94
|
+
end
|
95
|
+
|
96
|
+
define( 'loadYAML' ) do | manager, blob |
|
97
|
+
lib = manager.string_value( blob.data )
|
98
|
+
path = manager.find_resource( lib, 'yaml|yml|', blob.delimiter == '<' )
|
99
|
+
manager.add_dependency( path )
|
100
|
+
YAML.load_file( path ).to_json
|
101
|
+
end
|
102
|
+
|
103
|
+
define( 't', :outdent => true ) do | manager, blob |
|
104
|
+
require 'jejune/ejjs'
|
105
|
+
Jejune::EJJS.new( blob.data, :line => blob.line, :file => blob.file ).as_eval
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
#--
|
4
|
+
# Copyright (c) 2010-2011 Kyle C. Yetter
|
5
|
+
#
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
# a copy of this software and associated documentation files (the
|
8
|
+
# "Software"), to deal in the Software without restriction, including
|
9
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
# the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be
|
15
|
+
# included in all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
24
|
+
#++
|
25
|
+
|
26
|
+
module Jejune
|
27
|
+
module Constants
|
28
|
+
include TokenData
|
29
|
+
|
30
|
+
# pseudo-type used by the interpolation parser
|
31
|
+
CODE = PROGRAM
|
32
|
+
|
33
|
+
PROPERTY_DEFINITION_TYPES = Set[ GET, SET, COLON, ARROW ]
|
34
|
+
EXPRESSION_TYPES = Set[
|
35
|
+
AMP_ASGN, AND, AREF, ARRAY, ASGN, CALL, COLON, COMMA, DECR,
|
36
|
+
DELETE, DOT, EQ, EQQ, FALSE, GEQ, GREATER, HAT, HAT_ASGN,
|
37
|
+
ID, IN, INCR, INSTANCEOF, ITER, IVAR, LEQ, LESS, LSHIFT,
|
38
|
+
LSHIFT_ASGN, MINUS, MINUS_ASGN, MOD, MOD_ASGN, NEQ, NEQQ,
|
39
|
+
NEW, NOT, NULL, NUMBER, OBJECT, OR, PIPE, PIPE_ASGN, PLUS,
|
40
|
+
PLUS_ASGN, POST_DECR, POST_INCR, QMARK, REGEX, RSHIFT,
|
41
|
+
RSHIFT3, RSHIFT3_ASGN, RSHIFT_ASGN, SLASH, SLASH_ASGN, STAR,
|
42
|
+
STAR_ASGN, STRING, THIS, TILDE, TRUE, TYPEOF, UNDEFINED,
|
43
|
+
VOID, FUNCTION, ARROW, DSTRING, DDOC, DGENERAL, UPLUS, UMINUS
|
44
|
+
]
|
45
|
+
PROPERTY_TYPES = Set[ STRING, ID, NUMBER ]
|
46
|
+
|
47
|
+
CATCH_TYPES = Set[ CATCH, FINALLY ]
|
48
|
+
FUNCTION_TYPES = Set[ ARROW, FUNCTION ]
|
49
|
+
|
50
|
+
OPERATOR_PRECEDENCE = Hash.new do | h, k |
|
51
|
+
name = TOKEN_NAMES[ k ] and
|
52
|
+
warn( "Jejune Token Type `#{ name }[#{ k }]' is not Jejune::Constants::OPERATOR_PRECEDENCE" )
|
53
|
+
h[ k ] = -1
|
54
|
+
end
|
55
|
+
|
56
|
+
op_list = <<-END
|
57
|
+
COMMA
|
58
|
+
ASGN AMP_ASGN HAT_ASGN LSHIFT_ASGN MINUS_ASGN MOD_ASGN OR_ASGN PIPE_ASGN PLUS_ASGN RSHIFT3_ASGN RSHIFT_ASGN SLASH_ASGN STAR_ASGN
|
59
|
+
QMARK
|
60
|
+
OR
|
61
|
+
AND
|
62
|
+
PIPE
|
63
|
+
HAT
|
64
|
+
AMP
|
65
|
+
EQ EQQ NEQ NEQQ
|
66
|
+
LESS GREATER LEQ GEQ INSTANCEOF IN
|
67
|
+
LSHIFT RSHIFT RSHIFT3
|
68
|
+
PLUS MINUS
|
69
|
+
STAR SLASH MOD
|
70
|
+
DELETE VOID TYPEOF INCR DECR UPLUS UMINUS TILDE NOT
|
71
|
+
POST_INCR POST_DECR
|
72
|
+
NEW CALL AREF DOT ITER
|
73
|
+
THIS IVAR ID NULL TRUE FALSE UNDEFINED STRING DOC REGEX ARRAY OBJECT FUNCTION
|
74
|
+
END
|
75
|
+
|
76
|
+
# use the list above to give each expression node
|
77
|
+
# a precedence number for use in situations such
|
78
|
+
# as determining whether code needs to be surrounded
|
79
|
+
# in parentheses to protect operator precedence in an expression
|
80
|
+
op_list.strip.split( $/ ).each_with_index do | line, index |
|
81
|
+
for name in line.strip.split( /\s+/ )
|
82
|
+
value = TokenData.const_get( name )
|
83
|
+
OPERATOR_PRECEDENCE[ value ] = index
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
DELIMS =
|
88
|
+
{
|
89
|
+
"@"=>"@", "+"=>"+", "`"=>"`", ","=>",", "!"=>"!", "-"=>"-",
|
90
|
+
"\""=>"\"", "."=>".", "#"=>"#", ":"=>":", "/"=>"/",
|
91
|
+
"$"=>"$", ";"=>";", "%"=>"%", "{"=>"}", "["=>"]", "|"=>"|",
|
92
|
+
"\\"=>"\\", "&"=>"&", "<"=>">", "="=>"=", "'"=>"'",
|
93
|
+
"~"=>"~", "^"=>"^", "("=>")", "?"=>"?", "*"=>"*"
|
94
|
+
}
|
95
|
+
OPEN_DELIM = /([#{ Regexp.escape( DELIMS.keys.join( '' ) ) }])/
|
96
|
+
|
97
|
+
DEFAULT_EXT = "jjs|js|"
|
98
|
+
BROWSERS = %w( opera firefox chrome )
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
module Jejune
|
103
|
+
include ANTLR3
|
104
|
+
RewriteStream = TokenRewriteStream
|
105
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
#--
|
5
|
+
# Copyright (c) 2010-2011 Kyle C. Yetter
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
# a copy of this software and associated documentation files (the
|
9
|
+
# "Software"), to deal in the Software without restriction, including
|
10
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
# the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be
|
16
|
+
# included in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
#++
|
26
|
+
|
27
|
+
module Jejune
|
28
|
+
module Data
|
29
|
+
class Handler
|
30
|
+
include Constants
|
31
|
+
include Utils
|
32
|
+
|
33
|
+
attr_reader :type, :processor
|
34
|
+
|
35
|
+
def initialize( type, options = {}, &processor )
|
36
|
+
@type = type
|
37
|
+
@string = @outdent = @interpolate = false
|
38
|
+
@processor = nil
|
39
|
+
configure( options, &processor )
|
40
|
+
end
|
41
|
+
|
42
|
+
def configure( options, &processor )
|
43
|
+
@processor = processor || options.fetch( :processor, @processor )
|
44
|
+
@interpolate = options.fetch( :interpolate, @interpolate )
|
45
|
+
@outdent = options.fetch( :outdent, @outdent )
|
46
|
+
@string = options.fetch( :string, @string )
|
47
|
+
end
|
48
|
+
|
49
|
+
def process( manager, blob )
|
50
|
+
blob.data = prepare( manager, blob )
|
51
|
+
@processor and blob.data = @processor.call( manager, blob )
|
52
|
+
return( blob.data )
|
53
|
+
end
|
54
|
+
|
55
|
+
def prepare( manager, blob )
|
56
|
+
@interpolate and return manager.interpolate( blob, @outdent )
|
57
|
+
source = blob.data
|
58
|
+
@outdent and source = manager.outdent( source )
|
59
|
+
@string ? blob.delimiter == "'" ? manager.jstring( source, "'" ) : manager.jstring( source ) : source
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
unless defined?( Blob )
|
65
|
+
Blob = Struct.new(
|
66
|
+
:type, :data, :delimiter, :prefix,
|
67
|
+
:flags, :file, :line, :column
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
class Blob
|
72
|
+
include Constants
|
73
|
+
|
74
|
+
def self.extract( token, type = nil )
|
75
|
+
source = token.text
|
76
|
+
if source =~ /\A%?\s*(\w*\s*)/
|
77
|
+
prefix, source = $&, $'
|
78
|
+
signifier = $1.to_s.strip
|
79
|
+
type ||= signifier.empty? ? 'Q' : signifier
|
80
|
+
else
|
81
|
+
prefix = ''
|
82
|
+
end
|
83
|
+
source =~ OPEN_DELIM or raise( "%p doesn't have a delimiter" % source )
|
84
|
+
delimiter, source, closer = $&, $', DELIMS[ $1 ]
|
85
|
+
|
86
|
+
data, d, flags = source.rpartition( closer )
|
87
|
+
new(
|
88
|
+
type || delimiter, data,
|
89
|
+
delimiter, prefix, flags,
|
90
|
+
token.source_name, token.line, token.column
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
def inspect
|
95
|
+
'' << prefix << delimiter << data << DELIMS[ delimiter ] << flags
|
96
|
+
end
|
97
|
+
|
98
|
+
def data_column
|
99
|
+
column + prefix.length + delimiter.length
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
module Jejune
|
108
|
+
module Data
|
109
|
+
include Constants
|
110
|
+
defined?( @@global_handlers ) or @@global_handlers = {}
|
111
|
+
|
112
|
+
def self.define( type, options = {}, &action )
|
113
|
+
@@global_handlers[ type ] = Handler.new( type, options, &action )
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.define_string( type, *opts )
|
117
|
+
options = { :string => true }
|
118
|
+
for opt in opts
|
119
|
+
options[ opt ] = true
|
120
|
+
end
|
121
|
+
define( type, options )
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
# lazy attribute reader for
|
126
|
+
def data_handlers
|
127
|
+
@data_handlers ||= {}
|
128
|
+
end
|
129
|
+
|
130
|
+
def define_handler( type, options = {}, &action )
|
131
|
+
@data_handler[ type ] = Handler.new( type, options, &action )
|
132
|
+
end
|
133
|
+
|
134
|
+
def handler_for( blob )
|
135
|
+
type = blob.type
|
136
|
+
data_handlers.fetch( type ) do
|
137
|
+
@@global_handlers.fetch( type ) do
|
138
|
+
raise( "no data handler defined for `#{ type }'" )
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
#
|
4
|
+
# author: Kyle Yetter
|
5
|
+
#
|
6
|
+
|
7
|
+
module Jejune
|
8
|
+
class DependencyScanner
|
9
|
+
include Constants
|
10
|
+
include Utils
|
11
|
+
|
12
|
+
attr_accessor :manager, :dependency_map, :file_stack, :file
|
13
|
+
|
14
|
+
def initialize( manager )
|
15
|
+
@manager = manager
|
16
|
+
@dependency_map = {}
|
17
|
+
@file_stack = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def dependencies
|
21
|
+
out = {}
|
22
|
+
for path, set in @dependency_map
|
23
|
+
out[ path ] = set.to_a.sort
|
24
|
+
end
|
25
|
+
return out
|
26
|
+
end
|
27
|
+
|
28
|
+
def scan( file )
|
29
|
+
file = File.expand_path( file )
|
30
|
+
deps = @dependency_map[ file ] and return( deps )
|
31
|
+
|
32
|
+
deps = Set.new
|
33
|
+
dir = File.dirname( file )
|
34
|
+
|
35
|
+
open( file ) do | f |
|
36
|
+
f.grep( /^\s*%%\s*(include|require)\s*(\S.*)/ ) do
|
37
|
+
type, target = $1, $2.strip
|
38
|
+
case type
|
39
|
+
when 'require'
|
40
|
+
if dep = find_in_path_list( @manager.load_path, target, DEFAULT_EXT )
|
41
|
+
deps.add File.expand_path( dep )
|
42
|
+
end
|
43
|
+
when 'include'
|
44
|
+
if dep = find_in_directory( dir, target, DEFAULT_EXT )
|
45
|
+
deps.add File.expand_path( dep )
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
@dependency_map[ file ] = deps
|
52
|
+
@file_stack.each do | f |
|
53
|
+
@dependency_map[ file ].merge( deps )
|
54
|
+
end
|
55
|
+
|
56
|
+
begin
|
57
|
+
@file_stack.push( file )
|
58
|
+
deps.grep( /\.jjs$/i ) do | child |
|
59
|
+
scan( child )
|
60
|
+
end
|
61
|
+
ensure
|
62
|
+
@file_stack.pop
|
63
|
+
end
|
64
|
+
|
65
|
+
return deps
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
data/lib/jejune/ejjs.rb
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
#--
|
4
|
+
# Copyright (c) 2010-2011 Kyle C. Yetter
|
5
|
+
#
|
6
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
# a copy of this software and associated documentation files (the
|
8
|
+
# "Software"), to deal in the Software without restriction, including
|
9
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
# the following conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be
|
15
|
+
# included in all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
24
|
+
#++
|
25
|
+
|
26
|
+
#require 'jejune/lo-fi-lexer'
|
27
|
+
|
28
|
+
module Jejune
|
29
|
+
class EJJS
|
30
|
+
|
31
|
+
#class Lexer < LoFiLexer::StatefulLexer
|
32
|
+
# state :line_start, :go_to => :template do
|
33
|
+
# rule( :indentation, /[^\n\r]+?(?=%\|)/ )
|
34
|
+
# rule( :comment_line, /(?:[^\n\r%\\]|\\[^\n\r])*%#([\n\r]*)\r?\n/, :text => 1 )
|
35
|
+
# delimited( :insert_indented, /%\|=?/, /\r?\n|\z/, :text => :body )
|
36
|
+
# delimited( :control_line, '%', /\r?\n|\z/, :text => :body )
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# state :template do
|
40
|
+
# rule( :newline, /\r?\n/, :go_to => :line_start )
|
41
|
+
# delimited( :comment, '<%#', '%>', :text => :body )
|
42
|
+
# delimited( :insert, '<%=', '%>', :text => :body )
|
43
|
+
# delimited( :control, '<%', '%>', :text => :body )
|
44
|
+
# rule( :literal, /(?:[^\r\n\\<]|\\[^\r\n]|<(?!%)|\\)+(?=<%|\r?\n|\z)/ )
|
45
|
+
# end
|
46
|
+
#end # class Lexer
|
47
|
+
|
48
|
+
module Compile
|
49
|
+
module_function
|
50
|
+
|
51
|
+
def scan( source, options = {} )
|
52
|
+
block_given? or return( enum_for( :scan, source, options ) )
|
53
|
+
|
54
|
+
s = SourceScanner.new( source, options )
|
55
|
+
line_start = true
|
56
|
+
until s.eos?
|
57
|
+
if line_start
|
58
|
+
case
|
59
|
+
when s.scan( %r< % \# ((?: [^\\\r\n]+ | \\ \r? \n | \\ . )*) (?: \r? \n | \z ) >x )
|
60
|
+
yield( :comment_line, s[ 1 ] )
|
61
|
+
when s.scan( %r< % ((?: [^\\\r\n]+ | \\ \r? \n | \\ . )*) (?: \r? \n | \z ) >x )
|
62
|
+
yield( :control_line, s[ 1 ] )
|
63
|
+
else
|
64
|
+
line_start = false
|
65
|
+
end
|
66
|
+
else
|
67
|
+
case
|
68
|
+
when s.scan( /\r?\n/ )
|
69
|
+
line_start = true
|
70
|
+
yield( :newline, s.matched )
|
71
|
+
when m = s.nested!( '<%#', '%>' )
|
72
|
+
content = m[ 3, m.length - 5 ].to_s
|
73
|
+
yield( :comment, content )
|
74
|
+
when m = s.nested!( '<%=', '%>' )
|
75
|
+
content = m[ 3, m.length - 5 ].to_s
|
76
|
+
yield( :insert, content )
|
77
|
+
when m = s.nested!( '<%', '%>' )
|
78
|
+
content = m[ 2, m.length - 4 ].to_s
|
79
|
+
yield( :control, content )
|
80
|
+
when s.scan( /(?:[^\r\n\\<]|\\[^\r\n]|<(?!%)|\\)+(?=<%|\r?\n|\z)/ )
|
81
|
+
text = s.matched
|
82
|
+
yield( :literal, text )
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def compile( source, options = {} )
|
91
|
+
#lexer = Lexer.new( source.to_s, options )
|
92
|
+
var = options.fetch( :variable, '__data__' )
|
93
|
+
lines = []
|
94
|
+
line = []
|
95
|
+
indentation = ''
|
96
|
+
previous = nil
|
97
|
+
clear_line = false
|
98
|
+
|
99
|
+
scan( source, options ) do | type, text |
|
100
|
+
case type
|
101
|
+
when :literal
|
102
|
+
line << %(#{ var }.push( #{ str( text ) } ))
|
103
|
+
when :comment_line
|
104
|
+
line << "//" << text
|
105
|
+
clear_line = true
|
106
|
+
when :insert
|
107
|
+
line << %[#{ var }.push( #{ text } )]
|
108
|
+
when :newline
|
109
|
+
line << %[#{ var }.push( #{ str( text ) } )]
|
110
|
+
clear_line = true
|
111
|
+
when :control
|
112
|
+
line << text
|
113
|
+
when :control_line
|
114
|
+
line << text
|
115
|
+
clear_line = true
|
116
|
+
else
|
117
|
+
raise( "BUG: unknown token type `#{ type }' -- this shouldn't happen" )
|
118
|
+
end
|
119
|
+
|
120
|
+
if clear_line
|
121
|
+
lines << line.join( '; ' )
|
122
|
+
line = []
|
123
|
+
indentation = ''
|
124
|
+
clear_line = false
|
125
|
+
end
|
126
|
+
#previous = token.type
|
127
|
+
end
|
128
|
+
|
129
|
+
lines << line.join( '; ' )
|
130
|
+
return lines.join( "\n" )
|
131
|
+
end
|
132
|
+
|
133
|
+
def str( text )
|
134
|
+
JString.jstring( text )
|
135
|
+
end
|
136
|
+
|
137
|
+
end # module Compile
|
138
|
+
|
139
|
+
include Compile
|
140
|
+
|
141
|
+
attr_accessor :file, :line, :source, :parameters, :variable, :name, :js_body
|
142
|
+
|
143
|
+
def initialize( source, options = nil )
|
144
|
+
@source = source
|
145
|
+
if options
|
146
|
+
@variable = options.fetch( :variable, '__text__' )
|
147
|
+
@file = options.fetch( :file, '(erb)' )
|
148
|
+
@line = options.fetch( :line, 1 )
|
149
|
+
@name = options[ :name ]
|
150
|
+
@parameters = options[ :parameters ]
|
151
|
+
else
|
152
|
+
@variable = '__text__'
|
153
|
+
@file = '(ejs)'
|
154
|
+
@line = 1
|
155
|
+
@name = nil
|
156
|
+
@parameters = nil
|
157
|
+
end
|
158
|
+
jjs_output = compile( source, :variable => @variable, :file => @file, :line => @line )
|
159
|
+
@js_body = Jejune.translate( jjs_output, :file => @file, :line => @line )
|
160
|
+
end
|
161
|
+
|
162
|
+
def as_eval
|
163
|
+
"(#{ as_function })()"
|
164
|
+
end
|
165
|
+
|
166
|
+
def as_function
|
167
|
+
code = "function"
|
168
|
+
@name and code << " " << @name
|
169
|
+
code << ( @parameters ? @parameters.declaration : '()' )
|
170
|
+
code << " { var #{ @variable } = []; "
|
171
|
+
@parameters and code << @parameters.parsing_source
|
172
|
+
code << @js_body << "; "
|
173
|
+
code << "return #{ @variable }.join( '' ) }"
|
174
|
+
return code
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
end
|