diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 1c6afb9..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "parserOptions": { - "ecmaVersion": 6, - "sourceType": "module" - }, - "extends": "eslint:recommended", - "rules": { - "strict": [2, "global"], - "block-scoped-var": 2, - "consistent-return": 2, - "eqeqeq": [2, "smart"], - "guard-for-in": 2, - "no-caller": 2, - "no-extend-native": 2, - "no-loop-func": 2, - "no-new": 2, - "no-param-reassign": 2, - "no-return-assign": 2, - "no-unused-expressions": 2, - "no-use-before-define": 2, - "radix": [2, "always"], - "indent": [2, 2], - "quotes": [2, "double"], - "semi": [2, "always"] - } -} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c69237a..d624d2e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,32 +4,24 @@ on: push: branches: [master] pull_request: - branches: [master] jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - - uses: purescript-contrib/setup-purescript@main - with: - purescript: "unstable" + - uses: actions/checkout@v4 - - uses: actions/setup-node@v2 + - uses: cachix/install-nix-action@v27 with: - node-version: "14.x" + extra_nix_config: | + extra-substituters = https://cache.iog.io https://purescript-lua.cachix.org + extra-trusted-public-keys = hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ= purescript-lua.cachix.org-1:yLs4ei2HtnuPtzLekOrW3xdfm95+Etw15gwgyIGTayA= - - name: Install dependencies - run: | - npm install -g bower - npm install - bower install --production + - name: Build + run: nix develop -c ./scripts/build - - name: Build source - run: npm run-script build + - name: Test + run: if [ -f scripts/test ]; then nix develop -c bash ./scripts/test; fi - - name: Run tests - run: | - bower install - npm run-script test --if-present + - name: Luacheck + run: nix develop -c luacheck --quiet --std lua51 --no-unused-args src/ diff --git a/.gitignore b/.gitignore index 7bb9d6d..db67e9a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,4 @@ /.* !/.gitignore -!/.github -!/.eslintrc.json -!/.travis.yml -package-lock.json -/bower_components/ -/node_modules/ +!/.github/ /output/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..12c3f5f --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,41 @@ +# AGENTS.md + +A PureScript→Lua FFI fork in the [`purescript-lua`](https://github.com/purescript-lua/purescript-lua) package set. Generated code targets **Lua 5.1**. + +## Commands + +All commands run inside the nix dev shell: + +- Build: `nix develop -c ./scripts/build` +- Test: `nix develop -c bash ./scripts/test` (only forks that ship Lua regression tests have one) +- Lint: `nix develop -c luacheck --quiet --std lua51 --no-unused-args src/` + +## Lua 5.1 target + +The output runs on Lua 5.1, which is stricter than 5.3: + +- No `table.unpack`, `bit32`, `utf8`, or the `//` operator. `math.pow` and `math.atan2` do exist. +- Array-style tables are 1-indexed: the first element is `t[1]`, not `t[0]`. +- `unit` is `{}`, never `nil`: a `nil` table element silently disappears, which would collapse `Array Unit` into an empty table. +- Lua 5.1 mangles some Lua 5.3 string escapes, so keep FFI string escapes 5.1-safe. + +## FFI files (`src/*.lua`) + +pslua's foreign-file parser needs every exported value wrapped in parentheses: + +```lua +return { + identity = (function(x) return x end), + answer = (42), +} +``` + +A bare `function … end` or an unparenthesised expression fails to parse. + +## Toolchain + +`flake.nix` pins everything through [`purescript-overlay`](https://github.com/thomashoneyman/purescript-overlay): purs 0.15.16 (`purs-bin.purs-0_15_16`), spago 0.21.0 (`spago-bin.spago-0_21_0`), Lua 5.1 (`lua51Packages`). The `pslua` input tracks `github:purescript-lua/purescript-lua`; keep `flake.lock` reasonably current, since a long-stale pslua pin won't create the `--lua-output-file` directory and CI fails. + +## Releasing + +Tag-driven, with no GitHub Release or changelog entry. The full conventions live in the [package-set repo](https://github.com/purescript-lua/purescript-lua-package-sets/blob/master/CONTRIBUTING.md): push an annotated tag on `master`, bump this fork's `version` in the package set's `src/packages.dhall`, refresh `latest-compatible-sets.json`, and push a `psc-*` set tag. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/bower.json b/bower.json deleted file mode 100644 index 19b1992..0000000 --- a/bower.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "purescript-arrays", - "homepage": "https://github.com/purescript/purescript-arrays", - "license": "BSD-3-Clause", - "repository": { - "type": "git", - "url": "https://github.com/purescript/purescript-arrays.git" - }, - "ignore": [ - "**/.*", - "bower_components", - "node_modules", - "output", - "bower.json", - "package.json" - ], - "dependencies": { - "purescript-bifunctors": "^6.0.0", - "purescript-control": "^6.0.0", - "purescript-foldable-traversable": "^6.0.0", - "purescript-functions": "^6.0.0", - "purescript-maybe": "^6.0.0", - "purescript-nonempty": "^7.0.0", - "purescript-partial": "^4.0.0", - "purescript-prelude": "^6.0.0", - "purescript-safe-coerce": "^2.0.0", - "purescript-st": "^6.2.0", - "purescript-tailrec": "^6.0.0", - "purescript-tuples": "^7.0.0", - "purescript-unfoldable": "^6.0.0", - "purescript-unsafe-coerce": "^6.0.0" - }, - "devDependencies": { - "purescript-assert": "^6.0.0", - "purescript-console": "^6.0.0", - "purescript-const": "^6.0.0", - "purescript-minibench": "^4.0.0" - } -} diff --git a/flake.lock b/flake.lock index 780c119..6b6c417 100644 --- a/flake.lock +++ b/flake.lock @@ -16,23 +16,6 @@ "type": "github" } }, - "cabal-32": { - "flake": false, - "locked": { - "lastModified": 1603716527, - "narHash": "sha256-X0TFfdD4KZpwl0Zr6x+PLxUt/VyKQfX7ylXHdmZIL+w=", - "owner": "haskell", - "repo": "cabal", - "rev": "48bf10787e27364730dd37a42b603cee8d6af7ee", - "type": "github" - }, - "original": { - "owner": "haskell", - "ref": "3.2", - "repo": "cabal", - "type": "github" - } - }, "cabal-34": { "flake": false, "locked": { @@ -88,11 +71,11 @@ "flake-utils": "flake-utils_2" }, "locked": { - "lastModified": 1710161569, - "narHash": "sha256-lcIRIOFCdIWEGyKyG/tB4KvxM9zoWuBRDxW+T+mvIb0=", + "lastModified": 1763814099, + "narHash": "sha256-YazeA9u0JdxykexV6HHG5DMtsnwqXoiAcWPjncO1XHM=", "owner": "justinwoo", "repo": "easy-purescript-nix", - "rev": "117fd96acb69d7d1727df95b6fde9d8715e031fc", + "rev": "8fcd84f54d75d9007b2f1c7c9da5af843105a55f", "type": "github" }, "original": { @@ -101,35 +84,35 @@ "type": "github" } }, - "easyps": { + "flake-compat": { "flake": false, "locked": { - "lastModified": 1710161569, - "narHash": "sha256-lcIRIOFCdIWEGyKyG/tB4KvxM9zoWuBRDxW+T+mvIb0=", - "owner": "justinwoo", - "repo": "easy-purescript-nix", - "rev": "117fd96acb69d7d1727df95b6fde9d8715e031fc", + "lastModified": 1672831974, + "narHash": "sha256-z9k3MfslLjWQfnjBtEtJZdq3H7kyi2kQtUThfTgdRk0=", + "owner": "input-output-hk", + "repo": "flake-compat", + "rev": "45f2638735f8cdc40fe302742b79f248d23eb368", "type": "github" }, "original": { - "owner": "justinwoo", - "repo": "easy-purescript-nix", + "owner": "input-output-hk", + "ref": "hkm/gitlab-fix", + "repo": "flake-compat", "type": "github" } }, - "flake-compat": { + "flake-compat_2": { "flake": false, "locked": { - "lastModified": 1672831974, - "narHash": "sha256-z9k3MfslLjWQfnjBtEtJZdq3H7kyi2kQtUThfTgdRk0=", - "owner": "input-output-hk", + "lastModified": 1765121682, + "narHash": "sha256-4VBOP18BFeiPkyhy9o4ssBNQEvfvv1kXkasAYd0+rrA=", + "owner": "edolstra", "repo": "flake-compat", - "rev": "45f2638735f8cdc40fe302742b79f248d23eb368", + "rev": "65f23138d8d09a92e30f1e5c87611b23ef451bf3", "type": "github" }, "original": { - "owner": "input-output-hk", - "ref": "hkm/gitlab-fix", + "owner": "edolstra", "repo": "flake-compat", "type": "github" } @@ -139,11 +122,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -175,11 +158,11 @@ "systems": "systems_3" }, "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -188,68 +171,47 @@ "type": "github" } }, - "ghc-8.6.5-iohk": { + "hackage": { "flake": false, "locked": { - "lastModified": 1600920045, - "narHash": "sha256-DO6kxJz248djebZLpSzTGD6s8WRpNI9BTwUeOf5RwY8=", + "lastModified": 1781226159, + "narHash": "sha256-BD3MeXxK03Tnh1KxVKMYlU+H8hks+dVMOQLeriAkK8M=", "owner": "input-output-hk", - "repo": "ghc", - "rev": "95713a6ecce4551240da7c96b6176f980af75cae", + "repo": "hackage.nix", + "rev": "cc1f434e3407d58970261cbff2e4a5dab00b803b", "type": "github" }, "original": { "owner": "input-output-hk", - "ref": "release/8.6.5-iohk", - "repo": "ghc", + "repo": "hackage.nix", "type": "github" } }, - "ghc910X": { - "flake": false, - "locked": { - "lastModified": 1709693152, - "narHash": "sha256-j7K/oZLy1ZZIpOsjq101IF7cz/i/UxY1ofIeNUfuuXc=", - "ref": "ghc-9.10", - "rev": "21e3f3250e88640087a1a60bee2cc113bf04509f", - "revCount": 62524, - "submodules": true, - "type": "git", - "url": "https://gitlab.haskell.org/ghc/ghc" - }, - "original": { - "ref": "ghc-9.10", - "submodules": true, - "type": "git", - "url": "https://gitlab.haskell.org/ghc/ghc" - } - }, - "ghc911": { + "hackage-for-stackage": { "flake": false, "locked": { - "lastModified": 1710286031, - "narHash": "sha256-fz71zsU/ZukFMUsRNk2Ro3xTNMKsNrpvQtRtPqRI60c=", - "ref": "refs/heads/master", - "rev": "e6bfb85c842edca36754bb8914e725fbaa1a83a6", - "revCount": 62586, - "submodules": true, - "type": "git", - "url": "https://gitlab.haskell.org/ghc/ghc" + "lastModified": 1781226148, + "narHash": "sha256-95My9dq0a6wCV1v/78OJ+NUKBdD1TwyZomuTc/AxJFg=", + "owner": "input-output-hk", + "repo": "hackage.nix", + "rev": "6233e06fc1f6345f27324f55c485561848d784db", + "type": "github" }, "original": { - "submodules": true, - "type": "git", - "url": "https://gitlab.haskell.org/ghc/ghc" + "owner": "input-output-hk", + "ref": "for-stackage", + "repo": "hackage.nix", + "type": "github" } }, - "hackage": { + "hackage-internal": { "flake": false, "locked": { - "lastModified": 1710807758, - "narHash": "sha256-lQY4KSZQlMyKizC+Xbsl29hxNPHV52pRTDZac+vPaeU=", + "lastModified": 1750307553, + "narHash": "sha256-iiafNoeLHwlSLQTyvy8nPe2t6g5AV4PPcpMeH/2/DLs=", "owner": "input-output-hk", "repo": "hackage.nix", - "rev": "dc3cafd9dbaeebe37c96613a9d145e162cadcf79", + "rev": "f7867baa8817fab296528f4a4ec39d1c7c4da4f3", "type": "github" }, "original": { @@ -261,48 +223,50 @@ "haskellNix": { "inputs": { "HTTP": "HTTP", - "cabal-32": "cabal-32", "cabal-34": "cabal-34", "cabal-36": "cabal-36", "cardano-shell": "cardano-shell", "flake-compat": "flake-compat", - "ghc-8.6.5-iohk": "ghc-8.6.5-iohk", - "ghc910X": "ghc910X", - "ghc911": "ghc911", "hackage": "hackage", + "hackage-for-stackage": "hackage-for-stackage", + "hackage-internal": "hackage-internal", + "hls": "hls", "hls-1.10": "hls-1.10", "hls-2.0": "hls-2.0", + "hls-2.10": "hls-2.10", + "hls-2.11": "hls-2.11", + "hls-2.12": "hls-2.12", "hls-2.2": "hls-2.2", "hls-2.3": "hls-2.3", "hls-2.4": "hls-2.4", "hls-2.5": "hls-2.5", "hls-2.6": "hls-2.6", + "hls-2.7": "hls-2.7", + "hls-2.8": "hls-2.8", + "hls-2.9": "hls-2.9", "hpc-coveralls": "hpc-coveralls", - "hydra": "hydra", "iserv-proxy": "iserv-proxy", - "nix-tools-static": "nix-tools-static", "nixpkgs": [ "pslua", "haskellNix", "nixpkgs-unstable" ], - "nixpkgs-2003": "nixpkgs-2003", - "nixpkgs-2105": "nixpkgs-2105", - "nixpkgs-2111": "nixpkgs-2111", - "nixpkgs-2205": "nixpkgs-2205", - "nixpkgs-2211": "nixpkgs-2211", "nixpkgs-2305": "nixpkgs-2305", "nixpkgs-2311": "nixpkgs-2311", + "nixpkgs-2405": "nixpkgs-2405", + "nixpkgs-2411": "nixpkgs-2411", + "nixpkgs-2505": "nixpkgs-2505", + "nixpkgs-2511": "nixpkgs-2511", "nixpkgs-unstable": "nixpkgs-unstable", "old-ghc-nix": "old-ghc-nix", "stackage": "stackage" }, "locked": { - "lastModified": 1710809412, - "narHash": "sha256-uLn/+4rBqk/+y5OPqe0LqUZHW7uEnWiljO/oGEx3L7c=", + "lastModified": 1781228148, + "narHash": "sha256-Qu6Zml4PHTgV1vs5RuLZzHuNOZYbeOtjeQq8yLIUbTU=", "owner": "input-output-hk", "repo": "haskell.nix", - "rev": "bc43eea89699683d1659eed3c3cc787e432ebfe7", + "rev": "fe9f2b0bfc6fc1bcc15eba41c729228135cda6ed", "type": "github" }, "original": { @@ -311,6 +275,22 @@ "type": "github" } }, + "hls": { + "flake": false, + "locked": { + "lastModified": 1741604408, + "narHash": "sha256-tuq3+Ip70yu89GswZ7DSINBpwRprnWnl6xDYnS4GOsc=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "682d6894c94087da5e566771f25311c47e145359", + "type": "github" + }, + "original": { + "owner": "haskell", + "repo": "haskell-language-server", + "type": "github" + } + }, "hls-1.10": { "flake": false, "locked": { @@ -345,6 +325,57 @@ "type": "github" } }, + "hls-2.10": { + "flake": false, + "locked": { + "lastModified": 1743069404, + "narHash": "sha256-q4kDFyJDDeoGqfEtrZRx4iqMVEC2MOzCToWsFY+TOzY=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "2318c61db3a01e03700bd4b05665662929b7fe8b", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.10.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.11": { + "flake": false, + "locked": { + "lastModified": 1747306193, + "narHash": "sha256-/MmtpF8+FyQlwfKHqHK05BdsxC9LHV70d/FiMM7pzBM=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "46ef4523ea4949f47f6d2752476239f1c6d806fe", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.11.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, + "hls-2.12": { + "flake": false, + "locked": { + "lastModified": 1758709460, + "narHash": "sha256-xkI8MIIVEVARskfWbGAgP5sHG/lyeKnkm0LIOJ19X5w=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "7d983de4fa7ff54369f6dd31444bdb9869aec83e", + "type": "github" + }, + "original": { + "owner": "haskell", + "ref": "2.12.0.0", + "repo": "haskell-language-server", + "type": "github" + } + }, "hls-2.2": { "flake": false, "locked": { @@ -430,124 +461,97 @@ "type": "github" } }, - "hpc-coveralls": { + "hls-2.7": { "flake": false, "locked": { - "lastModified": 1607498076, - "narHash": "sha256-8uqsEtivphgZWYeUo5RDUhp6bO9j2vaaProQxHBltQk=", - "owner": "sevanspowell", - "repo": "hpc-coveralls", - "rev": "14df0f7d229f4cd2e79f8eabb1a740097fdfa430", + "lastModified": 1708965829, + "narHash": "sha256-LfJ+TBcBFq/XKoiNI7pc4VoHg4WmuzsFxYJ3Fu+Jf+M=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "50322b0a4aefb27adc5ec42f5055aaa8f8e38001", "type": "github" }, "original": { - "owner": "sevanspowell", - "repo": "hpc-coveralls", + "owner": "haskell", + "ref": "2.7.0.0", + "repo": "haskell-language-server", "type": "github" } }, - "hydra": { - "inputs": { - "nix": "nix", - "nixpkgs": [ - "pslua", - "haskellNix", - "hydra", - "nix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1671755331, - "narHash": "sha256-hXsgJj0Cy0ZiCiYdW2OdBz5WmFyOMKuw4zyxKpgUKm4=", - "owner": "NixOS", - "repo": "hydra", - "rev": "f48f00ee6d5727ae3e488cbf9ce157460853fea8", - "type": "github" - }, - "original": { - "id": "hydra", - "type": "indirect" - } - }, - "iserv-proxy": { + "hls-2.8": { "flake": false, "locked": { - "lastModified": 1708894040, - "narHash": "sha256-Rv+PajrnuJ6AeyhtqzMN+bcR8z9+aEnrUass+N951CQ=", - "owner": "stable-haskell", - "repo": "iserv-proxy", - "rev": "2f2a318fd8837f8063a0d91f329aeae29055fba9", + "lastModified": 1715153580, + "narHash": "sha256-Vi/iUt2pWyUJlo9VrYgTcbRviWE0cFO6rmGi9rmALw0=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "dd1be1beb16700de59e0d6801957290bcf956a0a", "type": "github" }, "original": { - "owner": "stable-haskell", - "ref": "iserv-syms", - "repo": "iserv-proxy", + "owner": "haskell", + "ref": "2.8.0.0", + "repo": "haskell-language-server", "type": "github" } }, - "lowdown-src": { + "hls-2.9": { "flake": false, "locked": { - "lastModified": 1633514407, - "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", - "owner": "kristapsdz", - "repo": "lowdown", - "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "lastModified": 1719993701, + "narHash": "sha256-wy348++MiMm/xwtI9M3vVpqj2qfGgnDcZIGXw8sF1sA=", + "owner": "haskell", + "repo": "haskell-language-server", + "rev": "90319a7e62ab93ab65a95f8f2bcf537e34dae76a", "type": "github" }, "original": { - "owner": "kristapsdz", - "repo": "lowdown", + "owner": "haskell", + "ref": "2.9.0.1", + "repo": "haskell-language-server", "type": "github" } }, - "nix": { - "inputs": { - "lowdown-src": "lowdown-src", - "nixpkgs": "nixpkgs_2", - "nixpkgs-regression": "nixpkgs-regression" - }, + "hpc-coveralls": { + "flake": false, "locked": { - "lastModified": 1661606874, - "narHash": "sha256-9+rpYzI+SmxJn+EbYxjGv68Ucp22bdFUSy/4LkHkkDQ=", - "owner": "NixOS", - "repo": "nix", - "rev": "11e45768b34fdafdcf019ddbd337afa16127ff0f", + "lastModified": 1607498076, + "narHash": "sha256-8uqsEtivphgZWYeUo5RDUhp6bO9j2vaaProQxHBltQk=", + "owner": "sevanspowell", + "repo": "hpc-coveralls", + "rev": "14df0f7d229f4cd2e79f8eabb1a740097fdfa430", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "2.11.0", - "repo": "nix", + "owner": "sevanspowell", + "repo": "hpc-coveralls", "type": "github" } }, - "nix-tools-static": { + "iserv-proxy": { "flake": false, "locked": { - "lastModified": 1706266250, - "narHash": "sha256-9t+GRk3eO9muCtKdNAwBtNBZ5dH1xHcnS17WaQyftwA=", - "owner": "input-output-hk", - "repo": "haskell-nix-example", - "rev": "580cb6db546a7777dad3b9c0fa487a366c045c4e", + "lastModified": 1778457436, + "narHash": "sha256-bzZAHGzwcQGzBTipJuUs9tvMGO28kp0373zqnpn0g5A=", + "owner": "stable-haskell", + "repo": "iserv-proxy", + "rev": "8cdc446f8e2d91b120ecc075063e9475d387df52", "type": "github" }, "original": { - "owner": "input-output-hk", - "ref": "nix", - "repo": "haskell-nix-example", + "owner": "stable-haskell", + "ref": "iserv-syms", + "repo": "iserv-proxy", "type": "github" } }, "nixpkgs": { "locked": { - "lastModified": 1711703276, - "narHash": "sha256-iMUFArF0WCatKK6RzfUJknjem0H9m4KgorO/p3Dopkk=", + "lastModified": 1781074563, + "narHash": "sha256-md8WlXOlfnIeHeOScMTTHFyf2d6iaTwPl2apR5EQ3P4=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d8fe5e6c92d0d190646fb9f1056741a229980089", + "rev": "9ae611a455b90cf061d8f332b977e387bda8e1ca", "type": "github" }, "original": { @@ -556,162 +560,114 @@ "type": "indirect" } }, - "nixpkgs-2003": { - "locked": { - "lastModified": 1620055814, - "narHash": "sha256-8LEHoYSJiL901bTMVatq+rf8y7QtWuZhwwpKE2fyaRY=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "1db42b7fe3878f3f5f7a4f2dc210772fd080e205", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-20.03-darwin", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-2105": { - "locked": { - "lastModified": 1659914493, - "narHash": "sha256-lkA5X3VNMKirvA+SUzvEhfA7XquWLci+CGi505YFAIs=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "022caabb5f2265ad4006c1fa5b1ebe69fb0c3faf", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-21.05-darwin", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-2111": { + "nixpkgs-2305": { "locked": { - "lastModified": 1659446231, - "narHash": "sha256-hekabNdTdgR/iLsgce5TGWmfIDZ86qjPhxDg/8TlzhE=", + "lastModified": 1705033721, + "narHash": "sha256-K5eJHmL1/kev6WuqyqqbS1cdNnSidIZ3jeqJ7GbrYnQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "eabc38219184cc3e04a974fe31857d8e0eac098d", + "rev": "a1982c92d8980a0114372973cbdfe0a307f1bdea", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixpkgs-21.11-darwin", + "ref": "nixpkgs-23.05-darwin", "repo": "nixpkgs", "type": "github" } }, - "nixpkgs-2205": { + "nixpkgs-2311": { "locked": { - "lastModified": 1685573264, - "narHash": "sha256-Zffu01pONhs/pqH07cjlF10NnMDLok8ix5Uk4rhOnZQ=", + "lastModified": 1719957072, + "narHash": "sha256-gvFhEf5nszouwLAkT9nWsDzocUTqLWHuL++dvNjMp9I=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "380be19fbd2d9079f677978361792cb25e8a3635", + "rev": "7144d6241f02d171d25fba3edeaf15e0f2592105", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixpkgs-22.05-darwin", + "ref": "nixpkgs-23.11-darwin", "repo": "nixpkgs", "type": "github" } }, - "nixpkgs-2211": { + "nixpkgs-2405": { "locked": { - "lastModified": 1688392541, - "narHash": "sha256-lHrKvEkCPTUO+7tPfjIcb7Trk6k31rz18vkyqmkeJfY=", + "lastModified": 1735564410, + "narHash": "sha256-HB/FA0+1gpSs8+/boEavrGJH+Eq08/R2wWNph1sM1Dg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "ea4c80b39be4c09702b0cb3b42eab59e2ba4f24b", + "rev": "1e7a8f391f1a490460760065fa0630b5520f9cf8", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixpkgs-22.11-darwin", + "ref": "nixpkgs-24.05-darwin", "repo": "nixpkgs", "type": "github" } }, - "nixpkgs-2305": { + "nixpkgs-2411": { "locked": { - "lastModified": 1701362232, - "narHash": "sha256-GVdzxL0lhEadqs3hfRLuj+L1OJFGiL/L7gCcelgBlsw=", + "lastModified": 1751290243, + "narHash": "sha256-kNf+obkpJZWar7HZymXZbW+Rlk3HTEIMlpc6FCNz0Ds=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d2332963662edffacfddfad59ff4f709dde80ffe", + "rev": "5ab036a8d97cb9476fbe81b09076e6e91d15e1b6", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixpkgs-23.05-darwin", + "ref": "nixpkgs-24.11-darwin", "repo": "nixpkgs", "type": "github" } }, - "nixpkgs-2311": { + "nixpkgs-2505": { "locked": { - "lastModified": 1701386440, - "narHash": "sha256-xI0uQ9E7JbmEy/v8kR9ZQan6389rHug+zOtZeZFiDJk=", + "lastModified": 1764560356, + "narHash": "sha256-M5aFEFPppI4UhdOxwdmceJ9bDJC4T6C6CzCK1E2FZyo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "293822e55ec1872f715a66d0eda9e592dc14419f", + "rev": "6c8f0cca84510cc79e09ea99a299c9bc17d03cb6", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixpkgs-23.11-darwin", + "ref": "nixpkgs-25.05-darwin", "repo": "nixpkgs", "type": "github" } }, - "nixpkgs-regression": { + "nixpkgs-2511": { "locked": { - "lastModified": 1643052045, - "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "lastModified": 1775749320, + "narHash": "sha256-msT6frWJSQ2WR+0cpk+KPcZdLTLagUIsJwQwIX9JNSo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "rev": "74b87959b2d16f59f54d8559cf3cf26b9d907949", "type": "github" }, "original": { "owner": "NixOS", + "ref": "nixpkgs-25.11-darwin", "repo": "nixpkgs", - "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", "type": "github" } }, "nixpkgs-unstable": { "locked": { - "lastModified": 1694822471, - "narHash": "sha256-6fSDCj++lZVMZlyqOe9SIOL8tYSBz1bI8acwovRwoX8=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "47585496bcb13fb72e4a90daeea2f434e2501998", - "type": "github" - }, - "original": { - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "47585496bcb13fb72e4a90daeea2f434e2501998", - "type": "github" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1657693803, - "narHash": "sha256-G++2CJ9u0E7NNTAi9n5G8TdDmGJXcIjkJ3NF8cetQB8=", + "lastModified": 1775888245, + "narHash": "sha256-nwASzrRDD1JBEu/o8ekKYEXm/oJW6EMCzCRdrwcLe90=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "365e1b3a859281cf11b94f87231adeabbdd878a2", + "rev": "13043924aaa7375ce482ebe2494338e058282925", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-22.05-small", + "ref": "nixpkgs-unstable", "repo": "nixpkgs", "type": "github" } @@ -745,35 +701,56 @@ ] }, "locked": { - "lastModified": 1711822377, - "narHash": "sha256-3cqI8lL04RZA7RrLImYzGsOOrE2Z5UpXzEvnvT9HqmA=", - "owner": "Unisay", + "lastModified": 1781449244, + "narHash": "sha256-evfLWZ+i55b0cSfd/jgU5mXuBg2KDEP5WhEj2iuzIiM=", + "owner": "purescript-lua", "repo": "purescript-lua", - "rev": "ccc2df825af5a6abe33d663ecdd8eb3ab9feb63a", + "rev": "94c13cecd1146494de56196a7badb3b1374d364d", "type": "github" }, "original": { - "owner": "Unisay", + "owner": "purescript-lua", "repo": "purescript-lua", "type": "github" } }, + "purescript-overlay": { + "inputs": { + "flake-compat": "flake-compat_2", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1780972251, + "narHash": "sha256-PwHYPpfLP+WjSwj765zJ1N0Xp4ET3+8vRqxJ031ua3M=", + "owner": "thomashoneyman", + "repo": "purescript-overlay", + "rev": "1cf88ab9d83596db0e0c0d304a16809c410e2917", + "type": "github" + }, + "original": { + "owner": "thomashoneyman", + "repo": "purescript-overlay", + "type": "github" + } + }, "root": { "inputs": { - "easyps": "easyps", "flake-utils": "flake-utils", "nixpkgs": "nixpkgs", - "pslua": "pslua" + "pslua": "pslua", + "purescript-overlay": "purescript-overlay" } }, "stackage": { "flake": false, "locked": { - "lastModified": 1710461339, - "narHash": "sha256-l2/ekwA4Z4NjiaCZytZrBTag2VaAOBUvsNttsH6kH4E=", + "lastModified": 1781225038, + "narHash": "sha256-eG1GGyHdEI8y2chHOmgx0rypbHyjIn9ViawYmN2CEd4=", "owner": "input-output-hk", "repo": "stackage.nix", - "rev": "724970b7dc837bf0d813b91f821948c3c5cc719f", + "rev": "e2af13c92a93da96529a874428acae18d085bafe", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index fbde00f..5198865 100644 --- a/flake.nix +++ b/flake.nix @@ -4,34 +4,45 @@ inputs = { flake-utils.url = "github:numtide/flake-utils"; nixpkgs.url = "nixpkgs/nixos-unstable"; - easyps = { - url = "github:justinwoo/easy-purescript-nix"; - flake = false; + purescript-overlay = { + url = "github:thomashoneyman/purescript-overlay"; + inputs.nixpkgs.follows = "nixpkgs"; }; - pslua.url = "github:Unisay/purescript-lua"; + pslua.url = "github:purescript-lua/purescript-lua"; }; - outputs = { self, nixpkgs, flake-utils, easyps, pslua }: + outputs = { self, nixpkgs, flake-utils, purescript-overlay, pslua }: flake-utils.lib.eachDefaultSystem (system: let - p = nixpkgs.legacyPackages.${system}; - e = import easyps { pkgs = p; }; - l = p.lua51Packages; + pkgs = import nixpkgs { + inherit system; + overlays = [ purescript-overlay.overlays.default ]; + }; in { - devShell = p.mkShell { - buildInputs = [ - p.dhall - l.lua - l.luacheck - p.luaformatter - p.nixfmt - pslua.packages.${system}.default - e.purs-0_15_15 - e.purs-tidy - e.spago - p.treefmt - ]; + devShell = pkgs.mkShell { + buildInputs = with pkgs; [ + dhall + lua51Packages.lua + lua51Packages.luacheck + luaformatter + nixfmt-rfc-style + pslua.packages.${system}.default + purs-bin.purs-0_15_16 + spago-bin.spago-0_21_0 + treefmt + ]; }; }); -} + # --- Flake Local Nix Configuration ---------------------------- + nixConfig = { + extra-substituters = [ + "https://cache.iog.io" + "https://purescript-lua.cachix.org" + ]; + extra-trusted-public-keys = [ + "hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=" + "purescript-lua.cachix.org-1:yLs4ei2HtnuPtzLekOrW3xdfm95+Etw15gwgyIGTayA=" + ]; + }; +} diff --git a/package.json b/package.json deleted file mode 100644 index ee9cb3a..0000000 --- a/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "private": true, - "scripts": { - "clean": "rimraf output && rimraf .pulp-cache", - "build": "eslint src && pulp build -- --censor-lib --strict", - "test": "pulp test", - "bench:build": "pulp build --include bench -- --censor-lib --strict", - "bench:run": "node --expose-gc -e \"require('./output/Bench.Main/index.js').main()\"", - "bench": "npm run bench:build && npm run bench:run" - }, - "devDependencies": { - "eslint": "^7.15.0", - "pulp": "16.0.0-0", - "purescript-psa": "^0.8.2", - "rimraf": "^3.0.2" - } -} diff --git a/packages.dhall b/packages.dhall index cd08d59..03ddf10 100644 --- a/packages.dhall +++ b/packages.dhall @@ -3,7 +3,7 @@ let upstream-ps = sha256:ae8a25645e81ff979beb397a21e5d272fae7c9ebdb021a96b1b431388c8f3c34 let upstream-lua = - https://github.com/Unisay/purescript-lua-package-sets/releases/download/psc-0.15.15-20240338/packages.dhall + https://github.com/purescript-lua/purescript-lua-package-sets/releases/download/psc-0.15.15-20240338/packages.dhall sha256:8a7527f82c9a8a9ec2c1c945bf45a75faa4bf847609b8ada570c1cd969bca7eb in upstream-ps // upstream-lua diff --git a/scripts/build b/scripts/build index 6bee995..f0ef609 100755 --- a/scripts/build +++ b/scripts/build @@ -1,4 +1,5 @@ #!/usr/bin/env bash +set -euo pipefail echo "Building..." diff --git a/scripts/test b/scripts/test new file mode 100755 index 0000000..67bf2dc --- /dev/null +++ b/scripts/test @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "Running regression tests..." + +lua test/regression/array_st.lua diff --git a/src/Data/Array.lua b/src/Data/Array.lua index 5fc0c23..733d37d 100644 --- a/src/Data/Array.lua +++ b/src/Data/Array.lua @@ -41,7 +41,7 @@ return { length = (function(xs) return #xs end), unconsImpl = (function(empty, next, xs) if #xs == 0 then return empty({}) end - return next(xs[1])(table.pack(table.unpack(xs, 2))) + return next(xs[1])({ unpack(xs, 2) }) end), indexImpl = (function(just, nothing, xs, i) if i < 0 or i >= #xs then @@ -67,19 +67,19 @@ return { end), _insertAt = (function(just, nothing, i, a, l) if i < 0 or i > #l then return nothing end - local l1 = table.pack(table.unpack(l)) + local l1 = { unpack(l) } table.insert(l1, i + 1, a) return just(l1) end), _deleteAt = (function(just, nothing, i, l) if i < 0 or i >= #l then return nothing end - local l1 = table.pack(table.unpack(l)) + local l1 = { unpack(l) } table.remove(l1, i + 1) return just(l1) end), _updateAt = (function(just, nothing, i, f, l) if i < 0 or i >= #l then return nothing end - local l1 = table.pack(table.unpack(l)) + local l1 = { unpack(l) } l1[i + 1] = f(l1[i + 1]) return just(l1) end), @@ -185,8 +185,8 @@ return { return function(compare, fromOrdering, xs) if #xs < 2 then return xs end - local out = table.pack(table.unpack(xs)) - local slice = table.pack(table.unpack(xs)) + local out = { unpack(xs) } + local slice = { unpack(xs) } mergeFromTo(compare, fromOrdering, out, slice, 0, #xs) return out end diff --git a/src/Data/Array/ST.lua b/src/Data/Array/ST.lua index a7a14e3..bac8b15 100644 --- a/src/Data/Array/ST.lua +++ b/src/Data/Array/ST.lua @@ -1,4 +1,20 @@ -local function copyImpl(xs) return function() return table.move(xs, 1, #xs, 1, {}) end end +-- Lua 5.1 has no table.move, so provide an overlap-safe equivalent with the +-- same semantics as Lua 5.3's table.move(a1, f, e, t, a2): copy a1[f..e] to +-- a2 starting at t, iterating backwards when the source and destination overlap +-- so a forward shift to the right does not clobber elements before reading them. +local function move(a1, f, e, t, a2) + a2 = a2 or a1 + if e >= f then + if a1 ~= a2 or t <= f or t > e then + for i = 0, e - f do a2[t + i] = a1[f + i] end + else + for i = e - f, 0, -1 do a2[t + i] = a1[f + i] end + end + end + return a2 +end + +local function copyImpl(xs) return function() return move(xs, 1, #xs, 1, {}) end end return { new = (function() return {} end), @@ -25,7 +41,7 @@ return { end end), pushAllImpl = (function(as, xs) - local r = table.move(as, 1, #as, #xs + 1, xs) + local r = move(as, 1, #as, #xs + 1, xs) return #r end), shiftImpl = (function(just, nothing, xs) @@ -36,10 +52,10 @@ return { end end), unshiftAllImpl = (function(as, xs) - local r = table.move(as, 1, #as, 1, xs) + local r = move(as, 1, #as, 1, xs) return #r end), - spliceImpl = (function(i, howMany, bs, xs) return table.move(xs, i + howMany + 1, #xs, i + #bs + 1, xs) end), + spliceImpl = (function(i, howMany, bs, xs) return move(xs, i + howMany + 1, #xs, i + #bs + 1, xs) end), unsafeFreezeImpl = (function(xs) return xs end), unsafeThawImpl = (function(xs) return xs end), freeze = (copyImpl), @@ -84,7 +100,7 @@ return { return function(compare, fromOrdering, xs) if #xs < 2 then return xs end - mergeFromTo(compare, fromOrdering, xs, table.move(xs, 1, #xs, 1, {}), 0, #xs) + mergeFromTo(compare, fromOrdering, xs, move(xs, 1, #xs, 1, {}), 0, #xs) return xs end end)()), diff --git a/test/regression/array_st.lua b/test/regression/array_st.lua new file mode 100644 index 0000000..33d3a05 --- /dev/null +++ b/test/regression/array_st.lua @@ -0,0 +1,156 @@ +-- Regression guard for the Lua 5.1 FFI rewrite of the array foreign modules. +-- +-- Lua 5.1 has no table.pack / table.unpack / table.move, so Data/Array.lua now +-- uses `{ unpack(...) }` and Data/Array/ST.lua uses a hand-written overlap-safe +-- `move`. These checks pin the behaviour of exactly those rewritten paths, +-- especially the two overlap directions of `move` that a naive copy gets wrong. +-- +-- Run from the repo root: `lua test/regression/array_st.lua`. + +local ST = dofile("src/Data/Array/ST.lua") +local A = dofile("src/Data/Array.lua") + +local failures = 0 + +local function eqArray(a, b) + if #a ~= #b then return false end + for i = 1, #a do + if a[i] ~= b[i] then return false end + end + return true +end + +local function show(a) + local parts = {} + for i = 1, #a do parts[i] = tostring(a[i]) end + return "{" .. table.concat(parts, ", ") .. "}" +end + +local function checkArray(name, got, want) + if eqArray(got, want) then + print("ok - " .. name) + else + failures = failures + 1 + print("FAIL - " .. name .. ": got " .. show(got) .. ", want " .. show(want)) + end +end + +local function check(name, cond, detail) + if cond then + print("ok - " .. name) + else + failures = failures + 1 + print("FAIL - " .. name .. ": " .. tostring(detail)) + end +end + +-------------------------------------------------------------------------------- +-- Data/Array/ST.lua: move-based operations ----------------------------------- + +-- freeze (= copyImpl, the move-to-fresh-table path) is an independent copy. +do + local xs = { 1, 2, 3 } + local copy = ST.freeze(xs)() + checkArray("freeze copies", copy, { 1, 2, 3 }) + copy[1] = 99 + check("freeze is independent", xs[1] == 1, "source mutated to " .. tostring(xs[1])) +end + +-- pushAllImpl appends and reports the new length. +do + local xs = { 1, 2 } + local n = ST.pushAllImpl({ 3, 4, 5 }, xs) + checkArray("pushAllImpl appends", xs, { 1, 2, 3, 4, 5 }) + check("pushAllImpl returns length", n == 5, "got " .. tostring(n)) +end + +-- spliceImpl shifting the tail LEFT (destination before source: forward copy). +do + local xs = { 10, 20, 30, 40, 50 } + ST.spliceImpl(1, 1, {}, xs) -- move(xs, 3, 5, 2, xs) + checkArray("spliceImpl shift-left overlap", xs, { 10, 30, 40, 50, 50 }) +end + +-- spliceImpl shifting the tail RIGHT (destination inside source: must copy +-- backwards or it clobbers). This is the case a naive forward loop breaks. +do + local xs = { 1, 2, 3, 4 } + ST.spliceImpl(0, 0, { 9 }, xs) -- move(xs, 1, 4, 2, xs) + checkArray("spliceImpl shift-right overlap", xs, { 1, 1, 2, 3, 4 }) +end + +-- sortByImpl sorts in place (it copies through move internally). +do + local function compare(x) return function(y) + if x < y then return -1 elseif x > y then return 1 else return 0 end + end end + local function fromOrdering(o) return o end + local xs = { 3, 1, 2, 1 } + local sorted = ST.sortByImpl(compare, fromOrdering, xs) + checkArray("sortByImpl sorts ascending", sorted, { 1, 1, 2, 3 }) +end + +-- sortByImpl is stable: records with equal keys keep their input order. +do + local function byKey(x) return function(y) + if x.k < y.k then return -1 elseif x.k > y.k then return 1 else return 0 end + end end + local function fromOrdering(o) return o end + -- Two elements share key 1 ("a" before "b"); a stable sort keeps a before b. + local xs = { + { k = 1, id = "a" }, + { k = 0, id = "c" }, + { k = 1, id = "b" }, + } + ST.sortByImpl(byKey, fromOrdering, xs) + local keys, ids = {}, {} + for i = 1, #xs do keys[i], ids[i] = xs[i].k, xs[i].id end + checkArray("sortByImpl orders by key", keys, { 0, 1, 1 }) + check("sortByImpl is stable on equal keys", ids[1] == "c" and ids[2] == "a" and ids[3] == "b", + "got order " .. table.concat(ids, ",")) +end + +-------------------------------------------------------------------------------- +-- Data/Array.lua: unpack-based operations ------------------------------------ + +local function just(x) return { tag = "just", value = x } end +local nothing = { tag = "nothing" } + +-- unconsImpl splits head / tail; the tail is `{ unpack(xs, 2) }`. +do + local function empty(_) return { tag = "empty" } end + local function next(h) return function(t) return { tag = "cons", head = h, tail = t } end end + local r = A.unconsImpl(empty, next, { 1, 2, 3 }) + check("unconsImpl head", r.head == 1, "got " .. tostring(r.head)) + checkArray("unconsImpl tail", r.tail, { 2, 3 }) + check("unconsImpl empty", A.unconsImpl(empty, next, {}).tag == "empty", "non-empty result") +end + +-- _insertAt copies via unpack, then inserts; the source is left untouched. +do + local src = { 1, 2, 3 } + local r = A._insertAt(just, nothing, 1, 99, src) + checkArray("_insertAt result", r.value, { 1, 99, 2, 3 }) + checkArray("_insertAt keeps source", src, { 1, 2, 3 }) + check("_insertAt out of range", A._insertAt(just, nothing, 9, 0, src).tag == "nothing", "expected nothing") +end + +-- _deleteAt copies via unpack, then removes. +do + local r = A._deleteAt(just, nothing, 1, { 1, 2, 3 }) + checkArray("_deleteAt result", r.value, { 1, 3 }) +end + +-- _updateAt copies via unpack, then updates one slot. +do + local function times100(x) return x * 100 end + local r = A._updateAt(just, nothing, 1, times100, { 1, 2, 3 }) + checkArray("_updateAt result", r.value, { 1, 200, 3 }) +end + +-------------------------------------------------------------------------------- + +if failures > 0 then + error(failures .. " regression check(s) failed") +end +print("purescript-lua-arrays: all FFI regression checks passed")