Package wycs.builders

Source Code of wycs.builders.CodeGeneration

package wycs.builders;

import java.math.BigInteger;
import java.util.*;

import wycc.lang.Attribute;
import wycc.lang.NameID;
import wycc.lang.SyntacticElement;
import wycc.util.Pair;
import wycc.util.ResolveError;
import wycc.util.Triple;
import wycs.core.*;
import wycs.syntax.*;
import wyfs.util.Trie;
import static wycc.lang.SyntaxError.*;
import static wycs.transforms.TypePropagation.returnType;

/**
* Responsible for translating an individual <code>WyalFile</code> into a
* <code>WycsFile</code>. By the time this is run, both type propagation and
* constraint expansion (for uninterpreted functions) must already have
* occurred. In most cases, the translation is straightforward as there is a
* one-one correspondence between many Wyal and Wycs constructs. However, some
* differences exist such as, for example, the lack of an implication statement
* and any notion of a list.
*
* @author David J. Pearce
*
*/
public class CodeGeneration {
  private final Wyal2WycsBuilder builder;
  private String filename;

  public CodeGeneration(Wyal2WycsBuilder builder) {
    this.builder = builder;
  }

  public WycsFile generate(WyalFile file) {
    this.filename = file.filename();
    ArrayList<WycsFile.Declaration> declarations = new ArrayList();
    for(WyalFile.Declaration d : file.declarations()) {
      WycsFile.Declaration e = generate(d);
      if(e != null) {
        declarations.add(e);
      }
    }
    return new WycsFile(file.id(),file.filename(),declarations);
  }

  protected WycsFile.Declaration generate(WyalFile.Declaration declaration) {
    if(declaration instanceof WyalFile.Import) {
      // not sure what to do here?
      return null;
    } else if(declaration instanceof WyalFile.Macro) {
      return generate((WyalFile.Macro)declaration);
    } else if(declaration instanceof WyalFile.Function) {
      return generate((WyalFile.Function)declaration);
    } else if(declaration instanceof WyalFile.Assert) {
      return generate((WyalFile.Assert)declaration);
    } else {
      internalFailure("unknown declaration encounterd",filename,declaration);
      return null;
    }
  }

  protected WycsFile.Declaration generate(WyalFile.Macro d) {
    // First, determine function type
    SemanticType from = builder.convert(d.from, d.generics, d);
    SemanticType to = SemanticType.Bool;
    SemanticType.Var[] generics = new SemanticType.Var[d.generics.size()];
    for (int i = 0; i != generics.length; ++i) {
      generics[i] = SemanticType.Var(d.generics.get(i));
    }
    SemanticType.Function type = SemanticType.Function(from, to, generics);
    // Second, generate macro body
    HashMap<String,Code> environment = new HashMap<String,Code>();
    Code parameter = Code.Variable(from, new Code[0], 0,
        attributes(d.from));
    addDeclaredVariables(parameter,d.from,environment);
    Code condition = generate(d.body, environment, d);
    // Third, create declaration
    return new WycsFile.Macro(d.name, type, condition,
        attributes(d));
  }

  protected WycsFile.Declaration generate(WyalFile.Function d) {
    // First, determine function type
    SemanticType from = builder.convert(d.from, d.generics, d);
    SemanticType to = builder.convert(d.to, d.generics, d);
    SemanticType.Var[] generics = new SemanticType.Var[d.generics.size()];
    for (int i = 0; i != generics.length; ++i) {
      generics[i] = SemanticType.Var(d.generics.get(i));
    }
    SemanticType.Function type = SemanticType.Function(from, to, generics);
    // Second, generate function condition (if applicable)
    Code condition = null;
    if (d.constraint != null) {
      HashMap<String,Code> environment = new HashMap<String,Code>();
      Code ret = Code.Variable(to, new Code[0], 0,
          attributes(d.to));
      Code parameter = Code.Variable(from, new Code[0], 1,
          attributes(d.from));
      addDeclaredVariables(parameter,d.from,environment);
      addDeclaredVariables(ret,d.to,environment);
      condition = generate(d.constraint, environment, d);
    }
    // Third, create declaration
    return new WycsFile.Function(d.name, type, condition,
        attributes(d));
  }

