aberdysh
aberdysh

Reputation: 1664

A function or a macro for retrieving attributes of annotated strings

I have strings with annotated attributes. You can think of them as XML-document strings, but with custom syntax of annotation.

Attributes in a string are encoded as follows:

#<atr_name>=<num_of_chars>:<atr_value>\n

where

That is attribute name is prefixed with # and postfixed with =, then followed by number that indicates number of characters in the value of the attribute, then followed by :, then followed by the attribute's value itself, and then followed by with newline character \n

Here is one example:

julia> string_with_attributes = """
some text
...
#name=6:Azamat
...
#year=4:2016
...
some other text
"""

Now I want to write a function or a macro that would allow me to call as:

julia> string_with_attributes["name"]
"Azamat"

julia> string_with_attributes["year"]
"2016"

julia> 

Any ideas on how to do this?

Upvotes: 1

Views: 103

Answers (3)

HarmonicaMuse
HarmonicaMuse

Reputation: 7893

Following @Gnimuc answer, you could make your own string macro AKA non standard string literal if that suit your needs, ie:

julia> function attr_str(s::S)::Dict{S, S} where {S <: AbstractString}
           d = Dict{S, S}()
           for i in eachmatch(r"(?<=#)\b.*(?==).*(?=\n)", s)
               push!(d, match(r".*(?==)", i.match).match => match(r"(?<=:).*", i.match).match)
           end
           push!(d, "string" => s)
           return d
       end
attr_str (generic function with 1 method)

julia> macro attr_str(s::AbstractString)
           :(attr_str($s))
       end
@attr_str (macro with 1 method)

julia> attr"""
           some text
           dgdfg:dgdf=ert
           #name=6:Azamat
           all34)%(*)#:DG:Ko_=ddhaogj;ldg
           #year=4:2016
           #dkgjdlkdag:dfgdfgd
           some other text
           """
Dict{String,String} with 3 entries:
  "name"   => "Azamat"
  "string" => "some text\ndgdfg:dgdf=ert\n#name=6:Azamat\nall34)%(*)#:DG:Ko_=ddhaogj;ldg\n#year=4:2016\n#dkgjdlkdag:dfgdfgd\nsome other text\n"
  "year"   => "2016"

julia>

Upvotes: 2

aberdysh
aberdysh

Reputation: 1664

So, turns out what I needed was to extend the Base.getindex method from Indexing interface.

Here is the solution that I ended up doing:

julia> 
function Base.getindex(object::S, attribute::AbstractString) where {S <: AbstractString}
    m = match( Regex("#$(attribute)=(\\d*):(.*)\n"), object )
    (typeof(m) == Void) && error("$(object) has no attribute with the name $(attribute)")
    return m.captures[end]::SubString{S}
end


julia> string_with_attributes = """
    some text
    dgdfg:dgdf=ert
    #name=6:Azamat
    all34)%(*)#:DG:Ko_=ddhaogj;ldg
    #year=4:2016
    #dkgjdlkdag:dfgdfgd
    some other text
    """

julia> string_with_attributes["name"]
"Azamat"

julia> string_with_attributes["year"]
"2016"

Upvotes: 0

Gnimuc
Gnimuc

Reputation: 8566

seems like a job for regex:

julia> string_with_attributes = """
       some text
       dgdfg:dgdf=ert
       #name=6:Azamat
       all34)%(*)#:DG:Ko_=ddhaogj;ldg
       #year=4:2016
       #dkgjdlkdag:dfgdfgd
       some other text
       """
"some text\ndgdfg:dgdf=ert\n#name=6:Azamat\nall34)%(*)#:DG:Ko_=ddhaogj;ldg\n#year=4:2016\n#dkgjdlkdag:dfgdfgd\nsome other text\n"
julia> s = Dict()
Dict{Any,Any} with 0 entries

julia> for i in eachmatch(r"(?<=#)\b.*(?==).*(?=\n)", string_with_attributes)
         push!(s, match(r".*(?==)", i.match).match => match(r"(?<=:).*", i.match).match)
       end

julia> s
Dict{Any,Any} with 2 entries:
  "name" => "Azamat"
  "year" => "2016"

Upvotes: 2

Related Questions