Package wycs.io

Source Code of wycs.io.WycsFileWriter$PATH_Item

package wycs.io;

import java.io.*;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import wyautl.core.Automaton;
import wyautl.io.BinaryAutomataWriter;
import wyautl.util.BigRational;
import wycc.lang.NameID;
import wycc.util.Pair;
import wycc.util.Triple;
import wycs.core.*;
import wyfs.io.BinaryOutputStream;
import wyfs.lang.Path;

public class WycsFileWriter {
  private static final int MAJOR_VERSION = 0;
  private static final int MINOR_VERSION = 1;

  private BinaryOutputStream out;

  private final ArrayList<String> stringPool  = new ArrayList<String>();
  private final HashMap<String,Integer> stringCache = new HashMap<String,Integer>();
  private final ArrayList<PATH_Item> pathPool = new ArrayList<PATH_Item>();
  private final HashMap<Path.ID,Integer> pathCache = new HashMap<Path.ID,Integer>();
  private final ArrayList<NAME_Item> namePool = new ArrayList<NAME_Item>();
  private final HashMap<NameID,Integer> nameCache = new HashMap<NameID,Integer>();
  private final ArrayList<Value> constantPool = new ArrayList<Value>();
  private final HashMap<Value,Integer> constantCache = new HashMap<Value,Integer>();
  private final ArrayList<SemanticType> typePool = new ArrayList<SemanticType>();
  private final HashMap<SemanticType,Integer> typeCache = new HashMap<SemanticType,Integer>();

  public WycsFileWriter(OutputStream output) {
    this.out = new BinaryOutputStream(output);
  }

  public void write(WycsFile module) throws IOException {
    // first, write magic number
    out.write_u8(0x57); // W
    out.write_u8(0x59); // Y
    out.write_u8(0x43); // C
    out.write_u8(0x53); // S
    out.write_u8(0x46); // F
    out.write_u8(0x49); // I
    out.write_u8(0x4C); // L
    out.write_u8(0x45); // E

    // second, build pools
    buildPools(module);

    // third, write head block
    writeBlock(BLOCK_Header,module,out);

    // fourth, write module block(s)
    writeBlock(BLOCK_Module,module,out);

    out.flush();
  }

  private void writeBlock(int kind, Object data, BinaryOutputStream output)
      throws IOException {

    output.pad_u8(); // pad out to next byte boundary

    // determine bytes for block
    byte[] bytes = null;
    switch(kind) {
      case BLOCK_Header:
        bytes = generateHeaderBlock((WycsFile) data);
        break;
      case BLOCK_Module:
        bytes = generateModuleBlock((WycsFile) data);
        break;
      case BLOCK_Macro:
        bytes = generateMacroBlock((WycsFile.Macro) data);
        break;
      case BLOCK_Function:
        bytes = generateFunctionBlock((WycsFile.Function) data);
        break;
      case BLOCK_Assert:
        bytes = generateAssertBlock((WycsFile.Assert) data);
        break;
      case BLOCK_Code:
        bytes = generateCodeBlock((Code) data);
        break;
    }

    output.write_uv(kind);
    output.write_uv(bytes.length);
    output.pad_u8(); // pad out to next byte boundary
    output.write(bytes);
  }

  /**
   * Write the header information for this WYCS file, including the stratified
   * resource pool.
   *
   * @param module
   *
   * @throws IOException
   */
  private byte[] generateHeaderBlock(WycsFile module)
      throws IOException {
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    BinaryOutputStream output = new BinaryOutputStream(bytes);

    // second, write the file version number
    output.write_uv(MAJOR_VERSION);
    output.write_uv(MINOR_VERSION);

    // third, write the various pool sizes
    output.write_uv(stringPool.size());
    output.write_uv(pathPool.size());
    output.write_uv(namePool.size());
    output.write_uv(typePool.size());
    output.write_uv(constantPool.size());

    // finally, write the number of remaining blocks
    output.write_uv(module.declarations().size());

    writeStringPool(output);
    writePathPool(output);
    writeNamePool(output);
    writeTypePool(output);
    writeConstantPool(output);

    output.close();

    return bytes.toByteArray();
  }

