Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@ jobs:
run: if [ -f scripts/test ]; then nix develop -c bash ./scripts/test; fi

- name: Luacheck
run: nix develop -c luacheck --quiet --std lua51 --no-unused-args src/
run: nix develop -c luacheck --quiet --std lua51 --no-unused-args --max-line-length 130 src/

- name: Format check
run: nix fmt && git diff --exit-code
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/.*
!/.gitignore
!/.github/
!/.tidyrc.json
!/.lua-format
/output/
10 changes: 10 additions & 0 deletions .lua-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# LuaFormatter config for the hand-written FFI under src/.
# 2-space indent. Keep simple functions on one line; column_limit sits a few
# columns under luacheck's 130 limit because lua-format under-counts the leading
# indent and trailing comma, so this keeps every emitted line within 130.
indent_width: 2
use_tab: false
column_limit: 126
continuation_indent_width: 2
keep_simple_function_one_line: true
keep_simple_control_block_one_line: true
10 changes: 10 additions & 0 deletions .tidyrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"importSort": "source",
"importWrap": "source",
"indent": 2,
"operatorsFile": null,
"ribbon": 1,
"typeArrowPlacement": "first",
"unicode": "source",
"width": 80
}
16 changes: 13 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,21 @@ A PureScript→Lua FFI fork in the [`purescript-lua`](https://github.com/purescr

## Commands

All commands run inside the nix dev shell:

- Build: `nix develop -c ./scripts/build`
- Test: `nix develop -c bash ./scripts/test` (only forks that ship Lua regression tests have one)
- Lint: `nix develop -c luacheck --quiet --std lua51 --no-unused-args src/`
- Lint: `nix develop -c luacheck --quiet --std lua51 --no-unused-args --max-line-length 130 src/`
- Format: `nix fmt` (check: `nix fmt && git diff --exit-code`)

## Formatting

`nix fmt` runs treefmt (`treefmt.nix`): nixfmt for Nix, `dhall format`, purs-tidy
for `*.purs` (config in `.tidyrc.json`), and LuaFormatter for the `*.lua` FFI
(config in `.lua-format`). LuaFormatter is used over StyLua because it keeps the
parentheses pslua's foreign-file parser requires. The Lua line budget is 130
columns, matching the `luacheck --max-line-length` above. The check is
content-based (`nix fmt && git diff --exit-code`) rather than `treefmt --ci`,
since the in-place formatters bump mtime even when content is unchanged, which
trips treefmt's `--fail-on-change`. CI and the pre-commit hook use it.

## Lua 5.1 target

Expand Down
7 changes: 4 additions & 3 deletions bench/Data/Array.purs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ benchArray = do
where

benchMapMaybe = do
let shortNats = Array.range 0 100
longNats = Array.range 0 10000
onlyEven x = if x `mod` 2 == 0 then Just x else Nothing
let
shortNats = Array.range 0 100
longNats = Array.range 0 10000
onlyEven x = if x `mod` 2 == 0 then Just x else Nothing

log $ "mapMaybe (" <> show (Array.length shortNats) <> ")"
benchWith 1000 \_ -> Array.mapMaybe onlyEven shortNats
Expand Down
23 changes: 22 additions & 1 deletion flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 39 additions & 4 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,33 @@
inputs.nixpkgs.follows = "nixpkgs";
};
pslua.url = "github:purescript-lua/purescript-lua";
treefmt-nix = {
url = "github:numtide/treefmt-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};

outputs = { self, nixpkgs, flake-utils, purescript-overlay, pslua }:
flake-utils.lib.eachDefaultSystem (system:
outputs =
{
self,
nixpkgs,
flake-utils,
purescript-overlay,
pslua,
treefmt-nix,
}:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [ purescript-overlay.overlays.default ];
};
in {
treefmtEval = treefmt-nix.lib.evalModule pkgs ./treefmt.nix;
in
{
formatter = treefmtEval.config.build.wrapper;
checks.formatting = treefmtEval.config.build.check self;
devShell = pkgs.mkShell {
buildInputs = with pkgs; [
dhall
Expand All @@ -31,8 +48,26 @@
spago-bin.spago-0_21_0
treefmt
];
# Install a content-based pre-commit hook. It compares the working
# tree diff before and after `nix fmt`, so it only objects to changes
# the formatter itself introduces (not the developer's existing
# unstaged work) and is not fooled by formatters that only bump mtime.
# Rewritten each shell entry to stay in sync with this flake.
shellHook = ''
hook=.git/hooks/pre-commit
if [ -d .git ]; then
printf '%s\n' \
'#!/usr/bin/env bash' \
'before=$(git diff)' \
'nix fmt >/dev/null 2>&1 || exit 0' \
'[ "$before" = "$(git diff)" ] || { echo "nix fmt changed files; re-stage them, then commit." >&2; exit 1; }' \
> "$hook"
chmod +x "$hook"
fi
'';
Comment thread
Unisay marked this conversation as resolved.
};
});
}
);

