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

prompto.transpiler.Transpiler Maven / Gradle / Ivy

The newest version!
package prompto.transpiler;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.stream.Collectors;

import prompto.declaration.CategoryDeclaration;
import prompto.declaration.IDeclaration;
import prompto.grammar.Identifier;
import prompto.runtime.Context;
import prompto.type.CategoryType;
import prompto.utils.Logger;
import prompto.utils.ResourceUtils;

public class Transpiler {

	static final Logger logger = new Logger();

	IJSEngine engine;
	Context context;
	Transpiler parent;
	Set declared;
	Set required;
	Set registered;
	int escapeMode;
	Stack lines;
	StringBuilder line;
	List initializers;
	String indents;
	String getterName;
	String setterName;
	
	public Transpiler(IJSEngine engine, Context context) {
		this.engine = engine;
		this.context = context;
		this.declared = new HashSet<>();
	    this.required = new HashSet<>();
	    this.registered = new HashSet<>();
	    this.escapeMode = 0;
	    this.lines = new Stack<>();
	    this.line = new StringBuilder();
	    this.initializers = new ArrayList<>();
	    this.indents = "";
	    this.getterName = null;
	    this.setterName = null;
	    // load polyfills
		this.require("Utils");
		this.require("ReactUtils");
		engine.getPolyfills().forEach(poly->this.require(poly));
 	}
	
	public Transpiler(Transpiler copyFrom, Context context) {
		this.engine = copyFrom.engine;
		this.context = context;
	    this.declared = copyFrom.declared;
	    this.required = copyFrom.required;
	    this.registered = copyFrom.registered;
	    this.escapeMode = copyFrom.escapeMode;
	    this.lines = copyFrom.lines;
	    this.line = copyFrom.line;
	    this.initializers = copyFrom.initializers;
	    this.indents = copyFrom.indents;
	    this.parent = copyFrom;
	    this.getterName = copyFrom.getterName;
	    this.setterName = copyFrom.setterName;
	}

	public IJSEngine getEngine() {
		return engine;
	}
	
	public Context getContext() {
		return context;
	}
	
	public String getGetterName() {
		return getterName;
	}

	public String getSetterName() {
		return setterName;
	}
	
	public int getEscapeMode() {
		return escapeMode;
	}
	
	public Transpiler newLocalTranspiler() {
		Context context = this.context.newLocalContext();
		return this.copyTranspiler(context);
	}

	public Transpiler newChildTranspiler() {
		return newChildTranspiler(this.context.newChildContext());
	}
	
	public Transpiler newChildTranspiler(Context calling) {
		return this.copyTranspiler(calling);
	}
	
	public Transpiler newInstanceTranspiler(CategoryType categoryType) {
		Context context = this.context.newInstanceContext(categoryType, true);
		return this.copyTranspiler(context);
	}
	
	
	public Transpiler newResourceTranspiler() {
		Context context = this.context.newResourceContext();
	    return this.copyTranspiler(context);
	}

	public Transpiler newDocumentTranspiler() {
		Context context = this.context.newDocumentContext(null, true);
	    return this.copyTranspiler(context);
	}

	public Transpiler newGetterTranspiler(String name) {
		Transpiler transpiler = this.newChildTranspiler();
	    transpiler.getterName = name;
	    return transpiler;
	}

	public Transpiler newSetterTranspiler(String name) {
		Transpiler transpiler = this.newChildTranspiler();
	    transpiler.setterName = name;
	    return transpiler;
	}

	public Transpiler copyTranspiler(Context context) {
		Transpiler transpiler = new Transpiler(this, context);
	    return transpiler;
	}
	
	public Transpiler append(Identifier value) {
		this.line.append(value.toString());
		return this;
	}

	public Transpiler append(String value) {
		this.line.append(value);
		return this;
	}
	
	public Transpiler append(char value) {
		this.line.append(value);
		return this;
	}

	public Transpiler append(boolean value) {
		this.line.append(value);
		return this;
	}
	
	public Transpiler indent() {
		return indent(false);
	}

	public Transpiler indent(boolean indentOnly) {
	   if(!indentOnly)
	        this.lines.push(this.line.toString());
	    this.indents += '\t';
	    this.line = new StringBuilder(this.indents);
	    return this;
	}
	
