diff --git a/src/parser.jl b/src/parser.jl index 9523726..76e0132 100644 --- a/src/parser.jl +++ b/src/parser.jl @@ -1,29 +1,29 @@ -struct Parser{T} +mutable struct Parser{T} tokens::T substitutions::Dict{String, String} records::Dict{String, Dict{String, String}} - line::Ref{Int} + line::Int + bracket_counter::Int end Base.eltype(p::Parser) = eltype(p.tokens) Base.one(p::Parser) = eltype(p)("") -Parser(tokens::T, substitutions, records, line) where T = - Parser{T}(tokens, substitutions, records, line) +Parser(tokens::T, substitutions, records, line, bracket_counter) where T = + Parser{T}(tokens, substitutions, records, line, bracket_counter) -parse_text(text) = begin - tokens = matchall(r"[^\s\"#{}@,=]+|\s+|\"|#|{|}|@|,|=", text) - Parser(tokens, Dict{String, String}(), Dict{String, String}(), Ref(1)) -end +Parser(tokens) = Parser(tokens, Dict{String, String}(), Dict{String, Dict{String, String}}(), 1, 0) -location(parser) = "on line $(parser.line.x)" +parse_text(text) = matchall(r"[^\s\"#{}@,=\\]+|\s+|\"|#|{|}|@|,|=|\\", text) |> Parser + +location(parser) = "on line $(parser.line)" next_token_default!(parser) = if isempty(parser.tokens) one(parser) else result = shift!(parser.tokens) - parser.line.x = parser.line.x + count(x -> x == '\n', result) + parser.line = parser.line + count(x -> x == '\n', result) if all(isspace, result) eltype(parser)(" ") else @@ -56,29 +56,34 @@ expect(parser, result, expectation) = expect!(parser, expectation) = expect(parser, next_token!(parser, expectation), expectation) -token_and_counter!(parser, bracket_counter = 1) = begin - token = next_token_with_space!(parser, "}") +token_and_counter!(parser, eol) = begin + token = next_token_with_space!(parser, eol) if token == "{" - bracket_counter += 1 + parser.bracket_counter += 1 elseif token == "}" - bracket_counter -= 1 + parser.bracket_counter -= 1 + end + if parser.bracket_counter < 0 + error("} without corresponding { $(location(parser))") + else + token end - token, bracket_counter end value!(parser, values = eltype(parser)[]) = begin token = next_token!(parser) if token == "\"" - token = next_token_with_space!(parser, "\"") - while token != "\"" + token = token_and_counter!(parser, "\"") + while !(token == "\"" && parser.bracket_counter == 0) push!(values, token) - token = next_token_with_space!(parser, "\"") + token = token_and_counter!(parser, "\" or }") end elseif token == "{" - token, counter = token_and_counter!(parser) - while counter > 0 + parser.bracket_counter += 1 + token = token_and_counter!(parser, "}") + while parser.bracket_counter > 0 push!(values, token) - token, counter = token_and_counter!(parser, counter) + token = token_and_counter!(parser, "}") end else push!(values, getkey(parser.substitutions, token, String(token) ) ) @@ -125,7 +130,7 @@ julia> preamble, result = parse_bibtex(""\" @string{short = long} @a{b, c = {{c} c}, - d = "d d", + d = "d {"} d", e = f # short } ""\"); @@ -140,7 +145,7 @@ julia> result["b"]["c"] "{c} c" julia> result["b"]["d"] -"d d" +"d {\\"} d" julia> result["b"]["e"] "f short"