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

prompto.expression.FetchOneExpression Maven / Gradle / Ivy

The newest version!
package prompto.expression;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import prompto.compiler.ClassConstant;
import prompto.compiler.CompilerUtils;
import prompto.compiler.Flags;
import prompto.compiler.IConstantOperand;
import prompto.compiler.IOperand;
import prompto.compiler.InterfaceConstant;
import prompto.compiler.MethodConstant;
import prompto.compiler.MethodInfo;
import prompto.compiler.Opcode;
import prompto.compiler.ResultInfo;
import prompto.compiler.StringConstant;
import prompto.declaration.CategoryDeclaration;
import prompto.error.PromptoError;
import prompto.error.SyntaxError;
import prompto.grammar.Identifier;
import prompto.intrinsic.PromptoList;
import prompto.intrinsic.PromptoRoot;
import prompto.parser.CodeSection;
import prompto.parser.Dialect;
import prompto.runtime.Context;
import prompto.statement.UnresolvedCall;
import prompto.store.AttributeInfo;
import prompto.store.DataStore;
import prompto.store.IQuery;
import prompto.store.IQueryBuilder;
import prompto.store.IQueryBuilder.MatchOp;
import prompto.store.IStore;
import prompto.store.IStored;
import prompto.transpiler.Transpiler;
import prompto.type.AnyType;
import prompto.type.CategoryType;
import prompto.type.IType;
import prompto.utils.CodeWriter;
import prompto.utils.IdentifierList;
import prompto.value.IValue;
import prompto.value.NullValue;

public class FetchOneExpression extends CodeSection implements IFetchExpression {

	protected CategoryType type;
	protected IExpression predicate;
	protected IdentifierList include;
	
	public FetchOneExpression(CategoryType type, IExpression predicate, IdentifierList include) {
		this.type = type;
		this.predicate = predicate;
		this.include = include;
	}
	
	@Override
	public String toString() {
		CodeWriter writer = new CodeWriter(Dialect.E, Context.newGlobalsContext());
		toDialect(writer);
		return writer.toString();
	}

	public CategoryType getType() {
		return type;
	}
	
	public IPredicate getPredicate(Context context) {
		IExpression predicate = this.predicate;
		if(predicate instanceof UnresolvedCall)
			predicate = ((UnresolvedCall)predicate).resolve(context);
		return (IPredicate)predicate; // assume this was checked earlier
	}

	@Override
	public void toDialect(CodeWriter writer) {
		switch(writer.getDialect()) {
		case E:
			writer.append("fetch one ");
			if(type!=null) {
				type.toDialect(writer);
				writer.append(" ");
			}
			writer.append("where ");
			predicate.toDialect(writer);
			if(include != null) {
				writer.append(" include ");
				include.toDialect(writer, true);
			}
			break;
		case O:
			writer.append("fetch one ");
			if(type!=null) {
				writer.append("(");
				type.toDialect(writer);
				writer.append(") ");
			}
			writer.append("where (");
			predicate.toDialect(writer);
			writer.append(")");
			if(include != null) {
				writer.append(" include (");
				include.toDialect(writer, false);
				writer.append(")");
			}
			break;
		case M:
			writer.append("fetch one ");
			if(type!=null) {
				type.toDialect(writer);
				writer.append(" ");
			}
			writer.append("where ");
			predicate.toDialect(writer);
			if(include != null) {
				writer.append(" include ");
				include.toDialect(writer, false);
			}
			break;
		}
	}
	
	@Override
	public IType check(Context context) {
		IType type = checkType(context);
		checkPredicate(context);
		checkInclude(context);
		return type!=null ? type : AnyType.instance();
	}
	
	protected void checkInclude(Context context) {
		/*if(include != null) {
			for(Identifier id : include)
				checkAttribute(context, id);
		}*/
	}
	
	protected void checkPredicate(Context context) {
		if(predicate instanceof IPredicate)
			((IPredicate)predicate).checkQuery(context);
		else
			context.getProblemListener().reportIllegalPredicate(this, predicate);
	}

