Code 2 Flowchart

Entradas Archivos Wallpaper Problemas

Este es un programa que pense hacer en los primeros semestres cuando me pedian hacer un diagrama de flujo de los programas que hacia, este programa usa treesitter para poder obtener la syntaxis de cualquier lenguaje. El programa lo hice en TS para que pueda ser usado en cualquier lado

Parsing

Instalamos el parser desde el package manager

bun add tree-sitter-c # para el caso de C

Ya instalados los movemos para que se puedan usar, en este caso se hace como script en el package.json

"setup-wasm": "mkdir -p static/wasm && cp node_modules/web-tree-sitter/tree-sitter.wasm static/wasm/ && cp node_modules/tree-sitter-javascript/tree-sitter-javascript.wasm static/wasm/ && cp node_modules/tree-sitter-python/tree-sitter-python.wasm static/wasm/ && cp node_modules/tree-sitter-c/tree-sitter-c.wasm static/wasm/ && cp node_modules/tree-sitter-go/tree-sitter-go.wasm static/wasm/ && cp node_modules/tree-sitter-rust/tree-sitter-rust.wasm static/wasm/ && cp node_modules/tree-sitter-java/tree-sitter-java.wasm static/wasm/ && cp node_modules/tree-sitter-cpp/tree-sitter-cpp.wasm static/wasm/"

Importamos tree-sitter y cargamos el parser

import { Parser, Language, type SyntaxNode } from "web-tree-sitter";

  let selectedLanguageId = "c";
  let sourceCode = exampleCodes[selectedLanguageId];
  onMount(async () => {
    try {
      await Parser.init({
        locateFile: (path) => `/wasm/${path}`,
      });
      parser = new Parser();

      await Promise.all(
        LANGUAGES.map(async (lang) => {
          loadingMessage = `Cargando gramática de ${lang.name}...`;
          const language = await Language.load(`/wasm/${lang.wasmFile}`);
          loadedLanguages.set(lang.id, language);
        }),
      );

      isLoading = false;
      await parseCode();
    } catch (error) {
      console.error("Error al inicializar Tree-sitter:", error);
      errorMessage =
        "No se pudo cargar el parser. Verifique la carpeta public/wasm/.";
      isLoading = false;
    }
  });

Luego podemos parserar el codigo fuente simplemente con

  var ast_Output = "";

  async function parseCode() {
    if (isLoading || !parser) return;

    const currentLanguage = loadedLanguages.get(selectedLanguageId);
    if (!currentLanguage) return;

    try {
      parser.setLanguage(currentLanguage);
      const tree = parser.parse(sourceCode);
      flowchartNodes = astToFlowNodes(tree.rootNode);
      ast_Output = tree?.rootNode.toString();
    } catch (e) {
      console.error(e);
    }
  }

Y luego recorremos el arbol de sintaxis de manera recursiva, identificando cada elemento y asignandole un tipo y un estilo para el nodo en el diagrama

function createFlowNode(
  id: string,
  text: string,
  type: FlowNode["type"],
  targets: string[],
): FlowNode {
  return { id, text, type, targets };
}

function addNode(
  id: string,
  text: string,
  type: FlowNode["type"],
  targets: string[] = [],
): FlowNode {
  const node = createFlowNode(id, text, type, targets);
  nodes.push(node);
  return node;
}

function connectNodes(sourceIds: string[], targetId: string) {
  for (const sourceId of sourceIds) {
    const sourceNode = nodes.find((n) => n.id === sourceId);
    if (sourceNode && !sourceNode.targets.includes(targetId)) {
      sourceNode.targets.push(targetId);
    }
  }
}

Y dependiendo del tipo del nodo se le asigna un estilo en especifico

      // 3. Decisions (If)
      if (
        ["if_statement", "elif_clause", "if_expression"].includes(node.type)
      ) {
        const condition = node.childForFieldName("condition");
        const consequence = node.childForFieldName("consequence");
        const alternative = node.childForFieldName("alternative");

        const decisionId = getNewId("decision");
        addNode(decisionId, cleanText(condition?.text ?? "?"), "decision");
        connectNodes(entryIds, decisionId);

        let exitPaths: string[] = [];
        if (consequence) exitPaths.push(...walk(consequence, [decisionId]));
        if (alternative) {
          exitPaths.push(...walk(alternative, [decisionId]));
        } else {
          exitPaths.push(decisionId);
        }
        return exitPaths;
      }