packages feed

jsonlogic-0.1.0.0: src/JsonLogic/Operation/Boolean.hs

{-# LANGUAGE OverloadedLists #-}

-- |
-- Module      : JsonLogic.Operation.Boolean
-- Description : Internal JsonLogic operations on booleans
-- Copyright   : (c) Marien Matser, Gerard van Schie, Jelle Teeuwissen, 2022
-- License     : MIT
-- Maintainer  : jelleteeuwissen@hotmail.nl
-- Stability   : experimental
module JsonLogic.Operation.Boolean (booleanOperations, if', (==), (===), (!=), (!==), (!), (!!), and, or) where

import Control.Monad.Except
import JsonLogic.Json
import JsonLogic.Operation.Primitive
import JsonLogic.Operation.Utils
import JsonLogic.Type
import Prelude hiding (all, and, any, filter, map, max, min, or, sum, (!!), (&&), (==), (||))
import qualified Prelude as P hiding (and, or)

booleanOperations :: Monad m => Operations m
booleanOperations = [if', (==), (===), (!=), (!==), (!), (!!), and, or]

if' :: Monad m => Operation m
if' = ("if", evaluateIf)

-- Implementation for bool -> bool -> bool operators
(==), (===), (!=), (!==), (!), (!!), and, or :: Monad m => Operation m
(==) = ("==", looseEquals)
(===) = ("===", evaluateLogic (P.==))
(!=) = ("!=", looseNotEquals)
(!==) = ("!==", evaluateLogic (P./=))
(!) = ("!", evaluateFalsey)
(!!) = ("!!", evaluateTruthy)
and = ("and", evaluateLogic (P.&&))
or = ("or", evaluateLogic (P.||))

evaluateIf :: Monad m => Function m Json
evaluateIf evaluator (JsonArray [c, x, y]) vars = do
  res <- evaluateBool evaluator c vars
  evaluator (if res then x else y) vars
evaluateIf _ _ _ = throw "Wrong number of arguments for if"

-- Helper functions

evaluateLogic :: Monad m => (Bool -> Bool -> Bool) -> Function m Json
evaluateLogic operator evaluator (JsonArray [x, y]) vars = do
  x' <- evaluateBool evaluator x vars
  y' <- evaluateBool evaluator y vars
  return $ JsonBool $ x' `operator` y'
evaluateLogic _ _ _ _ = throw "Wrong number of arguments for logic operator"

evaluateTruthy :: Monad m => Function m Json
evaluateTruthy evaluator json vars = JsonBool <$> evaluateBool evaluator (evaluateUnaryArgument json) vars

evaluateFalsey :: Monad m => Function m Json
evaluateFalsey evaluator json vars = JsonBool . not <$> evaluateBool evaluator (evaluateUnaryArgument json) vars

-- | Evaluate loose equals
looseEquals :: Monad m => Function m Json
looseEquals evaluator (JsonArray [x, y]) vars = do
  x' <- evaluator x vars
  y' <- evaluator y vars
  return $ JsonBool $ looseEq x' y'
looseEquals _ _ _ = throw "Wrong number of arguments for loose not equals operator"

looseNotEquals :: Monad m => Function m Json
looseNotEquals evaluator (JsonArray [x, y]) vars = do
  x' <- evaluator x vars
  y' <- evaluator y vars
  return $ JsonBool $ not $ looseEq x' y'
looseNotEquals _ _ _ = throw "Wrong number of arguments for loose not equals operator"

-- | See: https://github.com/gregsdennis/json-everything/blob/master/JsonLogic/JsonElementExtensions.cs#L117
-- See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Equality
looseEq :: Json -> Json -> Bool
looseEq (JsonArray a) (JsonArray b) = a P.== b
looseEq (JsonObject a) (JsonObject b) = a P.== b
looseEq (JsonNumber a) (JsonNumber b) = a P.== b
looseEq (JsonString a) (JsonString b) = a P.== b
looseEq (JsonBool a) (JsonBool b) = a P.== b
looseEq JsonNull JsonNull = True
looseEq JsonNull _ = False
looseEq _ JsonNull = False
looseEq (JsonObject _) _ = False
looseEq _ (JsonObject _) = False
looseEq a@(JsonNumber _) (JsonArray b) = looseEq a (JsonString $ show b)
looseEq (JsonArray a) b@(JsonNumber _) = looseEq (JsonString $ show a) b
looseEq a@(JsonNumber _) b = a P.== numberify b
looseEq a b@(JsonNumber _) = numberify a P.== b
looseEq (JsonArray a) (JsonString b) = show a P.== b
looseEq (JsonString a) (JsonArray b) = a P.== show b
looseEq _ _ = False

-- See https://github.com/gregsdennis/json-everything/blob/master/JsonLogic/JsonElementExtensions.cs#L84
numberify :: Json -> Json
numberify (JsonString s) = JsonNumber (read s)
numberify n@(JsonNumber _) = n
numberify (JsonBool b) = JsonNumber $ toEnum $ fromEnum b
numberify _ = JsonNull