package 000755 000000 000000 00000000000 14112463221 010534 5 ustar 00 000000 000000 package/LICENSE.md 000644 000000 000000 00000002060 14036024550 012220 0 ustar 00 000000 000000 MIT License
Copyright (c) 2020 Phil Plückthun
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
package/README.md 000644 000000 000000 00000036530 14112455270 012106 0 ustar 00 000000 000000
The magical sticky regex-based parser generator
Leveraging the power of sticky regexes and JS code generation, `reghex` allows
you to code parsers quickly, by surrounding regular expressions with a regex-like
[DSL](https://en.wikipedia.org/wiki/Domain-specific_language).
With `reghex` you can generate a parser from a tagged template literal, which is
quick to prototype and generates reasonably compact and performant code.
_This project is still in its early stages and is experimental. Its API may still
change and some issues may need to be ironed out._
## Quick Start
##### 1. Install with yarn or npm
```sh
yarn add reghex
# or
npm install --save reghex
```
##### 2. Add the plugin to your Babel configuration _(optional)_
In your `.babelrc`, `babel.config.js`, or `package.json:babel` add:
```json
{
"plugins": ["reghex/babel"]
}
```
Alternatively, you can set up [`babel-plugin-macros`](https://github.com/kentcdodds/babel-plugin-macros) and
import `reghex` from `"reghex/macro"` instead.
This step is **optional**. `reghex` can also generate its optimised JS code during runtime.
This will only incur a tiny parsing cost on initialisation, but due to the JIT of modern
JS engines there won't be any difference in performance between pre-compiled and compiled
versions otherwise.
Since the `reghex` runtime is rather small, for larger grammars it may even make sense not
to precompile the matchers at all. For this case you may pass the `{ "codegen": false }`
option to the Babel plugin, which will minify the `reghex` matcher templates without
precompiling them.
##### 3. Have fun writing parsers!
```js
import { match, parse } from 'reghex';
const name = match('name')`
${/\w+/}
`;
parse(name)('hello');
// [ "hello", .tag = "name" ]
```
## Concepts
The fundamental concept of `reghex` are regexes, specifically
[sticky regexes](https://www.loganfranken.com/blog/831/es6-everyday-sticky-regex-matches/)!
These are regular expressions that don't search a target string, but instead match at the
specific position they're at. The flag for sticky regexes is `y` and hence
they can be created using `/phrase/y` or `new RegExp('phrase', 'y')`.
**Sticky Regexes** are the perfect foundation for a parsing framework in JavaScript!
Because they only match at a single position they can be used to match patterns
continuously, as a parser would. Like global regexes, we can then manipulate where
they should be matched by setting `regex.lastIndex = index;` and after matching
read back their updated `regex.lastIndex`.
> **Note:** Sticky Regexes aren't natively
> [supported in any versions of Internet Explorer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky#Browser_compatibility). `reghex` works around this by imitating its behaviour, which may decrease performance on IE11.
This primitive allows us to build up a parser from regexes that you pass when
authoring a parser function, also called a "matcher" in `reghex`. When `reghex` compiles
to parser code, this code is just a sequence and combination of sticky regexes that
are executed in order!
```js
let input = 'phrases should be parsed...';
let lastIndex = 0;
const regex = /phrase/y;
function matcher() {
let match;
// Before matching we set the current index on the RegExp
regex.lastIndex = lastIndex;
// Then we match and store the result
if ((match = regex.exec(input))) {
// If the RegExp matches successfully, we update our lastIndex
lastIndex = regex.lastIndex;
}
}
```
This mechanism is used in all matcher functions that `reghex` generates.
Internally `reghex` keeps track of the input string and the current index on
that string, and the matcher functions execute regexes against this state.
## Authoring Guide
You can write "matchers" by importing the `match` import from `reghex` and
using it to write a matcher expression.
```js
import { match } from 'reghex';
const name = match('name')`
${/\w+/}
`;
```
As can be seen above, the `match` function, is called with a "node name" and
is then called as a tagged template. This template is our **parsing definition**.
`reghex` functions only with its Babel plugin, which will detect `match('name')`
and replace the entire tag with a parsing function, which may then look like
the following in your transpiled code:
```js
import { _pattern /* ... */ } from 'reghex';
var _name_expression = _pattern(/\w+/);
var name = function name() {
/* ... */
};
```
We've now successfully created a matcher, which matches a single regex, which
is a pattern of one or more letters. We can execute this matcher by calling
it with the curried `parse` utility:
```js
import { parse } from 'reghex';
const result = parse(name)('Tim');
console.log(result); // [ "Tim", .tag = "name" ]
console.log(result.tag); // "name"
```
If the string (Here: "Tim") was parsed successfully by the matcher, it will
return an array that contains the result of the regex. The array is special
in that it will also have a `tag` property set to the matcher's name, here
`"name"`, which we determined when we defined the matcher as `match('name')`.
```js
import { parse } from 'reghex';
parse(name)('42'); // undefined
```
Similarly, if the matcher does not parse an input string successfully, it will
return `undefined` instead.
### Nested matchers
This on its own is nice, but a parser must be able to traverse a string and
turn it into an [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree).
To introduce nesting to `reghex` matchers, we can refer to one matcher in another!
Let's extend our original example;
```js
import { match } from 'reghex';
const name = match('name')`
${/\w+/}
`;
const hello = match('hello')`
${/hello /} ${name}
`;
```
The new `hello` matcher is set to match `/hello /` and then attempts to match
the `name` matcher afterwards. If either of these matchers fail, it will return
`undefined` as well and roll back its changes. Using this matcher will give us
**nested abstract output**.
We can also see in this example that _outside_ of the regex interpolations,
whitespace and newlines don't matter.
```js
import { parse } from 'reghex';
parse(hello)('hello tim');
/*
[
"hello",
["tim", .tag = "name"],
.tag = "hello"
]
*/
```
Furthermore, interpolations don't have to just be RegHex matchers. They can
also be functions returning matchers or completely custom matching functions.
This is useful when your DSL becomes _self-referential_, i.e. when one matchers
start referencing each other forming a loop. To fix this we can create a
function that returns our root matcher:
```js
import { match } from 'reghex';
const value = match('value')`
(${/\w+/} | ${() => root})+
`;
const root = match('root')`
${/root/}+ ${value}
`;
```
### Regex-like DSL
We've seen in the previous examples that matchers are authored using tagged
template literals, where interpolations can either be filled using regexes,
`${/pattern/}`, or with other matchers `${name}`.
The tagged template syntax supports more ways to match these interpolations,
using a regex-like Domain Specific Language. Unlike in regexes, whitespace
and newlines don't matter, which makes it easier to format and read matchers.
We can create **sequences** of matchers by adding multiple expressions in
a row. A matcher using `${/1/} ${/2/}` will attempt to match `1` and then `2`
in the parsed string. This is just one feature of the regex-like DSL. The
available operators are the following:
| Operator | Example | Description |
| -------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `?` | `${/1/}?` | An **optional** may be used to make an interpolation optional. This means that the interpolation may or may not match. |
| `*` | `${/1/}*` | A **star** can be used to match an arbitrary amount of interpolation or none at all. This means that the interpolation may repeat itself or may not be matched at all. |
| `+` | `${/1/}+` | A **plus** is used like `*` and must match one or more times. When the matcher doesn't match, that's considered a failing case, since the match isn't optional. |
| `\|` | `${/1/} \| ${/2/}` | An **alternation** can be used to match either one thing or another, falling back when the first interpolation fails. |
| `()` | `(${/1/} ${/2/})+` | A **group** can be used to apply one of the other operators to an entire group of interpolations. |
| `(?: )` | `(?: ${/1/})` | A **non-capturing group** is like a regular group, but the interpolations matched inside it don't appear in the parser's output. |
| `(?= )` | `(?= ${/1/})` | A **positive lookahead** checks whether interpolations match, and if so continues the matcher without changing the input. If it matches, it's essentially ignored. |
| `(?! )` | `(?! ${/1/})` | A **negative lookahead** checks whether interpolations _don't_ match, and if so continues the matcher without changing the input. If the interpolations do match the matcher is aborted. |
A couple of operators also support "short hands" that allow you to write
lookaheads or non-capturing groups a little quicker.
| Shorthand | Example | Description |
| --------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `:` | `:${/1/}` | A **non-capturing group** is like a regular group, but the interpolations matched inside it don't appear in the parser's output. |
| `=` | `=${/1/}` | A **positive lookahead** checks whether interpolations match, and if so continues the matcher without changing the input. If it matches, it's essentially ignored. |
| `!` | `!${/1/}` | A **negative lookahead** checks whether interpolations _don't_ match, and if so continues the matcher without changing the input. If the interpolations do match the matcher is aborted. |
We can combine and compose these operators to create more complex matchers.
For instance, we can extend the original example to only allow a specific set
of names by using the `|` operator:
```js
const name = match('name')`
${/tim/} | ${/tom/} | ${/tam/}
`;
parse(name)('tim'); // [ "tim", .tag = "name" ]
parse(name)('tom'); // [ "tom", .tag = "name" ]
parse(name)('patrick'); // undefined
```
The above will now only match specific name strings. When one pattern in this
chain of **alternations** does not match, it will try the next one.
We can also use **groups** to add more matchers around the alternations themselves,
by surrounding the alternations with `(` and `)`
```js
const name = match('name')`
(${/tim/} | ${/tom/}) ${/!/}
`;
parse(name)('tim!'); // [ "tim", "!", .tag = "name" ]
parse(name)('tom!'); // [ "tom", "!", .tag = "name" ]
parse(name)('tim'); // undefined
```
Maybe we're also not that interested in the `"!"` showing up in the output node.
If we want to get rid of it, we can use a **non-capturing group** to hide it,
while still requiring it.
```js
const name = match('name')`
(${/tim/} | ${/tom/}) (?: ${/!/})
`;
parse(name)('tim!'); // [ "tim", .tag = "name" ]
parse(name)('tim'); // undefined
```
Lastly, like with regexes, `?`, `*`, and `+` may be used as "quantifiers". The first two
may also be optional and _not_ match their patterns without the matcher failing.
The `+` operator is used to match an interpolation _one or more_ times, while the
`*` operators may match _zero or more_ times. Let's use this to allow the `"!"`
to repeat.
```js
const name = match('name')`
(${/tim/} | ${/tom/})+ (?: ${/!/})*
`;
parse(name)('tim!'); // [ "tim", .tag = "name" ]
parse(name)('tim!!!!'); // [ "tim", .tag = "name" ]
parse(name)('tim'); // [ "tim", .tag = "name" ]
parse(name)('timtim'); // [ "tim", tim", .tag = "name" ]
```
As we can see from the above, like in regexes, quantifiers can be combined with groups,
non-capturing groups, or other groups.
### Transforming as we match
In the previous sections, we've seen that the **nodes** that `reghex` outputs are arrays containing
match strings or other nodes and have a special `tag` property with the node's type.
We can **change this output** while we're parsing by passing a function to our matcher definition.
```js
const name = match('name', (x) => x[0])`
(${/tim/} | ${/tom/}) ${/!/}
`;
parse(name)('tim'); // "tim"
```
In the above example, we're passing a small function, `x => x[0]` to the matcher as a
second argument. This will change the matcher's output, which causes the parser to
now return a new output for this matcher.
We can use this function creatively by outputting full AST nodes, maybe even like the
ones that resemble Babel's output:
```js
const identifier = match('identifier', (x) => ({
type: 'Identifier',
name: x[0],
}))`
${/[\w_][\w\d_]+/}
`;
parse(name)('var_name'); // { type: "Identifier", name: "var_name" }
```
We've now entirely changed the output of the parser for this matcher. Given that each
matcher can change its output, we're free to change the parser's output entirely.
By returning `null` or `undefined` in this matcher, we can also change the matcher
to not have matched, which would cause other matchers to treat it like a mismatch!
```js
import { match, parse } from 'reghex';
const name = match('name')((x) => {
return x[0] !== 'tim' ? x : undefined;
})`
${/\w+/}
`;
const hello = match('hello')`
${/hello /} ${name}
`;
parse(name)('tom'); // ["hello", ["tom", .tag = "name"], .tag = "hello"]
parse(name)('tim'); // undefined
```
Lastly, if we need to create these special array nodes ourselves, we can use `reghex`'s
`tag` export for this purpose.
```js
import { tag } from 'reghex';
tag(['test'], 'node_name');
// ["test", .tag = "node_name"]
```
### Tagged Template Parsing
Any grammar in RegHex can also be used to parse a tagged template literal.
A tagged template literal consists of a list of literals alternating with
a list of "interpolations".
In RegHex we can add an `interpolation` matcher to our grammars to allow it
to parse interpolations in a template literal.
```js
import { interpolation } from 'reghex';
const anyNumber = interpolation((x) => typeof x === 'number');
const num = match('num')`
${/[+-]?/} ${anyNumber}
`;
parse(num)`+${42}`;
// ["+", 42, .tag = "num"]
```
This grammar now allows us to match arbitrary values if they're input into the
parser. We can now call our grammar using a tagged template literal themselves
to parse this.
**That's it! May the RegExp be ever in your favor.**
package/babel.js 000644 000000 000000 00000000064 14112455270 012223 0 ustar 00 000000 000000 module.exports = require('./dist/reghex-babel.js');
package/dist 000755 000000 000000 00000000000 14112463223 011501 5 ustar 00 000000 000000 package/macro.js 000644 000000 000000 00000000074 14036024550 012256 0 ustar 00 000000 000000 module.exports = require('./dist/reghex-macro.js').default;
package/package.json 000644 000000 000000 00000004115 14112463214 013104 0 ustar 00 000000 000000 {
"name": "reghex",
"version": "3.0.2",
"description": "The magical sticky regex-based parser generator 🧙",
"author": "Phil Pluckthun ",
"license": "MIT",
"main": "dist/reghex-core",
"module": "dist/reghex-core.mjs",
"source": "src/core.js",
"sideEffects": false,
"files": [
"README.md",
"LICENSE.md",
"dist",
"src",
"babel.js",
"macro.js"
],
"exports": {
".": {
"import": "./dist/reghex-core.mjs",
"require": "./dist/reghex-core.js"
},
"./babel": {
"require": "./dist/reghex-babel.js"
},
"./macro": {
"require": "./dist/reghex-macro.js"
},
"./package.json": "./package.json"
},
"scripts": {
"prepublishOnly": "run-s clean build test",
"clean": "rimraf dist ./node_modules/.cache",
"build": "rollup -c rollup.config.js",
"test": "jest"
},
"keywords": [
"regex",
"sticky regex",
"parser",
"parser generator",
"babel"
],
"repository": "https://github.com/kitten/reghex",
"bugs": {
"url": "https://github.com/kitten/reghex/issues"
},
"devDependencies": {
"@ampproject/rollup-plugin-closure-compiler": "^0.27.0",
"@babel/core": "7.15.0",
"@babel/plugin-transform-modules-commonjs": "^7.15.0",
"@babel/plugin-transform-template-literals": "^7.14.5",
"@rollup/plugin-buble": "^0.21.3",
"@rollup/plugin-commonjs": "^20.0.0",
"@rollup/plugin-node-resolve": "^13.0.4",
"@rollup/pluginutils": "^4.1.1",
"@sucrase/jest-plugin": "^2.1.1",
"babel-jest": "^27.1.0",
"babel-plugin-closure-elimination": "^1.3.2",
"husky-v4": "^4.3.8",
"jest": "^27.1.0",
"lint-staged": "^11.1.2",
"npm-run-all": "^4.1.5",
"prettier": "^2.3.2",
"rimraf": "^3.0.2",
"rollup": "^2.56.3"
},
"prettier": {
"singleQuote": true
},
"lint-staged": {
"*.{js,jsx,json,md}": "prettier --write"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged --quiet --relative"
}
},
"jest": {
"testEnvironment": "node",
"transform": {
"\\.js$": "@sucrase/jest-plugin"
}
}
}
package/src 000755 000000 000000 00000000000 14112455272 011332 5 ustar 00 000000 000000 package/dist/d56fb69a.js 000644 000000 000000 00000020566 14112463223 013355 0 ustar 00 000000 000000 'use strict';
var _state = "state", _node = "node", _match = "x";
function js() {
for (var c = arguments, e = arguments[0][0], b = 1; b < arguments.length; b++) {
e = e + c[b] + c[0][b];
}
return e.trim();
}
var copy = function(c) {
var e = {}, b;
for (b in c) {
e[b] = c[b];
}
return e;
}, assignIndex = function(c) {
return " var y" + (c + ("=" + (_state + (".y,x" + (c + ("=" + (_state + ".x;")))))));
}, restoreIndex = function(c) {
return _state + (".y=y" + (c + (";" + (_state + (".x=x" + (c + ";"))))));
}, astExpression = function(c, e, b) {
e = !!b.capture && !c.capture;
var d = b.length && b.abort && _node + (".length=ln" + (b.length + ";")) || "";
return " if((" + (_match + " = " + c.expression.id + "(" + _state + ")) " + (e ? "!=" : "==") + " null)" + ((e ? "{" + (_node + (".push(" + (_match + ");}else "))) : "") + ("{" + (restoreIndex(b.index) + (d + (b.abort + "}"))))));
}, astGroup = function(c, e, b) {
var d = !!b.capture && !c.capture;
b = copy(b);
b.capture = d;
return !b.length && d ? (b.length = e, "var ln" + (e + ("=" + (_node + ".length;"))) + (astSequence(c.sequence, e + 1, b) + "")) : astSequence(c.sequence, e + 1, b);
}, astChild = function(c, e, b) {
return c.expression ? astExpression(c, e, b) : astGroup(c, e, b);
}, astQuantifier = function(c, e, b) {
var d = b.index, p = b.abort, q = "inv_" + e, h = "group_" + e;
b = copy(b);
"!" === c.capture && (b.index = e, b.abort = "break " + (q + ""));
"+" === c.quantifier ? (h = copy(c), h.quantifier = "*", b = astChild(c, e, b) + (astQuantifier(h, e, b) + "")) : "*" === c.quantifier ? (b.length = 0, b.index = e, b.abort = "break " + (h + ";"), b = h + (":for(;;){" + (assignIndex(e) + (astChild(c, e, b) + "}")))) : "?" === c.quantifier && c.expression ? (b.index = e, b.abort = "", b = assignIndex(e) + (astChild(c, e, b) + "")) : "?" === c.quantifier ? (b.index = e, b.abort = "break " + (h + ""), b = h + (":{" + (assignIndex(e) + (astChild(c,
e, b) + "}")))) : b = astChild(c, e, b);
return "!" === c.capture ? q + (":{" + (assignIndex(e) + (b + (restoreIndex(d) + (p + "}"))))) : "=" === c.capture ? assignIndex(e) + (b + (restoreIndex(e) + "")) : b;
}, astSequence = function(c, e, b) {
for (var d = c.alternation ? "alt_" + e : "", p = ""; c; c = c.alternation) {
var q = "block_" + e, h = b;
c.alternation && (h = copy(b), h.index = e, h.abort = "break " + (q + ";"));
for (var l = "", m = 0; m < c.length; m++) {
l += astQuantifier(c[m], e, h);
}
p = c.alternation ? p + (q + (":{" + (assignIndex(e) + (l + (" break " + (d + ";}")))))) : p + l;
}
return d ? d + (":{" + (p + "}")) : p;
}, astRoot = function(c, e, b) {
return "(function(" + (_state + ("){" + (assignIndex(1) + (" var " + (_node + ("=[];var " + (_match + (";" + (astSequence(c, 2, {index:1, length:0, abort:"return;", capture:!0}) + (" if(" + (e + (")" + (_node + (".tag=" + (e + (";return " + ((b ? "(" + (b + (")(" + (_node + ")"))) : _node) + ";})")))))))))))))))));
}, syntaxError = function(c) {
throw new SyntaxError('Unexpected token "' + c + '"');
}, parse = function(c, e) {
for (var b = [], d = [], p = null, q, h = d, l, m, a = 0; a < c.length + e.length; a++) {
0 !== a % 2 && (m = e[a++ >> 1], h.push({expression:m, capture:l}), l = void 0);
var k = c[a >> 1];
for (m = 0; m < k.length;) {
var f = k[m++];
" " !== f && "\t" !== f && "\r" !== f && "\n" !== f && ("|" === f && h.length ? h = h.alternation = [] : ")" === f && h.length ? (p = null, (h = b.pop()) || syntaxError(f)) : "(" === f ? (b.push(h), h.push(p = {sequence:[], capture:l}), h = p.sequence, l = void 0) : ":" === f || "=" === f || "!" === f ? (l = f, k[m] && "(" !== k[m] && syntaxError(f)) : "?" === f && !h.length && p ? (l = k[m++], ":" === l || "=" === l || "!" === l ? (p.capture = l, l = void 0) : syntaxError(f)) : "?" !== f &&
"+" !== f && "*" !== f || !(q = h[h.length - 1]) ? syntaxError(f) : q.quantifier = f);
}
}
return d;
};
function _ref3(c) {
return c.value.cooked;
}
function makeHelpers(c) {
function e(a) {
return d.isImportSpecifier(a) && "match" === a.imported.name;
}
function b(a) {
return d.stringLiteral(a.value.cooked.replace(/\s*/g, ""));
}
var d = c.types, p = c.template, q = /reghex$|^reghex\/macro/, h = !1;
d.identifier("match");
var l = d.identifier("__pattern"), m = new Map;
return {updateImport:function(a) {
!h && q.test(a.node.source.value) && (h = !0, "reghex" !== a.node.source.value && (a.node.source = d.stringLiteral("reghex")), l = a.scope.generateUidIdentifier("_pattern"), a.node.specifiers.push(d.importSpecifier(l, d.identifier("__pattern"))), a.node.specifiers.find(e) || a.node.specifiers.push(d.importSpecifier(a.scope.generateUidIdentifier("match"), d.identifier("match"))));
}, isMatch:function(a) {
return d.isTaggedTemplateExpression(a.node) && d.isCallExpression(a.node.tag) && d.isIdentifier(a.node.tag.callee) && a.scope.hasBinding(a.node.tag.callee.name) ? (d.isVariableDeclarator(a.parentPath) && (a.parentPath._isMatch = !0), !0) : d.isVariableDeclarator(a.parentPath) && a.parentPath._isMatch;
}, getMatchImport:function(a) {
d.assertTaggedTemplateExpression(a.node);
a = a.scope.getBinding(a.node.tag.callee.name);
return "module" === a.kind && d.isImportDeclaration(a.path.parent) && q.test(a.path.parent.source.value) && d.isImportSpecifier(a.path.node) ? a.path.parentPath : null;
}, getMatchName:function(a) {
d.assertTaggedTemplateExpression(a.node);
var k = a.get("tag.arguments.0");
if (k) {
var f = k.evaluate(), r = f.confident;
f = f.value;
if (!r && d.isIdentifier(k.node)) {
return k.node.name;
}
if (r && "string" === typeof f) {
return f;
}
}
return a.scope.generateUidIdentifierBasedOnNode(a.node);
}, _prepareExpressions:function(a) {
var k = this;
d.assertTaggedTemplateExpression(a.node);
var f = [], r = this.getMatchName(a), t = a.node.quasi.expressions.map(function(g, n) {
d.isArrowFunctionExpression(g) && d.isIdentifier(g.body) ? g = g.body : (d.isFunctionExpression(g) || d.isArrowFunctionExpression(g)) && d.isBlockStatement(g.body) && 1 === g.body.body.length && d.isReturnStatement(g.body.body[0]) && d.isIdentifier(g.body.body[0].argument) && (g = g.body.body[0].argument);
if (n = d.isIdentifier(g) && a.scope.hasBinding(g.name)) {
var u = a.scope.getBinding(g.name);
if (d.isVariableDeclarator(u.path.node)) {
u = u.path.get("init");
if (k.isMatch(u)) {
return g;
}
if (m.has(g.name)) {
return d.identifier(m.get(g.name));
}
}
}
n = a.scope.generateUidIdentifier(n ? g.name + "_expression" : r + "_expression");
f.push(d.variableDeclarator(n, d.callExpression(d.identifier(l.name), [g])));
d.isIdentifier(g) && m.set(g.name, n.name);
return n;
});
f.length && a.getStatementParent().insertBefore(d.variableDeclaration("var", f));
return t.map(function(g) {
var n = a.scope.getBinding(g.name);
return n && d.isVariableDeclarator(n.path.node) && (n = n.path.get("init"), k.isMatch(n)) ? {fn:!0, id:g.name} : {fn:!1, id:d.isStringLiteral(g) ? JSON.stringify(g.value) : g.name};
});
}, _prepareTransform:function(a) {
var k = a.node.tag.arguments[1];
if (!k) {
return null;
}
if (d.isIdentifier(k)) {
return k.name;
}
var f = this.getMatchName(a);
f = a.scope.generateUidIdentifier(f + "_transform");
k = d.variableDeclarator(f, k);
a.getStatementParent().insertBefore(d.variableDeclaration("var", [k]));
return f.name;
}, minifyMatch:function(a) {
var k = a.node.quasi.quasis.map(b), f = a.node.quasi.expressions;
this._prepareTransform(a);
a.replaceWith(d.callExpression(a.node.tag, [d.arrayExpression(k)].concat(f)));
}, transformMatch:function(a) {
var k = a.node.tag.arguments[0];
k || (k = d.nullLiteral());
var f = a.node.quasi.quasis.map(_ref3), r = this._prepareExpressions(a), t = this._prepareTransform(a);
try {
var g = parse(f, r);
} catch (n) {
if ("SyntaxError" !== n.name) {
throw n;
}
throw a.get("quasi").buildCodeFrameError(n.message);
}
f = astRoot(g, "%%name%%", t && "%%transform%%");
a.replaceWith(p.expression(f)(t ? {name:k, transform:t} : {name:k}));
}};
}
exports.makeHelpers = makeHelpers
//# sourceMappingURL=d56fb69a.js.map
package/dist/d56fb69a.js.map 000644 000000 000000 00000043117 14112463223 014126 0 ustar 00 000000 000000 {"version":3,"file":"d56fb69a.js","sources":["../src/codegen.js","../src/parser.js","../src/babel/transform.js"],"sourcesContent":["const _state = 'state';\nconst _node = 'node';\nconst _match = 'x';\n\nfunction js(/* arguments */) {\n let body = arguments[0][0];\n for (let i = 1; i < arguments.length; i++)\n body = body + arguments[i] + arguments[0][i];\n return body.trim();\n}\n\nconst copy = (prev) => {\n const next = {};\n for (const key in prev) next[key] = prev[key];\n return next;\n};\n\nconst assignIndex = (depth) => js`\n var y${depth} = ${_state}.y,\n x${depth} = ${_state}.x;\n`;\n\nconst restoreIndex = (depth) => js`\n ${_state}.y = y${depth};\n ${_state}.x = x${depth};\n`;\n\nconst astExpression = (ast, depth, opts) => {\n const capture = !!opts.capture && !ast.capture;\n const restoreLength =\n (opts.length && opts.abort && js`${_node}.length = ln${opts.length};`) ||\n '';\n const condition = `(${_match} = ${ast.expression.id}(${_state})) ${\n capture ? '!=' : '=='\n } null`;\n return js`\n if (${condition}) ${\n capture\n ? js`{\n ${_node}.push(${_match});\n } else `\n : ''\n }{\n ${restoreIndex(opts.index)}\n ${restoreLength}\n ${opts.abort}\n }\n `;\n};\n\nconst astGroup = (ast, depth, opts) => {\n const capture = !!opts.capture && !ast.capture;\n\n opts = copy(opts);\n opts.capture = capture;\n\n if (!opts.length && capture) {\n opts.length = depth;\n return js`\n ${js`var ln${depth} = ${_node}.length;`}\n ${astSequence(ast.sequence, depth + 1, opts)}\n `;\n }\n\n return astSequence(ast.sequence, depth + 1, opts);\n};\n\nconst astChild = (ast, depth, opts) =>\n ast.expression ? astExpression(ast, depth, opts) : astGroup(ast, depth, opts);\n\nconst astQuantifier = (ast, depth, opts) => {\n const { index, abort } = opts;\n const invert = `inv_${depth}`;\n const group = `group_${depth}`;\n\n opts = copy(opts);\n if (ast.capture === '!') {\n opts.index = depth;\n opts.abort = js`break ${invert}`;\n }\n\n let child;\n if (ast.quantifier === '+') {\n const starAst = copy(ast);\n starAst.quantifier = '*';\n child = js`\n ${astChild(ast, depth, opts)}\n ${astQuantifier(starAst, depth, opts)}\n `;\n } else if (ast.quantifier === '*') {\n opts.length = 0;\n opts.index = depth;\n opts.abort = js`break ${group};`;\n\n child = js`\n ${group}: for (;;) {\n ${assignIndex(depth)}\n ${astChild(ast, depth, opts)}\n }\n `;\n } else if (ast.quantifier === '?' && ast.expression) {\n opts.index = depth;\n opts.abort = '';\n\n child = js`\n ${assignIndex(depth)}\n ${astChild(ast, depth, opts)}\n `;\n } else if (ast.quantifier === '?') {\n opts.index = depth;\n opts.abort = js`break ${group}`;\n\n child = js`\n ${group}: {\n ${assignIndex(depth)}\n ${astChild(ast, depth, opts)}\n }\n `;\n } else {\n child = astChild(ast, depth, opts);\n }\n\n if (ast.capture === '!') {\n return js`\n ${invert}: {\n ${assignIndex(depth)}\n ${child}\n ${restoreIndex(index)}\n ${abort}\n }\n `;\n } else if (ast.capture === '=') {\n return js`\n ${assignIndex(depth)}\n ${child}\n ${restoreIndex(depth)}\n `;\n } else {\n return child;\n }\n};\n\nconst astSequence = (ast, depth, opts) => {\n const alternation = ast.alternation ? `alt_${depth}` : '';\n\n let body = '';\n for (; ast; ast = ast.alternation) {\n const block = `block_${depth}`;\n\n let childOpts = opts;\n if (ast.alternation) {\n childOpts = copy(opts);\n childOpts.index = depth;\n childOpts.abort = js`break ${block};`;\n }\n\n let sequence = '';\n for (let i = 0; i < ast.length; i++)\n sequence += astQuantifier(ast[i], depth, childOpts);\n\n if (!ast.alternation) {\n body += sequence;\n } else {\n body += js`\n ${block}: {\n ${assignIndex(depth)}\n ${sequence}\n break ${alternation};\n }\n `;\n }\n }\n\n if (!alternation) return body;\n\n return js`\n ${alternation}: {\n ${body}\n }\n `;\n};\n\nconst astRoot = (ast, name, transform) => {\n return js`\n (function (${_state}) {\n ${assignIndex(1)}\n var ${_node} = [];\n var ${_match};\n\n ${astSequence(ast, 2, {\n index: 1,\n length: 0,\n abort: js`return;`,\n capture: true,\n })}\n\n if (${name}) ${_node}.tag = ${name};\n return ${transform ? js`(${transform})(${_node})` : _node};\n })\n `;\n};\n\nexport { astRoot };\n","const syntaxError = (char) => {\n throw new SyntaxError('Unexpected token \"' + char + '\"');\n};\n\nexport const parse = (quasis, expressions) => {\n let quasiIndex = 0;\n let stackIndex = 0;\n\n const sequenceStack = [];\n const rootSequence = [];\n\n let currentGroup = null;\n let lastMatch;\n let currentSequence = rootSequence;\n let capture;\n\n for (\n let quasiIndex = 0, stackIndex = 0;\n stackIndex < quasis.length + expressions.length;\n stackIndex++\n ) {\n if (stackIndex % 2 !== 0) {\n const expression = expressions[stackIndex++ >> 1];\n currentSequence.push({ expression, capture });\n capture = undefined;\n }\n\n const quasi = quasis[stackIndex >> 1];\n for (quasiIndex = 0; quasiIndex < quasi.length; ) {\n const char = quasi[quasiIndex++];\n if (char === ' ' || char === '\\t' || char === '\\r' || char === '\\n') {\n } else if (char === '|' && currentSequence.length) {\n currentSequence = currentSequence.alternation = [];\n } else if (char === ')' && currentSequence.length) {\n currentGroup = null;\n currentSequence = sequenceStack.pop();\n if (!currentSequence) syntaxError(char);\n } else if (char === '(') {\n sequenceStack.push(currentSequence);\n currentSequence.push((currentGroup = { sequence: [], capture }));\n currentSequence = currentGroup.sequence;\n capture = undefined;\n } else if (char === ':' || char === '=' || char === '!') {\n capture = char;\n const nextChar = quasi[quasiIndex];\n if (quasi[quasiIndex] && quasi[quasiIndex] !== '(') syntaxError(char);\n } else if (char === '?' && !currentSequence.length && currentGroup) {\n capture = quasi[quasiIndex++];\n if (capture === ':' || capture === '=' || capture === '!') {\n currentGroup.capture = capture;\n capture = undefined;\n } else {\n syntaxError(char);\n }\n } else if (\n (char === '?' || char === '+' || char === '*') &&\n (lastMatch = currentSequence[currentSequence.length - 1])\n ) {\n lastMatch.quantifier = char;\n } else {\n syntaxError(char);\n }\n }\n }\n\n return rootSequence;\n};\n","import { astRoot } from '../codegen';\nimport { parse } from '../parser';\n\nexport function makeHelpers({ types: t, template }) {\n const regexPatternsRe = /^[()\\[\\]|.+?*]|[^\\\\][()\\[\\]|.+?*$^]|\\\\[wdsWDS]/;\n const importSourceRe = /reghex$|^reghex\\/macro/;\n const importName = 'reghex';\n\n let _hasUpdatedImport = false;\n let _matchId = t.identifier('match');\n let _patternId = t.identifier('__pattern');\n\n const _hoistedExpressions = new Map();\n\n return {\n /** Adds the reghex import declaration to the Program scope */\n updateImport(path) {\n if (_hasUpdatedImport) return;\n if (!importSourceRe.test(path.node.source.value)) return;\n _hasUpdatedImport = true;\n\n if (path.node.source.value !== importName) {\n path.node.source = t.stringLiteral(importName);\n }\n\n _patternId = path.scope.generateUidIdentifier('_pattern');\n path.node.specifiers.push(\n t.importSpecifier(_patternId, t.identifier('__pattern'))\n );\n\n const tagImport = path.node.specifiers.find((node) => {\n return t.isImportSpecifier(node) && node.imported.name === 'match';\n });\n\n if (!tagImport) {\n path.node.specifiers.push(\n t.importSpecifier(\n (_matchId = path.scope.generateUidIdentifier('match')),\n t.identifier('match')\n )\n );\n } else {\n _matchId = tagImport.imported;\n }\n },\n\n /** Determines whether the given tagged template expression is a reghex match */\n isMatch(path) {\n if (\n t.isTaggedTemplateExpression(path.node) &&\n t.isCallExpression(path.node.tag) &&\n t.isIdentifier(path.node.tag.callee) &&\n path.scope.hasBinding(path.node.tag.callee.name)\n ) {\n if (t.isVariableDeclarator(path.parentPath))\n path.parentPath._isMatch = true;\n return true;\n }\n\n return (\n t.isVariableDeclarator(path.parentPath) && path.parentPath._isMatch\n );\n },\n\n /** Given a reghex match, returns the path to reghex's match import declaration */\n getMatchImport(path) {\n t.assertTaggedTemplateExpression(path.node);\n const binding = path.scope.getBinding(path.node.tag.callee.name);\n\n if (\n binding.kind !== 'module' ||\n !t.isImportDeclaration(binding.path.parent) ||\n !importSourceRe.test(binding.path.parent.source.value) ||\n !t.isImportSpecifier(binding.path.node)\n ) {\n return null;\n }\n\n return binding.path.parentPath;\n },\n\n /** Given a match, returns an evaluated name or a best guess */\n getMatchName(path) {\n t.assertTaggedTemplateExpression(path.node);\n const nameArgumentPath = path.get('tag.arguments.0');\n if (nameArgumentPath) {\n const { confident, value } = nameArgumentPath.evaluate();\n if (!confident && t.isIdentifier(nameArgumentPath.node)) {\n return nameArgumentPath.node.name;\n } else if (confident && typeof value === 'string') {\n return value;\n }\n }\n\n return path.scope.generateUidIdentifierBasedOnNode(path.node);\n },\n\n /** Given a match, hoists its expressions in front of the match's statement */\n _prepareExpressions(path) {\n t.assertTaggedTemplateExpression(path.node);\n\n const variableDeclarators = [];\n const matchName = this.getMatchName(path);\n\n const hoistedExpressions = path.node.quasi.expressions.map(\n (expression, i) => {\n if (\n t.isArrowFunctionExpression(expression) &&\n t.isIdentifier(expression.body)\n ) {\n expression = expression.body;\n } else if (\n (t.isFunctionExpression(expression) ||\n t.isArrowFunctionExpression(expression)) &&\n t.isBlockStatement(expression.body) &&\n expression.body.body.length === 1 &&\n t.isReturnStatement(expression.body.body[0]) &&\n t.isIdentifier(expression.body.body[0].argument)\n ) {\n expression = expression.body.body[0].argument;\n }\n\n const isBindingExpression =\n t.isIdentifier(expression) &&\n path.scope.hasBinding(expression.name);\n if (isBindingExpression) {\n const binding = path.scope.getBinding(expression.name);\n if (t.isVariableDeclarator(binding.path.node)) {\n const matchPath = binding.path.get('init');\n if (this.isMatch(matchPath)) {\n return expression;\n } else if (_hoistedExpressions.has(expression.name)) {\n return t.identifier(_hoistedExpressions.get(expression.name));\n }\n }\n }\n\n const id = path.scope.generateUidIdentifier(\n isBindingExpression\n ? `${expression.name}_expression`\n : `${matchName}_expression`\n );\n\n variableDeclarators.push(\n t.variableDeclarator(\n id,\n t.callExpression(t.identifier(_patternId.name), [expression])\n )\n );\n\n if (t.isIdentifier(expression)) {\n _hoistedExpressions.set(expression.name, id.name);\n }\n\n return id;\n }\n );\n\n if (variableDeclarators.length) {\n path\n .getStatementParent()\n .insertBefore(t.variableDeclaration('var', variableDeclarators));\n }\n\n return hoistedExpressions.map((id) => {\n const binding = path.scope.getBinding(id.name);\n if (binding && t.isVariableDeclarator(binding.path.node)) {\n const matchPath = binding.path.get('init');\n if (this.isMatch(matchPath)) {\n return { fn: true, id: id.name };\n }\n }\n\n const input = t.isStringLiteral(id)\n ? JSON.stringify(id.value)\n : id.name;\n return { fn: false, id: input };\n });\n },\n\n _prepareTransform(path) {\n const transformNode = path.node.tag.arguments[1];\n\n if (!transformNode) return null;\n if (t.isIdentifier(transformNode)) return transformNode.name;\n\n const matchName = this.getMatchName(path);\n const id = path.scope.generateUidIdentifier(`${matchName}_transform`);\n const declarator = t.variableDeclarator(id, transformNode);\n\n path\n .getStatementParent()\n .insertBefore(t.variableDeclaration('var', [declarator]));\n\n return id.name;\n },\n\n minifyMatch(path) {\n const quasis = path.node.quasi.quasis.map((x) =>\n t.stringLiteral(x.value.cooked.replace(/\\s*/g, ''))\n );\n const expressions = path.node.quasi.expressions;\n const transform = this._prepareTransform(path);\n\n path.replaceWith(\n t.callExpression(path.node.tag, [\n t.arrayExpression(quasis),\n ...expressions,\n ])\n );\n },\n\n transformMatch(path) {\n let name = path.node.tag.arguments[0];\n if (!name) {\n name = t.nullLiteral();\n }\n\n const quasis = path.node.quasi.quasis.map((x) => x.value.cooked);\n\n const expressions = this._prepareExpressions(path);\n const transform = this._prepareTransform(path);\n\n let ast;\n try {\n ast = parse(quasis, expressions);\n } catch (error) {\n if (error.name !== 'SyntaxError') throw error;\n throw path.get('quasi').buildCodeFrameError(error.message);\n }\n\n const code = astRoot(ast, '%%name%%', transform && '%%transform%%');\n\n path.replaceWith(\n template.expression(code)(transform ? { name, transform } : { name })\n );\n },\n };\n}\n"],"names":["const","_state","_node","_match","js","i","arguments","length","copy","next","assignIndex","restoreIndex","depth","astExpression","capture","opts","index","group","abort","ast","child","quantifier","invert","astSequence","childOpts","block","sequence","body","syntaxError","char","parse","currentSequence","expression","quasi","quasis","_ref3","let","makeHelpers","node","name","stringLiteral","value","identifier","_patternId","path","source","importSpecifier","t","parentPath","nameArgumentPath","matchName","hoistedExpressions","getBinding","generateUidIdentifier","variableDeclarators","insertBefore","get","id","transformNode","getMatchName","error","buildCodeFrameError","code","astRoot","exports"],"mappings":";AAAAA,IAAMC,SAAS,OAAfD,EACME,QAAQ,MADdF,EAEMG,SAAS,GAFfH;AAIAI,QAASA,GAAE;AAET,+CAASC,IAAI,CAAb,EAAgBA,CAAhB,GAAoBC,SAAUC,CAAAA,MAA9B,EAAsCF,CAAA,EAAtC;wBAC0CA;AAD1C;;;AAOF,WAAaG;AACX,UAAW,EAAX,GAAA;;AAGIC,KAAA,EAAA,CAAA,OAAA;;;CAJN,EAUIC;AACF,iBAAA,KAAA,OAAA,UAAA,UAAA,KAAA,OAAA,UAAA,QAAA;CAXF,EAcMC;AACJX,eAAAA,UAAAA,IAAmCY,CAAnCZ,OAAAA,UAAAA,UAAAA,KAAAA,MAAAA;CAfF,EAkBIa;AACIC,GAAAA,KAAYC,SAAZD,IAA4B,UAA5BA;;AAGN,SAAO,OAAP,UAAA,QAAA,kBAAA,MAAA,SAAA,QAAA,mBAAA,YAAA,gEAAA,QAAA,yBAAA,KAAA,WAAA,MAAA;CAtBF;MA0BMA,eAAAA;;;SAIA;CA9BN,yBAsCgCF;;CAtChC;AA2CE,MAAII,WAAJ,aAAA,gBAAA,EAGIC,YAAAA,GAAmBL,CAHvB;;sCAQOM,CAAAA;sDAOuB,kBACHC,4EAKzB,IAAA,GAFKH,CAAAA,KAEL,IAAA,GADKE,CAAAA,KACL,GADa,QACb,KAAA,MAAA,GAAAE,CAAA,IAAA,eAAA,kBAAA,qBAAA,MAAA,UACK,MAAQC,CAAAA,UAAR,gBAAA,4EAAA,OAIA,iBAAA,IACLrB,OAEA,IAAA,EADAe,OACA,WAAA,IADyBE,CACzB,KAAA,GAAAG,CAAA,IAAA,QAAA,kBAAA;OAAA,MAAA,GAHK,KAAA,YAKYD;6BAIJG,yEACR,MAAQR,CAAAA,OAAR,iBAAA,KAAA,mBAAA,KAAA;CA/ET,EAsFIS;4CACmD;AAInD,wBAAA,EACIC,IAAYT,CADhB;iBAGA,qDAGgCU,QAHhC;aAMIC,mBAEgBP,UAAYd,CAAA;;;AAO9BsB,KAAA,GAHGR,aAAL,GAGEQ,CAHF,KAAA,QAAA,kBAAA,KAAA,aAAA,KAAA,OAAA,UAAA,IAGE;;;CA7GN;4IA2HIpB,2BAEAO;CA7HJ,EAiIMc,uBAAeC;AACnB7B,wBAAwB,+BAAxBA;CAlIF,UAqIY8B;wCAKNC,OACAjB;qBASA,cAAA,SAJqB,CACrBkB,WAAYA,CADS,WAAA,EAIrB,EAAAlB,CAAA;QAGEmB,IAAQC,CAAA,EAAA,KAAA;;;sFAKRH,kGAQK,MAAA,+EAAA,+CASKF,qDAKDA,uBACTf,CAEA,GAFUmB,CAAA,IAAA,CAEV,KAAA,MAAA,OAAA,MAAA,OAAA,MAAA,aAEE,IAAA,EAAAnB,CAAA,SAFF;yDCjMAc,WAAA,EAAA;;;AAON5B,UAAAA;CDKF;ACFAmC,iBAAkB;AAChBC,uBAAAA;AADgB;AAIlBC;aAYkBC,EAAM;AACpB,iCAAA,WAAA,eAA8CC,CAAAA,IAA9C;AADoB;;YAMXC,CAAAA,eAAgBC,CAAAA;;AAjB3B,iBAAA,gBAAA,8BAAA,QAAA;GAKEC,CAAAA;MAEEC,KAAeD,CAAAA;;+DA2BXE,MAAUC,CAAAA,oIAKcC,CAAAA,gBAAgBH,8DAIrCL,CAAAA;;;;AC/CLS,oCAAA,OAAA,CAAA;;yJAOkBC,CAAAA;;;AAMpB,YAAuBJ,KAAA,kBAAA,CAAvB;QAEIK;AACF,0BAAA,iBAAA;;AAIA,YAAA,0BAAA;;AAAA;;eAGSR;;;;;;;AAaX,cAAA,EACIS,wBADJ,EAEIC,IAAqBP,CAAKN,CAAAA,IAAKL,CAAAA,qBAAV;oCACvB,0BAAA,IAAA,IAC0BN,CAAAA,IAD1B,6BAAA,kCAAA,+BAAA,KAAA,uBAAA,uCAAA,2CAAA,MAAA,IAG0BA,CAAAA,IAAKA,CAAAA,gBAH/B,CAAA;;AASE,uBAAyByB,CAAAA,WAAWpB,OAApC;;;;;;oBAOuCA;;;;;iBAMrBqB,CAAAA;AACpBC,YAAA,qEAAA,CAAA;;;KAxBuB,CAFzB;YAmCA,0BAC8BC,CAAAA,6CAD9B;;AAGqB,wCAAA;oEAGYC,CAAAA,wCAKrBC;;;aAWInB,CAAAA;;;;;aAOPoB;;YAGO,IAAKC,CAAAA,YAAL,EAAA;sCAC0BT;gCACAQ;;;;QAKtC1D;;gDAK8C+C,iBAAA,EAAA;6BAEhBH;;cAIzBG,aAAA;;;cAYDjB,KAAA,EAAA,GAAA;aACC8B;;;;0BAKiBC,CAAAA;;AAGtBC,KAAAA,GAAOC,OAAA,EAAA,YAAA,GAAA,mBAAA,CAAPD;uCAEFvB,uBAEE,CACFA,MADE;;;AAOVyB,OAAQhE,CAAAA,WAAR,GAAwBqC,WAAxB;;"} package/dist/reghex-babel.js 000644 000000 000000 00000001011 14112463223 014434 0 ustar 00 000000 000000 'use strict';
var transform = require("./d56fb69a.js");
function reghexPlugin(d, c) {
void 0 === c && (c = {});
var a;
return {name:"reghex", visitor:{Program:function() {
a = transform.makeHelpers(d);
}, ImportDeclaration:function(b) {
!1 !== c.codegen && a.updateImport(b);
}, TaggedTemplateExpression:function(b) {
a.isMatch(b) && a.getMatchImport(b) && (!1 === c.codegen ? a.minifyMatch(b) : a.transformMatch(b));
}}};
}
module.exports = reghexPlugin
//# sourceMappingURL=reghex-babel.js.map
package/dist/reghex-babel.js.map 000644 000000 000000 00000001724 14112463223 015223 0 ustar 00 000000 000000 {"version":3,"file":"reghex-babel.js","sources":["../src/babel/plugin.js"],"sourcesContent":["import { makeHelpers } from './transform';\n\nexport default function reghexPlugin(babel, opts = {}) {\n let helpers;\n\n return {\n name: 'reghex',\n visitor: {\n Program() {\n helpers = makeHelpers(babel);\n },\n ImportDeclaration(path) {\n if (opts.codegen === false) return;\n helpers.updateImport(path);\n },\n TaggedTemplateExpression(path) {\n if (helpers.isMatch(path) && helpers.getMatchImport(path)) {\n if (opts.codegen === false) {\n helpers.minifyMatch(path);\n } else {\n helpers.transformMatch(path);\n }\n }\n },\n },\n };\n}\n"],"names":["reghexPlugin","babel","opts","let","TaggedTemplateExpression","codegen"],"mappings":";;AAEeA,QAASA,aAAY,CAACC,CAAD,EAAQC,CAAR,CAAmB;;;AACrDC;;;;8BAa8BC,QAAiC,EAAA;4CAElC,CAAA,OAAZC,CAAAA;KAfjBF;AADqD;;;"} package/dist/reghex-core.js 000644 000000 000000 00000013671 14112463221 014334 0 ustar 00 000000 000000 'use strict';
var _state = "state", _node = "node", _match = "x";
function js() {
for (var b = arguments, a = arguments[0][0], c = 1; c < arguments.length; c++) {
a = a + b[c] + b[0][c];
}
return a.trim();
}
var copy = function(b) {
var a = {}, c;
for (c in b) {
a[c] = b[c];
}
return a;
}, assignIndex = function(b) {
return " var y" + (b + ("=" + (_state + (".y,x" + (b + ("=" + (_state + ".x;")))))));
}, restoreIndex = function(b) {
return _state + (".y=y" + (b + (";" + (_state + (".x=x" + (b + ";"))))));
}, astExpression = function(b, a, c) {
a = !!c.capture && !b.capture;
var d = c.length && c.abort && _node + (".length=ln" + (c.length + ";")) || "";
return " if((" + (_match + " = " + b.expression.id + "(" + _state + ")) " + (a ? "!=" : "==") + " null)" + ((a ? "{" + (_node + (".push(" + (_match + ");}else "))) : "") + ("{" + (restoreIndex(c.index) + (d + (c.abort + "}"))))));
}, astGroup = function(b, a, c) {
var d = !!c.capture && !b.capture;
c = copy(c);
c.capture = d;
return !c.length && d ? (c.length = a, "var ln" + (a + ("=" + (_node + ".length;"))) + (astSequence(b.sequence, a + 1, c) + "")) : astSequence(b.sequence, a + 1, c);
}, astChild = function(b, a, c) {
return b.expression ? astExpression(b, a, c) : astGroup(b, a, c);
}, astQuantifier = function(b, a, c) {
var d = c.index, f = c.abort, l = "inv_" + a, e = "group_" + a;
c = copy(c);
"!" === b.capture && (c.index = a, c.abort = "break " + (l + ""));
"+" === b.quantifier ? (e = copy(b), e.quantifier = "*", c = astChild(b, a, c) + (astQuantifier(e, a, c) + "")) : "*" === b.quantifier ? (c.length = 0, c.index = a, c.abort = "break " + (e + ";"), c = e + (":for(;;){" + (assignIndex(a) + (astChild(b, a, c) + "}")))) : "?" === b.quantifier && b.expression ? (c.index = a, c.abort = "", c = assignIndex(a) + (astChild(b, a, c) + "")) : "?" === b.quantifier ? (c.index = a, c.abort = "break " + (e + ""), c = e + (":{" + (assignIndex(a) + (astChild(b,
a, c) + "}")))) : c = astChild(b, a, c);
return "!" === b.capture ? l + (":{" + (assignIndex(a) + (c + (restoreIndex(d) + (f + "}"))))) : "=" === b.capture ? assignIndex(a) + (c + (restoreIndex(a) + "")) : c;
}, astSequence = function(b, a, c) {
for (var d = b.alternation ? "alt_" + a : "", f = ""; b; b = b.alternation) {
var l = "block_" + a, e = c;
b.alternation && (e = copy(c), e.index = a, e.abort = "break " + (l + ";"));
for (var h = "", k = 0; k < b.length; k++) {
h += astQuantifier(b[k], a, e);
}
f = b.alternation ? f + (l + (":{" + (assignIndex(a) + (h + (" break " + (d + ";}")))))) : f + h;
}
return d ? d + (":{" + (f + "}")) : f;
}, astRoot = function(b, a, c) {
return "(function(" + (_state + ("){" + (assignIndex(1) + (" var " + (_node + ("=[];var " + (_match + (";" + (astSequence(b, 2, {index:1, length:0, abort:"return;", capture:!0}) + (" if(" + (a + (")" + (_node + (".tag=" + (a + (";return " + ((c ? "(" + (c + (")(" + (_node + ")"))) : _node) + ";})")))))))))))))))));
}, syntaxError = function(b) {
throw new SyntaxError('Unexpected token "' + b + '"');
}, parse$1 = function(b, a) {
for (var c = [], d = [], f = null, l, e = d, h, k, m = 0; m < b.length + a.length; m++) {
0 !== m % 2 && (k = a[m++ >> 1], e.push({expression:k, capture:h}), h = void 0);
var n = b[m >> 1];
for (k = 0; k < n.length;) {
var g = n[k++];
" " !== g && "\t" !== g && "\r" !== g && "\n" !== g && ("|" === g && e.length ? e = e.alternation = [] : ")" === g && e.length ? (f = null, (e = c.pop()) || syntaxError(g)) : "(" === g ? (c.push(e), e.push(f = {sequence:[], capture:h}), e = f.sequence, h = void 0) : ":" === g || "=" === g || "!" === g ? (h = g, n[k] && "(" !== n[k] && syntaxError(g)) : "?" === g && !e.length && f ? (h = n[k++], ":" === h || "=" === h || "!" === h ? (f.capture = h, h = void 0) : syntaxError(g)) : "?" !== g &&
"+" !== g && "*" !== g || !(l = e[e.length - 1]) ? syntaxError(g) : l.quantifier = g);
}
}
return d;
}, isStickySupported = "boolean" === typeof/./g.sticky, execLambda = function(b) {
return b.length ? b : function(a) {
return b()(a);
};
}, execString = function(b) {
return function(a) {
if (a.x < a.quasis.length) {
for (var c = a.quasis[a.x], d = 0, f = b.length; d < f; d++) {
if (c.charCodeAt(a.y + d) !== b.charCodeAt(d)) {
return null;
}
}
a.y += b.length;
return b;
}
};
}, execRegex = function(b) {
b = isStickySupported ? new RegExp(b.source, "y") : new RegExp(b.source + "|()", "g");
return function(a) {
if (a.x < a.quasis.length) {
var c = a.quasis[a.x];
b.lastIndex = a.y;
var d;
isStickySupported ? b.test(c) && (d = c.slice(a.y, b.lastIndex)) : (c = b.exec(c), null == c[1] && (d = c[0]));
a.y = b.lastIndex;
return d;
}
};
}, __pattern = function(b) {
return "function" === typeof b ? execLambda(b) : "string" === typeof b ? execString(b) : execRegex(b);
}, interpolation = function(b) {
return function(a) {
if (a.x < a.expressions.length && a.y >= a.quasis[a.x].length) {
a.y = 0;
var c = a.expressions[a.x++];
b && c && (c = b(c));
}
return c;
};
}, parse = function(b) {
return function(a) {
for (var c = [], d = arguments.length - 1; 0 < d--;) {
c[d] = arguments[d + 1];
}
"string" === typeof a && (a = [a]);
return b({quasis:a, expressions:c, x:0, y:0});
};
};
function _ref(b, a) {
return {id:"_" + a};
}
function _ref2(b, a) {
return "_" + a;
}
var match = function(b, a) {
return function(c) {
for (var d = [], f = arguments.length - 1; 0 < f--;) {
d[f] = arguments[f + 1];
}
f = parse$1(c, d.map(_ref));
return (new Function("_n,_t," + d.map(_ref2).join(","), "return " + astRoot(f, "_n", a ? "_t" : null))).apply(void 0, [b, a].concat(d.map(__pattern)));
};
};
exports.__pattern = __pattern;
exports.interpolation = interpolation;
exports.match = match;
exports.parse = parse
//# sourceMappingURL=reghex-core.js.map
package/dist/reghex-core.js.map 000644 000000 000000 00000027347 14112463221 015115 0 ustar 00 000000 000000 {"version":3,"file":"reghex-core.js","sources":["../src/codegen.js","../src/parser.js","../src/core.js"],"sourcesContent":["const _state = 'state';\nconst _node = 'node';\nconst _match = 'x';\n\nfunction js(/* arguments */) {\n let body = arguments[0][0];\n for (let i = 1; i < arguments.length; i++)\n body = body + arguments[i] + arguments[0][i];\n return body.trim();\n}\n\nconst copy = (prev) => {\n const next = {};\n for (const key in prev) next[key] = prev[key];\n return next;\n};\n\nconst assignIndex = (depth) => js`\n var y${depth} = ${_state}.y,\n x${depth} = ${_state}.x;\n`;\n\nconst restoreIndex = (depth) => js`\n ${_state}.y = y${depth};\n ${_state}.x = x${depth};\n`;\n\nconst astExpression = (ast, depth, opts) => {\n const capture = !!opts.capture && !ast.capture;\n const restoreLength =\n (opts.length && opts.abort && js`${_node}.length = ln${opts.length};`) ||\n '';\n const condition = `(${_match} = ${ast.expression.id}(${_state})) ${\n capture ? '!=' : '=='\n } null`;\n return js`\n if (${condition}) ${\n capture\n ? js`{\n ${_node}.push(${_match});\n } else `\n : ''\n }{\n ${restoreIndex(opts.index)}\n ${restoreLength}\n ${opts.abort}\n }\n `;\n};\n\nconst astGroup = (ast, depth, opts) => {\n const capture = !!opts.capture && !ast.capture;\n\n opts = copy(opts);\n opts.capture = capture;\n\n if (!opts.length && capture) {\n opts.length = depth;\n return js`\n ${js`var ln${depth} = ${_node}.length;`}\n ${astSequence(ast.sequence, depth + 1, opts)}\n `;\n }\n\n return astSequence(ast.sequence, depth + 1, opts);\n};\n\nconst astChild = (ast, depth, opts) =>\n ast.expression ? astExpression(ast, depth, opts) : astGroup(ast, depth, opts);\n\nconst astQuantifier = (ast, depth, opts) => {\n const { index, abort } = opts;\n const invert = `inv_${depth}`;\n const group = `group_${depth}`;\n\n opts = copy(opts);\n if (ast.capture === '!') {\n opts.index = depth;\n opts.abort = js`break ${invert}`;\n }\n\n let child;\n if (ast.quantifier === '+') {\n const starAst = copy(ast);\n starAst.quantifier = '*';\n child = js`\n ${astChild(ast, depth, opts)}\n ${astQuantifier(starAst, depth, opts)}\n `;\n } else if (ast.quantifier === '*') {\n opts.length = 0;\n opts.index = depth;\n opts.abort = js`break ${group};`;\n\n child = js`\n ${group}: for (;;) {\n ${assignIndex(depth)}\n ${astChild(ast, depth, opts)}\n }\n `;\n } else if (ast.quantifier === '?' && ast.expression) {\n opts.index = depth;\n opts.abort = '';\n\n child = js`\n ${assignIndex(depth)}\n ${astChild(ast, depth, opts)}\n `;\n } else if (ast.quantifier === '?') {\n opts.index = depth;\n opts.abort = js`break ${group}`;\n\n child = js`\n ${group}: {\n ${assignIndex(depth)}\n ${astChild(ast, depth, opts)}\n }\n `;\n } else {\n child = astChild(ast, depth, opts);\n }\n\n if (ast.capture === '!') {\n return js`\n ${invert}: {\n ${assignIndex(depth)}\n ${child}\n ${restoreIndex(index)}\n ${abort}\n }\n `;\n } else if (ast.capture === '=') {\n return js`\n ${assignIndex(depth)}\n ${child}\n ${restoreIndex(depth)}\n `;\n } else {\n return child;\n }\n};\n\nconst astSequence = (ast, depth, opts) => {\n const alternation = ast.alternation ? `alt_${depth}` : '';\n\n let body = '';\n for (; ast; ast = ast.alternation) {\n const block = `block_${depth}`;\n\n let childOpts = opts;\n if (ast.alternation) {\n childOpts = copy(opts);\n childOpts.index = depth;\n childOpts.abort = js`break ${block};`;\n }\n\n let sequence = '';\n for (let i = 0; i < ast.length; i++)\n sequence += astQuantifier(ast[i], depth, childOpts);\n\n if (!ast.alternation) {\n body += sequence;\n } else {\n body += js`\n ${block}: {\n ${assignIndex(depth)}\n ${sequence}\n break ${alternation};\n }\n `;\n }\n }\n\n if (!alternation) return body;\n\n return js`\n ${alternation}: {\n ${body}\n }\n `;\n};\n\nconst astRoot = (ast, name, transform) => {\n return js`\n (function (${_state}) {\n ${assignIndex(1)}\n var ${_node} = [];\n var ${_match};\n\n ${astSequence(ast, 2, {\n index: 1,\n length: 0,\n abort: js`return;`,\n capture: true,\n })}\n\n if (${name}) ${_node}.tag = ${name};\n return ${transform ? js`(${transform})(${_node})` : _node};\n })\n `;\n};\n\nexport { astRoot };\n","const syntaxError = (char) => {\n throw new SyntaxError('Unexpected token \"' + char + '\"');\n};\n\nexport const parse = (quasis, expressions) => {\n let quasiIndex = 0;\n let stackIndex = 0;\n\n const sequenceStack = [];\n const rootSequence = [];\n\n let currentGroup = null;\n let lastMatch;\n let currentSequence = rootSequence;\n let capture;\n\n for (\n let quasiIndex = 0, stackIndex = 0;\n stackIndex < quasis.length + expressions.length;\n stackIndex++\n ) {\n if (stackIndex % 2 !== 0) {\n const expression = expressions[stackIndex++ >> 1];\n currentSequence.push({ expression, capture });\n capture = undefined;\n }\n\n const quasi = quasis[stackIndex >> 1];\n for (quasiIndex = 0; quasiIndex < quasi.length; ) {\n const char = quasi[quasiIndex++];\n if (char === ' ' || char === '\\t' || char === '\\r' || char === '\\n') {\n } else if (char === '|' && currentSequence.length) {\n currentSequence = currentSequence.alternation = [];\n } else if (char === ')' && currentSequence.length) {\n currentGroup = null;\n currentSequence = sequenceStack.pop();\n if (!currentSequence) syntaxError(char);\n } else if (char === '(') {\n sequenceStack.push(currentSequence);\n currentSequence.push((currentGroup = { sequence: [], capture }));\n currentSequence = currentGroup.sequence;\n capture = undefined;\n } else if (char === ':' || char === '=' || char === '!') {\n capture = char;\n const nextChar = quasi[quasiIndex];\n if (quasi[quasiIndex] && quasi[quasiIndex] !== '(') syntaxError(char);\n } else if (char === '?' && !currentSequence.length && currentGroup) {\n capture = quasi[quasiIndex++];\n if (capture === ':' || capture === '=' || capture === '!') {\n currentGroup.capture = capture;\n capture = undefined;\n } else {\n syntaxError(char);\n }\n } else if (\n (char === '?' || char === '+' || char === '*') &&\n (lastMatch = currentSequence[currentSequence.length - 1])\n ) {\n lastMatch.quantifier = char;\n } else {\n syntaxError(char);\n }\n }\n }\n\n return rootSequence;\n};\n","import { astRoot } from './codegen';\nimport { parse as parseDSL } from './parser';\n\nconst isStickySupported = typeof /./g.sticky === 'boolean';\n\nconst execLambda = (pattern) => {\n if (pattern.length) return pattern;\n return (state) => pattern()(state);\n};\n\nconst execString = (pattern) => {\n return (state) => {\n if (state.x < state.quasis.length) {\n const input = state.quasis[state.x];\n for (let i = 0, l = pattern.length; i < l; i++)\n if (input.charCodeAt(state.y + i) !== pattern.charCodeAt(i))\n return null;\n state.y += pattern.length;\n return pattern;\n }\n };\n};\n\nconst execRegex = (pattern) => {\n pattern = isStickySupported\n ? new RegExp(pattern.source, 'y')\n : new RegExp(pattern.source + '|()', 'g');\n return (state) => {\n if (state.x < state.quasis.length) {\n const input = state.quasis[state.x];\n pattern.lastIndex = state.y;\n let match;\n if (isStickySupported) {\n if (pattern.test(input))\n match = input.slice(state.y, pattern.lastIndex);\n } else {\n const x = pattern.exec(input);\n if (x[1] == null) match = x[0];\n }\n\n state.y = pattern.lastIndex;\n return match;\n }\n };\n};\n\nexport const __pattern = (input) => {\n if (typeof input === 'function') {\n return execLambda(input);\n } else if (typeof input === 'string') {\n return execString(input);\n } else {\n return execRegex(input);\n }\n};\n\nexport const interpolation = (predicate) => (state) => {\n let match;\n\n if (\n state.x < state.expressions.length &&\n state.y >= state.quasis[state.x].length\n ) {\n state.y = 0;\n match = state.expressions[state.x++];\n if (predicate && match) match = predicate(match);\n }\n\n return match;\n};\n\nexport const parse = (matcher) => (quasis, ...expressions) => {\n if (typeof quasis === 'string') quasis = [quasis];\n const state = { quasis, expressions, x: 0, y: 0 };\n return matcher(state);\n};\n\nexport const match = (name, transform) => (quasis, ...expressions) => {\n const ast = parseDSL(\n quasis,\n expressions.map((_, i) => ({ id: `_${i}` }))\n );\n return new Function(\n '_n,_t,' + expressions.map((_expression, i) => `_${i}`).join(','),\n 'return ' + astRoot(ast, '_n', transform ? '_t' : null)\n )(name, transform, ...expressions.map(__pattern));\n};\n"],"names":["const","_state","_node","_match","js","i","arguments","length","copy","next","assignIndex","restoreIndex","depth","astExpression","capture","opts","index","group","abort","ast","child","quantifier","invert","astSequence","childOpts","block","sequence","body","syntaxError","char","parse$1","quasis","currentSequence","expression","quasi","let","pattern","state","stackIndex","y","quasiIndex","isStickySupported","match","input","x","__pattern","predicate","expressions","len","_ref","_ref2","transform","exports"],"mappings":";AAAAA,IAAMC,SAAS,OAAfD,EACME,QAAQ,MADdF,EAEMG,SAAS,GAFfH;AAIAI,QAASA,GAAE;AAET,+CAASC,IAAI,CAAb,EAAgBA,CAAhB,GAAoBC,SAAUC,CAAAA,MAA9B,EAAsCF,CAAA,EAAtC;wBAC0CA;AAD1C;;;AAOF,WAAaG;AACX,UAAW,EAAX,GAAA;;AAGIC,KAAA,EAAA,CAAA,OAAA;;;CAJN,EAUIC;AACF,iBAAA,KAAA,OAAA,UAAA,UAAA,KAAA,OAAA,UAAA,QAAA;CAXF,EAcMC;AACJX,eAAAA,UAAAA,IAAmCY,CAAnCZ,OAAAA,UAAAA,UAAAA,KAAAA,MAAAA;CAfF,EAkBIa;AACIC,GAAAA,KAAYC,SAAZD,IAA4B,UAA5BA;;AAGN,SAAO,OAAP,UAAA,QAAA,kBAAA,MAAA,SAAA,QAAA,mBAAA,YAAA,gEAAA,QAAA,yBAAA,KAAA,WAAA,MAAA;CAtBF;MA0BMA,eAAAA;;;SAIA;CA9BN,yBAsCgCF;;CAtChC;AA2CE,MAAII,WAAJ,aAAA,gBAAA,EAGIC,YAAAA,GAAmBL,CAHvB;;sCAQOM,CAAAA;sDAOuB,kBACHC,4EAKzB,IAAA,GAFKH,CAAAA,KAEL,IAAA,GADKE,CAAAA,KACL,GADa,QACb,KAAA,MAAA,GAAAE,CAAA,IAAA,eAAA,kBAAA,qBAAA,MAAA,UACK,MAAQC,CAAAA,UAAR,gBAAA,4EAAA,OAIA,iBAAA,IACLrB,OAEA,IAAA,EADAe,OACA,WAAA,IADyBE,CACzB,KAAA,GAAAG,CAAA,IAAA,QAAA,kBAAA;OAAA,MAAA,GAHK,KAAA,YAKYD;6BAIJG,yEACR,MAAQR,CAAAA,OAAR,iBAAA,KAAA,mBAAA,KAAA;CA/ET,EAsFIS;4CACmD;AAInD,wBAAA,EACIC,IAAYT,CADhB;iBAGA,qDAGgCU,QAHhC;aAMIC,mBAEgBP,UAAYd,CAAA;;;AAO9BsB,KAAA,GAHGR,aAAL,GAGEQ,CAHF,KAAA,QAAA,kBAAA,KAAA,aAAA,KAAA,OAAA,UAAA,IAGE;;;CA7GN;4IA2HIpB,2BAEAO;CA7HJ,EAiIMc,uBAAeC;AACnB7B,wBAAwB,+BAAxBA;CAlIF,YAqIc8B,QAAS,CAACC,CAAD,GAAA;wCAKjBC,OACAlB;qBASA,cAAA,SAJqB,CACrBmB,WAAYA,CADS,WAAA,EAIrB,EAAAnB,CAAA;QAGEoB,IAAQH,CAAA,EAAA,KAAA;;;sFAKRC,kGAQK,MAAA,+EAAA,+CASKH,qDAKDA,uBACTf,CAEA,GAFUoB,CAAA,IAAA,CAEV,KAAA,MAAA,OAAA,MAAA,OAAA,MAAA,aAEE,IAAA,EAAApB,CAAA,SAFF;yDCjMAc,WAAA,EAAA;;;AAON5B,UAAAA;CDKF,qDAAA,0BCAoC;AAClCmC,SAAIC,CAAQ7B,CAAAA,MAAZ4B,IAAAA,YAIeE,EAAO;AACpBC,iBAAAA;AADoB,GAJtBH;AADkC,CDApC,uBCU0B,CAACC,CAAD;oBACA;;uDAIY/B;0BACHkC,CAAAA,IAAIlC;AAC7B,qBAAA;;;;aAMG+B;;AAZa;CDX1B;;iBC8BkB,EAAA;QAEVC,MAAUG,QAAWjC,CAAAA;;;;AAKnBkC,wCAEAC,mCAKF,UAFqBC,EAErB,MAAA,QAAA,MAAA,GACUC,CAAA,EAAA,CADV;;;;;CD5CR,EEVMC;gEAGO,MAAOF;CFOpB,2BEAgCG;kBACbT,EAAO;eAGFU,CAAAA;AAClBV,SAAA,IAAA;;;;;AAJoB;CFD1B,qBEiB+B;;qBAGvBW,aAAgBzC,CAAAA,MAAhByC;;;iBAIA,MAAOjB;;;AAPgB,CFjB/B;AEsCAkB,QAASA;;;AAMTC;;;AAIA,IAAIR,gBAAiB,EAAA,EAAOS,CAAP;;;;;QAOPrB,OAAA,EAAA,aAAA;;;CAPd;;AAU+DsB,qBAAA,gBAAA;AAC/DA,aAAA,QAAA;AACAA,aAAA,QAAA;;"} package/dist/reghex-core.mjs 000644 000000 000000 00000011553 14112463221 014506 0 ustar 00 000000 000000
function q(a) {
var b = {}, c;
for (c in a) {
b[c] = a[c];
}
return b;
}
function r(a) {
return " var y" + (a + ("=state.y,x" + (a + "=state.x;")));
}
function v(a) {
return "state.y=y" + (a + (";state.x=x" + (a + ";")));
}
function w(a, b, c) {
if (a.expression) {
b = !!c.capture && !a.capture;
var d = c.length && c.abort && "node.length=ln" + (c.length + ";") || "";
a = " if((x = " + (a.expression.id + "(state)) " + (b ? "!=" : "==") + " null)" + ((b ? "{node.push(x);}else " : "") + ("{" + (v(c.index) + (d + (c.abort + "}"))))));
} else {
d = !!c.capture && !a.capture, c = q(c), c.capture = d, !c.length && d ? (c.length = b, a = "var ln" + (b + "=node.length;") + (x(a.sequence, b + 1, c) + "")) : a = x(a.sequence, b + 1, c);
}
return a;
}
function y(a, b, c) {
var d = c.index, f = c.abort, l = "inv_" + b, g = "group_" + b;
c = q(c);
"!" === a.capture && (c.index = b, c.abort = "break " + (l + ""));
"+" === a.quantifier ? (g = q(a), g.quantifier = "*", c = w(a, b, c) + (y(g, b, c) + "")) : "*" === a.quantifier ? (c.length = 0, c.index = b, c.abort = "break " + (g + ";"), c = g + (":for(;;){" + (r(b) + (w(a, b, c) + "}")))) : "?" === a.quantifier && a.expression ? (c.index = b, c.abort = "", c = r(b) + (w(a, b, c) + "")) : "?" === a.quantifier ? (c.index = b, c.abort = "break " + (g + ""), c = g + (":{" + (r(b) + (w(a, b, c) + "}")))) : c = w(a, b, c);
return "!" === a.capture ? l + (":{" + (r(b) + (c + (v(d) + (f + "}"))))) : "=" === a.capture ? r(b) + (c + (v(b) + "")) : c;
}
function x(a, b, c) {
for (var d = a.alternation ? "alt_" + b : "", f = ""; a; a = a.alternation) {
var l = "block_" + b, g = c;
a.alternation && (g = q(c), g.index = b, g.abort = "break " + (l + ";"));
for (var m = "", n = 0; n < a.length; n++) {
m += y(a[n], b, g);
}
f = a.alternation ? f + (l + (":{" + (r(b) + (m + (" break " + (d + ";}")))))) : f + m;
}
return d ? d + (":{" + (f + "}")) : f;
}
function z(a) {
throw new SyntaxError('Unexpected token "' + a + '"');
}
var A = "boolean" === typeof/./g.sticky;
function B(a) {
return a.length ? a : function(b) {
return a()(b);
};
}
function C(a) {
return function(b) {
if (b.x < b.quasis.length) {
for (var c = b.quasis[b.x], d = 0, f = a.length; d < f; d++) {
if (c.charCodeAt(b.y + d) !== a.charCodeAt(d)) {
return null;
}
}
b.y += a.length;
return a;
}
};
}
function D(a) {
a = A ? new RegExp(a.source, "y") : new RegExp(a.source + "|()", "g");
return function(b) {
if (b.x < b.quasis.length) {
var c = b.quasis[b.x];
a.lastIndex = b.y;
var d;
A ? a.test(c) && (d = c.slice(b.y, a.lastIndex)) : (c = a.exec(c), null == c[1] && (d = c[0]));
b.y = a.lastIndex;
return d;
}
};
}
function E(a) {
return "function" === typeof a ? B(a) : "string" === typeof a ? C(a) : D(a);
}
function F(a, b) {
return {id:"_" + b};
}
function G(a, b) {
return "_" + b;
}
function interpolation(a) {
return function(b) {
if (b.x < b.expressions.length && b.y >= b.quasis[b.x].length) {
b.y = 0;
var c = b.expressions[b.x++];
a && c && (c = a(c));
}
return c;
};
};
function match(a, b) {
return function(c) {
for (var d = [], f = arguments.length - 1; 0 < f--;) {
d[f] = arguments[f + 1];
}
var l = d.map(F), g = [];
f = [];
for (var m = null, n, k = f, h, p, t = 0; t < c.length + l.length; t++) {
0 !== t % 2 && (p = l[t++ >> 1], k.push({expression:p, capture:h}), h = void 0);
var u = c[t >> 1];
for (p = 0; p < u.length;) {
var e = u[p++];
" " !== e && "\t" !== e && "\r" !== e && "\n" !== e && ("|" === e && k.length ? k = k.alternation = [] : ")" === e && k.length ? (m = null, (k = g.pop()) || z(e)) : "(" === e ? (g.push(k), k.push(m = {sequence:[], capture:h}), k = m.sequence, h = void 0) : ":" === e || "=" === e || "!" === e ? (h = e, u[p] && "(" !== u[p] && z(e)) : "?" === e && !k.length && m ? (h = u[p++], ":" === h || "=" === h || "!" === h ? (m.capture = h, h = void 0) : z(e)) : "?" !== e && "+" !== e && "*" !== e ||
!(n = k[k.length - 1]) ? z(e) : n.quantifier = e);
}
}
n = [a, b].concat(d.map(E));
h = Function;
d = "_n,_t," + d.map(G).join(",");
l = b ? "_t" : null;
f = "(function(state){" + (r(1) + (" var node=[];var x;" + (x(f, 2, {index:1, length:0, abort:"return;", capture:!0}) + (" if(_n)node.tag=_n;return " + ((l ? "(" + (l + ")(node)") : "node") + ";})")))));
return (new h(d, "return " + f)).apply(void 0, n);
};
};
function parse(a) {
return function(b) {
for (var c = [], d = arguments.length - 1; 0 < d--;) {
c[d] = arguments[d + 1];
}
"string" === typeof b && (b = [b]);
return a({quasis:b, expressions:c, x:0, y:0});
};
};export{E as __pattern,interpolation,match,parse}
//# sourceMappingURL=reghex-core.mjs.map
package/dist/reghex-core.mjs.map 000644 000000 000000 00000026424 14112463221 015265 0 ustar 00 000000 000000 {"version":3,"file":"reghex-core.mjs","sources":["../src/codegen.js","../src/parser.js","../src/core.js"],"sourcesContent":["const _state = 'state';\nconst _node = 'node';\nconst _match = 'x';\n\nfunction js(/* arguments */) {\n let body = arguments[0][0];\n for (let i = 1; i < arguments.length; i++)\n body = body + arguments[i] + arguments[0][i];\n return body.trim();\n}\n\nconst copy = (prev) => {\n const next = {};\n for (const key in prev) next[key] = prev[key];\n return next;\n};\n\nconst assignIndex = (depth) => js`\n var y${depth} = ${_state}.y,\n x${depth} = ${_state}.x;\n`;\n\nconst restoreIndex = (depth) => js`\n ${_state}.y = y${depth};\n ${_state}.x = x${depth};\n`;\n\nconst astExpression = (ast, depth, opts) => {\n const capture = !!opts.capture && !ast.capture;\n const restoreLength =\n (opts.length && opts.abort && js`${_node}.length = ln${opts.length};`) ||\n '';\n const condition = `(${_match} = ${ast.expression.id}(${_state})) ${\n capture ? '!=' : '=='\n } null`;\n return js`\n if (${condition}) ${\n capture\n ? js`{\n ${_node}.push(${_match});\n } else `\n : ''\n }{\n ${restoreIndex(opts.index)}\n ${restoreLength}\n ${opts.abort}\n }\n `;\n};\n\nconst astGroup = (ast, depth, opts) => {\n const capture = !!opts.capture && !ast.capture;\n\n opts = copy(opts);\n opts.capture = capture;\n\n if (!opts.length && capture) {\n opts.length = depth;\n return js`\n ${js`var ln${depth} = ${_node}.length;`}\n ${astSequence(ast.sequence, depth + 1, opts)}\n `;\n }\n\n return astSequence(ast.sequence, depth + 1, opts);\n};\n\nconst astChild = (ast, depth, opts) =>\n ast.expression ? astExpression(ast, depth, opts) : astGroup(ast, depth, opts);\n\nconst astQuantifier = (ast, depth, opts) => {\n const { index, abort } = opts;\n const invert = `inv_${depth}`;\n const group = `group_${depth}`;\n\n opts = copy(opts);\n if (ast.capture === '!') {\n opts.index = depth;\n opts.abort = js`break ${invert}`;\n }\n\n let child;\n if (ast.quantifier === '+') {\n const starAst = copy(ast);\n starAst.quantifier = '*';\n child = js`\n ${astChild(ast, depth, opts)}\n ${astQuantifier(starAst, depth, opts)}\n `;\n } else if (ast.quantifier === '*') {\n opts.length = 0;\n opts.index = depth;\n opts.abort = js`break ${group};`;\n\n child = js`\n ${group}: for (;;) {\n ${assignIndex(depth)}\n ${astChild(ast, depth, opts)}\n }\n `;\n } else if (ast.quantifier === '?' && ast.expression) {\n opts.index = depth;\n opts.abort = '';\n\n child = js`\n ${assignIndex(depth)}\n ${astChild(ast, depth, opts)}\n `;\n } else if (ast.quantifier === '?') {\n opts.index = depth;\n opts.abort = js`break ${group}`;\n\n child = js`\n ${group}: {\n ${assignIndex(depth)}\n ${astChild(ast, depth, opts)}\n }\n `;\n } else {\n child = astChild(ast, depth, opts);\n }\n\n if (ast.capture === '!') {\n return js`\n ${invert}: {\n ${assignIndex(depth)}\n ${child}\n ${restoreIndex(index)}\n ${abort}\n }\n `;\n } else if (ast.capture === '=') {\n return js`\n ${assignIndex(depth)}\n ${child}\n ${restoreIndex(depth)}\n `;\n } else {\n return child;\n }\n};\n\nconst astSequence = (ast, depth, opts) => {\n const alternation = ast.alternation ? `alt_${depth}` : '';\n\n let body = '';\n for (; ast; ast = ast.alternation) {\n const block = `block_${depth}`;\n\n let childOpts = opts;\n if (ast.alternation) {\n childOpts = copy(opts);\n childOpts.index = depth;\n childOpts.abort = js`break ${block};`;\n }\n\n let sequence = '';\n for (let i = 0; i < ast.length; i++)\n sequence += astQuantifier(ast[i], depth, childOpts);\n\n if (!ast.alternation) {\n body += sequence;\n } else {\n body += js`\n ${block}: {\n ${assignIndex(depth)}\n ${sequence}\n break ${alternation};\n }\n `;\n }\n }\n\n if (!alternation) return body;\n\n return js`\n ${alternation}: {\n ${body}\n }\n `;\n};\n\nconst astRoot = (ast, name, transform) => {\n return js`\n (function (${_state}) {\n ${assignIndex(1)}\n var ${_node} = [];\n var ${_match};\n\n ${astSequence(ast, 2, {\n index: 1,\n length: 0,\n abort: js`return;`,\n capture: true,\n })}\n\n if (${name}) ${_node}.tag = ${name};\n return ${transform ? js`(${transform})(${_node})` : _node};\n })\n `;\n};\n\nexport { astRoot };\n","const syntaxError = (char) => {\n throw new SyntaxError('Unexpected token \"' + char + '\"');\n};\n\nexport const parse = (quasis, expressions) => {\n let quasiIndex = 0;\n let stackIndex = 0;\n\n const sequenceStack = [];\n const rootSequence = [];\n\n let currentGroup = null;\n let lastMatch;\n let currentSequence = rootSequence;\n let capture;\n\n for (\n let quasiIndex = 0, stackIndex = 0;\n stackIndex < quasis.length + expressions.length;\n stackIndex++\n ) {\n if (stackIndex % 2 !== 0) {\n const expression = expressions[stackIndex++ >> 1];\n currentSequence.push({ expression, capture });\n capture = undefined;\n }\n\n const quasi = quasis[stackIndex >> 1];\n for (quasiIndex = 0; quasiIndex < quasi.length; ) {\n const char = quasi[quasiIndex++];\n if (char === ' ' || char === '\\t' || char === '\\r' || char === '\\n') {\n } else if (char === '|' && currentSequence.length) {\n currentSequence = currentSequence.alternation = [];\n } else if (char === ')' && currentSequence.length) {\n currentGroup = null;\n currentSequence = sequenceStack.pop();\n if (!currentSequence) syntaxError(char);\n } else if (char === '(') {\n sequenceStack.push(currentSequence);\n currentSequence.push((currentGroup = { sequence: [], capture }));\n currentSequence = currentGroup.sequence;\n capture = undefined;\n } else if (char === ':' || char === '=' || char === '!') {\n capture = char;\n const nextChar = quasi[quasiIndex];\n if (quasi[quasiIndex] && quasi[quasiIndex] !== '(') syntaxError(char);\n } else if (char === '?' && !currentSequence.length && currentGroup) {\n capture = quasi[quasiIndex++];\n if (capture === ':' || capture === '=' || capture === '!') {\n currentGroup.capture = capture;\n capture = undefined;\n } else {\n syntaxError(char);\n }\n } else if (\n (char === '?' || char === '+' || char === '*') &&\n (lastMatch = currentSequence[currentSequence.length - 1])\n ) {\n lastMatch.quantifier = char;\n } else {\n syntaxError(char);\n }\n }\n }\n\n return rootSequence;\n};\n","import { astRoot } from './codegen';\nimport { parse as parseDSL } from './parser';\n\nconst isStickySupported = typeof /./g.sticky === 'boolean';\n\nconst execLambda = (pattern) => {\n if (pattern.length) return pattern;\n return (state) => pattern()(state);\n};\n\nconst execString = (pattern) => {\n return (state) => {\n if (state.x < state.quasis.length) {\n const input = state.quasis[state.x];\n for (let i = 0, l = pattern.length; i < l; i++)\n if (input.charCodeAt(state.y + i) !== pattern.charCodeAt(i))\n return null;\n state.y += pattern.length;\n return pattern;\n }\n };\n};\n\nconst execRegex = (pattern) => {\n pattern = isStickySupported\n ? new RegExp(pattern.source, 'y')\n : new RegExp(pattern.source + '|()', 'g');\n return (state) => {\n if (state.x < state.quasis.length) {\n const input = state.quasis[state.x];\n pattern.lastIndex = state.y;\n let match;\n if (isStickySupported) {\n if (pattern.test(input))\n match = input.slice(state.y, pattern.lastIndex);\n } else {\n const x = pattern.exec(input);\n if (x[1] == null) match = x[0];\n }\n\n state.y = pattern.lastIndex;\n return match;\n }\n };\n};\n\nexport const __pattern = (input) => {\n if (typeof input === 'function') {\n return execLambda(input);\n } else if (typeof input === 'string') {\n return execString(input);\n } else {\n return execRegex(input);\n }\n};\n\nexport const interpolation = (predicate) => (state) => {\n let match;\n\n if (\n state.x < state.expressions.length &&\n state.y >= state.quasis[state.x].length\n ) {\n state.y = 0;\n match = state.expressions[state.x++];\n if (predicate && match) match = predicate(match);\n }\n\n return match;\n};\n\nexport const parse = (matcher) => (quasis, ...expressions) => {\n if (typeof quasis === 'string') quasis = [quasis];\n const state = { quasis, expressions, x: 0, y: 0 };\n return matcher(state);\n};\n\nexport const match = (name, transform) => (quasis, ...expressions) => {\n const ast = parseDSL(\n quasis,\n expressions.map((_, i) => ({ id: `_${i}` }))\n );\n return new Function(\n '_n,_t,' + expressions.map((_expression, i) => `_${i}`).join(','),\n 'return ' + astRoot(ast, '_n', transform ? '_t' : null)\n )(name, transform, ...expressions.map(__pattern));\n};\n"],"names":["copy","next","const","_state","depth","capture","_node","index","group","abort","ast","child","quantifier","opts","invert","childOpts","block","sequence","i","body","char","let","pattern","length","state","stackIndex","y","quasiIndex","isStickySupported","match","input","x","_ref","_ref2","window","predicate","expressions","transform","currentSequence","expression","quasi","syntaxError","len","quasis"],"mappings":";AAaaA,QAAA;AACX,UAAW,EAAX,GAAA;;AAGIC,KAAA,EAAA,CAAA,OAAA;;;;;AAOJ,iBAAA,KAAA,gBAAA,KAAA,cAAA;;;AAIAC,SA5BaC,WA4BbD,IAAmCE,CAAnCF,gBAAAA,KAAAA,MAAAA;;cAuB8BE;;AAnBxBC,KAAAA,cAAAA,IAA4B,UAA5BA;mCA/BMC;AAkCZ,KAAA,GAAO,WAAP,mBAAA,cAAA,mBAAA,YAAA,oCAAA,QAAA,cAAA,KAAA,WAAA,MAAA;;AAIID,4DAIA,yGAKJ;;;;;AAQA,MAAIE,WAAJ,aAAA,gBAAA,EAGIC,YAAAA,GAAmBJ,CAHvB;;sCAQOK,CAAAA;mDAOuB,WACHC,gEAKzB,IAAA,GAFKH,CAAAA,KAEL,IAAA,GADKE,CAAAA,KACL,GADa,QACb,KAAA,MAAA,GAAAE,CAAA,IAAA,eAAA,QAAA,cAAA,MAAA,UACK,MAAQC,CAAAA,UAAR,gBAAA,2DAAA,OAIA,iBAAA,IACLV,OAEA,IAAA,EADAW,OACA,WAAA,IADyBL,CACzB,KAAA,GAAAG,CAAA,IAAA,QAAA,QAAA,cAAA,MAAA,GAHK,KAAA,KAKYD;6BAIJI,oDACR,MAAQT,CAAAA,OAAR,OAAA,KAAA,QAAA,KAAA;;;4CAQ8C;AAInD,wBAAA,EACIU,IAAYF,CADhB;iBAGA,kDAGgCG,QAHhC;aAMIC,mBAEgBP,UAAYQ,CAAA;;;AAO9BC,KAAA,GAHGT,aAAL,GAGES,CAHF,KAAA,QAAA,QAAA,KAAA,aAAA,KAAA,OAAA,UAAA,IAGE;;;;WAoBeC;AACnBlB,wBAAwB,+BAAxBA;;ACpIF,uCAAA;aAEoC;AAClCmB,SAAIC,CAAQC,CAAAA,MAAZF,IAAAA,YAIeG,EAAO;AACpBC,iBAAAA;AADoB,GAJtBJ;AADkC;UAUV,CAACC,CAAD;oBACA;;uDAIYJ;0BACHQ,CAAAA,IAAIR;AAC7B,qBAAA;;;;aAMGI;;AAZa;;;;iBAmBR,EAAA;QAEVE,MAAUG,QAAWJ,CAAAA;;;;AAKnBK,wBAEAC,mCAKF,UAFqBC,EAErB,MAAA,QAAA,MAAA,GACUC,CAAA,EAAA,CADV;;;;;;;uDCnDK,MAAOD;;AA6CpBE,QAASA;;;AAMTC;;;AAc+DC,MAAA,CAAA,SAAA,IAAA;AAC/DA,MAAA,CAAA,aAAA,YA3DgCC;kBACbX,EAAO;eAGFY,CAAAA;AAClBZ,SAAA,IAAA;;;;;AAJoB;CA0D1B;AACAU,MAAA,CAAA,KAAA,WAZqB,EAAA,EAAOG,CAAP;;;;;;;0BF0FfC,OACAjC;uBASA,cAAA,SAJqB,CACrBkC,WAAYA,CADS,WAAA,EAIrB,EAAAlC,CAAA;UAGEmC,KAAQ,EAAA,KAAA;;;wFAKRF,wFAQK,MAAA,+EAAA,+CASKlB,2CAKDA,uBACTf,CAEA,GAFUmC,CAAA,IAAA,CAEV,KAAA,MAAA,OAAA,MAAA,OAAA,MAAA,aAEE,IAAA,EAAAnC,CAAA,SAFF;iCCjMAoC,CAAA,EAAA;;;;;;;kFDuIJlB,2BAEAlB,6EAzIUC;;;CEwEd;AACA4B,MAAA,CAAA,KAAA,cA5C+B;;qBAGvBQ,aAAgBnB,CAAAA,MAAhBmB;;;iBAIA,MAAOC;;;AAPgB,CA4C/B;;"} package/dist/reghex-macro.js 000644 000000 000000 00000001106 14112463223 014475 0 ustar 00 000000 000000 'use strict';
var babelPluginMacros = require("babel-plugin-macros"), transform = require("./d56fb69a.js");
function reghexMacro(c) {
var e = c.references, b = transform.makeHelpers(c.babel);
(e.default || []).forEach(function(a) {
if (t.isCallExpression(a.parentPath.node) && (a = a.parentPath.parentPath, b.isMatch(a))) {
var d = b.getMatchImport(a);
d && (b.updateImport(d), b.transformMatch(a));
}
});
return {keepImports:!0};
}
var macro = babelPluginMacros.createMacro(reghexMacro);
module.exports = macro
//# sourceMappingURL=reghex-macro.js.map
package/dist/reghex-macro.js.map 000644 000000 000000 00000001775 14112463223 015265 0 ustar 00 000000 000000 {"version":3,"file":"reghex-macro.js","sources":["../src/babel/macro.js"],"sourcesContent":["import { createMacro } from 'babel-plugin-macros';\nimport { makeHelpers } from './transform';\n\nfunction reghexMacro({ references, babel }) {\n const helpers = makeHelpers(babel);\n const defaultRefs = references.default || [];\n\n defaultRefs.forEach((ref) => {\n if (!t.isCallExpression(ref.parentPath.node)) return;\n const path = ref.parentPath.parentPath;\n if (!helpers.isMatch(path)) return;\n\n const importPath = helpers.getMatchImport(path);\n if (!importPath) return;\n\n helpers.updateImport(importPath);\n helpers.transformMatch(path);\n });\n\n return {\n keepImports: true,\n };\n}\n\nexport default createMacro(reghexMacro);\n"],"names":["helpers","makeHelpers","references","default","parentPath","importPath","createMacro"],"mappings":";;;wBAIQA,IAAUC,qBAAAA,QAAAA;GACIC,CAAWC,CAAAA,WAAW;4BAErBC,CAAAA,sBAMd,0BAAA,EAAAJ,SAAA,EAAA,GAAL;AAIA,iCAAA;AAEKK,gCAKMC,CAAAA;AAXX;;;;;;;"} package/src/babel 000755 000000 000000 00000000000 14112455270 012375 5 ustar 00 000000 000000 package/src/codegen.js 000644 000000 000000 00000010375 14112461123 013351 0 ustar 00 000000 000000 const _state = 'state';
const _node = 'node';
const _match = 'x';
function js(/* arguments */) {
let body = arguments[0][0];
for (let i = 1; i < arguments.length; i++)
body = body + arguments[i] + arguments[0][i];
return body.trim();
}
const copy = (prev) => {
const next = {};
for (const key in prev) next[key] = prev[key];
return next;
};
const assignIndex = (depth) => js`
var y${depth} = ${_state}.y,
x${depth} = ${_state}.x;
`;
const restoreIndex = (depth) => js`
${_state}.y = y${depth};
${_state}.x = x${depth};
`;
const astExpression = (ast, depth, opts) => {
const capture = !!opts.capture && !ast.capture;
const restoreLength =
(opts.length && opts.abort && js`${_node}.length = ln${opts.length};`) ||
'';
const condition = `(${_match} = ${ast.expression.id}(${_state})) ${
capture ? '!=' : '=='
} null`;
return js`
if (${condition}) ${
capture
? js`{
${_node}.push(${_match});
} else `
: ''
}{
${restoreIndex(opts.index)}
${restoreLength}
${opts.abort}
}
`;
};
const astGroup = (ast, depth, opts) => {
const capture = !!opts.capture && !ast.capture;
opts = copy(opts);
opts.capture = capture;
if (!opts.length && capture) {
opts.length = depth;
return js`
${js`var ln${depth} = ${_node}.length;`}
${astSequence(ast.sequence, depth + 1, opts)}
`;
}
return astSequence(ast.sequence, depth + 1, opts);
};
const astChild = (ast, depth, opts) =>
ast.expression ? astExpression(ast, depth, opts) : astGroup(ast, depth, opts);
const astQuantifier = (ast, depth, opts) => {
const { index, abort } = opts;
const invert = `inv_${depth}`;
const group = `group_${depth}`;
opts = copy(opts);
if (ast.capture === '!') {
opts.index = depth;
opts.abort = js`break ${invert}`;
}
let child;
if (ast.quantifier === '+') {
const starAst = copy(ast);
starAst.quantifier = '*';
child = js`
${astChild(ast, depth, opts)}
${astQuantifier(starAst, depth, opts)}
`;
} else if (ast.quantifier === '*') {
opts.length = 0;
opts.index = depth;
opts.abort = js`break ${group};`;
child = js`
${group}: for (;;) {
${assignIndex(depth)}
${astChild(ast, depth, opts)}
}
`;
} else if (ast.quantifier === '?' && ast.expression) {
opts.index = depth;
opts.abort = '';
child = js`
${assignIndex(depth)}
${astChild(ast, depth, opts)}
`;
} else if (ast.quantifier === '?') {
opts.index = depth;
opts.abort = js`break ${group}`;
child = js`
${group}: {
${assignIndex(depth)}
${astChild(ast, depth, opts)}
}
`;
} else {
child = astChild(ast, depth, opts);
}
if (ast.capture === '!') {
return js`
${invert}: {
${assignIndex(depth)}
${child}
${restoreIndex(index)}
${abort}
}
`;
} else if (ast.capture === '=') {
return js`
${assignIndex(depth)}
${child}
${restoreIndex(depth)}
`;
} else {
return child;
}
};
const astSequence = (ast, depth, opts) => {
const alternation = ast.alternation ? `alt_${depth}` : '';
let body = '';
for (; ast; ast = ast.alternation) {
const block = `block_${depth}`;
let childOpts = opts;
if (ast.alternation) {
childOpts = copy(opts);
childOpts.index = depth;
childOpts.abort = js`break ${block};`;
}
let sequence = '';
for (let i = 0; i < ast.length; i++)
sequence += astQuantifier(ast[i], depth, childOpts);
if (!ast.alternation) {
body += sequence;
} else {
body += js`
${block}: {
${assignIndex(depth)}
${sequence}
break ${alternation};
}
`;
}
}
if (!alternation) return body;
return js`
${alternation}: {
${body}
}
`;
};
const astRoot = (ast, name, transform) => {
return js`
(function (${_state}) {
${assignIndex(1)}
var ${_node} = [];
var ${_match};
${astSequence(ast, 2, {
index: 1,
length: 0,
abort: js`return;`,
capture: true,
})}
if (${name}) ${_node}.tag = ${name};
return ${transform ? js`(${transform})(${_node})` : _node};
})
`;
};
export { astRoot };
package/src/core.js 000644 000000 000000 00000004421 14112455270 012676 0 ustar 00 000000 000000 import { astRoot } from './codegen';
import { parse as parseDSL } from './parser';
const isStickySupported = typeof /./g.sticky === 'boolean';
const execLambda = (pattern) => {
if (pattern.length) return pattern;
return (state) => pattern()(state);
};
const execString = (pattern) => {
return (state) => {
if (state.x < state.quasis.length) {
const input = state.quasis[state.x];
for (let i = 0, l = pattern.length; i < l; i++)
if (input.charCodeAt(state.y + i) !== pattern.charCodeAt(i))
return null;
state.y += pattern.length;
return pattern;
}
};
};
const execRegex = (pattern) => {
pattern = isStickySupported
? new RegExp(pattern.source, 'y')
: new RegExp(pattern.source + '|()', 'g');
return (state) => {
if (state.x < state.quasis.length) {
const input = state.quasis[state.x];
pattern.lastIndex = state.y;
let match;
if (isStickySupported) {
if (pattern.test(input))
match = input.slice(state.y, pattern.lastIndex);
} else {
const x = pattern.exec(input);
if (x[1] == null) match = x[0];
}
state.y = pattern.lastIndex;
return match;
}
};
};
export const __pattern = (input) => {
if (typeof input === 'function') {
return execLambda(input);
} else if (typeof input === 'string') {
return execString(input);
} else {
return execRegex(input);
}
};
export const interpolation = (predicate) => (state) => {
let match;
if (
state.x < state.expressions.length &&
state.y >= state.quasis[state.x].length
) {
state.y = 0;
match = state.expressions[state.x++];
if (predicate && match) match = predicate(match);
}
return match;
};
export const parse = (matcher) => (quasis, ...expressions) => {
if (typeof quasis === 'string') quasis = [quasis];
const state = { quasis, expressions, x: 0, y: 0 };
return matcher(state);
};
export const match = (name, transform) => (quasis, ...expressions) => {
const ast = parseDSL(
quasis,
expressions.map((_, i) => ({ id: `_${i}` }))
);
return new Function(
'_n,_t,' + expressions.map((_expression, i) => `_${i}`).join(','),
'return ' + astRoot(ast, '_n', transform ? '_t' : null)
)(name, transform, ...expressions.map(__pattern));
};
package/src/core.test.js 000644 000000 000000 00000046676 14112462074 013676 0 ustar 00 000000 000000 import { parse, match, interpolation } from './core';
const expectToParse = (node, input, result, lastIndex = 0) => {
const state = { quasis: [input], expressions: [], x: 0, y: 0 };
if (result) result.tag = 'node';
expect(node(state)).toEqual(result);
// NOTE: After parsing we expect the current index to exactly match the
// sum amount of matched characters
if (result === undefined) {
expect(state.y).toBe(0);
} else {
const index = lastIndex || result.reduce((acc, x) => acc + x.length, 0);
expect(state.y).toBe(index);
}
};
describe('can create nameless matchers', () => {
it('matches without tagging', () => {
const state = { quasis: ['1'], expressions: [], x: 0, y: 0 };
const node = match(null)`${/1/}`;
expect(node(state)).toEqual(['1']);
});
});
describe('required matcher', () => {
const node = match('node')`${/1/}`;
it.each`
input | result
${'1'} | ${['1']}
${''} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
it('matches empty regex patterns', () => {
const node = match('node')`${/[ ]*/}`;
expectToParse(node, '', ['']);
});
});
describe('optional matcher', () => {
const node = match('node')`${/1/}?`;
it.each`
input | result
${'1'} | ${['1']}
${'_'} | ${[]}
${''} | ${[]}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('star matcher', () => {
const node = match('node')`${/1/}*`;
it.each`
input | result
${'1'} | ${['1']}
${'11'} | ${['1', '1']}
${'111'} | ${['1', '1', '1']}
${'_'} | ${[]}
${''} | ${[]}
`('should return $result when "$input" is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('plus matcher', () => {
const node = match('node')`${/1/}+`;
it.each`
input | result
${'1'} | ${['1']}
${'11'} | ${['1', '1']}
${'111'} | ${['1', '1', '1']}
${'_'} | ${undefined}
${''} | ${undefined}
`('should return $result when "$input" is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('optional then required matcher', () => {
const node = match('node')`${/1/}? ${/2/}`;
it.each`
input | result
${'12'} | ${['1', '2']}
${'2'} | ${['2']}
${''} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('star then required matcher', () => {
const node = match('node')`${/1/}* ${/2/}`;
it.each`
input | result
${'12'} | ${['1', '2']}
${'112'} | ${['1', '1', '2']}
${'2'} | ${['2']}
${''} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('plus then required matcher', () => {
const node = match('node')`${/1/}+ ${/2/}`;
it.each`
input | result
${'12'} | ${['1', '2']}
${'112'} | ${['1', '1', '2']}
${'2'} | ${undefined}
${''} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('optional group then required matcher', () => {
const node = match('node')`(${/1/} ${/2/})? ${/3/}`;
it.each`
input | result
${'123'} | ${['1', '2', '3']}
${'3'} | ${['3']}
${'23'} | ${undefined}
${'_'} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('star group then required matcher', () => {
const node = match('node')`(${/1/} ${/2/})* ${/3/}`;
it.each`
input | result
${'123'} | ${['1', '2', '3']}
${'12123'} | ${['1', '2', '1', '2', '3']}
${'3'} | ${['3']}
${'23'} | ${undefined}
${'13'} | ${undefined}
${'_'} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('plus group then required matcher', () => {
const node = match('node')`(${/1/} ${/2/})+ ${/3/}`;
it.each`
input | result
${'123'} | ${['1', '2', '3']}
${'12123'} | ${['1', '2', '1', '2', '3']}
${'23'} | ${undefined}
${'3'} | ${undefined}
${'13'} | ${undefined}
${'_'} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('optional group with nested optional matcher, then required matcher', () => {
const node = match('node')`(${/1/}? ${/2/})? ${/3/}`;
it.each`
input | result
${'123'} | ${['1', '2', '3']}
${'23'} | ${['2', '3']}
${'3'} | ${['3']}
${'13'} | ${undefined}
${'_'} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('star group with nested optional matcher, then required matcher', () => {
const node = match('node')`(${/1/}? ${/2/})* ${/3/}`;
it.each`
input | result
${'123'} | ${['1', '2', '3']}
${'23'} | ${['2', '3']}
${'223'} | ${['2', '2', '3']}
${'2123'} | ${['2', '1', '2', '3']}
${'3'} | ${['3']}
${'13'} | ${undefined}
${'_'} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('plus group with nested optional matcher, then required matcher', () => {
const node = match('node')`(${/1/}? ${/2/})+ ${/3/}`;
it.each`
input | result
${'123'} | ${['1', '2', '3']}
${'23'} | ${['2', '3']}
${'223'} | ${['2', '2', '3']}
${'2123'} | ${['2', '1', '2', '3']}
${'3'} | ${undefined}
${'13'} | ${undefined}
${'_'} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('plus group with nested plus matcher, then required matcher', () => {
const node = match('node')`(${/1/}+ ${/2/})+ ${/3/}`;
it.each`
input | result
${'123'} | ${['1', '2', '3']}
${'1123'} | ${['1', '1', '2', '3']}
${'12123'} | ${['1', '2', '1', '2', '3']}
${'121123'} | ${['1', '2', '1', '1', '2', '3']}
${'3'} | ${undefined}
${'23'} | ${undefined}
${'13'} | ${undefined}
${'_'} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('plus group with nested required and plus matcher, then required matcher', () => {
const node = match('node')`(${/1/} ${/2/}+)+ ${/3/}`;
it.each`
input | result
${'123'} | ${['1', '2', '3']}
${'1223'} | ${['1', '2', '2', '3']}
${'122123'} | ${['1', '2', '2', '1', '2', '3']}
${'13'} | ${undefined}
${'_'} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('nested plus group with nested required and plus matcher, then required matcher or alternate', () => {
const node = match('node')`(${/1/} ${/2/}+)+ ${/3/} | ${/1/}`;
it.each`
input | result
${'123'} | ${['1', '2', '3']}
${'1223'} | ${['1', '2', '2', '3']}
${'122123'} | ${['1', '2', '2', '1', '2', '3']}
${'1'} | ${['1']}
${'13'} | ${['1']}
${'_'} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('nested plus group with nested required and plus matcher, then alternate', () => {
const node = match('node')`(${/1/} ${/2/}+)+ (${/3/} | ${/4/})`;
it.each`
input | result
${'123'} | ${['1', '2', '3']}
${'124'} | ${['1', '2', '4']}
${'1223'} | ${['1', '2', '2', '3']}
${'1224'} | ${['1', '2', '2', '4']}
${'1'} | ${undefined}
${'13'} | ${undefined}
${'_'} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('regular alternate', () => {
const node = match('node')`${/1/} | ${/2/} | ${/3/} | ${/4/}`;
it.each`
input | result
${'1'} | ${['1']}
${'2'} | ${['2']}
${'3'} | ${['3']}
${'4'} | ${['4']}
${'_'} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('nested alternate in nested alternate in alternate', () => {
const node = match('node')`((${/1/} | ${/2/}) | ${/3/}) | ${/4/}`;
it.each`
input | result
${'1'} | ${['1']}
${'2'} | ${['2']}
${'3'} | ${['3']}
${'4'} | ${['4']}
${'_'} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('alternate after required matcher', () => {
const node = match('node')`${/1/} (${/2/} | ${/3/})`;
it.each`
input | result
${'12'} | ${['1', '2']}
${'13'} | ${['1', '3']}
${'14'} | ${undefined}
${'3'} | ${undefined}
${'_'} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('alternate with star group and required matcher after required matcher', () => {
const node = match('node')`${/1/} (${/2/}* ${/3/} | ${/4/})`;
it.each`
input | result
${'123'} | ${['1', '2', '3']}
${'1223'} | ${['1', '2', '2', '3']}
${'13'} | ${['1', '3']}
${'14'} | ${['1', '4']}
${'12'} | ${undefined}
${'15'} | ${undefined}
${'_'} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('alternate with plus group and required matcher after required matcher', () => {
const node = match('node')`${/1/} (${/2/}+ ${/3/} | ${/4/})`;
it.each`
input | result
${'123'} | ${['1', '2', '3']}
${'1223'} | ${['1', '2', '2', '3']}
${'14'} | ${['1', '4']}
${'13'} | ${undefined}
${'12'} | ${undefined}
${'15'} | ${undefined}
${'_'} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('alternate with optional and required matcher after required matcher', () => {
const node = match('node')`${/1/} (${/2/}? ${/3/} | ${/4/})`;
it.each`
input | result
${'123'} | ${['1', '2', '3']}
${'13'} | ${['1', '3']}
${'14'} | ${['1', '4']}
${'12'} | ${undefined}
${'15'} | ${undefined}
${'_'} | ${undefined}
`('should return $result when $input is passed', ({ input, result }) => {
expectToParse(node, input, result);
});
});
describe('non-capturing group', () => {
const node = match('node')`${/1/} (?: ${/2/}+)`;
it.each`
input | result | lastIndex
${'12'} | ${['1']} | ${2}
${'122'} | ${['1']} | ${3}
${'13'} | ${undefined} | ${0}
${'1'} | ${undefined} | ${0}
${'_'} | ${undefined} | ${0}
`(
'should return $result when $input is passed',
({ input, result, lastIndex }) => {
expectToParse(node, input, result, lastIndex);
}
);
});
describe('non-capturing shorthand', () => {
const node = match('node')`${/1/} :${/2/}+`;
it.each`
input | result | lastIndex
${'12'} | ${['1']} | ${2}
${'122'} | ${['1']} | ${3}
${'13'} | ${undefined} | ${0}
${'1'} | ${undefined} | ${0}
${'_'} | ${undefined} | ${0}
`(
'should return $result when $input is passed',
({ input, result, lastIndex }) => {
expectToParse(node, input, result, lastIndex);
}
);
});
describe('non-capturing group with plus matcher, then required matcher', () => {
const node = match('node')`(?: ${/1/}+) ${/2/}`;
it.each`
input | result | lastIndex
${'12'} | ${['2']} | ${2}
${'112'} | ${['2']} | ${3}
${'1'} | ${undefined} | ${0}
${'13'} | ${undefined} | ${0}
${'2'} | ${undefined} | ${0}
${'_'} | ${undefined} | ${0}
`(
'should return $result when $input is passed',
({ input, result, lastIndex }) => {
expectToParse(node, input, result, lastIndex);
}
);
});
describe('non-capturing group with star group and required matcher, then required matcher', () => {
const node = match('node')`(?: ${/1/}* ${/2/}) ${/3/}`;
it.each`
input | result | lastIndex
${'123'} | ${['3']} | ${3}
${'1123'} | ${['3']} | ${4}
${'23'} | ${['3']} | ${2}
${'13'} | ${undefined} | ${0}
${'2'} | ${undefined} | ${0}
${'_'} | ${undefined} | ${0}
`(
'should return $result when $input is passed',
({ input, result, lastIndex }) => {
expectToParse(node, input, result, lastIndex);
}
);
});
describe('non-capturing group with plus group and required matcher, then required matcher', () => {
const node = match('node')`(?: ${/1/}+ ${/2/}) ${/3/}`;
it.each`
input | result | lastIndex
${'123'} | ${['3']} | ${3}
${'1123'} | ${['3']} | ${4}
${'23'} | ${undefined} | ${0}
${'13'} | ${undefined} | ${0}
${'2'} | ${undefined} | ${0}
${'_'} | ${undefined} | ${0}
`(
'should return $result when $input is passed',
({ input, result, lastIndex }) => {
expectToParse(node, input, result, lastIndex);
}
);
});
describe('non-capturing group with optional and required matcher, then required matcher', () => {
const node = match('node')`(?: ${/1/}? ${/2/}) ${/3/}`;
it.each`
input | result | lastIndex
${'123'} | ${['3']} | ${3}
${'23'} | ${['3']} | ${2}
${'13'} | ${undefined} | ${0}
${'2'} | ${undefined} | ${0}
${'_'} | ${undefined} | ${0}
`(
'should return $result when $input is passed',
({ input, result, lastIndex }) => {
expectToParse(node, input, result, lastIndex);
}
);
});
describe('positive lookahead group', () => {
const node = match('node')`(?= ${/1/}) ${/\d/}`;
it.each`
input | result | lastIndex
${'1'} | ${['1']} | ${1}
${'13'} | ${['1']} | ${1}
${'2'} | ${undefined} | ${0}
${'_'} | ${undefined} | ${0}
`(
'should return $result when $input is passed',
({ input, result, lastIndex }) => {
expectToParse(node, input, result, lastIndex);
}
);
});
describe('positive lookahead shorthand', () => {
const node = match('node')`=${/1/} ${/\d/}`;
it.each`
input | result | lastIndex
${'1'} | ${['1']} | ${1}
${'13'} | ${['1']} | ${1}
${'2'} | ${undefined} | ${0}
${'_'} | ${undefined} | ${0}
`(
'should return $result when $input is passed',
({ input, result, lastIndex }) => {
expectToParse(node, input, result, lastIndex);
}
);
});
describe('positive lookahead group with plus matcher', () => {
const node = match('node')`(?= ${/1/}+) ${/\d/}`;
it.each`
input | result | lastIndex
${'1'} | ${['1']} | ${1}
${'11'} | ${['1']} | ${1}
${'12'} | ${['1']} | ${1}
${'22'} | ${undefined} | ${0}
${'2'} | ${undefined} | ${0}
${'_'} | ${undefined} | ${0}
`(
'should return $result when $input is passed',
({ input, result, lastIndex }) => {
expectToParse(node, input, result, lastIndex);
}
);
});
describe('positive lookahead group with plus group and required matcher', () => {
const node = match('node')`(?= ${/1/}+ ${/2/}) ${/\d/}`;
it.each`
input | result | lastIndex
${'12'} | ${['1']} | ${1}
${'112'} | ${['1']} | ${1}
${'1123'} | ${['1']} | ${1}
${'2'} | ${undefined} | ${0}
${'1'} | ${undefined} | ${0}
${'2'} | ${undefined} | ${0}
${'_'} | ${undefined} | ${0}
`(
'should return $result when $input is passed',
({ input, result, lastIndex }) => {
expectToParse(node, input, result, lastIndex);
}
);
});
describe('negative lookahead group', () => {
const node = match('node')`(?! ${/1/}) ${/\d/}`;
it.each`
input | result | lastIndex
${'2'} | ${['2']} | ${1}
${'23'} | ${['2']} | ${1}
${'1'} | ${undefined} | ${0}
${'1'} | ${undefined} | ${0}
${'_'} | ${undefined} | ${0}
`(
'should return $result when $input is passed',
({ input, result, lastIndex }) => {
expectToParse(node, input, result, lastIndex);
}
);
});
describe('negative lookahead shorthand', () => {
const node = match('node')`!${/1/} ${/\d/}`;
it.each`
input | result | lastIndex
${'2'} | ${['2']} | ${1}
${'23'} | ${['2']} | ${1}
${'1'} | ${undefined} | ${0}
${'1'} | ${undefined} | ${0}
${'_'} | ${undefined} | ${0}
`(
'should return $result when $input is passed',
({ input, result, lastIndex }) => {
expectToParse(node, input, result, lastIndex);
}
);
});
describe('longer negative lookahead group', () => {
const node = match('node')`${/1/} (?! ${/2/} ${/3/}) ${/\d/} ${/\d/}`;
it.each`
input | result | lastIndex
${'145'} | ${['1', '4', '5']} | ${3}
${'124'} | ${['1', '2', '4']} | ${3}
${'123'} | ${undefined} | ${0}
${'2'} | ${undefined} | ${0}
${'_'} | ${undefined} | ${0}
`(
'should return $result when $input is passed',
({ input, result, lastIndex }) => {
expectToParse(node, input, result, lastIndex);
}
);
});
describe('negative lookahead group with plus matcher', () => {
const node = match('node')`(?! ${/1/}+) ${/\d/}`;
it.each`
input | result | lastIndex
${'2'} | ${['2']} | ${1}
${'21'} | ${['2']} | ${1}
${'22'} | ${['2']} | ${1}
${'11'} | ${undefined} | ${0}
${'1'} | ${undefined} | ${0}
${'_'} | ${undefined} | ${0}
`(
'should return $result when $input is passed',
({ input, result, lastIndex }) => {
expectToParse(node, input, result, lastIndex);
}
);
});
describe('negative lookahead group with plus group and required matcher', () => {
const node = match('node')`(?! ${/1/}+ ${/2/}) ${/\d/}`;
it.each`
input | result | lastIndex
${'21'} | ${['2']} | ${1}
${'211'} | ${['2']} | ${1}
${'113'} | ${['1']} | ${1}
${'1'} | ${['1']} | ${1}
${'112'} | ${undefined} | ${0}
${'12'} | ${undefined} | ${0}
${'_'} | ${undefined} | ${0}
`(
'should return $result when $input is passed',
({ input, result, lastIndex }) => {
expectToParse(node, input, result, lastIndex);
}
);
});
describe('interpolation parsing', () => {
const node = match('node')`
${/1/}
${interpolation((x) => (x > 1 ? x : null))}
${/3/}
`;
it('matches interpolations', () => {
const expected = ['1', 2, '3'];
expected.tag = 'node';
expect(parse(node)`1${2}3`).toEqual(expected);
});
it('does not match invalid inputs', () => {
expect(parse(node)`13`).toBe(undefined);
expect(parse(node)`13${2}`).toBe(undefined);
expect(parse(node)`${2}13`).toBe(undefined);
expect(parse(node)`1${1}3`).toBe(undefined);
});
});
describe('string matching', () => {
const node = match('node')`
${'1'}
${'2'}
`;
it('matches strings', () => {
const expected = ['1', '2'];
expected.tag = 'node';
expect(parse(node)('12')).toEqual(expected);
expect(parse(node)('13')).toBe(undefined);
});
});
package/src/parser.js 000644 000000 000000 00000004221 14112455270 013240 0 ustar 00 000000 000000 const syntaxError = (char) => {
throw new SyntaxError('Unexpected token "' + char + '"');
};
export const parse = (quasis, expressions) => {
let quasiIndex = 0;
let stackIndex = 0;
const sequenceStack = [];
const rootSequence = [];
let currentGroup = null;
let lastMatch;
let currentSequence = rootSequence;
let capture;
for (
let quasiIndex = 0, stackIndex = 0;
stackIndex < quasis.length + expressions.length;
stackIndex++
) {
if (stackIndex % 2 !== 0) {
const expression = expressions[stackIndex++ >> 1];
currentSequence.push({ expression, capture });
capture = undefined;
}
const quasi = quasis[stackIndex >> 1];
for (quasiIndex = 0; quasiIndex < quasi.length; ) {
const char = quasi[quasiIndex++];
if (char === ' ' || char === '\t' || char === '\r' || char === '\n') {
} else if (char === '|' && currentSequence.length) {
currentSequence = currentSequence.alternation = [];
} else if (char === ')' && currentSequence.length) {
currentGroup = null;
currentSequence = sequenceStack.pop();
if (!currentSequence) syntaxError(char);
} else if (char === '(') {
sequenceStack.push(currentSequence);
currentSequence.push((currentGroup = { sequence: [], capture }));
currentSequence = currentGroup.sequence;
capture = undefined;
} else if (char === ':' || char === '=' || char === '!') {
capture = char;
const nextChar = quasi[quasiIndex];
if (quasi[quasiIndex] && quasi[quasiIndex] !== '(') syntaxError(char);
} else if (char === '?' && !currentSequence.length && currentGroup) {
capture = quasi[quasiIndex++];
if (capture === ':' || capture === '=' || capture === '!') {
currentGroup.capture = capture;
capture = undefined;
} else {
syntaxError(char);
}
} else if (
(char === '?' || char === '+' || char === '*') &&
(lastMatch = currentSequence[currentSequence.length - 1])
) {
lastMatch.quantifier = char;
} else {
syntaxError(char);
}
}
}
return rootSequence;
};
package/src/parser.test.js 000644 000000 000000 00000007432 14112455270 014225 0 ustar 00 000000 000000 import { parse } from './parser';
const parseTag = (quasis, ...expressions) => parse(quasis, expressions);
it('supports parsing expressions with quantifiers', () => {
let ast;
ast = parseTag`${1}?`;
expect(ast).toHaveProperty('0.quantifier', '?');
ast = parseTag`${1}+`;
expect(ast).toHaveProperty('0.quantifier', '+');
ast = parseTag`${1}*`;
expect(ast).toHaveProperty('0.quantifier', '*');
});
it('supports top-level alternations', () => {
let ast;
ast = parseTag`${1} | ${2}`;
expect(ast).toHaveProperty('length', 1);
expect(ast).toHaveProperty('0.expression', 1);
expect(ast).toHaveProperty('alternation.0.expression', 2);
ast = parseTag`${1}? | ${2}?`;
expect(ast).toHaveProperty('0.quantifier', '?');
});
it('supports groups with quantifiers', () => {
let ast;
ast = parseTag`(${1} ${2})`;
expect(ast).toHaveProperty('length', 1);
expect(ast).toHaveProperty('0.sequence.length', 2);
expect(ast).toHaveProperty('0.sequence.0.expression', 1);
expect(ast).toHaveProperty('0.sequence.1.expression', 2);
ast = parseTag`(${1} ${2}?)?`;
expect(ast).toHaveProperty('length', 1);
expect(ast).toHaveProperty('0.quantifier', '?');
expect(ast).toHaveProperty('0.sequence.0.quantifier', undefined);
});
describe('non-capturing syntax', () => {
it('supports regex-like syntax', () => {
const ast = parseTag`(?: ${1})`;
expect(ast).toHaveProperty('length', 1);
expect(ast).toHaveProperty('0.capture', ':');
expect(ast).toHaveProperty('0.sequence.length', 1);
});
it('supports shorthand', () => {
let ast = parseTag`:${1}`;
expect(ast).toHaveProperty('length', 1);
expect(ast).toHaveProperty('0.capture', ':');
expect(ast).toHaveProperty('0.expression', 1);
ast = parseTag`:(${1})`;
expect(ast).toHaveProperty('length', 1);
expect(ast).toHaveProperty('0.capture', ':');
expect(ast).toHaveProperty('0.sequence.length', 1);
});
it('fails on invalid usage', () => {
expect(() => parseTag`${1} : ${2}`).toThrow();
expect(() => parseTag`${1} :|${2}`).toThrow();
});
});
describe('positive lookaheads syntax', () => {
it('supports regex-like syntax', () => {
const ast = parseTag`(?= ${1})`;
expect(ast).toHaveProperty('length', 1);
expect(ast).toHaveProperty('0.capture', '=');
expect(ast).toHaveProperty('0.sequence.length', 1);
});
it('supports shorthand', () => {
let ast = parseTag`=${1}`;
expect(ast).toHaveProperty('length', 1);
expect(ast).toHaveProperty('0.capture', '=');
expect(ast).toHaveProperty('0.expression', 1);
ast = parseTag`=(${1})`;
expect(ast).toHaveProperty('length', 1);
expect(ast).toHaveProperty('0.capture', '=');
expect(ast).toHaveProperty('0.sequence.length', 1);
});
});
describe('negative lookaheads syntax', () => {
it('supports regex-like syntax', () => {
const ast = parseTag`(?! ${1})`;
expect(ast).toHaveProperty('length', 1);
expect(ast).toHaveProperty('0.capture', '!');
expect(ast).toHaveProperty('0.sequence.length', 1);
});
it('supports shorthand', () => {
let ast = parseTag`!${1}`;
expect(ast).toHaveProperty('length', 1);
expect(ast).toHaveProperty('0.capture', '!');
expect(ast).toHaveProperty('0.expression', 1);
ast = parseTag`!(${1})`;
expect(ast).toHaveProperty('length', 1);
expect(ast).toHaveProperty('0.capture', '!');
expect(ast).toHaveProperty('0.sequence.length', 1);
});
});
it('supports groups with alternates', () => {
expect(parseTag`(${1} | ${2}) ${3}`).toMatchInlineSnapshot(`
Array [
Object {
"capture": undefined,
"sequence": Array [
Object {
"capture": undefined,
"expression": 1,
},
],
},
Object {
"capture": undefined,
"expression": 3,
},
]
`);
});
package/src/babel/__snapshots__ 000755 000000 000000 00000000000 14112455270 015213 5 ustar 00 000000 000000 package/src/babel/macro.js 000644 000000 000000 00000001211 14112455270 014106 0 ustar 00 000000 000000 import { createMacro } from 'babel-plugin-macros';
import { makeHelpers } from './transform';
function reghexMacro({ references, babel }) {
const helpers = makeHelpers(babel);
const defaultRefs = references.default || [];
defaultRefs.forEach((ref) => {
if (!t.isCallExpression(ref.parentPath.node)) return;
const path = ref.parentPath.parentPath;
if (!helpers.isMatch(path)) return;
const importPath = helpers.getMatchImport(path);
if (!importPath) return;
helpers.updateImport(importPath);
helpers.transformMatch(path);
});
return {
keepImports: true,
};
}
export default createMacro(reghexMacro);
package/src/babel/plugin.js 000644 000000 000000 00000001202 14112455270 014303 0 ustar 00 000000 000000 import { makeHelpers } from './transform';
export default function reghexPlugin(babel, opts = {}) {
let helpers;
return {
name: 'reghex',
visitor: {
Program() {
helpers = makeHelpers(babel);
},
ImportDeclaration(path) {
if (opts.codegen === false) return;
helpers.updateImport(path);
},
TaggedTemplateExpression(path) {
if (helpers.isMatch(path) && helpers.getMatchImport(path)) {
if (opts.codegen === false) {
helpers.minifyMatch(path);
} else {
helpers.transformMatch(path);
}
}
},
},
};
}
package/src/babel/plugin.test.js 000644 000000 000000 00000006553 14112462311 015271 0 ustar 00 000000 000000 import { transform } from '@babel/core';
import reghexPlugin from './plugin';
it('works with standard features', () => {
const code = `
import { match } from 'reghex/macro';
const node = match('node')\`
\${1}+ | \${2}+ (\${3} ( \${4}? \${5} ) )*
\`;
`;
expect(
transform(code, { babelrc: false, presets: [], plugins: [reghexPlugin] })
.code
).toMatchSnapshot();
});
it('works with nameless matchers', () => {
const code = `
import { match } from 'reghex/macro';
const node = match()\`
\${1}+ | \${2}+ (\${3} ( \${4}? \${5} ) )*
\`;
`;
expect(
transform(code, { babelrc: false, presets: [], plugins: [reghexPlugin] })
.code
).toMatchSnapshot();
});
it('works while only minifying', () => {
const code = `
import { match } from 'reghex/macro';
const node = match('node')\`
\${1}+ | \${2}+ (\${3} ( \${4}? \${5} ) )*
\`;
`;
expect(
transform(code, {
babelrc: false,
presets: [],
plugins: [[reghexPlugin, { codegen: false }]],
}).code
).toMatchSnapshot();
});
it('deduplicates hoisted expressions', () => {
const code = `
import { match } from 'reghex/macro';
const re = /1/;
const str = '1';
const a = match('a')\`
\${re}
\${str}
\`;
const b = match('b')\`
\${re}
\${'2'}
\`;
`;
expect(
transform(code, { babelrc: false, presets: [], plugins: [reghexPlugin] })
.code
).toMatchSnapshot();
});
it('works with local recursion', () => {
// NOTE: A different default name is allowed
const code = `
import { match as m, tag } from 'reghex';
const inner = m('inner')\`
\${/inner/}
\`;
const node = m('node')\`
\${inner}
\`;
`;
expect(
transform(code, { babelrc: false, presets: [], plugins: [reghexPlugin] })
.code
).toMatchSnapshot();
});
it('works with self-referential thunks', () => {
const code = `
import { match, tag } from 'reghex';
const inner = match('inner')\`
\${() => node}
\`;
const node = match('node')\`
\${inner}
\`;
`;
expect(
transform(code, { babelrc: false, presets: [], plugins: [reghexPlugin] })
.code
).toMatchSnapshot();
});
it('works with transform functions', () => {
const code = `
import { match } from 'reghex';
const first = match('inner', x => x)\`\`;
const transform = x => x;
const second = match('node', transform)\`\`;
`;
expect(
transform(code, { babelrc: false, presets: [], plugins: [reghexPlugin] })
.code
).toMatchSnapshot();
});
it('works with non-capturing groups', () => {
const code = `
import { match } from 'reghex';
const node = match('node')\`
\${1} (\${2} | (?: \${3})+)
\`;
`;
expect(
transform(code, { babelrc: false, presets: [], plugins: [reghexPlugin] })
.code
).toMatchSnapshot();
});
it('works together with @babel/plugin-transform-modules-commonjs', () => {
const code = `
import { match } from 'reghex';
const node = match('node')\`
\${1} \${2}
\`;
`;
expect(
transform(code, {
babelrc: false,
presets: [],
plugins: [
reghexPlugin,
[
'@babel/plugin-transform-modules-commonjs',
{
noInterop: true,
loose: true,
},
],
],
}).code
).toMatchSnapshot();
});
package/src/babel/transform.js 000644 000000 000000 00000016636 14112462756 015050 0 ustar 00 000000 000000 import { astRoot } from '../codegen';
import { parse } from '../parser';
export function makeHelpers({ types: t, template }) {
const regexPatternsRe = /^[()\[\]|.+?*]|[^\\][()\[\]|.+?*$^]|\\[wdsWDS]/;
const importSourceRe = /reghex$|^reghex\/macro/;
const importName = 'reghex';
let _hasUpdatedImport = false;
let _matchId = t.identifier('match');
let _patternId = t.identifier('__pattern');
const _hoistedExpressions = new Map();
return {
/** Adds the reghex import declaration to the Program scope */
updateImport(path) {
if (_hasUpdatedImport) return;
if (!importSourceRe.test(path.node.source.value)) return;
_hasUpdatedImport = true;
if (path.node.source.value !== importName) {
path.node.source = t.stringLiteral(importName);
}
_patternId = path.scope.generateUidIdentifier('_pattern');
path.node.specifiers.push(
t.importSpecifier(_patternId, t.identifier('__pattern'))
);
const tagImport = path.node.specifiers.find((node) => {
return t.isImportSpecifier(node) && node.imported.name === 'match';
});
if (!tagImport) {
path.node.specifiers.push(
t.importSpecifier(
(_matchId = path.scope.generateUidIdentifier('match')),
t.identifier('match')
)
);
} else {
_matchId = tagImport.imported;
}
},
/** Determines whether the given tagged template expression is a reghex match */
isMatch(path) {
if (
t.isTaggedTemplateExpression(path.node) &&
t.isCallExpression(path.node.tag) &&
t.isIdentifier(path.node.tag.callee) &&
path.scope.hasBinding(path.node.tag.callee.name)
) {
if (t.isVariableDeclarator(path.parentPath))
path.parentPath._isMatch = true;
return true;
}
return (
t.isVariableDeclarator(path.parentPath) && path.parentPath._isMatch
);
},
/** Given a reghex match, returns the path to reghex's match import declaration */
getMatchImport(path) {
t.assertTaggedTemplateExpression(path.node);
const binding = path.scope.getBinding(path.node.tag.callee.name);
if (
binding.kind !== 'module' ||
!t.isImportDeclaration(binding.path.parent) ||
!importSourceRe.test(binding.path.parent.source.value) ||
!t.isImportSpecifier(binding.path.node)
) {
return null;
}
return binding.path.parentPath;
},
/** Given a match, returns an evaluated name or a best guess */
getMatchName(path) {
t.assertTaggedTemplateExpression(path.node);
const nameArgumentPath = path.get('tag.arguments.0');
if (nameArgumentPath) {
const { confident, value } = nameArgumentPath.evaluate();
if (!confident && t.isIdentifier(nameArgumentPath.node)) {
return nameArgumentPath.node.name;
} else if (confident && typeof value === 'string') {
return value;
}
}
return path.scope.generateUidIdentifierBasedOnNode(path.node);
},
/** Given a match, hoists its expressions in front of the match's statement */
_prepareExpressions(path) {
t.assertTaggedTemplateExpression(path.node);
const variableDeclarators = [];
const matchName = this.getMatchName(path);
const hoistedExpressions = path.node.quasi.expressions.map(
(expression, i) => {
if (
t.isArrowFunctionExpression(expression) &&
t.isIdentifier(expression.body)
) {
expression = expression.body;
} else if (
(t.isFunctionExpression(expression) ||
t.isArrowFunctionExpression(expression)) &&
t.isBlockStatement(expression.body) &&
expression.body.body.length === 1 &&
t.isReturnStatement(expression.body.body[0]) &&
t.isIdentifier(expression.body.body[0].argument)
) {
expression = expression.body.body[0].argument;
}
const isBindingExpression =
t.isIdentifier(expression) &&
path.scope.hasBinding(expression.name);
if (isBindingExpression) {
const binding = path.scope.getBinding(expression.name);
if (t.isVariableDeclarator(binding.path.node)) {
const matchPath = binding.path.get('init');
if (this.isMatch(matchPath)) {
return expression;
} else if (_hoistedExpressions.has(expression.name)) {
return t.identifier(_hoistedExpressions.get(expression.name));
}
}
}
const id = path.scope.generateUidIdentifier(
isBindingExpression
? `${expression.name}_expression`
: `${matchName}_expression`
);
variableDeclarators.push(
t.variableDeclarator(
id,
t.callExpression(t.identifier(_patternId.name), [expression])
)
);
if (t.isIdentifier(expression)) {
_hoistedExpressions.set(expression.name, id.name);
}
return id;
}
);
if (variableDeclarators.length) {
path
.getStatementParent()
.insertBefore(t.variableDeclaration('var', variableDeclarators));
}
return hoistedExpressions.map((id) => {
const binding = path.scope.getBinding(id.name);
if (binding && t.isVariableDeclarator(binding.path.node)) {
const matchPath = binding.path.get('init');
if (this.isMatch(matchPath)) {
return { fn: true, id: id.name };
}
}
const input = t.isStringLiteral(id)
? JSON.stringify(id.value)
: id.name;
return { fn: false, id: input };
});
},
_prepareTransform(path) {
const transformNode = path.node.tag.arguments[1];
if (!transformNode) return null;
if (t.isIdentifier(transformNode)) return transformNode.name;
const matchName = this.getMatchName(path);
const id = path.scope.generateUidIdentifier(`${matchName}_transform`);
const declarator = t.variableDeclarator(id, transformNode);
path
.getStatementParent()
.insertBefore(t.variableDeclaration('var', [declarator]));
return id.name;
},
minifyMatch(path) {
const quasis = path.node.quasi.quasis.map((x) =>
t.stringLiteral(x.value.cooked.replace(/\s*/g, ''))
);
const expressions = path.node.quasi.expressions;
const transform = this._prepareTransform(path);
path.replaceWith(
t.callExpression(path.node.tag, [
t.arrayExpression(quasis),
...expressions,
])
);
},
transformMatch(path) {
let name = path.node.tag.arguments[0];
if (!name) {
name = t.nullLiteral();
}
const quasis = path.node.quasi.quasis.map((x) => x.value.cooked);
const expressions = this._prepareExpressions(path);
const transform = this._prepareTransform(path);
let ast;
try {
ast = parse(quasis, expressions);
} catch (error) {
if (error.name !== 'SyntaxError') throw error;
throw path.get('quasi').buildCodeFrameError(error.message);
}
const code = astRoot(ast, '%%name%%', transform && '%%transform%%');
path.replaceWith(
template.expression(code)(transform ? { name, transform } : { name })
);
},
};
}
package/src/babel/__snapshots__/plugin.test.js.snap 000644 000000 000000 00000021721 14112462760 021051 0 ustar 00 000000 000000 // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`deduplicates hoisted expressions 1`] = `
"import { match, __pattern as _pattern } from \\"reghex\\";
const re = /1/;
const str = '1';
var _re_expression = _pattern(re),
_str_expression = _pattern(str);
const a = function (state) {
var y1 = state.y,
x1 = state.x;
var node = [];
var x;
if ((x = _re_expression(state)) != null) {
node.push(x);
} else {
state.y = y1;
state.x = x1;
return;
}
if ((x = _str_expression(state)) != null) {
node.push(x);
} else {
state.y = y1;
state.x = x1;
return;
}
if ('a') node.tag = 'a';
return node;
};
var _b_expression = _pattern('2');
const b = function (state) {
var y1 = state.y,
x1 = state.x;
var node = [];
var x;
if ((x = _re_expression(state)) != null) {
node.push(x);
} else {
state.y = y1;
state.x = x1;
return;
}
if ((x = _b_expression(state)) != null) {
node.push(x);
} else {
state.y = y1;
state.x = x1;
return;
}
if ('b') node.tag = 'b';
return node;
};"
`;
exports[`works together with @babel/plugin-transform-modules-commonjs 1`] = `
"\\"use strict\\";
var _reghex = require(\\"reghex\\");
var _node_expression = (0, _reghex.__pattern)(1),
_node_expression2 = (0, _reghex.__pattern)(2);
const node = function (state) {
var y1 = state.y,
x1 = state.x;
var node = [];
var x;
if ((x = _node_expression(state)) != null) {
node.push(x);
} else {
state.y = y1;
state.x = x1;
return;
}
if ((x = _node_expression2(state)) != null) {
node.push(x);
} else {
state.y = y1;
state.x = x1;
return;
}
if ('node') node.tag = 'node';
return node;
};"
`;
exports[`works while only minifying 1`] = `
"import { match } from 'reghex/macro';
const node = match('node')([\\"\\", \\"+|\\", \\"+(\\", \\"(\\", \\"?\\", \\"))*\\"], 1, 2, 3, 4, 5);"
`;
exports[`works with local recursion 1`] = `
"import { match as m, tag, __pattern as _pattern } from 'reghex';
var _inner_expression = _pattern(/inner/);
const inner = function (state) {
var y1 = state.y,
x1 = state.x;
var node = [];
var x;
if ((x = _inner_expression(state)) != null) {
node.push(x);
} else {
state.y = y1;
state.x = x1;
return;
}
if ('inner') node.tag = 'inner';
return node;
};
const node = function (state) {
var y1 = state.y,
x1 = state.x;
var node = [];
var x;
if ((x = inner(state)) != null) {
node.push(x);
} else {
state.y = y1;
state.x = x1;
return;
}
if ('node') node.tag = 'node';
return node;
};"
`;
exports[`works with nameless matchers 1`] = `
"import { match, __pattern as _pattern } from \\"reghex\\";
var _objectObject_expression = _pattern(1),
_objectObject_expression2 = _pattern(2),
_objectObject_expression3 = _pattern(3),
_objectObject_expression4 = _pattern(4),
_objectObject_expression5 = _pattern(5);
const node = function (state) {
var y1 = state.y,
x1 = state.x;
var node = [];
var x;
alt_2: {
block_2: {
var y2 = state.y,
x2 = state.x;
if ((x = _objectObject_expression(state)) != null) {
node.push(x);
} else {
state.y = y2;
state.x = x2;
break block_2;
}
group_2: for (;;) {
var y2 = state.y,
x2 = state.x;
if ((x = _objectObject_expression(state)) != null) {
node.push(x);
} else {
state.y = y2;
state.x = x2;
break group_2;
}
}
break alt_2;
}
if ((x = _objectObject_expression2(state)) != null) {
node.push(x);
} else {
state.y = y1;
state.x = x1;
return;
}
group_2: for (;;) {
var y2 = state.y,
x2 = state.x;
if ((x = _objectObject_expression2(state)) != null) {
node.push(x);
} else {
state.y = y2;
state.x = x2;
break group_2;
}
}
group_2: for (;;) {
var y2 = state.y,
x2 = state.x;
var ln2 = node.length;
if ((x = _objectObject_expression3(state)) != null) {
node.push(x);
} else {
state.y = y2;
state.x = x2;
node.length = ln2;
break group_2;
}
var y4 = state.y,
x4 = state.x;
if ((x = _objectObject_expression4(state)) != null) {
node.push(x);
} else {
state.y = y4;
state.x = x4;
}
if ((x = _objectObject_expression5(state)) != null) {
node.push(x);
} else {
state.y = y2;
state.x = x2;
node.length = ln2;
break group_2;
}
}
}
if (null) node.tag = null;
return node;
};"
`;
exports[`works with non-capturing groups 1`] = `
"import { match, __pattern as _pattern } from 'reghex';
var _node_expression = _pattern(1),
_node_expression2 = _pattern(2),
_node_expression3 = _pattern(3);
const node = function (state) {
var y1 = state.y,
x1 = state.x;
var node = [];
var x;
if ((x = _node_expression(state)) != null) {
node.push(x);
} else {
state.y = y1;
state.x = x1;
return;
}
var ln2 = node.length;
alt_3: {
block_3: {
var y3 = state.y,
x3 = state.x;
if ((x = _node_expression2(state)) != null) {
node.push(x);
} else {
state.y = y3;
state.x = x3;
node.length = ln2;
break block_3;
}
break alt_3;
}
if ((x = _node_expression3(state)) == null) {
state.y = y1;
state.x = x1;
node.length = ln2;
return;
}
group_3: for (;;) {
var y3 = state.y,
x3 = state.x;
if ((x = _node_expression3(state)) == null) {
state.y = y3;
state.x = x3;
break group_3;
}
}
}
if ('node') node.tag = 'node';
return node;
};"
`;
exports[`works with self-referential thunks 1`] = `
"import { match, tag, __pattern as _pattern } from 'reghex';
const inner = function (state) {
var y1 = state.y,
x1 = state.x;
var node = [];
var x;
if ((x = node(state)) != null) {
node.push(x);
} else {
state.y = y1;
state.x = x1;
return;
}
if ('inner') node.tag = 'inner';
return node;
};
const node = function (state) {
var y1 = state.y,
x1 = state.x;
var node = [];
var x;
if ((x = inner(state)) != null) {
node.push(x);
} else {
state.y = y1;
state.x = x1;
return;
}
if ('node') node.tag = 'node';
return node;
};"
`;
exports[`works with standard features 1`] = `
"import { match, __pattern as _pattern } from \\"reghex\\";
var _node_expression = _pattern(1),
_node_expression2 = _pattern(2),
_node_expression3 = _pattern(3),
_node_expression4 = _pattern(4),
_node_expression5 = _pattern(5);
const node = function (state) {
var y1 = state.y,
x1 = state.x;
var node = [];
var x;
alt_2: {
block_2: {
var y2 = state.y,
x2 = state.x;
if ((x = _node_expression(state)) != null) {
node.push(x);
} else {
state.y = y2;
state.x = x2;
break block_2;
}
group_2: for (;;) {
var y2 = state.y,
x2 = state.x;
if ((x = _node_expression(state)) != null) {
node.push(x);
} else {
state.y = y2;
state.x = x2;
break group_2;
}
}
break alt_2;
}
if ((x = _node_expression2(state)) != null) {
node.push(x);
} else {
state.y = y1;
state.x = x1;
return;
}
group_2: for (;;) {
var y2 = state.y,
x2 = state.x;
if ((x = _node_expression2(state)) != null) {
node.push(x);
} else {
state.y = y2;
state.x = x2;
break group_2;
}
}
group_2: for (;;) {
var y2 = state.y,
x2 = state.x;
var ln2 = node.length;
if ((x = _node_expression3(state)) != null) {
node.push(x);
} else {
state.y = y2;
state.x = x2;
node.length = ln2;
break group_2;
}
var y4 = state.y,
x4 = state.x;
if ((x = _node_expression4(state)) != null) {
node.push(x);
} else {
state.y = y4;
state.x = x4;
}
if ((x = _node_expression5(state)) != null) {
node.push(x);
} else {
state.y = y2;
state.x = x2;
node.length = ln2;
break group_2;
}
}
}
if ('node') node.tag = 'node';
return node;
};"
`;
exports[`works with transform functions 1`] = `
"import { match, __pattern as _pattern } from 'reghex';
var _inner_transform = x => x;
const first = function (state) {
var y1 = state.y,
x1 = state.x;
var node = [];
var x;
if ('inner') node.tag = 'inner';
return _inner_transform(node);
};
const transform = x => x;
const second = function (state) {
var y1 = state.y,
x1 = state.x;
var node = [];
var x;
if ('node') node.tag = 'node';
return transform(node);
};"
`;