Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use const field instead of internal RefValue #70

Merged
merged 1 commit into from
Aug 10, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading