tom-weatherhead/thaw-grammar

View on GitHub
src/languages/prolog/domain-object-model/prolog-clause.ts

Summary

Maintainability
B
4 hrs
Test Coverage
// prolog-clause.ts

import { IImmutableSet } from 'thaw-common-utilities.ts';

import { PrologGlobalInfo } from './prolog-global-info';
import { PrologGoal } from './prolog-goal';
import { createSubstitution } from './prolog-substitution';
// import { PrologVariable } from './prolog-variable';

// import { IPrologExpression } from './interfaces/iprolog-expression';
import { IPrologNumber } from './interfaces/iprolog-number';
import { ISubstitution } from './interfaces/isubstitution';
import { IPrologVariable } from './interfaces/ivariable';

export class PrologClause /* implements IPrologExpression */ {
    public readonly Lhs: PrologGoal;
    public readonly Rhs: PrologGoal[];

    constructor(lhs: PrologGoal, rhs: PrologGoal[]) {
        this.Lhs = lhs;
        this.Rhs = rhs;
    }

    public toString(): string {
        let tail = '';

        if (this.Rhs.length > 0) {
            tail = ' :- ' + this.Rhs.map((goal: PrologGoal) => goal.toString()).join(', ');
        }

        return this.Lhs.toString() + tail;
    }

    // public override bool Equals(object obj)
    // {

    //     var otherClause = obj as PrologClause;

    //     if (otherClause == null || !Lhs.Equals(otherClause.Lhs) || Rhs.Count != otherClause.Rhs.Count)
    //     {
    //         return false;
    //     }

    //     for (var i = 0; i < Rhs.Count; ++i)
    //     {

    //         if (!Rhs[i].Equals(otherClause.Rhs[i]))
    //         {
    //             return false;
    //         }
    //     }

    //     return true;
    // }

    public FindBindingVariables(): IImmutableSet<IPrologVariable> {
        let result = this.Lhs.FindBindingVariables();

        for (const subgoal of this.Rhs) {
            result = result.union(subgoal.FindBindingVariables());
        }

        return result;
    }

    public GetListOfBindingVariables(): IPrologVariable[] {
        return this.FindBindingVariables().toArray();
    }

    public ContainsVariable(v: IPrologVariable): boolean {
        return (
            this.ContainsVariable(v) ||
            this.Rhs.some((goal: PrologGoal) => goal.ContainsVariable(v))
        );
    }

    public ApplySubstitution(substitution: ISubstitution): PrologClause {
        return new PrologClause(
            this.Lhs.ApplySubstitution(substitution) as PrologGoal,
            this.Rhs.map(
                (subgoal: PrologGoal) => subgoal.ApplySubstitution(substitution) as PrologGoal
            )
        );
    }

    private isVariableInArrayOfVariables(v: IPrologVariable, a: IPrologVariable[]): boolean {
        return typeof a.find((vv: IPrologVariable) => vv.Name === v.Name) !== 'undefined';
    }

    public RenameVariables(
        variablesToAvoid: IImmutableSet<IPrologVariable>,
        globalInfo: PrologGlobalInfo
    ): PrologClause {
        const oldVariables = this.FindBindingVariables();
        const substitution = createSubstitution();
        const arrayOfOldVariables = oldVariables.toArray();
        const arrayOfVariablesToAvoid = variablesToAvoid.toArray();

        // for (const oldVariable of oldVariables.getIterator()) {
        for (const oldVariable of oldVariables) {
            // if (!variablesToAvoid.contains(oldVariable)) {
            if (!this.isVariableInArrayOfVariables(oldVariable, arrayOfVariablesToAvoid)) {
                continue;
            }

            let newVariable: IPrologVariable;

            do {
                newVariable = globalInfo.GetNextUniqueVariable();
                // console.log(
                //     `Clause.RenameVariables() : Name of new uniqueVariable: '${newVariable.Name}'`
                // );
            } while (
                // oldVariables.contains(newVariable) ||
                // variablesToAvoid.contains(newVariable)
                this.isVariableInArrayOfVariables(newVariable, arrayOfOldVariables) ||
                this.isVariableInArrayOfVariables(newVariable, arrayOfVariablesToAvoid)
            );

            substitution.SubstitutionList.set(oldVariable.Name, newVariable); // This is safe because all of the oldVariables and newVariables are unique.
            //substitution.SubstitutionList[oldVariable] = globalInfo.GetNextUniqueVariable();    // This would probably work too.
        }

        return this.ApplySubstitution(substitution) as PrologClause;
    }

    public Unify(otherExpr: PrologClause): ISubstitution | undefined {
        const otherClause = otherExpr as PrologClause;

        if (typeof otherClause === 'undefined' || this.Rhs.length !== otherClause.Rhs.length) {
            return undefined;
        }

        let substitution = this.Lhs.Unify(otherClause.Lhs);

        if (typeof substitution === 'undefined') {
            return undefined;
        }

        for (let i = 0; i < this.Rhs.length; ++i) {
            const newGoal1 = this.Rhs[i].ApplySubstitution(substitution);
            const newGoal2 = otherClause.Rhs[i].ApplySubstitution(substitution);
            const substitution2 = newGoal1.Unify(newGoal2);

            if (typeof substitution2 === 'undefined') {
                return undefined;
            }

            substitution = substitution.compose(substitution2);
        }

        return substitution;
    }

    // public IsIsomorphicTo(
    //     otherClause: PrologClause,
    //     variablesToAvoid: Set<PrologVariable> | undefined,
    //     globalInfo: PrologGlobalInfo
    // ): boolean {
    //     if (typeof variablesToAvoid === 'undefined') {
    //         variablesToAvoid = this.FindBindingVariables();
    //     }

    //     otherClause = otherClause.RenameVariables(variablesToAvoid, globalInfo);

    //     const unifier = this.Unify(otherClause);

    //     return typeof unifier !== 'undefined' && unifier.IsOneToOne;
    // }

    public get IsGround(): boolean {
        return this.Lhs.IsGround && this.Rhs.every((goal: PrologGoal) => goal.IsGround);
    }

    public EvaluateToNumber(): IPrologNumber | undefined {
        return undefined;
    }
}