evalPNExpr :: [SimValue] -> PNExpr -> SimFlow SimValue
    evalPNExpr inputs expr = body expr
        where
            body (PNLiteral value) = return value
            body (PNBool True) = return $ SimValue 1 []
            body (PNBool False) = return $ SimValue 0 []
            body (PNInput i) = return $ inputs !! i
            body (PNBitfield slice expr) = subExpr expr >>= return . getBitfield slice
            body (PNInsertBitfield slice expr1 expr2) = do
                val1 <- subExpr expr1
                val2 <- subExpr expr2
                return $ simInsertBitfield slice val1 val2
            body (PNOp terms expr) = do
                v <- subExpr expr
                results <- evalOTerms terms v
                let (_, result) = last results
                return result
            body (PNAppend widths exprs) = do
                vs <- mapM subExpr exprs
                return $ simAppendValues widths vs
            body (PNMatch slice imps expr) = do
                v <- subExpr expr
                case v of
                    SimValue {} -> do
                        let SimValue guard _ = simExtractBitfield slice v
                        return $ SimValue (
                            if any (impIsCoveredByImp (sliceWidth slice) (Imp guard 0)) imps
                                then 1
                                else 0) []
                    _ -> return NoSimValue

            subExpr expr = evalPNExpr inputs expr
evalPNExpr :: [SimValue] -> PNExpr -> SimFlow SimValue
evalPNExpr inputs expr = body expr
        where
            body (PNLiteral value) = return value
            body (PNBool True) = return $ SimValue 1 []
            body (PNBool False) = return $ SimValue 0 []
            body (PNInput i) = return $ inputs !! i
            body (PNBitfield slice expr) = subExpr expr >>= return . getBitfield slice
            body (PNInsertBitfield slice expr1 expr2) = do
                val1 <- subExpr expr1
                val2 <- subExpr expr2
                return $ simInsertBitfield slice val1 val2
            body (PNOp terms expr) = do
                v <- subExpr expr
                results <- evalOTerms terms v
                let (_, result) = last results
                return result
            body (PNAppend widths exprs) = do
                vs <- mapM subExpr exprs
                return $ simAppendValues widths vs
            body (PNMatch slice imps expr) = do
                v <- subExpr expr
                case v of
                    SimValue {} -> do
                        let SimValue guard _ = simExtractBitfield slice v
                        return $ SimValue (
                            if any (impIsCoveredByImp (sliceWidth slice) (Imp guard 0)) imps
                                then 1
                                else 0) []
                    _ -> return NoSimValue

            subExpr expr = evalPNExpr inputs expr
evalPNExpr inputs expr = body expr
        where
            body (PNLiteral value) = return value
            body (PNBool True) = return $ SimValue 1 []
            body (PNBool False) = return $ SimValue 0 []
            body (PNInput i) = return $ inputs !! i
            body (PNBitfield slice expr) = subExpr expr >>= return . getBitfield slice
            body (PNInsertBitfield slice expr1 expr2) = do
                val1 <- subExpr expr1
                val2 <- subExpr expr2
                return $ simInsertBitfield slice val1 val2
            body (PNOp terms expr) = do
                v <- subExpr expr
                results <- evalOTerms terms v
                let (_, result) = last results
                return result
            body (PNAppend widths exprs) = do
                vs <- mapM subExpr exprs
                return $ simAppendValues widths vs
            body (PNMatch slice imps expr) = do
                v <- subExpr expr
                case v of
                    SimValue {} -> do
                        let SimValue guard _ = simExtractBitfield slice v
                        return $ SimValue (
                            if any (impIsCoveredByImp (sliceWidth slice) (Imp guard 0)) imps
                                then 1
                                else 0) []
                    _ -> return NoSimValue

            subExpr expr = evalPNExpr inputs expr