bibtex-ruby 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bibtex-ruby might be problematic. Click here for more details.

Files changed (67) hide show
  1. data/Gemfile +6 -1
  2. data/Gemfile.lock +48 -5
  3. data/History.txt +16 -1
  4. data/Manifest +43 -19
  5. data/README.md +178 -167
  6. data/Rakefile +26 -5
  7. data/auto.watchr +6 -0
  8. data/bibtex-ruby.gemspec +8 -5
  9. data/examples/bib2html.rb +28 -18
  10. data/features/bibtex.feature +96 -0
  11. data/features/entries.feature +67 -0
  12. data/features/issues/slash_keys.feature +21 -0
  13. data/features/names.feature +72 -0
  14. data/features/preambles.feature +27 -0
  15. data/features/query.feature +56 -0
  16. data/features/replacement.feature +68 -0
  17. data/features/step_definitions/bibtex_steps.rb +74 -0
  18. data/features/step_definitions/name_steps.rb +13 -0
  19. data/features/strings.feature +52 -0
  20. data/features/support/env.rb +7 -0
  21. data/lib/bibtex.rb +5 -1
  22. data/lib/bibtex/bibliography.rb +218 -95
  23. data/lib/bibtex/bibtex.y +18 -15
  24. data/lib/bibtex/elements.rb +130 -136
  25. data/lib/bibtex/entry.rb +133 -69
  26. data/lib/bibtex/extensions.rb +0 -35
  27. data/lib/bibtex/lexer.rb +9 -9
  28. data/lib/bibtex/name_parser.output +464 -0
  29. data/lib/bibtex/name_parser.rb +490 -0
  30. data/lib/bibtex/names.rb +162 -0
  31. data/lib/bibtex/names.y +196 -0
  32. data/lib/bibtex/parser.output +5 -5
  33. data/lib/bibtex/parser.rb +19 -16
  34. data/lib/bibtex/replaceable.rb +52 -0
  35. data/lib/bibtex/utilities.rb +23 -5
  36. data/lib/bibtex/value.rb +201 -0
  37. data/lib/bibtex/version.rb +1 -1
  38. data/test/benchmark.rb +52 -0
  39. data/test/bibtex/test_bibliography.rb +141 -0
  40. data/test/bibtex/test_elements.rb +40 -0
  41. data/test/bibtex/test_entry.rb +99 -0
  42. data/test/bibtex/test_names.rb +23 -0
  43. data/test/bibtex/test_parser.rb +79 -0
  44. data/test/bibtex/test_string.rb +83 -0
  45. data/test/bibtex/test_utilities.rb +34 -0
  46. data/test/bibtex/test_value.rb +70 -0
  47. data/test/{bib/10_bibdesk.bib → fixtures/bibdesk.bib} +1 -1
  48. data/test/{bib/05_comment.bib → fixtures/comment.bib} +0 -0
  49. data/test/{bib/08_decoret.bib → fixtures/decoret.bib} +0 -0
  50. data/test/{bib/00_empty.bib → fixtures/empty.bib} +0 -0
  51. data/test/{bib/07_entry.bib → fixtures/entry.bib} +0 -0
  52. data/test/{bib/09_errors.bib → fixtures/errors.bib} +0 -0
  53. data/test/{bib/01_no_bibtex.bib → fixtures/no_bibtex.bib} +0 -0
  54. data/test/{bib/06_preamble.bib → fixtures/preamble.bib} +1 -1
  55. data/test/{bib/11_roundtrip.bib → fixtures/roundtrip.bib} +1 -1
  56. data/test/helper.rb +17 -2
  57. data/test/test_bibtex.rb +87 -93
  58. data/test/test_export.rb +18 -22
  59. metadata +85 -30
  60. data/test/bib/02_string.bib +0 -1
  61. data/test/bib/03_string.bib +0 -25
  62. data/test/bib/04_string_replacement.bib +0 -16
  63. data/test/test_comment.rb +0 -21
  64. data/test/test_entry.rb +0 -98
  65. data/test/test_preamble.rb +0 -39
  66. data/test/test_string.rb +0 -97
  67. data/test/test_utilities.rb +0 -36
