Write a README.md for "Python++", my own version of Python:

const fs = require('fs');
const { spawn } = require('child_process');

const config = 'CONFIG';
let mainName = 'app.pypp';

if (fs.existsSync(config)) {
    fs.readFile(config, 'utf8', (err, data) => {
        if (err) throw err;
        const match = data.match(/APP_NAME=(.+)\.pypp/);
        if (match) {
            mainName = match[1] + '.pypp';
        } else {
            formatError(`Couldn't find the main file name in the config file.`);
            process.exit(1);
        }

        if (fs.existsSync(mainName)) {
            translate(mainName);
        } else {
            formatError(`Couldn't find the file '${mainName}' in directory.`);
            process.exit(1);
        }
    });
} else {
    if (fs.existsSync(mainName)) {
        translate(mainName);
    } else {
        formatError(`Couldn't find the file '${mainName}' in directory.`);
        process.exit(1);
    }
}

function formatError(error, color = '\x1b[31m') {
    console.error(`${color}ERROR: ${error}\x1b[37m`);
}

function translate(fileName) {
    fs.readFile(fileName, 'utf8', (err, data) => {
        if (err) throw err;

        const formattedData = processCode(data);
        if (!formattedData) return; // Error has already been handled

        fs.writeFile('app.py', formattedData, err => {
            if (err) throw err;

            const pythonProcess = spawn('python', ['app.py'], { stdio: 'inherit' });

            pythonProcess.on('close', () => {
                fs.unlink('app.py', err => {
                    if (err) throw err;
                });
            });
        });
    });
}

