module Expr where

import State
import qualified Data.DList as DList -- needs cabal install --lib dlist

import Types
import Hs
import SmartList
import qualified Data.Set as Set


-- if change, change downloads.md
version = "nibbles 1.00+ (unnumbered release)"
webpage = ""

data ArgUsedness = UnusedArg | OptionalArg | UsedArg | UsednessDoesntMatter deriving (Show, Eq)
data OptionalLets = OptionalLets [VT] -- used to denote that the extra rets should be marked optional

data Impl = Impl { implType::VT
                 , implCode::HsCode
                 , implDeps::Set.Set Int
                 , implName::Maybe String -- to go into arg
                 , implUsed::ArgUsedness -- to go into arg
                 } deriving (Eq, Show)
noArgsUsed = Impl (error "undefined impl type") (hsAtom "(error \"undefined impl code\")") (Set.singleton 0) Nothing UnusedArg

data ArgKind = LambdaArg | LetArg { argKindDef::HsCode } deriving (Show, Eq)
data Args = Args { argsImpls::[Impl], argsKind::ArgKind, argsFrom::String } deriving Show

-- cp is the number of characters consumed so far
data Code = Lit { fullLit::String, codeLit::String, litcp::Int }
          | Nib { codeNib::[Int], nibcp::Int } deriving Show
isBinary (Nib _ _) = True
isBinary _ = False

uselessOp = 6 :: Int -- for padding odd nibbles into bytes

app1Hs :: String -> Impl -> Impl
app1Hs s impl = impl { implCode=hsApp (hsAtom s) (implCode impl) }

data ParseData = ParseData { pdCode :: Code
                           , pdContext :: [Args]
                           , pdNib :: SmartList Int
                           , pdLit :: DList.DList Char
                           , pdDataUsed :: Bool -- (used directly, not through $)
                           , pdImplicitArgUsed :: Bool -- to disable commutative extensions
                           , pdLitWarnings :: [String]
                           , pdNextUniqLetId :: Int}
type ParseState = State ParseData

-- all relevant information for determining arg match in a more abridge form than arg spec
data MatchTestData = MatchTestData { mtdTypes :: [VT]
                                   , mtdNibs :: [SmartList Int]
                                   , mtdState :: ParseData }

data ReqArg = ReqDontCare | ReqArg | ReqConst deriving Eq

-- -of-different-types
data ArgSpec
   = Cond String {-desc-} (MatchTestData -> Bool)
   | ParseArg String {-desc-} (ParseState (VT, String))
   | FakeAuto String -- an auto for quickref purposes only, it is implemented by a hidden op
   | Auto
   | AutoData ArgSpec
   | AutoDefault ArgSpec Integer -- todo make any type
   | Fn ReqArg ArgUsedness ([VT] -> (Int, [VT]))
   | OrAuto String {- desc -} ArgSpec
   | AutoNot ArgSpec {- only Fn -}
   | AutoOption String {- desc -}
   | OptionalFn ([VT] -> (Int, [VT])) -- returns a fn if its args are used, otherwise an impl with type ItWasAConstant
   | ZipMode
   | FoldMode
   | CharClassMode
   | AnyS -- basically a const fn, but shown as any*
  only used in spec , converted to BinCode / LitCode
   | BinCode Int
   | LitCode Char
   | NotEOF
   | EOF

data OpBehavior = LitWarn String | CodeGen ([VT]->ParseState ([VT], Impl))
type Operation = ([ArgSpec], OpBehavior)

dToList = DList.toList

addLitWarning :: String -> ParseState ()
addLitWarning msg = do
   modify $ \s -> s { pdLitWarnings = msg : pdLitWarnings s }
   return ()

appendRepH :: (SmartList Int,DList.DList Char) -> ParseState ()
appendRepH (nib2,lit2) = do
   nib <- gets pdNib
   lit <- gets pdLit
   modify $ \s -> s { pdNib = smartAppend nib nib2
                    , pdLit = DList.append lit lit2 }

appendRep :: ([Int],String) -> ParseState ()
appendRep (nib2,lit2) = appendRepH (newSmartList nib2, DList.fromList lit2)

appendRepA :: ([Int],[String]) -> ParseState ()
appendRepA (nib,lit) = appendRep (nib,concat lit)

