textpow1x 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,8 +3,6 @@ module Textpow
3
3
  end
4
4
  require 'oniguruma' unless Textpow::RUBY_19
5
5
 
6
- require 'plist'
7
-
8
6
  module Textpow
9
7
  class SyntaxProxy
10
8
  def initialize hash, syntax
@@ -42,10 +40,6 @@ module Textpow
42
40
  end
43
41
 
44
42
  class SyntaxNode
45
- unless Textpow::RUBY_19
46
- OPTIONS = {:options => Oniguruma::OPTION_CAPTURE_GROUP}
47
- end
48
-
49
43
  @@syntaxes = {}
50
44
 
51
45
  attr_accessor :syntax
@@ -67,72 +61,84 @@ module Textpow
67
61
  attr_accessor :repository
68
62
  attr_accessor :patterns
69
63
 
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
64
+ def self.load(file, name_space = :default)
65
+ table = convert_file_to_table(file)
66
+ SyntaxNode.new(table, nil, name_space)
85
67
  end
86
68
 
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"]
69
+ def initialize(table, syntax = nil, name_space = :default)
91
70
  @syntax = syntax || self
92
- hash.each do |key, value|
71
+ @name_space = name_space
72
+
73
+ register_in_syntaxes(table["scopeName"])
74
+ parse_and_store_syntax_info(table)
75
+ end
76
+
77
+ def syntaxes
78
+ @@syntaxes[@name_space]
79
+ end
80
+
81
+ def parse(string, processor = RecordingProcessor.new)
82
+ processor.start_parsing scopeName
83
+ stack = [[self, nil]]
84
+ string.each_line do |line|
85
+ parse_line stack, line, processor
86
+ end
87
+ processor.end_parsing scopeName
88
+
89
+ processor
90
+ end
91
+
92
+ protected
93
+
94
+ def parse_and_store_syntax_info(table)
95
+ table.each do |key, value|
93
96
  case key
94
97
  when "firstLineMatch", "foldingStartMarker", "foldingStopMarker", "match", "begin"
95
98
  begin
96
- if Textpow::RUBY_19
99
+ regex = if Textpow::RUBY_19
97
100
  value.force_encoding("ASCII-8BIT")
98
- instance_variable_set( "@#{key}", Regexp.new( value ) )
101
+ Regexp.new(value)
99
102
  else
100
- instance_variable_set( "@#{key}", Oniguruma::ORegexp.new( value, OPTIONS ) )
103
+ Oniguruma::ORegexp.new(value, :options => Oniguruma::OPTION_CAPTURE_GROUP)
101
104
  end
105
+ instance_variable_set("@#{key}", regex)
102
106
  rescue ArgumentError => e
103
107
  raise ParsingError, "Parsing error in #{value}: #{e.to_s}"
104
108
  end
105
109
  when "content", "fileTypes", "name", "contentName", "end", "scopeName", "keyEquivalent"
106
- instance_variable_set( "@#{key}", value )
110
+ instance_variable_set("@#{key}", value)
107
111
  when "captures", "beginCaptures", "endCaptures"
108
- instance_variable_set( "@#{key}", value.sort )
112
+ instance_variable_set("@#{key}", value.sort)
109
113
  when "repository"
110
114
  parse_repository value
111
115
  when "patterns"
112
116
  create_children value
117
+ when "comment"
113
118
  else
114
119
  STDERR.puts "Ignoring: #{key} => #{value.gsub("\n", "\n>>")}" if $DEBUG
115
120
  end
116
121
  end
117
122
  end
118
123
 
119
-
120
- def syntaxes
121
- @@syntaxes[@name_space]
124
+ # register in global syntax list -> can be found by include
125
+ def register_in_syntaxes(scope)
126
+ @@syntaxes[@name_space] ||= {}
127
+ @@syntaxes[@name_space][scope] = self if scope
122
128
  end
123
129
 
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
130
+ def self.convert_file_to_table(file)
131
+ raise "File not found: #{file}" unless File.exist?(file)
132
+
133
+ case file
134
+ when /(\.tmSyntax|\.plist)$/
135
+ require 'plist'
136
+ Plist::parse_xml(file)
137
+ else
138
+ YAML.load_file(file)
129
139
  end
130
- processor.end_parsing self.scopeName if processor
131
- processor
132
140
  end
