/******************************************************************************* * Copyright (c) 2014 Bruno Medeiros and other Contributors. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Bruno Medeiros - initial API and implementation *******************************************************************************/ package dtool.engine.analysis.templates; import static melnorme.utilbox.core.Assert.AssertNamespace.assertNotNull; import static melnorme.utilbox.core.Assert.AssertNamespace.assertTrue; import melnorme.lang.tooling.ast.INamedElementNode; import melnorme.lang.tooling.ast.util.NodeVector; import melnorme.lang.tooling.context.ISemanticContext; import melnorme.lang.tooling.engine.ErrorElement; import melnorme.lang.tooling.engine.ErrorElement.InvalidRefErrorElement; import melnorme.lang.tooling.engine.ErrorElement.Invalid_TypeErrorElement; import melnorme.lang.tooling.engine.OverloadedNamedElement; import melnorme.lang.tooling.engine.PickedElement; import melnorme.lang.tooling.engine.resolver.ReferenceSemantics; import melnorme.lang.tooling.engine.resolver.ResolvableUtil; import melnorme.lang.tooling.symbols.INamedElement; import melnorme.lang.tooling.symbols.ITypeNamedElement; import melnorme.utilbox.collections.ArrayList2; import melnorme.utilbox.collections.ArrayView; import melnorme.utilbox.collections.Indexable; import dtool.ast.definitions.EArcheType; import dtool.ast.definitions.ITemplatableElement; import dtool.ast.definitions.ITemplateParameter; import dtool.ast.definitions.TemplateTupleParam; import dtool.ast.expressions.Resolvable; import dtool.ast.references.RefTemplateInstance; import dtool.ast.references.Reference; import dtool.engine.analysis.templates.TemplateParameterAnalyser.TplMatchLevel; public class RefTemplateInstanceSemantics extends ReferenceSemantics { protected final RefTemplateInstance refTemplateInstance; public RefTemplateInstanceSemantics(RefTemplateInstance refTemplateInstance, PickedElement<?> pickedElement) { super(refTemplateInstance, pickedElement); this.refTemplateInstance = refTemplateInstance; } @Override public INamedElement doResolveTargetElement() { return createTemplateInstance(); } protected INamedElement createTemplateInstance() { INamedElement resolvedTemplate = refTemplateInstance.tplRef.getSemantics(context).resolveTargetElement_(); final ArrayList2<ITemplatableElement> templates = new ArrayList2<>(); if(isTemplate(resolvedTemplate)) { ITemplatableElement defTemplate = (ITemplatableElement) resolvedTemplate; templates.add(defTemplate); } else if(resolvedTemplate instanceof OverloadedNamedElement) { OverloadedNamedElement overload = (OverloadedNamedElement) resolvedTemplate; for (INamedElement overloadElement : overload.getOverloadedElements()) { if(isTemplate(overloadElement)) { ITemplatableElement defTemplate = (ITemplatableElement) overloadElement; templates.add(defTemplate); } else { return overload; } } } else { if(resolvedTemplate.getArcheType() == EArcheType.Error) { return resolvedTemplate; } return new InvalidRefErrorElement(ERROR__NotATemplate, refTemplateInstance, resolvedTemplate, null); } return instantiateTemplate(templates); } protected static boolean isTemplate(INamedElement resolvedTemplate) { if(resolvedTemplate instanceof ITemplatableElement) { ITemplatableElement templatableElement = (ITemplatableElement) resolvedTemplate; return templatableElement.isTemplated(); } return false; } protected INamedElement instantiateTemplate(final ArrayList2<ITemplatableElement> originalTemplates) { Indexable<Resolvable> tplArgs = refTemplateInstance.getEffectiveArguments(); ArrayList2<ITemplatableElement> matchingTemplates = originalTemplates; int tplArgsSize = tplArgs.size(); for (int ix = 0; ix < tplArgsSize; ix++) { Resolvable tplArg = tplArgs.get(ix); TplMatchLevel matchLevel = TplMatchLevel.NONE; ArrayList2<ITemplatableElement> templates = matchingTemplates; matchingTemplates = new ArrayList2<>(); for (ITemplatableElement defTemplate : templates) { if(defTemplate.isTemplated() == false) continue; TplMatchLevel newMatchLevel = getMatchLevel(defTemplate, ix, tplArg, context); if(newMatchLevel == TplMatchLevel.NONE) continue; if(newMatchLevel.isHigherPriority(matchLevel)) { matchLevel = newMatchLevel; matchingTemplates.clear(); // Clear previous less-priority matches } matchingTemplates.add(defTemplate); } } ArrayList2<ITemplatableElement> templates = matchingTemplates; matchingTemplates = new ArrayList2<>(); // Match remaining templates against default args. for (ITemplatableElement defTemplate : templates) { if(canMatchRemainingParameters(tplArgsSize, defTemplate)) { matchingTemplates.add(defTemplate); } } if(matchingTemplates.size() == 0) { return new ErrorElement(ERROR__TPL_REF_MATCHED_NONE, refTemplateInstance, null); } else if(matchingTemplates.size() > 1) { // This should be an error, but because there are limitations in our matching code, // we ignore this error and just use the first match as the effective match. } ITemplatableElement defTemplate = matchingTemplates.get(0); TemplateInstance templateInstance = createTemplateInstance(defTemplate); if(templateInstance == null) { return new ErrorElement(ERROR__TPL_REF_MATCHED_NONE, refTemplateInstance, null); } return templateInstance.instantiatedElement; } protected boolean canMatchRemainingParameters(int tplArgsSize, ITemplatableElement defTemplate) { for (int ix = tplArgsSize; ix < defTemplate.getTemplateParameters().size(); ix++) { TplMatchLevel newMatchLevel = getMatchLevel(defTemplate, ix, null, context); if(newMatchLevel == TplMatchLevel.NONE) { return false; } } return true; } protected static TplMatchLevel getMatchLevel(ITemplatableElement defTemplate, int paramIndex, Resolvable tplArg, ISemanticContext context) { assertTrue(defTemplate.isTemplated()); ArrayView<ITemplateParameter> tplParams = defTemplate.getTemplateParameters(); if(tplParams.size() <= paramIndex) { if(tplParams.size() > 0) { ITemplateParameter lastParam = tplParams.get(tplParams.size()-1); if(lastParam instanceof TemplateTupleParam) { return TplMatchLevel.TUPLE; } } return TplMatchLevel.NONE; } ITemplateParameter tplParam = tplParams.get(paramIndex); return tplParam.getParameterAnalyser().getMatchPriority(tplArg, context); } protected TemplateInstance createTemplateInstance(ITemplatableElement templateDef) { RefTemplateInstance templateRef = refTemplateInstance; Indexable<Resolvable> tplArgs = templateRef.getEffectiveArguments(); NodeVector<ITemplateParameter> templateParams = templateDef.getTemplateParameters(); // TODO: cache for template instance int paramSize = templateParams.size(); ArrayList2<INamedElementNode> instantiatedArgs = new ArrayList2<>(); for (int ix = 0; ix < paramSize; ix++) { ITemplateParameter tplParameter = templateParams.get(ix); INamedElementNode templateArgument = tplParameter.getParameterAnalyser().createTemplateArgument( tplArgs, ix, context); if(templateArgument == null) { return null; } instantiatedArgs.add(templateArgument); } return new TemplateInstance(templateRef, context, templateDef, instantiatedArgs); } /* ----------------- ----------------- */ public static final String ERROR__NotATemplate = ErrorElement.ERROR_PREFIX + "NotATemplate"; public static final String ERROR__TPL_ARG__NotAType = ErrorElement.ERROR_PREFIX + "TemplateArgumentIsNotAType"; public static final String ERROR__TPL_REF_MATCHED_NONE = ErrorElement.ERROR_PREFIX + "InstantiationDidNotMatchAnyTemplate"; public static ITypeNamedElement resolveTargetTypeOfArg(Resolvable target, ISemanticContext parentContext) { assertNotNull(target); if(target instanceof Reference) { Reference reference = (Reference) target; return ResolvableUtil.resolveTargetType(reference, parentContext); } else { return new Invalid_TypeErrorElement(ERROR__TPL_ARG__NotAType, target, null, null); } } }