blankRep :: Code -> [Args] -> Int -> ParseData
blankRep code context nextUniqLetId =
   ParseData code context (newSmartList []) (DList.fromList "") False False [] nextUniqLetId

getNib :: ParseData -> [Int]
getNib = smartToList . pdNib

getLit :: ParseData -> String
getLit = DList.toList . pdLit

 needs cabal install --lib dlist
 if change, change downloads.md
 used to denote that the extra rets should be marked optional
 to go into arg
 to go into arg
 cp is the number of characters consumed so far
 for padding odd nibbles into bytes
 (used directly, not through $)
 to disable commutative extensions
 all relevant information for determining arg match in a more abridge form than arg spec
 -of-different-types
desc
desc
 an auto for quickref purposes only, it is implemented by a hidden op
 todo make any type
 desc 
 only Fn 
 desc 
 returns a fn if its args are used, otherwise an impl with type ItWasAConstant
 basically a const fn, but shown as any*
module Expr where

import State

import Types
import Hs
import SmartList
import qualified Data.Set as Set


version = "nibbles 1.00+ (unnumbered release)"
webpage = ""

data ArgUsedness = UnusedArg | OptionalArg | UsedArg | UsednessDoesntMatter deriving (Show, Eq)

data Impl = Impl { implType::VT
                 , implCode::HsCode
                 , implDeps::Set.Set Int
                 } deriving (Eq, Show)
noArgsUsed = Impl (error "undefined impl type") (hsAtom "(error \"undefined impl code\")") (Set.singleton 0) Nothing UnusedArg

data ArgKind = LambdaArg | LetArg { argKindDef::HsCode } deriving (Show, Eq)
data Args = Args { argsImpls::[Impl], argsKind::ArgKind, argsFrom::String } deriving Show

data Code = Lit { fullLit::String, codeLit::String, litcp::Int }
          | Nib { codeNib::[Int], nibcp::Int } deriving Show
isBinary (Nib _ _) = True
isBinary _ = False


app1Hs :: String -> Impl -> Impl
app1Hs s impl = impl { implCode=hsApp (hsAtom s) (implCode impl) }

data ParseData = ParseData { pdCode :: Code
                           , pdContext :: [Args]
                           , pdNib :: SmartList Int
                           , pdLit :: DList.DList Char
                           , pdLitWarnings :: [String]
                           , pdNextUniqLetId :: Int}
type ParseState = State ParseData

data MatchTestData = MatchTestData { mtdTypes :: [VT]
                                   , mtdNibs :: [SmartList Int]
                                   , mtdState :: ParseData }

data ReqArg = ReqDontCare | ReqArg | ReqConst deriving Eq

data ArgSpec
   | Auto
   | AutoData ArgSpec
   | Fn ReqArg ArgUsedness ([VT] -> (Int, [VT]))
   | ZipMode
   | FoldMode
   | CharClassMode
  only used in spec , converted to BinCode / LitCode
   | BinCode Int
   | LitCode Char
   | NotEOF
   | EOF

data OpBehavior = LitWarn String | CodeGen ([VT]->ParseState ([VT], Impl))
type Operation = ([ArgSpec], OpBehavior)

dToList = DList.toList

addLitWarning :: String -> ParseState ()
addLitWarning msg = do
   modify $ \s -> s { pdLitWarnings = msg : pdLitWarnings s }
   return ()

appendRepH :: (SmartList Int,DList.DList Char) -> ParseState ()
appendRepH (nib2,lit2) = do
   nib <- gets pdNib
   lit <- gets pdLit
   modify $ \s -> s { pdNib = smartAppend nib nib2
                    , pdLit = DList.append lit lit2 }

appendRep :: ([Int],String) -> ParseState ()
appendRep (nib2,lit2) = appendRepH (newSmartList nib2, DList.fromList lit2)

appendRepA :: ([Int],[String]) -> ParseState ()
appendRepA (nib,lit) = appendRep (nib,concat lit)

blankRep :: Code -> [Args] -> Int -> ParseData
blankRep code context nextUniqLetId =
   ParseData code context (newSmartList []) (DList.fromList "") False False [] nextUniqLetId

getNib :: ParseData -> [Int]
getNib = smartToList . pdNib

getLit :: ParseData -> String
getLit = DList.toList . pdLit
