diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c69237a..dff0666 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,32 +4,22 @@ 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" - - - name: Install dependencies - run: | - npm install -g bower - npm install - bower install --production + extra_nix_config: | + accept-flake-config = true + 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: Build source - run: npm run-script build + - name: Test + run: nix develop -c ./scripts/test - - name: Run tests - run: | - bower install - npm run-script test --if-present + - name: Luacheck + run: nix develop -c luacheck --quiet --std min src/ diff --git a/flake.lock b/flake.lock index 780c119..b1b25c5 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": { @@ -104,11 +87,11 @@ "easyps": { "flake": false, "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": { @@ -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": { + "hackage-for-stackage": { "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": { - "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", - "type": "github" - } - }, - "hydra": { - "inputs": { - "nix": "nix", - "nixpkgs": [ - "pslua", - "haskellNix", - "hydra", - "nix", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1671755331, - "narHash": "sha256-hXsgJj0Cy0ZiCiYdW2OdBz5WmFyOMKuw4zyxKpgUKm4=", - "owner": "NixOS", - "repo": "hydra", - "rev": "f48f00ee6d5727ae3e488cbf9ce157460853fea8", + "owner": "haskell", + "ref": "2.7.0.0", + "repo": "haskell-language-server", "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": 1775888245, + "narHash": "sha256-nwASzrRDD1JBEu/o8ekKYEXm/oJW6EMCzCRdrwcLe90=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d8fe5e6c92d0d190646fb9f1056741a229980089", + "rev": "13043924aaa7375ce482ebe2494338e058282925", "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,11 +701,11 @@ ] }, "locked": { - "lastModified": 1711822377, - "narHash": "sha256-3cqI8lL04RZA7RrLImYzGsOOrE2Z5UpXzEvnvT9HqmA=", + "lastModified": 1781335909, + "narHash": "sha256-gg1K5vcNPYJWWyQz8f/fOxf/8UJG6rJnj2ofQ18tZqQ=", "owner": "Unisay", "repo": "purescript-lua", - "rev": "ccc2df825af5a6abe33d663ecdd8eb3ab9feb63a", + "rev": "be5956742c67a6a35ab59dbbacafc3eaf5d2fc8c", "type": "github" }, "original": { @@ -769,11 +725,11 @@ "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..f813607 100644 --- a/flake.nix +++ b/flake.nix @@ -27,7 +27,6 @@ p.nixfmt pslua.packages.${system}.default e.purs-0_15_15 - e.purs-tidy e.spago p.treefmt ]; diff --git a/packages.dhall b/packages.dhall index 0319420..f240736 100644 --- a/packages.dhall +++ b/packages.dhall @@ -1,9 +1,9 @@ let upstream-ps = - https://github.com/purescript/package-sets/releases/download/psc-0.15.15-20240320/packages.dhall - sha256:ae8a25645e81ff979beb397a21e5d272fae7c9ebdb021a96b1b431388c8f3c34 + https://github.com/purescript/package-sets/releases/download/psc-0.15.15-20260605/packages.dhall + sha256:e48c9b283ca89ec994453459fb74c4b5b5a9432349f83a2e104f39dd869a0f6e let upstream-lua = - https://github.com/Unisay/purescript-lua-package-sets/releases/download/psc-0.15.15-20240342/packages.dhall - sha256:c7b900b5f26717504a50b14fdcc24796ce505801ad068c9e2496519e6042e139 + https://github.com/Unisay/purescript-lua-package-sets/releases/download/psc-0.15.15-20260613/packages.dhall + sha256:4cb4784187583587818384ca3c4930f8fe77b15796ff7d487f628ef4590d8058 in upstream-ps // upstream-lua diff --git a/scripts/test b/scripts/test index 990d2e2..789e5c1 100755 --- a/scripts/test +++ b/scripts/test @@ -1,11 +1,13 @@ #!/usr/bin/env bash +set -euo pipefail echo "Testing..." spago build --config spago-test.dhall --quiet -if lua -e 'dofile("dist/test.lua").main()'; then +if lua -e 'dofile("dist/test.lua").main()'; then echo "✅ Tests succeeded." else echo "❌ Tests failed." + exit 1 fi diff --git a/spago-test.dhall b/spago-test.dhall new file mode 100644 index 0000000..b598ded --- /dev/null +++ b/spago-test.dhall @@ -0,0 +1,15 @@ +let conf = ./spago.dhall + +in conf + // { sources = conf.sources # [ "test/**/*.purs" ] + , dependencies = + conf.dependencies # [ "assert", "console", "effect" ] + , backend = + '' + pslua \ + --foreign-path . \ + --ps-output output \ + --lua-output-file dist/test.lua \ + --entry Test.Main + '' + } diff --git a/src/Data/String/CodePoints.lua b/src/Data/String/CodePoints.lua index 0c3837d..b5f5d59 100644 --- a/src/Data/String/CodePoints.lua +++ b/src/Data/String/CodePoints.lua @@ -1,8 +1,140 @@ -return { - _toCodePointArray = (function (str) - error("Not implemented: _toCodePointArray") - end), - _fromCodePointArray = (function (codePoints) - error("Not implemented: _fromCodePointArray") - end), +-- In pslua a PureScript String is a Lua byte string holding UTF-8, +-- so code-point operations decode/encode UTF-8 directly. The PureScript +-- fallback arguments are written for UTF-16 code units and are wrong +-- under this representation; every export ignores them. +-- +-- Lua 5.1: no utf8 library, no bit operators - plain arithmetic only. + +-- Decodes the code point starting at byte position i. +-- Returns the code point and the position of the next one. +-- An invalid leading byte is returned as-is (one byte consumed). +local function decode(s, i) + local b1 = s:byte(i) + if b1 < 0x80 then return b1, i + 1 end + if b1 >= 0xC2 and b1 <= 0xDF then + local b2 = s:byte(i + 1) + if b2 and b2 >= 0x80 and b2 <= 0xBF then + return (b1 - 0xC0) * 0x40 + (b2 - 0x80), i + 2 + end + elseif b1 >= 0xE0 and b1 <= 0xEF then + local b2, b3 = s:byte(i + 1, i + 2) + if b2 and b2 >= 0x80 and b2 <= 0xBF + and b3 and b3 >= 0x80 and b3 <= 0xBF then + return (b1 - 0xE0) * 0x1000 + (b2 - 0x80) * 0x40 + (b3 - 0x80), i + 3 + end + elseif b1 >= 0xF0 and b1 <= 0xF4 then + local b2, b3, b4 = s:byte(i + 1, i + 3) + if b2 and b2 >= 0x80 and b2 <= 0xBF + and b3 and b3 >= 0x80 and b3 <= 0xBF + and b4 and b4 >= 0x80 and b4 <= 0xBF then + return (b1 - 0xF0) * 0x40000 + (b2 - 0x80) * 0x1000 + + (b3 - 0x80) * 0x40 + (b4 - 0x80), i + 4 + end + end + return b1, i + 1 +end + +-- Encodes a code point as a UTF-8 byte string. +local function encode(cp) + if cp < 0x80 then return string.char(cp) end + if cp < 0x800 then + return string.char(0xC0 + math.floor(cp / 0x40), 0x80 + cp % 0x40) + end + if cp < 0x10000 then + return string.char( + 0xE0 + math.floor(cp / 0x1000), + 0x80 + math.floor(cp / 0x40) % 0x40, + 0x80 + cp % 0x40 + ) + end + return string.char( + 0xF0 + math.floor(cp / 0x40000), + 0x80 + math.floor(cp / 0x1000) % 0x40, + 0x80 + math.floor(cp / 0x40) % 0x40, + 0x80 + cp % 0x40 + ) +end + +return { + _singleton = (function(_) + return function(cp) return encode(cp) end + end), + _fromCodePointArray = (function(_) + return function(cps) + local t = {} + for k = 1, #cps do t[k] = encode(cps[k]) end + return table.concat(t) + end + end), + _toCodePointArray = (function(_) + return function(_) + return function(s) + local t, k, i = {}, 0, 1 + while i <= #s do + local cp, j = decode(s, i) + k = k + 1 + t[k] = cp + i = j + end + return t + end + end + end), + _codePointAt = (function(_) + return function(just) + return function(nothing) + return function(_) + return function(n) + return function(s) + local k, i = 0, 1 + while i <= #s do + local cp, j = decode(s, i) + if k == n then return just(cp) end + k = k + 1 + i = j + end + return nothing + end + end + end + end + end + end), + _countPrefix = (function(_) + return function(_) + return function(pred) + return function(s) + local k, i = 0, 1 + while i <= #s do + local cp, j = decode(s, i) + if not pred(cp) then break end + k = k + 1 + i = j + end + return k + end + end + end + end), + _take = (function(_) + return function(n) + return function(s) + if n < 1 then return "" end + local k, i = 0, 1 + while i <= #s do + local _, j = decode(s, i) + k = k + 1 + i = j + if k == n then break end + end + return s:sub(1, i - 1) + end + end + end), + _unsafeCodePointAt0 = (function(_) + return function(s) + local cp = decode(s, 1) + return cp + end + end), } diff --git a/src/Data/String/CodePoints.purs b/src/Data/String/CodePoints.purs index 65e0b55..915023b 100644 --- a/src/Data/String/CodePoints.purs +++ b/src/Data/String/CodePoints.purs @@ -188,18 +188,16 @@ codePointAtFallback n s = case uncons s of -- | Nothing -- | ``` -- | +-- In pslua strings are UTF-8 byte strings (a code unit is a byte). The tail +-- must start exactly where the foreign decoder stopped reading the head, so +-- it is taken as `drop 1` rather than recomputed from the head's value: a +-- malformed leading byte decodes to itself and advances a single byte, and +-- recomputing the width from that value would skip too far and desynchronise +-- the rest of the string. uncons :: String -> Maybe { head :: CodePoint, tail :: String } -uncons s = case CU.length s of - 0 -> Nothing - 1 -> Just { head: CodePoint (fromEnum (Unsafe.charAt 0 s)), tail: "" } - _ -> - let - cu0 = fromEnum (Unsafe.charAt 0 s) - cu1 = fromEnum (Unsafe.charAt 1 s) - in - if isLead cu0 && isTrail cu1 - then Just { head: unsurrogate cu0 cu1, tail: CU.drop 2 s } - else Just { head: CodePoint cu0, tail: CU.drop 1 s } +uncons s + | CU.length s == 0 = Nothing + | otherwise = Just { head: unsafeCodePointAt0 s, tail: drop 1 s } -- | Returns the number of code points in the string. Operates in constant -- | space and in time linear to the length of the string. @@ -325,8 +323,12 @@ lastIndexOf' p i s = -- | "b �" -- | ``` -- | +-- Eta-expanded (rather than point-free `_take takeFallback`) so this binding +-- is a function: uncons now reaches take via drop, and the resulting +-- recursive group (uncons → drop → take → takeFallback → uncons) is only +-- well-formed if every member is a function rather than a plain value. take :: Int -> String -> String -take = _take takeFallback +take n s = _take takeFallback n s foreign import _take :: (Int -> String -> String) -> Int -> String -> String diff --git a/src/Data/String/CodeUnits.lua b/src/Data/String/CodeUnits.lua index c786f68..acde3c5 100644 --- a/src/Data/String/CodeUnits.lua +++ b/src/Data/String/CodeUnits.lua @@ -1,3 +1,8 @@ +-- PureScript indices are 0-based, Lua string positions are 1-based; +-- the exports below convert between the two. Pattern arguments are +-- literal strings, hence string.find in plain mode. Index clamping +-- mirrors the upstream JS implementation (String.prototype.indexOf, +-- lastIndexOf, slice and substring). return { fromCharArray = (function(a) return table.concat(a) end), toCharArray = (function(s) @@ -10,8 +15,8 @@ return { return function(nothing) return function(i) return function(s) - if i >= 1 and i <= #s then - return just(s:sub(i, i)) + if i >= 0 and i < #s then + return just(s:sub(i + 1, i + 1)) else return nothing end @@ -44,7 +49,7 @@ return { return function(s) local i = s:find(x, 1, true) if i then - return just(i) + return just(i - 1) else return nothing end @@ -57,9 +62,10 @@ return { return function(x) return function(startAt) return function(s) - local i = s:find(x, startAt, true) + if startAt < 0 or startAt > #s then return nothing end + local i = s:find(x, startAt + 1, true) if i then - return just(i) + return just(i - 1) else return nothing end @@ -74,7 +80,7 @@ return { return function(s) local i = s:reverse():find(x:reverse(), 1, true) if i then - return just(#s - i + 1) + return just(#s - i - #x + 1) else return nothing end @@ -87,9 +93,11 @@ return { return function(x) return function(startAt) return function(s) - local i = s:reverse():find(x:reverse(), #s - startAt + 1, true) + local from = math.max(0, math.min(startAt, #s)) + local init = math.max(1, #s - #x + 1 - from) + local i = s:reverse():find(x:reverse(), init, true) if i then - return just(#s - i + 1) + return just(#s - i - #x + 1) else return nothing end @@ -98,8 +106,23 @@ return { end end end), - take = (function(n) return function(s) return s:sub(1, n) end end), - drop = (function(n) return function(s) return s:sub(n + 1) end end), - slice = (function(b) return function(e) return function(s) return s:sub(b + 1, e) end end end), - splitAt = (function(i) return function(s) return {before = s:sub(1, i), after = s:sub(i + 1)} end end) + take = (function(n) return function(s) return s:sub(1, math.max(n, 0)) end end), + drop = (function(n) return function(s) return s:sub(math.max(n, 0) + 1) end end), + slice = (function(b) + return function(e) + return function(s) + local len = #s + local from = b < 0 and math.max(len + b, 0) or math.min(b, len) + local to = e < 0 and math.max(len + e, 0) or math.min(e, len) + if to <= from then return "" end + return s:sub(from + 1, to) + end + end + end), + splitAt = (function(i) + return function(s) + local k = math.max(i, 0) + return {before = s:sub(1, k), after = s:sub(k + 1)} + end + end) } diff --git a/src/Data/String/Common.lua b/src/Data/String/Common.lua index 0e7b606..93285ef 100644 --- a/src/Data/String/Common.lua +++ b/src/Data/String/Common.lua @@ -1,3 +1,7 @@ +-- Pattern and Replacement are literal strings in PureScript, so the +-- implementations below must not interpret them as Lua patterns: +-- string.find is used with its `plain` flag and results are spliced +-- together by hand. return { _localeCompare = (function(lt) return function(eq) @@ -7,15 +11,54 @@ return { end end), replace = (function(pattern) - return function(replacement) return function(string) return string:gsub(pattern, replacement, 1) end end + return function(replacement) + return function(s) + if pattern == "" then return replacement .. s end + local a, b = s:find(pattern, 1, true) + if a == nil then return s end + return s:sub(1, a - 1) .. replacement .. s:sub(b + 1) + end + end end), replaceAll = (function(pattern) - return function(replacement) return function(string) return string:gsub(pattern, replacement) end end + return function(replacement) + return function(s) + if pattern == "" then + local out = { replacement } + for i = 1, #s do + out[#out + 1] = s:sub(i, i) + out[#out + 1] = replacement + end + return table.concat(out) + end + local out, i = {}, 1 + while true do + local a, b = s:find(pattern, i, true) + if a == nil then break end + out[#out + 1] = s:sub(i, a - 1) + out[#out + 1] = replacement + i = b + 1 + end + out[#out + 1] = s:sub(i) + return table.concat(out) + end + end end), split = (function(sep) return function(s) local t = {} - for str in s:gmatch("([^" .. sep .. "]+)") do table.insert(t, str) end + if sep == "" then + for i = 1, #s do t[i] = s:sub(i, i) end + return t + end + local i = 1 + while true do + local a, b = s:find(sep, i, true) + if a == nil then break end + t[#t + 1] = s:sub(i, a - 1) + i = b + 1 + end + t[#t + 1] = s:sub(i) return t end end), diff --git a/test/Test/Data/String/CodePoints.purs b/test/Test/Data/String/CodePoints.purs index 587ec89..c5304e7 100644 --- a/test/Test/Data/String/CodePoints.purs +++ b/test/Test/Data/String/CodePoints.purs @@ -1,3 +1,18 @@ +-- Deviation from upstream purescript-strings: in pslua a String is a UTF-8 +-- byte string, not a sequence of UTF-16 code units. The upstream test string +-- was built around lone surrogates ("a\xDC00\xD800\xD800\x16805\x16A06z"), +-- which are not representable in UTF-8. This port keeps the structure of the +-- upstream suite (7 code points, a repeated one, astral characters, ASCII +-- anchors at both ends) but uses well-formed Unicode of varying UTF-8 widths: +-- 'a' (1 byte), 'é' (2), 'П' (2, repeated), U+16805 (4), U+16A06 (4), +-- 'z' (1). Cases probing lone-surrogate behaviour (e.g. searching for +-- "\xD81A", the lead surrogate of U+16805) have no UTF-8 counterpart and +-- were dropped. The Char type is a single byte in pslua, so +-- codePointFromChar is only exercised on ASCII. +-- +-- The single upstream do-block is also split into per-section functions: +-- one huge do-block generates Lua nested beyond the parser limit of stock +-- Lua 5.1 interpreters (Unisay/purescript-lua#46). module Test.Data.String.CodePoints (testStringCodePoints) where import Prelude @@ -5,6 +20,7 @@ import Prelude import Data.Enum (fromEnum, toEnum) import Data.Maybe (Maybe(..), fromJust) import Data.String.CodePoints as SCP +import Data.String.CodeUnits as SCU import Data.String.Pattern (Pattern(..)) import Effect (Effect) import Effect.Console (log) @@ -12,11 +28,34 @@ import Partial.Unsafe (unsafePartial) import Test.Assert (assertEqual) str :: String -str = "a\xDC00\xD800\xD800\x16805\x16A06z" +str = "aéПП\x16805\x16A06z" testStringCodePoints :: Effect Unit testStringCodePoints = do + testShow + testCodePointFromChar + testSingleton + testToFromCodePointArray + testBoundaries + testMalformed + testCodePointAt + testUncons + testLength + testCountPrefix + testIndexOf + testIndexOfStartingAt + testLastIndexOf + testLastIndexOfStartingAt1 + testLastIndexOfStartingAt2 + testTake + testTakeWhile + testDrop + testDropWhile + testSplitAt1 + testSplitAt2 +testShow :: Effect Unit +testShow = do log "show" assertEqual { actual: map show (SCP.codePointAt 0 str) @@ -24,15 +63,15 @@ testStringCodePoints = do } assertEqual { actual: map show (SCP.codePointAt 1 str) - , expected: Just "(CodePoint 0xDC00)" + , expected: Just "(CodePoint 0xE9)" } assertEqual { actual: map show (SCP.codePointAt 2 str) - , expected: Just "(CodePoint 0xD800)" + , expected: Just "(CodePoint 0x41F)" } assertEqual { actual: map show (SCP.codePointAt 3 str) - , expected: Just "(CodePoint 0xD800)" + , expected: Just "(CodePoint 0x41F)" } assertEqual { actual: map show (SCP.codePointAt 4 str) @@ -47,6 +86,8 @@ testStringCodePoints = do , expected: Just "(CodePoint 0x7A)" } +testCodePointFromChar :: Effect Unit +testCodePointFromChar = do log "codePointFromChar" assertEqual { actual: Just (SCP.codePointFromChar 'A') @@ -57,20 +98,123 @@ testStringCodePoints = do , expected: toEnum 0 } assertEqual - { actual: (SCP.codePointFromChar <$> toEnum 0xFFFF) - , expected: toEnum 0xFFFF + { actual: (SCP.codePointFromChar <$> toEnum 0x7A) + , expected: toEnum 0x7A } +testSingleton :: Effect Unit +testSingleton = do log "singleton" assertEqual { actual: (SCP.singleton <$> toEnum 0x30) , expected: Just "0" } + assertEqual + { actual: (SCP.singleton <$> toEnum 0xE9) + , expected: Just "é" + } + assertEqual + { actual: (SCP.singleton <$> toEnum 0x20AC) + , expected: Just "€" + } assertEqual { actual: (SCP.singleton <$> toEnum 0x16805) , expected: Just "\x16805" } +testToFromCodePointArray :: Effect Unit +testToFromCodePointArray = do + log "toCodePointArray" + assertEqual + { actual: SCP.toCodePointArray "" + , expected: [] + } + assertEqual + { actual: map fromEnum (SCP.toCodePointArray str) + , expected: [ 0x61, 0xE9, 0x41F, 0x41F, 0x16805, 0x16A06, 0x7A ] + } + log "fromCodePointArray" + assertEqual + { actual: SCP.fromCodePointArray [] + , expected: "" + } + assertEqual + { actual: SCP.fromCodePointArray (SCP.toCodePointArray str) + , expected: str + } + +-- Exercises the UTF-8 encoder/decoder at every width boundary. Each code +-- point round-trips through fromCodePointArray (encode) and back through +-- toCodePointArray (decode), and singleton agrees with a one-element array. +testBoundaries :: Effect Unit +testBoundaries = do + log "encode/decode boundaries" + let + -- 1-byte edges, 2-byte edges, 3-byte edges, 4-byte edges + codes = [ 0x0, 0x7F, 0x80, 0x7FF, 0x800, 0xFFFF, 0x10000, 0x10FFFF ] + cps = map cp codes + encoded = SCP.fromCodePointArray cps + assertEqual + { actual: map fromEnum (SCP.toCodePointArray encoded) + , expected: codes + } + assertEqual + { actual: SCP.length encoded + , expected: 8 + } + -- byte widths: 1 + 1 + 2 + 2 + 3 + 3 + 4 + 4 + assertEqual + { actual: SCU.length encoded + , expected: 20 + } + assertEqual + { actual: map (\c -> SCU.length (SCP.singleton c)) cps + , expected: [ 1, 1, 2, 2, 3, 3, 4, 4 ] + } + +-- Malformed UTF-8 (a lone continuation byte, a truncated multi-byte lead) +-- must decode one byte at a time without crashing or desynchronising the +-- bytes that follow. The raw bytes are built with CodeUnits.singleton +-- (a Char is one byte in pslua); a non-ASCII string literal would be +-- re-encoded as valid UTF-8 by the compiler and could not express them. +testMalformed :: Effect Unit +testMalformed = do + log "malformed input" + let + loneCont = "a" <> byte 0x80 <> "b" + truncated2 = byte 0xC2 + truncated3 = byte 0xE2 <> byte 0x82 + -- lone continuation byte between ASCII anchors: 'b' must survive + assertEqual + { actual: map fromEnum (SCP.toCodePointArray loneCont) + , expected: [ 0x61, 0x80, 0x62 ] + } + assertEqual + { actual: SCP.length loneCont + , expected: 3 + } + -- uncons of the lone byte must consume exactly one byte, leaving "b" + assertEqual + { actual: (_.tail) <$> SCP.uncons (byte 0x80 <> "b") + , expected: Just "b" + } + assertEqual + { actual: (fromEnum <<< _.head) <$> SCP.uncons (byte 0x80 <> "b") + , expected: Just 0x80 + } + -- truncated two-byte lead (0xC2 with no continuation) + assertEqual + { actual: map fromEnum (SCP.toCodePointArray truncated2) + , expected: [ 0xC2 ] + } + -- truncated three-byte lead (first two bytes of the euro sign) + assertEqual + { actual: map fromEnum (SCP.toCodePointArray truncated3) + , expected: [ 0xE2, 0x82 ] + } + +testCodePointAt :: Effect Unit +testCodePointAt = do log "codePointAt" assertEqual { actual: SCP.codePointAt (-1) str @@ -82,15 +226,15 @@ testStringCodePoints = do } assertEqual { actual: SCP.codePointAt 1 str - , expected: (toEnum 0xDC00) + , expected: (toEnum 0xE9) } assertEqual { actual: SCP.codePointAt 2 str - , expected: (toEnum 0xD800) + , expected: (toEnum 0x41F) } assertEqual { actual: SCP.codePointAt 3 str - , expected: (toEnum 0xD800) + , expected: (toEnum 0x41F) } assertEqual { actual: SCP.codePointAt 4 str @@ -109,40 +253,44 @@ testStringCodePoints = do , expected: Nothing } +testUncons :: Effect Unit +testUncons = do log "uncons" assertEqual { actual: SCP.uncons str - , expected: Just {head: cp 0x61, tail: "\xDC00\xD800\xD800\x16805\x16A06z"} + , expected: Just { head: cp 0x61, tail: "éПП\x16805\x16A06z" } } assertEqual { actual: SCP.uncons (SCP.drop 1 str) - , expected: Just {head: cp 0xDC00, tail: "\xD800\xD800\x16805\x16A06z"} + , expected: Just { head: cp 0xE9, tail: "ПП\x16805\x16A06z" } } assertEqual { actual: SCP.uncons (SCP.drop 2 str) - , expected: Just {head: cp 0xD800, tail: "\xD800\x16805\x16A06z"} + , expected: Just { head: cp 0x41F, tail: "П\x16805\x16A06z" } } assertEqual { actual: SCP.uncons (SCP.drop 3 str) - , expected: Just {head: cp 0xD800, tail: "\x16805\x16A06z"} + , expected: Just { head: cp 0x41F, tail: "\x16805\x16A06z" } } assertEqual { actual: SCP.uncons (SCP.drop 4 str) - , expected: Just {head: cp 0x16805, tail: "\x16A06z"} + , expected: Just { head: cp 0x16805, tail: "\x16A06z" } } assertEqual { actual: SCP.uncons (SCP.drop 5 str) - , expected: Just {head: cp 0x16A06, tail: "z"} + , expected: Just { head: cp 0x16A06, tail: "z" } } assertEqual { actual: SCP.uncons (SCP.drop 6 str) - , expected: Just {head: cp 0x7A, tail: ""} + , expected: Just { head: cp 0x7A, tail: "" } } assertEqual { actual: SCP.uncons "" , expected: Nothing } +testLength :: Effect Unit +testLength = do log "length" assertEqual { actual: SCP.length "" @@ -156,11 +304,25 @@ testStringCodePoints = do { actual: SCP.length "ab" , expected: 2 } + assertEqual + { actual: SCP.length "é" + , expected: 1 + } + assertEqual + { actual: SCP.length "€" + , expected: 1 + } + assertEqual + { actual: SCP.length "\x16805" + , expected: 1 + } assertEqual { actual: SCP.length str , expected: 7 } +testCountPrefix :: Effect Unit +testCountPrefix = do log "countPrefix" assertEqual { actual: SCP.countPrefix (\_ -> true) "" @@ -179,10 +341,12 @@ testStringCodePoints = do , expected: 4 } assertEqual - { actual: SCP.countPrefix (\x -> fromEnum x < 0xDC00) str + { actual: SCP.countPrefix (\x -> fromEnum x < 0xE9) str , expected: 1 } +testIndexOf :: Effect Unit +testIndexOf = do log "indexOf" assertEqual { actual: SCP.indexOf (Pattern "") "" @@ -194,30 +358,26 @@ testStringCodePoints = do } assertEqual { actual: SCP.indexOf (Pattern str) str - , expected: Just 0 - } + , expected: Just 0 + } assertEqual { actual: SCP.indexOf (Pattern "a") str , expected: Just 0 } assertEqual - { actual: SCP.indexOf (Pattern "\xDC00\xD800\xD800") str + { actual: SCP.indexOf (Pattern "éПП") str , expected: Just 1 } assertEqual - { actual: SCP.indexOf (Pattern "\xD800") str + { actual: SCP.indexOf (Pattern "П") str , expected: Just 2 } assertEqual - { actual: SCP.indexOf (Pattern "\xD800\xD800") str + { actual: SCP.indexOf (Pattern "ПП") str , expected: Just 2 } assertEqual - { actual: SCP.indexOf (Pattern "\xD800\xD81A") str - , expected: Just 3 - } - assertEqual - { actual: SCP.indexOf (Pattern "\xD800\x16805") str + { actual: SCP.indexOf (Pattern "П\x16805") str , expected: Just 3 } assertEqual @@ -236,11 +396,9 @@ testStringCodePoints = do { actual: SCP.indexOf (Pattern "\n") str , expected: Nothing } - assertEqual - { actual: SCP.indexOf (Pattern "\xD81A") str - , expected: Just 4 - } +testIndexOfStartingAt :: Effect Unit +testIndexOfStartingAt = do log "indexOf'" assertEqual { actual: SCP.indexOf' (Pattern "") 0 "" @@ -295,6 +453,8 @@ testStringCodePoints = do , expected: Nothing } +testLastIndexOf :: Effect Unit +testLastIndexOf = do log "lastIndexOf" assertEqual { actual: SCP.lastIndexOf (Pattern "") "" @@ -313,23 +473,19 @@ testStringCodePoints = do , expected: Just 0 } assertEqual - { actual: SCP.lastIndexOf (Pattern "\xDC00\xD800\xD800") str + { actual: SCP.lastIndexOf (Pattern "éПП") str , expected: Just 1 } assertEqual - { actual: SCP.lastIndexOf (Pattern "\xD800") str + { actual: SCP.lastIndexOf (Pattern "П") str , expected: Just 3 } assertEqual - { actual: SCP.lastIndexOf (Pattern "\xD800\xD800") str + { actual: SCP.lastIndexOf (Pattern "ПП") str , expected: Just 2 } assertEqual - { actual: SCP.lastIndexOf (Pattern "\xD800\xD81A") str - , expected: Just 3 - } - assertEqual - { actual: SCP.lastIndexOf (Pattern "\xD800\x16805") str + { actual: SCP.lastIndexOf (Pattern "П\x16805") str , expected: Just 3 } assertEqual @@ -348,11 +504,9 @@ testStringCodePoints = do { actual: SCP.lastIndexOf (Pattern "\n") str , expected: Nothing } - assertEqual - { actual: SCP.lastIndexOf (Pattern "\xD81A") str - , expected: Just 5 - } +testLastIndexOfStartingAt1 :: Effect Unit +testLastIndexOfStartingAt1 = do log "lastIndexOf'" assertEqual { actual: SCP.lastIndexOf' (Pattern "") 0 "" @@ -414,36 +568,40 @@ testStringCodePoints = do { actual: SCP.lastIndexOf' (Pattern "z") 7 str , expected: Just 6 } + +testLastIndexOfStartingAt2 :: Effect Unit +testLastIndexOfStartingAt2 = do + log "lastIndexOf' (multibyte)" assertEqual - { actual: SCP.lastIndexOf' (Pattern "\xD800") 7 str + { actual: SCP.lastIndexOf' (Pattern "П") 7 str , expected: Just 3 } assertEqual - { actual: SCP.lastIndexOf' (Pattern "\xD800") 6 str + { actual: SCP.lastIndexOf' (Pattern "П") 6 str , expected: Just 3 } assertEqual - { actual: SCP.lastIndexOf' (Pattern "\xD800") 5 str + { actual: SCP.lastIndexOf' (Pattern "П") 5 str , expected: Just 3 } assertEqual - { actual: SCP.lastIndexOf' (Pattern "\xD800") 4 str + { actual: SCP.lastIndexOf' (Pattern "П") 4 str , expected: Just 3 } assertEqual - { actual: SCP.lastIndexOf' (Pattern "\xD800") 3 str + { actual: SCP.lastIndexOf' (Pattern "П") 3 str , expected: Just 3 } assertEqual - { actual: SCP.lastIndexOf' (Pattern "\xD800") 2 str + { actual: SCP.lastIndexOf' (Pattern "П") 2 str , expected: Just 2 } assertEqual - { actual: SCP.lastIndexOf' (Pattern "\xD800") 1 str + { actual: SCP.lastIndexOf' (Pattern "П") 1 str , expected: Nothing } assertEqual - { actual: SCP.lastIndexOf' (Pattern "\xD800") 0 str + { actual: SCP.lastIndexOf' (Pattern "П") 0 str , expected: Nothing } assertEqual @@ -467,6 +625,8 @@ testStringCodePoints = do , expected: Nothing } +testTake :: Effect Unit +testTake = do log "take" assertEqual { actual: SCP.take (-1) str @@ -482,23 +642,23 @@ testStringCodePoints = do } assertEqual { actual: SCP.take 2 str - , expected: "a\xDC00" + , expected: "aé" } assertEqual { actual: SCP.take 3 str - , expected: "a\xDC00\xD800" + , expected: "aéП" } assertEqual { actual: SCP.take 4 str - , expected: "a\xDC00\xD800\xD800" + , expected: "aéПП" } assertEqual { actual: SCP.take 5 str - , expected: "a\xDC00\xD800\xD800\x16805" + , expected: "aéПП\x16805" } assertEqual { actual: SCP.take 6 str - , expected: "a\xDC00\xD800\xD800\x16805\x16A06" + , expected: "aéПП\x16805\x16A06" } assertEqual { actual: SCP.take 7 str @@ -509,6 +669,8 @@ testStringCodePoints = do , expected: str } +testTakeWhile :: Effect Unit +testTakeWhile = do log "takeWhile" assertEqual { actual: SCP.takeWhile (\_ -> true) str @@ -520,13 +682,15 @@ testStringCodePoints = do } assertEqual { actual: SCP.takeWhile (\c -> fromEnum c < 0xFFFF) str - , expected: "a\xDC00\xD800\xD800" + , expected: "aéПП" } assertEqual - { actual: SCP.takeWhile (\c -> fromEnum c < 0xDC00) str + { actual: SCP.takeWhile (\c -> fromEnum c < 0xE9) str , expected: "a" } +testDrop :: Effect Unit +testDrop = do log "drop" assertEqual { actual: SCP.drop (-1) str @@ -538,15 +702,15 @@ testStringCodePoints = do } assertEqual { actual: SCP.drop 1 str - , expected: "\xDC00\xD800\xD800\x16805\x16A06z" + , expected: "éПП\x16805\x16A06z" } assertEqual { actual: SCP.drop 2 str - , expected: "\xD800\xD800\x16805\x16A06z" + , expected: "ПП\x16805\x16A06z" } assertEqual { actual: SCP.drop 3 str - , expected: "\xD800\x16805\x16A06z" + , expected: "П\x16805\x16A06z" } assertEqual { actual: SCP.drop 4 str @@ -569,6 +733,8 @@ testStringCodePoints = do , expected: "" } +testDropWhile :: Effect Unit +testDropWhile = do log "dropWhile" assertEqual { actual: SCP.dropWhile (\_ -> true) str @@ -583,71 +749,83 @@ testStringCodePoints = do , expected: "\x16805\x16A06z" } assertEqual - { actual: SCP.dropWhile (\c -> fromEnum c < 0xDC00) str - , expected: "\xDC00\xD800\xD800\x16805\x16A06z" + { actual: SCP.dropWhile (\c -> fromEnum c < 0xE9) str + , expected: "éПП\x16805\x16A06z" } +testSplitAt1 :: Effect Unit +testSplitAt1 = do log "splitAt" assertEqual { actual: SCP.splitAt 0 "" - , expected: {before: "", after: "" } + , expected: { before: "", after: "" } } assertEqual { actual: SCP.splitAt 1 "" - , expected: {before: "", after: "" } + , expected: { before: "", after: "" } } assertEqual { actual: SCP.splitAt 0 "a" - , expected: {before: "", after: "a"} + , expected: { before: "", after: "a" } } assertEqual { actual: SCP.splitAt 1 "ab" - , expected: {before: "a", after: "b"} + , expected: { before: "a", after: "b" } } assertEqual { actual: SCP.splitAt 3 "aabcc" - , expected: {before: "aab", after: "cc"} + , expected: { before: "aab", after: "cc" } } assertEqual { actual: SCP.splitAt (-1) "abc" - , expected: {before: "", after: "abc"} + , expected: { before: "", after: "abc" } } + +testSplitAt2 :: Effect Unit +testSplitAt2 = do + log "splitAt (multibyte)" assertEqual { actual: SCP.splitAt 0 str - , expected: {before: "", after: str} + , expected: { before: "", after: str } } assertEqual { actual: SCP.splitAt 1 str - , expected: {before: "a", after: "\xDC00\xD800\xD800\x16805\x16A06z"} + , expected: { before: "a", after: "éПП\x16805\x16A06z" } } assertEqual { actual: SCP.splitAt 2 str - , expected: {before: "a\xDC00", after: "\xD800\xD800\x16805\x16A06z"} + , expected: { before: "aé", after: "ПП\x16805\x16A06z" } } assertEqual { actual: SCP.splitAt 3 str - , expected: {before: "a\xDC00\xD800", after: "\xD800\x16805\x16A06z"} + , expected: { before: "aéП", after: "П\x16805\x16A06z" } } assertEqual { actual: SCP.splitAt 4 str - , expected: {before: "a\xDC00\xD800\xD800", after: "\x16805\x16A06z"} + , expected: { before: "aéПП", after: "\x16805\x16A06z" } } assertEqual { actual: SCP.splitAt 5 str - , expected: {before: "a\xDC00\xD800\xD800\x16805", after: "\x16A06z"} + , expected: { before: "aéПП\x16805", after: "\x16A06z" } } assertEqual { actual: SCP.splitAt 6 str - , expected: {before: "a\xDC00\xD800\xD800\x16805\x16A06", after: "z"} + , expected: { before: "aéПП\x16805\x16A06", after: "z" } } assertEqual { actual: SCP.splitAt 7 str - , expected: {before: str, after: ""} + , expected: { before: str, after: "" } } assertEqual { actual: SCP.splitAt 8 str - , expected: {before: str, after: ""} + , expected: { before: str, after: "" } } cp :: Int -> SCP.CodePoint cp = unsafePartial fromJust <<< toEnum + +-- A single raw byte as a String. `toEnum n :: Maybe Char` is the byte with +-- code n (a Char is one byte in pslua), so CodeUnits.singleton wraps it +-- without any UTF-8 re-encoding. +byte :: Int -> String +byte n = SCU.singleton (unsafePartial fromJust (toEnum n)) diff --git a/test/Test/Data/String/CodeUnits.purs b/test/Test/Data/String/CodeUnits.purs index ddd512b..a9cea2d 100644 --- a/test/Test/Data/String/CodeUnits.purs +++ b/test/Test/Data/String/CodeUnits.purs @@ -12,6 +12,32 @@ import Test.Assert (assert, assertEqual) testStringCodeUnits :: Effect Unit testStringCodeUnits = do + testStripPrefix + testStripSuffix + testCharAt + testSingleton + testCharCodeAt + testToChar + testUncons + testTakeWhile + testDropWhile + testFromCharArray + testIndexOf + testIndexOf' + testLastIndexOf + testLastIndexOf' + testLength + testTake + testTakeRight + testDrop + testDropRight + testCountPrefix + testSplitAt + testToCharArray + testSlice + +testStripPrefix :: Effect Unit +testStripPrefix = do log "stripPrefix" assertEqual { actual: SCU.stripPrefix (Pattern "abc") "abcde" @@ -38,6 +64,8 @@ testStringCodeUnits = do , expected: Just "" } +testStripSuffix :: Effect Unit +testStripSuffix = do log "stripSuffix" assertEqual { actual: SCU.stripSuffix (Pattern "cde") "abcde" @@ -64,6 +92,8 @@ testStringCodeUnits = do , expected: Just "" } +testCharAt :: Effect Unit +testCharAt = do log "charAt" assertEqual { actual: SCU.charAt 0 "" @@ -90,12 +120,16 @@ testStringCodeUnits = do , expected: Nothing } +testSingleton :: Effect Unit +testSingleton = do log "singleton" assertEqual { actual: SCU.singleton 'a' , expected: "a" } +testCharCodeAt :: Effect Unit +testCharCodeAt = do log "charCodeAt" assertEqual { actual: (fromEnum <$> SCU.charAt 0 "") @@ -122,6 +156,8 @@ testStringCodeUnits = do , expected: Nothing } +testToChar :: Effect Unit +testToChar = do log "toChar" assertEqual { actual: SCU.toChar "" @@ -136,6 +172,8 @@ testStringCodeUnits = do , expected: Nothing } +testUncons :: Effect Unit +testUncons = do log "uncons" assert $ isNothing (SCU.uncons "") assertEqual @@ -147,6 +185,8 @@ testStringCodeUnits = do , expected: Just { head: 'a', tail: "b" } } +testTakeWhile :: Effect Unit +testTakeWhile = do log "takeWhile" assertEqual { actual: SCU.takeWhile (\c -> true) "abc" @@ -161,6 +201,8 @@ testStringCodeUnits = do , expected: "aa" } +testDropWhile :: Effect Unit +testDropWhile = do log "dropWhile" assertEqual { actual: SCU.dropWhile (\c -> true) "abc" @@ -175,6 +217,8 @@ testStringCodeUnits = do , expected: "bbcc" } +testFromCharArray :: Effect Unit +testFromCharArray = do log "fromCharArray" assertEqual { actual: SCU.fromCharArray [] @@ -185,6 +229,8 @@ testStringCodeUnits = do , expected: "ab" } +testIndexOf :: Effect Unit +testIndexOf = do log "indexOf" assertEqual { actual: SCU.indexOf (Pattern "") "" @@ -203,6 +249,8 @@ testStringCodeUnits = do , expected: Nothing } +testIndexOf' :: Effect Unit +testIndexOf' = do log "indexOf'" assertEqual { actual: SCU.indexOf' (Pattern "") 0 "" @@ -245,6 +293,8 @@ testStringCodeUnits = do , expected: Nothing } +testLastIndexOf :: Effect Unit +testLastIndexOf = do log "lastIndexOf" assertEqual { actual: SCU.lastIndexOf (Pattern "") "" @@ -263,6 +313,8 @@ testStringCodeUnits = do , expected: Nothing } +testLastIndexOf' :: Effect Unit +testLastIndexOf' = do log "lastIndexOf'" assertEqual { actual: SCU.lastIndexOf' (Pattern "") 0 "" @@ -305,6 +357,8 @@ testStringCodeUnits = do , expected: Nothing } +testLength :: Effect Unit +testLength = do log "length" assertEqual { actual: SCU.length "" @@ -319,6 +373,8 @@ testStringCodeUnits = do , expected: 2 } +testTake :: Effect Unit +testTake = do log "take" assertEqual { actual: SCU.take 0 "ab" @@ -341,6 +397,8 @@ testStringCodeUnits = do , expected: "" } +testTakeRight :: Effect Unit +testTakeRight = do log "takeRight" assertEqual { actual: SCU.takeRight 0 "ab" @@ -363,6 +421,8 @@ testStringCodeUnits = do , expected: "" } +testDrop :: Effect Unit +testDrop = do log "drop" assertEqual { actual: SCU.drop 0 "ab" @@ -385,6 +445,8 @@ testStringCodeUnits = do , expected: "ab" } +testDropRight :: Effect Unit +testDropRight = do log "dropRight" assertEqual { actual: SCU.dropRight 0 "ab" @@ -407,6 +469,8 @@ testStringCodeUnits = do , expected: "ab" } +testCountPrefix :: Effect Unit +testCountPrefix = do log "countPrefix" assertEqual { actual: SCU.countPrefix (_ == 'a') "" @@ -425,6 +489,8 @@ testStringCodeUnits = do , expected: 1 } +testSplitAt :: Effect Unit +testSplitAt = do log "splitAt" assertEqual { actual: SCU.splitAt 1 "" @@ -455,6 +521,8 @@ testStringCodeUnits = do , expected: {before: "Hi", after: ""} } +testToCharArray :: Effect Unit +testToCharArray = do log "toCharArray" assertEqual { actual: SCU.toCharArray "" @@ -469,6 +537,8 @@ testStringCodeUnits = do , expected: ['a', 'b'] } +testSlice :: Effect Unit +testSlice = do log "slice" assertEqual { actual: SCU.slice 0 0 "purescript" diff --git a/test/Test/Data/String/NonEmpty.purs b/test/Test/Data/String/NonEmpty.purs index a4103ec..fe7e923 100644 --- a/test/Test/Data/String/NonEmpty.purs +++ b/test/Test/Data/String/NonEmpty.purs @@ -14,7 +14,25 @@ import Type.Proxy (Proxy(..)) testNonEmptyString :: Effect Unit testNonEmptyString = do + testFromString + testToString + testAppendString + testPrependString + testContains + testLocaleCompare + testReplace + testReplaceAll + testStripPrefix + testStripSuffix + testToLower + testToUpper + testTrim + testJoinWith + testJoin1With + testJoinWith1 +testFromString :: Effect Unit +testFromString = do log "fromString" assertEqual { actual: NES.fromString "" @@ -25,12 +43,16 @@ testNonEmptyString = do , expected: Just (nes (Proxy :: Proxy "hello")) } +testToString :: Effect Unit +testToString = do log "toString" assertEqual { actual: (NES.toString <$> NES.fromString "hello") , expected: Just "hello" } +testAppendString :: Effect Unit +testAppendString = do log "appendString" assertEqual { actual: NES.appendString (nes (Proxy :: Proxy "Hello")) " world" @@ -41,6 +63,8 @@ testNonEmptyString = do , expected: nes (Proxy :: Proxy "Hello") } +testPrependString :: Effect Unit +testPrependString = do log "prependString" assertEqual { actual: NES.prependString "be" (nes (Proxy :: Proxy "fore")) @@ -51,6 +75,8 @@ testNonEmptyString = do , expected: nes (Proxy :: Proxy "fore") } +testContains :: Effect Unit +testContains = do log "contains" assert $ NES.contains (Pattern "") (nes (Proxy :: Proxy "abcd")) assert $ NES.contains (Pattern "bc") (nes (Proxy :: Proxy "abcd")) @@ -58,6 +84,8 @@ testNonEmptyString = do assert $ NES.contains (Pattern "needle") (nes (Proxy :: Proxy "haystack with needle")) assert $ not NES.contains (Pattern "needle") (nes (Proxy :: Proxy "haystack")) +testLocaleCompare :: Effect Unit +testLocaleCompare = do log "localeCompare" assertEqual { actual: NES.localeCompare (nes (Proxy :: Proxy "a")) (nes (Proxy :: Proxy "a")) @@ -72,6 +100,8 @@ testNonEmptyString = do , expected: GT } +testReplace :: Effect Unit +testReplace = do log "replace" assertEqual { actual: NES.replace (Pattern "b") (NES.NonEmptyReplacement (nes (Proxy :: Proxy "!"))) (nes (Proxy :: Proxy "abc")) @@ -86,6 +116,8 @@ testNonEmptyString = do , expected: nes (Proxy :: Proxy "abc") } +testReplaceAll :: Effect Unit +testReplaceAll = do log "replaceAll" assertEqual { actual: NES.replaceAll (Pattern "[b]") (NES.NonEmptyReplacement (nes (Proxy :: Proxy "!"))) (nes (Proxy :: Proxy "a[b]c")) @@ -100,6 +132,8 @@ testNonEmptyString = do , expected: nes (Proxy :: Proxy "abc") } +testStripPrefix :: Effect Unit +testStripPrefix = do log "stripPrefix" assertEqual { actual: NES.stripPrefix (Pattern "") (nes (Proxy :: Proxy "abc")) @@ -130,6 +164,8 @@ testNonEmptyString = do , expected: Nothing } +testStripSuffix :: Effect Unit +testStripSuffix = do log "stripSuffix" assertEqual { actual: NES.stripSuffix (Pattern ".exe") (nes (Proxy :: Proxy "purs.exe")) @@ -144,18 +180,24 @@ testNonEmptyString = do , expected: Nothing } +testToLower :: Effect Unit +testToLower = do log "toLower" assertEqual { actual: NES.toLower (nes (Proxy :: Proxy "bAtMaN")) , expected: nes (Proxy :: Proxy "batman") } +testToUpper :: Effect Unit +testToUpper = do log "toUpper" assertEqual { actual: NES.toUpper (nes (Proxy :: Proxy "bAtMaN")) , expected: nes (Proxy :: Proxy "BATMAN") } +testTrim :: Effect Unit +testTrim = do log "trim" assertEqual { actual: NES.trim (nes (Proxy :: Proxy " abc ")) @@ -166,6 +208,8 @@ testNonEmptyString = do , expected: Nothing } +testJoinWith :: Effect Unit +testJoinWith = do log "joinWith" assertEqual { actual: NES.joinWith "" [] @@ -180,6 +224,8 @@ testNonEmptyString = do , expected: "a--b--c" } +testJoin1With :: Effect Unit +testJoin1With = do log "join1With" assertEqual { actual: NES.join1With "" (nea [nes (Proxy :: Proxy "a"), nes (Proxy :: Proxy "b")]) @@ -198,6 +244,8 @@ testNonEmptyString = do , expected: nes (Proxy :: Proxy "applebanana") } +testJoinWith1 :: Effect Unit +testJoinWith1 = do log "joinWith1" assertEqual { actual: NES.joinWith1 (nes (Proxy :: Proxy " ")) (nea ["a", "b"]) diff --git a/test/Test/Data/String/NonEmpty/CodeUnits.purs b/test/Test/Data/String/NonEmpty/CodeUnits.purs index e810dd9..930753e 100644 --- a/test/Test/Data/String/NonEmpty/CodeUnits.purs +++ b/test/Test/Data/String/NonEmpty/CodeUnits.purs @@ -15,7 +15,34 @@ import Type.Proxy (Proxy(..)) testNonEmptyStringCodeUnits :: Effect Unit testNonEmptyStringCodeUnits = do + testFromCharArray + testFromNonEmptyCharArray + testSingleton + testCons + testSnoc + testFromFoldable1 + testCharAt + testCharCodeAt + testToChar + testToCharArray + testToNonEmptyCharArray + testUncons + testTakeWhile + testDropWhile + testIndexOf + testIndexOf' + testLastIndexOf + testLastIndexOf' + testLength + testTake + testTakeRight + testDrop + testDropRight + testCountPrefix + testSplitAt +testFromCharArray :: Effect Unit +testFromCharArray = do log "fromCharArray" assertEqual { actual: NESCU.fromCharArray [] @@ -26,18 +53,24 @@ testNonEmptyStringCodeUnits = do , expected: Just (nes (Proxy :: Proxy "ab")) } +testFromNonEmptyCharArray :: Effect Unit +testFromNonEmptyCharArray = do log "fromNonEmptyCharArray" assertEqual { actual: NESCU.fromNonEmptyCharArray (NEA.singleton 'b') , expected: NESCU.singleton 'b' } +testSingleton :: Effect Unit +testSingleton = do log "singleton" assertEqual { actual: NESCU.singleton 'a' , expected: nes (Proxy :: Proxy "a") } +testCons :: Effect Unit +testCons = do log "cons" assertEqual { actual: NESCU.cons 'a' "bc" @@ -48,6 +81,8 @@ testNonEmptyStringCodeUnits = do , expected: nes (Proxy :: Proxy "a") } +testSnoc :: Effect Unit +testSnoc = do log "snoc" assertEqual { actual: NESCU.snoc 'c' "ab" @@ -58,6 +93,8 @@ testNonEmptyStringCodeUnits = do , expected: nes (Proxy :: Proxy "a") } +testFromFoldable1 :: Effect Unit +testFromFoldable1 = do log "fromFoldable1" assertEqual { actual: NESCU.fromFoldable1 (nea ['a']) @@ -68,6 +105,8 @@ testNonEmptyStringCodeUnits = do , expected: nes (Proxy :: Proxy "abc") } +testCharAt :: Effect Unit +testCharAt = do log "charAt" assertEqual { actual: NESCU.charAt 0 (nes (Proxy :: Proxy "a")) @@ -98,6 +137,8 @@ testNonEmptyStringCodeUnits = do , expected: Nothing } +testCharCodeAt :: Effect Unit +testCharCodeAt = do log "charCodeAt" assertEqual { actual: fromEnum <$> NESCU.charAt 0 (nes (Proxy :: Proxy "a")) @@ -119,15 +160,19 @@ testNonEmptyStringCodeUnits = do { actual: fromEnum <$> NESCU.charAt 2 (nes (Proxy :: Proxy "ab")) , expected: Nothing } + -- pslua: a code unit is a byte; index 2 holds the first byte of the + -- UTF-8 encoding of '€' (0xE2), not the code point 0x20AC. assertEqual { actual: fromEnum <$> NESCU.charAt 2 (nes (Proxy :: Proxy "5 €")) - , expected: Just 0x20AC + , expected: Just 0xE2 } assertEqual { actual: fromEnum <$> NESCU.charAt 10 (nes (Proxy :: Proxy "5 €")) , expected: Nothing } +testToChar :: Effect Unit +testToChar = do log "toChar" assertEqual { actual: NESCU.toChar (nes (Proxy :: Proxy "a")) @@ -138,6 +183,8 @@ testNonEmptyStringCodeUnits = do , expected: Nothing } +testToCharArray :: Effect Unit +testToCharArray = do log "toCharArray" assertEqual { actual: NESCU.toCharArray (nes (Proxy :: Proxy "a")) @@ -147,17 +194,24 @@ testNonEmptyStringCodeUnits = do { actual: NESCU.toCharArray (nes (Proxy :: Proxy "ab")) , expected: ['a', 'b'] } + -- pslua: a Char is a byte, so the upstream "Hello☺\n" case (with a + -- multi-byte '☺' Char) is not expressible; an ASCII string keeps the + -- intent (letters plus a control character). assertEqual - { actual: NESCU.toCharArray (nes (Proxy :: Proxy "Hello☺\n")) - , expected: ['H','e','l','l','o','☺','\n'] + { actual: NESCU.toCharArray (nes (Proxy :: Proxy "Hello!\n")) + , expected: ['H','e','l','l','o','!','\n'] } +testToNonEmptyCharArray :: Effect Unit +testToNonEmptyCharArray = do log "toNonEmptyCharArray" assertEqual { actual: NESCU.toNonEmptyCharArray (nes (Proxy :: Proxy "ab")) , expected: nea ['a', 'b'] } +testUncons :: Effect Unit +testUncons = do log "uncons" assertEqual { actual: NESCU.uncons (nes (Proxy :: Proxy "a")) @@ -168,6 +222,8 @@ testNonEmptyStringCodeUnits = do , expected: { head: 'H', tail: Just (nes (Proxy :: Proxy "ello World")) } } +testTakeWhile :: Effect Unit +testTakeWhile = do log "takeWhile" assertEqual { actual: NESCU.takeWhile (\_ -> true) (nes (Proxy :: Proxy "abc")) @@ -190,6 +246,8 @@ testNonEmptyStringCodeUnits = do , expected: Nothing } +testDropWhile :: Effect Unit +testDropWhile = do log "dropWhile" assertEqual { actual: NESCU.dropWhile (\_ -> true) (nes (Proxy :: Proxy "abc")) @@ -208,6 +266,8 @@ testNonEmptyStringCodeUnits = do , expected: Just (nes (Proxy :: Proxy ".purs")) } +testIndexOf :: Effect Unit +testIndexOf = do log "indexOf" assertEqual { actual: NESCU.indexOf (Pattern "") (nes (Proxy :: Proxy "abcd")) @@ -222,6 +282,8 @@ testNonEmptyStringCodeUnits = do , expected: Nothing } +testIndexOf' :: Effect Unit +testIndexOf' = do log "indexOf'" assertEqual { actual: NESCU.indexOf' (Pattern "") (-1) (nes (Proxy :: Proxy "ab")) @@ -260,6 +322,8 @@ testNonEmptyStringCodeUnits = do , expected: Nothing } +testLastIndexOf :: Effect Unit +testLastIndexOf = do log "lastIndexOf" assertEqual { actual: NESCU.lastIndexOf (Pattern "") (nes (Proxy :: Proxy "abcd")) @@ -274,6 +338,8 @@ testNonEmptyStringCodeUnits = do , expected: Nothing } +testLastIndexOf' :: Effect Unit +testLastIndexOf' = do log "lastIndexOf'" assertEqual { actual: NESCU.lastIndexOf' (Pattern "") (-1) (nes (Proxy :: Proxy "ab")) @@ -312,6 +378,8 @@ testNonEmptyStringCodeUnits = do , expected: Nothing } +testLength :: Effect Unit +testLength = do log "length" assertEqual { actual: NESCU.length (nes (Proxy :: Proxy "a")) @@ -322,6 +390,8 @@ testNonEmptyStringCodeUnits = do , expected: 2 } +testTake :: Effect Unit +testTake = do log "take" assertEqual { actual: NESCU.take 0 (nes (Proxy :: Proxy "ab")) @@ -344,6 +414,8 @@ testNonEmptyStringCodeUnits = do , expected: Nothing } +testTakeRight :: Effect Unit +testTakeRight = do log "takeRight" assertEqual { actual: NESCU.takeRight 0 (nes (Proxy :: Proxy "ab")) @@ -366,6 +438,8 @@ testNonEmptyStringCodeUnits = do , expected: Nothing } +testDrop :: Effect Unit +testDrop = do log "drop" assertEqual { actual: NESCU.drop 0 (nes (Proxy :: Proxy "ab")) @@ -388,6 +462,8 @@ testNonEmptyStringCodeUnits = do , expected: Just (nes (Proxy :: Proxy "ab")) } +testDropRight :: Effect Unit +testDropRight = do log "dropRight" assertEqual { actual: NESCU.dropRight 0 (nes (Proxy :: Proxy "ab")) @@ -410,6 +486,8 @@ testNonEmptyStringCodeUnits = do , expected: Just (nes (Proxy :: Proxy "ab")) } +testCountPrefix :: Effect Unit +testCountPrefix = do log "countPrefix" assertEqual { actual: NESCU.countPrefix (_ == 'a') (nes (Proxy :: Proxy "ab")) @@ -428,6 +506,8 @@ testNonEmptyStringCodeUnits = do , expected: 0 } +testSplitAt :: Effect Unit +testSplitAt = do log "splitAt" assertEqual { actual: NESCU.splitAt 0 (nes (Proxy :: Proxy "a")) diff --git a/test/Test/Data/String/Regex.purs b/test/Test/Data/String/Regex.purs deleted file mode 100644 index 01d583b..0000000 --- a/test/Test/Data/String/Regex.purs +++ /dev/null @@ -1,61 +0,0 @@ -module Test.Data.String.Regex (testStringRegex) where - -import Data.String.Regex - -import Data.Array.NonEmpty (NonEmptyArray, fromArray) -import Data.Either (isLeft) -import Data.Maybe (Maybe(..), fromJust) -import Data.String.Regex.Flags (dotAll, global, ignoreCase, noFlags) -import Data.String.Regex.Unsafe (unsafeRegex) -import Effect (Effect) -import Effect.Console (log) -import Partial.Unsafe (unsafePartial) -import Prelude (type (~>), Unit, discard, not, show, ($), (<<<), (<>), (==)) -import Test.Assert (assert) - -testStringRegex :: Effect Unit -testStringRegex = do - log "regex" - assert $ test (unsafeRegex "^a" noFlags) "abc" - assert $ not (test (unsafeRegex "^b" noFlags) "abc") - assert $ isLeft (regex "+" noFlags) - - log "flags" - assert $ "quxbarfoobaz" == replace (unsafeRegex "foo" noFlags) "qux" "foobarfoobaz" - assert $ "quxbarquxbaz" == replace (unsafeRegex "foo" global) "qux" "foobarfoobaz" - assert $ "quxbarquxbaz" == replace (unsafeRegex "foo" (global <> ignoreCase)) "qux" "foobarFOObaz" - assert $ "quxbarfoobaz" == replace (unsafeRegex ".foo" dotAll) "qux" "\nfoobarfoobaz" - - log "match" - assert $ match (unsafeRegex "^abc$" noFlags) "abc" == Just (nea [Just "abc"]) - assert $ match (unsafeRegex "^abc$" noFlags) "xyz" == Nothing - - log "replace" - assert $ replace (unsafeRegex "-" noFlags) "!" "a-b-c" == "a!b-c" - - log "replace'" - assert $ replace' (unsafeRegex "-" noFlags) (\s xs -> "!") "a-b-c" == "a!b-c" - assert $ replace' (unsafeRegex "(foo)(bar)?" noFlags) (\s xs -> show xs) "<>" == "<>" - assert $ replace' (unsafeRegex "(foo)(bar)?" noFlags) (\s xs -> show xs) "" == "<[(Just \"foo\"),Nothing]>" - assert $ replace' (unsafeRegex "(foo)(bar)?" noFlags) (\s xs -> show xs) "" == "<[(Just \"foo\"),(Just \"bar\")]>" - assert $ replace' (unsafeRegex "@(?\\w+)" noFlags) (\s xs -> show xs) "@purescript" == "[(Just \"purescript\")]" - - log "search" - assert $ search (unsafeRegex "b" noFlags) "abc" == Just 1 - assert $ search (unsafeRegex "d" noFlags) "abc" == Nothing - - log "split" - assert $ split (unsafeRegex "" noFlags) "" == [] - assert $ split (unsafeRegex "" noFlags) "abc" == ["a", "b", "c"] - assert $ split (unsafeRegex "b" noFlags) "" == [""] - assert $ split (unsafeRegex "b" noFlags) "abc" == ["a", "c"] - - log "test" - -- Ensure that we have referential transparency for calls to 'test'. No - -- global state should be maintained between these two calls: - let pattern = unsafeRegex "a" (parseFlags "g") - assert $ test pattern "a" - assert $ test pattern "a" - -nea :: Array ~> NonEmptyArray -nea = unsafePartial fromJust <<< fromArray diff --git a/test/Test/Main.purs b/test/Test/Main.purs index fb9f32e..e531310 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -10,7 +10,6 @@ import Test.Data.String.CodePoints (testStringCodePoints) import Test.Data.String.CodeUnits (testStringCodeUnits) import Test.Data.String.NonEmpty (testNonEmptyString) import Test.Data.String.NonEmpty.CodeUnits (testNonEmptyStringCodeUnits) -import Test.Data.String.Regex (testStringRegex) import Test.Data.String.Unsafe (testStringUnsafe) main :: Effect Unit @@ -23,8 +22,6 @@ main = do testStringCodeUnits log "\n--- Data.String.Unsafe ---\n" testStringUnsafe - log "\n--- Data.String.Regex ---\n" - testStringRegex log "\n--- Data.String.CaseInsensitive ---\n" testCaseInsensitiveString log "\n--- Data.String.NonEmpty ---\n"