textpow1x 0.11.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.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .DS_Store
2
+ pkg/
3
+ *.gemspec
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source :rubygems
2
+ gemspec
3
+
4
+ gem 'oniguruma', :platform => :ruby_18
5
+
6
+ group :dev do
7
+ gem 'rake', '0.8.7'
8
+ gem 'jeweler'
9
+ gem 'hoe'
10
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,29 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ textpow1x (0.11.0)
5
+ plist (>= 3.0.1)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ git (1.2.5)
11
+ hoe (2.12.3)
12
+ rake (~> 0.8)
13
+ jeweler (1.6.4)
14
+ bundler (~> 1.0)
15
+ git (>= 1.2.5)
16
+ rake
17
+ oniguruma (1.1.0)
18
+ plist (3.1.0)
19
+ rake (0.8.7)
20
+
21
+ PLATFORMS
22
+ ruby
23
+
24
+ DEPENDENCIES
25
+ hoe
26
+ jeweler
27
+ oniguruma
28
+ rake (= 0.8.7)
29
+ textpow1x!
data/History.rdoc ADDED
@@ -0,0 +1,17 @@
1
+ == 0.11.0 / 2009-06-12
2
+ * Updated to 1.9 compatibility. Does not use onigumura gem anymore
3
+
4
+ == 0.10.1 / 2008-02-20
5
+ * FIX: Endless loop in method_missing when @proxy_variable is not present.
6
+
7
+ == 0.10.0 / 2007-06-15
8
+ * 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.
9
+
10
+ == 0.9.1 / 2007-06-14
11
+ * FIX: Score manager now handles multiple subtractions in scope definitions.
12
+
13
+ == 0.9.0 / 2007-03-19
14
+
15
+ * 1 major enhancement
16
+ * Birthday!
17
+
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/version.rb
7
+ lib/textpow/score_manager.rb
8
+ lib/textpow/debug_processor.rb
9
+ test/test_textpow.rb
10
+ History.rdoc
11
+ Rakefile
12
+ Manifest.txt
13
+ README.rdoc
data/README.rdoc ADDED
@@ -0,0 +1,212 @@
1
+ A library for parsing {TextMate}[http://macromates.com/] bundles.
2
+
3
+ For Ruby 1.8 and 1.9.
4
+
5
+
6
+ == INSTALL:
7
+
8
+ gem install textpow1x
9
+
10
+ === Ruby 1.8
11
+ Install oniguruma
12
+
13
+ # Ubuntu
14
+ sudo apt-get -y install libonig-dev
15
+ gem install oniguruma
16
+
17
+ # OsX (no idea if this works...)
18
+ port install oniguruma4
19
+
20
+
21
+ == USAGE:
22
+
23
+ 1. Get a .tmSyntax File
24
+
25
+ 2. Load the Syntax File:
26
+
27
+ require 'textpow'
28
+ syntax = Textpow::SyntaxNode.load("ruby.tmSyntax")
29
+
30
+ 3. Initialize a processor:
31
+
32
+ processor = Textpow::DebugProcessor.new
33
+
34
+ 4. Parse some text:
35
+
36
+ syntax.parse( text, processor )
37
+
38
+
39
+ === INDEPTH:
40
+
41
+ At the heart of syntax parsing are ..., well, syntax files. Lets see for instance
42
+ the example syntax that appears in textmate's
43
+ {documentation}[http://macromates.com/textmate/manual/language_grammars#language_grammars]:
44
+
45
+
46
+ { scopeName = 'source.untitled';
47
+ fileTypes = ( txt );
48
+ foldingStartMarker = '\{\s*$';
49
+ foldingStopMarker = '^\s*\}';
50
+ patterns = (
51
+ { name = 'keyword.control.untitled';
52
+ match = '\b(if|while|for|return)\b';
53
+ },
54
+ { name = 'string.quoted.double.untitled';
55
+ begin = '"';
56
+ end = '"';
57
+ patterns = (
58
+ { name = 'constant.character.escape.untitled';
59
+ match = '\\.';
60
+ }
61
+ );
62
+ },
63
+ );
64
+ }
65
+
66
+ But Textpow is not able to parse text pfiles. However, in practice this is not a problem,
67
+ since it is possible to convert both text and binary pfiles to an XML format. Indeed, all
68
+ the syntaxes in the Textmate syntax {repository}[http://macromates.com/svn/Bundles/trunk/Bundles/]
69
+ are in XML format:
70
+
71
+ <?xml version="1.0" encoding="UTF-8"?>
72
+ <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
73
+ <plist version="1.0">
74
+ <dict>
75
+ <key>scopeName</key>
76
+ <string>source.untitled</string>
77
+ <key>fileTypes</key>
78
+ <array>
79
+ <string>txt</string>
80
+ </array>
81
+ <key>foldingStartMarker</key>
82
+ <string>\{\s*$</string>
83
+ <key>foldingStopMarker</key>
84
+ <string>^\s*\}</string>
85
+ <key>patterns</key>
86
+ <array>
87
+ <dict>
88
+ <key>name</key>
89
+ <string>keyword.control.untitled</string>
90
+ <key>match</key>
91
+ <string>\b(if|while|for|return)\b</string>
92
+ </dict>
93
+ <dict>
94
+ <key>name</key>
95
+ <string>string.quoted.double.untitled</string>
96
+ <key>begin</key>
97
+ <string>"</string>
98
+ <key>end</key>
99
+ <string>"</string>
100
+ <key>patterns</key>
101
+ <array>
102
+ <dict>
103
+ <key>name</key>
104
+ <string>constant.character.escape.untitled</string>
105
+ <key>match</key>
106
+ <string>\\.</string>
107
+ </dict>
108
+ </array>
109
+ </dict>
110
+ </array>
111
+ </dict>
112
+
113
+ Of course, most people find XML both ugly and cumbersome. Fortunately, it is
114
+ also possible to store syntax files in YAML format, which is much easier to
115
+ read:
116
+
117
+ ---
118
+ fileTypes:
119
+ - txt
120
+ scopeName: source.untitled
121
+ foldingStartMarker: \{\s*$
122
+ foldingStopMarker: ^\s*\}
123
+ patterns:
124
+ - name: keyword.control.untitled
125
+ match: \b(if|while|for|return)\b
126
+ - name: string.quoted.double.untitled
127
+ begin: '"'
128
+ end: '"'
129
+ patterns:
130
+ - name: constant.character.escape.untitled
131
+ match: \\.
132
+
133
+ ==== Processors
134
+
135
+ Until now we have talked about the parsing process without explaining what
136
+ it is exactly. Basically, parsing consists in reading text from a string or
137
+ file and applying tags to parts of the text according to what has been
138
+ specified in the syntax file.
139
+
140
+ In textpow, the process takes place line by line, from the beginning to the
141
+ end and from left to right for every line. As the text is parsed, events are
142
+ sent to a processor object when a tag is open or closed and so on.
143
+ A processor is any object which implements one or more of the following
144
+ methods:
145
+
146
+ class Processor
147
+ def open_tag name, position
148
+ end
149
+
150
+ def close_tag name, position
151
+ end
152
+
153
+ def new_line line
154
+ end
155
+
156
+ def start_parsing name
157
+ end
158
+
159
+ def end_parsing name
160
+ end
161
+ end
162
+
163
+ * <tt>open_tag</tt>. Is called when a new tag is opened, it receives the tag's name and
164
+ its position (relative to the current line).
165
+ * <tt>close_tag</tt>. The same that <tt>open_tag</tt>, but it is called when a tag is closed.
166
+ * <tt>new_line</tt>. Is called every time that a new line is processed, it receives the
167
+ line's contents.
168
+ * <tt>start_parsing</tt>. Is called once at the beginning of the parsing process. It
169
+ receives the scope name for the syntax being used.
170
+ * <tt>end_parsing</tt>. Is called once after all the input text has been parsed. It
171
+ receives the scope name for the syntax being used.
172
+
173
+ Textpow ensures that the methods are called in parsing order, thus,
174
+ for example, if there are two subsequent calls to <tt>open_tag</tt>, the first
175
+ having <tt>name="text.string", position=10</tt> and the second having
176
+ <tt>name="markup.string", position=10</tt>, it should be understood that the
177
+ <tt>"markup.string"</tt> tag is inside the <tt>"text.string"</tt> tag.
178
+
179
+ == CREDITS:
180
+
181
+ * {Michael Grosser}[http://github.com/grosser]
182
+ * {Chris Hoffman}[http://github.com/cehoffman]
183
+ * {Spox}[http://github.com/spox]
184
+ * Dizan Vasque
185
+
186
+ == LICENSE:
187
+
188
+ (The MIT License)
189
+
190
+ * Copyright (c) 2011 Michael Grosser
191
+ * Copyright (c) 2010 Chris Hoffman
192
+ * Copyright (c) 2009 Spox
193
+ * Copyright (c) 2007-2008 Dizan Vasquez
194
+
195
+ Permission is hereby granted, free of charge, to any person obtaining
196
+ a copy of this software and associated documentation files (the
197
+ 'Software'), to deal in the Software without restriction, including
198
+ without limitation the rights to use, copy, modify, merge, publish,
199
+ distribute, sublicense, and/or sell copies of the Software, and to
200
+ permit persons to whom the Software is furnished to do so, subject to
201
+ the following conditions:
202
+
203
+ The above copyright notice and this permission notice shall be
204
+ included in all copies or substantial portions of the Software.
205
+
206
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
207
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
208
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
209
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
210
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
211
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
212
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require "bundler"
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rake/testtask'
5
+ Rake::TestTask.new do |test|
6
+ test.libs << 'lib'
7
+ test.pattern = 'test/**/*_test.rb'
8
+ test.verbose = true
9
+ end
10
+
11
+ task :default => :test
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,323 @@
1
+ module Textpow
2
+ RUBY_19 = (RUBY_VERSION > "1.9.0")
3
+ end
4
+ require 'oniguruma' unless Textpow::RUBY_19
5
+
6
+ require 'plist'
7
+
8
+ module Textpow
9
+ class SyntaxProxy
10
+ def initialize hash, syntax
11
+ @syntax = syntax
12
+ @proxy = hash["include"]
13
+ end
14
+
15
+ def method_missing method, *args, &block
16
+ if @proxy
17
+ @proxy_value = proxy unless @proxy_value
18
+ if @proxy_value
19
+ @proxy_value.send(method, *args, &block)
20
+ else
21
+ STDERR.puts "Failed proxying #{@proxy}.#{method}(#{args.join(', ')})"
22
+ end
23
+ end
24
+ end
25
+
26
+ def proxy
27
+ case @proxy
28
+ when /^#/
29
+ if @syntax.repository && @syntax.repository[@proxy[1..-1]]
30
+ #puts "Repository"
31
+ #@table["syntax"].repository.each_key{|k| puts k}
32
+ return @syntax.repository[@proxy[1..-1]]
33
+ end
34
+ when "$self"
35
+ return @syntax
36
+ when "$base"
37
+ return @syntax
38
+ else
39
+ return @syntax.syntaxes[@proxy]
40
+ end
41
+ end
42
+ end
43
+
44
+ class SyntaxNode
45
+ unless Textpow::RUBY_19
46
+ OPTIONS = {:options => Oniguruma::OPTION_CAPTURE_GROUP}
47
+ end
48
+
49
+ @@syntaxes = {}
50
+
51
+ attr_accessor :syntax
52
+ attr_accessor :firstLineMatch
53
+ attr_accessor :foldingStartMarker
54
+ attr_accessor :foldingStopMarker
55
+ attr_accessor :match
56
+ attr_accessor :begin
57
+ attr_accessor :content
58
+ attr_accessor :fileTypes
59
+ attr_accessor :name
60
+ attr_accessor :contentName
61
+ attr_accessor :end
62
+ attr_accessor :scopeName
63
+ attr_accessor :keyEquivalent
64
+ attr_accessor :captures
65
+ attr_accessor :beginCaptures
66
+ attr_accessor :endCaptures
67
+ attr_accessor :repository
68
+ attr_accessor :patterns
69
+
70
+ def self.load filename, name_space = :default
71
+ table = nil
72
+ case filename
73
+ when /(\.tmSyntax|\.plist)$/
74
+ table = Plist::parse_xml( filename )
75
+ else
76
+ File.open( filename ) do |f|
77
+ table = YAML.load( f )
78
+ end
79
+ end
80
+ if table
81
+ SyntaxNode.new( table, nil, name_space )
82
+ else
83
+ nil
84
+ end
85
+ end
86
+
87
+ def initialize hash, syntax = nil, name_space = :default
88
+ @name_space = name_space
89
+ @@syntaxes[@name_space] ||= {}
90
+ @@syntaxes[@name_space][hash["scopeName"]] = self if hash["scopeName"]
91
+ @syntax = syntax || self
92
+ hash.each do |key, value|
93
+ case key
94
+ when "firstLineMatch", "foldingStartMarker", "foldingStopMarker", "match", "begin"
95
+ begin
96
+ if Textpow::RUBY_19
97
+ value.force_encoding("ASCII-8BIT")
98
+ instance_variable_set( "@#{key}", Regexp.new( value ) )
99
+ else
100
+ instance_variable_set( "@#{key}", Oniguruma::ORegexp.new( value, OPTIONS ) )
101
+ end
102
+ rescue ArgumentError => e
103
+ raise ParsingError, "Parsing error in #{value}: #{e.to_s}"
104
+ end
105
+ when "content", "fileTypes", "name", "contentName", "end", "scopeName", "keyEquivalent"
106
+ instance_variable_set( "@#{key}", value )
107
+ when "captures", "beginCaptures", "endCaptures"
108
+ instance_variable_set( "@#{key}", value.sort )
109
+ when "repository"
110
+ parse_repository value
111
+ when "patterns"
112
+ create_children value
113
+ else
114
+ STDERR.puts "Ignoring: #{key} => #{value.gsub("\n", "\n>>")}" if $DEBUG
115
+ end
116
+ end
117
+ end
118
+
119
+
120
+ def syntaxes
121
+ @@syntaxes[@name_space]
122
+ end
123
+
124
+ def parse( string, processor = nil )
125
+ processor.start_parsing self.scopeName if processor
126
+ stack = [[self, nil]]
127
+ string.each_line do |line|
128
+ parse_line stack, line, processor
129
+ end
130
+ processor.end_parsing self.scopeName if processor
131
+ processor
132
+ end
133
+
134
+ protected
135
+
136
+ def parse_repository repository
137
+ @repository = {}
138
+ repository.each do |key, value|
139
+ if value["include"]
140
+ @repository[key] = SyntaxProxy.new( value, self.syntax )
141
+ else
142
+ @repository[key] = SyntaxNode.new( value, self.syntax, @name_space )
143
+ end
144
+ end
145
+ end
146
+
147
+ def create_children patterns
148
+ @patterns = []
149
+ patterns.each do |p|
150
+ if p["include"]
151
+ @patterns << SyntaxProxy.new( p, self.syntax )
152
+ else
153
+ @patterns << SyntaxNode.new( p, self.syntax, @name_space )
154
+ end
155
+ end
156
+ end
157
+
158
+ def parse_captures name, pattern, match, processor
159
+ captures = pattern.match_captures( name, match )
160
+ captures.reject! { |group, range, name| ! range.first || range.first == range.last }
161
+ starts = []
162
+ ends = []
163
+ captures.each do |group, range, name|
164
+ starts << [range.first, group, name]
165
+ ends << [range.last, -group, name]
166
+ end
167
+
168
+ # STDERR.puts '-' * 100
169
+ # starts.sort!.reverse!.each{|c| STDERR.puts c.join(', ')}
170
+ # STDERR.puts
171
+ # ends.sort!.reverse!.each{|c| STDERR.puts c.join(', ')}
172
+ starts.sort!.reverse!
173
+ ends.sort!.reverse!
174
+
175
+ while ! starts.empty? || ! ends.empty?
176
+ if starts.empty?
177
+ pos, key, name = ends.pop
178
+ processor.close_tag name, pos
179
+ elsif ends.empty?
180
+ pos, key, name = starts.pop
181
+ processor.open_tag name, pos
182
+ elsif ends.last[1].abs < starts.last[1]
183
+ pos, key, name = ends.pop
184
+ processor.close_tag name, pos
185
+ else
186
+ pos, key, name = starts.pop
187
+ processor.open_tag name, pos
188
+ end
189
+ end
190
+ end
191
+
192
+ def match_captures name, match
193
+ matches = []
194
+ captures = instance_variable_get "@#{name}"
195
+ if captures
196
+ captures.each do |key, value|
197
+ if key =~ /^\d*$/
198
+ matches << [key.to_i, match.offset( key.to_i ), value["name"]] if key.to_i < match.size
199
+ else
200
+ matches << [match.to_index( key.to_sym ), match.offset( key.to_sym), value["name"]] if match.to_index( key.to_sym )
201
+ end
202
+ end
203
+ end
204
+ matches
205
+ end
206
+
207
+ def match_first string, position
208
+ if self.match
209
+ if match = self.match.match( string, position )
210
+ return [self, match]
211
+ end
212
+ elsif self.begin
213
+ if match = self.begin.match( string, position )
214
+ return [self, match]
215
+ end
216
+ elsif self.end
217
+ else
218
+ return match_first_son( string, position )
219
+ end
220
+ nil
221
+ end
222
+
223
+ def match_end string, match, position
224
+ regstring = self.end.clone
225
+ regstring.gsub!( /\\([1-9])/ ) { |s| match[$1.to_i] }
226
+
227
+ # in spox-textpow this is \\g in 1.9 !?
228
+ regstring.gsub!( /\\k<(.*?)>/ ) { |s| match[$1.to_sym] }
229
+ if Textpow::RUBY_19
230
+ Regexp.new( regstring ).match( string, position )
231
+ else
232
+ Oniguruma::ORegexp.new( regstring ).match( string, position )
233
+ end
234
+ end
235
+
236
+ def match_first_son string, position
237
+ match = nil
238
+ if self.patterns
239
+ self.patterns.each do |p|
240
+ tmatch = p.match_first string, position
241
+ if tmatch
242
+ ok = if Textpow::RUBY_19
243
+ ! match || match[1].offset(0).first > tmatch[1].offset(0).first
244
+ else
245
+ ! match || match[1].offset.first > tmatch[1].offset.first
246
+ end
247
+ match = tmatch if ok
248
+ #break if tmatch[1].offset.first == position
249
+ end
250
+ end
251
+ end
252
+ match
253
+ end
254
+
255
+ def parse_line stack, line, processor
256
+ processor.new_line line if processor
257
+ top, match = stack.last
258
+ position = 0
259
+ #@ln ||= 0
260
+ #@ln += 1
261
+ #STDERR.puts @ln
262
+ while true
263
+ if top.patterns
264
+ pattern, pattern_match = top.match_first_son line, position
265
+ else
266
+ pattern, pattern_match = nil
267
+ end
268
+
269
+ end_match = nil
270
+ if top.end
271
+ end_match = top.match_end( line, match, position )
272
+ end
273
+
274
+ ok = if Textpow::RUBY_19
275
+ end_match && ( ! pattern_match || pattern_match.offset(0).first >= end_match.offset(0).first )
276
+ else
277
+ end_match && ( ! pattern_match || pattern_match.offset.first >= end_match.offset.first )
278
+ end
279
+
280
+ if ok
281
+ pattern_match = end_match
282
+ if Textpow::RUBY_19
283
+ start_pos = pattern_match.offset(0).first
284
+ end_pos = pattern_match.offset(0).last
285
+ else
286
+ start_pos = pattern_match.offset.first
287
+ end_pos = pattern_match.offset.last
288
+ end
289
+ processor.close_tag top.contentName, start_pos if top.contentName && processor
290
+ parse_captures "captures", top, pattern_match, processor if processor
291
+ parse_captures "endCaptures", top, pattern_match, processor if processor
292
+ processor.close_tag top.name, end_pos if top.name && processor
293
+ stack.pop
294
+ top, match = stack.last
295
+ else
296
+ break unless pattern
297
+ if Textpow::RUBY_19
298
+ start_pos = pattern_match.offset(0).first
299
+ end_pos = pattern_match.offset(0).last
300
+ else
301
+ start_pos = pattern_match.offset.first
302
+ end_pos = pattern_match.offset.last
303
+ end
304
+
305
+ if pattern.begin
306
+ processor.open_tag pattern.name, start_pos if pattern.name && processor
307
+ parse_captures "captures", pattern, pattern_match, processor if processor
308
+ parse_captures "beginCaptures", pattern, pattern_match, processor if processor
309
+ processor.open_tag pattern.contentName, end_pos if pattern.contentName && processor
310
+ top = pattern
311
+ match = pattern_match
312
+ stack << [top, match]
313
+ elsif pattern.match
314
+ processor.open_tag pattern.name, start_pos if pattern.name && processor
315
+ parse_captures "captures", pattern, pattern_match, processor if processor
316
+ processor.close_tag pattern.name, end_pos if pattern.name && processor
317
+ end
318
+ end
319
+ position = end_pos
320
+ end
321
+ end
322
+ end
323
+ end
@@ -0,0 +1,3 @@
1
+ module Textpow
2
+ Version = "0.11.0"
3
+ end
data/lib/textpow.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'yaml'
2
+ require 'textpow/syntax'
3
+ require 'textpow/debug_processor'
4
+ require 'textpow/score_manager'
5
+ require 'textpow/version'
6
+
7
+ module Textpow
8
+ class ParsingError < Exception; end
9
+ end
10
+
@@ -0,0 +1,24 @@
1
+ require 'rubygems'
2
+ require 'textpow'
3
+ require 'test/unit'
4
+
5
+ class ScoreManagerTest < Test::Unit::TestCase
6
+ include Textpow
7
+
8
+ def test_score
9
+ sp = ScoreManager.new
10
+ reference_scope = 'text.html.basic source.php.embedded.html string.quoted.double.php'
11
+
12
+ assert_not_equal( 0, sp.score( 'source.php string', reference_scope ) )
13
+ assert_not_equal( 0, sp.score( 'text.html source.php', reference_scope ) )
14
+ assert_equal( 0, sp.score( 'string source.php', reference_scope ) )
15
+ assert_equal( 0, sp.score( 'source.php text.html', reference_scope ) )
16
+
17
+ assert_equal( 0, sp.score( 'text.html source.php - string', reference_scope ) )
18
+ assert_not_equal( 0, sp.score( 'text.html source.php - ruby', reference_scope ) )
19
+
20
+ assert( sp.score( 'string', reference_scope ) > sp.score( 'source.php', reference_scope ) )
21
+ assert( sp.score( 'string.quoted', reference_scope ) > sp.score( 'source.php', reference_scope ) )
22
+ assert( sp.score( 'text source string', reference_scope ) > sp.score( 'source string', reference_scope ) )
23
+ end
24
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: textpow1x
3
+ version: !ruby/object:Gem::Version
4
+ hash: 51
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 11
9
+ - 0
10
+ version: 0.11.0
11
+ platform: ruby
12
+ authors:
13
+ - Dizan Vasquez
14
+ - Spox
15
+ - Chris Hoffman
16
+ - Michael Grosser
17
+ autorequire:
18
+ bindir: bin
19
+ cert_chain: []
20
+
21
+ date: 2011-09-12 00:00:00 +02:00
22
+ default_executable:
23
+ dependencies:
24
+ - !ruby/object:Gem::Dependency
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ hash: 5
31
+ segments:
32
+ - 3
33
+ - 0
34
+ - 1
35
+ version: 3.0.1
36
+ type: :runtime
37
+ name: plist
38
+ version_requirements: *id001
39
+ prerelease: false
40
+ description: A library for parsing TextMate bundles on ruby 1.x
41
+ email:
42
+ - michael@grosser.it
43
+ executables:
44
+ - plist2yaml
45
+ - plist2syntax
46
+ extensions: []
47
+
48
+ extra_rdoc_files: []
49
+
50
+ files:
51
+ - .gitignore
52
+ - Gemfile
53
+ - Gemfile.lock
54
+ - History.rdoc
55
+ - Manifest.txt
56
+ - README.rdoc
57
+ - Rakefile
58
+ - bin/plist2syntax
59
+ - bin/plist2yaml
60
+ - lib/textpow.rb
61
+ - lib/textpow/debug_processor.rb
62
+ - lib/textpow/score_manager.rb
63
+ - lib/textpow/syntax.rb
64
+ - lib/textpow/version.rb
65
+ - test/textpow_test.rb
66
+ has_rdoc: true
67
+ homepage: http://github.com/grosser/textpow
68
+ licenses:
69
+ - MIT
70
+ post_install_message:
71
+ rdoc_options:
72
+ - --main
73
+ - README.rdoc
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ hash: 3
82
+ segments:
83
+ - 0
84
+ version: "0"
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ hash: 3
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ requirements: []
95
+
96
+ rubyforge_project:
97
+ rubygems_version: 1.6.2
98
+ signing_key:
99
+ specification_version: 3
100
+ summary: A library for parsing TextMate bundles on ruby 1.x
101
+ test_files: []
102
+