/** * @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); const Comment = Regex(/#.*\n/y); const PreCommand = AtLeast(0, Choose(InterCommandWhitespace, Comment)); const PreWordWhitespace = Regex(/[^\S\n;]*/y); const BasicWord = Regex(/[^\s;]+/y).map(([word]) => ({ text: word })); // WIP, need to be able to escape braces correctly // WIP, error if anything after closing brace /** @type {Peg.Pattern} */ const Brace = Sequence( Regex(/\{/y), AtLeast( 0, Choose( Use(() => Brace).map((text) => `{${text}}`), Regex(/[^{}]+/y).map(([text]) => text) ) ), Regex(/\}/y) ).map(([_left, fragments, _right]) => fragments.join("")); const Word = Sequence( PreWordWhitespace, Choose( Brace.map((text) => ({ text })), BasicWord ) ).map(([_, word]) => word); const CommandTerminator = Sequence( PreWordWhitespace, Choose(/** @type {Peg.Pattern} */ (Regex(/[\n;]/y)), End) ); /** @type {Peg.Pattern} */ const Command = Sequence(PreCommand, AtLeast(0, Word), CommandTerminator).map( ([_padding, words, _end]) => words ); /** @type {Peg.Pattern} */ const Script = Sequence(AtLeast(0, Command), End).map( ([commands, _eof]) => commands ); return { /** * Parse out a Notcl script into an easier-to-interpret representation. * No script is actually executed yet. * * @param {string} code * @returns Script */ parse(code) { /* Preprocess */ // fold line endings code = code.replace(/(?