  private byte[] generateModuleBlock(WycsFile module) throws IOException {
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    BinaryOutputStream output = new BinaryOutputStream(bytes);

    output.write_uv(pathCache.get(module.id())); // FIXME: BROKEN!
    output.write_uv(module.declarations().size());

    for (WycsFile.Declaration d : module.declarations()) {
      writeModuleBlock(d, output);
    }

    output.close();

    return bytes.toByteArray();
  }

  /**
   * Write the list of strings making up the string pool in UTF8.
   *
   * @throws IOException
   */
  private void writeStringPool(BinaryOutputStream output) throws IOException {
    //System.out.println("Writing " + stringPool.size() + " string item(s).");
    for (String s : stringPool) {
      try {
        byte[] bytes = s.getBytes("UTF-8");
        output.write_uv(bytes.length);
        output.write(bytes, 0, bytes.length);
      } catch (UnsupportedEncodingException e) {
        // hmmm, this aint pretty ;)
      }
    }
  }

  private void writePathPool(BinaryOutputStream output) throws IOException {
    for(int i=1;i<pathPool.size();++i) {
      PATH_Item p = pathPool.get(i);
      output.write_uv(p.parentIndex);
      output.write_uv(p.stringIndex);
    }
  }

  private void writeNamePool(BinaryOutputStream output) throws IOException {
    //System.out.println("Writing " + stringPool.size() + " name item(s).");
    for (NAME_Item p : namePool) {
      //output.write_uv(p.kind.kind());
      output.write_uv(p.pathIndex);
      output.write_uv(p.nameIndex);
    }
  }

  private void writeConstantPool(BinaryOutputStream output) throws IOException {
    // System.out.println("Writing " + constantPool.size() + " constant item(s).");

    for (Value val : constantPool) {
       if(val instanceof Value.Bool) {
        Value.Bool b = (Value.Bool) val;
        output.write_uv(b.value ? CONSTANT_True : CONSTANT_False);
      } else if(val instanceof Value.Integer) {
        Value.Integer i = (Value.Integer) val;
        BigInteger num = i.value;
        byte[] numbytes = num.toByteArray();
        output.write_uv(CONSTANT_Int);
        output.write_uv(numbytes.length);
        output.write(numbytes);
      } else if(val instanceof Value.String) {
        Value.String s = (Value.String) val;
        output.write_uv(CONSTANT_String);
        String value = s.value;
        output.write_uv(stringCache.get(value));

      } else if(val instanceof Value.Decimal) {
        Value.Decimal r = (Value.Decimal) val;
        output.write_uv(CONSTANT_Real);
        BigInteger mantissa = r.value.unscaledValue();
        int exponent = r.value.scale();
        byte[] bytes = mantissa.toByteArray();
        output.write_uv(bytes.length);
        output.write(bytes);
        output.write_uv(exponent);

      } else if(val instanceof Value.Set) {
        Value.Set s = (Value.Set) val;
        output.write_uv(CONSTANT_Set);
        output.write_uv(s.values.size());
        for(Value v : s.values) {
          int index = constantCache.get(v);
          output.write_uv(index);
        }

      } else if(val instanceof Value.Tuple) {
        Value.Tuple t = (Value.Tuple) val;
        output.write_uv(CONSTANT_Tuple);
        output.write_uv(t.values.size());
        for(Value v : t.values) {
          int index = constantCache.get(v);
          output.write_uv(index);
        }
      } else {
        throw new RuntimeException("Unknown value encountered - " + val);
      }
    }
  }

  private void writeTypePool(BinaryOutputStream output) throws IOException {
    // First, we accumulate the automata for all types in the pool into one
    // automaton. This helps reduce the amount of redundancy between types.
    Automaton global = new Automaton();
    for (int i = 0; i != typePool.size(); ++i) {
      Automaton automaton = typePool.get(i).automaton();
      int root = global.addAll(automaton.getRoot(0), automaton);
      global.setRoot(i, root);
    }

    global.minimise();
    global.compact();
    // FIXME: put this back in!!
    // global.canonicalise();

    // Second, we write the single global automaton to the output stream.
    BinaryAutomataWriter writer = new BinaryAutomataWriter(output,
        Types.SCHEMA);
    writer.write(global);
  }