function processCode(code) {
    let functionCounter = 0;
    let output = '';
    let functionsCode = '';
    let i = 0;

    let readonlyVars = new Set();
    let imports = new Set();
    let importLines = ''; // Collect import lines from the original code

    // State variables for tracking strings and comments
    let inSingleQuote = false;
    let inDoubleQuote = false;
    let inTripleSingleQuote = false;
    let inTripleDoubleQuote = false;
    let inSingleLineComment = false;
    let inMultiLineComment = false;

    while (i < code.length) {
        let char = code[i];
        let nextChars = code.substr(i, 3);

        // Handle comments and strings
        if (inSingleLineComment) {
            // In single-line comment
            output += char;
            if (char === '\n') {
                inSingleLineComment = false;
            }
            i++;
            continue;
        } else if (inMultiLineComment) {
            // In multi-line comment
            output += char;
            if (code.substr(i, 2) === '*/') {
                inMultiLineComment = false;
                output += '*';
                i++;
            }
            i++;
            continue;
        } else if (inSingleQuote) {
            // In single-quoted string
            output += char;
            if (char === "'" && code[i - 1] !== '\\') {
                inSingleQuote = false;
            }
            i++;
            continue;
        } else if (inDoubleQuote) {
            // In double-quoted string
            output += char;
            if (char === '"' && code[i - 1] !== '\\') {
                inDoubleQuote = false;
            }
            i++;
            continue;
        } else if (inTripleSingleQuote) {
            // In triple-single-quoted string
            output += char;
            if (code.substr(i, 3) === "'''") {
                inTripleSingleQuote = false;
                output += "''";
                i += 2;
            }
            i++;
            continue;
        } else if (inTripleDoubleQuote) {
            // In triple-double-quoted string
            output += char;
            if (code.substr(i, 3) === '"""') {
                inTripleDoubleQuote = false;
                output += '""';
                i += 2;
            }
            i++;
            continue;
        } else if (code.substr(i, 2) === '//') {
            // Start of single-line comment
            inSingleLineComment = true;
            output += '//';
            i += 2;
            continue;
        } else if (code.substr(i, 2) === '/*') {
            // Start of multi-line comment
            inMultiLineComment = true;
            output += '/*';
            i += 2;
            continue;
        } else if (char === "'") {
            inSingleQuote = true;
            output += char;
            i++;
            continue;
        } else if (char === '"') {
            inDoubleQuote = true;
            output += char;
            i++;
            continue;
        } else if (code.substr(i, 3) === "'''") {
            inTripleSingleQuote = true;
            output += "'''";
            i += 3;
            continue;
        } else if (code.substr(i, 3) === '"""') {
            inTripleDoubleQuote = true;
            output += '"""';
            i += 3;
            continue;
        } else if (/^\s$/.test(char)) {
            // Whitespace
            output += char;
            i++;
            continue;
        }

        // Handle import statements
        if (!inSingleQuote && !inDoubleQuote && !inTripleSingleQuote && !inTripleDoubleQuote) {
            if (code.startsWith('import ', i) || code.startsWith('from ', i)) {
                let importStart = i;
                while (i < code.length && code[i] !== '\n') {
                    i++;
                }
                let importLine = code.substring(importStart, i + 1);
                importLines += importLine;
                i++; // Skip '\n'
                continue;
            }
        }

        // Handle 'readonly'
        if (code.substr(i, 8) === 'readonly' && (/\s/.test(code[i + 8] || ''))) {
            let j = i + 'readonly'.length;

            // Skip any whitespace after 'readonly'
            while (j < code.length && /\s/.test(code[j])) j++;

            // Now extract variable name
            let varNameMatch = code.substr(j).match(/^([a-zA-Z_][a-zA-Z0-9_]*)/);
            if (varNameMatch) {
                let varName = varNameMatch[1];
                j += varName.length;

                // Skip whitespace
                while (j < code.length && /\s/.test(code[j])) j++;

                if (code[j] === '=') {
                    j++; // Skip '='

                    // Skip whitespace
                    while (j < code.length && /\s/.test(code[j])) j++;

                    // Extract value
                    let valueStart = j;
                    while (j < code.length && code[j] !== '\n') j++;
                    let value = code.substring(valueStart, j).trim();

                    // Add to readonlyVars
                    readonlyVars.add(varName);

                    // Replace 'readonly x = y' with 'x = y'
                    output += `${varName} = ${value}\n`;
                    i = j + 1; // Move to next character after the newline
                } else {
                    formatError('Expected "=" after readonly variable name');
                    return '';
                }
                continue;
            } else {
                formatError('Invalid readonly variable declaration');
                return '';
            }
        }

        // Handle reassignments of readonly variables
        let assignmentMatch = code.substr(i).match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*=/);
        if (assignmentMatch) {
            let varName = assignmentMatch[1];

            if (readonlyVars.has(varName)) {
                // Inject code to raise exception
                output += `raise Exception("Cannot reassign readonly variable '${varName}'")\n`;
                // Skip to the end of the line
                while (i < code.length && code[i] !== '\n') i++;
                i++; // Skip '\n'
                continue;
            }
        }

        // Handle 'def' anywhere in the code
        if (!inSingleQuote && !inDoubleQuote && !inTripleSingleQuote && !inTripleDoubleQuote &&
            code.substr(i, 3) === 'def' && (/\s|\(/.test(code[i + 3] || ''))) {
            let j = i + 3;

            // Skip any whitespace after 'def'
            while (j < code.length && /\s/.test(code[j])) j++;

            if (code[j] === '(') {
                // Anonymous function
                let { funcCode, funcName, newIndex } = extractAnonymousFunction(code, i, functionCounter++);
                if (!funcCode) {
                    // Error during extraction
                    return '';
                }
                functionsCode += funcCode + '\n';
                output += funcName;
                i = newIndex;
                continue;
            } else if (/[a-zA-Z_]/.test(code[j])) {
                // Regular function
                let { funcCode, newIndex } = extractRegularFunction(code, i);
                if (!funcCode) {
                    // Error during extraction
                    return '';
                }
                output += funcCode;
                i = newIndex;
                continue;
            } else {
                formatError('Invalid function definition after def');
                return '';
            }
        }

        // Check for 'enum'
        if (code.substr(i, 4) === 'enum' && (/\s|\{/.test(code[i + 4] || ''))) {
            let j = i + 4; // skip 'enum'
            // Skip any whitespace
            while (j < code.length && /\s/.test(code[j])) j++;

            // Now extract enum name
            let enumNameMatch = code.substr(j).match(/^([a-zA-Z_][a-zA-Z0-9_]*)/);
            if (enumNameMatch) {
                let enumName = enumNameMatch[1];
                j += enumName.length;

                // Skip whitespace
                while (j < code.length && /\s/.test(code[j])) j++;

                // Expect '{'
                if (code[j] === '{') {
                    j++; // Skip '{'

                    // Now extract enum members until '}'
                    let enumMembers = [];
                    let currentValue = 0;

                    while (j < code.length) {
                        // Skip whitespace
                        while (j < code.length && /\s/.test(code[j])) j++;

                        if (code[j] === '}') {
                            j++; // Skip '}'
                            break;
                        }

                        // Now extract member name
                        let memberNameMatch = code.substr(j).match(/^([a-zA-Z_][a-zA-Z0-9_]*)/);
                        if (memberNameMatch) {
                            let memberName = memberNameMatch[1];
                            j += memberName.length;

                            // Skip whitespace
                            while (j < code.length && /\s/.test(code[j])) j++;

                            let memberValue = null;

                            // Check for '='
                            if (code[j] === '=') {
                                j++; // Skip '='

                                // Skip whitespace
                                while (j < code.length && /\s/.test(code[j])) j++;

                                // Extract value
                                let valueStart = j;
                                while (j < code.length && /[^\s,}]/.test(code[j])) j++;
                                let valueStr = code.substring(valueStart, j).trim();

                                // Try to parse memberValue as integer
                                let parsedValue = parseInt(valueStr, 10);
                                if (!isNaN(parsedValue)) {
                                    memberValue = parsedValue;
                                    currentValue = memberValue;
                                } else {
                                    formatError(`Invalid enum value '${valueStr}' for member '${memberName}'`);
                                    return '';
                                }
                            } else {
                                memberValue = currentValue;
                            }

                            enumMembers.push({ name: memberName, value: memberValue });

                            currentValue++; // Increment for next member if no value specified

                            // Skip whitespace
                            while (j < code.length && /\s/.test(code[j])) j++;

                            // Check for ',' or '}'
                            if (code[j] === ',') {
                                j++; // Skip ','
                                continue;
                            } else if (code[j] === '}') {
                                continue;
                            } else if (code.substr(j, 2) === '//') {
                                // Handle single-line comment
                                j += 2;
                                while (j < code.length && code[j] !== '\n') j++;
                            } else if (code.substr(j, 2) === '/*') {
                                // Handle multi-line comment
                                j += 2;
                                while (j < code.length && !(code[j] === '*' && code[j + 1] === '/')) j++;
                                if (code[j] === '*' && code[j + 1] === '/') j += 2;
                            } else {
                                // For safety, skip to next ',' or '}'
                                while (j < code.length && code[j] !== ',' && code[j] !== '}') {
                                    j++;
                                }
                                if (code[j] === ',') {
                                    j++;
                                    continue;
                                } else if (code[j] === '}') {
                                    j++;
                                    break;
                                }
                            }
                        } else {
                            formatError('Invalid enum member declaration');
                            return '';
                        }
                    }

                    // Now generate Python code for the enum
                    imports.add('from enum import Enum');
                    let enumCode = `class ${enumName}(Enum):\n`;
                    enumMembers.forEach(member => {
                        enumCode += `    ${member.name} = ${member.value}\n`;
                    });

                    // Add to output
                    output += enumCode + '\n';
                    i = j;
                    continue;

                } else {
                    formatError('Expected "{" after enum name');
                    return '';
                }

            } else {
                formatError('Invalid enum declaration, expected enum name');
                return '';
            }
        }

        // Copy the character
        output += char;
        i++;
    }

    // Combine the imports from the code, additional imports, function definitions, and the rest of the code
    const additionalImports = Array.from(imports).join('\n');
    const finalOutput = importLines + '\n' + additionalImports + '\n' + functionsCode + '\n' + output;

    return replaceCurlyBrackets(finalOutput);
}

