{-# LANGUAGE OverloadedStrings #-}

-- | Module, that provides data necessary for the standard library support.
module StdLib where

import Analyzer.AnalyzedAst (Identifier)
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Text (Text, pack)
import qualified Data.Text as T
import Interpreter.Result (Err (Panic, UnexpectedError), ResultValue, RuntimeValue' (..))
import MaybeVoid (MaybeVoid (NonVoid, Void))

---------------------------------------------------------StdLib---------------------------------------------------------

-- * StdLib functions

-- | StdLib function.
data StdLibFunction = StdLibFunction
  { StdLibFunction -> Identifier
name :: Identifier,
    StdLibFunction -> StdLibFuncImpl
impl :: StdLibFuncImpl
  }

-- | Convenient type alias for stdlib function implementation.
type StdLibFuncImpl = [RuntimeValue'] -> StdLibFuncResult

-- | Convenient type alias for stdlib function result.
type StdLibFuncResult = ResultValue (MaybeVoid RuntimeValue', Text)

-- | All available stdlib functions.
stdLibFunctions :: [StdLibFunction]
stdLibFunctions :: [StdLibFunction]
stdLibFunctions = [StdLibFunction
lenFunction, StdLibFunction
printFunction, StdLibFunction
printlnFunction, StdLibFunction
panicFunction]

-- | 'stdLibFunctions' given in map representation for convenience.
stdLibFunctionsMap :: Map Identifier StdLibFuncImpl
stdLibFunctionsMap :: Map Identifier StdLibFuncImpl
stdLibFunctionsMap = forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList forall a b. (a -> b) -> a -> b
$ (\StdLibFunction
f -> (StdLibFunction -> Identifier
name StdLibFunction
f, StdLibFunction -> StdLibFuncImpl
impl StdLibFunction
f)) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [StdLibFunction]
stdLibFunctions

-------------------------------------------------------Functions--------------------------------------------------------

-- * StdLib functions implementation

-- ** @len@

-- | @len@ function.
lenFunction :: StdLibFunction
lenFunction :: StdLibFunction
lenFunction = StdLibFunction {name :: Identifier
name = Identifier
"len", impl :: StdLibFuncImpl
impl = StdLibFuncImpl
lenImpl}

-- | @len@ implementation.
lenImpl :: StdLibFuncImpl
lenImpl :: StdLibFuncImpl
lenImpl [RuntimeValue']
args = case [RuntimeValue']
args of
  [ValString' Identifier
x] -> forall {b} {a}.
IsString b =>
Int -> Either a (MaybeVoid RuntimeValue', b)
ok forall a b. (a -> b) -> a -> b
$ Identifier -> Int
T.length Identifier
x
  [ValArray' [RuntimeValue']
xs] -> forall {b} {a}.
IsString b =>
Int -> Either a (MaybeVoid RuntimeValue', b)
ok forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => t a -> Int
length [RuntimeValue']
xs
  [RuntimeValue']
_ -> forall a b. a -> Either a b
Left Err
UnexpectedError
  where
    ok :: Int -> Either a (MaybeVoid RuntimeValue', b)
ok Int
int = forall a b. b -> Either a b
Right (forall a. a -> MaybeVoid a
NonVoid forall a b. (a -> b) -> a -> b
$ Int -> RuntimeValue'
ValInt' Int
int, b
"")

-- ** @print@

-- | @print@ function.
printFunction :: StdLibFunction
printFunction :: StdLibFunction
printFunction = StdLibFunction {name :: Identifier
name = Identifier
"print", impl :: StdLibFuncImpl
impl = StdLibFuncImpl
printImpl}

-- | @print@ implementation.
printImpl :: StdLibFuncImpl
printImpl :: StdLibFuncImpl
printImpl [RuntimeValue']
args = forall a b. b -> Either a b
Right (forall a. MaybeVoid a
Void, [Identifier] -> Identifier
T.concat (String -> Identifier
pack forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Show a => a -> String
show forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [RuntimeValue']
args))

-- ** @println@

-- | @println@ function.
printlnFunction :: StdLibFunction
printlnFunction :: StdLibFunction
printlnFunction = StdLibFunction {name :: Identifier
name = Identifier
"println", impl :: StdLibFuncImpl
impl = StdLibFuncImpl
printlnImpl}

-- | @println@ implementation.
printlnImpl :: StdLibFuncImpl
printlnImpl :: StdLibFuncImpl
printlnImpl [RuntimeValue']
args = forall a b. b -> Either a b
Right (forall a. MaybeVoid a
Void, [Identifier] -> Identifier
T.unwords (String -> Identifier
pack forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Show a => a -> String
show forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [RuntimeValue']
args) forall a. Semigroup a => a -> a -> a
<> Identifier
"\n")

-- ** @panic@

-- | @panic@ function.
panicFunction :: StdLibFunction
panicFunction :: StdLibFunction
panicFunction = StdLibFunction {name :: Identifier
name = Identifier
"panic", impl :: StdLibFuncImpl
impl = StdLibFuncImpl
panicImpl}

-- | @panic@ implementation.
panicImpl :: StdLibFuncImpl
panicImpl :: StdLibFuncImpl
panicImpl [RuntimeValue']
args = case [RuntimeValue']
args of
  [ValString' Identifier
msg] -> forall a b. a -> Either a b
Left forall a b. (a -> b) -> a -> b
$ Identifier -> Err
Panic Identifier
msg
  [RuntimeValue']
_ -> forall a b. a -> Either a b
Left Err
UnexpectedError