mushcode-mcp-server
Version:
A specialized Model Context Protocol server for MUSHCODE development assistance. Provides AI-powered code generation, validation, optimization, and examples for MUD development.
368 lines • 14.6 kB
JavaScript
/**
* MUSHCODE compression engine
* Compresses and minifies MUSHCODE while preserving functionality
*/
import { ValidationError } from '../utils/errors.js';
export class MushcodeCompressor {
knowledgeBase;
constructor(knowledgeBase) {
this.knowledgeBase = knowledgeBase;
}
/**
* Compress MUSHCODE for size optimization
*/
async compress(request) {
this.validateRequest(request);
const lines = request.code.split('\n');
const context = this.createCompressionContext(request, lines);
const optimizationsApplied = [];
const warnings = [];
let compressedLines = [...lines];
// Apply compression strategies based on level
if (context.level === 'minimal' || context.level === 'moderate' || context.level === 'aggressive') {
// Remove comments if requested
if (context.removeComments) {
const commentResult = this.removeComments(compressedLines);
compressedLines = commentResult.lines;
if (commentResult.removed > 0) {
optimizationsApplied.push(`Removed ${commentResult.removed} comment lines`);
}
}
// Remove empty lines
const emptyLineResult = this.removeEmptyLines(compressedLines);
compressedLines = emptyLineResult.lines;
if (emptyLineResult.removed > 0) {
optimizationsApplied.push(`Removed ${emptyLineResult.removed} empty lines`);
}
// Trim whitespace
const whitespaceResult = this.trimWhitespace(compressedLines);
compressedLines = whitespaceResult.lines;
if (whitespaceResult.spacesRemoved > 0) {
optimizationsApplied.push(`Removed ${whitespaceResult.spacesRemoved} unnecessary spaces`);
}
}
if (context.level === 'moderate' || context.level === 'aggressive') {
// Compress function calls (remove spaces)
const functionResult = this.compressFunctionCalls(compressedLines, context);
compressedLines = functionResult.lines;
if (functionResult.compressed > 0) {
optimizationsApplied.push(`Compressed ${functionResult.compressed} function calls`);
if (functionResult.warnings.length > 0) {
warnings.push(...functionResult.warnings);
}
}
// Compress variable names
const variableResult = this.compressVariableNames(compressedLines, context);
compressedLines = variableResult.lines;
if (variableResult.compressed > 0) {
optimizationsApplied.push(`Compressed ${variableResult.compressed} variable names`);
if (variableResult.warnings.length > 0) {
warnings.push(...variableResult.warnings);
}
}
// Remove unnecessary parentheses
const parenResult = this.removeUnnecessaryParentheses(compressedLines);
compressedLines = parenResult.lines;
if (parenResult.removed > 0) {
optimizationsApplied.push(`Removed ${parenResult.removed} unnecessary parentheses`);
}
}
if (context.level === 'aggressive') {
// Merge lines where possible
const mergeResult = this.mergeLines(compressedLines, context);
compressedLines = mergeResult.lines;
if (mergeResult.merged > 0) {
optimizationsApplied.push(`Merged ${mergeResult.merged} lines`);
if (mergeResult.warnings.length > 0) {
warnings.push(...mergeResult.warnings);
}
}
}
const compressedCode = compressedLines.join('\n');
const originalSize = request.code.length;
const compressedSize = compressedCode.length;
const compressionRatio = originalSize > 0 ? (originalSize - compressedSize) / originalSize : 0;
const result = {
compressedCode,
originalSize,
compressedSize,
compressionRatio,
optimizationsApplied
};
if (warnings.length > 0) {
result.warnings = warnings;
}
return result;
}
/**
* Create compression context with all necessary information
*/
createCompressionContext(request, lines) {
const dialect = request.serverType ?
this.knowledgeBase.dialects.get(request.serverType) || null : null;
const level = request.compressionLevel || 'moderate';
return {
code: request.code,
lines,
dialect,
level,
preserveFunctionality: request.preserveFunctionality !== false,
removeComments: request.removeComments !== false,
serverType: request.serverType
};
}
/**
* Validate the compression request
*/
validateRequest(request) {
if (!request.code || request.code.trim().length === 0) {
throw new ValidationError('Code is required');
}
if (request.code.length > 50000) {
throw new ValidationError('Code is too long (max 50000 characters)');
}
if (request.serverType && this.knowledgeBase.dialects.size > 0 && !this.knowledgeBase.dialects.has(request.serverType)) {
throw new ValidationError(`Unknown server type: ${request.serverType}`);
}
const validLevels = ['minimal', 'moderate', 'aggressive'];
if (request.compressionLevel && !validLevels.includes(request.compressionLevel)) {
throw new ValidationError(`Invalid compression level: ${request.compressionLevel}. Must be one of: ${validLevels.join(', ')}`);
}
}
/**
* Remove comment lines
*/
removeComments(lines) {
let removed = 0;
const filteredLines = lines.filter(line => {
const trimmed = line.trim();
if (trimmed.startsWith('@@') || trimmed.startsWith('//')) {
removed++;
return false;
}
return true;
});
return { lines: filteredLines, removed };
}
/**
* Remove empty lines
*/
removeEmptyLines(lines) {
let removed = 0;
const filteredLines = lines.filter(line => {
if (line.trim().length === 0) {
removed++;
return false;
}
return true;
});
return { lines: filteredLines, removed };
}
/**
* Trim unnecessary whitespace
*/
trimWhitespace(lines) {
let spacesRemoved = 0;
const trimmedLines = lines.map(line => {
const originalLength = line.length;
const trimmed = line.trim();
spacesRemoved += originalLength - trimmed.length;
return trimmed;
});
return { lines: trimmedLines, spacesRemoved };
}
/**
* Compress variable names (registers)
*/
compressVariableNames(lines, context) {
const warnings = [];
let compressed = 0;
// Find all register variables (q0-q9, qa-qz)
const registerMap = new Map();
const usedRegisters = new Set();
// First pass: collect all used registers
for (const line of lines) {
const registers = line.match(/q[0-9a-z]/g);
if (registers) {
registers.forEach(reg => usedRegisters.add(reg));
}
}
// Create mapping to shorter register names
const availableShort = ['q0', 'q1', 'q2', 'q3', 'q4', 'q5', 'q6', 'q7', 'q8', 'q9'];
let shortIndex = 0;
// Sort registers by usage frequency (longer names first for compression)
const sortedRegisters = Array.from(usedRegisters).sort((a, b) => {
if (a.length !== b.length)
return b.length - a.length;
return a.localeCompare(b);
});
for (const register of sortedRegisters) {
if (register.length > 2 && shortIndex < availableShort.length) {
const shortReg = availableShort[shortIndex];
if (shortReg && !usedRegisters.has(shortReg)) {
registerMap.set(register, shortReg);
shortIndex++;
compressed++;
}
}
}
// Second pass: apply replacements
const compressedLines = lines.map(line => {
let compressedLine = line;
for (const [longReg, shortReg] of registerMap) {
const regex = new RegExp(`\\b${longReg}\\b`, 'g');
compressedLine = compressedLine.replace(regex, shortReg);
}
return compressedLine;
});
if (context.preserveFunctionality && compressed > 0) {
warnings.push('Variable name compression may affect code readability');
}
return { lines: compressedLines, compressed, warnings };
}
/**
* Remove unnecessary parentheses
*/
removeUnnecessaryParentheses(lines) {
let removed = 0;
const processedLines = lines.map(line => {
let processed = line;
// Remove parentheses around single values in simple contexts
// Be very conservative to preserve functionality
const patterns = [
// Remove parentheses around single numbers: (123) -> 123
/\((\d+)\)/g,
// Remove parentheses around single register variables: (q0) -> q0
/\((q[0-9a-z])\)/g
];
for (const pattern of patterns) {
const matches = processed.match(pattern);
if (matches) {
removed += matches.length;
processed = processed.replace(pattern, '$1');
}
}
return processed;
});
return { lines: processedLines, removed };
}
/**
* Compress function calls by removing unnecessary spaces
*/
compressFunctionCalls(lines, context) {
const warnings = [];
let compressed = 0;
const compressedLines = lines.map(line => {
let compressedLine = line;
const originalLength = compressedLine.length;
// Remove spaces around commas in function calls
compressedLine = compressedLine.replace(/\s*,\s*/g, ',');
// Remove spaces around equals in assignments
compressedLine = compressedLine.replace(/\s*=\s*/g, '=');
// Remove spaces around colons
compressedLine = compressedLine.replace(/\s*:\s*/g, ':');
// Remove spaces inside parentheses
compressedLine = compressedLine.replace(/\(\s+/g, '(');
compressedLine = compressedLine.replace(/\s+\)/g, ')');
if (originalLength > compressedLine.length) {
compressed++;
}
return compressedLine;
});
if (context.preserveFunctionality && compressed > 0) {
warnings.push('Function call compression may affect readability');
}
return { lines: compressedLines, compressed, warnings };
}
/**
* Merge lines where safe to do so
*/
mergeLines(lines, context) {
const warnings = [];
let merged = 0;
const mergedLines = [];
for (let i = 0; i < lines.length; i++) {
const currentLine = lines[i];
const nextLine = lines[i + 1];
// Only merge very simple cases to preserve functionality
if (currentLine && nextLine &&
currentLine.length + nextLine.length < 80 &&
!currentLine.includes('@@') &&
!nextLine.includes('@@') &&
this.canSafelyMerge(currentLine, nextLine)) {
mergedLines.push(currentLine + nextLine);
i++; // Skip the next line since we merged it
merged++;
}
else if (currentLine !== undefined) {
mergedLines.push(currentLine);
}
}
if (context.preserveFunctionality && merged > 0) {
warnings.push('Line merging may affect code structure and debugging');
}
return { lines: mergedLines, merged, warnings };
}
/**
* Check if two lines can be safely merged
*/
canSafelyMerge(line1, line2) {
// Very conservative merging rules
const trimmed1 = line1.trim();
const trimmed2 = line2.trim();
// Don't merge if either line is empty
if (trimmed1.length === 0 || trimmed2.length === 0) {
return false;
}
// Don't merge if either line contains control structures
const controlStructures = ['@if', '@switch', '@while', '@dolist', '@break', '@assert'];
for (const control of controlStructures) {
if (trimmed1.includes(control) || trimmed2.includes(control)) {
return false;
}
}
// Don't merge if either line contains function definitions
if (trimmed1.includes('&') || trimmed2.includes('&')) {
return false;
}
// Don't merge if either line contains complex expressions
if (this.calculateNestingDepth(trimmed1) > 2 || this.calculateNestingDepth(trimmed2) > 2) {
return false;
}
return true;
}
/**
* Calculate nesting depth of parentheses/brackets
*/
calculateNestingDepth(code) {
let maxDepth = 0;
let currentDepth = 0;
let inString = false;
let escapeNext = false;
for (const char of code) {
if (escapeNext) {
escapeNext = false;
continue;
}
if (char === '\\') {
escapeNext = true;
continue;
}
if (char === '"') {
inString = !inString;
continue;
}
if (inString)
continue;
if (char === '(' || char === '[' || char === '{') {
currentDepth++;
maxDepth = Math.max(maxDepth, currentDepth);
}
else if (char === ')' || char === ']' || char === '}') {
currentDepth = Math.max(0, currentDepth - 1);
}
}
return maxDepth;
}
}
//# sourceMappingURL=compressor.js.map