	protected IType checkType(Context context) {
		if(type!=null) {
			CategoryDeclaration decl = context.getRegisteredDeclaration(CategoryDeclaration.class, type.getTypeNameId());
			if(decl==null)
				context.getProblemListener().reportUnknownCategory(type, type.getTypeName());
			if(!decl.isStorable(context))
				context.getProblemListener().reportNotStorable(this, type.getTypeName());
			context = context.newInstanceContext(decl.getType(context), true);
		}
		return type!=null ? type : AnyType.instance();
	}

	@Override
	public Object fetchRaw(IStore store) {
		IQuery query = buildFetchOneQuery(Context.newGlobalsContext(), store);
		return store.fetchOne(query);
	}
	
	@Override
	public IValue fetch(Context context, IStore store) throws PromptoError {
		IQuery query = buildFetchOneQuery(context, store);
		IStored stored = store.fetchOne(query);
		if(stored==null)
			return NullValue.instance();
		else {
			@SuppressWarnings("unchecked")
			PromptoList categories = ((PromptoList)stored.getData("category"));
			String actualTypeName = categories.getLast();
			CategoryType actualType = new CategoryType(new Identifier(actualTypeName));
			if(type!=null)
				actualType.setMutable(type.isMutable());
			IValue value = actualType.newInstance(context, stored);
			if(value!=null)
				return value;
			if(type!=null) 
				value = type.newInstance(context, stored);
			if(value!=null)
				return value;
			else
				return NullValue.instance();
		}
	}
	
	public IQuery buildFetchOneQuery(Context context, IStore store) {
		IQueryBuilder builder = store.newQueryBuilder();
		if(type!=null) {
			builder.verify(AttributeInfo.CATEGORY, MatchOp.HAS, type.getTypeName());
		}
		if(predicate!=null) {
			if(!(predicate instanceof IPredicate))
				throw new SyntaxError("Filtering expression must be a predicate !");
			((IPredicate)predicate).interpretQuery(context, builder, store);
		}
		if(type!=null && predicate!=null)
			builder.and();
		if(include!=null)
			builder.project(include.stream().map(Identifier::toString).collect(Collectors.toList()));
		return builder.build();
	}

	@Override
	public ResultInfo compile(Context context, MethodInfo method, Flags flags) {
		compileNewQueryBuilder(context, method, flags); // -> IStore, IQueryBuilder
		compilePredicates(context, method, flags); // -> IStore, IQueryBuilder
		compileProject(context, method, flags); // -> IStore, IQueryBuilder
		compileBuildQuery(context, method, flags); // -> IStore, IQuery
		compileFetchOne(context, method, flags); // -> IStored
		return compileInstantiation(context, method, flags);
	}

	protected void compileBuildQuery(Context context, MethodInfo method, Flags flags) {
		// need a query
		InterfaceConstant i = new InterfaceConstant(IQueryBuilder.class, "build", IQuery.class);
		method.addInstruction(Opcode.INVOKEINTERFACE, i);
	}

	private ResultInfo compileInstantiation(Context context, MethodInfo method, Flags flags) {
		boolean mutable = this.type!=null ? this.type.isMutable() : false;
		method.addInstruction(mutable ? Opcode.ICONST_1 : Opcode.ICONST_0);
		MethodConstant m = new MethodConstant(PromptoRoot.class, "newInstance", IStored.class, boolean.class, PromptoRoot.class);
		method.addInstruction(Opcode.INVOKESTATIC, m);
		if(type!=null) {
			method.addInstruction(Opcode.CHECKCAST, new ClassConstant(type.toJavaType(context)));
			return new ResultInfo(type.toJavaType(context));
		} else
			return new ResultInfo(AnyType.instance().toJavaType(context));
	}

	protected void compileFetchOne(Context context, MethodInfo method, Flags flags) {
		InterfaceConstant i = new InterfaceConstant(IStore.class, "fetchOne", IQuery.class, IStored.class);
		method.addInstruction(Opcode.INVOKEINTERFACE, i);
	}

	protected void compilePredicates(Context context, MethodInfo method, Flags flags) {
		if(type!=null) {
			AttributeInfo info = AttributeInfo.CATEGORY;
			CompilerUtils.compileAttributeInfo(context, method, flags, info);
			CompilerUtils.compileJavaEnum(context, method, flags, MatchOp.HAS);
			method.addInstruction(Opcode.LDC, new StringConstant(type.toString()));
			InterfaceConstant i = new InterfaceConstant(IQueryBuilder.class, "verify", 
					AttributeInfo.class, MatchOp.class, Object.class, IQueryBuilder.class);
			method.addInstruction(Opcode.INVOKEINTERFACE, i);
		}
		if(predicate!=null)
			((IPredicate)predicate).compileQuery(context, method, flags);
		if(type!=null && predicate!=null) {
			InterfaceConstant i = new InterfaceConstant(IQueryBuilder.class, "and", IQueryBuilder.class);
			method.addInstruction(Opcode.INVOKEINTERFACE, i);
		}
	}
	