# --- Flake Local Nix Configuration ----------------------------
nixConfig = {
Expand Down
12 changes: 6 additions & 6 deletions src/Data/Array.lua
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ return {
length = (function(xs) return #xs end),
unconsImpl = (function(empty, next, xs)
if #xs == 0 then return empty({}) end
return next(xs[1])({ unpack(xs, 2) })
return next(xs[1])({unpack(xs, 2)})
end),
indexImpl = (function(just, nothing, xs, i)
if i < 0 or i >= #xs then
Expand All @@ -67,19 +67,19 @@ return {
end),
_insertAt = (function(just, nothing, i, a, l)
if i < 0 or i > #l then return nothing end
local l1 = { unpack(l) }
local l1 = {unpack(l)}
table.insert(l1, i + 1, a)
return just(l1)
end),
_deleteAt = (function(just, nothing, i, l)
if i < 0 or i >= #l then return nothing end
local l1 = { unpack(l) }
local l1 = {unpack(l)}
table.remove(l1, i + 1)
return just(l1)
end),
_updateAt = (function(just, nothing, i, f, l)
if i < 0 or i >= #l then return nothing end
local l1 = { unpack(l) }
local l1 = {unpack(l)}
l1[i + 1] = f(l1[i + 1])
return just(l1)
end),
Expand Down Expand Up @@ -185,8 +185,8 @@ return {

return function(compare, fromOrdering, xs)
if #xs < 2 then return xs end
local out = { unpack(xs) }
local slice = { unpack(xs) }
local out = {unpack(xs)}
local slice = {unpack(xs)}
mergeFromTo(compare, fromOrdering, out, slice, 0, #xs)
return out
end
Expand Down
30 changes: 20 additions & 10 deletions src/Data/Array.purs
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,8 @@ splitAt i xs = { before: slice 0 i xs, after: slice i (length xs) xs }
-- | powerSet :: forall a. Array a -> Array (Array a)
-- | powerSet = filterA (const [true, false])
-- | ```
filterA :: forall a f. Applicative f => (a -> f Boolean) -> Array a -> f (Array a)
filterA
:: forall a f. Applicative f => (a -> f Boolean) -> Array a -> f (Array a)
filterA p =
traverse (\x -> Tuple x <$> p x)
>>> map (mapMaybe (\(Tuple x b) -> if b then Just x else Nothing))
Expand Down Expand Up @@ -771,9 +772,11 @@ mapWithIndex = FWI.mapWithIndex
-- | updateAtIndices updates ["Hello", "World", "!"] = ["Hi", "World", "."]
-- | ```
-- |
updateAtIndices :: forall t a. Foldable t => t (Tuple Int a) -> Array a -> Array a
updateAtIndices
:: forall t a. Foldable t => t (Tuple Int a) -> Array a -> Array a
updateAtIndices us xs =
ST.run (STA.withArray (\res -> traverse_ (\(Tuple i a) -> STA.poke i a res) us) xs)
ST.run
(STA.withArray (\res -> traverse_ (\(Tuple i a) -> STA.poke i a res) us) xs)

-- | Apply a function to the element at the specified indices,
-- | creating a new array. Out-of-bounds indices will have no effect.
Expand All @@ -784,7 +787,8 @@ updateAtIndices us xs =
-- | = ["Hello", "WORLD", "and", "OTHERS"]
-- | ```
-- |
modifyAtIndices :: forall t a. Foldable t => t Int -> (a -> a) -> Array a -> Array a
modifyAtIndices
:: forall t a. Foldable t => t Int -> (a -> a) -> Array a -> Array a
modifyAtIndices is f xs =
ST.run (STA.withArray (\res -> traverse_ (\i -> STA.modify i f res) is) xs)

Expand Down Expand Up @@ -841,7 +845,8 @@ transpose xs = go 0 []
buildNext :: Int -> Maybe (Array a)
buildNext idx = do
xs # flip foldl Nothing \acc nextArr -> do
maybe acc (\el -> Just $ maybe [ el ] (flip snoc el) acc) $ index nextArr idx
maybe acc (\el -> Just $ maybe [ el ] (flip snoc el) acc) $ index nextArr
idx

-- | Fold a data structure from the left, keeping all intermediate results
-- | instead of only the final result. Note that the initial value does not
Expand Down Expand Up @@ -911,7 +916,8 @@ sortBy comp = runFn3 sortByImpl comp case _ of
sortWith :: forall a b. Ord b => (a -> b) -> Array a -> Array a
sortWith f = sortBy (comparing f)

foreign import sortByImpl :: forall a. Fn3 (a -> a -> Ordering) (Ordering -> Int) (Array a) (Array a)
foreign import sortByImpl
:: forall a. Fn3 (a -> a -> Ordering) (Ordering -> Int) (Array a) (Array a)

--------------------------------------------------------------------------------
-- Subarrays -------------------------------------------------------------------
Expand Down Expand Up @@ -1081,7 +1087,8 @@ groupBy op xs =
-- | = [NonEmptyArray [4], NonEmptyArray [3, 3, 3], NonEmptyArray [2], NonEmptyArray [1]]
-- | ```
-- |
groupAllBy :: forall a. (a -> a -> Ordering) -> Array a -> Array (NonEmptyArray a)
groupAllBy
:: forall a. (a -> a -> Ordering) -> Array a -> Array (NonEmptyArray a)
groupAllBy cmp = groupBy (\x y -> cmp x y == EQ) <<< sortBy cmp

-- | Remove the duplicates from an array, creating a new array.
Expand Down Expand Up @@ -1118,7 +1125,8 @@ nubBy comp xs = case head indexedAndSorted of
-- TODO: use NonEmptyArrays here to avoid partial functions
result <- STA.unsafeThaw $ singleton x
ST.foreach indexedAndSorted \pair@(Tuple _ x') -> do
lst <- snd <<< unsafePartial (fromJust <<< last) <$> STA.unsafeFreeze result
lst <- snd <<< unsafePartial (fromJust <<< last) <$> STA.unsafeFreeze
result
when (comp lst x' /= EQ) $ void $ STA.push pair result
STA.unsafeFreeze result
where
Expand Down Expand Up @@ -1192,7 +1200,8 @@ delete = deleteBy eq
-- |
deleteBy :: forall a. (a -> a -> Boolean) -> a -> Array a -> Array a
deleteBy _ _ [] = []
deleteBy eq x ys = maybe ys (\i -> unsafePartial $ fromJust (deleteAt i ys)) (findIndex (eq x) ys)
deleteBy eq x ys = maybe ys (\i -> unsafePartial $ fromJust (deleteAt i ys))
(findIndex (eq x) ys)

-- | Delete the first occurrence of each element in the second array from the
-- | first array, creating a new array.
Expand Down Expand Up @@ -1341,7 +1350,8 @@ foreign import allImpl :: forall a. Fn2 (a -> Boolean) (Array a) Boolean
-- | foldM (\x y -> Just (x + y)) 0 [1, 4] = Just 5
-- | ```
foldM :: forall m a b. Monad m => (b -> a -> m b) -> b -> Array a -> m b
foldM f b = runFn3 unconsImpl (\_ -> pure b) (\a as -> f b a >>= \b' -> foldM f b' as)
foldM f b = runFn3 unconsImpl (\_ -> pure b)
(\a as -> f b a >>= \b' -> foldM f b' as)

foldRecM :: forall m a b. MonadRec m => (b -> a -> m b) -> b -> Array a -> m b
foldRecM f b array = tailRecM2 go b 0
Expand Down
Loading
Loading