function extractAnonymousFunction(code, i, functionCounter) {
    let funcName = `__anonymous_function_${functionCounter}`;
    let funcCode = '';
    let j = i; // Position in the code

    // Skip 'def'
    j += 3;

    // Skip any whitespace after 'def'
    while (j < code.length && /\s/.test(code[j])) j++;

    if (code[j] !== '(') {
        formatError('Expected "(" after "def" in anonymous function definition');
        return {
            funcCode: '',
            funcName: '',
            newIndex: code.length
        };
    }
    let paramsStart = j + 1;
    let paramsEnd = code.indexOf(')', paramsStart);
    if (paramsEnd === -1) {
        formatError('Expected ")" in anonymous function definition');
        return {
            funcCode: '',
            funcName: '',
            newIndex: code.length
        };
    }
    let params = code.substring(paramsStart, paramsEnd).trim();

    j = paramsEnd + 1; // Move past ')'

    // Skip whitespace
    while (j < code.length && /\s/.test(code[j])) j++;

    // Expect '{'
    if (code[j] !== '{') {
        formatError('Expected "{" after anonymous function definition');
        return {
            funcCode: '',
            funcName: '',
            newIndex: code.length
        };
    }
    j++; // Skip '{'

    // Extract function body
    let braceDepth = 1;
    let bodyStart = j;

    while (j < code.length && braceDepth > 0) {
        if (code[j] === '{') {
            braceDepth++;
        } else if (code[j] === '}') {
            braceDepth--;
        }
        j++;
    }

    if (braceDepth !== 0) {
        formatError('Unmatched braces in anonymous function');
        return {
            funcCode: '',
            funcName: '',
            newIndex: code.length
        };
    }

    let bodyEnd = j - 1;
    let funcBody = code.slice(bodyStart, bodyEnd);

    // Indent the function body
    let funcBodyLines = funcBody.split('\n');
    funcBodyLines = funcBodyLines.map(line => '    ' + line.trimRight());
    funcBody = funcBodyLines.join('\n');

    // Build the function code
    funcCode += `def ${funcName}(${params}):\n${funcBody}`;

    // **New code added to skip any trailing whitespace or semicolons**
    // Skip any trailing whitespace or semicolons after the closing '}'
    while (j < code.length && (/\s/.test(code[j]) || code[j] === ';')) {
        j++;
    }

    // Return the function code and the new index
    return {
        funcCode,
        funcName,
        newIndex: j
    };
}

