All Downloads are FREE. Search and download functionalities are using the official Maven repository.

prompto.declaration.BaseMethodDeclaration Maven / Gradle / Ivy

The newest version!
package prompto.declaration;

import java.lang.reflect.Modifier;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import prompto.compiler.ClassFile;
import prompto.compiler.CompilerException;
import prompto.compiler.CompilerUtils;
import prompto.compiler.Descriptor;
import prompto.compiler.Flags;
import prompto.compiler.MethodInfo;
import prompto.error.PromptoError;
import prompto.error.SyntaxError;
import prompto.expression.IExpression;
import prompto.grammar.Argument;
import prompto.grammar.ArgumentList;
import prompto.grammar.Identifier;
import prompto.grammar.Specificity;
import prompto.param.IParameter;
import prompto.param.ParameterList;
import prompto.parser.Dialect;
import prompto.runtime.Context;
import prompto.transpiler.Transpiler;
import prompto.type.IType;
import prompto.type.VoidType;
import prompto.value.IValue;

public abstract class BaseMethodDeclaration extends BaseDeclaration implements IMethodDeclaration {

	CategoryDeclaration memberOf;
	IDeclaration closureOf;
	ParameterList parameters;
	IType returnType;
	
	public BaseMethodDeclaration(Identifier name, ParameterList parameters, IType returnType) {
		super(name);
		this.parameters = parameters!=null ? parameters : new ParameterList();
		this.returnType = returnType;
	}
	
	
	@Override
	public String toString() {
		return (returnType!=null ? returnType.toString() + " " : "")
				+ (memberOf!=null ? memberOf.toString() + "." : "") 
				+ id.toString() + "(" + parameters.toString() + ")";
	}
	
	@Override
	public int hashCode() {
		return (getName() + "/" + getProto()).hashCode();
	}
	
	@Override
	public boolean equals(Object obj) {
		return obj instanceof IMethodDeclaration && equals((IMethodDeclaration)obj);
	}
	
	public boolean equals(IMethodDeclaration method) {
		return (getName() + "/" + getProto()).equals(method.getId() + "" + method.getProto());
	}

	@Override
	public boolean isReference() {
		return false;
	}
	
	@Override
	public DeclarationType getDeclarationType() {
		return DeclarationType.METHOD;
	}
	
	@Override
	public void setMemberOf(CategoryDeclaration declaration) {
		this.memberOf = declaration;
	}
	
	@Override
	public CategoryDeclaration getMemberOf() {
		return memberOf;
	}
	
	@Override
	public void setClosureOf(IDeclaration declaration) {
		this.closureOf = declaration;
	}
	
	@Override
	public IDeclaration getClosureOf() {
		return closureOf;
	}
	
	@Override
	public String getSignature(Dialect dialect) {
		StringBuilder sb = new StringBuilder(getId().toString());
		sb.append('(');
		for(IParameter param : parameters) {
			sb.append(param.getSignature(dialect));
			sb.append(", ");
		}
		if(parameters.size()>0)
			sb.setLength(sb.length()-2); // strip ", "
		sb.append(')');
		return sb.toString();
	}
	
	@Override
	public String getProto() {
		StringBuilder sb = new StringBuilder();
		for(IParameter param : parameters) {
			if(sb.length()>0)
				sb.append('/');
			sb.append(param.getProto());
		}
		return sb.toString();
	}
	
	@Override
	public ParameterList getParameters() {
		return parameters;
	}
	
	@Override
	public IType getReturnType() {
		return returnType;
	}

	@Override
	public void register(Context context) {
		context.registerDeclaration(this);
	}
	
	@Override
	public void registerParameters (Context context) {
		if(parameters!=null)
			parameters.register(context);
	}
		
	@Override
	public IType getType(Context context) {
		try {
			return check(context, false);
		} catch (SyntaxError e) {
			throw new RuntimeException(e);
		}
	}
	