  private void writeModuleBlock(WycsFile.Declaration d,
      BinaryOutputStream output) throws IOException {
    if(d instanceof WycsFile.Macro) {
      writeBlock(BLOCK_Macro, d ,output);
    } else if(d instanceof WycsFile.Function) {
      writeBlock(BLOCK_Function, d, output);
    } else if(d instanceof WycsFile.Assert) {
      writeBlock(BLOCK_Assert, d, output);
    }
  }

  private byte[] generateMacroBlock(WycsFile.Macro md) throws IOException {
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    BinaryOutputStream output = new BinaryOutputStream(bytes);

    output.write_uv(stringCache.get(md.name()));
    output.write_uv(typeCache.get(md.type));
    output.write_uv(1);
    writeBlock(BLOCK_Code,md.condition,output);

    output.close();
    return bytes.toByteArray();
  }

  private byte[] generateFunctionBlock(WycsFile.Function fd) throws IOException {
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    BinaryOutputStream output = new BinaryOutputStream(bytes);

    output.write_uv(stringCache.get(fd.name()));
    output.write_uv(typeCache.get(fd.type));
    if(fd.constraint == null) {
      output.write_uv(0); // no sub-blocks
    } else {
      output.write_uv(1); // one sub-block
      writeBlock(BLOCK_Code,fd.constraint,output);
    }

    output.close();
    return bytes.toByteArray();
  }

  private byte[] generateAssertBlock(WycsFile.Assert td) throws IOException {
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    BinaryOutputStream output = new BinaryOutputStream(bytes);

    output.write_uv(stringCache.get(td.name()));
    output.write_uv(1); // one sub-block
    writeBlock(BLOCK_Code,td.condition,output);

    output.close();
    return bytes.toByteArray();
  }

  /**
   * Flatten a Wycs bytecode into a byte stream. Each Wycs bytecode represents
   * a tree of operations and, hence, this function recursively traverses the
   * tree.
   *
   * @param code
   * @return
   * @throws IOException
   */
  private byte[] generateCodeBlock(Code<?> code) throws IOException {
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    BinaryOutputStream output = new BinaryOutputStream(bytes);

    writeCode(code,output);

    output.close();
    return bytes.toByteArray();
  }

  /**
   * <p>
   * Write a given code to a binary output stream. Every bytecode is written
   * in the following structure:
   * </p>
   *
   * <pre>
   * +---------------+
   * | opcode        |
   * +---------------+
   * | type index    |
   * +---------------+
   * | operand count |
   * +---------------+
   * | 1st operand   |
   * +---------------+
   * |               |
   *
   * ...
   * |               |
   * +---------------+
   * | Nth operand   |
   * +---------------+
   * | Payload       |
   * +---------------+
   * </pre>
   *
   * <p>
   * Each cell here is of varying size and is written using one or more 4-bit
   * nibbles. The type index is an index into the type pool which gives the
   * type associated with this bytecode. The operand count determines the
   * number of operands (if any). The payload is an option field which
   * contains bytecode specific data. For example, a constant bytecode will
   * contain an index into the constant pool here.
   * </p>
   *
   * @param code
   * @param output
   * @throws IOException
   */
  private void writeCode(Code<?> code, BinaryOutputStream output) throws IOException {
    if(code == null) {
      // this is a special case
      output.write_u8(Code.Op.NULL.offset);
    } else {
      output.write_u8(code.opcode.offset);
      output.write_uv(typeCache.get(code.type));
      output.write_uv(code.operands.length);
      for(int i=0;i!=code.operands.length;++i) {
        writeCode(code.operands[i],output);
      }
      // now, write bytecode specific stuff
      switch(code.opcode){
      case VAR: {
        Code.Variable v = (Code.Variable) code;
        output.write_uv(v.index);
        break;
      }
      case CONST: {
        Code.Constant c = (Code.Constant) code;
        output.write_uv(constantCache.get(c.value));
        break;
      }
      case LOAD: {
        Code.Load c = (Code.Load) code;
        output.write_uv(c.index);
        break;
      }
      case FORALL:
      case EXISTS: {
        Code.Quantifier c = (Code.Quantifier) code;
        output.write_uv(c.types.length);
        for (Pair<SemanticType,Integer> t : c.types) {
          output.write_uv(typeCache.get(t.first()));
          output.write_uv(t.second());
        }
        break;
      }
      case FUNCALL: {
        Code.FunCall c = (Code.FunCall) code;
        output.write_uv(nameCache.get(c.nid));
      }
      }
    }
  }

