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

prompto.declaration.AttributeDeclaration Maven / Gradle / Ivy

The newest version!
package prompto.declaration;

import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

import prompto.compiler.ClassConstant;
import prompto.compiler.ClassFile;
import prompto.compiler.CompilerException;
import prompto.compiler.CompilerUtils;
import prompto.compiler.Descriptor;
import prompto.compiler.FieldConstant;
import prompto.compiler.FieldInfo;
import prompto.compiler.Flags;
import prompto.compiler.IVerifierEntry.VerifierType;
import prompto.compiler.MethodConstant;
import prompto.compiler.MethodInfo;
import prompto.compiler.Opcode;
import prompto.compiler.StackLocal;
import prompto.constraint.IAttributeConstraint;
import prompto.error.PromptoError;
import prompto.error.SyntaxError;
import prompto.expression.IExpression;
import prompto.grammar.Identifier;
import prompto.runtime.Context;
import prompto.store.AttributeInfo;
import prompto.store.FamilyInfo;
import prompto.transpiler.Transpiler;
import prompto.type.IType;
import prompto.type.IterableType;
import prompto.type.NativeType;
import prompto.utils.CodeWriter;
import prompto.utils.IdentifierList;
import prompto.utils.Logger;
import prompto.value.IValue;

public class AttributeDeclaration extends BaseDeclaration {
	
	static Logger logger = new Logger();
	
	IType type;
	IAttributeConstraint constraint;
	IdentifierList indexTypes;
	boolean storable = false;
	
	public AttributeDeclaration(Identifier id, IType type) {
		this(id, type, null, null);
	}

	public AttributeDeclaration(Identifier id, IType type, IAttributeConstraint constraint) {
		this(id, type, constraint, null);
	}
	
	public AttributeDeclaration(Identifier id, IType type, IdentifierList indexTypes) {
		this(id, type, null, indexTypes);
	}
	
	public AttributeDeclaration(Identifier id, IType type, IAttributeConstraint constraint, IdentifierList indexTypes) {
		super(id);
		this.type = type;
		this.constraint = constraint;
		this.indexTypes = indexTypes;
	}

	public AttributeDeclaration withStorable(boolean set) {
		this.storable = set;
		return this;
	}

	@Override
	public DeclarationType getDeclarationType() {
		return DeclarationType.ATTRIBUTE;
	}
	
	@Override
	public String toString() {
		return this.type.toString() + " " + this.getName();
	}
	
	public AttributeInfo getAttributeInfo(Context context) {
		return getAttributeInfo(context, id->context.getRegisteredDeclaration(IDeclaration.class, id));
	}
	
	
	public AttributeInfo getAttributeInfo(Context context, Function locator) {
		List list = indexTypes==null ?  
				null : 
				( indexTypes.isEmpty() ?
						Collections.singletonList("generic") :
						indexTypes.stream()
							.map((id)->id.toString())
							.collect(Collectors.toList()));
		FamilyInfo family = getFamilyInfo(context, type, locator);
		if(family == null)
			return null;
		else
			return new AttributeInfo(getName(), family.getFamily(), family.isCollection(), list);
	}
	
	private FamilyInfo getFamilyInfo(Context context, IType type, Function locator) {
		if(type == null)
			return null;
		else if(type instanceof NativeType)
			return type.getFamilyInfo(context);
		else if(type instanceof IterableType) {
			FamilyInfo info = getFamilyInfo(context, ((IterableType)type).getItemType(), locator);
			return new FamilyInfo(info.getFamily(), true);
		} else {
			Identifier typeName = type.getTypeNameId();
			try {
				IDeclaration decl = locator.apply(typeName);
				return decl.getType(context).getFamilyInfo(context);
			} catch (Throwable t) {
				logger.error(()->"Could not locate type: " + typeName, t);
				return null;
			}
		}
	}

	public IType getType() {
		return type;
	}
	
	public IAttributeConstraint getConstraint() {
		return constraint;
	}
	
	public void setStorable(boolean storable) {
		this.storable = storable;
	}
	
	@Override
	public boolean isStorable(Context context) {
		return storable;
	}
	
