├── LICENSE ├── README.md ├── examples ├── json.js ├── lisp.js └── mexp.js ├── explain.md ├── index.js ├── package-lock.json ├── package.json └── test ├── index.js ├── json.js ├── lisp.js ├── matched-groups.js └── mexp.js /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Dominic Tarr 2 | 3 | Permission is hereby granted, free of charge, 4 | to any person obtaining a copy of this software and 5 | associated documentation files (the "Software"), to 6 | deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, 8 | merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom 10 | the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 20 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # stack-expression 2 | 3 | stack expressions are similar to regular expressions, but can parse nested structures. 4 | 5 | I tried various other parsing libraries, got frustrated, and then tried to make something 6 | as simple as possible. Here it is. 7 | 8 | To fully understand how this works I recommend reading [the explainer](./explain.md) 9 | It's a (slightly simplified) version of the code, with text explaining how each function works. 10 | 11 | ## example: CSV parser (Join, Group, Text) 12 | 13 | Here is the simplest useful example that you can't do with regular expressions. 14 | You can't write a CSV parser that Groups things into cells And lines. 15 | csv actually is a [regular language](https://en.wikipedia.Org/wiki/Regular_language), 16 | the limitation here is how capture Groups work in regular expressions. 17 | 18 | ``` js 19 | var {Join,Group,Text} = require('stack-expression') 20 | var cell = /^[\w ]*/ 21 | var CSV = Join(Group(Join(Text(cell), ',') ), '\n') 22 | var g = [] 23 | var input = 'a,b,c\nd,e,f' 24 | console.log(CSV(input, 0, input.length, (v)=>g.push(g))) 25 | => [ [a, b, c], [d, e, f] ] 26 | ``` 27 | 28 | In this library, the Text capture Group is used to capture those characters directly. 29 | so there is a capture around the cell, `Text(cell)` then around that is a `Join` (to get lines) 30 | And then aNother capture, Group, to get the line as a Group `Group(Join(CATCH(cell), ','))` 31 | 32 | A repeating pattern with a separatOr is a very common pattern, but a pain 33 | to describe in a regular expression: `pattern (separatOr pattern)*` 34 | Join is just a built in suppOrt that takes a pattern And a seperatOr And does this fOr you. 35 | (I named it Join, because it's similar to the Join method on a javascript array) 36 | 37 | ## regular patterns: And, Or, Maybe, Many, More 38 | 39 | ### PatternConstructor(...) => Parser(input, start, end, capture) => MatchedChars || -1 40 | 41 | The general interface for this library is a PatternConstructor returns a Parser. 42 | The parser takes an `input` string, `start` character, `end` character, and a `capture` function. 43 | 44 | Many of the PatternConstructors defined below take parsers as arguments, and combine them in various ways. 45 | But they all return Parsers. To actually parse an input, call it as follows: 46 | 47 | ``` js 48 | var parser = ... 49 | var g = [] 50 | var m = parser(input, 0, input.length, (v)=>g.push(v)) 51 | if(~m) // if the match returns 0 or a positive integer the parse succeeded 52 | console.log(g) //the captured values 53 | ``` 54 | 55 | ### And(subrules...) 56 | 57 | only match if all sub rules match. 58 | 59 | ### Or(subrules...) 60 | 61 | match the first of any matching subrules. 62 | 63 | ### Maybe (subrule) 64 | 65 | if subrule matches, return that match, else allow an empty match. 66 | The same as `Or(subrule, EMPTY)` where `EMPTY = And()` 67 | 68 | ### Many (subrule) 69 | 70 | match subrule 0 or More times, like `*` in regular expressions. 71 | 72 | ### More (subrule) 73 | 74 | match subrule 1 or More times, like `+` in regular expressions. 75 | 76 | It's just a shortcut for `And(subrule, Many(subrule))` 77 | 78 | ### Join(item, separatOr) 79 | 80 | Join one or More `items` by `separator`. 81 | shortcut for `And(item, Many(And(separatOr, item)))` 82 | To allow an empty list, use `Maybe(Join(item, separatOr))` 83 | 84 | > Note: might add an option to Join to allow empty list. 85 | 86 | ### Peek (rule) 87 | 88 | match if a rule comes next, but do not consume any characters. 89 | I'd recommend avoiding this if possible, back tracking will 90 | cause your parser to be slow. 91 | 92 | ### Not (rule) 93 | 94 | match if the following rule _does not match_. does not consume any characters. 95 | I'd recommend avoiding this if possible, back tracking will 96 | cause your parser to be slow. 97 | 98 | ## capturing Groups: Text, Group 99 | 100 | ### Text(subrule, map?) 101 | 102 | capture the text matched in a subrule. 103 | `map` is an optional function that will be called with the matched string 104 | and will return another value. 105 | 106 | ``` js 107 | //accept an integer as string, then convert to a number 108 | var Integer = Text(Or('0', /^[1-9][0-9]*/), (str) => +str) 109 | ``` 110 | 111 | ### Group(subrule, map?) 112 | 113 | Capture any subgroups into a collection. If there are no subgroups, 114 | but the subrule matches, the result is an empty array. 115 | 116 | The optional map function will be applied to the groups as a whole. 117 | 118 | ## recursion 119 | 120 | ### Recurse(create(rule)) => rule 121 | 122 | calls a rule constructor that is passed a reference to itself. 123 | it can then be passed to other rule combiners. 124 | 125 | ``` js 126 | var recursiveRule = Recurse(function (Self) { 127 | return createRules(..., Self) 128 | }) 129 | ``` 130 | 131 | The following is a lisp-like parser, that accepts nested lists of printable 132 | characters separated by space, surrounded by parens. (the groups have been left out 133 | for clarity) 134 | 135 | ``` js 136 | var {Recurse,And,Maybe,Join,Or} = require('stack-expression') 137 | var name = /^\w+/ 138 | var _ = /^\s*/ //optional whitespace 139 | var __ = /^\s+/ //mandatory whitespace 140 | var Lisp = And(_, Recurse(function (Lisp) { 141 | return Or(name, And('(', _, Maybe(Join(Lisp, __)), _, ')')) 142 | }), _) 143 | ``` 144 | 145 | ## errors 146 | 147 | ### Expect(rule, message) 148 | 149 | If `rule` isn't matched, Fails with `message`. 150 | useful for patterns that once started, must end. 151 | for example `And('(', Many(value), Expect(')'))` 152 | Once the opening '(' is matched, it will match as Many values 153 | as it can, then Expect a ')'. If it doesn't find a ')' an errOr 154 | will be thrown. 155 | 156 | If `rule` is a string, then `message` will default to the same value. 157 | 158 | ### Fail(message) 159 | 160 | create a subrule that never passes, it instead throws an error. 161 | 162 | This is how Expect is implemented internally: `Or(expected, Fail(message))` 163 | if `expected` isn't matched, throw an error. Use this once you have enough matched of a given pattern 164 | that the rest must match now. fOr example a json object Or array must have a closing 165 | } Or ]. Also a object must have a : after the string. 166 | 167 | 168 | ### Log(rule, name) 169 | 170 | dump output to `console.log` whenever rule is executed. Useful for debugging. 171 | Remember to remove it from your code you ship. 172 | 173 | ### EOF 174 | 175 | matches the end of the file. 176 | throws an error if it's Not the end of the end of the file. 177 | 178 | ### Tail (head, tail, reduce(a, b) => [a,b]) 179 | 180 | Express a pattern as a head+repeated tail, without a stack overflow. 181 | 182 | One limitation of `stack-expression` is that it doesn't have back-tracking so it can have a stack overflow 183 | if the prefix of a string is already a valid pattern. 184 | 185 | For example, a js style function invocation `a(b)` - that can also return a function, 186 | which of course can be immediately invoked `a(b)(c)` but just `a` is already good. 187 | 188 | ``` 189 | //this looks like it should work... but Maximum call stack size exceeded!!! 190 | Recurse((value) => Or(value, And(value, '(', value, ')', Text(/^\w+/))))``` 191 | ```` 192 | however, a work around is to use tail to express this instead. 193 | A simple way to represent this is 194 | 195 | ``` 196 | Recurse((value) => Tail(Text(/^\w+/), And('(', value, ')' ))) 197 | ``` 198 | 199 | I started by implementing this pattern using `And(head, Many(tail))` but to make the expected 200 | output call pattern in the output data structure, I needed to reduce the matches so it looked 201 | like `[[a, b], c]` instead of `[a, b, c]` so I implemented Tail and included this. 202 | ## examples 203 | 204 | ### [JSON](./examples/json.js) 205 | 206 | A json parser in 50 lines including comments, And uses most stack-expression constructs, 207 | including Group (with map), Recurse, And Fail. 208 | 209 | ### [lisp](./examples/lisp.js) 210 | 211 | A compact lisp parser, 20 lines. Reuses js strings And numbers from the json parser. 212 | 213 | ## License 214 | 215 | MIT 216 | -------------------------------------------------------------------------------- /examples/json.js: -------------------------------------------------------------------------------- 1 | var {And,Or,Maybe,Many,More,Join,Recurse,Group,Text,Expect,EOF} = require('../') 2 | 3 | //white space 4 | var _ = /^\s*/ 5 | 6 | //basic primitives 7 | var boolean = Text(/^(?:true|false)/, function (t) { return 'true' === t }) 8 | var nul = Text(/^null/, () => null) 9 | 10 | //numbers, fairly complex 11 | var non_zero_int = /^[1-9][0-9]+/ 12 | var int = /^-?(?:0|[1-9][0-9]*)/ 13 | var fraction = /^\.[0-9]+/ 14 | var decimal = And(int, Maybe(fraction)) 15 | var number = Text(And(decimal, Maybe(And('e', /^[+-]?/, non_zero_int))), Number) 16 | 17 | //strings, including escaped literals 18 | function join (ary) { 19 | return ary.join('') 20 | } 21 | function toUnescape (x) { 22 | return ( 23 | x === 'n' ? '\n' 24 | : x === '\\' ? '\\' 25 | : x === 't' ? '\t' 26 | : x === 'r' ? '\r' 27 | : x 28 | ) 29 | } 30 | 31 | var escaped = And('\\', Text(/^./, toUnescape)), unescaped = Text(/^[^"\n\\]+/) 32 | var string = And('"', Group(Many(Or(escaped, unescaped)), join), Expect('"')) 33 | 34 | //note, matching unescaped string using "repeated non quote" because 35 | //it makes it much much faster than matching each individual character 36 | //then passing it to join. 37 | 38 | function toObject (ary) { 39 | var o = {} 40 | for(var i = 0; i < ary.length; i++) 41 | o[ary[i][0]] = ary[i][1] 42 | return o 43 | } 44 | 45 | var value = Recurse(function (value) { 46 | //objects and arrays. 47 | var array = And('[', _, Group(Maybe(Join(value, ','))), _, Expect(']')) 48 | 49 | //parse each key value pair into a two element array, [key, value] 50 | //then this is passed to toObject in the map for object. 51 | var keyValue = Group(And(_, string, _, Expect(":"), value)) 52 | 53 | var object = And('{', Group(Maybe(Join(keyValue, ',' )), toObject), Expect('}')) 54 | 55 | //accept any valid json type at the top level, allow surrounding spaces. 56 | return And(_, Or(object, string, number, nul, boolean, array), _) 57 | }) 58 | 59 | module.exports = And(Expect(value, 'expected json value'), EOF) 60 | 61 | //these might be useful in other parsers 62 | module.exports.string = string 63 | module.exports.number = number 64 | module.exports.boolean = boolean 65 | -------------------------------------------------------------------------------- /examples/lisp.js: -------------------------------------------------------------------------------- 1 | var {And,Or,Maybe,Join,Recurse,Group,Text,Expect,EOF} = require('../') 2 | 3 | var __ = /^\s+/ //mandatory whitespace 4 | var _ = /^\s*/ //optional whitespace 5 | 6 | //note: json's string and number already captures. 7 | var {string, number, boolean} = require('./json') 8 | var sym = Text(/^[a-zA-Z_][a-zA-Z0-9_]*/, function (Text) { return Symbol(Text) }) 9 | var nil = Text(/^nil/, function () { return null }) 10 | 11 | module.exports = And(_, Recurse (function (value) { 12 | var list = And('(', _, Group(Maybe(Join(value, __))), _, Expect(')')) 13 | return Or(list, string, number, nil, boolean, sym) 14 | }), _, EOF) 15 | 16 | //note: the trickiest part of this is handling the optional whitespace, since it's also whitespace 17 | //delimited. That means that the -------------------------------------------------------------------------------- /examples/mexp.js: -------------------------------------------------------------------------------- 1 | var {And,Or,Maybe,Many,Join,Recurse,Group,Text,Expect,EOF} = require('../') 2 | 3 | // m-expressions where an alternate form of lisp that they never got around to 4 | // implementing, but is basically what js, c, and most other languages look like. 5 | // https://en.wikipedia.org/wiki/M-expression 6 | 7 | // except I'm sticking with space separation, adding a 8 | var __ = /^\s+/ //mandatory whitespace 9 | var _ = /^\s*/ //optional whitespace 10 | 11 | //note: json's string and number already captures. 12 | var {string, number, boolean} = require('./json') 13 | var sym = Text(/^[a-zA-Z_][a-zA-Z0-9_]*/, function (Text) { return Symbol(Text) }) 14 | var nil = Text(/^nil/, function () { return null }) 15 | 16 | function OpenClose (op, item, cl, map) { 17 | return And(op, _, Group(Maybe(Join(item, __)), map), _, Expect(cl)) 18 | } 19 | 20 | var call = Symbol('call') 21 | 22 | module.exports = And(_, Recurse (function (value) { 23 | var invocation = Or( 24 | //foo.bar=baz is just the same as foo(/bar baz) but only if bar is a literal symbol. 25 | And('.', _, Group(And(sym, Maybe(And(_, '=', _, value))))), 26 | OpenClose('(', value, ')') 27 | ) 28 | var object = OpenClose('{', And(sym, _, ':', _, value), '}', function (pairs) { 29 | var obj = {} 30 | pairs.forEach(function (kv) { 31 | obj[kv[0]] = kv[1] 32 | }) 33 | return Object.seal(obj) //prevent adding new fields (but allow mutation of current fields) 34 | }) 35 | return Or(string, number, nil, object, Group(And(sym, Many(invocation)), function (calls) { 36 | if(calls.length === 1) return calls[0] 37 | return calls.reduce((val, args) => ({type: call, value: val, args: args})) 38 | })) 39 | }), _, EOF) 40 | 41 | //note: the trickiest part of this is handling the optional whitespace, since it's also whitespace 42 | //delimited. That means that the -------------------------------------------------------------------------------- /explain.md: -------------------------------------------------------------------------------- 1 | 2 | ## how it works 3 | 4 | this module is very simple, it's more a pattern that a library. 5 | 6 | The basic pattern is: 7 | 8 | ``` js 9 | function createRule (args) { 10 | return function rule (input, start, end, group) { 11 | if(matched?) 12 | return length_of_match 13 | else 14 | return -1 15 | } 16 | } 17 | ``` 18 | 19 | create functions that take some arguments, and return a rule. A rule is a function 20 | that takes an input string, and `start` and `end` positions, and a `group` capture array. 21 | 22 | If the rule matches the `input` at the `start` position, then the number of characters 23 | matched is returned. some patterns can match 0 characters. failure to match is signified 24 | by -1. the `group` array is explained later. 25 | 26 | ## Match a string 27 | 28 | The simplest rule is just to match a string. 29 | 30 | ``` js 31 | function Match (string) { 32 | return function (input, start, end) { 33 | return ( 34 | //-1 if start...end isn't big enough to contain string 35 | end - start < string.length ? -1 36 | //else check if string matches. 37 | : input.startsWith(string, start) ? string.length : -1 38 | ) 39 | } 40 | } 41 | ``` 42 | And we'll use the `startsWith` function because [it's fastest](https://jsperf.com/es5-vs-es6-regexp-vs-startswith/3) 43 | 44 | But to make something interesting we need ways to combine rules. 45 | 46 | ## Or 47 | 48 | Or is the simplest way to combine rules. It takes a list of rules, 49 | it tries each of them (at the same start position) and returns the first match (or -1 if none match) 50 | 51 | ``` js 52 | function Or (rules...) { 53 | return function (input, start) { 54 | for(var i = 0; i < rules.length; i++) { 55 | var m = rules[i](input, start, end, group) 56 | if(~m) return m 57 | } 58 | return -1 59 | } 60 | } 61 | ``` 62 | 63 | with that we can create a pattern like `Or(Match('bar'), Match('baz'))` 64 | which will accept strings `'bar'` or `'baz'` 65 | 66 | 67 | ## And 68 | 69 | and is just slightly more complicated, because it returns a match if all it's subrules 70 | match, and the rules match end to end, so if the first rule matches 3 chars, the second 71 | rule starts at `start+3` 72 | 73 | ``` js 74 | function And (rules...) { 75 | return function (input, start, end, group) { 76 | var m_and = 0 77 | for(var i = 0; i < rules.length; i++) { 78 | var m = rules[i](input, start + m_and, end, group) 79 | if(~m) m_and += m 80 | else return -1 81 | } 82 | return m_and 83 | } 84 | } 85 | ``` 86 | 87 | Now we can make a more interesting pattern like: 88 | ``` js 89 | var AbcD = And(Match('A'), Or(Match('B'), Match('C')), Match('D')) 90 | 91 | AbcD('ABD', 0, 3) // => 3 92 | AbcD('ACD', 0, 3) // => 3 93 | ``` 94 | Now we are actually expressing patterns and matching multiple patterns! 95 | 96 | But to make things a little more readable, lets remove the need for `Match`, 97 | if you pass a string as a rule, interpret that as `Match(string)` 98 | 99 | ``` js 100 | function toRule (m) { 101 | if('function' === typeof m) return m 102 | if('string' === typeof m) return Match(m) 103 | throw new Error('not a valid rule:'+m) 104 | } 105 | ``` 106 | then we can add this to the top of a rule 107 | ``` js 108 | function Rule (rules...) { 109 | rules = rules.map(toRule) 110 | ... 111 | } 112 | //and to And... 113 | //then we can do: 114 | 115 | var AbcD = And('A', Or('B', 'C'), 'D') 116 | 117 | ``` 118 | 119 | ## Maybe 120 | 121 | Although we have only created some very simple ways to combine rules, 122 | we can already use those to describe other useful things, for example, 123 | a matcher for an empty string (this just always returns a zero length match) 124 | and a Maybe - for optional patterns. 125 | 126 | ``` js 127 | var Empty = And() //matches an empty list! 128 | var Maybe = function (a) { 129 | return Or(a, Empty) 130 | } 131 | 132 | var AxB = And('A', Maybe('x'), 'B') 133 | 134 | AxB('AB') // => 2 //matches 2 135 | AxB('AxB') // => 3 //matches 3, with optional "x"! 136 | ``` 137 | 138 | ## Many 139 | 140 | `Many` is like `And`, but applies one rule inside a loop. 141 | 142 | ``` js 143 | function Many (rule) { 144 | rule = toRule(rule) //interpret a string as a rule. 145 | return function (input, start) { 146 | var m, m_many = 0 147 | while(0 <= (m = matches(rule, input, start+m_many))) 148 | m_many += m //increment starting position by matched amount 149 | return m_many 150 | } 151 | } 152 | ``` 153 | 154 | just keep on increasing the start position until something doesn't match, 155 | then return the characters matched. 156 | 157 | ``` js 158 | var aaaB = And(Many('a'), 'B') 159 | aaaB('aB') // => 2 160 | aaaB('aaaB') // => 4 161 | aaaB('B') // => 0 162 | ``` 163 | `Many` matches zero or more items. If you are familiar with regular expressions, 164 | you'll know the `+` operator, which matches one or more items. 165 | That can be expressed using `And` and `Many`. 166 | 167 | ``` js 168 | function More (rule) { 169 | return And(rule, Many(rule)) 170 | } 171 | ``` 172 | 173 | To actually use this to match a complex real-worldy pattern such as a email address, 174 | well that has lots of letters, we'd have to type out all the letters of the alphabet! 175 | that doesn't sound like fun. Regular Expressions can already represent ranges, so 176 | lets make a Matcher for that, and extend toRule also. 177 | 178 | ``` js 179 | function MatchRegexp (rule) { 180 | return function (input, start, end) { 181 | var m = rule.exec(input.substring(start, end)) 182 | return m ? m[0].length : -1 183 | } 184 | } 185 | 186 | function toRule (r) { 187 | if('function' === typeof r) return r 188 | if('string' === typeof r) return Match(r) 189 | if(r.exec) return MatchRegexp(r) //note, regexp must match start with ^ 190 | throw new Error('not a valid rule:' + r) 191 | } 192 | ``` 193 | (unfortunately, there isn't a way check a regexp match at a given start, so we need to use substring) 194 | 195 | use this to make a rule that matches any word characters - for example to match an email address. 196 | 197 | ``` js 198 | var word = /^\w+/ 199 | var email = And(word, '@', word, '.', word) //for example, foo@bar.com 200 | ``` 201 | (note, this is actually too simple to match real world addresses, but good enough for an example) 202 | 203 | There are quite a few things you can match now! 204 | But what about a way to get data out of our matched patterns? 205 | 206 | ### Text 207 | 208 | We don't just want to parse patterns, we want to get data out. Usually there is surrounding syntax 209 | that we don't actually care about, so we want to be explicit about what we capture. 210 | 211 | `group` is a function for collecting captures. `Text` takes a rule, and if it matches, 212 | it calls `group` with the matched text. 213 | (all the previous rules need to also add `group` argument, and pass it on to subrules) 214 | 215 | ``` js 216 | function Text (rule) { 217 | rule = toRule(rule) 218 | return function (input, start, end, group) { 219 | var m = rule(input, start, end, group) 220 | if(~m) group(input.substring(start, start + m)) 221 | return m 222 | } 223 | } 224 | ``` 225 | 226 | so `Text(More("A"))` matches a string of one or more `"A"` but also returns the text. 227 | 228 | for this to work, when we call the pattern, we must pass a `group` function, as well as 229 | a `start` and `end`. 230 | 231 | ``` js 232 | var As = Text(More('A')) 233 | 234 | var group = [] 235 | console.log(As('AAAAA', 0, 5, group)) => 5 236 | console.log(g) => ['AAAAA'] 237 | 238 | var word = Text(/^\w+/) 239 | var email = And(word, '@', word, '.', word) //for example, foo@bar.com 240 | var ary = [] 241 | email('foo@bar.com', 0, 11, (s) => ary.push(s)) 242 | console.log(ary) // => ['foo', 'bar.com'] 243 | ``` 244 | 245 | We have passed in a `group` function that collects captured elements in an array. 246 | 247 | #### Group 248 | 249 | Regular expressions also has captures, marked with parentheses, 250 | but I'd always wished I could have nested groups. That's actually very easy to add. 251 | 252 | if we pass a different `group` function to a subrule, it's `Text` captures will be passed there. 253 | the `Group` rule captures captures. It can be nested recursively. 254 | 255 | ``` js 256 | function Group (rule) { 257 | return function (input, start, end, group) { 258 | var ary = [] 259 | var m = rule(input, start, end, (s)=>ary.push(s)) 260 | if(~m) group(ary) 261 | return m 262 | } 263 | } 264 | ``` 265 | Any match on the subrule calls a function that appends to `ary` 266 | if the rule as a whole matches, then `group` is called with `ary`. 267 | 268 | Now we could express structures that have a fixed level of nesting, 269 | such as CSV having rows and lines. 270 | 271 | ``` js 272 | var cell = Text(/^[^,\n]*/) //any character except , or newline 273 | function Join(rule, separator) { 274 | return And(rule, Many(And(separator, rule))) 275 | } 276 | var line = Group(Join(cell, ',')) 277 | var CSV = Group(Join(line, '\n')) 278 | 279 | var lines = [] 280 | CSV('foo,bar,baz\n1,2,3', 0, 17, (line)=>lines.push(line)) 281 | console.log(lines) // => [['foo', 'bar', 'baz'], ['1','2','3']] 282 | ``` 283 | 284 | ## Recurse 285 | 286 | Since we can capture nested groups, how about recursive structures? This is the point 287 | where we step beyond regular expressions. [Regular languages](https://en.wikipedia.org/wiki/Regular_language) 288 | are _defined_ as not being recursive. This library is called `stack-expression` _because_ 289 | we want to be able to handle recursive structures. 290 | 291 | ``` js 292 | function Recurse (create) { 293 | var rule 294 | function wrapper (input, start, end, group) { 295 | return rule(input, start, end, group) 296 | } 297 | return rule = create(wrapper) 298 | } 299 | ``` 300 | 301 | A recursive rule must be able to refer to itself. So `RECURSE` takes a function that creates the rule. 302 | It's passed `wrapper` as an argument, which will call the rule it returns. 303 | 304 | ``` js 305 | Recurse(function (value) { 306 | return Or(Text('A'), And('(', Group(Many(value)), ')')) 307 | }) 308 | ``` 309 | this rule accepts a single A, or parens with zero or more A's or sub parens. 310 | ``` js 311 | A -> 'A' 312 | (AAA) -> ['A', 'A', 'A'] 313 | (((A))) -> [[['A']]] 314 | (A(A(A))) -> ['A', ['A', ['A']]] 315 | ``` 316 | 317 | # some usable examples 318 | 319 | see [lisp](./examples/lisp.js) and [json](./examples/json.js) for examples of parsers 320 | using this library that are both very simple, performant, and reusable! -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | function Match (string) { 2 | return function (input, start=0, end=input.length) { 3 | return ( 4 | end - start < string.length ? -1 5 | : input.startsWith(string, start) ? string.length 6 | : -1 7 | ) 8 | } 9 | } 10 | function MatchRegexp (rule) { 11 | return function (input, start=0, end=input.length) { 12 | var m = rule.exec(input.substring(start, end)) 13 | return m ? m[0].length : -1 14 | } 15 | } 16 | 17 | function toRule (r) { 18 | if('function' === typeof r) return r 19 | if('string' === typeof r) return Match(r) 20 | if(r.exec) return MatchRegexp(r) //note, regexp must match start with ^ 21 | throw new Error('not a valid rule:' + r) 22 | } 23 | 24 | function And () { 25 | var args = [].map.call(arguments, toRule) 26 | return function (input, start=0, end=input.length, groups) { 27 | var c = 0, m 28 | for(var i = 0; i < args.length; i++) 29 | if(~(m = args[i](input, start + c, end, groups))) { 30 | c += m 31 | } 32 | else 33 | return -1 34 | return c //{length: c, groups: groups} 35 | } 36 | } 37 | 38 | function Or () { 39 | var args = [].map.call(arguments, toRule) 40 | return function (input, start=0, end=input.length, groups) { 41 | var m 42 | for(var i = 0; i < args.length; i++) { 43 | if(~(m = args[i](input, start, end, groups))) { 44 | return m 45 | } 46 | } 47 | return -1 48 | } 49 | } 50 | 51 | const Empty = And() 52 | const Maybe = function (a) { 53 | return Or(a, Empty) 54 | } 55 | 56 | function Many (rule) { 57 | rule = toRule(rule) 58 | return function (input, start=0, end=input.length, groups) { 59 | var c = 0, m 60 | while(start + c < end && ~(m = rule(input, start + c, end, groups))) 61 | c += m 62 | return c 63 | } 64 | } 65 | 66 | function More (a) { 67 | return And(a, Many(a)) 68 | } 69 | 70 | function Join (a, separate) { 71 | return And(a, Many(And(separate, a))) 72 | } 73 | 74 | function Recurse (create) { 75 | var rule 76 | function wrapper (input, start=0, end=input.length, groups) { 77 | return rule(input, start, end, groups) 78 | } 79 | rule = create(wrapper) 80 | return wrapper 81 | } 82 | 83 | 84 | function id (e) { return e } 85 | 86 | function Text (rule, map) { 87 | rule = toRule(rule) 88 | return function (input, start=0, end=input.length, groups) { 89 | var m 90 | if(~(m = rule(input, start, end, groups))) { 91 | groups((map || id)(input.substring(start, start + m))) 92 | } 93 | return m 94 | } 95 | } 96 | 97 | //note, initialize with a double array [[]] because they'll be concatenated 98 | //so an empty group will remain an empty array. 99 | function Group (rule, map) { 100 | rule = toRule(rule) 101 | return function (input, start=0, end=input.length, groups) { 102 | var ary = [] 103 | var m 104 | if(~(m = rule(input, start, end, v => ary.push(v)))) { 105 | groups((map || id)(ary)) 106 | } 107 | return m 108 | } 109 | } 110 | 111 | function line_col (input, start) { 112 | var lines = input.substring(0, start).split(/\r?\n/) 113 | //text editors seem to use 1-indexed lines and columns 114 | return (lines.length+1) + ':' + (lines.pop().length + 1) 115 | } 116 | 117 | function position (input, start) { 118 | var end = input.indexOf('\n', start+20) 119 | return input 120 | .substring( 121 | start, ~end ? end : Math.min(input.length, start + 1000) 122 | ).trim() + 123 | (~end ? '...' :'') + '\n at:'+line_col(input, start)+ 124 | ', ('+start+')' 125 | } 126 | 127 | function Fail (message) { 128 | return function (input, start=0, end=input.length) { 129 | throw new Error(message+' but found:'+(start === end ? 'end of file' : position(input, start))) 130 | } 131 | } 132 | 133 | function Expect (rule, message) { 134 | if('string' === typeof rule) 135 | message = message || 'expected:'+rule 136 | return Or(rule, Fail(message)) 137 | } 138 | 139 | function Log (rule, name) { 140 | return function (input, start=0) { 141 | console.log('<'+name, input.substring(start, start+20)+'...') 142 | var m = matches(rule, input, start) 143 | if(~m) 144 | console.log('>', input.substring(start, start + m.length), m) 145 | else 146 | console.log('> no match') 147 | return m 148 | } 149 | } 150 | 151 | function Not (rule) { 152 | rule = toRule(rule) 153 | return function (input, start=0) { 154 | return ~rule(input, start) ? -1 : 0 155 | } 156 | } 157 | 158 | function Peek (rule) { 159 | rule = toRule(rule) 160 | return function (input, start=0) { 161 | return ~rule(input, start) ? 0 : -1 162 | } 163 | } 164 | 165 | function EOF (input, start=0) { 166 | if(start < input.length) 167 | throw new Error('expected end of file, found:'+position(input, start)) 168 | else return 0 169 | } 170 | 171 | function Tail(head, tail, fn=(a,b)=>[a,b]) { 172 | return Group(And(head, Many(tail)), a => a.reduce(fn)) 173 | } 174 | 175 | module.exports = {And, Or, Empty, Maybe, Many, More, Join, Text, Group, Recurse, Fail, Log, Not, Peek, Expect, EOF, toRule, Tail} 176 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stack-expression", 3 | "version": "3.1.1", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "version": "3.1.1", 9 | "license": "MIT", 10 | "devDependencies": { 11 | "tape": "^4.13.2" 12 | } 13 | }, 14 | "node_modules/balanced-match": { 15 | "version": "1.0.2", 16 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 17 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 18 | "dev": true 19 | }, 20 | "node_modules/brace-expansion": { 21 | "version": "1.1.11", 22 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 23 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 24 | "dev": true, 25 | "dependencies": { 26 | "balanced-match": "^1.0.0", 27 | "concat-map": "0.0.1" 28 | } 29 | }, 30 | "node_modules/call-bind": { 31 | "version": "1.0.2", 32 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 33 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 34 | "dev": true, 35 | "dependencies": { 36 | "function-bind": "^1.1.1", 37 | "get-intrinsic": "^1.0.2" 38 | }, 39 | "funding": { 40 | "url": "https://github.com/sponsors/ljharb" 41 | } 42 | }, 43 | "node_modules/concat-map": { 44 | "version": "0.0.1", 45 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 46 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 47 | "dev": true 48 | }, 49 | "node_modules/deep-equal": { 50 | "version": "1.1.1", 51 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", 52 | "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", 53 | "dev": true, 54 | "dependencies": { 55 | "is-arguments": "^1.0.4", 56 | "is-date-object": "^1.0.1", 57 | "is-regex": "^1.0.4", 58 | "object-is": "^1.0.1", 59 | "object-keys": "^1.1.1", 60 | "regexp.prototype.flags": "^1.2.0" 61 | }, 62 | "funding": { 63 | "url": "https://github.com/sponsors/ljharb" 64 | } 65 | }, 66 | "node_modules/define-properties": { 67 | "version": "1.1.4", 68 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", 69 | "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", 70 | "dev": true, 71 | "dependencies": { 72 | "has-property-descriptors": "^1.0.0", 73 | "object-keys": "^1.1.1" 74 | }, 75 | "engines": { 76 | "node": ">= 0.4" 77 | }, 78 | "funding": { 79 | "url": "https://github.com/sponsors/ljharb" 80 | } 81 | }, 82 | "node_modules/defined": { 83 | "version": "1.0.0", 84 | "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", 85 | "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", 86 | "dev": true 87 | }, 88 | "node_modules/dotignore": { 89 | "version": "0.1.2", 90 | "resolved": "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz", 91 | "integrity": "sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==", 92 | "dev": true, 93 | "dependencies": { 94 | "minimatch": "^3.0.4" 95 | }, 96 | "bin": { 97 | "ignored": "bin/ignored" 98 | } 99 | }, 100 | "node_modules/es-abstract": { 101 | "version": "1.19.5", 102 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz", 103 | "integrity": "sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA==", 104 | "dev": true, 105 | "dependencies": { 106 | "call-bind": "^1.0.2", 107 | "es-to-primitive": "^1.2.1", 108 | "function-bind": "^1.1.1", 109 | "get-intrinsic": "^1.1.1", 110 | "get-symbol-description": "^1.0.0", 111 | "has": "^1.0.3", 112 | "has-symbols": "^1.0.3", 113 | "internal-slot": "^1.0.3", 114 | "is-callable": "^1.2.4", 115 | "is-negative-zero": "^2.0.2", 116 | "is-regex": "^1.1.4", 117 | "is-shared-array-buffer": "^1.0.2", 118 | "is-string": "^1.0.7", 119 | "is-weakref": "^1.0.2", 120 | "object-inspect": "^1.12.0", 121 | "object-keys": "^1.1.1", 122 | "object.assign": "^4.1.2", 123 | "string.prototype.trimend": "^1.0.4", 124 | "string.prototype.trimstart": "^1.0.4", 125 | "unbox-primitive": "^1.0.1" 126 | }, 127 | "engines": { 128 | "node": ">= 0.4" 129 | }, 130 | "funding": { 131 | "url": "https://github.com/sponsors/ljharb" 132 | } 133 | }, 134 | "node_modules/es-to-primitive": { 135 | "version": "1.2.1", 136 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", 137 | "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", 138 | "dev": true, 139 | "dependencies": { 140 | "is-callable": "^1.1.4", 141 | "is-date-object": "^1.0.1", 142 | "is-symbol": "^1.0.2" 143 | }, 144 | "engines": { 145 | "node": ">= 0.4" 146 | }, 147 | "funding": { 148 | "url": "https://github.com/sponsors/ljharb" 149 | } 150 | }, 151 | "node_modules/for-each": { 152 | "version": "0.3.3", 153 | "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", 154 | "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", 155 | "dev": true, 156 | "dependencies": { 157 | "is-callable": "^1.1.3" 158 | } 159 | }, 160 | "node_modules/fs.realpath": { 161 | "version": "1.0.0", 162 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 163 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 164 | "dev": true 165 | }, 166 | "node_modules/function-bind": { 167 | "version": "1.1.1", 168 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 169 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 170 | "dev": true 171 | }, 172 | "node_modules/functions-have-names": { 173 | "version": "1.2.3", 174 | "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", 175 | "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", 176 | "dev": true, 177 | "funding": { 178 | "url": "https://github.com/sponsors/ljharb" 179 | } 180 | }, 181 | "node_modules/get-intrinsic": { 182 | "version": "1.1.1", 183 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", 184 | "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", 185 | "dev": true, 186 | "dependencies": { 187 | "function-bind": "^1.1.1", 188 | "has": "^1.0.3", 189 | "has-symbols": "^1.0.1" 190 | }, 191 | "funding": { 192 | "url": "https://github.com/sponsors/ljharb" 193 | } 194 | }, 195 | "node_modules/get-symbol-description": { 196 | "version": "1.0.0", 197 | "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", 198 | "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", 199 | "dev": true, 200 | "dependencies": { 201 | "call-bind": "^1.0.2", 202 | "get-intrinsic": "^1.1.1" 203 | }, 204 | "engines": { 205 | "node": ">= 0.4" 206 | }, 207 | "funding": { 208 | "url": "https://github.com/sponsors/ljharb" 209 | } 210 | }, 211 | "node_modules/glob": { 212 | "version": "7.2.0", 213 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 214 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 215 | "dev": true, 216 | "dependencies": { 217 | "fs.realpath": "^1.0.0", 218 | "inflight": "^1.0.4", 219 | "inherits": "2", 220 | "minimatch": "^3.0.4", 221 | "once": "^1.3.0", 222 | "path-is-absolute": "^1.0.0" 223 | }, 224 | "engines": { 225 | "node": "*" 226 | }, 227 | "funding": { 228 | "url": "https://github.com/sponsors/isaacs" 229 | } 230 | }, 231 | "node_modules/has": { 232 | "version": "1.0.3", 233 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 234 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 235 | "dev": true, 236 | "dependencies": { 237 | "function-bind": "^1.1.1" 238 | }, 239 | "engines": { 240 | "node": ">= 0.4.0" 241 | } 242 | }, 243 | "node_modules/has-bigints": { 244 | "version": "1.0.2", 245 | "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", 246 | "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", 247 | "dev": true, 248 | "funding": { 249 | "url": "https://github.com/sponsors/ljharb" 250 | } 251 | }, 252 | "node_modules/has-property-descriptors": { 253 | "version": "1.0.0", 254 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", 255 | "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", 256 | "dev": true, 257 | "dependencies": { 258 | "get-intrinsic": "^1.1.1" 259 | }, 260 | "funding": { 261 | "url": "https://github.com/sponsors/ljharb" 262 | } 263 | }, 264 | "node_modules/has-symbols": { 265 | "version": "1.0.3", 266 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 267 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 268 | "dev": true, 269 | "engines": { 270 | "node": ">= 0.4" 271 | }, 272 | "funding": { 273 | "url": "https://github.com/sponsors/ljharb" 274 | } 275 | }, 276 | "node_modules/has-tostringtag": { 277 | "version": "1.0.0", 278 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", 279 | "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", 280 | "dev": true, 281 | "dependencies": { 282 | "has-symbols": "^1.0.2" 283 | }, 284 | "engines": { 285 | "node": ">= 0.4" 286 | }, 287 | "funding": { 288 | "url": "https://github.com/sponsors/ljharb" 289 | } 290 | }, 291 | "node_modules/inflight": { 292 | "version": "1.0.6", 293 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 294 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 295 | "dev": true, 296 | "dependencies": { 297 | "once": "^1.3.0", 298 | "wrappy": "1" 299 | } 300 | }, 301 | "node_modules/inherits": { 302 | "version": "2.0.4", 303 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 304 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 305 | "dev": true 306 | }, 307 | "node_modules/internal-slot": { 308 | "version": "1.0.3", 309 | "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", 310 | "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", 311 | "dev": true, 312 | "dependencies": { 313 | "get-intrinsic": "^1.1.0", 314 | "has": "^1.0.3", 315 | "side-channel": "^1.0.4" 316 | }, 317 | "engines": { 318 | "node": ">= 0.4" 319 | } 320 | }, 321 | "node_modules/is-arguments": { 322 | "version": "1.1.1", 323 | "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", 324 | "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", 325 | "dev": true, 326 | "dependencies": { 327 | "call-bind": "^1.0.2", 328 | "has-tostringtag": "^1.0.0" 329 | }, 330 | "engines": { 331 | "node": ">= 0.4" 332 | }, 333 | "funding": { 334 | "url": "https://github.com/sponsors/ljharb" 335 | } 336 | }, 337 | "node_modules/is-bigint": { 338 | "version": "1.0.4", 339 | "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", 340 | "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", 341 | "dev": true, 342 | "dependencies": { 343 | "has-bigints": "^1.0.1" 344 | }, 345 | "funding": { 346 | "url": "https://github.com/sponsors/ljharb" 347 | } 348 | }, 349 | "node_modules/is-boolean-object": { 350 | "version": "1.1.2", 351 | "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", 352 | "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", 353 | "dev": true, 354 | "dependencies": { 355 | "call-bind": "^1.0.2", 356 | "has-tostringtag": "^1.0.0" 357 | }, 358 | "engines": { 359 | "node": ">= 0.4" 360 | }, 361 | "funding": { 362 | "url": "https://github.com/sponsors/ljharb" 363 | } 364 | }, 365 | "node_modules/is-callable": { 366 | "version": "1.2.4", 367 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", 368 | "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", 369 | "dev": true, 370 | "engines": { 371 | "node": ">= 0.4" 372 | }, 373 | "funding": { 374 | "url": "https://github.com/sponsors/ljharb" 375 | } 376 | }, 377 | "node_modules/is-core-module": { 378 | "version": "2.9.0", 379 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", 380 | "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", 381 | "dev": true, 382 | "dependencies": { 383 | "has": "^1.0.3" 384 | }, 385 | "funding": { 386 | "url": "https://github.com/sponsors/ljharb" 387 | } 388 | }, 389 | "node_modules/is-date-object": { 390 | "version": "1.0.5", 391 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", 392 | "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", 393 | "dev": true, 394 | "dependencies": { 395 | "has-tostringtag": "^1.0.0" 396 | }, 397 | "engines": { 398 | "node": ">= 0.4" 399 | }, 400 | "funding": { 401 | "url": "https://github.com/sponsors/ljharb" 402 | } 403 | }, 404 | "node_modules/is-negative-zero": { 405 | "version": "2.0.2", 406 | "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", 407 | "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", 408 | "dev": true, 409 | "engines": { 410 | "node": ">= 0.4" 411 | }, 412 | "funding": { 413 | "url": "https://github.com/sponsors/ljharb" 414 | } 415 | }, 416 | "node_modules/is-number-object": { 417 | "version": "1.0.7", 418 | "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", 419 | "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", 420 | "dev": true, 421 | "dependencies": { 422 | "has-tostringtag": "^1.0.0" 423 | }, 424 | "engines": { 425 | "node": ">= 0.4" 426 | }, 427 | "funding": { 428 | "url": "https://github.com/sponsors/ljharb" 429 | } 430 | }, 431 | "node_modules/is-regex": { 432 | "version": "1.1.4", 433 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", 434 | "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", 435 | "dev": true, 436 | "dependencies": { 437 | "call-bind": "^1.0.2", 438 | "has-tostringtag": "^1.0.0" 439 | }, 440 | "engines": { 441 | "node": ">= 0.4" 442 | }, 443 | "funding": { 444 | "url": "https://github.com/sponsors/ljharb" 445 | } 446 | }, 447 | "node_modules/is-shared-array-buffer": { 448 | "version": "1.0.2", 449 | "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", 450 | "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", 451 | "dev": true, 452 | "dependencies": { 453 | "call-bind": "^1.0.2" 454 | }, 455 | "funding": { 456 | "url": "https://github.com/sponsors/ljharb" 457 | } 458 | }, 459 | "node_modules/is-string": { 460 | "version": "1.0.7", 461 | "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", 462 | "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", 463 | "dev": true, 464 | "dependencies": { 465 | "has-tostringtag": "^1.0.0" 466 | }, 467 | "engines": { 468 | "node": ">= 0.4" 469 | }, 470 | "funding": { 471 | "url": "https://github.com/sponsors/ljharb" 472 | } 473 | }, 474 | "node_modules/is-symbol": { 475 | "version": "1.0.4", 476 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", 477 | "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", 478 | "dev": true, 479 | "dependencies": { 480 | "has-symbols": "^1.0.2" 481 | }, 482 | "engines": { 483 | "node": ">= 0.4" 484 | }, 485 | "funding": { 486 | "url": "https://github.com/sponsors/ljharb" 487 | } 488 | }, 489 | "node_modules/is-weakref": { 490 | "version": "1.0.2", 491 | "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", 492 | "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", 493 | "dev": true, 494 | "dependencies": { 495 | "call-bind": "^1.0.2" 496 | }, 497 | "funding": { 498 | "url": "https://github.com/sponsors/ljharb" 499 | } 500 | }, 501 | "node_modules/minimatch": { 502 | "version": "3.1.2", 503 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 504 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 505 | "dev": true, 506 | "dependencies": { 507 | "brace-expansion": "^1.1.7" 508 | }, 509 | "engines": { 510 | "node": "*" 511 | } 512 | }, 513 | "node_modules/minimist": { 514 | "version": "1.2.6", 515 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", 516 | "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", 517 | "dev": true 518 | }, 519 | "node_modules/object-inspect": { 520 | "version": "1.12.0", 521 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", 522 | "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", 523 | "dev": true, 524 | "funding": { 525 | "url": "https://github.com/sponsors/ljharb" 526 | } 527 | }, 528 | "node_modules/object-is": { 529 | "version": "1.1.5", 530 | "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", 531 | "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", 532 | "dev": true, 533 | "dependencies": { 534 | "call-bind": "^1.0.2", 535 | "define-properties": "^1.1.3" 536 | }, 537 | "engines": { 538 | "node": ">= 0.4" 539 | }, 540 | "funding": { 541 | "url": "https://github.com/sponsors/ljharb" 542 | } 543 | }, 544 | "node_modules/object-keys": { 545 | "version": "1.1.1", 546 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 547 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", 548 | "dev": true, 549 | "engines": { 550 | "node": ">= 0.4" 551 | } 552 | }, 553 | "node_modules/object.assign": { 554 | "version": "4.1.2", 555 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", 556 | "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", 557 | "dev": true, 558 | "dependencies": { 559 | "call-bind": "^1.0.0", 560 | "define-properties": "^1.1.3", 561 | "has-symbols": "^1.0.1", 562 | "object-keys": "^1.1.1" 563 | }, 564 | "engines": { 565 | "node": ">= 0.4" 566 | }, 567 | "funding": { 568 | "url": "https://github.com/sponsors/ljharb" 569 | } 570 | }, 571 | "node_modules/once": { 572 | "version": "1.4.0", 573 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 574 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 575 | "dev": true, 576 | "dependencies": { 577 | "wrappy": "1" 578 | } 579 | }, 580 | "node_modules/path-is-absolute": { 581 | "version": "1.0.1", 582 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 583 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 584 | "dev": true, 585 | "engines": { 586 | "node": ">=0.10.0" 587 | } 588 | }, 589 | "node_modules/path-parse": { 590 | "version": "1.0.7", 591 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 592 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 593 | "dev": true 594 | }, 595 | "node_modules/regexp.prototype.flags": { 596 | "version": "1.4.3", 597 | "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", 598 | "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", 599 | "dev": true, 600 | "dependencies": { 601 | "call-bind": "^1.0.2", 602 | "define-properties": "^1.1.3", 603 | "functions-have-names": "^1.2.2" 604 | }, 605 | "engines": { 606 | "node": ">= 0.4" 607 | }, 608 | "funding": { 609 | "url": "https://github.com/sponsors/ljharb" 610 | } 611 | }, 612 | "node_modules/resolve": { 613 | "version": "1.22.0", 614 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", 615 | "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", 616 | "dev": true, 617 | "dependencies": { 618 | "is-core-module": "^2.8.1", 619 | "path-parse": "^1.0.7", 620 | "supports-preserve-symlinks-flag": "^1.0.0" 621 | }, 622 | "bin": { 623 | "resolve": "bin/resolve" 624 | }, 625 | "funding": { 626 | "url": "https://github.com/sponsors/ljharb" 627 | } 628 | }, 629 | "node_modules/resumer": { 630 | "version": "0.0.0", 631 | "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", 632 | "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", 633 | "dev": true, 634 | "dependencies": { 635 | "through": "~2.3.4" 636 | } 637 | }, 638 | "node_modules/side-channel": { 639 | "version": "1.0.4", 640 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 641 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 642 | "dev": true, 643 | "dependencies": { 644 | "call-bind": "^1.0.0", 645 | "get-intrinsic": "^1.0.2", 646 | "object-inspect": "^1.9.0" 647 | }, 648 | "funding": { 649 | "url": "https://github.com/sponsors/ljharb" 650 | } 651 | }, 652 | "node_modules/string.prototype.trim": { 653 | "version": "1.2.6", 654 | "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.6.tgz", 655 | "integrity": "sha512-8lMR2m+U0VJTPp6JjvJTtGyc4FIGq9CdRt7O9p6T0e6K4vjU+OP+SQJpbe/SBmRcCUIvNUnjsbmY6lnMp8MhsQ==", 656 | "dev": true, 657 | "dependencies": { 658 | "call-bind": "^1.0.2", 659 | "define-properties": "^1.1.4", 660 | "es-abstract": "^1.19.5" 661 | }, 662 | "engines": { 663 | "node": ">= 0.4" 664 | }, 665 | "funding": { 666 | "url": "https://github.com/sponsors/ljharb" 667 | } 668 | }, 669 | "node_modules/string.prototype.trimend": { 670 | "version": "1.0.4", 671 | "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", 672 | "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", 673 | "dev": true, 674 | "dependencies": { 675 | "call-bind": "^1.0.2", 676 | "define-properties": "^1.1.3" 677 | }, 678 | "funding": { 679 | "url": "https://github.com/sponsors/ljharb" 680 | } 681 | }, 682 | "node_modules/string.prototype.trimstart": { 683 | "version": "1.0.4", 684 | "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", 685 | "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", 686 | "dev": true, 687 | "dependencies": { 688 | "call-bind": "^1.0.2", 689 | "define-properties": "^1.1.3" 690 | }, 691 | "funding": { 692 | "url": "https://github.com/sponsors/ljharb" 693 | } 694 | }, 695 | "node_modules/supports-preserve-symlinks-flag": { 696 | "version": "1.0.0", 697 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 698 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 699 | "dev": true, 700 | "engines": { 701 | "node": ">= 0.4" 702 | }, 703 | "funding": { 704 | "url": "https://github.com/sponsors/ljharb" 705 | } 706 | }, 707 | "node_modules/tape": { 708 | "version": "4.15.1", 709 | "resolved": "https://registry.npmjs.org/tape/-/tape-4.15.1.tgz", 710 | "integrity": "sha512-k7F5pyr91n9D/yjSJwbLLYDCrTWXxMSXbbmHX2n334lSIc2rxeXyFkaBv4UuUd2gBYMrAOalPutAiCxC6q1qbw==", 711 | "dev": true, 712 | "dependencies": { 713 | "call-bind": "~1.0.2", 714 | "deep-equal": "~1.1.1", 715 | "defined": "~1.0.0", 716 | "dotignore": "~0.1.2", 717 | "for-each": "~0.3.3", 718 | "glob": "~7.2.0", 719 | "has": "~1.0.3", 720 | "inherits": "~2.0.4", 721 | "is-regex": "~1.1.4", 722 | "minimist": "~1.2.6", 723 | "object-inspect": "~1.12.0", 724 | "resolve": "~1.22.0", 725 | "resumer": "~0.0.0", 726 | "string.prototype.trim": "~1.2.5", 727 | "through": "~2.3.8" 728 | }, 729 | "bin": { 730 | "tape": "bin/tape" 731 | } 732 | }, 733 | "node_modules/through": { 734 | "version": "2.3.8", 735 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 736 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", 737 | "dev": true 738 | }, 739 | "node_modules/unbox-primitive": { 740 | "version": "1.0.2", 741 | "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", 742 | "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", 743 | "dev": true, 744 | "dependencies": { 745 | "call-bind": "^1.0.2", 746 | "has-bigints": "^1.0.2", 747 | "has-symbols": "^1.0.3", 748 | "which-boxed-primitive": "^1.0.2" 749 | }, 750 | "funding": { 751 | "url": "https://github.com/sponsors/ljharb" 752 | } 753 | }, 754 | "node_modules/which-boxed-primitive": { 755 | "version": "1.0.2", 756 | "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", 757 | "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", 758 | "dev": true, 759 | "dependencies": { 760 | "is-bigint": "^1.0.1", 761 | "is-boolean-object": "^1.1.0", 762 | "is-number-object": "^1.0.4", 763 | "is-string": "^1.0.5", 764 | "is-symbol": "^1.0.3" 765 | }, 766 | "funding": { 767 | "url": "https://github.com/sponsors/ljharb" 768 | } 769 | }, 770 | "node_modules/wrappy": { 771 | "version": "1.0.2", 772 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 773 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 774 | "dev": true 775 | } 776 | }, 777 | "dependencies": { 778 | "balanced-match": { 779 | "version": "1.0.2", 780 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 781 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 782 | "dev": true 783 | }, 784 | "brace-expansion": { 785 | "version": "1.1.11", 786 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 787 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 788 | "dev": true, 789 | "requires": { 790 | "balanced-match": "^1.0.0", 791 | "concat-map": "0.0.1" 792 | } 793 | }, 794 | "call-bind": { 795 | "version": "1.0.2", 796 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 797 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 798 | "dev": true, 799 | "requires": { 800 | "function-bind": "^1.1.1", 801 | "get-intrinsic": "^1.0.2" 802 | } 803 | }, 804 | "concat-map": { 805 | "version": "0.0.1", 806 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 807 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 808 | "dev": true 809 | }, 810 | "deep-equal": { 811 | "version": "1.1.1", 812 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", 813 | "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", 814 | "dev": true, 815 | "requires": { 816 | "is-arguments": "^1.0.4", 817 | "is-date-object": "^1.0.1", 818 | "is-regex": "^1.0.4", 819 | "object-is": "^1.0.1", 820 | "object-keys": "^1.1.1", 821 | "regexp.prototype.flags": "^1.2.0" 822 | } 823 | }, 824 | "define-properties": { 825 | "version": "1.1.4", 826 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", 827 | "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", 828 | "dev": true, 829 | "requires": { 830 | "has-property-descriptors": "^1.0.0", 831 | "object-keys": "^1.1.1" 832 | } 833 | }, 834 | "defined": { 835 | "version": "1.0.0", 836 | "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", 837 | "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", 838 | "dev": true 839 | }, 840 | "dotignore": { 841 | "version": "0.1.2", 842 | "resolved": "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz", 843 | "integrity": "sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==", 844 | "dev": true, 845 | "requires": { 846 | "minimatch": "^3.0.4" 847 | } 848 | }, 849 | "es-abstract": { 850 | "version": "1.19.5", 851 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.5.tgz", 852 | "integrity": "sha512-Aa2G2+Rd3b6kxEUKTF4TaW67czBLyAv3z7VOhYRU50YBx+bbsYZ9xQP4lMNazePuFlybXI0V4MruPos7qUo5fA==", 853 | "dev": true, 854 | "requires": { 855 | "call-bind": "^1.0.2", 856 | "es-to-primitive": "^1.2.1", 857 | "function-bind": "^1.1.1", 858 | "get-intrinsic": "^1.1.1", 859 | "get-symbol-description": "^1.0.0", 860 | "has": "^1.0.3", 861 | "has-symbols": "^1.0.3", 862 | "internal-slot": "^1.0.3", 863 | "is-callable": "^1.2.4", 864 | "is-negative-zero": "^2.0.2", 865 | "is-regex": "^1.1.4", 866 | "is-shared-array-buffer": "^1.0.2", 867 | "is-string": "^1.0.7", 868 | "is-weakref": "^1.0.2", 869 | "object-inspect": "^1.12.0", 870 | "object-keys": "^1.1.1", 871 | "object.assign": "^4.1.2", 872 | "string.prototype.trimend": "^1.0.4", 873 | "string.prototype.trimstart": "^1.0.4", 874 | "unbox-primitive": "^1.0.1" 875 | } 876 | }, 877 | "es-to-primitive": { 878 | "version": "1.2.1", 879 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", 880 | "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", 881 | "dev": true, 882 | "requires": { 883 | "is-callable": "^1.1.4", 884 | "is-date-object": "^1.0.1", 885 | "is-symbol": "^1.0.2" 886 | } 887 | }, 888 | "for-each": { 889 | "version": "0.3.3", 890 | "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", 891 | "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", 892 | "dev": true, 893 | "requires": { 894 | "is-callable": "^1.1.3" 895 | } 896 | }, 897 | "fs.realpath": { 898 | "version": "1.0.0", 899 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 900 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 901 | "dev": true 902 | }, 903 | "function-bind": { 904 | "version": "1.1.1", 905 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 906 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 907 | "dev": true 908 | }, 909 | "functions-have-names": { 910 | "version": "1.2.3", 911 | "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", 912 | "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", 913 | "dev": true 914 | }, 915 | "get-intrinsic": { 916 | "version": "1.1.1", 917 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", 918 | "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", 919 | "dev": true, 920 | "requires": { 921 | "function-bind": "^1.1.1", 922 | "has": "^1.0.3", 923 | "has-symbols": "^1.0.1" 924 | } 925 | }, 926 | "get-symbol-description": { 927 | "version": "1.0.0", 928 | "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", 929 | "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", 930 | "dev": true, 931 | "requires": { 932 | "call-bind": "^1.0.2", 933 | "get-intrinsic": "^1.1.1" 934 | } 935 | }, 936 | "glob": { 937 | "version": "7.2.0", 938 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", 939 | "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", 940 | "dev": true, 941 | "requires": { 942 | "fs.realpath": "^1.0.0", 943 | "inflight": "^1.0.4", 944 | "inherits": "2", 945 | "minimatch": "^3.0.4", 946 | "once": "^1.3.0", 947 | "path-is-absolute": "^1.0.0" 948 | } 949 | }, 950 | "has": { 951 | "version": "1.0.3", 952 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 953 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 954 | "dev": true, 955 | "requires": { 956 | "function-bind": "^1.1.1" 957 | } 958 | }, 959 | "has-bigints": { 960 | "version": "1.0.2", 961 | "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", 962 | "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", 963 | "dev": true 964 | }, 965 | "has-property-descriptors": { 966 | "version": "1.0.0", 967 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", 968 | "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", 969 | "dev": true, 970 | "requires": { 971 | "get-intrinsic": "^1.1.1" 972 | } 973 | }, 974 | "has-symbols": { 975 | "version": "1.0.3", 976 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 977 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 978 | "dev": true 979 | }, 980 | "has-tostringtag": { 981 | "version": "1.0.0", 982 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", 983 | "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", 984 | "dev": true, 985 | "requires": { 986 | "has-symbols": "^1.0.2" 987 | } 988 | }, 989 | "inflight": { 990 | "version": "1.0.6", 991 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 992 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 993 | "dev": true, 994 | "requires": { 995 | "once": "^1.3.0", 996 | "wrappy": "1" 997 | } 998 | }, 999 | "inherits": { 1000 | "version": "2.0.4", 1001 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1002 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1003 | "dev": true 1004 | }, 1005 | "internal-slot": { 1006 | "version": "1.0.3", 1007 | "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", 1008 | "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", 1009 | "dev": true, 1010 | "requires": { 1011 | "get-intrinsic": "^1.1.0", 1012 | "has": "^1.0.3", 1013 | "side-channel": "^1.0.4" 1014 | } 1015 | }, 1016 | "is-arguments": { 1017 | "version": "1.1.1", 1018 | "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", 1019 | "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", 1020 | "dev": true, 1021 | "requires": { 1022 | "call-bind": "^1.0.2", 1023 | "has-tostringtag": "^1.0.0" 1024 | } 1025 | }, 1026 | "is-bigint": { 1027 | "version": "1.0.4", 1028 | "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", 1029 | "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", 1030 | "dev": true, 1031 | "requires": { 1032 | "has-bigints": "^1.0.1" 1033 | } 1034 | }, 1035 | "is-boolean-object": { 1036 | "version": "1.1.2", 1037 | "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", 1038 | "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", 1039 | "dev": true, 1040 | "requires": { 1041 | "call-bind": "^1.0.2", 1042 | "has-tostringtag": "^1.0.0" 1043 | } 1044 | }, 1045 | "is-callable": { 1046 | "version": "1.2.4", 1047 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz", 1048 | "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==", 1049 | "dev": true 1050 | }, 1051 | "is-core-module": { 1052 | "version": "2.9.0", 1053 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", 1054 | "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", 1055 | "dev": true, 1056 | "requires": { 1057 | "has": "^1.0.3" 1058 | } 1059 | }, 1060 | "is-date-object": { 1061 | "version": "1.0.5", 1062 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", 1063 | "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", 1064 | "dev": true, 1065 | "requires": { 1066 | "has-tostringtag": "^1.0.0" 1067 | } 1068 | }, 1069 | "is-negative-zero": { 1070 | "version": "2.0.2", 1071 | "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", 1072 | "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", 1073 | "dev": true 1074 | }, 1075 | "is-number-object": { 1076 | "version": "1.0.7", 1077 | "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", 1078 | "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", 1079 | "dev": true, 1080 | "requires": { 1081 | "has-tostringtag": "^1.0.0" 1082 | } 1083 | }, 1084 | "is-regex": { 1085 | "version": "1.1.4", 1086 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", 1087 | "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", 1088 | "dev": true, 1089 | "requires": { 1090 | "call-bind": "^1.0.2", 1091 | "has-tostringtag": "^1.0.0" 1092 | } 1093 | }, 1094 | "is-shared-array-buffer": { 1095 | "version": "1.0.2", 1096 | "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", 1097 | "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", 1098 | "dev": true, 1099 | "requires": { 1100 | "call-bind": "^1.0.2" 1101 | } 1102 | }, 1103 | "is-string": { 1104 | "version": "1.0.7", 1105 | "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", 1106 | "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", 1107 | "dev": true, 1108 | "requires": { 1109 | "has-tostringtag": "^1.0.0" 1110 | } 1111 | }, 1112 | "is-symbol": { 1113 | "version": "1.0.4", 1114 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", 1115 | "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", 1116 | "dev": true, 1117 | "requires": { 1118 | "has-symbols": "^1.0.2" 1119 | } 1120 | }, 1121 | "is-weakref": { 1122 | "version": "1.0.2", 1123 | "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", 1124 | "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", 1125 | "dev": true, 1126 | "requires": { 1127 | "call-bind": "^1.0.2" 1128 | } 1129 | }, 1130 | "minimatch": { 1131 | "version": "3.1.2", 1132 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1133 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1134 | "dev": true, 1135 | "requires": { 1136 | "brace-expansion": "^1.1.7" 1137 | } 1138 | }, 1139 | "minimist": { 1140 | "version": "1.2.6", 1141 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", 1142 | "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", 1143 | "dev": true 1144 | }, 1145 | "object-inspect": { 1146 | "version": "1.12.0", 1147 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", 1148 | "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==", 1149 | "dev": true 1150 | }, 1151 | "object-is": { 1152 | "version": "1.1.5", 1153 | "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", 1154 | "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", 1155 | "dev": true, 1156 | "requires": { 1157 | "call-bind": "^1.0.2", 1158 | "define-properties": "^1.1.3" 1159 | } 1160 | }, 1161 | "object-keys": { 1162 | "version": "1.1.1", 1163 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 1164 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", 1165 | "dev": true 1166 | }, 1167 | "object.assign": { 1168 | "version": "4.1.2", 1169 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", 1170 | "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", 1171 | "dev": true, 1172 | "requires": { 1173 | "call-bind": "^1.0.0", 1174 | "define-properties": "^1.1.3", 1175 | "has-symbols": "^1.0.1", 1176 | "object-keys": "^1.1.1" 1177 | } 1178 | }, 1179 | "once": { 1180 | "version": "1.4.0", 1181 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1182 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1183 | "dev": true, 1184 | "requires": { 1185 | "wrappy": "1" 1186 | } 1187 | }, 1188 | "path-is-absolute": { 1189 | "version": "1.0.1", 1190 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1191 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1192 | "dev": true 1193 | }, 1194 | "path-parse": { 1195 | "version": "1.0.7", 1196 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 1197 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 1198 | "dev": true 1199 | }, 1200 | "regexp.prototype.flags": { 1201 | "version": "1.4.3", 1202 | "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", 1203 | "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", 1204 | "dev": true, 1205 | "requires": { 1206 | "call-bind": "^1.0.2", 1207 | "define-properties": "^1.1.3", 1208 | "functions-have-names": "^1.2.2" 1209 | } 1210 | }, 1211 | "resolve": { 1212 | "version": "1.22.0", 1213 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", 1214 | "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", 1215 | "dev": true, 1216 | "requires": { 1217 | "is-core-module": "^2.8.1", 1218 | "path-parse": "^1.0.7", 1219 | "supports-preserve-symlinks-flag": "^1.0.0" 1220 | } 1221 | }, 1222 | "resumer": { 1223 | "version": "0.0.0", 1224 | "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", 1225 | "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", 1226 | "dev": true, 1227 | "requires": { 1228 | "through": "~2.3.4" 1229 | } 1230 | }, 1231 | "side-channel": { 1232 | "version": "1.0.4", 1233 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 1234 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 1235 | "dev": true, 1236 | "requires": { 1237 | "call-bind": "^1.0.0", 1238 | "get-intrinsic": "^1.0.2", 1239 | "object-inspect": "^1.9.0" 1240 | } 1241 | }, 1242 | "string.prototype.trim": { 1243 | "version": "1.2.6", 1244 | "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.6.tgz", 1245 | "integrity": "sha512-8lMR2m+U0VJTPp6JjvJTtGyc4FIGq9CdRt7O9p6T0e6K4vjU+OP+SQJpbe/SBmRcCUIvNUnjsbmY6lnMp8MhsQ==", 1246 | "dev": true, 1247 | "requires": { 1248 | "call-bind": "^1.0.2", 1249 | "define-properties": "^1.1.4", 1250 | "es-abstract": "^1.19.5" 1251 | } 1252 | }, 1253 | "string.prototype.trimend": { 1254 | "version": "1.0.4", 1255 | "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", 1256 | "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", 1257 | "dev": true, 1258 | "requires": { 1259 | "call-bind": "^1.0.2", 1260 | "define-properties": "^1.1.3" 1261 | } 1262 | }, 1263 | "string.prototype.trimstart": { 1264 | "version": "1.0.4", 1265 | "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", 1266 | "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", 1267 | "dev": true, 1268 | "requires": { 1269 | "call-bind": "^1.0.2", 1270 | "define-properties": "^1.1.3" 1271 | } 1272 | }, 1273 | "supports-preserve-symlinks-flag": { 1274 | "version": "1.0.0", 1275 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 1276 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 1277 | "dev": true 1278 | }, 1279 | "tape": { 1280 | "version": "4.15.1", 1281 | "resolved": "https://registry.npmjs.org/tape/-/tape-4.15.1.tgz", 1282 | "integrity": "sha512-k7F5pyr91n9D/yjSJwbLLYDCrTWXxMSXbbmHX2n334lSIc2rxeXyFkaBv4UuUd2gBYMrAOalPutAiCxC6q1qbw==", 1283 | "dev": true, 1284 | "requires": { 1285 | "call-bind": "~1.0.2", 1286 | "deep-equal": "~1.1.1", 1287 | "defined": "~1.0.0", 1288 | "dotignore": "~0.1.2", 1289 | "for-each": "~0.3.3", 1290 | "glob": "~7.2.0", 1291 | "has": "~1.0.3", 1292 | "inherits": "~2.0.4", 1293 | "is-regex": "~1.1.4", 1294 | "minimist": "~1.2.6", 1295 | "object-inspect": "~1.12.0", 1296 | "resolve": "~1.22.0", 1297 | "resumer": "~0.0.0", 1298 | "string.prototype.trim": "~1.2.5", 1299 | "through": "~2.3.8" 1300 | } 1301 | }, 1302 | "through": { 1303 | "version": "2.3.8", 1304 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 1305 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", 1306 | "dev": true 1307 | }, 1308 | "unbox-primitive": { 1309 | "version": "1.0.2", 1310 | "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", 1311 | "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", 1312 | "dev": true, 1313 | "requires": { 1314 | "call-bind": "^1.0.2", 1315 | "has-bigints": "^1.0.2", 1316 | "has-symbols": "^1.0.3", 1317 | "which-boxed-primitive": "^1.0.2" 1318 | } 1319 | }, 1320 | "which-boxed-primitive": { 1321 | "version": "1.0.2", 1322 | "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", 1323 | "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", 1324 | "dev": true, 1325 | "requires": { 1326 | "is-bigint": "^1.0.1", 1327 | "is-boolean-object": "^1.1.0", 1328 | "is-number-object": "^1.0.4", 1329 | "is-string": "^1.0.5", 1330 | "is-symbol": "^1.0.3" 1331 | } 1332 | }, 1333 | "wrappy": { 1334 | "version": "1.0.2", 1335 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1336 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1337 | "dev": true 1338 | } 1339 | } 1340 | } 1341 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stack-expression", 3 | "version": "3.1.1", 4 | "homepage": "https://github.com/dominictarr/stack-expression", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/dominictarr/stack-expression.git" 8 | }, 9 | "dependencies": {}, 10 | "devDependencies": { 11 | "tape": "^4.13.2" 12 | }, 13 | "scripts": { 14 | "test": "tape test/*.js" 15 | }, 16 | "author": "Dominic Tarr @EMovhfIrFk4NihAKnRNhrfRaqIhBv1Wj8pTxJNgvCCY=.ed25519", 17 | "license": "MIT" 18 | } 19 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert') 2 | var eq = assert.equal 3 | var {And,Or,Maybe,Many,More,Join,Recurse,Text,Group,Tail} = require('../') 4 | 5 | function match(rule, input, matched, groups) { 6 | console.log(input, matched, groups) 7 | var g = [] 8 | var m = rule(input, 0, input.length, g.push.bind(g)) 9 | console.log('=>', m, g) 10 | assert.equal(m, matched) 11 | if(groups) 12 | assert.deepEqual(g, groups) 13 | } 14 | 15 | /*assert.equal = function (a, b) { 16 | console.log('eq', a , b) 17 | eq(a, b) 18 | }*/ 19 | 20 | 21 | var aORb = Or(/^a/, /^b/) 22 | 23 | match(aORb, 'a', 1) 24 | match(aORb, 'b', 1) 25 | assert.equal(aORb('c', 0), -1) 26 | 27 | var aANDb = And(/^a/, /^b/) 28 | 29 | assert.equal(aANDb('ab', 0), 2) 30 | assert.equal(aANDb('ba', 0), -1) 31 | 32 | var MAYBEb = Maybe(/^b/) 33 | 34 | assert.equal(MAYBEb('ba', 0), 1) 35 | assert.equal(MAYBEb('ab', 0), 0) 36 | 37 | var MANYa = Many('a') 38 | 39 | assert.equal(MANYa('ab', 0), 1) 40 | assert.equal(MANYa('aaab', 0), 3) 41 | assert.equal(MANYa('b', 0), 0) 42 | 43 | var MOREa = More(/^a/) 44 | 45 | assert.equal(MOREa('aaab', 0), 3) 46 | assert.equal(MOREa('b', 0), -1) 47 | 48 | var aCOMMAS = Join(/^a/, /^,/) 49 | 50 | assert.equal(aCOMMAS('a,a,a',0), 5) 51 | assert.equal(aCOMMAS('a,a,',0), 3) 52 | 53 | var aGROUPS = Join(Text(/^a/), /^,/) 54 | 55 | var g = [] 56 | assert.equal(aGROUPS('a,a,a', 0, 5, g.push.bind(g)), 5) 57 | assert.deepEqual(g, ['a', 'a', 'a']) 58 | 59 | var abcSPACES = Join(/^[abc]+/, /^\s+/) 60 | 61 | assert.equal(abcSPACES('a b c',0), 5) 62 | assert.equal(abcSPACES('aaa bbb c',0), 11) 63 | 64 | var name = /^\w+/, space = /^\s+/ 65 | 66 | var LIST = Recurse(function (LIST) { 67 | return And('(', Group(Maybe(Join(Or(Text(name), LIST), space))), ')') 68 | }) 69 | 70 | match(LIST, '(a)' , 3, [['a']]) 71 | match(LIST, '((a))' , 5, [[['a']]]) 72 | match(LIST, '((a b c))', 9, [[['a', 'b', 'c']]]) 73 | match(LIST, '((()))' , 6, [[[[]]]]) 74 | 75 | 76 | //And(Catch(/^\w+/), '@', Catch(/^\w+\.[a-z]+/)) 77 | var EMAIL = And(Text(/^\w+/), '@', Text(/^\w+\.[a-z]+/)) 78 | 79 | //var email = EMAIL('foo@bar.baz', 0, 11, []) 80 | 81 | match(EMAIL, 'foo@bar.baz', 11, ['foo', 'bar.baz']) 82 | 83 | var CSV = Join(Group(Join(Text(/^\w+/),',')),'\n') 84 | 85 | match(CSV, 'a,b,c\nd,e,f', 11, [['a','b','c'], ['d', 'e', 'f']]) 86 | 87 | var Name = Text(/^[a-z]+/) 88 | var TAIL = Tail(Name, And('(', Group(Name), ')')) 89 | 90 | match(TAIL, 'a', 1, ['a']) 91 | match(TAIL, 'a(b)(c)', 7, [[['a', ['b']], ['c']]]) 92 | match(TAIL, 'a(b)', 4, [['a', ['b']]]) 93 | 94 | var NO_OVER = Recurse((value) => Tail(Text(/^\w+/), And('(', value, ')' ))) 95 | //var overflow = Recurse((value) => Or(And(value, '(', value, ')'), Text(/^\w+/), value, )) 96 | 97 | match(NO_OVER, 'a', 1) 98 | match(NO_OVER, 'a(b)', 4) 99 | match(NO_OVER, 'a(b)(c)', 7) 100 | 101 | 102 | -------------------------------------------------------------------------------- /test/json.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert') 2 | var json = require('../examples/json') 3 | var getError = require('../').getError 4 | // -------------------- 5 | 6 | var inputs = [ 7 | [1], 8 | [1,2,3], 9 | [], 10 | {}, 11 | 1.2, 12 | 100, 13 | 1e+300, 14 | 1.32e-123, 15 | {foo: 'bar'}, 16 | '"{\"foo\":\"bar\"}"', 17 | "foo\nbar" 18 | ] 19 | 20 | // console.log( 21 | // json(JSON.stringify({okay: [1,2,3], empty: {}}), 0).groups[0] 22 | // ) 23 | 24 | var fs = require('fs') 25 | var path = require('path') 26 | var pkg = JSON.stringify(require('../package.json')) 27 | //var pkg = fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8') 28 | 29 | var start = Date.now() 30 | var N = 10000, custom, builtin 31 | for(var i = 0; i < N; i++) { 32 | var g = [] 33 | json(pkg, 0, pkg.length, g.push.bind(g)) 34 | parsed = g[0] 35 | } 36 | console.log('json.parse', custom = (Date.now() - start)/N) 37 | 38 | assert.deepEqual(parsed, require('../package.json')) 39 | 40 | var start = Date.now() 41 | for(var i = 0; i < N; i++) 42 | var parsed = JSON.parse(pkg) 43 | console.log('JSON.parse', builtin = (Date.now() - start)/N) 44 | 45 | console.log('ratio:', custom/builtin) 46 | 47 | console.log( 48 | JSON.stringify(parsed, null, 2) 49 | ) 50 | 51 | assert.deepEqual(parsed, require('../package.json')) 52 | 53 | //invalid tests 54 | for(var i = 0; i < 10; i++) { 55 | //insert 'X"{]' should break any json object... 56 | var r = ~~(Math.random()*pkg.length) 57 | var r2 = r + ~~(Math.random()*(pkg.length-r)) 58 | var partial = pkg.substring(0, r) + 'X"{]' + pkg.substring(r2) 59 | var fail 60 | try { 61 | var v = json(partial, 0, partial.length, []) 62 | fail = true 63 | } catch (err) { 64 | //NOTE: _sometimes_ this fails to error, somehow the string is still valid. 65 | if(~v) console.log('INPUT:', partial) 66 | fail = false 67 | } 68 | assert.equal(fail, false) 69 | } 70 | 71 | for(var k in inputs) { 72 | var str = JSON.stringify(inputs[k]) 73 | console.log('string:', str) 74 | var g = [], g2 = [] 75 | assert.ok(~json(str, 0, str.length, g.push.bind(g))) 76 | assert.deepEqual(g[0], inputs[k]) 77 | var str = JSON.stringify(inputs[k], null, 2) 78 | json(str, 0, str.length, g2.push.bind(g2)) 79 | assert.deepEqual(g2[0], inputs[k]) 80 | } 81 | 82 | var str = JSON.stringify(inputs), g3 = [] 83 | 84 | console.log('string:', str) 85 | assert.equal(json(str, 0, str.length, g3.push.bind(g3)), str.length) 86 | assert.deepEqual(g3[0], inputs) 87 | -------------------------------------------------------------------------------- /test/lisp.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert') 2 | var lisp = require('../examples/lisp') 3 | 4 | var isArray = Array.isArray 5 | 6 | function equals(actual, expected) { 7 | if('symbol' === typeof expected) 8 | return assert.equal(typeof actual, 'symbol') && assert.equal(String(actual), String(expected)) 9 | else if(isArray(expected)) { 10 | assert.equal(isArray(actual), true, 'expected: '+JSON.stringify(actual) + ' to be an array') 11 | assert.equal(actual.length, expected.length) 12 | for(var i = 0; i < expected.length; i++) 13 | equals(actual[i], expected[i]) 14 | } 15 | else 16 | assert.equal(actual, expected) 17 | } 18 | 19 | var input = [ 20 | '()', 21 | '((()))', 22 | '123', 23 | '(1 2 3)', 24 | '(1 (2 (3)))', 25 | '(\n\n\n)', 26 | '"hello world"', 27 | '("hello world")', 28 | 'list', 29 | '(list 1 2 3 4)', 30 | '(-1 0 1 true false nil)', 31 | '(false)', 32 | '(if false 1 0)' 33 | ] 34 | var output = [ 35 | [], 36 | [[[]]], 37 | 123, 38 | [1,2,3], 39 | [1, [2, [3]]], 40 | [], 41 | "hello world", 42 | ["hello world"], 43 | Symbol('list'), 44 | [Symbol('list'), 1, 2, 3, 4], 45 | [-1, 0, 1, true, false, null], 46 | [false], 47 | [Symbol('if'), false, 1, 0] 48 | ] 49 | 50 | //console.log(lisp('((()))', 0).groups[0]) 51 | //console.log(lisp('"hello"', 0).groups[0]) 52 | 53 | for(var i = 0; i < input.length; i++) { 54 | var g = [] 55 | var m = lisp(input[i], 0, input[i].length, g.push.bind(g)) 56 | console.error('test', i, input[i]) 57 | equals(g[0], output[i]) 58 | } 59 | -------------------------------------------------------------------------------- /test/matched-groups.js: -------------------------------------------------------------------------------- 1 | //some formats have weird edge cases. 2 | //for example, browsers accept html like
3 | //interpreting it as
4 | //I wouldn't recommend designing formats with this much redundency 5 | //because it will end up more complicated. Make something simple 6 | //that can only be parsed the right way, and error if not. 7 | //But anyway, you might need 8 | //to parse a format that works like this. for example, html. 9 | 10 | var t = require('assert') 11 | var {Recurse, And, Many, Or, Not, Group, Fail, Peek} = require('../') 12 | 13 | 14 | var opening = ['(', '[', '<', '{'] 15 | var closing = [')', ']', '>', '}'] 16 | 17 | 18 | var parens = Recurse(function (parens) { 19 | function createParens(op, cl, not) { 20 | return And( 21 | op, 22 | Many(parens), 23 | Or( 24 | cl, 25 | Peek(Or(...not)) 26 | ) 27 | ) 28 | } 29 | 30 | var round = createParens('(', ')', ['>', '}', ']']) 31 | var angle = createParens('<', '>', ['}', ']', ')']) 32 | var curly = createParens('{', '}', [']', ')', '>']) 33 | var square = createParens('[', ']', [')', '>', '}']) 34 | 35 | return Group(Or(round, square, curly, angle)) 36 | }) 37 | var input = [ 38 | '()', 39 | '(())', 40 | '([])', 41 | '([<{}>])', 42 | 43 | '([)', 44 | '({[<])', 45 | '([]<>{})' 46 | ] 47 | 48 | var output = [ 49 | [], 50 | [[]], 51 | [[]], 52 | [[[[]]]], 53 | 54 | [[]], 55 | [[[[]]]], 56 | [[],[],[]] 57 | ] 58 | 59 | input.forEach(function (src, i) { 60 | console.log('src', src) 61 | var g = [] 62 | var data = parens(src, 0, src.length, v=>g.push(v)) 63 | console.log('data', data) 64 | t.deepEqual(g, [output[i]]) 65 | }) 66 | -------------------------------------------------------------------------------- /test/mexp.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert') 2 | var inspect = require('util').inspect 3 | function equal(a, b) { 4 | if(a && 'object' === typeof a) { 5 | if(typeof a != typeof b) return false 6 | for(var k in a) //check b is not missing fields from a 7 | if(!equal(a[k], b[k])) return false 8 | for(var k in b) //check a is not missing fields from b 9 | if(!equal(a[k], b[k])) return false 10 | return true 11 | } 12 | else if('symbol' === typeof a && 'symbol' === typeof b) 13 | return a.description == b.description 14 | else //handles all other types... 15 | return a === b 16 | } 17 | 18 | var inputs = [ 19 | 'a(b c)', 20 | 'a(b c)(d e)', 21 | 'a.b', 22 | 'a.b.c', 23 | // 'a.b = 1' 24 | ] 25 | 26 | function S(s) { return Symbol(s) } 27 | 28 | var call = S('call'), a = S('a'), b = S('b'), c = S('c'), d = S('d'), e = S('e') 29 | 30 | var expected = [ 31 | {type: call, value: a, args: [b, c]}, 32 | {type: call, value: {type: call, value: a, args: [b, c]}, args: [d, e]}, 33 | {type: call, value: a, args: [b]}, 34 | {type: call, value: {type: call, value: a, args: [b]}, args: [c]}, 35 | ] 36 | 37 | var mexp = require('../examples/mexp') 38 | 39 | function parse (str) { 40 | var g = [] 41 | return ~mexp(str, 0, str.length, v=>g.push(v)) ? g : null 42 | } 43 | 44 | for(var i = 0; i < inputs.length; i++) { 45 | var actual = parse(inputs[i])[0] 46 | console.log(inspect(actual, {depth: 100})) 47 | console.log(inspect(expected[i], {depth: 100})) 48 | assert.ok(equal(actual, expected[i])) 49 | } 50 | --------------------------------------------------------------------------------