  protected void addDeclaredVariables(Code root, TypePattern t,
      HashMap<String, Code> environment) {

    if(t instanceof TypePattern.Leaf) {
      TypePattern.Leaf tl = (TypePattern.Leaf) t;
      if(tl.var != null) {
        environment.put(tl.var.name, root);
      }
    } else if(t instanceof TypePattern.Rational) {
      // TODO: implement me!
    } else if(t instanceof TypePattern.Tuple) {
      TypePattern.Tuple tt = (TypePattern.Tuple) t;
      for (int i = 0; i != tt.elements.size(); ++i) {
        TypePattern p = tt.elements.get(i);
        addDeclaredVariables(
            Code.Load((SemanticType.Tuple) root.type, root, i,
                attributes(t)), p,
            environment);
      }
    } else if(t instanceof TypePattern.Record) {
      // TODO: implement me!
    } else if(t instanceof TypePattern.Intersection) {
      // TODO: implement me!
    } else if(t instanceof TypePattern.Union) {
      // TODO: implement me!
    }
  }

  protected WycsFile.Declaration generate(WyalFile.Assert d) {
    Code condition = generate(d.expr, new HashMap<String,Code>(),d);
    return new WycsFile.Assert(d.message, condition, attributes(d));
  }

  protected Code generate(Expr e, HashMap<String,Code> environment, WyalFile.Context context) {
    if (e instanceof Expr.Variable) {
      return generate((Expr.Variable) e, environment, context);
    } else if (e instanceof Expr.Constant) {
      return generate((Expr.Constant) e, environment, context);
    } else if (e instanceof Expr.Unary) {
      return generate((Expr.Unary) e, environment, context);
    } else if (e instanceof Expr.Binary) {
      return generate((Expr.Binary) e, environment, context);
    } else if (e instanceof Expr.Ternary) {
      return generate((Expr.Ternary) e, environment, context);
    } else if (e instanceof Expr.Nary) {
      return generate((Expr.Nary) e, environment, context);
    } else if (e instanceof Expr.Quantifier) {
      return generate((Expr.Quantifier) e, environment, context);
    } else if (e instanceof Expr.Invoke) {
      return generate((Expr.Invoke) e, environment, context);
    } else if (e instanceof Expr.IndexOf) {
      return generate((Expr.IndexOf) e, environment, context);
    } else {
      internalFailure("unknown expression encountered (" + e + ")",
          filename, e);
      return null;
    }
  }

  protected Code generate(Expr.Variable e, HashMap<String,Code> environment, WyalFile.Context context) {
    SemanticType type = e.attribute(TypeAttribute.class).type;
    return environment.get(e.name);
  }

  protected Code generate(Expr.Constant v, HashMap<String,Code> environment, WyalFile.Context context) {
    return Code.Constant(v.value,
        attributes(v));
  }

  protected Code generate(Expr.Unary e, HashMap<String,Code> environment, WyalFile.Context context) {
    SemanticType type = e.attribute(TypeAttribute.class).type;
    Code operand = generate(e.operand,environment, context);
    Code.Op opcode;
    switch(e.op) {
    case NEG:
      opcode = Code.Op.NEG;
      break;
    case NOT:
      opcode = Code.Op.NOT;
      break;
    case LENGTHOF:
      opcode = Code.Op.LENGTH;
      break;
    default:
      internalFailure("unknown unary opcode encountered (" + e + ")",
          filename, e);
      return null;
    }
    return Code.Unary(type, opcode, operand,
        attributes(e));
  }

