From 33cec82c9d697e247ad261088a0cc11d015589e6 Mon Sep 17 00:00:00 2001 From: bendtherules Date: Mon, 30 Mar 2026 17:58:39 +0530 Subject: [PATCH] feat(build-graph): add comments and pass options --- setup/build_graph.ts | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/setup/build_graph.ts b/setup/build_graph.ts index 41d50d2..8f63177 100644 --- a/setup/build_graph.ts +++ b/setup/build_graph.ts @@ -4,13 +4,26 @@ import * as cheerio from "cheerio"; import { glob } from "glob"; import Graph from "graphology"; +// Directory containing built ECMAScript specification HTML files (ecmarkup output) const SPEC_DIR = "./spec-built/multipage"; +// Directory containing the JavaScript engine implementation source code const CODE_DIR = "./engine262/src"; import { GRAPH_FILE } from "../constants"; +/** + * Builds a knowledge graph mapping ECMAScript specification sections + * to their implementation functions in the JavaScript engine. + * + * The graph contains: + * - Nodes: Spec sections and JS functions + * - Edges: LINKS_TO (spec section references) and IMPLEMENTS (code->spec) + */ async function buildGraph() { - const graph = new Graph({ multi: true }); + // Initialize a multi-graph (allows multiple edges between same nodes) + const graph = new Graph({ multi: true, type: "directed", allowSelfLoops: false }); + + // Phase 1: Discover and parse specification HTML files const htmlFiles = await glob(path.join(SPEC_DIR, "*.html")); console.log( `Found ${htmlFiles.length} specification HTML file(s) in ${SPEC_DIR}`, @@ -19,22 +32,27 @@ async function buildGraph() { console.warn(`Warning: No specification HTML files found in ${SPEC_DIR}`); } + // Phase 2: Extract spec sections and create nodes console.log("Processing specification for graph..."); for (const file of htmlFiles) { const fileName = path.basename(file); const content = fs.readFileSync(file, "utf-8"); const $ = cheerio.load(content); + // ecmarkup generates content within #spec-container const $container = $("#spec-container"); + + // Each specification section is an with an ID $container.find("emu-clause").each((_i, elem) => { const id = $(elem).attr("id"); const title = $(elem).find("h1").first().text().trim(); if (id) { + // Create node for this spec section if not already exists if (!graph.hasNode(id)) { graph.addNode(id, { title, type: "SpecSection", file: fileName }); } - // Extract internal links + // Extract internal links (placeholder for potential future use) $(elem) .find("a[href]") .each((_j, link) => { @@ -51,7 +69,8 @@ async function buildGraph() { }); } - // Add edges for internal links + // Phase 3: Create edges between spec sections based on internal links + // This pass runs after all nodes are created to ensure target nodes exist for (const file of htmlFiles) { const content = fs.readFileSync(file, "utf-8"); const $ = cheerio.load(content); @@ -65,6 +84,7 @@ async function buildGraph() { const href = $(link).attr("href"); if (href?.includes("#")) { const [, targetId] = href.split("#"); + // Only create edge if target node exists and is different from source if ( targetId && graph.hasNode(targetId) && @@ -80,6 +100,7 @@ async function buildGraph() { }); } + // Phase 4: Discover and parse JavaScript implementation files console.log("Processing code for graph..."); const jsFiles = await glob(path.join(CODE_DIR, "**/*.mts")); console.log(`Found ${jsFiles.length} code file(s) in ${CODE_DIR}`); @@ -87,11 +108,13 @@ async function buildGraph() { console.warn(`Warning: No code files found in ${CODE_DIR}`); } + // Phase 5: Extract evaluation functions and link to spec sections for (const file of jsFiles) { const fileName = path.basename(file); const content = fs.readFileSync(file, "utf-8"); - // Simple heuristic: look for function names starting with Evaluate_ + // Pattern matches: export function*? Evaluate_ + // The engine262 project uses this naming convention for spec implementations const functionMatches = content.matchAll( /export function\*? (Evaluate_([a-zA-Z0-9_]+))/g, ); @@ -100,6 +123,7 @@ async function buildGraph() { const shortName = match[2]; const funcNodeId = `func-${fullFuncName}`; + // Create node for this function if not already exists if (!graph.hasNode(funcNodeId)) { graph.addNode(funcNodeId, { name: fullFuncName, @@ -108,8 +132,9 @@ async function buildGraph() { }); } - // Try to link to a spec section - // Heuristic: Evaluate_IfStatement -> sec-if-statement + // Link function to spec section using naming convention heuristic + // Example: Evaluate_IfStatement -> sec-if-statement + // Converts CamelCase to kebab-case: IfStatement -> if-statement const specId = `sec-${shortName.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase()}`; if (graph.hasNode(specId)) { graph.addEdge(funcNodeId, specId, { type: "IMPLEMENTS" }); @@ -117,6 +142,7 @@ async function buildGraph() { } } + // Phase 6: Export the completed graph to JSON console.log(`Graph built with ${graph.order} nodes and ${graph.size} edges.`); fs.writeFileSync(GRAPH_FILE, JSON.stringify(graph.export(), null, 2)); console.log(`Graph saved to ${GRAPH_FILE}`);