Arsitektur parser di project ini dirancang supaya menambah bahasa baru tidak perlu menyentuh UI sama sekali. Cukup buat adapter baru, daftarkan ke registry, dan semua otomatis muncul — language selector, parser badge, sample code.
Source Code
│
▼
IParserAdapter.parse() → menghasilkan AST (format bebas, tiap adapter beda)
│
▼
IParserAdapter.toDiagram() → menghasilkan Mermaid diagram string
│
▼
UI render (PanZoomDiagram)
UI tidak tahu format AST-nya apa — itu urusan adapter masing-masing.
Yang penting: parse() dan toDiagram() mengikuti contract IParserAdapter.
src/lib/parser/adapters/
├── acorn/ ← JavaScript / TypeScript (sudah ada)
├── python-lezer/ ← Python scaffold (sudah ada)
└── nama-bahasa/ ← buat di sini
├── index.ts ← implementasi IParserAdapter
└── sampleCode.ts ← contoh kode untuk editor
Pilih parser yang bisa jalan di browser (no WASM murni, atau WASM yang ada NPM package-nya):
| Bahasa | Parser yang disarankan | NPM Package |
|---|---|---|
| Python | Lezer Python | @lezer/python |
| Java | java-parser | java-parser |
| PHP | @prettier/plugin-php AST |
php-parser |
| Rust | Lezer Rust | @lezer/rust (community) |
| Go | Lezer Go | @lezer/go (community) |
| C / C++ | Lezer C++ | @lezer/cpp |
| Ruby | ruby-wasm + parser | @ruby/wasm |
npm install <nama-package>Minimal yang harus ada:
import type {
IParserAdapter,
ILanguage,
ParseResult,
DiagramResult,
DiagramTypeInfo,
} from "../../core/types";
import type { FlowDirection } from "../../../astParser/astToMermaid";
class MyLanguageAdapter implements IParserAdapter {
// ── Identitas ──────────────────────────────────────────────────────────────
readonly id = "my-language"; // unik, kebab-case
readonly name = "My Language Parser"; // ditampilkan di UI
readonly description = "...";
// Ditampilkan sebagai badge "Parser: nama-versi" di header
readonly parserLibrary = "my-parser-lib";
readonly parserVersion = "1.2.3"; // samakan dengan package.json
readonly supportedLanguages: ILanguage[] = [
{
id: "mylang", // dipakai sebagai key di registry
name: "My Language", // tampil di language selector
ext: ".ml",
monacoId: "mylang", // Monaco language ID (jika ada)
icon: "🔧",
},
];
readonly supportedDiagramTypes: DiagramTypeInfo[] = [
{
type: "flowchart",
name: "Flowchart",
description: "Control-flow per function",
},
// tambahkan diagram type lain jika perlu
];
// ── parse() ───────────────────────────────────────────────────────────────
parse(code: string, languageId: string): ParseResult {
try {
// Gunakan parser library pilihan kamu
const ast = myParserLib.parse(code);
return {
success: true,
ast, // format bebas — kamu yang consume di toDiagram()
language: languageId,
adapterId: this.id,
error: null,
};
} catch (err: unknown) {
const e = err as { message?: string; line?: number; column?: number };
return {
success: false,
ast: null,
language: languageId,
adapterId: this.id,
error: {
message: e.message ?? "Parse error",
line: e.line,
column: e.column,
},
};
}
}
// ── toDiagram() ──────────────────────────────────────────────────────────
toDiagram(
ast: unknown,
diagramType: string,
direction: FlowDirection = "TD",
): DiagramResult {
switch (diagramType) {
case "flowchart":
return generateFlowchart(ast, direction);
default:
return {
success: false,
diagram: "",
error: `Diagram type "${diagramType}" not supported.`,
};
}
}
// ── getSampleCode() ───────────────────────────────────────────────────────
getSampleCode(_languageId: string): string {
return `// contoh kode My Language`;
}
}
export const myLanguageAdapter = new MyLanguageAdapter();Buka src/lib/parser/index.ts dan tambahkan 2 baris:
// di bagian exports
export { myLanguageAdapter } from "./adapters/nama-bahasa/index";
// di bagian auto-registration
import { myLanguageAdapter } from "./adapters/nama-bahasa/index";
parserRegistry.register(myLanguageAdapter);Selesai. Tidak ada file lain yang perlu diubah.
Untuk flowchart multi-fungsi, toDiagram() harus mengembalikan beberapa blok
Mermaid yang dipisah %%SPLIT%%. UI akan memisah dan merender masing-masing
sebagai card tersendiri.
Setiap blok juga bisa menyertakan metadata comment untuk grouping di UI:
flowchart TD
%% fn: methodName ⚡ ← nama yang ditampilkan di title bar card
%% class: ClassName ← (opsional) group card ini di bawah class header
start(["▶ ClassName.method"])
...
%% fn: <title>→ wajib, menjadi judul card%% class: <nama>→ opsional, jika ada maka card dikelompokkan dalam class group⚡di nama → otomatis memunculkan badge "async" kuning
Contoh output toDiagram() untuk 2 method dalam 1 class:
flowchart TD
%% fn: constructor
%% class: Dog
...
%%SPLIT%%
flowchart TD
%% fn: makeSound
%% class: Dog
...
%%SPLIT%%
flowchart TD
%% fn: greet ⚡
...
Setiap parser punya format AST berbeda. Pola umumnya:
function walk(node: MyAstNode): void {
switch (node.type) {
case "FunctionDeclaration":
handleFunction(node);
break;
case "ClassDeclaration":
handleClass(node);
break;
default:
// rekursi ke children
for (const child of node.children ?? []) {
walk(child);
}
}
}Gunakan counter sederhana supaya ID Mermaid tidak tabrakan antar diagram:
let nodeCounter = 0;
function freshId(prefix = "n"): string {
return `${prefix}${nodeCounter++}`;
}
// Reset counter di awal tiap diagram blockLabel yang mengandung ", [, ], (, ) bisa merusak syntax Mermaid:
function sanitizeLabel(text: string): string {
return text.replace(/["[\](){}]/g, (c) => `&#${c.charCodeAt(0)};`);
}Jika bahasa kamu belum ada di Monaco secara default, tambahkan di
src/lib/Components/MonacoEditor.svelte saat instance Monaco dibuat:
monaco.languages.register({ id: "mylang" });
monaco.languages.setMonarchTokensProvider("mylang", {
tokenizer: {
root: [
[/keywords/, "keyword"],
// ... token rules
],
},
});Kalau bahasa sudah didukung Monaco (python, java, go, rust, cpp, dll),
cukup set monacoId yang sesuai di ILanguage dan Monaco otomatis
memberikan syntax highlighting.
-
idadapter unik (tidak tabrakan dengan adapter lain) -
parserLibrarydanparserVersionsesuai dengan versi dipackage.json -
parse()mengembalikanerror.linedanerror.columnjika parser menyediakan -
toDiagram()mengembalikan error yang deskriptif jika tidak ada fungsi/class -
getSampleCode()mengembalikan kode yang cukup menarik untuk demo OOP - Adapter didaftarkan di
src/lib/parser/index.ts -
npm run checktidak menghasilkan error baru