133
141
 
134
- protected
135
-
136
142
  def parse_repository repository
137
143
  @repository = {}
138
144
  repository.each do |key, value|
@@ -233,91 +239,80 @@ module Textpow
233
239
  end
234
240
  end
235
241
 
236
- def match_first_son string, position
242
+ def match_first_son(string, position)
243
+ return if not patterns
244
+
237
245
  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
246
+ patterns.each do |p|
247
+ tmatch = p.match_first string, position
248
+ if tmatch
249
+ if not match or match_offset(match[1]).first > match_offset(tmatch[1]).first
250
+ match = tmatch
249
251
  end
252
+ #break if tmatch[1].offset.first == position
250
253
  end
251
254
  end
252
255
  match
253
256
  end
254
257
 
255
- def parse_line stack, line, processor
256
- processor.new_line line if processor
258
+ def parse_line(stack, line, processor)
259
+ processor.new_line line
257
260
  top, match = stack.last
258
261
  position = 0
259
262
  #@ln ||= 0
260
263
  #@ln += 1
261
264
  #STDERR.puts @ln
262
- while true
265
+ loop do
263
266
  if top.patterns
264
- pattern, pattern_match = top.match_first_son line, position
265
- else
266
- pattern, pattern_match = nil
267
+ pattern, pattern_match = top.match_first_son(line, position)
267
268
  end
268
269
 
269
- end_match = nil
270
270
  if top.end
271
271
  end_match = top.match_end( line, match, position )
272
272
  end
273
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
274
+ if end_match and (not pattern_match or match_offset(pattern_match).first >= match_offset(end_match).first)
281
275
  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
276
+ start_pos = match_offset(pattern_match).first
277
+ end_pos = match_offset(pattern_match).last
278
+
279
+ processor.close_tag top.contentName, start_pos if top.contentName
280
+ parse_captures "captures", top, pattern_match, processor
281
+ parse_captures "endCaptures", top, pattern_match, processor
282
+ processor.close_tag top.name, end_pos if top.name
293
283
  stack.pop
294
284
  top, match = stack.last
295
285
  else
296
286
  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
287
+
288
+ start_pos = match_offset(pattern_match).first
289
+ end_pos = match_offset(pattern_match).last
304
290
 
305
291
  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
292
+ processor.open_tag pattern.name, start_pos if pattern.name
293
+ parse_captures "captures", pattern, pattern_match, processor
294
+ parse_captures "beginCaptures", pattern, pattern_match, processor
295
+ processor.open_tag pattern.contentName, end_pos if pattern.contentName
310
296
  top = pattern
311
297
  match = pattern_match
312
298
  stack << [top, match]
313
299
  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
300
+ processor.open_tag pattern.name, start_pos if pattern.name
301
+ parse_captures "captures", pattern, pattern_match, processor
302
+ processor.close_tag pattern.name, end_pos if pattern.name
317
303
  end
318
304
  end
305
+
319
306
  position = end_pos
320
307
  end
321
308
  end
309
+
310
+ def match_offset(match)
311
+ if Textpow::RUBY_19
312
+ match.offset(0)
313
+ else
314
+ match.offset
315
+ end
316
+ end
322
317
  end
323
318
  end
@@ -1,3 +1,3 @@
1
1
  module Textpow
2
- Version = "1.0.0"
2
+ VERSION = Version = "1.1.0"
3
3
  end
data/lib/textpow.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require 'yaml'
2
2
  require 'textpow/syntax'
3
3
  require 'textpow/debug_processor'
4
+ require 'textpow/recording_processor'
4
5
  require 'textpow/score_manager'
5
6
  require 'textpow/version'
6
7
 