  protected Code generate(Expr.Binary e, HashMap<String,Code> environment, WyalFile.Context context) {
    SemanticType type = e.attribute(TypeAttribute.class).type;
    Code lhs = generate(e.leftOperand,environment, context);
    Code rhs = generate(e.rightOperand,environment, context);
    Code.Op opcode;
    switch(e.op) {
    case AND:
      return Code.Nary(type, Code.Op.AND, new Code[] { lhs, rhs },
          attributes(e));
    case OR:
      return Code.Nary(type, Code.Op.OR, new Code[] { lhs, rhs },
          attributes(e));
    case ADD:
      opcode = Code.Op.ADD;
      break;
    case SUB:
      opcode = Code.Op.SUB;
      break;
    case MUL:
      opcode = Code.Op.MUL;
      break;
    case DIV:
      opcode = Code.Op.DIV;
      break;
    case REM:
      opcode = Code.Op.REM;
      break;
    case EQ:
      opcode = Code.Op.EQ;
      break;
    case NEQ:
      opcode = Code.Op.NEQ;
      break;
    case IMPLIES:
      lhs = Code.Unary(type, Code.Unary.Op.NOT, lhs);
      return Code.Nary(type, Code.Op.OR, new Code[] { lhs, rhs },
          attributes(e));
    case IFF:
      Code nLhs = Code.Unary(type, Code.Unary.Op.NOT,lhs);
      Code nRhs = Code.Unary(type, Code.Unary.Op.NOT,rhs);
      lhs = Code.Nary(type, Code.Op.AND, new Code[]{lhs,rhs});
      rhs = Code.Nary(type, Code.Op.AND, new Code[]{nLhs,nRhs});
      return Code.Nary(type, Code.Op.OR, new Code[]{lhs,rhs},
          attributes(e));
    case LT:
      opcode = Code.Op.LT;
      break;
    case LTEQ:
      opcode = Code.Op.LTEQ;
      break;
    case GT: {
      opcode = Code.Op.LT;
      Code tmp = lhs;
      lhs = rhs;
      rhs = tmp;
      break;
    }
    case GTEQ: {
      opcode = Code.Op.LTEQ;
      Code tmp = lhs;
      lhs = rhs;
      rhs = tmp;
      break;
    }
    case IN:
      opcode = Code.Op.IN;
      break;
    case SUBSET:
      opcode = Code.Op.SUBSET;
      break;
    case SUBSETEQ:
      opcode = Code.Op.SUBSETEQ;
      break;
    case SUPSET: {
      opcode = Code.Op.SUBSET;
      Code tmp = lhs;
      lhs = rhs;
      rhs = tmp;
      break;
    }
    case SUPSETEQ: {
      opcode = Code.Op.SUBSETEQ;
      Code tmp = lhs;
      lhs = rhs;
      rhs = tmp;
      break;
    }
    case SETUNION:
    case SETINTERSECTION:
    case SETDIFFERENCE: {
      String fn;
      switch(e.op) {
      case SETUNION:
        fn = "Union";
        break;
      case SETINTERSECTION:
        fn = "Intersect";
        break;
      case SETDIFFERENCE:
        fn = "Difference";
        break;
      default:
        fn = ""; // deadcode
      }
      NameID nid = new NameID(WYCS_CORE_SET,fn);
      SemanticType.Tuple argType = SemanticType.Tuple(type, type);
      SemanticType.Function funType = SemanticType.Function(argType,
          type, ((SemanticType.Set)type).element());
      Code argument = Code.Nary(argType, Code.Op.TUPLE, new Code[] {
          lhs,rhs });
      return Code.FunCall(funType, argument, nid,
          attributes(e));
    }
    case LISTAPPEND: {
      NameID nid = new NameID(WYCS_CORE_LIST,"Append");
      SemanticType.Tuple argType = SemanticType.Tuple(type,type);
      SemanticType[] generics = bindGenerics(nid,argType,e);
      SemanticType.Function funType = SemanticType.Function(argType,
          type,generics);
      Code argument = Code.Nary(argType, Code.Op.TUPLE, new Code[] {
          lhs,rhs });
      return Code.FunCall(funType, argument, nid,
          attributes(e));
    }
    case RANGE: {
      NameID nid = new NameID(WYCS_CORE_LIST,"Range");
      SemanticType.Tuple argType = SemanticType.Tuple(SemanticType.Int,SemanticType.Int);
      SemanticType.Function funType = SemanticType.Function(argType,
          type);
      Code argument = Code.Nary(argType, Code.Op.TUPLE, new Code[] {
          lhs,rhs });
      return Code.FunCall(funType, argument, nid,
          attributes(e));
    }
    default:
      internalFailure("unknown binary opcode encountered (" + e + ")",
          filename, e);
      return null;
    }

    return Code.Binary(type, opcode, lhs, rhs,
        attributes(e));
  }