  /**
   * Build the various pools of items (strings, types, constants, etc) which
   * are used within the bytecodes, and within the declarations (e.g.
   * assertions, etc). Each pool consists of an array of indexed items of the
   * same kind. Items in some pools (e.g. constant) may refer to items in
   * other pools (e.g. string) and this referencing is done via an index into
   * the given pool.
   *
   * @param module
   */
  private void buildPools(WycsFile module) {
    stringPool.clear();
    stringCache.clear();

    pathPool.clear();
    pathCache.clear();
    // preload the path root
    pathPool.add(null);
    pathCache.put(wyfs.util.Trie.ROOT,0);

    constantPool.clear();
    constantCache.clear();

    typePool.clear();
    typeCache.clear();

    addPathItem(module.id());
    for(WycsFile.Declaration d : module.declarations()) {
      buildPools(d);
    }
  }

  private void buildPools(WycsFile.Declaration declaration) {
    if(declaration instanceof WycsFile.Macro) {
      buildPools((WycsFile.Macro)declaration);
    } else if(declaration instanceof WycsFile.Function) {
      buildPools((WycsFile.Function)declaration);
    } else if(declaration instanceof WycsFile.Assert) {
      buildPools((WycsFile.Assert)declaration);
    }
  }

  private void buildPools(WycsFile.Macro declaration) {
    addStringItem(declaration.name());
    addTypeItem(declaration.type);
    buildPools(declaration.condition);
  }

  private void buildPools(WycsFile.Function declaration) {
    addStringItem(declaration.name());
    addTypeItem(declaration.type);
    if(declaration.constraint != null) {
      buildPools(declaration.constraint);
    }
  }

  private void buildPools(WycsFile.Assert declaration) {
    addStringItem(declaration.name());
    buildPools(declaration.condition);
  }

  private void buildPools(Code code) {

    // First, deal with special cases
    if(code instanceof Code.Constant) {
      Code.Constant c = (Code.Constant) code;
      addConstantItem(c.value);
    } else if(code instanceof Code.Quantifier) {
      Code.Quantifier c = (Code.Quantifier) code;
      for(Pair<SemanticType,Integer> p : c.types) {
        addTypeItem(p.first());
      }
    } else if(code instanceof Code.FunCall) {
      Code.FunCall c = (Code.FunCall) code;
      addNameItem(c.nid);
    }

    // Second, deal with standard cases
    addTypeItem(code.type);

    for(Code operand : code.operands) {
      buildPools(operand);
    }
  }


  private int addNameItem(NameID name) {
    Integer index = nameCache.get(name);
    if(index == null) {
      int i = namePool.size();
      nameCache.put(name, i);
      namePool.add(new NAME_Item(addPathItem(name.module()),
          addStringItem(name.name())));
      return i;
    } else {
      return index;
    }
  }

  private int addStringItem(String string) {
    Integer index = stringCache.get(string);
    if(index == null) {
      int i = stringPool.size();
      stringCache.put(string, i);
      stringPool.add(string);
      return i;
    } else {
      return index;
    }
  }

  private int addPathItem(Path.ID pid) {
    Integer index = pathCache.get(pid);
    if(index == null) {
      int parent = addPathItem(pid.parent());
      int i = pathPool.size();
      pathPool.add(new PATH_Item(parent,addStringItem(pid.last())));
      pathCache.put(pid, i);
      return i;
    } else {
      return index;
    }
  }

