/** * @typedef {Notcl.Command[]} Notcl.Script * @typedef {Notcl.Word[]} Notcl.Command * @typedef {object} Notcl.Word * @property {string} text */ var Notcl = (() => { const { AtLeast, Choose, End, Regex, Sequence, Use } = Peg; const InterCommandWhitespace = Regex(/\s+/y).expects("whitespace"); const Comment = Regex(/#.*\n/y).expects("#"); const PreCommand = AtLeast(0, Choose(InterCommandWhitespace, Comment)); const PreWordWhitespace = Regex(/[^\S\n;]+/y).expects("whitespace"); const BasicWord = Regex(/(?!\{)[^\s;]+/y) .map(([word]) => ({ text: word })) .expects("BASIC_WORD"); // WIP, need to be able to escape braces correctly /** @type {Peg.Pattern} */ const Brace = Sequence( Regex(/\{/y).expects("{"), AtLeast( 0, Choose( Use(() => Brace).map((text) => `{${text}}`), Regex(/[^{}]+/y).map(([text]) => text) ) ), Regex(/\}/y).expects("}") ).map(([_left, fragments, _right]) => fragments.join("")); const Word = Choose( Brace.map((text) => ({ text })), BasicWord ); const CommandTerminator = Choose( /** @type {Peg.Pattern} */ (Regex(/[\n;]/y)).expects( "NEWLINE | ;" ), End() ); /** @type {Peg.Pattern} */ const Command = Choose( CommandTerminator.map(() => []), Sequence( Word, AtLeast( 0, Sequence(PreWordWhitespace, Word).map(([_padding, word]) => word) ), Choose(Sequence(PreWordWhitespace, Word), CommandTerminator) ).map(([word, moreWords, _end]) => [word].concat(moreWords)) ).expects("COMMAND"); /** @type {Peg.Pattern} */ const Script = Sequence( AtLeast(0, Sequence(PreCommand, Command)), Choose(End(), Sequence(PreCommand, Command)) ).map(([commands, _eof]) => commands.map(([_padding, command]) => command)); const ERROR_CONTEXT = /(?<=([^\n]{0,50}))([^\n]{0,50})/y; return { /** * Parse out a Notcl script into an easier-to-interpret representation. * No script is actually executed yet. * * @param {string} code to parse * @returns {[true, Notcl.Script] | [false, string]} parsed list of commands, or error message on failure */ parse(code) { /* Preprocess */ // fold line endings code = code.replace(/(?Error at position ${commands[1]} ${escapeHtml(before + "" + after)} ${"-".repeat(before.length)}^ Expected: ${escapeHtml(expected)}`, ]; } }, }; })();