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

com.redhat.ceylon.compiler.js.Destructurer Maven / Gradle / Ivy

There is a newer version: 1.3.3
Show newest version
package com.redhat.ceylon.compiler.js;

import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Set;

import com.redhat.ceylon.compiler.js.util.JsIdentifierNames;
import com.redhat.ceylon.compiler.js.util.JsWriter;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.model.typechecker.model.Value;

/** Recursively generates code for a destructuring pattern that may contain other
 * patterns.
 * 
 * @author Enrique Zamudio
 */
public class Destructurer extends Visitor {

    private final GenerateJsVisitor gen;
    private final JsWriter jsw;
    private final JsIdentifierNames names;
    private final String expvar;
    private final Set directAccess;
    private boolean first;
    private final boolean forAssert;
    private final Set added = new HashSet<>();
    private final Set attribs = new HashSet<>();
    private final Set caps = new HashSet<>();
    private final Map spread = new IdentityHashMap<>();

    /** Generate the code for the specified pattern. If null is passed instead of a
     * generator, no code is output but the patterns are still visited and their
     * declarations gathered.
     * @param that The pattern to visit.
     * @param gen The generator to output the code.
     * @param directAccess the set in which to store the declarations for direct access.
     * @param expvar the name of the variable storing the expression for destructuring.
     * @param first If false, a comma is output before the very first declaration is output.
     * @param forAssert indices if the destructuring is inside an assert, to try and coerce dynamic
     * objects. */
    public Destructurer(final Tree.Pattern that, final GenerateJsVisitor gen,
            final Set directAccess, final String expvar, boolean first, final boolean forAssert) {
        this.gen = gen;
        jsw = gen == null ? null : gen.out;
        names = gen == null ? null : gen.getNames();
        this.directAccess = directAccess;
        this.expvar = expvar;
        this.first=first;
        this.forAssert=forAssert;
        that.visit(this);
        if (jsw != null && !attribs.isEmpty()) {
            for (Value attr : attribs) {
                jsw.write(";", names.self((TypeDeclaration)attr.getContainer()), ".",
                        names.name(attr), "=", names.name(attr));
            }
        }
    }

    public void visit(final Tree.TuplePattern that) {
        int idx=0;
        for (Tree.Pattern p : that.getPatterns()) {
            if (p instanceof Tree.VariablePattern) {
                final boolean isVariadic = ((Tree.VariablePattern)p).getVariable().getType() instanceof Tree.SequencedType;
                final int minLength = isVariadic ? p.getUnit().getTupleMinimumLength(
                        ((Tree.VariablePattern) p).getVariable().getDeclarationModel().getType()) : 0;
                if (minLength > 0) {
                    spread.put(((Tree.VariablePattern) p).getVariable(), minLength);
                }
                p.visit(this);
                if (jsw != null) {
                    final boolean useRest = isVariadic && idx==1;
                    if (isVariadic) {
                        jsw.write(useRest?".rest":".skip(");
                    } else {
                        jsw.write(".$_get(");
                    }
                    if (!useRest) {
                        jsw.write(Integer.toString(idx++), ")");
                    }
                    if (isVariadic) {
                        if (minLength > 0) {
                            jsw.write(")");
                        } else if (!useRest) {
                            jsw.write(".sequence()");
                        }
                    }
                }
            } else {
                added.addAll(new Destructurer(p, gen, directAccess, expvar+".$_get("+(idx++)+")",
                        first && idx==0, forAssert).getVariables());
            }
        }
    }

    public void visit(final Tree.KeyValuePattern that) {
        if (that.getKey() instanceof Tree.VariablePattern) {
            that.getKey().visit(this);
            if (jsw != null) {
                jsw.write(".key");
            }
        } else {
            added.addAll(new Destructurer(that.getKey(), gen, directAccess, expvar+".key",
                    first, forAssert).getVariables());
            first = false;
        }
        if (that.getValue() instanceof Tree.VariablePattern) {
            that.getValue().visit(this);
            if (jsw != null) {
                jsw.write(".item");
            }
        } else {
            added.addAll(new Destructurer(that.getValue(), gen, directAccess, expvar+".item",
                    false, forAssert).getVariables());
        }
    }

    public void visit(final Tree.VariablePattern that) {
        Tree.Variable v = that.getVariable();
        final Value d = v.getDeclarationModel();
        if (directAccess != null) {
            directAccess.add(d);
        }
        if (d.isClassOrInterfaceMember()) {
            attribs.add(d);
        }
        if (d.isCaptured()) {
            caps.add(d);
        }
        added.add(v);
        if (first) {
            first=false;
        } else if (jsw != null) {
            jsw.write(",");
        }
        if (jsw != null) {
            jsw.write(names.name(d), "=");
            int minLength = spread.containsKey(v) ? spread.get(v) : 0;
            if (minLength > 0) {
                jsw.write(gen.getClAlias(), "$cksprdstr$(", Integer.toString(minLength),
                        ",\'", v.getDeclarationModel().getType().asString(), "','",
                        v.getIdentifier().getText(), "','", v.getLocation(), "','", v.getUnit().getFilename(), "',");
            }
            jsw.write(expvar);
        }
    }

    /** Returns the declarations gathered by this Destructurer. */
    public Set getDeclarations() {
        final HashSet decs = new HashSet<>(added.size());
        for (Tree.Variable v : added) {
            decs.add(v.getDeclarationModel());
        }
        return decs;
    }
    /** Returns the variables declared inside the destructuring. */
    public Set getVariables() {
        return added;
    }

    public Set getCapturedValues() {
        return caps;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy