/** * Baba Yaga Language Mode for CodeMirror * Provides syntax highlighting for the Baba Yaga functional programming language */ // Global function to initialize the Baba Yaga language mode window.initBabaYagaMode = function() { // Check if CodeMirror is available if (typeof CodeMirror === 'undefined') { console.log('CodeMirror not available yet, will retry...'); setTimeout(window.initBabaYagaMode, 100); return; } console.log('Initializing Baba Yaga language mode...'); // Baba Yaga language keywords const keywords = [ 'when', 'is', 'then', 'else', 'with', 'rec', 'in', 'Ok', 'Err', 'true', 'false', 'PI', 'INFINITY', 'and', 'or', 'xor', 'not', 'if' ]; // Baba Yaga type system const types = [ 'Int', 'String', 'Result', 'Float', 'Number', 'List', 'Table', 'Bool', 'Unit', 'Maybe' ]; // Function names that are commonly used const builtins = [ 'map', 'filter', 'reduce', 'fold', 'head', 'tail', 'length', 'append', 'concat', 'reverse', 'sort' ]; // Main tokenizer function function tokenize(stream, state) { // Handle comments if (stream.match(/\/\/.*/)) { return 'comment'; } // Handle multi-line comments if (stream.match(/\/\*/)) { state.inComment = true; return 'comment'; } if (state.inComment) { if (stream.match(/\*\//)) { state.inComment = false; } else { stream.next(); } return 'comment'; } // Handle whitespace if (stream.eatSpace()) { return null; } // Handle numbers (integers and floats) if (stream.match(/^-?\d+\.\d+/)) { return 'number'; } if (stream.match(/^-?\d+/)) { return 'number'; } // Handle strings if (stream.match(/^"[^"]*"/)) { return 'string'; } // Handle identifiers and keywords if (stream.match(/^[a-zA-Z_][a-zA-Z0-9_]*/)) { const word = stream.current(); if (keywords.includes(word)) { return 'keyword'; } if (types.includes(word)) { return 'type'; } if (builtins.includes(word)) { return 'builtin'; } // Check if it's a function declaration (followed by :) const nextChar = stream.peek(); if (nextChar === ':') { return 'function'; } return 'variable'; } // Handle operators and symbols if (stream.match(/^->/)) { return 'operator'; } if (stream.match(/^[=!<>]=/)) { return 'operator'; } if (stream.match(/^[+\-*/%=<>!&|^,;:()[\]{}]/)) { return 'operator'; } // Handle dots for member access if (stream.match(/^\./)) { return 'operator'; } // Handle unknown characters stream.next(); return null; } // Define the Baba Yaga language mode CodeMirror.defineMode("baba-yaga", function() { return { startState: function() { return { inComment: false, inString: false, indentLevel: 0 }; }, token: function(stream, state) { return tokenize(stream, state); }, // Indentation rules indent: function(state, textAfter) { const baseIndent = state.indentLevel * 2; // Increase indent after certain patterns if (textAfter.match(/^[a-zA-Z_][a-zA-Z0-9_]*\s*:/)) { return baseIndent + 2; } if (textAfter.match(/^->/)) { return baseIndent + 2; } if (textAfter.match(/^when/)) { return baseIndent + 2; } if (textAfter.match(/^with/)) { return baseIndent + 2; } return baseIndent; }, // Line comment character lineComment: "//", // Auto-indent on certain characters electricChars: "{}:->", // Fold code blocks fold: "indent" }; }); // Note: CodeMirror 5 doesn't have defineTheme, we use CSS instead // The theme is defined in the CSS with .cm-s-baba-yaga classes // Also register MIME types CodeMirror.defineMIME("text/x-baba-yaga", "baba-yaga"); CodeMirror.defineMIME("text/baba-yaga", "baba-yaga"); CodeMirror.defineMIME("application/x-baba-yaga", "baba-yaga"); console.log('Baba Yaga language mode loaded successfully!'); console.log('Available modes:', Object.keys(CodeMirror.modes)); // Dispatch a custom event to notify that the mode is ready window.dispatchEvent(new CustomEvent('baba-yaga-mode-ready')); }; // Start initialization console.log('Baba Yaga mode script loaded, waiting for CodeMirror...'); window.initBabaYagaMode();