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

@@ -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