Skip to content

Commit 6c5a86d

Browse files
committed
Read Doxygen XML file
1 parent 724b4ee commit 6c5a86d

File tree

4 files changed

+379
-9
lines changed

4 files changed

+379
-9
lines changed

scripts/js/lib/api/c/directMapXmlToMdx.test.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import { expect, test } from "@playwright/test";
1414
import { toMarkdown } from "mdast-util-to-markdown";
15+
import { Root as MdastRoot } from "mdast";
1516

1617
import { directMapXmlToMdx } from "./directMapXmlToMdx.js";
1718
import { xmlParser, collapseWhitespace } from "./xmlToMdx.js";
@@ -20,7 +21,10 @@ import { xmlParser, collapseWhitespace } from "./xmlToMdx.js";
2021
// This is easier than writing and reading the XML and MDX ASTs.
2122
function directMapXmlToMdxString(xml: string): string {
2223
const parsedXml = xmlParser.parse(xml);
23-
const mdastRoot = directMapXmlToMdx(parsedXml);
24+
const mdastRoot: MdastRoot = {
25+
type: "root",
26+
children: directMapXmlToMdx(parsedXml),
27+
};
2428
return collapseWhitespace(toMarkdown(mdastRoot));
2529
}
2630

scripts/js/lib/api/c/directMapXmlToMdx.ts