  protected Code generate(Expr.Ternary e, HashMap<String, Code> environment,
      WyalFile.Context context) {
    SemanticType.Set type = (SemanticType.Set) e.attribute(TypeAttribute.class).type;
    SemanticType.EffectiveTuple element = (SemanticType.EffectiveTuple) type.element();
    Code first = generate(e.firstOperand, environment, context);
    Code second = generate(e.secondOperand, environment, context);
    Code third = generate(e.thirdOperand, environment, context);
    SemanticType argType;
    NameID nid;
    switch (e.op) {
    case UPDATE:
      nid = new NameID(WYCS_CORE_LIST, "ListUpdate");
      argType = SemanticType.Tuple(type,SemanticType.Int, element.tupleElement(1));
      break;
    case SUBLIST:
      nid = new NameID(WYCS_CORE_LIST, "Sublist");
      argType = SemanticType.Tuple(type,SemanticType.Int,SemanticType.Int);
      break;
    default:
      internalFailure("unknown ternary opcode encountered (" + e + ")",
          filename, e);
      return null;
    }
    SemanticType[] generics = bindGenerics(nid,argType,e);
    Code argument = Code.Nary(argType, Code.Op.TUPLE, new Code[] { first,
        second, third });
    SemanticType.Function funType = SemanticType.Function(argType, type,
        generics);
    return Code.FunCall(funType, argument, nid,
        attributes(e));
  }

  protected Code generate(Expr.Nary e, HashMap<String,Code> environment, WyalFile.Context context) {
    SemanticType type = e.attribute(TypeAttribute.class).type;
    Code[] operands = new Code[e.operands.size()];
    for(int i=0;i!=operands.length;++i) {
      operands[i] = generate(e.operands.get(i),environment, context);
    }
    Code.Op opcode;
    switch(e.op) {
    case TUPLE:
      opcode = Code.Op.TUPLE;
      break;
    case SET:
      opcode = Code.Op.SET;
      break;
//    case MAP:
//      opcode = Code.Op.MAP;
//      break;
    case LIST: {

      // The goal here is convert from a list of the form [x,y,z] into a
      // set of tuples of the form {(0,x),(1,y),(2,z)}.

      for (int i = 0; i != operands.length; ++i) {
        SemanticType.Tuple tt = SemanticType.Tuple(SemanticType.Int,
            operands[i].returnType());
        Code.Constant idx = Code.Constant(Value.Integer(BigInteger
            .valueOf(i)));
        operands[i] = Code.Nary(tt, Code.Op.TUPLE, new Code[] { idx,
            operands[i] });
      }
      opcode = Code.Op.SET;
      break;
    }
    default:
      internalFailure("unknown unary opcode encountered (" + e + ")",
          filename, e);
      return null;
    }
    return Code.Nary(type, opcode, operands,
        attributes(e));
  }