	protected void compileProject(Context context, MethodInfo method, Flags flags) {
		if(include != null) {
			CompilerUtils.compileNewInstance(method, ArrayList.class);
			for(Identifier id : include) {
				method.addInstruction(Opcode.DUP); // need to keep a reference to the list on top of stack
				IConstantOperand operand = new StringConstant(id.toString());
				method.addInstruction(Opcode.LDC_W, operand);
				IOperand c = new MethodConstant(ArrayList.class, "add", Object.class, boolean.class);
				method.addInstruction(Opcode.INVOKEVIRTUAL, c);
				method.addInstruction(Opcode.POP); // consume the returned boolean		
			}
			InterfaceConstant i = new InterfaceConstant(IQueryBuilder.class, "project", List.class, IQueryBuilder.class);
			method.addInstruction(Opcode.INVOKEINTERFACE, i);
		}
	}
	
	protected void compileNewQueryBuilder(Context context, MethodInfo method, Flags flags) {
		// need the data store
		MethodConstant m = new MethodConstant(DataStore.class, "getInstance", IStore.class);
		method.addInstruction(Opcode.INVOKESTATIC, m);
		// need a copy for fetch one
		method.addInstruction(Opcode.DUP);
		// need a query factory
		InterfaceConstant i = new InterfaceConstant(IStore.class, "newQueryBuilder", IQueryBuilder.class);
		method.addInstruction(Opcode.INVOKEINTERFACE, i);
	}
	
	@Override
	public void declare(Transpiler transpiler) {
	    transpiler.require("MatchOp");
	    transpiler.require("DataStore");
	    transpiler.require("AttributeInfo");
	    transpiler.require("TypeFamily");
	    transpiler.require("NativeError"); // for NotMutableError
	    if (type != null)
	        type.declare(transpiler);
	    if (predicate instanceof IPredicate)
	        ((IPredicate)predicate).declareQuery(transpiler);
	}
	
	@Override
	public boolean transpile(Transpiler transpiler) {
	    transpiler.append("(function() {").indent();
	    transpileQuery(transpiler);
	    transpiler.append("var $stored = $DataStore.instance.fetchOne(builder.build());").newLine();
	    transpileConvert(transpiler, "result");
	    transpiler.append("return result;").dedent();
	    transpiler.append("})()");
	    return false;
	}

	protected void transpileConvert(Transpiler transpiler, String varName) {
		transpiler.append("var ").append(varName).append(" = null;").newLine();
		transpiler.append("if($stored!=null) {").indent();
	    transpiler.append("var $name = $stored.getData('category').slice(-1)[0];").newLine();
	    transpiler.append("var $type = eval($name);").newLine();
	    transpiler.append(varName).append(" = new $type(null, {}, ").append(this.type!=null && this.type.isMutable()).append(");").newLine();
	    transpiler.append(varName).append(".fromStored($stored);").dedent().append('}').newLine();
	}

	protected void transpileQuery(Transpiler transpiler) {
	    transpiler.append("var builder = $DataStore.instance.newQueryBuilder();").newLine();
	    if (type != null)
	        transpiler.append("builder.verify(new AttributeInfo('category', TypeFamily.TEXT, true, null), MatchOp.HAS, '").append(this.type.getTypeName()).append("');").newLine();
	    if (predicate instanceof IPredicate)
	        ((IPredicate)predicate).transpileQuery(transpiler, "builder");
	    if (include != null) {
	    	transpiler.append("builder.project([");
	    	include.forEach(id->transpiler.append('"').append(id.toString()).append('"').append(", "));
	    	transpiler.trimLast(", ".length());
	    	transpiler.append("]);").newLine();
	    }
	    if (type != null && predicate instanceof IPredicate)
	        transpiler.append("builder.and();").newLine();
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy