fix: rewrite parser to correctly parse parent_subsystem and module hierarchy

This commit is contained in:
2026-03-18 14:46:40 +08:00
parent 6c7ba142ba
commit ace133f0b7

View File

@@ -46,148 +46,219 @@ function extractYamlBlock(markdown: string, sectionName: string): string {
return match ? match[1].trim() : ''; return match ? match[1].trim() : '';
} }
function parseSubsystemList(yaml: string): Subsystem[] { function parseSubsystems(yaml: string): Subsystem[] {
const subsystems: Subsystem[] = []; const subsystems: Subsystem[] = [];
const lines = yaml.split('\n'); const lines = yaml.split('\n');
let currentSubsystem: Partial<Subsystem> | null = null;
let currentField = ''; let current: Partial<Subsystem> = {};
let currentList: string[] = []; let currentField: string | null = null;
for (const line of lines) { for (const line of lines) {
if (line.includes('- name:')) { const trimmed = line.trim();
if (currentSubsystem && currentSubsystem.name) {
if (trimmed.startsWith('subsystems:')) {
continue;
}
if (trimmed.startsWith('- name:')) {
if (current.name) {
subsystems.push({ subsystems.push({
id: currentSubsystem.name, id: current.name,
name: currentSubsystem.name, name: current.name,
responsibilities: currentSubsystem.responsibilities || [], responsibilities: current.responsibilities || [],
provides: currentSubsystem.provides || [], provides: current.provides || [],
depends_on: currentSubsystem.depends_on || [], depends_on: current.depends_on || [],
boundary: currentSubsystem.boundary boundary: current.boundary
}); });
} }
currentSubsystem = { name: line.split('- name:')[1].trim() }; current = {
currentField = ''; name: trimmed.replace('- name:', '').trim(),
currentList = []; responsibilities: [],
} else if (currentSubsystem && line.includes('responsibilities:')) { provides: [],
depends_on: []
};
currentField = null;
continue;
}
if (!current.name) continue;
if (trimmed.startsWith('responsibilities:')) {
currentField = 'responsibilities'; currentField = 'responsibilities';
currentList = []; continue;
} else if (currentSubsystem && line.includes('provides:')) { }
if (trimmed.startsWith('provides:')) {
currentField = 'provides'; currentField = 'provides';
currentList = []; const match = trimmed.match(/\[(.*?)\]/);
} else if (currentSubsystem && line.includes('depends_on:')) {
currentField = 'depends_on';
currentList = [];
} else if (currentSubsystem && line.includes('boundary:')) {
currentField = 'boundary';
currentSubsystem.boundary = { inputs: [], outputs: [] };
} else if (currentSubsystem && currentField === 'responsibilities' && line.includes('-')) {
const val = line.replace('-', '').trim().replace(/"/g, '');
if (val) currentList.push(val);
currentSubsystem.responsibilities = [...currentList];
} else if (currentSubsystem && currentField === 'provides' && line.includes('[')) {
const match = line.match(/\[(.*?)\]/);
if (match) { if (match) {
currentSubsystem.provides = match[1].split(',').map(s => s.trim()); current.provides = match[1].split(',').map(s => s.trim());
}
} else if (currentSubsystem && currentField === 'depends_on' && line.includes('[')) {
const match = line.match(/\[(.*?)\]/);
if (match && match[1].trim()) {
currentSubsystem.depends_on = match[1].split(',').map(s => s.trim());
} else { } else {
currentSubsystem.depends_on = []; current.provides = [];
} }
} else if (currentSubsystem && currentField === 'boundary' && line.includes('inputs:')) { continue;
const match = line.match(/\[(.*?)\]/); }
if (trimmed.startsWith('depends_on:')) {
currentField = 'depends_on';
const match = trimmed.match(/\[(.*?)\]/);
if (match && match[1].trim()) {
current.depends_on = match[1].split(',').map(s => s.trim());
} else {
current.depends_on = [];
}
continue;
}
if (trimmed.startsWith('boundary:')) {
currentField = 'boundary';
current.boundary = { inputs: [], outputs: [] };
continue;
}
if (currentField === 'responsibilities' && trimmed.startsWith('-')) {
const val = trimmed.replace('-', '').trim().replace(/"/g, '');
if (val) {
current.responsibilities = current.responsibilities || [];
current.responsibilities.push(val);
}
continue;
}
if (currentField === 'boundary' && trimmed.startsWith('inputs:')) {
const match = trimmed.match(/\[(.*?)\]/);
if (match) { if (match) {
currentSubsystem.boundary!.inputs = match[1].split(',').map(s => s.trim()); current.boundary!.inputs = match[1].split(',').map(s => s.trim());
} }
} else if (currentSubsystem && currentField === 'boundary' && line.includes('outputs:')) { continue;
const match = line.match(/\[(.*?)\]/); }
if (currentField === 'boundary' && trimmed.startsWith('outputs:')) {
const match = trimmed.match(/\[(.*?)\]/);
if (match) { if (match) {
currentSubsystem.boundary!.outputs = match[1].split(',').map(s => s.trim()); current.boundary!.outputs = match[1].split(',').map(s => s.trim());
} }
continue;
} }
} }
if (currentSubsystem && currentSubsystem.name) { if (current.name) {
subsystems.push({ subsystems.push({
id: currentSubsystem.name, id: current.name,
name: currentSubsystem.name, name: current.name,
responsibilities: currentSubsystem.responsibilities || [], responsibilities: current.responsibilities || [],
provides: currentSubsystem.provides || [], provides: current.provides || [],
depends_on: currentSubsystem.depends_on || [], depends_on: current.depends_on || [],
boundary: currentSubsystem.boundary boundary: current.boundary
}); });
} }
return subsystems; return subsystems;
} }
function parseModuleList(yaml: string): Module[] { function parseModules(yaml: string): Module[] {
const modules: Module[] = []; const modules: Module[] = [];
const lines = yaml.split('\n'); const lines = yaml.split('\n');
let currentModule: Partial<Module> | null = null;
let currentApi: { fn: string; params: { name: string; type: string }[]; returns?: { type: string } } | null = null; let current: Partial<Module> = {};
let currentApi: Module['public_api'][0] | null = null;
let inPublicApi = false; let inPublicApi = false;
let currentField = ''; let inParams = false;
for (const line of lines) { for (const line of lines) {
if (line.includes('- name:') && line.includes('parent_subsystem:')) { const trimmed = line.trim();
if (currentModule && currentModule.name && currentModule.parent_subsystem) {
if (trimmed.startsWith('modules:')) {
continue;
}
if (trimmed.startsWith('- name:')) {
if (current.name && current.parent_subsystem) {
modules.push({ modules.push({
id: currentModule.name, id: current.name,
name: currentModule.name, name: current.name,
parent_subsystem: currentModule.parent_subsystem, parent_subsystem: current.parent_subsystem,
responsibility: currentModule.responsibility || '', responsibility: current.responsibility || '',
public_api: currentModule.public_api || [] public_api: current.public_api || []
}); });
} }
const nameMatch = line.match(/- name:\s*(\S+)/); current = {
const parentMatch = line.match(/parent_subsystem:\s*(\S+)/); name: trimmed.replace('- name:', '').trim(),
currentModule = { parent_subsystem: '',
name: nameMatch ? nameMatch[1] : '', responsibility: '',
parent_subsystem: parentMatch ? parentMatch[1] : '',
public_api: [] public_api: []
}; };
inPublicApi = false; inPublicApi = false;
} else if (currentModule && line.includes('responsibility:')) { inParams = false;
const match = line.match(/responsibility:\s*"(.+)"/); currentApi = null;
continue;
}
if (!current.name) continue;
if (trimmed.startsWith('parent_subsystem:')) {
current.parent_subsystem = trimmed.replace('parent_subsystem:', '').trim();
continue;
}
if (trimmed.startsWith('responsibility:')) {
const match = trimmed.match(/responsibility:\s*"(.+)"/);
if (match) { if (match) {
currentModule.responsibility = match[1]; current.responsibility = match[1];
} }
} else if (currentModule && line.includes('public_api:')) { continue;
}
if (trimmed.startsWith('public_api:')) {
inPublicApi = true; inPublicApi = true;
} else if (currentModule && inPublicApi && line.includes('- fn:')) { continue;
const fnMatch = line.match(/- fn:\s*(\S+)/); }
if (fnMatch) {
currentApi = { fn: fnMatch[1], params: [] }; if (inPublicApi && trimmed.startsWith('- fn:')) {
currentModule.public_api = currentModule.public_api || []; currentApi = {
currentModule.public_api.push(currentApi); fn: trimmed.replace('- fn:', '').trim(),
} params: []
} else if (currentModule && currentApi && line.includes('params:')) { };
currentField = 'params'; current.public_api = current.public_api || [];
} else if (currentModule && currentApi && line.includes('returns:')) { current.public_api.push(currentApi);
currentField = 'returns'; inParams = false;
const returnsMatch = line.match(/returns:\s*type:\s*(\S+)/); continue;
if (returnsMatch) { }
currentApi.returns = { type: returnsMatch[1] };
} if (currentApi && trimmed.startsWith('params:')) {
} else if (currentModule && currentApi && currentField === 'params' && line.includes('name:')) { inParams = true;
const nameMatch = line.match(/name:\s*(\S+)/); continue;
}
if (currentApi && inParams && trimmed.startsWith('- name:')) {
const nameMatch = trimmed.match(/- name:\s*(\S+)/);
const typeMatch = line.match(/type:\s*(\S+)/); const typeMatch = line.match(/type:\s*(\S+)/);
if (nameMatch && typeMatch) { if (nameMatch && typeMatch) {
currentApi.params.push({ name: nameMatch[1], type: typeMatch[1] }); currentApi.params.push({
name: nameMatch[1],
type: typeMatch[1]
});
} }
continue;
}
if (currentApi && trimmed.startsWith('returns:')) {
const match = trimmed.match(/returns:\s*type:\s*(\S+)/);
if (match) {
currentApi.returns = { type: match[1] };
}
inParams = false;
continue;
} }
} }
if (currentModule && currentModule.name) { if (current.name && current.parent_subsystem) {
modules.push({ modules.push({
id: currentModule.name, id: current.name,
name: currentModule.name, name: current.name,
parent_subsystem: currentModule.parent_subsystem || '', parent_subsystem: current.parent_subsystem,
responsibility: currentModule.responsibility || '', responsibility: current.responsibility || '',
public_api: currentModule.public_api || [] public_api: current.public_api || []
}); });
} }
@@ -208,22 +279,22 @@ export function parseBlueprintFromMd(markdown: string): BlueprintData {
const metaLines = metaYaml.split('\n'); const metaLines = metaYaml.split('\n');
for (const line of metaLines) { for (const line of metaLines) {
if (line.includes('name:')) { const trimmed = line.trim();
meta.name = line.split('name:')[1].trim().replace(/"/g, ''); if (trimmed.startsWith('name:')) {
} else if (line.includes('version:')) { meta.name = trimmed.replace('name:', '').trim().replace(/"/g, '');
const ver = line.split('version:')[1].trim(); } else if (trimmed.startsWith('version:')) {
meta.version = ver; meta.version = trimmed.replace('version:', '').trim();
} else if (line.includes('type:')) { } else if (trimmed.startsWith('type:')) {
meta.type = line.split('type:')[1].trim().replace(/"/g, ''); meta.type = trimmed.replace('type:', '').trim().replace(/"/g, '');
} else if (line.includes('description:')) { } else if (trimmed.startsWith('description:')) {
meta.description = line.split('description:')[1].trim().replace(/"/g, ''); meta.description = trimmed.replace('description:', '').trim().replace(/"/g, '');
} else if (line.includes('target_runtime:')) { } else if (trimmed.startsWith('target_runtime:')) {
meta.target_runtime = line.split('target_runtime:')[1].trim().replace(/"/g, ''); meta.target_runtime = trimmed.replace('target_runtime:', '').trim().replace(/"/g, '');
} }
} }
const subsystems = parseSubsystemList(structureYaml); const subsystems = parseSubsystems(structureYaml);
const modules = parseModuleList(structureYaml); const modules = parseModules(structureYaml);
return { meta, subsystems, modules }; return { meta, subsystems, modules };
} }