| /* | |
| * Copyright (C) 2017 Apple Inc. All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions | |
| * are met: | |
| * 1. Redistributions of source code must retain the above copyright | |
| * notice, this list of conditions and the following disclaimer. | |
| * 2. Redistributions in binary form must reproduce the above copyright | |
| * notice, this list of conditions and the following disclaimer in the | |
| * documentation and/or other materials provided with the distribution. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| */ | |
| "use strict"; | |
| class Node { | |
| visit(visitor) | |
| { | |
| let visitFunc = visitor["visit" + this.constructor.name]; | |
| if (!visitFunc) | |
| throw new Error("No visit function for " + this.constructor.name + " in " + visitor.constructor.name); | |
| let returnValue = visitFunc.call(visitor, this); | |
| if ("returnValue" in visitor) | |
| returnValue = visitor.returnValue; | |
| return returnValue; | |
| } | |
| static visit(node, visitor) | |
| { | |
| if (node instanceof Node) | |
| return node.visit(visitor); | |
| return node; | |
| } | |
| unify(unificationContext, other) | |
| { | |
| if (!other) | |
| throw new Error("Null other"); | |
| let unifyThis = this.unifyNode; | |
| let unifyOther = other.unifyNode; | |
| if (unifyThis == unifyOther) | |
| return true; | |
| if (unifyOther.typeVariableUnify(unificationContext, unifyThis)) | |
| return true; | |
| return unifyThis.unifyImpl(unificationContext, unifyOther); | |
| } | |
| unifyImpl(unificationContext, other) | |
| { | |
| if (other.typeVariableUnify(unificationContext, this)) | |
| return true; | |
| return this == other; | |
| } | |
| typeVariableUnify(unificationContext, other) | |
| { | |
| return false; | |
| } | |
| _typeVariableUnifyImpl(unificationContext, other) | |
| { | |
| let realThis = unificationContext.find(this); | |
| if (realThis != this) | |
| return realThis.unify(unificationContext, other); | |
| unificationContext.union(this, other); | |
| return true; | |
| } | |
| // Most type variables don't care about this. | |
| prepareToVerify(unificationContext) { } | |
| commitUnification(unificationContext) { } | |
| get unifyNode() { return this; } | |
| get isUnifiable() { return false; } | |
| get isLiteral() { return false; } | |
| get isNative() { return false; } | |
| conversionCost(unificationContext) { return 0; } | |
| equals(other) | |
| { | |
| let unificationContext = new UnificationContext(); | |
| if (this.unify(unificationContext, other) && unificationContext.verify().result) | |
| return unificationContext; | |
| return false; | |
| } | |
| equalsWithCommit(other) | |
| { | |
| let unificationContext = this.equals(other); | |
| if (!unificationContext) | |
| return false; | |
| unificationContext.commit(); | |
| return unificationContext; | |
| } | |
| commit() | |
| { | |
| let unificationContext = new UnificationContext(); | |
| unificationContext.addExtraNode(this); | |
| let result = unificationContext.verify(); | |
| if (!result.result) | |
| throw new WError(node.origin.originString, "Could not infer type: " + result.reason); | |
| unificationContext.commit(); | |
| return unificationContext.find(this); | |
| } | |
| substitute(parameters, argumentList) | |
| { | |
| return this.visit(new Substitution(parameters, argumentList)); | |
| } | |
| substituteToUnification(parameters, unificationContext) | |
| { | |
| return this.substitute( | |
| parameters, | |
| parameters.map(type => unificationContext.find(type))); | |
| } | |
| } |