@@ -0,0 +1,162 @@
1
+ #--
2
+ # BibTeX-Ruby
3
+ # Copyright (C) 2010-2011 Sylvester Keil <sylvester.keil.or.at>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #++
18
+
19
+ require 'forwardable'
20
+
21
+ module BibTeX
22
+
23
+ class Names < Value
24
+ include Enumerable
25
+
26
+ def_delegators :@tokens, :each, :sort
27
+
28
+ def self.parse(string)
29
+ Names.new(NameParser.new.parse(string))
30
+ end
31
+
32
+ def initialize(*arguments)
33
+ @tokens = []
34
+ arguments.flatten.compact.each do |argument|
35
+ add(argument)
36
+ end
37
+ end
38
+
39
+ def replace(*arguments); self; end
40
+
41
+ def join; self; end
42
+
43
+ def value
44
+ @tokens.join(' and ')
45
+ end
46
+
47
+ def to_s(options = {})
48
+ return value unless options.has_key?(:quotes)
49
+ *q = options[:quotes]
50
+ [q[0], value, q[-1]].compact.join
51
+ end
52
+
53
+ def name?; true; end
54
+ def numeric?; false; end
55
+ def atomic?; true; end
56
+
57
+ alias :names? :name?
58
+ alias :symbol? :numeric?
59
+
60
+ def to_name; self; end
61
+
62
+ def add(name)
63
+ case
64
+ when name.is_a?(Name)
65
+ @tokens << name
66
+ when name.respond_to?(:to_s)
67
+ @tokens += Names.parse(name.to_s)
68
+ else
69
+ raise ArgumentError, "failed to add #{name.inspect}: not a name."
70
+ end
71
+ self
72
+ end
73
+
74
+ alias :<< :add
75
+ alias :push :add
76
+
77
+ def <=>(other)
78
+ other.respond_to?(:to_a) ? to_a <=> other.to_a : super
79
+ end
80
+
81
+ end
82
+
83
+ class Name < Struct.new(:first, :last, :prefix, :suffix)
84
+ extend Forwardable
85
+ include Comparable
86
+
87
+ def_delegators :to_s, :empty?, :=~, :match, :length, :intern, :to_sym, :end_with?, :start_with?, :include?, :upcase, :downcase, :reverse, :chop, :chomp, :rstrip, :gsub, :sub, :size, :strip, :succ, :to_str, :split, :each_byte, :each_char, :each_line
88
+
89
+ class << self
90
+ def parse(string)
91
+ [NameParser.new.parse(string)].flatten[0]
92
+ end
93
+
94
+ # Returns true if thing looks like a name.
95
+ # Actually converts thing to a string and tries to parse it.
96
+ def looks_like?(thing)
97
+ thing.respond_to?(:to_s) && [Name.new.parse(string)].flatten.compact.empty?
98
+ end
99
+ end
100
+
101
+ def initialize(attributes = {})
102
+ attributes.each do |key,value|
103
+ send("#{key}=", value) if respond_to?(key)
104
+ end
105
+ end
106
+
107
+ def initalize_copy(other)
108
+ each_pair { |k,v| self[k] = v }
109
+ end
110
+
111
+ def blank?
112
+ to_a.compact.empty?
113
+ end
114
+
115
+ def display_order
116
+ [prefix, last, first, suffix].compact.join(' ')
117
+ end
118
+
119
+ alias :display :display_order
120
+
121
+ def sort_order
122
+ [[prefix,last].compact.join(' '), suffix, first].compact.join(', ')
123
+ end
124
+
125
+ alias :to_s :sort_order
126
+
127
+ def <=>(other)
128
+ other.is_a?(Name) ? [last, prefix, first, suffix].compact.join(' ') <=> [other.last, other.prefix, other.first, other.suffix].compact.join(' ') : super
129
+ end
130
+
131
+ def to_hash
132
+ Hash[each_pair.to_a]
133
+ end
134
+
135
+ [:strip!, :upcase!, :downcase!, :sub!, :gsub!, :chop!, :chomp!, :rstrip!].each do |method_id|
136
+ define_method(method_id) do |*arguments, &block|
137
+ each do |part|
138
+ part.send(method_id, *arguments, &block)
139
+ end
140
+ self
141
+ end
142
+ end
143
+
144
+ def to_citeproc
145
+ {
146
+ 'family' => [prefix, last].compact.join(' '),
147
+ 'given' => [first, suffix].compact.join(', '),
148
+ 'parse-names' => true
149
+ }
150
+ end
151
+
152
+ alias :family :last
153
+ alias :family= :last=
154
+ alias :given :first
155
+ alias :given= :first=
156
+ alias :jr :suffix
157
+ alias :jr= :suffix=
158
+ alias :von :prefix
159
+ alias :von= :prefix=
160
+
161
+ end
162
+ end
@@ -0,0 +1,196 @@
1
+ #--
2
+ # BibTeX-Ruby
3
+ # Copyright (C) 2010-2011 Sylvester Keil <http://sylvester.keil.or.at>
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, either version 3 of the License, or
8
+ # (at your option) any later version.
9
+ #
10
+ # This program is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ # GNU General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU General Public License
16
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ #++
18
+ #
19
+ # A BibTeX grammar for the parser generator +racc+
20
+ #
21
+
22
+ # -*- racc -*-
23
+
24
+ class BibTeX::NameParser
25
+
26
+ token COMMA UWORD LWORD PWORD AND ERROR
27
+
28
+ expect 0
29
+
30
+ rule
31
+
32
+ result : { result = [] }
33
+ | names { result = val[0] }
34
+
35
+ names : name { result = [val[0]] }
36
+ | names AND name { result << val[2] }
37
+
38
+ name : last { result = Name.new(:von => val[0][0], :last => val[0][1]) }
39
+ | u_words last { result = Name.new(:first => val[0], :von => val[1][0], :last => val[1][1]) }
40
+ | sort COMMA first { result = Name.new(:von => val[0][0], :last => val[0][1], :jr => val[2][0], :first => val[2][1]) }
41
+
42
+ sort : u_words { result = [nil,val[0]]}
43
+ | LWORD { result = [nil,val[0]]}
44
+ | von LWORD { result = [val[0],val[1]]}
45
+ | von u_words { result = [val[0],val[1]]}
46
+
47
+ last : word { result = [nil,val[0]]}
48
+ | von LWORD { result = [val[0],val[1]]}
49
+ | von u_words { result = [val[0],val[1]]}
50
+
51
+ first : opt_words { result = [nil,val[0]] }
52
+ | opt_words COMMA opt_words { result = [val[0],val[2]] }
53
+
54
+ u_words : u_word { result = val[0] }
55
+ | u_words u_word { result = val[0,2].join(' ') }
56
+
57
+ u_word : UWORD { result = val[0] }
58
+ | PWORD { result = val[0] }
59
+
60
+ von : LWORD { result = val[0] }
61
+ | von u_words LWORD { result = val[0,3].join(' ') }
62
+ | von LWORD { result = val[0,2].join(' ') }
63
+
64
+ words : word { result = val[0] }
65
+ | words word { result = val[0,2].join(' ') }
66
+
67
+ opt_words : { result = nil }
68
+ | words { result = val[0] }
69
+
70
+ word : LWORD { result = val[0] }
71
+ | UWORD { result = val[0] }
72
+ | PWORD { result = val[0] }
73
+
74
+ end
75
+
76
+ ---- header
77
+ require 'strscan'
78
+
79
+ ---- inner
80
+
81
+ def initialize(options = {})
82
+ self.options.merge!(options)
83
+ end
84
+
85
+ def options
86
+ @options ||= { :debug => ENV['DEBUG'] == true }
87
+ end
88
+
89
+ def parse(input)
90
+ @yydebug = options[:debug]
91
+ scan(input)
92
+ do_parse
93
+ end
94
+
95
+ def next_token
96
+ @stack.shift
97
+ end
98
+
99
+ def on_error(tid, val, vstack)
100
+ BibTeX.log.error("Failed to parse BibTeX Name on value %s (%s) %s" % [val.inspect, token_to_str(tid) || '?', vstack.inspect])
101
+ end
102
+
103
+ def scan(input)
104
+ @src = StringScanner.new(input)
105
+ @brace_level = 0
106
+ @stack = []
107
+ @word = [:PWORD,'']
108
+ do_scan
109
+ end
110
+
111
+ private
112
+
113
+ def do_scan
114
+ until @src.eos?
115
+ case
116
+ when @src.scan(/,?\s+and\s+/io)
117
+ push_word
118
+ @stack.push([:AND,@src.matched])
119
+
120
+ when @src.scan(/[\t\r\n\s]+/o)
121
+ push_word
122
+
123
+ when @src.scan(/,/o)
124
+ push_word
125
+ @stack.push([:COMMA,@src.matched])
126
+
127
+ when @src.scan(/[[:lower:]][^\t\r\n\s\{\}\d\\,]*/o)
128
+ is_lowercase
129
+ @word[1] << @src.matched
130
+
131
+ when @src.scan(/[[:upper:]][^\t\r\n\s\{\}\d\\,]*/o)
132
+ is_uppercase
133
+ @word[1] << @src.matched
134
+
135
+ when @src.scan(/(\d|\\.)+/o)
136
+ @word[1] << @src.matched
137
+
138
+ when @src.scan(/\{/o)
139
+ scan_literal(@src.matched)
140
+
141
+ when @src.scan(/\}/o)
142
+ error_unbalanced
143
+
144
+ when @src.scan(/./o)
145
+ @word[1] << @src.matched
146
+ end
147
+ end
148
+
149
+ push_word
150
+ @stack
151
+ end
152
+
153
+ def push_word
154
+ unless @word[1].empty?
155
+ @stack.push(@word)
156
+ @word = [:PWORD,'']
157
+ end
158
+ end
159
+
160
+ def is_lowercase
161
+ @word[0] = :LWORD if @word[0] == :PWORD
162
+ end
163
+
164
+ def is_uppercase
165
+ @word[0] = :UWORD if @word[0] == :PWORD
166
+ end
167
+
168
+ def scan_literal(content = '')
169
+ @brace_level += 1
170
+ content << @src.scan_until(/[^\\][\{\}]/o).to_s # TODO accept empty braces {}
171
+ case @src.matched
172
+ when /\{/
173
+ scan_braced_expression(content)
174
+ when /\}/
175
+ @brace_level -= 1
176
+ if @brace_level >= 0
177
+ @word[1] << content
178
+ else
179
+ error_unbalanced
180
+ end
181
+ else
182
+ error_unbalanced
183
+ end
184
+ end
185
+
186
+ def error_unexpected
187
+ @stack.push [:ERROR,@src.matched]
188
+ BibTeX.log.warn("NameParser: unexpected token `#{@src.matched}' at position #{@src.pos}; brace level #{@brace_level}.")
189
+ end
190
+
191
+ def error_unbalanced
192
+ @stack.push [:ERROR,'}']
193
+ BibTeX.log.warn("NameParser: unbalanced braces at position #{@src.pos}; brace level #{@brace_level}.")
194
+ end
195
+
196
+ # -*- racc -*-
@@ -7,7 +7,7 @@ rule 2 bibliography: objects
7
7
  rule 3 objects: object
