Skip to content

Commit

Permalink
Use const fields in parser State instead of refs
Browse files Browse the repository at this point in the history
This avoids using the (technically) internal RefValue, and so the need
to use [] for access. It may also perform slightly better, not that this
is a performance-critical area of the codebase, but why not.
  • Loading branch information
KristofferC authored and tecosaur committed Aug 10, 2024
1 parent 35a3cdf commit 4fcd8bb
Showing 1 changed file with 54 additions and 54 deletions.
108 changes: 54 additions & 54 deletions src/styledmarkup.jl
Original file line number Diff line number Diff line change
Expand Up @@ -77,31 +77,31 @@ Its fields are as follows:
- `pending_styles::Vector{Tuple{UnitRange{Int}, Union{Symbol, Expr, Pair{Symbol, Any}}}}`,
A list of styles that have been terminated, and so are known to occur over a certain range,
but have yet to be applied.
- `offset::RefValue{Int}`, a record of the between the `content` index and the index in the resulting
- `offset::Int`, a record of the between the `content` index and the index in the resulting
styled string, as markup structures are absorbed.
- `point::RefValue{Int}`, the current index in `content`.
- `escape::RefValue{Bool}`, whether the last character seen was an escape character.
- `interpolated::RefValue{Bool}`, whether any interpolated values have been seen. Knowing whether or not
- `point::Int`, the current index in `content`.
- `escape::Bool`, whether the last character seen was an escape character.
- `interpolated::Bool`, whether any interpolated values have been seen. Knowing whether or not
anything needs to be evaluated allows the resulting string to be computed at macroexpansion time,
when possible.
- `errors::Vector`, any errors raised during parsing. We collect them instead of immediately throwing
so that we can list as many issues as possible at once, instead of forcing the author of the invalid
styled markup to resolve each issue one at a time. This is expected to be populated by invocations of
`styerr!`.
"""
struct State
content::String
bytes::Vector{UInt8}
s::Iterators.Stateful
mod::Union{Module, Nothing}
parts::Vector{Any}
active_styles::Vector{Vector{Tuple{Int, Int, Union{Symbol, Expr, Pair{Symbol, Any}}}}}
pending_styles::Vector{Tuple{UnitRange{Int}, Union{Symbol, Expr, Pair{Symbol, Any}}}}
offset::Base.RefValue{Int}
point::Base.RefValue{Int}
escape::Base.RefValue{Bool}
interpolated::Base.RefValue{Bool}
errors::Vector
mutable struct State
const content::String
const bytes::Vector{UInt8}
const s::Iterators.Stateful
const mod::Union{Module, Nothing}
const parts::Vector{Any}
const active_styles::Vector{Vector{Tuple{Int, Int, Union{Symbol, Expr, Pair{Symbol, Any}}}}}
const pending_styles::Vector{Tuple{UnitRange{Int}, Union{Symbol, Expr, Pair{Symbol, Any}}}}
offset::Int
point::Int
escape::Bool
interpolated::Bool
const errors::Vector
end

function State(content::AbstractString, mod::Union{Module, Nothing}=nothing)
Expand All @@ -110,8 +110,8 @@ function State(content::AbstractString, mod::Union{Module, Nothing}=nothing)
Any[], # parts
Vector{Tuple{Int, Int, Union{Symbol, Expr, Pair{Symbol, Any}}}}[], # active_styles
Tuple{UnitRange{Int}, Union{Symbol, Expr, Pair{Symbol, Any}}}[], # pending_styles
Ref(0), Ref(1), # offset, point
Ref(false), Ref(false), # escape, interpolated
0, 1, # offset, point
false, false, # escape, interpolated
NamedTuple{(:message, :position, :hint), # errors
Tuple{AnnotatedString{String}, <:Union{Int, Nothing}, String}}[])
end
Expand Down Expand Up @@ -192,11 +192,11 @@ This consumes all the content between `state.point` and `stop`, and shifts
`state.point` to be the index after `stop`.
"""
function addpart!(state::State, stop::Int)
if state.point[] > stop+state.offset[]+ncodeunits(state.content[stop])-1
return state.point[] = nextind(state.content, stop) + state.offset[]
if state.point > stop+state.offset+ncodeunits(state.content[stop])-1
return state.point = nextind(state.content, stop) + state.offset
end
str = String(state.bytes[
state.point[]:stop+state.offset[]+ncodeunits(state.content[stop])-1])
state.point:stop+state.offset+ncodeunits(state.content[stop])-1])
sty_type, tupl = if ismacro(state)
Expr, (a, b) -> Expr(:tuple, a, b)
else
Expand All @@ -210,15 +210,15 @@ function addpart!(state::State, stop::Int)
sort!(state.pending_styles, by = (r -> (first(r), -last(r))) first) # Prioritise the most specific styles
for (range, annot) in state.pending_styles
if !isempty(range)
push!(styles, tupl(range .- state.point[], annot))
push!(styles, tupl(range .- state.point, annot))
end
end
empty!(state.pending_styles)
relevant_styles = Iterators.filter(
(_, start, _)::Tuple -> start <= stop + state.offset[] + 1,
(_, start, _)::Tuple -> start <= stop + state.offset + 1,
Iterators.flatten(map(reverse, state.active_styles)))
for (_, start, annot) in relevant_styles
range = (start - state.point[]):(stop - state.point[] + state.offset[] + 1)
range = (start - state.point):(stop - state.point + state.offset + 1)
push!(styles, tupl(range, annot))
end
if isempty(styles)
Expand All @@ -229,7 +229,7 @@ function addpart!(state::State, stop::Int)
:(AnnotatedString($str, $(Expr(:vect, styles...))))
end
end)
state.point[] = nextind(state.content, stop) + state.offset[]
state.point = nextind(state.content, stop) + state.offset
end

