packages feed

moonlight-core-0.1.0.0: src-term/Moonlight/Core/Term/Database/Atom.hs

{-# LANGUAGE BangPatterns #-}

module Moonlight.Core.Term.Database.Atom where

import Control.Monad (foldM)
import Data.Map.Strict qualified as Map
import Data.Primitive.PrimArray qualified as PrimArray
import Moonlight.Core.DenseKey (DenseKey (..))
import Moonlight.Core.Term.Database.Types
import Prelude

boundColumn :: DenseKey key => QueryBinding key -> (Column, QueryTerm key) -> Maybe (Column, Int)
boundColumn binding (column, term) =
  fmap (\value -> (column, value)) (termBoundValue binding term)
{-# INLINE boundColumn #-}

termBoundValue :: DenseKey key => QueryBinding key -> QueryTerm key -> Maybe Int
termBoundValue binding term =
  case term of
    QueryBound key ->
      Just (encodeDenseKey key)
    QueryVariable variable ->
      fmap encodeDenseKey (Map.lookup variable (queryBindingAssignments binding))
{-# INLINE termBoundValue #-}

atomArity :: QueryAtom f key -> Int
atomArity atom =
  length (atomChildren atom)
{-# INLINE atomArity #-}

atomColumns :: QueryAtom f key -> [Column]
atomColumns atom =
  ResultColumn : fmap ChildColumn [0 .. atomArity atom - 1]
{-# INLINE atomColumns #-}

atomTerms :: QueryAtom f key -> [QueryTerm key]
atomTerms atom =
  atomResult atom : atomChildren atom
{-# INLINE atomTerms #-}

atomBindRow ::
  DenseKey key =>
  QueryAtom f key ->
  QueryBinding key ->
  DatabaseRow ->
  Maybe (QueryBinding key)
atomBindRow atom binding row =
  if atomArity atom == PrimArray.sizeofPrimArray children
    then do
      resultBinding <- bindTermValue binding (atomResult atom, rowResult row)
      snd <$> foldM bindChild (0, resultBinding) (atomChildren atom)
    else Nothing
  where
    children =
      rowChildrenArray row
    bindChild (!childIndex, !currentBinding) childTerm =
      fmap
        (\nextBinding -> (childIndex + 1, nextBinding))
        ( bindTermValue
            currentBinding
            (childTerm, PrimArray.indexPrimArray children childIndex)
        )
{-# INLINE atomBindRow #-}

bindTermValue :: DenseKey key => QueryBinding key -> (QueryTerm key, Int) -> Maybe (QueryBinding key)
bindTermValue binding (term, encodedValue) =
  case term of
    QueryBound key
      | encodeDenseKey key == encodedValue ->
          Just binding
      | otherwise ->
          Nothing
    QueryVariable variable ->
      bindVariableValue variable encodedValue binding
{-# INLINE bindTermValue #-}

bindVariableValue :: DenseKey key => QueryVar -> Int -> QueryBinding key -> Maybe (QueryBinding key)
bindVariableValue variable encodedValue binding =
  case Map.lookup variable (queryBindingAssignments binding) of
    Nothing ->
      Just
        binding
          { queryBindingAssignments =
              Map.insert variable (decodeDenseKey encodedValue) (queryBindingAssignments binding)
          }
    Just existingValue
      | encodeDenseKey existingValue == encodedValue ->
          Just binding
      | otherwise ->
          Nothing
{-# INLINE bindVariableValue #-}