Skip to content

Commit

Permalink
Code refactor and README update
Browse files Browse the repository at this point in the history
  • Loading branch information
Azzaare committed Jun 20, 2021
1 parent 0bf3fb3 commit 897c563
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 99 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@

This package provides an internal format to translate from/to other bibliographic format.

All entries depend of an abstract supertype `AbstractEntry`.
All entries depend on an abstract super type `AbstractEntry`.
One generic entry `GenericEntry` is available to make entries without any specific rules.

Currently, only one set of entries following the BibTeX rules is available. *Required* and *optional* BibTeX fields are checked by the constructor.

Pull Requests to add more entries (or update the BibTeX rules) are welcome.

Discussions are welcome either on this GitHub repository or on the `#modern-academics` channel of [Humans of Julia](https://humansofjulia.org/) (to join the Discord server, please click the `chat` badge above).

## Packages using BibInternal.jl
- [BibParser.jl](https://github.com/Humans-of-Julia/BibParser.jl) : A package to parse bibliography files
- BibParser.BibTEX: an Automa.jl based BibTeX parser
- [Bibliography.jl](https://github.com/Humans-of-Julia/Bibliography.jl) : A wrapper package to translate from/to different bibliographic formats (currently BibTeX and some web export)
- [Bibliography.jl](https://github.com/Humans-of-Julia/Bibliography.jl) : A wrapper package to translate from/to different bibliographic formats such as BibTeX, [StaticWebPages.jl](https://github.com/Humans-of-Julia/StaticWebPages.jl), and [DocumenterCitations.jl](https://github.com/ali-ramadhan/DocumenterCitations.jl).
5 changes: 0 additions & 5 deletions src/BibInternal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ Abstract entry supertype.
"""
abstract type AbstractEntry end

"""
`Fields = Dict{String, String}`. Stores the fields `name => value` of an entry.
"""
Fields = Dict{String,String}

# Includes
include("constant.jl")
include("utilities.jl")
Expand Down
21 changes: 7 additions & 14 deletions src/bibtex.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Required = Union{String,Tuple{String,String}}
const Required = Union{String,Tuple{String,String}}

"""
const rules = Dict([
Expand Down Expand Up @@ -40,10 +40,9 @@ const rules = Dict{String,Vector{Required}}([
check_entry(fields::Fields)
Check the validity of the fields of a BibTeX entry.
"""
function check_entry(
fields::Fields
)
function check_entry(fields)
errors = Vector{String}()

for t_field in rules[get(fields, "_type", "misc")]
at_least_one = false
if typeof(t_field) == Tuple{String,String}
Expand All @@ -57,26 +56,20 @@ function check_entry(
push!(errors, "{" * foldl((x, y) -> "$x$y", t_field; init="")[2:end] * "}")
end
else
if get(fields, t_field, "") == ""
push!(errors, t_field)
end
get(fields, t_field, "") == "" && push!(errors, t_field)
end
end

return errors
end

"""
make_bibtex_entry(id::String, fields::Fields)
Make an entry if the entry follows the BibTeX guidelines. Throw an error otherwise.
"""
function make_bibtex_entry(
id::String,
fields::Fields
)
function make_bibtex_entry(id, fields)
# @info id fields
if "eprint" keys(fields)
fields["_type"] = "eprint"
end
"eprint" keys(fields) && (fields["_type"] = "eprint")
fields = Dict(lowercase(k) => v for (k, v) in fields) # lowercase tag names
errors = check_entry(fields)
if length(errors) > 0
Expand Down
5 changes: 5 additions & 0 deletions src/constant.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
"""
`Fields = Dict{String, String}`. Stores the fields `name => value` of an entry.
"""
const Fields = Dict{String,String}

"""
const entries = [
:article,
Expand Down
123 changes: 53 additions & 70 deletions src/entry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Names = Vector{Name}
Name(str::String)
Decompose without ambiguities a name as `particle` (optional) `last`, `junior` (optional), `first` `middle` (optional) based on BibTeX possible input. As for BibTeX, the decomposition of a name in the form of `first` `last` is also possible, but ambiguities can occur.
"""
function Name(str::String)
function Name(str)
subnames = map(strip, split(str, r"[\n\r\t ]*,[\n\r\t ]*"; keepempty=false))

# subnames containers
Expand All @@ -46,77 +46,57 @@ function Name(str::String)
mark_in += 1
for s in aux[2:end-1]
mark_in += 1
if islowercase(s[1])
break;
end
islowercase(s[1]) && break
middle *= " $s"
end
for s in reverse(aux[mark_in:mark_out])
if islowercase(s[1])
break;
end
islowercase(s[1]) && break
mark_out -= 1
last = "$s " * last
end
for s in aux[mark_in:mark_out]
particle *= " $s"
end
foreach(s -> particle *= " $s", aux[mark_in:mark_out])
end
end

# BibTeX form 2: von Last, First Second
if length(subnames) == 2
aux = split(subnames[1], r"[\n\r ]+") # von Last
mark_out = length(aux) - 1
last = string(aux[end])
for s in reverse(aux[1:mark_out])
if islowercase(s[1])
break;
end
islowercase(s[1]) && break
mark_out -= 1
last = "$s " * last
end
for s in aux[1:mark_out]
particle *= " $s"
end
foreach(s -> particle *= " $s", aux[1:mark_out])
aux = split(subnames[2], r"[\n\r ]+")
first = aux[1]
if length(aux) > 1
for s in aux[2:end]
middle *= " $s"
end
end
length(aux) > 1 && foreach(s -> middle *= " $s", aux[2:end])
end
if length(subnames) == 3
aux = split(subnames[1], r"[\n\r ]+") # von Last
mark_out = length(aux) - 1
last = aux[end]
for s in reverse(aux[1:mark_out])
if islowercase(s[1])
break;
end
islowercase(s[1]) && break
mark_out -= 1
last = "$s " * last
end
for s in aux[1:mark_out]
particle *= " $s"
end
foreach(s -> particle *= " $s", aux[1:mark_out])
junior = subnames[2]
aux =split(subnames[3], r"[\n\r ]+")
first = aux[1]
if length(aux) > 1
for s in aux[2:end]
middle *= " $s"
end
end
length(aux) > 1 && foreach(s -> middle *= " $s", aux[2:end])
end

return Name(particle, last, junior, first, middle)
end

"""
names(str::String)
Decompose into parts a list of names in BibTeX compatible format. That is names sparated by `and`.
"""
function names(str::String)
function names(str)
aux = split(str, r"[\n\r ]and[\n\r ]")
return map(x -> Name(String(x)), aux)
end
Expand All @@ -139,7 +119,7 @@ end
Access(fields::Fields)
Construct the online access information based on the entry fields.
"""
function Access(fields::Fields)
function Access(fields)
doi = get_delete!(fields, "doi")
howpublished = get_delete!(fields, "howpublished")
url = fields["_type"] == "eprint" ? arxive_url(fields) : get_delete!(fields, "url")
Expand All @@ -164,7 +144,7 @@ end
Date(fields::Fields)
Construct the date information based on the entry fields.
"""
function Date(fields::Fields)
function Date(fields)
day = get_delete!(fields, "day")
month = get_delete!(fields, "month")
year = get_delete!(fields, "year")
Expand All @@ -189,7 +169,7 @@ end
Eprint(fields::Fields)
Construct the eprint arXiv information based on the entry fields. Handle old and current arXiv format.
"""
function Eprint(fields::Fields)
function Eprint(fields)
archive_prefix = get_delete!(fields, "archivePrefix")
eprint = get_delete!(fields, "eprint")
primary_class = get_delete!(fields, "primaryClass")
Expand Down Expand Up @@ -232,7 +212,7 @@ end
In(fields::Fields)
Construct the information of how an entry was published based on its fields
"""
function In(fields::Fields)
function In(fields)
address = get_delete!(fields, "address")
chapter = get_delete!(fields, "chapter")
edition = get_delete!(fields, "edition")
Expand Down Expand Up @@ -282,7 +262,7 @@ end
Entry(id::String, fields::Fields)
Construct an entry with a unique id and a list of `Fields`.
"""
function Entry(id::String, fields::Fields)
function Entry(id, fields)
# foreach((name, field) -> fields[name] = erase_spaces(field), fields)
access = Access(fields)
authors = names(get_delete!(fields, "author"))
Expand Down Expand Up @@ -311,37 +291,44 @@ is correct or valid!).
The silent ignoring of not parseable `month` or `day` fields will lead to
misbehaviour if using comparators like `==` or `!==`!
"""
function Base.isless(a::BibInternal.Date,b::BibInternal.Date)::Bool
numbers = "0123456789";
not_valid_year = isempty(a.year) || isempty(b.year) ||
!issubset(a.year,numbers) || !issubset(b.year,numbers);
if not_valid_year; throw(ArgumentError("Unsupported year format!")); end
a_y = parse(Int,a.year);
b_y = parse(Int,b.year);

not_valid_month = isempty(a.month) || isempty(b.month) ||
!issubset(a.month,numbers) || !issubset(b.month,numbers);
function Base.isless(a::BibInternal.Date,b::BibInternal.Date)
numbers = "0123456789" # TODO: use a regexp

empty_year = isempty(a.year) || isempty(b.year)
year_format = !issubset(a.year,numbers) || !issubset(b.year,numbers)
not_valid_year = empty_year || year_format
not_valid_year && throw(ArgumentError("Unsupported year format!"))

a_y = parse(Int,a.year)
b_y = parse(Int,b.year)

empty_month = isempty(a.month) || isempty(b.month)
month_format = !issubset(a.month,numbers) || !issubset(b.month,numbers)
not_valid_month = empty_month || month_format

if !not_valid_month
a_m = parse(Int,a.month);
b_m = parse(Int,b.month);
a_m = parse(Int,a.month)
b_m = parse(Int,b.month)
end

not_valid_day = isempty(a.day) || isempty(b.day) ||
!issubset(a.day,numbers) || !issubset(b.day,numbers);
empty_day = isempty(a.day) || isempty(b.day)
day_format = !issubset(a.day,numbers) || !issubset(b.day,numbers)
not_valid_day = empty_day || day_format

if !not_valid_day
a_d = parse(Int,a.day);
b_d = parse(Int,b.day);
a_d = parse(Int,a.day)
b_d = parse(Int,b.day)
end

if (a_y == b_y)
if a_y == b_y
if not_valid_month
return false;
return false
else
return (a_m == b_m) ? !not_valid_day && a_d < b_d : (a_m < b_m);
return a_m == b_m ? !not_valid_day && a_d < b_d : a_m < b_m
end
else
return (a_y < b_y);
end

return a_y < b_y
end

"""
Expand All @@ -357,18 +344,14 @@ rules are used for now.
The silent ignoring of the other fields might lead to misbehaviour if using
comparators like `==` or `!==`!
"""
function Base.isless(a::BibInternal.Name,b::BibInternal.Name)::Bool
if (a.last == b.last)
if (a.first == b.first)
if (a.middle == b.middle)
return false;
else
return (a.middle < b.middle);
end
function Base.isless(a::BibInternal.Name,b::BibInternal.Name)
if a.last == b.last
if a.first == b.first
return a.middle == b.middle ? false : a.middle < b.middle
else
return (a.first < b.first);
return a.first < b.first
end
else
return (a.last < b.last);
end

return a.last < b.last
end
12 changes: 4 additions & 8 deletions src/utilities.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@
space(field::Symbol)
Return the amount of spaces needed to export entries, for instance to BibTeX format.
"""
function space(field::Symbol)
return maxfieldlength - length(string(field))
end
space(field) = maxfieldlength - length(string(field))

"""
get_delete!(fields::Fields, key::String)
Get the value of a field and delete it afterward.
"""
function get_delete!(fields::Fields, key::String)
function get_delete!(fields, key)
ans = get(fields, key, "")
delete!(fields, key)
return ans
Expand All @@ -20,7 +18,7 @@ end
arxive_url(fields::Fields)
Make an arxiv url from an eprint entry. Work with both old and current arxiv BibTeX format.
"""
function arxive_url(fields::Fields)
function arxive_url(fields)
str = "https://arxiv.org/abs/"
if get(fields, "archiveprefix", "") == ""
aux = split(fields["eprint"], ":")
Expand All @@ -35,6 +33,4 @@ end
erase_spaces(str::String)
Erase extra spaces, i.e. `r"[\n\r ]+"`, from `str` and return a new string.
"""
function erase_spaces(str::String)
return replace(str, r"[\n\r ]+" => " ")
end
erase_spaces(str) = replace(str, r"[\n\r ]+" => " ")

0 comments on commit 897c563

Please sign in to comment.