"""
Expand All @@ -239,7 +239,7 @@ Create a new part based on (the eventual evaluation of) `expr`, running from
`start` to `stop`, taking the currently active styles into account.
"""
function addpart!(state::State, start::Int, expr, stop::Int)
if state.point[] < start
if state.point < start
addpart!(state, start)
end
if isempty(state.active_styles)
Expand Down Expand Up @@ -270,7 +270,7 @@ function addpart!(state::State, start::Int, expr, stop::Int)
end
end))
end
map!.((i, _, annot)::Tuple -> (i, stop + state.offset[] + 1, annot),
map!.((i, _, annot)::Tuple -> (i, stop + state.offset + 1, annot),
state.active_styles, state.active_styles)
end
end
Expand All @@ -282,8 +282,8 @@ Parse the escaped character `char`, at index `i`, into `state`
"""
function escaped!(state::State, i::Int, char::Char)
if char in ('{', '}', '\\') || (char == '$' && ismacro(state))
deleteat!(state.bytes, i + state.offset[] - 1)
state.offset[] -= ncodeunits('\\')
deleteat!(state.bytes, i + state.offset - 1)
state.offset -= ncodeunits('\\')
elseif char ('\n', '\r') && !isempty(state.s)
skipped = 0
if char == '\r' && isnextchar(state, '\n')
Expand All @@ -294,10 +294,10 @@ function escaped!(state::State, i::Int, char::Char)
popfirst!(state.s)
skipped += 1
end
deleteat!(state.bytes, i+state.offset[]-1:i+skipped+state.offset[])
state.offset[] -= skipped + ncodeunits("\\\n")
deleteat!(state.bytes, i+state.offset-1:i+skipped+state.offset)
state.offset -= skipped + ncodeunits("\\\n")
end
state.escape[] = false
state.escape = false
end

"""
Expand All @@ -307,11 +307,11 @@ Interpolate the expression starting at `i`, and add it as a part to `state`.
"""
function interpolated!(state::State, i::Int, _)
expr, nexti = readexpr!(state, i + ncodeunits('$'))
deleteat!(state.bytes, i + state.offset[])
state.offset[] -= ncodeunits('$')
deleteat!(state.bytes, i + state.offset)
state.offset -= ncodeunits('$')
addpart!(state, i, esc(expr), nexti)
state.point[] = nexti + state.offset[]
state.interpolated[] = true
state.point = nexti + state.offset
state.interpolated = true
end

"""
Expand Down Expand Up @@ -408,8 +408,8 @@ function begin_style!(state::State, i::Int, char::Char)
# style declaration(s).
if !isempty(state.s)
nexti = first(peek(state.s))
deleteat!(state.bytes, i+state.offset[]:nexti+state.offset[]-1)
state.offset[] -= nexti - i
deleteat!(state.bytes, i+state.offset:nexti+state.offset-1)
state.offset -= nexti - i
end
end

Expand All @@ -420,10 +420,10 @@ Close of the most recent active style in `state`, making it a pending style.
"""
function end_style!(state::State, i::Int, char::Char)
for (_, start, annot) in pop!(state.active_styles)
pushfirst!(state.pending_styles, (start:i+state.offset[], annot))
pushfirst!(state.pending_styles, (start:i+state.offset, annot))
end
deleteat!(state.bytes, i + state.offset[])
state.offset[] -= ncodeunits('}')
deleteat!(state.bytes, i + state.offset)
state.offset -= ncodeunits('}')
end

"""
Expand Down Expand Up @@ -511,7 +511,7 @@ function read_inlineface!(state::State, i::Int, char::Char, newstyles)
elseif isnextchar(state, '$') && ismacro(state)
expr, _ = readexpr!(state)
lastchar = last(popfirst!(state.s))
state.interpolated[] = true
state.interpolated = true
needseval = true
esc(expr)
else
Expand All @@ -525,7 +525,7 @@ function read_inlineface!(state::State, i::Int, char::Char, newstyles)
if isnextchar(state, '$') && ismacro(state)
expr, _ = readexpr!(state)
lastchar = last(popfirst!(state.s))
state.interpolated[] = true
state.interpolated = true
needseval = true
ustyle = esc(expr)
else
Expand Down Expand Up @@ -639,7 +639,7 @@ function read_inlineface!(state::State, i::Int, char::Char, newstyles)
val = if ismacro(state) && isnextchar(state, '$')
expr, _ = readexpr!(state)
lastchar = last(popfirst!(state.s))
state.interpolated[] = true
state.interpolated = true
needseval = true
esc(expr)
elseif key == :font
Expand Down Expand Up @@ -724,7 +724,7 @@ function read_inlineface!(state::State, i::Int, char::Char, newstyles)
end
face = Expr(:call, Face, kwargs...)
push!(newstyles,
(i, i + state.offset[] + 1,
(i, i + state.offset + 1,
if !ismacro(state)
Pair{Symbol, Any}(:face, Face(; NamedTuple(kwargs)...))
elseif needseval
Expand Down Expand Up @@ -766,7 +766,7 @@ function read_face_or_keyval!(state::State, i::Int, char::Char, newstyles)
# this isn't the 'last' char yet, but it will be
key = if ismacro(state) && last(peek(state.s)) == '$'
expr, _ = readexpr!(state)
state.interpolated[] = true
state.interpolated = true
needseval = true
esc(expr)
else
Expand All @@ -788,7 +788,7 @@ function read_face_or_keyval!(state::State, i::Int, char::Char, newstyles)
read_curlywrapped!(state)
elseif ismacro(state) && nextchar == '$'
expr, _ = readexpr!(state)
state.interpolated[] = true
state.interpolated = true
needseval = true
esc(expr)
else
Expand All @@ -800,7 +800,7 @@ function read_face_or_keyval!(state::State, i::Int, char::Char, newstyles)
String(chars)
end
push!(newstyles,
(i, i + state.offset[] + ncodeunits('{'),
(i, i + state.offset + ncodeunits('{'),
if key isa String && !(value isa Symbol || value isa Expr)
Pair{Symbol, Any}(Symbol(key), value)
elseif key isa Expr || key isa Symbol
Expand All @@ -811,7 +811,7 @@ function read_face_or_keyval!(state::State, i::Int, char::Char, newstyles)
end))
elseif key !== ""
push!(newstyles,
(i, i + state.offset[] + ncodeunits('{'),
(i, i + state.offset + ncodeunits('{'),
if key isa Symbol || key isa Expr
:(Pair{Symbol, Any}(:face, $key))
else # Face symbol
Expand All @@ -836,8 +836,8 @@ function run_state_machine!(state::State)
# Run the state machine
for (i, char) in state.s
if char == '\\'
state.escape[] = true
elseif state.escape[]
state.escape = true
elseif state.escape
escaped!(state, i, char)
elseif ismacro(state) && char == '$'
interpolated!(state, i, char)
Expand All @@ -852,7 +852,7 @@ function run_state_machine!(state::State)
end
end
# Ensure that any trailing unstyled content is added
if state.point[] <= lastindex(state.content) + state.offset[]
if state.point <= lastindex(state.content) + state.offset
addpart!(state, lastindex(state.content))
end
for incomplete in Iterators.flatten(state.active_styles)
Expand Down Expand Up @@ -988,7 +988,7 @@ macro styled_str(raw_content::String)
run_state_machine!(state)
if !isempty(state.errors)
throw(MalformedStylingMacro(state.content, state.errors))
elseif state.interpolated[]
elseif state.interpolated
:(annotatedstring($(state.parts...)) |> annotatedstring_optimize!)
else
annotatedstring(map(Base.Fix1(hygienic_eval, state), state.parts)...) |> annotatedstring_optimize!
Expand Down

0 comments on commit 4fcd8bb

Please sign in to comment.