  protected Code generate(Expr.Quantifier e,
      HashMap<String, Code> _environment, WyalFile.Context context) {
    SemanticType type = e.attribute(TypeAttribute.class).type;
    HashMap<String, Code> environment = new HashMap<String, Code>(
        _environment);

    ArrayList<Pair<SemanticType,Integer>> variables = new ArrayList<Pair<SemanticType,Integer>>();
    addQuantifiedVariables(e.pattern, variables, environment);

    Pair<SemanticType, Integer>[] types = variables.toArray(new Pair[variables.size()]);

    Code operand = generate(e.operand, environment, context);

    if(e instanceof Expr.ForAll) {
      return Code.Quantifier(type, Code.Op.FORALL, operand, types,
          attributes(e));
    } else {
      return Code.Quantifier(type, Code.Op.EXISTS, operand, types,
          attributes(e));
    }
  }

  // FIXME: The following is a bit of a hack really. The purpose is to ensure
  // every quantified variable is unique through an entire expression. This is
  // necessary because the rewrite rules for quantifiers don't proper handle
  // name clashes between quantified variables.  See #389
  private static int freshVar = 0;
  private static int freshVar(HashMap<String, Code> environment) {
    if(freshVar < environment.size()) {
      freshVar = environment.size();
    } else {
      freshVar++;
    }
    return freshVar;
  }

  protected void addQuantifiedVariables(TypePattern t,
      ArrayList<Pair<SemanticType, Integer>> variables,
      HashMap<String, Code> environment) {

    if(t instanceof TypePattern.Leaf) {
      TypePattern.Leaf tl = (TypePattern.Leaf) t;
      if (tl.var != null) {
        int index = freshVar(environment);
        SemanticType type = tl.attribute(TypeAttribute.class).type;
        variables.add(new Pair<SemanticType,Integer>(type,index));
        environment.put(
            tl.var.name,
            Code.Variable(type, index,
                attributes(tl)));
      }
    } else if(t instanceof TypePattern.Tuple) {
      TypePattern.Tuple tt = (TypePattern.Tuple) t;
      for (int i = 0; i != tt.elements.size(); ++i) {
        TypePattern p = tt.elements.get(i);
        addQuantifiedVariables(p, variables, environment);
      }
    }
  }


  protected Code generate(Expr.Invoke e, HashMap<String, Code> environment,
      WyalFile.Context context) {
    SemanticType.Function type = (SemanticType.Function) e
        .attribute(TypeAttribute.class).type;
    Code operand = generate(e.operand, environment, context);
    try {
      Pair<NameID, SemanticType.Function> p = builder
          .resolveAsFunctionType(e.name, context);
      return Code.FunCall(type, operand, p.first(),
          attributes(e));
    } catch (ResolveError re) {
      // should be unreachable if type propagation is already succeeded.
      syntaxError("cannot resolve as function or definition call",
          filename, e, re);
      return null;
    }
  }

  protected Code generate(Expr.IndexOf e, HashMap<String,Code> environment, WyalFile.Context context) {
    SemanticType operand_type = (SemanticType) e.attribute(TypeAttribute.class).type;
    Code source = generate(e.operand, environment, context);

    if(operand_type instanceof SemanticType.EffectiveTuple) {
      SemanticType.EffectiveTuple tt = (SemanticType.EffectiveTuple) operand_type;
      Value.Integer idx = (Value.Integer) ((Expr.Constant) e.index).value;
      return Code.Load(tt.tupleType(), source, idx.value.intValue(),
          attributes(e));
    } else {
      // FIXME: handle effective set here
      SemanticType.Set type = (SemanticType.Set) operand_type;
      SemanticType.EffectiveTuple element = (SemanticType.EffectiveTuple) type.element();
      SemanticType.Tuple argType = SemanticType.Tuple(type,
          element.tupleElement(0));
      SemanticType.Function funType = SemanticType.Function(argType,
          element.tupleElement(1),element.tupleElement(0),element.tupleElement(1));
      Code index = generate(e.index, environment, context);
      NameID nid = new NameID(WYCS_CORE_MAP, "IndexOf");
      Code argument = Code.Nary(argType, Code.Op.TUPLE, new Code[] {
          source, index });
      return Code.FunCall(funType, argument, nid,
          attributes(e));
    }
  }

