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
4 changes: 3 additions & 1 deletion .github/workflows/semantic-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ jobs:
ci: ${{ github.ref == github.event.repository.default_branch }}
extra_plugins: |
conventional-changelog-conventionalcommits
semantic-release-mirror-version
@semantic-release/changelog
@semantic-release/git
github:haskell-github-trust/semantic-release-haddock-since
semantic-release-stack-upload

- name: Semantic release output
Expand Down
91 changes: 19 additions & 72 deletions .github/workflows/stack-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,84 +10,31 @@ permissions:
contents: read

jobs:
generate:
name: Generate matrix
runs-on: ubuntu-latest
outputs:
stack-yamls: ${{ steps.generate.outputs.stack-yamls }}
steps:
- uses: actions/checkout@v4
- id: generate
uses: freckle/stack-action/generate-matrix@v5

build:
name: lts-${{ matrix.stack.lts }} / ghc-${{ matrix.stack.ghc }} / ${{ matrix.os }}
runs-on: ${{ matrix.os }}-latest
name: ${{ matrix.stack-yaml }} / ${{ matrix.os }}
needs: generate
runs-on: ${{ matrix.os == 'macos' && 'macos-15' || format('{0}-latest', matrix.os) }}
strategy:
fail-fast: false
matrix:
os: [ubuntu]
stack:
- lts: &default-lts 21.25
ghc: &default-ghc 9.4.8

- lts: 22.44
ghc: 9.6.7

- lts: 23.28
ghc: 9.8.4

include:
- os: windows
stack:
lts: *default-lts
ghc: *default-ghc

- os: macos
stack:
lts: *default-lts
ghc: *default-ghc

os: [ubuntu, macos, windows]
stack-yaml: ${{ fromJSON(needs.generate.outputs.stack-yamls) }}
env:
STACK_YAML: ${{ matrix.stack-yaml }}
steps:
- uses: actions/checkout@v3

- name: Set up Stack LTS ${{ matrix.stack-lts }}
uses: haskell-actions/setup@v2
id: setup
with:
ghc-version: ${{ matrix.stack.ghc }}
enable-stack: true
stack-setup-ghc: true
stack-no-global: true

- name: Configure the build
run: |
echo "with key: ${{ runner.os }}-lts-${{ matrix.stack.lts }}"
echo "stack root: ${{ steps.setup.outputs.stack-root }}"

stack config set resolver lts-${{ matrix.stack.lts }}
stack build --dry-run
# The last step generates dist-newstyle/cache/plan.json for the cache key.

- name: Restore cached dependencies
uses: actions/cache/restore@v3
id: cache
env:
key: ${{ runner.os }}-lts-${{ matrix.stack.lts }}

with:
path: ${{ steps.setup.outputs.stack-root }}
key: ${{ env.key }}-plan-${{ hashFiles('dist-newstyle/cache/plan.json') }}
restore-keys: ${{ env.key }}-

- name: Install dependencies
run: stack build --only-dependencies

# Cache dependencies already here, so that we do not have to rebuild them
# should the subsequent steps fail.
- name: Save cached dependencies
uses: actions/cache/save@v3
# Caches are immutable, trying to save with the same key would error.
if: ${{ steps.cache.outputs.cache-primary-key != steps.cache.outputs.cache-matched-key }}
with:
path: ${{ steps.setup.outputs.stack-root }}
key: ${{ steps.cache.outputs.cache-primary-key }}

- name: Build
run: stack build
- uses: actions/checkout@v4

- name: Run tests
run: stack test
- uses: freckle/stack-action@v5

- name: Check cabal file
run: stack exec -- cabal check
Expand Down
64 changes: 34 additions & 30 deletions .releaserc.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,38 @@
* @type {import('semantic-release').GlobalConfig}
*/
export default {
branches: ["main"],
tagFormat: "${version}",
plugins: [
[
"@semantic-release/commit-analyzer",
{
"preset": "conventionalcommits",
"releaseRules": [
{ "type": "docs", "release": "patch" }
]
}
],
"@semantic-release/release-notes-generator",
[
"semantic-release-mirror-version",
{
"fileGlob": "@(package.yaml|megaparsec-utils.cabal)",
"placeholderRegExp": "0.0.0-dev"
}
],
[
"@semantic-release/changelog",
{
changelogFile: "CHANGELOG.md",
changelogTitle: "Changelog"
}
],
"@semantic-release/github",
"semantic-release-stack-upload",
]
branches: ["main"],
tagFormat: "${version}",
plugins: [
[
"@semantic-release/commit-analyzer",
{
preset: "conventionalcommits",
releaseRules: [{ type: "docs", release: "patch" }],
},
],
"@semantic-release/release-notes-generator",
[
"semantic-release-haddock-since",
{
versionFiles: ["package.yaml"],
baseline: "api/megaparsec-utils.api",
},
],
[
"@semantic-release/changelog",
{
changelogFile: "CHANGELOG.md",
changelogTitle: "Changelog",
},
],
[
"@semantic-release/git",
{
assets: ["package.yaml", "CHANGELOG.md", "api/*.api", "src/**/*.hs"],
},
],
"@semantic-release/github",
"semantic-release-stack-upload",
],
}
3 changes: 2 additions & 1 deletion package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ homepage: https://github.com/haskell-github-trust/megaparsec-utils
bug-reports: https://github.com/haskell-github-trust/megaparsec-utils/issues
category: Parsing
tested-with:
- GHC == 9.10
- GHC == 9.8
- GHC == 9.6
- GHC == 9.4

