From 86e52bb9b314a4b1c67ce6877b321749f74a8335 Mon Sep 17 00:00:00 2001 From: Fredrik Ekre Date: Sat, 1 Jun 2024 23:49:07 +0200 Subject: [PATCH] Add metadata tags to the Node struct. --- src/Runic.jl | 13 ++++++-- src/chisels.jl | 84 ++++++++++++++++++++++++++++++++++++++++++++++-- src/runestone.jl | 12 +++---- test/runtests.jl | 2 +- 4 files changed, 100 insertions(+), 11 deletions(-) diff --git a/src/Runic.jl b/src/Runic.jl index 054c1fd..a233c2f 100644 --- a/src/Runic.jl +++ b/src/Runic.jl @@ -21,6 +21,8 @@ include("debug.jl") # Node # ######## +const TagType = UInt32 + # This is essentially just a re-packed `JuliaSyntax.GreenNode`. struct Node # The next three fields directly match JuliaSyntax.GreenNode. We can not store a @@ -29,20 +31,27 @@ struct Node head::JuliaSyntax.SyntaxHead span::UInt32 kids::Union{Tuple{}, Vector{Node}} + # Metadata for the formatter + tags::TagType +end + +function Node(head::JuliaSyntax.SyntaxHead, span::Integer) + return Node(head, span % UInt32, (), 0 % TagType) end # Re-package a GreenNode as a Node function Node(node::JuliaSyntax.GreenNode) + tags = 0 % TagType return Node( JuliaSyntax.head(node), JuliaSyntax.span(node), - map(Node, JuliaSyntax.children(node)), + map(Node, JuliaSyntax.children(node)), tags, ) end function Base.show(io::IO, node::Node) print(io, "Node({head: {kind: ") show(io, kind(node)) - print(io, ", flags: \"$(flags(node))\"}, span: $(span(node))})") + print(io, ", flags: \"$(stringify_flags(node))\"}, span: $(span(node)), tags: \"$(stringify_tags(node))\"})") return nothing end diff --git a/src/chisels.jl b/src/chisels.jl index 45eb345..d8aecbd 100644 --- a/src/chisels.jl +++ b/src/chisels.jl @@ -4,10 +4,90 @@ # Node utilities extensions and JuliaSyntax extensions # ######################################################## +# See JuliaSyntax/src/parse_stream.jl +function stringify_flags(node::Node) + io = IOBuffer() + if JuliaSyntax.has_flags(node, JuliaSyntax.TRIVIA_FLAG) + write(io, "trivia,") + end + if JuliaSyntax.is_operator(kind(node)) + if JuliaSyntax.has_flags(node, JuliaSyntax.DOTOP_FLAG) + write(io, "dotted,") + end + if JuliaSyntax.has_flags(node, JuliaSyntax.SUFFIXED_FLAG) + write(io, "suffixed,") + end + end + if kind(node) in KSet"call dotcall" + if JuliaSyntax.has_flags(node, JuliaSyntax.PREFIX_CALL_FLAG) + write(io, "prefix-call,") + end + if JuliaSyntax.has_flags(node, JuliaSyntax.INFIX_FLAG) + write(io, "infix-op,") + end + if JuliaSyntax.has_flags(node, JuliaSyntax.PREFIX_OP_FLAG) + write(io, "prefix-op,") + end + if JuliaSyntax.has_flags(node, JuliaSyntax.POSTFIX_OP_FLAG) + write(io, "postfix-op,") + end + end + if kind(node) in KSet"string cmdstring" && + JuliaSyntax.has_flags(node, JuliaSyntax.TRIPLE_STRING_FLAG) + write(io, "triple,") + end + if kind(node) in KSet"string cmdstring Identifier" && + JuliaSyntax.has_flags(node, JuliaSyntax.RAW_STRING_FLAG) + write(io, "raw,") + end + if kind(node) in KSet"tuple block macrocall" && + JuliaSyntax.has_flags(node, JuliaSyntax.PARENS_FLAG) + write(io, "parens,") + end + if kind(node) === K"quote" && JuliaSyntax.has_flags(node, JuliaSyntax.COLON_QUOTE) + write(io, "colon,") + end + if kind(node) === K"toplevel" && JuliaSyntax.has_flags(node, JuliaSyntax.TOPLEVEL_SEMICOLONS_FLAG) + write(io, "semicolons,") + end + if kind(node) === K"struct" && JuliaSyntax.has_flags(node, JuliaSyntax.MUTABLE_FLAG) + write(io, "mutable,") + end + if kind(node) === K"module" && JuliaSyntax.has_flags(node, JuliaSyntax.BARE_MODULE_FLAG) + write(io, "baremodule,") + end + truncate(io, max(0, position(io) - 1)) # Remove trailing comma + return String(take!(io)) +end + + +# Node tags # + +# This node is responsible for incrementing the indentation level +const TAG_INDENT = TagType(1) << 0 +# This node is responsible for decrementing the indentation level +const TAG_DEDENT = TagType(1) << 1 + +function has_tag(node::Node, tag::TagType) + return node.tags & tag != 0 +end + +function stringify_tags(node::Node) + io = IOBuffer() + if has_tag(node, TAG_INDENT) + write(io, "indent,") + end + if has_tag(node, TAG_DEDENT) + write(io, "dedent,") + end + truncate(io, max(0, position(io) - 1)) # Remove trailing comma + return String(take!(io)) +end + # Create a new node with the same head but new kids -function make_node(node::Node, kids′::Vector{Node}) +function make_node(node::Node, kids′::Vector{Node}, tags = TagType(0)) span′ = mapreduce(span, +, kids′; init = 0) - return Node(head(node), span′, kids′) + return Node(head(node), span′, kids′, tags) end function first_leaf(node::Node) diff --git a/src/runestone.jl b/src/runestone.jl index decbd30..1da505f 100644 --- a/src/runestone.jl +++ b/src/runestone.jl @@ -25,7 +25,7 @@ function trim_trailing_whitespace(ctx::Context, node::Node) nb = replace_bytes!(ctx, str′, span(node)) @assert nb != span(node) # Create new node and return it - node′ = Node(head(node), nb, ()) + node′ = Node(head(node), nb) return node′ end @@ -50,7 +50,7 @@ function format_hex_literals(ctx::Context, node::Node) nb = replace_bytes!(ctx, bytes, spn) @assert nb == length(bytes) == target_spans[i] # Create new node and return it - node′ = Node(head(node), nb, ()) + node′ = Node(head(node), nb) return node′ end @@ -88,7 +88,7 @@ function format_oct_literals(ctx::Context, node::Node) nb = replace_bytes!(ctx, bytes, spn) @assert nb == length(bytes) == target_span # Create new node and return it - node′ = Node(head(node), nb, ()) + node′ = Node(head(node), nb) return node′ end @@ -145,7 +145,7 @@ function format_float_literals(ctx::Context, node::Node) nb = replace_bytes!(ctx, bytes, span(node)) @assert nb == length(bytes) # Create new node and return it - node′ = Node(head(node), nb, ()) + node′ = Node(head(node), nb) return node′ end @@ -160,7 +160,7 @@ function spaces_around_x(ctx::Context, node::Node, is_x::F) where F kids′ = kids any_changes = false pos = position(ctx.fmt_io) - ws = Node(JuliaSyntax.SyntaxHead(K"Whitespace", JuliaSyntax.TRIVIA_FLAG), 1, ()) + ws = Node(JuliaSyntax.SyntaxHead(K"Whitespace", JuliaSyntax.TRIVIA_FLAG), 1) # Toggle for whether we are currently looking for whitespace or not looking_for_whitespace = false @@ -385,7 +385,7 @@ function replace_with_in(ctx::Context, node::Node) # Construct the replacement nb = replace_bytes!(ctx, "in", span(in_node)) in_node′ = Node( - JuliaSyntax.SyntaxHead(K"in", JuliaSyntax.TRIVIA_FLAG), nb, (), + JuliaSyntax.SyntaxHead(K"in", JuliaSyntax.TRIVIA_FLAG), nb, ) accept_node!(ctx, in_node′) kids′ = copy(kids) diff --git a/test/runtests.jl b/test/runtests.jl index 1a8d213..0e9e1de 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -11,7 +11,7 @@ using JuliaSyntax: node = Runic.Node(JuliaSyntax.parseall(JuliaSyntax.GreenNode, "a = 1 + b\n")) # Pretty-printing - @test sprint(show, node) == "Node({head: {kind: K\"toplevel\", flags: \"0\"}, span: 10})" + @test sprint(show, node) == "Node({head: {kind: K\"toplevel\", flags: \"\"}, span: 10, tags: \"\"})" # JuliaSyntax duck-typing for n in (node, Runic.verified_kids(node)...,)