@@ -0,0 +1,107 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>BBEditDocumentType</key>
6
+ <string>CodelessLanguageModule</string>
7
+ <key>BBLMColorsSyntax</key>
8
+ <true/>
9
+ <key>BBLMIsCaseSensitive</key>
10
+ <true/>
11
+ <key>BBLMKeywordList</key>
12
+ <array>
13
+ <string>and</string>
14
+ <string>or</string>
15
+ <string>xor</string>
16
+ <string>virtual</string>
17
+ <string>if</string>
18
+ <string>else</string>
19
+ <string>do</string>
20
+ <string>while</string>
21
+ <string>use</string>
22
+ <string>bundle</string>
23
+ <string>native</string>
24
+ <string>static</string>
25
+ <string>public</string>
26
+ <string>private</string>
27
+ <string>class</string>
28
+ <string>interface</string>
29
+ <string>select</string>
30
+ <string>other</string>
31
+ <string>enum</string>
32
+ <string>for</string>
33
+ <string>each</string>
34
+ <string>label</string>
35
+ <string>return</string>
36
+ <string>Byte</string>
37
+ <string>Int</string>
38
+ <string>Parent</string>
39
+ <string>from</string>
40
+ <string>Float</string>
41
+ <string>Char</string>
42
+ <string>Bool</string>
43
+ <string>String</string>
44
+ <string>Nil</string>
45
+ <string>true</string>
46
+ <string>false</string>
47
+ <string>function</string>
48
+ <string>method</string>
49
+ </array>
50
+ <key>BBLMLanguageCode</key>
51
+ <string>LSL</string>
52
+ <key>BBLMLanguageDisplayName</key>
53
+ <string>Objeck</string>
54
+ <key>BBLMScansFunctions</key>
55
+ <true/>
56
+ <key>BBLMSuffixMap</key>
57
+ <array>
58
+ <dict>
59
+ <key>BBLMLanguageSuffix</key>
60
+ <string>.obs</string>
61
+ </dict>
62
+ </array>
63
+ <key>Language Features</key>
64
+ <dict>
65
+ <key>Close Block Comments</key>
66
+ <string>~#</string>
67
+ <key>Close Parameter Lists</key>
68
+ <string>)</string>
69
+ <key>Close Statement Blocks</key>
70
+ <string>}</string>
71
+ <key>Close Strings 1</key>
72
+ <string>&quot;</string>
73
+ <key>Close Strings 2</key>
74
+ <string>&apos;</string>
75
+ <key>End-of-line Ends Strings 1</key>
76
+ <false/>
77
+ <key>End-of-line Ends Strings 2</key>
78
+ <false/>
79
+ <key>Escape Char in Strings 1</key>
80
+ <string>\</string>
81
+ <key>Escape Char in Strings 2</key>
82
+ <string>\</string>
83
+ <key>Identifier and Keyword Characters</key>
84
+ <string>@0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz</string>
85
+ <key>Open Block Comments</key>
86
+ <string>#~</string>
87
+ <key>Open Line Comments</key>
88
+ <string>#</string>
89
+ <key>Open Parameter Lists</key>
90
+ <string>(</string>
91
+ <key>Open Statement Blocks</key>
92
+ <string>{</string>
93
+ <key>Open Strings 1</key>
94
+ <string>&quot;</string>
95
+ <key>Open Strings 2</key>
96
+ <string>&apos;</string>
97
+ <key>Prefix for Functions</key>
98
+ <string></string>
99
+ <key>Prefix for Procedures</key>
100
+ <string></string>
101
+ <key>Terminator for Prototypes 1</key>
102
+ <string></string>
103
+ <key>Terminator for Prototypes 2</key>
104
+ <string></string>
105
+ </dict>
106
+ </dict>
107
+ </plist>
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe Textpow::ScoreManager do
4
+ it "calculates scores" do
5
+ sp = Textpow::ScoreManager.new
6
+ reference_scope = 'text.html.basic source.php.embedded.html string.quoted.double.php'
7
+
8
+ sp.score('source.php string', reference_scope).should_not == 0
9
+ sp.score('text.html source.php', reference_scope).should_not == 0
10
+ sp.score('string source.php', reference_scope).should == 0
11
+ sp.score('source.php text.html', reference_scope).should == 0
12
+
13
+ sp.score('text.html source.php - string', reference_scope).should == 0
14
+ sp.score('text.html source.php - ruby', reference_scope ).should_not == 0
15
+
16
+ sp.score('string', reference_scope).should > sp.score('source.php', reference_scope)
17
+ sp.score('string.quoted', reference_scope).should > sp.score('source.php', reference_scope)
18
+ sp.score('text source string', reference_scope).should > sp.score( 'source string', reference_scope)
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe "syntax files" do
4
+ before do
5
+ STDERR.stub!(:puts)
6
+ end
7
+
8
+ let(:processor){ Textpow::DebugProcessor.new }
9
+
10
+ it "has syntax files" do
11
+ Dir["#{Textpow.syntax_path}/*.syntax"].should_not == []
12
+ end
13
+
14
+ Dir["#{Textpow.syntax_path}/*.syntax"].each do |syntax|
15
+ it "#{syntax} can parse" do
16
+ node = Textpow::SyntaxNode.load(syntax)
17
+ node.parse("xxx\n1 + 1\n### xxx", processor)
18
+ end
19
+ end
20
+
21
+ # syntax broken in 1.9
22
+ xit "parses markdown" do
23
+ node = Textpow::SyntaxNode.load("#{Textpow.syntax_path}/broken/markdown.syntax")
24
+ node.parse("### xxx\nabc\n xxx\n yyy\n - abc\n - ac", processor)
25
+ end
26
+ end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ describe Textpow::SyntaxNode do
4
+ before do
5
+ Textpow::SyntaxNode.send(:class_variable_set, "@@syntaxes", {})
6
+ end
7
+
8
+ describe "#load" do
9
+ it "can load from xml .plist" do
10
+ Textpow::SyntaxNode.load('spec/fixtures/objeck.plist').should_not == nil
11
+ end
12
+
13
+ it "can load from yaml .syntax" do
14
+ Textpow::SyntaxNode.load('lib/textpow/syntax/ruby.syntax').should_not == nil
15
+ end
16
+
17
+ it "cannot load missing file" do
18
+ lambda{
19
+ Textpow::SyntaxNode.load('xxx.syntax')
20
+ }.should raise_error
21
+ end
22
+
23
+ it "cannot load missing plist file" do
24
+ lambda{
25
+ Textpow::SyntaxNode.load('xxx.plist')
26
+ }.should raise_error
27
+ end
28
+ end
29
+
30
+ describe "#new" do
31
+ it "loads strings from given hash" do
32
+ syntax = Textpow::SyntaxNode.new('content' => 'CONTENT', 'name' => 'NAME')
33
+ syntax.content.should == 'CONTENT'
34
+ syntax.name.should == 'NAME'
35
+ end
36
+
37
+ it "loads regex from given hash" do
38
+ syntax = Textpow::SyntaxNode.new('firstLineMatch' => 'aaa', 'foldingStartMarker' => 'bbb')
39
+ syntax.firstLineMatch.inspect.should == "/aaa/"
40
+ syntax.foldingStartMarker.inspect.should == "/bbb/"
41
+ end
42
+
43
+ it "raises ParsingError on invalid regex" do
44
+ lambda{
45
+ Textpow::SyntaxNode.new('firstLineMatch' => '$?)(:[/\]')
46
+ }.should raise_error(Textpow::ParsingError)
47
+ end
48
+
49
+ it "stores itself as parent" do
50
+ node = Textpow::SyntaxNode.new({})
51
+ node.syntax.should == node
52
+ end
53
+
54
+ it "stores given parent" do
55
+ node = Textpow::SyntaxNode.new({}, 1)
56
+ node.syntax.should == 1
57
+ end
58
+
59
+ it "stores itself in global namespace under scopeName" do
60
+ node = Textpow::SyntaxNode.new("scopeName" => 'xxx')
61
+ node.syntaxes['xxx'].should == node
62
+ Textpow::SyntaxNode.new({}).syntaxes['xxx'].should == node
63
+ end
64
+
65
+ it "stores itself in scoped global namespace under scopeName" do
66
+ node = Textpow::SyntaxNode.new({"scopeName" => 'xxx'}, nil, 'foo')
67
+ node.syntaxes['xxx'].should == node
68
+ Textpow::SyntaxNode.new({},nil,'foo').syntaxes['xxx'].should == node
69
+ Textpow::SyntaxNode.new({}).syntaxes['xxx'].should == nil
70
+ end
71
+ end
72
+
73
+ describe "#parse" do
74
+ let(:node){ Textpow::SyntaxNode.load('lib/textpow/syntax/ruby.syntax') }
75
+
76
+ it "uses a RecordingProcessor by default" do
77
+ node.parse("111").stack.should == [
78
+ [:start_parsing, "source.ruby"],
79
+ [:new_line, "111"],
80
+ [:open_tag, "constant.numeric.ruby", 0],
81
+ [:close_tag, "constant.numeric.ruby", 3],
82
+ [:end_parsing, "source.ruby"]
83
+ ]
84
+ end
85
+
86
+ it "can parse with a processor" do
87
+ processor = Textpow::RecordingProcessor.new
88
+ processor.stack << 'xxx'
89
+ node.parse("111", processor).stack.should == [
90
+ "xxx",
91
+ [:start_parsing, "source.ruby"],
92
+ [:new_line, "111"],
93
+ [:open_tag, "constant.numeric.ruby", 0],
94
+ [:close_tag, "constant.numeric.ruby", 3],
95
+ [:end_parsing, "source.ruby"]
96
+ ]
97
+ end
98
+ end
99
+ end
data/spec/textpow_spec.rb CHANGED
@@ -1,34 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Textpow::ScoreManager do
4
- # TODO legacy test
5
- it "calculates scores" do
6
- sp = Textpow::ScoreManager.new
7
- reference_scope = 'text.html.basic source.php.embedded.html string.quoted.double.php'
8
-
9
- sp.score('source.php string', reference_scope).should_not == 0
10
- sp.score('text.html source.php', reference_scope).should_not == 0
11
- sp.score('string source.php', reference_scope).should == 0
12
- sp.score('source.php text.html', reference_scope).should == 0
13
-
14
- sp.score('text.html source.php - string', reference_scope).should == 0
15
- sp.score('text.html source.php - ruby', reference_scope ).should_not == 0
16
-
17
- sp.score('string', reference_scope).should > sp.score('source.php', reference_scope)
18
- sp.score('string.quoted', reference_scope).should > sp.score('source.php', reference_scope)
19
- sp.score('text source string', reference_scope).should > sp.score( 'source string', reference_scope)
20
- end
21
-
22
- describe "syntax" do
23
- it "has syntax files" do
24
- Dir["#{Textpow.syntax_path}/*.syntax"].should_not == []
25
- end
26
-
27
- Dir["#{Textpow.syntax_path}/*.syntax"].each do |syntax|
28
- it "#{syntax} can parse" do
29
- node = Textpow::SyntaxNode.load(syntax)
30
- node.parse("xxx\n1 + 1\n### xxx")
31
- end
32
- end
3
+ describe Textpow do
4
+ it "has a version" do
5
+ Textpow::Version =~ /^\d\.\d\.\d$/
33
6
  end
34
7
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: textpow1x
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
+ - 1
8
9
  - 0
9
- - 0
10
- version: 1.0.0
10
+ version: 1.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Dizan Vasquez
@@ -18,7 +18,7 @@ autorequire:
18
18
  bindir: bin
19
19
  cert_chain: []
20
20
 
21
- date: 2011-10-02 00:00:00 +02:00
21
+ date: 2011-10-06 00:00:00 +02:00
22
22
  default_executable:
23
23
  dependencies:
24
24
  - !ruby/object:Gem::Dependency
@@ -49,16 +49,21 @@ extra_rdoc_files: []
49
49
 
50
50
  files:
51
51
  - .gitignore
52
+ - .travis.yml
52
53
  - Gemfile
53
54
  - Gemfile.lock
54
55
  - History.rdoc
56
+ - MIT-LICENSE.txt
55
57
  - Manifest.txt
56
58
  - README.rdoc
57
59
  - Rakefile
58
60
  - bin/plist2syntax
59
61
  - bin/plist2yaml
62
+ - examples/benchmark_js.rb
63
+ - examples/jquery.js
60
64
  - lib/textpow.rb
61
65
  - lib/textpow/debug_processor.rb
66
+ - lib/textpow/recording_processor.rb
62
67
  - lib/textpow/score_manager.rb
63
68
  - lib/textpow/syntax.rb
64
69
  - lib/textpow/syntax/actionscript.syntax
@@ -225,7 +230,11 @@ files:
225
230
  - lib/textpow/syntax/yaml.syntax
226
231
  - lib/textpow/syntax/yui_javascript.syntax
227
232
  - lib/textpow/version.rb
233
+ - spec/fixtures/objeck.plist
228
234
  - spec/spec_helper.rb
235
+ - spec/textpow/score_manager_spec.rb
236
+ - spec/textpow/syntax_files_spec.rb
237
+ - spec/textpow/syntax_spec.rb
229
238
  - spec/textpow_spec.rb
230
239
  has_rdoc: true
231
240
  homepage: http://github.com/grosser/textpow