Full Code of luc-tielen/eclair-lang for AI

main 5254a56ef14e cached
183 files
857.3 KB
275.8k tokens
1 requests
Download .txt
Showing preview only (927K chars total). Download the full file or copy to clipboard to get everything.
Repository: luc-tielen/eclair-lang
Branch: main
Commit: 5254a56ef14e
Files: 183
Total size: 857.3 KB

Directory structure:
gitextract_d5o1d0tz/

├── .dockerignore
├── .ghci
├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       ├── build.yml
│       └── ci.yml
├── .gitignore
├── .hlint.yaml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── cabal.project
├── cbits/
│   └── semantic_analysis.dl
├── docs/
│   ├── architecture_choices.md
│   └── getting_started.md
├── eclair-lang.cabal
├── hie.yaml
├── lib/
│   ├── Eclair/
│   │   ├── AST/
│   │   │   ├── Analysis.hs
│   │   │   ├── Codegen.hs
│   │   │   ├── IR.hs
│   │   │   ├── Lower.hs
│   │   │   ├── Transforms/
│   │   │   │   ├── ConstantFolding.hs
│   │   │   │   ├── DeadCodeElimination.hs
│   │   │   │   ├── NormalizeRules.hs
│   │   │   │   ├── RemoveAliases.hs
│   │   │   │   └── ReplaceStrings.hs
│   │   │   └── Transforms.hs
│   │   ├── ArgParser.hs
│   │   ├── Common/
│   │   │   ├── Config.hs
│   │   │   ├── Extern.hs
│   │   │   ├── Id.hs
│   │   │   ├── Literal.hs
│   │   │   ├── Location.hs
│   │   │   ├── Operator.hs
│   │   │   └── Pretty.hs
│   │   ├── Comonads.hs
│   │   ├── EIR/
│   │   │   ├── IR.hs
│   │   │   ├── Lower/
│   │   │   │   ├── API.hs
│   │   │   │   ├── Codegen.hs
│   │   │   │   └── Externals.hs
│   │   │   └── Lower.hs
│   │   ├── Error.hs
│   │   ├── JSON.hs
│   │   ├── LLVM/
│   │   │   ├── Allocator/
│   │   │   │   ├── Arena.hs
│   │   │   │   ├── Common.hs
│   │   │   │   ├── Malloc.hs
│   │   │   │   └── Page.hs
│   │   │   ├── BTree/
│   │   │   │   ├── Bounds.hs
│   │   │   │   ├── Compare.hs
│   │   │   │   ├── Create.hs
│   │   │   │   ├── Destroy.hs
│   │   │   │   ├── Find.hs
│   │   │   │   ├── Insert.hs
│   │   │   │   ├── Iterator.hs
│   │   │   │   ├── Size.hs
│   │   │   │   └── Types.hs
│   │   │   ├── BTree.hs
│   │   │   ├── Codegen.hs
│   │   │   ├── Config.hs
│   │   │   ├── Externals.hs
│   │   │   ├── Hash.hs
│   │   │   ├── HashMap.hs
│   │   │   ├── Metadata.hs
│   │   │   ├── Symbol.hs
│   │   │   ├── SymbolTable.hs
│   │   │   ├── Table.hs
│   │   │   ├── Template.hs
│   │   │   └── Vector.hs
│   │   ├── LSP/
│   │   │   ├── Handlers/
│   │   │   │   ├── Diagnostics.hs
│   │   │   │   ├── DocumentHighlight.hs
│   │   │   │   └── Hover.hs
│   │   │   ├── Handlers.hs
│   │   │   ├── JSON.hs
│   │   │   ├── Monad.hs
│   │   │   ├── Types.hs
│   │   │   └── VFS.hs
│   │   ├── LSP.hs
│   │   ├── Parser.hs
│   │   ├── RA/
│   │   │   ├── Codegen.hs
│   │   │   ├── IR.hs
│   │   │   ├── IndexSelection.hs
│   │   │   ├── Lower.hs
│   │   │   ├── Transforms/
│   │   │   │   └── HoistConstraints.hs
│   │   │   └── Transforms.hs
│   │   ├── Souffle/
│   │   │   └── IR.hs
│   │   ├── Transform.hs
│   │   └── TypeSystem.hs
│   ├── Eclair.hs
│   └── Prelude.hs
├── src/
│   └── eclair/
│       └── Main.hs
└── tests/
    ├── .gitignore
    ├── ast_transforms/
    │   ├── constant_folding.eclair
    │   ├── copy_propagation.eclair
    │   ├── dead_code_elimination.eclair
    │   ├── remove_contradictions.eclair
    │   └── shift_assignments.eclair
    ├── check.sh
    ├── eclair/
    │   ├── Test/
    │   │   └── Eclair/
    │   │       ├── ArgParserSpec.hs
    │   │       ├── JSONSpec.hs
    │   │       ├── LLVM/
    │   │       │   ├── Allocator/
    │   │       │   │   ├── MallocSpec.hs
    │   │       │   │   ├── PageSpec.hs
    │   │       │   │   └── Utils.hs
    │   │       │   ├── BTreeSpec.hs
    │   │       │   ├── HashMapSpec.hs
    │   │       │   ├── HashSpec.hs
    │   │       │   ├── SymbolSpec.hs
    │   │       │   ├── SymbolTableSpec.hs
    │   │       │   ├── SymbolUtils.hs
    │   │       │   └── VectorSpec.hs
    │   │       ├── LSP/
    │   │       │   ├── HandlersSpec.hs
    │   │       │   └── JSONSpec.hs
    │   │       └── RA/
    │   │           └── IndexSelectionSpec.hs
    │   ├── fixtures/
    │   │   └── lsp/
    │   │       ├── document_highlight.eclair
    │   │       ├── hover.eclair
    │   │       ├── invalid_syntax.eclair
    │   │       ├── semantic_errors.eclair
    │   │       ├── type_errors.eclair
    │   │       └── unparsable.eclair
    │   └── test.hs
    ├── end_to_end/
    │   ├── compile_and_run_native.eclair
    │   ├── compile_and_run_wasm.eclair
    │   └── compile_and_run_with_extern.eclair
    ├── hello.eclair
    ├── lit.cfg
    ├── lowering/
    │   ├── arithmetic.eclair
    │   ├── clause_with_same_vars.eclair
    │   ├── comparisons.eclair
    │   ├── different_types.eclair
    │   ├── extern_definitions.eclair
    │   ├── multiple_clauses_same_name.eclair
    │   ├── multiple_rule_clauses.eclair
    │   ├── mutually_recursive_rules.eclair
    │   ├── negation.eclair
    │   ├── negation_with_wildcards.eclair
    │   ├── no_top_level_facts.eclair
    │   ├── recursive_mix_of_rules.eclair
    │   ├── single_non_recursive_rule.eclair
    │   ├── single_recursive_rule.eclair
    │   ├── stratification.eclair
    │   ├── top_level_facts.eclair
    │   └── wasm_codegen.eclair
    ├── parser/
    │   ├── error_recovery.eclair
    │   ├── file_not_found.eclair
    │   └── valid.eclair
    ├── runtime/
    │   ├── hashmap_test.eclair
    │   ├── symbol_table_test.eclair
    │   └── vector_test.eclair
    ├── semantic_analysis/
    │   ├── cyclic_negation.eclair
    │   ├── dead_internal_relation.eclair
    │   ├── invalid_extern_usage.eclair
    │   ├── invalid_options_usage.eclair
    │   ├── invalid_wildcard_usage.eclair
    │   ├── no_output_relations.eclair
    │   ├── unconstrained_variables.eclair
    │   ├── ungrounded_variables.eclair
    │   ├── ungrounded_variables_arithmetic.eclair
    │   ├── ungrounded_variables_comparisons.eclair
    │   └── ungrounded_variables_negations.eclair
    ├── string_support/
    │   ├── encode_decode_string.eclair
    │   ├── encode_decode_string_native.eclair
    │   └── encode_decode_string_wasm.eclair
    ├── transpilation/
    │   └── souffle.eclair
    ├── typesystem/
    │   ├── arg_count_mismatch.eclair
    │   ├── arithmetic.eclair
    │   ├── comparisons.eclair
    │   ├── duplicate_type_declarations.eclair
    │   ├── extern_definitions.eclair
    │   ├── negation.eclair
    │   ├── no_rules_for_type.eclair
    │   ├── type_mismatch_in_rule.eclair
    │   ├── type_mismatch_in_rule_body.eclair
    │   ├── type_mismatch_in_rule_head.eclair
    │   ├── type_mismatch_top_level_atoms.eclair
    │   ├── typed_holes.eclair
    │   ├── unification_failure.eclair
    │   ├── unknown_atom_in_rule_body.eclair
    │   ├── unknown_atom_in_rule_head.eclair
    │   ├── unknown_atoms.eclair
    │   ├── unknown_top_level_atoms.eclair
    │   └── valid.eclair
    └── utils/
        └── extract_snippet

================================================
FILE CONTENTS
================================================

