Skip to content

Commit 7a8b9a6

Browse files
committed
Add section tree output to the tags backend
This adds the section tree to the tags JSON output, including which tags are in each section and the section IDs. For example for the RISC-V Privileged spec it outputs: ``` { "tags": { "norm:ext:Sm:highest_priv_mode": "machine-mode (M-mode), which is the highest privilege mode in a RISC-V\nhart.", "norm:ext:Sm:mode_at_reset": "M-mode is used for low-level access to a hardware platform and\nis the first mode entered at reset.", "norm:ext:Sm:access_all_lower_priv_CSRs": "M-mode code can access all CSRs at lower privilege levels.", ... "norm:csr:mstatus:sz_rw": "The mstatus register is an MXLEN-bit read/write register formatted as\nshown in &lt;&lt;mstatusreg-rv32&gt;&gt; for RV32 and &lt;&lt;mstatusreg&gt;&gt; for RV64.", "norm:csr:mstatush:sz_rw_rv32": "For RV32 only, mstatush is a 32-bit read/write register formatted as shown in &lt;&lt;mstatushreg&gt;&gt;.", "norm:csr:mstatush:encoding": "Bits 30:4 of mstatush generally contain the same fields found in bits 62:36 of mstatus for RV64. Fields SD, SXL, and UXL do not exist in mstatush." }, "sections": { "title": "", "id": "", "children": [ { "title": "Preface", "id": "_preface", "children": [], "tags": [] }, ... { "title": "Machine-Level ISA, Version 1.13", "id": "machine", "children": [ { "title": "Machine-Level CSRs", "id": "_machine_level_csrs", "children": [ { "title": "Machine ISA (misa) Register", "id": "misa", "children": [], "tags": [ "norm:csr:misa:sw_rw", "norm:csr:misa:always_readable", "norm:param:MISA_CSR_IMPLEMENTED:can_be_zero", ... "norm:csrfld:misa:e:not_i", "norm:csrfld:misa:extensions:dependencies", "norm:csr:misa:inc_ialign" ] }, { "title": "Machine Vendor ID (mvendorid) Register", "id": "_machine_vendor_id_mvendorid_register", "children": [], "tags": [ "norm:csr:mvendorid:sz_ro_meaning", "norm:csr:mvendorid:always_readable", "norm:csr:mvendorid:encoding", "norm:csr:mvendorid:bank_1_less_than_JEDEC" ] }, ``` Signed-off-by: Tim Hutt <[email protected]>
1 parent 3a40e07 commit 7a8b9a6

File tree

1 file changed

+58
-1
lines changed

1 file changed

+58
-1
lines changed

converters/tags.rb

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,26 @@
11
require 'json'
22

3+
# This is an Asciidoctor backend that allows extracting the content
4+
# from tagged snippets of text.
35
class TagsConverter
46
include Asciidoctor::Converter
57
register_for("tags")
68

9+
# Hash{String => String} - map from tag ID to its text contents.
710
@tag_map = {}
11+
# Array<String> - the stack of currently open sections in the
12+
# section tree. Each `Section` contains
13+
#
14+
# * title: String - Section title.
15+
# * id: String - Generated ID for the title which can be used for HTML links.
16+
# * children: Array<Section> - Child sections.
17+
# * tags: Array<String> - List of tags in this section directly (not in children).
18+
#
19+
# This always starts with a root section with an empty title and id.
20+
@section_stack = []
21+
22+
# Prefix on IDs that we require. We can't look at all IDs because
23+
# it includes a load of auto-generated ones.
824
@prefix = ""
925

1026
def initialize(backend, opts = {})
@@ -18,23 +34,64 @@ def initialize(backend, opts = {})
1834
# `node` is an `AbstractNode`.
1935
def convert(node, transform = node.node_name, opts = nil)
2036
if transform == "document" then
37+
# This is the top level node. First clear the outputs.
2138
@tag_map = {}
39+
# Root node of the section tree. For simplicity we always
40+
# have one root node with an empty title.
41+
@section_stack = [{
42+
"title" => "",
43+
"id" => "",
44+
"children" => [],
45+
"tags" => [],
46+
}]
47+
2248
# Calling node.content will recursively call convert() on all the nodes
2349
# and also expand blocks, creating inline nodes. We call this to convert
2450
# all nodes to text, and record their content in the tag map. Then we
2551
# throw away the text and output the tag map as JSON instead.
2652
node.content
53+
54+
# We must always add and remove an equal number of sections from the stack
55+
# and we started with one so should end with one.
56+
fail "Tags backend section logic error" if @section_stack.length != 1
57+
2758
JSON.pretty_generate({
2859
"tags": @tag_map,
60+
"sections": @section_stack.first,
2961
})
3062
else
31-
# Output the text content of this node.
63+
64+
# If it's a section add it to the section tree.
65+
if transform == "section" then
66+
section = {
67+
"title" => node.title,
68+
"id" => node.id,
69+
"children" => [],
70+
"tags" => [],
71+
}
72+
73+
@section_stack.last["children"] << section
74+
@section_stack << section
75+
end
76+
77+
# Recursively get the text content of this node.
3278
content = if node.inline? then node.text else node.content end
79+
80+
# Capture the content in the tag map and section tree if
81+
# this node is tagged appropriately.
3382
unless node.id.nil?
3483
if node.id.start_with?(@prefix)
3584
@tag_map[node.id] = content
85+
@section_stack.last["tags"] << node.id
3686
end
3787
end
88+
89+
# If it's a section, we've recursed through it (via `node.content`)
90+
# so pop it from the stack.
91+
if transform == "section" then
92+
@section_stack.pop()
93+
end
94+
3895
content
3996
end
4097
end

0 commit comments

Comments
 (0)