	@Override
	public boolean isAssignableTo(Context context, ArgumentList arguments, boolean checkInstance, boolean allowDerived, Predicate filter) {
		try {
			Context local = context.newLocalContext();
			registerParameters(local);
			ArgumentList argsList = new ArgumentList(arguments);
			for(IParameter parameter : parameters) {
				Argument argument = argsList.find(parameter.getId());
				if(argument==null) {
					IExpression expression = parameter.getDefaultExpression();
					if(expression!=null)
						argument = new Argument(parameter, expression);
				}
				if(argument==null) // missing argument
					return false;
				if(!isArgumentAssignableTo(local, parameter, argument, checkInstance, allowDerived, filter))
					return false;
				argsList.remove(argument);
			}
			return argsList.isEmpty();
		} catch (SyntaxError e) {
			return false;
		}
	}
	
	private boolean isArgumentAssignableTo(Context context, IParameter parameter, Argument argument, boolean useInstance, boolean allowDerived, Predicate filter) {
		Specificity spec = computeSpecificity(context, parameter, argument, useInstance, allowDerived);
		return filter.test(spec);
	}
	

	@Override
	public boolean isAssignableFrom(Context context, ArgumentList arguments) {
		try {
			Context local = context.newLocalContext();
			registerParameters(local);
			ArgumentList argsList = new ArgumentList(arguments);
			for(IParameter parameter : parameters) {
				Argument argument = argsList.find(parameter.getId());
				if(argument==null) {
					IExpression expression = parameter.getDefaultExpression();
					if(expression!=null)
						argument = new Argument(parameter, expression);
				}
				if(argument==null) // missing argument
					return false;
				if(!isArgumentAssignableFrom(local, parameter, argument))
					return false;
				argsList.remove(argument);
			}
			return argsList.isEmpty();
		} catch (SyntaxError e) {
			return false;
		}
	}
	
	private boolean isArgumentAssignableFrom(Context context, IParameter parameter, Argument argument) {
		try {
			IType requiredType = parameter.getType(context);
			IType actualType = argument.checkActualType(context, requiredType, false);
			if(actualType.equals(requiredType)
					|| actualType.isAssignableFrom(context, requiredType)
					|| requiredType.isAssignableFrom(context, actualType))
				return true;
			actualType = argument.resolve(context, this, false, false).check(context);
			return actualType.equals(requiredType)
					|| actualType.isAssignableFrom(context, requiredType)
					|| requiredType.isAssignableFrom(context, actualType);
		} catch(PromptoError error) {
			return false;
		}
	}

	@Override
	public Specificity computeSpecificity(Context context, IParameter parameter, Argument argument, boolean useInstance, boolean allowDerived) {
		try {
			IType requiredType = parameter.getType(context);
			if(requiredType==null)
				return Specificity.INCOMPATIBLE;
			else
				requiredType = requiredType.resolve(context, null);
			IType actualType = argument.checkActualType(context, requiredType, useInstance);
			if(actualType==null)
				return Specificity.INCOMPATIBLE;
			else
				actualType = actualType.resolve(context, null);
			if(actualType.equals(requiredType))
				return Specificity.EXACT;
			else if(requiredType.isAssignableFrom(context, actualType)) 
				return Specificity.INHERITED;
			else if(allowDerived && actualType.isAssignableFrom(context, requiredType)) 
				return Specificity.DERIVED;
		} catch(PromptoError error) {
			error = null; // convenient for debugging
		}
		return Specificity.INCOMPATIBLE; 
	}
	
	@Override
	public IValue interpret(Context context) throws PromptoError {
		throw new InternalError("Should never get there!");
	}
	
	@Override
	public boolean isEligibleAsMain() {
		return false;
	}
	
	@Override
	public void compilePrototype(Context context, boolean isStart, ClassFile classFile) {
		try {
			context = prepareContext(context, isStart);
			IType returnType = check(context, false);
			MethodInfo method = createMethodInfo(context, classFile, returnType, getName());
			method.addModifier(Modifier.ABSTRACT);
		} catch (PromptoError e) {
			throw new CompilerException(e);
		}
	}

