packages feed

oughta-0.1.0.0: src/Oughta/Pos.hs

{-# LANGUAGE OverloadedStrings #-}

module Oughta.Pos
  ( Pos(..)
  , incPos
  , Loc(..)
  , printLoc
  , Span(..)
  , startLoc
  , endLoc
  , printSpan
  ) where

import Data.List qualified as List
import Data.Maybe qualified as Maybe
import Data.Text qualified as Text
import Data.Text (Text)
import Prelude hiding (last, lines, span)

-- | Helper, not exported
tshow :: Show a => a -> Text
tshow = Text.pack . show

-- | A source position
data Pos
  = Pos
    { line :: {-# UNPACK #-} !Int
    , col :: {-# UNPACK #-} !Int
    }

-- | Move a position forward past a chunk of text.
incPos :: Pos -> Text -> Pos
incPos (Pos l c) t =
  let newlines = Text.count "\n" t in
  Pos (l + newlines) $
    if "\n" `Text.isSuffixOf` t
    then 1
    else
      case List.reverse (Text.lines t) of
        [] -> c
        (only : []) -> c + Text.length only
        (last : _) -> Text.length last

printPos :: Pos -> Text
printPos p = tshow (line p) <> ":" <> tshow (col p)

-- | A source location
data Loc
  = Loc
    { path :: !(Maybe FilePath)
    , pos :: {-# UNPACK #-} !Pos
    }

printLoc :: Loc -> Text
printLoc l =
  let p = Maybe.fromMaybe "<unknown file>" (path l) in
  Text.pack p <> ":" <> printPos (pos l)

-- | A source span
data Span
  = Span
  { spanPath :: !(Maybe FilePath)
  , spanStart :: {-# UNPACK #-} !Pos
  , spanEnd :: {-# UNPACK #-} !Pos
  }

startLoc :: Span -> Loc
startLoc s = Loc (spanPath s) (spanStart s)

endLoc :: Span -> Loc
endLoc s = Loc (spanPath s) (spanEnd s)

printSpan :: Span -> Text
printSpan s =
  let p = Maybe.fromMaybe "<unknown file>" (spanPath s) in
  Text.pack p <> ":" <> printPos (spanStart s) <> "-" <> printPos (spanEnd s)