8
8
  rule 4 objects: objects object
9
9
  rule 5 object: AT at_object
10
- rule 6 object: META_COMMENT
10
+ rule 6 object: META_CONTENT
11
11
  rule 7 object: ERROR
12
12
  rule 8 at_object: comment
13
13
  rule 9 at_object: string
@@ -106,7 +106,7 @@ rule 33 value: LBRACE content RBRACE
106
106
  ERROR (6) 7
107
107
  EQ (7) 17 30
108
108
  LBRACE (8) 12 15 16 25 33
109
- META_COMMENT (9) 6
109
+ META_CONTENT (9) 6
110
110
  NAME (10) 17 20 25 26 30
111
111
  NUMBER (11) 27 32
112
112
  PREAMBLE (12) 15
@@ -122,7 +122,7 @@ state 0
122
122
 
123
123
  AT shift, and go to state 4
124
124
  ERROR shift, and go to state 6
125
- META_COMMENT shift, and go to state 5
125
+ META_CONTENT shift, and go to state 5
126
126
  $default reduce using rule 1 (bibliography)
127
127
 
128
128
  bibliography go to state 1
@@ -142,7 +142,7 @@ state 2
142
142
 
143
143
  AT shift, and go to state 4
144
144
  ERROR shift, and go to state 6