	protected Context prepareContext(Context context, boolean isStart) {
		if(isStart) {
			// coming from nowhere, so need a clean context in which to register parameters
			context = context.newLocalContext();
			registerParameters(context);
		}
		return context;
	}
	
	protected MethodInfo createMethodInfo(Context context, ClassFile classFile, IType returnType, String methodName) {
		Descriptor.Method proto = CompilerUtils.createMethodDescriptor(context, parameters, returnType);
		MethodInfo method = classFile.newMethod(methodName, proto); 
		return method;
	}

	@Override
	public void compileParameters(Context context, MethodInfo method, Flags flags, ArgumentList arguments) {
		boolean isFirst = true;
		for(IParameter param : parameters.stripOutTemplateParameters()) {
			param.compileParameter(context, method, flags, arguments, isFirst);
			isFirst = false;
		}
	}
	
	public void declareParameters(Transpiler transpiler) {
		this.parameters.declare(transpiler);
	}

	public void transpileProlog(Transpiler transpiler) {
	    if (this.memberOf!=null) {
	        transpiler.append(this.memberOf.getName());
	        if(this.hasAnnotation(transpiler.getContext(), "@Static"))
	           	transpiler.append(".");
	        else
	        	transpiler.append(".prototype.");
	        transpiler.append(this.getTranspiledName(transpiler.getContext())).append(" = function (");
	    } else
	        transpiler.append("function ").append(this.getTranspiledName(transpiler.getContext())).append(" (");
	    this.parameters.transpile(transpiler);
	    transpiler.append(") {").indent();
	}


	public void transpileEpilog(Transpiler transpiler) {
	    transpiler.dedent().append("}");
	    if(this.memberOf!=null)
	        transpiler.append(";");
	    transpiler.newLine();
	}
	
	@Override
	public String getTranspiledName(Context context) {
	    // if this is a template instance, name is already transpiled
	    if(this.getName().indexOf("$")>0)
	    	return this.getName();
	    else if(this.hasLocalAnnotation("@Callback") || this.hasInheritedAnnotation(context, "@Callback"))
	    	return this.getName();
	    else 
	    	return getTranspiledName(context, this.getName());
	}
	
	@Override
	public String getTranspiledName(Context context, String methodName) {
    	Stream name = Stream.of(methodName);
    	Stream args = this.parameters.stream().map(arg->arg.getTranspiledName(context));
    	return Stream.concat(name, args).collect(Collectors.joining("$"));
	}
	
	
	@Override
	public void transpileMethodType(Transpiler transpiler) {
        IType returnType = this.returnType;
        if(returnType == null)
            returnType = this.check(transpiler.getContext(), true);
        if(returnType == null)
            returnType = VoidType.instance();
        transpiler.append("[");
        if(this.parameters.size() > 0) {
            this.parameters.forEach(param -> transpiler.append("'")
                .append(param.getType(transpiler.getContext()).getTypeName())
                .append("', "));
            transpiler.trimLast(2);
        }
        transpiler.append("], '")
            .append(returnType.getTypeName())
            .append("'");
    }

	@Override
	public boolean hasInheritedAnnotation(Context context, String name) {
		if(memberOf==null)
			return false;
		else
			return getOverriddenMethods(context).anyMatch(m->m.hasLocalAnnotation(name));
	}
	
	protected Stream getOverriddenMethods(Context context) {
		Stream categories = Stream.of(memberOf);
		if(memberOf.getDerivedFrom()!=null)
			categories = Stream.concat(categories, memberOf.getDerivedFrom().stream().map(id->context.getRegisteredDeclaration(CategoryDeclaration.class, id)));
		return categories
				.map(cat->cat.getLocalMethods().stream())
				.flatMap(Function.identity())
				.filter(m->this.getName().equals(m.getName()))
				.filter(m->this.getProto().equals(m.getProto()));
	}
	


}






© 2015 - 2024 Weber Informatics LLC | Privacy Policy