bibtex-ruby 1.2.1 → 1.3.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.

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