module Recalc.Engine.Core where
import Data.Char (isDigit, isUpper, ord, toLower)
import Data.Text (Text)
import Data.Text qualified as Text
import Network.URI (URI)
type SheetName = Text
type SheetId = (URI, SheetName)
type CellAddr = (Int, Int)
row, column :: CellAddr -> Int
row :: CellAddr -> Int
row = CellAddr -> Int
forall a b. (a, b) -> a
fst
column :: CellAddr -> Int
column = CellAddr -> Int
forall a b. (a, b) -> b
snd
type CellRange = (CellAddr, CellAddr)
type CellRangeRef = (SheetId, CellRange)
type CellRef = (SheetId, CellAddr)
readExcel :: Text -> Maybe CellAddr
readExcel :: Text -> Maybe CellAddr
readExcel Text
txt = (Text, Text) -> Maybe CellAddr
forall {a}.
(Ord a, Num a, Read a) =>
(Text, Text) -> Maybe (a, Int)
go ((Text, Text) -> Maybe CellAddr)
-> (Int -> (Text, Text)) -> Int -> Maybe CellAddr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int -> Text -> (Text, Text)
`Text.splitAt` Text
txt) (Int -> Maybe CellAddr) -> Maybe Int -> Maybe CellAddr
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Int -> Bool -> String -> Maybe Int
forall {a}. Num a => a -> Bool -> String -> Maybe a
alg Int
0 Bool
False (Text -> String
Text.unpack Text
txt)
where
go :: (Text, Text) -> Maybe (a, Int)
go (Text
letters, Text
digits)
| a
r a -> a -> Bool
forall a. Ord a => a -> a -> Bool
>= a
0 = (a, Int) -> Maybe (a, Int)
forall a. a -> Maybe a
Just (a
r, Text -> Int
readExcelCol Text
letters Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
| Bool
otherwise = Maybe (a, Int)
forall a. Maybe a
Nothing
where
r :: a
r = String -> a
forall a. Read a => String -> a
read (Text -> String
Text.unpack Text
digits) a -> a -> a
forall a. Num a => a -> a -> a
- a
1
alg :: a -> Bool -> String -> Maybe a
alg a
k Bool
b (Char
c : String
cs)
| Bool -> Bool
not Bool
b, Char -> Bool
isUpper Char
c = a -> Bool -> String -> Maybe a
alg (a
k a -> a -> a
forall a. Num a => a -> a -> a
+ a
1) Bool
b String
cs
| Bool -> Bool
not Bool
b, Char -> Bool
isDigit Char
c = a -> Bool -> String -> Maybe a
alg a
k Bool
True String
cs
| Bool
b, Char -> Bool
isDigit Char
c = a -> Bool -> String -> Maybe a
alg a
k Bool
True String
cs
| Bool
otherwise = Maybe a
forall a. Maybe a
Nothing
alg a
k Bool
True [] = a -> Maybe a
forall a. a -> Maybe a
Just a
k
alg a
_ Bool
_ String
_ = Maybe a
forall a. Maybe a
Nothing
readExcelCol :: Text -> Int
readExcelCol :: Text -> Int
readExcelCol = (Int -> Char -> Int) -> Int -> Text -> Int
forall a. (a -> Char -> a) -> a -> Text -> a
Text.foldl' (\Int
a Char
c -> Int
26 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
a Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Char -> Int
ord (Char -> Char
toLower Char
c) Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
96) Int
0
showExcel26 :: CellAddr -> String
showExcel26 :: CellAddr -> String
showExcel26 (Int
r, Int
c) = String
rowStr String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Int -> String
forall a. Show a => a -> String
show (Int
r Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
where
rowStr :: String
rowStr = ([String] -> [String]) -> [[String]] -> [String]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap [String] -> [String]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
forall (m :: * -> *) a. Monad m => [m a] -> m [a]
sequence ([[String]] -> [[String]]
forall a. HasCallStack => [a] -> [a]
tail ([[String]] -> [[String]]) -> [[String]] -> [[String]]
forall a b. (a -> b) -> a -> b
$ ([String] -> [String]) -> [String] -> [[String]]
forall a. (a -> a) -> a -> [a]
iterate ([Char
'A' .. Char
'Z'] :) []) [String] -> Int -> String
forall a. HasCallStack => [a] -> Int -> a
!! Int
c