├── README.md ├── web ├── src │ ├── vite-env.d.ts │ ├── lib │ │ ├── unsafeEntries.ts │ │ ├── cx.ts │ │ └── router.ts │ ├── main.tsx │ ├── index.css │ ├── colors.ts │ ├── error.tsx │ ├── lexer.tsx │ ├── Editor.tsx │ ├── ast.tsx │ ├── App.tsx │ └── evaluator.tsx ├── public │ ├── og.png │ └── favicon.ico ├── postcss.config.js ├── tailwind.config.js ├── tsconfig.node.json ├── vite.config.ts ├── .gitignore ├── .eslintrc.cjs ├── tsconfig.json ├── package.json └── index.html ├── pnpm-workspace.yaml ├── vercel.json ├── .gitignore ├── core ├── src │ ├── index.ts │ ├── test │ │ └── test-utils.ts │ ├── printer.ts │ ├── lexer.ts │ ├── ast.ts │ ├── factory.ts │ ├── interpreter.spec.ts │ ├── lexer.spec.ts │ ├── interpreter.ts │ ├── parser.ts │ └── parser.spec.ts ├── tsconfig.json ├── package.json └── pnpm-lock.yaml ├── tsconfig.json └── package.json /README.md: -------------------------------------------------------------------------------- 1 | # olang 2 | -------------------------------------------------------------------------------- /web/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "./web" 3 | - "./core" 4 | -------------------------------------------------------------------------------- /web/public/og.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beerose/olang/HEAD/web/public/og.png -------------------------------------------------------------------------------- /web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beerose/olang/HEAD/web/public/favicon.ico -------------------------------------------------------------------------------- /web/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [ 3 | { 4 | "source": "/(.*)", 5 | "destination": "/index.html" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /web/src/lib/unsafeEntries.ts: -------------------------------------------------------------------------------- 1 | export const unsafeEntries = Object.entries as (o: T) => { 2 | [K in keyof T]: [K, T[K]]; 3 | }[keyof T][]; 4 | -------------------------------------------------------------------------------- /web/src/lib/cx.ts: -------------------------------------------------------------------------------- 1 | export function cx(...classNames: (string | null | false | undefined)[]) { 2 | return classNames.filter(Boolean).join(" "); 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/dist/ 2 | node_modules/ 3 | pnpm-debug.log* 4 | 5 | .DS_Store 6 | 7 | .env.local 8 | .env.*.local 9 | 10 | *.tgz 11 | 12 | TODO 13 | 14 | .vercel -------------------------------------------------------------------------------- /core/src/index.ts: -------------------------------------------------------------------------------- 1 | export * as ast from "./ast" 2 | export * from "./parser" 3 | export * from "./lexer" 4 | export * from "./printer" 5 | export * from "./interpreter" 6 | -------------------------------------------------------------------------------- /web/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ["./src/*.tsx"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /web/src/main.tsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from "react-dom/client" 2 | import { App } from "./App.tsx" 3 | import "./index.css" 4 | 5 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( 6 | 7 | ) 8 | -------------------------------------------------------------------------------- /core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "noUnusedLocals": false, 5 | "outDir": "dist" 6 | }, 7 | "include": ["./src/*"], 8 | "exclude": ["node_modules", "dist", "src/**/*.spec.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /web/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /web/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | import vercel from 'vite-plugin-vercel' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react(), vercel()], 8 | base: '/', 9 | }) 10 | -------------------------------------------------------------------------------- /web/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | #root { 6 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 7 | line-height: 1.5; 8 | font-weight: 400; 9 | 10 | font-size: 16px; 11 | 12 | width: 100%; 13 | height: 100%; 14 | } 15 | -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /web/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { browser: true, es2020: true }, 3 | extends: [ 4 | 'eslint:recommended', 5 | 'plugin:@typescript-eslint/recommended', 6 | 'plugin:react-hooks/recommended', 7 | ], 8 | parser: '@typescript-eslint/parser', 9 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 10 | plugins: ['react-refresh'], 11 | rules: { 12 | 'react-refresh/only-export-components': 'warn', 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/strictest/tsconfig.json", 3 | "compilerOptions": { 4 | "module": "ESNext", 5 | "target": "ES2022", 6 | "moduleResolution": "bundler", 7 | "strict": true, 8 | "noUncheckedIndexedAccess": true, 9 | "outDir": "dist", 10 | "declaration": true, 11 | "skipLibCheck": true, 12 | "importHelpers": true 13 | }, 14 | "include": ["core/**/*", "web/**/*"], 15 | "exclude": ["node_modules", "dist"] 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "olang", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "vitest run", 8 | "test:watch": "vitest", 9 | "build": "pnpm -r run build", 10 | "start": "pnpm -r run preview", 11 | "dev": "pnpm -r run dev" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "@tsconfig/strictest": "^2.0.1", 18 | "typescript": "^5.1.6", 19 | "vitest": "^0.32.2" 20 | } 21 | } -------------------------------------------------------------------------------- /core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@olang/core", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "files": [ 8 | "dist" 9 | ], 10 | "scripts": { 11 | "build": "tsc -p tsconfig.json", 12 | "test": "vitest run", 13 | "test:watch": "vitest" 14 | }, 15 | "dependencies": { 16 | "tsup": "^7.2.0", 17 | "typescript": "^5.1.6", 18 | "typescript-parsec": "^0.3.4", 19 | "vitest": "^0.32.2" 20 | }, 21 | "keywords": [], 22 | "author": "", 23 | "license": "ISC" 24 | } -------------------------------------------------------------------------------- /web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "target": "ES2020", 5 | "useDefineForClassFields": true, 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "module": "ESNext", 8 | "skipLibCheck": true, 9 | 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | "noFallthroughCasesInSwitch": true 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /core/src/test/test-utils.ts: -------------------------------------------------------------------------------- 1 | import { expectEOF, expectSingleResult } from "typescript-parsec" 2 | import { Program } from "../parser" 3 | import { lexer } from "../lexer" 4 | 5 | export function parse(input: string) { 6 | let parserOutput = Program.parse(lexer.parse(input)) 7 | try { 8 | return expectSingleResult(expectEOF(parserOutput)) 9 | } catch (err) { 10 | console.error(err) 11 | if ( 12 | typeof err === "object" && 13 | err !== null && 14 | "errorMessage" in err && 15 | err.errorMessage === "Multiple results are returned." 16 | ) { 17 | debugger 18 | console.dir({ parserOutput }, { depth: 3 }) 19 | } 20 | 21 | throw err 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /core/src/printer.ts: -------------------------------------------------------------------------------- 1 | import * as ast from "./ast" 2 | 3 | export function printAst(node: ast.Node): string { 4 | switch (node.kind) { 5 | case "Program": 6 | return node.statements.map(printAst).join("\n") 7 | case "BinaryExpression": 8 | return `(${printAst(node.left)} ${node.operator} ${printAst(node.right)})` 9 | case "UnaryExpression": 10 | return `(-${printAst(node.operand)})` 11 | case "NumericLiteral": 12 | return `${node.value}` 13 | case "Identifier": 14 | return node.name 15 | case "VariableDeclaration": 16 | return `let ${node.name.name} = ${printAst(node.initializer)}` 17 | case "FunctionExpression": 18 | return `(${node.parameters.map(printAst).join(", ")}) => {\n${node.body 19 | .map(printAst) 20 | .map((line) => ` ${line}`) 21 | .join("\n")}\n}` 22 | case "FunctionCall": 23 | return `${node.name.name}(${node.arguments.map(printAst).join(", ")})` 24 | case "PrintExpression": 25 | return `${printAst(node.expression)}` 26 | case "ArrayExpression": 27 | return `[${node.elements.map(printAst).join(", ")}]` 28 | default: 29 | const _exhaustiveCheck: never = node 30 | throw new Error(`Unhandled node kind: ${(node as ast.Node).kind}`) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /web/src/lib/router.ts: -------------------------------------------------------------------------------- 1 | import { useSyncExternalStore } from "react"; 2 | 3 | let url: URL; 4 | const listeners: (() => void)[] = []; 5 | 6 | function createVerySimpleRouter() { 7 | const pushState = window.history.pushState; 8 | history.pushState = (data, _unused, uri) => { 9 | pushState.call(history, data, "", uri); 10 | url = new URL(uri || window.location.href); 11 | listeners.forEach((listener) => listener()); 12 | }; 13 | 14 | window.addEventListener("popstate", () => { 15 | url = new URL(window.location.href); 16 | listeners.forEach((listener) => listener()); 17 | }); 18 | 19 | window.addEventListener("click", (event) => { 20 | if (!(event.target instanceof HTMLAnchorElement)) return; 21 | event.preventDefault(); 22 | history.pushState({}, "", event.target.href); 23 | }); 24 | } 25 | 26 | if (typeof window !== "undefined") { 27 | url = new URL(window.location.href); 28 | createVerySimpleRouter(); 29 | } 30 | 31 | const subscribe = (listener: () => void) => { 32 | listeners.push(listener); 33 | return () => { 34 | const index = listeners.indexOf(listener); 35 | if (index > -1) { 36 | listeners.splice(index, 1); 37 | } 38 | }; 39 | }; 40 | 41 | const getSnapshot = (): URL => { 42 | return url; 43 | }; 44 | 45 | export function useRouter() { 46 | return useSyncExternalStore(subscribe, getSnapshot, getSnapshot); 47 | } 48 | -------------------------------------------------------------------------------- /web/src/colors.ts: -------------------------------------------------------------------------------- 1 | import { ast, TokenKind } from "@olang/core" 2 | 3 | export const tokenColors: { [key in TokenKind]: string } = { 4 | [TokenKind.Number]: "#00b5b3", 5 | [TokenKind.Equals]: "#422c4f", 6 | [TokenKind.Plus]: "#422c4f", 7 | [TokenKind.Minus]: "#422c4f", 8 | [TokenKind.Asterisk]: "#422c4f", 9 | [TokenKind.AsteriskAsterisk]: "#422c4f", 10 | [TokenKind.RightSlash]: "#422c4f", 11 | [TokenKind.LeftParen]: "#6a737d", 12 | [TokenKind.RightParen]: "#6a737d", 13 | [TokenKind.LeftBrace]: "#ad6757", 14 | [TokenKind.RightBrace]: "#4d4400", 15 | [TokenKind.Space]: "black", 16 | [TokenKind.Identifier]: "#005cc5", 17 | [TokenKind.LetKeyword]: "#d73a49", 18 | [TokenKind.Comma]: "rgb(78, 155, 86)", 19 | [TokenKind.Arrow]: "#6abba3", 20 | [TokenKind.Percent]: "#422c4f", 21 | [TokenKind.Newline]: "lightgray", 22 | [TokenKind.QuestionMark]: "#422c4f", 23 | [TokenKind.LeftSquareBracket]: "#422c4f", 24 | [TokenKind.RightSquareBracket]: "#422c4f", 25 | } 26 | 27 | export const expressionColors: { [key in ast.SyntaxKind]: string } = { 28 | BinaryExpression: "#422c4f", 29 | UnaryExpression: "#4f004e", 30 | NumericLiteral: "#015c6b", 31 | Identifier: "#005cc5", 32 | VariableDeclaration: "#0061b4", 33 | FunctionExpression: "#6446b9", 34 | FunctionCall: "rgb(58, 151, 10)", 35 | Program: "#c751c4", 36 | PrintExpression: "#422c4f", 37 | ArrayExpression: "#422c4f", 38 | } 39 | -------------------------------------------------------------------------------- /web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@olang/web", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@codemirror/language": "^6.8.0", 14 | "@codemirror/state": "^6.2.1", 15 | "@codemirror/view": "^6.14.1", 16 | "@lezer/highlight": "^1.1.6", 17 | "@lezer/javascript": "^1.4.4", 18 | "@olang/core": "workspace:*", 19 | "@uiw/codemirror-theme-github": "^4.21.7", 20 | "@uiw/codemirror-themes": "^4.21.7", 21 | "@uiw/react-codemirror": "^4.21.7", 22 | "react": "^18.2.0", 23 | "react-dom": "^18.2.0", 24 | "vite-plugin-vercel": "^0.3.5" 25 | }, 26 | "devDependencies": { 27 | "@tsconfig/strictest": "^2.0.1", 28 | "@types/react": "^18.0.37", 29 | "@types/react-dom": "^18.0.11", 30 | "@typescript-eslint/eslint-plugin": "^5.59.0", 31 | "@typescript-eslint/parser": "^5.59.0", 32 | "@vitejs/plugin-react": "^4.0.0", 33 | "autoprefixer": "^10.4.14", 34 | "eslint": "^8.38.0", 35 | "eslint-plugin-react-hooks": "^4.6.0", 36 | "eslint-plugin-react-refresh": "^0.3.4", 37 | "postcss": "^8.4.25", 38 | "tailwindcss": "^3.3.2", 39 | "typescript": "^5.0.2", 40 | "typescript-parsec": "^0.3.4", 41 | "vite": "^4.3.9", 42 | "vitest": "^0.32.2" 43 | } 44 | } -------------------------------------------------------------------------------- /web/src/error.tsx: -------------------------------------------------------------------------------- 1 | import { ParseError, TokenError } from "typescript-parsec"; 2 | 3 | export function Error(props: { 4 | tokenError?: TokenError | undefined; 5 | parseError?: ParseError | undefined; 6 | interpreterError?: Error | undefined; 7 | }) { 8 | if (props.tokenError) { 9 | return ( 10 |
11 |
Lexer error:
12 | {props.tokenError.errorMessage} 13 | 14 | Row: {props.tokenError.pos?.rowBegin}:{props.tokenError.pos?.rowEnd}{" "} 15 | Col: {props.tokenError.pos?.columnBegin}: 16 | {props.tokenError.pos?.columnEnd} 17 | 18 |
19 | ); 20 | } 21 | 22 | if (props.parseError) { 23 | return ( 24 |
25 |
Parser error:
26 | {props.parseError.message} 27 | 28 | Row: {props.parseError.pos?.rowBegin}:{props.parseError.pos?.rowEnd}{" "} 29 | Col: {props.parseError.pos?.columnBegin}: 30 | {props.parseError.pos?.columnEnd} 31 | 32 |
33 | ); 34 | } 35 | 36 | if (props.interpreterError) { 37 | return ( 38 |
39 |
Error:
40 | {props.interpreterError.message} 41 |
42 | ); 43 | } 44 | 45 | return null; 46 | } 47 | -------------------------------------------------------------------------------- /core/src/lexer.ts: -------------------------------------------------------------------------------- 1 | import { buildLexer } from "typescript-parsec" 2 | 3 | export enum TokenKind { 4 | Number = "n", 5 | Equals = "=", 6 | Plus = "+", 7 | Minus = "-", 8 | Asterisk = "*", 9 | Percent = "%", 10 | AsteriskAsterisk = "**", 11 | RightSlash = "/", 12 | LeftParen = "(", 13 | RightParen = ")", 14 | LeftBrace = "{", 15 | RightBrace = "}", 16 | LeftSquareBracket = "[", 17 | RightSquareBracket = "]", 18 | Space = " ", 19 | Identifier = "i", 20 | LetKeyword = "let", 21 | Comma = ",", 22 | Newline = "\n", 23 | Arrow = "=>", 24 | QuestionMark = "?", 25 | } 26 | 27 | export type OperatorTokenKind = 28 | | TokenKind.Equals 29 | | TokenKind.Plus 30 | | TokenKind.Minus 31 | | TokenKind.Asterisk 32 | | TokenKind.RightSlash 33 | | TokenKind.Asterisk 34 | | TokenKind.AsteriskAsterisk 35 | | TokenKind.Percent 36 | 37 | export const lexer = buildLexer([ 38 | [true, /^\n/g, TokenKind.Newline], 39 | [true, /^\d+(\.\d+)?/g, TokenKind.Number], 40 | [true, /^\=/g, TokenKind.Equals], 41 | [true, /^\+/g, TokenKind.Plus], 42 | [true, /^\-/g, TokenKind.Minus], 43 | [true, /^\%/g, TokenKind.Percent], 44 | [true, /^\*\*/g, TokenKind.AsteriskAsterisk], 45 | [true, /^\*/g, TokenKind.Asterisk], 46 | [true, /^\//g, TokenKind.RightSlash], 47 | [true, /^\(/g, TokenKind.LeftParen], 48 | [true, /^\)/g, TokenKind.RightParen], 49 | [false, /^\s+/g, TokenKind.Space], // ignore whitespace 50 | [true, /^\,/g, TokenKind.Comma], 51 | [true, /^\{/g, TokenKind.LeftBrace], 52 | [true, /^\}/g, TokenKind.RightBrace], 53 | [true, /^=>/g, TokenKind.Arrow], 54 | [true, /^let/g, TokenKind.LetKeyword], 55 | [true, /^[a-zA-Z_][a-zA-Z0-9_]*/g, TokenKind.Identifier], 56 | [true, /^\?/g, TokenKind.QuestionMark], 57 | [true, /^\[/g, TokenKind.LeftSquareBracket], 58 | [true, /^\]/g, TokenKind.RightSquareBracket], 59 | ]) 60 | -------------------------------------------------------------------------------- /web/src/lexer.tsx: -------------------------------------------------------------------------------- 1 | import { Token, TokenError } from "typescript-parsec" 2 | import { TokenKind } from "@olang/core" 3 | import { tokenColors } from "./colors" 4 | import { unsafeEntries } from "./lib/unsafeEntries" 5 | import { Error } from "./error" 6 | 7 | const tokenToDisplayName = Object.fromEntries( 8 | unsafeEntries(TokenKind).map(([key, value]) => [value, key]) 9 | ) as { [key in TokenKind]: string } 10 | 11 | export function Lexer({ 12 | tokens, 13 | tokenError: error, 14 | setHighlightRange, 15 | }: { 16 | tokens: Token[] | undefined 17 | tokenError: TokenError | undefined 18 | setHighlightRange: (range: { from: number; to: number }) => void 19 | }) { 20 | return ( 21 |
22 | {error ? ( 23 | 24 | ) : ( 25 |
26 | {tokens?.map((token, i) => ( 27 | 47 | ))} 48 |
49 | )} 50 |
51 | ) 52 | } 53 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 14 | 15 | olang 16 | 20 | 24 | 25 | 26 | 30 | 34 | 38 | 42 | 46 | 47 | 48 | 52 | 56 | 60 | 64 | 68 | 69 | 70 |
71 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /core/src/ast.ts: -------------------------------------------------------------------------------- 1 | import { TokenKind } from "./lexer" 2 | 3 | export type SyntaxKind = 4 | | "BinaryExpression" 5 | | "UnaryExpression" 6 | | "NumericLiteral" 7 | | "Identifier" 8 | | "VariableDeclaration" 9 | | "FunctionExpression" 10 | | "FunctionCall" 11 | | "PrintExpression" 12 | | "ArrayExpression" 13 | | "Program" 14 | 15 | export interface BaseNode { 16 | kind: SyntaxKind 17 | meta: { 18 | from: number 19 | to: number 20 | } 21 | } 22 | 23 | export interface NumericLiteral extends BaseNode { 24 | kind: "NumericLiteral" 25 | value: number 26 | } 27 | 28 | export interface BinaryExpression extends BaseNode { 29 | kind: "BinaryExpression" 30 | left: Expression 31 | operator: 32 | | TokenKind.Equals 33 | | TokenKind.Plus 34 | | TokenKind.Minus 35 | | TokenKind.Asterisk 36 | | TokenKind.RightSlash 37 | | TokenKind.Asterisk 38 | | TokenKind.Percent 39 | | TokenKind.AsteriskAsterisk 40 | right: Expression 41 | } 42 | 43 | export interface UnaryExpression extends BaseNode { 44 | kind: "UnaryExpression" 45 | operator: TokenKind.Minus 46 | operand: Expression 47 | } 48 | 49 | export interface Identifier extends BaseNode { 50 | kind: "Identifier" 51 | name: string 52 | } 53 | 54 | export interface VariableDeclaration extends BaseNode { 55 | kind: "VariableDeclaration" 56 | name: Identifier 57 | initializer: Expression 58 | } 59 | 60 | export interface FunctionExpression extends BaseNode { 61 | kind: "FunctionExpression" 62 | parameters: Identifier[] 63 | body: Expression[] 64 | } 65 | 66 | export interface FunctionCall extends BaseNode { 67 | kind: "FunctionCall" 68 | name: Identifier 69 | arguments: Expression[] 70 | } 71 | 72 | export interface PrintExpression extends BaseNode { 73 | kind: "PrintExpression" 74 | expression: Expression 75 | } 76 | 77 | export interface ArrayExpression extends BaseNode { 78 | kind: "ArrayExpression" 79 | elements: Expression[] 80 | } 81 | 82 | export interface Program extends BaseNode { 83 | kind: "Program" 84 | statements: Expression[] 85 | } 86 | 87 | export type Node = Program | Expression 88 | 89 | export type Expression = 90 | | NumericLiteral 91 | | BinaryExpression 92 | | UnaryExpression 93 | | Identifier 94 | | FunctionExpression 95 | | FunctionCall 96 | | VariableDeclaration 97 | | PrintExpression 98 | | ArrayExpression 99 | -------------------------------------------------------------------------------- /core/src/factory.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "vitest" 2 | import * as ast from "./ast" 3 | import type { OperatorTokenKind, TokenKind } from "./lexer" 4 | 5 | export function NumericLiteral( 6 | value: number, 7 | meta: ast.Node["meta"] = expect.any(Object) 8 | ): ast.NumericLiteral { 9 | return { 10 | kind: "NumericLiteral", 11 | value, 12 | meta, 13 | } as const 14 | } 15 | 16 | export function BinaryExpression( 17 | left: ast.Expression, 18 | operator: OperatorTokenKind, 19 | right: ast.Expression, 20 | meta: ast.Node["meta"] = expect.any(Object) 21 | ): ast.BinaryExpression { 22 | return { 23 | kind: "BinaryExpression", 24 | left, 25 | operator, 26 | right, 27 | meta, 28 | } as const 29 | } 30 | 31 | export function UnaryExpression( 32 | operator: TokenKind.Minus, 33 | operand: ast.Expression, 34 | meta: ast.Node["meta"] = expect.any(Object) 35 | ): ast.UnaryExpression { 36 | return { 37 | kind: "UnaryExpression", 38 | operator, 39 | operand, 40 | meta, 41 | } as const 42 | } 43 | 44 | export function Identifier( 45 | name: string, 46 | meta: ast.Node["meta"] = expect.any(Object) 47 | ): ast.Identifier { 48 | return { 49 | kind: "Identifier", 50 | name, 51 | meta, 52 | } as const 53 | } 54 | 55 | export function VariableDeclaration( 56 | name: ast.Identifier, 57 | initializer: ast.Expression, 58 | meta: ast.Node["meta"] = expect.any(Object) 59 | ): ast.VariableDeclaration { 60 | return { 61 | kind: "VariableDeclaration", 62 | name, 63 | initializer, 64 | meta, 65 | } as const 66 | } 67 | 68 | export function FunctionExpression( 69 | parameters: ast.Identifier[], 70 | body: ast.Expression[], 71 | meta: ast.Node["meta"] = expect.any(Object) 72 | ): ast.FunctionExpression { 73 | return { 74 | kind: "FunctionExpression", 75 | parameters, 76 | body, 77 | meta, 78 | } as const 79 | } 80 | 81 | export function CallExpression( 82 | name: ast.Identifier, 83 | args: ast.Expression[], 84 | meta: ast.Node["meta"] = expect.any(Object) 85 | ): ast.FunctionCall { 86 | return { 87 | kind: "FunctionCall", 88 | name, 89 | arguments: args, 90 | meta, 91 | } as const 92 | } 93 | 94 | export function Program( 95 | statements: ast.Expression[], 96 | meta: ast.Node["meta"] = expect.any(Object) 97 | ): ast.Program { 98 | return { 99 | kind: "Program", 100 | statements, 101 | meta, 102 | } as const 103 | } 104 | -------------------------------------------------------------------------------- /core/src/interpreter.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest" 2 | import { interpret } from "./interpreter" 3 | import { parse } from "./test/test-utils" 4 | 5 | const expectEvaluated = (source: string, expected: unknown) => { 6 | expect(interpret(parse(source))).toBe(expected) 7 | } 8 | 9 | describe(interpret, () => { 10 | it("evaluates arithmetic", () => { 11 | expectEvaluated("1", 1) 12 | 13 | expectEvaluated("2 + 3", 5) 14 | 15 | expectEvaluated("1 + 1", 2) 16 | 17 | expectEvaluated("1 + 2 * 3", 7) 18 | 19 | expectEvaluated("1 + 2 * 18", 37) 20 | 21 | expectEvaluated("1 + 2 * 18 / 3", 13) 22 | 23 | expectEvaluated("1 + 2 * 18 / 3 * 2", 25) 24 | 25 | expectEvaluated("2 ** 2", 4) 26 | 27 | expectEvaluated("2 ** 3", 8) 28 | 29 | expectEvaluated("2 ** 3 ** 2", 512) 30 | 31 | expectEvaluated("2 ** 3 ** 2 ** 2", 2.4178516392292583e24) 32 | 33 | expectEvaluated("(1 + 2) * 3", 9) 34 | 35 | expectEvaluated("1 + 2 * (3 + 4)", 15) 36 | 37 | expectEvaluated("2 * 3 ** 2", 18) 38 | 39 | expectEvaluated("-1", -1) 40 | 41 | expectEvaluated("-1 + 2", 1) 42 | 43 | expectEvaluated("7 % 2", 1) 44 | 45 | expectEvaluated("7 % 3", 1) 46 | 47 | expectEvaluated("7 % 4", 3) 48 | }) 49 | 50 | it("evaluates functions", () => { 51 | expectEvaluated( 52 | ` 53 | let inc = (x) => x + 1 54 | inc(1) 55 | `, 56 | 2 57 | ) 58 | 59 | expectEvaluated( 60 | ` 61 | let square = (x) => { 62 | x ** 2 63 | } 64 | let inc = (x) => x + 1 65 | let main = (x) => { 66 | let y = inc(x) 67 | square(y) 68 | } 69 | main(11) 70 | `, 71 | 144 72 | ) 73 | 74 | expectEvaluated( 75 | ` 76 | let square = (x) => { 77 | x ** 2 78 | } 79 | let inc = (x) => x + 1 80 | let main = (x) => { 81 | let y = inc(x) 82 | square(y) 83 | } 84 | main(11) + main(12) 85 | `, 86 | 313 87 | ) 88 | 89 | expectEvaluated( 90 | ` 91 | let test = (x) => { 92 | let inc = (y) => y + 1 93 | let z = inc(inc(x)) 94 | x ** z 95 | } 96 | test(2) 97 | `, 98 | 16 99 | ) 100 | 101 | expectEvaluated( 102 | ` 103 | let test = (x) => { 104 | let inc = (y) => y + 1 105 | let z = inc(inc(x)) 106 | x ** z 107 | } 108 | test(2) + test(3) 109 | `, 110 | 259 111 | ) 112 | 113 | expectEvaluated( 114 | ` 115 | let test = (x) => { 116 | let test2 = (y) => { 117 | let inc = (z) => z + 1 118 | let z = inc(inc(y)) 119 | y ** z 120 | } 121 | test2(x) 122 | } 123 | test(2) 124 | 125 | `, 126 | 16 127 | ) 128 | }) 129 | }) 130 | -------------------------------------------------------------------------------- /core/src/lexer.spec.ts: -------------------------------------------------------------------------------- 1 | import { assert, describe, it } from "vitest" 2 | import { TokenKind, lexer } from "./lexer" 3 | 4 | function notUndefined(t: T | undefined): T { 5 | assert.notStrictEqual(t, undefined) 6 | return t 7 | } 8 | 9 | describe("lexer", () => { 10 | it("tokenizes simple number", () => { 11 | let token = lexer.parse("123") 12 | 13 | token = notUndefined(token) 14 | assert.strictEqual(token.kind, TokenKind.Number) 15 | assert.strictEqual(token.text, "123") 16 | 17 | token = token.next 18 | assert.strictEqual(token, undefined) 19 | }) 20 | 21 | it("muplitple numbers", () => { 22 | let token = lexer.parse("123 456") 23 | 24 | while (token !== undefined) { 25 | token = notUndefined(token) 26 | assert.strictEqual(token.kind, TokenKind.Number) 27 | token = token.next 28 | } 29 | }) 30 | 31 | it("tokenizes expression", () => { 32 | let token = lexer.parse("1 + 2 - 3") 33 | 34 | token = notUndefined(token) 35 | assert.strictEqual(token.kind, TokenKind.Number) 36 | assert.strictEqual(token.text, "1") 37 | 38 | token = notUndefined(token.next) 39 | assert.strictEqual(token.kind, TokenKind.Plus) 40 | assert.strictEqual(token.text, "+") 41 | 42 | token = notUndefined(token.next) 43 | assert.strictEqual(token.kind, TokenKind.Number) 44 | assert.strictEqual(token.text, "2") 45 | 46 | token = notUndefined(token.next) 47 | assert.strictEqual(token.kind, TokenKind.Minus) 48 | assert.strictEqual(token.text, "-") 49 | 50 | token = notUndefined(token.next) 51 | assert.strictEqual(token.kind, TokenKind.Number) 52 | assert.strictEqual(token.text, "3") 53 | 54 | token = token.next 55 | assert.strictEqual(token, undefined) 56 | }) 57 | 58 | it("handles identifier", () => { 59 | let token = lexer.parse("a") 60 | 61 | token = notUndefined(token) 62 | assert.strictEqual(token.kind, TokenKind.Identifier) 63 | assert.strictEqual(token.text, "a") 64 | 65 | token = token.next 66 | assert.strictEqual(token, undefined) 67 | }) 68 | 69 | it("handles variable declaration", () => { 70 | let token = lexer.parse("let a = 1") 71 | 72 | token = notUndefined(token) 73 | assert.strictEqual(token.kind, TokenKind.LetKeyword) 74 | assert.strictEqual(token.text, "let") 75 | 76 | token = notUndefined(token.next) 77 | assert.strictEqual(token.kind, TokenKind.Identifier) 78 | assert.strictEqual(token.text, "a") 79 | 80 | token = notUndefined(token.next) 81 | assert.strictEqual(token.kind, TokenKind.Equals) 82 | assert.strictEqual(token.text, "=") 83 | 84 | token = notUndefined(token.next) 85 | assert.strictEqual(token.kind, TokenKind.Number) 86 | assert.strictEqual(token.text, "1") 87 | 88 | token = token.next 89 | assert.strictEqual(token, undefined) 90 | }) 91 | 92 | it("handles function declaration", () => { 93 | let token = lexer.parse("(a, b) = a + b") 94 | 95 | token = notUndefined(token) 96 | assert.strictEqual(token.kind, TokenKind.LeftParen) 97 | assert.strictEqual(token.text, "(") 98 | 99 | token = notUndefined(token.next) 100 | assert.strictEqual(token.kind, TokenKind.Identifier) 101 | assert.strictEqual(token.text, "a") 102 | 103 | token = notUndefined(token.next) 104 | assert.strictEqual(token.kind, TokenKind.Comma) 105 | assert.strictEqual(token.text, ",") 106 | 107 | token = notUndefined(token.next) 108 | assert.strictEqual(token.kind, TokenKind.Identifier) 109 | assert.strictEqual(token.text, "b") 110 | 111 | token = notUndefined(token.next) 112 | assert.strictEqual(token.kind, TokenKind.RightParen) 113 | assert.strictEqual(token.text, ")") 114 | 115 | token = notUndefined(token.next) 116 | assert.strictEqual(token.kind, TokenKind.Equals) 117 | assert.strictEqual(token.text, "=") 118 | 119 | token = notUndefined(token.next) 120 | assert.strictEqual(token.kind, TokenKind.Identifier) 121 | assert.strictEqual(token.text, "a") 122 | 123 | token = notUndefined(token.next) 124 | assert.strictEqual(token.kind, TokenKind.Plus) 125 | assert.strictEqual(token.text, "+") 126 | 127 | token = notUndefined(token.next) 128 | assert.strictEqual(token.kind, TokenKind.Identifier) 129 | assert.strictEqual(token.text, "b") 130 | 131 | token = token.next 132 | assert.strictEqual(token, undefined) 133 | }) 134 | }) 135 | -------------------------------------------------------------------------------- /web/src/Editor.tsx: -------------------------------------------------------------------------------- 1 | import CodeMirror, { 2 | ReactCodeMirrorRef, 3 | ReactCodeMirrorProps, 4 | } from "@uiw/react-codemirror"; 5 | import { parser as javascriptParser } from "@lezer/javascript"; 6 | import { StateField, StateEffect, Extension } from "@codemirror/state"; 7 | 8 | import { githubLight } from "@uiw/codemirror-theme-github"; 9 | import { 10 | LRLanguage, 11 | LanguageSupport, 12 | delimitedIndent, 13 | continuedIndent, 14 | indentNodeProp, 15 | foldNodeProp, 16 | foldInside, 17 | } from "@codemirror/language"; 18 | import { Decoration, DecorationSet, EditorView } from "@codemirror/view"; 19 | import { useLayoutEffect, useRef } from "react"; 20 | 21 | const addHighlight = StateEffect.define<{ from: number; to: number }>({ 22 | map: ({ from, to }, change) => ({ 23 | from: change.mapPos(from), 24 | to: change.mapPos(to), 25 | }), 26 | }); 27 | const highlightMark = Decoration.mark({ class: "highlight-mark" }); 28 | const highlightField = StateField.define({ 29 | create() { 30 | return Decoration.none; 31 | }, 32 | update(highlights, transaction) { 33 | highlights = highlights.map(transaction.changes); 34 | for (const e of transaction.effects) { 35 | if (e.is(addHighlight)) { 36 | highlights = Decoration.set( 37 | highlightMark.range(e.value.from, e.value.to) 38 | ); 39 | } 40 | } 41 | return highlights; 42 | }, 43 | provide: (stateField) => EditorView.decorations.from(stateField), 44 | }); 45 | 46 | const codemirrorOlangSupport = new LanguageSupport( 47 | LRLanguage.define({ 48 | name: "olang", 49 | languageData: { 50 | closeBrackets: { brackets: ["(", "[", "{", "'", '"', "`"] }, 51 | commentTokens: { line: "//", block: { open: "/*", close: "*/" } }, 52 | indentOnInput: /^\s*(?:case |default:|\{|\}|<\/)$/, 53 | wordChars: "$", 54 | }, 55 | parser: javascriptParser.configure({ 56 | props: [ 57 | indentNodeProp.add({ 58 | IfStatement: continuedIndent({ except: /^\s*({|else\b)/ }), 59 | TryStatement: continuedIndent({ 60 | except: /^\s*({|catch\b|finally\b)/, 61 | }), 62 | SwitchBody: (context) => { 63 | const after = context.textAfter, 64 | closed = /^\s*\}/.test(after), 65 | isCase = /^\s*(case|default)\b/.test(after); 66 | return ( 67 | context.baseIndent + (closed ? 0 : isCase ? 1 : 2) * context.unit 68 | ); 69 | }, 70 | Block: delimitedIndent({ closing: "}" }), 71 | ArrowFunction: (cx) => cx.baseIndent + cx.unit, 72 | "TemplateString BlockComment": () => null, 73 | "Statement Property": continuedIndent({ except: /^{/ }), 74 | }), 75 | foldNodeProp.add({ 76 | "Block ClassBody SwitchBody EnumBody ObjectExpression ArrayExpression ObjectType": 77 | foldInside, 78 | BlockComment(tree) { 79 | return { from: tree.from + 2, to: tree.to - 2 }; 80 | }, 81 | }), 82 | ], 83 | }), 84 | }) 85 | ); 86 | 87 | const extensions: ReactCodeMirrorProps["extensions"] = [ 88 | highlightField, 89 | codemirrorOlangSupport, 90 | EditorView.baseTheme({ 91 | ".highlight-mark": { 92 | backgroundColor: "#ffff0077", 93 | outline: "1px solid #00000022", 94 | borderRadius: "2px", 95 | }, 96 | }), 97 | ]; 98 | 99 | export function Editor(props: { 100 | value: string; 101 | onChange: (value: string) => void; 102 | highlightRange: { from: number; to: number } | undefined; 103 | }) { 104 | const editorRef = useRef(null); 105 | 106 | useLayoutEffect(() => { 107 | const view = editorRef.current?.view; 108 | 109 | if (!view) return; 110 | 111 | const from = props.highlightRange?.from; 112 | const to = props.highlightRange?.to; 113 | 114 | if (from === undefined || to === undefined) return; 115 | if (from === to) return; 116 | 117 | const effects: StateEffect<{ from: number; to: number } | Extension>[] = [ 118 | addHighlight.of({ from, to }), 119 | ]; 120 | if (!view.state.field(highlightField, false)) { 121 | // todo: learn why does this have to be defined in two places 122 | effects.push(StateEffect.appendConfig.of([highlightField])); 123 | } 124 | 125 | view.dispatch({ effects }); 126 | }, [props.highlightRange, editorRef]); 127 | 128 | return ( 129 | { 132 | const from = props.highlightRange?.from; 133 | const to = props.highlightRange?.to; 134 | if (from !== undefined && to !== undefined) { 135 | editor.dispatch({ 136 | effects: [addHighlight.of({ from, to })], 137 | }); 138 | } 139 | }} 140 | value={props.value} 141 | onChange={props.onChange} 142 | className="h-full [&>div]:h-full" 143 | theme={githubLight} 144 | extensions={extensions || []} 145 | autoFocus 146 | /> 147 | ); 148 | } 149 | -------------------------------------------------------------------------------- /web/src/ast.tsx: -------------------------------------------------------------------------------- 1 | import { ast, Value } from "@olang/core" 2 | import { ParseError, TokenError } from "typescript-parsec" 3 | import { expressionColors } from "./colors" 4 | import { Error } from "./error" 5 | 6 | import { useState } from "react" 7 | 8 | const ASTNode = ({ 9 | node, 10 | indentLevel, 11 | setHighlightRange, 12 | }: { 13 | node: ast.Node 14 | indentLevel: number 15 | setHighlightRange: (range: { from: number; to: number }) => void 16 | }) => { 17 | const color = expressionColors[node.kind] 18 | const [expanded, setExpanded] = useState(() => { 19 | if (node.kind === "Program") { 20 | return ["statements"] 21 | } 22 | return [] 23 | }) 24 | 25 | const handleCollapedToggle = (key: string) => { 26 | setExpanded((prev) => { 27 | if (prev.includes(key)) { 28 | return prev.filter((item) => item !== key) 29 | } 30 | return [...prev, key] 31 | }) 32 | } 33 | 34 | const handleHighlight = () => { 35 | if ( 36 | typeof node.meta.from !== "number" || 37 | typeof node.meta.to !== "number" 38 | ) { 39 | return 40 | } 41 | setHighlightRange({ 42 | from: node.meta.from, 43 | to: node.meta.to, 44 | }) 45 | } 46 | 47 | const renderNode = (key: string, value: Value) => { 48 | if (key === "kind") { 49 | return ( 50 | 51 | 54 | 55 | ) 56 | } 57 | if (Array.isArray(value)) { 58 | return ( 59 | <> 60 |
61 |
62 | {key}: 63 |
64 |
65 | 71 |
72 |
73 | {expanded.includes(key) && ( 74 |
75 | {value.map((item, i) => ( 76 |
77 | 82 |
83 | ))} 84 |
85 | )} 86 | 87 | ) 88 | } 89 | if (typeof value === "object" && value !== null) { 90 | return ( 91 | <> 92 |
93 |
94 | {key}: 95 |
96 |
97 | 103 |
104 |
105 | {expanded.includes(key) && ( 106 |
107 | 112 |
113 | )} 114 | 115 | ) 116 | } 117 | return ( 118 |
119 |
120 | {key}: 121 |
122 |
123 | {value} 124 |
125 |
126 | ) 127 | } 128 | 129 | return ( 130 |
131 |
136 | {Object.entries(node) 137 | .filter(([key]) => key !== "meta") 138 | .map(([key, value]) => ( 139 |
{renderNode(key, value)}
140 | ))} 141 |
142 |
143 | ) 144 | } 145 | 146 | interface AstViewerProps { 147 | ast: ast.Program | undefined 148 | parseError: ParseError | undefined 149 | lexerError: TokenError | undefined 150 | setHighlightRange: (range: { from: number; to: number }) => void 151 | } 152 | 153 | export const AstViewer = ({ 154 | ast, 155 | parseError, 156 | lexerError, 157 | setHighlightRange, 158 | }: AstViewerProps) => { 159 | if (parseError || lexerError) { 160 | return ( 161 | 165 | ) 166 | } 167 | 168 | if (!ast) { 169 | return null 170 | } 171 | 172 | return ( 173 |
174 | 179 |
180 | ) 181 | } 182 | -------------------------------------------------------------------------------- /web/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useLayoutEffect, useMemo, useState } from "react" 2 | import { ParseError, Token, TokenError } from "typescript-parsec" 3 | import { 4 | ast, 5 | TokenKind, 6 | lexer, 7 | EvaluationEvents, 8 | Value, 9 | astDebugger, 10 | interpret, 11 | parse, 12 | } from "@olang/core" 13 | 14 | import { AstViewer } from "./ast" 15 | import { cx } from "./lib/cx" 16 | import { Evaluator } from "./evaluator" 17 | import { Lexer } from "./lexer" 18 | import { useRouter } from "./lib/router" 19 | 20 | import "./index.css" 21 | import { Editor } from "./Editor" 22 | 23 | const DEFAULT_CODE = ` 24 | let add = (a, b) => { 25 | a + b 26 | } 27 | add(1, 2) 28 | `.trim() 29 | 30 | const tabs = [ 31 | { name: "Lexer", href: "/lexer" }, 32 | { name: "Parser", href: "/parser" }, 33 | { name: "Interpreter", href: "/interpreter" }, 34 | ] 35 | 36 | const runCode = ( 37 | code: string 38 | ): { 39 | tokens?: Token[] | undefined 40 | tokenError?: TokenError | undefined 41 | parseError?: ParseError | undefined 42 | interpreterError?: Error | undefined 43 | ast?: ast.Program | undefined 44 | evaluationEvents?: EvaluationEvents | undefined 45 | result?: Value | undefined 46 | } => { 47 | const tokens: Token[] = [] 48 | let tokenError: TokenError | undefined 49 | try { 50 | let token = lexer.parse(code) 51 | while (token) { 52 | tokens.push(token) 53 | token = token.next 54 | } 55 | } catch (err) { 56 | tokenError = err as TokenError 57 | } 58 | if (tokenError) return { tokenError } 59 | 60 | let ast: ast.Program | undefined 61 | let parseError: ParseError | undefined 62 | 63 | try { 64 | const parseResult = parse(code) 65 | if (parseResult.kind === "Error") parseError = parseResult 66 | else ast = parseResult 67 | } catch (err) { 68 | parseError = err as ParseError 69 | } 70 | if (parseError) return { parseError } 71 | 72 | const evaluationEvents: EvaluationEvents = [] 73 | let programResult: Value | undefined 74 | let interpreterError: Error | undefined 75 | try { 76 | programResult = ast && interpret(ast, astDebugger(evaluationEvents)) 77 | } catch (err) { 78 | interpreterError = err as Error 79 | } 80 | 81 | return { 82 | tokens, 83 | tokenError, 84 | parseError, 85 | interpreterError, 86 | ast, 87 | evaluationEvents: evaluationEvents.reverse(), 88 | result: programResult, 89 | } 90 | } 91 | 92 | export const App = () => { 93 | const { pathname } = useRouter() 94 | const [code, setCode] = useState(DEFAULT_CODE) 95 | const [highlightRange, setHighlightRange] = useState< 96 | | { 97 | from: number 98 | to: number 99 | } 100 | | undefined 101 | >(undefined) 102 | 103 | useLayoutEffect(() => { 104 | if (pathname === "/interpreter") { 105 | setHighlightRange({ 106 | from: 0, 107 | to: code.length, 108 | }) 109 | } 110 | }, [code.length, pathname]) 111 | 112 | const { 113 | tokens, 114 | tokenError, 115 | parseError, 116 | interpreterError, 117 | ast, 118 | evaluationEvents, 119 | result: programResult, 120 | } = useMemo(() => runCode(code), [code]) 121 | 122 | return ( 123 |
124 |
125 | 130 |
131 |
132 |
133 | 153 |
154 |
155 | {(pathname === "/lexer" || pathname === "/") && ( 156 | 161 | )} 162 | {pathname === "/parser" && ( 163 | 169 | )} 170 | {pathname === "/interpreter" && ( 171 | 178 | )} 179 |
180 |
181 |
182 | ) 183 | } 184 | -------------------------------------------------------------------------------- /core/src/interpreter.ts: -------------------------------------------------------------------------------- 1 | import * as ast from "./ast" 2 | import { Node } from "./ast" 3 | import { TokenKind } from "./lexer" 4 | import { printAst } from "./printer" 5 | 6 | // #region debugger 7 | export function astDebugger(events: EvaluationEvents) { 8 | return function debug( 9 | node: ast.Node, 10 | scope: Scope, 11 | code: string, 12 | value: Value 13 | ) { 14 | events.push({ 15 | kind: node.kind, 16 | pos: node.meta, 17 | scope: scope, 18 | code, 19 | value, 20 | }) 21 | } 22 | } 23 | 24 | export type EvaluationEvents = Array<{ 25 | kind: ast.SyntaxKind 26 | pos: Node["meta"] 27 | scope: Scope 28 | code: string 29 | value: Value 30 | }> 31 | // #endregion 32 | 33 | interface Scope { 34 | parent: Scope | null 35 | bindings: Record 36 | } 37 | 38 | function getBinding(scope: Scope, name: string): Value { 39 | if (name in scope.bindings) return scope.bindings[name] 40 | if (scope.parent) return getBinding(scope.parent, name) 41 | throw new Error(`Undefined variable: ${name}`) 42 | } 43 | 44 | // TODO: This might be nice to refactor into a custom type. 45 | export type Value = 46 | | undefined 47 | | null 48 | | number 49 | | string 50 | | boolean 51 | | ast.FunctionExpression 52 | 53 | export function interpret( 54 | programAst: ast.Program, 55 | debug?: ReturnType 56 | ): Value { 57 | function createFunctionScope(node: ast.FunctionCall, scope: Readonly) { 58 | const functionName = node.name.name 59 | const functionDeclaration = getBinding( 60 | scope, 61 | functionName 62 | ) as ast.FunctionExpression 63 | 64 | const fnScope: Scope = { 65 | parent: scope, 66 | bindings: {}, 67 | } 68 | 69 | for (let i = 0; i < functionDeclaration.parameters.length; i++) { 70 | const parameter = functionDeclaration.parameters[i] 71 | const argument = node.arguments[i] 72 | if (!argument || !parameter) { 73 | throw new Error("Illegal function call. Missing argument.") 74 | } 75 | 76 | fnScope.bindings[parameter.name] = evaluate(argument, scope) 77 | } 78 | 79 | return { functionDeclaration, fnScope } 80 | } 81 | 82 | function evaluate(node: ast.Node, scope: Scope): Value { 83 | switch (node.kind) { 84 | case "Identifier": 85 | const value = getBinding(scope, node.name) 86 | debug?.(node, scope, node.name, value) 87 | 88 | return value 89 | case "NumericLiteral": 90 | debug?.(node, scope, node.value.toString(), node.value) 91 | 92 | return node.value 93 | case "UnaryExpression": 94 | const operand = evaluate(node.operand, scope) 95 | if (typeof operand !== "number") { 96 | throw new Error("Operand must be a number") 97 | } 98 | 99 | debug?.(node, scope, printAst(node), -operand) 100 | 101 | return -operand 102 | case "BinaryExpression": 103 | const left = evaluate(node.left, scope) 104 | const right = evaluate(node.right, scope) 105 | 106 | if (typeof left !== "number" || typeof right !== "number") { 107 | // TODO: String concatenation 108 | throw new Error("Operands must be numbers") 109 | } 110 | 111 | let binaryResult: Value = null 112 | switch (node.operator) { 113 | case TokenKind.Plus: 114 | binaryResult = left + right 115 | break 116 | case TokenKind.Minus: 117 | binaryResult = left - right 118 | break 119 | case TokenKind.Asterisk: 120 | binaryResult = left * right 121 | break 122 | case TokenKind.RightSlash: 123 | binaryResult = left / right 124 | break 125 | case TokenKind.AsteriskAsterisk: 126 | binaryResult = left ** right 127 | break 128 | case TokenKind.Percent: 129 | binaryResult = left % right 130 | break 131 | default: 132 | throw new Error(`Unhandled binary operator: ${node.operator}`) 133 | } 134 | debug?.(node, scope, printAst(node), binaryResult) 135 | 136 | return binaryResult 137 | case "FunctionCall": { 138 | let result: Value = null 139 | const { fnScope, functionDeclaration } = createFunctionScope( 140 | node, 141 | scope 142 | ) 143 | 144 | for (const statement of functionDeclaration.body) { 145 | result = evaluate(statement, fnScope) 146 | } 147 | 148 | debug?.(node, scope, printAst(node), result) 149 | return result 150 | } 151 | 152 | case "VariableDeclaration": { 153 | const value = evaluate(node.initializer, scope) 154 | if (scope.bindings[node.name.name]) { 155 | throw new Error(`Variable already declared: ${node.name.name}`) 156 | } 157 | scope.bindings[node.name.name] = value 158 | 159 | debug?.(node, scope, printAst(node), value) 160 | return value 161 | } 162 | 163 | case "FunctionExpression": { 164 | debug?.(node, scope, printAst(node), node) 165 | return node 166 | } 167 | 168 | case "PrintExpression": { 169 | debug?.(node, scope, printAst(node), evaluate(node.expression, scope)) 170 | const printExpressionResult = evaluate(node.expression, scope) 171 | 172 | console.log(`${printAst(node)} -> ${printExpressionResult}`) 173 | 174 | return printExpressionResult 175 | } 176 | 177 | case "ArrayExpression": { 178 | return "TODO: ArrayExpression" 179 | } 180 | 181 | case "Program": { 182 | let result: Value = null 183 | for (const statement of node.statements) { 184 | result = evaluate(statement, scope) 185 | } 186 | 187 | debug?.(node, scope, printAst(node), result) 188 | return result 189 | } 190 | } 191 | } 192 | 193 | return evaluate(programAst, { 194 | parent: null, 195 | bindings: {}, 196 | }) 197 | } 198 | -------------------------------------------------------------------------------- /web/src/evaluator.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-case-declarations */ 2 | import { useState } from "react" 3 | import { ast, type EvaluationEvents, Value } from "@olang/core" 4 | import { ParseError, TokenError } from "typescript-parsec" 5 | import { unsafeEntries } from "./lib/unsafeEntries" 6 | import { expressionColors } from "./colors" 7 | import { Error } from "./error" 8 | 9 | export function Evaluator({ 10 | ast: ast, 11 | result, 12 | errors, 13 | evaluationEvents: events, 14 | setHighlightRange, 15 | }: { 16 | ast: ast.Program | undefined 17 | result: Value 18 | errors: { 19 | tokenError?: TokenError | undefined 20 | parseError?: ParseError | undefined 21 | interpreterError?: Error | undefined 22 | } 23 | evaluationEvents: EvaluationEvents 24 | setHighlightRange: (range: { from: number; to: number }) => void 25 | }) { 26 | const [currentIndex, setCurrentIndex] = useState(0) 27 | 28 | const onNext = () => { 29 | const nextIndex = 30 | currentIndex + 1 >= events.length ? currentIndex : currentIndex + 1 31 | setCurrentIndex(nextIndex) 32 | const currentEvent = events[nextIndex] 33 | if ( 34 | currentEvent && 35 | typeof currentEvent.pos.from !== "undefined" && 36 | typeof currentEvent.pos.to !== "undefined" 37 | ) { 38 | setHighlightRange({ 39 | from: currentEvent.pos.from, 40 | to: currentEvent.pos.to, 41 | }) 42 | } 43 | } 44 | 45 | const onPrev = () => { 46 | const prevIndex = currentIndex - 1 < 0 ? currentIndex : currentIndex - 1 47 | setCurrentIndex(prevIndex) 48 | const currentEvent = events[prevIndex] 49 | if ( 50 | currentEvent && 51 | typeof currentEvent.pos.from !== "undefined" && 52 | typeof currentEvent.pos.to !== "undefined" 53 | ) { 54 | setHighlightRange({ 55 | from: currentEvent.pos.from, 56 | to: currentEvent.pos.to, 57 | }) 58 | } 59 | } 60 | 61 | if (errors.tokenError || errors.parseError || errors.interpreterError) { 62 | return ( 63 | 68 | ) 69 | } 70 | 71 | if (!ast) { 72 | return null 73 | } 74 | 75 | return ( 76 |
77 |
78 | Result: 79 | 80 |
{JSON.stringify(result)}
81 |
82 |
83 | 84 |
85 |

86 | Evaluation steps: {currentIndex + 1} / {events.length} 87 |

88 | 89 | 98 | 106 | 107 | 108 | 109 |
110 |
111 | ) 112 | } 113 | 114 | type EventInfoProps = { 115 | event: EvaluationEvents[number] | undefined 116 | } 117 | 118 | const EventInfo = ({ event }: EventInfoProps) => { 119 | if (!event) return null 120 | 121 | return ( 122 |
123 | Current node:{" "} 124 | 131 | {event.kind} 132 | 133 |
Node's scope:
134 | 135 |
136 | ) 137 | } 138 | 139 | type BindingValueProps = { 140 | value: Value 141 | } 142 | 143 | const BindingValue = ({ value }: BindingValueProps) => { 144 | const [expanded, setExpanded] = useState(false) 145 | if (typeof value === "object" && value !== null && "kind" in value) { 146 | return ( 147 | 148 | {expanded ? ( 149 |
{JSON.stringify(value, null, 2)}
150 | ) : ( 151 | 158 | {value.kind} 159 | 160 | )} 161 | 167 |
168 | ) 169 | } 170 | 171 | return {value} 172 | } 173 | 174 | type ScopeProps = { 175 | scope: EvaluationEvents[number]["scope"] 176 | } 177 | 178 | const getAllBindings = ( 179 | scope: EvaluationEvents[number]["scope"] 180 | ): Record => { 181 | if (!scope.parent) return scope.bindings 182 | return { ...scope.bindings, ...getAllBindings(scope.parent) } 183 | } 184 | 185 | const Scope = ({ scope }: ScopeProps) => { 186 | const allBindings = getAllBindings(scope) 187 | return ( 188 | 189 | 190 | {unsafeEntries(allBindings).map(([name, value]) => ( 191 | 192 | 193 | 196 | 197 | ))} 198 | 199 |
{name} 194 | 195 |
200 | ) 201 | } 202 | -------------------------------------------------------------------------------- /core/src/parser.ts: -------------------------------------------------------------------------------- 1 | import { 2 | alt, 3 | alt_sc, 4 | apply, 5 | kleft, 6 | kmid, 7 | list_sc, 8 | lrec_sc, 9 | opt_sc, 10 | rule, 11 | seq, 12 | str, 13 | tok, 14 | } from "typescript-parsec" 15 | import { TokenKind, lexer } from "./lexer" 16 | import type * as ast from "./ast" 17 | 18 | export const Expression = rule() 19 | 20 | const NumericLiteral = rule() 21 | const Identifier = rule() 22 | const Term = rule() 23 | 24 | const UnaryExpression = rule() 25 | const MultiplicativeExpression = rule() 26 | const PowerExpression = rule() 27 | const AdditiveExpression = rule() 28 | const AssignmentExpression = rule() 29 | const VariableDeclaration = rule() 30 | const FunctionExpression = rule() 31 | const FunctionCall = rule() 32 | const PrintExpression = rule() 33 | const ArrayExpression = rule() 34 | 35 | export const Program = rule() 36 | 37 | NumericLiteral.setPattern( 38 | apply(tok(TokenKind.Number), (value, tokenRange): ast.NumericLiteral => { 39 | return { 40 | kind: "NumericLiteral", 41 | value: parseFloat(value.text), 42 | meta: { 43 | from: tokenRange[0]?.pos.index || 0, 44 | to: tokenRange[0]?.pos.index! + tokenRange[0]?.text.length!, 45 | }, 46 | } 47 | }) 48 | ) 49 | 50 | Identifier.setPattern( 51 | apply(tok(TokenKind.Identifier), (token, tokenRange): ast.Identifier => { 52 | return { 53 | kind: "Identifier", 54 | name: token.text, 55 | meta: { 56 | from: tokenRange[0]?.pos.index || 0, 57 | to: (tokenRange[0]?.pos.index || 0) + token.text.length, 58 | }, 59 | } 60 | }) 61 | ) 62 | 63 | UnaryExpression.setPattern( 64 | apply( 65 | seq(tok(TokenKind.Minus), Term), 66 | ([operator, operand], tokenRange): ast.UnaryExpression => { 67 | return { 68 | kind: "UnaryExpression", 69 | operator: operator.kind, 70 | operand: operand, 71 | meta: { 72 | from: tokenRange[0]?.pos.index || 0, 73 | to: operand.meta.to, 74 | }, 75 | } 76 | } 77 | ) 78 | ) 79 | 80 | Term.setPattern( 81 | alt_sc( 82 | FunctionCall, 83 | NumericLiteral, 84 | Identifier, 85 | UnaryExpression, 86 | kmid(str("("), Expression, str(")")) 87 | ) 88 | ) 89 | 90 | PowerExpression.setPattern( 91 | alt( 92 | Term, 93 | apply( 94 | seq(Term, tok(TokenKind.AsteriskAsterisk), PowerExpression), 95 | ([left, , right]) => ({ 96 | kind: "BinaryExpression", 97 | operator: TokenKind.AsteriskAsterisk, 98 | left, 99 | right, 100 | meta: { 101 | from: left.meta.from, 102 | to: right.meta.to, 103 | }, 104 | }) 105 | ) 106 | ) 107 | ) 108 | 109 | MultiplicativeExpression.setPattern( 110 | lrec_sc( 111 | PowerExpression, 112 | seq( 113 | alt( 114 | tok(TokenKind.Asterisk), 115 | tok(TokenKind.RightSlash), 116 | tok(TokenKind.Percent) 117 | ), 118 | PowerExpression 119 | ), 120 | (left, [operator, right]): ast.BinaryExpression => ({ 121 | kind: "BinaryExpression", 122 | operator: operator.kind, 123 | left, 124 | right, 125 | meta: { 126 | from: left.meta.from, 127 | to: right.meta.to, 128 | }, 129 | }) 130 | ) 131 | ) 132 | 133 | AdditiveExpression.setPattern( 134 | lrec_sc( 135 | MultiplicativeExpression, 136 | seq( 137 | alt(tok(TokenKind.Plus), tok(TokenKind.Minus)), 138 | MultiplicativeExpression 139 | ), 140 | (left, [operator, right]): ast.BinaryExpression => ({ 141 | kind: "BinaryExpression", 142 | operator: operator.kind, 143 | left, 144 | right, 145 | meta: { 146 | from: left.meta.from, 147 | to: right.meta.to, 148 | }, 149 | }) 150 | ) 151 | ) 152 | 153 | AssignmentExpression.setPattern( 154 | lrec_sc( 155 | AdditiveExpression, 156 | seq(tok(TokenKind.Equals), AdditiveExpression), 157 | (left, [operator, right]): ast.BinaryExpression => ({ 158 | kind: "BinaryExpression", 159 | operator: operator.kind, 160 | left, 161 | right, 162 | meta: { 163 | from: left.meta.from, 164 | to: right.meta.to, 165 | }, 166 | }) 167 | ) 168 | ) 169 | 170 | VariableDeclaration.setPattern( 171 | apply( 172 | seq( 173 | tok(TokenKind.LetKeyword), 174 | Identifier, 175 | tok(TokenKind.Equals), 176 | Expression 177 | ), 178 | ([, identifier, , initializer], tokenRange): ast.VariableDeclaration => { 179 | return { 180 | kind: "VariableDeclaration", 181 | name: identifier, 182 | initializer, 183 | meta: { 184 | from: tokenRange[0]?.pos.index || 0, 185 | to: initializer.meta.to, 186 | }, 187 | } 188 | } 189 | ) 190 | ) 191 | 192 | FunctionExpression.setPattern( 193 | apply( 194 | seq( 195 | tok(TokenKind.LeftParen), 196 | opt_sc( 197 | kleft( 198 | list_sc(opt_sc(Identifier), tok(TokenKind.Comma)), 199 | opt_sc(tok(TokenKind.Comma)) 200 | ) 201 | ), 202 | tok(TokenKind.RightParen), 203 | tok(TokenKind.Arrow), 204 | alt( 205 | Expression, 206 | seq( 207 | tok(TokenKind.LeftBrace), 208 | opt_sc( 209 | kleft( 210 | list_sc(Expression, opt_sc(tok(TokenKind.Newline))), 211 | opt_sc(tok(TokenKind.Newline)) 212 | ) 213 | ), 214 | tok(TokenKind.RightBrace) 215 | ) 216 | ) 217 | ), 218 | ([, parameters, , , body], tokenRange): ast.FunctionExpression => ({ 219 | kind: "FunctionExpression", 220 | parameters: parameters?.filter((p): p is ast.Identifier => !!p) || [], 221 | body: Array.isArray(body) ? body[1] || [] : [body], 222 | meta: { 223 | from: tokenRange[0]?.pos.index || 0, 224 | to: Array.isArray(body) ? body[2]?.pos.index + 1 : body.meta.to, 225 | }, 226 | }) 227 | ) 228 | ) 229 | 230 | FunctionCall.setPattern( 231 | apply( 232 | seq( 233 | Identifier, 234 | tok(TokenKind.LeftParen), 235 | opt_sc( 236 | kleft( 237 | list_sc(opt_sc(Expression), tok(TokenKind.Comma)), 238 | opt_sc(tok(TokenKind.Comma)) 239 | ) 240 | ), 241 | tok(TokenKind.RightParen) 242 | ), 243 | ( 244 | [identifier, _leftParen, parameters, _rightParen], 245 | tokenRange 246 | ): ast.FunctionCall => { 247 | return { 248 | kind: "FunctionCall", 249 | name: identifier, 250 | arguments: (parameters || []).filter((e): e is ast.Expression => !!e), 251 | meta: { 252 | from: tokenRange[0]?.pos.index || 0, 253 | to: _rightParen.pos.index + 1, 254 | }, 255 | } 256 | } 257 | ) 258 | ) 259 | 260 | PrintExpression.setPattern( 261 | apply( 262 | seq(Term, tok(TokenKind.QuestionMark)), 263 | ([expression], tokenRange): ast.PrintExpression => { 264 | return { 265 | kind: "PrintExpression", 266 | expression, 267 | meta: { 268 | from: tokenRange[0]?.pos.index || 0, 269 | to: expression.meta.to + 1, 270 | }, 271 | } 272 | } 273 | ) 274 | ) 275 | 276 | ArrayExpression.setPattern( 277 | apply( 278 | seq( 279 | tok(TokenKind.LeftSquareBracket), 280 | list_sc(opt_sc(Expression), tok(TokenKind.Comma)), 281 | tok(TokenKind.RightSquareBracket) 282 | ), 283 | ([, elements], tokenRange): ast.ArrayExpression => { 284 | return { 285 | kind: "ArrayExpression", 286 | elements: elements.filter((e): e is ast.Expression => !!e), 287 | meta: { 288 | from: tokenRange[0]?.pos.index || 0, 289 | to: (elements?.[elements.length - 1]?.meta.to || 0) + 1, 290 | }, 291 | } 292 | } 293 | ) 294 | ) 295 | 296 | Expression.setPattern( 297 | alt_sc( 298 | PrintExpression, 299 | ArrayExpression, 300 | FunctionExpression, 301 | VariableDeclaration, 302 | AssignmentExpression 303 | ) 304 | ) 305 | 306 | Program.setPattern( 307 | apply( 308 | seq( 309 | opt_sc( 310 | kleft( 311 | list_sc(opt_sc(Expression), opt_sc(tok(TokenKind.Newline))), 312 | opt_sc(tok(TokenKind.Newline)) 313 | ) 314 | ), 315 | opt_sc(tok(TokenKind.Newline)) 316 | ), 317 | ([statements = []], tokenRange): ast.Program => { 318 | return { 319 | kind: "Program", 320 | statements: statements.filter((e): e is ast.Expression => !!e), 321 | meta: { 322 | from: tokenRange[0]?.pos.index || 0, 323 | to: statements[statements.length - 1]?.meta.to || 0, 324 | }, 325 | } 326 | } 327 | ) 328 | ) 329 | 330 | export function parse(expr: string) { 331 | const parsed = Program.parse(lexer.parse(expr)) 332 | if (parsed.successful) { 333 | return parsed.candidates[0]?.result! 334 | } else { 335 | return parsed.error 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /core/src/parser.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "vitest" 2 | 3 | import { Node } from "./ast" 4 | import { TokenKind } from "./lexer" 5 | import { parse } from "./test/test-utils" 6 | import { 7 | BinaryExpression, 8 | UnaryExpression, 9 | NumericLiteral, 10 | Identifier, 11 | VariableDeclaration, 12 | FunctionExpression, 13 | CallExpression, 14 | Program, 15 | } from "./factory" 16 | 17 | const expectParsed = (expression: string, expected: Node) => { 18 | if (!("statements" in expected)) expected = Program([expected]) 19 | 20 | expect(parse(expression).statements).toStrictEqual(expected.statements) 21 | } 22 | 23 | describe("parser", () => { 24 | it("parses numeric literals", () => { 25 | expectParsed("1", { 26 | kind: "NumericLiteral", 27 | value: 1, 28 | meta: expect.any(Object), 29 | }) 30 | 31 | expectParsed("1000.0002", { 32 | kind: "NumericLiteral", 33 | value: 1000.0002, 34 | meta: expect.any(Object), 35 | }) 36 | }) 37 | 38 | it("parses unary expressions", () => { 39 | expectParsed("-1.5", UnaryExpression(TokenKind.Minus, NumericLiteral(1.5))) 40 | }) 41 | 42 | describe("arithmetic expressions", () => { 43 | it("parses multiplication", () => { 44 | expectParsed( 45 | "1 * 2", 46 | BinaryExpression( 47 | NumericLiteral(1), 48 | TokenKind.Asterisk, 49 | NumericLiteral(2) 50 | ) 51 | ) 52 | 53 | expectParsed("1 * 2 * 3", { 54 | kind: "BinaryExpression", 55 | operator: TokenKind.Asterisk, 56 | left: { 57 | kind: "BinaryExpression", 58 | operator: TokenKind.Asterisk, 59 | left: NumericLiteral(1), 60 | right: NumericLiteral(2), 61 | meta: expect.any(Object), 62 | }, 63 | right: NumericLiteral(3), 64 | meta: expect.any(Object), 65 | }) 66 | }) 67 | 68 | it("parses exponentation", () => { 69 | expectParsed( 70 | "2 ** 3", 71 | BinaryExpression( 72 | NumericLiteral(2), 73 | TokenKind.AsteriskAsterisk, 74 | NumericLiteral(3) 75 | ) 76 | ) 77 | 78 | // 2 ^ (3 ^ 2) 79 | expectParsed( 80 | "2 ** 3 ** 2", 81 | BinaryExpression( 82 | NumericLiteral(2), 83 | TokenKind.AsteriskAsterisk, 84 | BinaryExpression( 85 | NumericLiteral(3), 86 | TokenKind.AsteriskAsterisk, 87 | NumericLiteral(2) 88 | ) 89 | ) 90 | ) 91 | 92 | expectParsed( 93 | "10 ** 2 * 3", 94 | BinaryExpression( 95 | BinaryExpression( 96 | NumericLiteral(10), 97 | TokenKind.AsteriskAsterisk, 98 | NumericLiteral(2) 99 | ), 100 | TokenKind.Asterisk, 101 | NumericLiteral(3) 102 | ) 103 | ) 104 | 105 | expectParsed( 106 | "10 * 2 ** 3", 107 | BinaryExpression( 108 | NumericLiteral(10), 109 | TokenKind.Asterisk, 110 | BinaryExpression( 111 | NumericLiteral(2), 112 | TokenKind.AsteriskAsterisk, 113 | NumericLiteral(3) 114 | ) 115 | ) 116 | ) 117 | }) 118 | 119 | // (parens, exponents, multiplications and division (from left to right), multiplication and subtraction (from left to right) 120 | it("satisfies PEMDAS", () => { 121 | expectParsed( 122 | "-1 * 2", 123 | BinaryExpression( 124 | UnaryExpression(TokenKind.Minus, NumericLiteral(1)), 125 | TokenKind.Asterisk, 126 | NumericLiteral(2) 127 | ) 128 | ) 129 | 130 | expectParsed( 131 | "1 + 2 * 2 + 1", 132 | BinaryExpression( 133 | BinaryExpression( 134 | NumericLiteral(1), 135 | TokenKind.Plus, 136 | BinaryExpression( 137 | NumericLiteral(2), 138 | TokenKind.Asterisk, 139 | NumericLiteral(2) 140 | ) 141 | ), 142 | TokenKind.Plus, 143 | NumericLiteral(1) 144 | ) 145 | ) 146 | }) 147 | }) 148 | 149 | it("parses parentheses", () => { 150 | expectParsed("(1)", NumericLiteral(1)) 151 | 152 | expectParsed( 153 | "(1 + 2)", 154 | BinaryExpression(NumericLiteral(1), TokenKind.Plus, NumericLiteral(2)) 155 | ) 156 | 157 | expectParsed( 158 | "1 * (2 + 3)", 159 | BinaryExpression( 160 | NumericLiteral(1), 161 | TokenKind.Asterisk, 162 | BinaryExpression(NumericLiteral(2), TokenKind.Plus, NumericLiteral(3)) 163 | ) 164 | ) 165 | 166 | expectParsed( 167 | "(1 + 2) * 3", 168 | BinaryExpression( 169 | BinaryExpression(NumericLiteral(1), TokenKind.Plus, NumericLiteral(2)), 170 | TokenKind.Asterisk, 171 | NumericLiteral(3) 172 | ) 173 | ) 174 | }) 175 | 176 | it("parses identifiers", () => { 177 | expectParsed("a", Identifier("a")) 178 | 179 | expectParsed( 180 | "a + b", 181 | BinaryExpression(Identifier("a"), TokenKind.Plus, Identifier("b")) 182 | ) 183 | 184 | expectParsed( 185 | "a + b * c", 186 | BinaryExpression( 187 | Identifier("a"), 188 | TokenKind.Plus, 189 | BinaryExpression(Identifier("b"), TokenKind.Asterisk, Identifier("c")) 190 | ) 191 | ) 192 | 193 | expectParsed( 194 | "a * b + c", 195 | BinaryExpression( 196 | BinaryExpression(Identifier("a"), TokenKind.Asterisk, Identifier("b")), 197 | TokenKind.Plus, 198 | Identifier("c") 199 | ) 200 | ) 201 | 202 | expectParsed( 203 | "a * (b + c) / 2", 204 | BinaryExpression( 205 | BinaryExpression( 206 | Identifier("a"), 207 | TokenKind.Asterisk, 208 | BinaryExpression(Identifier("b"), TokenKind.Plus, Identifier("c")) 209 | ), 210 | TokenKind.RightSlash, 211 | NumericLiteral(2) 212 | ) 213 | ) 214 | 215 | expectParsed( 216 | "2 * (a + b) * c", 217 | BinaryExpression( 218 | BinaryExpression( 219 | NumericLiteral(2), 220 | TokenKind.Asterisk, 221 | BinaryExpression(Identifier("a"), TokenKind.Plus, Identifier("b")) 222 | ), 223 | TokenKind.Asterisk, 224 | Identifier("c") 225 | ) 226 | ) 227 | }) 228 | 229 | it("parses assignments", () => { 230 | expectParsed( 231 | "a = 1", 232 | BinaryExpression(Identifier("a"), TokenKind.Equals, NumericLiteral(1)) 233 | ) 234 | }) 235 | 236 | it("parses variable declarations", () => { 237 | expectParsed( 238 | "let a = 1", 239 | VariableDeclaration(Identifier("a"), NumericLiteral(1)) 240 | ) 241 | 242 | expectParsed( 243 | "let a = 1 + 2", 244 | VariableDeclaration( 245 | Identifier("a"), 246 | BinaryExpression(NumericLiteral(1), TokenKind.Plus, NumericLiteral(2)) 247 | ) 248 | ) 249 | 250 | expectParsed( 251 | "let a = inc(1)", 252 | VariableDeclaration( 253 | Identifier("a"), 254 | CallExpression(Identifier("inc"), [NumericLiteral(1)]) 255 | ) 256 | ) 257 | 258 | expectParsed( 259 | "let a = 1 + 2 * 3", 260 | VariableDeclaration( 261 | Identifier("a"), 262 | BinaryExpression( 263 | NumericLiteral(1), 264 | TokenKind.Plus, 265 | BinaryExpression( 266 | NumericLiteral(2), 267 | TokenKind.Asterisk, 268 | NumericLiteral(3) 269 | ) 270 | ) 271 | ) 272 | ) 273 | }) 274 | 275 | it("parses function expression", () => { 276 | expectParsed( 277 | // noop 278 | "() => {}", 279 | FunctionExpression([], []) 280 | ) 281 | 282 | expectParsed( 283 | "() => 1 + 2", 284 | FunctionExpression( 285 | [], 286 | [BinaryExpression(NumericLiteral(1), TokenKind.Plus, NumericLiteral(2))] 287 | ) 288 | ) 289 | 290 | expectParsed( 291 | "(b) => 1", 292 | FunctionExpression([Identifier("b")], [NumericLiteral(1)]) 293 | ) 294 | 295 | expectParsed( 296 | "(b, c) => 1", 297 | FunctionExpression( 298 | [Identifier("b"), Identifier("c")], 299 | [NumericLiteral(1)] 300 | ) 301 | ) 302 | 303 | expectParsed( 304 | "(b, c) => b + c", 305 | FunctionExpression( 306 | [Identifier("b"), Identifier("c")], 307 | [BinaryExpression(Identifier("b"), TokenKind.Plus, Identifier("c"))] 308 | ) 309 | ) 310 | 311 | expectParsed( 312 | "(b, c) => { b + c }", 313 | FunctionExpression( 314 | [Identifier("b"), Identifier("c")], 315 | [BinaryExpression(Identifier("b"), TokenKind.Plus, Identifier("c"))] 316 | ) 317 | ) 318 | 319 | expectParsed( 320 | `(b, c) => { 321 | b + c 322 | b - c 323 | }`, 324 | FunctionExpression( 325 | [Identifier("b"), Identifier("c")], 326 | [ 327 | BinaryExpression(Identifier("b"), TokenKind.Plus, Identifier("c")), 328 | BinaryExpression(Identifier("b"), TokenKind.Minus, Identifier("c")), 329 | ] 330 | ) 331 | ) 332 | 333 | expectParsed( 334 | `(b, c) => { 335 | let x = b + c 336 | x 337 | }`, 338 | FunctionExpression( 339 | [Identifier("b"), Identifier("c")], 340 | [ 341 | VariableDeclaration( 342 | Identifier("x"), 343 | BinaryExpression(Identifier("b"), TokenKind.Plus, Identifier("c")) 344 | ), 345 | Identifier("x"), 346 | ] 347 | ) 348 | ) 349 | }) 350 | 351 | it("parses function declarations", () => { 352 | expectParsed( 353 | "let a = () => 1", 354 | VariableDeclaration( 355 | Identifier("a"), 356 | FunctionExpression([], [NumericLiteral(1)]) 357 | ) 358 | ) 359 | 360 | expectParsed( 361 | "let a = (b) => 1", 362 | VariableDeclaration( 363 | Identifier("a"), 364 | FunctionExpression([Identifier("b")], [NumericLiteral(1)]) 365 | ) 366 | ) 367 | 368 | expectParsed( 369 | "let a = (b, c) => { b + c }", 370 | VariableDeclaration( 371 | Identifier("a"), 372 | FunctionExpression( 373 | [Identifier("b"), Identifier("c")], 374 | [BinaryExpression(Identifier("b"), TokenKind.Plus, Identifier("c"))] 375 | ) 376 | ) 377 | ) 378 | }) 379 | 380 | it("parses function calls", () => { 381 | expectParsed("a()", CallExpression(Identifier("a"), [])) 382 | 383 | expectParsed("a(b)", CallExpression(Identifier("a"), [Identifier("b")])) 384 | 385 | expectParsed( 386 | "a(b, c)", 387 | CallExpression(Identifier("a"), [Identifier("b"), Identifier("c")]) 388 | ) 389 | 390 | expectParsed( 391 | "a(b, c, d)", 392 | CallExpression(Identifier("a"), [ 393 | Identifier("b"), 394 | Identifier("c"), 395 | Identifier("d"), 396 | ]) 397 | ) 398 | 399 | expectParsed( 400 | "a(1 + 2)", 401 | CallExpression(Identifier("a"), [ 402 | BinaryExpression(NumericLiteral(1), TokenKind.Plus, NumericLiteral(2)), 403 | ]) 404 | ) 405 | 406 | expectParsed( 407 | "a(b + c)", 408 | CallExpression(Identifier("a"), [ 409 | BinaryExpression(Identifier("b"), TokenKind.Plus, Identifier("c")), 410 | ]) 411 | ) 412 | 413 | expectParsed( 414 | "a(b, c + d)", 415 | CallExpression(Identifier("a"), [ 416 | Identifier("b"), 417 | BinaryExpression(Identifier("c"), TokenKind.Plus, Identifier("d")), 418 | ]) 419 | ) 420 | 421 | expectParsed( 422 | "inc(inc(1))", 423 | CallExpression(Identifier("inc"), [ 424 | CallExpression(Identifier("inc"), [NumericLiteral(1)]), 425 | ]) 426 | ) 427 | 428 | expectParsed( 429 | "inc(inc(1), inc(2))", 430 | CallExpression(Identifier("inc"), [ 431 | CallExpression(Identifier("inc"), [NumericLiteral(1)]), 432 | CallExpression(Identifier("inc"), [NumericLiteral(2)]), 433 | ]) 434 | ) 435 | 436 | expectParsed( 437 | "inc(inc(1) + inc(2))", 438 | CallExpression(Identifier("inc"), [ 439 | BinaryExpression( 440 | CallExpression(Identifier("inc"), [NumericLiteral(1)]), 441 | TokenKind.Plus, 442 | CallExpression(Identifier("inc"), [NumericLiteral(2)]) 443 | ), 444 | ]) 445 | ) 446 | }) 447 | 448 | it("parses function calls within binary operations", () => { 449 | expectParsed( 450 | "a() + b()", 451 | BinaryExpression( 452 | CallExpression(Identifier("a"), []), 453 | TokenKind.Plus, 454 | CallExpression(Identifier("b"), []) 455 | ) 456 | ) 457 | }) 458 | 459 | it("parses programs", () => { 460 | expectParsed("let a = 1", { 461 | kind: "Program", 462 | statements: [VariableDeclaration(Identifier("a"), NumericLiteral(1))], 463 | meta: expect.any(Object), 464 | }) 465 | 466 | expectParsed( 467 | `let a = 1 468 | let b = 2`, 469 | { 470 | kind: "Program", 471 | statements: [ 472 | VariableDeclaration(Identifier("a"), NumericLiteral(1)), 473 | VariableDeclaration(Identifier("b"), NumericLiteral(2)), 474 | ], 475 | meta: expect.any(Object), 476 | } 477 | ) 478 | 479 | expectParsed( 480 | `let a = 1 481 | let b = 2 482 | let c = 3 483 | `, 484 | { 485 | kind: "Program", 486 | statements: [ 487 | VariableDeclaration(Identifier("a"), NumericLiteral(1)), 488 | VariableDeclaration(Identifier("b"), NumericLiteral(2)), 489 | VariableDeclaration(Identifier("c"), NumericLiteral(3)), 490 | ], 491 | meta: expect.any(Object), 492 | } 493 | ) 494 | 495 | expectParsed( 496 | `let a = 1 497 | let b = 2 498 | let c = 3 499 | let d = 4`, 500 | { 501 | kind: "Program", 502 | statements: [ 503 | VariableDeclaration(Identifier("a"), NumericLiteral(1)), 504 | VariableDeclaration(Identifier("b"), NumericLiteral(2)), 505 | VariableDeclaration(Identifier("c"), NumericLiteral(3)), 506 | VariableDeclaration(Identifier("d"), NumericLiteral(4)), 507 | ], 508 | meta: expect.any(Object), 509 | } 510 | ) 511 | 512 | expectParsed( 513 | `let a = () => 1 514 | let b = a 515 | let c = (d) => { a + 10 }`, 516 | { 517 | kind: "Program", 518 | statements: [ 519 | VariableDeclaration( 520 | Identifier("a"), 521 | FunctionExpression([], [NumericLiteral(1)]) 522 | ), 523 | VariableDeclaration(Identifier("b"), Identifier("a")), 524 | VariableDeclaration( 525 | Identifier("c"), 526 | FunctionExpression( 527 | [Identifier("d")], 528 | [ 529 | BinaryExpression( 530 | Identifier("a"), 531 | TokenKind.Plus, 532 | NumericLiteral(10) 533 | ), 534 | ] 535 | ) 536 | ), 537 | ], 538 | meta: expect.any(Object), 539 | } 540 | ) 541 | }) 542 | }) 543 | -------------------------------------------------------------------------------- /core/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: 5.4 2 | 3 | specifiers: 4 | tsup: ^7.2.0 5 | typescript: ^5.1.6 6 | typescript-parsec: ^0.3.4 7 | vitest: ^0.32.2 8 | 9 | dependencies: 10 | tsup: 7.2.0_typescript@5.2.2 11 | typescript: 5.2.2 12 | typescript-parsec: 0.3.4 13 | vitest: 0.32.4 14 | 15 | packages: 16 | 17 | /@esbuild/android-arm/0.18.20: 18 | resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} 19 | engines: {node: '>=12'} 20 | cpu: [arm] 21 | os: [android] 22 | requiresBuild: true 23 | dev: false 24 | optional: true 25 | 26 | /@esbuild/android-arm64/0.18.20: 27 | resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} 28 | engines: {node: '>=12'} 29 | cpu: [arm64] 30 | os: [android] 31 | requiresBuild: true 32 | dev: false 33 | optional: true 34 | 35 | /@esbuild/android-x64/0.18.20: 36 | resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} 37 | engines: {node: '>=12'} 38 | cpu: [x64] 39 | os: [android] 40 | requiresBuild: true 41 | dev: false 42 | optional: true 43 | 44 | /@esbuild/darwin-arm64/0.18.20: 45 | resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} 46 | engines: {node: '>=12'} 47 | cpu: [arm64] 48 | os: [darwin] 49 | requiresBuild: true 50 | dev: false 51 | optional: true 52 | 53 | /@esbuild/darwin-x64/0.18.20: 54 | resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} 55 | engines: {node: '>=12'} 56 | cpu: [x64] 57 | os: [darwin] 58 | requiresBuild: true 59 | dev: false 60 | optional: true 61 | 62 | /@esbuild/freebsd-arm64/0.18.20: 63 | resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} 64 | engines: {node: '>=12'} 65 | cpu: [arm64] 66 | os: [freebsd] 67 | requiresBuild: true 68 | dev: false 69 | optional: true 70 | 71 | /@esbuild/freebsd-x64/0.18.20: 72 | resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} 73 | engines: {node: '>=12'} 74 | cpu: [x64] 75 | os: [freebsd] 76 | requiresBuild: true 77 | dev: false 78 | optional: true 79 | 80 | /@esbuild/linux-arm/0.18.20: 81 | resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} 82 | engines: {node: '>=12'} 83 | cpu: [arm] 84 | os: [linux] 85 | requiresBuild: true 86 | dev: false 87 | optional: true 88 | 89 | /@esbuild/linux-arm64/0.18.20: 90 | resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} 91 | engines: {node: '>=12'} 92 | cpu: [arm64] 93 | os: [linux] 94 | requiresBuild: true 95 | dev: false 96 | optional: true 97 | 98 | /@esbuild/linux-ia32/0.18.20: 99 | resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} 100 | engines: {node: '>=12'} 101 | cpu: [ia32] 102 | os: [linux] 103 | requiresBuild: true 104 | dev: false 105 | optional: true 106 | 107 | /@esbuild/linux-loong64/0.18.20: 108 | resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} 109 | engines: {node: '>=12'} 110 | cpu: [loong64] 111 | os: [linux] 112 | requiresBuild: true 113 | dev: false 114 | optional: true 115 | 116 | /@esbuild/linux-mips64el/0.18.20: 117 | resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} 118 | engines: {node: '>=12'} 119 | cpu: [mips64el] 120 | os: [linux] 121 | requiresBuild: true 122 | dev: false 123 | optional: true 124 | 125 | /@esbuild/linux-ppc64/0.18.20: 126 | resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} 127 | engines: {node: '>=12'} 128 | cpu: [ppc64] 129 | os: [linux] 130 | requiresBuild: true 131 | dev: false 132 | optional: true 133 | 134 | /@esbuild/linux-riscv64/0.18.20: 135 | resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} 136 | engines: {node: '>=12'} 137 | cpu: [riscv64] 138 | os: [linux] 139 | requiresBuild: true 140 | dev: false 141 | optional: true 142 | 143 | /@esbuild/linux-s390x/0.18.20: 144 | resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} 145 | engines: {node: '>=12'} 146 | cpu: [s390x] 147 | os: [linux] 148 | requiresBuild: true 149 | dev: false 150 | optional: true 151 | 152 | /@esbuild/linux-x64/0.18.20: 153 | resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} 154 | engines: {node: '>=12'} 155 | cpu: [x64] 156 | os: [linux] 157 | requiresBuild: true 158 | dev: false 159 | optional: true 160 | 161 | /@esbuild/netbsd-x64/0.18.20: 162 | resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} 163 | engines: {node: '>=12'} 164 | cpu: [x64] 165 | os: [netbsd] 166 | requiresBuild: true 167 | dev: false 168 | optional: true 169 | 170 | /@esbuild/openbsd-x64/0.18.20: 171 | resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} 172 | engines: {node: '>=12'} 173 | cpu: [x64] 174 | os: [openbsd] 175 | requiresBuild: true 176 | dev: false 177 | optional: true 178 | 179 | /@esbuild/sunos-x64/0.18.20: 180 | resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} 181 | engines: {node: '>=12'} 182 | cpu: [x64] 183 | os: [sunos] 184 | requiresBuild: true 185 | dev: false 186 | optional: true 187 | 188 | /@esbuild/win32-arm64/0.18.20: 189 | resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} 190 | engines: {node: '>=12'} 191 | cpu: [arm64] 192 | os: [win32] 193 | requiresBuild: true 194 | dev: false 195 | optional: true 196 | 197 | /@esbuild/win32-ia32/0.18.20: 198 | resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} 199 | engines: {node: '>=12'} 200 | cpu: [ia32] 201 | os: [win32] 202 | requiresBuild: true 203 | dev: false 204 | optional: true 205 | 206 | /@esbuild/win32-x64/0.18.20: 207 | resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} 208 | engines: {node: '>=12'} 209 | cpu: [x64] 210 | os: [win32] 211 | requiresBuild: true 212 | dev: false 213 | optional: true 214 | 215 | /@jest/schemas/29.6.3: 216 | resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} 217 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 218 | dependencies: 219 | '@sinclair/typebox': 0.27.8 220 | dev: false 221 | 222 | /@jridgewell/gen-mapping/0.3.3: 223 | resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} 224 | engines: {node: '>=6.0.0'} 225 | dependencies: 226 | '@jridgewell/set-array': 1.1.2 227 | '@jridgewell/sourcemap-codec': 1.4.15 228 | '@jridgewell/trace-mapping': 0.3.19 229 | dev: false 230 | 231 | /@jridgewell/resolve-uri/3.1.1: 232 | resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} 233 | engines: {node: '>=6.0.0'} 234 | dev: false 235 | 236 | /@jridgewell/set-array/1.1.2: 237 | resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} 238 | engines: {node: '>=6.0.0'} 239 | dev: false 240 | 241 | /@jridgewell/sourcemap-codec/1.4.15: 242 | resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} 243 | dev: false 244 | 245 | /@jridgewell/trace-mapping/0.3.19: 246 | resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} 247 | dependencies: 248 | '@jridgewell/resolve-uri': 3.1.1 249 | '@jridgewell/sourcemap-codec': 1.4.15 250 | dev: false 251 | 252 | /@nodelib/fs.scandir/2.1.5: 253 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 254 | engines: {node: '>= 8'} 255 | dependencies: 256 | '@nodelib/fs.stat': 2.0.5 257 | run-parallel: 1.2.0 258 | dev: false 259 | 260 | /@nodelib/fs.stat/2.0.5: 261 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 262 | engines: {node: '>= 8'} 263 | dev: false 264 | 265 | /@nodelib/fs.walk/1.2.8: 266 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 267 | engines: {node: '>= 8'} 268 | dependencies: 269 | '@nodelib/fs.scandir': 2.1.5 270 | fastq: 1.15.0 271 | dev: false 272 | 273 | /@sinclair/typebox/0.27.8: 274 | resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} 275 | dev: false 276 | 277 | /@types/chai-subset/1.3.3: 278 | resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} 279 | dependencies: 280 | '@types/chai': 4.3.6 281 | dev: false 282 | 283 | /@types/chai/4.3.6: 284 | resolution: {integrity: sha512-VOVRLM1mBxIRxydiViqPcKn6MIxZytrbMpd6RJLIWKxUNr3zux8no0Oc7kJx0WAPIitgZ0gkrDS+btlqQpubpw==} 285 | dev: false 286 | 287 | /@types/node/20.8.0: 288 | resolution: {integrity: sha512-LzcWltT83s1bthcvjBmiBvGJiiUe84NWRHkw+ZV6Fr41z2FbIzvc815dk2nQ3RAKMuN2fkenM/z3Xv2QzEpYxQ==} 289 | dev: false 290 | 291 | /@vitest/expect/0.32.4: 292 | resolution: {integrity: sha512-m7EPUqmGIwIeoU763N+ivkFjTzbaBn0n9evsTOcde03ugy2avPs3kZbYmw3DkcH1j5mxhMhdamJkLQ6dM1bk/A==} 293 | dependencies: 294 | '@vitest/spy': 0.32.4 295 | '@vitest/utils': 0.32.4 296 | chai: 4.3.10 297 | dev: false 298 | 299 | /@vitest/runner/0.32.4: 300 | resolution: {integrity: sha512-cHOVCkiRazobgdKLnczmz2oaKK9GJOw6ZyRcaPdssO1ej+wzHVIkWiCiNacb3TTYPdzMddYkCgMjZ4r8C0JFCw==} 301 | dependencies: 302 | '@vitest/utils': 0.32.4 303 | p-limit: 4.0.0 304 | pathe: 1.1.1 305 | dev: false 306 | 307 | /@vitest/snapshot/0.32.4: 308 | resolution: {integrity: sha512-IRpyqn9t14uqsFlVI2d7DFMImGMs1Q9218of40bdQQgMePwVdmix33yMNnebXcTzDU5eiV3eUsoxxH5v0x/IQA==} 309 | dependencies: 310 | magic-string: 0.30.4 311 | pathe: 1.1.1 312 | pretty-format: 29.7.0 313 | dev: false 314 | 315 | /@vitest/spy/0.32.4: 316 | resolution: {integrity: sha512-oA7rCOqVOOpE6rEoXuCOADX7Lla1LIa4hljI2MSccbpec54q+oifhziZIJXxlE/CvI2E+ElhBHzVu0VEvJGQKQ==} 317 | dependencies: 318 | tinyspy: 2.1.1 319 | dev: false 320 | 321 | /@vitest/utils/0.32.4: 322 | resolution: {integrity: sha512-Gwnl8dhd1uJ+HXrYyV0eRqfmk9ek1ASE/LWfTCuWMw+d07ogHqp4hEAV28NiecimK6UY9DpSEPh+pXBA5gtTBg==} 323 | dependencies: 324 | diff-sequences: 29.6.3 325 | loupe: 2.3.6 326 | pretty-format: 29.7.0 327 | dev: false 328 | 329 | /acorn-walk/8.2.0: 330 | resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} 331 | engines: {node: '>=0.4.0'} 332 | dev: false 333 | 334 | /acorn/8.10.0: 335 | resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} 336 | engines: {node: '>=0.4.0'} 337 | hasBin: true 338 | dev: false 339 | 340 | /ansi-styles/5.2.0: 341 | resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} 342 | engines: {node: '>=10'} 343 | dev: false 344 | 345 | /any-promise/1.3.0: 346 | resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} 347 | dev: false 348 | 349 | /anymatch/3.1.3: 350 | resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} 351 | engines: {node: '>= 8'} 352 | dependencies: 353 | normalize-path: 3.0.0 354 | picomatch: 2.3.1 355 | dev: false 356 | 357 | /array-union/2.1.0: 358 | resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} 359 | engines: {node: '>=8'} 360 | dev: false 361 | 362 | /assertion-error/1.1.0: 363 | resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} 364 | dev: false 365 | 366 | /balanced-match/1.0.2: 367 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 368 | dev: false 369 | 370 | /binary-extensions/2.2.0: 371 | resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} 372 | engines: {node: '>=8'} 373 | dev: false 374 | 375 | /brace-expansion/1.1.11: 376 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 377 | dependencies: 378 | balanced-match: 1.0.2 379 | concat-map: 0.0.1 380 | dev: false 381 | 382 | /braces/3.0.2: 383 | resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} 384 | engines: {node: '>=8'} 385 | dependencies: 386 | fill-range: 7.0.1 387 | dev: false 388 | 389 | /bundle-require/4.0.2_esbuild@0.18.20: 390 | resolution: {integrity: sha512-jwzPOChofl67PSTW2SGubV9HBQAhhR2i6nskiOThauo9dzwDUgOWQScFVaJkjEfYX+UXiD+LEx8EblQMc2wIag==} 391 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 392 | peerDependencies: 393 | esbuild: '>=0.17' 394 | dependencies: 395 | esbuild: 0.18.20 396 | load-tsconfig: 0.2.5 397 | dev: false 398 | 399 | /cac/6.7.14: 400 | resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} 401 | engines: {node: '>=8'} 402 | dev: false 403 | 404 | /chai/4.3.10: 405 | resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} 406 | engines: {node: '>=4'} 407 | dependencies: 408 | assertion-error: 1.1.0 409 | check-error: 1.0.3 410 | deep-eql: 4.1.3 411 | get-func-name: 2.0.2 412 | loupe: 2.3.6 413 | pathval: 1.1.1 414 | type-detect: 4.0.8 415 | dev: false 416 | 417 | /check-error/1.0.3: 418 | resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} 419 | dependencies: 420 | get-func-name: 2.0.2 421 | dev: false 422 | 423 | /chokidar/3.5.3: 424 | resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} 425 | engines: {node: '>= 8.10.0'} 426 | dependencies: 427 | anymatch: 3.1.3 428 | braces: 3.0.2 429 | glob-parent: 5.1.2 430 | is-binary-path: 2.1.0 431 | is-glob: 4.0.3 432 | normalize-path: 3.0.0 433 | readdirp: 3.6.0 434 | optionalDependencies: 435 | fsevents: 2.3.3 436 | dev: false 437 | 438 | /commander/4.1.1: 439 | resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} 440 | engines: {node: '>= 6'} 441 | dev: false 442 | 443 | /concat-map/0.0.1: 444 | resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} 445 | dev: false 446 | 447 | /cross-spawn/7.0.3: 448 | resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} 449 | engines: {node: '>= 8'} 450 | dependencies: 451 | path-key: 3.1.1 452 | shebang-command: 2.0.0 453 | which: 2.0.2 454 | dev: false 455 | 456 | /debug/4.3.4: 457 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} 458 | engines: {node: '>=6.0'} 459 | peerDependencies: 460 | supports-color: '*' 461 | peerDependenciesMeta: 462 | supports-color: 463 | optional: true 464 | dependencies: 465 | ms: 2.1.2 466 | dev: false 467 | 468 | /deep-eql/4.1.3: 469 | resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} 470 | engines: {node: '>=6'} 471 | dependencies: 472 | type-detect: 4.0.8 473 | dev: false 474 | 475 | /diff-sequences/29.6.3: 476 | resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} 477 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 478 | dev: false 479 | 480 | /dir-glob/3.0.1: 481 | resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} 482 | engines: {node: '>=8'} 483 | dependencies: 484 | path-type: 4.0.0 485 | dev: false 486 | 487 | /esbuild/0.18.20: 488 | resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} 489 | engines: {node: '>=12'} 490 | hasBin: true 491 | requiresBuild: true 492 | optionalDependencies: 493 | '@esbuild/android-arm': 0.18.20 494 | '@esbuild/android-arm64': 0.18.20 495 | '@esbuild/android-x64': 0.18.20 496 | '@esbuild/darwin-arm64': 0.18.20 497 | '@esbuild/darwin-x64': 0.18.20 498 | '@esbuild/freebsd-arm64': 0.18.20 499 | '@esbuild/freebsd-x64': 0.18.20 500 | '@esbuild/linux-arm': 0.18.20 501 | '@esbuild/linux-arm64': 0.18.20 502 | '@esbuild/linux-ia32': 0.18.20 503 | '@esbuild/linux-loong64': 0.18.20 504 | '@esbuild/linux-mips64el': 0.18.20 505 | '@esbuild/linux-ppc64': 0.18.20 506 | '@esbuild/linux-riscv64': 0.18.20 507 | '@esbuild/linux-s390x': 0.18.20 508 | '@esbuild/linux-x64': 0.18.20 509 | '@esbuild/netbsd-x64': 0.18.20 510 | '@esbuild/openbsd-x64': 0.18.20 511 | '@esbuild/sunos-x64': 0.18.20 512 | '@esbuild/win32-arm64': 0.18.20 513 | '@esbuild/win32-ia32': 0.18.20 514 | '@esbuild/win32-x64': 0.18.20 515 | dev: false 516 | 517 | /execa/5.1.1: 518 | resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} 519 | engines: {node: '>=10'} 520 | dependencies: 521 | cross-spawn: 7.0.3 522 | get-stream: 6.0.1 523 | human-signals: 2.1.0 524 | is-stream: 2.0.1 525 | merge-stream: 2.0.0 526 | npm-run-path: 4.0.1 527 | onetime: 5.1.2 528 | signal-exit: 3.0.7 529 | strip-final-newline: 2.0.0 530 | dev: false 531 | 532 | /fast-glob/3.3.1: 533 | resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} 534 | engines: {node: '>=8.6.0'} 535 | dependencies: 536 | '@nodelib/fs.stat': 2.0.5 537 | '@nodelib/fs.walk': 1.2.8 538 | glob-parent: 5.1.2 539 | merge2: 1.4.1 540 | micromatch: 4.0.5 541 | dev: false 542 | 543 | /fastq/1.15.0: 544 | resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} 545 | dependencies: 546 | reusify: 1.0.4 547 | dev: false 548 | 549 | /fill-range/7.0.1: 550 | resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} 551 | engines: {node: '>=8'} 552 | dependencies: 553 | to-regex-range: 5.0.1 554 | dev: false 555 | 556 | /fs.realpath/1.0.0: 557 | resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} 558 | dev: false 559 | 560 | /fsevents/2.3.3: 561 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 562 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 563 | os: [darwin] 564 | requiresBuild: true 565 | dev: false 566 | optional: true 567 | 568 | /get-func-name/2.0.2: 569 | resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} 570 | dev: false 571 | 572 | /get-stream/6.0.1: 573 | resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} 574 | engines: {node: '>=10'} 575 | dev: false 576 | 577 | /glob-parent/5.1.2: 578 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 579 | engines: {node: '>= 6'} 580 | dependencies: 581 | is-glob: 4.0.3 582 | dev: false 583 | 584 | /glob/7.1.6: 585 | resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} 586 | dependencies: 587 | fs.realpath: 1.0.0 588 | inflight: 1.0.6 589 | inherits: 2.0.4 590 | minimatch: 3.1.2 591 | once: 1.4.0 592 | path-is-absolute: 1.0.1 593 | dev: false 594 | 595 | /globby/11.1.0: 596 | resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} 597 | engines: {node: '>=10'} 598 | dependencies: 599 | array-union: 2.1.0 600 | dir-glob: 3.0.1 601 | fast-glob: 3.3.1 602 | ignore: 5.2.4 603 | merge2: 1.4.1 604 | slash: 3.0.0 605 | dev: false 606 | 607 | /human-signals/2.1.0: 608 | resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} 609 | engines: {node: '>=10.17.0'} 610 | dev: false 611 | 612 | /ignore/5.2.4: 613 | resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} 614 | engines: {node: '>= 4'} 615 | dev: false 616 | 617 | /inflight/1.0.6: 618 | resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} 619 | dependencies: 620 | once: 1.4.0 621 | wrappy: 1.0.2 622 | dev: false 623 | 624 | /inherits/2.0.4: 625 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 626 | dev: false 627 | 628 | /is-binary-path/2.1.0: 629 | resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} 630 | engines: {node: '>=8'} 631 | dependencies: 632 | binary-extensions: 2.2.0 633 | dev: false 634 | 635 | /is-extglob/2.1.1: 636 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 637 | engines: {node: '>=0.10.0'} 638 | dev: false 639 | 640 | /is-glob/4.0.3: 641 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 642 | engines: {node: '>=0.10.0'} 643 | dependencies: 644 | is-extglob: 2.1.1 645 | dev: false 646 | 647 | /is-number/7.0.0: 648 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 649 | engines: {node: '>=0.12.0'} 650 | dev: false 651 | 652 | /is-stream/2.0.1: 653 | resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} 654 | engines: {node: '>=8'} 655 | dev: false 656 | 657 | /isexe/2.0.0: 658 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 659 | dev: false 660 | 661 | /joycon/3.1.1: 662 | resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} 663 | engines: {node: '>=10'} 664 | dev: false 665 | 666 | /jsonc-parser/3.2.0: 667 | resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} 668 | dev: false 669 | 670 | /lilconfig/2.1.0: 671 | resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} 672 | engines: {node: '>=10'} 673 | dev: false 674 | 675 | /lines-and-columns/1.2.4: 676 | resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} 677 | dev: false 678 | 679 | /load-tsconfig/0.2.5: 680 | resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} 681 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 682 | dev: false 683 | 684 | /local-pkg/0.4.3: 685 | resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} 686 | engines: {node: '>=14'} 687 | dev: false 688 | 689 | /lodash.sortby/4.7.0: 690 | resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} 691 | dev: false 692 | 693 | /loupe/2.3.6: 694 | resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} 695 | dependencies: 696 | get-func-name: 2.0.2 697 | dev: false 698 | 699 | /magic-string/0.30.4: 700 | resolution: {integrity: sha512-Q/TKtsC5BPm0kGqgBIF9oXAs/xEf2vRKiIB4wCRQTJOQIByZ1d+NnUOotvJOvNpi5RNIgVOMC3pOuaP1ZTDlVg==} 701 | engines: {node: '>=12'} 702 | dependencies: 703 | '@jridgewell/sourcemap-codec': 1.4.15 704 | dev: false 705 | 706 | /merge-stream/2.0.0: 707 | resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} 708 | dev: false 709 | 710 | /merge2/1.4.1: 711 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 712 | engines: {node: '>= 8'} 713 | dev: false 714 | 715 | /micromatch/4.0.5: 716 | resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} 717 | engines: {node: '>=8.6'} 718 | dependencies: 719 | braces: 3.0.2 720 | picomatch: 2.3.1 721 | dev: false 722 | 723 | /mimic-fn/2.1.0: 724 | resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} 725 | engines: {node: '>=6'} 726 | dev: false 727 | 728 | /minimatch/3.1.2: 729 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 730 | dependencies: 731 | brace-expansion: 1.1.11 732 | dev: false 733 | 734 | /mlly/1.4.2: 735 | resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==} 736 | dependencies: 737 | acorn: 8.10.0 738 | pathe: 1.1.1 739 | pkg-types: 1.0.3 740 | ufo: 1.3.1 741 | dev: false 742 | 743 | /ms/2.1.2: 744 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 745 | dev: false 746 | 747 | /mz/2.7.0: 748 | resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} 749 | dependencies: 750 | any-promise: 1.3.0 751 | object-assign: 4.1.1 752 | thenify-all: 1.6.0 753 | dev: false 754 | 755 | /nanoid/3.3.6: 756 | resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} 757 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 758 | hasBin: true 759 | dev: false 760 | 761 | /normalize-path/3.0.0: 762 | resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} 763 | engines: {node: '>=0.10.0'} 764 | dev: false 765 | 766 | /npm-run-path/4.0.1: 767 | resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} 768 | engines: {node: '>=8'} 769 | dependencies: 770 | path-key: 3.1.1 771 | dev: false 772 | 773 | /object-assign/4.1.1: 774 | resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} 775 | engines: {node: '>=0.10.0'} 776 | dev: false 777 | 778 | /once/1.4.0: 779 | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} 780 | dependencies: 781 | wrappy: 1.0.2 782 | dev: false 783 | 784 | /onetime/5.1.2: 785 | resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} 786 | engines: {node: '>=6'} 787 | dependencies: 788 | mimic-fn: 2.1.0 789 | dev: false 790 | 791 | /p-limit/4.0.0: 792 | resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} 793 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 794 | dependencies: 795 | yocto-queue: 1.0.0 796 | dev: false 797 | 798 | /path-is-absolute/1.0.1: 799 | resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} 800 | engines: {node: '>=0.10.0'} 801 | dev: false 802 | 803 | /path-key/3.1.1: 804 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 805 | engines: {node: '>=8'} 806 | dev: false 807 | 808 | /path-type/4.0.0: 809 | resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} 810 | engines: {node: '>=8'} 811 | dev: false 812 | 813 | /pathe/1.1.1: 814 | resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} 815 | dev: false 816 | 817 | /pathval/1.1.1: 818 | resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} 819 | dev: false 820 | 821 | /picocolors/1.0.0: 822 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 823 | dev: false 824 | 825 | /picomatch/2.3.1: 826 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 827 | engines: {node: '>=8.6'} 828 | dev: false 829 | 830 | /pirates/4.0.6: 831 | resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} 832 | engines: {node: '>= 6'} 833 | dev: false 834 | 835 | /pkg-types/1.0.3: 836 | resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} 837 | dependencies: 838 | jsonc-parser: 3.2.0 839 | mlly: 1.4.2 840 | pathe: 1.1.1 841 | dev: false 842 | 843 | /postcss-load-config/4.0.1: 844 | resolution: {integrity: sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==} 845 | engines: {node: '>= 14'} 846 | peerDependencies: 847 | postcss: '>=8.0.9' 848 | ts-node: '>=9.0.0' 849 | peerDependenciesMeta: 850 | postcss: 851 | optional: true 852 | ts-node: 853 | optional: true 854 | dependencies: 855 | lilconfig: 2.1.0 856 | yaml: 2.3.2 857 | dev: false 858 | 859 | /postcss/8.4.31: 860 | resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} 861 | engines: {node: ^10 || ^12 || >=14} 862 | dependencies: 863 | nanoid: 3.3.6 864 | picocolors: 1.0.0 865 | source-map-js: 1.0.2 866 | dev: false 867 | 868 | /pretty-format/29.7.0: 869 | resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} 870 | engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 871 | dependencies: 872 | '@jest/schemas': 29.6.3 873 | ansi-styles: 5.2.0 874 | react-is: 18.2.0 875 | dev: false 876 | 877 | /punycode/2.3.0: 878 | resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} 879 | engines: {node: '>=6'} 880 | dev: false 881 | 882 | /queue-microtask/1.2.3: 883 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 884 | dev: false 885 | 886 | /react-is/18.2.0: 887 | resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} 888 | dev: false 889 | 890 | /readdirp/3.6.0: 891 | resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} 892 | engines: {node: '>=8.10.0'} 893 | dependencies: 894 | picomatch: 2.3.1 895 | dev: false 896 | 897 | /resolve-from/5.0.0: 898 | resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} 899 | engines: {node: '>=8'} 900 | dev: false 901 | 902 | /reusify/1.0.4: 903 | resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} 904 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 905 | dev: false 906 | 907 | /rollup/3.29.4: 908 | resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} 909 | engines: {node: '>=14.18.0', npm: '>=8.0.0'} 910 | hasBin: true 911 | optionalDependencies: 912 | fsevents: 2.3.3 913 | dev: false 914 | 915 | /run-parallel/1.2.0: 916 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 917 | dependencies: 918 | queue-microtask: 1.2.3 919 | dev: false 920 | 921 | /shebang-command/2.0.0: 922 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 923 | engines: {node: '>=8'} 924 | dependencies: 925 | shebang-regex: 3.0.0 926 | dev: false 927 | 928 | /shebang-regex/3.0.0: 929 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 930 | engines: {node: '>=8'} 931 | dev: false 932 | 933 | /siginfo/2.0.0: 934 | resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} 935 | dev: false 936 | 937 | /signal-exit/3.0.7: 938 | resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} 939 | dev: false 940 | 941 | /slash/3.0.0: 942 | resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} 943 | engines: {node: '>=8'} 944 | dev: false 945 | 946 | /source-map-js/1.0.2: 947 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} 948 | engines: {node: '>=0.10.0'} 949 | dev: false 950 | 951 | /source-map/0.8.0-beta.0: 952 | resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} 953 | engines: {node: '>= 8'} 954 | dependencies: 955 | whatwg-url: 7.1.0 956 | dev: false 957 | 958 | /stackback/0.0.2: 959 | resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} 960 | dev: false 961 | 962 | /std-env/3.4.3: 963 | resolution: {integrity: sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==} 964 | dev: false 965 | 966 | /strip-final-newline/2.0.0: 967 | resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} 968 | engines: {node: '>=6'} 969 | dev: false 970 | 971 | /strip-literal/1.3.0: 972 | resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} 973 | dependencies: 974 | acorn: 8.10.0 975 | dev: false 976 | 977 | /sucrase/3.34.0: 978 | resolution: {integrity: sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==} 979 | engines: {node: '>=8'} 980 | hasBin: true 981 | dependencies: 982 | '@jridgewell/gen-mapping': 0.3.3 983 | commander: 4.1.1 984 | glob: 7.1.6 985 | lines-and-columns: 1.2.4 986 | mz: 2.7.0 987 | pirates: 4.0.6 988 | ts-interface-checker: 0.1.13 989 | dev: false 990 | 991 | /thenify-all/1.6.0: 992 | resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} 993 | engines: {node: '>=0.8'} 994 | dependencies: 995 | thenify: 3.3.1 996 | dev: false 997 | 998 | /thenify/3.3.1: 999 | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} 1000 | dependencies: 1001 | any-promise: 1.3.0 1002 | dev: false 1003 | 1004 | /tinybench/2.5.1: 1005 | resolution: {integrity: sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==} 1006 | dev: false 1007 | 1008 | /tinypool/0.5.0: 1009 | resolution: {integrity: sha512-paHQtnrlS1QZYKF/GnLoOM/DN9fqaGOFbCbxzAhwniySnzl9Ebk8w73/dd34DAhe/obUbPAOldTyYXQZxnPBPQ==} 1010 | engines: {node: '>=14.0.0'} 1011 | dev: false 1012 | 1013 | /tinyspy/2.1.1: 1014 | resolution: {integrity: sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w==} 1015 | engines: {node: '>=14.0.0'} 1016 | dev: false 1017 | 1018 | /to-regex-range/5.0.1: 1019 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 1020 | engines: {node: '>=8.0'} 1021 | dependencies: 1022 | is-number: 7.0.0 1023 | dev: false 1024 | 1025 | /tr46/1.0.1: 1026 | resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} 1027 | dependencies: 1028 | punycode: 2.3.0 1029 | dev: false 1030 | 1031 | /tree-kill/1.2.2: 1032 | resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} 1033 | hasBin: true 1034 | dev: false 1035 | 1036 | /ts-interface-checker/0.1.13: 1037 | resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} 1038 | dev: false 1039 | 1040 | /tsup/7.2.0_typescript@5.2.2: 1041 | resolution: {integrity: sha512-vDHlczXbgUvY3rWvqFEbSqmC1L7woozbzngMqTtL2PGBODTtWlRwGDDawhvWzr5c1QjKe4OAKqJGfE1xeXUvtQ==} 1042 | engines: {node: '>=16.14'} 1043 | hasBin: true 1044 | peerDependencies: 1045 | '@swc/core': ^1 1046 | postcss: ^8.4.12 1047 | typescript: '>=4.1.0' 1048 | peerDependenciesMeta: 1049 | '@swc/core': 1050 | optional: true 1051 | postcss: 1052 | optional: true 1053 | typescript: 1054 | optional: true 1055 | dependencies: 1056 | bundle-require: 4.0.2_esbuild@0.18.20 1057 | cac: 6.7.14 1058 | chokidar: 3.5.3 1059 | debug: 4.3.4 1060 | esbuild: 0.18.20 1061 | execa: 5.1.1 1062 | globby: 11.1.0 1063 | joycon: 3.1.1 1064 | postcss-load-config: 4.0.1 1065 | resolve-from: 5.0.0 1066 | rollup: 3.29.4 1067 | source-map: 0.8.0-beta.0 1068 | sucrase: 3.34.0 1069 | tree-kill: 1.2.2 1070 | typescript: 5.2.2 1071 | transitivePeerDependencies: 1072 | - supports-color 1073 | - ts-node 1074 | dev: false 1075 | 1076 | /type-detect/4.0.8: 1077 | resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} 1078 | engines: {node: '>=4'} 1079 | dev: false 1080 | 1081 | /typescript-parsec/0.3.4: 1082 | resolution: {integrity: sha512-6RD4xOxp26BTZLopNbqT2iErqNhQZZWb5m5F07/UwGhldGvOAKOl41pZ3fxsFp04bNL+PbgMjNfb6IvJAC/uYQ==} 1083 | dev: false 1084 | 1085 | /typescript/5.2.2: 1086 | resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} 1087 | engines: {node: '>=14.17'} 1088 | hasBin: true 1089 | dev: false 1090 | 1091 | /ufo/1.3.1: 1092 | resolution: {integrity: sha512-uY/99gMLIOlJPwATcMVYfqDSxUR9//AUcgZMzwfSTJPDKzA1S8mX4VLqa+fiAtveraQUBCz4FFcwVZBGbwBXIw==} 1093 | dev: false 1094 | 1095 | /vite-node/0.32.4_@types+node@20.8.0: 1096 | resolution: {integrity: sha512-L2gIw+dCxO0LK14QnUMoqSYpa9XRGnTTTDjW2h19Mr+GR0EFj4vx52W41gFXfMLqpA00eK4ZjOVYo1Xk//LFEw==} 1097 | engines: {node: '>=v14.18.0'} 1098 | hasBin: true 1099 | dependencies: 1100 | cac: 6.7.14 1101 | debug: 4.3.4 1102 | mlly: 1.4.2 1103 | pathe: 1.1.1 1104 | picocolors: 1.0.0 1105 | vite: 4.4.9_@types+node@20.8.0 1106 | transitivePeerDependencies: 1107 | - '@types/node' 1108 | - less 1109 | - lightningcss 1110 | - sass 1111 | - stylus 1112 | - sugarss 1113 | - supports-color 1114 | - terser 1115 | dev: false 1116 | 1117 | /vite/4.4.9_@types+node@20.8.0: 1118 | resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==} 1119 | engines: {node: ^14.18.0 || >=16.0.0} 1120 | hasBin: true 1121 | peerDependencies: 1122 | '@types/node': '>= 14' 1123 | less: '*' 1124 | lightningcss: ^1.21.0 1125 | sass: '*' 1126 | stylus: '*' 1127 | sugarss: '*' 1128 | terser: ^5.4.0 1129 | peerDependenciesMeta: 1130 | '@types/node': 1131 | optional: true 1132 | less: 1133 | optional: true 1134 | lightningcss: 1135 | optional: true 1136 | sass: 1137 | optional: true 1138 | stylus: 1139 | optional: true 1140 | sugarss: 1141 | optional: true 1142 | terser: 1143 | optional: true 1144 | dependencies: 1145 | '@types/node': 20.8.0 1146 | esbuild: 0.18.20 1147 | postcss: 8.4.31 1148 | rollup: 3.29.4 1149 | optionalDependencies: 1150 | fsevents: 2.3.3 1151 | dev: false 1152 | 1153 | /vitest/0.32.4: 1154 | resolution: {integrity: sha512-3czFm8RnrsWwIzVDu/Ca48Y/M+qh3vOnF16czJm98Q/AN1y3B6PBsyV8Re91Ty5s7txKNjEhpgtGPcfdbh2MZg==} 1155 | engines: {node: '>=v14.18.0'} 1156 | hasBin: true 1157 | peerDependencies: 1158 | '@edge-runtime/vm': '*' 1159 | '@vitest/browser': '*' 1160 | '@vitest/ui': '*' 1161 | happy-dom: '*' 1162 | jsdom: '*' 1163 | playwright: '*' 1164 | safaridriver: '*' 1165 | webdriverio: '*' 1166 | peerDependenciesMeta: 1167 | '@edge-runtime/vm': 1168 | optional: true 1169 | '@vitest/browser': 1170 | optional: true 1171 | '@vitest/ui': 1172 | optional: true 1173 | happy-dom: 1174 | optional: true 1175 | jsdom: 1176 | optional: true 1177 | playwright: 1178 | optional: true 1179 | safaridriver: 1180 | optional: true 1181 | webdriverio: 1182 | optional: true 1183 | dependencies: 1184 | '@types/chai': 4.3.6 1185 | '@types/chai-subset': 1.3.3 1186 | '@types/node': 20.8.0 1187 | '@vitest/expect': 0.32.4 1188 | '@vitest/runner': 0.32.4 1189 | '@vitest/snapshot': 0.32.4 1190 | '@vitest/spy': 0.32.4 1191 | '@vitest/utils': 0.32.4 1192 | acorn: 8.10.0 1193 | acorn-walk: 8.2.0 1194 | cac: 6.7.14 1195 | chai: 4.3.10 1196 | debug: 4.3.4 1197 | local-pkg: 0.4.3 1198 | magic-string: 0.30.4 1199 | pathe: 1.1.1 1200 | picocolors: 1.0.0 1201 | std-env: 3.4.3 1202 | strip-literal: 1.3.0 1203 | tinybench: 2.5.1 1204 | tinypool: 0.5.0 1205 | vite: 4.4.9_@types+node@20.8.0 1206 | vite-node: 0.32.4_@types+node@20.8.0 1207 | why-is-node-running: 2.2.2 1208 | transitivePeerDependencies: 1209 | - less 1210 | - lightningcss 1211 | - sass 1212 | - stylus 1213 | - sugarss 1214 | - supports-color 1215 | - terser 1216 | dev: false 1217 | 1218 | /webidl-conversions/4.0.2: 1219 | resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} 1220 | dev: false 1221 | 1222 | /whatwg-url/7.1.0: 1223 | resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} 1224 | dependencies: 1225 | lodash.sortby: 4.7.0 1226 | tr46: 1.0.1 1227 | webidl-conversions: 4.0.2 1228 | dev: false 1229 | 1230 | /which/2.0.2: 1231 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 1232 | engines: {node: '>= 8'} 1233 | hasBin: true 1234 | dependencies: 1235 | isexe: 2.0.0 1236 | dev: false 1237 | 1238 | /why-is-node-running/2.2.2: 1239 | resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} 1240 | engines: {node: '>=8'} 1241 | hasBin: true 1242 | dependencies: 1243 | siginfo: 2.0.0 1244 | stackback: 0.0.2 1245 | dev: false 1246 | 1247 | /wrappy/1.0.2: 1248 | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 1249 | dev: false 1250 | 1251 | /yaml/2.3.2: 1252 | resolution: {integrity: sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==} 1253 | engines: {node: '>= 14'} 1254 | dev: false 1255 | 1256 | /yocto-queue/1.0.0: 1257 | resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} 1258 | engines: {node: '>=12.20'} 1259 | dev: false 1260 | --------------------------------------------------------------------------------