diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 903232f5..456b2dec 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -11,19 +11,19 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: webfactory/ssh-agent@v0.5.3 + - uses: actions/checkout@v3 + - uses: webfactory/ssh-agent@v0.8.0 with: ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} - name: Read .nvmrc run: echo "##[set-output name=NVMRC;]$(cat .nvmrc)" id: nvm - name: Use Node.js (.nvmrc) - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: "${{ steps.nvm.outputs.NVMRC }}" - run: npm ci - - run: npm run test:coverage + - run: npm run build && npm run test:coverage - name: Coveralls uses: coverallsapp/github-action@master with: diff --git a/.nvmrc b/.nvmrc index 112a2eae..c32828c2 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -lts/gallium \ No newline at end of file +lts/hydrogen \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 53e06166..ba5e3995 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## v4.0.6 +**Wollok Version: `3.1.9`** + +- New Formatter / Printer. +- Validate concrete class on instantiation. +- Validate uninitialized consts. + ## v4.0.5 **Wollok Version: `3.1.9`** diff --git a/package-lock.json b/package-lock.json index aa110594..1a319fcc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,41 +1,43 @@ { "name": "wollok-ts", - "version": "4.0.5", + "version": "4.0.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "wollok-ts", - "version": "4.0.5", + "version": "4.0.6", "license": "MIT", "dependencies": { - "@types/parsimmon": "^1.10.6", + "@types/parsimmon": "^1.10.8", "parsimmon": "^1.18.1", - "unraw": "^2.0.1", - "uuid": "9.0.0" + "prettier-printer": "^1.1.4", + "unraw": "^3.0.0", + "uuid": "^9.0.1" }, "devDependencies": { - "@types/chai": "^4.3.4", - "@types/mocha": "^10.0.1", + "@types/chai": "^4.3.9", + "@types/mocha": "^10.0.3", "@types/node": "^18.14.1", - "@types/sinon": "^10.0.13", - "@types/sinon-chai": "^3.2.9", - "@types/uuid": "^9.0.1", + "@types/sinon": "^10.0.20", + "@types/sinon-chai": "^3.2.11", + "@types/uuid": "^9.0.6", "@types/yargs": "^17.0.22", "@typescript-eslint/eslint-plugin": "^5.53.0", "@typescript-eslint/parser": "^5.53.0", "chai": "^4.3.7", "chalk": "^5.2.0", + "dedent": "^1.5.1", "eslint": "^8.35.0", "globby": "^11.1.0", "mocha": "^10.2.0", "nyc": "^15.1.0", - "simple-git": "^3.16.1", - "sinon": "15.0.1", + "simple-git": "^3.20.0", + "sinon": "17.0.0", "sinon-chai": "^3.7.0", "ts-node": "^10.9.1", "typescript": "^4.9.5", - "yargs": "^17.7.1" + "yargs": "^17.7.2" } }, "node_modules/@ampproject/remapping": { @@ -52,17 +54,89 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/compat-data": { "version": "7.18.8", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", @@ -103,22 +177,23 @@ } }, "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.10.tgz", - "integrity": "sha512-0+sW7e3HjQbiHbj1NeU/vN8ornohYlacAfZIaXhdoGweQqgcNy69COVciYYqEXJ/v+9OBA7Frxm4CVAuNqKeNA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "dependencies": { - "@babel/types": "^7.18.10", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { @@ -158,43 +233,43 @@ } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -244,30 +319,30 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" @@ -297,13 +372,13 @@ } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -382,9 +457,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.10.tgz", - "integrity": "sha512-TYk3OA0HKL6qNryUayb5UUEhM/rkOQozIBEA5ITXh5DWrSp0TlUQXMyZmnWxG/DizSWBeeQ0Zbc5z8UGaaqoeg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -394,33 +469,33 @@ } }, "node_modules/@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.10.tgz", - "integrity": "sha512-J7ycxg0/K9XCtLyHf0cz2DqDihonJeIo+z+HEdRe9YuT8TY4A66i+Ab2/xZCEW7Ro60bPCBBfqqboHSamoV3+g==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -438,13 +513,13 @@ } }, "node_modules/@babel/types": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", - "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -684,13 +759,13 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", - "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@kwsites/file-exists": { @@ -744,27 +819,27 @@ } }, "node_modules/@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", "dev": true, "dependencies": { "type-detect": "4.0.8" } }, "node_modules/@sinonjs/fake-timers": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", - "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", + "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", "dev": true, "dependencies": { - "@sinonjs/commons": "^2.0.0" + "@sinonjs/commons": "^3.0.0" } }, "node_modules/@sinonjs/samsam": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz", - "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", "dev": true, "dependencies": { "@sinonjs/commons": "^2.0.0", @@ -772,6 +847,15 @@ "type-detect": "^4.0.8" } }, + "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, "node_modules/@sinonjs/text-encoding": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", @@ -803,9 +887,9 @@ "dev": true }, "node_modules/@types/chai": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz", - "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.9.tgz", + "integrity": "sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==", "dev": true }, "node_modules/@types/json-schema": { @@ -815,9 +899,9 @@ "dev": true }, "node_modules/@types/mocha": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", - "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.3.tgz", + "integrity": "sha512-RsOPImTriV/OE4A9qKjMtk2MnXiuLLbcO3nCXK+kvq4nr0iMfFgpjaX3MPLb6f7+EL1FGSelYvuJMV6REH+ZPQ==", "dev": true }, "node_modules/@types/node": { @@ -827,9 +911,9 @@ "dev": true }, "node_modules/@types/parsimmon": { - "version": "1.10.6", - "resolved": "https://registry.npmjs.org/@types/parsimmon/-/parsimmon-1.10.6.tgz", - "integrity": "sha512-FwAQwMRbkhx0J6YELkwIpciVzCcgEqXEbIrIn3a2P5d3kGEHQ3wVhlN3YdVepYP+bZzCYO6OjmD4o9TGOZ40rA==" + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@types/parsimmon/-/parsimmon-1.10.8.tgz", + "integrity": "sha512-i6oOxO9QKaqwMdMnnagvtVduc0ii9rl+BkNdAdSDhN27k9ryP8Fm6S9bcvNtuauK7PwTdoxCRorPcJvyGqwHgQ==" }, "node_modules/@types/semver": { "version": "7.3.13", @@ -838,18 +922,18 @@ "dev": true }, "node_modules/@types/sinon": { - "version": "10.0.13", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.13.tgz", - "integrity": "sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==", + "version": "10.0.20", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.20.tgz", + "integrity": "sha512-2APKKruFNCAZgx3daAyACGzWuJ028VVCUDk6o2rw/Z4PXT0ogwdV4KUegW0MwVs0Zu59auPXbbuBJHF12Sx1Eg==", "dev": true, "dependencies": { "@types/sinonjs__fake-timers": "*" } }, "node_modules/@types/sinon-chai": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.9.tgz", - "integrity": "sha512-/19t63pFYU0ikrdbXKBWj9PCdnKyTd0Qkz0X91Ta081cYsq90OxYdcWwK/dwEoDa6dtXgj2HJfmzgq+QZTHdmQ==", + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.11.tgz", + "integrity": "sha512-1C5SBFzwn9hjiMr1xfqbULcSI9qXVpkGZT/LYbbd3jWiTo2MSvA+iFfwODlSoAXGeCgBw6S509dxy8zSIacr3Q==", "dev": true, "dependencies": { "@types/chai": "*", @@ -863,9 +947,9 @@ "dev": true }, "node_modules/@types/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==", + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.6.tgz", + "integrity": "sha512-BT2Krtx4xaO6iwzwMFUYvWBWkV2pr37zD68Vmp1CDV196MzczBRxuEpD6Pr395HAgebC/co7hOphs53r8V7jew==", "dev": true }, "node_modules/@types/yargs": { @@ -1527,6 +1611,20 @@ "node": ">=0.10.0" } }, + "node_modules/dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, "node_modules/deep-eql": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", @@ -2061,9 +2159,9 @@ } }, "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, "engines": { "node": "*" @@ -2249,6 +2347,11 @@ "node": ">=8" } }, + "node_modules/infestines": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/infestines/-/infestines-0.4.11.tgz", + "integrity": "sha512-09nHagZLOYUaXKHqdV+nxEaYaD0hRlKyhQMhgTMwfbvWpMkowXf4XLZzAkLq6Y90wZ7Wqm6aMoL2trBsNNKGeg==" + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -2422,9 +2525,9 @@ } }, "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -2682,9 +2785,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -2861,9 +2964,9 @@ "dev": true }, "node_modules/nise": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", - "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.5.tgz", + "integrity": "sha512-VJuPIfUFaXNRzETTQEEItTOP8Y171ijr+JLq42wHes3DiryR8vT+1TXQW/Rx8JNUhyYYWyIvjXTU6dOhJcs9Nw==", "dev": true, "dependencies": { "@sinonjs/commons": "^2.0.0", @@ -2873,6 +2976,33 @@ "path-to-regexp": "^1.7.0" } }, + "node_modules/nise/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/nise/node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/nise/node_modules/@sinonjs/fake-timers/node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, "node_modules/node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", @@ -3177,6 +3307,23 @@ "resolved": "https://registry.npmjs.org/parsimmon/-/parsimmon-1.18.1.tgz", "integrity": "sha512-u7p959wLfGAhJpSDJVYXoyMCXWYwHia78HhRBWqk7AIbxdmlrfdp5wX0l3xv/iTSH5HvhN9K7o26hwwpgS5Nmw==" }, + "node_modules/partial.lenses": { + "version": "14.17.0", + "resolved": "https://registry.npmjs.org/partial.lenses/-/partial.lenses-14.17.0.tgz", + "integrity": "sha512-Iq+wDw5b5iwmn7b9MO+//buOqF0YoAC2Hsj+HoeG88BGeT3NkL/YwHNGDrySyX7xZSqj0LFEWwfgGSj4cSFmXQ==", + "dependencies": { + "infestines": "^0.4.11" + } + }, + "node_modules/partial.lenses.validation": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/partial.lenses.validation/-/partial.lenses.validation-2.0.0.tgz", + "integrity": "sha512-NBZhmrunQWc4Ih5pJGYPHCEJfjIITPEkYn0Z6YB8qmW0iN4ZeqN0eZTWAPAH8MxjsEGx3G+8xnTMsEHbzy0k/A==", + "dependencies": { + "infestines": "^0.4.10", + "partial.lenses": "^14.0.0" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3322,6 +3469,15 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier-printer": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/prettier-printer/-/prettier-printer-1.1.4.tgz", + "integrity": "sha512-gQIWF7PKVknRcMoZ4Lsqj2icc97W+Q9JTa7xQgNem07yZFzOimUPq50N5oRfKkSRBZT8QqrVW1IBEMBPWqjBgQ==", + "dependencies": { + "infestines": "^0.4.11", + "partial.lenses.validation": "^2.0.0" + } + }, "node_modules/process-on-spawn": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", @@ -3487,9 +3643,9 @@ "dev": true }, "node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -3544,9 +3700,9 @@ "dev": true }, "node_modules/simple-git": { - "version": "3.16.1", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.16.1.tgz", - "integrity": "sha512-xzRxMKiy1zEYeHGXgAzvuXffDS0xgsq07Oi4LWEEcVH29vLpcZ2tyQRWyK0NLLlCVaKysZeem5tC1qHEOxsKwA==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.20.0.tgz", + "integrity": "sha512-ozK8tl2hvLts8ijTs18iFruE+RoqmC/mqZhjs/+V7gS5W68JpJ3+FCTmLVqmR59MaUQ52MfGQuWsIqfsTbbJ0Q==", "dev": true, "dependencies": { "@kwsites/file-exists": "^1.1.1", @@ -3559,16 +3715,16 @@ } }, "node_modules/sinon": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.1.tgz", - "integrity": "sha512-PZXKc08f/wcA/BMRGBze2Wmw50CWPiAH3E21EOi4B49vJ616vW4DQh4fQrqsYox2aNR/N3kCqLuB0PwwOucQrg==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.0.tgz", + "integrity": "sha512-p4lJiYKBoOEVUxxVIC9H1MM2znG1/c8gud++I2BauJA5hsz7hHsst35eurNWXTusBsIq66FzOQbZ/uMdpvbPIQ==", "dev": true, "dependencies": { - "@sinonjs/commons": "^2.0.0", - "@sinonjs/fake-timers": "10.0.2", - "@sinonjs/samsam": "^7.0.1", - "diff": "^5.0.0", - "nise": "^5.1.2", + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.5", "supports-color": "^7.2.0" }, "funding": { @@ -3586,6 +3742,15 @@ "sinon": ">=4.0.0" } }, + "node_modules/sinon/node_modules/diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -3856,9 +4021,9 @@ } }, "node_modules/unraw": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unraw/-/unraw-2.0.1.tgz", - "integrity": "sha512-tdOvLfRzHolwYcHS6HIX860MkK9LQ4+oLuNwFYL7bpgTEO64PZrcQxkisgwJYCfF8sKiWLwwu1c83DvMkbefIQ==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unraw/-/unraw-3.0.0.tgz", + "integrity": "sha512-08/DA66UF65OlpUDIQtbJyrqTR0jTAlJ+jsnkQ4jxR7+K5g5YG1APZKQSMCE1vqqmD+2pv6+IdEjmopFatacvg==" }, "node_modules/update-browserslist-db": { "version": "1.0.5", @@ -3896,9 +4061,13 @@ } }, "node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } @@ -3931,9 +4100,9 @@ "dev": true }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -3996,9 +4165,9 @@ "dev": true }, "node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "dependencies": { "cliui": "^8.0.1", @@ -4118,12 +4287,71 @@ } }, "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "dev": true, "requires": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "@babel/compat-data": { @@ -4156,21 +4384,22 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } }, "@babel/generator": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.10.tgz", - "integrity": "sha512-0+sW7e3HjQbiHbj1NeU/vN8ornohYlacAfZIaXhdoGweQqgcNy69COVciYYqEXJ/v+9OBA7Frxm4CVAuNqKeNA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "requires": { - "@babel/types": "^7.18.10", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "dependencies": { @@ -4200,36 +4429,36 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } }, "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true }, "@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "requires": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-module-imports": { @@ -4267,24 +4496,24 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true }, "@babel/helper-validator-option": { @@ -4305,13 +4534,13 @@ } }, "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "dependencies": { @@ -4374,36 +4603,36 @@ } }, "@babel/parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.10.tgz", - "integrity": "sha512-TYk3OA0HKL6qNryUayb5UUEhM/rkOQozIBEA5ITXh5DWrSp0TlUQXMyZmnWxG/DizSWBeeQ0Zbc5z8UGaaqoeg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true }, "@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/traverse": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.10.tgz", - "integrity": "sha512-J7ycxg0/K9XCtLyHf0cz2DqDihonJeIo+z+HEdRe9YuT8TY4A66i+Ab2/xZCEW7Ro60bPCBBfqqboHSamoV3+g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -4417,13 +4646,13 @@ } }, "@babel/types": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", - "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, @@ -4606,13 +4835,13 @@ "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", - "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "dev": true, "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "@kwsites/file-exists": { @@ -4657,32 +4886,43 @@ } }, "@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", "dev": true, "requires": { "type-detect": "4.0.8" } }, "@sinonjs/fake-timers": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", - "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", + "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", "dev": true, "requires": { - "@sinonjs/commons": "^2.0.0" + "@sinonjs/commons": "^3.0.0" } }, "@sinonjs/samsam": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz", - "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", "dev": true, "requires": { "@sinonjs/commons": "^2.0.0", "lodash.get": "^4.4.2", "type-detect": "^4.0.8" + }, + "dependencies": { + "@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + } } }, "@sinonjs/text-encoding": { @@ -4716,9 +4956,9 @@ "dev": true }, "@types/chai": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz", - "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.9.tgz", + "integrity": "sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg==", "dev": true }, "@types/json-schema": { @@ -4728,9 +4968,9 @@ "dev": true }, "@types/mocha": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", - "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.3.tgz", + "integrity": "sha512-RsOPImTriV/OE4A9qKjMtk2MnXiuLLbcO3nCXK+kvq4nr0iMfFgpjaX3MPLb6f7+EL1FGSelYvuJMV6REH+ZPQ==", "dev": true }, "@types/node": { @@ -4740,9 +4980,9 @@ "dev": true }, "@types/parsimmon": { - "version": "1.10.6", - "resolved": "https://registry.npmjs.org/@types/parsimmon/-/parsimmon-1.10.6.tgz", - "integrity": "sha512-FwAQwMRbkhx0J6YELkwIpciVzCcgEqXEbIrIn3a2P5d3kGEHQ3wVhlN3YdVepYP+bZzCYO6OjmD4o9TGOZ40rA==" + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/@types/parsimmon/-/parsimmon-1.10.8.tgz", + "integrity": "sha512-i6oOxO9QKaqwMdMnnagvtVduc0ii9rl+BkNdAdSDhN27k9ryP8Fm6S9bcvNtuauK7PwTdoxCRorPcJvyGqwHgQ==" }, "@types/semver": { "version": "7.3.13", @@ -4751,18 +4991,18 @@ "dev": true }, "@types/sinon": { - "version": "10.0.13", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.13.tgz", - "integrity": "sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==", + "version": "10.0.20", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.20.tgz", + "integrity": "sha512-2APKKruFNCAZgx3daAyACGzWuJ028VVCUDk6o2rw/Z4PXT0ogwdV4KUegW0MwVs0Zu59auPXbbuBJHF12Sx1Eg==", "dev": true, "requires": { "@types/sinonjs__fake-timers": "*" } }, "@types/sinon-chai": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.9.tgz", - "integrity": "sha512-/19t63pFYU0ikrdbXKBWj9PCdnKyTd0Qkz0X91Ta081cYsq90OxYdcWwK/dwEoDa6dtXgj2HJfmzgq+QZTHdmQ==", + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.11.tgz", + "integrity": "sha512-1C5SBFzwn9hjiMr1xfqbULcSI9qXVpkGZT/LYbbd3jWiTo2MSvA+iFfwODlSoAXGeCgBw6S509dxy8zSIacr3Q==", "dev": true, "requires": { "@types/chai": "*", @@ -4776,9 +5016,9 @@ "dev": true }, "@types/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==", + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.6.tgz", + "integrity": "sha512-BT2Krtx4xaO6iwzwMFUYvWBWkV2pr37zD68Vmp1CDV196MzczBRxuEpD6Pr395HAgebC/co7hOphs53r8V7jew==", "dev": true }, "@types/yargs": { @@ -5224,6 +5464,13 @@ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true }, + "dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "requires": {} + }, "deep-eql": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", @@ -5621,9 +5868,9 @@ "dev": true }, "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true }, "get-package-type": { @@ -5754,6 +6001,11 @@ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true }, + "infestines": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/infestines/-/infestines-0.4.11.tgz", + "integrity": "sha512-09nHagZLOYUaXKHqdV+nxEaYaD0hRlKyhQMhgTMwfbvWpMkowXf4XLZzAkLq6Y90wZ7Wqm6aMoL2trBsNNKGeg==" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -5882,9 +6134,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -6081,9 +6333,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -6223,9 +6475,9 @@ "dev": true }, "nise": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", - "integrity": "sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.5.tgz", + "integrity": "sha512-VJuPIfUFaXNRzETTQEEItTOP8Y171ijr+JLq42wHes3DiryR8vT+1TXQW/Rx8JNUhyYYWyIvjXTU6dOhJcs9Nw==", "dev": true, "requires": { "@sinonjs/commons": "^2.0.0", @@ -6233,6 +6485,37 @@ "@sinonjs/text-encoding": "^0.7.1", "just-extend": "^4.0.2", "path-to-regexp": "^1.7.0" + }, + "dependencies": { + "@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.0" + }, + "dependencies": { + "@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + } + } + } } }, "node-preload": { @@ -6475,6 +6758,23 @@ "resolved": "https://registry.npmjs.org/parsimmon/-/parsimmon-1.18.1.tgz", "integrity": "sha512-u7p959wLfGAhJpSDJVYXoyMCXWYwHia78HhRBWqk7AIbxdmlrfdp5wX0l3xv/iTSH5HvhN9K7o26hwwpgS5Nmw==" }, + "partial.lenses": { + "version": "14.17.0", + "resolved": "https://registry.npmjs.org/partial.lenses/-/partial.lenses-14.17.0.tgz", + "integrity": "sha512-Iq+wDw5b5iwmn7b9MO+//buOqF0YoAC2Hsj+HoeG88BGeT3NkL/YwHNGDrySyX7xZSqj0LFEWwfgGSj4cSFmXQ==", + "requires": { + "infestines": "^0.4.11" + } + }, + "partial.lenses.validation": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/partial.lenses.validation/-/partial.lenses.validation-2.0.0.tgz", + "integrity": "sha512-NBZhmrunQWc4Ih5pJGYPHCEJfjIITPEkYn0Z6YB8qmW0iN4ZeqN0eZTWAPAH8MxjsEGx3G+8xnTMsEHbzy0k/A==", + "requires": { + "infestines": "^0.4.10", + "partial.lenses": "^14.0.0" + } + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6580,6 +6880,15 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prettier-printer": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/prettier-printer/-/prettier-printer-1.1.4.tgz", + "integrity": "sha512-gQIWF7PKVknRcMoZ4Lsqj2icc97W+Q9JTa7xQgNem07yZFzOimUPq50N5oRfKkSRBZT8QqrVW1IBEMBPWqjBgQ==", + "requires": { + "infestines": "^0.4.11", + "partial.lenses.validation": "^2.0.0" + } + }, "process-on-spawn": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", @@ -6683,9 +6992,9 @@ "dev": true }, "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -6728,9 +7037,9 @@ "dev": true }, "simple-git": { - "version": "3.16.1", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.16.1.tgz", - "integrity": "sha512-xzRxMKiy1zEYeHGXgAzvuXffDS0xgsq07Oi4LWEEcVH29vLpcZ2tyQRWyK0NLLlCVaKysZeem5tC1qHEOxsKwA==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.20.0.tgz", + "integrity": "sha512-ozK8tl2hvLts8ijTs18iFruE+RoqmC/mqZhjs/+V7gS5W68JpJ3+FCTmLVqmR59MaUQ52MfGQuWsIqfsTbbJ0Q==", "dev": true, "requires": { "@kwsites/file-exists": "^1.1.1", @@ -6739,17 +7048,25 @@ } }, "sinon": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.1.tgz", - "integrity": "sha512-PZXKc08f/wcA/BMRGBze2Wmw50CWPiAH3E21EOi4B49vJ616vW4DQh4fQrqsYox2aNR/N3kCqLuB0PwwOucQrg==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-17.0.0.tgz", + "integrity": "sha512-p4lJiYKBoOEVUxxVIC9H1MM2znG1/c8gud++I2BauJA5hsz7hHsst35eurNWXTusBsIq66FzOQbZ/uMdpvbPIQ==", "dev": true, "requires": { - "@sinonjs/commons": "^2.0.0", - "@sinonjs/fake-timers": "10.0.2", - "@sinonjs/samsam": "^7.0.1", - "diff": "^5.0.0", - "nise": "^5.1.2", + "@sinonjs/commons": "^3.0.0", + "@sinonjs/fake-timers": "^11.2.2", + "@sinonjs/samsam": "^8.0.0", + "diff": "^5.1.0", + "nise": "^5.1.5", "supports-color": "^7.2.0" + }, + "dependencies": { + "diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true + } } }, "sinon-chai": { @@ -6945,9 +7262,9 @@ "dev": true }, "unraw": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unraw/-/unraw-2.0.1.tgz", - "integrity": "sha512-tdOvLfRzHolwYcHS6HIX860MkK9LQ4+oLuNwFYL7bpgTEO64PZrcQxkisgwJYCfF8sKiWLwwu1c83DvMkbefIQ==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unraw/-/unraw-3.0.0.tgz", + "integrity": "sha512-08/DA66UF65OlpUDIQtbJyrqTR0jTAlJ+jsnkQ4jxR7+K5g5YG1APZKQSMCE1vqqmD+2pv6+IdEjmopFatacvg==" }, "update-browserslist-db": { "version": "1.0.5", @@ -6969,9 +7286,9 @@ } }, "uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" }, "v8-compile-cache-lib": { "version": "3.0.1", @@ -6995,9 +7312,9 @@ "dev": true }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, "workerpool": { @@ -7048,9 +7365,9 @@ "dev": true }, "yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "requires": { "cliui": "^8.0.1", diff --git a/package.json b/package.json index d65a3a42..c06bc5c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wollok-ts", - "version": "4.0.6", + "version": "4.0.7", "wollokVersion": "3.1.9", "description": "TypeScript based Wollok language implementation", "repository": "https://github.com/uqbar-project/wollok-ts", @@ -22,37 +22,41 @@ "test:sanity": "npm run test:wtest -- --root language/test/sanity", "test:validations": "mocha --parallel -r ts-node/register/transpile-only test/validator.test.ts", "test:wtest": "mocha --delay -t 10000 -r ts-node/register/transpile-only test/wtest.ts", + "test:printer": "mocha --parallel -r ts-node/register/transpile-only test/printer.test.ts", + "test:parser": "mocha --parallel -r ts-node/register/transpile-only test/parser.test.ts", "prepublishOnly": "npm run build && npm test", "postpublish": "git tag v$npm_package_version && git push --tags", "prepack": "npm run build" }, "dependencies": { - "@types/parsimmon": "^1.10.6", + "@types/parsimmon": "^1.10.8", "parsimmon": "^1.18.1", - "unraw": "^2.0.1", - "uuid": "9.0.0" + "prettier-printer": "^1.1.4", + "unraw": "^3.0.0", + "uuid": "^9.0.1" }, "devDependencies": { - "@types/chai": "^4.3.4", - "@types/mocha": "^10.0.1", + "@types/chai": "^4.3.9", + "@types/mocha": "^10.0.3", "@types/node": "^18.14.1", - "@types/sinon": "^10.0.13", - "@types/sinon-chai": "^3.2.9", - "@types/uuid": "^9.0.1", + "@types/sinon": "^10.0.20", + "@types/sinon-chai": "^3.2.11", + "@types/uuid": "^9.0.6", "@types/yargs": "^17.0.22", "@typescript-eslint/eslint-plugin": "^5.53.0", "@typescript-eslint/parser": "^5.53.0", "chai": "^4.3.7", "chalk": "^5.2.0", + "dedent": "^1.5.1", "eslint": "^8.35.0", "globby": "^11.1.0", "mocha": "^10.2.0", "nyc": "^15.1.0", - "simple-git": "^3.16.1", - "sinon": "15.0.1", + "simple-git": "^3.20.0", + "sinon": "17.0.0", "sinon-chai": "^3.7.0", "ts-node": "^10.9.1", "typescript": "^4.9.5", - "yargs": "^17.7.1" + "yargs": "^17.7.2" } } diff --git a/src/constants.ts b/src/constants.ts index 66b79297..29339361 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,5 +1,64 @@ +import { Name } from './model' + export const WOLLOK_EXTRA_STACK_TRACE_HEADER = 'Derived from TypeScript stack' export const WOLLOK_BASE_PACKAGE = 'wollok.' -export const INITIALIZE_METHOD_NAME = 'initialize' \ No newline at end of file +export const INITIALIZE_METHOD_NAME = 'initialize' + +export const PREFIX_OPERATORS: Record = { + '!': 'negate', + '-': 'invert', + '+': 'plus', + 'not': 'negate', +} + +export const ASSIGNATION_OPERATORS = ['=', '||=', '/=', '-=', '+=', '*=', '&&=', '%='] + +export const INFIX_OPERATORS = [ + ['||', 'or'], + ['&&', 'and'], + ['===', '==', '!==', '!='], + ['>=', '>', '<=', '<'], + ['?:', '>>>', '>>', '>..', '<>', '<=>', '<<<', '<<', '..<', '..', '->'], + ['-', '+'], + ['/', '*'], + ['**', '%'], +] + +export const LIST_MODULE= 'wollok.lang.List' +export const SET_MODULE= 'wollok.lang.Set' +export const OBJECT_MODULE= 'wollok.lang.Object' + +export const KEYWORDS = { + IF: 'if', + ELSE: 'else', + NEW: 'new', + SELF: 'self', + SUPER: 'super', + NULL: 'null', + METHOD: 'method', + VAR: 'var', + THROW: 'throw', + TRY: 'try', + CATCH: 'catch', + THEN: 'then', + ALWAYS: 'always', + PROPERTY: 'property', + CONST: 'const', + OVERRIDE: 'override', + NATIVE: 'native', + RETURN: 'return', + IMPORT: 'import', + SUITE: 'describe', + TEST: 'test', + MIXIN: 'mixin', + CLASS: 'class', + INHERITS: 'inherits', + DERIVED: 'derived', + MIXED_AND: 'and', + WKO: 'object', + FIXTURE: 'fixture', + PROGRAM: 'program', + PACKAGE: 'package', +} as const diff --git a/src/index.ts b/src/index.ts index dd8e0bd9..f498d3e6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,7 @@ import { List } from './extensions' import { fromJSON } from './jsonUtils' import * as parse from './parser' import validate from './validator' +import print from './printer/print' import WRE from './wre/wre.json' import WRENatives from './wre/wre.natives' @@ -29,4 +30,5 @@ export { parse, link, validate, + print, } \ No newline at end of file diff --git a/src/interpreter/runtimeModel.ts b/src/interpreter/runtimeModel.ts index 9c87a1b8..552abca9 100644 --- a/src/interpreter/runtimeModel.ts +++ b/src/interpreter/runtimeModel.ts @@ -1,8 +1,8 @@ import { v4 as uuid } from 'uuid' -import { INITIALIZE_METHOD_NAME, WOLLOK_BASE_PACKAGE, WOLLOK_EXTRA_STACK_TRACE_HEADER } from '../constants' +import { INITIALIZE_METHOD_NAME, LIST_MODULE, SET_MODULE, WOLLOK_BASE_PACKAGE, WOLLOK_EXTRA_STACK_TRACE_HEADER } from '../constants' import { getPotentiallyUninitializedLazy } from '../decorators' import { get, is, last, List, match, raise, when } from '../extensions' -import { Assignment, Body, Catch, Describe, Entity, Environment, Expression, Id, If, Literal, LiteralValue, Method, Module, Name, New, Node, Package, Program, Reference, Return, Self, Send, Singleton, Super, Test, Throw, Try, Variable } from '../model' +import { Assignment, Body, Catch, Class, Describe, Entity, Environment, Expression, Id, If, Literal, LiteralValue, Method, Module, Name, New, Node, Package, Program, Reference, Return, Self, Send, Singleton, Super, Test, Throw, Try, Variable } from '../model' import { Interpreter } from './interpreter' const { isArray } = Array @@ -487,6 +487,12 @@ export class Evaluation { yield node const target = node.instantiated.target ?? raise(new Error(`Could not resolve reference to instantiated module ${node.instantiated.name}`)) + const name = node.instantiated.name + if (!target.is(Class)) raise(new Error(`${name} is not a class, you cannot generate instances of it`)) + if (target.isAbstract) raise(new Error(`${name} is an abstract class, you cannot generate instances`)) + + if (target.fullyQualifiedName === LIST_MODULE) return yield* this.list() + if (target.fullyQualifiedName === SET_MODULE) return yield* this.set() return yield* this.instantiate(target, args) } diff --git a/src/model.ts b/src/model.ts index d22d606d..57a24b39 100644 --- a/src/model.ts +++ b/src/model.ts @@ -1,5 +1,5 @@ import { cached, getPotentiallyUninitializedLazy, lazy } from './decorators' -import { ConstructorFor, InstanceOf, is, last, List, mapObject, Mixable, MixinDefinition, MIXINS, notEmpty, TypeDefinition } from './extensions' +import { ConstructorFor, InstanceOf, List, MIXINS, Mixable, MixinDefinition, TypeDefinition, is, last, mapObject, notEmpty } from './extensions' const { isArray } = Array const { entries, values, assign } = Object @@ -471,6 +471,16 @@ export function Module>(supertype: S) { return undefined } + @cached + get isAbstract(): boolean { + return this.abstractMethods.some(method => !this.lookupMethod(method.name, method.parameters.length)) + } + + @cached + get abstractMethods(): List { + return this.hierarchy.flatMap(module => module.methods.filter(method => method.isAbstract())) + } + @cached defaultValueFor(field: Field): Expression { if(!this.allFields.includes(field)) throw new Error('Field does not belong to the module') @@ -508,12 +518,6 @@ export class Class extends Module(Node) { return this === objectClass ? undefined : objectClass } } - - @cached - get isAbstract(): boolean { - const abstractMethods = this.hierarchy.flatMap(module => module.methods.filter(method => method.isAbstract())) - return abstractMethods.some(method => !this.lookupMethod(method.name, method.parameters.length)) - } } @@ -750,7 +754,7 @@ export class Super extends Expression(Node) { export class New extends Expression(Node) { get kind(): 'New' { return 'New' } - readonly instantiated!: Reference + readonly instantiated!: Reference readonly args!: List constructor({ args = [], ...payload }: Payload) { diff --git a/src/parser.ts b/src/parser.ts index ac4233e0..29df8e7f 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -1,7 +1,8 @@ -import Parsimmon, { alt as alt_parser, index, lazy, makeSuccess, notFollowedBy, of, Parser, regex, seq, seqObj, string, whitespace, any, Index, newline } from 'parsimmon' +import Parsimmon, { Index, Parser, alt as alt_parser, any, index, lazy, makeSuccess, newline, notFollowedBy, of, regex, seq, seqObj, string, whitespace } from 'parsimmon' import unraw from 'unraw' -import { BaseProblem, SourceIndex, Assignment as AssignmentNode, Body as BodyNode, Catch as CatchNode, Class as ClassNode, Describe as DescribeNode, Entity as EntityNode, Expression as ExpressionNode, Field as FieldNode, If as IfNode, Import as ImportNode, Literal as LiteralNode, Method as MethodNode, Mixin as MixinNode, Name, NamedArgument as NamedArgumentNode, New as NewNode, Node, Package as PackageNode, Parameter as ParameterNode, Program as ProgramNode, Reference as ReferenceNode, Return as ReturnNode, Self as SelfNode, Send as SendNode, Sentence as SentenceNode, Singleton as SingletonNode, Super as SuperNode, Test as TestNode, Throw as ThrowNode, Try as TryNode, Variable as VariableNode, SourceMap, Closure as ClosureNode, ParameterizedType as ParameterizedTypeNode, Level, LiteralValue, Annotation } from './model' -import { List, mapObject, discriminate, is } from './extensions' +import { ASSIGNATION_OPERATORS, INFIX_OPERATORS, PREFIX_OPERATORS } from './constants' +import { List, discriminate, is, mapObject } from './extensions' +import { Annotation, Assignment as AssignmentNode, BaseProblem, Body as BodyNode, Catch as CatchNode, Class as ClassNode, Closure as ClosureNode, Describe as DescribeNode, Entity as EntityNode, Expression as ExpressionNode, Field as FieldNode, If as IfNode, Import as ImportNode, Level, Literal as LiteralNode, LiteralValue, Method as MethodNode, Mixin as MixinNode, Name, NamedArgument as NamedArgumentNode, New as NewNode, Node, Package as PackageNode, Parameter as ParameterNode, ParameterizedType as ParameterizedTypeNode, Program as ProgramNode, Reference as ReferenceNode, Return as ReturnNode, Self as SelfNode, Send as SendNode, Sentence as SentenceNode, Singleton as SingletonNode, SourceIndex, SourceMap, Super as SuperNode, Test as TestNode, Throw as ThrowNode, Try as TryNode, Variable as VariableNode } from './model' // TODO: Use description in lazy() for better errors // TODO: Support FQReferences to singletons as expressions @@ -9,26 +10,6 @@ import { List, mapObject, discriminate, is } from './extensions' const { keys, values, fromEntries } = Object const { isArray } = Array -const PREFIX_OPERATORS: Record = { - '!': 'negate', - '-': 'invert', - '+': 'plus', - 'not': 'negate', -} - -const ASSIGNATION_OPERATORS = ['=', '||=', '/=', '-=', '+=', '*=', '&&=', '%='] - -const INFIX_OPERATORS = [ - ['||', 'or'], - ['&&', 'and'], - ['===', '==', '!==', '!='], - ['>=', '>', '<=', '<'], - ['?:', '>>>', '>>', '>..', '<>', '<=>', '<<<', '<<', '..<', '..', '->'], - ['-', '+'], - ['/', '*'], - ['**', '%'], -] - const ALL_OPERATORS = [ ...values(PREFIX_OPERATORS), ...INFIX_OPERATORS.flat(), @@ -57,7 +38,7 @@ const error = (code: string) => (...safewords: string[]) => { const skippable = (...breakpoints: Parser[]): Parser => lazy(() => alt( skippableContext, - comment, + comment('start'), notFollowedBy(alt(key('}'), newline, ...breakpoints)).then(any), ) ) @@ -99,11 +80,16 @@ const key = (str: T): Parser => ( : string(str) ).trim(_) -const comment = lazy('comment', () => regex(/\/\*(.|[\r\n])*?\*\/|\/\/.*/)) -const _ = optional(comment.or(whitespace).atLeast(1)) +const _ = optional(whitespace.atLeast(1)) const __ = optional(key(';').or(_)) +const comment = (position: 'start' | 'end') => lazy('comment', () => regex(/\/\*(.|[\r\n])*?\*\/|\/\/.*/)).map(text => new Annotation('comment', { text, position })) +const endComment = alt( + optional(_).then(comment('end')), // same-line comment + comment('end').sepBy(_) // after-line comments +) + export const annotation: Parser = lazy(() => string('@').then(obj({ name, @@ -119,15 +105,15 @@ export const annotation: Parser = lazy(() => const node = (constructor: new (payload: P) => N) => (parser: () => Parser

): Parser => seq( - annotation.sepBy(_).wrap(_, _), + alt(annotation, comment('start')).sepBy(_).wrap(_, _), index, lazy(parser), - index - ).map(([metadata, start, payload, end]) => - new constructor({ metadata, sourceMap: buildSourceMap(start, end), ...payload }) + endComment, + index, + ).map(([metadata, start, payload, comment, end]) => + new constructor({ metadata: metadata.concat(comment), sourceMap: buildSourceMap(start, end), ...payload }) ) - export const File = (fileName: string): Parser => lazy(() => obj({ fileName: of(fileName), @@ -409,9 +395,10 @@ const messageChain = (receiver: Parser, message: Parser, a index, receiver, seq(message, args, index).many(), - ).map(([start, initialReceiver, calls]) => + endComment, + ).map(([start, initialReceiver, calls, comments]) => calls.reduce((receiver, [message, args, end]) => - new SendNode({ receiver, message, args, sourceMap: buildSourceMap(start, end) }) + new SendNode({ receiver, message, args, sourceMap: buildSourceMap(start, end), metadata: Array.isArray(comments) ? comments : [comments] }) , initialReceiver) ) ) diff --git a/src/printer/print.ts b/src/printer/print.ts new file mode 100644 index 00000000..c98750d2 --- /dev/null +++ b/src/printer/print.ts @@ -0,0 +1,515 @@ +import { IDoc, align, append, choice, enclose, intersperse, lineBreak, lineBreaks, hang as nativeHang, indent as nativeIndent, nest as nativeNest, parens, prepend, render, softBreak, softLine } from 'prettier-printer' +import { KEYWORDS, LIST_MODULE, PREFIX_OPERATORS, SET_MODULE } from '../constants' +import { List, isEmpty, match, notEmpty, when } from '../extensions' +import { Annotation, Assignment, Body, Catch, Class, Describe, Expression, Field, If, Import, Literal, Method, Mixin, Name, NamedArgument, New, Node, Package, Parameter, ParameterizedType, Program, Reference, Return, Self, Send, Sentence, Singleton, Super, Test, Throw, Try, Variable } from '../model' +import { DocTransformer, WS, body, defaultToEmpty, enclosedList, infixOperators, listEnclosers, listed, setEnclosers, stringify } from './utils' + +type PrintSettings = { + maxWidth: number, + useSpaces: boolean, + /** + * @example `x = x + 1` abbreviated to `x += 1` + */ + abbreviateAssignments: boolean +} + +type PrintContext = { + indent: DocTransformer, + nest: DocTransformer, + hang: DocTransformer, + abbreviateAssignments: boolean, +} + +export default print + + +function print(node: Node, { maxWidth, useSpaces, abbreviateAssignments }: PrintSettings): string { + const indentationCharacters = useSpaces ? ' ' : '\t' + return render( + maxWidth, + format({ + indent: nativeIndent(indentationCharacters), + nest: nativeNest(indentationCharacters), + hang: nativeHang(indentationCharacters), + abbreviateAssignments, + })(node)) +} + +// ----------------------- +// ----NODE FORMATTERS---- +// ----------------------- + +type Formatter = (node: T) => IDoc +type FormatterWithContext = (context: PrintContext) => Formatter +const format: FormatterWithContext = context => node => { + const metadata: [IDoc, IDoc] = splitMetadata(context, node.metadata) + const formattedNode: IDoc = match(node)( + when(Package)(formatPackage(context)), + when(Program)(formatProgram(context)), + when(Assignment)(formatAssignment(context)), + when(Singleton)(formatSingleton(context)), + when(Class)(formatClass(context)), + when(Mixin)(formatMixin(context)), + when(Method)(formatMethod(context)), + when(Field)(formatField(context)), + when(Variable)(formatVariable(context)), + when(Describe)(formatDescribe(context)), + when(Test)(formatTest(context)), + when(Parameter)(formatParameter), + when(Literal)(formatLiteral(context)), + when(Body)(formatBody(context)), + when(Send)(formatSend(context)), + when(If)(formatIf(context)), + when(New)(formatNew(context)), + when(ParameterizedType)(formatParameterizedType(context)), + when(NamedArgument)(formatNamedArgument(context)), + when(Return)(formatReturn(context)), + when(Try)(formatTry(context)), + when(Catch)(formatCatch(context)), + when(Throw)(formatThrow(context)), + when(Reference)(formatReference), + when(Self)(formatSelf), + when(Import)(formatImport), + when(Super)(formatSuper(context)), + ) + + return enclose(metadata, formattedNode) +} + +const formatPackage: FormatterWithContext = context => node => { + const contents = [notEmpty(node.imports) ? [intersperse(lineBreak, node.imports.map(format(context))), lineBreaks] : [], intersperse( + lineBreaks, + node.members.map(format(context)) + )] + + return node.isSynthetic ? + contents : + intersperse(WS, [KEYWORDS.PACKAGE, node.name, body(context.nest)(contents)]) +} + +const formatImport: Formatter = node => { + const wildcard = node.entity.target?.is(Package) ? '.*' : '' + + return [KEYWORDS.IMPORT, WS, node.entity.name, wildcard] +} + +const formatProgram: FormatterWithContext = context => node => intersperse(WS, [KEYWORDS.PROGRAM, node.name, format(context)(node.body)]) + +const formatMethod: FormatterWithContext = context => node => { + const formatWithContext = format(context) + const signature = [ + node.isOverride ? [KEYWORDS.OVERRIDE, WS] : [], + KEYWORDS.METHOD, + WS, + node.name, + enclosedListOfNodes(context)(parens, node.parameters), + ] + + if(node.isNative()){ + return [signature, WS, KEYWORDS.NATIVE] + } else if (node.isAbstract()){ + return signature + } else if(node.isConcrete()) { + if( + node.body.sentences.length === 1 && + node.body.sentences[0].is(Return) && + node.body.sentences[0].value + ) { + return intersperse(WS, [signature, '=', formatWithContext(node.body!.sentences[0].value)]) + } + else { + return [signature, WS, formatWithContext(node.body as Body)] + } + } else { + throw Error('Malformed method') + } +} + +const formatBody: (context: PrintContext) => Formatter = context => node => body(context.nest)(formatSentences(context)(node.sentences)) + +const formatReturn: FormatterWithContext = context => node => node.value ? + [KEYWORDS.RETURN, WS, format(context)(node.value)] + : KEYWORDS.RETURN + +const formatReference = (node: Reference) => node.name + +const formatField: FormatterWithContext = context => node => { + let modifiers: IDoc = [node.isConstant ? KEYWORDS.CONST : KEYWORDS.VAR] + if(node.isProperty){ + modifiers = append([WS, KEYWORDS.PROPERTY], modifiers) + } + return [ + modifiers, + WS, + formatAssign(context, true)(node.name, node.value), + ] +} + +const formatVariable: FormatterWithContext = context => node => { + return [ + node.isConstant ? KEYWORDS.CONST : KEYWORDS.VAR, + WS, + formatAssign(context)(node.name, node.value), + ] +} + +const formatParameter: Formatter = node => node.name + +const formatTest: FormatterWithContext = context => node => { + return intersperse(WS, [ + KEYWORDS.TEST, + node.name, + body(context.nest)(formatSentences(context)(node.body.sentences)), + ]) +} + +const formatDescribe: FormatterWithContext = context => node => intersperse( + WS, + [KEYWORDS.SUITE, node.name, formatModuleMembers(context)(node.members)] +) + + +const formatAssignment: FormatterWithContext= context => node => + canBeAbbreviated(node) && context.abbreviateAssignments ? + formatAssign(context)(node.variable.name, node.value.args[0], assignmentOperationByMessage[node.value.message]) : + formatAssign(context)(node.variable.name, node.value) + +const formatSuper: FormatterWithContext = context => node => + [KEYWORDS.SUPER, formatArguments(context)(node.args)] + +const formatIf: FormatterWithContext = context => node => { + const condition = [KEYWORDS.IF, WS, enclose(parens, format(context)(node.condition))] + + if(canInlineBody(node.thenBody) && (node.elseBody.isSynthetic || canInlineBody(node.elseBody))){ + return formatInlineIf(condition)(context)(node) + } + + const thenBody = body(context.nest)(formatSentences(context)(node.thenBody.sentences)) + const elseBody = !(isEmpty(node.elseBody.sentences) || node.elseBody.isSynthetic) ? body(context.nest)(formatSentences(context)(node.elseBody.sentences)) : undefined + return [ + condition, + WS, + thenBody, + elseBody ? [WS, KEYWORDS.ELSE, WS, elseBody] : [], + ] +} + +const formatInlineIf: (condition: IDoc) => FormatterWithContext = condition => context => node => choice( + [ + condition, + WS, + format(context)(node.thenBody.sentences[0]), + notEmpty(node.elseBody.sentences) ? [WS, KEYWORDS.ELSE, WS, format(context)(node.elseBody.sentences[0])] : [], + ], + [ + align([ + context.hang([ + condition, + softLine, + format(context)(node.thenBody.sentences[0]), + ]), + notEmpty(node.elseBody.sentences) ? [ + lineBreak, + context.hang([KEYWORDS.ELSE, softLine, format(context)(node.elseBody.sentences[0])]), + ] : [], + ]), + ] +) + +function canInlineBody(aBody: Body): aBody is Body & { sentences: [Expression] } { + return aBody.sentences.length === 1 && [Send, Literal, Reference].some(kind => aBody.sentences[0].is(kind)) +} + +const formatNew: FormatterWithContext = context => node => { + const args = + enclosedListOfNodes(context)(parens, node.args) + return [ + KEYWORDS.NEW, + WS, + node.instantiated.name, + args, + ] +} + +const formatThrow: FormatterWithContext = context => node => [ + KEYWORDS.THROW, + WS, + format(context)(node.exception), +] + +const formatTry: FormatterWithContext = context => node => { + const formatter = format(context) + const always = notEmpty(node.always.sentences) ? + prepend(WS, intersperse(WS, [KEYWORDS.THEN, KEYWORDS.ALWAYS, formatter(node.always)])) + : [] + + return [ + KEYWORDS.TRY, + WS, + formatter(node.body), + node.catches ? [WS, intersperse(WS, node.catches.map(formatter))] : [], + always, + ] +} + +const formatCatch: FormatterWithContext = context => node => { + const type = prepend(WS, intersperse(WS, [node.parameter.name, ':', format(context)(node.parameterType)])) + return [ + KEYWORDS.CATCH, + type, + WS, + format(context)(node.body), + ] +} + +const formatLiteral: FormatterWithContext = context => node => { + if(node.isBoolean()){ + return `${node.value}` + } else if(node.isNumeric()) { + return node.value.toString() //ToDo presition + } else if(node.isNull()){ + return KEYWORDS.NULL + } else if(node.isString()){ + return stringify(`${node.value}`) + } else if(node.isCollection()){ + const [{ name: moduleName }, elements] = node.value as any + switch(moduleName){ + case LIST_MODULE: + return formatCollection(context)(elements as Expression[], listEnclosers) + case SET_MODULE: + return formatCollection(context)(elements as Expression[], setEnclosers) + default: + throw new Error('Unknown collection type') + } + } else { + throw new Error('Unknown literal type') + } +} + +const formatSelf: Formatter = (_: Self) => KEYWORDS.SELF + +const formatClass: FormatterWithContext = context => node => { + let header: IDoc = [ + KEYWORDS.CLASS, + node.name, + ] + + if(inherits(node)){ + header = [...header, formatInheritance(context)(node)] + } + + return intersperse(WS, [...header, formatModuleMembers(context)(node.members)]) +} + +const formatMixin: FormatterWithContext =context => node => { + const declaration = [ + KEYWORDS.MIXIN, + WS, + node.name, + notEmpty(node.supertypes) ? [WS, KEYWORDS.INHERITS, WS, intersperse([WS, KEYWORDS.MIXED_AND, WS], node.supertypes.map(format(context)))] : [], + ] + + return [declaration, WS, formatModuleMembers(context)(node.members)] +} + +const formatParameterizedType: FormatterWithContext = + context => node => [ + node.reference.name, + notEmpty(node.args) ? + [WS, enclosedListOfNodes(context)(parens, node.args)] : + [], + ] + +const formatNamedArgument: FormatterWithContext = + context => node => intersperse(WS, [node.name, '=', format(context)(node.value)]) + +// SINGLETON FORMATTERS + +const formatSingleton: FormatterWithContext = context => (node: Singleton) => { + const formatter = node.isClosure() ? formatClosure : formatWKO + return formatter(context)(node) +} + +const formatClosure: FormatterWithContext = context => node => { + const applyMethod = node.members[0] as Method + const hasParameters = notEmpty(applyMethod.parameters) + const parameters = hasParameters ? + [listed(applyMethod.parameters.map(format(context))), WS, '=>'] + : [] + + const sentences = (applyMethod.body! as Body).sentences + + if(sentences.length === 1) { + const firstSentence = sentences[0] + // remove 'return' if it's the only sentence + const sentence = format(context)(firstSentence.is(Return) && firstSentence.value ? firstSentence.value : firstSentence) + return enclose([['{', WS], [WS, '}']], context.nest([hasParameters ? [parameters, softLine] : [], sentence])) + } else { + return enclose([['{', WS], '}'], [parameters, lineBreak, context.indent(formatSentences(context)(sentences)), lineBreak]) + } +} + +const formatWKO: FormatterWithContext = context => node => { + const members = formatModuleMembers(context)(node.members) + let formatted: IDoc = [KEYWORDS.WKO] + + if(node.name){ + formatted = [...formatted, node.name] + } + + if(inherits(node)){ + formatted = [...formatted, formatInheritance(context)(node)] + } + + return intersperse(WS, [...formatted, members]) +} + +const inherits = (node: Singleton | Class) => notEmpty(node.supertypes) + +const formatInheritance: FormatterWithContext = (context: PrintContext) => node => { + return intersperse(WS, [ + KEYWORDS.INHERITS, + listed(node.supertypes.map(format(context))), + ]) +} + + +// SEND FORMATTERS + +const formatSend: FormatterWithContext = context => node => { + const formatter = isInfixOperator(node) ? formatInfixSend : isPrefixOperator(node) ? formatPrefixSend : formatDotSend + return formatter(context)(node) +} + +const formatDotSend: FormatterWithContext = context => node => [ + addParenthesisIfNeeded(context, node.receiver), + '.', + node.message, + formatArguments(context)(node.args), +] + +const formatInfixSend: FormatterWithContext = context => node => { + return intersperse(WS, [ + addParenthesisIfNeeded(context, node.receiver), + node.message, + addParenthesisIfNeeded(context, node.args[0]), + ]) +} + +const formatPrefixSend: FormatterWithContext = context => node => { + return [prefixOperatorByMessage[node.message], addParenthesisIfNeeded(context, node.receiver)] +} + +function addParenthesisIfNeeded(context: PrintContext, expression: Expression): IDoc { + // ToDo: add more cases where parenthesis aren't needed + const formatted = format(context)(expression) + return expression.is(Send) && (isInfixOperator(expression) || isPrefixOperator(expression)) ? + enclose(parens, formatted) : + formatted +} + + +// AUXILIARY FORMATTERS + +const formatSentences = (context: PrintContext) => (sentences: List, simplifyLastReturn = false) => sentences.reduce((formatted, sentence, i, sentences) => { + const shouldShortenReturn = i === sentences.length - 1 && sentence.is(Return) && sentence.value && simplifyLastReturn + const previousSentence = sentences[i-1] + return [formatted, formatSentenceInBody(context)(!shouldShortenReturn ? sentence : sentence.value, previousSentence)] +}, []) + +const formatArguments = (context: PrintContext) => (args: List): IDoc => enclosedListOfNodes(context)(parens, args) + +const formatSentenceInBody = (context: PrintContext) => (sentence: Sentence, previousSentence: Sentence | undefined): IDoc => { + const distanceFromLastSentence = (sentence.sourceMap && (!previousSentence || previousSentence.sourceMap) ? + previousSentence ? + sentence.sourceMap!.start.line - previousSentence.sourceMap!.end.line //difference + : -1 // first sentence + : 0 // defaults to 1 line diff + ) + 1 + return [Array(distanceFromLastSentence).fill(lineBreak), format(context)(sentence)] +} + +const formatAssign = (context: PrintContext, ignoreNull = false) => (name: string, value: Expression, assignmentOperator = '=') => [ + name, + ignoreNull && value.is(Literal) && value.isNull() && value.isSynthetic ? + [] : + [ + WS, + assignmentOperator, + [softBreak, choice(WS, context.indent([]))], + format(context)(value), + ], +] + +const formatCollection = (context: PrintContext) => (values: Expression[], enclosers: [IDoc, IDoc]) => { + return enclosedListOfNodes(context)(enclosers, values) +} + +const formatModuleMembers = (context: PrintContext) => (members: List): IDoc => { + const formatter = format(context) + const concreteMembers = members.filter(member => !member.isSynthetic) + const fields = concreteMembers.filter(member => member.is(Field)).map(formatter) + const others = concreteMembers.filter(member => !member.is(Field)).map(formatter) + return body(context.nest)([fields.length > 0 ? [intersperse(lineBreak, fields), others.length > 0 ? lineBreaks : []] : [], intersperse(lineBreaks, others)]) +} + +// assignment operations +const canBeAbbreviated = (node: Assignment): node is Assignment & {value: Send & {message: keyof typeof assignmentOperationByMessage}} => node.value.is(Send) && node.value.receiver.is(Reference) && node.value.receiver.name === node.variable.name && node.value.message in assignmentOperationByMessage + +const assignmentOperationByMessage = { '||':'||=', '/':'/=', '-':'-=', '+':'+=', '*':'*=', '&&':'&&=', '%':'%=' } as const + +// send utils + +/* + * ToDo: safe way of telling if this is a parser-generated message or a user-defined one + * e.g. x.negate() shouldnt be formatted to !x + */ +const isPrefixOperator = (node: Send): boolean => + Object.values(PREFIX_OPERATORS).includes(node.message) && + isEmpty(node.args) + +const isInfixOperator = (node: Send): boolean => + infixOperators.includes(node.message) && + node.args.length === 1 + +//ToDo: missing 'not' +const prefixOperatorByMessage: Record = { + 'negate': '!', + 'invert': '-', + 'plus': '+', +} + +// metadata +const splitMetadata = (context: PrintContext, metadata: List): [IDoc, IDoc] => { + const withSplittedMultilineComments = metadata.map(annotation => annotation.name === 'comment' && (annotation.args.get('text')! as string).includes('\n') ? + (annotation.args.get('text')! as string).split('\n').map(commentSection => new Annotation('comment', { text: commentSection.trimStart(), position:annotation.args.get('position')! } )) : + annotation + ).flat() + + const prevMetadata = withSplittedMultilineComments.filter(metadata => !isComment(metadata) || metadata.args.get('position') === 'start') + const afterMetadata = withSplittedMultilineComments.filter(metadata => metadata.args.get('position') === 'end') + const metadataBefore = defaultToEmpty(notEmpty(prevMetadata), [intersperse(lineBreak, prevMetadata.map(formatAnnotation(context))), lineBreak]) + const metadataAfter = defaultToEmpty(notEmpty(afterMetadata), [softLine, intersperse(lineBreak, afterMetadata.map(formatAnnotation(context)))]) + + return [metadataBefore, metadataAfter] +} + +const formatAnnotation = (context: PrintContext) => (annotation: Annotation): IDoc => { + if(annotation.name === 'comment') return annotation.args.get('text')! as string + return ['@', annotation.name, enclosedList(context.nest)(parens, [...annotation.args.entries()].map( + ([name, value]) => intersperse(WS, [name, '=', format(context)(new Literal({ value }))]) + ))] +} + +function isComment(annotation: Annotation): annotation is Annotation & {name: 'comment'} { + return annotation.name === 'comment' +} + + +//lists +const enclosedListOfNodes = (context: PrintContext) => (enclosers: [IDoc, IDoc], nodes: List): IDoc => + enclosedList(context.nest)( + enclosers, + nodes.map(format(context)), + nodes.some(aNode => aNode.metadata.some(entry => entry.name ==='comment')) + ) \ No newline at end of file diff --git a/src/printer/utils.ts b/src/printer/utils.ts new file mode 100644 index 00000000..3062df69 --- /dev/null +++ b/src/printer/utils.ts @@ -0,0 +1,39 @@ +import { IDoc, append, braces, brackets, choice, dquotes, enclose, intersperse, lineBreak, softLine } from 'prettier-printer' +import { INFIX_OPERATORS } from '../constants' + +export type DocTransformer = (doc: IDoc) => IDoc + +export const infixOperators = INFIX_OPERATORS.flat() + + +type Encloser = [IDoc, IDoc] +export const listEnclosers: Encloser = brackets +export const setEnclosers: Encloser = ['#{', '}'] + +export const WS = ' ' as IDoc + +export const body = (nest: DocTransformer) => (content: IDoc): IDoc => encloseIndented(braces, content, nest) + +/** + * Formats a list of documents to "doc1, doc2, doc3" spreading it over multiple lines when needed + */ +export const listed = (contents: IDoc[], separator: IDoc = ','): IDoc => intersperse([separator, softLine], contents) + +export const enclosedList = (nest: DocTransformer) => (enclosers: [IDoc, IDoc], content: IDoc[], forceSpread = false, separator: IDoc = ','): IDoc => { + if(content.length === 0) return enclose(enclosers, '') + const narrowFormat = encloseIndented(['', ''], intersperse([separator, lineBreak], content), nest) + return enclose( + enclosers, + forceSpread ? narrowFormat : choice( + intersperse([separator, WS], content), + narrowFormat + ) + ) +} + +export const encloseIndented = (enclosers: [IDoc, IDoc], content: IDoc, nest: DocTransformer): IDoc => + enclose(enclosers, append(lineBreak, nest([lineBreak, content]))) + +export const stringify = enclose(dquotes) + +export const defaultToEmpty = (condition: boolean, doc: IDoc): IDoc => condition ? doc : [] \ No newline at end of file diff --git a/src/validator.ts b/src/validator.ts index ca7ad039..04f84d2f 100644 --- a/src/validator.ts +++ b/src/validator.ts @@ -139,9 +139,9 @@ export const shouldHaveCatchOrAlways = error(node => notEmpty(node.catches) || notEmpty(node.always.sentences) ) -export const methodShouldHaveDifferentSignature = error(node => { - return node.parent.methods.every(other => node === other || !other.matchesSignature(node.name, node.parameters.length)) -}) +export const methodShouldHaveDifferentSignature = error(node => + node.parent.methods.every(other => node === other || !other.matchesSignature(node.name, node.parameters.length)) +) export const shouldNotOnlyCallToSuper = warning(node => { const callsSuperWithSameArgs = (sentence?: Sentence) => sentence?.is(Super) && sentence.args.every((arg, index) => arg.is(Reference) && arg.target === node.parameters[index]) @@ -438,11 +438,18 @@ export const getterMethodShouldReturnAValue = warning(node => export const shouldNotUseReservedWords = warning(node => !usesReservedWords(node)) export const shouldInitializeGlobalReference = error(node => - !(node.isAtPackageLevel && node.value.isSynthetic && node.value.is(Literal) && node.value.isNull()) + !(node.isAtPackageLevel && isInitialized(node)) ) export const shouldNotDefineUnusedVariables = warning(node => !unusedVariable(node)) +export const shouldInitializeConst = error(node => + !( + getContainer(node)?.is(Program) && + node.isConstant && + isInitialized(node)) +) + export const shouldNotDuplicatePackageName = error(node => !node.siblings().some(sibling => sibling.is(Package) && sibling.name == node.name) ) @@ -506,6 +513,15 @@ export const shouldNotUseVoidMethodAsValue = error(node => { return !method || method.isNative() || method.isAbstract() || returnsValue(method) }) +export const shouldHaveDifferentName = error(node => { + const tests: List = match(node.parent)( + when(Describe)(describe => describe.tests), + when(Package)(module => module.members.filter(member => member.is(Test)) as unknown as List), + when(Node)(_ => []), + ) + return !tests || tests.every(other => node === other || other.name !== node.name) +}) + // ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════ // HELPER FUNCTIONS // ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════ @@ -747,6 +763,11 @@ const methodExists = (node: Send): boolean => match(node.receiver)( when(Node)(() => true), ) +const isInitialized = (node: Variable) => + node.value.isSynthetic && + node.value.is(Literal) && + node.value.isNull() + // ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════ // REPORT HELPERS // ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════ @@ -770,13 +791,13 @@ const validationsByKind = (node: Node): Record> => match when(Catch)(() => ({ shouldCatchUsingExceptionHierarchy, catchShouldBeReachable })), when(Package)(() => ({ shouldNotDuplicatePackageName })), when(Program)(() => ({ nameShouldNotBeKeyword, shouldNotUseReservedWords, shouldMatchFileExtension, shouldNotDuplicateEntities })), - when(Test)(() => ({ shouldHaveNonEmptyName, shouldNotMarkMoreThanOneOnlyTest, shouldHaveAssertInTest, shouldMatchFileExtension })), + when(Test)(() => ({ shouldHaveNonEmptyName, shouldNotMarkMoreThanOneOnlyTest, shouldHaveAssertInTest, shouldMatchFileExtension, shouldHaveDifferentName })), when(Class)(() => ({ nameShouldBeginWithUppercase, nameShouldNotBeKeyword, shouldNotHaveLoopInHierarchy, linearizationShouldNotRepeatNamedArguments, shouldNotDefineMoreThanOneSuperclass, superclassShouldBeLastInLinearization, shouldNotDuplicateGlobalDefinitions, shouldNotDuplicateVariablesInLinearization, shouldImplementAllMethodsInHierarchy, shouldNotUseReservedWords, shouldNotDuplicateEntities })), when(Singleton)(() => ({ nameShouldBeginWithLowercase, inlineSingletonShouldBeAnonymous, topLevelSingletonShouldHaveAName, nameShouldNotBeKeyword, shouldInitializeInheritedAttributes, linearizationShouldNotRepeatNamedArguments, shouldNotDefineMoreThanOneSuperclass, superclassShouldBeLastInLinearization, shouldNotDuplicateGlobalDefinitions, shouldNotDuplicateVariablesInLinearization, shouldImplementInheritedAbstractMethods, shouldImplementAllMethodsInHierarchy, shouldNotUseReservedWords, shouldNotDuplicateEntities })), when(Mixin)(() => ({ nameShouldBeginWithUppercase, shouldNotHaveLoopInHierarchy, shouldOnlyInheritFromMixin, shouldNotDuplicateGlobalDefinitions, shouldNotDuplicateVariablesInLinearization, shouldNotDuplicateEntities })), when(Field)(() => ({ nameShouldBeginWithLowercase, shouldNotAssignToItselfInDeclaration, nameShouldNotBeKeyword, shouldNotDuplicateFields, shouldNotUseReservedWords, shouldNotDefineUnusedVariables, shouldDefineConstInsteadOfVar, shouldInitializeSingletonAttribute })), when(Method)(() => ({ onlyLastParameterCanBeVarArg, nameShouldNotBeKeyword, methodShouldHaveDifferentSignature, shouldNotOnlyCallToSuper, shouldUseOverrideKeyword, possiblyReturningBlock, shouldNotUseOverride, shouldMatchSuperclassReturnValue, shouldNotDefineNativeMethodsOnUnnamedSingleton, overridingMethodShouldHaveABody, getterMethodShouldReturnAValue, shouldHaveBody })), - when(Variable)(() => ({ nameShouldBeginWithLowercase, nameShouldNotBeKeyword, shouldNotAssignToItselfInDeclaration, shouldNotDuplicateLocalVariables, shouldNotDuplicateGlobalDefinitions, shouldNotDefineGlobalMutableVariables, shouldNotUseReservedWords, shouldInitializeGlobalReference, shouldDefineConstInsteadOfVar, shouldNotDuplicateEntities })), + when(Variable)(() => ({ nameShouldBeginWithLowercase, nameShouldNotBeKeyword, shouldNotAssignToItselfInDeclaration, shouldNotDuplicateLocalVariables, shouldNotDuplicateGlobalDefinitions, shouldNotDefineGlobalMutableVariables, shouldNotUseReservedWords, shouldInitializeGlobalReference, shouldDefineConstInsteadOfVar, shouldNotDuplicateEntities, shouldInitializeConst })), when(Assignment)(() => ({ shouldNotAssignToItself, shouldNotReassignConst })), when(Reference)(() => ({ missingReference, shouldUseSelfAndNotSingletonReference })), when(Self)(() => ({ shouldNotUseSelf })), diff --git a/test/assertions.ts b/test/assertions.ts index f638d93d..a5aa43b6 100644 --- a/test/assertions.ts +++ b/test/assertions.ts @@ -1,14 +1,14 @@ +import dedent from 'dedent' +import { promises } from 'fs' +import globby from 'globby' import { formatError, Parser } from 'parsimmon' -import link from '../src/linker' -import { Name, Node, Package, Reference, Environment as EnvironmentType, Environment } from '../src/model' +import { join } from 'path' +import { buildEnvironment as buildEnv, print } from '../src' import { List } from '../src/extensions' -import { Validation } from '../src/validator' +import link from '../src/linker' +import { Environment, Environment as EnvironmentType, Name, Node, Package, Reference } from '../src/model' import { ParseError } from '../src/parser' -import globby from 'globby' -import { promises } from 'fs' -import { buildEnvironment as buildEnv } from '../src' -import { join } from 'path' -import validate from '../src/validator' +import validate, { Validation } from '../src/validator' const { readFile } = promises @@ -21,6 +21,8 @@ declare global { tracedTo(start: number, end: number): Assertion recoveringFrom(code: Name, start: number, end: number): Assertion + formattedTo(expected: string): Assertion + linkedInto(expected: List): Assertion target(node: Node): Assertion @@ -91,9 +93,9 @@ export const parserAssertions: Chai.ChaiPlugin = (chai, utils) => { const expectedProblems = flag(this, 'expectedProblems') ?? [] const actualProblems = this._obj.problems?.map(({ code, sourceMap: { start, end } }: ParseError) => ({ code, start: start.offset, end: end.offset })) ?? [] + new Assertion(this._obj.metadata ?? []).to.have.deep.members(expected.metadata ?? []) new Assertion(expectedProblems).to.deep.contain.all.members(actualProblems, 'Unexpected problem found') new Assertion(actualProblems).to.deep.contain.all.members(expectedProblems, 'Expected problem not found') - new Assertion(plucked(this._obj)).to.deep.equal(plucked(expected)) }) @@ -110,6 +112,22 @@ export const parserAssertions: Chai.ChaiPlugin = (chai, utils) => { }) } +// ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════ +// PRINTER ASSERTIONS +// ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════ + +export const printerAssertions: Chai.ChaiPlugin = (chai) => { + const { Assertion } = chai + + Assertion.addMethod('formattedTo', function (expected: string) { + const name = 'formatted' + const environment = buildEnv([{ name, content: this._obj }]) + const printerConfig = { maxWidth: 80, useSpaces: true, abbreviateAssignments: true } + const formatted = print(environment.getNodeByFQN(name), printerConfig) + new Assertion(formatted).to.equal(dedent(expected)) + }) +} + // ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════ // LINKER ASSERTIONS // ══════════════════════════════════════════════════════════════════════════════════════════════════════════════════ diff --git a/test/parser.test.ts b/test/parser.test.ts index 5e05ca7d..21b4aae8 100644 --- a/test/parser.test.ts +++ b/test/parser.test.ts @@ -14,20 +14,46 @@ describe('Wollok parser', () => { describe('Comments', () => { const parser = parse.Import + it('line comments should be parsed as metadata', () => { + `//some comment + import p`.should.be.parsedBy(parser).into(new Import({ + entity: new Reference({ name: 'p' }), + metadata: [new Annotation('comment', { text: '//some comment', position: 'start' })], + })) + .and.be.tracedTo(21, 29) + .and.have.nested.property('entity').tracedTo(28, 29) + }) + it('multiline comments should be ignored in between tokens', () => { `/*some comment*/import /* some - comment */ p`.should.be.parsedBy(parser).into(new Import({ entity: new Reference({ name: 'p' }) })) + comment */ p`.should.be.parsedBy(parser).into(new Import({ + entity: new Reference({ + name: 'p', + // the assertion is not validating metadata recursively + metadata: [new Annotation('comment', { text: '/* some\n comment */', position: 'end' } )], + }), + metadata: [new Annotation('comment', { text: '/*some comment*/', position: 'start' })], + })) .and.be.tracedTo(16, 49) .and.have.nested.property('entity').tracedTo(48, 49) }) it('line comments should be ignored at the end of line', () => { `import //some comment - p`.should.be.parsedBy(parser).into(new Import({ entity: new Reference({ name: 'p' }) })) + p`.should.be.parsedBy(parser).into(new Import({ entity: new Reference({ name: 'p', metadata: [new Annotation('comment', { text: '//some comment', position: 'end' })] }) })) .and.be.tracedTo(0, 29) .and.have.nested.property('entity').tracedTo(28, 29) }) + it('comments after sends should be parsed', () => { + 'pepita.vola() //some comment' + .should.be.parsedBy(parse.Send).into(new Send({ + receiver: new Reference({ name: 'pepita' }), + message: 'vola', + metadata: [new Annotation('comment', { text: '//some comment', position: 'end' })], + })) + }) + it('should not parse elements inside line comment', () => { '// import p'.should.not.be.parsedBy(parser) }) diff --git a/test/printer.test.ts b/test/printer.test.ts new file mode 100644 index 00000000..fd58e5de --- /dev/null +++ b/test/printer.test.ts @@ -0,0 +1,2457 @@ +import { use, should } from 'chai' +import { printerAssertions } from './assertions' + +use(printerAssertions) +should() + +describe('Wollok Printer', () => { + describe('Basic expressions', () => { + describe('Send', () => { + it('Send long parameters', () => { + `object pepita { + method volar(a,b,c,d,e){} + + method prueba() { + self.volar("aaaaaaaaaaaa", "bbbbbbbbbbb", "cccccccccc", "dddddddddd", "eeeeeeeeee") + } + }`.should.be.formattedTo(` + object pepita { + method volar(a, b, c, d, e) { + + } + + method prueba() { + self.volar( + "aaaaaaaaaaaa", + "bbbbbbbbbbb", + "cccccccccc", + "dddddddddd", + "eeeeeeeeee" + ) + } + } + `) + }) + it('Send short parameters', () => { + `object pepita { + method volar(a,b){} + + method prueba() { + self.volar("aaaaaaaaaaaa", + "bbbbbbbbbbb") + } + }`.should.be.formattedTo(` + object pepita { + method volar(a, b) { + + } + + method prueba() { + self.volar("aaaaaaaaaaaa", "bbbbbbbbbbb") + } + } + `) + }) + }) + + describe('If', () => { + it('full then and else body', () => { + `program prueba { + if(true){return 1}else{return 2} + }`.should.be.formattedTo(` + program prueba { + if (true) { + return 1 + } else { + return 2 + } + }`) + }) + + + it('with no else body', () => { + `program prueba { + if(true){return 1} + }`.should.be.formattedTo(` + program prueba { + if (true) { + return 1 + } + }`) + }) + + it('if expression short', () => { + `program prueba { + if(true)1 + else 2 + }`.should.be.formattedTo(` + program prueba { + if (true) 1 else 2 + }`) + }) + + it('if expression long', () => { + `program prueba { + const pepita = object { + method volar(param){} + } + if ("a very very very very very very very very long string".length() > 0)pepita.volar("a very very very very very very very very long argument") else 2 + }`.should.be.formattedTo(` + program prueba { + const pepita = object { + method volar(param) { + + } + } + if ("a very very very very very very very very long string".length() > 0) + pepita.volar("a very very very very very very very very long argument") + else 2 + }`) + }) + }) + + describe('Comments', () => { + it('single line comment', () => { + `program prueba { + // comentario + const a = 1 // other comment but this comment is actually very veeeeeeeeeeeeery veeeeeeeeeery long + const b = 2 + // last comentario + }`.should.be.formattedTo(` + program prueba { + // comentario + const a = 1 + // other comment but this comment is actually very veeeeeeeeeeeeery veeeeeeeeeery long + const b = 2 // last comentario + }`) + }) + + it('comments on send', () => { + `program prueba { + // ok + 5.even() // ok + }`.should.be.formattedTo(` + program prueba { + // ok + 5.even() // ok + }`) + }) + + it('many comments', () => { + `program prueba { + // comentario + // comentario + const a = 1 + }`.should.be.formattedTo(` + program prueba { + // comentario + // comentario + const a = 1 + }`) + }) + + //ToDo smarter trimming + it('multi line comments', () => { + `program prueba { + /* comentario + comentario */ + const a = 1 + }`.should.be.formattedTo(` + program prueba { + /* comentario + comentario */ + const a = 1 + }`) + }) + + it('side comment', () => { + `program prueba { + const a = 1 // comentario + }`.should.be.formattedTo(` + program prueba { + const a = 1 // comentario + }`) + }) + + xit('comment on a list', () => { + `program prueba { + const a = [1 + ,2//comment on a lista + ,3] + }`.should.be.formattedTo(` + program prueba { + const a = [ + 1, + 2, //comment on a lista + 3 + ] + }`) + }) + }) + }) + describe('Object', () => { + it('testBasicObjectDefinition', () => { + `object pepita { var energia = 0 + method volar() { energia += 10 } + } + `.should.be.formattedTo(` + object pepita { + var energia = 0 + + method volar() { + energia += 10 + } + }`) + }) + + it('testBasicUnnamedObjectDefinition', () => { + `program prueba{ + + const pepita = object{ + var energia = 0 + method volar() { + energia+=1 } + } + }`.should.be.formattedTo(` + program prueba { + const pepita = object { + var energia = 0 + + method volar() { + energia += 1 + } + } + } + `) + }) + + it('testInheritingPositionalUnnamedObjectDefinition', () => { + ` + class A { var _n = 0 } + program prueba { + + const pepita = object inherits A(n= + + 5 + + ) + {var energia = + 0 + method volar() {energia+=1 } + } + } + `.should.be.formattedTo(` + class A { + var _n = 0 + } + + program prueba { + const pepita = object inherits A (n = 5) { + var energia = 0 + + method volar() { + energia += 1 + } + } + } + ` + ) + }) + + it('testInheritingNamedParametersUnnamedObjectDefinition', () => { + + ` + class A { var edad = 0 var nombre = ""} + program prueba { + + const pepita = object inherits A( + + + + edad = 22 + + + , + + + nombre + + + = + "Carlono" + ) + {var energia = + 0 + method volar() {energia+=1 } + } + } + `.should.be.formattedTo(` + class A { + var edad = 0 + var nombre = "" + } + + program prueba { + const pepita = object inherits A (edad = 22, nombre = "Carlono") { + var energia = 0 + + method volar() { + energia += 1 + } + } + } + ` + ) + }) + + it('testUnnamedObjectDefinitionInAnExpression', () => { + ` + program prueba { + + assert.equals( + + object { var energia + = 0}, object { + var energia = 0 + var color = "rojo" } + ) + }`.should.be.formattedTo(` + program prueba { + assert.equals( + object { + var energia = 0 + }, + object { + var energia = 0 + var color = "rojo" + } + ) + }`) + }) + + it('testInheritingPositionalParametersObjectDefinition', () => { + `class Ave{} + object pepita + inherits + Ave { + var energia = 0 + method volar() { energia = energia +10 } + } + `.should.be.formattedTo(` + class Ave { + + } + + object pepita inherits Ave { + var energia = 0 + + method volar() { + energia += 10 + } + }`) + }) + + it('classFormatting_oneLineBetweenVarsAndMethods', () => { + `class Golondrina { + const energia = 10 + const kmRecorridos = 0 + method comer(gr){energia=energia+gr} method jugar(){ return true }}`.should.be.formattedTo(` + class Golondrina { + const energia = 10 + const kmRecorridos = 0 + + method comer(gr) { + energia += gr + } + + method jugar() = true + }`) + }) + + it('testObjectDefinitionWithOneVariableOnly', () => { + ` + object pepita { + + + var + + z } + `.should.be.formattedTo(` + object pepita { + var z + }`) + }) + + it('testObjectDefinitionWithOneMethodOnly', () => { + ` + object pepita + + + { method volar() { return 2 } } + `.should.be.formattedTo(` + object pepita { + method volar() = 2 + }`) + }) + + it('testClassDefinitionWithOneMethodOnly', () => { + ` + class Ave + + + { method volar() { return 2 } }`.should.be.formattedTo(` + class Ave { + method volar() = 2 + }`) + }) + + it('testInheritingObjectDefinitionWithDefinitionItself', () => { + `class Ave{method volar() {} + + } + object pepita + + + + inherits + Ave + + + { var energia = 0 + + + override method volar() { energia += + 10 }}`.should.be.formattedTo(` + class Ave { + method volar() { + + } + } + + object pepita inherits Ave { + var energia = 0 + + override method volar() { + energia += 10 + } + }`) + }) + + it('testInheritingObjectDefinitionWithDefinitionItselfAfter', () => { + ` + object pepita + + + + inherits + Ave + + + { var energia = 0 + + + override method volar() { energia += + 10 } } + + + class Ave{method volar() {}} + + `.should.be.formattedTo(` + object pepita inherits Ave { + var energia = 0 + + override method volar() { + energia += 10 + } + } + + class Ave { + method volar() { + + } + }`) + }) + + it('testClassDefinitionWithVar', () => { + ` + class Ave { + + + var energia + + }`.should.be.formattedTo(` + class Ave { + var energia + }`) + }) + + it('testBasicMixinDefinition', () => { + ` + + mixin Volador { + + + var energia + + method volar(lugar) {energia = 0 }} + `.should.be.formattedTo(` + mixin Volador { + var energia + + method volar(lugar) { + energia = 0 + } + }`) + }) + + it('testBasicMixinUse', () => { + ` + mixin Volador { + + + var energia + + method volar(lugar) {energia = 0 }} class Ave + + inherits + + Volador { + + method comer() { energia = 100 + } + }`.should.be.formattedTo( + ` + mixin Volador { + var energia + + method volar(lugar) { + energia = 0 + } + } + + class Ave inherits Volador { + method comer() { + energia = 100 + } + }`) + + }) + + it('testObjectInheritingNamedParametersForWKO', () => { + `class Musico{var calidad} + object luisAlberto inherits Musico (calidad + + + + = + 8 + + , + + + cancionPreferida = + "estrelicia" + ) { + + var guitarra + }`.should.be.formattedTo(` + class Musico { + var calidad + } + + object luisAlberto inherits Musico ( + calidad = 8, + cancionPreferida = "estrelicia" + ) { + var guitarra + }`) + }) + + it('testClassDefinition', () => { + ` + + + + + + class Cancion { + + + }`.should.be.formattedTo(` + class Cancion { + + }`) + }) + + it('objectDefinition', () => { + ` + class Musico{} object luisAlberto inherits Musico { + + var guitarra = null + + method cambiarGuitarra(_guitarra) { + guitarra = _guitarra + } + + override method habilidad() = 100.min(8 * guitarra.unidadesDeGuitarra()) + override method remuneracion(presentacion) = if (presentacion.participo(self)) self.costoPresentacion(presentacion) else 0 + + method costoPresentacion(presentacion) = if (presentacion.fecha() < new Date(day = 01, month = 09, year = 2017)) { + return 1000 + } else { + return 1200 + } + + override method interpretaBien(cancion) = true + + }`.should.be.formattedTo(` + class Musico { + + } + + object luisAlberto inherits Musico { + var guitarra = null + + method cambiarGuitarra(_guitarra) { + guitarra = _guitarra + } + + override method habilidad() = 100.min(8 * guitarra.unidadesDeGuitarra()) + + override method remuneracion(presentacion) = if (presentacion.participo(self)) + self.costoPresentacion( + presentacion + ) + else 0 + + method costoPresentacion(presentacion) = if (presentacion.fecha() < new Date( + day = 1, + month = 9, + year = 2017 + )) { + return 1000 + } else { + return 1200 + } + + override method interpretaBien(cancion) = true + }` + ) + }) + + it('testPresentacion', () => { + `class Presentacion { + + const fecha + const locacion + var participantes = [] + + method fecha() = fecha + + method locacion() = locacion + + method participantes() = participantes + + method participo(musico) = participantes.contains(musico) + + method capacidad() = self.locacion().capacidadPorDia(fecha) + + method agregarParticipantes(persona) { + if (participantes.contains(persona)) { + self.error("La persona que se desea agregar ya pertence a la presentacion") + } else { + participantes.add(persona) + } + } + + method quitarParticipante(persona) { + if (! (participantes.isEmpty())) { + if (participantes.contains(persona)) { + participantes.remove(persona) + } else { + self.error("La persona que se desea quitar no era integrante de la presentacion") + } + } else { + self.error("El conjunto de participantes esta vacio") + } + } + + method costoPresentacion() { + var costo = 0 + self.participantes().forEach{ participante => costo += participante.remuneracion(self) } + return costo + } + + }`.should.be.formattedTo(` + class Presentacion { + const fecha + const locacion + var participantes = [] + + method fecha() = fecha + + method locacion() = locacion + + method participantes() = participantes + + method participo(musico) = participantes.contains(musico) + + method capacidad() = self.locacion().capacidadPorDia(fecha) + + method agregarParticipantes(persona) { + if (participantes.contains(persona)) self.error( + "La persona que se desea agregar ya pertence a la presentacion" + ) + else participantes.add(persona) + } + + method quitarParticipante(persona) { + if (!participantes.isEmpty()) { + if (participantes.contains(persona)) participantes.remove(persona) + else self.error( + "La persona que se desea quitar no era integrante de la presentacion" + ) + } else { + self.error("El conjunto de participantes esta vacio") + } + } + + method costoPresentacion() { + var costo = 0 + self.participantes().forEach( + { participante => costo += participante.remuneracion(self) } + ) + return costo + } + }`) + }) + + it('testObjectVarsInitialized', () => { + `class Musico{} + object luisAlberto inherits Musico { + + const valorFechaTope = 1200 + const valorFechaNoTope = 1000 + var guitarra = fender + const initializedAsNullVar = null + const uninitializedVar + + const fechaTope = new Date(day = 01, month = 09, year = 2017) + + override method habilidad() = (8 * guitarra.valor()).min(100) + + override method interpretaBien(cancion) = true + + method guitarra(_guitarra) { + guitarra = _guitarra + } + + method costo(presentacion) = if (presentacion.fecha() < fechaTope) valorFechaNoTope else valorFechaTope + + }`.should.be.formattedTo(` + class Musico { + + } + + object luisAlberto inherits Musico { + const valorFechaTope = 1200 + const valorFechaNoTope = 1000 + var guitarra = fender + const initializedAsNullVar = null + const uninitializedVar + const fechaTope = new Date(day = 1, month = 9, year = 2017) + + override method habilidad() = (8 * guitarra.valor()).min(100) + + override method interpretaBien(cancion) = true + + method guitarra(_guitarra) { + guitarra = _guitarra + } + + method costo(presentacion) = if (presentacion.fecha() < fechaTope) + valorFechaNoTope + else valorFechaTope + }`) + }) + }) + describe('Methods', () => { + it('testBasicFormattingInMethod', () => { + ` + object foo { + method bar( param , param2 ) { + console.println("") + console.println("") + } + } + `.should.be.formattedTo(` + object foo { + method bar(param, param2) { + console.println("") + console.println("") + } + }`) + }) + + it('testBasicFormattingSeveralMethods', () => { + ` + object foo { + method bar( param , param2 ) { + console.println("") + console.println("") + }method bar2() { return 3 } + + + method bar3() { assert.that(true) var a = 1 + 1 console.println(a)} + } + `.should.be.formattedTo(` + object foo { + method bar(param, param2) { + console.println("") + console.println("") + } + + method bar2() = 3 + + method bar3() { + assert.that(true) + var a = 1 + 1 + console.println(a) + } + }`) + }) + + it('testReturnMethod', () => { + ` + object foo { + method bar( param , param2 ) + = 2 + + method bar2() = self.bar(1, "hola") + } + `.should.be.formattedTo(` + object foo { + method bar(param, param2) = 2 + + method bar2() = self.bar(1, "hola") + }`) + }) + + it('testOverrideMethod', () => { + ` + class Parent { + method bar( param , param2 ) + = 2 + + method bar2() { + + return self.bar(1, "hola") + } + } class Child + inherits Parent{ var a = 0 + override method bar(param, param2) = super() + + + 10 + override method bar2() { a+=1 } + } + `.should.be.formattedTo(` + class Parent { + method bar(param, param2) = 2 + + method bar2() = self.bar(1, "hola") + } + + class Child inherits Parent { + var a = 0 + + override method bar(param, param2) = super() + 10 + + override method bar2() { + a += 1 + } + }`) + }) + + it('testNativeMethod', () => { + ` + object foo { + method bar( param , param2 ) native + method bar2() + + + native + + } + `.should.be.formattedTo(` + object foo { + method bar(param, param2) native + + method bar2() native + }`) + }) + + it('abstractMethods', () => { + `class Vehicle { + method numberOfPassengers() method maxSpeed() + method expenseFor100Km() + method efficiency() { + return self.numberOfPassengers() * self.maxSpeed() / + + + self.expenseFor100Km() + } + } + `.should.be.formattedTo(` + class Vehicle { + method numberOfPassengers() + + method maxSpeed() + + method expenseFor100Km() + + method efficiency() = (self.numberOfPassengers() * self.maxSpeed()) / self.expenseFor100Km() + }`) + }) + + it('testClassFormattingOneLineMethod', () => { + `class Golondrina { const energia = 10 + + + const kmRecorridos= 0 method comer(gr) { energia = energia + gr } }`.should.be.formattedTo(` + class Golondrina { + const energia = 10 + const kmRecorridos = 0 + + method comer(gr) { + energia += gr + } + }`) + }) + + it('testClassFormattingOneLineMethodStaysInNewLine', () => { + `class Golondrina { const energia = 10 const kmRecorridos = 0 method comer(gr) { + energia = energia + gr + } }`.should.be.formattedTo(` + class Golondrina { + const energia = 10 + const kmRecorridos = 0 + + method comer(gr) { + energia += gr + } + }`) + }) + + it('keepNewlinesInSequences', () => { + `object foo { + method bar() { + self.bar().bar().bar() + + console.println("") console.println("") + + console.println("") + console.println("") + } + }`.should.be.formattedTo( ` + object foo { + method bar() { + self.bar().bar().bar() + console.println("") + console.println("") + console.println("") + console.println("") + } + }`) + }) + + it('testClassFormattingOneLineMethodStaysInNewLine', () => { + `class Golondrina { const energia = 10 const kmRecorridos = 0 method comer(gr) { + energia = energia + gr + } }`.should.be.formattedTo(` + class Golondrina { + const energia = 10 + const kmRecorridos = 0 + + method comer(gr) { + energia += gr + } + }`) + }) + + it('keepNewlinesInSequences', () => { + ` + object foo { + method bar() { + self.bar().bar().bar() + + console.println("") console.println("") + + console.println("") + console.println("") + } + }`.should.be.formattedTo(` + object foo { + method bar() { + self.bar().bar().bar() + console.println("") + console.println("") + console.println("") + console.println("") + } + }`) + }) + + it('messageSendParameters', () => { + `program p { + const a = null + + a . doSomething ( a, a, a , a , a ) + a. doSomething ( a, a, a , a , a ) + a. doSomething ({=> a .doSomething()}) + }`.should.be.formattedTo(` + program p { + const a = null + a.doSomething(a, a, a, a, a) + a.doSomething(a, a, a, a, a) + a.doSomething({ a.doSomething() }) + }`) + }) + + + it('listWithPreviousConflicts', () => { + ` + class Presentacion { + var fecha + var musicos = [] + var lugar + + method fecha(_fecha) { + fecha = _fecha + } + method lugar(_lugar) { + lugar = _lugar + } + method agregarMusico(musico) { + musicos.add(musico) + } + method eliminarMusicos() { + musicos.clear() + } + method fecha() = fecha + + method lugarConcurrido() = lugar.capacidad(fecha) > 5000 + method tocaSolo(musico) = [ musico ] == musicos + method costo() = musicos.sum{ + musico => + musico.precioPorPresentacion(self) + } + } + `.should.be.formattedTo(` + class Presentacion { + var fecha + var musicos = [] + var lugar + + method fecha(_fecha) { + fecha = _fecha + } + + method lugar(_lugar) { + lugar = _lugar + } + + method agregarMusico(musico) { + musicos.add(musico) + } + + method eliminarMusicos() { + musicos.clear() + } + + method fecha() = fecha + + method lugarConcurrido() = lugar.capacidad(fecha) > 5000 + + method tocaSolo(musico) = [musico] == musicos + + method costo() = musicos.sum({ musico => musico.precioPorPresentacion(self) }) + }`) + }) + + + it('testSuperInvocation', () => { + `class Ave { + + + var energia = 0 + method volar(minutos) { energia -= minutos}} + class Golondrina { + + override method volar(minutos) { super + + (minutos * ( 10 - 2 ) ) } + }`.should.be.formattedTo(` + class Ave { + var energia = 0 + + method volar(minutos) { + energia -= minutos + } + } + + class Golondrina { + override method volar(minutos) { + super(minutos * (10 - 2)) + } + }`) + }) + + + it('methodReturningValuesFromIfExpression', () => { + ` + object luisAlberto inherits Musico ( valor + + = 8) { + + var guitarra + + method guitarra() = guitarra + + method guitarra(_guitarra) { + guitarra = _guitarra + } + + override method costo(presentacion) { + if (presentacion.dia() < ( new Date(day = 30, month = 9, year = 2017) )) { + return + + + + 1000 + } else { + return 1200 + } + } + + } + `.should.be.formattedTo(` + object luisAlberto inherits Musico (valor = 8) { + var guitarra + + method guitarra() = guitarra + + method guitarra(_guitarra) { + guitarra = _guitarra + } + + override method costo(presentacion) { + if (presentacion.dia() < new Date(day = 30, month = 9, year = 2017)) { + return 1000 + } else { + return 1200 + } + } + }`) + }) + + it('testWithSeveralExpressions', () => { + ` + class Presentacion { + + var escenario + var dia + var musicos = #{} + var restricciones = #{} + + method agregarMusico(_musico) { + restricciones.forEach{ restriccion => restriccion.aplica(_musico) } + musicos.add(_musico) + return self + } + } + `.should.be.formattedTo(` + class Presentacion { + var escenario + var dia + var musicos = #{} + var restricciones = #{} + + method agregarMusico(_musico) { + restricciones.forEach({ restriccion => restriccion.aplica(_musico) }) + musicos.add(_musico) + return self + } + }`) + }) + }) + describe('Package', () => { + it('testBasicPackageDefinition', () => { + ` + package aves + + + { + object pepita { var energia = 0 method volar() { energia += + 10 }} + }`.should.be.formattedTo(` + package aves { + object pepita { + var energia = 0 + + method volar() { + energia += 10 + } + } + }`) + }) + + it('testBasicImportDefinition', () => { + `import wollok. game.* + import pepita + + program abc{ + game.addVisual(pepita) + game.start() pepita.vola(100) + + } + `.should.be.formattedTo(` + import wollok.game.* + import pepita + + program abc { + game.addVisual(pepita) + game.start() + pepita.vola(100) + }`) + }) + }) + describe('Program', () => { + it('testSimpleProgramWithVariablesAndMessageSend', () => { + 'program p { const a = 10 const b = 20 self.println(a + b) }'.should.be.formattedTo(` + program p { + const a = 10 + const b = 20 + self.println(a + b) + }`) + }) + }) + describe('Testing', () => { + it('testConstantsFormatting', () => { + `const a = new Sobreviviente() + + + const b = new Sobreviviente() + test "aSimpleTest"{ assert.that(true) }`.should.be.formattedTo( ` + const a = new Sobreviviente() + + const b = new Sobreviviente() + + test "aSimpleTest" { + assert.that(true) + } + `) + }) + + + it('testSimpleTestFormatting', () => { + 'test "aSimpleTest"{ assert.that(true) }'.should.be.formattedTo(` + test "aSimpleTest" { + assert.that(true) + } + `) + }) + + it('severalTestsSimplesTestFormatting', () => { + `test "aSimpleTest" { + assert.that(true) + } test "secondTest" + + { + var text = "hola" + + assert.equals(4, text.length() ) + assert.equals(4 - 0, ( - 4) . inverted() ) + }`.should.be.formattedTo( ` + test "aSimpleTest" { + assert.that(true) + } + + test "secondTest" { + var text = "hola" + + assert.equals(4, text.length()) + assert.equals(4 - 0, (-4).inverted()) + } + `) + }) + + it('testTestSeveralLinesFormatting', () => { + `test "aSimpleTest"{assert.that(true) assert.notThat(false) + + const a = 1 assert.equals( 1 , a) + assert.equals(a, a) + }`.should.be.formattedTo(` + test "aSimpleTest" { + assert.that(true) + assert.notThat(false) + const a = 1 + assert.equals(1, a) + assert.equals(a, a) + } + `) + }) + + it('testSimpleDescribeFormatting', () => { + `describe "group of tests" + { + + + test "aSimpleTest" + { + + + + assert.that(true)}}`.should.be.formattedTo(` + describe "group of tests" { + test "aSimpleTest" { + assert.that(true) + } + }`) + }) + + it('testSimpleDescribeFormatting2', () => { + 'describe "group of tests"{test "aSimpleTest"{assert.that(true)}}'.should.be.formattedTo(` + describe "group of tests" { + test "aSimpleTest" { + assert.that(true) + } + }`) + }) + + it('testSimpleDescribeWithInitializeMethod', () => { + `describe "group of tests" + { + var a method + initialize() { a = 1 } + + test "aSimpleTest" + { + + + + assert.equals(1, a)}}`.should.be.formattedTo(` + describe "group of tests" { + var a + + method initialize() { + a = 1 + } + + test "aSimpleTest" { + assert.equals(1, a) + } + }`) + }) + + it('testSimpleDescribeWithInitializeMethodInSeveralLines', () => { + `describe "group of tests" + { + var a var b var c = 3 + method + initialize() { + a = 1 + + b = "hola" + } + + test "aSimpleTest" + { + + + + assert.equals(1, a) + + + assert.equals(b.length() - 1, c) + }} + `.should.be.formattedTo( ` + describe "group of tests" { + var a + var b + var c = 3 + + method initialize() { + a = 1 + + b = "hola" + } + + test "aSimpleTest" { + assert.equals(1, a) + assert.equals(b.length() - 1, c) + } + } + + `) + }) + + it('testDescribeWithObjectDefinition', () => { + `object foo { method bar() = 1 }describe "group of tests" + { + var a + = foo.bar() + + test "aSimpleTest" + { + + + + assert.equals(1, a) + + + }}`.should.be.formattedTo(` + object foo { + method bar() = 1 + } + + describe "group of tests" { + var a = foo.bar() + + test "aSimpleTest" { + assert.equals(1, a) + } + }`) + }) + + it('testComplexInitializeDefinition', () => { + ` + + describe "tests - entrega 1" { + + var cisne + var laFamilia var presentacionLunaPark var presentacionLaTrastienda + + method initialize() { + cisne = new Cancion(minutos = 312, letra = "Hoy el viento se abrió quedó vacío el aire una vez más y el manantial brotó y nadie está aquí y puedo ver que solo estallan las hojas al brillar") + laFamilia = new Cancion( minutos = 264, letra = "Quiero brindar por mi gente sencilla, por el amor brindo por la familia") + presentacionLunaPark = new Presentacion() + presentacionLunaPark.fecha(new Date(day = 20, month = 4, year = 2017)) + presentacionLunaPark.lugar(lunaPark) + presentacionLunaPark.agregarMusico(luisAlberto) + presentacionLunaPark.agregarMusico(joaquin) + presentacionLunaPark.agregarMusico(lucia) + presentacionLaTrastienda = new Presentacion() + presentacionLaTrastienda.fecha(new Date(day = 15, month = 11, year = 2017)) + presentacionLaTrastienda.lugar(laTrastienda) presentacionLaTrastienda.agregarMusico(luisAlberto) + presentacionLaTrastienda.agregarMusico(joaquin) + presentacionLaTrastienda.agregarMusico(lucia) + } + + test "habilidad de Joaquín en grupo" { + assert.equals(25, joaquin.habilidad()) + } + + + } + `.should.be.formattedTo(` + describe "tests - entrega 1" { + var cisne + var laFamilia + var presentacionLunaPark + var presentacionLaTrastienda + + method initialize() { + cisne = new Cancion( + minutos = 312, + letra = "Hoy el viento se abrió quedó vacío el aire una vez más y el manantial brotó y nadie está aquí y puedo ver que solo estallan las hojas al brillar" + ) + laFamilia = new Cancion( + minutos = 264, + letra = "Quiero brindar por mi gente sencilla, por el amor brindo por la familia" + ) + presentacionLunaPark = new Presentacion() + presentacionLunaPark.fecha(new Date(day = 20, month = 4, year = 2017)) + presentacionLunaPark.lugar(lunaPark) + presentacionLunaPark.agregarMusico(luisAlberto) + presentacionLunaPark.agregarMusico(joaquin) + presentacionLunaPark.agregarMusico(lucia) + presentacionLaTrastienda = new Presentacion() + presentacionLaTrastienda.fecha(new Date(day = 15, month = 11, year = 2017)) + presentacionLaTrastienda.lugar(laTrastienda) + presentacionLaTrastienda.agregarMusico(luisAlberto) + presentacionLaTrastienda.agregarMusico(joaquin) + presentacionLaTrastienda.agregarMusico(lucia) + } + + test "habilidad de Joaquín en grupo" { + assert.equals(25, joaquin.habilidad()) + } + }`) + }) + + it('testUsingPreviousExpressions', () => { + ` + test "La capacidad del Luna Park el 08 de agosot de 2017 es 9290" { + + var dia = new Date( + day = 08, month = 08, year = 2017) + + assert.equals(9290, lunaPark.capacidad(dia)) + } + + + `.should.be.formattedTo(` + test "La capacidad del Luna Park el 08 de agosot de 2017 es 9290" { + var dia = new Date(day = 8, month = 8, year = 2017) + assert.equals(9290, lunaPark.capacidad(dia)) + }`) + }) + + it('testAnotherInitializeWithComplexDefinition', () => { + ` + describe "testDeMusicGuide" { + + var soledad + var kike + var lucia + var joaquin + const cisne = new Cancion(titulo = "Cisne", minutos = 312, letra ="Hoy el viento se abrio quedo vacio el aire una vez mas y el manantial broto y nadie esta aqui y puedo ver que solo estallan las hojas al brillar") + const laFamilia = new Cancion(titulo = "La Familia", minutos=264, letra = "Quiero brindar por mi gente sencilla, por el amor brindo por la familia") + const almaDeDiamante = new Cancion(titulo + ="Alma de Diamante", + minutos=216, letra + = "Ven a mi con tu dulce luz alma de diamante. Y aunque el sol se nuble despues sos alma de diamante. Cielo o piel silencio o verdad sos alma de diamante. Por eso ven asi con la humanidad alma de diamante") + const crisantemo = new Cancion(titulo="Crisantemo", minutos=175, letra="Tocame junto a esta pared, yo quede por aqui...cuando no hubo mas luz...quiero mirar a traves de mi piel...Crisantemo, que se abrio...encuentra el camino hacia el cielo") + const eres = new Cancion(titulo="Eres", minutos=145, letra + ="Eres lo mejor que me paso en la vida, no tengo duda, no habra mas nada despues de ti. Eres lo que le dio brillo al dia a dia, y asi sera por siempre, no cambiara, hasta el final de mis dias") + const corazonAmericano = new Cancion(titulo + ="Corazon Americano",minutos= 154, letra="Canta corazon, canta mas alto, que tu pena al fin se va marchando, el nuevo milenio ha de encontrarnos, junto corazon, como soiamos") + const aliciaEnElPais = new Cancion(titulo="Cancion de Alicia en el pais", minutos=510, letra="Quien sabe Alicia, este pais no estuvo hecho porque si. Te vas a ir, vas a salir pero te quedas, ¿donde más vas a ir? Y es que aqui, sabes el trabalenguas, trabalenguas, el asesino te asesina, y es mucho para ti. Se acabo ese juego que te hacia feliz") + const remixLaFamilia = new Remix(titulo = laFamilia.nombre(), minutos + = + laFamilia.duracion(), letra =laFamilia.letra()) + const mashupAlmaCrisantemo = new Mashup(titulo = "nombre", minutos = "duracion", letra = "letra", temas = [ almaDeDiamante, crisantemo ]) + const paraLosArboles = new Album(titulo = "Para los arboles", fecha = new Date(day = 31, month = 3, year = 2003), editados = 50000, vendidos = 49000).agregarCancion(cisne).agregarCancion(almaDeDiamante) + const justCrisantemo = new Album(titulo = "Just Crisantemo", fecha = new Date(day=05, month=12, year=2007), editados = 28000, vendidos=27500).agregarCancion(crisantemo) + const especialLaFamilia = new Album(titulo = "Especial La Familia", fecha = new Date(day = 17, month = 06, year = 1992), editados = 100000, vendidos = 89000).agregarCancion(laFamilia) + const laSole = new Album(titulo = "La Sole", fecha = new Date(day = 04, month = 02, year = 2005), editados = 200000, vendidos = 130000).agregarCancion(eres).agregarCancion(corazonAmericano) + var presentacionEnLuna + var presentacionEnTrastienda + const fender = new Guitarra() + const gibson = new Gibson() method + + initialize() { + soledad = new VocalistaPopular().habilidad(55).palabraBienInterpretada("amor").agregarAlbum(laSole).agregarCancionDeSuAutoria(eres).agregarCancionDeSuAutoria(corazonAmericano) + kike = new MusicoDeGrupo().habilidad(60).plusPorCantarEnGrupo(20) + lucia = new VocalistaPopular().habilidad(70).palabraBienInterpretada("familia").grupo("Pimpinela") + joaquin = new MusicoDeGrupo().habilidad(20).plusPorCantarEnGrupo(5).grupo("Pimpinela").agregarAlbum(especialLaFamilia).agregarCancionDeSuAutoria(laFamilia) + luisAlberto.agregarGuitarra(fender).agregarGuitarra(gibson).agregarAlbum(paraLosArboles).agregarAlbum(justCrisantemo).agregarCancionDeSuAutoria(cisne).agregarCancionDeSuAutoria(almaDeDiamante).agregarCancionDeSuAutoria(crisantemo).cambiarGuitarraActiva(gibson) + presentacionEnLuna = new Presentacion(lugar = lunaPark, fecha = new Date(day = 20, month = 04, year = 2017), artistas = [ joaquin, lucia, luisAlberto ]) + presentacionEnTrastienda = new Presentacion(lugar = laTrastienda, fecha = new Date(day = 15, + month + = 11, year=2017), artistas = + [ joaquin, lucia, luisAlberto ]) + pdpalooza.lugar(lunaPark).fecha(new Date(day = 15, month = 12, year =2017)) + restriccionPuedeCantarCancion.parametroRestrictivo(aliciaEnElPais) + } + + test "fake" { assert.that(true) } + } + `.should.be.formattedTo(` + describe "testDeMusicGuide" { + var soledad + var kike + var lucia + var joaquin + const cisne = new Cancion( + titulo = "Cisne", + minutos = 312, + letra = "Hoy el viento se abrio quedo vacio el aire una vez mas y el manantial broto y nadie esta aqui y puedo ver que solo estallan las hojas al brillar" + ) + const laFamilia = new Cancion( + titulo = "La Familia", + minutos = 264, + letra = "Quiero brindar por mi gente sencilla, por el amor brindo por la familia" + ) + const almaDeDiamante = new Cancion( + titulo = "Alma de Diamante", + minutos = 216, + letra = "Ven a mi con tu dulce luz alma de diamante. Y aunque el sol se nuble despues sos alma de diamante. Cielo o piel silencio o verdad sos alma de diamante. Por eso ven asi con la humanidad alma de diamante" + ) + const crisantemo = new Cancion( + titulo = "Crisantemo", + minutos = 175, + letra = "Tocame junto a esta pared, yo quede por aqui...cuando no hubo mas luz...quiero mirar a traves de mi piel...Crisantemo, que se abrio...encuentra el camino hacia el cielo" + ) + const eres = new Cancion( + titulo = "Eres", + minutos = 145, + letra = "Eres lo mejor que me paso en la vida, no tengo duda, no habra mas nada despues de ti. Eres lo que le dio brillo al dia a dia, y asi sera por siempre, no cambiara, hasta el final de mis dias" + ) + const corazonAmericano = new Cancion( + titulo = "Corazon Americano", + minutos = 154, + letra = "Canta corazon, canta mas alto, que tu pena al fin se va marchando, el nuevo milenio ha de encontrarnos, junto corazon, como soiamos" + ) + const aliciaEnElPais = new Cancion( + titulo = "Cancion de Alicia en el pais", + minutos = 510, + letra = "Quien sabe Alicia, este pais no estuvo hecho porque si. Te vas a ir, vas a salir pero te quedas, ¿donde más vas a ir? Y es que aqui, sabes el trabalenguas, trabalenguas, el asesino te asesina, y es mucho para ti. Se acabo ese juego que te hacia feliz" + ) + const remixLaFamilia = new Remix( + titulo = laFamilia.nombre(), + minutos = laFamilia.duracion(), + letra = laFamilia.letra() + ) + const mashupAlmaCrisantemo = new Mashup( + titulo = "nombre", + minutos = "duracion", + letra = "letra", + temas = [almaDeDiamante, crisantemo] + ) + const paraLosArboles = new Album( + titulo = "Para los arboles", + fecha = new Date(day = 31, month = 3, year = 2003), + editados = 50000, + vendidos = 49000 + ).agregarCancion(cisne).agregarCancion(almaDeDiamante) + const justCrisantemo = new Album( + titulo = "Just Crisantemo", + fecha = new Date(day = 5, month = 12, year = 2007), + editados = 28000, + vendidos = 27500 + ).agregarCancion(crisantemo) + const especialLaFamilia = new Album( + titulo = "Especial La Familia", + fecha = new Date(day = 17, month = 6, year = 1992), + editados = 100000, + vendidos = 89000 + ).agregarCancion(laFamilia) + const laSole = new Album( + titulo = "La Sole", + fecha = new Date(day = 4, month = 2, year = 2005), + editados = 200000, + vendidos = 130000 + ).agregarCancion(eres).agregarCancion(corazonAmericano) + var presentacionEnLuna + var presentacionEnTrastienda + const fender = new Guitarra() + const gibson = new Gibson() + + method initialize() { + soledad = new VocalistaPopular().habilidad(55).palabraBienInterpretada( + "amor" + ).agregarAlbum(laSole).agregarCancionDeSuAutoria( + eres + ).agregarCancionDeSuAutoria(corazonAmericano) + kike = new MusicoDeGrupo().habilidad(60).plusPorCantarEnGrupo(20) + lucia = new VocalistaPopular().habilidad(70).palabraBienInterpretada( + "familia" + ).grupo("Pimpinela") + joaquin = new MusicoDeGrupo().habilidad(20).plusPorCantarEnGrupo(5).grupo( + "Pimpinela" + ).agregarAlbum(especialLaFamilia).agregarCancionDeSuAutoria(laFamilia) + luisAlberto.agregarGuitarra(fender).agregarGuitarra(gibson).agregarAlbum( + paraLosArboles + ).agregarAlbum(justCrisantemo).agregarCancionDeSuAutoria( + cisne + ).agregarCancionDeSuAutoria(almaDeDiamante).agregarCancionDeSuAutoria( + crisantemo + ).cambiarGuitarraActiva(gibson) + presentacionEnLuna = new Presentacion( + lugar = lunaPark, + fecha = new Date(day = 20, month = 4, year = 2017), + artistas = [joaquin, lucia, luisAlberto] + ) + presentacionEnTrastienda = new Presentacion( + lugar = laTrastienda, + fecha = new Date(day = 15, month = 11, year = 2017), + artistas = [joaquin, lucia, luisAlberto] + ) + pdpalooza.lugar(lunaPark).fecha(new Date(day = 15, month = 12, year = 2017)) + restriccionPuedeCantarCancion.parametroRestrictivo(aliciaEnElPais) + } + + test "fake" { + assert.that(true) + } + }`) + }) + + it('testDescribeWithMethodDefinition', () => { + `describe "group of tests" + + + + + { method bar() = 1 + + + + + + + method bar2() { + + + + + + + + + + return 1} test "aSimpleTest" + { + + + + assert.equals( self.bar(), + self.bar2()) + + + }} + + + `.should.be.formattedTo(` + describe "group of tests" { + method bar() = 1 + + method bar2() = 1 + + test "aSimpleTest" { + assert.equals(self.bar(), self.bar2()) + } + }`) + }) + }) + describe('Variables', () => { + it('testSeveralVariableDefinitionsToConstantsInMethods', () => { + ` + class Foo { + var x var y var z + method addition() { var a = x x = 1 y = 2 z=x+y } + }`.should.be.formattedTo (` + class Foo { + var x + var y + var z + + method addition() { + var a = x + x = 1 + y = 2 + z = x + y + } + }`) + }) + + it('testSeveralVariableDefinitionsToConstantsInMethods2', () => { + ` + class Foo { + var x var y = 5 var z + method addition() { + + + var a = x + + + x = 1 + + + y = 2 z = x + y } + + + }`.should.be.formattedTo (` + class Foo { + var x + var y = 5 + var z + + method addition() { + var a = x + + + + x = 1 + + + + y = 2 + z = x + y + } + }`) + }) + + it('testSeveralVariableDefinitionsToConstantsInMethods3', () => { + ` + class Foo { + var x var y var z + method addition () { + var a = x + x = 1 + y = 2 + z = x + y } + + + }`.should.be.formattedTo (` + class Foo { + var x + var y + var z + + method addition() { + var a = x + x = 1 + y = 2 + z = x + y + } + }`) + }) + + + it('testSeveralVariableDefinitionsToConstantsInMethods4', () => { + ` + class Foo { + var x + method addition() { x = 1 var a = 2 a = x a += 1 a . inverted() } + }`.should.be.formattedTo ( + ` + class Foo { + var x + + method addition() { + x = 1 + var a = 2 + a = x + a += 1 + a.inverted() + } + }`) + }) + + + it('propertyDefinitionInClass', () => { + ` + class Foo { + var property + + + x + + const + property + + + y + = + 1 + }`.should.be.formattedTo (` + class Foo { + var property x + const property y = 1 + }`) + }) + + it('propertyDefinitionInWko', () => { + ` + object romualdo { + var property + + + x + + const + + property + + + y + = + 1 + }`.should.be.formattedTo (` + object romualdo { + var property x + const property y = 1 + }`) + }) + + + it('propertyDefinitionInMixin', () => { + ` + mixin Jugable { + var property + + + x + + const + property + + + y + = + 1 + }`.should.be.formattedTo (` + mixin Jugable { + var property x + const property y = 1 + }`) + }) + + + it('propertyDefinitionInDescribe', () => { + + ` + describe + + "group of tests" + { + var property + + + x + + const property + + + y + = + 1 + + test "true is true" { assert.that(true) } + }`.should.be.formattedTo (` + describe "group of tests" { + var property x + const property y = 1 + + test "true is true" { + assert.that(true) + } + }`) + }) + }) + describe('Constructor call', () => { + it('constructorCallFormatting', () => { + `class A { var a + var b = 2 var c var d + } + class B { + + method buildA() { + new A( a + + + = + + 200 + , + b = "Hello" + + , + + c = new Date() + + , d=#{1 , 8} ) + }} + `.should.be.formattedTo(` + class A { + var a + var b = 2 + var c + var d + } + + class B { + method buildA() { + new A(a = 200, b = "Hello", c = new Date(), d = #{1, 8}) + } + }`) + }) + + }) + describe('Complex flow', () => { + it('program_ifInline', () => { + `program p { + const a = 10 + const b = 0 + + const c = if (a > 0) b else + + + 0 + }`.should.be.formattedTo( ` + program p { + const a = 10 + const b = 0 + + const c = if (a > 0) b else 0 + }`) + }) + + it('program_ifInlineWithExpressions', () => { + `program p { + const a = 10 + const b = 0 + + const c = if (a > 0) b+1 else b-1 + }`.should.be.formattedTo( ` + program p { + const a = 10 + const b = 0 + + const c = if (a > 0) b + 1 else b - 1 + }`) + }) + + it('issue702_forEachAndIf', () => { + ` + object foo { + method bar() { + [3, 4 ,50, 100 ].forEach({ it => if (it > 4) { console.println(4) } else {console.println(it) + } + }) + } + } + `.should.be.formattedTo(` + object foo { + method bar() { + [3, 4, 50, 100].forEach( + { it => if (it > 4) console.println(4) else console.println(it) } + ) + } + }`) + }) + + it('issue702_forEachAndIf_2', () => { + ` + object foo { + method bar() { + [3, 4 ,50, 100 ].forEach({ it => if (it > 4) console.println(4) else console.println(it) + + }) + } + } + `.should.be.formattedTo(` + object foo { + method bar() { + [3, 4, 50, 100].forEach( + { it => if (it > 4) console.println(4) else console.println(it) } + ) + } + }`) + }) + + it('program_maxOneLineBreakBetweenLines', () => { + `program p { + const a = 10 + const b = 0 + + + + const c = a + b + }`.should.be.formattedTo( ` + program p { + const a = 10 + const b = 0 + + + + const c = a + b + }`) + }) + + it('basicTryCatch', () => { + ` +program abc { + console.println(4) + try + { + 5 + 5 + } + catch e : Exception + { + console.println(e) + } + } + `.should.be.formattedTo(` + program abc { + console.println(4) + try { + 5 + 5 + } catch e : Exception { + console.println(e) + } + }`) + }) + + it('tryBlockWithSeveralCatchsAndAFinally', () => { + ` + program abc { + console.println(4) + try{5 + 5} + catch e : UserException { + console.println(e) + } catch e2:Exception {console.println("Bad error") console.println("Do nothing")} + + + + + + then always { console.println("finally") + + + } + } + `.should.be.formattedTo(` + program abc { + console.println(4) + try { + 5 + 5 + } catch e : UserException { + console.println(e) + } catch e2 : Exception { + console.println("Bad error") + console.println("Do nothing") + } then always { + console.println("finally") + } + }`) + }) + + it('oneLineTryCatch', () => { + ` + program abc { + console.println(4) + try + + + + + 5 + 5 + + + catch e : Exception + + + console.println(e) + } + `.should.be.formattedTo(` + program abc { + console.println(4) + try { + 5 + 5 + } catch e : Exception { + console.println(e) + } + }`) + }) + + it('throwFormattingTest', () => { + + ` + object foo { + method attack(target) { + var attackers = self.standingMembers() + if (attackers.isEmpty()) throw + new CannotAttackException( + + + + message + + + ="No attackers available") attackers.forEach({ + aMember => + + + aMember. + attack(target) }) + } + } + `.should.be.formattedTo(` + object foo { + method attack(target) { + var attackers = self.standingMembers() + if (attackers.isEmpty()) { + throw new CannotAttackException(message = "No attackers available") + } + attackers.forEach({ aMember => aMember.attack(target) }) + } + }`) + }) + + it('testAllWithClosure', () => { + + ` + class Cantante { const albumes = new Set() +method esMinimalista() = albumes.all{ + album => + album.sonTodasCancionesCortas() + } + } + `.should.be.formattedTo(` + class Cantante { + const albumes = new Set() + + method esMinimalista() = albumes.all( + { album => album.sonTodasCancionesCortas() } + ) + }`) + }) + + it('testForEachWithComplexClosure', () => { + + ` + class Cantante { const albumes = new Set() + method mejorarAlbumes() { + albumes.forEach{ + album => + album.agregarCancion(new Cancion()) + album.sumarCosto(100) + }} + } + `.should.be.formattedTo(` + class Cantante { + const albumes = new Set() + + method mejorarAlbumes() { + albumes.forEach( + { album => + album.agregarCancion(new Cancion()) + return album.sumarCosto(100) + } + ) + } + }`) + }) + + it('doubleIfInMethod', () => { + ` + object pepita { + const posicion = game.at(2, 0) + var energia = 50 + method energia() { + return energia + } + method imagen() { + if (energia < 150) return "pepita.png" + if (energia < 300) return "pepita1.png" + return "pepita2.png" + } + + + } + `.should.be.formattedTo(` + object pepita { + const posicion = game.at(2, 0) + var energia = 50 + + method energia() = energia + + method imagen() { + if (energia < 150) { + return "pepita.png" + } + if (energia < 300) { + return "pepita1.png" + } + return "pepita2.png" + } + }`) + }) + + it('testWithIfExpression', () => { + + ` + object laTrastienda { + + const capacidadPlantaBaja = 400 + const capacidadPrimerPiso = 300 + + method capacidad(dia) { + if (dia.dayOfWeek() + + + == 6) { + return capacidadPlantaBaja + capacidadPrimerPiso + } else { + return capacidadPlantaBaja + } + } + + } + `.should.be.formattedTo(` + object laTrastienda { + const capacidadPlantaBaja = 400 + const capacidadPrimerPiso = 300 + + method capacidad(dia) { + if (dia.dayOfWeek() == 6) { + return capacidadPlantaBaja + capacidadPrimerPiso + } else { + return capacidadPlantaBaja + } + } + }`) + + }) + + it('testFold', () => { + ` + class Mashup inherits Cancion { + + var nombre = "" + var duracion = 0 + var letra = "" + + var bloqueNumeroPositivo = { num => num > 0 } + + + + + + method concatenarNombres(canciones) { + return canciones.fold("" , { acum , cancion => acum + cancion.nombre() } + + + ) + }) + + } + `.should.be.formattedTo(` + class Mashup inherits Cancion { + var nombre = "" + var duracion = 0 + var letra = "" + var bloqueNumeroPositivo = { num => num > 0 } + + method concatenarNombres(canciones) = canciones.fold( + "", + { acum, cancion => acum + cancion.nombre() } + ) + }`) + }) + + it( 'testReturnAndIf', () => { + ` + object lucia { + + const costePresentacionNoConcurrida = 400 + const costePresentacionConcurrida = 500 + var cantaEnGrupo = true + const habilidad = 70 + + method habilidad() = habilidad + self.sumaHabilidad() + + method sumaHabilidad() { + if (cantaEnGrupo) return - 20 + return 0 + } + } + `.should.be.formattedTo(` + object lucia { + const costePresentacionNoConcurrida = 400 + const costePresentacionConcurrida = 500 + var cantaEnGrupo = true + const habilidad = 70 + + method habilidad() = habilidad + self.sumaHabilidad() + + method sumaHabilidad() { + if (cantaEnGrupo) { + return -20 + } + return 0 + } + }` + ) + }) + + it('testReturnSelfExpression', () => { + ` + class AlbumBuilder { + + var fechaLanzamiento + + method fechaLanzamiento(dia, mes, anio) { + fechaLanzamiento = new Date(day = dia, month = mes, year = anio) + return self + } + + } `.should.be.formattedTo(` + class AlbumBuilder { + var fechaLanzamiento + + method fechaLanzamiento(dia, mes, anio) { + fechaLanzamiento = new Date(day = dia, month = mes, year = anio) + return self + } + }`) + }) + + it('unaryWordExpression', () => { + ` + object lunaPark {} + class Presentacion { var fecha var lugar var musicos } + object pdpalooza inherits Presentacion(fecha = new Date(day = 15, month = 12, year = 2017), lugar = lunaPark, musicos = []){ + const restriccionHabilidad = { musico => if (musico.habilidad() < 70) throw new Exception(message = "La habilidad del músico debe ser mayor a 70")} + const restriccionCompusoAlgunaCancion = {musico => if (! musico.compusoAlgunaCancion()) throw new Exception(message = "El músico debe haber compuesto al menos una canción")} + } + `.should.be.formattedTo(` + object lunaPark { + + } + + class Presentacion { + var fecha + var lugar + var musicos + } + + object pdpalooza inherits Presentacion ( + fecha = new Date(day = 15, month = 12, year = 2017), + lugar = lunaPark, + musicos = [] + ) { + const restriccionHabilidad = { musico => if (musico.habilidad() < 70) { + throw new Exception( + message = "La habilidad del músico debe ser mayor a 70" + ) + } } + const restriccionCompusoAlgunaCancion = { musico => + if (!musico.compusoAlgunaCancion()) { + throw new Exception( + message = "El músico debe haber compuesto al menos una canción" + ) + } } + }`) + }) + + it('testObjectWithClosureImplementingRestrictions', () => { + + ` + object restriccionCompositor { + + method verificarMusico(musico) { + if (!musico.cancionesPublicadas().any{ unaCancion => musico.esSuCancion(unaCancion)}) { + throw new UserException(message = "No se puede agregar al musico ya que no compuso ninguna cancion") + } + } + + } + `.should.be.formattedTo(` + object restriccionCompositor { + method verificarMusico(musico) { + if (!musico.cancionesPublicadas().any( + { unaCancion => musico.esSuCancion(unaCancion) } + )) { + throw new UserException( + message = "No se puede agregar al musico ya que no compuso ninguna cancion" + ) + } + } + }`) + }) + }) +}) \ No newline at end of file