	@Override
	public void declarationToDialect(CodeWriter writer) {
		switch(writer.getDialect()) {
		case E:
			writer.append("define ");
			writer.append(getId());
			writer.append(" as ");
			if(storable)
				writer.append("storable ");
			type.toDialect(writer);
			writer.append(" attribute");
			if(constraint!=null)
				constraint.toDialect(writer);
			if(indexTypes!=null) {
				writer.append(" with ");
				indexTypes.toDialect(writer, true);
				writer.append(" index");
			}
			break;
		case O:
			if(storable)
				writer.append("storable ");
			writer.append("attribute ");
			writer.append(getId());
			writer.append(" : ");
			type.toDialect(writer);
			if(constraint!=null)
				constraint.toDialect(writer);
			if(indexTypes!=null) {
				writer.append(" with index");
				if(!indexTypes.isEmpty()) {
					writer.append(" (");
					indexTypes.toDialect(writer, false);
					writer.append(')');
				}
			}
			writer.append(';');
			break;
		case M:
			if(storable)
				writer.append("storable ");
			writer.append("attr ");
			writer.append(getId());
			writer.append(" (");
			type.toDialect(writer);
			writer.append("):\n");
			writer.indent();
			if(constraint!=null)
				constraint.toDialect(writer);
			if(indexTypes!=null) {
				if(constraint!=null)
					writer.newLine();
				writer.append("index (");
				indexTypes.toDialect(writer, false);
				writer.append(')');
			}
			if(constraint==null && indexTypes==null)
				writer.append("pass");
			writer.dedent();
			break;
		}
	}
	
	@Override
	public void register(Context context) {
		context.registerDeclaration(this);
	}
	
	@Override
	public IType check(Context context) {
		type.checkExists(context);
		return type;
	}
	
	@Override
	public IType getType(Context context) {
		return type;
	}

	public IValue checkValue(Context context, IExpression expression) throws PromptoError {
		IValue value = expression.interpret(context);
		if(constraint==null)
			return value;
		constraint.checkValue(context, value);
		return value;
	}

	public FieldInfo toFieldInfo(Context context) {
		return new FieldInfo(getName(), type.toJavaType(context));
	}

	public ClassFile compile(Context context, String fullName) {
		java.lang.reflect.Type type = CompilerUtils.attributeInterfaceTypeFrom(fullName);
		ClassFile classFile = new ClassFile(type);
		classFile.addModifier(Modifier.ABSTRACT | Modifier.INTERFACE);
		FieldInfo field = this.toFieldInfo(context);
		compileSetterPrototype(context, classFile, field);
		compileGetterPrototype(context, classFile, field);
		compileDefaultChecker(context, classFile, field);
		ClassFile concrete = compileConcreteClass(context, classFile, fullName);
		classFile.addInnerClass(concrete);
		return classFile;
	}

	private void compileDefaultChecker(Context context, ClassFile classFile, FieldInfo field) {
		if(constraint!=null) {
			String checkerName = CompilerUtils.checkerName(field.getName().getValue());
			Descriptor.Method proto = new Descriptor.Method(field.getType(), void.class);
			MethodInfo method = classFile.newMethod(checkerName, proto);
			method.registerLocal("this", VerifierType.ITEM_Object, classFile.getThisClass());
			method.registerLocal("value", VerifierType.ITEM_Object, new ClassConstant(field.getType()));
			constraint.compile(context, method, new Flags());
			method.addInstruction(Opcode.RETURN);
		}
	}

	protected ClassFile compileConcreteClass(Context context, ClassFile outerClass, String fullName) {
		try {
			java.lang.reflect.Type concreteType = CompilerUtils.attributeConcreteTypeFrom(fullName);
			ClassFile classFile = new ClassFile(concreteType);
			classFile.setSuperClass(new ClassConstant(Object.class));
			classFile.addInterface(outerClass.getThisClass());
			FieldInfo field = this.toFieldInfo(context);
			classFile.addField(field);
			compileGetter(context, classFile, field);
			compileSetter(context, classFile, field);
			compileCopyConstructor(context, classFile, field);
			return classFile;
		} catch(SyntaxError e) {
			throw new CompilerException(e);
		}
	}

