packages feed

lingo-0.4.0.0: src/Data/Languages/Templates.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveLift #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}

module Data.Languages.Templates
  ( Language (..),
    LanguageKey,
    generateLanguageMap,
    languageName,
  )
where

import Data.Aeson
import Data.ByteString (ByteString)
import qualified Data.Map.Strict as Map
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Yaml
import GHC.Generics (Generic)
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
#ifndef LANGUAGES_YAML_PATH
import Paths_lingo
#endif

-- | Type synonym for linguist's language name key.
type LanguageKey = String

-- | Identifies a programming language.
data Language = Language
  { languageId :: Integer,
    languageKey :: LanguageKey,
    languageExtensions :: [String],
    languageFileNames :: [String]
  }
  deriving (Eq, Show, Lift)

languageName :: Language -> Text
languageName = Text.pack . languageKey

instance FromJSON Language where
  parseJSON = withObject "Language" $ \l ->
    Language
      <$> l .: "language_id"
      <*> pure "unspecified" -- this is set later in map iteration
      <*> l .:? "extensions" .!= []
      <*> l .:? "filenames" .!= []

languagesYamlPath :: IO String
#ifdef LANGUAGES_YAML_PATH
languagesYamlPath = pure LANGUAGES_YAML_PATH
#else
languagesYamlPath = getDataFileName "languages.yml"
#endif

generateLanguageMap :: DecsQ
generateLanguageMap = do
  langYaml <- runIO languagesYamlPath
  langs <- runIO (decodeFileThrow @IO @(Map.Map LanguageKey Language) langYaml)
  let normalizedLangs = Map.mapWithKey (\name lang -> lang {languageKey = name}) langs

      byExtension = Map.foldr (process languageExtensions) mempty normalizedLangs
      byFileName = Map.foldr (process languageFileNames) mempty normalizedLangs

      process :: (Language -> [String]) -> Language -> Map.Map String [LanguageKey] -> Map.Map String [LanguageKey]
      process selector lang acc = foldr (\ext -> Map.insertWith mappend ext [languageKey lang]) acc (selector lang)

  [d|
    languages :: Map.Map LanguageKey Language
    languages = Map.fromDistinctAscList $(lift (Map.toAscList normalizedLangs))

    languagesByExtension :: Map.Map String [LanguageKey]
    languagesByExtension = Map.fromDistinctAscList $(lift (Map.toAscList byExtension))

    languagesByFileName :: Map.Map String [LanguageKey]
    languagesByFileName = Map.fromDistinctAscList $(lift (Map.toAscList byFileName))
    |]