spox-textpow 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,14 @@
1
+ == 0.10.1 / 2008-02-20
2
+ * FIX: Endless loop in method_missing when @proxy_variable is not present.
3
+
4
+ == 0.10.0 / 2007-06-15
5
+ * CHANGE: Now the scopeName for the entire syntax is passed to start_parsing and end_parsing. This means that programs using older version should modify their processors accordingly.
6
+
7
+ == 0.9.1 / 2007-06-14
8
+ * FIX: Score manager now handles multiple subtractions in scope definitions.
9
+
10
+ == 0.9.0 / 2007-03-19
11
+
12
+ * 1 major enhancement
13
+ * Birthday!
14
+
data/Manifest.txt ADDED
@@ -0,0 +1,13 @@
1
+ bin/plist2yaml
2
+ bin/plist2syntax
3
+ lib/textpow.rb
4
+ lib/textpow
5
+ lib/textpow/syntax.rb
6
+ lib/textpow/score_manager.rb
7
+ lib/textpow/debug_processor.rb
8
+ mm/manual.mm
9
+ test/test_textpow.rb
10
+ History.txt
11
+ Rakefile
12
+ Manifest.txt
13
+ README.txt
data/README.txt ADDED
@@ -0,0 +1,48 @@
1
+ == Textpow
2
+
3
+ A library for parsing TextMate[http://macromates.com/] bundles.
4
+
5
+ == SYNTAX
6
+
7
+
8
+ == REQUIREMENTS:
9
+
10
+ * Oniguruma for Ruby[http://oniguruma.rubyforge.org] v1.1.0 or higher.
11
+
12
+ == INSTALL:
13
+
14
+ sudo gem install -r mama
15
+
16
+ == BUGS/PROBLEMS/INCOMPATIBILITIES:
17
+
18
+
19
+ == TODO:
20
+
21
+
22
+ == CREDITS:
23
+
24
+
25
+ == LICENSE:
26
+
27
+ (The MIT License)
28
+
29
+ Copyright (c) 2007 FIX
30
+
31
+ Permission is hereby granted, free of charge, to any person obtaining
32
+ a copy of this software and associated documentation files (the
33
+ 'Software'), to deal in the Software without restriction, including
34
+ without limitation the rights to use, copy, modify, merge, publish,
35
+ distribute, sublicense, and/or sell copies of the Software, and to
36
+ permit persons to whom the Software is furnished to do so, subject to
37
+ the following conditions:
38
+
39
+ The above copyright notice and this permission notice shall be
40
+ included in all copies or substantial portions of the Software.
41
+
42
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
43
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
44
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
45
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
46
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
47
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
48
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/bin/plist2syntax ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'plist'
5
+ require 'yaml'
6
+
7
+ result = Plist::parse_xml( ARGV[0] )
8
+ standard_name = File.basename( ARGV[0] ).downcase.gsub(/\s+/, '_').gsub(/\.(plist|tm[Ll]anguage)/, '').gsub(/\(|\)|:/, '').gsub(/_+/, '_')
9
+ puts standard_name
10
+
11
+ File.open( "#{standard_name}.yaml", "w" ) {|f| YAML.dump( result, f ) }
data/bin/plist2yaml ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'plist'
5
+ require 'yaml'
6
+
7
+ result = Plist::parse_xml( ARGV[0] )
8
+ YAML.dump( result, STDOUT )
@@ -0,0 +1,36 @@
1
+ module Textpow
2
+ class DebugProcessor
3
+ def initialize
4
+ @line_number = 0
5
+ @printable_line = ""
6
+ end
7
+
8
+ def pprint line, string, position = 0
9
+ line.replace line.ljust( position + string.size, " ")
10
+ line[position,string.size] = string
11
+ line
12
+ end
13
+
14
+ def open_tag name, position
15
+ STDERR.puts pprint( "", "{#{name}", position + @line_marks.size)
16
+ end
17
+
18
+ def close_tag name, position
19
+ STDERR.puts pprint( "", "}#{name}", position + @line_marks.size)
20
+ end
21
+
22
+ def new_line line
23
+ @line_number += 1
24
+ @line_marks = "[#{@line_number.to_s.rjust( 4, '0' )}] "
25
+ STDERR.puts "#{@line_marks}#{line}"
26
+ end
27
+
28
+ def start_parsing name
29
+ STDERR.puts "{#{name}"
30
+ end
31
+
32
+ def end_parsing name
33
+ STDERR.puts "}#{name}"
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,65 @@
1
+ module Textpow
2
+ class ScoreManager
3
+ POINT_DEPTH = 4
4
+ NESTING_DEPTH = 40
5
+ START_VALUE = 2 ** ( POINT_DEPTH * NESTING_DEPTH )
6
+ BASE = 2 ** POINT_DEPTH
7
+
8
+ def initialize
9
+ @scores = {}
10
+ end
11
+
12
+ def score search_scope, reference_scope
13
+ max = 0
14
+ search_scope.split( ',' ).each do |scope|
15
+ arrays = scope.split(/\B-/)
16
+ if arrays.size == 1
17
+ max = [max, score_term( arrays[0], reference_scope )].max
18
+ elsif arrays.size > 1
19
+ excluded = false
20
+ arrays[1..-1].each do |a|
21
+ if score_term( arrays[1], reference_scope ) > 0
22
+ excluded = true
23
+ break
24
+ end
25
+ end
26
+ max = [max, score_term( arrays[0], reference_scope )].max unless excluded
27
+ else
28
+ raise ParsingError, "Error in scope string: '#{search_scope}' #{arrays.size} is not a valid number of operands" if arrays.size < 1
29
+ end
30
+ end
31
+ max
32
+ end
33
+
34
+ private
35
+
36
+ def score_term search_scope, reference_scope
37
+ unless @scores[reference_scope] && @scores[reference_scope][search_scope]
38
+ @scores[reference_scope] ||= {}
39
+ @scores[reference_scope][search_scope] = score_array( search_scope.split(' '), reference_scope.split( ' ' ) )
40
+ end
41
+ @scores[reference_scope][search_scope]
42
+ end
43
+
44
+ def score_array search_array, reference_array
45
+ pending = search_array
46
+ current = reference_array.last
47
+ reg = Regexp.new( "^#{Regexp.escape( pending.last )}" )
48
+ multiplier = START_VALUE
49
+ result = 0
50
+ while pending.size > 0 && current
51
+ if reg =~ current
52
+ point_score = (2**POINT_DEPTH) - current.count( '.' ) + Regexp.last_match[0].count( '.' )
53
+ result += point_score * multiplier
54
+ pending.pop
55
+ reg = Regexp.new( "^#{Regexp.escape( pending.last )}" ) if pending.size > 0
56
+ end
57
+ multiplier = multiplier / BASE
58
+ reference_array.pop
59
+ current = reference_array.last
60
+ end
61
+ result = 0 if pending.size > 0
62
+ result
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,287 @@
1
+ require 'plist'
2
+
3
+ module Textpow
4
+
5
+ class SyntaxProxy
6
+ def initialize hash, syntax
7
+ @syntax = syntax
8
+ @proxy = hash["include"]
9
+ end
10
+
11
+ def method_missing method, *args, &block
12
+ if @proxy
13
+ @proxy_value = proxy unless @proxy_value
14
+ if @proxy_value
15
+ @proxy_value.send(method, *args, &block)
16
+ else
17
+ STDERR.puts "Failed proxying #{@proxy}.#{method}(#{args.join(', ')})"
18
+ end
19
+ end
20
+ end
21
+
22
+ def proxy
23
+ case @proxy
24
+ when /^#/
25
+ if @syntax.repository && @syntax.repository[@proxy[1..-1]]
26
+ #puts "Repository"
27
+ #@table["syntax"].repository.each_key{|k| puts k}
28
+ return @syntax.repository[@proxy[1..-1]]
29
+ end
30
+ when "$self"
31
+ return @syntax
32
+ when "$base"
33
+ return @syntax
34
+ else
35
+ return @syntax.syntaxes[@proxy]
36
+ end
37
+ end
38
+ end
39
+
40
+ class SyntaxNode
41
+ #OPTIONS = {:options => Oniguruma::OPTION_CAPTURE_GROUP}
42
+
43
+ @@syntaxes = {}
44
+
45
+ attr_accessor :syntax
46
+ attr_accessor :firstLineMatch
47
+ attr_accessor :foldingStartMarker
48
+ attr_accessor :foldingStopMarker
49
+ attr_accessor :match
50
+ attr_accessor :begin
51
+ attr_accessor :content
52
+ attr_accessor :fileTypes
53
+ attr_accessor :name
54
+ attr_accessor :contentName
55
+ attr_accessor :end
56
+ attr_accessor :scopeName
57
+ attr_accessor :keyEquivalent
58
+ attr_accessor :captures
59
+ attr_accessor :beginCaptures
60
+ attr_accessor :endCaptures
61
+ attr_accessor :repository
62
+ attr_accessor :patterns
63
+
64
+ def self.load filename, name_space = :default
65
+ table = nil
66
+ case filename
67
+ when /(\.tmSyntax|\.plist)$/
68
+ table = Plist::parse_xml( filename )
69
+ else
70
+ File.open( filename ) do |f|
71
+ table = YAML.load( f )
72
+ end
73
+ end
74
+ if table
75
+ SyntaxNode.new( table, nil, name_space )
76
+ else
77
+ nil
78
+ end
79
+ end
80
+
81
+ def initialize hash, syntax = nil, name_space = :default
82
+ @name_space = name_space
83
+ @@syntaxes[@name_space] ||= {}
84
+ @@syntaxes[@name_space][hash["scopeName"]] = self if hash["scopeName"]
85
+ @syntax = syntax || self
86
+ hash.each do |key, value|
87
+ case key
88
+ when "firstLineMatch", "foldingStartMarker", "foldingStopMarker", "match", "begin"
89
+ begin
90
+ value.force_encoding("ASCII-8BIT")
91
+ instance_variable_set( "@#{key}", Regexp.new( value ) )
92
+ rescue ArgumentError => e
93
+ raise ParsingError, "Parsing error in #{value}: #{e.to_s}"
94
+ end
95
+ when "content", "fileTypes", "name", "contentName", "end", "scopeName", "keyEquivalent"
96
+ instance_variable_set( "@#{key}", value )
97
+ when "captures", "beginCaptures", "endCaptures"
98
+ instance_variable_set( "@#{key}", value.sort )
99
+ when "repository"
100
+ parse_repository value
101
+ when "patterns"
102
+ create_children value
103
+ else
104
+ STDERR.puts "Ignoring: #{key} => #{value.gsub("\n", "\n>>")}" if $DEBUG
105
+ end
106
+ end
107
+ end
108
+
109
+
110
+ def syntaxes
111
+ @@syntaxes[@name_space]
112
+ end
113
+
114
+ def parse( string, processor = nil )
115
+ processor.start_parsing self.scopeName if processor
116
+ stack = [[self, nil]]
117
+ string.each_line do |line|
118
+ parse_line stack, line, processor
119
+ end
120
+ processor.end_parsing self.scopeName if processor
121
+ processor
122
+ end
123
+
124
+ protected
125
+
126
+ def parse_repository repository
127
+ @repository = {}
128
+ repository.each do |key, value|
129
+ if value["include"]
130
+ @repository[key] = SyntaxProxy.new( value, self.syntax )
131
+ else
132
+ @repository[key] = SyntaxNode.new( value, self.syntax, @name_space )
133
+ end
134
+ end
135
+ end
136
+
137
+ def create_children patterns
138
+ @patterns = []
139
+ patterns.each do |p|
140
+ if p["include"]
141
+ @patterns << SyntaxProxy.new( p, self.syntax )
142
+ else
143
+ @patterns << SyntaxNode.new( p, self.syntax, @name_space )
144
+ end
145
+ end
146
+ end
147
+
148
+ def parse_captures name, pattern, match, processor
149
+ captures = pattern.match_captures( name, match )
150
+ captures.reject! { |group, range, name| ! range.first || range.first == range.last }
151
+ starts = []
152
+ ends = []
153
+ captures.each do |group, range, name|
154
+ starts << [range.first, group, name]
155
+ ends << [range.last, -group, name]
156
+ end
157
+
158
+ # STDERR.puts '-' * 100
159
+ # starts.sort!.reverse!.each{|c| STDERR.puts c.join(', ')}
160
+ # STDERR.puts
161
+ # ends.sort!.reverse!.each{|c| STDERR.puts c.join(', ')}
162
+ starts.sort!.reverse!
163
+ ends.sort!.reverse!
164
+
165
+ while ! starts.empty? || ! ends.empty?
166
+ if starts.empty?
167
+ pos, key, name = ends.pop
168
+ processor.close_tag name, pos
169
+ elsif ends.empty?
170
+ pos, key, name = starts.pop
171
+ processor.open_tag name, pos
172
+ elsif ends.last[1].abs < starts.last[1]
173
+ pos, key, name = ends.pop
174
+ processor.close_tag name, pos
175
+ else
176
+ pos, key, name = starts.pop
177
+ processor.open_tag name, pos
178
+ end
179
+ end
180
+ end
181
+
182
+ def match_captures name, match
183
+ matches = []
184
+ captures = instance_variable_get "@#{name}"
185
+ if captures
186
+ captures.each do |key, value|
187
+ if key =~ /^\d*$/
188
+ matches << [key.to_i, match.offset( key.to_i ), value["name"]] if key.to_i < match.size
189
+ else
190
+ matches << [match.to_index( key.to_sym ), match.offset( key.to_sym), value["name"]] if match.to_index( key.to_sym )
191
+ end
192
+ end
193
+ end
194
+ matches
195
+ end
196
+
197
+ def match_first string, position
198
+ if self.match
199
+ if match = self.match.match( string, position )
200
+ return [self, match]
201
+ end
202
+ elsif self.begin
203
+ if match = self.begin.match( string, position )
204
+ return [self, match]
205
+ end
206
+ elsif self.end
207
+ else
208
+ return match_first_son( string, position )
209
+ end
210
+ nil
211
+ end
212
+
213
+ def match_end string, match, position
214
+ regstring = self.end.clone
215
+ regstring.gsub!( /\\([1-9])/ ) { |s| match[$1.to_i] }
216
+ regstring.gsub!( /\\g<(.*?)>/ ) { |s| match[$1.to_sym] }
217
+ Regexp.new( regstring ).match( string, position )
218
+ end
219
+
220
+ def match_first_son string, position
221
+ match = nil
222
+ if self.patterns
223
+ self.patterns.each do |p|
224
+ tmatch = p.match_first string, position
225
+ if tmatch
226
+ if ! match || match[1].offset(0).first > tmatch[1].offset(0).first
227
+ match = tmatch
228
+ end
229
+ #break if tmatch[1].offset.first == position
230
+ end
231
+ end
232
+ end
233
+ match
234
+ end
235
+
236
+ def parse_line stack, line, processor
237
+ processor.new_line line if processor
238
+ top, match = stack.last
239
+ position = 0
240
+ #@ln ||= 0
241
+ #@ln += 1
242
+ #STDERR.puts @ln
243
+ while true
244
+ if top.patterns
245
+ pattern, pattern_match = top.match_first_son line, position
246
+ else
247
+ pattern, pattern_match = nil
248
+ end
249
+
250
+ end_match = nil
251
+ if top.end
252
+ end_match = top.match_end( line, match, position )
253
+ end
254
+
255
+ if end_match && ( ! pattern_match || pattern_match.offset(0).first >= end_match.offset(0).first )
256
+ pattern_match = end_match
257
+ start_pos = pattern_match.offset(0).first
258
+ end_pos = pattern_match.offset(0).last
259
+ processor.close_tag top.contentName, start_pos if top.contentName && processor
260
+ parse_captures "captures", top, pattern_match, processor if processor
261
+ parse_captures "endCaptures", top, pattern_match, processor if processor
262
+ processor.close_tag top.name, end_pos if top.name && processor
263
+ stack.pop
264
+ top, match = stack.last
265
+ else
266
+ break unless pattern
267
+ start_pos = pattern_match.offset(0).first
268
+ end_pos = pattern_match.offset(0).last
269
+ if pattern.begin
270
+ processor.open_tag pattern.name, start_pos if pattern.name && processor
271
+ parse_captures "captures", pattern, pattern_match, processor if processor
272
+ parse_captures "beginCaptures", pattern, pattern_match, processor if processor
273
+ processor.open_tag pattern.contentName, end_pos if pattern.contentName && processor
274
+ top = pattern
275
+ match = pattern_match
276
+ stack << [top, match]
277
+ elsif pattern.match
278
+ processor.open_tag pattern.name, start_pos if pattern.name && processor
279
+ parse_captures "captures", pattern, pattern_match, processor if processor
280
+ processor.close_tag pattern.name, end_pos if pattern.name && processor
281
+ end
282
+ end
283
+ position = end_pos
284
+ end
285
+ end
286
+ end
287
+ end
data/lib/textpow.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'yaml'
2
+ require 'textpow/syntax'
3
+ require 'textpow/debug_processor'
4
+ require 'textpow/score_manager'
5
+
6
+
7
+ module Textpow
8
+ class ParsingError < Exception; end
9
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spox-textpow
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.10.2
5
+ platform: ruby
6
+ authors:
7
+ - spox
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-16 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: plist
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: Textpow is a library to parse and process Textmate bundles.
26
+ email: spox@modspox.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README.txt
33
+ files:
34
+ - bin/plist2yaml
35
+ - bin/plist2syntax
36
+ - README.txt
37
+ - History.txt
38
+ - lib/textpow.rb
39
+ - lib/textpow/score_manager.rb
40
+ - lib/textpow/syntax.rb
41
+ - lib/textpow/debug_processor.rb
42
+ - Manifest.txt
43
+ has_rdoc: true
44
+ homepage:
45
+ post_install_message:
46
+ rdoc_options:
47
+ - --title
48
+ - TextPow
49
+ - --main
50
+ - README.txt
51
+ - --line-numbers
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: 1.9.0
59
+ version:
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ requirements: []
67
+
68
+ rubyforge_project:
69
+ rubygems_version: 1.2.0
70
+ signing_key:
71
+ specification_version: 2
72
+ summary: textpow
73
+ test_files: []
74
+