/** * @typedef {Notcl.Command[]} Notcl.Script * @typedef {Notcl.Word[]} Notcl.Command * @typedef {object} Notcl.Word * @property {string} text */ var Notcl = (() => { const InterCommandWhitespace = Peg.Regex(/\s+/y); const Comment = Peg.Regex(/#.*\n/y); const PreCommand = Peg.AtLeast( 0, Peg.Choose(InterCommandWhitespace, Comment) ); const PreWordWhitespace = Peg.Regex(/[^\S\n;]*/y); const BasicWord = Peg.Map(Peg.Regex(/[^\s;]+/y), ([word]) => ({ text: word, })); // WIP, need to be able to escape braces correctly // WIP, error if anything after closing brace const BracePattern = Peg.Map( Peg.Sequence( Peg.Regex(/\{/y), Peg.AtLeast( 0, Peg.Choose( Peg.Map(Brace, (text) => `{${text}}`), Peg.Map(Peg.Regex(/[^{}]+/y), ([text]) => text) ) ), Peg.Regex(/\}/y) ), ([_left, fragments, _right]) => fragments.join("") ); /** * @type {Peg.Pattern} */ function Brace(source, index) { // Thunk to allow Brace to recurse return BracePattern(source, index); } const Word = Peg.Map( Peg.Sequence( PreWordWhitespace, Peg.Choose( Peg.Map(BracePattern, (text) => ({ text, })), BasicWord ) ), ([_, word]) => word ); const CommandTerminator = Peg.Sequence( PreWordWhitespace, Peg.Choose( /** @type {Peg.Pattern} */ (Peg.Regex(/[\n;]/y)), Peg.End ) ); const Command = Peg.Map( Peg.Sequence(PreCommand, Peg.AtLeast(0, Word), CommandTerminator), ([_padding, words, _end]) => words ); 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(/(?