@tricoteuses/arbre-de-la-loi
Version:
Generate ASTs from the French bills & laws; manipulate & export them to Markdown, etc.
404 lines (403 loc) • 14.7 kB
JavaScript
import xastToString from "xast-util-to-string";
import { DocumentNodeType, documentNodeWithHeadlinesTypes, } from "./documents";
import { HastType } from "./hast";
import hastFromXast from "./hast_from_xast";
import { assertNeverXastNode, XastType, } from "./xast";
export function billFromSenatAkomaNtosoXast(node) {
const bill = { children: [], type: DocumentNodeType.Bill };
for (const child of node.children) {
switch (child.type) {
case XastType.Cdata:
case XastType.Comment:
warnUnexpectedNode(child, node);
break;
case XastType.Doctype:
break;
case XastType.Element:
if (child.name === "akomaNtoso") {
convertAkomaNtoso(child, bill);
}
else {
warnUnexpectedElement(child, node);
}
break;
case XastType.Instruction:
break;
case XastType.Text:
warnWhenNonEmptyText(child, node);
break;
default:
assertNeverXastNode(child);
}
}
return bill;
}
function convertAkomaNtoso(node, bill) {
console.assert(node.name === "akomaNtoso", `convertAkomaNtoso: Unexpected node name: ${node.name}`);
for (const child of node.children) {
switch (child.type) {
case XastType.Cdata:
case XastType.Comment:
case XastType.Instruction:
warnUnexpectedNode(child, node);
break;
case XastType.Element:
if (child.name === "bill") {
convertBill(child, bill);
}
else {
warnUnexpectedElement(child, node);
}
break;
case XastType.Text:
warnWhenNonEmptyText(child, node);
break;
default:
assertNeverXastNode(child);
}
}
}
function convertBill(node, bill) {
console.assert(node.name === "bill", `convertBill: Unexpected node name: ${node.name}`);
// const name = node.attributes?.name
// if (name !== undefined) {
// bill.name = name
// }
for (const child of node.children) {
switch (child.type) {
case XastType.Cdata:
case XastType.Comment:
case XastType.Instruction:
warnUnexpectedNode(child, node);
break;
case XastType.Element:
switch (child.name) {
case "body":
convertBody(child, bill);
break;
case "meta":
break;
case "preamble":
convertPreamble(child, bill);
break;
default:
warnUnexpectedElement(child, node);
}
break;
case XastType.Text:
warnWhenNonEmptyText(child, node);
break;
default:
assertNeverXastNode(child);
}
}
}
function convertBody(node, bill) {
console.assert(node.name === "body", `convertBody: Unexpected node name: ${node.name}`);
for (const child of node.children) {
switch (child.type) {
case XastType.Cdata:
case XastType.Comment:
case XastType.Instruction:
warnUnexpectedNode(child, node);
break;
case XastType.Element:
switch (child.name) {
case "article":
convertToArticle(child, bill, bill);
break;
case "chapter":
case "part":
case "title":
convertToHierarchyItem(child, bill, bill);
break;
default:
warnUnexpectedElement(child, node);
}
break;
case XastType.Text:
warnWhenNonEmptyText(child, node);
break;
default:
assertNeverXastNode(child);
}
}
}
function convertPreamble(node, bill) {
console.assert(node.name === "preamble", `convertPreamble: Unexpected node name: ${node.name}`);
for (const child of node.children) {
switch (child.type) {
case XastType.Cdata:
case XastType.Comment:
case XastType.Instruction:
warnUnexpectedNode(child, node);
break;
case XastType.Element:
switch (child.name) {
case "docTitle":
console.assert(bill.headlines === undefined, "convertPreamble: bill.preamble.docTitle is present more than once");
bill.headlines = [{ type: HastType.Text, value: xastToString(child) }];
break;
default:
warnUnexpectedElement(child, node);
}
break;
case XastType.Text:
warnWhenNonEmptyText(child, node);
break;
default:
assertNeverXastNode(child);
}
}
}
function convertToAlinea(node, _bill, parent) {
console.assert(node.name === "alinea", `convertToAlinea: Unexpected node name: ${node.name}`);
if (parent.lines === undefined) {
parent.lines = [];
}
// const alinea: Alinea = {
// content: [],
// number: -1,
// type: DocumentNodeType.Alinea,
// }
// const chip = node.attributes?.["data:pastille"]
// if (chip !== undefined) {
// alinea.number = parseInt(chip)
// }
// const eId = node.attributes?.eId
// if (eId !== undefined) {
// alinea.eId = eId
// }
// const guid = node.attributes?.GUID
// if (guid !== undefined) {
// alinea.guid = guid
// }
for (const child of node.children) {
switch (child.type) {
case XastType.Cdata:
case XastType.Comment:
case XastType.Instruction:
warnUnexpectedNode(child, node);
break;
case XastType.Element:
switch (child.name) {
case "content":
for (const xastNode of child.children) {
parent.lines.push(hastFromXast(xastNode));
}
break;
default:
warnUnexpectedElement(child, node);
}
break;
case XastType.Text:
warnWhenNonEmptyText(child, node);
break;
default:
assertNeverXastNode(child);
}
}
}
function convertToAnnex(node, _bill, parent) {
const annex = {
lines: [],
type: DocumentNodeType.Annex,
};
const eId = node.attributes?.eId;
if (eId !== undefined) {
annex.eId = eId;
}
const guid = node.attributes?.GUID;
if (guid !== undefined) {
annex.guid = guid;
}
for (const child of node.children) {
switch (child.type) {
case XastType.Cdata:
case XastType.Comment:
case XastType.Instruction:
warnUnexpectedNode(child, node);
break;
case XastType.Element:
switch (child.name) {
case "alinea":
// TODO
break;
case "headlines":
console.assert(annex.headlines === undefined, `convertToAnnex: ${node.name}.headlines is present more than once`);
annex.headlines = [
{ type: HastType.Text, value: xastToString(child) },
];
break;
case "num":
// console.assert(
// annex.number === undefined,
// `convertToAnnex: ${node.name}.num is present more than once`,
// )
// annex.number = xastToString(child)
break;
default:
warnUnexpectedElement(child, node);
}
break;
case XastType.Text:
warnWhenNonEmptyText(child, node);
break;
default:
assertNeverXastNode(child);
}
}
parent.children.push(annex);
}
function convertToArticle(node, bill, parent) {
console.assert(node.name === "article", `convertToArticle: Unexpected node name: ${node.name}`);
if (node.name === "article" && node.attributes?.class === "annexe") {
return convertToAnnex(node, bill, parent);
}
const article = {
children: [],
type: DocumentNodeType.Article,
};
const eId = node.attributes?.eId;
if (eId !== undefined) {
article.eId = eId;
}
const guid = node.attributes?.GUID;
if (guid !== undefined) {
article.guid = guid;
}
for (const child of node.children) {
switch (child.type) {
case XastType.Cdata:
case XastType.Comment:
case XastType.Instruction:
warnUnexpectedNode(child, node);
break;
case XastType.Element:
switch (child.name) {
case "alinea":
convertToAlinea(child, bill, article);
break;
case "num":
// console.assert(
// article.number === undefined,
// `convertToArticle: ${node.name}.num is present more than once`,
// )
// article.number = xastToString(child)
break;
default:
warnUnexpectedElement(child, node);
}
break;
case XastType.Text:
warnWhenNonEmptyText(child, node);
break;
default:
assertNeverXastNode(child);
}
}
parent.children.push(article);
}
function convertToHierarchyItem(node, bill, parent) {
console.assert(["article", "chapter", "title", "part"].includes(node.name), `convertToHierarchyItem: Unexpected node name: ${node.name}`);
const hierarchy = {
children: [],
type: node.name,
};
if (node.name === "article" && node.attributes?.class === "annexe") {
;
hierarchy.type = DocumentNodeType.Annex;
}
const eId = node.attributes?.eId;
if (eId !== undefined) {
hierarchy.eId = eId;
}
const guid = node.attributes?.GUID;
if (guid !== undefined) {
hierarchy.guid = guid;
}
for (const child of node.children) {
switch (child.type) {
case XastType.Cdata:
case XastType.Comment:
case XastType.Instruction:
warnUnexpectedNode(child, node);
break;
case XastType.Element:
switch (child.name) {
case "article":
convertToArticle(child, bill, hierarchy);
break;
case "chapter":
case "part":
case "title":
convertToHierarchyItem(child, bill, hierarchy);
break;
case "headlines":
console.assert(documentNodeWithHeadlinesTypes.has(hierarchy.type), "convertToHierarchyItem: Element ${node.name} is not expected to have a headlines");
console.assert(hierarchy.headlines === undefined, `convertToHierarchyItem: ${node.name}.headlines is present more than once`);
hierarchy.headlines = [
{ type: HastType.Text, value: xastToString(child) },
];
break;
case "num":
// console.assert(
// hierarchy.number === undefined,
// `convertToHierarchyItem: ${node.name}.num is present more than once`,
// )
// hierarchy.number = xastToString(child)
break;
default:
warnUnexpectedElement(child, node);
}
break;
case XastType.Text:
warnWhenNonEmptyText(child, node);
break;
default:
assertNeverXastNode(child);
}
}
parent.children.push(hierarchy);
}
function warnUnexpectedElement(node, parent) {
switch (parent.type) {
case XastType.Element:
console.warn(`Unexpected element of name "${node.name}" in node of name: ${parent.name}`);
break;
case XastType.Root:
console.warn(`Unexpected element of name "${node.name}" in node of type: ${parent.type}`);
break;
default:
assertNeverXastNode(parent);
}
// console.warn(JSON.stringify(parent, null, 2))
}
function warnUnexpectedNode(node, parent) {
switch (parent.type) {
case XastType.Element:
console.warn(`Unexpected node of type "${node.type}" in node of name: ${parent.name}`);
break;
case XastType.Root:
console.warn(`Unexpected node of type "${node.type}" in node of type: ${parent.type}`);
break;
default:
assertNeverXastNode(parent);
}
// console.warn(JSON.stringify(parent, null, 2))
}
function warnWhenNonEmptyText(node, parent) {
// See <https://html.spec.whatwg.org/#space-character>.
if (!node.value.match(/^[ \t\n\f\r]*$/)) {
switch (parent.type) {
case XastType.Element:
console.warn(`Unexpected non empty text "${node.value}" in node of name: ${parent.name}`);
break;
case XastType.Root:
console.warn(`Unexpected non empty text "${node.value}" in node of type: ${parent.type}`);
break;
default:
assertNeverXastNode(parent);
}
}
}