function extractRegularFunction(code, i) {
    let j = i; // Position in the code
    let funcDef = '';

    // Build the function definition

    // Skip 'def'
    funcDef += code.substr(j, 3); // 'def'
    j += 3;

    // Accumulate until the opening brace '{'
    while (j < code.length && code[j] !== '{') {
        funcDef += code[j];
        j++;
    }

    if (j >= code.length) {
        formatError('Expected "{" after function declaration');
        return '';
    }

    // Replace '{' with ':'
    funcDef += ':';
    j++; // Skip '{'

    // Now find the matching closing brace '}'
    let braceDepth = 1;
    let functionBodyStart = j;
    while (j < code.length && braceDepth > 0) {
        if (code[j] === '{') braceDepth++;
        else if (code[j] === '}') braceDepth--;
        j++;
    }

    if (braceDepth !== 0) {
        formatError('Unmatched braces in function definition');
        return '';
    }

    let functionBodyEnd = j - 1;
    let functionBody = code.slice(functionBodyStart, functionBodyEnd);

    // Indent the function body
    let functionBodyLines = functionBody.split('\n');
    functionBodyLines = functionBodyLines.map(line => '    ' + line.trimRight());
    functionBody = functionBodyLines.join('\n');

    funcDef += '\n' + functionBody + '\n';

    return {
        funcCode: funcDef,
        newIndex: j
    };
}

