|
1 |
| -export savename, @dict, @ntuple, @strdict |
| 1 | +export savename, @dict, @ntuple, @strdict, parse_savename |
2 | 2 | export ntuple2dict, dict2ntuple
|
3 | 3 |
|
4 | 4 | """
|
@@ -26,6 +26,7 @@ See [`default_prefix`](@ref) for more.
|
26 | 26 |
|
27 | 27 | `savename` can be very conveniently combined with
|
28 | 28 | [`@dict`](@ref) or [`@ntuple`](@ref).
|
| 29 | +See also [`parse_savename`](@ref). |
29 | 30 |
|
30 | 31 | ## Keywords
|
31 | 32 | * `allowedtypes = default_allowed(c)` : Only values of type subtyping
|
|
250 | 251 | function dict2ntuple(dict::Dict{Symbol, T}) where T
|
251 | 252 | NamedTuple{Tuple(keys(dict))}(values(dict))
|
252 | 253 | end
|
| 254 | + |
| 255 | + |
| 256 | +""" |
| 257 | + parse_savename(filename::AbstractString; kwargs...) |
| 258 | +Try to convert a shorthand name produced with [`savename`](@ref) into a dictionary |
| 259 | +containing the parameters and their values, a prefix and suffix string. |
| 260 | +Return `prefix, parameters, suffix`. |
| 261 | +
|
| 262 | +Parsing the key-value parts of `filename` is performed under the assumption that the value |
| 263 | +is delimited by `=` and the closest `connector`. This allows the user to have `connector` |
| 264 | +(eg. `_`) in a key name (variable name) but not in the value part. |
| 265 | +
|
| 266 | +## Keywords |
| 267 | +* `connector = "_"` : string used to connect the various entries. |
| 268 | +* `parsetypes = (Int, Float64)` : tuple used to define the types which should |
| 269 | + be tried when parsing the values given in `filename`. Fallback is `String`. |
| 270 | +""" |
| 271 | +function parse_savename(filename::AbstractString; |
| 272 | + parsetypes = (Int, Float64), |
| 273 | + connector::AbstractString = "_") |
| 274 | + length(connector) == 1 || error( |
| 275 | + "Cannot parse savenames where the 'connector'"* |
| 276 | + " string consists of more than one character.") |
| 277 | + |
| 278 | + # Prefix can be anything, so it might also contain a folder which's |
| 279 | + # name was generated using savename. Therefore first the path is split |
| 280 | + # into folders and filename. |
| 281 | + prefix_part, savename_part = dirname(filename),basename(filename) |
| 282 | + # Extract the suffix. A suffix is identified by searching for the last "." |
| 283 | + # after the last "=". |
| 284 | + last_eq = findlast("=",savename_part) |
| 285 | + last_dot = findlast(".",savename_part) |
| 286 | + if last_dot == nothing || last_eq > last_dot |
| 287 | + # if no dot is after the last "=" |
| 288 | + # there is no suffix |
| 289 | + name, suffix = savename_part,"" |
| 290 | + else |
| 291 | + # Check if the last dot is part of a float number by parsing it as Int |
| 292 | + if tryparse(Int,savename_part[first(last_dot)+1:end]) == nothing |
| 293 | + # no int, so the part after the last dot is the suffix |
| 294 | + name, suffix = savename_part[1:first(last_dot)-1], savename_part[first(last_dot)+1:end] |
| 295 | + else |
| 296 | + # no suffix, because the dot just denotes the decimal places. |
| 297 | + name, suffix = savename_part, "" |
| 298 | + end |
| 299 | + end |
| 300 | + # Extract the prefix by searching for the first connector that comes before |
| 301 | + # an "=". |
| 302 | + first_eq = findfirst("=",name) |
| 303 | + first_connector = findfirst(connector,name) |
| 304 | + if first_connector == nothing || first(first_eq) < first(first_connector) |
| 305 | + prefix, _parameters = "", name |
| 306 | + else |
| 307 | + # There is a connector symbol before, so there might be a connector. |
| 308 | + # Of course the connector symbol could also be part of the variable name. |
| 309 | + prefix, _parameters = name[1:first(first_connector)-1], name[first(first_connector)+1:end] |
| 310 | + end |
| 311 | + # Add leading directory back to prefix |
| 312 | + prefix = joinpath(prefix_part,prefix) |
| 313 | + parameters = Dict{String,Any}() |
| 314 | + # Regex that matches smalles possible range between = and connector. |
| 315 | + # This way it is possible to corretly match something where the |
| 316 | + # connector ("_") was used as a variable name. |
| 317 | + # var_with_undersocore_1=foo_var=123.32_var_name_with_underscore=4.4 |
| 318 | + # var_with_undersocore_1[=foo_]var[=123.32_]var_name_with_underscore=4.4 |
| 319 | + name_seperator = Regex("=[^$connector]+$connector") |
| 320 | + c_idx = 1 |
| 321 | + while (next_range = findnext(name_seperator,_parameters,c_idx)) != nothing |
| 322 | + equal_sign, end_of_value = first(next_range), last(next_range)-1 |
| 323 | + parameters[_parameters[c_idx:equal_sign-1]] = |
| 324 | + parse_from_savename_value(parsetypes,_parameters[equal_sign+1:end_of_value]) |
| 325 | + c_idx = end_of_value+2 |
| 326 | + end |
| 327 | + # The last = cannot be followed by a connector, so it's not captured by the regex. |
| 328 | + equal_sign = findnext("=",_parameters,c_idx) |> first |
| 329 | + parameters[_parameters[c_idx:equal_sign-1]] = |
| 330 | + parse_from_savename_value(parsetypes,_parameters[equal_sign+1:end]) |
| 331 | + return prefix,parameters,suffix |
| 332 | +end |
| 333 | + |
| 334 | +""" |
| 335 | + parse_from_savename_value(types,str) |
| 336 | +Try parsing `str` with the types given in `types`. The first working parse is returned. |
| 337 | +Fallback is `String` ie. `str` is returned. |
| 338 | +""" |
| 339 | +function parse_from_savename_value(types::NTuple{N,<:Type},str::AbstractString) where N |
| 340 | + for t in types |
| 341 | + res = tryparse(t,str) |
| 342 | + res == nothing || return res |
| 343 | + end |
| 344 | + return str |
| 345 | +end |
0 commit comments