Skip to content

Commit d3aa7e1

Browse files
jakobnissentecosaur
authored andcommitted
Improve inference with a function over a closure
The previous code pattern using `takewhile` relied on Julia's type inference to produce correct results. In particular, it relied on `takewhile(f, x) |> collect` to correctly infer to `Vector{Pair{<:Any, Char}}`. However, type inference in Julia is an optimization, and should not be relied on to produce the correct answer. Instead, use a normal function to replace the functional style of programming.
1 parent 6901610 commit d3aa7e1

File tree

2 files changed

+51
-19
lines changed

2 files changed

+51
-19
lines changed

docs/src/internals.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ StyledStrings.StyledMarkup.escaped!
4949
StyledStrings.StyledMarkup.interpolated!
5050
StyledStrings.StyledMarkup.readexpr!
5151
StyledStrings.StyledMarkup.skipwhitespace!
52+
StyledStrings.StyledMarkup.read_while!
5253
StyledStrings.StyledMarkup.begin_style!
5354
StyledStrings.StyledMarkup.end_style!
5455
StyledStrings.StyledMarkup.read_annotation!

src/styledmarkup.jl

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,50 @@ function skipwhitespace!(state::State)
348348
end
349349
end
350350

351+
"""
352+
read_while!(f::Function, state::Base.Stateful, lastchar::Char)
353+
354+
Read `state` until `f(::Char)` is `false`.
355+
356+
Given a `Stateful` that iterates `(_, char::Char)` pairs, and a predicate
357+
`f(::Char)::Bool`, return `(str, lastchar)`, where `str::String` contains all the
358+
`char` for which `f(char) == true`, and `lastchar` the last `char` element seen,
359+
or the input `lastchar` there are no elements of `state`.
360+
361+
# Examples
362+
363+
```julia-repl
364+
julia> s = Base.Stateful(pairs("abc"));
365+
366+
julia> read_while!(isnumeric, s, 'w')
367+
("", 'a')
368+
369+
julia> first(s) # s is mutated
370+
2 => 'b'
371+
372+
julia> read_while!(isascii, Base.Stateful(pairs("123Σω")), 'k')
373+
("123", 'Σ')
374+
375+
julia> read_while!(isascii, Base.Stateful(pairs("abcde")), 'α')
376+
("abcde", 'e')
377+
378+
julia> read_while!(isascii , Base.Stateful(pairs("")), 'k')
379+
("", 'k')
380+
```
381+
"""
382+
function read_while!(f::Function, state::Base.Stateful, lastchar::Char)
383+
v = Char[]
384+
for (_, char::Char) in state
385+
lastchar = char
386+
if f(char)
387+
push!(v, char)
388+
else
389+
break
390+
end
391+
end
392+
if isempty(v) "" else String(v) end, lastchar
393+
end
394+
351395
"""
352396
begin_style!(state::State, i::Int, char::Char)
353397
@@ -438,16 +482,8 @@ it to `newstyles`.
438482
"""
439483
function read_inlineface!(state::State, i::Int, char::Char, newstyles)
440484
# Substructure parsing helper functions
441-
function readalph!(state, lastchar)
442-
Iterators.takewhile(
443-
c -> 'a' <= (lastchar = last(c)) <= 'z', state.s) |>
444-
collect .|> last |> String, lastchar
445-
end
446-
function readsymbol!(state, lastchar)
447-
Iterators.takewhile(
448-
c -> (lastchar = last(c)) (' ', '\t', '\n', '\r', ',', ')'), state.s) |>
449-
collect .|> last |> String, lastchar
450-
end
485+
readalph!(state, lastchar) = read_while!(c -> 'a' <= c <= 'z', state.s, lastchar)
486+
readsymbol!(state, lastchar) = read_while!(((' ', '\t', '\n', '\r', ',', ')')), state.s, lastchar)
451487
function parsecolor(color::String)
452488
if color == "nothing"
453489
elseif startswith(color, '#') && length(color) == 7
@@ -554,18 +590,14 @@ function read_inlineface!(state::State, i::Int, char::Char, newstyles)
554590
lastchar = last(popfirst!(state.s))
555591
break
556592
end
557-
facename = Iterators.takewhile(
558-
c -> (lastchar = last(c)) (',', ']', ')'), state.s) |>
559-
collect .|> last |> String
593+
facename, lastchar = read_while!(((',', ']', ')')), state.s, lastchar)
560594
push!(inherit, Symbol(rstrip(facename)))
561595
if lastchar != ','
562596
break
563597
end
564598
end
565599
else
566-
facename = Iterators.takewhile(
567-
c -> (lastchar = last(c)) (',', ']', ')'), state.s) |>
568-
collect .|> last |> String
600+
facename, lastchar = read_while!(((',', ']', ')')), state.s, lastchar)
569601
push!(inherit, Symbol(rstrip(facename)))
570602
end
571603
inherit, lastchar
@@ -614,9 +646,8 @@ function read_inlineface!(state::State, i::Int, char::Char, newstyles)
614646
if isnextchar(state, '"')
615647
readexpr!(state, first(peek(state.s))) |> first
616648
else
617-
Iterators.takewhile(
618-
c -> (lastchar = last(c)) (',', ')'), state.s) |>
619-
collect .|> last |> String
649+
str, lastchar = read_while!(((',', ')')), state.s, lastchar)
650+
str
620651
end
621652
elseif key == :height
622653
if isnextchar(state, ('.', '0':'9'...))

0 commit comments

Comments
 (0)