	public Transpiler dedent() {
	    String line = this.line.toString();
	    if(!(line.equals(this.indents)))
	        this.lines.push(line);
	    if(this.indents.isEmpty()) {
	        throw new RuntimeException("Illegal dedent!");
	    }
	    this.indents = this.indents.substring(1);
	    this.line = new StringBuilder(this.indents);
	    return this;
	}
	
	public Transpiler newLine() {
		this.lines.push(this.line.toString());
		this.line = new StringBuilder(this.indents);
		return this;
	}

	
	public Transpiler escape() {
	    this.escapeMode++;
	    return this;
	}
	
	
	public Transpiler unescape() {
	    this.escapeMode--;
	    return this;
	}

	
	public Transpiler trimLast(int count) {
	    this.line.setLength(this.line.length() - count);
	    return this;
	}

	
	public Transpiler printTestName(String testName) {
		this.append("print('").append(testName).append(" test ");
		return this;
	}


	public void flush() {
	    if(this.parent!=null) {
	        this.parent.line = this.line;
	        this.parent.indents = this.indents;
	    }
	}

	public void declare(ITranspilable transpilable) {
		if(transpilable instanceof IDeclaration && ((IDeclaration)transpilable).hasAnnotation(context, "Inlined"))
			return;
		else
			declared.add(transpilable);
	}
	
	public boolean isDeclared(ITranspilable transpilable) {
		if(transpilable instanceof IDeclaration && ((IDeclaration)transpilable).hasAnnotation(context, "Inlined"))
			return true;
		else
			return declared.contains(transpilable);
	}
	
	
	public Set getDeclared() {
		return declared;
	}

	public void require(String script) {
		required.add(script);
	}
	
	public Set getRequired() {
		return required;
	}
	
	public boolean requires(String script) {
		return required.contains(script);
	}
	
	public void register(String script) {
		required.add(script);
		registered.add(script);
	}

	
	@Override
	public String toString() {
		populateLines();
	    return this.lines.stream().collect(Collectors.joining("\n"));
	}
	
	private void populateLines() {
	    this.appendAllRequired();
	    this.appendAllRegistered();
	    this.appendAllDeclared();
	    this.appendAllInitializers();
	    this.flush();
	}

	public void print(PrintWriter printer) {
		populateLines();
	    this.lines.forEach(printer::println);
	}


	private void appendAllDeclared() {
		List list = new ArrayList<>();
		Set set = new HashSet<>();
	    this.declared.forEach(decl -> {
	        if(decl instanceof CategoryDeclaration)
	            ((CategoryDeclaration)decl).ensureDeclarationOrder(this.context, list, set);
	        else
	            list.add(decl);
	    });
	    list.forEach(this::appendOneDeclared);
	}

	private void appendOneDeclared(ITranspilable decl) {
		Transpiler transpiler = this.newLocalTranspiler();
	    decl.transpile(transpiler);
	    transpiler.flush();
	    String line = this.line.toString();
	    if(!(line.equals(this.indents))) {
	        this.lines.push(line);
	        this.line = new StringBuilder(this.indents);
	    }
	    this.lines.push("");
	}
	
	private void appendAllRequired() {
		// make sure DataStore comes before RemoteStore
		if(required.remove("DataStore"))
			appendOneRequired("DataStore");
		required.forEach(this::appendOneRequired);
	}

	private void appendOneRequired(String module) {
		String result = getResourceAsString("prompto/intrinsic/" + module + ".js");
		if(result==null)
			result = getResourceAsString("polyfills/" + module + ".js");
		if(result!=null)
			this.lines.push(result);
		else
			throw new RuntimeException("Could not locate script: " + module);
	}
	
	private void appendAllRegistered() {
		registered.forEach(this::appendOneRegistered);
	}
	
	private void appendOneRegistered(String module) {
		this.lines.push("intrinsic." + module + " = " + module + ";");
	}


	private String getResourceAsString(String path) {
		try {
			return ResourceUtils.getResourceAsString(path);
		} catch(Throwable t) {
			return null;
		}
	}

	public void addInitializer(String line) {
		this.initializers.add(line);
	}

	private void appendAllInitializers() {
		this.initializers.forEach(this.lines::push);
	}


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy