Skip to content

Commit 0fc5fb3

Browse files
committed
Parse file based on line indexes
1 parent cd56b1a commit 0fc5fb3

File tree

2 files changed

+60
-57
lines changed

2 files changed

+60
-57
lines changed

src/pwf2dict.jl

Lines changed: 52 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -406,12 +406,16 @@ const _pwf_defaults = Dict("DBAR" => _default_dbar, "DLIN" => _default_dlin, "DC
406406

407407
const title_identifier = "TITU"
408408
const end_section_identifier = "99999"
409+
const commented_line_identifier = '('
409410

410-
function _remove_titles_from_file_lines(file_lines::Vector{String}, section_titles_idx::Vector{Int64})
411-
remove_titles_idx = vcat(section_titles_idx, section_titles_idx .+ 1)
412-
file_lines_without_titles_idx = setdiff(1:length(file_lines), remove_titles_idx)
413-
file_lines = file_lines[file_lines_without_titles_idx]
414-
return file_lines
411+
function _is_valid_line(line_number::Int64, file_lines::Vector{String})
412+
line = file_lines[line_number]
413+
if line_number == 1
414+
return !isempty(line) && !startswith(line, commented_line_identifier) && line != title_identifier
415+
else
416+
previous_line = file_lines[line_number - 1]
417+
return !isempty(line) && !startswith(line, commented_line_identifier) && line != title_identifier && previous_line != title_identifier
418+
end
415419
end
416420

417421
"""
@@ -422,43 +426,37 @@ element corresponds to a section, divided by the delimiter 99999.
422426
"""
423427
function _split_sections(io::IO)
424428
file_lines = readlines(io)
425-
filter!(x -> x != "" && x[1] != '(', file_lines) # Ignore commented and empty lines
426429
file_lines = replace.(file_lines, repeat([Char(65533) => ' '], length(file_lines)))
427-
sections = Vector{String}[]
430+
sections = Dict{String, Vector{Int64}}()
428431

429432
section_titles_idx = findall(line -> line == title_identifier, file_lines)
430433
if !isempty(section_titles_idx)
431-
last_section_title_idx = section_titles_idx[end]:section_titles_idx[end] + 1
432-
push!(sections, file_lines[last_section_title_idx])
434+
sections[title_identifier] = [section_titles_idx[end] + 1]
433435
end
434436

435-
file_lines = _remove_titles_from_file_lines(
436-
file_lines, section_titles_idx
437-
)
438-
439437
section_delim = vcat(
440438
0,
441439
findall(x -> x == end_section_identifier, file_lines)
442440
)
443441

444442
num_sections = length(section_delim) - 1
443+
num_lines = length(file_lines)
445444

446445
for i in 1:num_sections
447-
section_begin_idx = section_delim[i] + 1
446+
section_name_idx = filter(idx -> _is_valid_line(idx, file_lines), section_delim[i] + 1:num_lines)[1]
447+
section_name = file_lines[section_name_idx]
448+
449+
section_begin_idx = section_name_idx + 1
448450
section_end_idx = section_delim[i + 1] - 1
449451

450-
# Account for multiple sections in the same pwf
451-
section_i = findall(x -> x[1] == file_lines[section_begin_idx], sections)
452-
@assert length(section_i) < 2
453-
if length(section_i) == 0
454-
push!(sections, file_lines[section_begin_idx:section_end_idx])
455-
else
456-
section_i = section_i[1]
457-
sections[section_i] = vcat(sections[section_i], file_lines[section_begin_idx + 1:section_end_idx])
458-
end
452+
section_range = collect(section_begin_idx:section_end_idx)
453+
filter!(idx -> _is_valid_line(idx, file_lines), section_range)
454+
455+
current = get(sections, section_name, Int64[])
456+
sections[section_name] = vcat(current, section_range)
459457
end
460458

461-
return sections
459+
return file_lines, sections
462460
end
463461

464462
function _handle_implicit_decimal_point!(
@@ -475,8 +473,9 @@ end
475473
Internal function. Parses a single line of data elements from a PWF file
476474
and saves it into `data::Dict`.
477475
"""
478-
function _parse_line_element!(data::Dict{String, Any}, line::String, section::AbstractString)
479-
476+
function _parse_line_element!(data::Dict{String, Any}, line_number::Int64, section::AbstractString, file_lines::Vector{String})
477+
line = file_lines[line_number]
478+
480479
line_length = _pwf_dtypes[section][end][3][end]
481480
if length(line) < line_length
482481
extra_characters_needed = line_length - length(line)
@@ -498,7 +497,8 @@ function _parse_line_element!(data::Dict{String, Any}, line::String, section::Ab
498497
end
499498
catch message
500499
if !_needs_default(element)
501-
throw(Memento.error(_LOGGER, "Parsing error at section $section: $field should be of type $dtype, received $element"))
500+
throw(Memento.error(_LOGGER, "Parsing error in line $line_number at section $section:
501+
$field should be of type $dtype, received $element"))
502502
end
503503
data[field] = element
504504
end
@@ -507,11 +507,12 @@ function _parse_line_element!(data::Dict{String, Any}, line::String, section::Ab
507507

508508
end
509509

510-
function _parse_line_element!(data::Dict{String, Any}, lines::Vector{String}, section::AbstractString)
510+
function _parse_line_element!(data::Dict{String, Any}, lines_idx::Vector{Int64}, section::AbstractString, file_lines::Vector{String})
511511

512512
mn_keys, mn_values, mn_type = _mnemonic_pairs[section]
513513

514-
for line in lines
514+
for line_number in lines_idx
515+
line = file_lines[line_number]
515516
for i in 1:length(mn_keys)
516517
k, v = mn_keys[i], mn_values[i]
517518
if v[end] <= length(line)
@@ -521,8 +522,9 @@ function _parse_line_element!(data::Dict{String, Any}, lines::Vector{String}, se
521522
data[line[k]] = parse(mn_type, line[v])
522523
catch message
523524
if !_needs_default(line[v])
524-
throw(Memento.error(_LOGGER, "Parsing error at section $section: $field should be of type $dtype, received $element"))
525-
end
525+
throw(Memento.error(_LOGGER, "Parsing error in line $line_number at section $section:
526+
$field should be of type $dtype, received $element"))
527+
end
526528
!_needs_default(line[k]) ? data[line[k]] = line[v] : nothing
527529
end
528530
else
@@ -539,42 +541,43 @@ end
539541
Internal function. Parses a section containing a system component.
540542
Returns a Vector of Dict, where each entry corresponds to a single element.
541543
"""
542-
function _parse_section_element!(data::Dict{String, Any}, section_lines::Vector{String}, section::AbstractString, idx::Int64=1)
544+
function _parse_section_element!(data::Dict{String, Any}, section_lines_idx::Vector{Int64}, section::AbstractString, file_lines::Vector{String}, idx::Int64 = 1)
543545

544546
if section == "DBAR"
545-
for line in section_lines[2:end]
547+
for line_number in section_lines_idx
546548

547549
line_data = Dict{String, Any}()
548-
_parse_line_element!(line_data, line, section)
550+
_parse_line_element!(line_data, line_number, section, file_lines)
549551

550552
bus_i = line_data["NUMBER"]
551553
data["$bus_i"] = line_data
552554
end
553555

554556
else
555-
for line in section_lines[2:end]
557+
for line_number in section_lines_idx
556558

557559
line_data = Dict{String, Any}()
558-
_parse_line_element!(line_data, line, section)
560+
_parse_line_element!(line_data, line_number, section, file_lines)
559561

560562
data["$idx"] = line_data
561563
idx += 1
562564
end
563565
end
564566
end
565567

566-
function _parse_divided_section!(data::Dict{String, Any}, section_lines::Vector{String}, section::String)
568+
function _parse_divided_section!(data::Dict{String, Any}, section_lines_idx::Vector{Int64}, section::String, file_lines::Vector{String})
569+
section_lines = file_lines[section_lines_idx]
567570

568571
separator = _divided_sections[section]["separator"]
569-
sub_titles_idx = vcat(1, findall(x -> x == separator, section_lines))
572+
sub_titles_idx = vcat(0, findall(x -> x == separator, section_lines))
570573
for (i, idx) in enumerate(sub_titles_idx)
571574

572575
if idx != sub_titles_idx[end]
573576
next_idx = sub_titles_idx[i + 1]
574-
_parse_section_element!(data, section_lines[idx:idx + 1], _divided_sections[section]["first name"], i)
577+
_parse_section_element!(data, [section_lines_idx[idx + 1]], _divided_sections[section]["first name"], file_lines, i)
575578

576579
rc = Dict{String, Any}()
577-
_parse_section_element!(rc, section_lines[idx + 1:next_idx - 1], _divided_sections[section]["second name"], i)
580+
_parse_section_element!(rc, section_lines_idx[idx + 2:next_idx - 1], _divided_sections[section]["second name"], file_lines, i)
578581

579582
group = _divided_sections[section]["subgroup"]
580583
data["$i"][group] = rc
@@ -589,21 +592,21 @@ end
589592
Internal function. Receives an array of lines corresponding to a PWF section,
590593
transforms it into a Dict and saves it into `data::Dict`.
591594
"""
592-
function _parse_section!(data::Dict{String, Any}, section_lines::Vector{String})
593-
section = section_lines[1]
595+
function _parse_section!(data::Dict{String, Any}, section::String, section_lines_idx::Vector{Int64}, file_lines::Vector{String})
594596
section_data = Dict{String, Any}()
595597

596598
if section == title_identifier
597-
section_data = section_lines[end]
599+
@assert length(section_lines_idx) == 1
600+
section_data = file_lines[section_lines_idx[1]]
598601

599602
elseif section in keys(_mnemonic_pairs)
600-
_parse_line_element!(section_data, section_lines[2:end], section)
603+
_parse_line_element!(section_data, section_lines_idx, section, file_lines)
601604

602605
elseif section in keys(_pwf_dtypes)
603-
_parse_section_element!(section_data, section_lines, section)
606+
_parse_section_element!(section_data, section_lines_idx, section, file_lines)
604607

605608
elseif section in keys(_divided_sections)
606-
_parse_divided_section!(section_data, section_lines, section)
609+
_parse_divided_section!(section_data, section_lines_idx, section, file_lines)
607610

608611
else
609612
Memento.warn(_LOGGER, "Currently there is no support for $section parsing")
@@ -820,11 +823,11 @@ Internal function. Receives a pwf file as an IOStream and parses into a Dict.
820823
"""
821824
function _parse_pwf_data(data_io::IO)
822825

823-
sections = _split_sections(data_io)
826+
file_lines, sections = _split_sections(data_io)
824827
pwf_data = Dict{String, Any}()
825828
pwf_data["name"] = match(r"^\<file\s[\/\\]*(?:.*[\/\\])*(.*)\.pwf\>$", lowercase(data_io.name)).captures[1]
826-
for section in sections
827-
_parse_section!(pwf_data, section)
829+
for (section_name, section) in sections
830+
_parse_section!(pwf_data, section_name, section, file_lines)
828831
end
829832
_populate_defaults!(pwf_data)
830833

test/test_pwf.jl

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,21 @@
22
@testset "Intermediary functions" begin
33
file = open(joinpath(@__DIR__,"data/pwf/test_system.pwf"))
44

5-
sections = PWF._split_sections(file)
6-
@test isa(sections, Vector{Vector{String}})
5+
file_lines, sections = PWF._split_sections(file)
6+
@test isa(file_lines, Vector{String})
7+
@test isa(sections, Dict{String, Vector{Int64}})
78
@test length(sections) == 5
8-
@test sections[1][1] == "TITU"
99

1010
data = Dict{String, Any}()
11-
PWF._parse_section!(data, sections[1])
11+
PWF._parse_section!(data, "TITU", sections["TITU"], file_lines)
1212
@test haskey(data, "TITU")
13-
PWF._parse_section!(data, sections[2])
13+
PWF._parse_section!(data, "DOPC IMPR", sections["DOPC IMPR"], file_lines)
1414
@test haskey(data, "DOPC IMPR")
15-
PWF._parse_section!(data, sections[3])
15+
PWF._parse_section!(data, "DCTE", sections["DCTE"], file_lines)
1616
@test haskey(data, "DCTE")
17-
PWF._parse_section!(data, sections[4])
17+
PWF._parse_section!(data, "DBAR", sections["DBAR"], file_lines)
1818
@test haskey(data, "DBAR")
19-
PWF._parse_section!(data, sections[5])
19+
PWF._parse_section!(data, "DLIN", sections["DLIN"], file_lines)
2020
@test haskey(data, "DLIN")
2121
end
2222

0 commit comments

Comments
 (0)