jejune 1.1.0
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.
- 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
|