factor out helpers for PEG parsing
This commit is contained in:
parent
7b42a496c2
commit
71581253d5
3 changed files with 80 additions and 79 deletions
|
@ -6,6 +6,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<script src="helpers.js"></script>
|
||||
<script src="peg.js"></script>
|
||||
<script src="notcl.js"></script>
|
||||
<script src="3x5.js"></script>
|
||||
</body>
|
||||
|
|
85
notcl.js
85
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<T>} pattern
|
||||
* @param {(value: T)=> U} map
|
||||
* @return {Pattern<U>}
|
||||
*/
|
||||
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<RegExpExecArray>}
|
||||
*/
|
||||
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<T>} patterns
|
||||
* @return {Pattern<T>}
|
||||
*/
|
||||
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<T[K]>}} patterns
|
||||
* @return {Pattern<T>}
|
||||
*/
|
||||
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
|
||||
);
|
||||
|
||||
|
|
73
peg.js
Normal file
73
peg.js
Normal file
|
@ -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<T>} pattern
|
||||
* @param {(value: T)=> U} map
|
||||
* @return {Peg.Pattern<U>}
|
||||
*/
|
||||
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<RegExpExecArray>}
|
||||
*/
|
||||
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<T>} patterns
|
||||
* @return {Peg.Pattern<T>}
|
||||
*/
|
||||
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<T[K]>}} patterns
|
||||
* @return {Peg.Pattern<T>}
|
||||
*/
|
||||
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];
|
||||
};
|
||||
},
|
||||
};
|
Loading…
Reference in a new issue