// package.json dependencies
/*
{
"dependencies": {
"peggy": "^4.0.3"
},
"devDependencies": {
"@types/node": "^20.0.0",
"typescript": "^5.0.0"
}
}
*/
// 1. First, save the PEG grammar to a file: routeros.pegjs
// Then generate the parser:
import fs from 'fs';
import peggy from 'peggy';
// Generate parser from PEG grammar
const grammarSource = fs.readFileSync('routeros.pegjs', 'utf8');
const parser = peggy.generate(grammarSource, {
output: 'source',
format: 'es'
});
// Save generated parser
fs.writeFileSync('routeros-parser.js', parser);
// 2. Usage example:
interface RouterOSNode {
type: string;
value?: any;
children?: RouterOSNode[];
line?: number;
column?: number;
}
class RouterOSParser {
private parser: any;
constructor() {
// Import the generated parser
this.parser = require('./routeros-parser.js');
}
parse(scriptContent: string): RouterOSNode {
try {
return this.parser.parse(scriptContent);
} catch (error: any) {
throw new Error(`RouterOS Script Parse Error: ${error.message} at line ${error.location?.start.line}, column ${error.location?.start.column}`);
}
}
validateScript(scriptContent: string): { valid: boolean; errors: string[] } {
const errors: string[] = [];
try {
this.parse(scriptContent);
return { valid: true, errors: [] };
} catch (error: any) {
errors.push(error.message);
return { valid: false, errors };
}
}
// Extract specific elements from parsed AST
extractCommands(ast: RouterOSNode): string[] {
const commands: string[] = [];
function traverse(node: RouterOSNode) {
if (node.type === 'Command') {
commands.push(node.value);
}
if (node.children) {
node.children.forEach(traverse);
}
}
traverse(ast);
return commands;
}
extractVariables(ast: RouterOSNode): { name: string; type: 'global' | 'local' }[] {
const variables: { name: string; type: 'global' | 'local' }[] = [];
function traverse(node: RouterOSNode) {
if (node.type === 'GlobalAssignment' || node.type === 'LocalAssignment') {
variables.push({
name: node.value.name,
type: node.type === 'GlobalAssignment' ? 'global' : 'local'
});
}
if (node.children) {
node.children.forEach(traverse);
}
}
traverse(ast);
return variables;
}
}
// 3. Usage example:
async function main() {
const parser = new RouterOSParser();
// Example RouterOS script
const routerOSScript = `
# Configure interface
:global interfaceName "ether1"
:local ipAddress "192.168.1.1/24"
/interface ethernet
set [find name=$interfaceName] disabled=no
/ip address
add address=$ipAddress interface=$interfaceName
:if ([/ip address print count-only] > 0) do={
:put "IP address configured successfully"
} else={
:error "Failed to configure IP address"
}
`;
try {
// Parse the script
const ast = parser.parse(routerOSScript);
console.log('Parsed successfully!');
// Validate script
const validation = parser.validateScript(routerOSScript);
console.log('Validation result:', validation);
// Extract information
const commands = parser.extractCommands(ast);
const variables = parser.extractVariables(ast);
console.log('Commands found:', commands);
console.log('Variables found:', variables);
} catch (error) {
console.error('Parse error:', error.message);
}
}
// 4. Build script for generating parser:
// build.js
const fs = require('fs');
const peggy = require('peggy');
function generateParser() {
const grammarSource = fs.readFileSync('routeros.pegjs', 'utf8');
const parser = peggy.generate(grammarSource, {
output: 'source',
format: 'commonjs'
});
fs.writeFileSync('src/routeros-parser.js', parser);
console.log('Parser generated successfully!');
}
generateParser();
// 5. TypeScript types for AST nodes:
export interface RouterOSCommand {
type: 'Command';
path: string[];
parameters: RouterOSParameter[];
}
export interface RouterOSParameter {
type: 'Parameter';
name?: string;
value: string | number | boolean;
}
export interface RouterOSVariable {
type: 'Variable';
scope: 'global' | 'local';
name: string;
value?: any;
}
export interface RouterOSControlFlow {
type: 'IfStatement' | 'WhileLoop' | 'ForLoop' | 'ForeachLoop';
condition?: RouterOSExpression;
body: RouterOSNode[];
}
export interface RouterOSExpression {
type: 'Expression';
operator?: string;
left?: RouterOSNode;
right?: RouterOSNode;
value?: any;
}
export type RouterOSNode =
| RouterOSCommand
| RouterOSParameter
| RouterOSVariable
| RouterOSControlFlow
| RouterOSExpression;
main();