├── .gitignore ├── .npmignore ├── LICENSE.txt ├── README.md ├── package.json ├── src ├── index.ts ├── parser │ ├── index.ts │ ├── misc.ts │ ├── parsers.ts │ └── vars.ts ├── tag │ ├── extension.ts │ ├── index.ts │ ├── metaparser.ts │ ├── misc.ts │ └── peg.ts ├── test │ ├── competitor.ts │ └── index.test.ts └── utility │ ├── hooks.ts │ ├── index.ts │ ├── location.ts │ ├── logs.ts │ └── tracing.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/dist 3 | .idea 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/dist/test 3 | **/src 4 | .idea 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Segun Adebayo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pegase 2 | 3 | ![NPM](https://img.shields.io/npm/l/pegase?color=black&style=flat-square) ![npm](https://img.shields.io/npm/v/pegase?color=black&style=flat-square) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/pegase?color=black&label=gzip&logoColor=black&style=flat-square) ![npm](https://img.shields.io/npm/dw/pegase?color=black&style=flat-square) 4 | 5 |

6 | pegase 7 |

8 | 9 | > ⚠️ This library is still under development. This is a pre-release but some functionalities might still change. 10 | 11 | [Pegase](https://strblr.github.io/pegase/) is a PEG parser generator for JavaScript and TypeScript. It's: 12 | 13 | - **_Inline_**, meaning grammars are directly expressed as [tagged template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates). No generation step, no CLI. Pegase works in symbiosis with JS. 14 | - **_Fast_**. Pegase is heavily optimized to be extremely fast while providing an extensive range of features. 15 | - **_Complete_**. Pegase has *everything* you will ever need: an elegant grammar syntax with lots of flexibility, semantic actions, parametrized rules, support for native regexps, error recovery, warnings, integrated AST generation and visitors, cut operator, back references, grammar merging, and [a lot more](/pegase/basic-concepts/Building-parsers/). 16 | - **_Lightweight_**. Pegase is a _zero-dependency_ package, and weights around 9kB gzipped. 17 | - **_Intuitive_**, in that it lets you express complex processes in very simple ways. You will never feel lost. 18 | - **_Extensible_**: You can define your own `Parser` subclasses, add plugins, write custom directives, etc. 19 | 20 | # [🔗 Go to the official website](https://strblr.github.io/pegase/) 21 | 22 | 23 | 24 | ## What Pegase has to offer: 25 | 26 | ### Concise, readable yet powerful parsers in symbiosis with JS 27 | 28 | The following parses math expressions and calculates the result *on the fly*: 29 | 30 | ```js 31 | import peg from "pegase"; 32 | 33 | function calc(left, op, right) { 34 | switch (op) { 35 | case "+": return left + right; 36 | case "-": return left - right; 37 | case "*": return left * right; 38 | case "/": return left / right; 39 | } 40 | } 41 | 42 | const expr = peg` 43 | expr: term % ("+" | "-") @infix(${calc}) 44 | term: fact % ("*" | "/") @infix(${calc}) 45 | fact: $integer | '(' expr ')' 46 | $integer @number: '-'? [0-9]+ 47 | `; 48 | ``` 49 | 50 | **`expr.value("2 + (17-2*30) *(-5)+2")`** 51 | 52 | ```json 53 | 219 54 | ``` 55 | 56 | **`expr.test("2* (4 + )/32")`** 57 | 58 | ```json 59 | false 60 | ``` 61 | 62 | **`expr.parse("2* (4 + )/32").log()`** 63 | 64 | ```text 65 | (1:9) Failure: Expected integer or "(" 66 | 67 | > 1 | 2* (4 + )/32 68 | | ^ 69 | ``` 70 | 71 | Read more in [Building parsers](https://strblr.github.io/pegase/basic-concepts/Building-parsers/) and [Semantic action and dataflow](https://strblr.github.io/pegase/basic-concepts/Semantic-action-and-dataflow/). 72 | 73 | ### Automatic whitespace skipping 74 | 75 | ```ts 76 | const bitArray = peg`'[' (0 | 1) % ',' ']'`; 77 | 78 | bitArray.test(" [ 0,1 ,0 , 1, 1] "); // true 79 | ``` 80 | 81 | With, obviously, the possibility to opt-out and do it yourself: 82 | 83 | ```ts 84 | const g = peg` 85 | array: '[' _ '1' % ',' _ ']' 86 | _: \s+ 87 | `; 88 | 89 | g.test("[1,1,1]", { skip: false }); // Opt-out as a one-off 90 | 91 | // Or for all executions by default: 92 | 93 | g.defaultOptions.skip = false; 94 | 95 | g.test("[1,1,1]"); // false 96 | g.test("[ 1,1,1 ]"); // true 97 | g.test("[ 1, 1,1 ]"); // false 98 | ``` 99 | 100 | Read more in [Handling whitespaces](https://strblr.github.io/pegase/basic-concepts/Handling-whitespaces/). 101 | 102 | ### Great warning and failure reports 103 | 104 | ```ts 105 | import peg, { $raw, $warn } from "pegase"; 106 | 107 | function isCap(str) { 108 | return /^[A-Z]/.test(str) 109 | } 110 | 111 | const g = peg` 112 | classDef: 113 | 'class' 114 | ($identifier ${() => { 115 | if (!isCap($raw())) $warn("Class names should be capitalized"); 116 | }}) 117 | '{' '}' 118 | 119 | $identifier: [a-zA-Z]+ 120 | `; 121 | ``` 122 | 123 | **`g.parse("class test {").log()`** 124 | 125 | ```text 126 | (1:7) Warning: Class names should be capitalized 127 | 128 | > 1 | class test { 129 | | ^ 130 | 131 | (1:13) Failure: Expected "}" 132 | 133 | > 1 | class test { 134 | | ^ 135 | ``` 136 | 137 | Read more in [Failures and warnings](https://strblr.github.io/pegase/basic-concepts/Failures-and-warnings/). 138 | 139 | ### Parametrized rules 140 | 141 | With the possibility of omitted parameters and default parameter values. 142 | 143 | ```ts 144 | const g = peg` 145 | root: array | array('a') | array('b' | 'c') 146 | array(item = \d): '[' commaList(item) ']' 147 | commaList(item): item % ',' 148 | `; 149 | 150 | g.test("[ a, a, a, a]"); // true 151 | g.test("[ a, 5, a, a]"); // false 152 | g.test("[b, c]"); // true 153 | g.test("[b, a]"); // false 154 | g.test("[4, 5, 3, 9, 0]"); // true 155 | ``` 156 | 157 | ### Report multiple failures with recovery 158 | 159 | ```ts 160 | const g = peg` 161 | bitArray: '[' (bit | sync) % ',' ']' 162 | bit: 0 | 1 163 | sync: @@commit ...&(',' | ']') 164 | `; 165 | ``` 166 | 167 | **`g.parse("[0, 4, 1, 2, 0, 1]").log()`** 168 | 169 | ```text 170 | (1:5) Failure: Expected "0" or "1" 171 | 172 | > 1 | [0, 4, 1, 2, 0, 1] 173 | | ^ 174 | 175 | (1:11) Failure: Expected "0" or "1" 176 | 177 | > 1 | [0, 4, 1, 2, 0, 1] 178 | | ^ 179 | ``` 180 | 181 | Read more in [Error recovery](https://strblr.github.io/pegase/advanced-concepts/Error-recovery/). 182 | 183 | ### Support for native `RegExp` 184 | 185 | ```ts 186 | const time = /(\d+):(\d+)/; 187 | 188 | const minutes = peg` 189 | ${time} ${() => { 190 | const [hr, min] = $children(); 191 | return 60 * Number(hr) + Number(min); 192 | }} 193 | `; 194 | 195 | minutes.value("2:43"); // 163 196 | ``` 197 | 198 | Pegase also supports regex literals: 199 | 200 | ```ts 201 | const minutes = peg` 202 | /(\d+):(\d+)/ ${() => { 203 | const [hr, min] = $children(); 204 | return 60 * Number(hr) + Number(min); 205 | }} 206 | `; 207 | ``` 208 | 209 | Named capturing groups are converted to Pegase captures: 210 | 211 | ```ts 212 | const date = /(?\d{4})-(?\d{2})-(?\d{2})/; 213 | const yearIs = peg` 214 | ${date} ${({ year }) => "The year is " + year} 215 | `; 216 | 217 | yearIs.value("2021-08-19"); // "The year is 2021" 218 | ``` 219 | 220 | Read more in [Working with RegExp](https://strblr.github.io/pegase/advanced-concepts/Working-with-RegExp/). 221 | 222 | ### Painless AST and visitors 223 | 224 | ```ts 225 | const prefix = peg` 226 | expr: 227 | | <>$integer => 'INT' 228 | | '+' expr expr => 'PLUS' 229 | 230 | $integer @raw: \d+ 231 | `; 232 | 233 | const sumVisitor = { 234 | INT: node => Number(node.$integer), 235 | PLUS: node => $visit(node.a) + $visit(node.b) 236 | }; 237 | 238 | prefix.value("182", { visit: sumVisitor }); // 182 239 | prefix.value("+ 12 + 42 3", { visit: sumVisitor }); // 57 240 | ``` 241 | 242 | Read more in [AST and visitors](https://strblr.github.io/pegase/advanced-concepts/AST-and-visitors/). 243 | 244 | ### Extensible functionalities 245 | 246 | Here we define a directive `@max` 247 | 248 | ```ts 249 | import peg, { $children } from "pegase"; 250 | 251 | peg.plugins.push({ 252 | directives: { 253 | max: parser => peg`${parser} ${() => Math.max(...$children())}` 254 | } 255 | }); 256 | 257 | const max = peg` 258 | list: $int+ @max 259 | $int: \d+ @number 260 | `; 261 | 262 | max.value("36 12 42 3"); // 42 263 | ``` 264 | 265 | Read more in [Writing a plugin](https://strblr.github.io/pegase/advanced-concepts/Writing-a-plugin/). 266 | 267 | ## And a lot more 268 | 269 | There is so much more to see. To learn more about Pegase, [please go to the official website](https://strblr.github.io/pegase/). 270 | 271 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pegase", 3 | "version": "0.8.0", 4 | "author": "strblr", 5 | "license": "MIT", 6 | "description": "A fast, inline, powerful and lightweight PEG parser generator for JavaScript and TypeScript", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/strblr/pegase.git" 10 | }, 11 | "homepage": "https://strblr.github.io/pegase/", 12 | "bugs": { 13 | "url": "https://github.com/strblr/pegase/issues" 14 | }, 15 | "keywords": [ 16 | "parser", 17 | "parsing", 18 | "peg", 19 | "parse", 20 | "syntax", 21 | "grammar", 22 | "rule", 23 | "tree", 24 | "template", 25 | "literal", 26 | "semantic", 27 | "expression", 28 | "ast", 29 | "visitor", 30 | "semantic", 31 | "recovery", 32 | "regexp" 33 | ], 34 | "type": "module", 35 | "main": "dist/index.js", 36 | "types": "dist/index.d.ts", 37 | "scripts": { 38 | "build": "tsc", 39 | "test": "vitest run" 40 | }, 41 | "devDependencies": { 42 | "@types/prettier": "^3.0.0", 43 | "prettier": "^3.2.4", 44 | "typescript": "^5.3.3", 45 | "vitest": "^1.2.2" 46 | }, 47 | "prettier": { 48 | "trailingComma": "none", 49 | "arrowParens": "avoid", 50 | "endOfLine": "auto" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./utility/index.js"; 2 | export * from "./parser/index.js"; 3 | export * from "./tag/index.js"; 4 | 5 | import { createTag } from "./tag"; 6 | 7 | export const peg = createTag(); 8 | export default peg; 9 | -------------------------------------------------------------------------------- /src/parser/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./misc.js"; 2 | export * from "./vars.js"; 3 | export * from "./parsers.js"; 4 | -------------------------------------------------------------------------------- /src/parser/misc.ts: -------------------------------------------------------------------------------- 1 | import { CompileOptions } from "../index.js"; 2 | 3 | export type SemanticAction = (captures: Record) => any; 4 | 5 | export const defaultSkipper = /\s*/y; 6 | 7 | export function uncompiledParse(): never { 8 | throw new Error("parse method cannot be called on uncompiled parsers"); 9 | } 10 | 11 | export function cond(condition: unknown, code: string, elseCode = "") { 12 | return condition ? code : elseCode; 13 | } 14 | 15 | export function noop(options: CompileOptions) { 16 | return ` 17 | options.to = options.from; 18 | ${options.children} = []; 19 | `; 20 | } 21 | -------------------------------------------------------------------------------- /src/parser/parsers.ts: -------------------------------------------------------------------------------- 1 | import { 2 | castExpectation, 3 | cond, 4 | consoleTracer, 5 | defaultSkipper, 6 | EndOfInputExpectation, 7 | ExpectationType, 8 | Failure, 9 | FailureType, 10 | hooks, 11 | IdGenerator, 12 | LiteralExpectation, 13 | noop, 14 | Range, 15 | RegexExpectation, 16 | SemanticAction, 17 | TokenExpectation, 18 | TraceEventType, 19 | Tracer, 20 | uncompiledParse, 21 | Warning, 22 | WarningType 23 | } from "../index.js"; 24 | 25 | export interface Options { 26 | from: number; 27 | complete: boolean; 28 | skipper: RegExp; 29 | skip: boolean; 30 | ignoreCase: boolean; 31 | tracer: Tracer; 32 | trace: boolean; 33 | log: boolean; 34 | context: Context; 35 | } 36 | 37 | export type Result = SuccessResult | FailResult; 38 | 39 | export interface SuccessResult extends Range { 40 | success: true; 41 | children: any[]; 42 | warnings: Warning[]; 43 | failures: Failure[]; 44 | } 45 | 46 | export interface FailResult { 47 | success: false; 48 | warnings: Warning[]; 49 | failures: Failure[]; 50 | } 51 | 52 | export interface CompileOptions { 53 | id: IdGenerator; 54 | children: string; 55 | grammarStart: boolean; 56 | nonTerminals: { 57 | has: boolean; 58 | }; 59 | captures: { 60 | has: false | string; 61 | }; 62 | cut: { 63 | possible: boolean; 64 | has: false | string; 65 | }; 66 | actions: { 67 | has: 68 | | false 69 | | { 70 | children: string; 71 | emit: string; 72 | failed: string; 73 | ffIndex: string; 74 | ffType: string; 75 | ffSemantic: string; 76 | ffExpectations: string; 77 | }; 78 | }; 79 | } 80 | 81 | /** 82 | * Abstract base class for all parsers 83 | */ 84 | 85 | export abstract class Parser { 86 | parse: (input: string, options?: Partial) => Result; 87 | 88 | constructor() { 89 | this.parse = uncompiledParse; 90 | } 91 | 92 | test(input: string, options?: Partial>) { 93 | return this.parse(input, options).success; 94 | } 95 | 96 | children(input: string, options?: Partial>) { 97 | const result = this.parse(input, options); 98 | return !result.success ? undefined : result.children; 99 | } 100 | 101 | value(input: string, options?: Partial>) { 102 | const result = this.parse(input, options); 103 | return !result.success || result.children.length !== 1 104 | ? undefined 105 | : result.children[0]; 106 | } 107 | 108 | compile() { 109 | const id = new IdGenerator(); 110 | const options: CompileOptions = { 111 | id, 112 | children: id.generate(), 113 | grammarStart: true, 114 | nonTerminals: { has: false }, 115 | captures: { has: false }, 116 | cut: { possible: false, has: false }, 117 | actions: { has: false } 118 | }; 119 | 120 | const defSkipper = id.generate(defaultSkipper); 121 | const consTracer = id.generate(consoleTracer); 122 | const endOfInputChildren = id.generate(); 123 | const endOfInputFrom = id.generate(); 124 | 125 | const code = this.generate(options); 126 | const endOfInput = new EndOfInputParser().generate({ 127 | ...options, 128 | children: endOfInputChildren 129 | }); 130 | 131 | const traceAt = options.nonTerminals.has && id.generate(); 132 | const traceChildren = options.nonTerminals.has && id.generate(); 133 | const hook = options.actions.has && id.generate(hooks); 134 | const castExpect = options.actions.has && id.generate(castExpectation); 135 | 136 | const parse = new Function( 137 | "links", 138 | "input", 139 | "opts", 140 | ` 141 | const { ${id 142 | .entries() 143 | .map(([id]) => id) 144 | .join(",")} } = links; 145 | 146 | const options = { 147 | from: 0, 148 | complete: true, 149 | skipper: ${defSkipper}, 150 | skip: true, 151 | ignoreCase: false, 152 | tracer: ${consTracer}, 153 | trace: false, 154 | log: true, 155 | context: undefined, 156 | to: 0, 157 | warnings: [], 158 | failures: [], 159 | ffIndex: 0, 160 | ffType: null, 161 | ffSemantic: null, 162 | ffExpectations: [], 163 | ffExpect(expected) { 164 | if ( 165 | this.ffIndex === this.from && 166 | this.ffType !== "${FailureType.Semantic}" 167 | ) { 168 | this.ffType = "${FailureType.Expectation}"; 169 | this.ffExpectations.push(expected); 170 | } else if (this.ffIndex < this.from) { 171 | this.ffIndex = this.from; 172 | this.ffType = "${FailureType.Expectation}"; 173 | this.ffExpectations = [expected]; 174 | } 175 | }, 176 | ffFail(message) { 177 | if (this.ffIndex <= this.from) { 178 | this.ffIndex = this.from; 179 | this.ffType = "${FailureType.Semantic}"; 180 | this.ffSemantic = message; 181 | } 182 | }, 183 | ffCommit() { 184 | if (this.ffType !== null) { 185 | const pos = $at(this.ffIndex); 186 | if (this.ffType === "${FailureType.Expectation}") 187 | this.failures.push({ 188 | from: pos, 189 | to: pos, 190 | type: "${FailureType.Expectation}", 191 | expected: this.ffExpectations 192 | }); 193 | else 194 | this.failures.push({ 195 | from: pos, 196 | to: pos, 197 | type: "${FailureType.Semantic}", 198 | message: this.ffSemantic 199 | }); 200 | this.ffType = null; 201 | } 202 | }, 203 | ...opts 204 | }; 205 | 206 | let acc = 0; 207 | const indexes = input.split(/[\\r\\n]/).map(chunk => { 208 | const start = acc; 209 | acc += chunk.length + 1; 210 | return start; 211 | }); 212 | 213 | function $at(index) { 214 | let line = 0; 215 | let n = indexes.length - 1; 216 | while (line < n) { 217 | const k = line + ((n - line) >> 1); 218 | if (index < indexes[k]) n = k - 1; 219 | else if (index >= indexes[k + 1]) line = k + 1; 220 | else { 221 | line = k; 222 | break; 223 | } 224 | } 225 | return { 226 | input, 227 | index, 228 | line: line + 1, 229 | column: index - indexes[line] + 1 230 | }; 231 | } 232 | 233 | function $skip() { 234 | options.skipper.lastIndex = options.from; 235 | if (!options.skipper.test(input)) return false; 236 | options.from = options.skipper.lastIndex; 237 | return true; 238 | } 239 | 240 | ${cond( 241 | options.nonTerminals.has, 242 | ` 243 | function $trace(rule, exec) { 244 | var ${traceAt} = $at(options.from); 245 | options.tracer({ 246 | type: "${TraceEventType.Enter}", 247 | rule, 248 | at: ${traceAt} 249 | }); 250 | var ${traceChildren} = exec(); 251 | if (${traceChildren} === null) 252 | options.tracer({ 253 | type: "${TraceEventType.Fail}", 254 | rule, 255 | at: ${traceAt} 256 | }); 257 | else 258 | options.tracer({ 259 | type: "${TraceEventType.Match}", 260 | rule, 261 | at: ${traceAt}, 262 | from: $at(options.from), 263 | to: $at(options.to), 264 | children: ${traceChildren} 265 | }); 266 | return ${traceChildren}; 267 | } 268 | ` 269 | )} 270 | 271 | ${ 272 | !options.actions.has 273 | ? "" 274 | : ` 275 | var ${Object.values(options.actions.has).join(",")}; 276 | ${hook}.push({ 277 | $from: () => $at(options.from), 278 | $to: () => $at(options.to), 279 | $children: () => ${options.actions.has.children}, 280 | $value: () => ${ 281 | options.actions.has.children 282 | }.length !== 1 ? void 0 : ${options.actions.has.children}[0], 283 | $raw: () => input.substring(options.from, options.to), 284 | $context: () => options.context, 285 | $options: () => options, 286 | $warn(message) { 287 | options.log && 288 | options.warnings.push({ 289 | from: $at(options.from), 290 | to: $at(options.to), 291 | type: "${WarningType.Message}", 292 | message 293 | }); 294 | }, 295 | $fail(message) { 296 | ${options.actions.has.failed} = true; 297 | options.ffIndex = ${options.actions.has.ffIndex}; 298 | options.ffType = ${options.actions.has.ffType}; 299 | options.ffSemantic = ${options.actions.has.ffSemantic}; 300 | options.ffExpectations = ${ 301 | options.actions.has.ffExpectations 302 | }; 303 | options.log && options.ffFail(message); 304 | }, 305 | $expected(expected) { 306 | ${options.actions.has.failed} = true; 307 | options.ffIndex = ${options.actions.has.ffIndex}; 308 | options.ffType = ${options.actions.has.ffType}; 309 | options.ffSemantic = ${options.actions.has.ffSemantic}; 310 | options.ffExpectations = ${ 311 | options.actions.has.ffExpectations 312 | }; 313 | options.log && 314 | expected 315 | .map(${castExpect}) 316 | .forEach(expected => options.ffExpect(expected)); 317 | }, 318 | $commit: () => options.ffCommit(), 319 | $emit(children) { 320 | ${options.actions.has.emit} = children; 321 | } 322 | }); 323 | ` 324 | } 325 | 326 | var ${options.children}; 327 | ${cond(options.captures.has, `var ${options.captures.has} = {};`)} 328 | 329 | ${cond( 330 | options.actions.has, 331 | ` 332 | try { 333 | ${code} 334 | } finally { 335 | ${hook}.pop(); 336 | } 337 | `, 338 | code 339 | )} 340 | 341 | if(${options.children} !== null && options.complete) { 342 | var ${endOfInputChildren}; 343 | var ${endOfInputFrom} = options.from; 344 | options.from = options.to; 345 | ${endOfInput} 346 | if(${endOfInputChildren} !== null) { 347 | options.from = ${endOfInputFrom}; 348 | } else { 349 | ${options.children} = null; 350 | } 351 | } 352 | 353 | if(${options.children} === null) { 354 | options.ffCommit(); 355 | return { 356 | success: false, 357 | warnings: options.warnings, 358 | failures: options.failures 359 | } 360 | } 361 | return { 362 | success: true, 363 | from: $at(options.from), 364 | to: $at(options.to), 365 | children: ${options.children}, 366 | warnings: options.warnings, 367 | failures: options.failures 368 | }; 369 | ` 370 | ); 371 | 372 | this.parse = parse.bind(null, Object.fromEntries(id.entries())); 373 | (this.parse as any).code = parse.toString(); 374 | return this; 375 | } 376 | 377 | abstract generate(options: CompileOptions): string; 378 | } 379 | 380 | // LiteralParser 381 | 382 | export class LiteralParser extends Parser { 383 | readonly literal: string; 384 | readonly emit: boolean; 385 | private readonly caseSensitive: boolean; 386 | 387 | constructor(literal: string, emit: boolean = false) { 388 | super(); 389 | this.literal = literal; 390 | this.emit = emit; 391 | this.caseSensitive = literal.toLowerCase() !== literal.toUpperCase(); 392 | } 393 | 394 | generate(options: CompileOptions, test?: boolean): string { 395 | const substr = options.id.generate(); 396 | const uncased = this.literal.toLowerCase(); 397 | const children = this.emit && options.id.generate([this.literal]); 398 | const expectation = options.id.generate({ 399 | type: ExpectationType.Literal, 400 | literal: this.literal 401 | }); 402 | return ` 403 | if(options.skip && !$skip()) 404 | ${options.children} = null; 405 | else { 406 | ${cond( 407 | this.literal.length === 0, 408 | noop(options), 409 | ` 410 | var ${substr} = ${cond( 411 | this.literal.length === 1, 412 | "input[options.from]", 413 | `input.substring(options.from, options.from + ${this.literal.length})` 414 | )}; 415 | if(${cond( 416 | this.caseSensitive, 417 | `options.ignoreCase 418 | ? ${JSON.stringify(uncased)} === ${substr}.toLowerCase() : 419 | ` 420 | )} ${JSON.stringify(this.literal)} === ${substr}) { 421 | options.to = options.from + ${this.literal.length}; 422 | ${options.children} = ${children || "[]"}; 423 | } else { 424 | ${options.children} = null; 425 | options.log && options.ffExpect(${expectation}); 426 | } 427 | ` 428 | )} 429 | } 430 | `; 431 | } 432 | } 433 | 434 | // RegexParser 435 | 436 | export class RegexParser extends Parser { 437 | readonly regex: RegExp; 438 | private readonly hasGroups: boolean; 439 | private readonly hasNamedGroups: boolean; 440 | 441 | constructor(regex: RegExp) { 442 | super(); 443 | this.regex = regex; 444 | this.hasGroups = /(?({ 453 | type: ExpectationType.RegExp, 454 | regex: this.regex 455 | }); 456 | const usedRegex = options.id.generate(); 457 | const result = options.id.generate(); 458 | const captures = 459 | this.hasNamedGroups && 460 | (options.captures.has || (options.captures.has = options.id.generate())); 461 | return ` 462 | if(options.skip && !$skip()) 463 | ${options.children} = null; 464 | else { 465 | var ${usedRegex} = options.ignoreCase ? ${regexNocase} : ${regex}; 466 | ${usedRegex}.lastIndex = options.from; 467 | ${cond( 468 | !this.hasGroups, 469 | ` 470 | if(${usedRegex}.test(input)) { 471 | options.to = ${usedRegex}.lastIndex; 472 | ${options.children} = []; 473 | } 474 | `, 475 | ` 476 | var ${result} = ${usedRegex}.exec(input); 477 | if(${result} !== null) { 478 | ${cond(captures, `Object.assign(${captures}, ${result}.groups);`)} 479 | options.to = ${usedRegex}.lastIndex; 480 | ${options.children} = ${result}.slice(1); 481 | } 482 | ` 483 | )} 484 | else { 485 | ${options.children} = null; 486 | options.log && options.ffExpect(${expectation}); 487 | } 488 | } 489 | `; 490 | } 491 | } 492 | 493 | // EndOfInputParser 494 | 495 | export class EndOfInputParser extends Parser { 496 | generate(options: CompileOptions) { 497 | const expectation = options.id.generate({ 498 | type: ExpectationType.EndOfInput 499 | }); 500 | return ` 501 | if(options.skip && !$skip()) 502 | ${options.children} = null; 503 | else { 504 | if(options.from === input.length) { 505 | ${noop(options)} 506 | } else { 507 | ${options.children} = null; 508 | options.log && options.ffExpect(${expectation}); 509 | } 510 | } 511 | `; 512 | } 513 | } 514 | 515 | // TokenParser 516 | 517 | export class TokenParser extends Parser { 518 | readonly parser: Parser; 519 | readonly displayName?: string; 520 | 521 | constructor(parser: Parser, displayName?: string) { 522 | super(); 523 | this.parser = parser; 524 | this.displayName = displayName; 525 | } 526 | 527 | generate(options: CompileOptions): string { 528 | const skip = options.id.generate(); 529 | const log = this.displayName && options.id.generate(); 530 | const expectation = 531 | this.displayName && 532 | options.id.generate({ 533 | type: ExpectationType.Token, 534 | displayName: this.displayName 535 | }); 536 | const code = this.parser.generate(options); 537 | return ` 538 | if(options.skip && !$skip()) 539 | ${options.children} = null; 540 | else { 541 | var ${skip} = options.skip; 542 | options.skip = false; 543 | ${cond( 544 | !this.displayName, 545 | code, 546 | ` 547 | var ${log} = options.log; 548 | options.log = false; 549 | ${code} 550 | options.log = ${log}; 551 | if(${options.children} === null && ${log}) 552 | options.ffExpect(${expectation}); 553 | ` 554 | )} 555 | options.skip = ${skip}; 556 | } 557 | `; 558 | } 559 | } 560 | 561 | // BackReferenceParser 562 | 563 | export class BackReferenceParser extends Parser { 564 | readonly name: string; 565 | 566 | constructor(name: string) { 567 | super(); 568 | this.name = name; 569 | } 570 | 571 | generate(options: CompileOptions): string { 572 | const reference = options.id.generate(); 573 | const raw = options.id.generate(); 574 | const captures = 575 | options.captures.has || (options.captures.has = options.id.generate()); 576 | return ` 577 | if(options.skip && !$skip()) 578 | ${options.children} = null; 579 | else { 580 | var ${reference} = ${captures}["${this.name}"]; 581 | options.to = options.from + ${reference}.length; 582 | var ${raw} = input.substring(options.from, options.to); 583 | if(options.ignoreCase 584 | ? ${reference}.toLowerCase() === ${raw}.toLowerCase() 585 | : ${reference} === ${raw}) 586 | ${options.children} = []; 587 | else { 588 | ${options.children} = null; 589 | options.log && options.ffExpect({ 590 | type: "${ExpectationType.Literal}", 591 | literal: ${reference} 592 | }); 593 | } 594 | } 595 | `; 596 | } 597 | } 598 | 599 | // CutParser 600 | 601 | export class CutParser extends Parser { 602 | generate(options: CompileOptions): string { 603 | const id = options.cut.possible 604 | ? options.cut.has || (options.cut.has = options.id.generate()) 605 | : null; 606 | return ` 607 | ${cond(id, `${id} = true;`)} 608 | ${noop(options)} 609 | `; 610 | } 611 | } 612 | 613 | // AlternativeParser 614 | 615 | export class AlternativeParser extends Parser { 616 | readonly parsers: Parser[]; 617 | 618 | constructor(parsers: Parser[]) { 619 | super(); 620 | this.parsers = parsers; 621 | } 622 | 623 | generate(options: CompileOptions): string { 624 | const from = options.id.generate(); 625 | return ` 626 | var ${from} = options.from; 627 | ${this.parsers.reduceRight((acc, parser, index) => { 628 | const cut: CompileOptions["cut"] = { 629 | possible: index !== this.parsers.length - 1, 630 | has: false 631 | }; 632 | const code = parser.generate({ ...options, cut }); 633 | return ` 634 | ${cond(cut.has, `var ${cut.has} = false;`)} 635 | ${code} 636 | if(${options.children} === null ${cond(cut.has, `&& !${cut.has}`)}) { 637 | options.from = ${from}; 638 | ${acc} 639 | } 640 | `; 641 | }, "")} 642 | `; 643 | } 644 | } 645 | 646 | // SequenceParser 647 | 648 | export class SequenceParser extends Parser { 649 | readonly parsers: Parser[]; 650 | 651 | constructor(parsers: Parser[]) { 652 | super(); 653 | this.parsers = parsers; 654 | } 655 | 656 | generate(options: CompileOptions): string { 657 | const [first, ...rest] = this.parsers; 658 | const from = options.id.generate(); 659 | const acc = options.id.generate(); 660 | return ` 661 | ${first.generate(options)} 662 | if(${options.children} !== null) { 663 | var ${from} = options.from; 664 | var ${acc} = ${options.children}.concat(); 665 | ${rest.reduceRight( 666 | (code, parser) => ` 667 | options.from = options.to; 668 | ${parser.generate(options)} 669 | if(${options.children} !== null) { 670 | ${acc}.push.apply(${acc}, ${options.children}); 671 | ${code} 672 | } 673 | `, 674 | ` 675 | options.from = ${from}; 676 | ${options.children} = ${acc}; 677 | ` 678 | )} 679 | } 680 | `; 681 | } 682 | } 683 | 684 | // RepetitionParser 685 | 686 | export class RepetitionParser extends Parser { 687 | readonly parser: Parser; 688 | readonly min: number; 689 | readonly max: number; 690 | 691 | constructor(parser: Parser, [min, max]: [number, number]) { 692 | super(); 693 | this.parser = parser; 694 | this.min = min; 695 | this.max = max; 696 | } 697 | 698 | generate(options: CompileOptions): string { 699 | const from = options.id.generate(); 700 | const to = options.id.generate(); 701 | const acc = options.id.generate(); 702 | const code = this.parser.generate(options); 703 | if (this.max === 1) { 704 | if (this.min === 1) return code; 705 | return ` 706 | var ${from} = options.from; 707 | ${code} 708 | if(${options.children} === null) { 709 | options.from = options.to = ${from}; 710 | ${options.children} = []; 711 | } 712 | `; 713 | } 714 | const temp = options.id.generate(); 715 | const iterate = (times: number, finish: string = "") => 716 | Array.from(Array(times)).reduceRight( 717 | code => ` 718 | options.from = ${to}; 719 | ${temp}(); 720 | if(${options.children} !== null) { 721 | ${acc}.push.apply(${acc}, ${options.children}); 722 | ${to} = options.to; 723 | ${code} 724 | } 725 | `, 726 | finish 727 | ); 728 | return ` 729 | var ${from}${cond(this.min === 0, " = options.from")}; 730 | function ${temp}() { ${code} } 731 | ${temp}(); 732 | if(${options.children} !== null) { 733 | ${from} = options.from; 734 | var ${to} = options.to; 735 | var ${acc} = ${options.children}.concat(); 736 | ${iterate( 737 | this.min === 0 ? 0 : this.min - 1, 738 | ` 739 | ${ 740 | this.max !== Infinity 741 | ? iterate(this.max - (this.min === 0 ? 1 : this.min)) 742 | : ` 743 | while(true) { 744 | options.from = ${to}; 745 | ${temp}(); 746 | if(${options.children} === null) 747 | break; 748 | ${acc}.push.apply(${acc}, ${options.children}); 749 | ${to} = options.to; 750 | } 751 | ` 752 | } 753 | options.from = ${from}; 754 | options.to = ${to}; 755 | ${options.children} = ${acc}; 756 | ` 757 | )} 758 | } 759 | ${cond( 760 | this.min === 0, 761 | ` 762 | else { 763 | options.from = options.to = ${from}; 764 | ${options.children} = []; 765 | } 766 | ` 767 | )} 768 | `; 769 | } 770 | } 771 | 772 | // GrammarParser 773 | 774 | export type RuleConfig = [ 775 | rule: string, 776 | parameters: [parameter: string, defaultValue: Parser | null][], 777 | definition: Parser 778 | ]; 779 | 780 | export class GrammarParser extends Parser { 781 | readonly rules: RuleConfig[]; 782 | private readonly start: NonTerminalParser; 783 | 784 | constructor(rules: RuleConfig[]) { 785 | super(); 786 | this.rules = rules; 787 | this.start = new NonTerminalParser(rules[0][0]); 788 | } 789 | 790 | generate(options: CompileOptions): string { 791 | const children = options.id.generate(); 792 | return ` 793 | ${this.rules 794 | .map(([rule, parameters, parser]) => { 795 | const captures: CompileOptions["captures"] = { has: false }; 796 | const code = parser.generate({ ...options, children, captures }); 797 | const assignCode = parameters 798 | .filter(([_, defaultParser]) => defaultParser) 799 | .map( 800 | ([name, defaultParser]) => ` 801 | r_${name} = r_${name} !== void 0 ? r_${name} : function() { 802 | var ${children}; 803 | ${defaultParser!.generate({ 804 | ...options, 805 | children, 806 | captures 807 | })} 808 | return ${children}; 809 | } 810 | ` 811 | ) 812 | .join("\n"); 813 | return ` 814 | function r_${rule}(${parameters 815 | .map(([name]) => `r_${name}`) 816 | .join(",")}) { 817 | var ${children}; 818 | ${cond(captures.has, `var ${captures.has} = {};`)} 819 | ${assignCode} 820 | ${code} 821 | return ${children}; 822 | } 823 | `; 824 | }) 825 | .join("\n")} 826 | ${options.grammarStart ? this.start.generate(options) : ""} 827 | `; 828 | } 829 | } 830 | 831 | // NonTerminalParser 832 | 833 | export class NonTerminalParser extends Parser { 834 | readonly rule: string; 835 | readonly parameters: (Parser | null)[]; 836 | 837 | constructor(rule: string, parameters: (Parser | null)[] = []) { 838 | super(); 839 | this.rule = rule; 840 | this.parameters = parameters; 841 | } 842 | 843 | generate(options: CompileOptions): string { 844 | options.nonTerminals.has = true; 845 | const children = options.id.generate(); 846 | const parameters = this.parameters.map(parameter => 847 | parameter ? ([options.id.generate(), parameter] as const) : null 848 | ); 849 | const call = ` 850 | r_${this.rule}(${parameters 851 | .map(parameter => parameter?.[0] ?? "void 0") 852 | .join(",")}) 853 | `; 854 | return ` 855 | ${parameters 856 | .filter(parameter => Boolean(parameter)) 857 | .map( 858 | parameter => ` 859 | function ${parameter![0]}() { 860 | var ${children}; 861 | ${parameter![1].generate({ ...options, children })} 862 | return ${children}; 863 | }; 864 | ` 865 | ) 866 | .join("\n")} 867 | if(options.trace) { 868 | ${options.children} = $trace("${this.rule}", function() { 869 | return (${call}); 870 | }) 871 | } else { 872 | ${options.children} = ${call}; 873 | } 874 | `; 875 | } 876 | } 877 | 878 | // PredicateParser 879 | 880 | export class PredicateParser extends Parser { 881 | readonly parser: Parser; 882 | readonly polarity: boolean; 883 | 884 | constructor(parser: Parser, polarity: boolean) { 885 | super(); 886 | this.parser = parser; 887 | this.polarity = polarity; 888 | } 889 | 890 | generate(options: CompileOptions): string { 891 | const from = options.id.generate(); 892 | const code = this.parser.generate(options); 893 | if (this.polarity) 894 | return ` 895 | var ${from} = options.from; 896 | ${code} 897 | if(${options.children} !== null) { 898 | options.from = options.to = ${from}; 899 | ${options.children} = []; 900 | } 901 | `; 902 | const log = options.id.generate(); 903 | return ` 904 | var ${from} = options.from; 905 | var ${log} = options.log; 906 | options.log = false; 907 | ${code} 908 | options.log = ${log}; 909 | if(${options.children} === null) { 910 | options.from = options.to = ${from}; 911 | ${options.children} = []; 912 | } else { 913 | ${options.children} = null; 914 | options.log && 915 | options.ffExpect({ 916 | type: "${ExpectationType.Mismatch}", 917 | match: input.substring(options.from, options.to) 918 | }); 919 | } 920 | `; 921 | } 922 | } 923 | 924 | // CaptureParser 925 | 926 | export class CaptureParser extends Parser { 927 | readonly parser: Parser; 928 | readonly captures: [spread: boolean, name: string][]; 929 | 930 | constructor(parser: Parser, captures: [spread: boolean, name: string][]) { 931 | super(); 932 | this.parser = parser; 933 | this.captures = captures; 934 | } 935 | 936 | generate(options: CompileOptions): string { 937 | const captures = 938 | options.captures.has || (options.captures.has = options.id.generate()); 939 | return ` 940 | ${this.parser.generate(options)} 941 | if(${options.children} !== null) 942 | [${this.captures 943 | .map(([spread, name]) => `${cond(spread, "...")}${captures}.${name}`) 944 | .join(",")}] = ${options.children}; 945 | `; 946 | } 947 | } 948 | 949 | // CustomParser 950 | 951 | export class CustomParser extends Parser { 952 | readonly generate: (options: CompileOptions) => string; 953 | 954 | constructor(generate: (options: CompileOptions) => string) { 955 | super(); 956 | this.generate = generate; 957 | } 958 | } 959 | 960 | // ActionParser 961 | 962 | export class ActionParser extends Parser { 963 | readonly parser: Parser; 964 | readonly action: SemanticAction; 965 | 966 | constructor(parser: Parser, action: SemanticAction) { 967 | super(); 968 | this.parser = parser; 969 | this.action = action; 970 | } 971 | 972 | generate(options: CompileOptions): string { 973 | options.actions.has = options.actions.has || { 974 | children: options.id.generate(), 975 | emit: options.id.generate(), 976 | failed: options.id.generate(), 977 | ffIndex: options.id.generate(), 978 | ffType: options.id.generate(), 979 | ffSemantic: options.id.generate(), 980 | ffExpectations: options.id.generate() 981 | }; 982 | const value = options.id.generate(); 983 | const action = options.id.generate(this.action); 984 | const captures = 985 | options.captures.has || (options.captures.has = options.id.generate()); 986 | const ffIndex = options.id.generate(); 987 | const ffType = options.id.generate(); 988 | const ffSemantic = options.id.generate(); 989 | const ffExpectations = options.id.generate(); 990 | return ` 991 | var ${ffIndex} = options.ffIndex, 992 | ${ffType} = options.ffType, 993 | ${ffSemantic} = options.ffSemantic, 994 | ${ffExpectations} = options.ffExpectations.concat(); 995 | 996 | ${this.parser.generate(options)} 997 | 998 | if(${options.children} !== null) { 999 | ${options.actions.has.children} = ${options.children}; 1000 | ${options.actions.has.ffIndex} = ${ffIndex}; 1001 | ${options.actions.has.ffType} = ${ffType}; 1002 | ${options.actions.has.ffSemantic} = ${ffSemantic}; 1003 | ${options.actions.has.ffExpectations} = ${ffExpectations}; 1004 | ${options.actions.has.emit} = void 0; 1005 | ${options.actions.has.failed} = false; 1006 | 1007 | var ${value} = ${action}(${captures}); 1008 | 1009 | if (${options.actions.has.failed}) { 1010 | ${options.children} = null; 1011 | } else if (${options.actions.has.emit} !== void 0) { 1012 | ${options.children} = ${options.actions.has.emit}; 1013 | } else if (${value} !== void 0) { 1014 | ${options.children} = [${value}] 1015 | } 1016 | } 1017 | `; 1018 | } 1019 | } 1020 | -------------------------------------------------------------------------------- /src/parser/vars.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Creates an identifier factory and symbol table (used to compile parsers) 3 | */ 4 | 5 | export class IdGenerator { 6 | private counter = 0; 7 | private table = new Map<{}, string>(); 8 | private create = () => `_${(this.counter++).toString(36)}`; 9 | 10 | generate(value?: T) { 11 | if (value === undefined) { 12 | return this.create(); 13 | } 14 | let memo = this.table.get(value); 15 | if (memo === undefined) { 16 | this.table.set(value, (memo = this.create())); 17 | } 18 | return memo; 19 | } 20 | 21 | entries() { 22 | return [...this.table.entries()].map( 23 | ([value, key]) => [key, value] as const 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/tag/extension.ts: -------------------------------------------------------------------------------- 1 | import { 2 | $children, 3 | $commit, 4 | $emit, 5 | $fail, 6 | $from, 7 | $raw, 8 | ActionParser, 9 | AlternativeParser, 10 | CompileOptions, 11 | CustomParser, 12 | CutParser, 13 | GrammarParser, 14 | LiteralParser, 15 | Parser, 16 | RegexParser, 17 | SemanticAction, 18 | TokenParser, 19 | Tracer, 20 | wrap 21 | } from "../index.js"; 22 | 23 | export interface Extension { 24 | cast?(arg: any): Parser | undefined; 25 | directives?: Record; 26 | } 27 | 28 | export type Directive = (parser: Parser, ...args: any[]) => Parser; 29 | 30 | export const defaultExtension: Extension = { 31 | cast(arg) { 32 | if (typeof arg === "number") return new LiteralParser(String(arg)); 33 | if (typeof arg === "string") return new LiteralParser(arg); 34 | if (arg instanceof RegExp) return new RegexParser(arg); 35 | if (arg instanceof Parser) return arg; 36 | }, 37 | directives: { 38 | // Option tweaks 39 | skip: (parser, nextSkipper?: RegExp) => 40 | new CustomParser(options => { 41 | let code = wrap( 42 | parser.generate(options), 43 | "options.skip", 44 | "true", 45 | options 46 | ); 47 | if (nextSkipper) 48 | code = wrap( 49 | code, 50 | "options.skipper", 51 | options.id.generate(nextSkipper), 52 | options 53 | ); 54 | return code; 55 | }), 56 | noskip: parser => 57 | new CustomParser(options => 58 | wrap(parser.generate(options), "options.skip", "false", options) 59 | ), 60 | case: parser => 61 | new CustomParser(options => 62 | wrap(parser.generate(options), "options.ignoreCase", "false", options) 63 | ), 64 | nocase: parser => 65 | new CustomParser(options => 66 | wrap(parser.generate(options), "options.ignoreCase", "true", options) 67 | ), 68 | trace: (parser, nextTracer?: Tracer) => 69 | new CustomParser(options => { 70 | let code = wrap( 71 | parser.generate(options), 72 | "options.trace", 73 | "true", 74 | options 75 | ); 76 | if (nextTracer) 77 | code = wrap( 78 | code, 79 | "options.tracer", 80 | options.id.generate(nextTracer), 81 | options 82 | ); 83 | return code; 84 | }), 85 | notrace: parser => 86 | new CustomParser(options => 87 | wrap(parser.generate(options), "options.trace", "false", options) 88 | ), 89 | context: (parser, nextContext: any) => 90 | new CustomParser(options => 91 | wrap( 92 | parser.generate(options), 93 | "options.context", 94 | nextContext === undefined 95 | ? "void 0" 96 | : options.id.generate(nextContext), 97 | options 98 | ) 99 | ), 100 | // Children transforms 101 | omit: parser => new ActionParser(parser, () => $emit([])), 102 | raw: parser => new ActionParser(parser, () => $raw()), 103 | length: parser => new ActionParser(parser, () => $raw().length), 104 | number: parser => new ActionParser(parser, () => Number($raw())), 105 | index: parser => new ActionParser(parser, () => $from().index), 106 | clump: parser => new ActionParser(parser, () => $children()), 107 | count: parser => new ActionParser(parser, () => $children().length), 108 | every: (parser, predicate) => 109 | new ActionParser(parser, () => $children().every(predicate)), 110 | filter: (parser, predicate) => 111 | new ActionParser(parser, () => $emit($children().filter(predicate))), 112 | find: (parser, predicate, defaultValue?) => 113 | new ActionParser(parser, () => 114 | $emit([$children().find(predicate) ?? defaultValue]) 115 | ), 116 | flat: (parser, depth) => 117 | new ActionParser(parser, () => $emit($children().flat(depth))), 118 | forEach: (parser, callback) => 119 | new ActionParser(parser, () => $children().forEach(callback)), 120 | join: (parser, separator = ",") => 121 | new ActionParser(parser, () => $children().join(separator)), 122 | map: (parser, mapper) => 123 | new ActionParser(parser, () => $emit($children().map(mapper))), 124 | reduce: (parser, ...args) => 125 | new ActionParser(parser, () => ($children().reduce as Function)(...args)), 126 | infix: (parser, reducer, ltr = true) => 127 | new ActionParser( 128 | parser, 129 | ltr 130 | ? () => 131 | $children().reduce((acc, op, index) => 132 | index % 2 ? reducer(acc, op, $children()[index + 1]) : acc 133 | ) 134 | : () => 135 | $children().reduceRight((acc, op, index) => 136 | index % 2 ? reducer($children()[index - 1], op, acc) : acc 137 | ) 138 | ), 139 | reverse: parser => 140 | new ActionParser(parser, () => $emit([...$children()].reverse())), 141 | some: (parser, predicate) => 142 | new ActionParser(parser, () => $children().some(predicate)), 143 | reorder: (parser, ...indexes) => 144 | new ActionParser(parser, () => 145 | $emit(indexes.map(index => $children()[index])) 146 | ), 147 | // Other 148 | action: (parser, action: SemanticAction) => 149 | new ActionParser(parser, action), 150 | fail: (parser, message) => new ActionParser(parser, () => $fail(message)), 151 | echo: (parser, message) => 152 | new ActionParser(parser, () => console.log(message)), 153 | token: (parser, displayName?: string) => 154 | new TokenParser(parser, displayName), 155 | commit: parser => new ActionParser(parser, () => $commit()), 156 | test: parser => 157 | new AlternativeParser([ 158 | new ActionParser(parser, () => true), 159 | new ActionParser(new CutParser(), () => false) 160 | ]), 161 | import: (parser, ...grammars: Parser[]) => { 162 | if (grammars.some(g => !(g instanceof GrammarParser))) 163 | throw new Error("Arguments to @use directive can only be grammars"); 164 | return new CustomParser(options => { 165 | const grammarOptions: CompileOptions = { 166 | ...options, 167 | grammarStart: false 168 | }; 169 | return ` 170 | ${grammars.map(g => g.generate(grammarOptions)).join("\n")} 171 | ${parser.generate(options)} 172 | `; 173 | }); 174 | } 175 | } 176 | }; 177 | -------------------------------------------------------------------------------- /src/tag/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./misc.js"; 2 | export * from "./extension.js"; 3 | export * from "./metaparser.js"; 4 | export * from "./peg.js"; 5 | -------------------------------------------------------------------------------- /src/tag/metaparser.ts: -------------------------------------------------------------------------------- 1 | import { 2 | $children, 3 | $context, 4 | $emit, 5 | $fail, 6 | $raw, 7 | ActionParser, 8 | AlternativeParser, 9 | BackReferenceParser, 10 | CaptureParser, 11 | CutParser, 12 | defaultExtension, 13 | EndOfInputParser, 14 | Extension, 15 | GrammarParser, 16 | LiteralParser, 17 | modulo, 18 | NonTerminalParser, 19 | Parser, 20 | pipeDirectives, 21 | PredicateParser, 22 | RegexParser, 23 | RepetitionParser, 24 | resolveCast, 25 | resolveDirective, 26 | SequenceParser, 27 | spaceCase, 28 | TokenParser, 29 | unresolvedDirectiveFail 30 | } from "../index.js"; 31 | 32 | export interface MetaContext { 33 | extensions: Extension[]; 34 | args: any[]; 35 | } 36 | 37 | export function createMetaparser(): Parser { 38 | return new GrammarParser([ 39 | [ 40 | "parser", 41 | [], 42 | new ActionParser( 43 | new SequenceParser([ 44 | new NonTerminalParser("directives"), 45 | new AlternativeParser([ 46 | new NonTerminalParser("grammarParser"), 47 | new NonTerminalParser("optionsParser") 48 | ]) 49 | ]), 50 | () => pipeDirectives($children()[1], [...$children()[0]].reverse()) 51 | ) 52 | ], 53 | [ 54 | "grammarParser", 55 | [], 56 | new ActionParser( 57 | new RepetitionParser( 58 | new ActionParser( 59 | new SequenceParser([ 60 | new NonTerminalParser("identifier"), 61 | new NonTerminalParser("ruleParameterDefinitions"), 62 | new NonTerminalParser("directives"), 63 | new LiteralParser(":"), 64 | new CutParser(), 65 | new NonTerminalParser("optionsParser") 66 | ]), 67 | () => { 68 | let [rule, parameters, directives, parser] = $children(); 69 | if (rule.startsWith("$")) { 70 | const token = resolveDirective($context().extensions, "token"); 71 | if (!token) return unresolvedDirectiveFail("token"); 72 | directives = [ 73 | ...directives, 74 | [token, [spaceCase(rule.substring(1))]] 75 | ]; 76 | } 77 | return [rule, parameters, pipeDirectives(parser, directives)]; 78 | } 79 | ), 80 | [1, Infinity] 81 | ), 82 | () => new GrammarParser($children()) 83 | ) 84 | ], 85 | [ 86 | "optionsParser", 87 | [], 88 | new ActionParser( 89 | new SequenceParser([ 90 | new RepetitionParser(new LiteralParser("|"), [0, 1]), 91 | modulo( 92 | new NonTerminalParser("directiveParser"), 93 | new LiteralParser("|") 94 | ) 95 | ]), 96 | () => 97 | $children().length === 1 98 | ? $children()[0] 99 | : new AlternativeParser($children()) 100 | ) 101 | ], 102 | [ 103 | "directiveParser", 104 | [], 105 | new ActionParser( 106 | new SequenceParser([ 107 | new NonTerminalParser("sequenceParser"), 108 | new NonTerminalParser("directives") 109 | ]), 110 | () => pipeDirectives($children()[0], $children()[1]) 111 | ) 112 | ], 113 | [ 114 | "sequenceParser", 115 | [], 116 | new ActionParser( 117 | new RepetitionParser(new NonTerminalParser("minusParser"), [ 118 | 1, 119 | Infinity 120 | ]), 121 | () => 122 | $children().length === 1 123 | ? $children()[0] 124 | : new SequenceParser($children()) 125 | ) 126 | ], 127 | [ 128 | "minusParser", 129 | [], 130 | new ActionParser( 131 | modulo(new NonTerminalParser("moduloParser"), new LiteralParser("-")), 132 | () => 133 | ($children() as Parser[]).reduce( 134 | (acc, not) => 135 | new SequenceParser([new PredicateParser(not, false), acc]) 136 | ) 137 | ) 138 | ], 139 | [ 140 | "moduloParser", 141 | [], 142 | new ActionParser( 143 | modulo( 144 | new NonTerminalParser("forwardParser"), 145 | new SequenceParser([ 146 | new LiteralParser("%"), 147 | new AlternativeParser([ 148 | new NonTerminalParser("repetitionRange"), 149 | new ActionParser(new LiteralParser(""), () => [0, Infinity]) 150 | ]) 151 | ]) 152 | ), 153 | () => 154 | $children().reduce((acc, rep, index) => 155 | index % 2 ? modulo(acc, $children()[index + 1], rep) : acc 156 | ) 157 | ) 158 | ], 159 | [ 160 | "forwardParser", 161 | [], 162 | new ActionParser( 163 | new SequenceParser([ 164 | new RepetitionParser(new LiteralParser("...", true), [0, 1]), 165 | new NonTerminalParser("captureParser") 166 | ]), 167 | () => { 168 | if ($children().length === 1) return $children()[0]; 169 | return new SequenceParser([ 170 | new RepetitionParser( 171 | new SequenceParser([ 172 | new PredicateParser($children()[1], false), 173 | new RegexParser(/./) 174 | ]), 175 | [0, Infinity] 176 | ), 177 | $children()[1] 178 | ]); 179 | } 180 | ) 181 | ], 182 | [ 183 | "captureParser", 184 | [], 185 | new ActionParser( 186 | new SequenceParser([ 187 | new RepetitionParser( 188 | new ActionParser( 189 | new SequenceParser([ 190 | new LiteralParser("<"), 191 | modulo( 192 | new ActionParser( 193 | new SequenceParser([ 194 | new AlternativeParser([ 195 | new ActionParser(new LiteralParser("..."), () => true), 196 | new ActionParser(new CutParser(), () => false) 197 | ]), 198 | new AlternativeParser([ 199 | new NonTerminalParser("identifier"), 200 | new ActionParser(new CutParser(), () => null) 201 | ]) 202 | ]), 203 | () => $children() 204 | ), 205 | new LiteralParser(",") 206 | ), 207 | new LiteralParser(">") 208 | ]), 209 | () => $children() 210 | ), 211 | [0, 1] 212 | ), 213 | new NonTerminalParser("predicateParser") 214 | ]), 215 | () => { 216 | if ($children().length === 1) return $children()[0]; 217 | const someEmpty = $children()[0].some( 218 | ([_, id]: [boolean, string | null]) => id === null 219 | ); 220 | if (someEmpty && !($children()[1] instanceof NonTerminalParser)) { 221 | return $fail("Auto-captures can only be applied to non-terminals"); 222 | } 223 | const captures = $children()[0].map( 224 | ([spread, id]: [boolean, string | null]) => [ 225 | spread, 226 | id ?? $children()[1].rule 227 | ] 228 | ); 229 | return new CaptureParser($children()[1], captures); 230 | } 231 | ) 232 | ], 233 | [ 234 | "predicateParser", 235 | [], 236 | new ActionParser( 237 | new SequenceParser([ 238 | new RepetitionParser( 239 | new AlternativeParser([ 240 | new LiteralParser("&", true), 241 | new LiteralParser("!", true) 242 | ]), 243 | [0, 1] 244 | ), 245 | new NonTerminalParser("repetitionParser") 246 | ]), 247 | () => 248 | $children().length === 1 249 | ? $children()[0] 250 | : new PredicateParser($children()[1], $children()[0] === "&") 251 | ) 252 | ], 253 | [ 254 | "repetitionParser", 255 | [], 256 | new ActionParser( 257 | new SequenceParser([ 258 | new NonTerminalParser("primaryParser"), 259 | new RepetitionParser(new NonTerminalParser("repetitionRange"), [0, 1]) 260 | ]), 261 | () => 262 | $children().length === 1 263 | ? $children()[0] 264 | : new RepetitionParser($children()[0], $children()[1]) 265 | ) 266 | ], 267 | [ 268 | "primaryParser", 269 | [], 270 | new AlternativeParser([ 271 | new ActionParser(new LiteralParser("."), () => new RegexParser(/./)), 272 | new ActionParser( 273 | new SequenceParser([ 274 | new PredicateParser(new NonTerminalParser("identifier"), false), 275 | new LiteralParser("$") 276 | ]), 277 | () => new EndOfInputParser() 278 | ), 279 | new ActionParser(new LiteralParser("ε"), () => new LiteralParser("")), 280 | new ActionParser(new LiteralParser("^"), () => new CutParser()), 281 | new SequenceParser([ 282 | new LiteralParser("("), 283 | new CutParser(), 284 | new NonTerminalParser("parser"), 285 | new LiteralParser(")") 286 | ]), 287 | new ActionParser( 288 | new SequenceParser([ 289 | new LiteralParser("\\<"), 290 | new CutParser(), 291 | new NonTerminalParser("identifier"), 292 | new LiteralParser(">") 293 | ]), 294 | () => new BackReferenceParser($children()[0]) 295 | ), 296 | new ActionParser( 297 | new SequenceParser([ 298 | new LiteralParser("@"), 299 | new CutParser(), 300 | new NonTerminalParser("directive") 301 | ]), 302 | () => pipeDirectives(new LiteralParser(""), $children()) 303 | ), 304 | new SequenceParser([ 305 | new PredicateParser( 306 | new SequenceParser([ 307 | new NonTerminalParser("identifier"), 308 | new NonTerminalParser("ruleParameterDefinitions"), 309 | new NonTerminalParser("directives"), 310 | new LiteralParser(":") 311 | ]), 312 | false 313 | ), 314 | new NonTerminalParser("nonTerminal") 315 | ]), 316 | new ActionParser(new NonTerminalParser("stringLiteral"), () => { 317 | const [value, emit] = $children()[0]; 318 | return new LiteralParser(value, emit); 319 | }), 320 | new ActionParser( 321 | new NonTerminalParser("numberLiteral"), 322 | () => new LiteralParser(String($children()[0])) 323 | ), 324 | new ActionParser( 325 | new NonTerminalParser("characterClass"), 326 | () => new RegexParser($children()[0]) 327 | ), 328 | new ActionParser( 329 | new NonTerminalParser("escapedMeta"), 330 | () => new RegexParser($children()[0]) 331 | ), 332 | new ActionParser( 333 | new NonTerminalParser("regexLiteral"), 334 | () => new RegexParser($children()[0]) 335 | ), 336 | new NonTerminalParser("castableTagArgument") 337 | ]) 338 | ], 339 | [ 340 | "nonTerminal", 341 | [], 342 | new ActionParser( 343 | new SequenceParser([ 344 | new NonTerminalParser("identifier"), 345 | new NonTerminalParser("ruleParameters") 346 | ]), 347 | () => new NonTerminalParser($children()[0], $children()[1]) 348 | ) 349 | ], 350 | [ 351 | "repetitionRange", 352 | [], 353 | new AlternativeParser([ 354 | new ActionParser(new LiteralParser("?"), () => [0, 1]), 355 | new ActionParser(new LiteralParser("+"), () => [1, Infinity]), 356 | new ActionParser(new LiteralParser("*"), () => [0, Infinity]), 357 | new ActionParser( 358 | new SequenceParser([ 359 | new LiteralParser("{"), 360 | new NonTerminalParser("value"), 361 | new RepetitionParser( 362 | new SequenceParser([ 363 | new LiteralParser(","), 364 | new ActionParser( 365 | new RepetitionParser(new NonTerminalParser("value"), [0, 1]), 366 | () => ($children().length === 0 ? Infinity : undefined) 367 | ) 368 | ]), 369 | [0, 1] 370 | ), 371 | new LiteralParser("}") 372 | ]), 373 | () => { 374 | const [min, max = min] = $children(); 375 | if (typeof min !== "number" || typeof max !== "number") 376 | return $fail("A repetition range can be defined by numbers only"); 377 | return [min, max]; 378 | } 379 | ) 380 | ]) 381 | ], 382 | [ 383 | "ruleParameterDefinitions", 384 | [], 385 | new ActionParser( 386 | new RepetitionParser( 387 | new SequenceParser([ 388 | defaultExtension.directives!.noskip(new LiteralParser("(")), 389 | modulo( 390 | new ActionParser( 391 | new SequenceParser([ 392 | new NonTerminalParser("identifier"), 393 | new AlternativeParser([ 394 | new SequenceParser([ 395 | new LiteralParser("="), 396 | new NonTerminalParser("optionsParser") 397 | ]), 398 | new ActionParser(new CutParser(), () => null) 399 | ]) 400 | ]), 401 | () => $children() 402 | ), 403 | new LiteralParser(",") 404 | ), 405 | new LiteralParser(")") 406 | ]), 407 | [0, 1] 408 | ), 409 | () => $children() 410 | ) 411 | ], 412 | [ 413 | "ruleParameters", 414 | [], 415 | new ActionParser( 416 | new RepetitionParser( 417 | new SequenceParser([ 418 | defaultExtension.directives!.noskip(new LiteralParser("(")), 419 | modulo( 420 | new AlternativeParser([ 421 | new NonTerminalParser("optionsParser"), 422 | new ActionParser(new CutParser(), () => null) 423 | ]), 424 | new LiteralParser(",") 425 | ), 426 | new LiteralParser(")") 427 | ]), 428 | [0, 1] 429 | ), 430 | () => $children() 431 | ) 432 | ], 433 | [ 434 | "directives", 435 | [], 436 | new ActionParser( 437 | new RepetitionParser(new NonTerminalParser("directive"), [0, Infinity]), 438 | () => $children() 439 | ) 440 | ], 441 | [ 442 | "directive", 443 | [], 444 | new ActionParser( 445 | new AlternativeParser([ 446 | new ActionParser( 447 | new SequenceParser([ 448 | new LiteralParser("@"), 449 | new CutParser(), 450 | new NonTerminalParser("identifier"), 451 | new NonTerminalParser("directiveParameters") 452 | ]), 453 | () => $children() 454 | ), 455 | new ActionParser(new NonTerminalParser("actionTagArgument"), () => [ 456 | "action", 457 | $children() 458 | ]) 459 | ]), 460 | () => { 461 | const [name, args] = $children()[0]; 462 | const directive = resolveDirective($context().extensions, name); 463 | return directive ? [directive, args] : unresolvedDirectiveFail(name); 464 | } 465 | ) 466 | ], 467 | [ 468 | "directiveParameters", 469 | [], 470 | new ActionParser( 471 | new RepetitionParser( 472 | new SequenceParser([ 473 | defaultExtension.directives!.noskip(new LiteralParser("(")), 474 | modulo(new NonTerminalParser("value"), new LiteralParser(",")), 475 | new LiteralParser(")") 476 | ]), 477 | [0, 1] 478 | ), 479 | () => $children() 480 | ) 481 | ], 482 | [ 483 | "value", 484 | [], 485 | new AlternativeParser([ 486 | new NonTerminalParser("tagArgument"), 487 | new ActionParser( 488 | new NonTerminalParser("stringLiteral"), 489 | () => $children()[0][0] 490 | ), 491 | new NonTerminalParser("numberLiteral"), 492 | new NonTerminalParser("nonTerminal"), 493 | new NonTerminalParser("characterClass"), 494 | new NonTerminalParser("escapedMeta"), 495 | new NonTerminalParser("regexLiteral") 496 | ]) 497 | ], 498 | [ 499 | "identifier", 500 | [], 501 | new TokenParser( 502 | new RegexParser(/(\$?[_a-zA-Z][_a-zA-Z0-9]*)/), 503 | "identifier" 504 | ) 505 | ], 506 | [ 507 | "numberLiteral", 508 | [], 509 | new TokenParser( 510 | new ActionParser(new RegexParser(/[0-9]+\.?[0-9]*/), () => 511 | Number($raw()) 512 | ), 513 | "number literal" 514 | ) 515 | ], 516 | [ 517 | "stringLiteral", 518 | [], 519 | new TokenParser( 520 | new AlternativeParser([ 521 | new ActionParser(new RegexParser(/'((?:[^\\']|\\.)*)'/), () => [ 522 | JSON.parse(`"${$children()[0]}"`), 523 | false 524 | ]), 525 | new ActionParser(new RegexParser(/"(?:[^\\"]|\\.)*"/), () => [ 526 | JSON.parse($raw()), 527 | true 528 | ]) 529 | ]), 530 | "string literal" 531 | ) 532 | ], 533 | [ 534 | "regexLiteral", 535 | [], 536 | new TokenParser( 537 | new ActionParser( 538 | new RegexParser(/\/((?:\[[^\]]*]|[^\\\/]|\\.)+)\//), 539 | () => new RegExp($children()[0]) 540 | ), 541 | "regex literal" 542 | ) 543 | ], 544 | [ 545 | "characterClass", 546 | [], 547 | new TokenParser( 548 | new ActionParser( 549 | new RegexParser(/\[(?:[^\\\]]|\\.)*]/), 550 | () => new RegExp($raw()) 551 | ), 552 | "character class" 553 | ) 554 | ], 555 | [ 556 | "escapedMeta", 557 | [], 558 | new TokenParser( 559 | new ActionParser( 560 | new RegexParser(/\\[a-zA-Z0-9]+/), 561 | () => new RegExp($raw()) 562 | ), 563 | "escaped metacharacter" 564 | ) 565 | ], 566 | [ 567 | "tagArgument", 568 | [], 569 | new TokenParser( 570 | new ActionParser(new RegexParser(/~(\d+)/), () => 571 | $emit([$context().args[$children()[0]]]) 572 | ), 573 | "tag argument" 574 | ) 575 | ], 576 | [ 577 | "castableTagArgument", 578 | [], 579 | new TokenParser( 580 | new ActionParser( 581 | new NonTerminalParser("tagArgument"), 582 | () => 583 | resolveCast($context().extensions, $children()[0]) ?? 584 | $fail( 585 | "Couldn't cast value to Parser, you can add support for it via peg.extend" 586 | ) 587 | ), 588 | "castable tag argument" 589 | ) 590 | ], 591 | [ 592 | "actionTagArgument", 593 | [], 594 | new TokenParser( 595 | new ActionParser(new NonTerminalParser("tagArgument"), () => { 596 | if (typeof $children()[0] !== "function") 597 | return $fail("The tag argument is not a function"); 598 | return $children()[0]; 599 | }), 600 | "semantic action" 601 | ) 602 | ] 603 | ]).compile(); 604 | } 605 | -------------------------------------------------------------------------------- /src/tag/misc.ts: -------------------------------------------------------------------------------- 1 | import { 2 | $fail, 3 | CompileOptions, 4 | Directive, 5 | Extension, 6 | Parser, 7 | RepetitionParser, 8 | SequenceParser 9 | } from "../index.js"; 10 | 11 | /** 12 | * The skipper used in pegase grammars 13 | */ 14 | 15 | export const pegSkipper = /(?:\s|#[^#\r\n]*[#\r\n])*/y; 16 | 17 | /** 18 | * Find a cast resolver by scanning the extension list 19 | * @param extensions 20 | * @param value 21 | */ 22 | 23 | export function resolveCast(extensions: Extension[], value: any) { 24 | let parser: Parser | undefined; 25 | for (const extension of extensions) 26 | if ((parser = extension.cast?.(value))) break; 27 | return parser; 28 | } 29 | 30 | /** 31 | * Find a directive definition by name by scanning the extension list 32 | * @param extensions 33 | * @param directive 34 | */ 35 | 36 | export function resolveDirective(extensions: Extension[], directive: string) { 37 | return extensions.find( 38 | extension => 39 | extension.directives && 40 | Object.prototype.hasOwnProperty.call(extension.directives, directive) 41 | )?.directives![directive]; 42 | } 43 | 44 | /** 45 | * Composes a new parser by piping one through the directive list 46 | * @param parser 47 | * @param directives 48 | */ 49 | 50 | export function pipeDirectives( 51 | parser: Parser, 52 | directives: [Directive, any[]][] 53 | ) { 54 | return directives.reduce( 55 | (parser, [directive, args]) => directive(parser, ...args), 56 | parser 57 | ); 58 | } 59 | 60 | /** 61 | * Generates a (meta)parsing error when a directive couldn't be resolved 62 | * @param directive 63 | */ 64 | 65 | export function unresolvedDirectiveFail(directive: string) { 66 | $fail( 67 | `Couldn't resolve directive "${directive}", you can add support for it via peg.extend` 68 | ); 69 | } 70 | 71 | /** 72 | * Explicit builder for modulo expressions (a % b) 73 | * @param item 74 | * @param separator 75 | * @param repetitionRange 76 | */ 77 | 78 | export function modulo( 79 | item: Parser, 80 | separator: Parser, 81 | repetitionRange: [number, number] = [0, Infinity] 82 | ) { 83 | return new SequenceParser([ 84 | item, 85 | new RepetitionParser(new SequenceParser([separator, item]), repetitionRange) 86 | ]); 87 | } 88 | 89 | /** 90 | * Converts a string to space case 91 | * @param input 92 | */ 93 | 94 | export function spaceCase(input: string) { 95 | return input 96 | .replace("_", " ") 97 | .replace(/([a-z])([A-Z])/g, "$1 $2") 98 | .toLowerCase(); 99 | } 100 | 101 | /** 102 | * Overrides a target value before a given code segment, then restores it 103 | * @param code 104 | * @param target 105 | * @param value 106 | * @param options 107 | */ 108 | 109 | export function wrap( 110 | code: string, 111 | target: string, 112 | value: string, 113 | options: CompileOptions 114 | ) { 115 | const saved = options.id.generate(); 116 | return ` 117 | var ${saved} = ${target}; 118 | ${target} = ${value}; 119 | ${code} 120 | ${target} = ${saved}; 121 | `; 122 | } 123 | -------------------------------------------------------------------------------- /src/tag/peg.ts: -------------------------------------------------------------------------------- 1 | import { 2 | createMetaparser, 3 | defaultExtension, 4 | Extension, 5 | log, 6 | MetaContext, 7 | Parser, 8 | pegSkipper 9 | } from "../index.js"; 10 | 11 | /** 12 | * Type definitions 13 | */ 14 | 15 | export interface TagOptions { 16 | metaparser: Parser; 17 | trace: boolean; 18 | extensions: Extension[]; 19 | } 20 | 21 | export interface Tag { 22 | ( 23 | chunks: TemplateStringsArray | string, 24 | ...args: Any[] 25 | ): Parser; 26 | trace: Tag; 27 | extend(extensions: Extension | Extension[]): Tag; 28 | } 29 | 30 | export type Any = 31 | | null 32 | | undefined 33 | | string 34 | | number 35 | | boolean 36 | | symbol 37 | | bigint 38 | | object 39 | | ((...args: any[]) => any); // no "implicit any" error 40 | 41 | /** 42 | * Creates a new template literal tag to parse grammars 43 | * @param options 44 | */ 45 | 46 | export function createTag(options?: Partial) { 47 | const opts: TagOptions = { 48 | metaparser: options?.metaparser ?? createMetaparser(), 49 | trace: options?.trace ?? false, 50 | extensions: options?.extensions ?? [defaultExtension] 51 | }; 52 | 53 | function peg( 54 | chunks: TemplateStringsArray | string, 55 | ...args: Any[] 56 | ): Parser { 57 | const result = opts.metaparser.parse( 58 | typeof chunks === "string" 59 | ? chunks 60 | : chunks.raw.reduce( 61 | (acc, chunk, index) => acc + `~${index - 1}` + chunk 62 | ), 63 | { 64 | skipper: pegSkipper, 65 | trace: opts.trace, 66 | context: { extensions: opts.extensions, args } 67 | } 68 | ); 69 | if (!result.success) { 70 | throw new Error(log(result)); 71 | } 72 | return result.children[0].compile(); 73 | } 74 | 75 | const trace = (): Tag["trace"] => createTag({ ...opts, trace: true }); 76 | 77 | const extend: Tag["extend"] = extensions => 78 | createTag({ 79 | ...opts, 80 | extensions: [ 81 | ...opts.extensions, 82 | ...(Array.isArray(extensions) ? extensions : [extensions]) 83 | ] 84 | }); 85 | 86 | Object.defineProperties(peg, { 87 | trace: { get: trace }, 88 | extend: { value: extend } 89 | }); 90 | 91 | return peg as Tag; 92 | } 93 | -------------------------------------------------------------------------------- /src/test/competitor.ts: -------------------------------------------------------------------------------- 1 | // @ts-nocheck 2 | const { parse } = (function () { 3 | "use strict"; 4 | 5 | function peg$subclass(child, parent) { 6 | function ctor() { 7 | this.constructor = child; 8 | } 9 | ctor.prototype = parent.prototype; 10 | child.prototype = new ctor(); 11 | } 12 | 13 | function peg$SyntaxError(message, expected, found, location) { 14 | this.message = message; 15 | this.expected = expected; 16 | this.found = found; 17 | this.location = location; 18 | this.name = "SyntaxError"; 19 | 20 | if (typeof Error.captureStackTrace === "function") { 21 | Error.captureStackTrace(this, peg$SyntaxError); 22 | } 23 | } 24 | 25 | peg$subclass(peg$SyntaxError, Error); 26 | 27 | peg$SyntaxError.buildMessage = function (expected, found) { 28 | var DESCRIBE_EXPECTATION_FNS = { 29 | literal: function (expectation) { 30 | return '"' + literalEscape(expectation.text) + '"'; 31 | }, 32 | 33 | class: function (expectation) { 34 | var escapedParts = "", 35 | i; 36 | 37 | for (i = 0; i < expectation.parts.length; i++) { 38 | escapedParts += 39 | expectation.parts[i] instanceof Array 40 | ? classEscape(expectation.parts[i][0]) + 41 | "-" + 42 | classEscape(expectation.parts[i][1]) 43 | : classEscape(expectation.parts[i]); 44 | } 45 | 46 | return "[" + (expectation.inverted ? "^" : "") + escapedParts + "]"; 47 | }, 48 | 49 | any: function (expectation) { 50 | return "any character"; 51 | }, 52 | 53 | end: function (expectation) { 54 | return "end of input"; 55 | }, 56 | 57 | other: function (expectation) { 58 | return expectation.description; 59 | } 60 | }; 61 | 62 | function hex(ch) { 63 | return ch.charCodeAt(0).toString(16).toUpperCase(); 64 | } 65 | 66 | function literalEscape(s) { 67 | return s 68 | .replace(/\\/g, "\\\\") 69 | .replace(/"/g, '\\"') 70 | .replace(/\0/g, "\\0") 71 | .replace(/\t/g, "\\t") 72 | .replace(/\n/g, "\\n") 73 | .replace(/\r/g, "\\r") 74 | .replace(/[\x00-\x0F]/g, function (ch) { 75 | return "\\x0" + hex(ch); 76 | }) 77 | .replace(/[\x10-\x1F\x7F-\x9F]/g, function (ch) { 78 | return "\\x" + hex(ch); 79 | }); 80 | } 81 | 82 | function classEscape(s) { 83 | return s 84 | .replace(/\\/g, "\\\\") 85 | .replace(/\]/g, "\\]") 86 | .replace(/\^/g, "\\^") 87 | .replace(/-/g, "\\-") 88 | .replace(/\0/g, "\\0") 89 | .replace(/\t/g, "\\t") 90 | .replace(/\n/g, "\\n") 91 | .replace(/\r/g, "\\r") 92 | .replace(/[\x00-\x0F]/g, function (ch) { 93 | return "\\x0" + hex(ch); 94 | }) 95 | .replace(/[\x10-\x1F\x7F-\x9F]/g, function (ch) { 96 | return "\\x" + hex(ch); 97 | }); 98 | } 99 | 100 | function describeExpectation(expectation) { 101 | return DESCRIBE_EXPECTATION_FNS[expectation.type](expectation); 102 | } 103 | 104 | function describeExpected(expected) { 105 | var descriptions = new Array(expected.length), 106 | i, 107 | j; 108 | 109 | for (i = 0; i < expected.length; i++) { 110 | descriptions[i] = describeExpectation(expected[i]); 111 | } 112 | 113 | descriptions.sort(); 114 | 115 | if (descriptions.length > 0) { 116 | for (i = 1, j = 1; i < descriptions.length; i++) { 117 | if (descriptions[i - 1] !== descriptions[i]) { 118 | descriptions[j] = descriptions[i]; 119 | j++; 120 | } 121 | } 122 | descriptions.length = j; 123 | } 124 | 125 | switch (descriptions.length) { 126 | case 1: 127 | return descriptions[0]; 128 | 129 | case 2: 130 | return descriptions[0] + " or " + descriptions[1]; 131 | 132 | default: 133 | return ( 134 | descriptions.slice(0, -1).join(", ") + 135 | ", or " + 136 | descriptions[descriptions.length - 1] 137 | ); 138 | } 139 | } 140 | 141 | function describeFound(found) { 142 | return found ? '"' + literalEscape(found) + '"' : "end of input"; 143 | } 144 | 145 | return ( 146 | "Expected " + 147 | describeExpected(expected) + 148 | " but " + 149 | describeFound(found) + 150 | " found." 151 | ); 152 | }; 153 | 154 | function peg$parse(input, options?) { 155 | options = options !== void 0 ? options : {}; 156 | 157 | var peg$FAILED = {}, 158 | peg$startRuleFunctions = { Expr: peg$parseExpr }, 159 | peg$startRuleFunction = peg$parseExpr, 160 | peg$c0 = "+", 161 | peg$c1 = peg$literalExpectation("+", false), 162 | peg$c2 = "-", 163 | peg$c3 = peg$literalExpectation("-", false), 164 | peg$c4 = function (head, tail) { 165 | return tail.reduce(function (result, element) { 166 | if (element[1] === "+") { 167 | return result + element[3]; 168 | } 169 | if (element[1] === "-") { 170 | return result - element[3]; 171 | } 172 | }, head); 173 | }, 174 | peg$c5 = "*", 175 | peg$c6 = peg$literalExpectation("*", false), 176 | peg$c7 = "/", 177 | peg$c8 = peg$literalExpectation("/", false), 178 | peg$c9 = function (head, tail) { 179 | return tail.reduce(function (result, element) { 180 | if (element[1] === "*") { 181 | return result * element[3]; 182 | } 183 | if (element[1] === "/") { 184 | return result / element[3]; 185 | } 186 | }, head); 187 | }, 188 | peg$c10 = "(", 189 | peg$c11 = peg$literalExpectation("(", false), 190 | peg$c12 = ")", 191 | peg$c13 = peg$literalExpectation(")", false), 192 | peg$c14 = function (expr) { 193 | return expr; 194 | }, 195 | peg$c15 = peg$otherExpectation("integer"), 196 | peg$c16 = /^[0-9]/, 197 | peg$c17 = peg$classExpectation([["0", "9"]], false, false), 198 | peg$c18 = function () { 199 | return parseInt(text(), 10); 200 | }, 201 | peg$c19 = /^[ \t\n\r]/, 202 | peg$c20 = peg$classExpectation([" ", "\t", "\n", "\r"], false, false), 203 | peg$currPos = 0, 204 | peg$savedPos = 0, 205 | peg$posDetailsCache = [{ line: 1, column: 1 }], 206 | peg$maxFailPos = 0, 207 | peg$maxFailExpected = [], 208 | peg$silentFails = 0, 209 | peg$result; 210 | 211 | if ("startRule" in options) { 212 | if (!(options.startRule in peg$startRuleFunctions)) { 213 | throw new Error( 214 | "Can't start parsing from rule \"" + options.startRule + '".' 215 | ); 216 | } 217 | 218 | peg$startRuleFunction = peg$startRuleFunctions[options.startRule]; 219 | } 220 | 221 | function text() { 222 | return input.substring(peg$savedPos, peg$currPos); 223 | } 224 | 225 | function location() { 226 | return peg$computeLocation(peg$savedPos, peg$currPos); 227 | } 228 | 229 | function expected(description, location) { 230 | location = 231 | location !== void 0 232 | ? location 233 | : peg$computeLocation(peg$savedPos, peg$currPos); 234 | 235 | throw peg$buildStructuredError( 236 | [peg$otherExpectation(description)], 237 | input.substring(peg$savedPos, peg$currPos), 238 | location 239 | ); 240 | } 241 | 242 | function error(message, location) { 243 | location = 244 | location !== void 0 245 | ? location 246 | : peg$computeLocation(peg$savedPos, peg$currPos); 247 | 248 | throw peg$buildSimpleError(message, location); 249 | } 250 | 251 | function peg$literalExpectation(text, ignoreCase) { 252 | return { type: "literal", text: text, ignoreCase: ignoreCase }; 253 | } 254 | 255 | function peg$classExpectation(parts, inverted, ignoreCase) { 256 | return { 257 | type: "class", 258 | parts: parts, 259 | inverted: inverted, 260 | ignoreCase: ignoreCase 261 | }; 262 | } 263 | 264 | function peg$anyExpectation() { 265 | return { type: "any" }; 266 | } 267 | 268 | function peg$endExpectation() { 269 | return { type: "end" }; 270 | } 271 | 272 | function peg$otherExpectation(description) { 273 | return { type: "other", description: description }; 274 | } 275 | 276 | function peg$computePosDetails(pos) { 277 | var details = peg$posDetailsCache[pos], 278 | p; 279 | 280 | if (details) { 281 | return details; 282 | } else { 283 | p = pos - 1; 284 | while (!peg$posDetailsCache[p]) { 285 | p--; 286 | } 287 | 288 | details = peg$posDetailsCache[p]; 289 | details = { 290 | line: details.line, 291 | column: details.column 292 | }; 293 | 294 | while (p < pos) { 295 | if (input.charCodeAt(p) === 10) { 296 | details.line++; 297 | details.column = 1; 298 | } else { 299 | details.column++; 300 | } 301 | 302 | p++; 303 | } 304 | 305 | peg$posDetailsCache[pos] = details; 306 | return details; 307 | } 308 | } 309 | 310 | function peg$computeLocation(startPos, endPos) { 311 | var startPosDetails = peg$computePosDetails(startPos), 312 | endPosDetails = peg$computePosDetails(endPos); 313 | 314 | return { 315 | start: { 316 | offset: startPos, 317 | line: startPosDetails.line, 318 | column: startPosDetails.column 319 | }, 320 | end: { 321 | offset: endPos, 322 | line: endPosDetails.line, 323 | column: endPosDetails.column 324 | } 325 | }; 326 | } 327 | 328 | function peg$fail(expected) { 329 | if (peg$currPos < peg$maxFailPos) { 330 | return; 331 | } 332 | 333 | if (peg$currPos > peg$maxFailPos) { 334 | peg$maxFailPos = peg$currPos; 335 | peg$maxFailExpected = []; 336 | } 337 | 338 | peg$maxFailExpected.push(expected); 339 | } 340 | 341 | function peg$buildSimpleError(message, location) { 342 | return new peg$SyntaxError(message, null, null, location); 343 | } 344 | 345 | function peg$buildStructuredError(expected, found, location) { 346 | return new peg$SyntaxError( 347 | peg$SyntaxError.buildMessage(expected, found), 348 | expected, 349 | found, 350 | location 351 | ); 352 | } 353 | 354 | function peg$parseExpr() { 355 | var s0, s1, s2, s3, s4, s5, s6, s7; 356 | 357 | s0 = peg$currPos; 358 | s1 = peg$parseTerm(); 359 | if (s1 !== peg$FAILED) { 360 | s2 = []; 361 | s3 = peg$currPos; 362 | s4 = peg$parse_(); 363 | if (s4 !== peg$FAILED) { 364 | if (input.charCodeAt(peg$currPos) === 43) { 365 | s5 = peg$c0; 366 | peg$currPos++; 367 | } else { 368 | s5 = peg$FAILED; 369 | if (peg$silentFails === 0) { 370 | peg$fail(peg$c1); 371 | } 372 | } 373 | if (s5 === peg$FAILED) { 374 | if (input.charCodeAt(peg$currPos) === 45) { 375 | s5 = peg$c2; 376 | peg$currPos++; 377 | } else { 378 | s5 = peg$FAILED; 379 | if (peg$silentFails === 0) { 380 | peg$fail(peg$c3); 381 | } 382 | } 383 | } 384 | if (s5 !== peg$FAILED) { 385 | s6 = peg$parse_(); 386 | if (s6 !== peg$FAILED) { 387 | s7 = peg$parseTerm(); 388 | if (s7 !== peg$FAILED) { 389 | s4 = [s4, s5, s6, s7]; 390 | s3 = s4; 391 | } else { 392 | peg$currPos = s3; 393 | s3 = peg$FAILED; 394 | } 395 | } else { 396 | peg$currPos = s3; 397 | s3 = peg$FAILED; 398 | } 399 | } else { 400 | peg$currPos = s3; 401 | s3 = peg$FAILED; 402 | } 403 | } else { 404 | peg$currPos = s3; 405 | s3 = peg$FAILED; 406 | } 407 | while (s3 !== peg$FAILED) { 408 | s2.push(s3); 409 | s3 = peg$currPos; 410 | s4 = peg$parse_(); 411 | if (s4 !== peg$FAILED) { 412 | if (input.charCodeAt(peg$currPos) === 43) { 413 | s5 = peg$c0; 414 | peg$currPos++; 415 | } else { 416 | s5 = peg$FAILED; 417 | if (peg$silentFails === 0) { 418 | peg$fail(peg$c1); 419 | } 420 | } 421 | if (s5 === peg$FAILED) { 422 | if (input.charCodeAt(peg$currPos) === 45) { 423 | s5 = peg$c2; 424 | peg$currPos++; 425 | } else { 426 | s5 = peg$FAILED; 427 | if (peg$silentFails === 0) { 428 | peg$fail(peg$c3); 429 | } 430 | } 431 | } 432 | if (s5 !== peg$FAILED) { 433 | s6 = peg$parse_(); 434 | if (s6 !== peg$FAILED) { 435 | s7 = peg$parseTerm(); 436 | if (s7 !== peg$FAILED) { 437 | s4 = [s4, s5, s6, s7]; 438 | s3 = s4; 439 | } else { 440 | peg$currPos = s3; 441 | s3 = peg$FAILED; 442 | } 443 | } else { 444 | peg$currPos = s3; 445 | s3 = peg$FAILED; 446 | } 447 | } else { 448 | peg$currPos = s3; 449 | s3 = peg$FAILED; 450 | } 451 | } else { 452 | peg$currPos = s3; 453 | s3 = peg$FAILED; 454 | } 455 | } 456 | if (s2 !== peg$FAILED) { 457 | peg$savedPos = s0; 458 | s1 = peg$c4(s1, s2); 459 | s0 = s1; 460 | } else { 461 | peg$currPos = s0; 462 | s0 = peg$FAILED; 463 | } 464 | } else { 465 | peg$currPos = s0; 466 | s0 = peg$FAILED; 467 | } 468 | 469 | return s0; 470 | } 471 | 472 | function peg$parseTerm() { 473 | var s0, s1, s2, s3, s4, s5, s6, s7; 474 | 475 | s0 = peg$currPos; 476 | s1 = peg$parseFact(); 477 | if (s1 !== peg$FAILED) { 478 | s2 = []; 479 | s3 = peg$currPos; 480 | s4 = peg$parse_(); 481 | if (s4 !== peg$FAILED) { 482 | if (input.charCodeAt(peg$currPos) === 42) { 483 | s5 = peg$c5; 484 | peg$currPos++; 485 | } else { 486 | s5 = peg$FAILED; 487 | if (peg$silentFails === 0) { 488 | peg$fail(peg$c6); 489 | } 490 | } 491 | if (s5 === peg$FAILED) { 492 | if (input.charCodeAt(peg$currPos) === 47) { 493 | s5 = peg$c7; 494 | peg$currPos++; 495 | } else { 496 | s5 = peg$FAILED; 497 | if (peg$silentFails === 0) { 498 | peg$fail(peg$c8); 499 | } 500 | } 501 | } 502 | if (s5 !== peg$FAILED) { 503 | s6 = peg$parse_(); 504 | if (s6 !== peg$FAILED) { 505 | s7 = peg$parseFact(); 506 | if (s7 !== peg$FAILED) { 507 | s4 = [s4, s5, s6, s7]; 508 | s3 = s4; 509 | } else { 510 | peg$currPos = s3; 511 | s3 = peg$FAILED; 512 | } 513 | } else { 514 | peg$currPos = s3; 515 | s3 = peg$FAILED; 516 | } 517 | } else { 518 | peg$currPos = s3; 519 | s3 = peg$FAILED; 520 | } 521 | } else { 522 | peg$currPos = s3; 523 | s3 = peg$FAILED; 524 | } 525 | while (s3 !== peg$FAILED) { 526 | s2.push(s3); 527 | s3 = peg$currPos; 528 | s4 = peg$parse_(); 529 | if (s4 !== peg$FAILED) { 530 | if (input.charCodeAt(peg$currPos) === 42) { 531 | s5 = peg$c5; 532 | peg$currPos++; 533 | } else { 534 | s5 = peg$FAILED; 535 | if (peg$silentFails === 0) { 536 | peg$fail(peg$c6); 537 | } 538 | } 539 | if (s5 === peg$FAILED) { 540 | if (input.charCodeAt(peg$currPos) === 47) { 541 | s5 = peg$c7; 542 | peg$currPos++; 543 | } else { 544 | s5 = peg$FAILED; 545 | if (peg$silentFails === 0) { 546 | peg$fail(peg$c8); 547 | } 548 | } 549 | } 550 | if (s5 !== peg$FAILED) { 551 | s6 = peg$parse_(); 552 | if (s6 !== peg$FAILED) { 553 | s7 = peg$parseFact(); 554 | if (s7 !== peg$FAILED) { 555 | s4 = [s4, s5, s6, s7]; 556 | s3 = s4; 557 | } else { 558 | peg$currPos = s3; 559 | s3 = peg$FAILED; 560 | } 561 | } else { 562 | peg$currPos = s3; 563 | s3 = peg$FAILED; 564 | } 565 | } else { 566 | peg$currPos = s3; 567 | s3 = peg$FAILED; 568 | } 569 | } else { 570 | peg$currPos = s3; 571 | s3 = peg$FAILED; 572 | } 573 | } 574 | if (s2 !== peg$FAILED) { 575 | peg$savedPos = s0; 576 | s1 = peg$c9(s1, s2); 577 | s0 = s1; 578 | } else { 579 | peg$currPos = s0; 580 | s0 = peg$FAILED; 581 | } 582 | } else { 583 | peg$currPos = s0; 584 | s0 = peg$FAILED; 585 | } 586 | 587 | return s0; 588 | } 589 | 590 | function peg$parseFact() { 591 | var s0, s1, s2, s3, s4, s5; 592 | 593 | s0 = peg$currPos; 594 | if (input.charCodeAt(peg$currPos) === 40) { 595 | s1 = peg$c10; 596 | peg$currPos++; 597 | } else { 598 | s1 = peg$FAILED; 599 | if (peg$silentFails === 0) { 600 | peg$fail(peg$c11); 601 | } 602 | } 603 | if (s1 !== peg$FAILED) { 604 | s2 = peg$parse_(); 605 | if (s2 !== peg$FAILED) { 606 | s3 = peg$parseExpr(); 607 | if (s3 !== peg$FAILED) { 608 | s4 = peg$parse_(); 609 | if (s4 !== peg$FAILED) { 610 | if (input.charCodeAt(peg$currPos) === 41) { 611 | s5 = peg$c12; 612 | peg$currPos++; 613 | } else { 614 | s5 = peg$FAILED; 615 | if (peg$silentFails === 0) { 616 | peg$fail(peg$c13); 617 | } 618 | } 619 | if (s5 !== peg$FAILED) { 620 | peg$savedPos = s0; 621 | s1 = peg$c14(s3); 622 | s0 = s1; 623 | } else { 624 | peg$currPos = s0; 625 | s0 = peg$FAILED; 626 | } 627 | } else { 628 | peg$currPos = s0; 629 | s0 = peg$FAILED; 630 | } 631 | } else { 632 | peg$currPos = s0; 633 | s0 = peg$FAILED; 634 | } 635 | } else { 636 | peg$currPos = s0; 637 | s0 = peg$FAILED; 638 | } 639 | } else { 640 | peg$currPos = s0; 641 | s0 = peg$FAILED; 642 | } 643 | if (s0 === peg$FAILED) { 644 | s0 = peg$parseInteger(); 645 | } 646 | 647 | return s0; 648 | } 649 | 650 | function peg$parseInteger() { 651 | var s0, s1, s2, s3; 652 | 653 | peg$silentFails++; 654 | s0 = peg$currPos; 655 | s1 = peg$parse_(); 656 | if (s1 !== peg$FAILED) { 657 | s2 = []; 658 | if (peg$c16.test(input.charAt(peg$currPos))) { 659 | s3 = input.charAt(peg$currPos); 660 | peg$currPos++; 661 | } else { 662 | s3 = peg$FAILED; 663 | if (peg$silentFails === 0) { 664 | peg$fail(peg$c17); 665 | } 666 | } 667 | if (s3 !== peg$FAILED) { 668 | while (s3 !== peg$FAILED) { 669 | s2.push(s3); 670 | if (peg$c16.test(input.charAt(peg$currPos))) { 671 | s3 = input.charAt(peg$currPos); 672 | peg$currPos++; 673 | } else { 674 | s3 = peg$FAILED; 675 | if (peg$silentFails === 0) { 676 | peg$fail(peg$c17); 677 | } 678 | } 679 | } 680 | } else { 681 | s2 = peg$FAILED; 682 | } 683 | if (s2 !== peg$FAILED) { 684 | peg$savedPos = s0; 685 | s1 = peg$c18(); 686 | s0 = s1; 687 | } else { 688 | peg$currPos = s0; 689 | s0 = peg$FAILED; 690 | } 691 | } else { 692 | peg$currPos = s0; 693 | s0 = peg$FAILED; 694 | } 695 | peg$silentFails--; 696 | if (s0 === peg$FAILED) { 697 | s1 = peg$FAILED; 698 | if (peg$silentFails === 0) { 699 | peg$fail(peg$c15); 700 | } 701 | } 702 | 703 | return s0; 704 | } 705 | 706 | function peg$parse_() { 707 | var s0, s1; 708 | 709 | s0 = []; 710 | if (peg$c19.test(input.charAt(peg$currPos))) { 711 | s1 = input.charAt(peg$currPos); 712 | peg$currPos++; 713 | } else { 714 | s1 = peg$FAILED; 715 | if (peg$silentFails === 0) { 716 | peg$fail(peg$c20); 717 | } 718 | } 719 | while (s1 !== peg$FAILED) { 720 | s0.push(s1); 721 | if (peg$c19.test(input.charAt(peg$currPos))) { 722 | s1 = input.charAt(peg$currPos); 723 | peg$currPos++; 724 | } else { 725 | s1 = peg$FAILED; 726 | if (peg$silentFails === 0) { 727 | peg$fail(peg$c20); 728 | } 729 | } 730 | } 731 | 732 | return s0; 733 | } 734 | 735 | peg$result = peg$startRuleFunction(); 736 | 737 | if (peg$result !== peg$FAILED && peg$currPos === input.length) { 738 | return peg$result; 739 | } else { 740 | if (peg$result !== peg$FAILED && peg$currPos < input.length) { 741 | peg$fail(peg$endExpectation()); 742 | } 743 | 744 | throw peg$buildStructuredError( 745 | peg$maxFailExpected, 746 | peg$maxFailPos < input.length ? input.charAt(peg$maxFailPos) : null, 747 | peg$maxFailPos < input.length 748 | ? peg$computeLocation(peg$maxFailPos, peg$maxFailPos + 1) 749 | : peg$computeLocation(peg$maxFailPos, peg$maxFailPos) 750 | ); 751 | } 752 | } 753 | 754 | return { 755 | SyntaxError: peg$SyntaxError, 756 | parse: peg$parse 757 | }; 758 | })(); 759 | 760 | export { parse }; 761 | -------------------------------------------------------------------------------- /src/test/index.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | import * as competitor from "./competitor.js"; 3 | import peg, { 4 | $children, 5 | $context, 6 | $emit, 7 | $fail, 8 | $raw, 9 | $warn, 10 | ActionParser, 11 | createMetaparser, 12 | createTag, 13 | defaultExtension, 14 | LiteralParser, 15 | log, 16 | RegexParser, 17 | SuccessResult 18 | } from "../index"; 19 | import { format } from "prettier"; 20 | 21 | function echoBuild(p: any) { 22 | console.log(format(p.parse.code, { parser: "babel" })); 23 | } 24 | 25 | test("The peg tag should work with raw strings", () => { 26 | /*let a = peg`a: ^ b: ^`; 27 | let c = peg` 28 | c @include(${a}): ^ 29 | `; 30 | echoBuild(c);*/ 31 | 32 | const g1 = peg` "My name is \"pegase\"." `; 33 | const g2 = peg`[\]]`; 34 | const g3 = peg`\s`; 35 | expect(g1).toBeInstanceOf(LiteralParser); 36 | expect((g1 as LiteralParser).literal).toBe('My name is "pegase".'); 37 | expect(g2).toBeInstanceOf(RegexParser); 38 | expect((g2 as RegexParser).regex.toString()).toBe("/[\\]]/"); 39 | expect(g3).toBeInstanceOf(RegexParser); 40 | expect((g3 as RegexParser).regex.toString()).toBe("/\\s/"); 41 | expect(g3.test("", { skip: false })).toBe(false); 42 | expect(g3.test(" ", { skip: false })).toBe(true); 43 | }); 44 | 45 | test("The peg tag should correctly parse regex literals", () => { 46 | const g1 = peg`/abc/` as RegexParser; 47 | const g2 = peg`/a\//` as RegexParser; 48 | const g3 = peg`/a\\\//` as RegexParser; 49 | const g4 = peg`/a[ab/cd]b/` as RegexParser; 50 | const g5 = peg`/[[^()]*|(\(.*?\))]*/` as RegexParser; 51 | expect(g1.regex.toString()).toBe(/abc/.toString()); 52 | expect(g2.regex.toString()).toBe(/a\//.toString()); 53 | expect(g3.regex.toString()).toBe(/a\\\//.toString()); 54 | expect(g4.regex.toString()).toBe(/a[ab/cd]b/.toString()); 55 | expect(g5.regex.toString()).toBe(/[[^()]*|(\(.*?\))]*/.toString()); 56 | }); 57 | 58 | test("The 'complete' option should work", () => { 59 | const p = peg`0`; 60 | expect(p.test(" 0 ")).toBe(true); 61 | expect(p.test(" 0 1 ")).toBe(false); 62 | expect(p.test(" 0 ", { complete: false })).toBe(true); 63 | expect(p.test(" 0 1 ", { complete: false })).toBe(true); 64 | }); 65 | 66 | test("Repetition parsers should work", () => { 67 | const g1 = peg`"a"?`; 68 | const g2 = peg`"a" +`; 69 | const g3 = peg`"a"*`; 70 | const g4 = peg`"a"{3}`; 71 | const g5 = peg`"a" {2, ${4}}`; 72 | const g6 = peg`"a"{2,} `; 73 | 74 | const a = (n: number) => [..."a".repeat(n)]; 75 | 76 | expect(g1.test("")).toBe(true); 77 | expect(g1.value("a")).toBe("a"); 78 | expect(g1.children("aa", { complete: false })).toEqual(["a"]); 79 | expect(g2.test("")).toBe(false); 80 | expect(g2.value("a")).toBe("a"); 81 | expect(g2.children("aa")).toEqual(a(2)); 82 | expect(g2.children("aaaa")).toEqual(a(4)); 83 | expect(g3.test("")).toBe(true); 84 | expect(g3.value("a")).toBe("a"); 85 | expect(g3.children("aa")).toEqual(a(2)); 86 | expect(g3.children("aaaa")).toEqual(a(4)); 87 | expect(g4.test("a")).toBe(false); 88 | expect(g4.test("aa")).toBe(false); 89 | expect(g4.test("aaa")).toBe(true); 90 | expect(g4.children("aaaaaa", { complete: false })).toEqual(a(3)); 91 | expect(g5.test("a")).toBe(false); 92 | expect(g5.test("aa")).toBe(true); 93 | expect(g5.test("aaa")).toBe(true); 94 | expect(g5.children("aaaaaa", { complete: false })).toEqual(a(4)); 95 | expect(g6.test("a")).toBe(false); 96 | expect(g6.test("aa")).toBe(true); 97 | expect(g6.test("aaa")).toBe(true); 98 | expect(g6.children("a".repeat(103))).toEqual(a(103)); 99 | }); 100 | 101 | test("Captures should work", () => { 102 | const g1 = peg`([abc] @raw)`; 103 | const g2 = peg`${g1}*`; 104 | const g3 = peg`...("a" | "b"){1} .*`; 105 | const g4 = peg`("a" ("b" | "c") "d" @count)`; 106 | const g5 = peg`a: (<>b) <>c ${({ val, b, c }) => 107 | val + c + b} b: "b" c: "c"`; 108 | const g6 = peg`'a' (${/(?(?[bc])(?[de]))/} @raw) 'f'`; 109 | const g7 = peg`&(("0" | "1")) ("0" | "1")`; 110 | 111 | // TODO: Add tests to verify scopes of captures 112 | 113 | /*expect((g1.parse("a") as SuccessResult).captures.get("val")).toBe("a"); 114 | expect((g2.parse("abc") as SuccessResult).captures.get("val")).toBe("c"); 115 | expect( 116 | (g3.parse("#@@@°#§¬ba.aps") as SuccessResult).captures.get("val") 117 | ).toBe("b"); 118 | expect(g5.value("bc")).toBe("bcb"); 119 | expect((g7.parse("1") as SuccessResult).captures.get("val")).toBe("1"); 120 | 121 | const result = g4.parse("acd") as SuccessResult; 122 | expect(result.captures.get("val1")).toBe(3); 123 | expect(result.captures.get("val2")).toBe("c"); 124 | 125 | const result2 = g6.parse("a ce f") as SuccessResult; 126 | expect(result2.captures.get("val")).toBe("ce"); 127 | expect(result2.captures.get("val1")).toBe("c"); 128 | expect(result2.captures.get("val2")).toBe("e"); 129 | expect(result2.captures.get("val3")).toBe("ce");*/ 130 | 131 | try { 132 | peg`<>"a"`; 133 | } catch (e: any) { 134 | expect(e.message) 135 | .toBe(`(1:1) Failure: Auto-captures can only be applied to non-terminals 136 | 137 | > 1 | <>"a" 138 | | ^ 139 | `); 140 | } 141 | }); 142 | 143 | test("The modulo operator should work", () => { 144 | const i = (n: number) => [..."1".repeat(n)]; 145 | 146 | const g1 = peg`"1" % ','`; 147 | expect(g1.children("1")).toEqual(["1"]); 148 | expect(g1.children("1,1")).toEqual(i(2)); 149 | expect(g1.children("1 ,1, 1 , 1")).toEqual(i(4)); 150 | 151 | const g2 = peg`"1" %? ','`; 152 | expect(g2.children("1")).toEqual(["1"]); 153 | expect(g2.children("1,1")).toEqual(i(2)); 154 | expect(g2.children("1 ,1, 1 , 1", { complete: false })).toEqual(i(2)); 155 | 156 | const g3 = peg`"1" %{2} ','`; 157 | expect(g3.test("1")).toBe(false); 158 | expect(g3.test("1,1")).toBe(false); 159 | expect(g3.children("1 ,1, 1 , 1", { complete: false })).toEqual(i(3)); 160 | 161 | const g4 = peg`("1" % ',' @count) % '|'`; 162 | expect(g4.test(" 2, 1, 1 | 1")).toBe(false); 163 | expect(g4.test(" 1 ,1,1 |1,1, 1,1|1 |1,1 ")).toBe(true); 164 | expect(g4.children("1 ,1,1 |1,1, 1 , 1,1|1 | 1,1 ")).toEqual([ 165 | 3, 5, 1, 2 166 | ]); 167 | }); 168 | 169 | test("Parametrized rules should work", () => { 170 | const g = peg` 171 | root: array | array('a') | array('b' | 'c') 172 | array(item = \d): '[' commaList(item) ']' 173 | commaList(item): item % ',' 174 | `; 175 | 176 | expect(g.test("[ a, a, a, a]")).toBe(true); // true 177 | expect(g.test("[ a, 5, a, a]")).toBe(false); // false 178 | expect(g.test("[b, c]")).toBe(true); // true 179 | expect(g.test("[b, a]")).toBe(false); // false 180 | expect(g.test("[4, 5, 3, 9, 0]")).toBe(true); // true 181 | }); 182 | 183 | test("Prefix math expressions should be correctly converted to postfix", () => { 184 | const g = peg` 185 | expr: 186 | | $number 187 | | <>operator expr expr ${({ operator, e1, e2 }) => 188 | [e1, e2, operator].join(" ")} 189 | 190 | operator: 191 | "+" | "-" | "*" | "/" 192 | 193 | $number @raw: 194 | [0-9]+ 195 | `; 196 | 197 | expect(g.value("23")).toBe("23"); 198 | expect(g.test("+")).toBe(false); 199 | expect(g.value("+ 1 2")).toBe("1 2 +"); 200 | expect(g.value("* + 1 2 3")).toBe("1 2 + 3 *"); 201 | expect(g.value("+ - 1 25 * / 369 4 5")).toBe("1 25 - 369 4 / 5 * +"); 202 | }); 203 | 204 | test("The cut operator should work correctly", () => { 205 | const g1 = peg`'a' 'b' | 'a' 'c' | 'a' 'd'`; 206 | const g2 = peg`'a' ^ 'b' | 'a' 'c' | 'a' 'd'`; 207 | const g3 = peg`('a' ^ 'b' | 'a' 'c') | 'a' 'd'`; 208 | 209 | expect(g1.test("ab")).toBe(true); 210 | expect(g2.test("ab")).toBe(true); 211 | expect(g3.test("ab")).toBe(true); 212 | expect(g1.test("ac")).toBe(true); 213 | expect(g2.test("ac")).toBe(false); 214 | expect(g3.test("ac")).toBe(false); 215 | expect(g1.test("ad")).toBe(true); 216 | expect(g2.test("ad")).toBe(false); 217 | expect(g3.test("ad")).toBe(true); 218 | }); 219 | 220 | test("Back references should work correctly", () => { 221 | const g = peg`(\d @raw) \*`; 222 | expect(g.test("5")).toBe(true); 223 | expect(g.test("6 6")).toBe(true); 224 | expect(g.test("7 7 6")).toBe(false); 225 | expect(log(g.parse("7 7 6"))) 226 | .toBe(`(1:5) Failure: Expected "7" or end of input 227 | 228 | > 1 | 7 7 6 229 | | ^ 230 | `); 231 | }); 232 | 233 | test("The extension system should work", () => { 234 | const custom = createTag({ 235 | extensions: [ 236 | defaultExtension, 237 | { 238 | directives: { 239 | min: parser => peg`${parser} ${() => Math.min(...$children())}`, 240 | max: parser => 241 | new ActionParser(parser, () => Math.max(...$children())) 242 | } 243 | } 244 | ] 245 | }); 246 | 247 | const max = custom` 248 | list: $int+ @max 249 | $int: \d+ @number 250 | `; 251 | 252 | expect(max.value("36 12 42 3")).toBe(42); 253 | }); 254 | 255 | test("Semantic actions must correctly propagate children (including undefined)", () => { 256 | const g = peg`( 257 | | 0 ${() => $emit([undefined])} 258 | | 1 ${() => 1} 259 | | 2 ${() => undefined} 260 | ) % ','`; 261 | 262 | expect(g.children("0,1,1,2,0,0,1,2,0")).toEqual([ 263 | undefined, 264 | 1, 265 | 1, 266 | undefined, 267 | undefined, 268 | 1, 269 | undefined 270 | ]); 271 | }); 272 | 273 | test("Tracing should work", () => { 274 | const g = peg` 275 | a: b(d) 276 | b(c): c 277 | d: 'a' 278 | `; 279 | 280 | let output = ""; 281 | const saved = console.log; 282 | console.log = (...args) => (output += args.join(" ") + "\n"); 283 | g.test("a", { trace: true }); 284 | console.log = saved; 285 | expect(output).toBe(`Entered "a" at (1:1) 286 | Entered "b" at (1:1) 287 | Entered "c" at (1:1) 288 | Entered "d" at (1:1) 289 | Matched "d" from (1:1) to (1:2) 290 | Matched "c" from (1:1) to (1:2) 291 | Matched "b" from (1:1) to (1:2) 292 | Matched "a" from (1:1) to (1:2) 293 | `); 294 | 295 | /*const traced: Array<{ 296 | event: string; 297 | rule: string; 298 | at: string; 299 | from?: string; 300 | to?: string; 301 | input: string; 302 | }> = []; 303 | 304 | const formatLocation = (loc: Location) => `(${loc.line}:${loc.column})`; 305 | const inputSubstring = (loc: Location) => 306 | loc.input.substring(loc.index, loc.index + 8); 307 | 308 | g.test(" a", { 309 | trace: true, 310 | tracer(event) { 311 | const common = { rule: event.rule, at: formatLocation(event.at) }; 312 | switch (event.type) { 313 | case TraceEventType.Enter: 314 | traced.push({ 315 | event: "Entered", 316 | ...common, 317 | input: inputSubstring(event.at) 318 | }); 319 | break; 320 | case TraceEventType.Match: 321 | traced.push({ 322 | event: "Matched", 323 | ...common, 324 | from: formatLocation(event.from), 325 | to: formatLocation(event.to), 326 | input: inputSubstring(event.from) 327 | }); 328 | break; 329 | case TraceEventType.Fail: 330 | traced.push({ 331 | event: "Failed", 332 | ...common, 333 | input: inputSubstring(event.at) 334 | }); 335 | } 336 | } 337 | }); 338 | 339 | console.table(traced, ["event", "rule", "at", "from", "to", "input"]);*/ 340 | }); 341 | 342 | test("L-attributed grammars should be implementable using context", () => { 343 | const g = peg` 344 | expr: 345 | (num ${() => ($context().acc = $children()[0])}) 346 | exprRest 347 | ${() => $context().acc} 348 | @context(${{ acc: 0 }}) 349 | 350 | exprRest: 351 | | ('-' num ${() => ($context().acc -= $children()[0])}) 352 | exprRest 353 | | ε 354 | 355 | num @number @token("number"): 356 | [0-9]+ 357 | `; 358 | 359 | expect(g.test("")).toBe(false); 360 | expect(g.value("5")).toBe(5); 361 | expect(g.value("42-6")).toBe(36); 362 | expect(g.value(" 13 - 16 -1")).toBe(-4); 363 | expect(g.value("61- 20 -14 - 3")).toBe(24); 364 | }); 365 | 366 | test("Warnings should work correctly", () => { 367 | const g = peg` 368 | class: 369 | 'class' 370 | ($identifier ${() => { 371 | if (!/^[A-Z]/.test($raw())) $warn("Class names should be capitalized"); 372 | }}) 373 | '{' '}' 374 | 375 | $identifier @raw: [a-zA-Z]+ 376 | `; 377 | 378 | expect(log(g.parse("class test {"))) 379 | .toBe(`(1:7) Warning: Class names should be capitalized 380 | 381 | > 1 | class test { 382 | | ^ 383 | 384 | (1:13) Failure: Expected "}" 385 | 386 | > 1 | class test { 387 | | ^ 388 | `); 389 | }); 390 | 391 | test("Grammar fragmentation should work", () => { 392 | const c = peg` 393 | c: 'c' d 394 | `; 395 | const d = peg` 396 | d: 'd' a? 397 | `; 398 | const g1 = peg` 399 | @import(${c}, ${d}) 400 | 401 | a: 'a' b 402 | b: 'b' c 403 | `; 404 | const g2 = peg` 405 | @import(${c}) 406 | @import(${d}) 407 | 408 | a: 'a' b 409 | b: 'b' c 410 | `; 411 | 412 | expect(g1.test("abc")).toBe(false); 413 | expect(g1.test("abcd")).toBe(true); 414 | expect(g1.test("abcdabcc")).toBe(false); 415 | expect(g1.test("abcdabcd")).toBe(true); 416 | expect(g2.test("abc")).toBe(false); 417 | expect(g2.test("abcd")).toBe(true); 418 | expect(g2.test("abcdabcc")).toBe(false); 419 | expect(g2.test("abcdabcd")).toBe(true); 420 | }); 421 | 422 | test("Failure recovery should work", () => { 423 | const g = peg` 424 | bitArray: '[' (bit | sync) % ',' ']' 425 | bit: 0 | 1 426 | sync: @@commit ...&(',' | ']') 427 | `; 428 | 429 | const result = g.parse("[1, 0, 1, 3, 0, 1, 2, 1]"); 430 | 431 | expect(result.success).toBe(true); 432 | expect(log(result)).toBe(`(1:11) Failure: Expected "0" or "1" 433 | 434 | > 1 | [1, 0, 1, 3, 0, 1, 2, 1] 435 | | ^ 436 | 437 | (1:20) Failure: Expected "0" or "1" 438 | 439 | > 1 | [1, 0, 1, 3, 0, 1, 2, 1] 440 | | ^ 441 | `); 442 | }); 443 | 444 | test("Failure heuristic should work correctly", () => { 445 | const g1 = peg`[a-z]+ ${() => { 446 | const val = $context().get($raw()); 447 | if (!val) $fail(`Undeclared identifier "${$raw()}"`); 448 | return val; 449 | }}`; 450 | 451 | const context = new Map([ 452 | ["foo", 42], 453 | ["bar", 18] 454 | ]); 455 | 456 | expect(g1.value("foo", { context })).toBe(42); 457 | expect(log(g1.parse("baz", { context }))) 458 | .toBe(`(1:1) Failure: Undeclared identifier "baz" 459 | 460 | > 1 | baz 461 | | ^ 462 | `); 463 | }); 464 | 465 | test("Hooks should work correctly", () => { 466 | const a = peg`"0" ${() => {}}`; 467 | const b = peg`"1" ${() => { 468 | a.parse("0"); 469 | $warn("This is a warning"); 470 | return $children()[0]; 471 | }}`; 472 | 473 | const r = b.parse("1") as SuccessResult; 474 | expect(r.children).toEqual(["1"]); 475 | expect(log(r)).toBe(`(1:1) Warning: This is a warning 476 | 477 | > 1 | 1 478 | | ^ 479 | `); 480 | }); 481 | 482 | test("Benchmark between Pegase and competitor", () => { 483 | const lowOp = (left: number, op: string, right: number) => { 484 | switch (op) { 485 | case "+": 486 | return left + right; 487 | case "-": 488 | return left - right; 489 | } 490 | }; 491 | 492 | const highOp = (left: number, op: string, right: number) => { 493 | switch (op) { 494 | case "*": 495 | return left * right; 496 | case "/": 497 | return left / right; 498 | } 499 | }; 500 | 501 | const calc = peg` 502 | expr: term % ("+" | "-") @infix(${lowOp}) 503 | term: fact % ("*" | "/") @infix(${highOp}) 504 | fact: '(' expr ')' | $integer 505 | $integer @number: [0-9]+ 506 | `; 507 | 508 | const a = new Date(); 509 | for (let i = 0; i !== 20000; ++i) { 510 | calc.parse("42"); 511 | calc.parse("42 + 63"); 512 | calc.parse("42 + 63 * (12 / 3)"); 513 | } 514 | const b = new Date(); 515 | for (let i = 0; i !== 20000; ++i) { 516 | competitor.parse("42"); 517 | competitor.parse("42 + 63"); 518 | competitor.parse("42 + 63 * (12 / 3)"); 519 | } 520 | const c = new Date(); 521 | const pegaseTime = b.getTime() - a.getTime(); 522 | const competitorTime = c.getTime() - b.getTime(); 523 | console.log( 524 | pegaseTime, 525 | "ms vs.", 526 | competitorTime, 527 | "ms |", 528 | pegaseTime - competitorTime, 529 | "ms" 530 | ); 531 | }); 532 | 533 | test("Benchmark of metaparsing", () => { 534 | const a = new Date(); 535 | for (let i = 0; i !== 1000; ++i) createMetaparser(); 536 | const b = new Date(); 537 | console.log(b.getTime() - a.getTime(), "ms"); 538 | }); 539 | 540 | test("Math expressions should be correctly calculated", () => { 541 | const doop = (left: number, op: string, right: number) => { 542 | switch (op) { 543 | case "+": 544 | return left + right; 545 | case "-": 546 | return left - right; 547 | case "*": 548 | return left * right; 549 | case "/": 550 | return left / right; 551 | } 552 | }; 553 | 554 | const calc = peg` 555 | expr: operation(term, "+" | "-") 556 | term: operation(fact, "*" | "/") 557 | fact: $number | '(' expr ')' 558 | $number @number: 559 | '-'? [0-9]+ ('.' [0-9]*)? 560 | 561 | operation(operand, operator): 562 | operand % operator @infix(${doop}) 563 | `; 564 | 565 | expect(calc.value("2 + 3")).toBe(5); 566 | expect(calc.value("2 * 3")).toBe(6); 567 | expect(calc.value("2 * -3")).toBe(-6); 568 | expect(calc.value("89")).toBe(89); 569 | expect(calc.value("2.53")).toBe(2.53); 570 | expect(calc.value("-1.2")).toBe(-1.2); 571 | expect(calc.test("")).toBe(false); 572 | expect(calc.test("1 +")).toBe(false); 573 | expect(calc.test("(1 +")).toBe(false); 574 | expect(calc.value(" 12 - 8 ")).toBe(4); 575 | expect(calc.value("142 -9 ")).toBe(133); 576 | expect(calc.value("72+ 15")).toBe(87); 577 | expect(calc.value(" 12* 4")).toBe(48); 578 | expect(calc.value(" 50/10 ")).toBe(5); 579 | expect(calc.value("2.53")).toBe(2.53); 580 | expect(calc.value("4*2.5 + 8.5+1.5 / 3.0")).toBe(19); 581 | expect(calc.value("5.0005 + 0.0095")).toBe(5.01); 582 | expect(calc.value("67+2")).toBe(69); 583 | expect(calc.value(" 2-7")).toBe(-5); 584 | expect(calc.value("5*7")).toBe(35); 585 | expect(calc.value("8/4")).toBe(2); 586 | expect(calc.value("2 -4 +6 -1 -1- 0 +8")).toBe(10); 587 | expect(calc.value("1 -1 + 2 - 2 + 4 - 4 + 6")).toBe(6); 588 | expect(calc.value(" 2*3 - 4*5 + 6/3 ")).toBe(-12); 589 | expect(calc.value("2*3*4/8 - 5/2*4 + 6 + 0/3 ")).toBe(-1); 590 | expect(calc.value("10/4")).toBe(2.5); 591 | expect(calc.value("5/3")).toBeCloseTo(1.66666); 592 | expect(calc.value("3 + 8/5 -1 -2*5")).toBeCloseTo(-6.4); 593 | expect(calc.test(" 6 + c")).toBe(false); 594 | expect(calc.test(" 7 & 2")).toBe(false); 595 | expect(calc.test(" % ")).toBe(false); 596 | expect(calc.test(" 5 + + 6")).toBe(false); 597 | expect(calc.value("5/0")).toBe(Infinity); 598 | expect(calc.value("(2)")).toBe(2); 599 | expect(calc.value("(5 + 2*3 - 1 + 7 * 8)")).toBe(66); 600 | expect(calc.value("(67 + 2 * 3 - 67 + 2/1 - 7)")).toBe(1); 601 | expect(calc.value("(2) + (17*2-30) * (5)+2 - (8/2)*4")).toBe(8); 602 | expect(calc.value("(5*7/5) + (23) - 5 * (98-4)/(6*7-42)")).toBe(-Infinity); 603 | expect(calc.value("(((((5)))))")).toBe(5); 604 | expect(calc.value("(( ((2)) + 4))*((5))")).toBe(30); 605 | expect(calc.value("(( ((2)) + 4))*((5) -1) ")).toBe(24); 606 | expect(calc.test("2 + (5 * 2")).toBe(false); 607 | expect(calc.test("(((((4))))")).toBe(false); 608 | expect(calc.test("((((4)))))")).toBe(false); 609 | expect(calc.test("((2)) * ((3")).toBe(false); 610 | expect( 611 | calc.value( 612 | " ( (( ( (485.56) - 318.95) *( 486.17/465.96 - 324.49/-122.8 )+ -422.8) * 167.73+-446.4 *-88.31) -271.61/ ( (( 496.31 / (( -169.3* 453.70) ) )/-52.22 )* (( (-134.9* (-444.1-(( 278.79 * ( -384.5)) ) / (-270.6/ 396.89-( -391.5/150.39- -422.9 )* -489.2 ) )+-38.02 )) )) )" 613 | ) 614 | ).toBeCloseTo(71470.126502); 615 | }); 616 | 617 | test("JSON.parse should be correctly reproduced", () => { 618 | const json = peg` 619 | json: 620 | value $ 621 | 622 | value: 623 | | string 624 | | number 625 | | 'true' ${() => true} 626 | | 'false' ${() => false} 627 | | 'null' ${() => null} 628 | | '[' (value % ',')? ']' ${() => $children()} 629 | | '{' ((string ':' value) % ',')? '}' 630 | ${() => { 631 | const result: any = {}; 632 | for (let i = 0; i < $children().length; i += 2) 633 | result[$children()[i]] = $children()[i + 1]; 634 | return result; 635 | }} 636 | 637 | string @token: 638 | '\"' ([^\"] | '\\'.)* '\"' 639 | ${() => $raw().substring(1, $raw().length - 1)} 640 | 641 | number @token: 642 | '-'? \d+ ('.' \d*)? 643 | ${() => Number($raw())} 644 | `; 645 | 646 | expect(json.value(`"test"`)).toBe("test"); 647 | expect(json.value("true")).toBe(true); 648 | expect(json.value("null")).toBe(null); 649 | expect(json.value("[]")).toEqual([]); 650 | expect(json.value("{}")).toEqual({}); 651 | expect(json.value("[true, null, false]")).toEqual([true, null, false]); 652 | expect(json.value("[[], {}, [[] ]]")).toEqual([[], {}, [[]]]); 653 | expect(json.value(`[{ "pi": 3.14 }]`)).toEqual([{ pi: 3.14 }]); 654 | expect(json.value(`{ "x": {"y" :null }}`)).toEqual({ x: { y: null } }); 655 | expect(json.value(`{"x": 45,"y":false , "z" :[1, "test"] } `)).toEqual({ 656 | x: 45, 657 | y: false, 658 | z: [1, "test"] 659 | }); 660 | 661 | const bigSample = ` 662 | {"web-app": { 663 | "servlet": [ 664 | { 665 | "servlet-name": "cofaxCDS", 666 | "servlet-class": "org.cofax.cds.CDSServlet", 667 | "init-param": { 668 | "configGlossary:installationAt": "Philadelphia, PA", 669 | "configGlossary:adminEmail": "ksm@pobox.com", 670 | "configGlossary:poweredBy": "Cofax", 671 | "configGlossary:poweredByIcon": "/images/cofax.gif", 672 | "configGlossary:staticPath": "/content/static", 673 | "templateProcessorClass": "org.cofax.WysiwygTemplate", 674 | "templateLoaderClass": "org.cofax.FilesTemplateLoader", 675 | "templatePath": "templates", 676 | "templateOverridePath": "", 677 | "defaultListTemplate": "listTemplate.htm", 678 | "defaultFileTemplate": "articleTemplate.htm", 679 | "useJSP": false, 680 | "jspListTemplate": "listTemplate.jsp", 681 | "jspFileTemplate": "articleTemplate.jsp", 682 | "cachePackageTagsTrack": 200, 683 | "cachePackageTagsStore": 200, 684 | "cachePackageTagsRefresh": 60, 685 | "cacheTemplatesTrack": 100, 686 | "cacheTemplatesStore": 50, 687 | "cacheTemplatesRefresh": 15, 688 | "cachePagesTrack": 200, 689 | "cachePagesStore": 100, 690 | "cachePagesRefresh": 10, 691 | "cachePagesDirtyRead": 10, 692 | "searchEngineListTemplate": "forSearchEnginesList.htm", 693 | "searchEngineFileTemplate": "forSearchEngines.htm", 694 | "searchEngineRobotsDb": "WEB-INF/robots.db", 695 | "useDataStore": true, 696 | "dataStoreClass": "org.cofax.SqlDataStore", 697 | "redirectionClass": "org.cofax.SqlRedirection", 698 | "dataStoreName": "cofax", 699 | "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", 700 | "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", 701 | "dataStoreUser": "sa", 702 | "dataStorePassword": "dataStoreTestQuery", 703 | "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", 704 | "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", 705 | "dataStoreInitConns": 10, 706 | "dataStoreMaxConns": 100, 707 | "dataStoreConnUsageLimit": 100, 708 | "dataStoreLogLevel": "debug", 709 | "maxUrlLength": 500}}, 710 | { 711 | "servlet-name": "cofaxEmail", 712 | "servlet-class": "org.cofax.cds.EmailServlet", 713 | "init-param": { 714 | "mailHost": "mail1", 715 | "mailHostOverride": "mail2"}}, 716 | { 717 | "servlet-name": "cofaxAdmin", 718 | "servlet-class": "org.cofax.cds.AdminServlet"}, 719 | 720 | { 721 | "servlet-name": "fileServlet", 722 | "servlet-class": "org.cofax.cds.FileServlet"}, 723 | { 724 | "servlet-name": "cofaxTools", 725 | "servlet-class": "org.cofax.cms.CofaxToolsServlet", 726 | "init-param": { 727 | "templatePath": "toolstemplates/", 728 | "log": 1, 729 | "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", 730 | "logMaxSize": "", 731 | "dataLog": 1, 732 | "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", 733 | "dataLogMaxSize": "", 734 | "removePageCache": "/content/admin/remove?cache=pages&id=", 735 | "removeTemplateCache": "/content/admin/remove?cache=templates&id=", 736 | "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", 737 | "lookInContext": 1, 738 | "adminGroupID": 4, 739 | "betaServer": true}}], 740 | "servlet-mapping": { 741 | "cofaxCDS": "/", 742 | "cofaxEmail": "/cofaxutil/aemail/*", 743 | "cofaxAdmin": "/admin/*", 744 | "fileServlet": "/static/*", 745 | "cofaxTools": "/tools/*"}, 746 | 747 | "taglib": { 748 | "taglib-uri": "cofax.tld", 749 | "taglib-location": "/WEB-INF/tlds/cofax.tld"}}} 750 | `; 751 | 752 | expect(json.value(bigSample)).toEqual(JSON.parse(bigSample)); 753 | }); 754 | -------------------------------------------------------------------------------- /src/utility/hooks.ts: -------------------------------------------------------------------------------- 1 | import { Expectation, ExpectationType, Location, Options } from "../index.js"; 2 | 3 | // Hooks 4 | 5 | export interface Hooks { 6 | $from(): Location; 7 | $to(): Location; 8 | $children(): any[]; 9 | $value(): any; 10 | $raw(): string; 11 | $context(): any; 12 | $options(): Options & Record; 13 | $warn(message: string): void; 14 | $fail(message: string): void; 15 | $expected(expected: ExpectationInput[]): void; 16 | $commit(): void; 17 | $emit(children: any[]): void; 18 | } 19 | 20 | function hook(key: K) { 21 | return new Function( 22 | `return this[this.length-1].${key}.apply(null, arguments)` 23 | ).bind(hooks) as Hooks[K]; 24 | } 25 | 26 | export const hooks: Hooks[] = []; 27 | export const $from = hook("$from"); 28 | export const $to = hook("$to"); 29 | export const $children = hook("$children"); 30 | export const $value = hook("$value"); 31 | export const $raw = hook("$raw"); 32 | export const $context = hook("$context"); 33 | export const $options = hook("$options"); 34 | export const $warn = hook("$warn"); 35 | export const $fail = hook("$fail"); 36 | export const $expected = hook("$expected"); 37 | export const $commit = hook("$commit"); 38 | export const $emit = hook("$emit"); 39 | 40 | // Hooks utilities 41 | 42 | export type ExpectationInput = string | RegExp | Expectation; 43 | 44 | export function castExpectation(expected: ExpectationInput): Expectation { 45 | return typeof expected === "string" 46 | ? { type: ExpectationType.Literal, literal: expected } 47 | : expected instanceof RegExp 48 | ? { type: ExpectationType.RegExp, regex: expected } 49 | : expected; 50 | } 51 | -------------------------------------------------------------------------------- /src/utility/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./location.js"; 2 | export * from "./tracing.js"; 3 | export * from "./hooks.js"; 4 | export * from "./logs.js"; 5 | -------------------------------------------------------------------------------- /src/utility/location.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Location types 3 | */ 4 | 5 | export interface Range { 6 | from: Location; 7 | to: Location; 8 | } 9 | 10 | export interface Location { 11 | input: string; 12 | index: number; 13 | line: number; 14 | column: number; 15 | } 16 | -------------------------------------------------------------------------------- /src/utility/logs.ts: -------------------------------------------------------------------------------- 1 | import { Location, Range } from "../index.js"; 2 | 3 | // Warning 4 | 5 | export enum WarningType { 6 | Message = "MESSAGE" 7 | } 8 | 9 | export interface Warning extends Range { 10 | type: WarningType.Message; 11 | message: string; 12 | } 13 | 14 | // Failure 15 | 16 | export enum FailureType { 17 | Expectation = "EXPECTATION", 18 | Semantic = "SEMANTIC" 19 | } 20 | 21 | export type Failure = ExpectationFailure | SemanticFailure; 22 | 23 | export interface ExpectationFailure extends Range { 24 | type: FailureType.Expectation; 25 | expected: Expectation[]; 26 | } 27 | 28 | export interface SemanticFailure extends Range { 29 | type: FailureType.Semantic; 30 | message: string; 31 | } 32 | 33 | export enum ExpectationType { 34 | Literal = "LITERAL", 35 | RegExp = "REGEXP", 36 | EndOfInput = "END_OF_INPUT", 37 | Token = "TOKEN", 38 | Mismatch = "MISMATCH", 39 | Custom = "CUSTOM" 40 | } 41 | 42 | export type Expectation = 43 | | LiteralExpectation 44 | | RegexExpectation 45 | | EndOfInputExpectation 46 | | TokenExpectation 47 | | MismatchExpectation 48 | | CustomExpectation; 49 | 50 | export interface LiteralExpectation { 51 | type: ExpectationType.Literal; 52 | literal: string; 53 | } 54 | 55 | export interface RegexExpectation { 56 | type: ExpectationType.RegExp; 57 | regex: RegExp; 58 | } 59 | 60 | export interface EndOfInputExpectation { 61 | type: ExpectationType.EndOfInput; 62 | } 63 | 64 | export interface TokenExpectation { 65 | type: ExpectationType.Token; 66 | displayName: string; 67 | } 68 | 69 | export interface MismatchExpectation { 70 | type: ExpectationType.Mismatch; 71 | match: string; 72 | } 73 | 74 | export interface CustomExpectation { 75 | type: ExpectationType.Custom; 76 | display: string; 77 | [field: string]: any; 78 | } 79 | 80 | // Arguments 81 | 82 | export interface Entries { 83 | warnings?: Warning[]; 84 | failures?: Failure[]; 85 | } 86 | 87 | export interface LogOptions { 88 | showWarnings: boolean; 89 | showFailures: boolean; 90 | showCodeFrames: boolean; 91 | linesBefore: number; 92 | linesAfter: number; 93 | } 94 | 95 | /** 96 | * Stringifies a list of entries (warnings and failures) with code frames 97 | * @param entries 98 | * @param options 99 | */ 100 | 101 | export function log(entries: Entries, options?: Partial) { 102 | const opts: LogOptions = { 103 | showWarnings: true, 104 | showFailures: true, 105 | showCodeFrames: true, 106 | linesBefore: 2, 107 | linesAfter: 2, 108 | ...options 109 | }; 110 | const list = [ 111 | ...((opts.showWarnings && entries.warnings) || []), 112 | ...((opts.showFailures && entries.failures) || []) 113 | ]; 114 | if (list.length === 0) return ""; 115 | const lines = list[0].from.input.split(/[\r\n]/); 116 | return list 117 | .sort((a, b) => a.from.index - b.from.index) 118 | .map( 119 | entry => 120 | `(${entry.from.line}:${entry.from.column}) ${entryToString(entry)}${ 121 | opts.showCodeFrames ? `\n\n${codeFrame(lines, entry.from, opts)}` : "" 122 | }` 123 | ) 124 | .join("\n"); 125 | } 126 | 127 | /** 128 | * Stringifies a log entry (a warning or a failure) 129 | * @param entry 130 | */ 131 | 132 | export function entryToString(entry: Warning | Failure) { 133 | switch (entry.type) { 134 | case WarningType.Message: 135 | return `Warning: ${entry.message}`; 136 | case FailureType.Semantic: 137 | return `Failure: ${entry.message}`; 138 | case FailureType.Expectation: 139 | const expectations = unique( 140 | entry.expected.map(expectationToString) 141 | ).reduce( 142 | (acc, expected, index, { length }) => 143 | `${acc}${index === length - 1 ? " or " : ", "}${expected}` 144 | ); 145 | return `Failure: Expected ${expectations}`; 146 | } 147 | } 148 | 149 | /** 150 | * Stringifies an expectation object (literal, regex, token, etc.) 151 | * @param expectation 152 | */ 153 | 154 | export function expectationToString(expectation: Expectation) { 155 | switch (expectation.type) { 156 | case ExpectationType.Literal: 157 | return `"${expectation.literal}"`; 158 | case ExpectationType.RegExp: 159 | return `${expectation.regex.source} (regex)`; 160 | case ExpectationType.EndOfInput: 161 | return "end of input"; 162 | case ExpectationType.Token: 163 | return expectation.displayName; 164 | case ExpectationType.Mismatch: 165 | return `mismatch of "${expectation.match}"`; 166 | case ExpectationType.Custom: 167 | return expectation.display; 168 | } 169 | } 170 | 171 | /** 172 | * Creates a string code frame to highlight a location 173 | * @param lines 174 | * @param location 175 | * @param options 176 | */ 177 | 178 | export function codeFrame( 179 | lines: string[], 180 | location: Location, 181 | options: LogOptions 182 | ) { 183 | const start = Math.max(1, location.line - options.linesBefore); 184 | const end = Math.min(lines.length, location.line + options.linesAfter); 185 | const maxLineNum = String(end).length; 186 | const padding = " ".repeat(maxLineNum); 187 | let acc = ""; 188 | for (let i = start; i !== end + 1; i++) { 189 | const lineNum = (padding + i).slice(-maxLineNum); 190 | const current = lines[i - 1]; 191 | const normalized = current.replace(/\t+/, tabs => " ".repeat(tabs.length)); 192 | if (i !== location.line) acc += ` ${lineNum} | ${normalized}\n`; 193 | else { 194 | const count = Math.max( 195 | 0, 196 | normalized.length - current.length + location.column - 1 197 | ); 198 | acc += `> ${lineNum} | ${normalized}\n`; 199 | acc += ` ${padding} | ${" ".repeat(count)}^\n`; 200 | } 201 | } 202 | return acc; 203 | } 204 | 205 | /** 206 | * Creates a duplicate-free version of an iterable 207 | * @param items 208 | */ 209 | 210 | export function unique(items: Iterable) { 211 | return [...new Set(items)]; 212 | } 213 | -------------------------------------------------------------------------------- /src/utility/tracing.ts: -------------------------------------------------------------------------------- 1 | import { Location, Range } from "../index.js"; 2 | 3 | // Trace events 4 | 5 | export enum TraceEventType { 6 | Enter = "ENTER", 7 | Match = "MATCH", 8 | Fail = "FAIL" 9 | } 10 | 11 | export type TraceEvent = 12 | | EnterEvent 13 | | MatchEvent 14 | | FailEvent; 15 | 16 | export interface EnterEvent extends TraceCommon { 17 | type: TraceEventType.Enter; 18 | } 19 | 20 | export interface MatchEvent extends Range, TraceCommon { 21 | type: TraceEventType.Match; 22 | children: any[]; 23 | } 24 | 25 | export interface FailEvent extends TraceCommon { 26 | type: TraceEventType.Fail; 27 | } 28 | 29 | export interface TraceCommon { 30 | rule: string; 31 | at: Location; 32 | } 33 | 34 | // Tracer signature 35 | 36 | export type Tracer = (event: TraceEvent) => void; 37 | 38 | /** 39 | * A generic console.log tracer to quickly debug a grammar 40 | * @param event 41 | */ 42 | 43 | export const consoleTracer: Tracer = event => { 44 | const { at } = event; 45 | let adjective = ""; 46 | let complement = ""; 47 | switch (event.type) { 48 | case TraceEventType.Enter: 49 | adjective = "Entered"; 50 | complement = `at (${at.line}:${at.column})`; 51 | break; 52 | case TraceEventType.Match: 53 | const { from, to } = event; 54 | adjective = "Matched"; 55 | complement = `from (${from.line}:${from.column}) to (${to.line}:${to.column})`; 56 | break; 57 | case TraceEventType.Fail: 58 | adjective = "Failed"; 59 | complement = `at (${at.line}:${at.column})`; 60 | break; 61 | } 62 | console.log(adjective, `"${event.rule}"`, complement); 63 | }; 64 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "compilerOptions": { 4 | "strict": true, 5 | "target": "ES6", 6 | "module": "ES6", 7 | "moduleResolution": "Node", 8 | "declaration": true, 9 | "removeComments": true, 10 | "lib": ["ESNext", "DOM"], 11 | "outDir": "./dist" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@esbuild/aix-ppc64@0.19.12": 6 | version "0.19.12" 7 | resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz#d1bc06aedb6936b3b6d313bf809a5a40387d2b7f" 8 | integrity sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA== 9 | 10 | "@esbuild/android-arm64@0.19.12": 11 | version "0.19.12" 12 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz#7ad65a36cfdb7e0d429c353e00f680d737c2aed4" 13 | integrity sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA== 14 | 15 | "@esbuild/android-arm@0.19.12": 16 | version "0.19.12" 17 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.12.tgz#b0c26536f37776162ca8bde25e42040c203f2824" 18 | integrity sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w== 19 | 20 | "@esbuild/android-x64@0.19.12": 21 | version "0.19.12" 22 | resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.12.tgz#cb13e2211282012194d89bf3bfe7721273473b3d" 23 | integrity sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew== 24 | 25 | "@esbuild/darwin-arm64@0.19.12": 26 | version "0.19.12" 27 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz#cbee41e988020d4b516e9d9e44dd29200996275e" 28 | integrity sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g== 29 | 30 | "@esbuild/darwin-x64@0.19.12": 31 | version "0.19.12" 32 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz#e37d9633246d52aecf491ee916ece709f9d5f4cd" 33 | integrity sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A== 34 | 35 | "@esbuild/freebsd-arm64@0.19.12": 36 | version "0.19.12" 37 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz#1ee4d8b682ed363b08af74d1ea2b2b4dbba76487" 38 | integrity sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA== 39 | 40 | "@esbuild/freebsd-x64@0.19.12": 41 | version "0.19.12" 42 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz#37a693553d42ff77cd7126764b535fb6cc28a11c" 43 | integrity sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg== 44 | 45 | "@esbuild/linux-arm64@0.19.12": 46 | version "0.19.12" 47 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz#be9b145985ec6c57470e0e051d887b09dddb2d4b" 48 | integrity sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA== 49 | 50 | "@esbuild/linux-arm@0.19.12": 51 | version "0.19.12" 52 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz#207ecd982a8db95f7b5279207d0ff2331acf5eef" 53 | integrity sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w== 54 | 55 | "@esbuild/linux-ia32@0.19.12": 56 | version "0.19.12" 57 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz#d0d86b5ca1562523dc284a6723293a52d5860601" 58 | integrity sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA== 59 | 60 | "@esbuild/linux-loong64@0.19.12": 61 | version "0.19.12" 62 | resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz#9a37f87fec4b8408e682b528391fa22afd952299" 63 | integrity sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA== 64 | 65 | "@esbuild/linux-mips64el@0.19.12": 66 | version "0.19.12" 67 | resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz#4ddebd4e6eeba20b509d8e74c8e30d8ace0b89ec" 68 | integrity sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w== 69 | 70 | "@esbuild/linux-ppc64@0.19.12": 71 | version "0.19.12" 72 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz#adb67dadb73656849f63cd522f5ecb351dd8dee8" 73 | integrity sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg== 74 | 75 | "@esbuild/linux-riscv64@0.19.12": 76 | version "0.19.12" 77 | resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz#11bc0698bf0a2abf8727f1c7ace2112612c15adf" 78 | integrity sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg== 79 | 80 | "@esbuild/linux-s390x@0.19.12": 81 | version "0.19.12" 82 | resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz#e86fb8ffba7c5c92ba91fc3b27ed5a70196c3cc8" 83 | integrity sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg== 84 | 85 | "@esbuild/linux-x64@0.19.12": 86 | version "0.19.12" 87 | resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz#5f37cfdc705aea687dfe5dfbec086a05acfe9c78" 88 | integrity sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg== 89 | 90 | "@esbuild/netbsd-x64@0.19.12": 91 | version "0.19.12" 92 | resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz#29da566a75324e0d0dd7e47519ba2f7ef168657b" 93 | integrity sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA== 94 | 95 | "@esbuild/openbsd-x64@0.19.12": 96 | version "0.19.12" 97 | resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz#306c0acbdb5a99c95be98bdd1d47c916e7dc3ff0" 98 | integrity sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw== 99 | 100 | "@esbuild/sunos-x64@0.19.12": 101 | version "0.19.12" 102 | resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz#0933eaab9af8b9b2c930236f62aae3fc593faf30" 103 | integrity sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA== 104 | 105 | "@esbuild/win32-arm64@0.19.12": 106 | version "0.19.12" 107 | resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz#773bdbaa1971b36db2f6560088639ccd1e6773ae" 108 | integrity sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A== 109 | 110 | "@esbuild/win32-ia32@0.19.12": 111 | version "0.19.12" 112 | resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz#000516cad06354cc84a73f0943a4aa690ef6fd67" 113 | integrity sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ== 114 | 115 | "@esbuild/win32-x64@0.19.12": 116 | version "0.19.12" 117 | resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz#c57c8afbb4054a3ab8317591a0b7320360b444ae" 118 | integrity sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA== 119 | 120 | "@jest/schemas@^29.6.3": 121 | version "29.6.3" 122 | resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" 123 | integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== 124 | dependencies: 125 | "@sinclair/typebox" "^0.27.8" 126 | 127 | "@jridgewell/sourcemap-codec@^1.4.15": 128 | version "1.4.15" 129 | resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" 130 | integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== 131 | 132 | "@rollup/rollup-android-arm-eabi@4.9.6": 133 | version "4.9.6" 134 | resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.6.tgz#66b8d9cb2b3a474d115500f9ebaf43e2126fe496" 135 | integrity sha512-MVNXSSYN6QXOulbHpLMKYi60ppyO13W9my1qogeiAqtjb2yR4LSmfU2+POvDkLzhjYLXz9Rf9+9a3zFHW1Lecg== 136 | 137 | "@rollup/rollup-android-arm64@4.9.6": 138 | version "4.9.6" 139 | resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.6.tgz#46327d5b86420d2307946bec1535fdf00356e47d" 140 | integrity sha512-T14aNLpqJ5wzKNf5jEDpv5zgyIqcpn1MlwCrUXLrwoADr2RkWA0vOWP4XxbO9aiO3dvMCQICZdKeDrFl7UMClw== 141 | 142 | "@rollup/rollup-darwin-arm64@4.9.6": 143 | version "4.9.6" 144 | resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.6.tgz#166987224d2f8b1e2fd28ee90c447d52271d5e90" 145 | integrity sha512-CqNNAyhRkTbo8VVZ5R85X73H3R5NX9ONnKbXuHisGWC0qRbTTxnF1U4V9NafzJbgGM0sHZpdO83pLPzq8uOZFw== 146 | 147 | "@rollup/rollup-darwin-x64@4.9.6": 148 | version "4.9.6" 149 | resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.6.tgz#a2e6e096f74ccea6e2f174454c26aef6bcdd1274" 150 | integrity sha512-zRDtdJuRvA1dc9Mp6BWYqAsU5oeLixdfUvkTHuiYOHwqYuQ4YgSmi6+/lPvSsqc/I0Omw3DdICx4Tfacdzmhog== 151 | 152 | "@rollup/rollup-linux-arm-gnueabihf@4.9.6": 153 | version "4.9.6" 154 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.6.tgz#09fcd4c55a2d6160c5865fec708a8e5287f30515" 155 | integrity sha512-oNk8YXDDnNyG4qlNb6is1ojTOGL/tRhbbKeE/YuccItzerEZT68Z9gHrY3ROh7axDc974+zYAPxK5SH0j/G+QQ== 156 | 157 | "@rollup/rollup-linux-arm64-gnu@4.9.6": 158 | version "4.9.6" 159 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.6.tgz#19a3c0b6315c747ca9acf86e9b710cc2440f83c9" 160 | integrity sha512-Z3O60yxPtuCYobrtzjo0wlmvDdx2qZfeAWTyfOjEDqd08kthDKexLpV97KfAeUXPosENKd8uyJMRDfFMxcYkDQ== 161 | 162 | "@rollup/rollup-linux-arm64-musl@4.9.6": 163 | version "4.9.6" 164 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.6.tgz#94aaf95fdaf2ad9335983a4552759f98e6b2e850" 165 | integrity sha512-gpiG0qQJNdYEVad+1iAsGAbgAnZ8j07FapmnIAQgODKcOTjLEWM9sRb+MbQyVsYCnA0Im6M6QIq6ax7liws6eQ== 166 | 167 | "@rollup/rollup-linux-riscv64-gnu@4.9.6": 168 | version "4.9.6" 169 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.6.tgz#160510e63f4b12618af4013bddf1761cf9fc9880" 170 | integrity sha512-+uCOcvVmFUYvVDr27aiyun9WgZk0tXe7ThuzoUTAukZJOwS5MrGbmSlNOhx1j80GdpqbOty05XqSl5w4dQvcOA== 171 | 172 | "@rollup/rollup-linux-x64-gnu@4.9.6": 173 | version "4.9.6" 174 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.6.tgz#5ac5d068ce0726bd0a96ca260d5bd93721c0cb98" 175 | integrity sha512-HUNqM32dGzfBKuaDUBqFB7tP6VMN74eLZ33Q9Y1TBqRDn+qDonkAUyKWwF9BR9unV7QUzffLnz9GrnKvMqC/fw== 176 | 177 | "@rollup/rollup-linux-x64-musl@4.9.6": 178 | version "4.9.6" 179 | resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.6.tgz#bafa759ab43e8eab9edf242a8259ffb4f2a57a5d" 180 | integrity sha512-ch7M+9Tr5R4FK40FHQk8VnML0Szi2KRujUgHXd/HjuH9ifH72GUmw6lStZBo3c3GB82vHa0ZoUfjfcM7JiiMrQ== 181 | 182 | "@rollup/rollup-win32-arm64-msvc@4.9.6": 183 | version "4.9.6" 184 | resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.6.tgz#1cc3416682e5a20d8f088f26657e6e47f8db468e" 185 | integrity sha512-VD6qnR99dhmTQ1mJhIzXsRcTBvTjbfbGGwKAHcu+52cVl15AC/kplkhxzW/uT0Xl62Y/meBKDZvoJSJN+vTeGA== 186 | 187 | "@rollup/rollup-win32-ia32-msvc@4.9.6": 188 | version "4.9.6" 189 | resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.6.tgz#7d2251e1aa5e8a1e47c86891fe4547a939503461" 190 | integrity sha512-J9AFDq/xiRI58eR2NIDfyVmTYGyIZmRcvcAoJ48oDld/NTR8wyiPUu2X/v1navJ+N/FGg68LEbX3Ejd6l8B7MQ== 191 | 192 | "@rollup/rollup-win32-x64-msvc@4.9.6": 193 | version "4.9.6" 194 | resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.6.tgz#2c1fb69e02a3f1506f52698cfdc3a8b6386df9a6" 195 | integrity sha512-jqzNLhNDvIZOrt69Ce4UjGRpXJBzhUBzawMwnaDAwyHriki3XollsewxWzOzz+4yOFDkuJHtTsZFwMxhYJWmLQ== 196 | 197 | "@sinclair/typebox@^0.27.8": 198 | version "0.27.8" 199 | resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" 200 | integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== 201 | 202 | "@types/estree@1.0.5", "@types/estree@^1.0.0": 203 | version "1.0.5" 204 | resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" 205 | integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== 206 | 207 | "@types/prettier@^3.0.0": 208 | version "3.0.0" 209 | resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-3.0.0.tgz#e9bc8160230d3a461dab5c5b41cceef1ef723057" 210 | integrity sha512-mFMBfMOz8QxhYVbuINtswBp9VL2b4Y0QqYHwqLz3YbgtfAcat2Dl6Y1o4e22S/OVE6Ebl9m7wWiMT2lSbAs1wA== 211 | dependencies: 212 | prettier "*" 213 | 214 | "@vitest/expect@1.2.2": 215 | version "1.2.2" 216 | resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-1.2.2.tgz#39ea22e849bbf404b7e5272786551aa99e2663d0" 217 | integrity sha512-3jpcdPAD7LwHUUiT2pZTj2U82I2Tcgg2oVPvKxhn6mDI2On6tfvPQTjAI4628GUGDZrCm4Zna9iQHm5cEexOAg== 218 | dependencies: 219 | "@vitest/spy" "1.2.2" 220 | "@vitest/utils" "1.2.2" 221 | chai "^4.3.10" 222 | 223 | "@vitest/runner@1.2.2": 224 | version "1.2.2" 225 | resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-1.2.2.tgz#8b060a56ecf8b3d607b044d79f5f50d3cd9fee2f" 226 | integrity sha512-JctG7QZ4LSDXr5CsUweFgcpEvrcxOV1Gft7uHrvkQ+fsAVylmWQvnaAr/HDp3LAH1fztGMQZugIheTWjaGzYIg== 227 | dependencies: 228 | "@vitest/utils" "1.2.2" 229 | p-limit "^5.0.0" 230 | pathe "^1.1.1" 231 | 232 | "@vitest/snapshot@1.2.2": 233 | version "1.2.2" 234 | resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-1.2.2.tgz#f56fd575569774968f3eeba9382a166c26201042" 235 | integrity sha512-SmGY4saEw1+bwE1th6S/cZmPxz/Q4JWsl7LvbQIky2tKE35US4gd0Mjzqfr84/4OD0tikGWaWdMja/nWL5NIPA== 236 | dependencies: 237 | magic-string "^0.30.5" 238 | pathe "^1.1.1" 239 | pretty-format "^29.7.0" 240 | 241 | "@vitest/spy@1.2.2": 242 | version "1.2.2" 243 | resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-1.2.2.tgz#8fc2aeccb96cecbbdd192c643729bd5f97a01c86" 244 | integrity sha512-k9Gcahssw8d7X3pSLq3e3XEu/0L78mUkCjivUqCQeXJm9clfXR/Td8+AP+VC1O6fKPIDLcHDTAmBOINVuv6+7g== 245 | dependencies: 246 | tinyspy "^2.2.0" 247 | 248 | "@vitest/utils@1.2.2": 249 | version "1.2.2" 250 | resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-1.2.2.tgz#94b5a1bd8745ac28cf220a99a8719efea1bcfc83" 251 | integrity sha512-WKITBHLsBHlpjnDQahr+XK6RE7MiAsgrIkr0pGhQ9ygoxBfUeG0lUG5iLlzqjmKSlBv3+j5EGsriBzh+C3Tq9g== 252 | dependencies: 253 | diff-sequences "^29.6.3" 254 | estree-walker "^3.0.3" 255 | loupe "^2.3.7" 256 | pretty-format "^29.7.0" 257 | 258 | acorn-walk@^8.3.2: 259 | version "8.3.2" 260 | resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" 261 | integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== 262 | 263 | acorn@^8.10.0, acorn@^8.11.3: 264 | version "8.11.3" 265 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" 266 | integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== 267 | 268 | ansi-styles@^5.0.0: 269 | version "5.2.0" 270 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" 271 | integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== 272 | 273 | assertion-error@^1.1.0: 274 | version "1.1.0" 275 | resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" 276 | integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== 277 | 278 | cac@^6.7.14: 279 | version "6.7.14" 280 | resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" 281 | integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== 282 | 283 | chai@^4.3.10: 284 | version "4.4.1" 285 | resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1" 286 | integrity sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g== 287 | dependencies: 288 | assertion-error "^1.1.0" 289 | check-error "^1.0.3" 290 | deep-eql "^4.1.3" 291 | get-func-name "^2.0.2" 292 | loupe "^2.3.6" 293 | pathval "^1.1.1" 294 | type-detect "^4.0.8" 295 | 296 | check-error@^1.0.3: 297 | version "1.0.3" 298 | resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" 299 | integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== 300 | dependencies: 301 | get-func-name "^2.0.2" 302 | 303 | cross-spawn@^7.0.3: 304 | version "7.0.3" 305 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" 306 | integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== 307 | dependencies: 308 | path-key "^3.1.0" 309 | shebang-command "^2.0.0" 310 | which "^2.0.1" 311 | 312 | debug@^4.3.4: 313 | version "4.3.4" 314 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" 315 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== 316 | dependencies: 317 | ms "2.1.2" 318 | 319 | deep-eql@^4.1.3: 320 | version "4.1.3" 321 | resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" 322 | integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== 323 | dependencies: 324 | type-detect "^4.0.0" 325 | 326 | diff-sequences@^29.6.3: 327 | version "29.6.3" 328 | resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" 329 | integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== 330 | 331 | esbuild@^0.19.3: 332 | version "0.19.12" 333 | resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.12.tgz#dc82ee5dc79e82f5a5c3b4323a2a641827db3e04" 334 | integrity sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg== 335 | optionalDependencies: 336 | "@esbuild/aix-ppc64" "0.19.12" 337 | "@esbuild/android-arm" "0.19.12" 338 | "@esbuild/android-arm64" "0.19.12" 339 | "@esbuild/android-x64" "0.19.12" 340 | "@esbuild/darwin-arm64" "0.19.12" 341 | "@esbuild/darwin-x64" "0.19.12" 342 | "@esbuild/freebsd-arm64" "0.19.12" 343 | "@esbuild/freebsd-x64" "0.19.12" 344 | "@esbuild/linux-arm" "0.19.12" 345 | "@esbuild/linux-arm64" "0.19.12" 346 | "@esbuild/linux-ia32" "0.19.12" 347 | "@esbuild/linux-loong64" "0.19.12" 348 | "@esbuild/linux-mips64el" "0.19.12" 349 | "@esbuild/linux-ppc64" "0.19.12" 350 | "@esbuild/linux-riscv64" "0.19.12" 351 | "@esbuild/linux-s390x" "0.19.12" 352 | "@esbuild/linux-x64" "0.19.12" 353 | "@esbuild/netbsd-x64" "0.19.12" 354 | "@esbuild/openbsd-x64" "0.19.12" 355 | "@esbuild/sunos-x64" "0.19.12" 356 | "@esbuild/win32-arm64" "0.19.12" 357 | "@esbuild/win32-ia32" "0.19.12" 358 | "@esbuild/win32-x64" "0.19.12" 359 | 360 | estree-walker@^3.0.3: 361 | version "3.0.3" 362 | resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" 363 | integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== 364 | dependencies: 365 | "@types/estree" "^1.0.0" 366 | 367 | execa@^8.0.1: 368 | version "8.0.1" 369 | resolved "https://registry.yarnpkg.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" 370 | integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== 371 | dependencies: 372 | cross-spawn "^7.0.3" 373 | get-stream "^8.0.1" 374 | human-signals "^5.0.0" 375 | is-stream "^3.0.0" 376 | merge-stream "^2.0.0" 377 | npm-run-path "^5.1.0" 378 | onetime "^6.0.0" 379 | signal-exit "^4.1.0" 380 | strip-final-newline "^3.0.0" 381 | 382 | fsevents@~2.3.2, fsevents@~2.3.3: 383 | version "2.3.3" 384 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" 385 | integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== 386 | 387 | get-func-name@^2.0.1, get-func-name@^2.0.2: 388 | version "2.0.2" 389 | resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" 390 | integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== 391 | 392 | get-stream@^8.0.1: 393 | version "8.0.1" 394 | resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" 395 | integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== 396 | 397 | human-signals@^5.0.0: 398 | version "5.0.0" 399 | resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" 400 | integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== 401 | 402 | is-stream@^3.0.0: 403 | version "3.0.0" 404 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" 405 | integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== 406 | 407 | isexe@^2.0.0: 408 | version "2.0.0" 409 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 410 | integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== 411 | 412 | jsonc-parser@^3.2.0: 413 | version "3.2.1" 414 | resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz#031904571ccf929d7670ee8c547545081cb37f1a" 415 | integrity sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA== 416 | 417 | local-pkg@^0.5.0: 418 | version "0.5.0" 419 | resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.5.0.tgz#093d25a346bae59a99f80e75f6e9d36d7e8c925c" 420 | integrity sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg== 421 | dependencies: 422 | mlly "^1.4.2" 423 | pkg-types "^1.0.3" 424 | 425 | loupe@^2.3.6, loupe@^2.3.7: 426 | version "2.3.7" 427 | resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697" 428 | integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== 429 | dependencies: 430 | get-func-name "^2.0.1" 431 | 432 | magic-string@^0.30.5: 433 | version "0.30.5" 434 | resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.5.tgz#1994d980bd1c8835dc6e78db7cbd4ae4f24746f9" 435 | integrity sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA== 436 | dependencies: 437 | "@jridgewell/sourcemap-codec" "^1.4.15" 438 | 439 | merge-stream@^2.0.0: 440 | version "2.0.0" 441 | resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" 442 | integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== 443 | 444 | mimic-fn@^4.0.0: 445 | version "4.0.0" 446 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" 447 | integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== 448 | 449 | mlly@^1.2.0, mlly@^1.4.2: 450 | version "1.5.0" 451 | resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.5.0.tgz#8428a4617d54cc083d3009030ac79739a0e5447a" 452 | integrity sha512-NPVQvAY1xr1QoVeG0cy8yUYC7FQcOx6evl/RjT1wL5FvzPnzOysoqB/jmx/DhssT2dYa8nxECLAaFI/+gVLhDQ== 453 | dependencies: 454 | acorn "^8.11.3" 455 | pathe "^1.1.2" 456 | pkg-types "^1.0.3" 457 | ufo "^1.3.2" 458 | 459 | ms@2.1.2: 460 | version "2.1.2" 461 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 462 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 463 | 464 | nanoid@^3.3.7: 465 | version "3.3.7" 466 | resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" 467 | integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== 468 | 469 | npm-run-path@^5.1.0: 470 | version "5.2.0" 471 | resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.2.0.tgz#224cdd22c755560253dd71b83a1ef2f758b2e955" 472 | integrity sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg== 473 | dependencies: 474 | path-key "^4.0.0" 475 | 476 | onetime@^6.0.0: 477 | version "6.0.0" 478 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" 479 | integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== 480 | dependencies: 481 | mimic-fn "^4.0.0" 482 | 483 | p-limit@^5.0.0: 484 | version "5.0.0" 485 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-5.0.0.tgz#6946d5b7140b649b7a33a027d89b4c625b3a5985" 486 | integrity sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ== 487 | dependencies: 488 | yocto-queue "^1.0.0" 489 | 490 | path-key@^3.1.0: 491 | version "3.1.1" 492 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" 493 | integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== 494 | 495 | path-key@^4.0.0: 496 | version "4.0.0" 497 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" 498 | integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== 499 | 500 | pathe@^1.1.0, pathe@^1.1.1, pathe@^1.1.2: 501 | version "1.1.2" 502 | resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec" 503 | integrity sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ== 504 | 505 | pathval@^1.1.1: 506 | version "1.1.1" 507 | resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" 508 | integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== 509 | 510 | picocolors@^1.0.0: 511 | version "1.0.0" 512 | resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" 513 | integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== 514 | 515 | pkg-types@^1.0.3: 516 | version "1.0.3" 517 | resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.0.3.tgz#988b42ab19254c01614d13f4f65a2cfc7880f868" 518 | integrity sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A== 519 | dependencies: 520 | jsonc-parser "^3.2.0" 521 | mlly "^1.2.0" 522 | pathe "^1.1.0" 523 | 524 | postcss@^8.4.32: 525 | version "8.4.33" 526 | resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.33.tgz#1378e859c9f69bf6f638b990a0212f43e2aaa742" 527 | integrity sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg== 528 | dependencies: 529 | nanoid "^3.3.7" 530 | picocolors "^1.0.0" 531 | source-map-js "^1.0.2" 532 | 533 | prettier@*, prettier@^3.2.4: 534 | version "3.2.4" 535 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.4.tgz#4723cadeac2ce7c9227de758e5ff9b14e075f283" 536 | integrity sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ== 537 | 538 | pretty-format@^29.7.0: 539 | version "29.7.0" 540 | resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" 541 | integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== 542 | dependencies: 543 | "@jest/schemas" "^29.6.3" 544 | ansi-styles "^5.0.0" 545 | react-is "^18.0.0" 546 | 547 | react-is@^18.0.0: 548 | version "18.2.0" 549 | resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" 550 | integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== 551 | 552 | rollup@^4.2.0: 553 | version "4.9.6" 554 | resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.9.6.tgz#4515facb0318ecca254a2ee1315e22e09efc50a0" 555 | integrity sha512-05lzkCS2uASX0CiLFybYfVkwNbKZG5NFQ6Go0VWyogFTXXbR039UVsegViTntkk4OglHBdF54ccApXRRuXRbsg== 556 | dependencies: 557 | "@types/estree" "1.0.5" 558 | optionalDependencies: 559 | "@rollup/rollup-android-arm-eabi" "4.9.6" 560 | "@rollup/rollup-android-arm64" "4.9.6" 561 | "@rollup/rollup-darwin-arm64" "4.9.6" 562 | "@rollup/rollup-darwin-x64" "4.9.6" 563 | "@rollup/rollup-linux-arm-gnueabihf" "4.9.6" 564 | "@rollup/rollup-linux-arm64-gnu" "4.9.6" 565 | "@rollup/rollup-linux-arm64-musl" "4.9.6" 566 | "@rollup/rollup-linux-riscv64-gnu" "4.9.6" 567 | "@rollup/rollup-linux-x64-gnu" "4.9.6" 568 | "@rollup/rollup-linux-x64-musl" "4.9.6" 569 | "@rollup/rollup-win32-arm64-msvc" "4.9.6" 570 | "@rollup/rollup-win32-ia32-msvc" "4.9.6" 571 | "@rollup/rollup-win32-x64-msvc" "4.9.6" 572 | fsevents "~2.3.2" 573 | 574 | shebang-command@^2.0.0: 575 | version "2.0.0" 576 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" 577 | integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== 578 | dependencies: 579 | shebang-regex "^3.0.0" 580 | 581 | shebang-regex@^3.0.0: 582 | version "3.0.0" 583 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" 584 | integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== 585 | 586 | siginfo@^2.0.0: 587 | version "2.0.0" 588 | resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" 589 | integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== 590 | 591 | signal-exit@^4.1.0: 592 | version "4.1.0" 593 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" 594 | integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== 595 | 596 | source-map-js@^1.0.2: 597 | version "1.0.2" 598 | resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" 599 | integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== 600 | 601 | stackback@0.0.2: 602 | version "0.0.2" 603 | resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" 604 | integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== 605 | 606 | std-env@^3.5.0: 607 | version "3.7.0" 608 | resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2" 609 | integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg== 610 | 611 | strip-final-newline@^3.0.0: 612 | version "3.0.0" 613 | resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" 614 | integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== 615 | 616 | strip-literal@^1.3.0: 617 | version "1.3.0" 618 | resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-1.3.0.tgz#db3942c2ec1699e6836ad230090b84bb458e3a07" 619 | integrity sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg== 620 | dependencies: 621 | acorn "^8.10.0" 622 | 623 | tinybench@^2.5.1: 624 | version "2.6.0" 625 | resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.6.0.tgz#1423284ee22de07c91b3752c048d2764714b341b" 626 | integrity sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA== 627 | 628 | tinypool@^0.8.2: 629 | version "0.8.2" 630 | resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.8.2.tgz#84013b03dc69dacb322563a475d4c0a9be00f82a" 631 | integrity sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ== 632 | 633 | tinyspy@^2.2.0: 634 | version "2.2.0" 635 | resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.2.0.tgz#9dc04b072746520b432f77ea2c2d17933de5d6ce" 636 | integrity sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg== 637 | 638 | type-detect@^4.0.0, type-detect@^4.0.8: 639 | version "4.0.8" 640 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" 641 | integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== 642 | 643 | typescript@^5.3.3: 644 | version "5.3.3" 645 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" 646 | integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== 647 | 648 | ufo@^1.3.2: 649 | version "1.3.2" 650 | resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.3.2.tgz#c7d719d0628a1c80c006d2240e0d169f6e3c0496" 651 | integrity sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA== 652 | 653 | vite-node@1.2.2: 654 | version "1.2.2" 655 | resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-1.2.2.tgz#f6d329b06f9032130ae6eac1dc773f3663903c25" 656 | integrity sha512-1as4rDTgVWJO3n1uHmUYqq7nsFgINQ9u+mRcXpjeOMJUmviqNKjcZB7UfRZrlM7MjYXMKpuWp5oGkjaFLnjawg== 657 | dependencies: 658 | cac "^6.7.14" 659 | debug "^4.3.4" 660 | pathe "^1.1.1" 661 | picocolors "^1.0.0" 662 | vite "^5.0.0" 663 | 664 | vite@^5.0.0: 665 | version "5.0.12" 666 | resolved "https://registry.yarnpkg.com/vite/-/vite-5.0.12.tgz#8a2ffd4da36c132aec4adafe05d7adde38333c47" 667 | integrity sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w== 668 | dependencies: 669 | esbuild "^0.19.3" 670 | postcss "^8.4.32" 671 | rollup "^4.2.0" 672 | optionalDependencies: 673 | fsevents "~2.3.3" 674 | 675 | vitest@^1.2.2: 676 | version "1.2.2" 677 | resolved "https://registry.yarnpkg.com/vitest/-/vitest-1.2.2.tgz#9e29ad2a74a5df553c30c5798c57a062d58ce299" 678 | integrity sha512-d5Ouvrnms3GD9USIK36KG8OZ5bEvKEkITFtnGv56HFaSlbItJuYr7hv2Lkn903+AvRAgSixiamozUVfORUekjw== 679 | dependencies: 680 | "@vitest/expect" "1.2.2" 681 | "@vitest/runner" "1.2.2" 682 | "@vitest/snapshot" "1.2.2" 683 | "@vitest/spy" "1.2.2" 684 | "@vitest/utils" "1.2.2" 685 | acorn-walk "^8.3.2" 686 | cac "^6.7.14" 687 | chai "^4.3.10" 688 | debug "^4.3.4" 689 | execa "^8.0.1" 690 | local-pkg "^0.5.0" 691 | magic-string "^0.30.5" 692 | pathe "^1.1.1" 693 | picocolors "^1.0.0" 694 | std-env "^3.5.0" 695 | strip-literal "^1.3.0" 696 | tinybench "^2.5.1" 697 | tinypool "^0.8.2" 698 | vite "^5.0.0" 699 | vite-node "1.2.2" 700 | why-is-node-running "^2.2.2" 701 | 702 | which@^2.0.1: 703 | version "2.0.2" 704 | resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" 705 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== 706 | dependencies: 707 | isexe "^2.0.0" 708 | 709 | why-is-node-running@^2.2.2: 710 | version "2.2.2" 711 | resolved "https://registry.yarnpkg.com/why-is-node-running/-/why-is-node-running-2.2.2.tgz#4185b2b4699117819e7154594271e7e344c9973e" 712 | integrity sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA== 713 | dependencies: 714 | siginfo "^2.0.0" 715 | stackback "0.0.2" 716 | 717 | yocto-queue@^1.0.0: 718 | version "1.0.0" 719 | resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" 720 | integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== 721 | --------------------------------------------------------------------------------