bibtex-ruby 1.0.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.

@@ -0,0 +1,129 @@
1
+ #--
2
+ # BibTeX-Ruby
3
+ # Copyright (C) 2010 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::Parser
25
+
26
+ token AT COMMA COMMENT CONTENT ERROR EQ LBRACE META_COMMENT
27
+ NAME NUMBER PREAMBLE RBRACE SHARP STRING STRING_LITERAL
28
+
29
+ expect 0
30
+
31
+ rule
32
+
33
+ bibliography : /* empty */ { result = Bibliography.new }
34
+ | objects { result = val[0] }
35
+
36
+ objects : object { result = Bibliography.new << val[0] }
37
+ | objects object { result << val[1] }
38
+
39
+ object : AT at_object { result = val[1] }
40
+ | META_COMMENT { result = BibTeX::MetaComment.new(val[0]) }
41
+ | ERROR { result = BibTeX::Error.new(val[0]) }
42
+
43
+ at_object : comment { result = val[0] }
44
+ | string { result = val[0] }
45
+ | preamble { result = val[0] }
46
+ | entry { result = val[0] }
47
+
48
+ comment : COMMENT LBRACE content RBRACE { result = BibTeX::Comment.new(val[2]) }
49
+
50
+ content : /* empty */ { result = '' }
51
+ | CONTENT { result = val[0] }
52
+
53
+ preamble : PREAMBLE LBRACE string_value RBRACE { result = BibTeX::Preamble.new(val[2]) }
54
+
55
+ string : STRING LBRACE string_assignment RBRACE { result = BibTeX::String.new(val[2][0],val[2][1]); }
56
+
57
+ string_assignment : NAME EQ string_value { result = [val[0].downcase.to_sym, val[2]] }
58
+
59
+ string_value : string_literal { result = [val[0]] }
60
+ | string_value SHARP string_literal { result << val[2] }
61
+
62
+ string_literal : NAME { result = val[0].downcase.to_sym }
63
+ | STRING_LITERAL { result = val[0] }
64
+
65
+ entry : entry_head assignments RBRACE { result = val[0] << val[1] }
66
+ | entry_head assignments COMMA RBRACE { result = val[0] << val[1] }
67
+ | entry_head RBRACE { result = val[0] }
68
+
69
+ entry_head : NAME LBRACE key COMMA { result = BibTeX::Entry.new(val[0].downcase.to_sym,val[2]) }
70
+
71
+ key : NAME { result = val[0] }
72
+ | NUMBER { result = val[0] }
73
+
74
+ assignments : assignment { result = val[0] }
75
+ | assignments COMMA assignment { result.merge!(val[2]) }
76
+
77
+ assignment : NAME EQ value { result = { val[0].downcase.to_sym => val[2] } }
78
+
79
+ value : string_value { result = val[0] }
80
+ | NUMBER { result = val[0] }
81
+ | LBRACE content RBRACE { result = val[1] }
82
+
83
+ end
84
+
85
+ ---- header
86
+ require 'bibtex/lexer'
87
+
88
+ ---- inner
89
+
90
+ attr_reader :lexer
91
+
92
+ def initialize(options={})
93
+ @options = options
94
+ @options[:include] ||= [:errors]
95
+ @lexer = Lexer.new(options)
96
+ end
97
+
98
+ def parse(input)
99
+ @yydebug = self.debug?
100
+
101
+ self.lexer.src = input
102
+ self.lexer.analyse
103
+
104
+ do_parse
105
+ end
106
+
107
+ def next_token
108
+ token = self.lexer.next_token
109
+ if token[0] == :ERROR
110
+ self.include_errors? ? token : next_token
111
+ else
112
+ [token[0],token[1][0]]
113
+ end
114
+ end
115
+
116
+ def debug?
117
+ @options[:debug] == true || ENV['DEBUG'] == true
118
+ end
119
+
120
+ def include_errors?
121
+ @options[:include].include?(:errors)
122
+ end
123
+
124
+ def on_error(tid, val, vstack)
125
+ #raise(ParseError, "Failed to parse BibTeX on value %s (%s) %s" % [val.inspect, token_to_str(tid) || '?', vstack.inspect])
126
+ Log.error("Failed to parse BibTeX on value %s (%s) %s" % [val.inspect, token_to_str(tid) || '?', vstack.inspect])
127
+ end
128
+
129
+ # -*- racc -*-
@@ -0,0 +1,262 @@
1
+ #--
2
+ # BibTeX-Ruby
3
+ # Copyright (C) 2010 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 'json'
20
+ require 'rexml/document'
21
+ require 'yaml'
22
+
23
+ module BibTeX
24
+
25
+ #
26
+ # The base class for BibTeX objects.
27
+ #
28
+ class Element
29
+
30
+ attr_reader :bibliography
31
+
32
+ def initialize
33
+ @bibliography = nil
34
+ end
35
+
36
+ # Returns a string containing the object's content.
37
+ def content
38
+ ""
39
+ end
40
+
41
+ # Returns a string representation of the object.
42
+ def to_s
43
+ self.content
44
+ end
45
+
46
+ def to_hash
47
+ { self.class.name.downcase => content }
48
+ end
49
+
50
+ def to_yaml
51
+ self.to_hash.to_yaml
52
+ end
53
+
54
+ def to_json
55
+ self.to_hash.to_json
56
+ end
57
+
58
+ def to_xml
59
+ xml = REXML::Element.new(self.class.name.downcase)
60
+ xml.text = self.content
61
+ xml
62
+ end
63
+
64
+ # Called when the element was added to a bibliography.
65
+ def added_to_bibliography(bibliography)
66
+ @bibliography = bibliography
67
+ self
68
+ end
69
+
70
+ # Called when the element was removed from a bibliography.
71
+ def removed_from_bibliography(bibliography)
72
+ @bibliography = nil
73
+ self
74
+ end
75
+ end
76
+
77
+
78
+ #
79
+ # Represents a @string object.
80
+ #
81
+ # In BibTeX @string objects contain a single string constant
82
+ # assignment. For example, @string{ foo = "bar" } defines the
83
+ # constant `foo'; this constant can be used (using BibTeX's
84
+ # string concatenation syntax) in susbsequent
85
+ # @string and @preamble objects, as well as in field values
86
+ # of regular entries.
87
+ #
88
+ class String < Element
89
+ attr_reader :key, :value
90
+
91
+ # Creates a new instance.
92
+ def initialize(key=nil,value=nil)
93
+ self.key = key.to_sym unless key.nil?
94
+ self.value = value unless value.nil?
95
+ end
96
+
97
+ # Sets the string's key (i.e., the name of the constant)
98
+ def key=(key)
99
+ raise(ArgumentError, "BibTeX::String key must be of type Symbol; was: #{key.class.name}.") unless key.kind_of?(Symbol)
100
+ @key = key
101
+ end
102
+
103
+ # Sets the string's value (i.e., the string literal defined by the constant)
104
+ def value=(value)
105
+ raise(ArgumentError, "BibTeX::String value must be of type Array, Symbol, or String; was: #{value.class.name}.") unless [Array,::String,Symbol].map { |k| value.kind_of?(k) }.inject { |sum,n| sum || n }
106
+ @value = value.kind_of?(Array) ? value : [value]
107
+ end
108
+
109
+ # Replaces all constants in this string's value which are defined in +hsh+.
110
+ # Returns the new value (the @string object itself remains unchanged).
111
+ #
112
+ # call-seq:
113
+ # s.to_s
114
+ # => "@string{ foobar = foo # "bar"}"
115
+ # s.replace({:foo => 'foo'})
116
+ # => ["foo","bar"]
117
+ # s.to_s
118
+ # => "@string{ foobar = foo # "bar"}"
119
+ def replace(hsh)
120
+ StringReplacement.replace(@value,hsh)
121
+ end
122
+
123
+ # Replaces all constants in this string's value which are defined in +hsh+.
124
+ # Returns the new value (the @string object itself is changed as well).
125
+ #
126
+ # call-seq:
127
+ # s.to_s
128
+ # => "@string{ foobar = foo # "bar"}"
129
+ # s.replace({:foo => 'foo'})
130
+ # => ["foo","bar"]
131
+ # s.to_s
132
+ # => "@string{ foobar = "foo" # "bar"}"
133
+ def replace!(hsh)
134
+ @value = replace(hsh)
135
+ @bibliography.strings[@key] = value unless @bibliography.nil?
136
+ end
137
+
138
+ # Adds either a string constant or literal to the current value. The
139
+ # values will be concatenated using the `#' symbol.
140
+ def <<(value)
141
+ raise(ArgumentError, "BibTeX::String value can contain only instances of Symbol or String; was: #{value.class.name}.") unless [::String,Symbol].map { |k| value.kind_of?(k) }.inject { |sum,n| sum || n }
142
+ @value << value
143
+ end
144
+
145
+ # Called when the element was added to a bibliography.
146
+ def added_to_bibliography(bibliography)
147
+ super(bibliography)
148
+ bibliography.strings[@key] = @value
149
+ self
150
+ end
151
+
152
+ # Called when the element was removed from a bibliography.
153
+ def removed_from_bibliography(bibliography)
154
+ super(bibliography)
155
+ bibliography.strings[@key] = nil
156
+ self
157
+ end
158
+
159
+ # Returns a string representation of the @string's content.
160
+ def content
161
+ [@key.to_s,' = ',StringReplacement.to_s(@value)].join
162
+ end
163
+
164
+ # Returns a string representation of the @string object.
165
+ def to_s
166
+ ['@string{ ',content,'}'].join
167
+ end
168
+
169
+ def to_hash
170
+ { 'string' => { @key.to_s => StringReplacement.to_s(@value) } }
171
+ end
172
+
173
+ def to_xml
174
+ xml = REXML::Element.new('string')
175
+ key = REXML::Element.new('key')
176
+ val = REXML::Element.new('value')
177
+ key.text = @key.to_s
178
+ val.text = @value.to_s
179
+ xml
180
+ end
181
+ end
182
+
183
+ #
184
+ # Represents a @preamble object.
185
+ #
186
+ # In BibTeX an @preamble object contains a single string literal,
187
+ # a single constant, or a concatenation of string literals and
188
+ # constants.
189
+ class Preamble < Element
190
+ attr_reader :value
191
+
192
+ # Creates a new instance.
193
+ def initialize(value=[])
194
+ self.value = value
195
+ end
196
+
197
+ def value=(value)
198
+ raise(ArgumentError, "BibTeX::Preamble value must be of type Array, Symbol, or String; was: #{value.class.name}.") unless [Array,::String,Symbol].map { |k| value.kind_of?(k) }.inject { |sum,n| sum || n }
199
+ @value = value.kind_of?(Array) ? value : [value]
200
+ end
201
+
202
+ def replace(hsh)
203
+ StringReplacement.replace(@value,hsh)
204
+ end
205
+
206
+ def replace!(hsh)
207
+ @value = replace(hsh)
208
+ @bibliography.strings[@key] = @value unless @bibliography.nil?
209
+ end
210
+
211
+ # Returns a string representation of the @preamble's content.
212
+ def content
213
+ StringReplacement.to_s(@value)
214
+ end
215
+
216
+ # Returns a string representation of the @preamble object
217
+ def to_s
218
+ ['@preamble{ ',content,'}'].join
219
+ end
220
+
221
+ def to_hash
222
+ { 'preamble' => StringReplacement.to_s(@value) }
223
+ end
224
+ end
225
+
226
+ # Represents a @comment object.
227
+ class Comment < Element
228
+
229
+ def initialize(content='')
230
+ self.content = content
231
+ end
232
+
233
+ def content=(content)
234
+ raise(ArgumentError, "BibTeX::#{self.class.name} content must be of type String; was: #{content.class.name}.") unless content.kind_of?(::String)
235
+ @content = content
236
+ end
237
+
238
+ def content
239
+ @content
240
+ end
241
+
242
+ def to_s
243
+ ['@comment{ ',content,'}'].join
244
+ end
245
+
246
+ end
247
+
248
+ # Represents text in a `.bib' file, but outside of an
249
+ # actual BibTeX object; typically, such text is treated
250
+ # as a comment and is ignored by the parser.
251
+ # BibTeX-Ruby offers this class to allows for
252
+ # post-processing of this type of `meta' comment. If you
253
+ # want the parser to include +MetaComment+ objects, you
254
+ # need to add +:meta_comments+ to the parser's +:include+
255
+ # option.
256
+ class MetaComment < Comment
257
+ def to_s
258
+ @content
259
+ end
260
+ end
261
+
262
+ end
@@ -0,0 +1,154 @@
1
+ #--
2
+ # BibTeX-Ruby
3
+ # Copyright (C) 2010 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
+ module BibTeX
20
+ #
21
+ # Represents a regular BibTeX entry.
22
+ #
23
+ class Entry < Element
24
+ attr_reader :key, :type, :fields
25
+
26
+ # Hash containing the required fields of the standard entry types
27
+ @@RequiredFields = Hash.new([])
28
+ @@RequiredFields.merge!({
29
+ :article => [:author,:title,:journal,:year],
30
+ :book => [[:author,:editor],:title,:publisher,:year],
31
+ :booklet => [:title],
32
+ :conference => [:author,:title,:booktitle,:year],
33
+ :inbook => [[:author,:editor],:title,[:chapter,:pages],:publisher,:year],
34
+ :incollection => [:author,:title,:booktitle,:publisher,:year],
35
+ :inproceedings => [:author,:title,:booktitle,:year],
36
+ :manual => [:title],
37
+ :mastersthesis => [:author,:title,:school,:year],
38
+ :misc => [],
39
+ :phdthesis => [:author,:title,:school,:year],
40
+ :proceedings => [:title,:year],
41
+ :techreport => [:author,:title,:institution,:year],
42
+ :unpublished => [:author,:title,:note]
43
+ })
44
+
45
+ # Creates a new instance of a given +type+ (e.g., :article, :book, etc.)
46
+ # identified by a +key+.
47
+ def initialize(type=nil, key=nil)
48
+ self.key = key.to_s unless key.nil?
49
+ self.type = type.to_sym unless type.nil?
50
+ @fields = {}
51
+ end
52
+
53
+ # Sets the key of the entry
54
+ def key=(key)
55
+ raise(ArgumentError, "BibTeX::Entry key must be of type String; was: #{key.class.name}.") unless key.kind_of?(::String)
56
+ @key = key
57
+ end
58
+
59
+ # Sets the type of the entry.
60
+ def type=(type)
61
+ raise(ArgumentError, "BibTeX::Entry type must be of type Symbol; was: #{type.class.name}.") unless type.kind_of?(Symbol)
62
+ @type = type
63
+ end
64
+
65
+ # Returns the value of the field with the given name.
66
+ def [](name)
67
+ @fields[name.to_sym]
68
+ end
69
+
70
+ # Adds a new field (name-value pair) to the entry.
71
+ # Returns the new value.
72
+ def []=(name,value)
73
+ add(name,value)
74
+ end
75
+
76
+ # Adds a new field (name-value pair) to the entry.
77
+ # Returns the new value.
78
+ def add(name,value)
79
+ raise(ArgumentError, "BibTeX::Entry field name must be of type Symbol; was: #{name.class.name}.") unless name.kind_of?(Symbol)
80
+ raise(ArgumentError, "BibTeX::Entry field value must be of type Array, Symbol, or String; was: #{value.class.name}.") unless [Array,::String,Symbol].map { |k| value.kind_of?(k) }.inject { |sum,n| sum || n }
81
+ @fields[name] = value.kind_of?(Array) ? value : [value]
82
+ end
83
+
84
+ # Removes the field with a given name from the entry.
85
+ # Returns the value of the deleted field; nil if the field was not set.
86
+ def delete(name)
87
+ @fields.delete(name.to_sym)
88
+ end
89
+
90
+ # Adds all the fields contained in a given hash to the entry.
91
+ def <<(fields)
92
+ raise(ArgumentError, "BibTeX::Entry fields must be of type Hash; was: #{fields.class.name}.") unless fields.kind_of?(Hash)
93
+ fields.each { |n,v| add(n,v) }
94
+ self
95
+ end
96
+
97
+ # Returns true if the entry currently contains no field.
98
+ def empty?
99
+ @fields.empty?
100
+ end
101
+
102
+ # Returns false if the entry is one of the standard entry types and does not have
103
+ # definitions of all the required fields for that type.
104
+ def valid?
105
+ !@@RequiredFields[@type].map { |f|
106
+ f.kind_of?(Array) ? !(f & @fields.keys).empty? : !@fields[f].nil?
107
+ }.include?(false)
108
+ end
109
+
110
+ # Called when the element was added to a bibliography.
111
+ def added_to_bibliography(bibliography)
112
+ super(bibliography)
113
+ bibliography.entries[@key] = self
114
+ self
115
+ end
116
+
117
+ # Called when the element was removed from a bibliography.
118
+ def removed_from_bibliography(bibliography)
119
+ super(bibliography)
120
+ bibliography.entries[@key] = nil
121
+ self
122
+ end
123
+
124
+ # Replaces all constants in this entry's field values which are defined in +hsh+.
125
+ def replace!(hsh)
126
+ @fields.keys.each { |k| @fields[k] = StringReplacement.replace(@fields[k],hsh) }
127
+ end
128
+
129
+ # Returns a string of all the entry's fields.
130
+ def content
131
+ @fields.keys.map { |k| "#{k} = #{StringReplacement.to_s(@fields[k], :delimiter => ['{','}'])}" }.join(",\n")
132
+ end
133
+
134
+ # Returns a string representation of the entry.
135
+ def to_s
136
+ ["@#{type}{#{key},",content.gsub(/^/,' '),"}\n"].join("\n")
137
+ end
138
+
139
+ def to_hash
140
+ { @type.to_s => @fields.keys.map { |k| { k.to_s => StringReplacement.to_s(@fields[k], :delimiter => ['{','}']) } }.inject({ 'key' => @key }) { |sum,n| sum.merge(n) } }.to_yaml
141
+ end
142
+
143
+ def to_xml
144
+ xml = REXML::Element.new(@type.to_s)
145
+ xml.attributes['key'] = @key
146
+ @fields.each do |k,v|
147
+ e = REXML::Element.new(k.to_s)
148
+ e.text = StringReplacement.to_s(v, :delimiter => ['',''])
149
+ xml.add_element(e)
150
+ end
151
+ xml
152
+ end
153
+ end
154
+ end