using JuliaSyntaxfunction makeCodeSegment(raw::String, pos::Ref{Int}, ast::JuliaSyntax.GreenNode{JuliaSyntax.SyntaxHead})    head = ast.head    kind = head.kind    start = pos[] + 1    last = pos[] + ast.span    data = raw[start:last]    pos[] += ast.span    range = "start = $start last = $last"    if kind == JuliaSyntax.K"NewlineWs"        s = join(map(x->escapseNewlineWs(String(x)), split(data, '\n')), """<span class="linebreak"></span>""")        return s    elseif kind == JuliaSyntax.K"Whitespace"        data = escapseEmtpySpace(data)        return """<span class="secondary">$data</span>"""    else        data = htmlEscape(data)        if kind == JuliaSyntax.K"Comment"            return """<span class="comment">$data</span>"""        elseif JuliaSyntax.is_trivia(head)             return """<span class="delim text-Expr token" $range>$data</span>"""        else            return """<span class="mono text-Exp token" $range>$data</span>"""        end    end    #=     """<span class="mono text-Typ token">$s</span>"""     """<span class="secondary">&nbsp;</span>"""     """<span class="mono text-Pat token">$s</span>"""     """<span class="delim text-Exp token">fun</span>"""     """<span class="linebreak"></span>"""     """<span class="delim text-Pat token">)</span>"""          =#endfunction escapseEmtpySpace(s::String)::String    replace(s, ' '=>"&nbsp;")endfunction escapseNewlineWs(s::String)::String    replace(s, ' '=>"&nbsp;")endfunction htmlEscape(s::String)::String    replace(s, '<'=>"&lt;",                '>'=>"&gt;",                '&'=>"&amp;",                '\''=>"&apos;",                '"'=> "&quot;",               ' '=>"&nbsp;"               )endfunction convertGreenNode!(rel, pos::Ref{Int}, raw::String, ast::JuliaSyntax.GreenNode{JuliaSyntax.SyntaxHead})    children = ast.args    if children isa Tuple{}        push!(rel, makeCodeSegment(raw, pos, ast))    else        # a non-terminal        for i in children            convertGreenNode!(rel, pos, raw, i)        end    endendfunction convertGreenNode(raw::String, ast::JuliaSyntax.GreenNode{JuliaSyntax.SyntaxHead})    rel = String[]    pos::Ref{Int} = Ref{Int}(0)    convertGreenNode!(rel, pos, raw, ast)    return relendfunction parseAndConvert(raw::String;filename::String)    ast = JuliaSyntax.parseall(JuliaSyntax.SyntaxNode, raw;filename=filename)    rel = convertGreenNode(raw, ast.data.raw)    # we can't join them with '\n', which might introduce a 4px space between spans    join(rel)endfunction wrap(x::String)return """ <!DOCTYPE html> <html lang="en">   <head>     <meta charset="utf-8">     <title>title</title>     <link rel="stylesheet" href="./style.css">   </head>   <body>   <div id = "main">   <div class = "editor single">   <div class = "cell-container">   <div class = "cell cell-item selected single">   <div id = "code-container" class = "code-container">   <div class = "code">   <div class = "code-text">   $x   </div>   </div>   </div>   </div>   </div>   </div>   </div>   <script src="./final.js"></script>   </body> </html> """endmutable struct MutableGreenNode    const head::JuliaSyntax.SyntaxHead    const span::UInt32    const args::Union{Tuple{}, Vector{MutableGreenNode}}    backpointer::Union{Nothing, JuliaSyntax.TreeNode{JuliaSyntax.SyntaxData}}endmutable struct MutableTreeNode    const ast::JuliaSyntax.TreeNode{JuliaSyntax.SyntaxData}    const target::MutableGreenNodeendfunction testNoDuplicatePointer!(rel::Set, ast::JuliaSyntax.GreenNode{JuliaSyntax.SyntaxHead})::Bool    args = ast.args    if args isa Tuple{}        return true    else        if args in rel            return false        end        push!(rel, args)        for i in args            if !testNoDuplicatePointer!(rel, i)                return false            end        end    end    return trueendfunction testNoDuplicatePointer(ast)::Bool    rel = Set{Vector{JuliaSyntax.GreenNode{JuliaSyntax.SyntaxHead}}}()    testNoDuplicatePointer!(rel, ast)endfunction convert2MutableGreenNode!(mapping::Dict, childmapping::Dict, pos::Ref{UInt32}, ast::JuliaSyntax.GreenNode{JuliaSyntax.SyntaxHead})::MutableGreenNode    args = ast.args    if args isa Tuple{}        s = Tuple{UInt32, UInt32}((pos[] + 1, pos[] + ast.span))        m = MutableGreenNode(ast.head, ast.span, (), nothing)        pos[] += ast.span        childmapping[s] = m    else        margs = [convert2MutableGreenNode!(mapping, childmapping, pos, i) for i in args]        m = MutableGreenNode(ast.head, ast.span, margs, nothing)        # we establish a mapping here!        mapping[args] = m    end    return mendfunction convert2MutableGreenNode(ast::JuliaSyntax.GreenNode{JuliaSyntax.SyntaxHead})    mapping = Dict{Vector{JuliaSyntax.GreenNode{JuliaSyntax.SyntaxHead}}, MutableGreenNode}()    childmapping = Dict{Tuple{UInt32, UInt32}, MutableGreenNode}()    pos = Ref{UInt32}(0)    m = convert2MutableGreenNode!(mapping, childmapping, pos, ast)    return m, mapping, childmappingendfunction forwardPointer(tree::JuliaSyntax.TreeNode{JuliaSyntax.SyntaxData}, mapping, childmapping)    gnode = tree.data.raw    args = gnode.args    if args isa Tuple{}        @assert tree.children isa Nothing        start = UInt32(tree.data.position)        span = (start, start + gnode.span - 1)        m = childmapping[span]        m.backpointer = tree        return MutableTreeNode(tree, m)    else        m = mapping[args]        m.backpointer = tree        for i in tree.children::Vector{JuliaSyntax.TreeNode{JuliaSyntax.SyntaxData}}            forwardPointer(i, mapping, childmapping)        end        return MutableTreeNode(tree, m)    endendfunction constructBiMapping(ast::JuliaSyntax.TreeNode{JuliaSyntax.SyntaxData})    testNoDuplicatePointer(ast.data.raw)    m, mapping, childmapping = convert2MutableGreenNode(ast.data.raw)    mm = forwardPointer(ast, mapping, childmapping)    return mmendfunction serializeAST!(alloc::Ref{Int}, rel::Vector{String}, ast::JuliaSyntax.TreeNode{JuliaSyntax.SyntaxData}, parentid::String)::String    alloc[] += 1    id = alloc[]    start = UInt32(ast.data.position)    last = start + ast.data.raw.span - 1    head = ast.data.raw.head    push!(rel, "var ast$id = new ASTNode(new SyntaxHead(\"$(summary(head))\", $(head.flags)),$parentid$start$last)")    c = ast.children    if !(c isa Nothing)        param = join([serializeAST!(alloc, rel, i, "ast$id") for i in ast.children], ',')        push!(rel, "ast$id.children = [$param]")    end    return "ast$id"endfunction serializeAST(ast::JuliaSyntax.TreeNode{JuliaSyntax.SyntaxData})    alloc = Ref{Int}(0)    rel = String[]    serializeAST!(alloc, rel, ast, "null")    return join(rel, ";\n")endopen(joinpath(@__DIR__, "www", "index.html"), write=true) do f    s = open(@__FILE__) do x        parseAndConvert(read(x, String);filename=@__FILE__)    end    write(f, wrap(s))    open(joinpath(@__DIR__, "www", "data.js"), write=true) do f        ast = open(@__FILE__) do x            JuliaSyntax.parseall(JuliaSyntax.SyntaxNode, read(x, String);filename=@__FILE__)        end        mm = serializeAST(ast)        write(f, mm)    end    s3 = open(joinpath(@__DIR__, "www", "data.js")) do f        s1 = read(f, String)        s2 = open(joinpath(@__DIR__, "www", "script.js")) do f            read(f, String)        end        return s2*s1    end    open(joinpath(@__DIR__, "www", "final.js");write=true) do f        write(f, s3)    endend