UNPKG

@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
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); } } }