// Copyright (c) 2011, David J. Pearce (djp@ecs.vuw.ac.nz)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the <organization> nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL DAVID J. PEARCE BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package wyjc.runtime;
import java.io.IOException;
import java.util.*;
import wyfs.io.BinaryInputStream;
import wyrl.io.JavaIdentifierInputStream;
import static wyil.lang.Type.K_VOID;
import static wyil.lang.Type.K_ANY;
import static wyil.lang.Type.K_META;
import static wyil.lang.Type.K_NULL;
import static wyil.lang.Type.K_BOOL;
import static wyil.lang.Type.K_BYTE;
import static wyil.lang.Type.K_CHAR;
import static wyil.lang.Type.K_INT;
import static wyil.lang.Type.K_RATIONAL;
import static wyil.lang.Type.K_STRING;
import static wyil.lang.Type.K_TUPLE;
import static wyil.lang.Type.K_SET;
import static wyil.lang.Type.K_LIST;
import static wyil.lang.Type.K_MAP;
import static wyil.lang.Type.K_REFERENCE;
import static wyil.lang.Type.K_RECORD;
import static wyil.lang.Type.K_UNION;
import static wyil.lang.Type.K_NEGATION;
import static wyil.lang.Type.K_FUNCTION;
import static wyil.lang.Type.K_NOMINAL;
public abstract class WyType {
public final int kind;
private WyType(int kind) {
this.kind = kind;
}
public static final Void VOID = new Void();
public static final Any ANY = new Any();
public static final Meta META = new Meta();
public static final Null NULL = new Null();
public static final Bool BOOL = new Bool();
public static final Byte BYTE = new Byte();
public static final Char CHAR = new Char();
public static final Integer INT = new Integer();
public static final Rational REAL = new Rational();
public static final Strung STRING = new Strung();
private static final class Void extends WyType { Void() {super(K_VOID);}}
private static final class Any extends WyType { Any() {super(K_ANY);}}
private static final class Meta extends WyType { Meta() {super(K_META);}}
private static final class Null extends WyType { Null() {super(K_NULL);}}
private static final class Bool extends WyType { Bool() {super(K_BOOL);}}
private static final class Byte extends WyType { Byte() {super(K_BYTE);}}
private static final class Char extends WyType { Char() {super(K_CHAR);}}
private static final class Integer extends WyType { Integer() {super(K_INT);}}
private static final class Rational extends WyType { Rational() {super(K_RATIONAL);}}
private static final class Strung extends WyType { Strung() {super(K_STRING);}}
public static final class Reference extends WyType {
public WyType element;
public Reference(WyType element) {
super(K_REFERENCE);
this.element = element;
}
}
public static final class List extends WyType {
public WyType element;
public final boolean nonEmpty;
public List(WyType element, boolean nonEmpty) {
super(K_LIST);
this.element = element;
this.nonEmpty = nonEmpty;
}
}
public static final class Set extends WyType {
public WyType element;
public final boolean nonEmpty;
public Set(WyType element, boolean nonEmpty) {
super(K_SET);
this.element = element;
this.nonEmpty = nonEmpty;
}
}
public static final class Dictionary extends WyType {
public WyType key;
public WyType value;
public Dictionary(WyType key, WyType value) {
super(K_MAP);
this.key = key;
this.value = value;
}
}
public static final class Record extends WyType {
public final String[] names;
public final WyType[] types;
public final boolean isOpen;
public Record(String[] names, WyType[] types, boolean open) {
super(K_RECORD);
this.names = names;
this.types = types;
this.isOpen = open;
}
}
public static final class Tuple extends WyType {
public final WyType[] types;
public Tuple(WyType[] types) {
super(K_TUPLE);
this.types = types;
}
}
public static final class Union extends WyType {
public final WyType[] bounds;
public Union(WyType... bounds) {
super(K_UNION);
this.bounds = bounds;
}
}
private static final class Nominal extends WyType {
public final String name;
public Nominal(String name) {
super(K_NOMINAL);
this.name = name;
}
}
public static final class Negation extends WyType {
public WyType element;
public Negation(WyType element) {
super(K_NEGATION);
this.element = element;
}
}
public static final class Label extends WyType {
public final int label;
public Label(int label) {
super(-1);
this.label = label;
}
}
public static WyType valueOf(String str) throws IOException {
JavaIdentifierInputStream jin = new JavaIdentifierInputStream(str);
BinaryInputStream bin = new BinaryInputStream(jin);
ArrayList<WyType> nodes = new ArrayList<WyType>();
int size = bin.read_uv();
for(int i=0;i!=size;++i) {
nodes.add(readNode(bin, nodes));
}
for(int i=0;i!=size;++i) {
substitute(nodes.get(i),nodes);
}
return nodes.get(0);
}
private static WyType readNode(BinaryInputStream reader, ArrayList<WyType> nodes) throws IOException {
int kind = reader.read_uv();
boolean deterministic = reader.read_bit();
int nchildren = reader.read_uv();
WyType[] children = new WyType[nchildren];
for (int i=0;i!=nchildren;++i) {
children[i]=new Label(reader.read_uv());
}
switch(kind) {
case K_VOID:
return VOID;
case K_ANY:
return ANY;
case K_META:
return META;
case K_NULL:
return NULL;
case K_BOOL:
return BOOL;
case K_BYTE:
return BYTE;
case K_CHAR:
return CHAR;
case K_INT:
return INT;
case K_RATIONAL:
return REAL;
case K_STRING:
return STRING;
case K_TUPLE: {
return new Tuple(children);
}
case K_SET: {
boolean nonEmpty = reader.read_bit();
return new Set(children[0],nonEmpty);
}
case K_LIST: {
boolean nonEmpty = reader.read_bit();
return new List(children[0],nonEmpty);
}
case K_MAP: {
return new Dictionary(children[0],children[1]);
}
case K_REFERENCE: {
return new Reference(children[0]);
}
case K_RECORD: {
boolean isOpen = reader.read_bit();
int nfields = reader.read_uv();
String[] fields = new String[nfields];
for(int i=0;i!=nfields;++i) {
fields[i] = readString(reader);
}
return new Record(fields,children,isOpen);
}
case K_UNION: {
return new Union(children);
}
case K_NEGATION: {
return new Negation(children[0]);
}
case K_NOMINAL: {
String module = readString(reader);
String name = readString(reader);
return new WyType.Nominal(module + ":" + name);
}
}
throw new RuntimeException("unknow type encountered (kind: " + kind + ")");
}
private static String readString(BinaryInputStream reader) throws IOException {
String r = "";
int nchars = reader.read_uv();
for(int i=0;i!=nchars;++i) {
char c = (char) reader.read_u16();
r = r + c;
}
return r;
}
private static void substitute(WyType type, ArrayList<WyType> nodes) {
switch(type.kind) {
case K_VOID:
case K_ANY:
case K_META:
case K_NULL:
case K_BOOL:
case K_BYTE:
case K_CHAR:
case K_INT:
case K_RATIONAL:
case K_STRING:
case K_NOMINAL:
return;
case K_TUPLE: {
Tuple t = (Tuple) type;
substitute(t.types,nodes);
return;
}
case K_SET: {
Set t = (Set) type;
t.element = substitute((Label)t.element,nodes);
return;
}
case K_LIST: {
List t = (List) type;
t.element = substitute((Label)t.element,nodes);
return;
}
case K_MAP: {
Dictionary t = (Dictionary) type;
t.key = substitute((Label)t.key,nodes);
t.value = substitute((Label)t.value,nodes);
return;
}
case K_REFERENCE: {
Reference t = (Reference) type;
t.element = substitute((Label)t.element,nodes);
return;
}
case K_RECORD: {
Record t = (Record) type;
substitute(t.types,nodes);
return;
}
case K_UNION: {
Union t = (Union) type;
substitute(t.bounds,nodes);
return;
}
case K_NEGATION: {
Negation t = (Negation) type;
t.element = substitute((Label)t.element,nodes);
return;
}
}
throw new RuntimeException("unknow type encountered (kind: " + type.kind + ")");
}
private static void substitute(WyType[] types, ArrayList<WyType> nodes) {
for(int i=0;i!=types.length;++i) {
Label type = (Label) types[i];
types[i] = nodes.get(type.label);
}
}
private static WyType substitute(Label type, ArrayList<WyType> nodes) {
return nodes.get(type.label);
}
/**
* <p>
* This method connects up recursive links in a given type. In particular,
* it replaces all occurrences of variable <code>var</code> with
* <code>root</code>.
* </p>
*
* <b>NOTE:</b> the resulting type may contain a cycle. For this reason, the
* visited relation is required to ensure termination in the presence of
* such cycles.
*
* @param type
* - The type currently be explored
* @param label
* - The label to substitute for
* @param root
* - The root of the recursive type. Variable <code>var</code>
* will be replaced with this.
* @param visited
* - contains all of the visited nodes. This is needed to ensure
* termination in the presence of cycles.
* @return
*/
private static WyType substitute(WyType type, String label, WyType root, HashSet<WyType> visited) {
if(visited.contains(type)) {
return type;
} else {
visited.add(type);
}
switch(type.kind) {
case K_ANY:
case K_VOID:
case K_NULL:
case K_INT:
case K_RATIONAL:
case K_STRING:
break;
case K_NOMINAL:
{
WyType.Nominal leaf = (WyType.Nominal) type;
if(leaf.name.equals(label)) {
return root;
} else {
return leaf;
}
}
case K_LIST:
{
WyType.List list = (WyType.List) type;
list.element = substitute(list.element,label,root,visited);
break;
}
case K_SET:
{
WyType.Set set = (WyType.Set) type;
set.element = substitute(set.element,label,root,visited);
break;
}
case K_MAP:
{
WyType.Dictionary dict = (WyType.Dictionary) type;
dict.key = substitute(dict.key,label,root,visited);
dict.value = substitute(dict.value,label,root,visited);
break;
}
case K_RECORD:
{
WyType.Record rec = (WyType.Record) type;
WyType[] types = rec.types;
for(int i=0;i!=types.length;++i) {
types[i] = substitute(types[i],label,root,visited);
}
break;
}
case K_NEGATION:
{
WyType.Negation not = (WyType.Negation) type;
not.element = substitute(not.element,label,root,visited);
break;
}
case K_UNION:
{
WyType.Union un = (WyType.Union) type;
WyType[] types = un.bounds;
for(int i=0;i!=types.length;++i) {
types[i] = substitute(types[i],label,root,visited);
}
break;
}
}
return type;
}
}