================================================
FILE: .dockerignore
================================================
.direnv/
.git/
.github/
dist/
dist-newstyle/
result/
Dockerfile
.dockerignore
.envrc
.ghci
.gitignore
./*.ll
./*.o
./*.a
./*.wasm
./*.eclair
./*.dl
./logo*
hie.yaml


================================================
FILE: .ghci
================================================
:set prompt >


================================================
FILE: .github/FUNDING.yml
================================================
github: luc-tielen


================================================
FILE: .github/workflows/build.yml
================================================
name: "Build"
on: [push, pull_request]
jobs:
  build:
    strategy:
      matrix:
        os: [ubuntu-latest]
    runs-on: ${{matrix.os}}
    steps:
      - uses: actions/checkout@v3

      - name: Build and test
        run: |
          set -eo pipefail
          export TIMESTAMP=$(date +%s)
          docker build -f Dockerfile . -t eclair:$TIMESTAMP | tee eclair-lang-${{matrix.os}}.log
          docker run --rm eclair:$TIMESTAMP bash -c "make test" | tee -a eclair-lang-${{matrix.os}}.log

      - name: Check for disabled tests
        run: |
          ./tests/check.sh

      - name: Upload logs
        if: ${{ always() }}
        uses: actions/upload-artifact@v3
        with:
          name: eclair-lang-${{matrix.os}}.log
          path: eclair-lang-${{matrix.os}}.log


================================================
FILE: .github/workflows/ci.yml
================================================
name: lint
on:
  pull_request:
  push:
    branches:
      - main
      - "releases/*"
jobs:
  hlint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: "Set up HLint"
        uses: rwe/actions-hlint-setup@v1
        with:
          version: "3.6.1"

      - name: "Run HLint"
        uses: rwe/actions-hlint-run@v2
        with:
          path: '["lib/", "src/", "tests/"]'
          fail-on: warning


================================================
FILE: .gitignore
================================================
dist-newstyle/
dist/
.direnv/
.devcontainer/
cabal.project.local*

*.ll
*.bc
*.o
*.a
*.s
/*.wasm
/*.dl
/*.eclair
/*.js

result
eclair.prof
eclair.svg
*.eventlog*
eclair.hp
perf.data
perf.data.old
perf.svg

TODO*


================================================
FILE: .hlint.yaml
================================================
# HLint configuration file
# https://github.com/ndmitchell/hlint
##########################

# This file contains a template configuration file, which is typically
# placed as .hlint.yaml in the root of your project


# Specify additional command line arguments
#
# - arguments: [--color, --cpp-simple, -XQuasiQuotes]


# Control which extensions/flags/modules/functions can be used
#
# - extensions:
#   - default: false # all extension are banned by default
#   - name: [PatternGuards, ViewPatterns] # only these listed extensions can be used
#   - {name: CPP, within: CrossPlatform} # CPP can only be used in a given module
#
# - flags:
#   - {name: -w, within: []} # -w is allowed nowhere
#
# - modules:
#   - {name: [Data.Set, Data.HashSet], as: Set} # if you import Data.Set qualified, it must be as 'Set'
#   - {name: Control.Arrow, within: []} # Certain modules are banned entirely
#
# - functions:
#   - {name: unsafePerformIO, within: []} # unsafePerformIO can only appear in no modules

- arguments:
  - "-XRecursiveDo"

# Add custom hints for this project
#
# Will suggest replacing "wibbleMany [myvar]" with "wibbleOne myvar"
# - error: {lhs: "wibbleMany [x]", rhs: wibbleOne x}

# The hints are named by the string they display in warning messages.
# For example, if you see a warning starting like
#
# Main.hs:116:51: Warning: Redundant ==
#
# You can refer to that hint with `{name: Redundant ==}` (see below).

# Turn on hints that are off by default
#
# Ban "module X(module X) where", to require a real export list
# - warn: {name: Use explicit module export list}
#
# Replace a $ b $ c with a . b $ c
# - group: {name: dollar, enabled: true}
#
# Generalise map to fmap, ++ to <>
# - group: {name: generalise, enabled: true}


# Ignore some builtin hints
# - ignore: {name: Use let}
# - ignore: {name: Use const, within: SpecialModule} # Only within certain modules
- ignore: {name: Reduce duplication}


# Define some custom infix operators
# - fixity: infixr 3 ~^#^~


# To generate a suitable file for HLint do:
# $ hlint --default > .hlint.yaml

# Relude-specific (https://github.com/kowainik/relude/blob/main/.hlint.yaml)
- arguments:
  - "-XConstraintKinds"
  - "-XDeriveGeneric"
  - "-XGeneralizedNewtypeDeriving"
  - "-XLambdaCase"
  - "-XOverloadedStrings"
  - "-XRecordWildCards"
  - "-XScopedTypeVariables"
  - "-XStandaloneDeriving"
  - "-XTupleSections"
  - "-XTypeApplications"
  - "-XViewPatterns"
- ignore:
    name: Use head
- ignore:
    name: Use Foldable.forM_
- hint:
    lhs: "pure ()"
    note: "Use 'pass'"
    rhs: pass
- hint:
    lhs: "return ()"
    note: "Use 'pass'"
    rhs: pass
- hint:
    lhs: "(: [])"
    note: "Use `one`"
    rhs: one
- hint:
    lhs: "(:| [])"
    note: "Use `one`"
    rhs: one
- hint:
    lhs: Data.Sequence.singleton
    note: "Use `one`"
    rhs: one
- hint:
    lhs: Data.Text.singleton
    note: "Use `one`"
    rhs: one
- hint:
    lhs: Data.Text.Lazy.singleton
    note: "Use `one`"
    rhs: one
- hint:
    lhs: Data.ByteString.singleton
    note: "Use `one`"
    rhs: one
- hint:
    lhs: Data.ByteString.Lazy.singleton
    note: "Use `one`"
    rhs: one
- hint:
    lhs: Data.Map.singleton
    note: "Use `one`"
    rhs: one
- hint:
    lhs: Data.Map.Strict.singleton
    note: "Use `one`"
    rhs: one
- hint:
    lhs: Data.HashMap.Strict.singleton
    note: "Use `one`"
    rhs: one
- hint:
    lhs: Data.HashMap.Lazy.singleton
    note: "Use `one`"
    rhs: one
- hint:
    lhs: Data.IntMap.singleton
    note: "Use `one`"
    rhs: one
- hint:
    lhs: Data.IntMap.Strict.singleton
    note: "Use `one`"
    rhs: one
- hint:
    lhs: Data.Set.singleton
    note: "Use `one`"
    rhs: one
- hint:
    lhs: Data.HashSet.singleton
    note: "Use `one`"
    rhs: one
- hint:
    lhs: Data.IntSet.singleton
    note: "Use `one`"
    rhs: one
- warn:
    lhs: Control.Exception.evaluate
    rhs: evaluateWHNF
- warn:
    lhs: "Control.Exception.evaluate (force x)"
    rhs: evaluateNF x
- warn:
    lhs: "Control.Exception.evaluate (x `deepseq` ())"
    rhs: evaluateNF_ x
- warn:
    lhs: "void (evaluateWHNF x)"
    rhs: evaluateWHNF_ x
- warn:
    lhs: "void (evaluateNF x)"
    rhs: evaluateNF_ x
- hint:
    lhs: Control.Exception.throw
    note: "Use 'impureThrow'"
    rhs: impureThrow
- warn:
    lhs: Data.Text.IO.readFile
    rhs: readFileText
- warn:
    lhs: Data.Text.IO.writeFile
    rhs: writeFileText
- warn:
    lhs: Data.Text.IO.appendFile
    rhs: appendFileText
- warn:
    lhs: Data.Text.Lazy.IO.readFile
    rhs: readFileLText
- warn:
    lhs: Data.Text.Lazy.IO.writeFile
    rhs: writeFileLText
- warn:
    lhs: Data.Text.Lazy.IO.appendFile
    rhs: appendFileLText
- warn:
    lhs: Data.ByteString.readFile
    rhs: readFileBS
- warn:
    lhs: Data.ByteString.writeFile
    rhs: writeFileBS
- warn:
    lhs: Data.ByteString.appendFile
    rhs: appendFileBS
- warn:
    lhs: Data.ByteString.Lazy.readFile
    rhs: readFileLBS
- warn:
    lhs: Data.ByteString.Lazy.writeFile
    rhs: writeFileLBS
- warn:
    lhs: Data.ByteString.Lazy.appendFile
    rhs: appendFileLBS
- hint:
    lhs: "foldl' (flip f)"
    note: "Use 'flipfoldl''"
    rhs: "flipfoldl' f"
- warn:
    lhs: "foldl' (+) 0"
    rhs: sum
- warn:
    lhs: "foldl' (*) 1"
    rhs: product
- hint:
    lhs: "fmap and (sequence s)"
    note: Applying this hint would mean that some actions that were being executed previously would no longer be executed.
    rhs: andM s
- hint:
    lhs: "and <$> sequence s"
    note: Applying this hint would mean that some actions that were being executed previously would no longer be executed.
    rhs: andM s
- hint:
    lhs: "fmap or (sequence s)"
    note: Applying this hint would mean that some actions that were being executed previously would no longer be executed.
    rhs: orM s
- hint:
    lhs: "or <$> sequence s"
    note: Applying this hint would mean that some actions that were being executed previously would no longer be executed.
    rhs: orM s
- hint:
    lhs: "fmap and (mapM f s)"
    note: Applying this hint would mean that some actions that were being executed previously would no longer be executed.
    rhs: allM f s
- hint:
    lhs: "and <$> mapM f s"
    note: Applying this hint would mean that some actions that were being executed previously would no longer be executed.
    rhs: allM f s
- hint:
    lhs: "fmap or (mapM f s)"
    note: Applying this hint would mean that some actions that were being executed previously would no longer be executed.
    rhs: anyM f s
- hint:
    lhs: "or <$> mapM f s"
    note: Applying this hint would mean that some actions that were being executed previously would no longer be executed.
    rhs: anyM f s
- warn:
    lhs: "getAlt (foldMap (Alt . f) xs)"
    rhs: asumMap xs
- warn:
    lhs: "getAlt . foldMap (Alt . f)"
    rhs: asumMap
- hint:
    lhs: "foldr (\\x acc -> f x <|> acc) empty"
    note: "Use 'asumMap'"
    rhs: asumMap f
- hint:
    lhs: "asum (map f xs)"
    note: "Use 'asumMap'"
    rhs: asumMap f xs
- warn:
    lhs: "map fst &&& map snd"
    rhs: unzip
- hint:
    lhs: "fmap (fmap f) x"
    note: "Use '(<<$>>)'"
    rhs: "f <<$>> x"
- hint:
    lhs: "(\\f -> f x) <$> ff"
    note: Use flap operator
    rhs: "ff ?? x"
- hint:
    lhs: "fmap (\\f -> f x) ff"
    note: Use flap operator
    rhs: "ff ?? x"
- hint:
    lhs: "fmap ($ x) ff"
    note: Use flap operator
    rhs: "ff ?? x"
- hint:
    lhs: "($ x) <$> ff"
    note: Use flap operator
    rhs: "ff ?? x"
- warn:
    lhs: "fmap f (nonEmpty x)"
    rhs: viaNonEmpty f x
- warn:
    lhs: fmap f . nonEmpty
    rhs: viaNonEmpty f
- warn:
    lhs: "f <$> nonEmpty x"
    rhs: viaNonEmpty f x
- warn:
    lhs: partitionEithers . map f
    rhs: partitionWith f
- warn:
    lhs: partitionEithers $ map f x
    rhs: partitionWith f x
- warn:
    lhs: "f >>= guard"
    rhs: guardM f
- warn:
    lhs: guard =<< f
    rhs: guardM f
- warn:
    lhs: forever
    note: "'forever' is loosely typed and may hide errors"
    rhs: infinitely
- warn:
    lhs: "whenM (not <$> x)"
    rhs: unlessM x
- warn:
    lhs: "unlessM (not <$> x)"
    rhs: whenM x
- warn:
    lhs: "either (const True) (const False)"
    rhs: isLeft
- warn:
    lhs: "either (const False) (const True)"
    rhs: isRight
- warn:
    lhs: "either id (const a)"
    rhs: fromLeft a
- warn:
    lhs: "either (const b) id"
    rhs: fromRight b
- warn:
    lhs: "either Just (const Nothing)"
    rhs: leftToMaybe
- warn:
    lhs: "either (const Nothing) Just"
    rhs: rightToMaybe
- warn:
    lhs: "maybe (Left l) Right"
    rhs: maybeToRight l
- warn:
    lhs: "maybe (Right r) Left"
    rhs: maybeToLeft r
- warn:
    lhs: "case m of Just x -> f x; Nothing -> pure ()"
    rhs: whenJust m f
- warn:
    lhs: "case m of Just x -> f x; Nothing -> return ()"
    rhs: whenJust m f
- warn:
    lhs: "case m of Just x -> f x; Nothing -> pass"
    rhs: whenJust m f
- warn:
    lhs: "case m of Nothing -> pure ()  ; Just x -> f x"
    rhs: whenJust m f
- warn:
    lhs: "case m of Nothing -> return (); Just x -> f x"
    rhs: whenJust m f
- warn:
    lhs: "case m of Nothing -> pass     ; Just x -> f x"
    rhs: whenJust m f
- warn:
    lhs: "maybe (pure ())   f m"
    rhs: whenJust m f
- warn:
    lhs: "maybe (return ()) f m"
    rhs: whenJust m f
- warn:
    lhs: maybe pass        f m
    rhs: whenJust m f
- warn:
    lhs: "m >>= \\a -> whenJust a f"
    rhs: whenJustM m f
- warn:
    lhs: "m >>= \\case Just x -> f x; Nothing -> pure ()"
    rhs: whenJustM m f
- warn:
    lhs: "m >>= \\case Just x -> f x; Nothing -> return ()"
    rhs: whenJustM m f
- warn:
    lhs: "m >>= \\case Just x -> f x; Nothing -> pass"
    rhs: whenJustM m f
- warn:
    lhs: "m >>= \\case Nothing -> pure ()  ; Just x -> f x"
    rhs: whenJustM m f
- warn:
    lhs: "m >>= \\case Nothing -> return (); Just x -> f x"
    rhs: whenJustM m f
- warn:
    lhs: "m >>= \\case Nothing -> pass     ; Just x -> f x"
    rhs: whenJustM m f
- warn:
    lhs: "maybe (pure ())   f =<< m"
    rhs: whenJustM m f
- warn:
    lhs: "maybe (return ()) f =<< m"
    rhs: whenJustM m f
- warn:
    lhs: maybe pass        f =<< m
    rhs: whenJustM m f
- warn:
    lhs: "m >>= maybe (pure ())   f"
    rhs: whenJustM m f
- warn:
    lhs: "m >>= maybe (return ()) f"
    rhs: whenJustM m f
- warn:
    lhs: "m >>= maybe pass        f"
    rhs: whenJustM m f
- warn:
    lhs: "case m of Just _ -> pure ()  ; Nothing -> x"
    rhs: whenNothing_ m x
- warn:
    lhs: "case m of Just _ -> return (); Nothing -> x"
    rhs: whenNothing_ m x
- warn:
    lhs: "case m of Just _ -> pass     ; Nothing -> x"
    rhs: whenNothing_ m x
- warn:
    lhs: "case m of Nothing -> x; Just _ -> pure ()"
    rhs: whenNothing_ m x
- warn:
    lhs: "case m of Nothing -> x; Just _ -> return ()"
    rhs: whenNothing_ m x
- warn:
    lhs: "case m of Nothing -> x; Just _ -> pass"
    rhs: whenNothing_ m x
- warn:
    lhs: "maybe x (\\_ -> pure ()    ) m"
    rhs: whenNothing_ m x
- warn:
    lhs: "maybe x (\\_ -> return ()  ) m"
    rhs: whenNothing_ m x
- warn:
    lhs: "maybe x (\\_ -> pass       ) m"
    rhs: whenNothing_ m x
- warn:
    lhs: "maybe x (const (pure ()  )) m"
    rhs: whenNothing_ m x
- warn:
    lhs: "maybe x (const (return ())) m"
    rhs: whenNothing_ m x
- warn:
    lhs: "maybe x (const pass) m"
    rhs: whenNothing_ m x
- warn:
    lhs: "m >>= \\a -> whenNothing_ a x"
    rhs: whenNothingM_ m x
- warn:
    lhs: "m >>= \\case Just _ -> pure ()  ; Nothing -> x"
    rhs: whenNothingM_ m x
- warn:
    lhs: "m >>= \\case Just _ -> return (); Nothing -> x"
    rhs: whenNothingM_ m x
- warn:
    lhs: "m >>= \\case Just _ -> pass     ; Nothing -> x"
    rhs: whenNothingM_ m x
- warn:
    lhs: "m >>= \\case Nothing -> x; Just _ -> pure ()"
    rhs: whenNothingM_ m x
- warn:
    lhs: "m >>= \\case Nothing -> x; Just _ -> return ()"
    rhs: whenNothingM_ m x
- warn:
    lhs: "m >>= \\case Nothing -> x; Just _ -> pass"
    rhs: whenNothingM_ m x
- warn:
    lhs: "maybe x (\\_ -> pure ()    ) =<< m"
    rhs: whenNothingM_ m x
- warn:
    lhs: "maybe x (\\_ -> return ()  ) =<< m"
    rhs: whenNothingM_ m x
- warn:
    lhs: "maybe x (\\_ -> pass       ) =<< m"
    rhs: whenNothingM_ m x
- warn:
    lhs: "maybe x (const (pure ()  )) =<< m"
    rhs: whenNothingM_ m x
- warn:
    lhs: "maybe x (const (return ())) =<< m"
    rhs: whenNothingM_ m x
- warn:
    lhs: "maybe x (const pass) =<< m"
    rhs: whenNothingM_ m x
- warn:
    lhs: "m >>= maybe x (\\_ -> pure ())"
    rhs: whenNothingM_ m x
- warn:
    lhs: "m >>= maybe x (\\_ -> return ())"
    rhs: whenNothingM_ m x
- warn:
    lhs: "m >>= maybe x (\\_ -> pass)"
    rhs: whenNothingM_ m x
- warn:
    lhs: "m >>= maybe x (const (pure ())  )"
    rhs: whenNothingM_ m x
- warn:
    lhs: "m >>= maybe x (const (return ()))"
    rhs: whenNothingM_ m x
- warn:
    lhs: "m >>= maybe x (const pass)"
    rhs: whenNothingM_ m x
- warn:
    lhs: "whenLeft ()"
    rhs: whenLeft_
- warn:
    lhs: "case m of Left x -> f x; Right _ -> pure ()"
    rhs: whenLeft_ m f
- warn:
    lhs: "case m of Left x -> f x; Right _ -> return ()"
    rhs: whenLeft_ m f
- warn:
    lhs: "case m of Left x -> f x; Right _ -> pass"
    rhs: whenLeft_ m f
- warn:
    lhs: "case m of Right _ -> pure ()  ; Left x -> f x"
    rhs: whenLeft_ m f
- warn:
    lhs: "case m of Right _ -> return (); Left x -> f x"
    rhs: whenLeft_ m f
- warn:
    lhs: "case m of Right _ -> pass     ; Left x -> f x"
    rhs: whenLeft_ m f
- warn:
    lhs: "either f (\\_ -> pure ()    ) m"
    rhs: whenLeft_ m f
- warn:
    lhs: "either f (\\_ -> return ()  ) m"
    rhs: whenLeft_ m f
- warn:
    lhs: "either f (\\_ -> pass       ) m"
    rhs: whenLeft_ m f
- warn:
    lhs: "either f (const (pure ()  )) m"
    rhs: whenLeft_ m f
- warn:
    lhs: "either f (const (return ())) m"
    rhs: whenLeft_ m f
- warn:
    lhs: "either f (const pass) m"
    rhs: whenLeft_ m f
- warn:
    lhs: "m >>= \\a -> whenLeft_ a f"
    rhs: whenLeftM_ m f
- warn:
    lhs: "m >>= \\case Left x -> f x; Right _ -> pure ()"
    rhs: whenLeftM_ m f
- warn:
    lhs: "m >>= \\case Left x -> f x; Right _ -> return ()"
    rhs: whenLeftM_ m f
- warn:
    lhs: "m >>= \\case Left x -> f x; Right _ -> pass"
    rhs: whenLeftM_ m f
- warn:
    lhs: "m >>= \\case Right _ -> pure ()  ; Left x -> f x"
    rhs: whenLeftM_ m f
- warn:
    lhs: "m >>= \\case Right _ -> return (); Left x -> f x"
    rhs: whenLeftM_ m f
- warn:
    lhs: "m >>= \\case Right _ -> pass     ; Left x -> f x"
    rhs: whenLeftM_ m f
- warn:
    lhs: "either f (\\_ -> pure ()    ) =<< m"
    rhs: whenLeftM_ m f
- warn:
    lhs: "either f (\\_ -> return ()  ) =<< m"
    rhs: whenLeftM_ m f
- warn:
    lhs: "either f (\\_ -> pass       ) =<< m"
    rhs: whenLeftM_ m f
- warn:
    lhs: "either f (const (pure ()  )) =<< m"
    rhs: whenLeftM_ m f
- warn:
    lhs: "either f (const (return ())) =<< m"
    rhs: whenLeftM_ m f
- warn:
    lhs: "either f (const pass) =<< m"
    rhs: whenLeftM_ m f
- warn:
    lhs: "m >>= either f (\\_ -> pure ())"
    rhs: whenLeftM_ m f
- warn:
    lhs: "m >>= either f (\\_ -> return ())"
    rhs: whenLeftM_ m f
- warn:
    lhs: "m >>= either f (\\_ -> pass)"
    rhs: whenLeftM_ m f
- warn:
    lhs: "m >>= either f (const (pure ())  )"
    rhs: whenLeftM_ m f
- warn:
    lhs: "m >>= either f (const (return ()))"
    rhs: whenLeftM_ m f
- warn:
    lhs: "m >>= either f (const pass)"
    rhs: whenLeftM_ m f
- warn:
    lhs: "whenRight ()"
    rhs: whenRight_
- warn:
    lhs: "case m of Right x -> f x; Left _ -> pure ()"
    rhs: whenRight_ m f
- warn:
    lhs: "case m of Right x -> f x; Left _ -> return ()"
    rhs: whenRight_ m f
- warn:
    lhs: "case m of Right x -> f x; Left _ -> pass"
    rhs: whenRight_ m f
- warn:
    lhs: "case m of Left _ -> pure ()  ; Right x -> f x"
    rhs: whenRight_ m f
- warn:
    lhs: "case m of Left _ -> return (); Right x -> f x"
    rhs: whenRight_ m f
- warn:
    lhs: "case m of Left _ -> pass     ; Right x -> f x"
    rhs: whenRight_ m f
- warn:
    lhs: "either (\\_ -> pure ()    ) f m"
    rhs: whenRight_ m f
- warn:
    lhs: "either (\\_ -> return ()  ) f m"
    rhs: whenRight_ m f
- warn:
    lhs: "either (\\_ -> pass       ) f m"
    rhs: whenRight_ m f
- warn:
    lhs: "either (const (pure ()  )) f m"
    rhs: whenRight_ m f
- warn:
    lhs: "either (const (return ())) f m"
    rhs: whenRight_ m f
- warn:
    lhs: "either (const pass) f m"
    rhs: whenRight_ m f
- warn:
    lhs: "m >>= \\a -> whenRight_ a f"
    rhs: whenRightM_ m f
- warn:
    lhs: "m >>= \\case Right x -> f x; Left _ -> pure ()  "
    rhs: whenRightM_ m f
- warn:
    lhs: "m >>= \\case Right x -> f x; Left _ -> return ()"
    rhs: whenRightM_ m f
- warn:
    lhs: "m >>= \\case Right x -> f x; Left _ -> pass"
    rhs: whenRightM_ m f
- warn:
    lhs: "m >>= \\case Left _ -> pure ()  ; Right x -> f x"
    rhs: whenRightM_ m f
- warn:
    lhs: "m >>= \\case Left _ -> return (); Right x -> f x"
    rhs: whenRightM_ m f
- warn:
    lhs: "m >>= \\case Left _ -> pass     ; Right x -> f x"
    rhs: whenRightM_ m f
- warn:
    lhs: "either (\\_ -> pure ()    ) f =<< m"
    rhs: whenRightM_ m f
- warn:
    lhs: "either (\\_ -> return ()  ) f =<< m"
    rhs: whenRightM_ m f
- warn:
    lhs: "either (\\_ -> pass       ) f =<< m"
    rhs: whenRightM_ m f
- warn:
    lhs: "either (const (pure ()  )) f =<< m"
    rhs: whenRightM_ m f
- warn:
    lhs: "either (const (return ())) f =<< m"
    rhs: whenRightM_ m f
- warn:
    lhs: "either (const pass) f =<< m"
    rhs: whenRightM_ m f
- warn:
    lhs: "m >>= either (\\_ -> pure ())   f"
    rhs: whenRightM_ m f
- warn:
    lhs: "m >>= either (\\_ -> return ()) f"
    rhs: whenRightM_ m f
- warn:
    lhs: "m >>= either (\\_ -> pass)      f"
    rhs: whenRightM_ m f
- warn:
    lhs: "m >>= either (const (pure ())  ) f"
    rhs: whenRightM_ m f
- warn:
    lhs: "m >>= either (const (return ())) f"
    rhs: whenRightM_ m f
- warn:
    lhs: "m >>= either (const pass) f"
    rhs: whenRightM_ m f
- warn:
    lhs: "case m of Left x -> f x; Right _ -> pure d  "
    rhs: whenLeft d m f
- warn:
    lhs: "case m of Left x -> f x; Right _ -> return d"
    rhs: whenLeft d m f
- warn:
    lhs: "case m of Right _ -> pure d  ; Left x -> f x"
    rhs: whenLeft d m f
- warn:
    lhs: "case m of Right _ -> return d; Left x -> f x"
    rhs: whenLeft d m f
- warn:
    lhs: "either f (\\_ -> pure d    ) m"
    rhs: whenLeft d m f
- warn:
    lhs: "either f (\\_ -> return d  ) m"
    rhs: whenLeft d m f
- warn:
    lhs: "either f (const (pure d  )) m"
    rhs: whenLeft d m f
- warn:
    lhs: "either f (const (return d)) m"
    rhs: whenLeft d m f
- warn:
    lhs: "m >>= \\a -> whenLeft d a f"
    rhs: whenLeftM d m f
- warn:
    lhs: "m >>= \\case Left x -> f x; Right _ -> pure d"
    rhs: whenLeftM d m f
- warn:
    lhs: "m >>= \\case Left x -> f x; Right _ -> return d"
    rhs: whenLeftM d m f
- warn:
    lhs: "m >>= \\case Right _ -> pure d  ; Left x -> f x"
    rhs: whenLeftM d m f
- warn:
    lhs: "m >>= \\case Right _ -> return d; Left x -> f x"
    rhs: whenLeftM d m f
- warn:
    lhs: "either f (\\_ -> pure d    ) =<< m"
    rhs: whenLeftM d m f
- warn:
    lhs: "either f (\\_ -> return d  ) =<< m"
    rhs: whenLeftM d m f
- warn:
    lhs: "either f (const (pure d  )) =<< m"
    rhs: whenLeftM d m f
- warn:
    lhs: "either f (const (return d)) =<< m"
    rhs: whenLeftM d m f
- warn:
    lhs: "m >>= either f (\\_ -> pure d)"
    rhs: whenLeftM d m f
- warn:
    lhs: "m >>= either f (\\_ -> return d)"
    rhs: whenLeftM d m f
- warn:
    lhs: "m >>= either f (const (pure d))"
    rhs: whenLeftM d m f
- warn:
    lhs: "m >>= either f (const (return d))"
    rhs: whenLeftM d m f
- warn:
    lhs: "case m of Right x -> f x; Left _ -> pure d"
    rhs: whenRight d m f
- warn:
    lhs: "case m of Right x -> f x; Left _ -> return d"
    rhs: whenRight d m f
- warn:
    lhs: "case m of Left _ -> pure d  ; Right x -> f x"
    rhs: whenRight d m f
- warn:
    lhs: "case m of Left _ -> return d; Right x -> f x"
    rhs: whenRight d m f
- warn:
    lhs: "either (\\_ -> pure d    ) f m"
    rhs: whenRight d m f
- warn:
    lhs: "either (\\_ -> return d  ) f m"
    rhs: whenRight d m f
- warn:
    lhs: "either (const (pure d  )) f m"
    rhs: whenRight d m f
- warn:
    lhs: "either (const (return d)) f m"
    rhs: whenRight d m f
- warn:
    lhs: "m >>= \\a -> whenRight d a f"
    rhs: whenRightM d m f
- warn:
    lhs: "m >>= \\case Right x -> f x; Left _ -> pure d"
    rhs: whenRightM d m f
- warn:
    lhs: "m >>= \\case Right x -> f x; Left _ -> return d"
    rhs: whenRightM d m f
- warn:
    lhs: "m >>= \\case Left _ -> pure d  ; Right x -> f x"
    rhs: whenRightM d m f
- warn:
    lhs: "m >>= \\case Left _ -> return d; Right x -> f x"
    rhs: whenRightM d m f
- warn:
    lhs: "either (\\_ -> pure d    ) f =<< m"
    rhs: whenRightM d m f
- warn:
    lhs: "either (\\_ -> return d  ) f =<< m"
    rhs: whenRightM d m f
- warn:
    lhs: "either (const (pure d  )) f =<< m"
    rhs: whenRightM d m f
- warn:
    lhs: "either (const (return d)) f =<< m"
    rhs: whenRightM d m f
- warn:
    lhs: "m >>= either (\\_ -> pure d)   f"
    rhs: whenRightM d m f
- warn:
    lhs: "m >>= either (\\_ -> return d) f"
    rhs: whenRightM d m f
- warn:
    lhs: "m >>= either (const (pure d)  ) f"
    rhs: whenRightM d m f
- warn:
    lhs: "m >>= either (const (return d)) f"
    rhs: whenRightM d m f
- warn:
    lhs: "case m of [] -> return (); (x:xs) -> f (x :| xs)"
    rhs: whenNotNull m f
- warn:
    lhs: "case m of [] -> pure ()  ; (x:xs) -> f (x :| xs)"
    rhs: whenNotNull m f
- warn:
    lhs: "case m of [] -> pass     ; (x:xs) -> f (x :| xs)"
    rhs: whenNotNull m f
- warn:
    lhs: "case m of (x:xs) -> f (x :| xs); [] -> return ()"
    rhs: whenNotNull m f
- warn:
    lhs: "case m of (x:xs) -> f (x :| xs); [] -> pure ()  "
    rhs: whenNotNull m f
- warn:
    lhs: "case m of (x:xs) -> f (x :| xs); [] -> pass     "
    rhs: whenNotNull m f
- warn:
    lhs: "m >>= \\case [] -> pass     ; (x:xs) -> f (x :| xs)"
    rhs: whenNotNullM m f
- warn:
    lhs: "m >>= \\case [] -> pure ()  ; (x:xs) -> f (x :| xs)"
    rhs: whenNotNullM m f
- warn:
    lhs: "m >>= \\case [] -> return (); (x:xs) -> f (x :| xs)"
    rhs: whenNotNullM m f
- warn:
    lhs: "m >>= \\case (x:xs) -> f (x :| xs); [] -> pass     "
    rhs: whenNotNullM m f
- warn:
    lhs: "m >>= \\case (x:xs) -> f (x :| xs); [] -> pure ()  "
    rhs: whenNotNullM m f
- warn:
    lhs: "m >>= \\case (x:xs) -> f (x :| xs); [] -> return ()"
    rhs: whenNotNullM m f
- warn:
    lhs: mapMaybe leftToMaybe
    rhs: lefts
- warn:
    lhs: mapMaybe rightToMaybe
    rhs: rights
- warn:
    lhs: flip runReaderT
    rhs: usingReaderT
- warn:
    lhs: flip runReader
    rhs: usingReader
- warn:
    lhs: flip runStateT
    rhs: usingStateT
- warn:
    lhs: flip runState
    rhs: usingState
- warn:
    lhs: "fst <$> usingStateT s st"
    rhs: evaluatingStateT s st
- warn:
    lhs: "fst (usingState s st)"
    rhs: evaluatingState s st
- warn:
    lhs: "snd <$> usingStateT s st"
    rhs: executingStateT s st
- warn:
    lhs: "snd (usingState s st)"
    rhs: executingState s st
- warn:
    lhs: "MaybeT (pure m)"
    rhs: hoistMaybe m
- warn:
    lhs: "MaybeT (return m)"
    rhs: hoistMaybe m
- warn:
    lhs: MaybeT . pure
    rhs: hoistMaybe
- warn:
    lhs: MaybeT . return
    rhs: hoistMaybe
- warn:
    lhs: "ExceptT (pure m)"
    rhs: hoistEither m
- warn:
    lhs: "ExceptT (return m)"
    rhs: hoistEither m
- warn:
    lhs: ExceptT . pure
    rhs: hoistEither
- warn:
    lhs: ExceptT . return
    rhs: hoistEither
- warn:
    lhs: fromMaybe mempty
    rhs: maybeToMonoid
- warn:
    lhs: "m ?: mempty"
    rhs: maybeToMonoid m
- warn:
    lhs: "Data.Map.toAscList (Data.Map.fromList x)"
    rhs: sortWith fst x
- warn:
    lhs: "Data.Map.toDescList (Data.Map.fromList x)"
    rhs: "sortWith (Down . fst) x"
- warn:
    lhs: "Data.Set.toList (Data.Set.fromList l)"
    rhs: sortNub l
- warn:
    lhs: "Data.Set.assocs (Data.Set.fromList l)"
    rhs: sortNub l
- warn:
    lhs: "Data.Set.toAscList (Data.Set.fromList l)"
    rhs: sortNub l
- warn:
    lhs: "Data.HashSet.toList (Data.HashSet.fromList l)"
    rhs: unstableNub l
- warn:
    lhs: nub
    note: "'nub' is O(n^2), 'ordNub' is O(n log n)"
    rhs: ordNub
- warn:
    lhs: "sortBy (comparing f)"
    note: "If the function you are using for 'comparing' is slow, use 'sortOn' instead of 'sortWith', because 'sortOn' caches applications the function and 'sortWith' doesn't."
    rhs: sortWith f
- warn:
    lhs: sortOn fst
    note: "'sortWith' will be faster here because it doesn't do caching"
    rhs: sortWith fst
- warn:
    lhs: sortOn snd
    note: "'sortWith' will be faster here because it doesn't do caching"
    rhs: sortWith snd
- warn:
    lhs: "sortOn (Down . fst)"
    note: "'sortWith' will be faster here because it doesn't do caching"
    rhs: "sortWith (Down . fst)"
- warn:
    lhs: "sortOn (Down . snd)"
    note: "'sortWith' will be faster here because it doesn't do caching"
    rhs: "sortWith (Down . snd)"
- warn:
    lhs: Data.Text.IO.putStr
    rhs: putText
- warn:
    lhs: Data.Text.IO.putStrLn
    rhs: putTextLn
- warn:
    lhs: Data.Text.Lazy.IO.putStr
    rhs: putLText
- warn:
    lhs: Data.Text.Lazy.IO.putStrLn
    rhs: putLTextLn
- warn:
    lhs: Data.ByteString.Char8.putStr
    rhs: putBS
- warn:
    lhs: Data.ByteString.Char8.putStrLn
    rhs: putBSLn
- warn:
    lhs: Data.ByteString.Lazy.Char8.putStr
    rhs: putLBS
- warn:
    lhs: Data.ByteString.Lazy.Char8.putStrLn
    rhs: putLBSLn
- warn:
    lhs: Data.Text.Lazy.Text
    rhs: LText
- warn:
    lhs: Data.ByteString.Lazy.ByteString
    rhs: LByteString
- warn:
    lhs: Data.ByteString.UTF8.fromString
    rhs: encodeUtf8
- warn:
    lhs: Data.ByteString.UTF8.toString
    rhs: decodeUtf8
- warn:
    lhs: Data.Text.Encoding.encodeUtf8
    rhs: encodeUtf8
- warn:
    lhs: Data.Text.Encoding.decodeUtf8
    rhs: decodeUtf8
- warn:
    lhs: "Data.ByteString.Lazy.toStrict (encodeUtf8 x)"
    rhs: encodeUtf8 x
- warn:
    lhs: "toStrict (encodeUtf8 x)"
    rhs: encodeUtf8 x
- warn:
    lhs: "decodeUtf8 (Data.ByteString.Lazy.fromStrict x)"
    rhs: decodeUtf8 x
- warn:
    lhs: "decodeUtf8 (fromStrict x)"
    rhs: decodeUtf8 x
- warn:
    lhs: Data.ByteString.Lazy.UTF8.fromString
    rhs: encodeUtf8
- warn:
    lhs: Data.ByteString.Lazy.UTF8.toString
    rhs: decodeUtf8
- warn:
    lhs: "Data.ByteString.Lazy.fromStrict (Data.Text.Encoding.encodeUtf8 x)"
    rhs: encodeUtf8 x
- warn:
    lhs: "Data.ByteString.Lazy.fromStrict (encodeUtf8 x)"
    rhs: encodeUtf8 x
- warn:
    lhs: "Data.Text.Encoding.decodeUtf8 (Data.ByteString.Lazy.toStrict x)"
    rhs: decodeUtf8 x
- warn:
    lhs: "Data.Text.Encoding.decodeUtf8 (toStrict x)"
    rhs: decodeUtf8 x
- warn:
    lhs: "decodeUtf8 (Data.ByteString.Lazy.toStrict x)"
    rhs: decodeUtf8 x
- warn:
    lhs: "decodeUtf8 (toStrict x)"
    rhs: decodeUtf8 x
- warn:
    lhs: Data.Text.pack
    rhs: toText
- warn:
    lhs: Data.Text.unpack
    rhs: toString
- warn:
    lhs: Data.Text.Lazy.pack
    rhs: toLText
- warn:
    lhs: Data.Text.Lazy.unpack
    rhs: toString
- warn:
    lhs: Data.Text.Lazy.toStrict
    rhs: toText
- warn:
    lhs: Data.Text.Lazy.fromStrict
    rhs: toLText
- warn:
    lhs: "Data.Text.pack (show x)"
    rhs: show x
- warn:
    lhs: "Data.Text.Lazy.pack (show x)"
    rhs: show x
- warn:
    lhs: Data.ByteString.Lazy.fromStrict
    rhs: fromStrict
- warn:
    lhs: Data.ByteString.Lazy.toStrict
    rhs: toStrict
- warn:
    lhs: Data.Text.Lazy.fromStrict
    rhs: fromStrict
- warn:
    lhs: Data.Text.Lazy.toStrict
    rhs: toStrict
- warn:
    lhs: Control.Applicative.Alternative
    name: "Use 'Alternative' from Relude"
    note: "'Alternative' is already exported from Relude"
    rhs: Alternative
- warn:
    lhs: Control.Applicative.empty
    name: "Use 'empty' from Relude"
    note: "'empty' is already exported from Relude"
    rhs: empty
- warn:
    lhs: "(Control.Applicative.<|>)"
    name: "Use '<|>' from Relude"
    note: "Operator '(<|>)' is already exported from Relude"
    rhs: "(<|>)"
- warn:
    lhs: Control.Applicative.some
    name: "Use 'some' from Relude"
    note: "'some' is already exported from Relude"
    rhs: some
- warn:
    lhs: Control.Applicative.many
    name: "Use 'many' from Relude"
    note: "'many' is already exported from Relude"
    rhs: many
- warn:
    lhs: Control.Applicative.Const
    name: "Use 'Const' from Relude"
    note: "'Const' is already exported from Relude"
    rhs: Const
- warn:
    lhs: Control.Applicative.getConst
    name: "Use 'getConst' from Relude"
    note: "'getConst' is already exported from Relude"
    rhs: getConst
- warn:
    lhs: Control.Applicative.ZipList
    name: "Use 'ZipList' from Relude"
    note: "'ZipList' is already exported from Relude"
    rhs: ZipList
- warn:
    lhs: Control.Applicative.getZipList
    name: "Use 'getZipList' from Relude"
    note: "'getZipList' is already exported from Relude"
    rhs: getZipList
- warn:
    lhs: Control.Applicative.liftA2
    name: "Use 'liftA2' from Relude"
    note: "'liftA2' is already exported from Relude"
    rhs: liftA2
- warn:
    lhs: Control.Applicative.liftA3
    name: "Use 'liftA3' from Relude"
    note: "'liftA3' is already exported from Relude"
    rhs: liftA3
- warn:
    lhs: Control.Applicative.optional
    name: "Use 'optional' from Relude"
    note: "'optional' is already exported from Relude"
    rhs: optional
- warn:
    lhs: "(Control.Applicative.<**>)"
    name: "Use '<**>' from Relude"
    note: "Operator '(<**>)' is already exported from Relude"
    rhs: "(<**>)"
- warn:
    lhs: Data.Bits.xor
    name: "Use 'xor' from Relude"
    note: "'xor' is already exported from Relude"
    rhs: xor
- warn:
    lhs: Data.Char.chr
    name: "Use 'chr' from Relude"
    note: "'chr' is already exported from Relude"
    rhs: chr
- warn:
    lhs: Data.Int.Int8
    name: "Use 'Int8' from Relude"
    note: "'Int8' is already exported from Relude"
    rhs: Int8
- warn:
    lhs: Data.Int.Int16
    name: "Use 'Int16' from Relude"
    note: "'Int16' is already exported from Relude"
    rhs: Int16
- warn:
    lhs: Data.Int.Int32
    name: "Use 'Int32' from Relude"
    note: "'Int32' is already exported from Relude"
    rhs: Int32
- warn:
    lhs: Data.Int.Int64
    name: "Use 'Int64' from Relude"
    note: "'Int64' is already exported from Relude"
    rhs: Int64
- warn:
    lhs: Data.Word.Word8
    name: "Use 'Word8' from Relude"
    note: "'Word8' is already exported from Relude"
    rhs: Word8
- warn:
    lhs: Data.Word.Word16
    name: "Use 'Word16' from Relude"
    note: "'Word16' is already exported from Relude"
    rhs: Word16
- warn:
    lhs: Data.Word.Word32
    name: "Use 'Word32' from Relude"
    note: "'Word32' is already exported from Relude"
    rhs: Word32
- warn:
    lhs: Data.Word.Word64
    name: "Use 'Word64' from Relude"
    note: "'Word64' is already exported from Relude"
    rhs: Word64
- warn:
    lhs: Data.Word.byteSwap16
    name: "Use 'byteSwap16' from Relude"
    note: "'byteSwap16' is already exported from Relude"
    rhs: byteSwap16
- warn:
    lhs: Data.Word.byteSwap32
    name: "Use 'byteSwap32' from Relude"
    note: "'byteSwap32' is already exported from Relude"
    rhs: byteSwap32
- warn:
    lhs: Data.Word.byteSwap64
    name: "Use 'byteSwap64' from Relude"
    note: "'byteSwap64' is already exported from Relude"
    rhs: byteSwap64
- warn:
    lhs: Numeric.Natural.Natural
    name: "Use 'Natural' from Relude"
    note: "'Natural' is already exported from Relude"
    rhs: Natural
- warn:
    lhs: System.IO.IOMode
    name: "Use 'IOMode' from Relude"
    note: "'IOMode' is already exported from Relude"
    rhs: IOMode
- warn:
    lhs: System.IO.ReadMode
    name: "Use 'ReadMode' from Relude"
    note: "'ReadMode' is already exported from Relude"
    rhs: ReadMode
- warn:
    lhs: System.IO.WriteMode
    name: "Use 'WriteMode' from Relude"
    note: "'WriteMode' is already exported from Relude"
    rhs: WriteMode
- warn:
    lhs: System.IO.AppendMode
    name: "Use 'AppendMode' from Relude"
    note: "'AppendMode' is already exported from Relude"
    rhs: AppendMode
- warn:
    lhs: System.IO.ReadWriteMode
    name: "Use 'ReadWriteMode' from Relude"
    note: "'ReadWriteMode' is already exported from Relude"
    rhs: ReadWriteMode
- warn:
    lhs: Data.Ord.Down
    name: "Use 'Down' from Relude"
    note: "'Down' is already exported from Relude"
    rhs: Down
- warn:
    lhs: Data.Ord.comparing
    name: "Use 'comparing' from Relude"
    note: "'comparing' is already exported from Relude"
    rhs: comparing
- warn:
    lhs: Data.Coerce.Coercible
    name: "Use 'Coercible' from Relude"
    note: "'Coercible' is already exported from Relude"
    rhs: Coercible
- warn:
    lhs: Data.Coerce.coerce
    name: "Use 'coerce' from Relude"
    note: "'coerce' is already exported from Relude"
    rhs: coerce
- warn:
    lhs: Data.Kind.Constraint
    name: "Use 'Constraint' from Relude"
    note: "'Constraint' is already exported from Relude"
    rhs: Constraint
- warn:
    lhs: Data.Kind.Type
    name: "Use 'Type' from Relude"
    note: "'Type' is already exported from Relude"
    rhs: Type
- warn:
    lhs: Data.Typeable.Typeable
    name: "Use 'Typeable' from Relude"
    note: "'Typeable' is already exported from Relude"
    rhs: Typeable
- warn:
    lhs: Data.Proxy.Proxy
    name: "Use 'Proxy' from Relude"
    note: "'Proxy' is already exported from Relude"
    rhs: Proxy
- warn:
    lhs: Data.Typeable.Typeable
    name: "Use 'Typeable' from Relude"
    note: "'Typeable' is already exported from Relude"
    rhs: Typeable
- warn:
    lhs: Data.Void.Void
    name: "Use 'Void' from Relude"
    note: "'Void' is already exported from Relude"
    rhs: Void
- warn:
    lhs: Data.Void.absurd
    name: "Use 'absurd' from Relude"
    note: "'absurd' is already exported from Relude"
    rhs: absurd
- warn:
    lhs: Data.Void.vacuous
    name: "Use 'vacuous' from Relude"
    note: "'vacuous' is already exported from Relude"
    rhs: vacuous
- warn:
    lhs: Data.Base.maxInt
    name: "Use 'maxInt' from Relude"
    note: "'maxInt' is already exported from Relude"
    rhs: maxInt
- warn:
    lhs: Data.Base.minInt
    name: "Use 'minInt' from Relude"
    note: "'minInt' is already exported from Relude"
    rhs: minInt
- warn:
    lhs: Data.Base.ord
    name: "Use 'ord' from Relude"
    note: "'ord' is already exported from Relude"
    rhs: ord
- warn:
    lhs: GHC.Enum.boundedEnumFrom
    name: "Use 'boundedEnumFrom' from Relude"
    note: "'boundedEnumFrom' is already exported from Relude"
    rhs: boundedEnumFrom
- warn:
    lhs: GHC.Enum.boundedEnumFromThen
    name: "Use 'boundedEnumFromThen' from Relude"
    note: "'boundedEnumFromThen' is already exported from Relude"
    rhs: boundedEnumFromThen
- warn:
    lhs: GHC.Generics.Generic
    name: "Use 'Generic' from Relude"
    note: "'Generic' is already exported from Relude"
    rhs: Generic
- warn:
    lhs: GHC.Real.Ratio
    name: "Use 'Ratio' from Relude"
    note: "'Ratio' is already exported from Relude"
    rhs: Ratio
- warn:
    lhs: GHC.Real.Rational
    name: "Use 'Rational' from Relude"
    note: "'Rational' is already exported from Relude"
    rhs: Rational
- warn:
    lhs: GHC.Real.denominator
    name: "Use 'denominator' from Relude"
    note: "'denominator' is already exported from Relude"
    rhs: denominator
- warn:
    lhs: GHC.Real.numerator
    name: "Use 'numerator' from Relude"
    note: "'numerator' is already exported from Relude"
    rhs: numerator
- warn:
    lhs: GHC.TypeNats.CmpNat
    name: "Use 'CmpNat' from Relude"
    note: "'CmpNat' is already exported from Relude"
    rhs: CmpNat
- warn:
    lhs: GHC.TypeNats.KnownNat
    name: "Use 'KnownNat' from Relude"
    note: "'KnownNat' is already exported from Relude"
    rhs: KnownNat
- warn:
    lhs: GHC.TypeNats.Nat
    name: "Use 'Nat' from Relude"
    note: "'Nat' is already exported from Relude"
    rhs: Nat
- warn:
    lhs: GHC.TypeNats.SomeNat
    name: "Use 'SomeNat' from Relude"
    note: "'SomeNat' is already exported from Relude"
    rhs: SomeNat
- warn:
    lhs: GHC.TypeNats.natVal
    name: "Use 'natVal' from Relude"
    note: "'natVal' is already exported from Relude"
    rhs: natVal
- warn:
    lhs: GHC.TypeNats.someNatVal
    name: "Use 'someNatVal' from Relude"
    note: "'someNatVal' is already exported from Relude"
    rhs: someNatVal
- warn:
    lhs: GHC.TypeLits.CmpNat
    name: "Use 'CmpNat' from Relude"
    note: "'CmpNat' is already exported from Relude"
    rhs: CmpNat
- warn:
    lhs: GHC.TypeLits.KnownNat
    name: "Use 'KnownNat' from Relude"
    note: "'KnownNat' is already exported from Relude"
    rhs: KnownNat
- warn:
    lhs: GHC.TypeLits.Nat
    name: "Use 'Nat' from Relude"
    note: "'Nat' is already exported from Relude"
    rhs: Nat
- warn:
    lhs: GHC.TypeLits.SomeNat
    name: "Use 'SomeNat' from Relude"
    note: "'SomeNat' is already exported from Relude"
    rhs: SomeNat
- warn:
    lhs: GHC.TypeLits.natVal
    name: "Use 'natVal' from Relude"
    note: "'natVal' is already exported from Relude"
    rhs: natVal
- warn:
    lhs: GHC.TypeLits.someNatVal
    name: "Use 'someNatVal' from Relude"
    note: "'someNatVal' is already exported from Relude"
    rhs: someNatVal
- warn:
    lhs: GHC.ExecutionStack.getStackTrace
    name: "Use 'getStackTrace' from Relude"
    note: "'getStackTrace' is already exported from Relude"
    rhs: getStackTrace
- warn:
    lhs: GHC.ExecutionStack.showStackTrace
    name: "Use 'showStackTrace' from Relude"
    note: "'showStackTrace' is already exported from Relude"
    rhs: showStackTrace
- warn:
    lhs: GHC.OverloadedLabels.IsLabel
    name: "Use 'IsLabel' from Relude"
    note: "'IsLabel' is already exported from Relude"
    rhs: IsLabel
- warn:
    lhs: GHC.OverloadedLabels.fromLabel
    name: "Use 'fromLabel' from Relude"
    note: "'fromLabel' is already exported from Relude"
    rhs: fromLabel
- warn:
    lhs: GHC.Stack.CallStack
    name: "Use 'CallStack' from Relude"
    note: "'CallStack' is already exported from Relude"
    rhs: CallStack
- warn:
    lhs: GHC.Stack.HasCallStack
    name: "Use 'HasCallStack' from Relude"
    note: "'HasCallStack' is already exported from Relude"
    rhs: HasCallStack
- warn:
    lhs: GHC.Stack.callStack
    name: "Use 'callStack' from Relude"
    note: "'callStack' is already exported from Relude"
    rhs: callStack
- warn:
    lhs: GHC.Stack.currentCallStack
    name: "Use 'currentCallStack' from Relude"
    note: "'currentCallStack' is already exported from Relude"
    rhs: currentCallStack
- warn:
    lhs: GHC.Stack.getCallStack
    name: "Use 'getCallStack' from Relude"
    note: "'getCallStack' is already exported from Relude"
    rhs: getCallStack
- warn:
    lhs: GHC.Stack.prettyCallStack
    name: "Use 'prettyCallStack' from Relude"
    note: "'prettyCallStack' is already exported from Relude"
    rhs: prettyCallStack
- warn:
    lhs: GHC.Stack.prettySrcLoc
    name: "Use 'prettySrcLoc' from Relude"
    note: "'prettySrcLoc' is already exported from Relude"
    rhs: prettySrcLoc
- warn:
    lhs: GHC.Stack.withFrozenCallStack
    name: "Use 'withFrozenCallStack' from Relude"
    note: "'withFrozenCallStack' is already exported from Relude"
    rhs: withFrozenCallStack
- warn:
    lhs: Data.Bifoldable.Bifoldable
    name: "Use 'Bifoldable' from Relude"
    note: "'Bifoldable' is already exported from Relude"
    rhs: Bifoldable
- warn:
    lhs: Data.Bifoldable.bifold
    name: "Use 'bifold' from Relude"
    note: "'bifold' is already exported from Relude"
    rhs: bifold
- warn:
    lhs: Data.Bifoldable.bifoldMap
    name: "Use 'bifoldMap' from Relude"
    note: "'bifoldMap' is already exported from Relude"
    rhs: bifoldMap
- warn:
    lhs: Data.Bifoldable.bifoldr
    name: "Use 'bifoldr' from Relude"
    note: "'bifoldr' is already exported from Relude"
    rhs: bifoldr
- warn:
    lhs: Data.Bifoldable.bifoldl
    name: "Use 'bifoldl' from Relude"
    note: "'bifoldl' is already exported from Relude"
    rhs: bifoldl
- warn:
    lhs: "Data.Bifoldable.bifoldl'"
    name: "Use 'bifoldl'' from Relude"
    note: "'bifoldl'' is already exported from Relude"
    rhs: "bifoldl'"
- warn:
    lhs: Data.Bifoldable.bifoldlM
    name: "Use 'bifoldlM' from Relude"
    note: "'bifoldlM' is already exported from Relude"
    rhs: bifoldlM
- warn:
    lhs: "Data.Bifoldable.bifoldr'"
    name: "Use 'bifoldr'' from Relude"
    note: "'bifoldr'' is already exported from Relude"
    rhs: "bifoldr'"
- warn:
    lhs: Data.Bifoldable.bifoldrM
    name: "Use 'bifoldrM' from Relude"
    note: "'bifoldrM' is already exported from Relude"
    rhs: bifoldrM
- warn:
    lhs: Data.Bifoldable.bitraverse_
    name: "Use 'bitraverse_' from Relude"
    note: "'bitraverse_' is already exported from Relude"
    rhs: bitraverse_
- warn:
    lhs: Data.Bifoldable.bifor_
    name: "Use 'bifor_' from Relude"
    note: "'bifor_' is already exported from Relude"
    rhs: bifor_
- warn:
    lhs: Data.Bifoldable.biasum
    name: "Use 'biasum' from Relude"
    note: "'biasum' is already exported from Relude"
    rhs: biasum
- warn:
    lhs: Data.Bifoldable.bisequence_
    name: "Use 'bisequence_' from Relude"
    note: "'bisequence_' is already exported from Relude"
    rhs: bisequence_
- warn:
    lhs: Data.Bifoldable.biList
    name: "Use 'biList' from Relude"
    note: "'biList' is already exported from Relude"
    rhs: biList
- warn:
    lhs: Data.Bifoldable.binull
    name: "Use 'binull' from Relude"
    note: "'binull' is already exported from Relude"
    rhs: binull
- warn:
    lhs: Data.Bifoldable.bilength
    name: "Use 'bilength' from Relude"
    note: "'bilength' is already exported from Relude"
    rhs: bilength
- warn:
    lhs: Data.Bifoldable.bielem
    name: "Use 'bielem' from Relude"
    note: "'bielem' is already exported from Relude"
    rhs: bielem
- warn:
    lhs: Data.Bifoldable.biand
    name: "Use 'biand' from Relude"
    note: "'biand' is already exported from Relude"
    rhs: biand
- warn:
    lhs: Data.Bifoldable.bior
    name: "Use 'bior' from Relude"
    note: "'bior' is already exported from Relude"
    rhs: bior
- warn:
    lhs: Data.Bifoldable.biany
    name: "Use 'biany' from Relude"
    note: "'biany' is already exported from Relude"
    rhs: biany
- warn:
    lhs: Data.Bifoldable.biall
    name: "Use 'biall' from Relude"
    note: "'biall' is already exported from Relude"
    rhs: biall
- warn:
    lhs: Data.Bifoldable.bifind
    name: "Use 'bifind' from Relude"
    note: "'bifind' is already exported from Relude"
    rhs: bifind
- warn:
    lhs: Data.Bitraversable.Bitraversable
    name: "Use 'Bitraversable' from Relude"
    note: "'Bitraversable' is already exported from Relude"
    rhs: Bitraversable
- warn:
    lhs: Data.Bitraversable.bitraverse
    name: "Use 'bitraverse' from Relude"
    note: "'bitraverse' is already exported from Relude"
    rhs: bitraverse
- warn:
    lhs: Data.Bitraversable.bisequence
    name: "Use 'bisequence' from Relude"
    note: "'bisequence' is already exported from Relude"
    rhs: bisequence
- warn:
    lhs: Data.Bitraversable.bifor
    name: "Use 'bifor' from Relude"
    note: "'bifor' is already exported from Relude"
    rhs: bifor
- warn:
    lhs: Data.Bitraversable.bimapDefault
    name: "Use 'bimapDefault' from Relude"
    note: "'bimapDefault' is already exported from Relude"
    rhs: bimapDefault
- warn:
    lhs: Data.Bitraversable.bifoldMapDefault
    name: "Use 'bifoldMapDefault' from Relude"
    note: "'bifoldMapDefault' is already exported from Relude"
    rhs: bifoldMapDefault
- warn:
    lhs: Control.Monad.guard
    name: "Use 'guard' from Relude"
    note: "'guard' is already exported from Relude"
    rhs: guard
- warn:
    lhs: Control.Monad.unless
    name: "Use 'unless' from Relude"
    note: "'unless' is already exported from Relude"
    rhs: unless
- warn:
    lhs: Control.Monad.when
    name: "Use 'when' from Relude"
    note: "'when' is already exported from Relude"
    rhs: when
- warn:
    lhs: Data.Bool.bool
    name: "Use 'bool' from Relude"
    note: "'bool' is already exported from Relude"
    rhs: bool
- warn:
    lhs: Data.Hashable.Hashable
    name: "Use 'Hashable' from Relude"
    note: "'Hashable' is already exported from Relude"
    rhs: Hashable
- warn:
    lhs: Data.Hashable.hashWithSalt
    name: "Use 'hashWithSalt' from Relude"
    note: "'hashWithSalt' is already exported from Relude"
    rhs: hashWithSalt
- warn:
    lhs: Data.HashMap.Strict.HashMap
    name: "Use 'HashMap' from Relude"
    note: "'HashMap' is already exported from Relude"
    rhs: HashMap
- warn:
    lhs: Data.HashSet.HashSet
    name: "Use 'HashSet' from Relude"
    note: "'HashSet' is already exported from Relude"
    rhs: HashSet
- warn:
    lhs: Data.IntMap.Strict.IntMap
    name: "Use 'IntMap' from Relude"
    note: "'IntMap' is already exported from Relude"
    rhs: IntMap
- warn:
    lhs: Data.IntSet.IntSet
    name: "Use 'IntSet' from Relude"
    note: "'IntSet' is already exported from Relude"
    rhs: IntSet
- warn:
    lhs: Data.Map.Strict.Map
    name: "Use 'Map' from Relude"
    note: "'Map' is already exported from Relude"
    rhs: Map
- warn:
    lhs: Data.Sequence.Sequence
    name: "Use 'Sequence' from Relude"
    note: "'Sequence' is already exported from Relude"
    rhs: Sequence
- warn:
    lhs: Data.Set.Set
    name: "Use 'Set' from Relude"
    note: "'Set' is already exported from Relude"
    rhs: Set
- warn:
    lhs: Data.Tuple.swap
    name: "Use 'swap' from Relude"
    note: "'swap' is already exported from Relude"
    rhs: swap
- warn:
    lhs: Data.Vector.Vector
    name: "Use 'Vector' from Relude"
    note: "'Vector' is already exported from Relude"
    rhs: Vector
- warn:
    lhs: GHC.Exts.IsList
    name: "Use 'IsList' from Relude"
    note: "'IsList' is already exported from Relude"
    rhs: IsList
- warn:
    lhs: GHC.Exts.fromList
    name: "Use 'fromList' from Relude"
    note: "'fromList' is already exported from Relude"
    rhs: fromList
- warn:
    lhs: GHC.Exts.fromListN
    name: "Use 'fromListN' from Relude"
    note: "'fromListN' is already exported from Relude"
    rhs: fromListN
- warn:
    lhs: Debug.Trace.trace
    name: "Use 'trace' from Relude"
    note: "'trace' is already exported from Relude"
    rhs: trace
- warn:
    lhs: Debug.Trace.traceShow
    name: "Use 'traceShow' from Relude"
    note: "'traceShow' is already exported from Relude"
    rhs: traceShow
- warn:
    lhs: Debug.Trace.traceShowId
    name: "Use 'traceShowId' from Relude"
    note: "'traceShowId' is already exported from Relude"
    rhs: traceShowId
- warn:
    lhs: Debug.Trace.traceShowM
    name: "Use 'traceShowM' from Relude"
    note: "'traceShowM' is already exported from Relude"
    rhs: traceShowM
- warn:
    lhs: Debug.Trace.traceM
    name: "Use 'traceM' from Relude"
    note: "'traceM' is already exported from Relude"
    rhs: traceM
- warn:
    lhs: Debug.Trace.traceId
    name: "Use 'traceId' from Relude"
    note: "'traceId' is already exported from Relude"
    rhs: traceId
- warn:
    lhs: Control.DeepSeq.NFData
    name: "Use 'NFData' from Relude"
    note: "'NFData' is already exported from Relude"
    rhs: NFData
- warn:
    lhs: Control.DeepSeq.rnf
    name: "Use 'rnf' from Relude"
    note: "'rnf' is already exported from Relude"
    rhs: rnf
- warn:
    lhs: Control.DeepSeq.deepseq
    name: "Use 'deepseq' from Relude"
    note: "'deepseq' is already exported from Relude"
    rhs: deepseq
- warn:
    lhs: Control.DeepSeq.force
    name: "Use 'force' from Relude"
    note: "'force' is already exported from Relude"
    rhs: force
- warn:
    lhs: "(Control.DeepSeq.$!!)"
    name: "Use '$!!' from Relude"
    note: "Operator '($!!)' is already exported from Relude"
    rhs: "($!!)"
- warn:
    lhs: Control.Exception.Exception
    name: "Use 'Exception' from Relude"
    note: "'Exception' is already exported from Relude"
    rhs: Exception
- warn:
    lhs: Control.Exception.SomeException
    name: "Use 'SomeException' from Relude"
    note: "'SomeException' is already exported from Relude"
    rhs: SomeException
- warn:
    lhs: Control.Exception.toException
    name: "Use 'toException' from Relude"
    note: "'toException' is already exported from Relude"
    rhs: toException
- warn:
    lhs: Control.Exception.fromException
    name: "Use 'fromException' from Relude"
    note: "'fromException' is already exported from Relude"
    rhs: fromException
- warn:
    lhs: Control.Exception.displayException
    name: "Use 'displayException' from Relude"
    note: "'displayException' is already exported from Relude"
    rhs: displayException
- warn:
    lhs: Data.Foldable.asum
    name: "Use 'asum' from Relude"
    note: "'asum' is already exported from Relude"
    rhs: asum
- warn:
    lhs: Data.Foldable.find
    name: "Use 'find' from Relude"
    note: "'find' is already exported from Relude"
    rhs: find
- warn:
    lhs: Data.Foldable.find
    name: "Use 'find' from Relude"
    note: "'find' is already exported from Relude"
    rhs: find
# - warn:
#     lhs: Data.Foldable.fold
#     name: "Use 'fold' from Relude"
#     note: "'fold' is already exported from Relude"
#     rhs: fold
- warn:
    lhs: "Data.Foldable.foldl'"
    name: "Use 'foldl'' from Relude"
    note: "'foldl'' is already exported from Relude"
    rhs: "foldl'"
- warn:
    lhs: Data.Foldable.forM_
    name: "Use 'forM_' from Relude"
    note: "'forM_' is already exported from Relude"
    rhs: forM_
- warn:
    lhs: Data.Foldable.for_
    name: "Use 'for_' from Relude"
    note: "'for_' is already exported from Relude"
    rhs: for_
- warn:
    lhs: Data.Foldable.sequenceA_
    name: "Use 'sequenceA_' from Relude"
    note: "'sequenceA_' is already exported from Relude"
    rhs: sequenceA_
- warn:
    lhs: Data.Foldable.toList
    name: "Use 'toList' from Relude"
    note: "'toList' is already exported from Relude"
    rhs: toList
- warn:
    lhs: Data.Foldable.traverse_
    name: "Use 'traverse_' from Relude"
    note: "'traverse_' is already exported from Relude"
    rhs: traverse_
- warn:
    lhs: Data.Traversable.forM
    name: "Use 'forM' from Relude"
    note: "'forM' is already exported from Relude"
    rhs: forM
- warn:
    lhs: Data.Traversable.mapAccumL
    name: "Use 'mapAccumL' from Relude"
    note: "'mapAccumL' is already exported from Relude"
    rhs: mapAccumL
- warn:
    lhs: Data.Traversable.mapAccumR
    name: "Use 'mapAccumR' from Relude"
    note: "'mapAccumR' is already exported from Relude"
    rhs: mapAccumR
- warn:
    lhs: "(Control.Arrow.&&&)"
    name: "Use '&&&' from Relude"
    note: "Operator '(&&&)' is already exported from Relude"
    rhs: "(&&&)"
- warn:
    lhs: "(Control.Category.>>>)"
    name: "Use '>>>' from Relude"
    note: "Operator '(>>>)' is already exported from Relude"
    rhs: "(>>>)"
- warn:
    lhs: "(Control.Category.<<<)"
    name: "Use '<<<' from Relude"
    note: "Operator '(<<<)' is already exported from Relude"
    rhs: "(<<<)"
- warn:
    lhs: Data.Function.fix
    name: "Use 'fix' from Relude"
    note: "'fix' is already exported from Relude"
    rhs: fix
- warn:
    lhs: Data.Function.on
    name: "Use 'on' from Relude"
    note: "'on' is already exported from Relude"
    rhs: 'on'
- warn:
    lhs: Data.Bifunctor.Bifunctor
    name: "Use 'Bifunctor' from Relude"
    note: "'Bifunctor' is already exported from Relude"
    rhs: Bifunctor
- warn:
    lhs: Data.Bifunctor.bimap
    name: "Use 'bimap' from Relude"
    note: "'bimap' is already exported from Relude"
    rhs: bimap
- warn:
    lhs: Data.Bifunctor.first
    name: "Use 'first' from Relude"
    note: "'first' is already exported from Relude"
    rhs: first
- warn:
    lhs: Data.Bifunctor.second
    name: "Use 'second' from Relude"
    note: "'second' is already exported from Relude"
    rhs: second
- warn:
    lhs: Data.Functor.void
    name: "Use 'void' from Relude"
    note: "'void' is already exported from Relude"
    rhs: void
- warn:
    lhs: "(Data.Functor.$>)"
    name: "Use '$>' from Relude"
    note: "Operator '($>)' is already exported from Relude"
    rhs: "($>)"
- warn:
    lhs: "(Data.Functor.<&>)"
    name: "Use '<&>' from Relude"
    note: "Operator '(<&>)' is already exported from Relude"
    rhs: "(<&>)"
- warn:
    lhs: Data.Functor.Compose.Compose
    name: "Use 'Compose' from Relude"
    note: "'Compose' is already exported from Relude"
    rhs: Compose
- warn:
    lhs: Data.Functor.Compose.getCompose
    name: "Use 'getCompose' from Relude"
    note: "'getCompose' is already exported from Relude"
    rhs: getCompose
- warn:
    lhs: Data.Functor.Identity.Identity
    name: "Use 'Identity' from Relude"
    note: "'Identity' is already exported from Relude"
    rhs: Identity
- warn:
    lhs: Data.Functor.Identity.runIdentity
    name: "Use 'runIdentity' from Relude"
    note: "'runIdentity' is already exported from Relude"
    rhs: runIdentity
- warn:
    lhs: Control.Concurrent.MVar.MVar
    name: "Use 'MVar' from Relude"
    note: "'MVar' is already exported from Relude"
    rhs: MVar
- warn:
    lhs: Control.Concurrent.MVar.newEmptyMVar
    name: "Use 'newEmptyMVar' from Relude"
    note: "'newEmptyMVar' is already exported from Relude"
    rhs: newEmptyMVar
- warn:
    lhs: Control.Concurrent.MVar.newMVar
    name: "Use 'newMVar' from Relude"
    note: "'newMVar' is already exported from Relude"
    rhs: newMVar
- warn:
    lhs: Control.Concurrent.MVar.putMVar
    name: "Use 'putMVar' from Relude"
    note: "'putMVar' is already exported from Relude"
    rhs: putMVar
- warn:
    lhs: Control.Concurrent.MVar.readMVar
    name: "Use 'readMVar' from Relude"
    note: "'readMVar' is already exported from Relude"
    rhs: readMVar
- warn:
    lhs: Control.Concurrent.MVar.swapMVar
    name: "Use 'swapMVar' from Relude"
    note: "'swapMVar' is already exported from Relude"
    rhs: swapMVar
- warn:
    lhs: Control.Concurrent.MVar.takeMVar
    name: "Use 'takeMVar' from Relude"
    note: "'takeMVar' is already exported from Relude"
    rhs: takeMVar
- warn:
    lhs: Control.Concurrent.MVar.tryPutMVar
    name: "Use 'tryPutMVar' from Relude"
    note: "'tryPutMVar' is already exported from Relude"
    rhs: tryPutMVar
- warn:
    lhs: Control.Concurrent.MVar.tryReadMVar
    name: "Use 'tryReadMVar' from Relude"
    note: "'tryReadMVar' is already exported from Relude"
    rhs: tryReadMVar
- warn:
    lhs: Control.Concurrent.MVar.tryTakeMVar
    name: "Use 'tryTakeMVar' from Relude"
    note: "'tryTakeMVar' is already exported from Relude"
    rhs: tryTakeMVar
- warn:
    lhs: Control.Monad.STM.STM
    name: "Use 'STM' from Relude"
    note: "'STM' is already exported from Relude"
    rhs: STM
- warn:
    lhs: Control.Monad.STM.atomically
    name: "Use 'atomically' from Relude"
    note: "'atomically' is already exported from Relude"
    rhs: atomically
- warn:
    lhs: Control.Monad.STM.throwSTM
    name: "Use 'throwSTM' from Relude"
    note: "'throwSTM' is already exported from Relude"
    rhs: throwSTM
- warn:
    lhs: Control.Monad.STM.catchSTM
    name: "Use 'catchSTM' from Relude"
    note: "'catchSTM' is already exported from Relude"
    rhs: catchSTM
- warn:
    lhs: Control.Concurrent.STM.TVar.TVar
    name: "Use 'TVar' from Relude"
    note: "'TVar' is already exported from Relude"
    rhs: TVar
- warn:
    lhs: Control.Concurrent.STM.TVar.newTVarIO
    name: "Use 'newTVarIO' from Relude"
    note: "'newTVarIO' is already exported from Relude"
    rhs: newTVarIO
- warn:
    lhs: Control.Concurrent.STM.TVar.readTVarIO
    name: "Use 'readTVarIO' from Relude"
    note: "'readTVarIO' is already exported from Relude"
    rhs: readTVarIO
- warn:
    lhs: "Control.Concurrent.STM.TVar.modifyTVar'"
    name: "Use 'modifyTVar'' from Relude"
    note: "'modifyTVar'' is already exported from Relude"
    rhs: "modifyTVar'"
- warn:
    lhs: Control.Concurrent.STM.TVar.newTVar
    name: "Use 'newTVar' from Relude"
    note: "'newTVar' is already exported from Relude"
    rhs: newTVar
- warn:
    lhs: Control.Concurrent.STM.TVar.readTVar
    name: "Use 'readTVar' from Relude"
    note: "'readTVar' is already exported from Relude"
    rhs: readTVar
- warn:
    lhs: Control.Concurrent.STM.TVar.writeTVar
    name: "Use 'writeTVar' from Relude"
    note: "'writeTVar' is already exported from Relude"
    rhs: writeTVar
- warn:
    lhs: Control.Concurrent.STM.TMVar.TMVar
    name: "Use 'TMVar' from Relude"
    note: "'TMVar' is already exported from Relude"
    rhs: TMVar
- warn:
    lhs: Control.Concurrent.STM.TMVar.newTMVar
    name: "Use 'newTMVar' from Relude"
    note: "'newTMVar' is already exported from Relude"
    rhs: newTMVar
- warn:
    lhs: Control.Concurrent.STM.TMVar.newEmptyTMVar
    name: "Use 'newEmptyTMVar' from Relude"
    note: "'newEmptyTMVar' is already exported from Relude"
    rhs: newEmptyTMVar
- warn:
    lhs: Control.Concurrent.STM.TMVar.newTMVarIO
    name: "Use 'newTMVarIO' from Relude"
    note: "'newTMVarIO' is already exported from Relude"
    rhs: newTMVarIO
- warn:
    lhs: Control.Concurrent.STM.TMVar.newEmptyTMVarIO
    name: "Use 'newEmptyTMVarIO' from Relude"
    note: "'newEmptyTMVarIO' is already exported from Relude"
    rhs: newEmptyTMVarIO
- warn:
    lhs: Control.Concurrent.STM.TMVar.takeTMVar
    name: "Use 'takeTMVar' from Relude"
    note: "'takeTMVar' is already exported from Relude"
    rhs: takeTMVar
- warn:
    lhs: Control.Concurrent.STM.TMVar.putTMVar
    name: "Use 'putTMVar' from Relude"
    note: "'putTMVar' is already exported from Relude"
    rhs: putTMVar
- warn:
    lhs: Control.Concurrent.STM.TMVar.readTMVar
    name: "Use 'readTMVar' from Relude"
    note: "'readTMVar' is already exported from Relude"
    rhs: readTMVar
- warn:
    lhs: Control.Concurrent.STM.TMVar.tryReadTMVar
    name: "Use 'tryReadTMVar' from Relude"
    note: "'tryReadTMVar' is already exported from Relude"
    rhs: tryReadTMVar
- warn:
    lhs: Control.Concurrent.STM.TMVar.swapTMVar
    name: "Use 'swapTMVar' from Relude"
    note: "'swapTMVar' is already exported from Relude"
    rhs: swapTMVar
- warn:
    lhs: Control.Concurrent.STM.TMVar.tryTakeTMVar
    name: "Use 'tryTakeTMVar' from Relude"
    note: "'tryTakeTMVar' is already exported from Relude"
    rhs: tryTakeTMVar
- warn:
    lhs: Control.Concurrent.STM.TMVar.tryPutTMVar
    name: "Use 'tryPutTMVar' from Relude"
    note: "'tryPutTMVar' is already exported from Relude"
    rhs: tryPutTMVar
- warn:
    lhs: Control.Concurrent.STM.TMVar.isEmptyTMVar
    name: "Use 'isEmptyTMVar' from Relude"
    note: "'isEmptyTMVar' is already exported from Relude"
    rhs: isEmptyTMVar
- warn:
    lhs: Control.Concurrent.STM.TMVar.mkWeakTMVar
    name: "Use 'mkWeakTMVar' from Relude"
    note: "'mkWeakTMVar' is already exported from Relude"
    rhs: mkWeakTMVar
- warn:
    lhs: Data.IORef.IORef
    name: "Use 'IORef' from Relude"
    note: "'IORef' is already exported from Relude"
    rhs: IORef
- warn:
    lhs: Data.IORef.atomicModifyIORef
    name: "Use 'atomicModifyIORef' from Relude"
    note: "'atomicModifyIORef' is already exported from Relude"
    rhs: atomicModifyIORef
- warn:
    lhs: "Data.IORef.atomicModifyIORef'"
    name: "Use 'atomicModifyIORef'' from Relude"
    note: "'atomicModifyIORef'' is already exported from Relude"
    rhs: "atomicModifyIORef'"
- warn:
    lhs: Data.IORef.atomicWriteIORef
    name: "Use 'atomicWriteIORef' from Relude"
    note: "'atomicWriteIORef' is already exported from Relude"
    rhs: atomicWriteIORef
- warn:
    lhs: Data.IORef.modifyIORef
    name: "Use 'modifyIORef' from Relude"
    note: "'modifyIORef' is already exported from Relude"
    rhs: modifyIORef
- warn:
    lhs: "Data.IORef.modifyIORef'"
    name: "Use 'modifyIORef'' from Relude"
    note: "'modifyIORef'' is already exported from Relude"
    rhs: "modifyIORef'"
- warn:
    lhs: Data.IORef.newIORef
    name: "Use 'newIORef' from Relude"
    note: "'newIORef' is already exported from Relude"
    rhs: newIORef
- warn:
    lhs: Data.IORef.readIORef
    name: "Use 'readIORef' from Relude"
    note: "'readIORef' is already exported from Relude"
    rhs: readIORef
- warn:
    lhs: Data.IORef.writeIORef
    name: "Use 'writeIORef' from Relude"
    note: "'writeIORef' is already exported from Relude"
    rhs: writeIORef
- warn:
    lhs: "atomicModifyIORef ref (\\a -> (f a, ()))"
    rhs: atomicModifyIORef_ ref f
- warn:
    lhs: "atomicModifyIORef ref $ \\a -> (f a, ())"
    rhs: atomicModifyIORef_ ref f
- warn:
    lhs: "atomicModifyIORef' ref $ \\a -> (f a, ())"
    rhs: "atomicModifyIORef'_ ref f"
- warn:
    lhs: "atomicModifyIORef' ref (\\a -> (f a, ()))"
    rhs: "atomicModifyIORef'_ ref f"
- warn:
    lhs: Data.Text.IO.getLine
    name: "Use 'getLine' from Relude"
    note: "'getLine' is already exported from Relude"
    rhs: getLine
- warn:
    lhs: System.IO.hFlush
    name: "Use 'hFlush' from Relude"
    note: "'hFlush' is already exported from Relude"
    rhs: hFlush
- warn:
    lhs: System.IO.hIsEOF
    name: "Use 'hIsEOF' from Relude"
    note: "'hIsEOF' is already exported from Relude"
    rhs: hIsEOF
- warn:
    lhs: System.IO.hSetBuffering
    name: "Use 'hSetBuffering' from Relude"
    note: "'hSetBuffering' is already exported from Relude"
    rhs: hSetBuffering
- warn:
    lhs: System.IO.hGetBuffering
    name: "Use 'hGetBuffering' from Relude"
    note: "'hGetBuffering' is already exported from Relude"
    rhs: hGetBuffering
- warn:
    lhs: System.IO.Handle
    name: "Use 'Handle' from Relude"
    note: "'Handle' is already exported from Relude"
    rhs: Handle
- warn:
    lhs: System.IO.stdin
    name: "Use 'stdin' from Relude"
    note: "'stdin' is already exported from Relude"
    rhs: stdin
- warn:
    lhs: System.IO.stdout
    name: "Use 'stdout' from Relude"
    note: "'stdout' is already exported from Relude"
    rhs: stdout
- warn:
    lhs: System.IO.stderr
    name: "Use 'stderr' from Relude"
    note: "'stderr' is already exported from Relude"
    rhs: stderr
- warn:
    lhs: System.IO.withFile
    name: "Use 'withFile' from Relude"
    note: "'withFile' is already exported from Relude"
    rhs: withFile
- warn:
    lhs: System.IO.BufferMode
    name: "Use 'BufferMode' from Relude"
    note: "'BufferMode' is already exported from Relude"
    rhs: BufferMode
- warn:
    lhs: System.Environment.getArgs
    name: "Use 'getArgs' from Relude"
    note: "'getArgs' is already exported from Relude"
    rhs: getArgs
- warn:
    lhs: System.Environment.lookupEnv
    name: "Use 'lookupEnv' from Relude"
    note: "'lookupEnv' is already exported from Relude"
    rhs: lookupEnv
- warn:
    lhs: Data.List.genericDrop
    name: "Use 'genericDrop' from Relude"
    note: "'genericDrop' is already exported from Relude"
    rhs: genericDrop
- warn:
    lhs: Data.List.genericLength
    name: "Use 'genericLength' from Relude"
    note: "'genericLength' is already exported from Relude"
    rhs: genericLength
- warn:
    lhs: Data.List.genericReplicate
    name: "Use 'genericReplicate' from Relude"
    note: "'genericReplicate' is already exported from Relude"
    rhs: genericReplicate
- warn:
    lhs: Data.List.genericSplitAt
    name: "Use 'genericSplitAt' from Relude"
    note: "'genericSplitAt' is already exported from Relude"
    rhs: genericSplitAt
- warn:
    lhs: Data.List.genericTake
    name: "Use 'genericTake' from Relude"
    note: "'genericTake' is already exported from Relude"
    rhs: genericTake
- warn:
    lhs: Data.List.group
    name: "Use 'group' from Relude"
    note: "'group' is already exported from Relude"
    rhs: group
- warn:
    lhs: Data.List.inits
    name: "Use 'inits' from Relude"
    note: "'inits' is already exported from Relude"
    rhs: inits
- warn:
    lhs: Data.List.intercalate
    name: "Use 'intercalate' from Relude"
    note: "'intercalate' is already exported from Relude"
    rhs: intercalate
- warn:
    lhs: Data.List.intersperse
    name: "Use 'intersperse' from Relude"
    note: "'intersperse' is already exported from Relude"
    rhs: intersperse
- warn:
    lhs: Data.List.isPrefixOf
    name: "Use 'isPrefixOf' from Relude"
    note: "'isPrefixOf' is already exported from Relude"
    rhs: isPrefixOf
- warn:
    lhs: Data.List.permutations
    name: "Use 'permutations' from Relude"
    note: "'permutations' is already exported from Relude"
    rhs: permutations
- warn:
    lhs: "Data.List.scanl'"
    name: "Use 'scanl'' from Relude"
    note: "'scanl'' is already exported from Relude"
    rhs: "scanl'"
- warn:
    lhs: Data.List.sort
    name: "Use 'sort' from Relude"
    note: "'sort' is already exported from Relude"
    rhs: sort
- warn:
    lhs: Data.List.sortBy
    name: "Use 'sortBy' from Relude"
    note: "'sortBy' is already exported from Relude"
    rhs: sortBy
- warn:
    lhs: Data.List.sortOn
    name: "Use 'sortOn' from Relude"
    note: "'sortOn' is already exported from Relude"
    rhs: sortOn
- warn:
    lhs: Data.List.subsequences
    name: "Use 'subsequences' from Relude"
    note: "'subsequences' is already exported from Relude"
    rhs: subsequences
- warn:
    lhs: Data.List.tails
    name: "Use 'tails' from Relude"
    note: "'tails' is already exported from Relude"
    rhs: tails
- warn:
    lhs: Data.List.transpose
    name: "Use 'transpose' from Relude"
    note: "'transpose' is already exported from Relude"
    rhs: transpose
- warn:
    lhs: Data.List.uncons
    name: "Use 'uncons' from Relude"
    note: "'uncons' is already exported from Relude"
    rhs: uncons
- warn:
    lhs: Data.List.unfoldr
    name: "Use 'unfoldr' from Relude"
    note: "'unfoldr' is already exported from Relude"
    rhs: unfoldr
- warn:
    lhs: Data.List.NonEmpty.NonEmpty
    name: "Use 'NonEmpty' from Relude"
    note: "'NonEmpty' is already exported from Relude"
    rhs: NonEmpty
- warn:
    lhs: "(Data.List.NonEmpty.:|)"
    name: "Use ':|' from Relude"
    note: "Operator '(:|)' is already exported from Relude"
    rhs: "(:|)"
- warn:
    lhs: Data.List.NonEmpty.nonEmpty
    name: "Use 'nonEmpty' from Relude"
    note: "'nonEmpty' is already exported from Relude"
    rhs: nonEmpty
- warn:
    lhs: Data.List.NonEmpty.head
    name: "Use 'head' from Relude"
    note: "'head' is already exported from Relude"
    rhs: head
- warn:
    lhs: Data.List.NonEmpty.init
    name: "Use 'init' from Relude"
    note: "'init' is already exported from Relude"
    rhs: init
- warn:
    lhs: Data.List.NonEmpty.last
    name: "Use 'last' from Relude"
    note: "'last' is already exported from Relude"
    rhs: last
- warn:
    lhs: Data.List.NonEmpty.tail
    name: "Use 'tail' from Relude"
    note: "'tail' is already exported from Relude"
    rhs: tail
- warn:
    lhs: GHC.Exts.sortWith
    name: "Use 'sortWith' from Relude"
    note: "'sortWith' is already exported from Relude"
    rhs: sortWith
- warn:
    lhs: Control.Monad.Except.ExceptT
    name: "Use 'ExceptT' from Relude"
    note: "'ExceptT' is already exported from Relude"
    rhs: ExceptT
- warn:
    lhs: Control.Monad.Except.runExceptT
    name: "Use 'runExceptT' from Relude"
    note: "'runExceptT' is already exported from Relude"
    rhs: runExceptT
- warn:
    lhs: Control.Monad.Reader.MonadReader
    name: "Use 'MonadReader' from Relude"
    note: "'MonadReader' is already exported from Relude"
    rhs: MonadReader
- warn:
    lhs: Control.Monad.Reader.Reader
    name: "Use 'Reader' from Relude"
    note: "'Reader' is already exported from Relude"
    rhs: Reader
- warn:
    lhs: Control.Monad.Reader.ReaderT
    name: "Use 'ReaderT' from Relude"
    note: "'ReaderT' is already exported from Relude"
    rhs: ReaderT
- warn:
    lhs: Control.Monad.Reader.runReaderT
    name: "Use 'runReaderT' from Relude"
    note: "'runReaderT' is already exported from Relude"
    rhs: runReaderT
- warn:
    lhs: Control.Monad.Reader.ask
    name: "Use 'ask' from Relude"
    note: "'ask' is already exported from Relude"
    rhs: ask
- warn:
    lhs: Control.Monad.Reader.asks
    name: "Use 'asks' from Relude"
    note: "'asks' is already exported from Relude"
    rhs: asks
- warn:
    lhs: Control.Monad.Reader.local
    name: "Use 'local' from Relude"
    note: "'local' is already exported from Relude"
    rhs: local
- warn:
    lhs: Control.Monad.Reader.reader
    name: "Use 'reader' from Relude"
    note: "'reader' is already exported from Relude"
    rhs: reader
- warn:
    lhs: Control.Monad.Reader.runReader
    name: "Use 'runReader' from Relude"
    note: "'runReader' is already exported from Relude"
    rhs: runReader
- warn:
    lhs: Control.Monad.Reader.withReader
    name: "Use 'withReader' from Relude"
    note: "'withReader' is already exported from Relude"
    rhs: withReader
- warn:
    lhs: Control.Monad.Reader.withReaderT
    name: "Use 'withReaderT' from Relude"
    note: "'withReaderT' is already exported from Relude"
    rhs: withReaderT
- warn:
    lhs: Control.Monad.State.Strict.MonadState
    name: "Use 'MonadState' from Relude"
    note: "'MonadState' is already exported from Relude"
    rhs: MonadState
- warn:
    lhs: Control.Monad.State.Strict.State
    name: "Use 'State' from Relude"
    note: "'State' is already exported from Relude"
    rhs: State
- warn:
    lhs: Control.Monad.State.Strict.StateT
    name: "Use 'StateT' from Relude"
    note: "'StateT' is already exported from Relude"
    rhs: StateT
- warn:
    lhs: Control.Monad.State.Strict.runStateT
    name: "Use 'runStateT' from Relude"
    note: "'runStateT' is already exported from Relude"
    rhs: runStateT
- warn:
    lhs: Control.Monad.State.Strict.evalState
    name: "Use 'evalState' from Relude"
    note: "'evalState' is already exported from Relude"
    rhs: evalState
- warn:
    lhs: Control.Monad.State.Strict.evalStateT
    name: "Use 'evalStateT' from Relude"
    note: "'evalStateT' is already exported from Relude"
    rhs: evalStateT
- warn:
    lhs: Control.Monad.State.Strict.execState
    name: "Use 'execState' from Relude"
    note: "'execState' is already exported from Relude"
    rhs: execState
- warn:
    lhs: Control.Monad.State.Strict.execStateT
    name: "Use 'execStateT' from Relude"
    note: "'execStateT' is already exported from Relude"
    rhs: execStateT
- warn:
    lhs: Control.Monad.State.Strict.get
    name: "Use 'get' from Relude"
    note: "'get' is already exported from Relude"
    rhs: get
- warn:
    lhs: Control.Monad.State.Strict.gets
    name: "Use 'gets' from Relude"
    note: "'gets' is already exported from Relude"
    rhs: gets
- warn:
    lhs: Control.Monad.State.Strict.modify
    name: "Use 'modify' from Relude"
    note: "'modify' is already exported from Relude"
    rhs: modify
- warn:
    lhs: "Control.Monad.State.Strict.modify'"
    name: "Use 'modify'' from Relude"
    note: "'modify'' is already exported from Relude"
    rhs: "modify'"
- warn:
    lhs: Control.Monad.State.Strict.put
    name: "Use 'put' from Relude"
    note: "'put' is already exported from Relude"
    rhs: put
- warn:
    lhs: Control.Monad.State.Strict.runState
    name: "Use 'runState' from Relude"
    note: "'runState' is already exported from Relude"
    rhs: runState
- warn:
    lhs: Control.Monad.State.Strict.state
    name: "Use 'state' from Relude"
    note: "'state' is already exported from Relude"
    rhs: state
- warn:
    lhs: Control.Monad.State.Strict.withState
    name: "Use 'withState' from Relude"
    note: "'withState' is already exported from Relude"
    rhs: withState
- warn:
    lhs: Control.Monad.Trans.MonadIO
    name: "Use 'MonadIO' from Relude"
    note: "'MonadIO' is already exported from Relude"
    rhs: MonadIO
- warn:
    lhs: Control.Monad.Trans.MonadTrans
    name: "Use 'MonadTrans' from Relude"
    note: "'MonadTrans' is already exported from Relude"
    rhs: MonadTrans
- warn:
    lhs: Control.Monad.Trans.lift
    name: "Use 'lift' from Relude"
    note: "'lift' is already exported from Relude"
    rhs: lift
- warn:
    lhs: Control.Monad.Trans.liftIO
    name: "Use 'liftIO' from Relude"
    note: "'liftIO' is already exported from Relude"
    rhs: liftIO
- warn:
    lhs: Control.Monad.Trans.Identity.IdentityT
    name: "Use 'IdentityT' from Relude"
    note: "'IdentityT' is already exported from Relude"
    rhs: IdentityT
- warn:
    lhs: Control.Monad.Trans.Identity.runIdentityT
    name: "Use 'runIdentityT' from Relude"
    note: "'runIdentityT' is already exported from Relude"
    rhs: runIdentityT
- warn:
    lhs: Control.Monad.Trans.Maybe.MaybeT
    name: "Use 'MaybeT' from Relude"
    note: "'MaybeT' is already exported from Relude"
    rhs: MaybeT
- warn:
    lhs: Control.Monad.Trans.Maybe.maybeToExceptT
    name: "Use 'maybeToExceptT' from Relude"
    note: "'maybeToExceptT' is already exported from Relude"
    rhs: maybeToExceptT
- warn:
    lhs: Control.Monad.Trans.Maybe.exceptToMaybeT
    name: "Use 'exceptToMaybeT' from Relude"
    note: "'exceptToMaybeT' is already exported from Relude"
    rhs: exceptToMaybeT
- warn:
    lhs: Control.Monad.MonadPlus
    name: "Use 'MonadPlus' from Relude"
    note: "'MonadPlus' is already exported from Relude"
    rhs: MonadPlus
- warn:
    lhs: Control.Monad.mzero
    name: "Use 'mzero' from Relude"
    note: "'mzero' is already exported from Relude"
    rhs: mzero
- warn:
    lhs: Control.Monad.mplus
    name: "Use 'mplus' from Relude"
    note: "'mplus' is already exported from Relude"
    rhs: mplus
- warn:
    lhs: Control.Monad.filterM
    name: "Use 'filterM' from Relude"
    note: "'filterM' is already exported from Relude"
    rhs: filterM
- warn:
    lhs: Control.Monad.forever
    name: "Use 'forever' from Relude"
    note: "'forever' is already exported from Relude"
    rhs: forever
- warn:
    lhs: Control.Monad.join
    name: "Use 'join' from Relude"
    note: "'join' is already exported from Relude"
    rhs: join
- warn:
    lhs: Control.Monad.mapAndUnzipM
    name: "Use 'mapAndUnzipM' from Relude"
    note: "'mapAndUnzipM' is already exported from Relude"
    rhs: mapAndUnzipM
- warn:
    lhs: Control.Monad.mfilter
    name: "Use 'mfilter' from Relude"
    note: "'mfilter' is already exported from Relude"
    rhs: mfilter
- warn:
    lhs: Control.Monad.replicateM
    name: "Use 'replicateM' from Relude"
    note: "'replicateM' is already exported from Relude"
    rhs: replicateM
- warn:
    lhs: Control.Monad.replicateM_
    name: "Use 'replicateM_' from Relude"
    note: "'replicateM_' is already exported from Relude"
    rhs: replicateM_
- warn:
    lhs: Control.Monad.zipWithM
    name: "Use 'zipWithM' from Relude"
    note: "'zipWithM' is already exported from Relude"
    rhs: zipWithM
- warn:
    lhs: Control.Monad.zipWithM_
    name: "Use 'zipWithM_' from Relude"
    note: "'zipWithM_' is already exported from Relude"
    rhs: zipWithM_
- warn:
    lhs: "(Control.Monad.<$!>)"
    name: "Use '<$!>' from Relude"
    note: "Operator '(<$!>)' is already exported from Relude"
    rhs: "(<$!>)"
- warn:
    lhs: "(Control.Monad.<=<)"
    name: "Use '<=<' from Relude"
    note: "Operator '(<=<)' is already exported from Relude"
    rhs: "(<=<)"
- warn:
    lhs: "(Control.Monad.=<<)"
    name: "Use '=<<' from Relude"
    note: "Operator '(=<<)' is already exported from Relude"
    rhs: "(=<<)"
- warn:
    lhs: "(Control.Monad.>=>)"
    name: "Use '>=>' from Relude"
    note: "Operator '(>=>)' is already exported from Relude"
    rhs: "(>=>)"
- warn:
    lhs: Control.Monad.Fail.MonadFail
    name: "Use 'MonadFail' from Relude"
    note: "'MonadFail' is already exported from Relude"
    rhs: MonadFail
- warn:
    lhs: Data.Maybe.catMaybes
    name: "Use 'catMaybes' from Relude"
    note: "'catMaybes' is already exported from Relude"
    rhs: catMaybes
- warn:
    lhs: Data.Maybe.fromMaybe
    name: "Use 'fromMaybe' from Relude"
    note: "'fromMaybe' is already exported from Relude"
    rhs: fromMaybe
- warn:
    lhs: Data.Maybe.isJust
    name: "Use 'isJust' from Relude"
    note: "'isJust' is already exported from Relude"
    rhs: isJust
- warn:
    lhs: Data.Maybe.isNothing
    name: "Use 'isNothing' from Relude"
    note: "'isNothing' is already exported from Relude"
    rhs: isNothing
- warn:
    lhs: Data.Maybe.listToMaybe
    name: "Use 'listToMaybe' from Relude"
    note: "'listToMaybe' is already exported from Relude"
    rhs: listToMaybe
- warn:
    lhs: Data.Maybe.mapMaybe
    name: "Use 'mapMaybe' from Relude"
    note: "'mapMaybe' is already exported from Relude"
    rhs: mapMaybe
- warn:
    lhs: Data.Maybe.maybeToList
    name: "Use 'maybeToList' from Relude"
    note: "'maybeToList' is already exported from Relude"
    rhs: maybeToList
- warn:
    lhs: Data.Either.isLeft
    name: "Use 'isLeft' from Relude"
    note: "'isLeft' is already exported from Relude"
    rhs: isLeft
- warn:
    lhs: Data.Either.isRight
    name: "Use 'isRight' from Relude"
    note: "'isRight' is already exported from Relude"
    rhs: isRight
- warn:
    lhs: Data.Either.lefts
    name: "Use 'lefts' from Relude"
    note: "'lefts' is already exported from Relude"
    rhs: lefts
- warn:
    lhs: Data.Either.partitionEithers
    name: "Use 'partitionEithers' from Relude"
    note: "'partitionEithers' is already exported from Relude"
    rhs: partitionEithers
- warn:
    lhs: Data.Either.rights
    name: "Use 'rights' from Relude"
    note: "'rights' is already exported from Relude"
    rhs: rights
- warn:
    lhs: Data.Monoid.All
    name: "Use 'All' from Relude"
    note: "'All' is already exported from Relude"
    rhs: All
- warn:
    lhs: Data.Monoid.getAll
    name: "Use 'getAll' from Relude"
    note: "'getAll' is already exported from Relude"
    rhs: getAll
- warn:
    lhs: Data.Monoid.Alt
    name: "Use 'Alt' from Relude"
    note: "'Alt' is already exported from Relude"
    rhs: Alt
- warn:
    lhs: Data.Monoid.getAlt
    name: "Use 'getAlt' from Relude"
    note: "'getAlt' is already exported from Relude"
    rhs: getAlt
- warn:
    lhs: Data.Monoid.Any
    name: "Use 'Any' from Relude"
    note: "'Any' is already exported from Relude"
    rhs: Any
- warn:
    lhs: Data.Monoid.getAny
    name: "Use 'getAny' from Relude"
    note: "'getAny' is already exported from Relude"
    rhs: getAny
- warn:
    lhs: Data.Monoid.Ap
    name: "Use 'Ap' from Relude"
    note: "'Ap' is already exported from Relude"
    rhs: Ap
- warn:
    lhs: Data.Monoid.getAp
    name: "Use 'getAp' from Relude"
    note: "'getAp' is already exported from Relude"
    rhs: getAp
- warn:
    lhs: Data.Monoid.Dual
    name: "Use 'Dual' from Relude"
    note: "'Dual' is already exported from Relude"
    rhs: Dual
- warn:
    lhs: Data.Monoid.getDual
    name: "Use 'getDual' from Relude"
    note: "'getDual' is already exported from Relude"
    rhs: getDual
- warn:
    lhs: Data.Monoid.Endo
    name: "Use 'Endo' from Relude"
    note: "'Endo' is already exported from Relude"
    rhs: Endo
- warn:
    lhs: Data.Monoid.appEndo
    name: "Use 'appEndo' from Relude"
    note: "'appEndo' is already exported from Relude"
    rhs: appEndo
- warn:
    lhs: Data.Monoid.First
    name: "Use 'First' from Relude"
    note: "'First' is already exported from Relude"
    rhs: First
- warn:
    lhs: Data.Monoid.getFirst
    name: "Use 'getFirst' from Relude"
    note: "'getFirst' is already exported from Relude"
    rhs: getFirst
- warn:
    lhs: Data.Monoid.Last
    name: "Use 'Last' from Relude"
    note: "'Last' is already exported from Relude"
    rhs: Last
- warn:
    lhs: Data.Monoid.getLast
    name: "Use 'getLast' from Relude"
    note: "'getLast' is already exported from Relude"
    rhs: getLast
- warn:
    lhs: Data.Monoid.Product
    name: "Use 'Product' from Relude"
    note: "'Product' is already exported from Relude"
    rhs: Product
- warn:
    lhs: Data.Monoid.getProduct
    name: "Use 'getProduct' from Relude"
    note: "'getProduct' is already exported from Relude"
    rhs: getProduct
- warn:
    lhs: Data.Monoid.Sum
    name: "Use 'Sum' from Relude"
    note: "'Sum' is already exported from Relude"
    rhs: Sum
- warn:
    lhs: Data.Monoid.getSum
    name: "Use 'getSum' from Relude"
    note: "'getSum' is already exported from Relude"
    rhs: getSum
- warn:
    lhs: Data.Semigroup.Semigroup
    name: "Use 'Semigroup' from Relude"
    note: "'Semigroup' is already exported from Relude"
    rhs: Semigroup
- warn:
    lhs: Data.Semigroup.sconcat
    name: "Use 'sconcat' from Relude"
    note: "'sconcat' is already exported from Relude"
    rhs: sconcat
- warn:
    lhs: Data.Semigroup.stimes
    name: "Use 'stimes' from Relude"
    note: "'stimes' is already exported from Relude"
    rhs: stimes
- warn:
    lhs: "(Data.Semigroup.<>)"
    name: "Use '<>' from Relude"
    note: "Operator '(<>)' is already exported from Relude"
    rhs: "(<>)"
- warn:
    lhs: Data.Semigroup.WrappedMonoid
    name: "Use 'WrappedMonoid' from Relude"
    note: "'WrappedMonoid' is already exported from Relude"
    rhs: WrappedMonoid
- warn:
    lhs: Data.Semigroup.cycle1
    name: "Use 'cycle1' from Relude"
    note: "'cycle1' is already exported from Relude"
    rhs: cycle1
- warn:
    lhs: Data.Semigroup.mtimesDefault
    name: "Use 'mtimesDefault' from Relude"
    note: "'mtimesDefault' is already exported from Relude"
    rhs: mtimesDefault
- warn:
    lhs: Data.Semigroup.stimesIdempotent
    name: "Use 'stimesIdempotent' from Relude"
    note: "'stimesIdempotent' is already exported from Relude"
    rhs: stimesIdempotent
- warn:
    lhs: Data.Semigroup.stimesIdempotentMonoid
    name: "Use 'stimesIdempotentMonoid' from Relude"
    note: "'stimesIdempotentMonoid' is already exported from Relude"
    rhs: stimesIdempotentMonoid
- warn:
    lhs: Data.Semigroup.stimesMonoid
    name: "Use 'stimesMonoid' from Relude"
    note: "'stimesMonoid' is already exported from Relude"
    rhs: stimesMonoid
- warn:
    lhs: Data.ByteString.ByteString
    name: "Use 'ByteString' from Relude"
    note: "'ByteString' is already exported from Relude"
    rhs: ByteString
- warn:
    lhs: Data.ByteString.Short.ShortByteString
    name: "Use 'ShortByteString' from Relude"
    note: "'ShortByteString' is already exported from Relude"
    rhs: ShortByteString
- warn:
    lhs: Data.ByteString.Short.toShort
    name: "Use 'toShort' from Relude"
    note: "'toShort' is already exported from Relude"
    rhs: toShort
- warn:
    lhs: Data.ByteString.Short.fromShort
    name: "Use 'fromShort' from Relude"
    note: "'fromShort' is already exported from Relude"
    rhs: fromShort
- warn:
    lhs: Data.String.IsString
    name: "Use 'IsString' from Relude"
    note: "'IsString' is already exported from Relude"
    rhs: IsString
- warn:
    lhs: Data.String.fromString
    name: "Use 'fromString' from Relude"
    note: "'fromString' is already exported from Relude"
    rhs: fromString
- warn:
    lhs: Data.Text.Text
    name: "Use 'Text' from Relude"
    note: "'Text' is already exported from Relude"
    rhs: Text
- warn:
    lhs: Data.Text.lines
    name: "Use 'lines' from Relude"
    note: "'lines' is already exported from Relude"
    rhs: lines
- warn:
    lhs: Data.Text.unlines
    name: "Use 'unlines' from Relude"
    note: "'unlines' is already exported from Relude"
    rhs: unlines
- warn:
    lhs: Data.Text.words
    name: "Use 'words' from Relude"
    note: "'words' is already exported from Relude"
    rhs: words
- warn:
    lhs: Data.Text.unwords
    name: "Use 'unwords' from Relude"
    note: "'unwords' is already exported from Relude"
    rhs: unwords
- warn:
    lhs: "Data.Text.Encoding.decodeUtf8'"
    name: "Use 'decodeUtf8'' from Relude"
    note: "'decodeUtf8'' is already exported from Relude"
    rhs: "decodeUtf8'"
- warn:
    lhs: Data.Text.Encoding.decodeUtf8With
    name: "Use 'decodeUtf8With' from Relude"
    note: "'decodeUtf8With' is already exported from Relude"
    rhs: decodeUtf8With
- warn:
    lhs: Data.Text.Encoding.Error.OnDecodeError
    name: "Use 'OnDecodeError' from Relude"
    note: "'OnDecodeError' is already exported from Relude"
    rhs: OnDecodeError
- warn:
    lhs: Data.Text.Encoding.Error.OnError
    name: "Use 'OnError' from Relude"
    note: "'OnError' is already exported from Relude"
    rhs: OnError
- warn:
    lhs: Data.Text.Encoding.Error.UnicodeException
    name: "Use 'UnicodeException' from Relude"
    note: "'UnicodeException' is already exported from Relude"
    rhs: UnicodeException
- warn:
    lhs: Data.Text.Encoding.Error.lenientDecode
    name: "Use 'lenientDecode' from Relude"
    note: "'lenientDecode' is already exported from Relude"
    rhs: lenientDecode
- warn:
    lhs: Data.Text.Encoding.Error.strictDecode
    name: "Use 'strictDecode' from Relude"
    note: "'strictDecode' is already exported from Relude"
    rhs: strictDecode
- warn:
    lhs: Text.Read.Read
    name: "Use 'Read' from Relude"
    note: "'Read' is already exported from Relude"
    rhs: Read
- warn:
    lhs: Text.Read.readMaybe
    name: "Use 'readMaybe' from Relude"
    note: "'readMaybe' is already exported from Relude"
    rhs: readMaybe
- warn:
    lhs: "(liftIO (newEmptyMVar ))"
    name: "'liftIO' is not needed"
    note: "If you import 'newEmptyMVar' from Relude, it's already lifted"
    rhs: newEmptyMVar
- warn:
    lhs: "(liftIO (newMVar x))"
    name: "'liftIO' is not needed"
    note: "If you import 'newMVar' from Relude, it's already lifted"
    rhs: newMVar
- warn:
    lhs: "(liftIO (putMVar x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'putMVar' from Relude, it's already lifted"
    rhs: putMVar
- warn:
    lhs: "(liftIO (readMVar x))"
    name: "'liftIO' is not needed"
    note: "If you import 'readMVar' from Relude, it's already lifted"
    rhs: readMVar
- warn:
    lhs: "(liftIO (swapMVar x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'swapMVar' from Relude, it's already lifted"
    rhs: swapMVar
- warn:
    lhs: "(liftIO (takeMVar x))"
    name: "'liftIO' is not needed"
    note: "If you import 'takeMVar' from Relude, it's already lifted"
    rhs: takeMVar
- warn:
    lhs: "(liftIO (tryPutMVar x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'tryPutMVar' from Relude, it's already lifted"
    rhs: tryPutMVar
- warn:
    lhs: "(liftIO (tryReadMVar x))"
    name: "'liftIO' is not needed"
    note: "If you import 'tryReadMVar' from Relude, it's already lifted"
    rhs: tryReadMVar
- warn:
    lhs: "(liftIO (tryTakeMVar x))"
    name: "'liftIO' is not needed"
    note: "If you import 'tryTakeMVar' from Relude, it's already lifted"
    rhs: tryTakeMVar
- warn:
    lhs: "(liftIO (atomically x))"
    name: "'liftIO' is not needed"
    note: "If you import 'atomically' from Relude, it's already lifted"
    rhs: atomically
- warn:
    lhs: "(liftIO (newTVarIO x))"
    name: "'liftIO' is not needed"
    note: "If you import 'newTVarIO' from Relude, it's already lifted"
    rhs: newTVarIO
- warn:
    lhs: "(liftIO (readTVarIO x))"
    name: "'liftIO' is not needed"
    note: "If you import 'readTVarIO' from Relude, it's already lifted"
    rhs: readTVarIO
- warn:
    lhs: "(liftIO (newTMVarIO x))"
    name: "'liftIO' is not needed"
    note: "If you import 'newTMVarIO' from Relude, it's already lifted"
    rhs: newTMVarIO
- warn:
    lhs: "(liftIO (newEmptyTMVarIO ))"
    name: "'liftIO' is not needed"
    note: "If you import 'newEmptyTMVarIO' from Relude, it's already lifted"
    rhs: newEmptyTMVarIO
- warn:
    lhs: "(liftIO (exitWith x))"
    name: "'liftIO' is not needed"
    note: "If you import 'exitWith' from Relude, it's already lifted"
    rhs: exitWith
- warn:
    lhs: "(liftIO (exitFailure ))"
    name: "'liftIO' is not needed"
    note: "If you import 'exitFailure' from Relude, it's already lifted"
    rhs: exitFailure
- warn:
    lhs: "(liftIO (exitSuccess ))"
    name: "'liftIO' is not needed"
    note: "If you import 'exitSuccess' from Relude, it's already lifted"
    rhs: exitSuccess
- warn:
    lhs: "(liftIO (die x))"
    name: "'liftIO' is not needed"
    note: "If you import 'die' from Relude, it's already lifted"
    rhs: die
- warn:
    lhs: "(liftIO (readFile x))"
    name: "'liftIO' is not needed"
    note: "If you import 'readFile' from Relude, it's already lifted"
    rhs: readFile
- warn:
    lhs: "(liftIO (writeFile x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'writeFile' from Relude, it's already lifted"
    rhs: writeFile
- warn:
    lhs: "(liftIO (appendFile x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'appendFile' from Relude, it's already lifted"
    rhs: appendFile
- warn:
    lhs: "(liftIO (readFileText x))"
    name: "'liftIO' is not needed"
    note: "If you import 'readFileText' from Relude, it's already lifted"
    rhs: readFileText
- warn:
    lhs: "(liftIO (writeFileText x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'writeFileText' from Relude, it's already lifted"
    rhs: writeFileText
- warn:
    lhs: "(liftIO (appendFileText x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'appendFileText' from Relude, it's already lifted"
    rhs: appendFileText
- warn:
    lhs: "(liftIO (readFileLText x))"
    name: "'liftIO' is not needed"
    note: "If you import 'readFileLText' from Relude, it's already lifted"
    rhs: readFileLText
- warn:
    lhs: "(liftIO (writeFileLText x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'writeFileLText' from Relude, it's already lifted"
    rhs: writeFileLText
- warn:
    lhs: "(liftIO (appendFileLText x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'appendFileLText' from Relude, it's already lifted"
    rhs: appendFileLText
- warn:
    lhs: "(liftIO (readFileBS x))"
    name: "'liftIO' is not needed"
    note: "If you import 'readFileBS' from Relude, it's already lifted"
    rhs: readFileBS
- warn:
    lhs: "(liftIO (writeFileBS x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'writeFileBS' from Relude, it's already lifted"
    rhs: writeFileBS
- warn:
    lhs: "(liftIO (appendFileBS x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'appendFileBS' from Relude, it's already lifted"
    rhs: appendFileBS
- warn:
    lhs: "(liftIO (readFileLBS x))"
    name: "'liftIO' is not needed"
    note: "If you import 'readFileLBS' from Relude, it's already lifted"
    rhs: readFileLBS
- warn:
    lhs: "(liftIO (writeFileLBS x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'writeFileLBS' from Relude, it's already lifted"
    rhs: writeFileLBS
- warn:
    lhs: "(liftIO (appendFileLBS x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'appendFileLBS' from Relude, it's already lifted"
    rhs: appendFileLBS
- warn:
    lhs: "(liftIO (newIORef x))"
    name: "'liftIO' is not needed"
    note: "If you import 'newIORef' from Relude, it's already lifted"
    rhs: newIORef
- warn:
    lhs: "(liftIO (readIORef x))"
    name: "'liftIO' is not needed"
    note: "If you import 'readIORef' from Relude, it's already lifted"
    rhs: readIORef
- warn:
    lhs: "(liftIO (writeIORef x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'writeIORef' from Relude, it's already lifted"
    rhs: writeIORef
- warn:
    lhs: "(liftIO (modifyIORef x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'modifyIORef' from Relude, it's already lifted"
    rhs: modifyIORef
- warn:
    lhs: "(liftIO (modifyIORef' x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'modifyIORef'' from Relude, it's already lifted"
    rhs: "modifyIORef'"
- warn:
    lhs: "(liftIO (atomicModifyIORef x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'atomicModifyIORef' from Relude, it's already lifted"
    rhs: atomicModifyIORef
- warn:
    lhs: "(liftIO (atomicModifyIORef' x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'atomicModifyIORef'' from Relude, it's already lifted"
    rhs: "atomicModifyIORef'"
- warn:
    lhs: "(liftIO (atomicWriteIORef x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'atomicWriteIORef' from Relude, it's already lifted"
    rhs: atomicWriteIORef
- warn:
    lhs: "(liftIO (getLine ))"
    name: "'liftIO' is not needed"
    note: "If you import 'getLine' from Relude, it's already lifted"
    rhs: getLine
- warn:
    lhs: "(liftIO (print x))"
    name: "'liftIO' is not needed"
    note: "If you import 'print' from Relude, it's already lifted"
    rhs: print
- warn:
    lhs: "(liftIO (putStr x))"
    name: "'liftIO' is not needed"
    note: "If you import 'putStr' from Relude, it's already lifted"
    rhs: putStr
- warn:
    lhs: "(liftIO (putStrLn x))"
    name: "'liftIO' is not needed"
    note: "If you import 'putStrLn' from Relude, it's already lifted"
    rhs: putStrLn
- warn:
    lhs: "(liftIO (putText x))"
    name: "'liftIO' is not needed"
    note: "If you import 'putText' from Relude, it's already lifted"
    rhs: putText
- warn:
    lhs: "(liftIO (putTextLn x))"
    name: "'liftIO' is not needed"
    note: "If you import 'putTextLn' from Relude, it's already lifted"
    rhs: putTextLn
- warn:
    lhs: "(liftIO (putLText x))"
    name: "'liftIO' is not needed"
    note: "If you import 'putLText' from Relude, it's already lifted"
    rhs: putLText
- warn:
    lhs: "(liftIO (putLTextLn x))"
    name: "'liftIO' is not needed"
    note: "If you import 'putLTextLn' from Relude, it's already lifted"
    rhs: putLTextLn
- warn:
    lhs: "(liftIO (putBS x))"
    name: "'liftIO' is not needed"
    note: "If you import 'putBS' from Relude, it's already lifted"
    rhs: putBS
- warn:
    lhs: "(liftIO (putBSLn x))"
    name: "'liftIO' is not needed"
    note: "If you import 'putBSLn' from Relude, it's already lifted"
    rhs: putBSLn
- warn:
    lhs: "(liftIO (putLBS x))"
    name: "'liftIO' is not needed"
    note: "If you import 'putLBS' from Relude, it's already lifted"
    rhs: putLBS
- warn:
    lhs: "(liftIO (putLBSLn x))"
    name: "'liftIO' is not needed"
    note: "If you import 'putLBSLn' from Relude, it's already lifted"
    rhs: putLBSLn
- warn:
    lhs: "(liftIO (hFlush x))"
    name: "'liftIO' is not needed"
    note: "If you import 'hFlush' from Relude, it's already lifted"
    rhs: hFlush
- warn:
    lhs: "(liftIO (hIsEOF x))"
    name: "'liftIO' is not needed"
    note: "If you import 'hIsEOF' from Relude, it's already lifted"
    rhs: hIsEOF
- warn:
    lhs: "(liftIO (hSetBuffering x y))"
    name: "'liftIO' is not needed"
    note: "If you import 'hSetBuffering' from Relude, it's already lifted"
    rhs: hSetBuffering
- warn:
    lhs: "(liftIO (hGetBuffering x))"
    name: "'liftIO' is not needed"
    note: "If you import 'hGetBuffering' from Relude, it's already lifted"
    rhs: hGetBuffering
- warn:
    lhs: "(liftIO (getArgs ))"
    name: "'liftIO' is not needed"
    note: "If you import 'getArgs' from Relude, it's already lifted"
    rhs: getArgs
- warn:
    lhs: "(liftIO (lookupEnv x))"
    name: "'liftIO' is not needed"
    note: "If you import 'lookupEnv' from Relude, it's already lifted"
    rhs: lookupEnv
- hint:
    lhs: "fmap (bimap f g)"
    note: "Use `bimapF` from `Relude.Extra.Bifunctor`"
    rhs: bimapF f g
- hint:
    lhs: "bimap f g <$> x"
    note: "Use `bimapF` from `Relude.Extra.Bifunctor`"
    rhs: bimapF f g x
- hint:
    lhs: "fmap (first f)"
    note: "Use `firstF` from `Relude.Extra.Bifunctor`"
    rhs: firstF f
- hint:
    lhs: fmap . first
    note: "Use `firstF` from `Relude.Extra.Bifunctor`"
    rhs: firstF
- hint:
    lhs: "fmap (second f)"
    note: "Use `secondF` from `Relude.Extra.Bifunctor`"
    rhs: secondF f
- hint:
    lhs: fmap . second
    note: "Use `secondF` from `Relude.Extra.Bifunctor`"
    rhs: secondF
- hint:
    lhs: "[minBound .. maxBound]"
    note: "Use `universe` from `Relude.Extra.Enum`"
    rhs: universe
- hint:
    lhs: succ
    note: "`succ` from `Prelude` is a pure function but it may throw exception. Consider using `next` from `Relude.Extra.Enum` instead."
    rhs: next
- hint:
    lhs: pred
    note: "`pred` from `Prelude` is a pure function but it may throw exception. Consider using `prev` from `Relude.Extra.Enum` instead."
    rhs: prev
- hint:
    lhs: toEnum
    note: "`toEnum` from `Prelude` is a pure function but it may throw exception. Consider using `safeToEnum` from `Relude.Extra.Enum` instead."
    rhs: safeToEnum
- hint:
    lhs: sum xs / length xs
    note: "Use `average` from `Relude.Extra.Foldable`"
    rhs: average xs
- hint:
    lhs: "\\a -> (a, a)"
    note: "Use `dup` from `Relude.Extra.Tuple`"
    rhs: dup
- hint:
    lhs: "\\a -> (f a, a)"
    note: "Use `toFst` from `Relude.Extra.Tuple`"
    rhs: toFst f
- hint:
    lhs: "\\a -> (a, f a)"
    note: "Use `toSnd` from `Relude.Extra.Tuple`"
    rhs: toSnd f
- hint:
    lhs: fmap . toFst
    note: "Use `fmapToFst` from `Relude.Extra.Tuple`"
    rhs: fmapToFst
- hint:
    lhs: "fmap (toFst f)"
    note: "Use `fmapToFst` from `Relude.Extra.Tuple`"
    rhs: fmapToFst f
- hint:
    lhs: fmap . toSnd
    note: "Use `fmapToSnd` from `Relude.Extra.Tuple`"
    rhs: fmapToSnd
- hint:
    lhs: "fmap (toSnd f)"
    note: "Use `fmapToSnd` from `Relude.Extra.Tuple`"
    rhs: fmapToSnd f
- hint:
    lhs: map . toFst
    note: "Use `fmapToFst` from `Relude.Extra.Tuple`"
    rhs: fmapToFst
- hint:
    lhs: "map (toFst f)"
    note: "Use `fmapToFst` from `Relude.Extra.Tuple`"
    rhs: fmapToFst f
- hint:
    lhs: map . toSnd
    note: "Use `fmapToSnd` from `Relude.Extra.Tuple`"
    rhs: fmapToSnd
- hint:
    lhs: "map (toSnd f)"
    note: "Use `fmapToSnd` from `Relude.Extra.Tuple`"
    rhs: fmapToSnd f
- hint:
    lhs: "fmap (,a) (f a)"
    note: "Use `traverseToFst` from `Relude.Extra.Tuple`"
    rhs: traverseToFst f a
- hint:
    lhs: "fmap (flip (,) a) (f a)"
    note: "Use `traverseToFst` from `Relude.Extra.Tuple`"
    rhs: traverseToFst f a
- hint:
    lhs: "(,a) <$> f a"
    note: "Use `traverseToFst` from `Relude.Extra.Tuple`"
    rhs: traverseToFst f a
- hint:
    lhs: "flip (,) a <$> f a"
    note: "Use `traverseToFst` from `Relude.Extra.Tuple`"
    rhs: traverseToFst f a
- hint:
    lhs: "fmap (a,) (f a)"
    note: "Use `traverseToSnd` from `Relude.Extra.Tuple`"
    rhs: traverseToSnd f a
- hint:
    lhs: "fmap ((,) a) (f a)"
    note: "Use `traverseToSnd` from `Relude.Extra.Tuple`"
    rhs: traverseToSnd f a
- hint:
    lhs: "(a,) <$> f a"
    note: "Use `traverseToSnd` from `Relude.Extra.Tuple`"
    rhs: traverseToSnd f a
- hint:
    lhs: "(,) a <$> f a"
    note: "Use `traverseToSnd` from `Relude.Extra.Tuple`"
    rhs: traverseToSnd f a


================================================
FILE: CHANGELOG.md
================================================
# Changelog

All notable changes to this project (as seen by library users) will be documented in this file.
The CHANGELOG is available on [Github](https://github.com/luc-tielen/souffle-haskell.git/CHANGELOG.md).

## [0.2.0] - Unreleased

### Added

- Logical negation (of a single rule clause)
- Typed hole support
- Comparison operators
- Arithmetic operators (`+`, `-`, `*`, `/`)
- Possibility to link in external functions
- LSP support
  - Document highlight
  - Hover
  - Diagnostics
- Improved dead code elimination
- Optimization passes:
  - HoistConstraints (faster searches by narrowing search-space as early as possible)
- CLI: Allow emitting initial and transformed RA IR
- Support named fields in type definitions and extern definitions
- Support transpiling to Souffle
- Support running semantic analysis on multiple threads

### Changed

- Relations now can have additional qualifiers marking them as inputs or
  outputs. Not providing any qualifier means it is now an internal fact.

### Fixed

- 0 is now parsed correctly as a number.
- Type holes now correctly show all possible results in a rule.
- BTree implementation is now better suited for large sets of facts

## [0.1.0] - 2022-11-20

### Added

- WebAssembly support
- Support for the `string` data type
- Wildcards are now supported in rule bodies
- Assignments are now supported in rule bodies
- Support for multiple occurences of the same variable in a single clause of
  a rule body
- (UTF-8) strings in relations are now supported
- Optimizations on the AST level:
  - Copy propagation
  - Dead code elimination

### Changed

- Improved error reporting
- Parsing now continues after failure and reports multiple errors back to the
  user at once.

### Fixed

- Rules with multiple equalities.
- Edgecase in index selection algorithm. The algorithm now does not take
  `NoElem` variants into account.

## [0.0.1] - 2022-06-14

### Added

- First MVP of the compiler! The happy path should work as expected, unsupported
  features or semantic errors should result in a (poorly formatted) error.


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Code of Conduct

Contact: luc.tielen@gmail.com

## Why have a Code of Conduct?

As contributors and maintainers of this project, we are committed to providing a
friendly, safe and welcoming environment for all, regardless of age, disability,
gender, nationality, race, religion, sexuality, or similar personal
characteristic.

The goal of the Code of Conduct is to specify a baseline standard of behavior so
that people with different social values and communication styles can talk about
Eclair effectively, productively, and respectfully, even in face of
disagreements. The Code of Conduct also provides a mechanism for resolving
conflicts in the community when they arise.

## Our Values

These are the values Eclair developers should aspire to:

- Be friendly and welcoming
- Be kind
  - Remember that people have varying communication styles and that not
    everyone is using their native language. (Meaning and tone can be lost in
    translation.)
  - Interpret the arguments of others in good faith, do not seek to disagree.
  - When we do disagree, try to understand why.
- Be thoughtful
  - Productive communication requires effort. Think about how your words will
    be interpreted.
  - Remember that sometimes it is best to refrain entirely from commenting.
- Be respectful
  - In particular, respect differences of opinion. It is important that we
    resolve disagreements and differing views constructively.
- Be constructive
  - Avoid derailing: stay on topic; if you want to talk about something else,
    start a new conversation.
  - Avoid unconstructive criticism: don't merely decry the current state of
    affairs; offer — or at least solicit — suggestions as to how things may be
    improved.
  - Avoid harsh words and stern tone: we are all aligned towards the
    well-being of the community and the progress of the ecosystem. Harsh words
    exclude, demotivate, and lead to unnecessary conflict.
  - Avoid snarking (pithy, unproductive, sniping comments).
  - Avoid microaggressions (brief and commonplace verbal, behavioral and
    environmental indignities that communicate hostile, derogatory or negative
    slights and insults towards a project, person or group).
- Be responsible
  - What you say and do matters. Take responsibility for your words and
    actions, including their consequences, whether intended or otherwise.

The following actions are explicitly forbidden:

- Insulting, demeaning, hateful, or threatening remarks.
- Discrimination based on age, disability, gender, nationality, race,
  religion, sexuality, or similar personal characteristic.
- Bullying or systematic harassment.
- Unwelcome sexual advances.
- Incitement to any of these.

## Where does the Code of Conduct apply?

If you participate in or contribute to the Eclair ecosystem in any way, you are
encouraged to follow the Code of Conduct while doing so.

Explicit enforcement of the Code of Conduct applies to the official mediums
operated by the Eclair project:

- The [official GitHub project][1] and code reviews.
- The **[#Eclair][2]** Discord[2].

Other Eclair activities (such as conferences, meetups, and unofficial forums)
are encouraged to adopt this Code of Conduct. Such groups must provide their own
contact information.

Project maintainers may block, remove, edit, or reject comments, commits, code,
wiki edits, issues, and other contributions that are not aligned to this Code of
Conduct.

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by emailing: luc.tielen@gmail.com. All complaints will
be reviewed and investigated and will result in a response that is deemed
necessary and appropriate to the circumstances. **All reports will be kept
confidential**.

**The goal of the Code of Conduct is to resolve conflicts in the most harmonious
way possible**. We hope that in most cases issues may be resolved through polite
discussion and mutual agreement. Bannings and other forceful measures are to be
employed only as a last resort. **Do not** post about the issue publicly or try
to rally sentiment against a particular individual or group.

## Acknowledgements

This document was based on the Code of Conduct from the Elixir project (dated
Jul/2023), which in turn was based on the Go project (dated Sep/2021) and the
Contributor Covenant (v1.4).

[1]: https://github.com/luc-tielen/eclair-lang
[2]: https://discord.gg/mC2arUrxKg


================================================
FILE: Dockerfile
================================================
FROM primordus/souffle-ubuntu:2.3
ARG LLVM_VERSION=17

SHELL [ "/bin/bash", "-c" ]

# install packages
RUN echo 'tzdata tzdata/Areas select Europe' | debconf-set-selections \
    && echo 'tzdata tzdata/Zones/Europe select Paris' | debconf-set-selections \
    && apt-get update \
    && DEBIAN_FRONTEND=noninteractive apt-get install -y \
       wget software-properties-common gnupg curl libffi-dev make \
       python3 python3-pip libgmp-dev \
    && curl -o - https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash \
    && source /root/.nvm/nvm.sh \
    && nvm install 18.1.0 \
    && echo "source /root/.ghcup/env" >> ~/.bashrc \
    # install llvm 17
    && mkdir -p /tmp/llvm-dir \
    && cd /tmp/llvm-dir \
    && wget https://apt.llvm.org/llvm.sh \
    && chmod +x llvm.sh \
    && ./llvm.sh $LLVM_VERSION \
    && cd /tmp \
    && rm -rf /tmp/llvm-dir \
    && cd /usr/bin \
    && ln -s /usr/lib/llvm-$LLVM_VERSION/bin/split-file \
    && ln -s /usr/lib/llvm-$LLVM_VERSION/bin/FileCheck \
    && ln -s clang-$LLVM_VERSION clang \
    && ln -s wasm-ld-$LLVM_VERSION wasm-ld \
    && cd - \
    && pip install lit==14.0.6 \
    # install ghcup, ghc-9.6.3 and cabal-3.10.1.0
    && curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | \
    BOOTSTRAP_HASKELL_NONINTERACTIVE=1 BOOTSTRAP_HASKELL_GHC_VERSION=9.6.3 BOOTSTRAP_HASKELL_CABAL_VERSION=3.10.1.0 \
    BOOTSTRAP_HASKELL_INSTALL_STACK=1 BOOTSTRAP_HASKELL_INSTALL_HLS=1 BOOTSTRAP_HASKELL_ADJUST_BASHRC=P sh \
    && source /root/.ghcup/env \
    && cabal install cabal-fmt \
    && cabal install hspec-discover \
    && apt-get autoremove -y \
    && apt-get purge -y --auto-remove \
    && rm -rf /var/lib/apt/lists/*

VOLUME /code
WORKDIR /app/build
ENV DATALOG_DIR=/app/build/cbits

RUN echo -e '#!/bin/bash\nsource /root/.ghcup/env\nsource /root/.nvm/nvm.sh\nexec "$@"\n' > /app/build/entrypoint.sh \
    && chmod u+x /app/build/entrypoint.sh

# The entrypoint script sources ghcup setup script so we can easily call cabal etc.
ENTRYPOINT [ "/app/build/entrypoint.sh" ]

COPY . .

RUN source /root/.ghcup/env && make build \
    && echo -e '#!/bin/bash\nsource /root/.ghcup/env\n' > /usr/bin/eclair \
    && source /root/.ghcup/env \
    && echo -e "`cabal list-bin eclair` \"\$@\"" >> /usr/bin/eclair \
    && chmod u+x /usr/bin/eclair

# The default command to run, shows the help menu
CMD [ "eclair", "--help" ]


================================================
FILE: LICENSE
================================================
Copyright Luc Tielen (c) 2022

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above
      copyright notice, this list of conditions and the following
      disclaimer in the documentation and/or other materials provided
      with the distribution.

    * Neither the name of Luc Tielen nor the names of other
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: Makefile
================================================
build: configure
	@cabal build

configure:
	@cabal configure -f eclair-debug --enable-tests

clean:
	@cabal clean

test:
	@DATALOG_DIR=cbits/ cabal run eclair-test
	@lit tests/ -v

cabal-file:
	@cabal-fmt --Werror -i eclair-lang.cabal

.PHONY: build configure clean test cabal-file


================================================
FILE: README.md
================================================
<picture>
  <source media="(prefers-color-scheme: dark)" srcset="./logo_dark.png"/>
  <img
    src="./logo_light.png"
    alt="Logo for the Eclair programming language"
    loading="lazy"
    decoding="async"/>
</picture>

_An experimental and minimal Datalog implementation that compiles down to LLVM._

[![Build](https://github.com/luc-tielen/eclair-lang/actions/workflows/build.yml/badge.svg)](https://github.com/luc-tielen/eclair-lang/actions/workflows/build.yml)

## Features

Eclair is a minimal Datalog (for now). It supports the following features:

- Facts containing literals
- Rules consisting of one or more clauses.
- Rules can be non-recursive, recursive or mutually recursive.

Right now it compiles to LLVM but be aware there might still be bugs. 
Some edge cases might not be handled yet.

## Motivating example

Let's say we want to find out which points are reachable in a graph. We can
determine which points are reachable using the following two logical rules:

1. One point is reachable from another point, iff there is a direct edge between
   those two points.
2. One point is reachable from another point, iff there is a third point 'z' such
   that there is a direct edge between 'x' and 'z', and between 'z' and 'y'.

The Eclair code below can be used to calculate the solution:

```eclair
@def edge(u32, u32).
@def reachable(u32, u32).

reachable(x, y) :-
  edge(x, y).

reachable(x, z) :-
  edge(x, y),
  reachable(y, z).
```

The above code can be compiled to LLVM using the Docker image provided by this repo:

```bash
$ git clone git@github.com:luc-tielen/eclair-lang.git
$ cd eclair-lang
$ docker build . -t eclair
# The next line assumes the eclair code is saved as "example.dl" in the current directory
$ docker run -v $PWD:/code --rm -it eclair:latest compile /code/example.dl
# NOTE: output can be redirected to a file using standard shell functionality: docker run ... > example.ll
```

This will emit the generated LLVM IR to the stdout of the terminal. If we save
this generated LLVM IR to a file (e.g. `example.ll`), we can link it with the
following C code that calls into Eclair, using the following command:
`clang -o program main.c example.ll`.

```c
// Save this file as "main.c".
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>


struct program;

extern struct program* eclair_program_init();
extern void eclair_program_destroy(struct program*);
extern void eclair_program_run(struct program*);
extern void eclair_add_facts(struct program*, uint16_t fact_type, uint32_t* data, size_t fact_count);
extern void eclair_add_fact(struct program*, uint16_t fact_type, uint32_t* data);
extern uint32_t* eclair_get_facts(struct program*, uint16_t fact_type);
extern void eclair_free_buffer(uint32_t* data);

int main(int argc, char** argv)
{
    struct program* prog = eclair_program_init();

    // edge(1,2), edge(2,3)
    uint32_t data[] = {
        1, 2,
        2, 3
    };
    eclair_add_facts(prog, 0, data, 2);

    eclair_program_run(prog);

    // NOTE: normally you call btree_size here to figure out the size, but I know there are only 3 facts
    uint32_t* data_out = eclair_get_facts(prog, 1);
    printf("REACHABLE: (%d, %d)\n", data_out[0], data_out[1]);  // (1,2)
    printf("REACHABLE: (%d, %d)\n", data_out[2], data_out[3]);  // (2,3)
    printf("REACHABLE: (%d, %d)\n", data_out[4], data_out[5]);  // (1,3)

    eclair_free_buffer(data_out);

    eclair_program_destroy(prog);
    return 0;
}
```

If you run the resulting program, this should print the reachable node pairs
`(1,2)`, `(2,3)` and `(1,3)` to the screen!

## Roadmap

- [x] LSP support
- [x] Allow setting options on relations for performance finetuning
- [x] Comparison operators, != operator
- [x] Support arithmetic operators
- [x] Generic, extensible primops
- [x] Support logical negation
- [ ] Release 0.2.0
- [ ] Signed integer type (i32)
- [ ] Unary negation
- [ ] Optimizations on the AST / RA / LLVM level
- [ ] Support other underlying data structures than btree
- [ ] Syntactic sugar (disjunctions in rule bodies, multiple rule heads, ...)
- [ ] Support Datalog programs spanning multiple files
- [ ] ...

This roadmap is not set in stone, but it gives an idea on the direction of the
project. :smile:

## Contributing to Eclair

Contributions are welcome! Take a look at the
[getting started guide](./docs/getting_started.md) on how to set up your machine
to build, run and test the project. Once setup, the Makefile contains the most
commonly used commands needed during development.

You can also use the `Dockerfile` in this repository if you want to experiment
with Eclair without installing the toolchain yourself. You can do that as
follows:

```bash
$ docker build -f Dockerfile . -t eclair
$ touch test.eclair  # Here you can put your Eclair code
$ docker run -v $PWD:/code --rm -it eclair eclair compile /code/test.eclair
```

## Documentation

Take a look at our [docs folder](./docs/) for more information about Eclair.

## Why the name?

Eclair is inspired by [Soufflé](https://souffle-lang.github.io/), a high
performance Datalog that compiles to C++. Because of the similarities, I chose a
different kind of food that I like. I mean, an eclair contains _both_ chocolate and
pudding, what's not to like!?

Logo art by [Bruno Monts](https://www.instagram.com/bruno_monts/),
with special thanks to the [Fission](https://fission.codes) team.
Please contact Luc Tielen before using the logo for anything.


================================================
FILE: cabal.project
================================================
packages: .

source-repository-package
    type: git
    location: https://github.com/luc-tielen/llvm-codegen.git
    tag: 83b04cb576208ea74ddd62016e4fa03f0df138ac

source-repository-package
    type: git
    location: https://github.com/luc-tielen/souffle-haskell.git
    tag: e441c84f1d64890e31c92fbb278c074ae8bcaff5

source-repository-package
    type: git
    location: https://github.com/luc-tielen/diagnose.git
    tag: 24a1d7a2b716d74c1fe44d47941a76c9f5a90c23


================================================
FILE: cbits/semantic_analysis.dl
================================================
// Input facts
.decl lit_number(node_id: unsigned, value: unsigned)
.decl lit_string(node_id: unsigned, value: symbol)
.decl variable(node_id: unsigned, var_name: symbol)
.decl constraint(node_id: unsigned, op: symbol, lhs_node_id: unsigned, rhs_node_id: unsigned)
.decl binop(node_id: unsigned, op: symbol, lhs_node_id: unsigned, rhs_node_id: unsigned)
.decl atom(node_id: unsigned, name: symbol)
.decl atom_arg(atom_id: unsigned, atom_arg_pos: unsigned, atom_arg_id: unsigned)
.decl rule(rule_id: unsigned, name: symbol)
.decl rule_arg(rule_id: unsigned, rule_arg_pos: unsigned, rule_arg_id: unsigned)
.decl rule_clause(rule_id: unsigned, rule_clause_pos: unsigned, rule_clause_id: unsigned)
.decl negation(negation_node_id: unsigned, inner_node_id: unsigned)
.decl input_relation(relation_name: symbol)
.decl output_relation(relation_name: symbol)
.decl internal_relation(relation_name: symbol)
.decl extern_definition(node_id: unsigned, extern_name: symbol)
.decl declare_type(node_id: unsigned, name: symbol)
.decl module(node_id: unsigned)
.decl module_declaration(module_id: unsigned, declaration_id: unsigned)
.decl scoped_value(scope_id: unsigned, value_id: unsigned)

// Internal rules
.decl relation_atom(node_id: unsigned, name: symbol)
.decl extern_atom(node_id: unsigned, name: symbol)
.decl grounded_node(rule_node_id: unsigned, node_id: unsigned)
.decl assign(node_id: unsigned, lhs_node_id: unsigned, rhs_node_id: unsigned) inline
.decl inequality_op(op: symbol)
.decl has_output_relation(node_id: unsigned)
.decl literal_contradiction(lit_id1: unsigned, lit_id2: unsigned)
.decl wildcard(node_id: unsigned) inline
.decl rule_head_var(rule_id: unsigned, var_id: unsigned, var_name: symbol)
.decl alias(rule_id: unsigned, id1: unsigned, id2: unsigned)
.decl points_to(rule_id: unsigned, id1: unsigned, id2: unsigned)
.decl depends_on(r1: symbol, r2: symbol)
.decl transitive_depends_on(r1: symbol, r2: symbol)
.decl source(r: symbol)
.decl has_definitions(relation: symbol)
.decl live_rule(relation: symbol)
.decl dependency_cycle(relation: symbol)
.decl rule_scope(rule_id: unsigned, scope_id: unsigned)
.decl constrained_rule_var(rule_node_id: unsigned, var_node_id: unsigned, var_name: symbol)

// Output facts / rules
.decl grounded_variable(rule_id: unsigned, var_name: symbol)
.decl ungrounded_variable(rule_id: unsigned, var_id: unsigned, var_name: symbol)
.decl ungrounded_external_atom(rule_id: unsigned, atom_id: unsigned, atom_name: symbol)
.decl wildcard_in_fact(fact_node_id: unsigned, fact_arg_id: unsigned, pos: unsigned)
.decl wildcard_in_extern(atom_node_id: unsigned, atom_arg_id: unsigned, pos: unsigned)
.decl wildcard_in_rule_head(rule_node_id: unsigned, rule_arg_id: unsigned, pos: unsigned)
.decl wildcard_in_constraint(constraint_node_id: unsigned, wildcard_node_id: unsigned)
.decl wildcard_in_binop(binop_node_id: unsigned, wildcard_node_id: unsigned)
.decl unconstrained_rule_var(rule_node_id: unsigned, var_node_id: unsigned, var_name: symbol)
.decl rule_with_contradiction(rule_id: unsigned)
.decl dead_code(node_id: unsigned)
.decl no_output_relation(node_id: unsigned)
.decl dead_internal_relation(node_id: unsigned, relation_name: symbol)
.decl conflicting_definitions(node_id1: unsigned, node_id2: unsigned, name: symbol)
.decl extern_used_as_fact(node_id: unsigned, extern_node_id: unsigned, name: symbol)
.decl extern_used_as_rule(node_id: unsigned, extern_node_id: unsigned, name: symbol)
.decl cyclic_negation(negation_id: unsigned)

.input lit_number
.input lit_string
.input variable
.input constraint
.input binop
.input atom
.input atom_arg
.input rule
.input rule_arg
.input rule_clause
.input negation
.input input_relation
.input output_relation
.input internal_relation
.input extern_definition
.input declare_type
.input module
.input module_declaration
.input scoped_value

.output wildcard_in_fact
.output wildcard_in_extern
.output ungrounded_variable
.output ungrounded_external_atom
.output wildcard_in_rule_head
.output wildcard_in_constraint
.output wildcard_in_binop
.output unconstrained_rule_var
.output dead_code
.output no_output_relation
.output dead_internal_relation
.output conflicting_definitions
.output extern_used_as_fact
.output extern_used_as_rule
.output cyclic_negation

// An atom that is not defined externally. This is an important distinction
// since external atoms cannot ground variables!
relation_atom(node_id, name) :-
  declare_type(_, name),
  atom(node_id, name).

// An atom that is defined externally.
extern_atom(node_id, name) :-
  extern_definition(_, name),
  atom(node_id, name).

// r1 depends on r2 if rule r1 refers to r2 in the body
depends_on(r1, r2) :-
  rule(rule_id, r1),
  rule_clause(rule_id, _, clause_id),
  atom(clause_id, r2).

// Variant for negated atoms.
depends_on(r1, r2) :-
  rule(rule_id, r1),
  rule_clause(rule_id, _, negation_id),
  negation(negation_id, atom_id),
  atom(atom_id, r2).

// Variant for extern functions.
depends_on(r1, r2) :-
  rule(rule_id, r1),
  rule_clause(rule_id, _, assign_id),
  assign(assign_id, lhs_node_id, rhs_node_id),
  (
    extern_atom(lhs_node_id, r2);
    extern_atom(rhs_node_id, r2)
  ).

transitive_depends_on(r1, r2) :-
  depends_on(r1, r2).

transitive_depends_on(r1, r3) :-
  depends_on(r1, r2),
  transitive_depends_on(r2, r3).

// Rules have cyclic dependencies if a rule depends on itself (transitively)
dependency_cycle(r) :-
  transitive_depends_on(r, r).

// Negations are not allowed inside a rule if the negated atom is part of a
// dependency cycle since this is not stratifiable.
cyclic_negation(negation_id) :-
  dependency_cycle(r1),
  transitive_depends_on(r1, r2),
  rule(rule_id, r2),
  rule_clause(rule_id, _, negation_id),
  negation(negation_id, atom_id),
  atom(atom_id, r3),
  transitive_depends_on(r3, r1).

// An input can always be a source of data.
source(r) :-
  input_relation(r).

// An internal or output relation can be a source of data if they define top level facts.
source(r) :-
  module_declaration(_, atom_id),
  atom(atom_id, r),
  !input_relation(r).  // internal or output relation

// An output rule is live if it is a direct source of data.
live_rule(r) :-
  output_relation(r),
  source(r).

// An output rule is live if there is a path from the source to this output.
live_rule(r1) :-
  output_relation(r1),
  transitive_depends_on(r1, r2),
  source(r2).

// A rule is live if it is depended on by another live rule.
live_rule(r2) :-
  depends_on(r1, r2),
  live_rule(r1).

// Dead rules are the opposite set of all the live rules.
dead_code(node_id) :-
  rule(node_id, r),
  !live_rule(r).

// Type definitions also need to be marked as dead.
dead_code(node_id) :-
  declare_type(node_id, r),
  !live_rule(r).

// Extern definitions also need to be marked as dead.
dead_code(node_id) :-
  extern_definition(node_id, r),
  !live_rule(r).

// Atoms too.
dead_code(node_id) :-
  atom(node_id, r),
  !live_rule(r).

// Rules are dead if one of the clauses is statically known to produce no results.
dead_code(rule_id) :-
  rule_with_contradiction(rule_id).

// A rule is dead if it depends on another dead rule.
// Note that this only looks at one specific rule that contains the dead code, not the entire relation.
dead_code(rule_id) :-
  rule_clause(rule_id, _, rule_clause_id),
  dead_code(rule_clause_id).

has_definitions(r) :-
  module_declaration(_, node_id),
  (
    atom(node_id, r);
    rule(node_id, r)
  ).

// We consider an internal relation to be dead if there are no top level atoms or rules.
dead_internal_relation(decl_id, r) :-
  internal_relation(r),
  declare_type(decl_id, r),
  !has_definitions(r).

has_output_relation(node_id) :-
  module_declaration(node_id, decl_id),
  declare_type(decl_id, r),
  output_relation(r).

no_output_relation(node_id) :-
  module_declaration(node_id, _),
  !has_output_relation(node_id).

// Top level facts: no variables allowed
ungrounded_variable(atom_id, var_id, var_name) :-
  module_declaration(_, atom_id),
  atom(atom_id, _),
  scoped_value(atom_id, var_id),
  variable(var_id, var_name),
  var_name != "_".

// Rules: no variables allowed in rule head if not used in a rule clause
ungrounded_variable(rule_id, var_id, var_name) :-
  rule_head_var(rule_id, var_id, var_name),
  var_name != "_",
  !grounded_variable(rule_id, var_name).  // Only compare by variable name!

// Variables used in a constraint (comparison, equality or inequality) need to be grounded.
ungrounded_variable(rule_id, var_id, var_name) :-
  rule_clause(rule_id, _, rule_clause_id),
  constraint(rule_clause_id, op, var_id, _),
  variable(var_id, var_name),
  var_name != "_",
  !grounded_variable(rule_id, var_name).

ungrounded_variable(rule_id, var_id, var_name) :-
  rule_clause(rule_id, _, rule_clause_id),
  constraint(rule_clause_id, op, _, var_id),
  variable(var_id, var_name),
  var_name != "_",
  !grounded_variable(rule_id, var_name).

// Variables used in a binop need to be grounded.
ungrounded_variable(rule_id, var_id, var_name) :-
  binop(_, _, var_id, _),
  variable(var_id, var_name),
  scoped_value(scope_id, var_id),
  rule_scope(rule_id, scope_id),
  !grounded_variable(rule_id, var_name).

ungrounded_variable(rule_id, var_id, var_name) :-
  binop(_, _, _, var_id),
  variable(var_id, var_name),
  scoped_value(scope_id, var_id),
  rule_scope(rule_id, scope_id),
  !grounded_variable(rule_id, var_name).

// Variables used in a negation need to be grounded.
ungrounded_variable(rule_id, var_id, var_name) :-
  negation(negation_id, _),
  rule_clause(rule_id, _, negation_id),
  scoped_value(negation_id, var_id),
  variable(var_id, var_name),
  !grounded_node(rule_id, var_id).

// External atoms used in a fact need to be grounded.
ungrounded_external_atom(fact_id, atom_id, atom_name) :-
  module_declaration(_, fact_id),
  atom_arg(fact_id, _, atom_id),
  extern_atom(atom_id, atom_name),
  !grounded_node(fact_id, atom_id).

// External atoms used in a rule head need to be grounded.
ungrounded_external_atom(rule_id, atom_id, atom_name) :-
  rule_arg(rule_id, _, atom_id),
  atom(atom_id, atom_name),
  scoped_value(rule_id, atom_id),
  !grounded_node(rule_id, atom_id).

// External atoms used in a comparison or inequality need to be grounded.
ungrounded_external_atom(rule_id, atom_id, atom_name) :-
  rule_clause(rule_id, _, rule_clause_id),
  constraint(rule_clause_id, op, _, atom_id),
  inequality_op(op),
  atom(atom_id, atom_name),
  scoped_value(rule_id, atom_id),
  !grounded_node(rule_id, atom_id).

ungrounded_external_atom(rule_id, atom_id, atom_name) :-
  rule_clause(rule_id, _, rule_clause_id),
  constraint(rule_clause_id, op, atom_id, _),
  inequality_op(op),
  atom(atom_id, atom_name),
  scoped_value(rule_id, atom_id),
  !grounded_node(rule_id, atom_id).

// External atoms used in a binop need to be grounded.
ungrounded_external_atom(rule_id, atom_id, atom_name) :-
  binop(_, _, _, atom_id),
  atom(atom_id, atom_name),
  scoped_value(rule_id, atom_id),
  !grounded_node(rule_id, atom_id).

ungrounded_external_atom(rule_id, atom_id, atom_name) :-
  binop(_, _, atom_id, _),
  atom(atom_id, atom_name),
  scoped_value(rule_id, atom_id),
  !grounded_node(rule_id, atom_id).

rule_scope(rule_id, rule_id) :-
  rule(rule_id, _).

rule_scope(rule_id, negation_id) :-
  rule_clause(rule_id, _, negation_id),
  negation(negation_id, _).

inequality_op("!=").
inequality_op("<").
inequality_op("<=").
inequality_op(">").
inequality_op(">=").

wildcard(node_id) :-
  variable(node_id, "_").

wildcard_in_rule_head(rule_id, rule_arg_id, pos) :-
  rule_arg(rule_id, pos, rule_arg_id),
  wildcard(rule_arg_id).

wildcard_in_fact(atom_id, atom_arg_id, pos) :-
  module_declaration(_, atom_id),
  atom_arg(atom_id, pos, atom_arg_id),
  wildcard(atom_arg_id).

wildcard_in_extern(atom_id, atom_arg_id, pos) :-
  rule(rule_id, _),
  scoped_value(rule_id, atom_id),
  extern_atom(atom_id, _),
  atom_arg(atom_id, pos, atom_arg_id),
  wildcard(atom_arg_id).

wildcard_in_constraint(constraint_node_id, lhs_node_id) :-
  constraint(constraint_node_id, _, lhs_node_id, _),
  wildcard(lhs_node_id).

wildcard_in_constraint(constraint_node_id, rhs_node_id) :-
  constraint(constraint_node_id, _, _, rhs_node_id),
  wildcard(rhs_node_id).

wildcard_in_binop(binop_node_id, lhs_node_id) :-
  binop(binop_node_id, _, lhs_node_id, _),
  wildcard(lhs_node_id).

wildcard_in_binop(binop_node_id, rhs_node_id) :-
  binop(binop_node_id, _, _, rhs_node_id),
  wildcard(rhs_node_id).

// A rule variable is unconstrained if there is no other occurrence of the variable in the rule.
// (This works because groundedness of a variable is also checked..
unconstrained_rule_var(rule_id, var_id, var_name) :-
  rule_scope(rule_id, scope_id),
  scoped_value(scope_id, var_id),
  variable(var_id, var_name),
  !constrained_rule_var(rule_id, var_id, var_name).

// This could be done much simpler using count aggregate but this is not implemented in Eclair yet.
constrained_rule_var(rule_id, var_id1, var_name) :-
  rule_scope(rule_id, scope_id1),
  rule_scope(rule_id, scope_id2),
  scoped_value(scope_id1, var_id1),
  scoped_value(scope_id2, var_id2),
  variable(var_id1, var_name),
  variable(var_id2, var_name),
  var_id1 != var_id2.

assign(node_id, lhs_node_id, rhs_node_id) :-
  constraint(node_id, "=", lhs_node_id, rhs_node_id).

// All variables with the same name in a rule are aliases of each other.
alias(rule_id, var_id1, var_id2) :-
  scoped_value(rule_id, var_id1),
  variable(var_id1, var_name),
  scoped_value(rule_id, var_id2),
  var_id1 != var_id2,
  variable(var_id2, var_name).

// Two values are aliases if they are used inside an equality.
// NOTE: Datalog supports both x = 123 and 123 = x.
alias(rule_id, id1, id2),
alias(rule_id, id2, id1) :-
  rule_clause(rule_id, _, rule_clause_id),
  assign(rule_clause_id, id1, id2).

// Non-recursive case: what does a variable point to?
points_to(rule_id, id1, id2) :-
  alias(rule_id, id1, id2),
  variable(id1, _).

// Recursive case: a = b, b = c results in a = c
points_to(rule_id, id1, id4) :-
  points_to(rule_id, id1, id2),
  variable(id2, var_name),
  variable(id3, var_name),
  alias(rule_id, id3, id4).

// If we find two variables that point to different literal values,
// then there is a contradiction.
rule_with_contradiction(rule_id) :-
  points_to(rule_id, start_id, id1),
  points_to(rule_id, start_id, id2),
  literal_contradiction(id1, id2).

// This is also true for simple cases like '123 = 456'.
rule_with_contradiction(rule_id) :-
  rule_clause(rule_id, _, rule_clause_id),
  assign(rule_clause_id, id1, id2),
  literal_contradiction(id1, id2).

literal_contradiction(id1, id2) :-
  lit_number(id1, value1),
  lit_number(id2, value2),
  value1 != value2.

literal_contradiction(id1, id2) :-
  lit_string(id1, value1),
  lit_string(id2, value2),
  value1 != value2.


// Helper relation for getting all variables in head of a rule.
rule_head_var(rule_id, var_id, var_name) :-
  rule_arg(rule_id, _, var_id),
  variable(var_id, var_name).

// Helper relation for getting all grounded variables in body of a rule.
grounded_variable(rule_id, var_name) :-
  grounded_node(rule_id, var_id),
  variable(var_id, var_name).

// Variables are grounded if they are used in an atom (defined using '@def').
grounded_node(rule_id, var_id) :-
  rule_clause(rule_id, _, rule_clause_id),
  relation_atom(rule_clause_id, _),
  atom_arg(rule_clause_id, _, var_id),
  variable(var_id, _).

// All variables with same name are grounded at the same time
grounded_node(rule_id, var_id2) :-
  scoped_value(rule_id, var_id1),
  variable(var_id1, var_name),
  grounded_node(rule_id, var_id1),
  scoped_value(rule_id, var_id2),
  variable(var_id2, var_name).

// Variables are grounded inside a negation if they are grounded in another clause.
grounded_node(rule_id, var_id2) :-
  negation(negation_id, _),
  rule_clause(rule_id, _, negation_id),
  scoped_value(rule_id, var_id1),
  variable(var_id1, var_name),
  grounded_node(rule_id, var_id1),
  scoped_value(negation_id, var_id2),
  variable(var_id2, var_name).

// Literals are always grounded.
grounded_node(rule_id, node_id) :-
  scoped_value(rule_id, node_id),
  lit_number(node_id, _).

grounded_node(rule_id, node_id) :-
  scoped_value(rule_id, node_id),
  lit_string(node_id, _).

// A binop is grounded if both sides are grounded.
grounded_node(rule_id, node_id) :-
  grounded_node(rule_id, lhs_node_id),
  grounded_node(rule_id, rhs_node_id),
  binop(node_id, _, lhs_node_id, rhs_node_id).

// Assignment grounds one var, if the other side is already grounded.
grounded_node(rule_id, rhs_node_id) :-
  rule_clause(rule_id, _, rule_clause_id),
  assign(rule_clause_id, lhs_node_id, rhs_node_id),
  grounded_node(rule_id, lhs_node_id),
  variable(rhs_node_id, _).

grounded_node(rule_id, lhs_node_id) :-
  rule_clause(rule_id, _, rule_clause_id),
  assign(rule_clause_id, lhs_node_id, rhs_node_id),
  grounded_node(rule_id, rhs_node_id),
  variable(lhs_node_id, _).

conflicting_definitions(node_id, node_id2, name) :-
  declare_type(node_id, name),
  declare_type(node_id2, name),
  node_id < node_id2.

conflicting_definitions(node_id, node_id2, name) :-
  extern_definition(node_id, name),
  extern_definition(node_id2, name),
  node_id < node_id2.

conflicting_definitions(node_id, node_id2, name) :-
  declare_type(node_id, name),
  extern_definition(node_id2, name),
  node_id < node_id2.

conflicting_definitions(node_id, node_id2, name) :-
  extern_definition(node_id, name),
  declare_type(node_id2, name),
  node_id < node_id2.

extern_used_as_fact(node_id, extern_node_id, name) :-
  extern_definition(extern_node_id, name),
  module_declaration(_, node_id),
  atom(node_id, name).

extern_used_as_rule(node_id, extern_node_id, name) :-
  extern_definition(extern_node_id, name),
  rule(node_id, name).


================================================
FILE: docs/architecture_choices.md
================================================
# Architecture choices

This document contains all the high level choices that have been made with
regards to the architecture of the compiler. Besides this document, there are
also [several blogposts](https://luctielen.com/) with deep dives on specific
parts of the compiler.

## Inspired by Souffle

Eclair's approach to compiling high level Datalog syntax to assembly is heavily
inspired by Souffle Datalog (most notably: the compilation to relational algebra
and the minimum index selection algorithm). At the top of the corresponding
source code in Eclair, there is a comment pointing to the paper so contributors
can consult the theoretic background behind the code easily.

Eclair does have some notable differences though. First and foremost: it is
written in Haskell. This choice was made because Haskell makes it easy to
express high level ideas and algorithms that you commonly run into when building
a compiler. As an additional benefit, Haskell already has a great ecosystem of
libraries for building a compiler.
The second big change compared to Souffle is that Eclair compiles down directly
to LLVM instead of C++. This gives greater control of the generated assembly
(assembly is generated using a monad / "builder pattern" instead of
concatenating strings to form a C++ program) and also makes it easily portable
to other platforms (including WebAssembly). On top of that we can leverage
existing LLVM tools to analyze / transform the generated LLVM IR.

## IR Design

Eclair makes use of four different intermediate representations (IRs). Each of
the IRs has a different focus / view of the program. By using multiple IRs, we
can also gradually lower the Datalog syntax to the assembly level.

The four different IRs are:

1. AST
2. RA
3. EIR
4. LLVM IR

Each of these IRs is discussed in the subsections below. Every IR can be pretty
printed for inspection (when debugging a compiler issue or when writing Eclair
code).

Each of the IRs are designed in a similar way: they all consist of a single data
type each. This might be a bit controversial for most Haskellers that value type
safety, but this ends up working out because:

1. The parser, semantic analysis and typesystem steps halt when they determine
   the program is invalid;
2. Often we are only interested in a really small part of the IR anyway;
3. Most Haskell libraries support one simple type best.

The singly-typed IR is a conciously made trade-off, but it gives us great
benefits. Transformations (a large part of the compiler!) can be written down
succinctly thanks to the
[recursion-schemes library](https://hackage.haskell.org/package/recursion-schemes).
The transforms are guaranteed to terminate when written this way, and are
automatically composable. Besides that, all algorithms can be written down as a
pattern-match that focuses on only one node of the IR.

Besides having singly-typed IRs, each data constructor in the IR type has a
unique node ID attached to it. This makes it possible to link data from outside
the IR to it, without having to keep modifying the IR over and over. This is
especially useful for the semantic analysis and typesystem parts of the
compiler, since they can refer to parts of the program via a node ID.

### AST

The first IR is the `AST` type. AST stands for "Abstract Syntax Tree" and is a
tree representation of the original source code.

Semantic analysis and typechecking happens on this IR before any transformations
are performed, so we can report back exact locations to users of Eclair.

The AST is the most "high level" / starting IR. AST compiles down to RA, which
is described in the next section.

### RA

`RA` stands for "Relational Algebra". It represents a Eclair Datalog program as
a set of relational algebra operations. The data type is pretty much copied
directly from the Souffle paper, with some minor modifications. By first
transforming the AST to RA, we can subsequently lower the code even further down
to the assembly level (via EIR and LLVM IR).

### EIR

`EIR` is an abbreviation for "Eclair IR". It is a IR designed to be very close
to LLVM IR, but with the focus that it is also easy to debug and inspect. It was
mainly created to make the final lowering to LLVM IR as trivial as possible, but
it ended up being also useful for stitching the various Eclair functions in the
runtime together.

### LLVM IR

The LLVM IR is the final IR this compiler makes use of and bears the closest
resemblance to assembly. The
[llvm-codegen library](https://github.com/luc-tielen/llvm-codegen) is used to
generate LLVM instructions. From this point onwards, we can make use of the LLVM
compiler framework to get many optimizations and other tools all for free.

## Query-based compiler

Eclair is a so-called "query-based compiler". What this means is:

1. Each stage of the compiler builds on top of previous stages,
2. You can query the results of each of these "sub-computations".

This kind of architecture ends up being very useful for a compiler since a
compiler often is not a "pipeline" as it is usually presented, but instead has
a graph structure where later stages can depend on earlier stages. On top of
that, it makes it simple to access information at each stage of the compiler,
making it easy to write tools that can query the compiler as a database. (Useful
for developer tools such as LSP!)

The [rock library](https://hackage.haskell.org/package/rock) builds on top of
this idea and provides an API for describing your compiler in terms of build
system rules.

## The parser

The parser is written using parser combinators (from the `megaparsec` library).
This approach was chosen because now this parser is fully written in Haskell,
making it trivial to integrate with the rest of the compiler. On top of that, it
gives you full control over how the parsing happens. In the Eclair compiler the
parser adds a unique node id to each parsed AST node (see section about IR
design).

## Semantic analysis

Eclair makes use of Souffle Datalog during semantic analysis. Using Datalog for
semantic analysis is great, because you can write all your logic really
succinctly by writing down the "patterns" you are looking for in the AST and let
Datalog deduce all results.

The fact that each AST node has a unique ID (see section about IR design) makes
it easy to refer to certain parts of a program and also to make it easy to
serialize data back and forth between Haskell and Datalog.

Eventually, Eclair will be a bootstrapped compiler, meaning all the parts where
Souffle Datalog is currently used will be swapped out with an Eclair Datalog
counterpart. This will make it much easier to distribute and run the compiler
(since there is one big dependency less required).

## Typesystem

The typesystem is a bidirectional typesystem. This means that the typesystem
either checks a term against an expected type, or it tries to infer a type.
Bidirectional typecheckers are great because they are "straight-forward" to
implement (you pretty much need to write two functions that pattern match on the
syntax and handle each node type correspondingly), and produce better error
messages than typesystems that make heavy use of constraint solving.

On top of this, the typesystem tries to report as many type errors as possible
at once and with additional context how it came to these conclusions. This is
done to make the developer experience better.

## Error rendering

An effort is made to make Eclair errors as clear as possible for developers
(using Rust and Elm for inspiration). Right now we use the
[diagnose library](https://github.com/Mesabloo/diagnose) for reporting the
errors since it allows us to focus on other parts of the compiler, but later
this error rendering system will be implemented in the Eclair codebase itself to
allow for more customization.

## Tranformations

Eclair has a general
[Transform](https://github.com/luc-tielen/eclair-lang/blob/main/lib/Eclair/Transform.hs)
type that can be used for transforming the various IRs. Transformations can have
two goals: they either simplify the IR, or they try to optimize it (or both).

Eclair is a nano-pass compiler. Transformations should be small, focused and
composable; so that you can reason about them. It's better to have a few extra
passes in the compiler instead of a lot of extra complexity. The `Transform`
type provides helper functions and typeclass instances to compose them into
bigger transforms anyway.

Transformations can have local state, but the way it is setup it is impossible
for this state to "leak out" to the outside world. This is again done to make it
easier to reason about, while not limiting what's possible inside a transform.

Finally, transforms always run in a `TransformM` monad. This monad offers a way
of generating new unique node IDs in case extra IR nodes are generated.

## Tests

Currently the Eclair test suite is divided into two parts:

1. Unit tests written directly in Haskell
2. "Integration" tests that are executed by the `lit` executable provided by LLVM.

Over time, most of the tests will be integration tests, since they are more
rigorous and test larger parts of the compiler. On top of that, these style of
tests allow you to write an example directly in Eclair and compare the result
against actual output of the compiler.


================================================
FILE: docs/getting_started.md
================================================
# Getting started

Eclair requires a Haskell toolchain, Souffle 2.3 and LLVM 14 to be installed on
your system.

If you notice that the installation instructions below are incomplete or
outdated, please open a [Github issue](https://github.com/luc-tielen/eclair-lang/issues).

## Pre-requisites

### Ubuntu

NOTE: These commands were tested with Ubuntu 20.04, they may not work with older
versions.

#### Installing the Haskell toolchain

Run the following commands to install `ghcup`, `ghc` and `cabal`. `cabal-fmt`, 
`hspec-discover` and `hlint` are also installed but they are only needed when working on the
compiler.

```bash
$ curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
$ ghcup tui
# In the terminal UI, select GHC 9.2.4, Haskell language server 1.8 and Cabal 3.6.
# Important: both install + set them!
$ cabal install cabal-fmt
$ cabal install hspec-discover
$ cabal install hlint
```

Verify you installed the correct versions by running the commands below, and
comparing them against the versions mentioned in the previous command:

```bash
$ ghc --version
$ haskell-language-server-wrapper --version
$ cabal --version
```

#### Installing Souffle

Run the following commands to download and build Souffle from source:

```bash
$ sudo apt install bison build-essential cmake doxygen flex g++ git \
  libffi-dev libncurses5-dev libsqlite3-dev make mcpp python sqlite zlib1g-dev

$ git clone git@github.com:souffle-lang/souffle.git
$ cd souffle
$ git checkout 2.3
$ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
$ cmake --build build -j
$ sudo cmake --build build --target install
```

If this went correctly, Souffle should now be globally installed on your system.
Check this by executing the following command; it should print out the version
of Souffle (2.3).

```bash
$ souffle --version
```

#### Installing LLVM

Next we need to install LLVM 14. Run the steps below to install it on your
system.

```bash
# If these packages are not available on your system, try installing using this
# link: https://apt.llvm.org/.
$ sudo apt install llvm-14
$ sudo apt install lld-14
$ sudo apt install clang-14  # Optional, if you want to use clang instead of llc
                             # to compile the LLVM IR
# The following is only needed for development / testing
$ cd ~/.local/bin  # Or any other directory that is on your $PATH
$ ln -s /usr/lib/llvm-14/bin/split-file
$ ln -s /usr/bin/FileCheck-14 FileCheck
$ pip install lit==14.0.6
```

### Windows

Assuming you have Windows subsytem for Linux, the commands above should also
work for Windows? (If somebody could verify this, that would be great!)

### OSX

NOTE: These commands were tested with Intel MacOS 13.0, they may or may not not work
with older versions or on an ARM-based machine.

#### Installing the Haskell toolchain

Run the following commands (see https://www.haskell.org/ghcup/) to install `ghcup`, `ghc` and `cabal`.

```bash
$ curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
$ ghcup tui
# In the terminal UI, select GHC 9.2.4, Haskell language server 1.8 and Cabal 3.6.
# Important: both install + set them!
```

The following commands are only needed when working on the compiler.
Run the commands to install `cabal-fmt`, `hspec-discover`, and `hlint`.

```bash
$ cabal install cabal-fmt
$ cabal install hspec-discover
$ cabal install hlint
```

Verify you installed the correct versions by running the commands below, and
compare them against the versions mentioned in the previous command:

```bash
$ ghc --version
$ haskell-language-server-wrapper --version
$ cabal --version
```

#### Installing Souffle

Run the following commands to download and build Souffle from source
(instructions taken from [here](https://souffle-lang.github.io/build#mac-os-x-build)):

```bash
$ brew update
$ brew install cmake bison libffi mcpp pkg-config
$ brew reinstall gcc
$ brew link bison --force
$ brew link libffi --force
$ brew install souffle-lang/souffle/souffle
```

If this went correctly, Souffle should now be globally installed on your system.
Check this by executing the following command; it should print out the version
of Souffle (2.3).

```bash
$ souffle --version
```

#### Installing LLVM

Next we need to install LLVM 14. Run the steps below to install it on your
system.

```bash
$ brew install llvm@14
# The following is only needed for development / testing
$ cd ~/.local/bin  # Or any other directory that is on your $PATH
$ ln -s /usr/local/opt/llvm@14/bin/split-file
$ ln -s /usr/local/opt/llvm@14/bin/FileCheck
$ pip install lit==14.0.6
$ brew install node
```

## Building Eclair

Now that all the pre-requisites are built, you can build Eclair.

```bash
$ cabal build
$ cabal run eclair-test  # Unit tests
$ lit tests -v           # Integration tests
$ cabal list-bin eclair  # <= returns path to the Eclair compiler executable
```


================================================
FILE: eclair-lang.cabal
================================================
cabal-version:      2.2
name:               eclair-lang
version:            0.2.0
synopsis:
  Eclair: an experimental and minimal Datalog that compiles to LLVM.

description:
  Eclair: an experimental and minimal Datalog that compiles to LLVM.

category:           Compiler
homepage:           https://github.com/luc-tielen/eclair-lang
author:             Luc Tielen
maintainer:         luc.tielen@gmail.com
copyright:          Luc Tielen, 2023
license:            BSD-3-Clause
license-file:       LICENSE
build-type:         Simple
extra-source-files:
  cbits/semantic_analysis.dl
  CHANGELOG.md
  LICENSE
  README.md

flag debug
  description: Enables stack traces.
  manual:      True
  default:     False

library
  -- cabal-fmt: expand lib
  exposed-modules:
    Eclair
    Eclair.ArgParser
    Eclair.AST.Analysis
    Eclair.AST.Codegen
    Eclair.AST.IR
    Eclair.AST.Lower
    Eclair.AST.Transforms
    Eclair.AST.Transforms.ConstantFolding
    Eclair.AST.Transforms.DeadCodeElimination
    Eclair.AST.Transforms.NormalizeRules
    Eclair.AST.Transforms.RemoveAliases
    Eclair.AST.Transforms.ReplaceStrings
    Eclair.Common.Config
    Eclair.Common.Extern
    Eclair.Common.Id
    Eclair.Common.Literal
    Eclair.Common.Location
    Eclair.Common.Operator
    Eclair.Common.Pretty
    Eclair.Comonads
    Eclair.EIR.IR
    Eclair.EIR.Lower
    Eclair.EIR.Lower.API
    Eclair.EIR.Lower.Codegen
    Eclair.EIR.Lower.Externals
    Eclair.Error
    Eclair.JSON
    Eclair.LLVM.Allocator.Arena
    Eclair.LLVM.Allocator.Common
    Eclair.LLVM.Allocator.Malloc
    Eclair.LLVM.Allocator.Page
    Eclair.LLVM.BTree
    Eclair.LLVM.BTree.Bounds
    Eclair.LLVM.BTree.Compare
    Eclair.LLVM.BTree.Create
    Eclair.LLVM.BTree.Destroy
    Eclair.LLVM.BTree.Find
    Eclair.LLVM.BTree.Insert
    Eclair.LLVM.BTree.Iterator
    Eclair.LLVM.BTree.Size
    Eclair.LLVM.BTree.Types
    Eclair.LLVM.Codegen
    Eclair.LLVM.Config
    Eclair.LLVM.Externals
    Eclair.LLVM.Hash
    Eclair.LLVM.HashMap
    Eclair.LLVM.Metadata
    Eclair.LLVM.Symbol
    Eclair.LLVM.SymbolTable
    Eclair.LLVM.Table
    Eclair.LLVM.Template
    Eclair.LLVM.Vector
    Eclair.LSP
    Eclair.LSP.Handlers
    Eclair.LSP.Handlers.Diagnostics
    Eclair.LSP.Handlers.DocumentHighlight
    Eclair.LSP.Handlers.Hover
    Eclair.LSP.JSON
    Eclair.LSP.Monad
    Eclair.LSP.Types
    Eclair.LSP.VFS
    Eclair.Parser
    Eclair.RA.Codegen
    Eclair.RA.IndexSelection
    Eclair.RA.IR
    Eclair.RA.Lower
    Eclair.RA.Transforms
    Eclair.RA.Transforms.HoistConstraints
    Eclair.Souffle.IR
    Eclair.Transform
    Eclair.TypeSystem
    Prelude

  hs-source-dirs:     lib
  default-extensions:
    DataKinds
    DeriveAnyClass
    DeriveFoldable
    DeriveFunctor
    DeriveGeneric
    DeriveTraversable
    DerivingStrategies
    DerivingVia
    FlexibleContexts
    FlexibleInstances
    KindSignatures
    LambdaCase
    OverloadedStrings
    PatternSynonyms
    RankNTypes
    RecursiveDo
    ScopedTypeVariables
    TupleSections
    TypeFamilies
    ViewPatterns

  ghc-options:
    -Wall -Wincomplete-patterns -fhide-source-paths
    -fno-show-valid-hole-fits -fno-sort-valid-hole-fits

  cxx-options:        -std=c++17 -D__EMBEDDED_SOUFFLE__ -Wall
  build-depends:
    , algebraic-graphs             <1
    , base                         >=4.7   && <5
    , bytestring                   >=0.11  && <0.12
    , comonad                      >=5     && <6
    , containers                   <1
    , dependent-sum                >=0.6   && <1
    , diagnose                     >=2.3   && <2.4
    , directory                    >=1     && <2
    , dlist                        >=1     && <2
    , exceptions                   >=0.10  && <0.11
    , extra                        >=1     && <2
    , ghc-prim                     <1
    , hermes-json                  <1
    , llvm-codegen
    , megaparsec                   >=9     && <10
    , mmorph                       >=1     && <2
    , mtl                          >=2     && <3
    , optparse-applicative         >=0.16  && <0.17
    , parser-combinators           >=1.3   && <1.4
    , prettyprinter                >=1.7   && <1.8
    , prettyprinter-ansi-terminal  >=1     && <2
    , recursion-schemes            >=5     && <6
    , relude                       >=1.2   && <1.3
    , rock                         >=0.3   && <0.4
    , souffle-haskell              ==4.0.0
    , text                         >=2     && <3
    , text-builder-linear          <1
    , transformers                 <1
    , vector                       >=0.12  && <0.13

  mixins:             base hiding (Prelude)
  default-language:   Haskell2010

  if os(osx)
    extra-libraries: c++

  if flag(debug)
    ghc-options:   -fplugin=StackTrace.Plugin
    build-depends: haskell-stack-trace-plugin ==0.1.3.0

  if os(linux)
    extra-libraries: stdc++

executable eclair
  main-is:            Main.hs
  other-modules:      Paths_eclair_lang
  autogen-modules:    Paths_eclair_lang
  hs-source-dirs:     src/eclair
  default-extensions:
    DataKinds
    DeriveAnyClass
    DeriveFoldable
    DeriveFunctor
    DeriveGeneric
    DeriveTraversable
    DerivingStrategies
    DerivingVia
    FlexibleContexts
    FlexibleInstances
    KindSignatures
    LambdaCase
    OverloadedStrings
    PatternSynonyms
    RankNTypes
    RecursiveDo
    ScopedTypeVariables
    TupleSections
    TypeFamilies
    ViewPatterns

  ghc-options:
    -Wall -Wincomplete-patterns -fhide-source-paths
    -fno-show-valid-hole-fits -fno-sort-valid-hole-fits -threaded
    -rtsopts -with-rtsopts=-N

  cxx-options:        -std=c++17 -D__EMBEDDED_SOUFFLE__
  build-depends:
    , algebraic-graphs             <1
    , base                         >=4.7   && <5
    , bytestring                   >=0.11  && <0.12
    , comonad                      >=5     && <6
    , containers                   <1
    , dependent-sum                >=0.6   && <1
    , diagnose                     >=2.3   && <2.4
    , directory                    >=1     && <2
    , dlist                        >=1     && <2
    , eclair-lang
    , exceptions                   >=0.10  && <0.11
    , extra                        >=1     && <2
    , llvm-codegen
    , megaparsec                   >=9     && <10
    , mmorph                       >=1     && <2
    , mtl                          >=2     && <3
    , optparse-applicative         >=0.16  && <0.17
    , parser-combinators           >=1.3   && <1.4
    , prettyprinter                >=1.7   && <1.8
    , prettyprinter-ansi-terminal  >=1     && <2
    , process                      >=1.6   && <1.7
    , recursion-schemes            >=5     && <6
    , relude                       >=1.2   && <1.3
    , rock                         >=0.3   && <0.4
    , souffle-haskell              ==4.0.0
    , text                         >=2     && <3
    , transformers                 <1
    , vector                       >=0.12  && <0.13

  mixins:             base hiding (Prelude)
  default-language:   Haskell2010

  if os(osx)
    extra-libraries: c++

  if flag(debug)
    ghc-options:   -fplugin=StackTrace.Plugin
    build-depends: haskell-stack-trace-plugin ==0.1.3.0

test-suite eclair-test
  type:               exitcode-stdio-1.0
  main-is:            test.hs

  -- cabal-fmt: expand tests/eclair
  other-modules:
    Paths_eclair_lang
    Test.Eclair.ArgParserSpec
    Test.Eclair.JSONSpec
    Test.Eclair.LLVM.Allocator.MallocSpec
    Test.Eclair.LLVM.Allocator.PageSpec
    Test.Eclair.LLVM.Allocator.Utils
    Test.Eclair.LLVM.BTreeSpec
    Test.Eclair.LLVM.HashMapSpec
    Test.Eclair.LLVM.HashSpec
    Test.Eclair.LLVM.SymbolSpec
    Test.Eclair.LLVM.SymbolTableSpec
    Test.Eclair.LLVM.SymbolUtils
    Test.Eclair.LLVM.VectorSpec
    Test.Eclair.LSP.HandlersSpec
    Test.Eclair.LSP.JSONSpec
    Test.Eclair.RA.IndexSelectionSpec

  autogen-modules:    Paths_eclair_lang
  hs-source-dirs:     tests/eclair
  default-extensions:
    DataKinds
    DeriveAnyClass
    DeriveFoldable
    DeriveFunctor
    DeriveGeneric
    DeriveTraversable
    DerivingStrategies
    DerivingVia
    FlexibleContexts
    FlexibleInstances
    KindSignatures
    LambdaCase
    OverloadedStrings
    PatternSynonyms
    RankNTypes
    RecursiveDo
    ScopedTypeVariables
    TupleSections
    TypeFamilies
    ViewPatterns

  ghc-options:
    -Wall -Wincomplete-patterns -fhide-source-paths
    -fno-show-valid-hole-fits -fno-sort-valid-hole-fits

  cxx-options:        -std=c++17 -D__EMBEDDED_SOUFFLE__
  build-depends:
    , algebraic-graphs             <1
    , array                        >=0.5   && <1
    , base                         >=4.7   && <5
    , bytestring                   >=0.11  && <0.12
    , comonad                      >=5     && <6
    , containers                   <1
    , dependent-sum                >=0.6   && <1
    , diagnose                     >=2.3   && <2.4
    , dlist                        >=1     && <2
    , eclair-lang
    , exceptions                   >=0.10  && <0.11
    , extra                        >=1     && <2
    , filepath                     >=1     && <2
    , hedgehog                     >=1     && <2
    , hermes-json                  <1
    , hspec                        >=2.6.1 && <3.0.0
    , hspec-hedgehog               <1
    , libffi                       >=0.2   && <1
    , llvm-codegen
    , megaparsec                   >=9     && <10
    , mmorph                       >=1     && <2
    , mtl                          >=2     && <3
    , neat-interpolation           <1
    , optparse-applicative         >=0.16  && <0.17
    , parser-combinators           >=1.3   && <1.4
    , prettyprinter                >=1.7   && <1.8
    , prettyprinter-ansi-terminal  >=1     && <2
    , random                       >=1.2   && <2
    , recursion-schemes            >=5     && <6
    , relude                       >=1.2   && <1.3
    , rock                         >=0.3   && <0.4
    , silently                     >=1.2   && <1.3
    , souffle-haskell              ==4.0.0
    , text                         >=2     && <3
    , transformers                 <1
    , unix                         >=2.8   && <3
    , vector                       >=0.12  && <0.13

  mixins:             base hiding (Prelude)
  default-language:   Haskell2010

  if os(osx)
    extra-libraries: c++

  if flag(debug)
    ghc-options:   -fplugin=StackTrace.Plugin
    build-depends: haskell-stack-trace-plugin ==0.1.3.0


================================================
FILE: hie.yaml
================================================
cradle:
  cabal:
    - path: "lib"
      component: "lib:eclair-lang"

    - path: "src/eclair/Main.hs"
      component: "eclair-lang:exe:eclair"

    - path: "src/eclair/Paths_eclair_lang.hs"
      component: "eclair-lang:exe:eclair"

    - path: "src/lsp"
      component: "eclair-lang:exe:eclair-lsp-server"

    - path: "tests/eclair"
      component: "eclair-lang:test:eclair-test"

    - path: "tests/lsp/Main.hs"
      component: "eclair-lang:test:eclair-lsp-test"


================================================
FILE: lib/Eclair/AST/Analysis.hs
================================================
{-# LANGUAGE UndecidableInstances #-}

module Eclair.AST.Analysis
  ( Result(..)
  , SemanticInfo(..)
  , SemanticErrors(..)
  , hasSemanticErrors
  , runAnalysis
  , UngroundedVar(..)
  , WildcardInFact(..)
  , WildcardInRuleHead(..)
  , WildcardInConstraint(..)
  , WildcardInBinOp(..)
  , WildcardInExtern(..)
  , UnconstrainedRuleVar(..)
  , DeadCode(..)
  , DeadInternalRelation(..)
  , NoOutputRelation(..)
  , ConflictingDefinitionGroup(..)
  , ExternUsedAsFact(..)
  , ExternUsedAsRule(..)
  , CyclicNegation(..)
  , NodeId(..)
  , Container
  , computeUsageMapping
  ) where

import qualified Data.List.NonEmpty as NE
import Data.List.Extra (nubOrdOn)
import qualified Language.Souffle.Interpreted as S
import qualified Language.Souffle.Analysis as S
import qualified Eclair.AST.IR as IR
import qualified Data.Map as Map
import Eclair.Common.Id
import Eclair.Common.Location (NodeId(..))


type Position = Word32

-- The facts submitted to Datalog closely follow the AST structure,
-- but are denormalized so that Datalog can easily process it.

data LitNumber
  = LitNumber NodeId Word32
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions LitNumber "lit_number" 'S.Input

data LitString
  = LitString NodeId Text
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions LitString "lit_string" 'S.Input

data Var
  = Var NodeId Id
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions Var "variable" 'S.Input

newtype Hole
  = Hole NodeId
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions Hole "hole" 'S.Input

data Constraint
  = Constraint
  { constraintId :: NodeId
  , constraintOperator :: Text
  , constraintLhsId :: NodeId
  , constraintRhsId :: NodeId
  }
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions Constraint "constraint" 'S.Input

data BinOp
  = BinOp
  { binOpId :: NodeId
  , op :: Text
  , binOpLhsId :: NodeId
  , binOpRhsId :: NodeId
  }
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions BinOp "binop" 'S.Input

data Atom
  = Atom NodeId Id
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions Atom "atom" 'S.Input

data AtomArg
  = AtomArg { atomId :: NodeId, atomArgPos :: Word32, atomArgId :: NodeId }
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions AtomArg "atom_arg" 'S.Input

data Rule
  = Rule NodeId Id
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions Rule "rule" 'S.Input

data RuleArg
  = RuleArg { raRuleId :: NodeId, raArgPos :: Word32, raArgId :: NodeId }
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions RuleArg "rule_arg" 'S.Input

data RuleClause
  = RuleClause { rcRuleId :: NodeId, rcClausePos :: Word32, rcClauseId :: NodeId }
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions RuleClause "rule_clause" 'S.Input

data Negation
  = Negation
  { negationNodeId :: NodeId
  , negationInnerNodeId :: NodeId
  }
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions Negation "negation" 'S.Input

-- NOTE: not storing types right now, but might be useful later?
data DeclareType
  = DeclareType NodeId Id
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions DeclareType "declare_type" 'S.Input

data ExternDefinition
  = ExternDefinition NodeId Id
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions ExternDefinition "extern_definition" 'S.Input

newtype InputRelation
  = InputRelation Id
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions InputRelation "input_relation" 'S.Input

newtype OutputRelation
  = OutputRelation Id
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions OutputRelation "output_relation" 'S.Input

newtype InternalRelation
  = InternalRelation Id
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions InternalRelation "internal_relation" 'S.Input

newtype Module
  = Module NodeId
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions Module "module" 'S.Input

data ModuleDecl
  = ModuleDecl { moduleId :: NodeId, declId :: NodeId }
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions ModuleDecl "module_declaration" 'S.Input

data ScopedValue
  = ScopedValue { svScopeId :: NodeId, svNodeId :: NodeId }
  deriving stock Generic
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions ScopedValue "scoped_value" 'S.Input

data UngroundedVar loc
  = UngroundedVar
  { ungroundedRuleLoc :: loc
  , ungroundedVarLoc :: loc
  , ungroundedVarName :: Id
  }
  deriving stock (Generic, Eq, Functor)
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions (UngroundedVar loc) "ungrounded_variable" 'S.Output

data WildcardInFact loc
  = WildcardInFact
  { factLoc :: loc
  , factArgLoc :: loc
  , wildcardFactPos :: Position
  }
  deriving stock (Generic, Eq, Functor)
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions (WildcardInFact loc) "wildcard_in_fact" 'S.Output

data WildcardInRuleHead loc
  = WildcardInRuleHead
  { wildcardRuleLoc :: loc
  , wildcardRuleArgLoc :: loc
  , wildcardRuleHeadPos :: Position
  }
  deriving stock (Generic, Eq, Functor)
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions (WildcardInRuleHead loc) "wildcard_in_rule_head" 'S.Output

data WildcardInConstraint loc
  = WildcardInConstraint
  { wildcardConstraintLoc :: loc
  , wildcardConstraintPos :: loc
  }
  deriving stock (Generic, Eq, Functor)
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions (WildcardInConstraint loc) "wildcard_in_constraint" 'S.Output

data WildcardInBinOp loc
  = WildcardInBinOp
  { wildcardBinOpLoc :: loc
  , wildcardBinOpPos :: loc
  }
  deriving stock (Generic, Eq, Functor)
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions (WildcardInBinOp loc) "wildcard_in_binop" 'S.Output

data WildcardInExtern loc
  = WildcardInExtern
  { wildcardExternAtomLoc :: loc
  , wildcardExternAtomArgLoc :: loc
  , wildcardExternArgPos :: Position
  }
  deriving stock (Generic, Eq, Functor)
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions (WildcardInExtern loc) "wildcard_in_extern" 'S.Output

data UnconstrainedRuleVar loc
  = UnconstrainedRuleVar
  { urvRuleLoc :: loc
  , urvVarLoc :: loc
  , urvVarName :: Id
  }
  deriving stock (Generic, Eq, Functor)
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions (UnconstrainedRuleVar loc) "unconstrained_rule_var" 'S.Output

newtype DeadCode
  = DeadCode { unDeadCode :: NodeId }
  deriving stock (Generic, Eq)
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions DeadCode "dead_code" 'S.Output

newtype NoOutputRelation loc
  = NoOutputRelation loc
  deriving stock (Generic, Eq, Functor)
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions (NoOutputRelation loc) "no_output_relation" 'S.Output

data DeadInternalRelation loc
  = DeadInternalRelation loc Id
  deriving stock (Generic, Eq, Functor)
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions (DeadInternalRelation loc) "dead_internal_relation" 'S.Output

data ConflictingDefinitions loc
  = ConflictingDefinitions
  { cdFirstLoc :: loc
  , cdSecondLoc :: loc
  , cdName :: Id
  }
  deriving stock (Generic, Eq, Functor)
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions (ConflictingDefinitions loc) "conflicting_definitions" 'S.Output

data ConflictingDefinitionGroup loc
  = ConflictingDefinitionGroup
  { cdgName :: Id
  , cdgLocs :: NonEmpty loc
  } deriving stock (Eq, Functor)

data ExternUsedAsFact loc
  = ExternUsedAsFact
  { externAsFactLoc :: loc
  , externAsFactExternLoc :: loc
  , externAsFactName :: Id
  }
  deriving stock (Generic, Eq, Functor)
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions (ExternUsedAsFact loc) "extern_used_as_fact" 'S.Output

data ExternUsedAsRule loc
  = ExternUsedAsRule
  { externAsRuleLoc :: loc
  , externAsRuleExternLoc :: loc
  , externAsRuleName :: Id
  }
  deriving stock (Generic, Eq, Functor)
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions (ExternUsedAsRule loc) "extern_used_as_rule" 'S.Output

newtype CyclicNegation loc
  = CyclicNegation loc
  deriving stock (Generic, Eq, Functor)
  deriving anyclass S.Marshal
  deriving S.Fact via S.FactOptions (CyclicNegation loc) "cyclic_negation" 'S.Output

data SemanticAnalysis
  = SemanticAnalysis
  deriving S.Program
  via S.ProgramOptions SemanticAnalysis "semantic_analysis"
      '[ LitNumber
       , LitString
       , Var
       , Hole
       , Constraint
       , BinOp
       , Atom
       , AtomArg
       , Rule
       , RuleArg
       , RuleClause
       , Negation
       , DeclareType
       , ExternDefinition
       , InputRelation
       , OutputRelation
       , InternalRelation
       , Module
       , ModuleDecl
       , ScopedValue
       , UngroundedVar NodeId
       , WildcardInRuleHead NodeId
       , WildcardInFact NodeId
       , WildcardInConstraint NodeId
       , WildcardInBinOp NodeId
       , WildcardInExtern NodeId
       , UnconstrainedRuleVar NodeId
       , DeadCode
       , NoOutputRelation NodeId
       , DeadInternalRelation NodeId
       , ConflictingDefinitions NodeId
       , ExternUsedAsFact NodeId
       , ExternUsedAsRule NodeId
       , CyclicNegation NodeId
       ]

-- TODO: change to Vector when finished for performance
type Container = []

newtype SemanticInfo
  = SemanticInfo
  { deadCodeIds :: Container DeadCode
  } deriving Eq

data Result
  = Result
  { semanticInfo :: SemanticInfo
  , semanticErrors :: SemanticErrors NodeId
  }
  deriving Eq

data SemanticErrors loc
  = SemanticErrors
  { ungroundedVars :: Container (UngroundedVar loc)
  , wildcardsInFacts :: Container (WildcardInFact loc)
  , wildcardsInRuleHeads :: Container (WildcardInRuleHead loc)
  , wildcardsInConstraints :: Container (WildcardInConstraint loc)
  , wildcardsInBinOps :: Container (WildcardInBinOp loc)
  , wildcardsInExternAtoms :: Container (WildcardInExtern loc)
  , unconstrainedVars :: Container (UnconstrainedRuleVar loc)
  , deadInternalRelations :: Container (DeadInternalRelation loc)
  , noOutputRelations :: Container (NoOutputRelation loc)
  , conflictingDefinitions :: Container (ConflictingDefinitionGroup loc)
  , externsUsedAsFact :: Container (ExternUsedAsFact loc)
  , externsUsedAsRule :: Container (ExternUsedAsRule loc)
  , cyclicNegations :: Container (CyclicNegation loc)
  }
  deriving (Eq, Functor)

hasSemanticErrors :: Result -> Bool
hasSemanticErrors result =
  isNotNull ungroundedVars ||
  isNotNull wildcardsInFacts ||
  isNotNull wildcardsInRuleHeads ||
  isNotNull wildcardsInConstraints ||
  isNotNull wildcardsInBinOps ||
  isNotNull wildcardsInExternAtoms ||
  isNotNull unconstrainedVars ||
  isNotNull deadInternalRelations ||
  isNotNull noOutputRelations ||
  isNotNull conflictingDefinitions ||
  isNotNull externsUsedAsFact ||
  isNotNull cyclicNegations
  where
    errs = semanticErrors result
    isNotNull :: (SemanticErrors NodeId -> [a]) -> Bool
    isNotNull f = not . null $ f errs

analysis :: Word -> S.Handle SemanticAnalysis -> S.Analysis S.SouffleM IR.AST Result
analysis numCores prog = S.mkAnalysis addFacts run getFacts
  where
    addFacts :: IR.AST -> S.SouffleM ()
    addFacts ast = usingReaderT Nothing $ flip (zygo IR.getNodeIdF) ast $ \case
      IR.LitF nodeId lit -> do
        mScopeId <- ask
        for_ mScopeId $ \scopeId ->
          S.addFact prog $ ScopedValue scopeId nodeId
        case lit of
          IR.LNumber x ->
            S.addFact prog $ LitNumber nodeId x
          IR.LString x ->
            S.addFact prog $ LitString nodeId x
      IR.PWildcardF nodeId ->
        S.addFact prog $ Var nodeId (Id "_")
      IR.VarF nodeId var -> do
        S.addFact prog $ Var nodeId var
        mScopeId <- ask
        for_ mScopeId $ \scopeId ->
          S.addFact prog $ ScopedValue scopeId nodeId
      IR.HoleF nodeId ->
        S.addFact prog $ Hole nodeId
      IR.BinOpF nodeId arithOp (lhsId', lhsAction) (rhsId', rhsAction) -> do
        let textualOp = case arithOp of
              IR.Plus -> "+"
              IR.Minus -> "-"
              IR.Multiply -> "*"
              IR.Divide -> "/"
        mScopeId <- ask
        for_ mScopeId $ \scopeId ->
          S.addFact prog $ ScopedValue scopeId nodeId
        S.addFact prog $ BinOp nodeId textualOp lhsId' rhsId'
        lhsAction
        rhsAction
      IR.ConstraintF nodeId constraintOp (lhsId', lhsAction) (rhsId', rhsAction) -> do
        let textualOp = case constraintOp of
              IR.Equals -> "="
              IR.NotEquals -> "!="
              IR.LessThan -> "<"
              IR.LessOrEqual -> "<="
              IR.GreaterThan -> "<"
              IR.GreaterOrEqual -> "<="
        S.addFact prog $ Constraint nodeId textualOp lhsId' rhsId'
        lhsAction
        rhsAction
      IR.NotF nodeId (innerNodeId, action) -> do
        S.addFact prog $ Negation nodeId innerNodeId
        local (const $ Just nodeId) action
      IR.AtomF nodeId atom (unzip -> (argNodeIds, actions)) -> do
        S.addFact prog $ Atom nodeId atom
        mScopeId <- ask
        S.addFacts prog $ mapWithPos (AtomArg nodeId) argNodeIds

        for_ mScopeId $ \scopeId ->
          S.addFact prog $ ScopedValue scopeId nodeId

        let maybeAddScope =
              if isJust mScopeId
                then id
                else local (const $ Just nodeId)
        maybeAddScope $ sequence_ actions
      IR.RuleF nodeId rule ruleArgs ruleClauses -> do
        let (argNodeIds, argActions) = unzip ruleArgs
            (clauseNodeIds, clauseActions) = unzip ruleClauses
        S.addFact prog $ Rule nodeId rule
        S.addFacts prog $ mapWithPos (RuleArg nodeId) argNodeIds
        S.addFacts prog $ mapWithPos (RuleClause nodeId) clauseNodeIds
        local (const $ Just nodeId) $ do
          sequence_ argActions
          sequence_ clauseActions
      IR.ExternDefinitionF nodeId name _ _ -> do
        S.addFact prog $ ExternDefinition nodeId name
      IR.DeclareTypeF nodeId name _ usageMode -> do
        S.addFact prog $ DeclareType nodeId name

        case usageMode of
          IR.Input ->
            S.addFact prog $ InputRelation name
          IR.Output ->
            S.addFact prog $ OutputRelation name
          IR.InputOutput -> do
            S.addFact prog $ InputRelation name
            S.addFact prog $ OutputRelation name
          IR.Internal ->
            S.addFact prog $ InternalRelation name
      IR.ModuleF nodeId (unzip -> (declNodeIds, actions)) -> do
        S.addFact prog $ Module nodeId
        S.addFacts prog $ map (ModuleDecl nodeId) declNodeIds
        sequence_ actions

    run :: S.SouffleM ()
    run = do
      S.setNumThreads prog (fromIntegral numCores)
      S.run prog

    getFacts :: S.SouffleM Result
    getFacts = do
      info <- SemanticInfo <$> S.getFacts prog
      errs <- SemanticErrors <$> S.getFacts prog
                             <*> S.getFacts prog
                             <*> S.getFacts prog
                             <*> S.getFacts prog
                             <*> S.getFacts prog
                             <*> S.getFacts prog
                             <*> S.getFacts prog
                             <*> S.getFacts prog
                             <*> S.getFacts prog
                             <*> (groupConflicts <$> S.getFacts prog)
                             <*> S.getFacts prog
                             <*> S.getFacts prog
                             <*> S.getFacts prog
      pure $ Result info errs

    mapWithPos :: (Word32 -> a -> b) -> [a] -> [b]
    mapWithPos g = zipWith g [0..]

groupConflicts :: Container (ConflictingDefinitions NodeId) -> Container (ConflictingDefinitionGroup NodeId)
groupConflicts conflicts =
  conflicts
  & sortWith sameConflict
  & groupBy ((==) `on` sameConflict)
  & map (\cg ->
    let firstConflict = head cg
        declName = cdName firstConflict
        locs = NE.cons (cdFirstLoc firstConflict) (map cdSecondLoc cg)
     in ConflictingDefinitionGroup declName locs
  )
  & nubOrdOn cdgName
  where
    sameConflict = cdName &&& cdFirstLoc

runAnalysis :: Word -> IR.AST -> IO Result
runAnalysis numCores ast = S.runSouffle SemanticAnalysis $ \case
  Nothing -> panic "Failed to load Souffle during semantic analysis!"
  Just prog -> S.execAnalysis (analysis numCores prog) ast

computeUsageMapping :: IR.AST -> Map Id IR.UsageMode
computeUsageMapping ast =
  Map.fromList pairs
  where
    pairs = flip cata ast $ \case
      IR.DeclareTypeF _ name _ mode ->
        one (name, mode)
      astf ->
        fold astf


================================================
FILE: lib/Eclair/AST/Codegen.hs
================================================
{-# LANGUAGE DerivingVia #-}

module Eclair.AST.Codegen
  ( CodegenM
  , Env(..)
  , runCodegen
  , toTerm
  , project
  , search
  , loop
  , parallel
  , merge
  , swap
  , purge
  , exit
  , noElemOf
  , if'
  ) where

import Prelude hiding (swap, project)
import Data.DList (DList)
import qualified Data.DList as DList
import qualified Eclair.RA.IR as RA
import qualified Eclair.AST.IR as AST
import Eclair.Common.Location
import Eclair.Common.Literal
import Eclair.Common.Operator
import Eclair.Common.Id
import Eclair.Common.Extern


type AST = AST.AST
type RA = RA.RA
type Relation = RA.Relation
type Alias = RA.Alias
type Variable = Id

type Column = Int

newtype Row = Row { unRow :: Int }
  deriving (Eq, Ord)

data InLoop
  = InLoop
  deriving Eq

data Env
  = Env
  { envRow :: Row
  , envExterns :: [Extern]
  , envLoopContext :: Maybe InLoop
  }

data LowerState
  = LowerState
  { nextNodeId :: Word32  -- NOTE: Unrelated to NodeIDs used in AST!
  -- Constraints that can be resolved directly, but need to be emitted later.
  , directConstraints :: CodegenM RA -> CodegenM RA
  -- We keep track of which alias + column maps to which variables for later
  -- generation of constraints.
  , varMapping :: DList (Alias, Column, Variable)
  }

newtype CodegenM a
  = CodegenM (RWS Env () LowerState a)
  deriving (Functor, Applicative, Monad, MonadReader Env, MonadState LowerState)
  via RWS Env () LowerState

runCodegen :: [Extern] -> CodegenM a -> a
runCodegen externs (CodegenM m) =
  -- NOTE: NodeId starts at 1, since module is manually created, and has NodeId 0
  let beginState = LowerState 1 id mempty
   in fst $ evalRWS m (Env (Row 0) externs Nothing) beginState

freshNodeId :: CodegenM NodeId
freshNodeId = do
  next <- gets nextNodeId
  modify $ \s -> s { nextNodeId = next + 1 }
  pure $ NodeId next

project :: Relation -> [CodegenM RA] -> CodegenM RA
project r terms = do
  nodeId <- freshNodeId
  (addDirectConstraints, mapping) <- gets (directConstraints &&& varMapping)
  let grouped =
        mapping
        & toList
        & sortOn varNameOf
        & groupBy ((==) `on` varNameOf)
      eqs = map toIndirectConstraint grouped
      addIndirectConstraints = foldl' (.) id eqs

  noElemConstraint <- lookupNoElemConstraint
  addDirectConstraints . addIndirectConstraints . noElemConstraint $
    RA.Project nodeId r <$> sequence terms
  where
    varNameOf (_, _, v) = v

    resolveAliasValue (a, col, _) = do
      nodeId <- freshNodeId
      pure $ RA.ColumnIndex nodeId a col

    toIndirectConstraint bindingGroup m = case bindingGroup of
      initial :| rest -> do
        aliasValue <- resolveAliasValue initial
        aliasValues <- traverse resolveAliasValue rest
        let constraints = map (if' Equals aliasValue) aliasValues
            wrapConstraints = foldl' (.) id constraints
        wrapConstraints m

    lookupNoElemConstraint = do
      loopCtx <- asks envLoopContext
      if loopCtx == Just InLoop
        then pure $ noElemOf (stripIdPrefixes r) terms
        else pure id

search :: Relation -> [AST] -> CodegenM RA -> CodegenM RA
search r terms inner = do
  nodeId <- freshNodeId
  -- Potentially reset var mapping when we reach the first search,
  -- this makes it possible to easily support multiple project statements.
  maybeResetSearchState

  a <- relationToAlias r
  zipWithM_ (\col t -> emitSearchTerm t a col) [0..] terms
  action <- local nextRow inner
  pure $ RA.Search nodeId r a [] action
  where
    nextRow s = s { envRow = Row . (+1) . unRow $ envRow s }
    maybeResetSearchState = do
      Row row <- asks envRow
      when (row == 0) $ do
        modify $ \s -> s { varMapping = mempty }

    emitSearchTerm :: AST -> Alias -> Column -> CodegenM ()
    emitSearchTerm ast a col = do
      -- Based on what the term resolved to, we might need to create additional
      -- constraints. Literals can directly be converted to a constraint, variables
      -- are solved at the end (in the project statement).
      case ast of
        AST.Lit {} ->
          addDirectConstraint ast a col
        AST.BinOp {} ->
          addDirectConstraint ast a col
        AST.PWildcard _ ->
          pass
        AST.Var _ v ->
          -- We append new constraints at the end.
          -- This will cause indices to always trigger as soon as possible,
          -- which narrows down the search space and speeds up the query.
          modify $ \s -> s { varMapping = DList.snoc (varMapping s) (a, col, v) }
        _ ->
          pass

    addDirectConstraint ast a col = do
      ra <- toTerm ast
      nodeId <- freshNodeId
      let aliasValue = RA.ColumnIndex nodeId a col
          constraint = if' Equals aliasValue ra
      modify $ \s -> s { directConstraints = directConstraints s . constraint }

loop :: [CodegenM RA] -> CodegenM RA
loop ms = local (\env -> env { envLoopContext = Just InLoop}) $ do
  nodeId <- freshNodeId
  RA.Loop nodeId <$> sequence ms

parallel :: [CodegenM RA] -> CodegenM RA
parallel = \case
  [m] -> m
  ms -> do
    nodeId <- freshNodeId
    RA.Par nodeId <$> sequence ms

merge :: Relation -> Relation -> CodegenM RA
merge from' to' = do
  nodeId <- freshNodeId
  pure $ RA.Merge nodeId from' to'

swap :: Relation -> Relation -> CodegenM RA
swap r1 r2 = do
  nodeId <- freshNodeId
  pure $ RA.Swap nodeId r1 r2

purge :: Relation -> CodegenM RA
purge r = do
  nodeId <- freshNodeId
  pure $ RA.Purge nodeId r

exit :: [Relation] -> CodegenM RA
exit rs = do
  nodeId <- freshNodeId
  pure $ RA.Exit nodeId rs

noElemOf :: Relation -> [CodegenM RA] -> CodegenM RA -> CodegenM RA
noElemOf r ts inner = do
  notElemNodeId <- freshNodeId
  ifNodeId <- freshNodeId
  cond <- RA.NotElem notElemNodeId r <$> sequence ts
  RA.If ifNodeId cond <$> inner

if' :: LogicalOp -> RA -> RA -> CodegenM RA -> CodegenM RA
if' op lhs rhs body = do
  cmpNodeId <- freshNodeId
  ifNodeId <- freshNodeId
  let cond = RA.CompareOp cmpNodeId op lhs rhs
  RA.If ifNodeId cond <$> body

toTerm :: AST -> CodegenM RA
toTerm ast = do
  nodeId <- freshNodeId
  case ast of
    AST.Lit _ (LNumber lit) ->
      pure $ RA.Lit nodeId lit
    AST.PWildcard _ ->
      pure $ RA.Undef nodeId
    AST.Var _ v -> do
      gets (find (\(_, _, v') -> v == v') . varMapping) >>= \case
        Just (alias, col, _) -> do
          pure $ RA.ColumnIndex nodeId alias col
        Nothing ->
          panic $ "Found ungrounded variable '" <> unId v <> "' in 'toTerm'!"
    AST.BinOp _ op lhs rhs -> do
      lhsTerm <- toTerm lhs
      rhsTerm <- toTerm rhs
      pure $ RA.PrimOp nodeId (RA.BuiltinOp op) [lhsTerm, rhsTerm]
    AST.Atom _ name args -> do
      RA.PrimOp nodeId (RA.ExternOp name) <$> traverse toTerm args
    _ ->
      panic "Unexpected case in 'toTerm'!"

relationToAlias :: Relation -> CodegenM Alias
relationToAlias r =
  asks (appendToId r . show . unRow . envRow)


================================================
FILE: lib/Eclair/AST/IR.hs
================================================
{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}

module Eclair.AST.IR
  ( AST(.., PWildcard)
  , ASTF(.., PWildcardF)
  , Value
  , Clause
  , Decl
  , Literal(..)
  , Type(..)
  , ArithmeticOp(..)
  , LogicalOp(..)
  , isEqualityOp
  , getNodeId
  , getNodeIdF
  , getExternDefs
  , UsageMode(..)
  , Attributes
  ) where

import Prettyprinter
import Eclair.Common.Id
import Eclair.Common.Operator
import Eclair.Common.Extern
import Eclair.Common.Literal
import Eclair.Common.Pretty
import Eclair.Common.Location

type Value = AST
type Clause = AST
type Decl = AST

data Type
  = U32
  | Str
  | TUnknown Int  -- NOTE: unification variable, only used internally!
  deriving (Eq, Ord, Show)

data UsageMode
  = Input
  | Output
  | InputOutput
  | Internal  -- This variant is only used internally (pun intended).
  deriving (Eq, Show)

-- Later this will also contain (Maybe StorageType), ...
type Attributes = UsageMode

-- NOTE: There is no explicit "AND" node, conjunctions are inlined into other
-- nodes (as lists of clauses).
data AST
  -- Expressions
  = Lit NodeId Literal
  | Var NodeId Id
  | Hole NodeId
  | BinOp NodeId ArithmeticOp AST AST
  -- Statements
  | Constraint NodeId LogicalOp AST AST
  | Rule NodeId Id [Value] [Clause]
  | Not NodeId Clause
  | Atom NodeId Id [Value]  -- Can be both a Datalog relation, or a externally defined function / constraint
  | ExternDefinition NodeId Id [(Maybe Id, Type)] (Maybe Type)
  | DeclareType NodeId Id [(Maybe Id, Type)] Attributes
  | Module NodeId [Decl]
  deriving (Eq, Show)

pattern PWildcard :: NodeId -> AST
pattern PWildcard nodeId
  = Var nodeId (Id "_")

makeBaseFunctor ''AST

pattern PWildcardF :: NodeId -> ASTF r
pattern PWildcardF nodeId
  = VarF nodeId (Id "_")

getNodeId :: AST -> NodeId
getNodeId = \case
  Module nodeId _ -> nodeId
  DeclareType nodeId _ _ _ -> nodeId
  ExternDefinition nodeId _ _ _ -> nodeId
  Rule nodeId _ _ _ -> nodeId
  Not nodeId _ -> nodeId
  Atom nodeId _ _ -> nodeId
  BinOp nodeId _ _ _ -> nodeId
  Constraint nodeId _ _ _ -> nodeId
  Lit nodeId _ -> nodeId
  Var nodeId _ -> nodeId
  Hole nodeId -> nodeId

getNodeIdF :: ASTF a -> NodeId
getNodeIdF = \case
  ModuleF nodeId _ -> nodeId
  DeclareTypeF nodeId _ _ _ -> nodeId
  ExternDefinitionF nodeId _ _ _ -> nodeId
  RuleF nodeId _ _ _ -> nodeId
  NotF nodeId _ -> nodeId
  AtomF nodeId _ _ -> nodeId
  BinOpF nodeId _ _ _ -> nodeId
  ConstraintF nodeId _ _ _ -> nodeId
  LitF nodeId _ -> nodeId
  VarF nodeId _ -> nodeId
  HoleF nodeId -> nodeId

getExternDefs :: AST -> [Extern]
getExternDefs = cata $ \case
  ExternDefinitionF _ name argTys mRetTy ->
    let extKind = if isJust mRetTy then ExternFunction else ExternConstraint
     in one $ Extern name (length argTys) extKind
  astf ->
    fold astf

instance Pretty Type where
  pretty = \case
    U32 -> "u32"
    Str -> "string"
    TUnknown x -> "ty" <> show x

data RenderPosition = TopLevel | Nested

instance Pretty AST where
  pretty ast = runReader (pretty' ast) TopLevel
    where
      pretty' = \case
        Lit _ x ->
          pure $ pretty x
        Var _ v ->
          pure $ pretty v
        Hole _ ->
          pure "?"
        BinOp _ op lhs rhs -> do
          lhs' <- pretty' lhs
          rhs' <- pretty' rhs
          pure $ parens $ lhs' <+> pretty op <+> rhs'
        Constraint _ op lhs rhs -> do
          lhs' <- pretty' lhs
          rhs' <- pretty' rhs
          pure $ lhs' <+> pretty op <+> rhs'
        Not _ clause ->
          ("!" <>) <$> pretty' clause
        Atom _ name values -> do
          end <- ask <&> \case
            TopLevel -> "."
            Nested -> mempty
          values' <- traverse pretty' values
          pure $ pretty name <> parens (withCommas values') <> end
        Rule _ name values clauses -> do
          (values', clauses') <- local (const Nested) $ do
            (,) <$> traverse pretty' values <*> traverse pretty' clauses
          let separators = replicate (length clauses - 1) "," ++ ["."]
          pure $ pretty name <> parens (withCommas values') <+> ":-" <> hardline <>
                indent 2 (vsep (zipWith (<>) clauses' separators))
        ExternDefinition _ name args mRetTy -> do
          let prettyRetTy = case mRetTy of
                Just retTy -> " " <> pretty retTy
                Nothing    -> mempty
          pure $ "@extern" <+> pretty name <> parens (withCommas $ map prettyArg args)
                    <> prettyRetTy <> "."
        DeclareType _ name tys attrs ->
          pure $ "@def"
            <+> pretty name
             <> parens (withCommas $ map prettyArg tys)
             <> prettyAttrs
             <> "."
          where
            prettyAttrs = case attrs of
              Internal -> ""
              Input -> " input"
              Output -> " output"
              InputOutput -> " input output"
        Module _ decls -> do
          decls' <- traverse pretty' decls
          pure $ vsep $ intersperse mempty decls'

      prettyArg (mName, ty) =
        maybe (pretty ty) (\fieldName -> pretty fieldName <> ":" <+> pretty ty) mName


================================================
FILE: lib/Eclair/AST/Lower.hs
================================================
module Eclair.AST.Lower
  ( compileToRA
  ) where

import Prelude hiding (swap, project)
import qualified Data.Graph as G
import qualified Data.Map as M
import Eclair.AST.Codegen
import Eclair.AST.IR hiding (Clause)
import Eclair.Common.Id
import Eclair.Common.Location (NodeId(..))
import qualified Eclair.RA.IR as RA
import Eclair.Common.Extern


type RA = RA.RA
type Relation = RA.Relation

compileToRA :: [Extern] -> AST -> RA
compileToRA externs ast =
  RA.Module (NodeId 0) $ concatMap processDecls sortedDecls
  where
    sortedDecls = scc ast

    processDecls :: [AST] -> [RA]
    processDecls = \case
      [Atom _ name values] -> runCodegen externs $
        let literals = map toTerm values
        in one <$> project name literals
      [Rule _ name args clauses] ->
        let terms = map toTerm args
        in runCodegen externs $ processSingleRule [name] name terms clauses
      rules ->  -- case for multiple mutually recursive rules
        let sccNames = rules & mapMaybe (\case
              Rule _ name _ _ -> Just name
              _ -> Nothing)
        in runCodegen externs $ processMultipleRules sccNames rules

    scc :: AST -> [[AST]]
    scc = \case
      Module _ decls -> map G.flattenSCC sortedDecls'
        where
          relevantDecls = filter isRelevant decls
          sortedDecls' = G.stronglyConnComp $ zipWith (\i d -> (d, i, refersTo d)) [0..] relevantDecls
          declLineMapping = M.fromListWith (<>) $ zipWith (\i d -> (nameFor d, [i])) [0..] relevantDecls
          isRelevant = \case
            Atom {} -> True
            Rule {} -> True
            Not {} -> True
            _ -> False
          nameFor = \case
            Atom _ name _ -> name
            Rule _ name _ _ -> name
            _ ->  unreachable  -- Because of "isRelevant"
          refersTo :: AST -> [Int]
          refersTo = \case
            Rule _ _ _ clauses ->
              -- If no top level facts are defined, no entry exists in declLine mapping -> default to -1
              concatMap (fromMaybe [-1] . flip M.lookup declLineMapping . dependsOn) $ filter isRelevant clauses
            _ -> []
          dependsOn = \case
            Atom _ name _ -> name
            Rule _ name _ _ -> name
            Not _ (Atom _ name _) -> name
            _ ->  unreachable  -- Because of "isRelevant"
      _ -> unreachable         -- Because rejected by parser
      where unreachable = panic "Unreachable code in 'scc'"

-- NOTE: These rules can all be evaluated in parallel inside the fixpoint loop
processMultipleRules :: [Relation] -> [AST] -> CodegenM [RA]
processMultipleRules sccNames rules = sequence stmts where
  stmts = mergeStmts <> [loop (purgeStmts <> ruleStmts <> [exitStmt] <> endLoopStmts)]
  mergeStmts = map (\r -> merge r (deltaRelationOf r)) uniqRelations
  purgeStmts = map (purge . newRelationOf) uniqRelations
  ruleStmts = [parallel $ map lowerRule rulesInfo]
  exitStmt = exit $ map newRelationOf uniqRelations
  endLoopStmts = concatMap toMergeAndSwapStmts uniqRelations
  toMergeAndSwapStmts r =
    let newRelation = newRelationOf r
        deltaRelation = deltaRelationOf r
     in [merge newRelation r, swap newRelation deltaRelation]
  rulesInfo = mapMaybe extractRuleData rules
  relations = map (\(r, _, _) -> r) rulesInfo
  uniqRelations = uniqOrderPreserving relations
  -- TODO: better func name
  lowerRule (r, map toTerm -> ts, clauses) =
    recursiveRuleToStmts sccNames r ts clauses

processSingleRule :: [Relation] -> Relation -> [CodegenM RA] -> [AST] -> CodegenM [RA]
processSingleRule sccNames relation terms clauses
  | isRecursive sccNames clauses =
    let deltaRelation = deltaRelationOf relation
        newRelation = newRelationOf relation
        stmts =
          [ merge relation deltaRelation
          , loop
            [ purge newRelation
            , ruleToStmt sccNames relation terms clauses
            , exit [newRelation]
            , merge newRelation relation
            , swap newRelation deltaRelation
            ]
          ]
      in sequence stmts
  | otherwise = one <$> ruleToStmt sccNames relation terms clauses

ruleToStmt :: [Relation] -> Relation -> [CodegenM RA] -> [AST] -> CodegenM RA
ruleToStmt sccNames relation terms clauses
  | isRecursive sccNames clauses =
    recursiveRuleToStmts sccNames relation terms clauses
  | otherwise = nestedSearchAndProject relation terms clauses mempty

recursiveRuleToStmts :: [Relation] -> Relation -> [CodegenM RA] -> [AST] -> CodegenM RA
recursiveRuleToStmts sccNames relation terms clauses =
  parallel $
    [ stmt
    | i <- [0..sccClauseCount - 1]
    , let sccAtom = maybeAt i sccAtoms
          clauses' = map (maybeToDeltaClause sccAtom) clauses
          sccAtoms' = drop (i + 1) sccAtoms
          stmt = nestedSearchAndProject newRelation terms clauses' sccAtoms'
    ]
  where
    newRelation = newRelationOf relation
    sccAtoms = clauses & filter isPartOfScc & mapMaybe (\case
      Atom _ name args -> Just (name, args)
      _ -> Nothing)
    sccClauseCount = length sccAtoms
    isPartOfScc = \case
      Atom _ name _  -> name `elem` sccNames
      _ -> False
    maybeToDeltaClause sccAtom = \case
      Atom nodeId clauseName args | sccAtom == Just (clauseName, args) ->
        Atom nodeId (deltaRelationOf clauseName) args
      clause -> clause

nestedSearchAndProject
  :: Relation
  -> [CodegenM RA]
  -> [AST]
  -> [(Relation, [AST])]
  -> CodegenM RA
nestedSearchAndProject intoRelation terms clauses sccAtoms =
  flip (foldr processRuleClause) clauses $
    addNegatedDeltaAtoms sccAtoms $
      project intoRelation terms
  where
    processRuleClause :: AST -> CodegenM RA -> CodegenM RA
    processRuleClause clause inner = case clause of
      Not _ (Atom _ clauseName args) -> do
        -- No starts with check here, since cyclic negation is not allowed.
        let terms' = map toTerm args
        noElemOf clauseName terms' inner

      Constraint _ op lhs rhs -> do
        lhsTerm <- toTerm lhs
        rhsTerm <- toTerm rhs
        if' op lhsTerm rhsTerm inner

      Atom _ clauseName args -> do
        externs <- asks envExterns
        let isExtern = isJust $ find (\(Extern name _ _) -> clauseName == name) externs
        if isExtern
          then do
            clause' <- toTerm clause
            zero <- toTerm (Lit (NodeId 0) $ LNumber 0)
            if' NotEquals clause' zero inner
          else search clauseName args inner
      _ ->
        panic "Unexpected rule clause in 'nestedSearchAndProject'!"

    addNegatedDeltaAtoms =
      foldr (\(clauseName, args) wrapper -> wrapper . addNegatedDeltaAtom clauseName args) id

addNegatedDeltaAtom :: Relation -> [AST] -> CodegenM RA -> CodegenM RA
addNegatedDeltaAtom clauseName args =
  noElemOf (deltaRelationOf clauseName) (map toTerm args)

isRecursive :: [Relation] -> [AST] -> Bool
isRecursive sccNames clauses =
  let atomNames = flip mapMaybe clauses $ \case
        Atom _ name _ -> Just name
        _ -> Nothing
   in any (`elem` atomNames) sccNames

extractRuleData :: AST -> Maybe (Relation, [AST], [AST])
extractRuleData = \case
  Rule _ name args clauses -> Just (name, args, clauses)
  _ -> Nothing

newRelationOf, deltaRelationOf :: Relation -> Relation
deltaRelationOf = prependToId deltaPrefix
newRelationOf = prependToId newPrefix


================================================
FILE: lib/Eclair/AST/Transforms/ConstantFolding.hs
================================================
module Eclair.AST.Transforms.ConstantFolding
  ( transform
  ) where

import Eclair.AST.IR
import Eclair.Transform

transform :: Transform AST AST
transform = pureTransform $ cata $ \case
  BinOpF nodeId op (Lit _ (LNumber lhs)) (Lit _ (LNumber rhs)) ->
    let opFn = case op of
          Plus -> (+)
          Minus -> (-)
          Multiply -> (*)
          Divide -> div
    in Lit nodeId $ LNumber $ opFn lhs rhs
  ast ->
    embed ast


================================================
FILE: lib/Eclair/AST/Transforms/DeadCodeElimination.hs
================================================
module Eclair.AST.Transforms.DeadCodeElimination
  ( tr
Download .txt
gitextract_d5o1d0tz/

├── .dockerignore
├── .ghci
├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       ├── build.yml
│       └── ci.yml
├── .gitignore
├── .hlint.yaml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── cabal.project
├── cbits/
│   └── semantic_analysis.dl
├── docs/
│   ├── architecture_choices.md
│   └── getting_started.md
├── eclair-lang.cabal
├── hie.yaml
├── lib/
│   ├── Eclair/
│   │   ├── AST/
│   │   │   ├── Analysis.hs
│   │   │   ├── Codegen.hs
│   │   │   ├── IR.hs
│   │   │   ├── Lower.hs
│   │   │   ├── Transforms/
│   │   │   │   ├── ConstantFolding.hs
│   │   │   │   ├── DeadCodeElimination.hs
│   │   │   │   ├── NormalizeRules.hs
│   │   │   │   ├── RemoveAliases.hs
│   │   │   │   └── ReplaceStrings.hs
│   │   │   └── Transforms.hs
│   │   ├── ArgParser.hs
│   │   ├── Common/
│   │   │   ├── Config.hs
│   │   │   ├── Extern.hs
│   │   │   ├── Id.hs
│   │   │   ├── Literal.hs
│   │   │   ├── Location.hs
│   │   │   ├── Operator.hs
│   │   │   └── Pretty.hs
│   │   ├── Comonads.hs
│   │   ├── EIR/
│   │   │   ├── IR.hs
│   │   │   ├── Lower/
│   │   │   │   ├── API.hs
│   │   │   │   ├── Codegen.hs
│   │   │   │   └── Externals.hs
│   │   │   └── Lower.hs
│   │   ├── Error.hs
│   │   ├── JSON.hs
│   │   ├── LLVM/
│   │   │   ├── Allocator/
│   │   │   │   ├── Arena.hs
│   │   │   │   ├── Common.hs
│   │   │   │   ├── Malloc.hs
│   │   │   │   └── Page.hs
│   │   │   ├── BTree/
│   │   │   │   ├── Bounds.hs
│   │   │   │   ├── Compare.hs
│   │   │   │   ├── Create.hs
│   │   │   │   ├── Destroy.hs
│   │   │   │   ├── Find.hs
│   │   │   │   ├── Insert.hs
│   │   │   │   ├── Iterator.hs
│   │   │   │   ├── Size.hs
│   │   │   │   └── Types.hs
│   │   │   ├── BTree.hs
│   │   │   ├── Codegen.hs
│   │   │   ├── Config.hs
│   │   │   ├── Externals.hs
│   │   │   ├── Hash.hs
│   │   │   ├── HashMap.hs
│   │   │   ├── Metadata.hs
│   │   │   ├── Symbol.hs
│   │   │   ├── SymbolTable.hs
│   │   │   ├── Table.hs
│   │   │   ├── Template.hs
│   │   │   └── Vector.hs
│   │   ├── LSP/
│   │   │   ├── Handlers/
│   │   │   │   ├── Diagnostics.hs
│   │   │   │   ├── DocumentHighlight.hs
│   │   │   │   └── Hover.hs
│   │   │   ├── Handlers.hs
│   │   │   ├── JSON.hs
│   │   │   ├── Monad.hs
│   │   │   ├── Types.hs
│   │   │   └── VFS.hs
│   │   ├── LSP.hs
│   │   ├── Parser.hs
│   │   ├── RA/
│   │   │   ├── Codegen.hs
│   │   │   ├── IR.hs
│   │   │   ├── IndexSelection.hs
│   │   │   ├── Lower.hs
│   │   │   ├── Transforms/
│   │   │   │   └── HoistConstraints.hs
│   │   │   └── Transforms.hs
│   │   ├── Souffle/
│   │   │   └── IR.hs
│   │   ├── Transform.hs
│   │   └── TypeSystem.hs
│   ├── Eclair.hs
│   └── Prelude.hs
├── src/
│   └── eclair/
│       └── Main.hs
└── tests/
    ├── .gitignore
    ├── ast_transforms/
    │   ├── constant_folding.eclair
    │   ├── copy_propagation.eclair
    │   ├── dead_code_elimination.eclair
    │   ├── remove_contradictions.eclair
    │   └── shift_assignments.eclair
    ├── check.sh
    ├── eclair/
    │   ├── Test/
    │   │   └── Eclair/
    │   │       ├── ArgParserSpec.hs
    │   │       ├── JSONSpec.hs
    │   │       ├── LLVM/
    │   │       │   ├── Allocator/
    │   │       │   │   ├── MallocSpec.hs
    │   │       │   │   ├── PageSpec.hs
    │   │       │   │   └── Utils.hs
    │   │       │   ├── BTreeSpec.hs
    │   │       │   ├── HashMapSpec.hs
    │   │       │   ├── HashSpec.hs
    │   │       │   ├── SymbolSpec.hs
    │   │       │   ├── SymbolTableSpec.hs
    │   │       │   ├── SymbolUtils.hs
    │   │       │   └── VectorSpec.hs
    │   │       ├── LSP/
    │   │       │   ├── HandlersSpec.hs
    │   │       │   └── JSONSpec.hs
    │   │       └── RA/
    │   │           └── IndexSelectionSpec.hs
    │   ├── fixtures/
    │   │   └── lsp/
    │   │       ├── document_highlight.eclair
    │   │       ├── hover.eclair
    │   │       ├── invalid_syntax.eclair
    │   │       ├── semantic_errors.eclair
    │   │       ├── type_errors.eclair
    │   │       └── unparsable.eclair
    │   └── test.hs
    ├── end_to_end/
    │   ├── compile_and_run_native.eclair
    │   ├── compile_and_run_wasm.eclair
    │   └── compile_and_run_with_extern.eclair
    ├── hello.eclair
    ├── lit.cfg
    ├── lowering/
    │   ├── arithmetic.eclair
    │   ├── clause_with_same_vars.eclair
    │   ├── comparisons.eclair
    │   ├── different_types.eclair
    │   ├── extern_definitions.eclair
    │   ├── multiple_clauses_same_name.eclair
    │   ├── multiple_rule_clauses.eclair
    │   ├── mutually_recursive_rules.eclair
    │   ├── negation.eclair
    │   ├── negation_with_wildcards.eclair
    │   ├── no_top_level_facts.eclair
    │   ├── recursive_mix_of_rules.eclair
    │   ├── single_non_recursive_rule.eclair
    │   ├── single_recursive_rule.eclair
    │   ├── stratification.eclair
    │   ├── top_level_facts.eclair
    │   └── wasm_codegen.eclair
    ├── parser/
    │   ├── error_recovery.eclair
    │   ├── file_not_found.eclair
    │   └── valid.eclair
    ├── runtime/
    │   ├── hashmap_test.eclair
    │   ├── symbol_table_test.eclair
    │   └── vector_test.eclair
    ├── semantic_analysis/
    │   ├── cyclic_negation.eclair
    │   ├── dead_internal_relation.eclair
    │   ├── invalid_extern_usage.eclair
    │   ├── invalid_options_usage.eclair
    │   ├── invalid_wildcard_usage.eclair
    │   ├── no_output_relations.eclair
    │   ├── unconstrained_variables.eclair
    │   ├── ungrounded_variables.eclair
    │   ├── ungrounded_variables_arithmetic.eclair
    │   ├── ungrounded_variables_comparisons.eclair
    │   └── ungrounded_variables_negations.eclair
    ├── string_support/
    │   ├── encode_decode_string.eclair
    │   ├── encode_decode_string_native.eclair
    │   └── encode_decode_string_wasm.eclair
    ├── transpilation/
    │   └── souffle.eclair
    ├── typesystem/
    │   ├── arg_count_mismatch.eclair
    │   ├── arithmetic.eclair
    │   ├── comparisons.eclair
    │   ├── duplicate_type_declarations.eclair
    │   ├── extern_definitions.eclair
    │   ├── negation.eclair
    │   ├── no_rules_for_type.eclair
    │   ├── type_mismatch_in_rule.eclair
    │   ├── type_mismatch_in_rule_body.eclair
    │   ├── type_mismatch_in_rule_head.eclair
    │   ├── type_mismatch_top_level_atoms.eclair
    │   ├── typed_holes.eclair
    │   ├── unification_failure.eclair
    │   ├── unknown_atom_in_rule_body.eclair
    │   ├── unknown_atom_in_rule_head.eclair
    │   ├── unknown_atoms.eclair
    │   ├── unknown_top_level_atoms.eclair
    │   └── valid.eclair
    └── utils/
        └── extract_snippet
Condensed preview — 183 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (938K chars).
[
  {
    "path": ".dockerignore",
    "chars": 165,
    "preview": ".direnv/\n.git/\n.github/\ndist/\ndist-newstyle/\nresult/\nDockerfile\n.dockerignore\n.envrc\n.ghci\n.gitignore\n./*.ll\n./*.o\n./*.a"
  },
  {
    "path": ".ghci",
    "chars": 14,
    "preview": ":set prompt >\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 19,
    "preview": "github: luc-tielen\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 781,
    "preview": "name: \"Build\"\non: [push, pull_request]\njobs:\n  build:\n    strategy:\n      matrix:\n        os: [ubuntu-latest]\n    runs-o"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 438,
    "preview": "name: lint\non:\n  pull_request:\n  push:\n    branches:\n      - main\n      - \"releases/*\"\njobs:\n  hlint:\n    runs-on: ubunt"
  },
  {
    "path": ".gitignore",
    "chars": 212,
    "preview": "dist-newstyle/\ndist/\n.direnv/\n.devcontainer/\ncabal.project.local*\n\n*.ll\n*.bc\n*.o\n*.a\n*.s\n/*.wasm\n/*.dl\n/*.eclair\n/*.js\n\n"
  },
  {
    "path": ".hlint.yaml",
    "chars": 98776,
    "preview": "# HLint configuration file\n# https://github.com/ndmitchell/hlint\n##########################\n\n# This file contains a temp"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 2074,
    "preview": "# Changelog\n\nAll notable changes to this project (as seen by library users) will be documented in this file.\nThe CHANGEL"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 4406,
    "preview": "# Code of Conduct\n\nContact: luc.tielen@gmail.com\n\n## Why have a Code of Conduct?\n\nAs contributors and maintainers of thi"
  },
  {
    "path": "Dockerfile",
    "chars": 2415,
    "preview": "FROM primordus/souffle-ubuntu:2.3\nARG LLVM_VERSION=17\n\nSHELL [ \"/bin/bash\", \"-c\" ]\n\n# install packages\nRUN echo 'tzdata "
  },
  {
    "path": "LICENSE",
    "chars": 1517,
    "preview": "Copyright Luc Tielen (c) 2022\n\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\n"
  },
  {
    "path": "Makefile",
    "chars": 282,
    "preview": "build: configure\n\t@cabal build\n\nconfigure:\n\t@cabal configure -f eclair-debug --enable-tests\n\nclean:\n\t@cabal clean\n\ntest:"
  },
  {
    "path": "README.md",
    "chars": 5482,
    "preview": "<picture>\n  <source media=\"(prefers-color-scheme: dark)\" srcset=\"./logo_dark.png\"/>\n  <img\n    src=\"./logo_light.png\"\n  "
  },
  {
    "path": "cabal.project",
    "chars": 467,
    "preview": "packages: .\n\nsource-repository-package\n    type: git\n    location: https://github.com/luc-tielen/llvm-codegen.git\n    ta"
  },
  {
    "path": "cbits/semantic_analysis.dl",
    "chars": 17796,
    "preview": "// Input facts\n.decl lit_number(node_id: unsigned, value: unsigned)\n.decl lit_string(node_id: unsigned, value: symbol)\n."
  },
  {
    "path": "docs/architecture_choices.md",
    "chars": 9309,
    "preview": "# Architecture choices\n\nThis document contains all the high level choices that have been made with\nregards to the archit"
  },
  {
    "path": "docs/getting_started.md",
    "chars": 4886,
    "preview": "# Getting started\n\nEclair requires a Haskell toolchain, Souffle 2.3 and LLVM 14 to be installed on\nyour system.\n\nIf you "
  },
  {
    "path": "eclair-lang.cabal",
    "chars": 10499,
    "preview": "cabal-version:      2.2\nname:               eclair-lang\nversion:            0.2.0\nsynopsis:\n  Eclair: an experimental an"
  },
  {
    "path": "hie.yaml",
    "chars": 472,
    "preview": "cradle:\n  cabal:\n    - path: \"lib\"\n      component: \"lib:eclair-lang\"\n\n    - path: \"src/eclair/Main.hs\"\n      component:"
  },
  {
    "path": "lib/Eclair/AST/Analysis.hs",
    "chars": 17092,
    "preview": "{-# LANGUAGE UndecidableInstances #-}\n\nmodule Eclair.AST.Analysis\n  ( Result(..)\n  , SemanticInfo(..)\n  , SemanticErrors"
  },
  {
    "path": "lib/Eclair/AST/Codegen.hs",
    "chars": 6860,
    "preview": "{-# LANGUAGE DerivingVia #-}\n\nmodule Eclair.AST.Codegen\n  ( CodegenM\n  , Env(..)\n  , runCodegen\n  , toTerm\n  , project\n "
  },
  {
    "path": "lib/Eclair/AST/IR.hs",
    "chars": 5084,
    "preview": "{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}\n\nmodule Eclair.AST.IR\n  ( AST(.., PWildcard)\n  , ASTF(.., PWildcardF"
  },
  {
    "path": "lib/Eclair/AST/Lower.hs",
    "chars": 7311,
    "preview": "module Eclair.AST.Lower\n  ( compileToRA\n  ) where\n\nimport Prelude hiding (swap, project)\nimport qualified Data.Graph as "
  },
  {
    "path": "lib/Eclair/AST/Transforms/ConstantFolding.hs",
    "chars": 441,
    "preview": "module Eclair.AST.Transforms.ConstantFolding\n  ( transform\n  ) where\n\nimport Eclair.AST.IR\nimport Eclair.Transform\n\ntran"
  },
  {
    "path": "lib/Eclair/AST/Transforms/DeadCodeElimination.hs",
    "chars": 714,
    "preview": "module Eclair.AST.Transforms.DeadCodeElimination\n  ( transform\n  ) where\n\nimport Eclair.Transform\nimport Eclair.AST.Anal"
  },
  {
    "path": "lib/Eclair/AST/Transforms/NormalizeRules.hs",
    "chars": 905,
    "preview": "module Eclair.AST.Transforms.NormalizeRules\n  ( transform\n  ) where\n\nimport Data.List (partition)\nimport Eclair.Transfor"
  },
  {
    "path": "lib/Eclair/AST/Transforms/RemoveAliases.hs",
    "chars": 2800,
    "preview": "module Eclair.AST.Transforms.RemoveAliases\n  ( transform\n  ) where\n\nimport qualified Data.Map as M\nimport Eclair.Transfo"
  },
  {
    "path": "lib/Eclair/AST/Transforms/ReplaceStrings.hs",
    "chars": 1114,
    "preview": "module Eclair.AST.Transforms.ReplaceStrings\n  ( StringMap\n  , transform\n  ) where\n\nimport qualified Data.Map as Map\nimpo"
  },
  {
    "path": "lib/Eclair/AST/Transforms.hs",
    "chars": 1294,
    "preview": "module Eclair.AST.Transforms\n  ( simplify\n  , ReplaceStrings.StringMap\n  ) where\n\nimport Eclair.AST.IR\nimport Eclair.AST"
  },
  {
    "path": "lib/Eclair/ArgParser.hs",
    "chars": 2468,
    "preview": "module Eclair.ArgParser\n  ( parseArgs\n  , parser\n  , Config(..)\n  , CompileConfig(..)\n  , EmitKind(..)\n  , Target(..)\n  "
  },
  {
    "path": "lib/Eclair/Common/Config.hs",
    "chars": 739,
    "preview": "module Eclair.Common.Config\n  ( EmitKind(..)\n  , CompileConfig(..)\n  , Target(..)\n  , Config(..)\n  ) where\n\ndata EmitKin"
  },
  {
    "path": "lib/Eclair/Common/Extern.hs",
    "chars": 240,
    "preview": "module Eclair.Common.Extern\n  ( Extern(..)\n  , ExternKind(..)\n  ) where\n\nimport Eclair.Common.Id\n\n\ndata Extern = Extern "
  },
  {
    "path": "lib/Eclair/Common/Id.hs",
    "chars": 1120,
    "preview": "module Eclair.Common.Id\n  ( Id(..)\n  , prependToId\n  , appendToId\n  , startsWithId\n  , stripIdPrefixes\n  , startsWithIdP"
  },
  {
    "path": "lib/Eclair/Common/Literal.hs",
    "chars": 285,
    "preview": "module Eclair.Common.Literal\n  ( Literal(..)\n  ) where\nimport Prettyprinter (Pretty (pretty), dquotes)\n\ndata Literal\n  ="
  },
  {
    "path": "lib/Eclair/Common/Location.hs",
    "chars": 3027,
    "preview": "module Eclair.Common.Location\n  ( NodeId(..)\n  , Span(..)\n  , SpanMap(..)\n  , SourcePos(..)\n  , SourceSpan(..)\n  , inser"
  },
  {
    "path": "lib/Eclair/Common/Operator.hs",
    "chars": 1113,
    "preview": "module Eclair.Common.Operator\n  ( ArithmeticOp(..)\n  , LogicalOp(..)\n  , isEqualityOp\n  , invertLogicalOp\n  ) where\n\nimp"
  },
  {
    "path": "lib/Eclair/Common/Pretty.hs",
    "chars": 663,
    "preview": "module Eclair.Common.Pretty\n  ( module Eclair.Common.Pretty\n  , module Prettyprinter\n  , module Prettyprinter.Render.Tex"
  },
  {
    "path": "lib/Eclair/Comonads.hs",
    "chars": 522,
    "preview": "module Eclair.Comonads\n  ( module Eclair.Comonads\n  ) where\n\n\ndata Triple a b c\n  = Triple\n  { tFst :: a\n  , tSnd :: b\n "
  },
  {
    "path": "lib/Eclair/EIR/IR.hs",
    "chars": 5581,
    "preview": "{-# LANGUAGE TemplateHaskell #-}\n\nmodule Eclair.EIR.IR\n  ( EIR(..)\n  , EIRF(..)\n  , Relation\n  , Op(..)\n  , LogicalOp(.."
  },
  {
    "path": "lib/Eclair/EIR/Lower/API.hs",
    "chars": 13333,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\n\nmodule Eclair.EIR.Lower.API\n  ( CodegenInOutT\n  , mkInOutState\n  , cod"
  },
  {
    "path": "lib/Eclair/EIR/Lower/Codegen.hs",
    "chars": 5731,
    "preview": "module Eclair.EIR.Lower.Codegen\n  ( CodegenT\n  , runCodegenM\n  , LowerState(..)\n  , Table(..)\n  , Externals(..)\n  , labe"
  },
  {
    "path": "lib/Eclair/EIR/Lower/Externals.hs",
    "chars": 2885,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\n\nmodule Eclair.EIR.Lower.Externals\n  ( createExternals\n  ) where\n\nimpor"
  },
  {
    "path": "lib/Eclair/EIR/Lower.hs",
    "chars": 12570,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\n\nmodule Eclair.EIR.Lower\n  ( compileToLLVM\n  ) where\n\nimport Prelude hi"
  },
  {
    "path": "lib/Eclair/Error.hs",
    "chars": 27100,
    "preview": "{-# LANGUAGE MultiParamTypeClasses #-}\n{-# OPTIONS_GHC -fno-warn-orphans #-}\n\nmodule Eclair.Error\n  ( EclairError(..)\n  "
  },
  {
    "path": "lib/Eclair/JSON.hs",
    "chars": 1877,
    "preview": "{-# LANGUAGE LinearTypes, MagicHash #-}\n-- | Helper module encoding Haskell values as JSON.\n--   Only a limited set of f"
  },
  {
    "path": "lib/Eclair/LLVM/Allocator/Arena.hs",
    "chars": 3023,
    "preview": "{-# LANGUAGE GADTs #-}\n\nmodule Eclair.LLVM.Allocator.Arena\n  ( Arena\n  , allocator\n  ) where\n\nimport Prelude hiding (voi"
  },
  {
    "path": "lib/Eclair/LLVM/Allocator/Common.hs",
    "chars": 5932,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\n{-# LANGUAGE GADTs #-}\n\nmodule Eclair.LLVM.Allocator.Common\n  ( Allocat"
  },
  {
    "path": "lib/Eclair/LLVM/Allocator/Malloc.hs",
    "chars": 583,
    "preview": "module Eclair.LLVM.Allocator.Malloc\n  ( Malloc\n  , allocator\n  ) where\n\nimport Eclair.LLVM.Allocator.Common\nimport Eclai"
  },
  {
    "path": "lib/Eclair/LLVM/Allocator/Page.hs",
    "chars": 1441,
    "preview": "module Eclair.LLVM.Allocator.Page\n  ( Page\n  , allocator\n  , roundToNearestPageSize  -- for testing only\n  ) where\n\nimpo"
  },
  {
    "path": "lib/Eclair/LLVM/BTree/Bounds.hs",
    "chars": 5531,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\n\nmodule Eclair.LLVM.BTree.Bounds\n  ( mkLinearSearchLowerBound\n  , mkLin"
  },
  {
    "path": "lib/Eclair/LLVM/BTree/Compare.hs",
    "chars": 1598,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\n\nmodule Eclair.LLVM.BTree.Compare\n  ( mkCompare\n  ) where\n\nimport Eclai"
  },
  {
    "path": "lib/Eclair/LLVM/BTree/Create.hs",
    "chars": 2234,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\n\nmodule Eclair.LLVM.BTree.Create\n  ( mkNodeNew\n  , mkBtreeInit\n  , mkBt"
  },
  {
    "path": "lib/Eclair/LLVM/BTree/Destroy.hs",
    "chars": 1603,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\n\nmodule Eclair.LLVM.BTree.Destroy\n  ( mkBtreeDestroy\n  , mkBtreeClear\n "
  },
  {
    "path": "lib/Eclair/LLVM/BTree/Find.hs",
    "chars": 2320,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\n\nmodule Eclair.LLVM.BTree.Find\n  ( mkBtreeContains\n  , mkBtreeFind\n  ) "
  },
  {
    "path": "lib/Eclair/LLVM/BTree/Insert.hs",
    "chars": 15645,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\n\nmodule Eclair.LLVM.BTree.Insert\n  ( mkBtreeInsertValue\n  , mkBtreeInse"
  },
  {
    "path": "lib/Eclair/LLVM/BTree/Iterator.hs",
    "chars": 5177,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\n\nmodule Eclair.LLVM.BTree.Iterator\n  ( mkIteratorInit\n  , mkIteratorIni"
  },
  {
    "path": "lib/Eclair/LLVM/BTree/Size.hs",
    "chars": 1984,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\n\nmodule Eclair.LLVM.BTree.Size\n  ( mkNodeCountEntries\n  , mkBtreeIsEmpt"
  },
  {
    "path": "lib/Eclair/LLVM/BTree/Types.hs",
    "chars": 5520,
    "preview": "module Eclair.LLVM.BTree.Types\n  ( Meta(..)\n  , Types(..)\n  , SearchIndex\n  , SearchType(..)\n  , Sizes(..)\n  , CGState(."
  },
  {
    "path": "lib/Eclair/LLVM/BTree.hs",
    "chars": 6053,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\n{-# LANGUAGE FlexibleContexts, ScopedTypeVariables #-}\n\nmodule Eclair.L"
  },
  {
    "path": "lib/Eclair/LLVM/Codegen.hs",
    "chars": 2483,
    "preview": "{-# LANGUAGE RoleAnnotations, PolyKinds #-}\n\nmodule Eclair.LLVM.Codegen\n  ( module Eclair.LLVM.Codegen\n  , module Eclair"
  },
  {
    "path": "lib/Eclair/LLVM/Config.hs",
    "chars": 1169,
    "preview": "module Eclair.LLVM.Config\n  ( Config(..)\n  , ConfigT\n  , runConfigT\n  , MonadConfig(..)\n  ) where\n\nimport qualified LLVM"
  },
  {
    "path": "lib/Eclair/LLVM/Externals.hs",
    "chars": 351,
    "preview": "module Eclair.LLVM.Externals\n  ( Externals(..)\n  ) where\n\nimport Eclair.LLVM.Codegen (Operand)\n\n-- Functions that are de"
  },
  {
    "path": "lib/Eclair/LLVM/Hash.hs",
    "chars": 2021,
    "preview": "{-# LANGUAGE TypeApplications, UndecidableInstances, TypeOperators, DefaultSignatures #-}\n\nmodule Eclair.LLVM.Hash\n  ( H"
  },
  {
    "path": "lib/Eclair/LLVM/HashMap.hs",
    "chars": 7414,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\nmodule Eclair.LLVM.HashMap\n  ( HashMap(..)\n  , Types(..)\n  , codegen\n  "
  },
  {
    "path": "lib/Eclair/LLVM/Metadata.hs",
    "chars": 962,
    "preview": "module Eclair.LLVM.Metadata\n  ( Metadata(..)\n  , mkMeta\n  , getIndex\n  , getNumColumns\n  ) where\n\nimport Eclair.RA.Index"
  },
  {
    "path": "lib/Eclair/LLVM/Symbol.hs",
    "chars": 3103,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\nmodule Eclair.LLVM.Symbol\n  ( Symbol(..)\n  , codegen\n  , sizeOf\n  , dat"
  },
  {
    "path": "lib/Eclair/LLVM/SymbolTable.hs",
    "chars": 5430,
    "preview": "{-# OPTIONS_GHC -Wno-unused-top-binds #-}\n{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\n\nmodule Eclair.LLVM.SymbolTab"
  },
  {
    "path": "lib/Eclair/LLVM/Table.hs",
    "chars": 1240,
    "preview": "module Eclair.LLVM.Table\n  ( Table(..)\n  , IteratorParams(..)\n  ) where\n\nimport Eclair.LLVM.Codegen (Operand, Type, Temp"
  },
  {
    "path": "lib/Eclair/LLVM/Template.hs",
    "chars": 6469,
    "preview": "{-# LANGUAGE UndecidableInstances, FunctionalDependencies #-}\n\nmodule Eclair.LLVM.Template\n  ( TemplateT\n  , Template\n  "
  },
  {
    "path": "lib/Eclair/LLVM/Vector.hs",
    "chars": 7155,
    "preview": "{-# OPTIONS_GHC -Wno-unused-top-binds #-}\n{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\n\nmodule Eclair.LLVM.Vector\n  "
  },
  {
    "path": "lib/Eclair/LSP/Handlers/Diagnostics.hs",
    "chars": 1884,
    "preview": "module Eclair.LSP.Handlers.Diagnostics\n  ( diagnosticsHandler\n  , DiagnosticSource(..)\n  , Severity(..)\n  , Diagnostic(."
  },
  {
    "path": "lib/Eclair/LSP/Handlers/DocumentHighlight.hs",
    "chars": 2442,
    "preview": "module Eclair.LSP.Handlers.DocumentHighlight\n  ( documentHighlightHandler\n  , DocHLResult(..)\n  ) where\n\nimport Eclair\ni"
  },
  {
    "path": "lib/Eclair/LSP/Handlers/Hover.hs",
    "chars": 2473,
    "preview": "module Eclair.LSP.Handlers.Hover\n  ( hoverHandler\n  , HoverResult(..)\n  ) where\n\nimport Eclair\nimport Eclair.Error\nimpor"
  },
  {
    "path": "lib/Eclair/LSP/Handlers.hs",
    "chars": 284,
    "preview": "module Eclair.LSP.Handlers\n  ( module Eclair.LSP.Handlers.Hover\n  , module Eclair.LSP.Handlers.Diagnostics\n  , module Ec"
  },
  {
    "path": "lib/Eclair/LSP/JSON.hs",
    "chars": 4480,
    "preview": "module Eclair.LSP.JSON\n  ( responseToJSON\n  , diagnosticToJSON\n  , diagnosticSourceToJSON\n  , severityToJSON\n  , srcSpan"
  },
  {
    "path": "lib/Eclair/LSP/Monad.hs",
    "chars": 1315,
    "preview": "module Eclair.LSP.Monad\n  ( LspM\n  , runLSP\n  , liftLSP\n  , getParams\n  , module Eclair.LSP.VFS\n  , posToOffset\n  ) wher"
  },
  {
    "path": "lib/Eclair/LSP/Types.hs",
    "chars": 463,
    "preview": "module Eclair.LSP.Types\n  ( Command(..)\n  , Response(..)\n  ) where\n\nimport Eclair.Common.Location\nimport Eclair.LSP.Hand"
  },
  {
    "path": "lib/Eclair/LSP/VFS.hs",
    "chars": 1412,
    "preview": "module Eclair.LSP.VFS\n  ( VFS\n  , VFST\n  , runVFST\n  , getVfsVar\n  , vfsSetFile\n  , vfsLookupFile\n  , unsafeReadFromVFS\n"
  },
  {
    "path": "lib/Eclair/LSP.hs",
    "chars": 1941,
    "preview": "module Eclair.LSP\n  ( lspMain\n  ) where\n\nimport Eclair (Parameters(..))\nimport Eclair.LSP.Handlers\nimport Eclair.LSP.Mon"
  },
  {
    "path": "lib/Eclair/Parser.hs",
    "chars": 10780,
    "preview": "module Eclair.Parser\n  ( parseFile\n  , parseText\n  , Parser\n  , ParseError\n  , CustomParseErr\n  , ParsingError(..)\n  ) w"
  },
  {
    "path": "lib/Eclair/RA/Codegen.hs",
    "chars": 11328,
    "preview": "module Eclair.RA.Codegen\n  ( CodegenM\n  , runCodegen\n  , LowerState(..)\n  , mkLowerState\n  , CGState(..)\n  , CGInfo(..)\n"
  },
  {
    "path": "lib/Eclair/RA/IR.hs",
    "chars": 3077,
    "preview": "{-# LANGUAGE TemplateHaskell #-}\n\nmodule Eclair.RA.IR\n  ( Relation\n  , RA(..)\n  , RAF(..)\n  , Alias\n  , Clause\n  , Actio"
  },
  {
    "path": "lib/Eclair/RA/IndexSelection.hs",
    "chars": 8803,
    "preview": "module Eclair.RA.IndexSelection\n  ( IndexMap\n  , IndexSelector\n  , Index(..)\n  , SearchSignature(..)\n  , Column\n  , runI"
  },
  {
    "path": "lib/Eclair/RA/Lower.hs",
    "chars": 15375,
    "preview": "\nmodule Eclair.RA.Lower ( compileToEIR ) where\n\nimport Prelude\nimport Data.Maybe (fromJust)\n\nimport qualified Data.List "
  },
  {
    "path": "lib/Eclair/RA/Transforms/HoistConstraints.hs",
    "chars": 4445,
    "preview": "module Eclair.RA.Transforms.HoistConstraints\n  ( transform\n  ) where\n\nimport Eclair.Transform\nimport Eclair.RA.IR\nimport"
  },
  {
    "path": "lib/Eclair/RA/Transforms.hs",
    "chars": 300,
    "preview": "module Eclair.RA.Transforms\n  ( simplify\n  ) where\n\nimport Eclair.Common.Location (NodeId(..))\nimport Eclair.RA.IR\nimpor"
  },
  {
    "path": "lib/Eclair/Souffle/IR.hs",
    "chars": 4937,
    "preview": "module Eclair.Souffle.IR\n  ( Souffle(..)\n  , ConversionError(..)\n  , toSouffle\n  ) where\n\nimport qualified Eclair.AST.IR"
  },
  {
    "path": "lib/Eclair/Transform.hs",
    "chars": 2561,
    "preview": "module Eclair.Transform\n  ( Transform(..)\n  , pureTransform\n  , TransformM\n  , runTransform\n  , fixTransform\n  , freshNo"
  },
  {
    "path": "lib/Eclair/TypeSystem.hs",
    "chars": 15327,
    "preview": "module Eclair.TypeSystem\n  ( Type(..)\n  , TypeError(..)\n  , Context(..)\n  , getContextLocation\n  , TypeInfo(..)\n  , Defi"
  },
  {
    "path": "lib/Eclair.hs",
    "chars": 10993,
    "preview": "{-# LANGUAGE GADTs, StandaloneDeriving #-}\n\nmodule Eclair\n  ( parse\n  , semanticAnalysis\n  , typeCheck\n  , emitDiagnosti"
  },
  {
    "path": "lib/Prelude.hs",
    "chars": 1617,
    "preview": "module Prelude\n  ( module Relude\n  , module Control.Arrow\n  , module Control.Monad.Writer.Strict\n  , module Control.Mona"
  },
  {
    "path": "src/eclair/Main.hs",
    "chars": 1083,
    "preview": "module Main (main) where\n\nimport Eclair.ArgParser\nimport Eclair.LSP\nimport Eclair\nimport GHC.IO.Encoding\nimport System.D"
  },
  {
    "path": "tests/.gitignore",
    "chars": 32,
    "preview": "/**/.lit_test_times.txt\nOutput/\n"
  },
  {
    "path": "tests/ast_transforms/constant_folding.eclair",
    "chars": 1008,
    "preview": "// RUN: split-file %s %t\n// RUN: %eclair compile --emit ast-transformed %t/program.eclair > %t/actual.out\n// RUN: diff %"
  },
  {
    "path": "tests/ast_transforms/copy_propagation.eclair",
    "chars": 1130,
    "preview": "// RUN: split-file %s %t\n// RUN: %eclair compile --emit ast-transformed %t/program.eclair > %t/actual.out\n// RUN: diff %"
  },
  {
    "path": "tests/ast_transforms/dead_code_elimination.eclair",
    "chars": 1402,
    "preview": "// RUN: split-file %s %t\n// RUN: %eclair compile %t/program1.eclair --emit ast-transformed > %t/actual1.out\n// RUN: diff"
  },
  {
    "path": "tests/ast_transforms/remove_contradictions.eclair",
    "chars": 529,
    "preview": "// RUN: split-file %s %t\n// RUN: %eclair compile --emit ast-transformed %t/program.eclair > %t/actual.out\n// RUN: diff %"
  },
  {
    "path": "tests/ast_transforms/shift_assignments.eclair",
    "chars": 921,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile --emit ast-transformed %t/program1.eclair > %t/actual1.out\n// RUN: dif"
  },
  {
    "path": "tests/check.sh",
    "chars": 323,
    "preview": "#!/bin/bash\n\ngrep -rE \"(fdescribe|fit)\" tests/eclair\n\nif [ \"$?\" == \"0\" ]; then\n  echo \"Found disabled tests (marked with"
  },
  {
    "path": "tests/eclair/Test/Eclair/ArgParserSpec.hs",
    "chars": 2679,
    "preview": "module Test.Eclair.ArgParserSpec\n  ( module Test.Eclair.ArgParserSpec\n  ) where\n\nimport Test.Hspec\nimport qualified Data"
  },
  {
    "path": "tests/eclair/Test/Eclair/JSONSpec.hs",
    "chars": 1134,
    "preview": "{-# LANGUAGE QuasiQuotes #-}\n\nmodule Test.Eclair.JSONSpec\n  ( module Test.Eclair.JSONSpec\n  ) where\n\nimport Eclair.JSON\n"
  },
  {
    "path": "tests/eclair/Test/Eclair/LLVM/Allocator/MallocSpec.hs",
    "chars": 2328,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\nmodule Test.Eclair.LLVM.Allocator.MallocSpec\n  ( module Test.Eclair.LLV"
  },
  {
    "path": "tests/eclair/Test/Eclair/LLVM/Allocator/PageSpec.hs",
    "chars": 3964,
    "preview": "{-# OPTIONS_GHC -Wno-deprecations -Wno-incomplete-uni-patterns #-}\nmodule Test.Eclair.LLVM.Allocator.PageSpec\n  ( module"
  },
  {
    "path": "tests/eclair/Test/Eclair/LLVM/Allocator/Utils.hs",
    "chars": 2769,
    "preview": "{-# OPTIONS_GHC -Wno-deprecations #-}\nmodule Test.Eclair.LLVM.Allocator.Utils\n  ( Bindings(..)\n  , compileAllocatorCode\n"
  },
  {
    "path": "tests/eclair/Test/Eclair/LLVM/BTreeSpec.hs",
    "chars": 27488,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\nmodule Test.Eclair.LLVM.BTreeSpec\n  ( module Test.Eclair.LLVM.BTreeSpec"
  },
  {
    "path": "tests/eclair/Test/Eclair/LLVM/HashMapSpec.hs",
    "chars": 7457,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\nmodule Test.Eclair.LLVM.HashMapSpec\n  ( module Test.Eclair.LLVM.HashMap"
  },
  {
    "path": "tests/eclair/Test/Eclair/LLVM/HashSpec.hs",
    "chars": 1925,
    "preview": "module Test.Eclair.LLVM.HashSpec\n  ( module Test.Eclair.LLVM.HashSpec\n  ) where\n\nimport qualified Data.Set as Set\nimport"
  },
  {
    "path": "tests/eclair/Test/Eclair/LLVM/SymbolSpec.hs",
    "chars": 2613,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\nmodule Test.Eclair.LLVM.SymbolSpec\n  ( module Test.Eclair.LLVM.SymbolSp"
  },
  {
    "path": "tests/eclair/Test/Eclair/LLVM/SymbolTableSpec.hs",
    "chars": 9535,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\nmodule Test.Eclair.LLVM.SymbolTableSpec\n  ( module Test.Eclair.LLVM.Sym"
  },
  {
    "path": "tests/eclair/Test/Eclair/LLVM/SymbolUtils.hs",
    "chars": 3790,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\nmodule Test.Eclair.LLVM.SymbolUtils\n  ( Bindings(..)\n  , Symbol(..)\n  ,"
  },
  {
    "path": "tests/eclair/Test/Eclair/LLVM/VectorSpec.hs",
    "chars": 7463,
    "preview": "{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}\nmodule Test.Eclair.LLVM.VectorSpec\n  ( module Test.Eclair.LLVM.VectorSp"
  },
  {
    "path": "tests/eclair/Test/Eclair/LSP/HandlersSpec.hs",
    "chars": 5463,
    "preview": "module Test.Eclair.LSP.HandlersSpec\n  ( module Test.Eclair.LSP.HandlersSpec\n  ) where\n\nimport Eclair\nimport Eclair.TypeS"
  },
  {
    "path": "tests/eclair/Test/Eclair/LSP/JSONSpec.hs",
    "chars": 7714,
    "preview": "{-# LANGUAGE QuasiQuotes #-}\n\nmodule Test.Eclair.LSP.JSONSpec\n  ( module Test.Eclair.LSP.JSONSpec\n  ) where\n\nimport qual"
  },
  {
    "path": "tests/eclair/Test/Eclair/RA/IndexSelectionSpec.hs",
    "chars": 9017,
    "preview": "{-# LANGUAGE QuasiQuotes #-}\n\nmodule Test.Eclair.RA.IndexSelectionSpec\n  ( module Test.Eclair.RA.IndexSelectionSpec\n  ) "
  },
  {
    "path": "tests/eclair/fixtures/lsp/document_highlight.eclair",
    "chars": 134,
    "preview": "@def edge(u32, u32).\n@def reachable(u32, u32).\n\nreachable(x, y) :-\n  edge(x, y).\n\nreachable(x, y) :-\n  edge(x, z),\n  rea"
  },
  {
    "path": "tests/eclair/fixtures/lsp/hover.eclair",
    "chars": 193,
    "preview": "@def edge(u32, u32) input.\n@def reachable(u32, u32) output.\n@def literal(string) output.\n\nreachable(x, y) :-\n  edge(x, y"
  },
  {
    "path": "tests/eclair/fixtures/lsp/invalid_syntax.eclair",
    "chars": 69,
    "preview": "@def number(u32) output.\n\nnumber(123, ).\nnumber(456).\nnumber(789, ).\n"
  },
  {
    "path": "tests/eclair/fixtures/lsp/semantic_errors.eclair",
    "chars": 57,
    "preview": "@def wildcard_in_fact(u32) output.\n\nwildcard_in_fact(_).\n"
  },
  {
    "path": "tests/eclair/fixtures/lsp/type_errors.eclair",
    "chars": 173,
    "preview": "@def edge(u32, u32).\n@def reachable(string, u32).\n@def literal(u32).\n\nreachable(x, y) :-\n  edge(x, y).\n\nreachable(x, z) "
  },
  {
    "path": "tests/eclair/fixtures/lsp/unparsable.eclair",
    "chars": 130,
    "preview": "@def edge(u32, u32).\n@def reachable(u32, u32).\n@def literal(string).\n\nreachable(x, y) :-\n\nreachable(x, z) :-\n  1.\n\nliter"
  },
  {
    "path": "tests/eclair/test.hs",
    "chars": 90,
    "preview": "{-# OPTIONS_GHC -Wno-missing-export-lists #-}\n{-# OPTIONS_GHC -F -pgmF hspec-discover #-}\n"
  },
  {
    "path": "tests/end_to_end/compile_and_run_native.eclair",
    "chars": 1718,
    "preview": "// This checks if eclair can be correctly compiled and linked with C code.\n\n// RUN: split-file %s %t\n// RUN: %eclair com"
  },
  {
    "path": "tests/end_to_end/compile_and_run_wasm.eclair",
    "chars": 3181,
    "preview": "// This checks if eclair can be correctly compiled to WASM and used from JS.\n\n// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g"
  },
  {
    "path": "tests/end_to_end/compile_and_run_with_extern.eclair",
    "chars": 1821,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile %t/program.eclair > %t/program.ll\n// RUN: %clang -o %t/program -O0 %t/"
  },
  {
    "path": "tests/hello.eclair",
    "chars": 330,
    "preview": "// This is mostly a sanity check that the test-suite works\n// (and that LLVM can compile to LLVM correctly).\n\n// RUN: %e"
  },
  {
    "path": "tests/lit.cfg",
    "chars": 1148,
    "preview": "import subprocess\nimport lit.formats\nfrom lit.llvm.subst import ToolSubst\n\nconfig.name = \"Eclair integration tests\"\nconf"
  },
  {
    "path": "tests/lowering/arithmetic.eclair",
    "chars": 16601,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile --emit ra-transformed %t/program.eclair > %t/actual_ra.out\n// RUN: dif"
  },
  {
    "path": "tests/lowering/clause_with_same_vars.eclair",
    "chars": 11768,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile --emit ra-transformed %t/program.eclair > %t/actual_ra.out\n// RUN: dif"
  },
  {
    "path": "tests/lowering/comparisons.eclair",
    "chars": 13708,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile --emit ra-transformed %t/program.eclair > %t/actual_ra.out\n// RUN: dif"
  },
  {
    "path": "tests/lowering/different_types.eclair",
    "chars": 4640,
    "preview": "// TODO add tests for caching mechanism (e.g. single_nonrecursive_rule test)\n\n// RUN: split-file %s %t\n\n// RUN: %eclair "
  },
  {
    "path": "tests/lowering/extern_definitions.eclair",
    "chars": 8687,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile --emit ra-transformed %t/program.eclair > %t/actual_ra.out\n// RUN: dif"
  },
  {
    "path": "tests/lowering/multiple_clauses_same_name.eclair",
    "chars": 9655,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile --emit ra-transformed %t/program.eclair > %t/actual_ra.out\n// RUN: dif"
  },
  {
    "path": "tests/lowering/multiple_rule_clauses.eclair",
    "chars": 10520,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile --emit ra-transformed %t/program.eclair > %t/actual_ra.out\n// RUN: dif"
  },
  {
    "path": "tests/lowering/mutually_recursive_rules.eclair",
    "chars": 27137,
    "preview": "// TODO variant where one is recursive\n// TODO tests for rules with >2 clauses, ...\n\n// RUN: split-file %s %t\n\n// RUN: %"
  },
  {
    "path": "tests/lowering/negation.eclair",
    "chars": 10035,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile --emit ra-transformed %t/program.eclair > %t/actual_ra.out\n// RUN: dif"
  },
  {
    "path": "tests/lowering/negation_with_wildcards.eclair",
    "chars": 6027,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile --emit ra-transformed %t/program.eclair > %t/actual_ra.out\n// RUN: dif"
  },
  {
    "path": "tests/lowering/no_top_level_facts.eclair",
    "chars": 18540,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile --emit ra-transformed %t/program.eclair > %t/actual_ra.out\n// RUN: dif"
  },
  {
    "path": "tests/lowering/recursive_mix_of_rules.eclair",
    "chars": 1124,
    "preview": "\n// RUN: split-file %s %t\n\n// RUN: %eclair compile --emit ra-transformed %t/program.eclair > %t/actual_ra.out\n// RUN: di"
  },
  {
    "path": "tests/lowering/single_non_recursive_rule.eclair",
    "chars": 7223,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile --emit ra-transformed %t/program.eclair > %t/actual_ra.out\n// RUN: dif"
  },
  {
    "path": "tests/lowering/single_recursive_rule.eclair",
    "chars": 13809,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile --emit ra-transformed %t/program.eclair > %t/actual_ra.out\n// RUN: dif"
  },
  {
    "path": "tests/lowering/stratification.eclair",
    "chars": 2405,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile --emit ra-transformed %t/program.eclair > %t/actual_ra.out\n// RUN: dif"
  },
  {
    "path": "tests/lowering/top_level_facts.eclair",
    "chars": 5476,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile --emit ra-transformed %t/program.eclair > %t/actual_ra.out\n// RUN: dif"
  },
  {
    "path": "tests/lowering/wasm_codegen.eclair",
    "chars": 1596,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile --target wasm32 --emit llvm %t/program.eclair > %t/actual_llvm.out\n// "
  },
  {
    "path": "tests/parser/error_recovery.eclair",
    "chars": 2411,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/parser/file_not_found.eclair",
    "chars": 257,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/parser/valid.eclair",
    "chars": 1708,
    "preview": "// RUN: %eclair compile --emit ra-transformed %s | FileCheck %s\n// CHECK: project\n\n// u32\n@def fact1(u32) output.\n@def f"
  },
  {
    "path": "tests/runtime/hashmap_test.eclair",
    "chars": 4930,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile %t/program.eclair > %t/program.ll\n// RUN: %clang -O0 -o %t/program %t/"
  },
  {
    "path": "tests/runtime/symbol_table_test.eclair",
    "chars": 6587,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile %t/program.eclair > %t/program.ll\n// RUN: %clang -O0 -o %t/program %t/"
  },
  {
    "path": "tests/runtime/vector_test.eclair",
    "chars": 4135,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile %t/program.eclair > %t/program.ll\n// RUN: %clang -O0 -o %t/program %t/"
  },
  {
    "path": "tests/semantic_analysis/cyclic_negation.eclair",
    "chars": 1654,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/semantic_analysis/dead_internal_relation.eclair",
    "chars": 796,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/semantic_analysis/invalid_extern_usage.eclair",
    "chars": 8326,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n//--- program1.eclair"
  },
  {
    "path": "tests/semantic_analysis/invalid_options_usage.eclair",
    "chars": 867,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n//--- program1.eclair"
  },
  {
    "path": "tests/semantic_analysis/invalid_wildcard_usage.eclair",
    "chars": 8100,
    "preview": "// NOTE: happy path is mostly skipped, this is tested implicitly by running\n// the compiler end-to-end in most other tes"
  },
  {
    "path": "tests/semantic_analysis/no_output_relations.eclair",
    "chars": 768,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/semantic_analysis/unconstrained_variables.eclair",
    "chars": 1293,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/semantic_analysis/ungrounded_variables.eclair",
    "chars": 13856,
    "preview": "// NOTE: happy path is not tested, this is tested implicitly by running\n// the compiler end-to-end in most other tests\n\n"
  },
  {
    "path": "tests/semantic_analysis/ungrounded_variables_arithmetic.eclair",
    "chars": 1772,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/semantic_analysis/ungrounded_variables_comparisons.eclair",
    "chars": 3575,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/semantic_analysis/ungrounded_variables_negations.eclair",
    "chars": 1293,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/string_support/encode_decode_string.eclair",
    "chars": 1684,
    "preview": "// RUN: split-file %s %t\n\n// RUN: %eclair compile %t/program.eclair > %t/program.ll\n// RUN: %clang -O0 -o %t/program %t/"
  },
  {
    "path": "tests/string_support/encode_decode_string_native.eclair",
    "chars": 1938,
    "preview": "// This checks if strings can be encoded / decoded between C <=> Eclair\n\n// RUN: split-file %s %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/string_support/encode_decode_string_wasm.eclair",
    "chars": 2648,
    "preview": "// This checks if strings can be encoded / decoded between JS <=> Eclair\n\n// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s "
  },
  {
    "path": "tests/transpilation/souffle.eclair",
    "chars": 1969,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/typesystem/arg_count_mismatch.eclair",
    "chars": 4066,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/typesystem/arithmetic.eclair",
    "chars": 3311,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n//--- program1.eclair"
  },
  {
    "path": "tests/typesystem/comparisons.eclair",
    "chars": 3895,
    "preview": "// NOTE: happy path already tested in lowering tests.\n\n// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// "
  },
  {
    "path": "tests/typesystem/duplicate_type_declarations.eclair",
    "chars": 1326,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/typesystem/extern_definitions.eclair",
    "chars": 6123,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n//--- program1.eclair"
  },
  {
    "path": "tests/typesystem/negation.eclair",
    "chars": 926,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n//--- program1.eclair"
  },
  {
    "path": "tests/typesystem/no_rules_for_type.eclair",
    "chars": 127,
    "preview": "// RUN: %eclair compile %s | FileCheck %s\n\n@def edge(u32, u32) input.\n@def chain(u32, u32, u32) input output.\n\n// CHECK:"
  },
  {
    "path": "tests/typesystem/type_mismatch_in_rule.eclair",
    "chars": 687,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/typesystem/type_mismatch_in_rule_body.eclair",
    "chars": 2597,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/typesystem/type_mismatch_in_rule_head.eclair",
    "chars": 2411,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/typesystem/type_mismatch_top_level_atoms.eclair",
    "chars": 2345,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/typesystem/typed_holes.eclair",
    "chars": 3321,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n//--- program1.eclair"
  },
  {
    "path": "tests/typesystem/unification_failure.eclair",
    "chars": 4348,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/typesystem/unknown_atom_in_rule_body.eclair",
    "chars": 1398,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/typesystem/unknown_atom_in_rule_head.eclair",
    "chars": 631,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/typesystem/unknown_atoms.eclair",
    "chars": 2054,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/typesystem/unknown_top_level_atoms.eclair",
    "chars": 868,
    "preview": "// RUN: mkdir -p %t && sed s@TEST_DIR@%t@g %s > %t/input.test\n// RUN: split-file %t/input.test %t\n\n// RUN: %eclair compi"
  },
  {
    "path": "tests/typesystem/valid.eclair",
    "chars": 1329,
    "preview": "// RUN: %eclair compile %s --emit ra-transformed | FileCheck %s\n// CHECK: project\n\n@def fact1(u32, string).\n@def fact2(s"
  },
  {
    "path": "tests/utils/extract_snippet",
    "chars": 179,
    "preview": "#!/bin/bash\n# Print lines starting from a match on a pattern ($2), up to first empty line.\n# Use sed again to trim the t"
  }
]

About this extraction

This page contains the full source code of the luc-tielen/eclair-lang GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 183 files (857.3 KB), approximately 275.8k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!