{-# LANGUAGE OverloadedStrings, LambdaCase #-}

module Funcons.Core.Values.Composite.DatatypesBuiltin where

import Funcons.EDSL
import Funcons.MSOS (evalStrictSequence, evalSequence, Strictness(..))
import Funcons.Operations (Values(..), Types(..), ComputationTypes(..))
import Data.Text (pack,unpack)

library = libFromList [
        ("datatype-value", NonStrictFuncon evalADT)
    ,   ("non-strict-datatype-value", NonStrictFuncon evalLazyADT)
    -- backwards compatibility
    ,   ("adt-construct", StrictFuncon adtConstruct)
    ,   ("adt-type-construct", StrictFuncon adtTypeConstruct)
    ,   ("adt-constructor", StrictFuncon adtConstructor)
    ,   ("adt-fields", StrictFuncon adtFields)
    ]

datatype_value_ = applyFuncon "datatype-value"
adt_val_ = datatype_value_

evalADT (f:fs) = evalStrictSequence (f:fs) cont adt_val_
  where
    cont [] = error "eval-adt assert"
    cont (v:vs) = if isString_ v
      then rewritten $ ADTVal (pack (unString v)) $ map FValue vs
      else sortErr (adt_val_ (map FValue (v:vs))) ("first argument of datatype-value not a string")
evalADT [] = sortErr (adt_val_ [])
                    "algebraic-datatype not applied to a string and a sequence of fields"

lazy_adt_val_ = applyFuncon "non-strict-datatype-value"
evalLazyADT (f:fs) = evalSequence (Strict : replicate (length fs) NonStrict)
                          (f:fs) cont lazy_adt_val_
  where
    cont [] = error "lazy-eval-adt assert"
    cont (v:vs) = case v of
      FValue s | isString_ s
        -> rewritten $ ADTVal (pack (unString s)) vs
      _ -> sortErr (lazy_adt_val_ (v:vs)) ("first argument of value constructor not a string")
evalLazyADT [] = sortErr (lazy_adt_val_ [])
                    "value constructor not applied to a string and a sequence of computations"

adt_construct_ = applyFuncon "adt-construct"
adtConstruct (v:vs) =
  if isString_ v
    then rewritten $ ADTVal (pack (unString v)) $ map FValue vs
    else sortErr (adt_val_ (map FValue (v:vs))) ("first argument of adt-construct not a string")
adtConstruct [] = sortErr (adt_val_ [])
                    "adt-construct not applied to a string and a sequence of fields"

adt_type_construct_ = applyFuncon "adt-type-construct"
adtTypeConstruct (v:vs)
  | isString_ v = rewritten $ ComputationType $ Type $ ADT (pack (unString v)) fs
  | otherwise   = sortErr (adt_val_ (FValue v : fs)) ("first argument of adt-type-construct not a string")
 where fs = map FValue vs
adtTypeConstruct [] = sortErr (adt_val_ [])
                    "adt-type-construct not applied to a string and a sequence of type arguments (values)"

adt_constructor_ = applyFuncon "adt-constructor"
adtConstructor [ADTVal cons _] = rewritten $ string__ (unpack cons)
adtConstructor vs = sortErr (adt_constructor_ (map FValue vs)) "argument of adt-constructor not an adt value"

adt_fields_ = applyFuncon "adt-fields"
adtFields [ADTVal _ fs] = rewritten $ ADTVal "list" fs
adtFields vs = sortErr (adt_fields_ (map FValue vs)) "argument of adt-fields not an adt value"