Browse Source

Use `nothing` as sentinel value if the node didn't change.

pull/19/head
Fredrik Ekre 2 years ago
parent
commit
0d326642f0
No known key found for this signature in database
GPG Key ID: DE82E6D5E364C0A2
  1. 75
      src/Runic.jl

75
src/Runic.jl

@ -66,7 +66,7 @@ function accept_node!(ctx::Context, node::JuliaSyntax.GreenNode)
end end
# Write formatted thing and reset the output stream # Write formatted thing and reset the output stream
function write_and_reset(ctx::Context, bytes::Union{String, SubString{String}, Vector{UInt8}}) function write_and_reset(ctx::Context, bytes::Union{String, AbstractVector{UInt8}})
fmt_pos = position(ctx.fmt_io) fmt_pos = position(ctx.fmt_io)
nb = write(ctx.fmt_io, bytes) nb = write(ctx.fmt_io, bytes)
seek(ctx.fmt_io, fmt_pos) seek(ctx.fmt_io, fmt_pos)
@ -78,23 +78,30 @@ struct NullNode end
const nullnode = NullNode() const nullnode = NullNode()
function format_node_with_children!(ctx::Context, node::JuliaSyntax.GreenNode) function format_node_with_children!(ctx::Context, node::JuliaSyntax.GreenNode)
# If the node doesn't have children there is nothing to do here
if !JuliaSyntax.haschildren(node) if !JuliaSyntax.haschildren(node)
return node return nothing
end end
# Keep track of the siblings on this stack # Keep track of the siblings on this stack
prev_sibling = ctx.prev_sibling prev_sibling = ctx.prev_sibling
next_sibling = ctx.next_sibling next_sibling = ctx.next_sibling
ctx.prev_sibling = nothing ctx.prev_sibling = nothing
ctx.next_sibling = nothing ctx.next_sibling = nothing
# @assert JuliaSyntax.haschildren(node)
# A formatted node can have a larger span than the original so we need to backup the
# original bytes and keep track of the accumulated span of processed children
original_bytes = node_bytes(ctx, node) # TODO: Read into reusable buffer?
span_sum = 0 span_sum = 0
original_bytes = node_bytes(ctx, node) # TODO: Read into reusable buffer
children = JuliaSyntax.children(node) # The new node parts. `children′` aliases `children` and only copied below if any of the
# The new node parts # nodes change ("copy-on-write").
head′ = JuliaSyntax.head(node) head′ = JuliaSyntax.head(node)
children = children # This aliases until the need to copy below children = JuliaSyntax.children(node)
# Keep track of changes; if no child changes the original node can be returned children′ = children
any_child_changed = false any_child_changed = false
# Loop over all the children
for (i, child) in pairs(children) for (i, child) in pairs(children)
# Set the siblings: previous from children′, next from children # Set the siblings: previous from children′, next from children
ctx.prev_sibling = get(children′, i - 1, nothing) ctx.prev_sibling = get(children′, i - 1, nothing)
@ -103,23 +110,27 @@ function format_node_with_children!(ctx::Context, node::JuliaSyntax.GreenNode)
span_sum += JuliaSyntax.span(child) span_sum += JuliaSyntax.span(child)
this_child_changed = false this_child_changed = false
itr = 0 itr = 0
# Loop until this node reaches a steady state and is accepted
while true while true
# Format this node # Keep track of the stream position and reset it below if the node is changed
fmt_pos = position(ctx.fmt_io) fmt_pos = position(ctx.fmt_io)
# Format the child
child′′ = format_node!(ctx, child′) child′′ = format_node!(ctx, child′)
if child′′ === nullnode if child′′ === nullnode
# This node should be deleted from the tree
# TODO: When this is fixed the sibling setting above needs to be modified to
# handle this too
this_child_changed = true this_child_changed = true
# TODO: When this is fixed the sibling setting above needs to handle this
# too
error("TODO: handle removed children") error("TODO: handle removed children")
elseif child′′ === child′ elseif child′′ === nothing
child′ = child′′ # The node was accepted, continue to next sibling
@assert position(ctx.fmt_io) == fmt_pos + JuliaSyntax.span(child′) @assert position(ctx.fmt_io) == fmt_pos + JuliaSyntax.span(child′)
break break
else else
# The node should be replaced with the new one. Reset the stream and try
# again until it is accepted.
@assert child′′ isa JuliaSyntax.GreenNode
this_child_changed = true this_child_changed = true
# any_changed = true
# Reset the output stream and go again
seek(ctx.fmt_io, fmt_pos) seek(ctx.fmt_io, fmt_pos)
child′ = child′′ child′ = child′′
end end
@ -128,17 +139,15 @@ function format_node_with_children!(ctx::Context, node::JuliaSyntax.GreenNode)
end end
end end
if this_child_changed if this_child_changed
# If the node change we have to re-write the original bytes for the next # If the node changed we have to re-write the original bytes for the next
# children # children to the output stream and then reset
remaining_bytes = @view original_bytes[(span_sum+1):end] remaining_bytes = @view original_bytes[(span_sum+1):end]
fmt_pos = position(ctx.fmt_io) nb = write_and_reset(ctx, remaining_bytes)
nb = write(ctx.fmt_io, remaining_bytes)
seek(ctx.fmt_io, fmt_pos)
@assert nb == length(remaining_bytes) @assert nb == length(remaining_bytes)
end end
any_child_changed |= this_child_changed any_child_changed |= this_child_changed
if any_child_changed if any_child_changed
# De-alias the children if needed # De-alias the children if not already done
if children′ === children if children′ === children
children′ = eltype(children)[children[j] for j in 1:(i-1)] children′ = eltype(children)[children[j] for j in 1:(i-1)]
end end
@ -153,11 +162,19 @@ function format_node_with_children!(ctx::Context, node::JuliaSyntax.GreenNode)
span′ = mapreduce(JuliaSyntax.span, +, children′; init=0) span′ = mapreduce(JuliaSyntax.span, +, children′; init=0)
return JuliaSyntax.GreenNode(head′, span′, children′) return JuliaSyntax.GreenNode(head′, span′, children′)
else else
return node return nothing
end end
end end
function format_node!(ctx::Context, node::JuliaSyntax.GreenNode) """
format_node!(ctx::Context, node::JuliaSyntax.GreenNode)
Format a node. Return values:
- `nothing::Nothing`: The node is accepted as is
- `nullnode::NullNode`: The node should be deleted from the tree
- `node::JuliaSyntax.GreenNode`: The node should be replaced with the new node
"""
function format_node!(ctx::Context, node::JuliaSyntax.GreenNode)::Union{JuliaSyntax.GreenNode, Nothing, NullNode}
node_kind = JuliaSyntax.kind(node) node_kind = JuliaSyntax.kind(node)
# TODO: Split these into matchers and a handlers and move to another file # TODO: Split these into matchers and a handlers and move to another file
@ -279,6 +296,7 @@ function format_node!(ctx::Context, node::JuliaSyntax.GreenNode)
) )
@assert !JuliaSyntax.is_trivia(node) @assert !JuliaSyntax.is_trivia(node)
node′ = format_node_with_children!(ctx, node) node′ = format_node_with_children!(ctx, node)
@assert node′ !== nullnode
return node′ return node′
# Nodes that recurse! if not trivia # Nodes that recurse! if not trivia
@ -317,6 +335,7 @@ function format_node!(ctx::Context, node::JuliaSyntax.GreenNode)
node_kind === K"while" node_kind === K"while"
) )
node′ = format_node_with_children!(ctx, node) node′ = format_node_with_children!(ctx, node)
@assert node′ !== nullnode
return node′ return node′
# Nodes that should recurse if they have children (all??) # Nodes that should recurse if they have children (all??)
@ -325,6 +344,7 @@ function format_node!(ctx::Context, node::JuliaSyntax.GreenNode)
node_kind === K"else" # try-(catch|finally)-else node_kind === K"else" # try-(catch|finally)-else
) )
node′ = format_node_with_children!(ctx, node) node′ = format_node_with_children!(ctx, node)
@assert node′ !== nullnode
return node′ return node′
# Whitespace and comments emitted verbatim for now # Whitespace and comments emitted verbatim for now
@ -332,7 +352,7 @@ function format_node!(ctx::Context, node::JuliaSyntax.GreenNode)
node_kind === K"NewlineWs" || node_kind === K"NewlineWs" ||
node_kind === K"Comment" node_kind === K"Comment"
accept_node!(ctx, node) accept_node!(ctx, node)
return node return nothing
# Nodes that always emit like the source code # Nodes that always emit like the source code
elseif ( elseif (
@ -408,7 +428,7 @@ function format_node!(ctx::Context, node::JuliaSyntax.GreenNode)
) )
) )
accept_node!(ctx, node) accept_node!(ctx, node)
return node return nothing
else else
msg = "unhandled node of type $(node_kind), current text:\n" * String(take!(ctx.fmt_io)) msg = "unhandled node of type $(node_kind), current text:\n" * String(take!(ctx.fmt_io))
throw(ErrorException(msg)) throw(ErrorException(msg))
@ -438,11 +458,12 @@ function format_tree!(ctx::Context)
# This signals that the node should be deleted, but that doesn't make sense for # This signals that the node should be deleted, but that doesn't make sense for
# the root node so error instead # the root node so error instead
error("root node deleted") error("root node deleted")
elseif root′′ === root′ elseif root′′ === nothing
root′ = root′′ # root′ = root′′
@assert position(ctx.fmt_io) == fmt_pos + JuliaSyntax.span(root′) @assert position(ctx.fmt_io) == fmt_pos + JuliaSyntax.span(root′)
break break
else else
@assert root′′ isa JuliaSyntax.GreenNode
# The node was changed, reset the output stream and try again # The node was changed, reset the output stream and try again
seek(ctx.fmt_io, fmt_pos) seek(ctx.fmt_io, fmt_pos)
root′ = root′′ root′ = root′′

Loading…
Cancel
Save