  /**
   * This function attempts to find an appropriate binding for the generic
   * types accepted by a given function, and the supplied argument type. For
   * example, consider a call
   * <code>f(1)<code> for a function <code>f<T>(T)</code>. The appropriate
   * binding for this call is <code>{T=>int}</code>.
   *
   * @param nid
   *            --- name identifier for the named function
   * @param type
   *            --- the supplied argument type
   * @return
   */
  protected SemanticType[] bindGenerics(NameID nid, SemanticType argumentType,
      SyntacticElement elem) {
    try {
      WycsFile module = builder.getModule(nid.module());
      // module should not be null if TypePropagation has already passed.
      Object d = module.declaration(nid.name());
      SemanticType[] generics;
      SemanticType parameterType;
      if(d instanceof WycsFile.Function) {
        WycsFile.Function fn = (WycsFile.Function) d;
        generics = fn.type.generics();
        parameterType = fn.type.from();
      } else if(d instanceof WycsFile.Macro) {
        WycsFile.Macro fn = (WycsFile.Macro) d;
        generics = fn.type.generics();
        parameterType = fn.type.from();
      } else {
        internalFailure("cannot resolve as function or macro call",
            filename, elem);
        return null; // dead-code
      }
      HashMap<String,SemanticType> binding = new HashMap<String,SemanticType>();
      if (!SemanticType.bind(parameterType, argumentType, binding)) {
        internalFailure("cannot bind function or macro call", filename,
            elem);
      }
      SemanticType[] result = new SemanticType[generics.length];
      for(int i=0;i!=result.length;++i) {
        SemanticType.Var v = (SemanticType.Var) generics[i];
        SemanticType type = binding.get(v.name());
        if(type == null) {
          internalFailure("cannot bind function or macro call",
              filename, elem);
        }
        result[i] = type;
      }
      return result;
    } catch (Exception ex) {
      internalFailure(ex.getMessage(), filename, elem, ex);
      return null; // dead-code
    }
  }

  protected static Attribute[] attributes(SyntacticElement d) {
    ArrayList<Attribute> attrs = new ArrayList<Attribute>();
    for(Attribute a : d.attributes()) {
      if(a instanceof Attribute.Source || a instanceof Attribute.Origin) {
        attrs.add(a);
      }
    }
    return attrs.toArray(new Attribute[attrs.size()]);
  }

  protected static Code implies(Code lhs, Code rhs) {
    lhs = Code.Unary(SemanticType.Bool, Code.Op.NOT, lhs);
    return Code
        .Nary(SemanticType.Bool, Code.Op.OR, new Code[] { lhs, rhs });
  }

  protected static Code and(Code... constraints) {
    int count = 0;
    for(Code c : constraints) {
      if(c == null) {
        count++;
      }
    }
    if(count == 0) {
      return Code.Nary(SemanticType.Bool, Code.Op.AND, constraints);
    } else if(constraints.length-count == 1){
      for(Code c : constraints) {
        if(c != null) {
          return c;
        }
      }
    } else if(constraints.length-count > 0){
      Code[] nconstraints = new Code[constraints.length-count];
      int i=0;
      for(Code c : constraints) {
        if(c != null) {
          nconstraints[i++] = c;
        }
      }
      return Code.Nary(SemanticType.Bool, Code.Op.AND, nconstraints);
    }
    return Code.Constant(Value.Bool(true));
  }


  private static final Trie WYCS_CORE_SET = Trie.ROOT.append("wycs")
      .append("core").append("Set");
  private static final Trie WYCS_CORE_MAP = Trie.ROOT.append("wycs")
      .append("core").append("Map");
  private static final Trie WYCS_CORE_LIST = Trie.ROOT.append("wycs")
      .append("core").append("List");
}
TOP

Related Classes of wycs.builders.CodeGeneration

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.