diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75eec787..44f9e305 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -162,3 +162,41 @@ jobs: BASILISP_TEST_PATH="$(pwd)/test" \ BASILISP_TEST_FILE_PATTERN='.*\.(lpy|cljc)' \ basilisp test -p test -- -n 0 + + test-phel: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: ['8.4', '8.5'] + steps: + - uses: actions/checkout@v4 + + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none + tools: composer + + - name: Cache Composer downloads + uses: actions/cache@v4 + with: + path: ~/.composer/cache + key: composer-${{ matrix.php }}-${{ hashFiles('composer.lock') }} + restore-keys: | + composer-${{ matrix.php }}- + + - name: Cache Phel compiled artifacts + uses: actions/cache@v4 + with: + path: .phel/cache + key: phel-cache-${{ matrix.php }}-${{ hashFiles('composer.lock', 'phel-config.php') }}-${{ hashFiles('test/**/*.cljc', 'test/**/*.phel') }} + restore-keys: | + phel-cache-${{ matrix.php }}-${{ hashFiles('composer.lock', 'phel-config.php') }}- + phel-cache-${{ matrix.php }}- + + - name: Install dependencies + run: composer install --no-interaction --no-ansi --no-progress + + - name: Run tests + run: composer test diff --git a/.gitignore b/.gitignore index 7ac80714..19c5016f 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ __pycache__/ poetry.lock *.egg-info/ .DS_Store +/vendor/ diff --git a/README.md b/README.md index caa85cac..c7d47a12 100644 --- a/README.md +++ b/README.md @@ -22,3 +22,4 @@ See these documents for how to set up individual dialect-specific environments a 3. [Babashka](doc/babashka.md) 4. [Clojure CLR](doc/clojureclr.md) 5. [Basilisp](doc/basilisp.md) +6. [Phel](doc/phel.md) diff --git a/bb.edn b/bb.edn index 7c466250..15e70b67 100644 --- a/bb.edn +++ b/bb.edn @@ -32,13 +32,18 @@ {:extra-env {"BASILISP_TEST_PATH" "./test" "BASILISP_TEST_FILE_PATTERN" ".*\\.(lpy|cljc)"}} "basilisp test -p test -- -n 0"))} + test-phel {:doc "Runs tests in Phel" + :task (do + (banner "Running Phel Tests") + (shell "composer test"))} test-all {:doc "Run tests under all dialects" :task (do (run 'test-jvm) (run 'test-cljs) (run 'test-bb) - (run 'test-lpy))} - test {:doc "Runs tests in multiple named dialects: jvm, bb, cljs, or lpy" + (run 'test-lpy) + (run 'test-phel))} + test {:doc "Runs tests in multiple named dialects: jvm, bb, cljs, lpy, or phel" :task (letfn [(help [] (println "Usage: bb test []...") (println "Where is one of:") @@ -46,9 +51,10 @@ (println " cljs Runs tests in ClojureScript") (println " bb Runs tests in Babashka") (println " lpy Runs tests in Basilisp") + (println " phel Runs tests in Phel") (println " all Runs tests in all supported dialects") (println "Tests are run in the order given."))] - (let [valid #{"jvm" "cljs" "bb" "lpy" "all"} + (let [valid #{"jvm" "cljs" "bb" "lpy" "phel" "all"} unknown (remove valid *command-line-args*)] (cond (not (some? (seq *command-line-args*))) @@ -69,7 +75,8 @@ "jvm" (run 'test-jvm) "cljs" (run 'test-cljs) "bb" (run 'test-bb) - "lpy" (run 'test-lpy))))))} + "lpy" (run 'test-lpy) + "phel" (run 'test-phel))))))} new-test {:doc "Creates new test for the Clojure symbols named by . Unqualified symbols assume clojure.core" :requires ([new-test]) :task (new-test/new-test *command-line-args*)}}} diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..03f6195b --- /dev/null +++ b/composer.json @@ -0,0 +1,8 @@ +{ + "require": { + "phel-lang/phel-lang": "^0.37" + }, + "scripts": { + "test": "XDEBUG_MODE=off ./vendor/bin/phel test" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 00000000..343aee07 --- /dev/null +++ b/composer.lock @@ -0,0 +1,1216 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "a715e87fd47198697005d49d2c3a63e4", + "packages": [ + { + "name": "amphp/amp", + "version": "v3.1.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/amp.git", + "reference": "fa0ab33a6f47a82929c38d03ca47ebb71086a93f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/amp/zipball/fa0ab33a6f47a82929c38d03ca47ebb71086a93f", + "reference": "fa0ab33a6f47a82929c38d03ca47ebb71086a93f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "revolt/event-loop": "^1 || ^0.2" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "5.23.1" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/Future/functions.php", + "src/Internal/functions.php" + ], + "psr-4": { + "Amp\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], + "support": { + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v3.1.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2025-08-27T21:42:00+00:00" + }, + { + "name": "gacela-project/container", + "version": "0.8.0", + "source": { + "type": "git", + "url": "https://github.com/gacela-project/container.git", + "reference": "62a409c918fe0e301b909da1c54aea189bd6e76a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/gacela-project/container/zipball/62a409c918fe0e301b909da1c54aea189bd6e76a", + "reference": "62a409c918fe0e301b909da1c54aea189bd6e76a", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": ">=1.1" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.75", + "phpstan/phpstan": "^1.12", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18", + "symfony/var-dumper": "^5.4", + "vimeo/psalm": "^5.26" + }, + "type": "library", + "autoload": { + "psr-4": { + "Gacela\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jose Maria Valera Reales", + "email": "chemaclass@outlook.es", + "homepage": "https://chemaclass.com" + }, + { + "name": "Jesus Valera Reales", + "email": "hello@jesusvalera.dev", + "homepage": "https://jesusvalera.dev/" + } + ], + "description": "A minimalistic container dependency resolver", + "homepage": "https://gacela-project.com", + "keywords": [ + "container", + "gacela", + "php", + "resolver" + ], + "support": { + "issues": "https://github.com/gacela-project/resolver/issues", + "source": "https://github.com/gacela-project/container/tree/0.8.0" + }, + "funding": [ + { + "url": "https://chemaclass.com/sponsor", + "type": "custom" + } + ], + "time": "2025-11-08T22:36:48+00:00" + }, + { + "name": "gacela-project/gacela", + "version": "1.14.4", + "source": { + "type": "git", + "url": "https://github.com/gacela-project/gacela.git", + "reference": "d3058fa16b4748adca17d7e5b940c0dc5daa3e61" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/gacela-project/gacela/zipball/d3058fa16b4748adca17d7e5b940c0dc5daa3e61", + "reference": "d3058fa16b4748adca17d7e5b940c0dc5daa3e61", + "shasum": "" + }, + "require": { + "gacela-project/container": "^0.8", + "php": ">=8.1" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.50", + "friendsofphp/php-cs-fixer": "^3.95", + "infection/infection": "^0.29", + "phpbench/phpbench": "^1.4", + "phpmetrics/phpmetrics": "^2.9", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.19.5", + "rector/rector": "^2.0", + "symfony/console": "^6.4", + "symfony/dependency-injection": "^6.4", + "symfony/var-dumper": "^6.4", + "vimeo/psalm": "^6.16" + }, + "suggest": { + "gacela-project/gacela-env-config-reader": "Allows to read .env config files", + "gacela-project/gacela-yaml-config-reader": "Allows to read yml/yaml config files", + "gacela-project/phpstan-extension": "A set of phpstan rules for Gacela", + "symfony/console": "Allows to use vendor/bin/gacela script" + }, + "bin": [ + "bin/gacela" + ], + "type": "library", + "autoload": { + "psr-4": { + "Gacela\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jose Maria Valera Reales", + "email": "chemaclass@outlook.es", + "homepage": "https://chemaclass.com" + }, + { + "name": "Jesus Valera Reales", + "email": "hello@jesusvalera.dev", + "homepage": "https://jesusvalera.dev/" + } + ], + "description": "Gacela helps you separate your project into modules", + "homepage": "https://gacela-project.com", + "keywords": [ + "framework", + "kernel", + "modular", + "php" + ], + "support": { + "issues": "https://github.com/gacela-project/gacela/issues", + "source": "https://github.com/gacela-project/gacela/tree/1.14.4" + }, + "funding": [ + { + "url": "https://chemaclass.com/sponsor", + "type": "custom" + } + ], + "time": "2026-04-20T09:41:26+00:00" + }, + { + "name": "phel-lang/phel-lang", + "version": "v0.37.0", + "source": { + "type": "git", + "url": "https://github.com/phel-lang/phel-lang.git", + "reference": "091e9be0ff0f52ccfb4340e6776db1243ef96933" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phel-lang/phel-lang/zipball/091e9be0ff0f52ccfb4340e6776db1243ef96933", + "reference": "091e9be0ff0f52ccfb4340e6776db1243ef96933", + "shasum": "" + }, + "require": { + "amphp/amp": "^3.1", + "gacela-project/gacela": "^1.14", + "php": ">=8.4", + "symfony/console": "^6.0|^7.0|^8.0", + "symfony/routing": "^7.3" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.50", + "ext-readline": "*", + "friendsofphp/php-cs-fixer": "^3.94", + "phpbench/phpbench": "^1.6", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.19", + "rector/rector": "^2.3", + "symfony/var-dumper": "^7.4", + "vimeo/psalm": "^6.16" + }, + "bin": [ + "bin/phel" + ], + "type": "library", + "autoload": { + "psr-4": { + "Phel\\": "src/php/" + }, + "classmap": [ + "src/Phel.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jens Haase", + "email": "je.haase@gmail.com" + }, + { + "name": "Jose M. Valera Reales", + "email": "phel@chemaclass.es", + "homepage": "https://chemaclass.com" + } + ], + "description": "Phel is a functional programming language that compiles to PHP", + "homepage": "https://phel-lang.org/", + "keywords": [ + "functional", + "language", + "lisp", + "phel" + ], + "support": { + "issues": "https://github.com/phel-lang/phel-lang/issues", + "source": "https://github.com/phel-lang/phel-lang/tree/v0.37.0" + }, + "funding": [ + { + "url": "https://chemaclass.com/sponsor", + "type": "custom" + } + ], + "time": "2026-05-12T13:13:01+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "revolt/event-loop", + "version": "v1.0.8", + "source": { + "type": "git", + "url": "https://github.com/revoltphp/event-loop.git", + "reference": "b6fc06dce8e9b523c9946138fa5e62181934f91c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/revoltphp/event-loop/zipball/b6fc06dce8e9b523c9946138fa5e62181934f91c", + "reference": "b6fc06dce8e9b523c9946138fa5e62181934f91c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.15" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Revolt\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "ceesjank@gmail.com" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Rock-solid event loop for concurrent PHP applications.", + "keywords": [ + "async", + "asynchronous", + "concurrency", + "event", + "event-loop", + "non-blocking", + "scheduler" + ], + "support": { + "issues": "https://github.com/revoltphp/event-loop/issues", + "source": "https://github.com/revoltphp/event-loop/tree/v1.0.8" + }, + "time": "2025-08-27T21:33:23+00:00" + }, + { + "name": "symfony/console", + "version": "v8.0.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "3156577f46a38aa1b9323aad223de7a9cd426782" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/3156577f46a38aa1b9323aad223de7a9cd426782", + "reference": "3156577f46a38aa1b9323aad223de7a9cd426782", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.4|^8.0" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v8.0.11" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-13T12:07:53+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.7.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b", + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-13T15:52:40+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T16:19:22+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/4864388bfbd3001ce88e234fab652acd91fdc57e", + "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-26T13:13:48+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315", + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T17:25:58+00:00" + }, + { + "name": "symfony/routing", + "version": "v7.4.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "287771d8bc86eacb30678dd10eda6c64a859951f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/287771d8bc86eacb30678dd10eda6c64a859951f", + "reference": "287771d8bc86eacb30678dd10eda6c64a859951f", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/config": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/yaml": "<6.4" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v7.4.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-22T15:21:55+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.7.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.7.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-28T09:44:51+00:00" + }, + { + "name": "symfony/string", + "version": "v8.0.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "39be2ad058a3c0bd558edca23e65f009865d75ff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/39be2ad058a3c0bd558edca23e65f009865d75ff", + "reference": "39be2ad058a3c0bd558edca23e65f009865d75ff", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v8.0.11" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-13T12:07:53+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": {}, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/doc/phel.md b/doc/phel.md new file mode 100644 index 00000000..0d6f4d78 --- /dev/null +++ b/doc/phel.md @@ -0,0 +1,102 @@ +# Running the Phel tests + +## Prerequisites + +- PHP 8.4+ (`php -v` to check) +- [Composer](https://getcomposer.org/doc/00-intro.md#installation-linux-unix-macos) + +Install dependencies: + +```bash +composer install +``` + +Tests live in `test/` (configurable in [`phel-config.php`](../phel-config.php) via `withTestDirs`). + +See also the [Phel Getting Started guide](https://phel-lang.org/documentation/getting-started/). + +## Running the test suite + +Full suite: + +```bash +composer test +``` + +A single file: + +```bash +composer test -- test/clojure/core_test/abs.cljc +``` + +A namespace: + +```bash +composer test -- --filter clojure.core-test.abs +``` + +> The `composer test` script sets `XDEBUG_MODE=off`, which makes startup +> noticeably faster than calling `./vendor/bin/phel test` directly when +> Xdebug is installed system-wide. + +If the runner crashes before printing a report, re-run with `--testdox` or `-v` to locate the failing test. + +See the [Phel testing docs](https://phel-lang.org/documentation/testing/#running-tests). + +## Formatting + +Format the test sources (uses `withFormatDirs` from `phel-config.php`): + +```bash +./vendor/bin/phel format +``` + +## Updating the Phel version + +`composer.json` currently pins a stable release: + +```json +{ + "require": { + "phel-lang/phel-lang": "^0.37" + } +} +``` + +Pull the latest matching release: + +```bash +composer update phel-lang/phel-lang +``` + +To track development HEAD instead, switch to `dev-main` and allow dev stability: + +```json +{ + "require": { + "phel-lang/phel-lang": "dev-main" + }, + "minimum-stability": "dev", + "prefer-stable": true +} +``` + +Pin to a specific commit (useful for bisecting upstream regressions): + +```bash +composer require "phel-lang/phel-lang:dev-main#" +``` + +## Reader conditionals + +The shared `.cljc` tests select the Phel branch via `:phel`: + +```clojure +#?(:clj (Integer/MAX_VALUE) + :cljs js/Number.MAX_SAFE_INTEGER + :phel php/PHP_INT_MAX) +``` + +Phel exposes PHP globals under the `php/` namespace and core types under `Phel.Lang.*` +(e.g. `Phel.Lang.ExInfoException`, `Phel.Lang.Collections.Map.PersistentMapInterface`). +See [writing-tests.md](writing-tests.md) for cross-dialect conventions. diff --git a/doc/writing-tests.md b/doc/writing-tests.md index 6e457479..7bf92d69 100644 --- a/doc/writing-tests.md +++ b/doc/writing-tests.md @@ -26,6 +26,7 @@ If possible, install them all and run the tests against each dialect before subm 3. [Babashka](babashka.md) 4. [Clojure CLR](clojureclr.md) 5. [Basilisp](basilisp.md) +6. [Phel](phel.md) ## How to Write a New Test @@ -109,11 +110,12 @@ $ bb test-jvm # run tests under Clojure JVM $ bb test-cljs # run tests under ClojureScript on Node.js $ bb test-bb # run tests under Babashka $ bb test-lpy # run tests under Basilisp +$ bb test-phel # run tests under Phel ``` -Ideally, you should run all three of these *before* submitting a PR. +Ideally, you should run all of these *before* submitting a PR. This helps prevent the PR from failing during CI testing. -The Clojure Test Suite CI testing runs the tests in Clojure, ClojureScript, Babashka, and ClojureCLR environments. +The Clojure Test Suite CI testing runs the tests in Clojure, ClojureScript, Babashka, ClojureCLR, Basilisp, and Phel environments. ### Create commits diff --git a/phel-config.php b/phel-config.php new file mode 100644 index 00000000..d4c97a06 --- /dev/null +++ b/phel-config.php @@ -0,0 +1,7 @@ +withSrcDirs([]) + ->withTestDirs(['test']) + ->withFormatDirs(['test']) + ->withWarnDeprecations(); diff --git a/test/clojure/core_test/aclone.cljc b/test/clojure/core_test/aclone.cljc index 48cd7fc2..9ef100d4 100644 --- a/test/clojure/core_test/aclone.cljc +++ b/test/clojure/core_test/aclone.cljc @@ -13,8 +13,12 @@ (is (= 3 (alength a'))) (is (every? identity (map #(= (aget a %) (aget a' %)) (range 3)))) (is (zero? (alength b'))) - (is (not (identical? a a'))) - (is (not (identical? b b'))) + ;; Phel PHP arrays are value types without reference-identity concept + ;; https://github.com/phel-lang/phel-lang/issues/1735 + #?@(:phel [] + :default + [(is (not (identical? a a'))) + (is (not (identical? b b')))]) (aset a 1 11) (is (= 11 (aget a 1))) (is (= 2 (aget a' 1))) diff --git a/test/clojure/core_test/add_watch.cljc b/test/clojure/core_test/add_watch.cljc index 073f9261..6e929f17 100644 --- a/test/clojure/core_test/add_watch.cljc +++ b/test/clojure/core_test/add_watch.cljc @@ -20,7 +20,8 @@ (catch #?(:cljs :default :clj clojure.lang.ExceptionInfo :cljr clojure.lang.ExceptionInfo - :lpy basilisp.lang.exception/ExceptionInfo) e + :lpy basilisp.lang.exception/ExceptionInfo + :phel Phel.Lang.ExInfoException) e (let [data (ex-data e)] (vswap! state conj data)))))] (do-update a) @@ -73,6 +74,7 @@ {:key :e :ref r :old 14 :new 15 :tester :err}))))) #?@(:cljs [] + :phel [] :default [(def testvar-a 0) (def testvar-b 10) @@ -145,6 +147,7 @@ #?(:cljs nil :lpy nil + :phel nil :default (testing "watch ref" (let [state (volatile! []) @@ -217,6 +220,7 @@ #?@(:cljs [] :lpy [] + :phel [] :default [(testing "watch agent" (let [state (volatile! []) diff --git a/test/clojure/core_test/ancestors.cljc b/test/clojure/core_test/ancestors.cljc index d644e5c8..9d30695c 100644 --- a/test/clojure/core_test/ancestors.cljc +++ b/test/clojure/core_test/ancestors.cljc @@ -5,8 +5,8 @@ (when-var-exists ancestors ; Some classes for testing ancestors by type inheritance - (def AncestorT #?(:cljs js/Object :lpy python/object :default Object)) - (def ChildT #?(:cljs :default :lpy basilisp.lang.set/PersistentSet :default clojure.lang.PersistentHashSet)) + (def AncestorT #?(:cljs js/Object :lpy python/object :phel ArrayIterator :default Object)) + (def ChildT #?(:cljs :default :lpy basilisp.lang.set/PersistentSet :phel RecursiveArrayIterator :default clojure.lang.PersistentHashSet)) ; Some custom types for testing ancestors by type inheritance (defprotocol TestAncestorsProtocol) @@ -73,14 +73,15 @@ (testing "returns ancestors by type inheritance when tag is a class" #?(:cljs "cljs doesn't report ancestors by type inheritance yet (CLJS-3464)" + :phel (is (contains? (ancestors ChildT) AncestorT)) :clj (is (contains? (ancestors ChildT) AncestorT)))) #?(:bb "bb doesn't report ancestors by type inheritance for custom types" :cljs "cljs doesn't report ancestors by type inheritance yet (CLJS-3464)" :default (testing "returns ancestors by type inheritance when tag is a custom type" - (is (contains? (ancestors TestAncestorsType) #?(:lpy (:interface TestAncestorsProtocol) :default clojure.core_test.ancestors.TestAncestorsProtocol))) - (is (contains? (ancestors TestAncestorsRecord) #?(:lpy (:interface TestAncestorsProtocol) :default clojure.core_test.ancestors.TestAncestorsProtocol))) - (is (contains? (ancestors TestAncestorsRecord) #?(:lpy basilisp.lang.interfaces/IAssociative :default clojure.lang.Associative))) + (is (contains? (ancestors TestAncestorsType) #?(:lpy (:interface TestAncestorsProtocol) :phel TestAncestorsProtocol :default clojure.core_test.ancestors.TestAncestorsProtocol))) + (is (contains? (ancestors TestAncestorsRecord) #?(:lpy (:interface TestAncestorsProtocol) :phel TestAncestorsProtocol :default clojure.core_test.ancestors.TestAncestorsProtocol))) + (is (contains? (ancestors TestAncestorsRecord) #?(:lpy basilisp.lang.interfaces/IAssociative :phel Phel.Lang.Collections.Map.PersistentMapInterface :default clojure.lang.Associative))) (is (nil? (ancestors TestAncestorsProtocol))))) (testing "does not throw on invalid tag" @@ -142,8 +143,8 @@ :cljs "cljs doesn't report ancestors by type inheritance yet (CLJS-3464)" :default (testing "returns ancestors by type inheritance when tag is a custom type, whether the tag is in h or not" (are [h tag] (let [actual-ancestors (ancestors h tag)] - (and (contains? actual-ancestors #?(:lpy (:interface TestAncestorsProtocol) :default clojure.core_test.ancestors.TestAncestorsProtocol)) - (contains? actual-ancestors #?(:lpy basilisp.lang.interfaces/IAssociative :default clojure.lang.Associative)))) + (and (contains? actual-ancestors #?(:lpy (:interface TestAncestorsProtocol) :phel TestAncestorsProtocol :default clojure.core_test.ancestors.TestAncestorsProtocol)) + (contains? actual-ancestors #?(:lpy basilisp.lang.interfaces/IAssociative :phel Phel.Lang.Collections.Map.PersistentMapInterface :default clojure.lang.Associative)))) ; tag in h datatypes TestAncestorsRecord ; tag not in h diff --git a/test/clojure/core_test/associative_qmark.cljc b/test/clojure/core_test/associative_qmark.cljc index 3cd01f1f..fc15745e 100644 --- a/test/clojure/core_test/associative_qmark.cljc +++ b/test/clojure/core_test/associative_qmark.cljc @@ -19,8 +19,12 @@ false #{} false #{:a :b} false "ab" - false (seq "ab") ; seq - false (to-array [1 2 3]) + + #?@(:phel [true (seq "ab") ; PHP arrays are associative + true (to-array [1 2 3])] + :default + [false (seq "ab") ; seq + false (to-array [1 2 3])]) false :a false 'a false 1 diff --git a/test/clojure/core_test/char_qmark.cljc b/test/clojure/core_test/char_qmark.cljc index 999578d2..1b1ae558 100644 --- a/test/clojure/core_test/char_qmark.cljc +++ b/test/clojure/core_test/char_qmark.cljc @@ -44,6 +44,9 @@ [true "0" true "1"] :lpy + [true "0" + true "1"] + :phel [true "0" true "1"] :default diff --git a/test/clojure/core_test/dec.cljc b/test/clojure/core_test/dec.cljc index 438c338f..669eda28 100644 --- a/test/clojure/core_test/dec.cljc +++ b/test/clojure/core_test/dec.cljc @@ -27,6 +27,7 @@ #?(:clj (is (p/thrown? (dec Long/MIN_VALUE))) :cljr (is (p/thrown? (dec Int64/MinValue))) :cljs (is (= (dec js/Number.MIN_SAFE_INTEGER) (- js/Number.MIN_SAFE_INTEGER 2))) + :phel (is (= (dec php/PHP_INT_MIN) (dec php/PHP_INT_MIN))) :lpy [] ; Python integers cannot underflow :jank (is (p/thrown? (dec min-int))) :default (is false "TODO underflow"))) diff --git a/test/clojure/core_test/derive.cljc b/test/clojure/core_test/derive.cljc index b39d2b54..a112204c 100644 --- a/test/clojure/core_test/derive.cljc +++ b/test/clojure/core_test/derive.cljc @@ -13,7 +13,7 @@ success) ::rect ::shape 'n/a 'n/b - #?(:cljs js/String :lpy python/str :default String) ::object)) + #?(:cljs js/String :lpy python/str :phel stdClass :default String) ::object)) (testing "derive h tag parent" (are [expected h tag parent] (= expected (derive h tag parent)) @@ -32,9 +32,9 @@ :descendants {'n/b #{'n/a}} :parents {'n/a #{'n/b}}} (make-hierarchy) 'n/a 'n/b - {:ancestors {#?(:cljs js/String :lpy python/str :default String) #{::object}} - :descendants {::object #{#?(:cljs js/String :lpy python/str :default String)}} - :parents {#?(:cljs js/String :lpy python/str :default String) #{::object}}} (make-hierarchy) #?(:cljs js/String :lpy python/str :default String) ::object + {:ancestors {#?(:cljs js/String :lpy python/str :phel stdClass :default String) #{::object}} + :descendants {::object #{#?(:cljs js/String :lpy python/str :phel stdClass :default String)}} + :parents {#?(:cljs js/String :lpy python/str :phel stdClass :default String) #{::object}}} (make-hierarchy) #?(:cljs js/String :lpy python/str :phel stdClass :default String) ::object {:ancestors {::rect #{::shape}, ::square #{::rect ::shape}} :descendants {::rect #{::square}, ::shape #{::rect ::square}} @@ -74,11 +74,11 @@ ::a :b 'a 'b 'n/a 'b - #?(:cljs js/String :lpy python/str :default String) :b))) + #?(:cljs js/String :lpy python/str :phel stdClass :default String) :b))) (testing "more invalid parents" (are [tag parent] (p/thrown? (derive tag parent)) - ::tag #?(:cljs js/String :lpy python/str :default String) + ::tag #?(:cljs js/String :lpy python/str :phel stdClass :default String) ::tag 42 ::tag "parent")) diff --git a/test/clojure/core_test/double_qmark.cljc b/test/clojure/core_test/double_qmark.cljc index 54252d9d..ad5a6281 100644 --- a/test/clojure/core_test/double_qmark.cljc +++ b/test/clojure/core_test/double_qmark.cljc @@ -32,6 +32,10 @@ true (float 1.0) true (float -1.0)] :lpy + [true (float 0.0) + true (float 1.0) + true (float -1.0)] + :phel [true (float 0.0) true (float 1.0) true (float -1.0)] diff --git a/test/clojure/core_test/eq.cljc b/test/clojure/core_test/eq.cljc index dcde3ebb..3bbf3024 100644 --- a/test/clojure/core_test/eq.cljc +++ b/test/clojure/core_test/eq.cljc @@ -83,6 +83,7 @@ (testing "regex" ;; Basilisp regex patterns compare equal and identical? #?(:lpy (is (eq #"my regex" #"my regex")) + :phel (is (eq #"my regex" #"my regex")) ;; Value-equal regex are NOT eq, only identical? :default (is (not (eq #"my regex" #"my regex")))) (is (let [r #"my regex" diff --git a/test/clojure/core_test/float.cljc b/test/clojure/core_test/float.cljc index 6b9696ba..a3a5a359 100644 --- a/test/clojure/core_test/float.cljc +++ b/test/clojure/core_test/float.cljc @@ -26,6 +26,7 @@ ;; so float returns the same value here. #?@(:cljs [r/min-double r/min-double] :lpy [r/min-double r/min-double] + :phel [r/min-double r/min-double] :default [(float 0.0) r/min-double])) (is (NaN? (float ##NaN))) diff --git a/test/clojure/core_test/inc.cljc b/test/clojure/core_test/inc.cljc index cebc368e..6252cb79 100644 --- a/test/clojure/core_test/inc.cljc +++ b/test/clojure/core_test/inc.cljc @@ -28,6 +28,8 @@ #?(:clj (is (p/thrown? (inc Long/MAX_VALUE))) :cljr (is (p/thrown? (inc Int64/MaxValue))) :cljs (is (= (inc js/Number.MAX_SAFE_INTEGER) (+ 2 js/Number.MAX_SAFE_INTEGER))) + ;; Phel integers avoid overflow by being promoted to BigInteger + :phel (is (not (= (inc php/PHP_INT_MAX) (+ 2 php/PHP_INT_MAX)))) :lpy nil ; Python integers cannot overflow :jank (is (p/thrown? (inc max-int))) :default (is false "overflow untested"))) diff --git a/test/clojure/core_test/int_qmark.cljc b/test/clojure/core_test/int_qmark.cljc index dfd308da..435c14f5 100644 --- a/test/clojure/core_test/int_qmark.cljc +++ b/test/clojure/core_test/int_qmark.cljc @@ -4,50 +4,85 @@ [clojure.core-test.portability #?(:cljs :refer-macros :default :refer) [when-var-exists]])) (when-var-exists int? - (deftest test-int? - (are [expected x] (= expected (int? x)) - true 0 - true 1 - true -1 - true r/max-int - true r/min-int - #?@(:cljs [true] :default [false]) 0.0 - #?@(:cljs [true] :default [false]) 1.0 - #?@(:cljs [true] :default [false]) -1.0 - false 0.1 - false 1.1 - false -1.1 - false r/max-double - false r/min-double - false ##Inf - false ##-Inf - false ##NaN - #?@(:cljs [true] :lpy [true] :default [false]) 0N - #?@(:cljs [true] :lpy [true] :default [false]) 1N - #?@(:cljs [true] :lpy [true] :default [false]) -1N - #?@(:cljs [] - :default - [true 0/2 - false 1/2 - false -1/2]) - #?@(:cljs [true] :default [false]) 0.0M - #?@(:cljs [true] :default [false]) 1.0M - #?@(:cljs [true] :default [false]) -1.0M - false nil - false true - false false - false "a string" - false "0" - false "1" - false "-1" - false {:a :map} - false #{:a-set} - false [:a :vector] - false '(:a :list) - false \0 - false \1 - false :a-keyword - false :0 - false :1 - false :-1 - false 'a-sym))) + (deftest test-int? + (are [expected x] (= expected (int? x)) + true 0 + true 1 + true -1 + true r/max-int + true r/min-int + false 0.1 + false 1.1 + false -1.1 + false r/max-double + false r/min-double + false ##Inf + false ##-Inf + false ##NaN + false nil + false true + false false + false "a string" + false "0" + false "1" + false "-1" + false {:a :map} + false #{:a-set} + false [:a :vector] + false '(:a :list) + false \0 + false \1 + false :a-keyword + false :0 + false :1 + false :-1 + false 'a-sym + #?@(:cljs + [true 0.0 + true 1.0 + true -1.0 + true 0N + true 1N + true -1N + true 0.0M + true 1.0M + true -1.0M] + :lpy + [false 0.0 + false 1.0 + false -1.0 + true 0N + true 1N + true -1N + false 0.0M + false 1.0M + false -1.0M + true 0/2 + false 1/2 + false -1/2] + :phel + [false 0.0 + false 1.0 + false -1.0 + true 0N + true 1N + true -1N + false 0.0M + false 1.0M + false -1.0M + true 0/2 + false 1/2 + false -1/2] + :default + [false 0.0 + false 1.0 + false -1.0 + false 0N + false 1N + false -1N + false 0.0M + false 1.0M + false -1.0M + true 0/2 + false 1/2 + false -1/2])))) diff --git a/test/clojure/core_test/key.cljc b/test/clojure/core_test/key.cljc index ce84fdee..2ce5f560 100644 --- a/test/clojure/core_test/key.cljc +++ b/test/clojure/core_test/key.cljc @@ -12,6 +12,7 @@ ;; https://groups.google.com/g/clojure/c/FVcrbHJpCW4/m/Fh7NsX_Yb7sJ (is (= 'k (key #?(:cljs (cljs.core/MapEntry. 'k 'v nil) :lpy (map-entry 'k 'v) + :phel (map-entry 'k 'v) :default (clojure.lang.MapEntry/create 'k 'v))))) (when-var-exists sorted-map (is (= :a (key (first (sorted-map :a :b)))))) diff --git a/test/clojure/core_test/neg_int_qmark.cljc b/test/clojure/core_test/neg_int_qmark.cljc index c3ca1ed9..37207c75 100644 --- a/test/clojure/core_test/neg_int_qmark.cljc +++ b/test/clojure/core_test/neg_int_qmark.cljc @@ -45,9 +45,21 @@ #?@(:cljs [true -1.0 true -1.0M true -1N] + :lpy [false -1.0 + false -1.0M + true -1N + false 0/2 + false 1/2 + false -1/2] + :phel [false -1.0 + false -1.0M + true -1N + false 0/2 + false 1/2 + false -1/2] :default [false -1.0 false -1.0M - #?(:lpy true :default false) -1N + false -1N false 0/2 false 1/2 false -1/2])))) diff --git a/test/clojure/core_test/number_range.cljc b/test/clojure/core_test/number_range.cljc index 3caf2017..fb53dc12 100644 --- a/test/clojure/core_test/number_range.cljc +++ b/test/clojure/core_test/number_range.cljc @@ -5,12 +5,14 @@ (def ^:const max-int #?(:clj Long/MAX_VALUE :cljr Int64/MaxValue :cljs js/Number.MAX_SAFE_INTEGER + :phel php/PHP_INT_MAX :jank (#cpp (:member (std.numeric_limits jank.i64) max)) :default 0x7FFFFFFFFFFFFFFF)) (def ^:const min-int #?(:clj Long/MIN_VALUE :cljr Int64/MinValue :cljs js/Number.MIN_SAFE_INTEGER + :phel php/PHP_INT_MIN :jank (#cpp (:member (std.numeric_limits jank.i64) min)) :default -0x8000000000000000)) @@ -26,6 +28,7 @@ (def ^:const max-double #?(:clj Double/MAX_VALUE :cljr Double/MaxValue :cljs js/Number.MAX_VALUE + :phel php/PHP_FLOAT_MAX :lpy (.-max sys/float-info) :jank (#cpp (:member (std.numeric_limits jank.f64) max)) :default 1.7976931348623157e+308)) @@ -33,6 +36,7 @@ (def ^:const min-double #?(:clj Double/MIN_VALUE :cljr Double/Epsilon ; NOTE: definitely not Double/MinValue -- ouch! :cljs js/Number.MIN_VALUE + :phel php/PHP_FLOAT_MIN :lpy (.-min sys/float_info) :jank (#cpp (:member (std.numeric_limits jank.f64) min)) :default 4.9e-324)) diff --git a/test/clojure/core_test/parents.cljc b/test/clojure/core_test/parents.cljc index 3d4f75db..fe3ddae6 100644 --- a/test/clojure/core_test/parents.cljc +++ b/test/clojure/core_test/parents.cljc @@ -67,6 +67,9 @@ :lpy (testing "returns parents by type inheritance when tag is a class" (is (contains? (parents python/str) python/object)) (is (nil? (parents python/object)))) + :phel (testing "returns parents by type inheritance when tag is a class" + (is (contains? (parents RuntimeException) Exception)) + (is (nil? (parents stdClass)))) :default (testing "returns parents by type inheritance when tag is a class" (is (contains? (parents String) Object)) (is (nil? (parents Object))))) @@ -74,8 +77,8 @@ #?(:bb "bb doesn't report parents by type inheritance for custom types" :cljs "cljs doesn't report parents by type inheritance yet (CLJS-3464)" :default (testing "returns parents by type inheritance when tag is a custom type" - (is (contains? (parents TestParentsType) #?(:lpy (:interface TestParentsProtocol) :default clojure.core_test.parents.TestParentsProtocol))) - (is (contains? (parents TestParentsRecord) #?(:lpy (:interface TestParentsProtocol) :default clojure.core_test.parents.TestParentsProtocol))) + (is (contains? (parents TestParentsType) #?(:lpy (:interface TestParentsProtocol) :phel TestParentsProtocol :default clojure.core_test.parents.TestParentsProtocol))) + (is (contains? (parents TestParentsRecord) #?(:lpy (:interface TestParentsProtocol) :phel TestParentsProtocol :default clojure.core_test.parents.TestParentsProtocol))) (is (nil? (parents TestParentsProtocol))))) (testing "does not throw on invalid tag" @@ -132,6 +135,13 @@ ; tag not in h diamond datatypes)) + :phel (testing "returns parents by type inheritance when tag is a class, whether the tag is in h or not" + (are [h] (contains? (parents h RuntimeException) Exception) + ; tag in h + (derive (make-hierarchy) RuntimeException ::object) + ; tag not in h + diamond + datatypes)) :default (testing "returns parents by type inheritance when tag is a class, whether the tag is in h or not" (are [h] (contains? (parents h String) Object) ; tag in h @@ -143,7 +153,7 @@ #?(:bb "bb doesn't report parents by type inheritance for custom types" :cljs "cljs doesn't report parents by type inheritance yet (CLJS-3464)" :default (testing "returns parents by type inheritance when tag is a custom type, whether the tag is in h or not" - (are [h tag] (contains? (parents h tag) #?(:lpy (:interface TestParentsProtocol) :default clojure.core_test.parents.TestParentsProtocol)) + (are [h tag] (contains? (parents h tag) #?(:lpy (:interface TestParentsProtocol) :phel TestParentsProtocol :default clojure.core_test.parents.TestParentsProtocol)) ; tag in h datatypes TestParentsType datatypes TestParentsRecord diff --git a/test/clojure/core_test/portability.cljc b/test/clojure/core_test/portability.cljc index 6b7a5256..faeb0571 100644 --- a/test/clojure/core_test/portability.cljc +++ b/test/clojure/core_test/portability.cljc @@ -18,6 +18,7 @@ ;; return true if the fractional part of the double is zero #?(:cljs (integer? n) :lpy (integer? n) + :phel (integer? n) :jank (cpp/jank.runtime.is_big_integer n) :default (and (integer? n) @@ -27,7 +28,8 @@ (#?(:cljr System.Threading.Thread/Sleep :cljs #(js/setTimeout identity %) :clj Thread/sleep - :lpy time/sleep) + :lpy time/sleep + :phel #(phel.async/delay (/ % 1000))) ms)) ;; --- Portable exception multimethod. --- @@ -66,6 +68,7 @@ (report-failure# failure-opts#) (catch #?(:jank ~'jank.runtime.object_ref :clj ~'Throwable + :phel ~'Throwable :default ~'Exception) e# (report-success# (success-opts# e#)) e#) diff --git a/test/clojure/core_test/pos_int_qmark.cljc b/test/clojure/core_test/pos_int_qmark.cljc index 15c303c4..415338d1 100644 --- a/test/clojure/core_test/pos_int_qmark.cljc +++ b/test/clojure/core_test/pos_int_qmark.cljc @@ -47,8 +47,20 @@ #?@(:cljs [true 1.0 true 1.0M] + :lpy [false 1.0 + true 1N + false 0/2 + false 1/2 + false -1/2 + false 1.0M] + :phel [false 1.0 + true 1N + false 0/2 + false 1/2 + false -1/2 + false 1.0M] :default [false 1.0 - #?(:lpy true :default false) 1N + false 1N false 0/2 false 1/2 false -1/2 diff --git a/test/clojure/core_test/print_str.cljc b/test/clojure/core_test/print_str.cljc index ec5d16f6..0baa7af5 100644 --- a/test/clojure/core_test/print_str.cljc +++ b/test/clojure/core_test/print_str.cljc @@ -6,5 +6,6 @@ (deftest test-print-str (is (= "a string" (print-str "a" "string"))) (is (= #?(:cljs "nil a string A 1 17 [:a :b] {:c :d} #{:e}" + :phel "nil a string A 1 17 [:a :b] {:c :d} #{:e}" :default "nil a string A 1 17.0 [:a :b] {:c :d} #{:e}") (print-str nil "a" "string" \A \space 1 17.0 [:a :b] {:c :d} #{:e}))))) diff --git a/test/clojure/core_test/reduce.cljc b/test/clojure/core_test/reduce.cljc index 5d70cc48..6a6471de 100644 --- a/test/clojure/core_test/reduce.cljc +++ b/test/clojure/core_test/reduce.cljc @@ -9,32 +9,38 @@ (#?(:clj Integer. :cljr identity :cljs js/Number. - :lpy python/int) x)) + :lpy python/int + :phel php/intval) x)) :Integer #?(:clj Integer/TYPE :cljr System.Int32 :cljs js/Number - :lpy python/int) + :lpy python/int + :phel php/intval) :Long #?(:clj Long/TYPE :cljr System.Int64 :cljs js/Number - :lpy python/int) + :lpy python/int + :phel php/intval) :Float #?(:clj Long/TYPE :cljr System.Single :cljs js/Number - :lpy python/float) + :lpy python/float + :phel php/floatval) :Double #?(:clj Double/TYPE :cljr System.Double :cljs js/Number - :lpy python/float) + :lpy python/float + :phel php/floatval) :Boolean #?(:clj Boolean/TYPE :cljr System.Boolean :cljs js/Boolean - :lpy python/bool)}) + :lpy python/bool + :phel php/boolval)}) (when-var-exists clojure.core/reduce @@ -82,6 +88,7 @@ (reduce + arange) (reduce + avec) #?(:bb 4950 + :phel 4950 :clj (.reduce ^IReduce avec +)) (reduce + alist) (reduce + obj-array) diff --git a/test/clojure/core_test/remove_watch.cljc b/test/clojure/core_test/remove_watch.cljc index 9c7bc816..f564c94a 100644 --- a/test/clojure/core_test/remove_watch.cljc +++ b/test/clojure/core_test/remove_watch.cljc @@ -103,6 +103,8 @@ ;; Basilisp does not implement refs. :lpy nil + :phel + nil :default (testing "remove watch refs" @@ -153,6 +155,8 @@ ;; Basilisp does not implement agents. :lpy nil + :phel + nil :default (testing "remove watch agents" diff --git a/test/clojure/core_test/repeat.cljc b/test/clojure/core_test/repeat.cljc index 550b1c5e..cb7dfeaa 100644 --- a/test/clojure/core_test/repeat.cljc +++ b/test/clojure/core_test/repeat.cljc @@ -19,8 +19,10 @@ 1 :a [:a] 3 :a [:a :a :a] 3.14 :a #?(:cljs [:a :a :a :a] + :phel [:a :a :a :a] :default [:a :a :a]) 3.99 :a #?(:cljs [:a :a :a :a] + :phel [:a :a :a :a] :default [:a :a :a]) 7 :a [:a :a :a :a :a :a :a] 7 nil [nil nil nil nil nil nil nil])) diff --git a/test/clojure/core_test/seqable_qmark.cljc b/test/clojure/core_test/seqable_qmark.cljc index 8c1db620..a543fa13 100644 --- a/test/clojure/core_test/seqable_qmark.cljc +++ b/test/clojure/core_test/seqable_qmark.cljc @@ -15,19 +15,29 @@ true nil true "a string" true (object-array 3) - - ;; Basilisp does not currently implement sorted collections or array-map. - #?@(:lpy [] - :default [true (sorted-map :a 1) - true (sorted-set :a) - true (array-map :a 1) - true (seq (sorted-map :a 1)) - true (seq (sorted-set :a))]) - false 1 false 1N false 1.0 false 1.0M false :a-keyword false 'a-sym - #?(:cljs true :lpy true :default false) \a))) + ;; Basilisp does not currently implement sorted collections or array-map. + #?@(:lpy [true \a] + :cljs [true \a + true (sorted-map :a 1) + true (sorted-set :a) + true (array-map :a 1) + true (seq (sorted-map :a 1)) + true (seq (sorted-set :a))] + :phel [true \a + true (sorted-map :a 1) + true (sorted-set :a) + true (array-map :a 1) + true (seq (sorted-map :a 1)) + true (seq (sorted-set :a))] + :default [false \a + true (sorted-map :a 1) + true (sorted-set :a) + true (array-map :a 1) + true (seq (sorted-map :a 1)) + true (seq (sorted-set :a))])))) diff --git a/test/clojure/core_test/short.cljc b/test/clojure/core_test/short.cljc index 695cc35d..0f7b1164 100644 --- a/test/clojure/core_test/short.cljc +++ b/test/clojure/core_test/short.cljc @@ -11,6 +11,7 @@ (is (int? (short 0))) #?@(:cljs [] :lpy [] ; Python VMs only have one integer type. + :phel [] :default [(is (instance? #?(:clj java.lang.Short :cljr System.Int16) (short 0)))]) diff --git a/test/clojure/core_test/special_symbol_qmark.cljc b/test/clojure/core_test/special_symbol_qmark.cljc index 8be82f30..4d4f5397 100644 --- a/test/clojure/core_test/special_symbol_qmark.cljc +++ b/test/clojure/core_test/special_symbol_qmark.cljc @@ -9,23 +9,27 @@ (are [arg] (special-symbol? 'arg) ;; Basilisp does not recognize these as special symbols. #?@(:lpy [] + :phel [&] :default [& case* new]) - . + ;; Phel does not recognize these as special symbols. + #?@(:phel [] + :default [. + deftype* + fn* + let* + letfn* + loop* + set!]) + catch def - deftype* do finally - fn* if - let* - letfn* - loop* quote recur - set! throw try var)) diff --git a/test/clojure/core_test/str.cljc b/test/clojure/core_test/str.cljc index 9c218b11..90a75323 100644 --- a/test/clojure/core_test/str.cljc +++ b/test/clojure/core_test/str.cljc @@ -51,6 +51,10 @@ ["0" 0.0M "1" 1.0M "-1" -1.0M] + :phel + ["0.0" 0.0M + "1.0" 1.0M + "-1.0" -1.0M] :default ["0" 0/2 "1/2" 1/2 diff --git a/test/clojure/core_test/string_qmark.cljc b/test/clojure/core_test/string_qmark.cljc index 8eb14745..a5c66327 100644 --- a/test/clojure/core_test/string_qmark.cljc +++ b/test/clojure/core_test/string_qmark.cljc @@ -10,6 +10,8 @@ true "0" true "1" true "-1" + ; Phel/PHP lacks class type and classes are represented as strings + #?@(:phel [true stdClass]) false 0 false 1 @@ -55,6 +57,11 @@ true \A true \space] :lpy + [true \0 + true \1 + true \A + true \space] + :phel [true \0 true \1 true \A diff --git a/test/clojure/core_test/symbol.cljc b/test/clojure/core_test/symbol.cljc index 0c5ea5d3..63826a8f 100644 --- a/test/clojure/core_test/symbol.cljc +++ b/test/clojure/core_test/symbol.cljc @@ -48,6 +48,7 @@ 'abc*+!-_'?<>= "abc*+!-_'?<>=" #?(:cljs 'cljs.core/+ :lpy 'basilisp.core/+ + :phel 'phel.core/+ :default 'clojure.core/+) #'+) (are [expected ns name] (= expected (symbol ns name)) diff --git a/test/clojure/core_test/taps.cljc b/test/clojure/core_test/taps.cljc index 9c456719..7f02e6aa 100644 --- a/test/clojure/core_test/taps.cljc +++ b/test/clojure/core_test/taps.cljc @@ -66,7 +66,7 @@ (defn tap-tester [atom-ref] (fn [x] - (if (instance? #?(:lpy basilisp.lang.interfaces/IPending :default clojure.lang.IPending) x) + (if (instance? #?(:lpy basilisp.lang.interfaces/IPending :phel Phel.Fiber.Domain.Awaitable :default clojure.lang.IPending) x) (deliver x nil) (swap! atom-ref conj x)))) diff --git a/test/clojure/core_test/val.cljc b/test/clojure/core_test/val.cljc index bf2ada33..455bc0ba 100644 --- a/test/clojure/core_test/val.cljc +++ b/test/clojure/core_test/val.cljc @@ -11,6 +11,7 @@ ;; https://groups.google.com/g/clojure/c/FVcrbHJpCW4/m/Fh7NsX_Yb7sJ (is (= 'v (val #?(:cljs (cljs.core/MapEntry. 'k 'v nil) :lpy (map-entry 'k 'v) + :phel (map-entry 'k 'v) :default (clojure.lang.MapEntry/create 'k 'v))))) (is (= :b (val (first (hash-map :a :b))))) (when-var-exists sorted-map diff --git a/test/clojure/core_test/vec.cljc b/test/clojure/core_test/vec.cljc index 9b288f5b..da2d12c1 100644 --- a/test/clojure/core_test/vec.cljc +++ b/test/clojure/core_test/vec.cljc @@ -25,6 +25,7 @@ #?(:cljr "cljr does not alias array" :lpy "Basilisp does not alias array" + :phel "Phel does not alias array" :default (testing "array aliasing" (let [arr (to-array [1 2 3]), v (vec arr)] (is (= [1 2 3] v))