package prolog.builtins import prolog.Answers import prolog.Substitutions import prolog.ast.terms.Goal import prolog.ast.terms.Operator import prolog.ast.terms.Term import prolog.flags.AppliedCut import prolog.logic.applySubstitution class Call(private val goal: Term) : Operator("call", rightOperand = goal) { override fun satisfy(subs: Substitutions): Answers { val appliedGoal = applySubstitution(goal, subs) as Goal return appliedGoal.satisfy(subs) } } /** * Make a possibly nondeterministic [Goal] semideterministic, i.e. succeed at most once. */ class Once(private val goal: Term) : Operator("once", rightOperand = goal) { private val conjunction = Conjunction(Call(goal), Cut()) override fun satisfy(subs: Substitutions): Answers = conjunction.satisfy(subs).take(1) } /** * Calls [Goal] once, but succeeds, regardless of whether Goal succeeded or not. */ class Ignore(goal: Term) : Operator("ignore", rightOperand = goal) { private val disjunction = Disjunction( Conjunction(Call(goal), Cut()), True ) override fun satisfy(subs: Substitutions): Answers = sequence { disjunction.satisfy(subs).forEach { result -> result.fold( onSuccess = { newSubs -> yield(Result.success(newSubs)) }, onFailure = { failure -> if (failure is AppliedCut && failure.subs != null) { yield(Result.success(failure.subs)) } else { yield(Result.failure(failure)) } } ) } } }