mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-21 08:19:43 +02:00
feat: implement ANTLR generation functionality with CLI support
on-behalf-of: @Mermaid-Chart <hello@mermaidchart.com>
This commit is contained in:
92
.build/antlr-cli.ts
Normal file
92
.build/antlr-cli.ts
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
|
import { exec } from 'node:child_process';
|
||||||
|
import { promisify } from 'node:util';
|
||||||
|
import { resolve, dirname } from 'node:path';
|
||||||
|
import { readFile, mkdir, access } from 'node:fs/promises';
|
||||||
|
import { existsSync } from 'node:fs';
|
||||||
|
|
||||||
|
const execAsync = promisify(exec);
|
||||||
|
|
||||||
|
interface AntlrGrammarConfig {
|
||||||
|
id: string;
|
||||||
|
grammar: string;
|
||||||
|
outputDir: string;
|
||||||
|
language: string;
|
||||||
|
generateVisitor?: boolean;
|
||||||
|
generateListener?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AntlrConfig {
|
||||||
|
projectName: string;
|
||||||
|
grammars: AntlrGrammarConfig[];
|
||||||
|
mode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function generateFromConfig(configFile: string): Promise<void> {
|
||||||
|
const configPath = resolve(configFile);
|
||||||
|
|
||||||
|
if (!existsSync(configPath)) {
|
||||||
|
throw new Error(`ANTLR config file not found: ${configPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const configContent = await readFile(configPath, 'utf-8');
|
||||||
|
const config: AntlrConfig = JSON.parse(configContent);
|
||||||
|
|
||||||
|
const configDir = dirname(configPath);
|
||||||
|
|
||||||
|
for (const grammarConfig of config.grammars) {
|
||||||
|
await generateGrammar(grammarConfig, configDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateGrammar(grammarConfig: AntlrGrammarConfig, baseDir: string): Promise<void> {
|
||||||
|
const grammarFile = resolve(baseDir, grammarConfig.grammar);
|
||||||
|
const outputDir = resolve(baseDir, grammarConfig.outputDir);
|
||||||
|
|
||||||
|
// Check if grammar file exists
|
||||||
|
try {
|
||||||
|
await access(grammarFile);
|
||||||
|
} catch {
|
||||||
|
throw new Error(`Grammar file not found: ${grammarFile}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure output directory exists
|
||||||
|
await mkdir(outputDir, { recursive: true });
|
||||||
|
|
||||||
|
// Build ANTLR command arguments
|
||||||
|
|
||||||
|
// eslint-disable-next-line @cspell/spellchecker
|
||||||
|
const args = ['antlr-ng', `-Dlanguage=${grammarConfig.language}`];
|
||||||
|
|
||||||
|
if (grammarConfig.generateVisitor) {
|
||||||
|
args.push('--generate-visitor');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grammarConfig.generateListener) {
|
||||||
|
args.push('--generate-listener');
|
||||||
|
}
|
||||||
|
|
||||||
|
args.push('-o', `"${outputDir}"`, `"${grammarFile}"`);
|
||||||
|
|
||||||
|
const command = `npx ${args.join(' ')}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await execAsync(command);
|
||||||
|
console.log(`Generated ANTLR files for ${grammarConfig.id}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to generate ANTLR files for ${grammarConfig.id}:`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLI interface
|
||||||
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||||
|
const configFile = process.argv[2] || './packages/parser/antlr-config.json';
|
||||||
|
try {
|
||||||
|
await generateFromConfig(configFile);
|
||||||
|
console.log('ANTLR generation completed successfully!');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('ANTLR generation failed:', error.message);
|
||||||
|
}
|
||||||
|
}
|
5
.build/generateAntlr.ts
Normal file
5
.build/generateAntlr.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { generateFromConfig } from './antlr-cli.js';
|
||||||
|
|
||||||
|
export async function generateAntlr() {
|
||||||
|
await generateFromConfig('./packages/parser/antlr-config.json');
|
||||||
|
}
|
@@ -4,6 +4,7 @@ import { packageOptions } from '../.build/common.js';
|
|||||||
import { generateLangium } from '../.build/generateLangium.js';
|
import { generateLangium } from '../.build/generateLangium.js';
|
||||||
import type { MermaidBuildOptions } from './util.js';
|
import type { MermaidBuildOptions } from './util.js';
|
||||||
import { defaultOptions, getBuildConfig } from './util.js';
|
import { defaultOptions, getBuildConfig } from './util.js';
|
||||||
|
import { generateAntlr } from '../.build/generateAntlr.js';
|
||||||
|
|
||||||
const shouldVisualize = process.argv.includes('--visualize');
|
const shouldVisualize = process.argv.includes('--visualize');
|
||||||
|
|
||||||
@@ -95,6 +96,7 @@ const buildTinyMermaid = async () => {
|
|||||||
|
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
await generateLangium();
|
await generateLangium();
|
||||||
|
await generateAntlr();
|
||||||
await mkdir('stats', { recursive: true });
|
await mkdir('stats', { recursive: true });
|
||||||
const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[];
|
const packageNames = Object.keys(packageOptions) as (keyof typeof packageOptions)[];
|
||||||
// it should build `parser` before `mermaid` because it's a dependency
|
// it should build `parser` before `mermaid` because it's a dependency
|
||||||
|
@@ -10,6 +10,7 @@ import type { TemplateType } from 'rollup-plugin-visualizer/dist/plugin/template
|
|||||||
import istanbul from 'vite-plugin-istanbul';
|
import istanbul from 'vite-plugin-istanbul';
|
||||||
import { packageOptions } from '../.build/common.js';
|
import { packageOptions } from '../.build/common.js';
|
||||||
import { generateLangium } from '../.build/generateLangium.js';
|
import { generateLangium } from '../.build/generateLangium.js';
|
||||||
|
import { generateAntlr } from '../.build/generateAntlr.js';
|
||||||
|
|
||||||
const visualize = process.argv.includes('--visualize');
|
const visualize = process.argv.includes('--visualize');
|
||||||
const watch = process.argv.includes('--watch');
|
const watch = process.argv.includes('--watch');
|
||||||
@@ -123,6 +124,7 @@ const main = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
await generateLangium();
|
await generateLangium();
|
||||||
|
await generateAntlr();
|
||||||
|
|
||||||
if (watch) {
|
if (watch) {
|
||||||
await build(getBuildConfig({ minify: false, watch, core: false, entryName: 'parser' }));
|
await build(getBuildConfig({ minify: false, watch, core: false, entryName: 'parser' }));
|
||||||
|
14
packages/parser/antlr-config.json
Normal file
14
packages/parser/antlr-config.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"projectName": "Mermaid",
|
||||||
|
"grammars": [
|
||||||
|
{
|
||||||
|
"id": "usecase",
|
||||||
|
"grammar": "src/language/usecase/Usecase.g4",
|
||||||
|
"outputDir": "src/language/usecase/generated",
|
||||||
|
"language": "TypeScript",
|
||||||
|
"generateVisitor": true,
|
||||||
|
"generateListener": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"mode": "production"
|
||||||
|
}
|
@@ -20,7 +20,7 @@
|
|||||||
"clean": "rimraf dist src/language/generated",
|
"clean": "rimraf dist src/language/generated",
|
||||||
"langium:generate": "langium generate",
|
"langium:generate": "langium generate",
|
||||||
"langium:watch": "langium generate --watch",
|
"langium:watch": "langium generate --watch",
|
||||||
"antlr:generate": "cd src/language/usecase && npx antlr-ng -Dlanguage=TypeScript --generate-visitor --generate-listener -o generated Usecase.g4"
|
"antlr:generate": "tsx ../../.build/antlr-cli.ts antlr-config.json"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
Reference in New Issue
Block a user