packages feed

sbv-program-1.1.0.0: src/Data/SBV/Program/Utils.hs

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

module Data.SBV.Program.Utils (
  sampleSpec,

  isConstantComponent,

  mkVarName,
  mkInputLocName,
  mkOutputLocName,
  mkInputVarName,
  mkOutputVarName,

  writePseudocode,
  )
where

import Data.List (intercalate)
import Data.SBV
import Data.SBV.Control
import Data.SBV.Program.Types


-- | Given a 'SynthSpec' tries to generate a set of input/output values that satisfy the specification.
-- Uses solver under the hood.
sampleSpec :: forall a comp spec . (SymVal a, SynthSpec spec a) => spec a -> IO (Maybe (IOs a))
sampleSpec spec = runSMT $ do
    -- use solver to create initial values for I
    ins <- mkExistVars @a $ fromIntegral $ specArity spec
    out <- sbvExists_
    constrain $ specFunc spec ins out
    query $ do
      r <- checkSat
      case r of
        Sat -> Just <$> (IOs <$> mapM getValue ins <*> getValue out)
        _ -> pure Nothing


-- | Returns 'True' if the component is a __constant__ one. Constant components
-- have zero inputs (their 'specArity' \(=0\) ).
isConstantComponent :: forall a comp spec . (SynthSpec spec a, SynthComponent comp spec a) =>
     comp a
  -> Bool
isConstantComponent comp = specArity (compSpec comp) == 0

-- | Creates sanitized variable name suitable for SBV.
mkVarName :: String -- ^ Base name, which can be an empty string, in which case \"UnnamedComponent\" value will be used.
          -> Bool -- ^ Setting 'isLocation' to 'True' will append \"Loc\" to the name.
          -> Bool -- ^ If 'isOutput' is 'False' the value of 'i' is also appended to the name.
          -> Word -- ^ Number of an input. Can be 'undefined' for an output.
          -> String
mkVarName compName isLocation isOutput i = name1 ++ name2 ++ if not isOutput then show i else ""
  where
    name1 = if null compName then "UnnamedComponent" else compName
    name2 = (if isOutput then "Output" else "Input") ++ if isLocation then "Loc" else ""

-- | Shortcut for the more general 'mkVarName' function.
mkInputLocName compName = mkVarName compName True False
-- | Shortcut for the more general 'mkVarName' function.
mkOutputLocName compName = mkVarName compName True True undefined
-- | Shortcut for the more general 'mkVarName' function.
mkInputVarName compName = mkVarName compName False False
-- | Shortcut for the more general 'mkVarName' function.
mkOutputVarName compName = mkVarName compName False True undefined


-- | Renders the solution in SSA style.
writePseudocode :: (Show a, SynthComponent comp spec a) => Program Location (comp a) -> String
writePseudocode prog = unlines (header : body ++ ret)
  where
    prog' = sortInstructions prog
    header = concat [
          "function(",
          intercalate ", " $ map writeArg (_ins $ programIOs prog'),
          "):"
        ]
    body = flip map (programInstructions prog') $ \(Instruction (IOs {..}) comp) -> concat [
        "\t%",
        show _out,
        " = ",
        compName comp,
        " ",
        intercalate ", " $ map writeArg _ins,
        if isConstantComponent comp then show $ getConstValue comp else ""
        ]
    ret = ["\treturn " ++ writeArg (_out $ programIOs prog')]
    writeArg loc = '%' : show loc