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.
@@ -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