+7-8
Original file line numberDiff line numberDiff line change
@@ -28,24 +28,23 @@ import type {
2828
// * The XML `<parameterlist>` does NOT have a 1:1 mapping in MDX (we could map
2929
// to a list, but how would we map back?). Therefore, MdxMappableXmlNodes can't
3030
// have this as a descendent.
31-
type MdxMappableXmlNode =
31+
export type MdxMappableXmlNode =
3232
| XmlTextNode
3333
| XmlListNode
3434
| { title: MdxMappableXmlNode[] }
3535
| { para: MdxMappableXmlNode[] }
3636
| { computeroutput: MdxMappableXmlNode[] }
3737
| { verbatim: XmlTextNode[] };
38-
type XmlTextNode = { "#text": string };
38+
export type XmlTextNode = { "#text": string };
3939
type XmlListNode = { itemizedlist: Array<{ listitem: MdxMappableXmlNode[] }> };
4040

4141
/**
4242
* Try to map the XML tree to a markdown AST (mdast) as closely as possible.
4343
*/
44-
export function directMapXmlToMdx(nodes: MdxMappableXmlNode[]): MdastRoot {
45-
return {
46-
type: "root",
47-
children: nodes.flatMap((n) => xmlToBlockNodes(n, 1)),
48-
};
44+
export function directMapXmlToMdx(
45+
nodes: MdxMappableXmlNode[],
46+
): MdastBlockContent[] {
47+
return nodes.flatMap((n) => xmlToBlockNodes(n, 1));
4948
}
5049

5150
/**
@@ -131,7 +130,7 @@ function xmlListToMdx(node: XmlListNode): MdastList {
131130
return { type: "list", children };
132131
}
133132

134-
function extractText(textNodes: XmlTextNode[]): string {
133+
export function extractText(textNodes: XmlTextNode[]): string {
135134
return textNodes.map((node) => node["#text"]).join();
136135
}
137136

scripts/js/lib/api/c/readXml.test.ts

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// This code is a Qiskit project.
2+
//
3+
// (C) Copyright IBM 2025.
4+
//
5+
// This code is licensed under the Apache License, Version 2.0. You may
6+
// obtain a copy of this license in the LICENSE file in the root directory
7+
// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8+
//
9+
// Any modifications or derivative works of this code must retain this
10+
// copyright notice, and modified files need to carry a notice indicating
11+
// that they have been altered from the originals.
12+
13+
import { expect, test } from "@playwright/test";
14+
import { toMarkdown as rootToMarkdown } from "mdast-util-to-markdown";
15+
16+
import { readXml, collapseWhitespace } from "./readXml";
17+
18+
function toMarkdown(mdast: any): string {
19+
return collapseWhitespace(rootToMarkdown({ type: "root", children: mdast }));
20+
}
21+
22+
test.describe("directMapXmlToMdx", () => {
23+
test("page with no members", async () => {
24+
const xmlString = `<?xml version='1.0' encoding='UTF-8' standalone='no'?>
25+
<doxygen xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="compound.xsd" version="1.12.0" xml:lang="en-US">
26+
<compounddef id="group__QkSparseObservable" kind="group">
27+
<compoundname>QkSparseObservable</compoundname>
28+
<title>QkSparseObservable</title>
29+
<sectiondef kind="func">
30+
</sectiondef>
31+
<briefdescription>
32+
</briefdescription>
33+
<detaileddescription>
34+
<para>This is a group of functions for interacting with an opaque (Rust-space) SparseObservable instance. </para>
35+
</detaileddescription>
36+
</compounddef>
37+
</doxygen>
38+
`;
39+
const result = readXml(xmlString);
40+
expect(result.title).toEqual("QkSparseObservable");
41+
expect(toMarkdown(result.description)).toEqual(
42+
"This is a group of functions for interacting with an opaque (Rust-space) SparseObservable instance.",
43+
);
44+
});
45+
46+
test("page with a function", async () => {
47+
const xmlString = `<?xml version='1.0' encoding='UTF-8' standalone='no'?>
48+
<doxygen xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="compound.xsd" version="1.12.0" xml:lang="en-US">
49+
<compounddef id="group__QkSparseObservable" kind="group">
50+
<compoundname>Page title</compoundname>
51+
<title>Page title</title>
52+
<detaileddescription>Page description.</detaileddescription>
53+
<sectiondef kind="func">
54+
<memberdef kind="function" id="group__QkSparseObservable_1gaf6fff59681bd7c1dd6fc1164b5b1568d" prot="public" static="no" const="no" explicit="no" inline="no" virt="non-virtual">
55+
<type><ref refid="qiskit_8h_1aa11cd87e227f80467b3ba34cf41904e6" kindref="member">QkSparseObservable</ref> *</type>
56+
<definition>QkSparseObservable * qk_obs_zero</definition>
57+
<argsstring>(uint32_t num_qubits)</argsstring>
58+
<name>qk_obs_zero</name>
59+
<param>
60+
<type>uint32_t</type>
61+
<declname>num_qubits</declname>
62+
</param>
63+
<briefdescription>
64+
</briefdescription>
65+
<detaileddescription>
66+
<para>Construct the zero observable (without any terms).</para>
67+
<para>
68+
<parameterlist kind="param">
69+
<parameteritem>
70+
<parameternamelist>
71+
<parametername>num_qubits</parametername>
72+
</parameternamelist>
73+
<parameterdescription>
74+
<para>The number of qubits the observable is defined on.</para>
75+
</parameterdescription>
76+
</parameteritem>
77+
</parameterlist>
78+
<simplesect kind="return">
79+
<para>A pointer to the created observable.</para>
80+
</simplesect>
81+
</para>
82+
<sect1 id="group__QkSparseObservable_1autotoc_md3">
83+
<title>Example</title><para><verbatim>QkSparseObservable *zero = qk_obs_zero(100);
84+
</verbatim> </para>
85+
</sect1>
86+
</detaileddescription>
87+
<inbodydescription>
88+
</inbodydescription>
89+
<location file="dist/c/include/qiskit.h" line="149" column="20" declfile="dist/c/include/qiskit.h" declline="149" declcolumn="20"/>
90+
</memberdef>
91+
</sectiondef>
92+
</compounddef>
93+
</doxygen>
94+
`;
95+
const result = readXml(xmlString);
96+
expect(result.title).toEqual("Page title");
97+
expect(toMarkdown(result.description)).toEqual("Page description.");
98+
const func = result.functions.pop();
99+
expect(func).toBeDefined();
100+
expect(func?.name).toEqual("qk_obs_zero");
101+
expect(func?.signature).toEqual(
102+
"QkSparseObservable * qk_obs_zero(uint32_t num_qubits)",
103+
);
104+
expect(toMarkdown(func?.description)).toEqual(
105+
"Construct the zero observable (without any terms).",
106+
);
107+
const parameter = func?.parameters.pop();
108+
expect(parameter).toBeDefined();
109+
expect(parameter?.name).toEqual("num_qubits");
110+
expect(toMarkdown(parameter?.description)).toEqual(
111+
"The number of qubits the observable is defined on.",
112+
);
113+
expect(toMarkdown(func?.returns)).toEqual(
114+
"A pointer to the created observable.",
115+
);
116+
});
117+
});

0 commit comments

Comments
 (0)