packages feed

nix-lang-0.1.0.0: src/Nix/Lang/Utils.hs

-- | Miscellaneous shared helpers for spans, tokens, and small AST utilities.
--
-- This is package plumbing: span math, token text, and a few small helpers that
-- get reused across parsing, exact printing, and editing.
module Nix.Lang.Utils where

import Data.Char (isSpace)
import Data.Maybe (fromJust)
import Data.Text (Text)
import qualified Data.Text as T
import Nix.Lang.Annotation
import Nix.Lang.Span
import Nix.Lang.Types

mkSrcSpan :: String -> (Int, Int) -> (Int, Int) -> SrcSpan
mkSrcSpan
  srcSpanFilename
  (srcSpanStartLine, srcSpanStartColumn)
  (srcSpanEndLine, srcSpanEndColumn)
    | (srcSpanEndLine, srcSpanEndColumn) >= (srcSpanStartLine, srcSpanStartColumn) = SrcSpan {..}
    | otherwise = error $ show (srcSpanEndLine, srcSpanEndColumn) <> " is less than " <> show (srcSpanStartLine, srcSpanStartColumn)

isSubspanOf ::
  SrcSpan ->
  SrcSpan ->
  Bool
isSubspanOf src parent
  | srcSpanFilename parent /= srcSpanFilename src = False
  | otherwise =
      (srcSpanStartLine parent, srcSpanStartColumn parent) <= (srcSpanStartLine src, srcSpanStartColumn src)
        && (srcSpanEndLine parent, srcSpanEndColumn parent) >= (srcSpanEndLine src, srcSpanEndColumn src)

combineSrcSpans :: SrcSpan -> SrcSpan -> SrcSpan
combineSrcSpans span1 span2 = SrcSpan f sl sc el ec
  where
    (sl, sc) =
      min
        (srcSpanStartLine span1, srcSpanStartColumn span1)
        (srcSpanStartLine span2, srcSpanStartColumn span2)
    (el, ec) =
      max
        (srcSpanEndLine span1, srcSpanEndColumn span1)
        (srcSpanEndLine span2, srcSpanEndColumn span2)
    f = srcSpanFilename span1

unLoc :: Located a -> a
unLoc (L _ x) = x

getLoc :: Located a -> SrcSpan
getLoc (L s _) = s

showToken :: Ann -> Maybe Text
showToken = \case
  AnnDoubleSingleQuotes -> Just "''"
  AnnDoubleQuote -> Just "\""
  AnnAssert -> Just "assert"
  AnnIf -> Just "if"
  AnnElse -> Just "else"
  AnnThen -> Just "then"
  AnnLet -> Just "let"
  AnnIn -> Just "in"
  AnnInherit -> Just "inherit"
  AnnRec -> Just "rec"
  AnnWith -> Just "with"
  AnnOpenC -> Just "{"
  AnnCloseC -> Just "}"
  AnnOpenS -> Just "["
  AnnCloseS -> Just "]"
  AnnOpenP -> Just "("
  AnnCloseP -> Just ")"
  AnnAssign -> Just "="
  AnnAt -> Just "@"
  AnnColon -> Just ":"
  AnnComma -> Just ","
  AnnDot -> Just "."
  AnnEllipsis -> Just "..."
  AnnQuestion -> Just "?"
  AnnSemicolon -> Just ";"
  AnnConcat -> Just "++"
  AnnUpdate -> Just "//"
  AnnEx -> Just "!"
  AnnAdd -> Just "+"
  AnnSub -> Just "-"
  AnnMul -> Just "*"
  AnnDiv -> Just "/"
  AnnAnd -> Just "&&"
  AnnOr -> Just "||"
  AnnImpl -> Just "->"
  AnnEqual -> Just "=="
  AnnNEqual -> Just "!="
  AnnGT -> Just ">"
  AnnGE -> Just ">="
  AnnLT -> Just "<"
  AnnLE -> Just "<="
  AnnId -> Nothing
  AnnVal -> Nothing
  AnnInterpolOpen -> Just "${"
  AnnInterpolClose -> Just "}"
  AnnEnvPathOpen -> Just "<"
  AnnEnvPathClose -> Just ">"
  AnnNeg -> Just "-"
  AnnEof -> Nothing

opToToken :: BinaryOp -> Ann
opToToken = \case
  OpConcat -> AnnConcat
  OpMul -> AnnMul
  OpDiv -> AnnDiv
  OpAdd -> AnnAdd
  OpSub -> AnnSub
  OpUpdate -> AnnUpdate
  OpLT -> AnnLT
  OpLE -> AnnLE
  OpGT -> AnnGT
  OpGE -> AnnGE
  OpEq -> AnnEqual
  OpNEq -> AnnNEqual
  OpAnd -> AnnAnd
  OpOr -> AnnOr
  OpImpl -> AnnImpl

showBinOP :: BinaryOp -> Text
showBinOP = fromJust . showToken . opToToken

-- >>> opString
-- "++*/+-//<<=>>===!=&&||->"
opString :: String
opString = T.unpack . T.concat $ showBinOP <$> [OpConcat ..]

stripCommonPrefix :: String -> String
stripCommonPrefix src = unlines $ drop commonIndent <$> ls
  where
    ls = lines src
    commonIndent = case length . takeWhile isSpace <$> filter (any $ not . isSpace) ls of
      [] -> 0
      indents -> minimum indents