From 71581253d5fda16557a5486a09ce31261474b7b0 Mon Sep 17 00:00:00 2001 From: Tangent Wantwight Date: Sat, 29 Jul 2023 00:26:29 -0400 Subject: [PATCH] factor out helpers for PEG parsing --- index.html | 1 + notcl.js | 85 ++++-------------------------------------------------- peg.js | 73 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 79 deletions(-) create mode 100644 peg.js diff --git a/index.html b/index.html index f9c3689..7a0e9e4 100644 --- a/index.html +++ b/index.html @@ -6,6 +6,7 @@ + diff --git a/notcl.js b/notcl.js index 6ae8e6e..180d15c 100644 --- a/notcl.js +++ b/notcl.js @@ -5,91 +5,18 @@ * @property {string} text */ -/** - * A Pattern is a function that matches against a string starting at a given index. - * - * If it matches successfully, it returns some captured value, and the index following the match. - * - * @template T - * @typedef {(source: string, index: number) => ([T, number] | null)} Pattern - */ +const InterCommandWhitespace = Peg.Regex(/[^\S\n;]*/y); -/** - * Creates a pattern that wraps another pattern, transforming the returned value on a match - * @template T, U - * @param {Pattern} pattern - * @param {(value: T)=> U} map - * @return {Pattern} - */ -function MapPattern(pattern, map) { - return function (source, index) { - const match = pattern(source, index); - return match ? [map(match[0]), match[1]] : null; - }; -} +const CommentPattern = Peg.Regex(/#.*\n/y); -/** - * Creates a pattern matching a regex & returning any captures. The regex needs to be sticky (using the //y modifier) - * @param {RegExp} regex - * @return {Pattern} - */ -function RegexPattern(regex) { - return function (source, index) { - regex.lastIndex = index; - const matches = regex.exec(source); - return matches ? [matches, regex.lastIndex] : null; - }; -} +const PreWordWhitespace = Peg.Regex(/[^\S\n;]*/y); -/** - * @template T - * @param {...Pattern} patterns - * @return {Pattern} - */ -function Choose(...patterns) { - return function (source, index) { - for (const pattern of patterns) { - const match = pattern(source, index); - if (match) { - return match; - } - } - return null; - }; -} - -/** - * @template {unknown[]} T - * @param {{[K in keyof T]: Pattern}} patterns - * @return {Pattern} - */ -function Sequence(...patterns) { - return function (source, index) { - const values = /** @type {T} */ (/** @type {unknown} */ ([])); - for (const pattern of patterns) { - const match = pattern(source, index); - if (match == null) { - return null; - } - values.push(match[0]); - index = match[1]; - } - return [values, index]; - }; -} - -const InterCommandWhitespace = RegexPattern(/[^\S\n;]*/y); - -const CommentPattern = RegexPattern(/#.*\n/y); - -const PreWordWhitespace = RegexPattern(/[^\S\n;]*/y); - -const BasicWord = MapPattern(RegexPattern(/[^\s;]+/y), ([word]) => ({ +const BasicWord = Peg.Map(Peg.Regex(/[^\s;]+/y), ([word]) => ({ text: word, })); -const WordPattern = MapPattern( - Sequence(PreWordWhitespace, BasicWord), +const WordPattern = Peg.Map( + Peg.Sequence(PreWordWhitespace, BasicWord), ([_, word]) => word ); diff --git a/peg.js b/peg.js new file mode 100644 index 0000000..0b82fe7 --- /dev/null +++ b/peg.js @@ -0,0 +1,73 @@ +/** + * A Pattern is a function that matches against a string starting at a given index. + * + * If it matches successfully, it returns some captured value, and the index following the match. + * + * @template T + * @typedef {(source: string, index: number) => ([T, number] | null)} Peg.Pattern + */ +var Peg = { + /** + * Creates a pattern that wraps another pattern, transforming the returned value on a match + * @template T, U + * @param {Peg.Pattern} pattern + * @param {(value: T)=> U} map + * @return {Peg.Pattern} + */ + Map(pattern, map) { + return function (source, index) { + const match = pattern(source, index); + return match ? [map(match[0]), match[1]] : null; + }; + }, + + /** + * Creates a pattern matching a regex & returning any captures. The regex needs to be sticky (using the //y modifier) + * @param {RegExp} regex + * @return {Peg.Pattern} + */ + Regex(regex) { + return function (source, index) { + regex.lastIndex = index; + const matches = regex.exec(source); + return matches ? [matches, regex.lastIndex] : null; + }; + }, + + /** + * @template T + * @param {...Peg.Pattern} patterns + * @return {Peg.Pattern} + */ + Choose(...patterns) { + return function (source, index) { + for (const pattern of patterns) { + const match = pattern(source, index); + if (match) { + return match; + } + } + return null; + }; + }, + + /** + * @template {unknown[]} T + * @param {{[K in keyof T]: Peg.Pattern}} patterns + * @return {Peg.Pattern} + */ + Sequence(...patterns) { + return function (source, index) { + const values = /** @type {T} */ (/** @type {unknown} */ ([])); + for (const pattern of patterns) { + const match = pattern(source, index); + if (match == null) { + return null; + } + values.push(match[0]); + index = match[1]; + } + return [values, index]; + }; + }, +};