  private int addTypeItem(SemanticType t) {

    // TODO: this could be made way more efficient. In particular, we should
    // combine resources into a proper aliased pool rather than write out
    // types individually ... because that's sooooo inefficient!

    Integer index = typeCache.get(t);
    if(index == null) {
      int i = typePool.size();
      typeCache.put(t, i);
      typePool.add(t);
      return i;
    } else {
      return index;
    }
  }

  private int addConstantItem(Value v) {

    Integer index = constantCache.get(v);
    if(index == null) {
      // All subitems must have lower indices than the containing item.
      // So, we must add subitems first before attempting to allocate a
      // place for this value.
      addConstantSubitems(v);

      // finally allocate space for this constant.
      int i = constantPool.size();
      constantCache.put(v, i);
      constantPool.add(v);
      return i;
    }
    return index;
  }

  private void addConstantSubitems(Value v) {
    if(v instanceof Value.String) {
      Value.String s = (Value.String) v;
      addStringItem(s.value);
    } else if(v instanceof Value.Set) {
      Value.Set s = (Value.Set) v;
      for (Value e : s.values) {
        addConstantItem(e);
      }
    } else if(v instanceof Value.Tuple) {
      Value.Tuple t = (Value.Tuple) v;
      for (Value e : t.values) {
        addConstantItem(e);
      }
    }
  }

  /**
   * An PATH_Item represents a path item.
   *
   * @author David J. Pearce
   *
   */
  private class PATH_Item {
    /**
     * The index in the path pool of the parent for this item, or -1 if it
     * has not parent.
     */
    public final int parentIndex;

    /**
     * The index of this path component in the string pool
     */
    public final int stringIndex;

    public PATH_Item(int parentIndex, int stringIndex) {
      this.parentIndex = parentIndex;
      this.stringIndex = stringIndex;
    }
  }
  private enum NAME_Kind {
    PACKAGE(0),
    MODULE(1),
    CONSTANT(2),
    TYPE(3),
    FUNCTION(4),
    METHOD(5);

    private final int kind;

    private NAME_Kind(int kind) {
      this.kind = kind;
    }

    public int kind() {
      return kind;
    }
  }

  /**
   * A NAME_Item represents a named path item, such as a package, module or
   * something within a module (e.g. a function or method declaration).
   *
   * @author David J. Pearce
   *
   */
  private class NAME_Item {
    /**
     * The kind of name item this represents.
     */
    // public final NAME_Kind kind;

    /**
     * Index of path for this item in path pool
     */
    public final int pathIndex;

    /**
     * Index of string for this named item
     */
    public final int nameIndex;

    public NAME_Item(/*NAME_Kind kind, */int pathIndex, int nameIndex) {
      //this.kind = kind;
      this.pathIndex = pathIndex;
      this.nameIndex = nameIndex;
    }
  }

  // =========================================================================
  // BLOCK identifiers
  // =========================================================================

  public final static int BLOCK_Header = 0;
  public final static int BLOCK_Module = 1;
  public final static int BLOCK_Documentation = 2;
  public final static int BLOCK_License = 3;
  // ... (anticipating some others here)
  public final static int BLOCK_Code = 10;
  public final static int BLOCK_Macro = 11;
  public final static int BLOCK_Function = 12;
  public final static int BLOCK_Assert = 13;
  // ... (anticipating some others here)

  // =========================================================================
  // CONSTANT identifiers
  // =========================================================================

  public final static int CONSTANT_Null = 0;
  public final static int CONSTANT_True = 1;
  public final static int CONSTANT_False = 2;
  public final static int CONSTANT_Int = 3;
  public final static int CONSTANT_Real = 4;
  public final static int CONSTANT_Set = 5;
  public final static int CONSTANT_String = 6;
  public final static int CONSTANT_Tuple = 7;
}
TOP

Related Classes of wycs.io.WycsFileWriter$PATH_Item

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.