	private void compileCopyConstructor(Context context, ClassFile classFile, FieldInfo field) {
		Descriptor.Method proto = new Descriptor.Method(field.getType(), void.class);
		MethodInfo method = classFile.newMethod("", proto);
		// call super()
		StackLocal local = method.registerLocal("this", VerifierType.ITEM_UninitializedThis, classFile.getThisClass());
		CompilerUtils.compileALOAD(method, local);
		MethodConstant m = new MethodConstant(classFile.getSuperClass(), "", void.class);
		method.addInstruction(Opcode.INVOKESPECIAL, m);
		// call setter
		CompilerUtils.compileALOAD(method, local);
		StackLocal value = method.registerLocal("%value%", VerifierType.ITEM_Object, new ClassConstant(field.getType()));
		CompilerUtils.compileALOAD(method, value);
		String setterName = CompilerUtils.setterName(field.getName().getValue());
		m = new MethodConstant(classFile.getThisClass(), setterName, field.getType(), void.class);
		method.addInstruction(Opcode.INVOKEVIRTUAL, m);
		// done
		method.addInstruction(Opcode.RETURN);
	}

	private void compileSetter(Context context, ClassFile classFile, FieldInfo field) {
		String setterName = CompilerUtils.setterName(field.getName().getValue());
		Descriptor.Method proto = new Descriptor.Method(field.getType(), void.class);
		MethodInfo method = classFile.newMethod(setterName, proto);
		StackLocal local = method.registerLocal("this", VerifierType.ITEM_Object, classFile.getThisClass());
		StackLocal value = method.registerLocal("%value%", VerifierType.ITEM_Object, new ClassConstant(field.getType()));
		if(constraint!=null) {
			CompilerUtils.compileALOAD(method, local);
			CompilerUtils.compileALOAD(method, value);
			String checkerName = CompilerUtils.checkerName(field.getName().getValue());
			MethodConstant m = new MethodConstant(classFile.getThisClass(), checkerName, field.getType(), void.class);
			method.addInstruction(Opcode.INVOKEVIRTUAL, m);
		}
		CompilerUtils.compileALOAD(method, local);
		CompilerUtils.compileALOAD(method, value);
		FieldConstant fc = new FieldConstant(classFile.getThisClass(), field.getName().getValue(), field.getType());
		method.addInstruction(Opcode.PUTFIELD, fc);
		method.addInstruction(Opcode.RETURN);
	}

	private void compileGetter(Context context, ClassFile classFile, FieldInfo field) {
		String getterName = CompilerUtils.getterName(field.getName().getValue());
		Descriptor.Method proto = new Descriptor.Method(field.getType());
		MethodInfo method = classFile.newMethod(getterName, proto);
		StackLocal local = method.registerLocal("this", VerifierType.ITEM_Object, classFile.getThisClass());
		CompilerUtils.compileALOAD(method, local);
		FieldConstant fc = new FieldConstant(classFile.getThisClass(), field.getName().getValue(), field.getType());
		method.addInstruction(Opcode.GETFIELD, fc);
		method.addInstruction(Opcode.ARETURN);
	}

	private void compileGetterPrototype(Context context, ClassFile classFile, FieldInfo field) {
		String name = CompilerUtils.getterName(field.getName().getValue());
		Descriptor.Method proto = new Descriptor.Method(field.getType());
		MethodInfo method = classFile.newMethod(name, proto);
		method.addModifier(Modifier.ABSTRACT);
	}

	private void compileSetterPrototype(Context context, ClassFile classFile, FieldInfo field) {
		String name = CompilerUtils.setterName(field.getName().getValue());
		Descriptor.Method proto = new Descriptor.Method(field.getType(), void.class);
		MethodInfo method = classFile.newMethod(name, proto);
		method.addModifier(Modifier.ABSTRACT);
	}
	
	@Override
	public void declare(Transpiler transpiler) {
	    this.type.declare(transpiler);
	    if(this.constraint!=null)
	        this.constraint.declare(transpiler, this.getName(), this.type);
	}


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy