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

prompto.expression.CastExpression Maven / Gradle / Ivy

The newest version!
package prompto.expression;

import java.lang.reflect.Type;
import java.util.List;
import java.util.stream.Collectors;

import prompto.compiler.ClassConstant;
import prompto.compiler.CompilerUtils;
import prompto.compiler.Flags;
import prompto.compiler.InterfaceType;
import prompto.compiler.MethodConstant;
import prompto.compiler.MethodInfo;
import prompto.compiler.Opcode;
import prompto.compiler.ResultInfo;
import prompto.compiler.StringConstant;
import prompto.declaration.IDeclaration;
import prompto.declaration.IMethodDeclaration;
import prompto.error.PromptoError;
import prompto.intrinsic.PromptoProxy;
import prompto.param.ParameterList;
import prompto.parser.CodeSection;
import prompto.runtime.Context;
import prompto.runtime.Context.MethodDeclarationMap;
import prompto.transpiler.Transpiler;
import prompto.type.AnyType;
import prompto.type.DecimalType;
import prompto.type.IType;
import prompto.type.IntegerType;
import prompto.type.IterableType;
import prompto.type.MethodType;
import prompto.type.NativeType;
import prompto.type.VoidType;
import prompto.utils.CodeWriter;
import prompto.value.DecimalValue;
import prompto.value.IValue;
import prompto.value.IntegerValue;
import prompto.value.NullValue;

public class CastExpression extends CodeSection implements IExpression {
	
	IExpression expression;
	IType type;
	boolean mutable;
	
	public CastExpression(IExpression expression, IType type, boolean mutable) {
		this.expression = expression;
		this.type = type.anyfy();
		this.mutable = mutable;
	}
	
	@Override
	public String toString() {
		return expression.toString() + " as " + (mutable ? "mutable " : "") + type.toString();
	}
	
	@Override
	public IType check(Context context) {
		IType actual = expression.check(context).anyfy();
		IType target = getTargetType(context);
		if(target==null)
			return VoidType.instance();
		// check Any
		if(actual==AnyType.instance())
			return target;
		// check upcast
		if(target.isAssignableFrom(context, actual))
			return target;
		// check downcast
		if(actual.isAssignableFrom(context, target))
			return target;
		context.getProblemListener().reportIncompatibleTypes(this, actual, target);
		return VoidType.instance();
	}

	private IType getTargetType(Context context) {
		return getTargetType(context, type, mutable);
	}
	
	private static IType getTargetType(Context context, IType type, boolean mutable) {
		if(type instanceof IterableType) {
			IType itemType = ((IterableType)type).getItemType();
			IType resolvedItemType = getTargetType(context, itemType, false);
			if(resolvedItemType==null) {
				context.getProblemListener().reportUnknownCategory(type, itemType.getTypeName());
				return null;
			} else
				return ((IterableType)type).withItemType(resolvedItemType).asMutable(context, mutable);
		} else if(type instanceof NativeType)
			return type.asMutable(context, mutable);
		else
			return getTargetAtomicType(context, type, mutable);
	}
	
	private static IType getTargetAtomicType(Context context, IType type, boolean mutable) {
		IDeclaration decl = context.getRegisteredDeclaration(IDeclaration.class, type.getTypeNameId());
		if(decl==null) {
			context.getProblemListener().reportUnknownCategory(type, type.getTypeName());
			return null;
		} else if(decl instanceof MethodDeclarationMap) {
			MethodDeclarationMap map = (MethodDeclarationMap)decl;
			if(map.size()==1)
				return new MethodType(map.getFirst());
			else {
				context.getProblemListener().reportAmbiguousIdentifier(type, type.getTypeName());
				return null;
			}
		} else
			return decl.getType(context).asMutable(context, mutable);
	}

	@Override
	public IValue interpret(Context context) throws PromptoError {
		IValue value = expression.interpret(context);
		if(value!=null && value!=NullValue.instance()) {
			IType target = getTargetType(context);
			if(!target.equals(value.getType())) {
				if(target==DecimalType.instance() && value instanceof IntegerValue)
					value = new DecimalValue(((IntegerValue)value).doubleValue());
				else if(target==IntegerType.instance() && value instanceof DecimalValue)
					value = new IntegerValue(((DecimalValue)value).longValue());
				// if downcasting set value type
				else if(value.getType().isAssignableFrom(context, target))
					value.setType(target);
				// support upcasting
				else if(!target.isAssignableFrom(context, value.getType()))
					context.getProblemListener().reportIncompatibleTypes(this, value.getType(), target);
			}
		}
		return value;
	}
	
	@Override
	public ResultInfo compile(Context context, MethodInfo method, Flags flags) {
		IType target = getTargetType(context);
		if(target instanceof MethodType)
			return compileWithProxy(context, method, flags, (MethodType)target);
		else
			return compileCast(context, method, flags, target);
	}
	
	private ResultInfo compileWithProxy(Context context, MethodInfo method, Flags flags, MethodType target) {
		IMethodDeclaration decl = target.getMethod();
		ParameterList args = decl.getParameters();
		// the JVM can only cast to declared types, so we need a proxy to convert the FunctionalInterface call into the concrete one
		// 1st parameter is method reference
		expression.compile(context, method, flags); // this would return a lambda
		// what interface we are casting to
		ClassConstant dest = new ClassConstant(target.toJavaType(context));
		method.addInstruction(Opcode.LDC, dest);
		// method name
		InterfaceType intf = new InterfaceType(args, decl.getReturnType());
		String methodName = intf.getInterfaceMethodName(); 
		method.addInstruction(Opcode.LDC, new StringConstant(methodName));
		// method arg types
		List javaTypes = args.stream().map(arg->arg.getJavaType(context)).collect(Collectors.toList());
		CompilerUtils.compileClassConstantsArray(method, javaTypes);
		// create the proxy
		MethodConstant m = new MethodConstant(PromptoProxy.class, "newProxy", Object.class, Class.class, String.class, Class[].class, Object.class);
		method.addInstruction(Opcode.INVOKESTATIC, m);
		method.addInstruction(Opcode.CHECKCAST, dest);
		return new ResultInfo(dest.getType());
	}


	public ResultInfo compileCast(Context context, MethodInfo method, Flags flags, IType target) {
		ResultInfo src = expression.compile(context, method, flags);
		Type dest = target.toJavaType(context);
		if(dest==Long.class)
			return CompilerUtils.numberToLong(method, src);
		else if(dest==Double.class)
			return CompilerUtils.numberToDouble(method, src);
		ClassConstant c = new ClassConstant(dest);
		method.addInstruction(Opcode.CHECKCAST, c);
		return new ResultInfo(dest);
	}

	@Override
	public void toDialect(CodeWriter writer) {
		switch(writer.getDialect()) {
		case E:
		case M:
			expression.toDialect(writer);
			writer.append(" as ");
			if(mutable)
				writer.append("mutable ");
			type.toDialect(writer);
			break;
		case O:
			writer.append("(");
			if(mutable)
				writer.append("mutable ");
			type.toDialect(writer);
			writer.append(")");
			expression.toDialect(writer);
			break;
		}
		
	}
	
	@Override
	public void declare(Transpiler transpiler) {
		expression.declare(transpiler);
		IType target = getTargetType(transpiler.getContext());
		if(target!=null)
			target.declare(transpiler);
	}
	
	
	@Override
	public boolean transpile(Transpiler transpiler) {
	    IType expType = this.expression.check(transpiler.getContext());
		IType target = getTargetType(transpiler.getContext());
	    if(expType==DecimalType.instance() && target==IntegerType.instance()) {
	        transpiler.append("Math.floor(");
	        this.expression.transpile(transpiler);
	        transpiler.append(")");
	    } else
	        this.expression.transpile(transpiler);
	    return false;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy