jejune 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,71 @@
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
28
+ module NodeTest
29
+ include Constants
30
+
31
+ private
32
+
33
+ def property_definition?( node )
34
+ if PROPERTY_DEFINITION_TYPES.include?( node.type )
35
+ if node.type == ARROW and not PROPERTY_TYPES.include?( node.first.type )
36
+ return false
37
+ end
38
+ return true
39
+ end
40
+ return false
41
+ end
42
+
43
+ def need_parentheses?( type_in_question, base_type )
44
+ OPERATOR_PRECEDENCE[ type_in_question ] < OPERATOR_PRECEDENCE[ base_type ]
45
+ end
46
+
47
+ def property_name?( node )
48
+ PROPERTY_TYPES.include?( node.type )
49
+ end
50
+
51
+ def expression?( node )
52
+ EXPRESSION_TYPES.include?( node.type )
53
+ end
54
+
55
+ def enclosed?( node )
56
+ if node.start.type == LPAREN and node.stop.type == RPAREN
57
+ depth = 1
58
+ for token in node.tokens[ 1...-1 ]
59
+ case token.type
60
+ when LPAREN then depth += 1
61
+ when RPAREN
62
+ depth == 1 and return( false )
63
+ depth -= 1
64
+ end
65
+ end
66
+ return true
67
+ end
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/ruby
2
+ # encoding: utf-8
3
+ #
4
+ # author: Kyle Yetter
5
+ #
6
+
7
+
8
+ module Jejune
9
+
10
+ ParameterSet = Struct.new( :parameters, :defaults, :indices, :splat )
11
+
12
+ class ParameterSet
13
+ include Constants
14
+
15
+ def self.study( params )
16
+ set = self.new
17
+
18
+ # Check that the parameters node actually meets the current
19
+ # semantic requirements of req > def > splat
20
+ # Also, organize the actual parameters by type
21
+ last_type = ID
22
+ for node in params
23
+ if last_type == SPLAT
24
+ raise( "additional parameters may not follow a splat parameter" )
25
+ end
26
+
27
+ case type = node.type
28
+ when ID
29
+ last_type == ASGN and
30
+ raise( "required parameters may not follow default parameters" )
31
+ set.add( node.source )
32
+ when ASGN
33
+ set.add( node.first.source, node.last.source )
34
+ when SPLAT
35
+ set.splat = node.first.source
36
+ else
37
+ raise( "Why is `#{ node.name }' in a parameters list? That shouldn't happen" )
38
+ end
39
+
40
+ last_type = type
41
+ end
42
+
43
+ return set
44
+ end
45
+
46
+ def initialize( parameters = [], defaults = {}, indices = {}, splat = nil )
47
+ super
48
+ end
49
+
50
+ def declaration
51
+ parameters.empty? ? '()' : '( ' << parameters.join( ', ' ) << ' )'
52
+ end
53
+
54
+ def add( name, value = nil )
55
+ indices[ name ] = parameters.length
56
+ parameters << name
57
+ value and defaults[ name ] = value
58
+ self
59
+ end
60
+
61
+ def include?( name )
62
+ indices[ name ]
63
+ end
64
+
65
+ def parsing_source
66
+ arity = parameters.length
67
+ source = StringIO.new( '' )
68
+ if splat
69
+ source.puts( "var #{ splat } = Array.prototype.slice.call( arguments, #{ arity } );" )
70
+ end
71
+ unless defaults.empty?
72
+ source.puts( "switch ( arguments.length ) {" )
73
+ parameters.each_with_index do | name, i |
74
+ source.puts( " case #{ i }:" )
75
+ value = defaults[ name ] and source.puts( " #{ name } = #{ value };" )
76
+ end
77
+ source.puts( "}" )
78
+ end
79
+ return source.string
80
+ end
81
+ end
82
+
83
+ end
@@ -0,0 +1,61 @@
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 DebugRewrite
28
+ def debug_rewrite( tokens )
29
+ rewrite = tokens.rewrite
30
+
31
+ operations = rewrite.instance_variable_get( :@operations )
32
+ for op in operations
33
+ print_rewrite( op )
34
+ end
35
+ end
36
+
37
+ def print_rewrite( operation )
38
+ tokens = operation.stream
39
+
40
+ case operation.name
41
+ when 'insert-before'
42
+ print(
43
+ "\e[33minsert-before\e[0m : ",
44
+ operation.stream[ operation.location ].inspect,
45
+ ' -> ', operation.text.inspect
46
+ )
47
+ when 'replace'
48
+ range = operation.location
49
+ start, stop = range.begin, range.end
50
+ content = '%p %s %p' % [ tokens[ start ], range.exclude_end? ? '...' : '..', tokens[ stop ] ]
51
+ print( "\e[32mreplace\e[0m : ", content, ' -> ', operation.text.inspect )
52
+ when 'delete'
53
+ range = operation.location
54
+ start, stop = range.begin, range.end
55
+ content = '%p %s %p' % [ tokens[ start ], range.exclude_end? ? '...' : '..', tokens[ stop ] ]
56
+ print( "\e[34mdelete\e[0m : ", content )
57
+ end
58
+ puts
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,125 @@
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
+ class RewriteTree < CommonTree
28
+ attr_accessor :token_stream
29
+
30
+ def initialize( payload = nil, stream = nil )
31
+ super( payload )
32
+ @token_stream = stream
33
+ end
34
+
35
+ def tokens( range = token_range )
36
+ @token_stream[ range ]
37
+ end
38
+
39
+ def rewrite( *args, &block )
40
+ @token_stream.rewrite( *args, &block )
41
+ end
42
+
43
+ def start
44
+ start_index > 0 and @token_stream.at( start_index )
45
+ end
46
+
47
+ def stop
48
+ stop_index > 0 and @token_stream.at( stop_index )
49
+ end
50
+
51
+ def source( range = token_range )
52
+ @token_stream.rewrite.execute( range )
53
+ end
54
+
55
+ def inner_range
56
+ first.start_index .. last.stop_index
57
+ end
58
+
59
+ def inner_source
60
+ @token_stream.rewrite.execute( inner_range )
61
+ end
62
+
63
+ def insert_after!( text, *more )
64
+ if more.empty?
65
+ @token_stream.insert_after( stop_index, text )
66
+ else
67
+ @token_stream.insert_after( text, *more )
68
+ end
69
+ end
70
+
71
+ def insert_before!( text, *more )
72
+ if more.empty?
73
+ @token_stream.insert_before( start_index, text )
74
+ else
75
+ @token_stream.insert_before( text, *more )
76
+ end
77
+ end
78
+
79
+ def delete!( *more )
80
+ if more.empty?
81
+ @token_stream.delete( token_range )
82
+ else
83
+ @token_stream.delete( *more )
84
+ end
85
+ end
86
+
87
+ def replace!( text, *more )
88
+ if more.empty?
89
+ @token_stream.replace( token_range, text )
90
+ else
91
+ @token_stream.replace( text, *more )
92
+ end
93
+ end
94
+
95
+ def strip!
96
+ @token_stream.delete( start_index )
97
+ @token_stream.delete( stop_index )
98
+ end
99
+
100
+ def surround!( before, after = before )
101
+ @token_stream.insert_before( start_index, before )
102
+ @token_stream.insert_after( stop_index, after )
103
+ end
104
+
105
+ alias prepend! insert_before!
106
+ alias append! insert_after!
107
+
108
+ def source_name
109
+ token.source_name rescue nil
110
+ end
111
+ end
112
+
113
+ class RewriteAdaptor < CommonTreeAdaptor
114
+ def initialize( stream )
115
+ @token_stream = stream
116
+ super( TokenData::Token )
117
+ end
118
+
119
+ def create_with_payload( payload )
120
+ return RewriteTree.new( payload, @token_stream )
121
+ end
122
+ end
123
+ end
124
+
125
+
@@ -0,0 +1,201 @@
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 'strscan'
27
+ require 'delegate'
28
+
29
+ module Jejune
30
+ class SourceScanner < DelegateClass( StringScanner )
31
+ ESCAPE = /\\./m
32
+ SKIPPABLE = %r(
33
+ (?: // [^\n]* \n?
34
+ | /\* .*? \*/
35
+ | [ \ \t \f ]+
36
+ | \\ \r? \n
37
+ )+
38
+ )mx
39
+ SKIPPABLE_NL = %r(
40
+ (?: // [^\n]* \n?
41
+ | /\* .*? \*/
42
+ | \s+
43
+ )+
44
+ )mx
45
+ ID_RX = %r( [\$_a-z] [\$_a-z0-9]* )ix
46
+ STRING_RX = %r(
47
+ (?: " (?: [^ " \\ ] | \\ . )* "
48
+ | ' (?: [^ ' \\ ] | \\ . )* '
49
+ | ` (?: [^ ` \\ ] | \\ . )* `
50
+ )
51
+ )mx
52
+ CHUNK_RX = %r(
53
+ (?: [^ \s \\ ] | \\ . )+
54
+ )mx
55
+
56
+ attr_accessor :line, :column, :file
57
+ alias position pos
58
+ alias position= pos=
59
+
60
+ def self.from_token( token, options = {} )
61
+ options.is_a?( Integer ) and options = { :position => options }
62
+ source = token.text
63
+ options[ :file ] ||= token.source_name
64
+ options[ :line ] ||= token.line
65
+ options[ :column ] ||= token.column
66
+ new( source, options )
67
+ end
68
+
69
+ def initialize( source, options = {} )
70
+ @scanner = StringScanner.new( source )
71
+ super( @scanner )
72
+
73
+ @file = options.fetch( :file, nil )
74
+ @line = options.fetch( :line, 1 )
75
+ @column = options.fetch( :column, 0 )
76
+ @lexer = @stream = nil
77
+ if pos = options[ :position ] and pos > 0
78
+ @scanner.pos = pos
79
+ advance_over( source[ 0, pos ] )
80
+ end
81
+ end
82
+
83
+ for m in %w( scan scan_until skip skip_until )
84
+ class_eval( <<-END )
85
+ def #{ m }( pattern )
86
+ if match = super( pattern )
87
+ advance_over( match )
88
+ return match
89
+ end
90
+ end
91
+ END
92
+ end
93
+
94
+ def advance_over( str )
95
+ l = str.count( $/ )
96
+ if l.zero?
97
+ @column += str.length
98
+ else
99
+ @line += l
100
+ @column = str.length - str.rindex( $/ ) - 1
101
+ end
102
+ self
103
+ end
104
+
105
+ def stream
106
+ @stream ||= ANTLR3::StringStream.new(
107
+ @scanner.string,
108
+ :position => @scanner.pos,
109
+ :file => @file,
110
+ :line => @line,
111
+ :column => @column
112
+ )
113
+ end
114
+
115
+ def lexer
116
+ input = self.stream
117
+ input.position == @scanner.pos or input.seek( @scanner.pos )
118
+ lexer = Lexer.new( input )
119
+ block_given? or return( lexer )
120
+
121
+ value = yield( lexer )
122
+ @scanner.pos = @stream.position
123
+ @line = @stream.line
124
+ @column = @stream.column
125
+ return value
126
+ end
127
+
128
+ def jump_to( pattern )
129
+ if found = @scanner.check_until( pattern )
130
+ size = found.length - @scanner.matched.length
131
+ size > 0 or return ''
132
+ matched = found[ 0, size ]
133
+ @scanner.pos += size
134
+ advance_over( matched )
135
+ return matched
136
+ end
137
+ end
138
+
139
+ def see?( str )
140
+ peek( str.length ) == str
141
+ end
142
+
143
+ def space!( include_newlines = false )
144
+ scan( include_newlines ? SKIPPABLE_NL : SKIPPABLE )
145
+ end
146
+
147
+ def id!
148
+ scan( ID_RX )
149
+ end
150
+
151
+ @@delimited_rx = {}
152
+
153
+ def string!
154
+ scan( STRING_RX )
155
+ end
156
+
157
+ def chunk!
158
+ scan( CHUNK_RX )
159
+ end
160
+
161
+ def delimited!( open, close = open )
162
+ c = Regexp.escape( close[ 0, 1 ] )
163
+ pattern = /#{ cast_rx( open ) }(?m-ix:[^\\#{ c }]|\\.)*#{ cast_rx( close ) }/
164
+ scan( pattern )
165
+ end
166
+
167
+ def nested!( open, close )
168
+ start = @scanner.pos
169
+ @scanner.scan( cast_rx( open ) ) or return nil
170
+ depth = 1
171
+ pause = Regexp.union( open, close, /\\.|\z/m )
172
+ while @scanner.skip_until( pause )
173
+ case @scanner.matched
174
+ when open
175
+ depth += 1
176
+ when close
177
+ depth -= 1
178
+ depth.zero? and break
179
+ when /\\./m
180
+ # do nothing
181
+ else
182
+ @scanner.pos = start
183
+ return nil
184
+ end
185
+ end
186
+ text = @scanner.string[ start ... @scanner.pos ]
187
+ advance_over( text )
188
+ return text
189
+ end
190
+
191
+ private
192
+
193
+ def cast_rx( arg )
194
+ case arg
195
+ when Regexp then arg
196
+ else Regexp.new( Regexp.escape( arg ) )
197
+ end
198
+ end
199
+
200
+ end
201
+ end