From 4faf6da2fce740d17c1c9382e4dbc9c5fbd6566e Mon Sep 17 00:00:00 2001 From: harryprayiv Date: Tue, 16 Jun 2026 01:58:49 -0400 Subject: [PATCH 01/10] bitwise operators added --- binaryen/src/Binaryen.js | 14 ++++++++- binaryen/src/Binaryen.purs | 30 ++++++++++++++++++- .../PureScript/Backend/Wasm/Codegen/Prim.purs | 12 ++++++++ .../PureScript/Backend/Wasm/Intrinsics.purs | 10 +++++++ .../PureScript/Backend/Wasm/Lower/Reps.purs | 18 ++++++++++- 5 files changed, 81 insertions(+), 3 deletions(-) diff --git a/binaryen/src/Binaryen.js b/binaryen/src/Binaryen.js index f90dda7f..25f58639 100644 --- a/binaryen/src/Binaryen.js +++ b/binaryen/src/Binaryen.js @@ -87,6 +87,18 @@ export const i32AndImpl = (mod) => (left) => (right) => () => export const i32OrImpl = (mod) => (left) => (right) => () => mod.i32.or(left, right); +export const i32XorImpl = (mod) => (left) => (right) => () => + mod.i32.xor(left, right); + +export const i32ShlImpl = (mod) => (left) => (right) => () => + mod.i32.shl(left, right); + +export const i32ShrSImpl = (mod) => (left) => (right) => () => + mod.i32.shr_s(left, right); + +export const i32ShrUImpl = (mod) => (left) => (right) => () => + mod.i32.shr_u(left, right); + export const i32EqzImpl = (mod) => (value) => () => mod.i32.eqz(value); @@ -303,4 +315,4 @@ export const validateImpl = (mod) => () => mod.validate() !== 0; export const emitTextImpl = (mod) => () => mod.emitText(); -export const emitBinaryImpl = (mod) => () => mod.emitBinary(); +export const emitBinaryImpl = (mod) => () => mod.emitBinary(); \ No newline at end of file diff --git a/binaryen/src/Binaryen.purs b/binaryen/src/Binaryen.purs index 2d73bc6b..b29fe2e5 100644 --- a/binaryen/src/Binaryen.purs +++ b/binaryen/src/Binaryen.purs @@ -41,6 +41,10 @@ module Binaryen , i32LtS , i32And , i32Or + , i32Xor + , i32Shl + , i32ShrS + , i32ShrU , i32Eqz , i32Const , i32TruncF64S @@ -284,6 +288,30 @@ foreign import i32OrImpl :: Module -> Expression -> Expression -> Effect Express i32Or :: Module -> Expression -> Expression -> Effect Expression i32Or = i32OrImpl +foreign import i32XorImpl :: Module -> Expression -> Expression -> Effect Expression + +-- | `i32.xor`: bitwise XOR. +i32Xor :: Module -> Expression -> Expression -> Effect Expression +i32Xor = i32XorImpl + +foreign import i32ShlImpl :: Module -> Expression -> Expression -> Effect Expression + +-- | `i32.shl`: logical left shift (`left << (right & 31)`). +i32Shl :: Module -> Expression -> Expression -> Effect Expression +i32Shl = i32ShlImpl + +foreign import i32ShrSImpl :: Module -> Expression -> Expression -> Effect Expression + +-- | `i32.shr_s`: arithmetic right shift, sign-propagating (PureScript `shr`, JS `>>`). +i32ShrS :: Module -> Expression -> Expression -> Effect Expression +i32ShrS = i32ShrSImpl + +foreign import i32ShrUImpl :: Module -> Expression -> Expression -> Effect Expression + +-- | `i32.shr_u`: logical right shift, zero-filling (PureScript `zshr`, JS `>>>`). +i32ShrU :: Module -> Expression -> Expression -> Effect Expression +i32ShrU = i32ShrUImpl + foreign import i32EqzImpl :: Module -> Expression -> Effect Expression -- | `i32.eqz`: 1 if the operand is 0, 0 otherwise (logical NOT of a 0/1 value). @@ -569,4 +597,4 @@ foreign import emitBinaryImpl :: Module -> Effect Uint8Array -- | Emit the module as a wasm binary. emitBinary :: Module -> Effect Uint8Array -emitBinary = emitBinaryImpl +emitBinary = emitBinaryImpl \ No newline at end of file diff --git a/compiler/src/PureScript/Backend/Wasm/Codegen/Prim.purs b/compiler/src/PureScript/Backend/Wasm/Codegen/Prim.purs index acb4b9ed..eafce19f 100644 --- a/compiler/src/PureScript/Backend/Wasm/Codegen/Prim.purs +++ b/compiler/src/PureScript/Backend/Wasm/Codegen/Prim.purs @@ -257,6 +257,18 @@ genPrim ctx intr args = case intr, args of nothingElse <- genAtomAs ctx Boxed nothing inner <- B.if_ ctx.mod isInt justApplied nothingThen B.if_ ctx.mod guardE inner nothingElse + -- `Data.Int.Bits`: single i32 instructions. `IntShr` is arithmetic + -- (sign-propagating, JS `>>`); `IntZshr` is logical (zero-fill, JS `>>>`). + IntAnd, [ a, b ] -> intBinop B.i32And a b + IntOr, [ a, b ] -> intBinop B.i32Or a b + IntXor, [ a, b ] -> intBinop B.i32Xor a b + IntShl, [ a, b ] -> intBinop B.i32Shl a b + IntShr, [ a, b ] -> intBinop B.i32ShrS a b + IntZshr, [ a, b ] -> intBinop B.i32ShrU a b + IntComplement, [ a ] -> do + ea <- intArg a + m1 <- B.i32Const ctx.mod (-1) + B.i32Xor ctx.mod ea m1 _, _ -> throwException (error "Codegen: intrinsic given an operand list of the wrong arity") where -- operand at the representation the op needs (no-op if already that rep) diff --git a/compiler/src/PureScript/Backend/Wasm/Intrinsics.purs b/compiler/src/PureScript/Backend/Wasm/Intrinsics.purs index 59fc7049..242846e5 100644 --- a/compiler/src/PureScript/Backend/Wasm/Intrinsics.purs +++ b/compiler/src/PureScript/Backend/Wasm/Intrinsics.purs @@ -143,6 +143,16 @@ data Intrinsic -- | applying it to the unit (the erased `Partial` dictionary). Native so the wasm -- | closure never crosses to the JS foreign (which would call it as `f()`). | UnsafePartial + -- `Data.Int.Bits` 32-bit bitwise ops (JS `& | ^ << >> >>> ~`), each a single + -- i32 instruction. `IntShr` is *arithmetic* (sign-propagating, JS `>>`); + -- `IntZshr` is *logical* (zero-fill, JS `>>>`); `IntComplement x` = `x ^ -1`. + | IntAnd + | IntOr + | IntXor + | IntShl + | IntShr + | IntZshr + | IntComplement derive instance eqIntrinsic :: Eq Intrinsic derive instance genericIntrinsic :: Generic Intrinsic _ diff --git a/compiler/src/PureScript/Backend/Wasm/Lower/Reps.purs b/compiler/src/PureScript/Backend/Wasm/Lower/Reps.purs index 751b1b83..e00e89f5 100644 --- a/compiler/src/PureScript/Backend/Wasm/Lower/Reps.purs +++ b/compiler/src/PureScript/Backend/Wasm/Lower/Reps.purs @@ -22,6 +22,14 @@ primRep = case _ of IntDiv -> I32 IntMod -> I32 IntDegree -> I32 + -- `Data.Int.Bits` 32-bit bitwise ops all produce a raw i32 + IntAnd -> I32 + IntOr -> I32 + IntXor -> I32 + IntShl -> I32 + IntShr -> I32 + IntZshr -> I32 + IntComplement -> I32 NumToInt -> I32 StrLen -> I32 StrByteAt -> I32 -- a UTF-8 byte (0-255), as an Int @@ -63,6 +71,14 @@ primOperandReps = case _ of IntLt -> [ I32, I32 ] IntDegree -> [ I32 ] IntToNum -> [ I32 ] + -- `Data.Int.Bits`: both operands unboxed i32 (the shift count too) + IntAnd -> [ I32, I32 ] + IntOr -> [ I32, I32 ] + IntXor -> [ I32, I32 ] + IntShl -> [ I32, I32 ] + IntShr -> [ I32, I32 ] + IntZshr -> [ I32, I32 ] + IntComplement -> [ I32 ] FromNumberImpl -> [ Boxed, Boxed, F64 ] -- just, nothing, n (the Number is unboxed) NumToInt -> [ F64 ] NumAdd -> [ F64, F64 ] @@ -86,4 +102,4 @@ primOperandReps = case _ of OrdNumber -> [ Boxed, Boxed, Boxed, F64, F64 ] -- forE lo hi f (perform-unit): the bounds are unboxed i32, the body closure boxed ForE -> [ I32, I32, Boxed, Boxed ] - _ -> [] + _ -> [] \ No newline at end of file From e3e4fb65b94c54ce7010b37f9c213be3a3759dbd Mon Sep 17 00:00:00 2001 From: harryprayiv Date: Tue, 16 Jun 2026 02:12:13 -0400 Subject: [PATCH 02/10] bitwise operators --- .../PureScript/Backend/Wasm/Intrinsics.purs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/compiler/src/PureScript/Backend/Wasm/Intrinsics.purs b/compiler/src/PureScript/Backend/Wasm/Intrinsics.purs index 242846e5..5967ec4e 100644 --- a/compiler/src/PureScript/Backend/Wasm/Intrinsics.purs +++ b/compiler/src/PureScript/Backend/Wasm/Intrinsics.purs @@ -242,9 +242,9 @@ foreignIntrinsic = case _ of -- | Intrinsics resolved by *qualified* name (rather than the bare identifier the table -- | above uses): the `effect`-package primitives — `Effect.Ref` cell ops (ADR -- | 0017) and the `Effect` control-flow loops (ADR 0018) — plus --- | `Partial.Unsafe._unsafePartial`, a couple of `Data.Array` ops, and the --- | uncurried-function families. Qualified because names like `read` / `write` / `new` --- | / `forE` are too generic to claim globally. +-- | `Partial.Unsafe._unsafePartial`, a couple of `Data.Array` ops, the `Data.Int.Bits` +-- | bitwise ops, and the uncurried-function families. Qualified because names like +-- | `read` / `write` / `new` / `forE` / `and` / `or` are too generic to claim globally. -- | -- | The arity counts each op's value parameters plus the trailing `Effect` -- | perform-unit for the ops whose result is `Effect Unit` (`Ref.write`, `forE`, …): @@ -301,6 +301,16 @@ qualifiedIntrinsic = case _ of "Wasm.Int.lt" -> Just (Tuple IntLt 2) "Wasm.Int.div" -> Just (Tuple IntDiv 2) "Wasm.Int.mod" -> Just (Tuple IntMod 2) + -- `Data.Int.Bits` 32-bit bitwise ops (the `integers` package foreigns: bare + -- `and`/`or`/`xor`/`shl`/`shr`/`zshr`/`complement`, qualified here because the + -- bare names are too generic). Pure, so absent from `effectfulForeignNames`. + "Data.Int.Bits.and" -> Just (Tuple IntAnd 2) + "Data.Int.Bits.or" -> Just (Tuple IntOr 2) + "Data.Int.Bits.xor" -> Just (Tuple IntXor 2) + "Data.Int.Bits.shl" -> Just (Tuple IntShl 2) + "Data.Int.Bits.shr" -> Just (Tuple IntShr 2) + "Data.Int.Bits.zshr" -> Just (Tuple IntZshr 2) + "Data.Int.Bits.complement" -> Just (Tuple IntComplement 1) -- `reverse`/`sliceImpl`/`indexImpl`/`unconsImpl`, `Data.Foldable.fold{l,r}Array`, -- `Data.String.CodeUnits.{singleton,toCharArray,fromCharArray}`, `Data.Int.fromStringAsImpl` -- now live in `ulib//foreign.wat` (ADR 0012), resolved as merged foreigns. @@ -335,4 +345,4 @@ effectfulForeignNames = Set.fromFoldable , "Effect.foreachE" , "Effect.whileE" , "Effect.untilE" - ] + ] \ No newline at end of file From 67cac36974508beeedd3b3504cbfc20858f220d2 Mon Sep 17 00:00:00 2001 From: harryprayiv Date: Tue, 16 Jun 2026 16:34:46 -0400 Subject: [PATCH 03/10] make a performed application saturate as a direct call (the way the foreign path already does) --- .../src/PureScript/Backend/Wasm/Lower.purs | 32 +++++++----- flake.lock | 49 +++---------------- flake.nix | 8 +-- 3 files changed, 32 insertions(+), 57 deletions(-) diff --git a/compiler/src/PureScript/Backend/Wasm/Lower.purs b/compiler/src/PureScript/Backend/Wasm/Lower.purs index 5eb07ba9..331e454c 100644 --- a/compiler/src/PureScript/Backend/Wasm/Lower.purs +++ b/compiler/src/PureScript/Backend/Wasm/Lower.purs @@ -198,9 +198,16 @@ lowerArg env expr k = case expr of -- collapse removes most `Perform`s in the simplifier.) M.Perform e | isEffectForeignApp env e -> lowerArg env e k + -- A performed application of a non-foreign producer: append the perform unit to the + -- producer's OWN argument list, so the whole thing lowers as one application. A function + -- whose arity includes the perform-unit (ADR 0018) then saturates to a *direct* + -- `RCallKnown` (the same direct shape a foreign perform takes), and `applyArity` still + -- handles the over-applied case (call saturated, apply the unit to the result). The old + -- `head: e` form passed the application itself as the head, hit the closure/`RApply` + -- fallback, built the producer as a partial closure, and dropped the apply that feeds + -- the unit — silently discarding the effect. + | M.App h as <- e -> lowerApp env { head: h, args: as <> [ M.Lit (LitInt 0) ] } k | otherwise -> lowerApp env { head: e, args: [ M.Lit (LitInt 0) ] } k - -- An (uncurried) lambda lowers to a closure; closures are arity-1, so a - -- multi-parameter lambda peels one parameter and the rest stay an inner lambda. M.Abs params body -> case Array.uncons params of Nothing -> lowerArg env body k Just { head: param, tail } -> do @@ -519,15 +526,18 @@ recBindEtaArity env = case _ of <|> ((Array.length <<< _.params) <$> Object.lookup (qualifiedKeyOf q) env.foreignSigs) -- | The declared arity of an application head, resolved the same way `lowerApp` dispatches a call: --- | a constructor, a top-level/specialized function, an intrinsic, or a foreign import. -headArity :: Env -> M.Expr -> Maybe Int -headArity env = case _ of - M.Var q@(Qualified (Just _) ident) -> - (_.arity <$> Object.lookup (qualifiedKeyOf q) env.ctors) - <|> Object.lookup (qualifiedKeyOf q) env.knownFuncs - <|> (snd <$> (qualifiedIntrinsic (qualifiedKeyOf q) <|> foreignIntrinsic ident)) - <|> ((Array.length <<< _.params) <$> Object.lookup (qualifiedKeyOf q) env.foreignSigs) - _ -> Nothing +-- | a constructor, a top-level/specialized function, an intrinsic, or a foreign import. +-- headArity is now dead code. My patch routes the performed call back through lowerApp/applyArity, +-- which recomputes arity itself, so the standalone headArity lookup (which the old Perform branch was the only caller of) is now orphaned + +-- headArity :: Env -> M.Expr -> Maybe Int +-- headArity env = case _ of +-- M.Var q@(Qualified (Just _) ident) -> +-- (_.arity <$> Object.lookup (qualifiedKeyOf q) env.ctors) +-- <|> Object.lookup (qualifiedKeyOf q) env.knownFuncs +-- <|> (snd <$> (qualifiedIntrinsic (qualifiedKeyOf q) <|> foreignIntrinsic ident)) +-- <|> ((Array.length <<< _.params) <$> Object.lookup (qualifiedKeyOf q) env.foreignSigs) +-- _ -> Nothing -- | Compile a `case` into a `Switch` on the scrutinee's tag, finishing each branch -- | with `finish` (so the same compiler serves a tail-position `case` — `finish = diff --git a/flake.lock b/flake.lock index df23883b..ef87629f 100644 --- a/flake.lock +++ b/flake.lock @@ -34,47 +34,13 @@ "type": "github" } }, - "nix-claude-code": { - "inputs": { - "nixpkgs": "nixpkgs" - }, - "locked": { - "lastModified": 1780030383, - "narHash": "sha256-vininGIqTM3wYHDejG3c/cfHgVQch4T8e2heXHP5emA=", - "owner": "ryoppippi", - "repo": "nix-claude-code", - "rev": "02fce910bfd96f2dc9e2df734bd53fbe198b2eb0", - "type": "github" - }, - "original": { - "owner": "ryoppippi", - "repo": "nix-claude-code", - "type": "github" - } - }, "nixpkgs": { "locked": { - "lastModified": 1779560665, - "narHash": "sha256-tpyBcxPpcQb8ukyNF7DoCwfSY3VPsxHoYwj00Cayv5o=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "64c08a7ca051951c8eae34e3e3cb1e202fe36786", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1779796641, - "narHash": "sha256-ZsIrKmhp4vbBXoXXmR/tBXA/UCsAQiJL9vsgZEduhVY=", + "lastModified": 1781509190, + "narHash": "sha256-uJZs9Di8I6ciTp6jiojj0HzlNpBkud8ax5aT/O5aJkw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "25f538306313eae3927264466c70d7001dcea1df", + "rev": "d6df3513510aa548c83868fd22bfddd0a8c0a0d4", "type": "github" }, "original": { @@ -92,11 +58,11 @@ ] }, "locked": { - "lastModified": 1775771885, - "narHash": "sha256-TP7e1+K6CXlpin2bxF97rwLASJXrXZDdZNKmkj1flM0=", + "lastModified": 1780972251, + "narHash": "sha256-PwHYPpfLP+WjSwj765zJ1N0Xp4ET3+8vRqxJ031ua3M=", "owner": "thomashoneyman", "repo": "purescript-overlay", - "rev": "0aaa4c2124622bd7d2f58c5758037f97c580ee73", + "rev": "1cf88ab9d83596db0e0c0d304a16809c410e2917", "type": "github" }, "original": { @@ -108,8 +74,7 @@ "root": { "inputs": { "flake-utils": "flake-utils", - "nix-claude-code": "nix-claude-code", - "nixpkgs": "nixpkgs_2", + "nixpkgs": "nixpkgs", "purescript-overlay": "purescript-overlay" } }, diff --git a/flake.nix b/flake.nix index 0c3b6029..bcd03c9d 100644 --- a/flake.nix +++ b/flake.nix @@ -6,10 +6,10 @@ url = "github:thomashoneyman/purescript-overlay"; inputs.nixpkgs.follows = "nixpkgs"; }; - nix-claude-code.url = "github:ryoppippi/nix-claude-code"; + # nix-claude-code.url = "github:ryoppippi/nix-claude-code"; }; - outputs = { self, nixpkgs, flake-utils, purescript-overlay, nix-claude-code }: + outputs = { self, nixpkgs, flake-utils, purescript-overlay }: flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { @@ -18,7 +18,7 @@ purescript-overlay.overlays.default ]; }; - claude-code = nix-claude-code.packages.${system}.default; + # claude-code = nix-claude-code.packages.${system}.default; # The published `purs-wasm` CLI (npm) wrapped to run on Nix. The npm package is self-contained # (an esbuild bundle + the precompiled ulib lib + the runtime); its only runtime need is the @@ -80,7 +80,7 @@ nodejs_24 pnpm gnuplot - claude-code + # claude-code ]; }; } From f41c1feb28729a375654b848d8d85654aeddcb0d Mon Sep 17 00:00:00 2001 From: harryprayiv Date: Tue, 16 Jun 2026 19:09:36 -0400 Subject: [PATCH 04/10] [Bugfix] ADR-0015: run a performed user Effect function instead of dropping it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A discarded `perform` of a non-inlined user-defined `Effect`-returning function (e.g. `perform (bad "x")`) silently dropped its effect: `isEffectForeignApp` treated any binding with an `MEffect` *reconstructed* signature (ADR-0016) as a host foreign, so it was lowered as a bare producer value — a partial closure built but never applied to the perform-unit — instead of a direct call. The fix excludes `knownFuncs` (anything with a decl body) from the host-foreign check, mirroring the existing intrinsic / `foreignIntrinsic` exclusions, and lowers a performed non-foreign application by appending the perform-unit to the producer's own argument list (ADR-0018) so it saturates to a direct `RCallKnown`. Host foreigns (`log`) are unaffected — the host still performs the returned thunk. Refs ADR-0015 (perform lowering), ADR-0016 (signature reconstruction), ADR-0018 (arity includes the perform-unit). --- compiler/spago.yaml | 1 + .../src/PureScript/Backend/Wasm/Lower.purs | 12 ++-- package-lock.json | 61 +++++++++++++++++++ pnpm-lock.yaml | 20 ------ spago.lock | 10 +++ 5 files changed, 80 insertions(+), 24 deletions(-) create mode 100644 package-lock.json diff --git a/compiler/spago.yaml b/compiler/spago.yaml index b558a020..81d13b23 100644 --- a/compiler/spago.yaml +++ b/compiler/spago.yaml @@ -31,6 +31,7 @@ package: - tailrec - transformers - tuples + - debug test: main: Test.Unit.Compiler dependencies: diff --git a/compiler/src/PureScript/Backend/Wasm/Lower.purs b/compiler/src/PureScript/Backend/Wasm/Lower.purs index 331e454c..1aff1b57 100644 --- a/compiler/src/PureScript/Backend/Wasm/Lower.purs +++ b/compiler/src/PureScript/Backend/Wasm/Lower.purs @@ -119,13 +119,17 @@ isEffectForeignApp env = case _ of M.Var q -> isEff q _ -> false where - -- An intrinsic (incl. the `Effect.Ref` ops and `modifyImpl`, ADR 0017) is NOT a host - -- foreign: it is performed via the unit-application path (so its arity includes the - -- perform-unit), even though source reconstruction (ADR 0016) also lists it in - -- `foreignSigs` with an `MEffect` result. Exclude it here so that path is taken. isEff q@(Qualified _ ident) | isJust (qualifiedIntrinsic (qualifiedKeyOf q)) = false | isJust (foreignIntrinsic ident) = false + -- A user-defined top-level function has a decl body, so it is in `knownFuncs`; it is + -- NOT a host foreign. It is performed via the unit-application path (its arity includes + -- the perform-unit, ADR 0018), even though source reconstruction (ADR 0016) also lists + -- it in `foreignSigs` with an `MEffect` result. Without this exclusion, a performed + -- partial application of such a function (`perform (bad "x")`) is misrouted to the + -- host-foreign path and lowered as a bare producer value — a partial closure that is + -- built but never applied to the unit, silently dropping the effect. + | Object.member (qualifiedKeyOf q) env.knownFuncs = false | otherwise = case Object.lookup (qualifiedKeyOf q) env.foreignSigs of Just sig -> case sig.result of MEffect _ -> true diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..06ddbe94 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,61 @@ +{ + "name": "purescript-backend-wasm", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "purescript-backend-wasm" + }, + "node_modules/.pnpm/binaryen@123.0.0/node_modules/binaryen": { + "version": "123.0.0", + "extraneous": true, + "license": "Apache-2.0", + "bin": { + "wasm-as": "bin/wasm-as", + "wasm-ctor-eval": "bin/wasm-ctor-eval", + "wasm-dis": "bin/wasm-dis", + "wasm-merge": "bin/wasm-merge", + "wasm-metadce": "bin/wasm-metadce", + "wasm-opt": "bin/wasm-opt", + "wasm-reduce": "bin/wasm-reduce", + "wasm-shell": "bin/wasm-shell", + "wasm2js": "bin/wasm2js" + }, + "devDependencies": { + "dateformat": "^5.0.3", + "doctoc": "^2.2.1", + "esbuild": "^0.13.15", + "semver": "^7.6.0", + "simple-git": "^3.24.0", + "typescript": "^4.9.5" + } + }, + "node_modules/.pnpm/cbor@10.0.12": {}, + "node_modules/.pnpm/nofilter@3.1.0/node_modules/nofilter": { + "version": "3.1.0", + "extraneous": true, + "license": "MIT", + "devDependencies": { + "@cto.af/eslint-config": "^0.0.10", + "@types/node": "^16.10.3", + "chai": "^4.3", + "chai-as-promised": "^7.1.1", + "coveralls": "^3.1.1", + "eslint": "^7.32.0", + "eslint-plugin-chai-friendly": "^0.7.2", + "eslint-plugin-jsdoc": "^36.1.1", + "eslint-plugin-markdown": "^2.2.1", + "eslint-plugin-node": "^11.1.0", + "jsdoc": "^3.6.7", + "minami": "^1.2.3", + "mocha": "^9.1.2", + "nyc": "^15.1.0", + "p-event": "^4.2.0", + "typescript": "^4.4.3" + }, + "engines": { + "node": ">=12.19" + } + } + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a19f89b9..370abadd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,12 +10,6 @@ importers: bench: {} - bench/bench/output-js-es: {} - - bench/output: {} - - bench/output-js-es: {} - binaryen: dependencies: binaryen: @@ -28,20 +22,6 @@ importers: specifier: ^10.0.12 version: 10.0.12 - compiler/test/e2e-fixtures-out: {} - - examples/output: {} - - examples/output-effect-prim: {} - - examples/output-meta: {} - - examples/output-priv: {} - - examples/output-random: {} - - examples/output-run: {} - purs-wasm: dependencies: binaryen: diff --git a/spago.lock b/spago.lock index 13d70682..fdd7b131 100644 --- a/spago.lock +++ b/spago.lock @@ -53,6 +53,7 @@ "binaryen", "console", "control", + "debug", "effect", "either", "exceptions", @@ -1215,6 +1216,15 @@ "tuples" ] }, + "debug": { + "type": "registry", + "version": "6.0.2", + "integrity": "sha256-d/EzRm/J2JyzvlIeo2Ex78Y7HRB0IXBitnVJywL4gHk=", + "dependencies": [ + "functions", + "prelude" + ] + }, "distributive": { "type": "registry", "version": "6.0.0", From 309bd83747323df6c29cfe041ec1949ebfb19e28 Mon Sep 17 00:00:00 2001 From: harryprayiv Date: Tue, 16 Jun 2026 20:25:17 -0400 Subject: [PATCH 05/10] =?UTF-8?q?[Test]=20ADR-0015:=20e2e=20guard=20for=20?= =?UTF-8?q?discarded=20performs=20of=20non-inlined=20user=20Effect=20fns?= =?UTF-8?q?=20=20This=20bug=20was=20invisible:=20nothing=20asserted=20that?= =?UTF-8?q?=20a=20discarded=20`perform=20(f=20x)`=20actually=20ran,=20so?= =?UTF-8?q?=20the=20dropped=20effect=20failed=20silently=20with=20a=20full?= =?UTF-8?q?y=20green=20suite.=20This=20adds=20the=20guard=20that=20would?= =?UTF-8?q?=20have=20caught=20it=20on=20day=20one.=20=20Adds=20fixture=20E?= =?UTF-8?q?2E.PerformUserEffect=20+=20its=20Cli=20spec.=20`bump`=20is=20a?= =?UTF-8?q?=20user-defined=20Effect=20function=20padded=20past=20the=20inl?= =?UTF-8?q?ine=20cap=20and=20referenced=20more=20than=20once,=20so=20its?= =?UTF-8?q?=20call=20site=20survives=20as=20`perform=20(bump=20k)`=20?= =?UTF-8?q?=E2=80=94=20the=20exact=20path=20that=20regressed;=20a=20small/?= =?UTF-8?q?inlined=20helper=20would=20pass=20and=20prove=20nothing.=20`bum?= =?UTF-8?q?p`=20performs=20a=20host=20foreign=20(`record`),=20so=20it=20la?= =?UTF-8?q?nds=20in=20impureKeys=20and=20its=20perform=20survives=20the=20?= =?UTF-8?q?simplifier=20=E2=80=94=20pinning=20any=20failure=20to=20lowerin?= =?UTF-8?q?g,=20not=20purity.=20The=20effect=20is=20read=20back=20as=20an?= =?UTF-8?q?=20accumulated=20total=20via=20callI32x1;=20that=20total=20equa?= =?UTF-8?q?ls=20the=20sum=20of=20the=20bumped=20values=20iff=20each=20perf?= =?UTF-8?q?orm=20ran=20exactly=20once=20(catching=20both=20drops,=20which?= =?UTF-8?q?=20read=20low,=20and=20accidental=20duplications,=20which=20rea?= =?UTF-8?q?d=20high).=20=20Three=20cases=20cover=20the=20discard=20paths?= =?UTF-8?q?=20that=20all=20route=20through=20the=20same=20isEffectForeignA?= =?UTF-8?q?pp=20fix:=20do-notation=20(bindBody)=20=3D>=2011,=20void=20(map?= =?UTF-8?q?Body)=20=3D>=202,=20and=20`*>`=20(applyBody)=20=3D>=207.=20Live?= =?UTF-8?q?s=20in=20the=20routinely-run=20e2e=20lane,=20not=20test:bin.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- compiler/test/E2E/Cli.purs | 2 + compiler/test/E2E/Cli/PerformUserEffect.purs | 31 ++++++++ e2e-fixtures/src/E2E/PerformUserEffect.js | 8 +++ e2e-fixtures/src/E2E/PerformUserEffect.purs | 56 +++++++++++++++ pnpm-lock.yaml | 76 ++++++++++++++++++++ 5 files changed, 173 insertions(+) create mode 100644 compiler/test/E2E/Cli/PerformUserEffect.purs create mode 100644 e2e-fixtures/src/E2E/PerformUserEffect.js create mode 100644 e2e-fixtures/src/E2E/PerformUserEffect.purs diff --git a/compiler/test/E2E/Cli.purs b/compiler/test/E2E/Cli.purs index 686392cc..2509361b 100644 --- a/compiler/test/E2E/Cli.purs +++ b/compiler/test/E2E/Cli.purs @@ -24,6 +24,7 @@ import Test.E2E.Cli.FibAnd as FibAnd import Test.E2E.Cli.IntConv as IntConv import Test.E2E.Cli.Link as Link import Test.E2E.Cli.NestedRecordPat as NestedRecordPat +import Test.E2E.Cli.PerformUserEffect as PerformUserEffect import Test.E2E.Cli.PreludeArith as PreludeArith import Test.E2E.Cli.PreludeBool as PreludeBool import Test.E2E.Cli.PreludeBounded as PreludeBounded @@ -108,3 +109,4 @@ main = runSpecAndExitProcess [ consoleReporter ] do ForeignRecord.spec ForeignExport.spec ForeignEffect.spec + PerformUserEffect.spec diff --git a/compiler/test/E2E/Cli/PerformUserEffect.purs b/compiler/test/E2E/Cli/PerformUserEffect.purs new file mode 100644 index 00000000..f290e5a2 --- /dev/null +++ b/compiler/test/E2E/Cli/PerformUserEffect.purs @@ -0,0 +1,31 @@ +-- | CLI-driven e2e (ADR 0031 phase 5) regression guard for ADR-0015: a discarded +-- | `perform (f x)` of a NON-inlined user-defined `Effect` function must run, across the +-- | three discard combinators (do-notation `bindBody`, `void` `mapBody`, `*>` `applyBody`). +-- | `bump` records its argument; each entry reads the recorded total back, which equals the +-- | sum of the bumped values iff every perform ran exactly once. Before the fix these read 0 +-- | — the performs were lowered as partial closures never applied to the perform-unit. +module Test.E2E.Cli.PerformUserEffect (spec) where + +import Prelude + +import Effect.Class (liftEffect) +import Test.E2E.Cli.Loader (callI32x1, loadExports) +import Test.Spec (Spec, before, describe, it) +import Test.Spec.Assertions (shouldEqual) + +spec :: Spec Unit +spec = + describe "Performed user Effect fn (e2e/cli): discarded `perform (f x)` of a non-inlined user Effect fn runs across bind/map/apply discard -> purs-wasm build -> run (ADR-0015 regression)" + $ before (loadExports "E2E.PerformUserEffect") + $ do + it "do-notation discard (bindBody): bump 1; bump 10 => 11" \exp -> do + r <- liftEffect (callI32x1 exp "runBumps" 0) + r `shouldEqual` 11 + + it "void discard (mapBody): void (bump 2) => 2" \exp -> do + r <- liftEffect (callI32x1 exp "runVoid" 0) + r `shouldEqual` 2 + + it "applySecond discard (applyBody): bump 3 *> bump 4 => 7" \exp -> do + r <- liftEffect (callI32x1 exp "runSeq" 0) + r `shouldEqual` 7 \ No newline at end of file diff --git a/e2e-fixtures/src/E2E/PerformUserEffect.js b/e2e-fixtures/src/E2E/PerformUserEffect.js new file mode 100644 index 00000000..4e9f7447 --- /dev/null +++ b/e2e-fixtures/src/E2E/PerformUserEffect.js @@ -0,0 +1,8 @@ +let recorded = 0; +export const reset = () => { + recorded = 0; +}; +export const record = (n) => () => { + recorded += n; +}; +export const total = () => recorded; \ No newline at end of file diff --git a/e2e-fixtures/src/E2E/PerformUserEffect.purs b/e2e-fixtures/src/E2E/PerformUserEffect.purs new file mode 100644 index 00000000..15f089dc --- /dev/null +++ b/e2e-fixtures/src/E2E/PerformUserEffect.purs @@ -0,0 +1,56 @@ +-- e2e fixture (ADR 0031 phase 5): ADR-0015 regression guard. `bump` is a user-defined, +-- deliberately un-inlinable Effect function that performs the host foreign `record`. +-- Each entry performs `bump` with the result DISCARDED through a different combinator — +-- do-notation (`bindBody`), `void` (`mapBody`), and `*>` (`applyBody`) — then reads the +-- recorded total. The bug: a discarded `perform (bump k)` for a non-inlined user Effect +-- function was misrouted to the host-foreign lowering path (`isEffectForeignApp` matched +-- it because ADR-0016 gives it an `MEffect` reconstructed sig) and built as a partial +-- closure never applied to the perform-unit — silently dropping the effect. `total` reads +-- the sum of what actually ran; each entry's expected value is the sum of its bumped `k`s. +module E2E.PerformUserEffect where + +import Prelude + +import Effect (Effect) +import Effect.Unsafe (unsafePerformEffect) + +foreign import record :: Int -> Effect Unit +foreign import total :: Effect Int +foreign import reset :: Effect Unit + +-- Past the inline caps (Inline 24 / DictElim 32) and used many times, so it stays a +-- SEPARATE binding and the call sites survive as `perform (bump k)` — the regressed path. +-- The `record 0`s are genuine (kept) effects; pure padding would be DCE'd and could shrink +-- `bump` back under the cap, silently reverting these tests to the inlined path. +bump :: Int -> Effect Unit +bump k = do + record k + record 0 + record 0 + record 0 + record 0 + record 0 + record 0 + record 0 + +-- do-notation discard (bindBody path) => 1 + 10 = 11 +runBumps :: Int -> Int +runBumps _ = unsafePerformEffect do + reset + bump 1 + bump 10 + total + +-- void / Functor-map discard (mapBody path) => 2 +runVoid :: Int -> Int +runVoid _ = unsafePerformEffect do + reset + void (bump 2) + total + +-- applySecond / `*>` discard (applyBody path) => 3 + 4 = 7 +runSeq :: Int -> Int +runSeq _ = unsafePerformEffect do + reset + bump 3 *> bump 4 + total \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 370abadd..299f3e88 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,6 +10,8 @@ importers: bench: {} + bench/output: {} + binaryen: dependencies: binaryen: @@ -22,6 +24,80 @@ importers: specifier: ^10.0.12 version: 10.0.12 + compiler/test/e2e-build/E2E.Arrays: {} + + compiler/test/e2e-build/E2E.AsPattern: {} + + compiler/test/e2e-build/E2E.CafForeign: {} + + compiler/test/e2e-build/E2E.Closures: {} + + compiler/test/e2e-build/E2E.CountState: {} + + compiler/test/e2e-build/E2E.Counter: {} + + compiler/test/e2e-build/E2E.DataTypes: {} + + compiler/test/e2e-build/E2E.EffP: {} + + compiler/test/e2e-build/E2E.Expr: {} + + compiler/test/e2e-build/E2E.FFIScalar: {} + + compiler/test/e2e-build/E2E.FibAnd: {} + + compiler/test/e2e-build/E2E.Fld: {} + + compiler/test/e2e-build/E2E.ForeignArray: {} + + compiler/test/e2e-build/E2E.ForeignClosure: {} + + compiler/test/e2e-build/E2E.ForeignEffect: {} + + compiler/test/e2e-build/E2E.ForeignExport: {} + + compiler/test/e2e-build/E2E.ForeignRecord: {} + + compiler/test/e2e-build/E2E.ForeignString: {} + + compiler/test/e2e-build/E2E.GenSC: {} + + compiler/test/e2e-build/E2E.Guards: {} + + compiler/test/e2e-build/E2E.LinkB: {} + + compiler/test/e2e-build/E2E.Literals: {} + + compiler/test/e2e-build/E2E.Mnd: {} + + compiler/test/e2e-build/E2E.Mon: {} + + compiler/test/e2e-build/E2E.NestedRecordPat: {} + + compiler/test/e2e-build/E2E.PerformUserEffect: {} + + compiler/test/e2e-build/E2E.Rec: {} + + compiler/test/e2e-build/E2E.RecInst: {} + + compiler/test/e2e-build/E2E.RecordMeta: {} + + compiler/test/e2e-build/E2E.Records: {} + + compiler/test/e2e-build/E2E.Recursion: {} + + compiler/test/e2e-build/E2E.Sgp: {} + + compiler/test/e2e-build/E2E.ShowNumber: {} + + compiler/test/e2e-build/E2E.Strings: {} + + compiler/test/e2e-build/E2E.TypeClasses: {} + + compiler/test/e2e-build/E2E.TypeClassesSuper: {} + + compiler/test/e2e-fixtures-out: {} + purs-wasm: dependencies: binaryen: From 74356c0a92c60150c60d2905171ed79b59c17752 Mon Sep 17 00:00:00 2001 From: harryprayiv Date: Tue, 16 Jun 2026 20:26:45 -0400 Subject: [PATCH 06/10] drop debug --- compiler/spago.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/spago.yaml b/compiler/spago.yaml index 81d13b23..b558a020 100644 --- a/compiler/spago.yaml +++ b/compiler/spago.yaml @@ -31,7 +31,6 @@ package: - tailrec - transformers - tuples - - debug test: main: Test.Unit.Compiler dependencies: From 7733755e08026ba3562b624548deca3d9219a48a Mon Sep 17 00:00:00 2001 From: katsujukou Date: Thu, 18 Jun 2026 02:01:26 +0900 Subject: [PATCH 07/10] [Docs] ADR-0017: correct the origin of `isEffectForeignApp` over-coverage, which is `Externs.foreignSigs`, not ADR0016 --- .../0017-native-mutable-references.md | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/design-decisions/0017-native-mutable-references.md b/docs/design-decisions/0017-native-mutable-references.md index 5fb50399..f38499a6 100644 --- a/docs/design-decisions/0017-native-mutable-references.md +++ b/docs/design-decisions/0017-native-mutable-references.md @@ -3,6 +3,31 @@ - Status: Accepted - Date: 2026-06-04 +> **Update (2026-06-18) — the `isEffectForeignApp` non-host exclusion extends to *all* known +> functions, and the over-coverage it guards against is `Externs.foreignSigs`, not ADR 0016.** +> A dropped-effect bug (PR #42, guard `Test.E2E.Cli.PerformUserEffect`) refined the Decision's +> intrinsic-exclusion bullet on two points: +> - **The `foreignSigs` over-coverage originates in `Externs.foreignSigs`, not (only) the ADR 0016 +> source path.** `Externs.foreignSigs` is keyed off **every `EDValue` — every *exported* +> top-level value, ordinary functions included** — because the externs format does not flag which +> values are `foreign import`s (a foreign and a plain value are both `EDValue ident type`); its own +> doc comment notes the extra entries are "inert" *because foreign resolution only consults a sig +> where the name is an actual unresolved import*. ADR 0016 source reconstruction adds the +> *private*-foreign half on top, but a non-foreign user function (e.g. an exported +> `bump :: … -> Effect …`) lands in `foreignSigs` via the **externs** path, not the source path — +> so the Decision's "source reconstruction (ADR 0016) also lists these" is, more precisely, the +> externs `EDValue` coverage. +> - **`MEffect`-result is not a sound host-foreign classifier; exclude `knownFuncs`.** +> `isEffectForeignApp` broke the "inert" assumption by using a sig's `MEffect` result as the +> *classifier* for "is this a host foreign?". An exported `Effect`-returning **user function** then +> matched and was lowered as a bare producer value — a partial closure built but never applied to +> the perform-unit — silently dropping the effect when its `perform` was discarded (`void` / `*>` / +> a `do` statement). The fix excludes any binding in `knownFuncs` (anything with a decl body) from +> the predicate, mirroring the intrinsic / `foreignIntrinsic` exclusions: a binding we have a body +> for is by definition not an opaque host foreign — it is performed via the unit-application path +> (its arity includes the perform-unit, ADR 0018), so a performed application saturates to a direct +> `RCallKnown`. Refs ADR 0015 (perform lowering), ADR 0018 (perform-unit arity). + ## Context `Effect.Ref` (and `Control.Monad.ST`) provide a mutable cell. The standard library From f0f8e1779e07535f5cf98410fd177d4514fd03b3 Mon Sep 17 00:00:00 2001 From: katsujukou Date: Thu, 18 Jun 2026 02:02:15 +0900 Subject: [PATCH 08/10] [Docs] Intrinsics: doc comments for bit-wise operators --- compiler/src/PureScript/Backend/Wasm/Intrinsics.purs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/PureScript/Backend/Wasm/Intrinsics.purs b/compiler/src/PureScript/Backend/Wasm/Intrinsics.purs index 5967ec4e..d3a89ec5 100644 --- a/compiler/src/PureScript/Backend/Wasm/Intrinsics.purs +++ b/compiler/src/PureScript/Backend/Wasm/Intrinsics.purs @@ -143,9 +143,9 @@ data Intrinsic -- | applying it to the unit (the erased `Partial` dictionary). Native so the wasm -- | closure never crosses to the JS foreign (which would call it as `f()`). | UnsafePartial - -- `Data.Int.Bits` 32-bit bitwise ops (JS `& | ^ << >> >>> ~`), each a single - -- i32 instruction. `IntShr` is *arithmetic* (sign-propagating, JS `>>`); - -- `IntZshr` is *logical* (zero-fill, JS `>>>`); `IntComplement x` = `x ^ -1`. + -- | `Data.Int.Bits` 32-bit bitwise ops (JS `& | ^ << >> >>> ~`), each a single + -- | i32 instruction. `IntShr` is *arithmetic* (sign-propagating, JS `>>`); + -- | `IntZshr` is *logical* (zero-fill, JS `>>>`); `IntComplement x` = `x ^ -1`. | IntAnd | IntOr | IntXor From 5b1561fe7ed5bfe109231883395373945b5fd6d8 Mon Sep 17 00:00:00 2001 From: katsujukou Date: Thu, 18 Jun 2026 02:09:44 +0900 Subject: [PATCH 09/10] [Docs] Lower: remove unnecessary comments --- compiler/src/PureScript/Backend/Wasm/Lower.purs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/compiler/src/PureScript/Backend/Wasm/Lower.purs b/compiler/src/PureScript/Backend/Wasm/Lower.purs index 1aff1b57..c8fc80c9 100644 --- a/compiler/src/PureScript/Backend/Wasm/Lower.purs +++ b/compiler/src/PureScript/Backend/Wasm/Lower.purs @@ -529,20 +529,6 @@ recBindEtaArity env = case _ of <|> (snd <$> (qualifiedIntrinsic (qualifiedKeyOf q) <|> foreignIntrinsic ident)) <|> ((Array.length <<< _.params) <$> Object.lookup (qualifiedKeyOf q) env.foreignSigs) --- | The declared arity of an application head, resolved the same way `lowerApp` dispatches a call: --- | a constructor, a top-level/specialized function, an intrinsic, or a foreign import. --- headArity is now dead code. My patch routes the performed call back through lowerApp/applyArity, --- which recomputes arity itself, so the standalone headArity lookup (which the old Perform branch was the only caller of) is now orphaned - --- headArity :: Env -> M.Expr -> Maybe Int --- headArity env = case _ of --- M.Var q@(Qualified (Just _) ident) -> --- (_.arity <$> Object.lookup (qualifiedKeyOf q) env.ctors) --- <|> Object.lookup (qualifiedKeyOf q) env.knownFuncs --- <|> (snd <$> (qualifiedIntrinsic (qualifiedKeyOf q) <|> foreignIntrinsic ident)) --- <|> ((Array.length <<< _.params) <$> Object.lookup (qualifiedKeyOf q) env.foreignSigs) --- _ -> Nothing - -- | Compile a `case` into a `Switch` on the scrutinee's tag, finishing each branch -- | with `finish` (so the same compiler serves a tail-position `case` — `finish = -- | lowerTail` — and an argument-position one, where `finish` feeds the branch From fa863e93ddc95b90de686ae91056d185a5ea0551 Mon Sep 17 00:00:00 2001 From: katsujukou Date: Thu, 18 Jun 2026 02:15:29 +0900 Subject: [PATCH 10/10] [Chore] repo: reverte project configuration files --- flake.lock | 51 +++++++++++++++++++---- flake.nix | 10 ++--- package-lock.json | 61 --------------------------- pnpm-lock.yaml | 102 ++++++++++++---------------------------------- spago.lock | 10 ----- 5 files changed, 74 insertions(+), 160 deletions(-) delete mode 100644 package-lock.json diff --git a/flake.lock b/flake.lock index ef87629f..4fcdbe52 100644 --- a/flake.lock +++ b/flake.lock @@ -34,13 +34,47 @@ "type": "github" } }, + "nix-claude-code": { + "inputs": { + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1780030383, + "narHash": "sha256-vininGIqTM3wYHDejG3c/cfHgVQch4T8e2heXHP5emA=", + "owner": "ryoppippi", + "repo": "nix-claude-code", + "rev": "02fce910bfd96f2dc9e2df734bd53fbe198b2eb0", + "type": "github" + }, + "original": { + "owner": "ryoppippi", + "repo": "nix-claude-code", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1781509190, - "narHash": "sha256-uJZs9Di8I6ciTp6jiojj0HzlNpBkud8ax5aT/O5aJkw=", + "lastModified": 1779560665, + "narHash": "sha256-tpyBcxPpcQb8ukyNF7DoCwfSY3VPsxHoYwj00Cayv5o=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "64c08a7ca051951c8eae34e3e3cb1e202fe36786", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1779796641, + "narHash": "sha256-ZsIrKmhp4vbBXoXXmR/tBXA/UCsAQiJL9vsgZEduhVY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d6df3513510aa548c83868fd22bfddd0a8c0a0d4", + "rev": "25f538306313eae3927264466c70d7001dcea1df", "type": "github" }, "original": { @@ -58,11 +92,11 @@ ] }, "locked": { - "lastModified": 1780972251, - "narHash": "sha256-PwHYPpfLP+WjSwj765zJ1N0Xp4ET3+8vRqxJ031ua3M=", + "lastModified": 1775771885, + "narHash": "sha256-TP7e1+K6CXlpin2bxF97rwLASJXrXZDdZNKmkj1flM0=", "owner": "thomashoneyman", "repo": "purescript-overlay", - "rev": "1cf88ab9d83596db0e0c0d304a16809c410e2917", + "rev": "0aaa4c2124622bd7d2f58c5758037f97c580ee73", "type": "github" }, "original": { @@ -74,7 +108,8 @@ "root": { "inputs": { "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs", + "nix-claude-code": "nix-claude-code", + "nixpkgs": "nixpkgs_2", "purescript-overlay": "purescript-overlay" } }, @@ -96,4 +131,4 @@ }, "root": "root", "version": 7 -} +} \ No newline at end of file diff --git a/flake.nix b/flake.nix index bcd03c9d..da3d08d3 100644 --- a/flake.nix +++ b/flake.nix @@ -6,10 +6,10 @@ url = "github:thomashoneyman/purescript-overlay"; inputs.nixpkgs.follows = "nixpkgs"; }; - # nix-claude-code.url = "github:ryoppippi/nix-claude-code"; + nix-claude-code.url = "github:ryoppippi/nix-claude-code"; }; - outputs = { self, nixpkgs, flake-utils, purescript-overlay }: + outputs = { self, nixpkgs, flake-utils, purescript-overlay, nix-claude-code }: flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { @@ -18,7 +18,7 @@ purescript-overlay.overlays.default ]; }; - # claude-code = nix-claude-code.packages.${system}.default; + claude-code = nix-claude-code.packages.${system}.default; # The published `purs-wasm` CLI (npm) wrapped to run on Nix. The npm package is self-contained # (an esbuild bundle + the precompiled ulib lib + the runtime); its only runtime need is the @@ -80,9 +80,9 @@ nodejs_24 pnpm gnuplot - # claude-code + claude-code ]; }; } ); -} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 06ddbe94..00000000 --- a/package-lock.json +++ /dev/null @@ -1,61 +0,0 @@ -{ - "name": "purescript-backend-wasm", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "purescript-backend-wasm" - }, - "node_modules/.pnpm/binaryen@123.0.0/node_modules/binaryen": { - "version": "123.0.0", - "extraneous": true, - "license": "Apache-2.0", - "bin": { - "wasm-as": "bin/wasm-as", - "wasm-ctor-eval": "bin/wasm-ctor-eval", - "wasm-dis": "bin/wasm-dis", - "wasm-merge": "bin/wasm-merge", - "wasm-metadce": "bin/wasm-metadce", - "wasm-opt": "bin/wasm-opt", - "wasm-reduce": "bin/wasm-reduce", - "wasm-shell": "bin/wasm-shell", - "wasm2js": "bin/wasm2js" - }, - "devDependencies": { - "dateformat": "^5.0.3", - "doctoc": "^2.2.1", - "esbuild": "^0.13.15", - "semver": "^7.6.0", - "simple-git": "^3.24.0", - "typescript": "^4.9.5" - } - }, - "node_modules/.pnpm/cbor@10.0.12": {}, - "node_modules/.pnpm/nofilter@3.1.0/node_modules/nofilter": { - "version": "3.1.0", - "extraneous": true, - "license": "MIT", - "devDependencies": { - "@cto.af/eslint-config": "^0.0.10", - "@types/node": "^16.10.3", - "chai": "^4.3", - "chai-as-promised": "^7.1.1", - "coveralls": "^3.1.1", - "eslint": "^7.32.0", - "eslint-plugin-chai-friendly": "^0.7.2", - "eslint-plugin-jsdoc": "^36.1.1", - "eslint-plugin-markdown": "^2.2.1", - "eslint-plugin-node": "^11.1.0", - "jsdoc": "^3.6.7", - "minami": "^1.2.3", - "mocha": "^9.1.2", - "nyc": "^15.1.0", - "p-event": "^4.2.0", - "typescript": "^4.4.3" - }, - "engines": { - "node": ">=12.19" - } - } - } -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 299f3e88..63beb707 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,17 +1,20 @@ -lockfileVersion: '9.0' +lockfileVersion: "9.0" settings: autoInstallPeers: true excludeLinksFromLockfile: false importers: - .: {} bench: {} + bench/bench/output-js-es: {} + bench/output: {} + bench/output-js-es: {} + binaryen: dependencies: binaryen: @@ -24,79 +27,19 @@ importers: specifier: ^10.0.12 version: 10.0.12 - compiler/test/e2e-build/E2E.Arrays: {} - - compiler/test/e2e-build/E2E.AsPattern: {} - - compiler/test/e2e-build/E2E.CafForeign: {} - - compiler/test/e2e-build/E2E.Closures: {} - - compiler/test/e2e-build/E2E.CountState: {} - - compiler/test/e2e-build/E2E.Counter: {} - - compiler/test/e2e-build/E2E.DataTypes: {} - - compiler/test/e2e-build/E2E.EffP: {} - - compiler/test/e2e-build/E2E.Expr: {} - - compiler/test/e2e-build/E2E.FFIScalar: {} - - compiler/test/e2e-build/E2E.FibAnd: {} - - compiler/test/e2e-build/E2E.Fld: {} - - compiler/test/e2e-build/E2E.ForeignArray: {} - - compiler/test/e2e-build/E2E.ForeignClosure: {} - - compiler/test/e2e-build/E2E.ForeignEffect: {} - - compiler/test/e2e-build/E2E.ForeignExport: {} - - compiler/test/e2e-build/E2E.ForeignRecord: {} - - compiler/test/e2e-build/E2E.ForeignString: {} - - compiler/test/e2e-build/E2E.GenSC: {} - - compiler/test/e2e-build/E2E.Guards: {} - - compiler/test/e2e-build/E2E.LinkB: {} - - compiler/test/e2e-build/E2E.Literals: {} - - compiler/test/e2e-build/E2E.Mnd: {} - - compiler/test/e2e-build/E2E.Mon: {} - - compiler/test/e2e-build/E2E.NestedRecordPat: {} - - compiler/test/e2e-build/E2E.PerformUserEffect: {} - - compiler/test/e2e-build/E2E.Rec: {} - - compiler/test/e2e-build/E2E.RecInst: {} - - compiler/test/e2e-build/E2E.RecordMeta: {} - - compiler/test/e2e-build/E2E.Records: {} - - compiler/test/e2e-build/E2E.Recursion: {} + compiler/test/e2e-fixtures-out: {} - compiler/test/e2e-build/E2E.Sgp: {} + examples/output: {} - compiler/test/e2e-build/E2E.ShowNumber: {} + examples/output-effect-prim: {} - compiler/test/e2e-build/E2E.Strings: {} + examples/output-meta: {} - compiler/test/e2e-build/E2E.TypeClasses: {} + examples/output-priv: {} - compiler/test/e2e-build/E2E.TypeClassesSuper: {} + examples/output-random: {} - compiler/test/e2e-fixtures-out: {} + examples/output-run: {} purs-wasm: dependencies: @@ -107,21 +50,28 @@ importers: ulib-tooling: {} packages: - binaryen@123.0.0: - resolution: {integrity: sha512-/hls/a309aZCc0itqP6uhoR+5DsKSlJVfB8Opd2BY9Ndghs84IScTunlyidyF4r2Xe3lQttnfBNIDjaNpj6mTw==} + resolution: + { + integrity: sha512-/hls/a309aZCc0itqP6uhoR+5DsKSlJVfB8Opd2BY9Ndghs84IScTunlyidyF4r2Xe3lQttnfBNIDjaNpj6mTw==, + } hasBin: true cbor@10.0.12: - resolution: {integrity: sha512-exQDevYd7ZQLP4moMQcZkKCVZsXLAtUSflObr3xTh4xzFIv/xBCdvCd6L259kQOUP2kcTC0jvC6PpZIf/WmRXA==} - engines: {node: '>=20'} + resolution: + { + integrity: sha512-exQDevYd7ZQLP4moMQcZkKCVZsXLAtUSflObr3xTh4xzFIv/xBCdvCd6L259kQOUP2kcTC0jvC6PpZIf/WmRXA==, + } + engines: { node: ">=20" } nofilter@3.1.0: - resolution: {integrity: sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==} - engines: {node: '>=12.19'} + resolution: + { + integrity: sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==, + } + engines: { node: ">=12.19" } snapshots: - binaryen@123.0.0: {} cbor@10.0.12: diff --git a/spago.lock b/spago.lock index fdd7b131..13d70682 100644 --- a/spago.lock +++ b/spago.lock @@ -53,7 +53,6 @@ "binaryen", "console", "control", - "debug", "effect", "either", "exceptions", @@ -1216,15 +1215,6 @@ "tuples" ] }, - "debug": { - "type": "registry", - "version": "6.0.2", - "integrity": "sha256-d/EzRm/J2JyzvlIeo2Ex78Y7HRB0IXBitnVJywL4gHk=", - "dependencies": [ - "functions", - "prelude" - ] - }, "distributive": { "type": "registry", "version": "6.0.0",