145
- META_COMMENT shift, and go to state 5
145
+ META_CONTENT shift, and go to state 5
146
146
  $default reduce using rule 2 (bibliography)
147
147
 
148
148
  object go to state 8
@@ -172,7 +172,7 @@ state 4
172
172
 
173
173
  state 5
174
174
 
175
- 6) object : META_COMMENT _
175
+ 6) object : META_CONTENT _
176
176
 
177
177
  $default reduce using rule 6 (object)
178
178
 
@@ -15,32 +15,35 @@ module_eval(<<'...end bibtex.y/module_eval...', 'bibtex.y', 89)
15
15
 
16
16
  attr_reader :lexer
17
17
 
18
- def initialize(options={})
19
- @options = options
20
- @options[:include] ||= [:errors]
21
- @lexer = Lexer.new(options)
18
+ def initialize(options = {})
19
+ self.options.merge!(options)
20
+ @lexer = Lexer.new(@options)
22
21
  end
23
22
 
23
+ def options
24
+ @options ||= { :include => [:errors], :debug => ENV['DEBUG'] == true }
25
+ end
26
+
24
27
  def parse(input)
25
- @yydebug = self.debug?
28
+ @yydebug = debug?
26
29
 
27
- self.lexer.src = input
28
- self.lexer.analyse
30
+ @lexer.src = input
31
+ @lexer.analyse
29
32
 