extra-source-files:
- README.md
Expand All @@ -25,6 +25,7 @@ description: |
dependencies:
- base >= 4.7 && < 5
- aeson >= 2.0 && < 3
- bytestring >= 0.11 && < 0.13
- megaparsec >= 9.0 && < 10
- parser-combinators >= 1.0 && < 2
- text >= 2.0 && < 3
Expand Down
71 changes: 14 additions & 57 deletions src/Text/Megaparsec/Utils.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
-- Maintainer : drlkf@drlkf.net
-- Stability : experimental
--
-- Generic utilities and common parsers.
-- String-based shims over 'Text.Megaparsec.Utils.Char'. New code should use
-- 'Text.Megaparsec.Utils.Char' or 'Text.Megaparsec.Utils.Byte' directly.
module Text.Megaparsec.Utils (
-- * Scalar parsers
boolParser,
Expand All @@ -30,43 +31,25 @@ module Text.Megaparsec.Utils (
parsecToJSONParser,
) where

import Control.Applicative (many, some, (<|>))
import Control.Applicative.Combinators (choice)
import Control.Monad (replicateM)
import Control.Monad.Combinators (optional)
import Data.Aeson.Types (Parser, Value, withText)
import Data.Functor (($>))
import Data.List (intercalate, sortOn)
import Data.List.NonEmpty (NonEmpty ((:|)))
import Data.Maybe (fromJust)
import Data.List.NonEmpty (NonEmpty)
import qualified Data.Text as T (unpack)
import Data.UUID (UUID)
import qualified Data.UUID as U (fromString)
import Text.Megaparsec (
Parsec,
ShowErrorComponent,
anySingle,
errorBundlePretty,
runParser,
try,
)
import Text.Megaparsec.Char (
char,
digitChar,
hexDigitChar,
string',
)
import Text.Read (readMaybe)
import qualified Text.Megaparsec.Utils.Char as Char
import qualified Text.Megaparsec.Utils.Common as Common

-- | Parse a case-insensitive human-readable boolean, including C-style numbers,
-- English yes-no and @on@ / @off@.
boolParser
:: Ord e
=> Parsec e String Bool
boolParser = true <|> false
where
true = True <$ choice (map string' ["true", "y", "yes", "on", "1"])
false = False <$ choice (map string' ["false", "n", "no", "off", "0"])
boolParser = Char.boolParser

-- | Parse a 'Bounded' 'Enum' type that has a 'Show' instance, trying all
-- possibilities, case-insensitive, in the 'Enum' order.
Expand All @@ -77,66 +60,50 @@ boundedEnumShowParser
=> Enum a
=> Show a
=> Parsec e String a
boundedEnumShowParser =
choice . map parseShow $ sortOn (negate . length . show) [(minBound :: a) ..]
where
parseShow a = string' (show a) $> a
boundedEnumShowParser = Char.boundedEnumShowParser

-- | Parse a comma-separated list of items.
commaSeparated
:: Ord e
=> Parsec e String a
-> Parsec e String (NonEmpty a)
commaSeparated p = (:|) <$> p <*> many (char ',' >> p)
commaSeparated = Char.commaSeparated

-- | Parse any occurrence of a given parser. Consumes any input before occurrence.
occurrence
:: Ord e
=> Parsec e String a
-> Parsec e String a
occurrence p = go
where
go = p <|> (anySingle >> go)
occurrence = Common.occurrence

-- | Parse all occurrences of a given parser.
occurrences
:: Ord e
=> Parsec e String a
-> Parsec e String [a]
occurrences = some . try . occurrence . try
occurrences = Common.occurrences

-- | Parse a positive number, with or without decimals prefixed by a @.@.
posDecNumParser
:: Ord e
=> Read a
=> Parsec e String a
posDecNumParser = do
num <- some digitChar
dec <- maybe "" ("." <>) <$> optional (char '.' >> some digitChar)

let str = num <> dec

maybe (fail ("could not read from input: " <> str)) pure (readMaybe str)
posDecNumParser = Char.posDecNumParser

-- | Parse a positive integer.
posNumParser
:: Ord e
=> Read a
=> Parsec e String a
posNumParser = do
digits <- some digitChar
maybe
(fail ("could not read from digits: " <> digits))
pure
(readMaybe digits)
posNumParser = Char.posNumParser

-- | Parse an integer, without any space between minus sign and digits.
numParser
:: Ord e
=> Num a
=> Read a
=> Parsec e String a
numParser = (char '-' >> negate <$> posNumParser) <|> posNumParser
numParser = Char.numParser

-- | Convert a 'Parsec' parser into a 'Parser' suited for 'Data.Aeson.FromJSON'
-- instances.
Expand All @@ -161,14 +128,4 @@ parsecToReadsPrec p = either (const []) (\x -> [(x, "")]) . runParser p "string"
uuidParser
:: Ord e
=> Parsec e String UUID
uuidParser = do
part1 <- replicateM 8 hexDigitChar <* char '-'
part2 <- replicateM 4 hexDigitChar <* char '-'
part3 <- replicateM 4 hexDigitChar <* char '-'
part4 <- replicateM 4 hexDigitChar <* char '-'
part5 <- replicateM 12 hexDigitChar

pure
(fromJust
(U.fromString
(intercalate "-" [part1, part2, part3, part4, part5])))
uuidParser = Char.uuidParser
Loading
Loading