spox-textpow 0.10.2

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