function replaceCurlyBrackets(code) {
    let formattedCode = '';
    let indentLevel = 0;
    const indentSize = 4;
    let i = 0;

    // States
    let inSingleQuote = false;
    let inDoubleQuote = false;
    let inTripleSingleQuote = false;
    let inTripleDoubleQuote = false;
    let inMultiLineComment = false;
    let braceStack = [];

    while (i < code.length) {
        const char = code[i];
        const nextChars = code.substring(i, i + 3);

        // Handle multi-line comments start
        if (!inSingleQuote && !inDoubleQuote && !inMultiLineComment) {
            if (code.startsWith('/*', i)) {
                inMultiLineComment = true;
                formattedCode += '#';
                i += 2; // Skip '/*'
                continue;
            }
        }

        // Handle multi-line comments end
        if (inMultiLineComment) {
            if (code.startsWith('*/', i)) {
                inMultiLineComment = false;
                i += 2; // Skip '*/'
                continue;
            }

            if (char === '\n') {
                formattedCode += '\n#';
                i++;
            } else {
                formattedCode += char;
                i++;
            }
            continue;
        }

        // Handle string literals
        if (!inSingleQuote && !inDoubleQuote && !inTripleSingleQuote && !inTripleDoubleQuote) {
            if (nextChars === "'''") {
                inTripleSingleQuote = true;
                formattedCode += nextChars;
                i += 3;
                continue;
            } else if (nextChars === '"""') {
                inTripleDoubleQuote = true;
                formattedCode += nextChars;
                i += 3;
                continue;
            } else if (char === "'") {
                inSingleQuote = true;
                formattedCode += char;
                i++;
                continue;
            } else if (char === '"') {
                inDoubleQuote = true;
                formattedCode += char;
                i++;
                continue;
            }
        } else {
            // Inside string
            if (inTripleSingleQuote && nextChars === "'''") {
                inTripleSingleQuote = false;
                formattedCode += nextChars;
                i += 3;
                continue;
            } else if (inTripleDoubleQuote && nextChars === '"""') {
                inTripleDoubleQuote = false;
                formattedCode += nextChars;
                i += 3;
                continue;
            } else if (inSingleQuote && char === "'" && code[i - 1] !== '\\') {
                inSingleQuote = false;
                formattedCode += char;
                i++;
                continue;
            } else if (inDoubleQuote && char === '"' && code[i - 1] !== '\\') {
                inDoubleQuote = false;
                formattedCode += char;
                i++;
                continue;
            }
            formattedCode += char;
            i++;
            continue;
        }

        // Handle single-line comments
        if (!inSingleQuote && !inDoubleQuote && !inTripleSingleQuote && !inTripleDoubleQuote) {
            if (code.startsWith('//', i)) {
                // Single-line comment
                let endOfLine = code.indexOf('\n', i);
                if (endOfLine === -1) endOfLine = code.length;
                formattedCode += '#' + code.slice(i + 2, endOfLine);
                i = endOfLine;
                continue;
            }
        }

        // Skip semicolons
        if (char === ';') {
            i++;
            continue;
        }

        // Handle braces and code blocks
        if (char === '{') {
            // Determine if this is a code block or a data structure
            let isCodeBlock = false;

            let j = formattedCode.length - 1;
            while (j >= 0 && /\s/.test(formattedCode[j])) j--;

            if (j >= 0 && /[\):]/.test(formattedCode[j])) {
                isCodeBlock = true;
            } else if (j >= 0 && /[a-zA-Z0-9_]/.test(formattedCode[j])) {
                isCodeBlock = true;
            }

            if (isCodeBlock) {
                braceStack.push('block');
                formattedCode += ':';
                indentLevel++;
            } else {
                braceStack.push('data');
                formattedCode += char;
            }
            i++;
            continue;
        } else if (char === '}') {
            if (braceStack.length === 0) {
                formatError('Unmatched closing brace');
                return '';
            }
            let lastBrace = braceStack.pop();
            if (lastBrace === 'block') {
                indentLevel = Math.max(indentLevel - 1, 0);
            } else {
                formattedCode += char;
            }
            i++;
            continue;
        } else if (char === '\n') {
            // Handle newlines and indentation
            formattedCode += '\n' + ' '.repeat(indentLevel * indentSize);
            i++;
            continue;
        } else {
            formattedCode += char;
            i++;
            continue;
        }
    }

    if (braceStack.length > 0) {
        formatError('Unmatched opening brace(s)');
        return '';
    }

    return formattedCode.trimEnd();
}