30
33
  do_parse
31
34
  end
32
35
 
33
36
  def next_token
34
- token = self.lexer.next_token
37
+ token = @lexer.next_token
35
38
  if token[0] == :ERROR
36
- self.include_errors? ? token : next_token
39
+ include_errors? ? token : next_token
37
40
  else
38
41
  [token[0],token[1][0]]
39
42
  end
40
43
  end
41
44
 
42
45
  def debug?
43
- @options[:debug] == true || ENV['DEBUG'] == true
46
+ @options[:debug] == true
44
47
  end
45
48
 
46
49
  def include_errors?
@@ -156,7 +159,7 @@ racc_token_table = {
156
159
  :ERROR => 6,
157
160
  :EQ => 7,
158
161
  :LBRACE => 8,
159
- :META_COMMENT => 9,
162
+ :META_CONTENT => 9,
160
163
  :NAME => 10,
161
164
  :NUMBER => 11,
162
165
  :PREAMBLE => 12,
@@ -195,7 +198,7 @@ Racc_token_to_s_table = [
195
198
  "ERROR",
196
199
  "EQ",
197
200
  "LBRACE",
198
- "META_COMMENT",
201
+ "META_CONTENT",
199
202
  "NAME",
200
203
  "NUMBER",
201
204
  "PREAMBLE",
@@ -222,7 +225,7 @@ Racc_token_to_s_table = [
222
225
  "assignment",
223
226
  "value" ]
224
227
 
225
- Racc_debug_parser = true
228
+ Racc_debug_parser = false
226
229
 
227
230
  ##### State transition tables end #####
228
231
 
@@ -230,7 +233,7 @@ Racc_debug_parser = true
230
233
 
231
234
  module_eval(<<'.,.,', 'bibtex.y', 32)
232
235
  def _reduce_1(val, _values, result)
233
- result = Bibliography.new
236
+ result = BibTeX::Bibliography.new
234
237
  result
235
238
  end
236
239
  .,.,
@@ -244,7 +247,7 @@ module_eval(<<'.,.,', 'bibtex.y', 33)
244
247
 
245
248
  module_eval(<<'.,.,', 'bibtex.y', 35)
246
249
  def _reduce_3(val, _values, result)
247
- result = Bibliography.new << val[0]
250
+ result = BibTeX::Bibliography.new << val[0]
248
251
  result
249
252
  end
250
253
  .,.,
@@ -265,7 +268,7 @@ module_eval(<<'.,.,', 'bibtex.y', 38)
265
268
 
266
269
  module_eval(<<'.,.,', 'bibtex.y', 39)
267
270
  def _reduce_6(val, _values, result)
268
- result = BibTeX::MetaComment.new(val[0])
271
+ result = BibTeX::MetaContent.new(val[0])
269
272
  result
270
273
  end
271
274
  .,.,