/*******************************************************************************
* Copyright (c) 2009-2013 CWI
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* * Jurgen J. Vinju - Jurgen.Vinju@cwi.nl - CWI
* * Tijs van der Storm - Tijs.van.der.Storm@cwi.nl
* * Arnold Lankamp - Arnold.Lankamp@cwi.nl
*******************************************************************************/
package org.rascalmpl.interpreter.types;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.rascalmpl.interpreter.TypeReifier.TypeStoreWithSyntax;
import org.rascalmpl.interpreter.asserts.ImplementationError;
import org.rascalmpl.interpreter.utils.Symbols;
import org.rascalmpl.value.IConstructor;
import org.rascalmpl.value.IList;
import org.rascalmpl.value.ISet;
import org.rascalmpl.value.ISetWriter;
import org.rascalmpl.value.IValue;
import org.rascalmpl.value.IValueFactory;
import org.rascalmpl.value.type.Type;
import org.rascalmpl.value.type.TypeFactory.TypeReifier;
import org.rascalmpl.value.type.TypeStore;
import org.rascalmpl.values.ValueFactoryFactory;
import org.rascalmpl.values.uptr.IRascalValueFactory;
import org.rascalmpl.values.uptr.ITree;
import org.rascalmpl.values.uptr.ProductionAdapter;
import org.rascalmpl.values.uptr.RascalValueFactory;
import org.rascalmpl.values.uptr.SymbolAdapter;
import org.rascalmpl.values.uptr.TreeAdapter;
/**
* This is an "extension" of the PDB's type system with a special kind of type
* that implements the connection between Rascal's non-terminals and Rascal types.
*/
public class NonTerminalType extends RascalType {
private final IConstructor symbol;
/*package*/ public NonTerminalType(IConstructor cons) {
// TODO refactor this into different factory methods in RascalTypeFactory
if (cons.getConstructorType() == RascalValueFactory.Tree_Appl) {
// Note that here we go from * to + lists if the list is not empty
this.symbol = TreeAdapter.getType((ITree) cons);
}
else if (cons.getConstructorType() == RascalValueFactory.Tree_Amb) {
ISet alts = TreeAdapter.getAlternatives((ITree) cons);
if (!alts.isEmpty()) {
ITree first = (ITree) alts.iterator().next();
this.symbol = TreeAdapter.getType(first);
}
else {
this.symbol = IRascalValueFactory.getInstance().constructor(RascalValueFactory.Symbol_Empty);
}
}
else if (cons.getConstructorType() == RascalValueFactory.Tree_Cycle) {
this.symbol = TreeAdapter.getType((ITree) cons);
}
else if (cons.getType() == RascalValueFactory.Symbol) {
this.symbol = cons;
}
else if (cons.getType() == RascalValueFactory.Production) {
this.symbol = ProductionAdapter.getType(cons);
}
else {
throw new ImplementationError("Invalid concrete syntax type constructor:" + cons);
}
}
/*package*/ NonTerminalType(org.rascalmpl.ast.Type type, boolean lex, String layout) {
this(Symbols.typeToSymbol(type, lex, layout));
}
public static class Reifier implements TypeReifier {
@Override
public Set<Type> getSymbolConstructorTypes() {
return Arrays.stream(new Type[] {
RascalValueFactory.Symbol_Label,
RascalValueFactory.Symbol_Start_Sort,
RascalValueFactory.Symbol_Lit,
RascalValueFactory.Symbol_CiLit,
RascalValueFactory.Symbol_Empty,
RascalValueFactory.Symbol_Seq,
RascalValueFactory.Symbol_Opt,
RascalValueFactory.Symbol_Alt,
RascalValueFactory.Symbol_Sort,
RascalValueFactory.Symbol_Lex,
RascalValueFactory.Symbol_Keyword,
RascalValueFactory.Symbol_Meta,
RascalValueFactory.Symbol_Conditional,
RascalValueFactory.Symbol_IterSepX,
RascalValueFactory.Symbol_IterStarSepX,
RascalValueFactory.Symbol_IterPlus,
RascalValueFactory.Symbol_IterStar,
RascalValueFactory.Symbol_ParameterizedSort,
RascalValueFactory.Symbol_ParameterizedLex,
RascalValueFactory.Symbol_Parameter,
RascalValueFactory.Symbol_LayoutX,
RascalValueFactory.Symbol_CharClass,
RascalValueFactory.Production_Default
}).collect(Collectors.toSet());
}
@Override
public Type getSymbolConstructorType() {
// this reifier is for multiple constructor types
throw new UnsupportedOperationException();
}
@Override
public Type fromSymbol(IConstructor symbol, TypeStore store, Function<IConstructor, Set<IConstructor>> grammar) {
if (symbols().isLabel(symbol)) {
symbol = symbols().getLabeledSymbol(symbol);
}
return RTF.nonTerminalType((IConstructor) symbol);
}
@Override
public void asProductions(Type type, IValueFactory vf, TypeStore store, ISetWriter grammar,
Set<IConstructor> done) {
if (store instanceof TypeStoreWithSyntax) {
TypeStoreWithSyntax syntax = (TypeStoreWithSyntax) store;
addRulesForSort(vf, ((NonTerminalType) type).symbol, syntax, grammar, new HashSet<>());
}
}
@Override
public IConstructor toSymbol(Type type, IValueFactory vf, TypeStore store, ISetWriter grammar, Set<IConstructor> done) {
asProductions(type, vf, store, grammar, done);
return ((NonTerminalType) type).symbol;
}
private void addRulesForSort(IValueFactory vf, IConstructor sort, TypeStoreWithSyntax syntax, ISetWriter grammar, Set<IConstructor> done) {
if (done.contains(sort)) {
return;
}
for (IValue rule : syntax.getRules(sort)) {
grammar.insert(vf.tuple(sort, rule));
done.add(sort);
if (ProductionAdapter.isDefault((IConstructor) rule)) {
for (IValue arg : ProductionAdapter.getSymbols((IConstructor) rule)) {
addRulesForSort(vf, (IConstructor) arg, syntax, grammar, done);
}
}
}
}
@Override
public boolean isRecursive() {
return true;
}
@Override
public Type randomInstance(Supplier<Type> next, TypeStore store, Random rnd) {
IValueFactory vf = ValueFactoryFactory.getValueFactory();
// TODO: this is not random enough
return RascalTypeFactory.getInstance().nonTerminalType(vf.constructor(RascalValueFactory.Symbol_Sort, vf.string(randomLabel(rnd))));
}
}
@Override
public TypeReifier getTypeReifier() {
return new Reifier();
}
@Override
public boolean isNonterminal() {
return true;
}
@Override
public Type asAbstractDataType() {
return RascalValueFactory.Tree;
}
public IConstructor getSymbol() {
return symbol;
}
public int getArity() {
return symbol.arity();
}
public boolean isConcreteListType() {
return SymbolAdapter.isAnyList(getSymbol());
}
public boolean isOptionalType() {
return SymbolAdapter.isOpt(getSymbol());
}
@Override
public Type getAbstractDataType() {
return RascalValueFactory.Tree;
}
@Override
public boolean hasField(String fieldName) {
// safe over-approximation
return true;
}
@Override
public boolean hasKeywordField(String fieldName, TypeStore store) {
return RascalValueFactory.Tree.hasKeywordField(fieldName, store);
}
@Override
public String getName() {
return RascalValueFactory.Tree.getName();
}
@Override
public Type getTypeParameters() {
return RascalValueFactory.Tree.getTypeParameters();
}
@Override
public <T, E extends Throwable> T accept(IRascalTypeVisitor<T,E> visitor) throws E {
return visitor.visitNonTerminal(this);
}
@Override
protected boolean isSubtypeOfAbstractData(Type type) {
return type.equivalent(RascalValueFactory.Tree);
}
@Override
protected boolean isSubtypeOfNode(Type type) {
return true;
}
@Override
protected Type lubWithNode(Type type) {
return type;
}
@Override
protected Type lubWithAbstractData(Type type) {
return type.equivalent(RascalValueFactory.Tree) ? type : TF.nodeType();
}
@Override
protected Type lubWithConstructor(Type type) {
return type.getAbstractDataType().equivalent(RascalValueFactory.Tree) ? RascalValueFactory.Tree : TF.nodeType();
}
@Override
protected Type glbWithNode(Type type) {
return this;
}
@Override
protected Type glbWithAbstractData(Type type) {
return type.equivalent(RascalValueFactory.Tree) ? this : TF.voidType();
}
@Override
protected Type glbWithConstructor(Type type) {
return TF.voidType();
}
@Override
protected boolean isSupertypeOf(Type type) {
if (type instanceof NonTerminalType) {
return ((NonTerminalType) type).isSubtypeOfNonTerminal(this);
}
return super.isSupertypeOf(type);
}
@Override
protected boolean isSupertypeOf(RascalType type) {
return type.isSubtypeOfNonTerminal(this);
}
@Override
protected Type lub(RascalType type) {
return type.lubWithNonTerminal(this);
}
@Override
protected Type glb(RascalType type) {
return type.glbWithNonTerminal(this);
}
@Override
public boolean isSubtypeOfNonTerminal(RascalType other) {
if (other == this) {
return true;
}
IConstructor otherSym = ((NonTerminalType)other).symbol;
if ((SymbolAdapter.isIterPlus(symbol) && (SymbolAdapter.isIterStar(otherSym) || SymbolAdapter.isIterPlus(otherSym)))
|| (SymbolAdapter.isIterStar(symbol) && SymbolAdapter.isIterStar(otherSym))) {
RascalType nt1 = (RascalType) RTF.nonTerminalType(SymbolAdapter.getSymbol(symbol));
RascalType nt2 = (RascalType) RTF.nonTerminalType(SymbolAdapter.getSymbol(otherSym));
return nt1.isSubtypeOfNonTerminal(nt2);
}
else if ((SymbolAdapter.isIterPlusSeps(symbol) && (SymbolAdapter.isIterStarSeps(otherSym) || SymbolAdapter.isIterPlusSeps(otherSym)))
|| (SymbolAdapter.isIterStarSeps(symbol) && SymbolAdapter.isIterStarSeps(otherSym))) {
RascalType nt1 = (RascalType) RTF.nonTerminalType(SymbolAdapter.getSymbol(symbol));
RascalType nt2 = (RascalType) RTF.nonTerminalType(SymbolAdapter.getSymbol(otherSym));
if (nt1.isSubtypeOfNonTerminal(nt2)) {
IList seps1 = SymbolAdapter.getSeparators(symbol);
IList seps2 = SymbolAdapter.getSeparators(otherSym);
// this works around broken regular prods in the RVM which have the wrong or missing layout symbols:
int sep1index = seps1.length() == 3 ? 1 : 0;
int sep2index = seps2.length() == 3 ? 1 : 0;
nt1 = (RascalType) RTF.nonTerminalType((IConstructor) seps1.get(sep1index));
nt2 = (RascalType) RTF.nonTerminalType((IConstructor) seps2.get(sep2index));
return nt1.isSubtypeOfNonTerminal(nt2);
}
return false;
}
else if (SymbolAdapter.isOpt(symbol) && SymbolAdapter.isOpt(otherSym)) {
RascalType nt1 = (RascalType) RTF.nonTerminalType(SymbolAdapter.getSymbol(symbol));
RascalType nt2 = (RascalType) RTF.nonTerminalType(SymbolAdapter.getSymbol(otherSym));
return nt1.isSubtypeOfNonTerminal(nt2);
}
// else if (SymbolAdapter.isSequence(symbol) && SymbolAdapter.isSeq(otherSym)) {
// TODO pairwise issubtype
// }
if (SymbolAdapter.isParameter(otherSym)) {
RascalType bound = (RascalType) RTF.nonTerminalType((IConstructor) otherSym.get("bound"));
return isSubtypeOf(bound);
}
// TODO co-variance for the other structured symbols (sequence, opt, list)
return SymbolAdapter.isEqual(otherSym, symbol);
}
@Override
protected Type lubWithNonTerminal(RascalType other) {
IConstructor otherSym = ((NonTerminalType)other).symbol;
// * eats +
if (SymbolAdapter.isIterPlus(symbol) && SymbolAdapter.isIterStar(otherSym)) {
return other;
}
else if (SymbolAdapter.isIterPlus(otherSym) && SymbolAdapter.isIterStar(symbol)) {
return this;
}
else if (SymbolAdapter.isIterPlusSeps(symbol) && SymbolAdapter.isIterStarSeps(otherSym)) {
return other;
}
else if (SymbolAdapter.isIterPlusSeps(otherSym) && SymbolAdapter.isIterStarSeps(symbol)) {
return this;
}
return SymbolAdapter.isEqual(otherSym, symbol) ? this : RascalValueFactory.Tree;
}
@Override
protected Type glbWithNonTerminal(RascalType other) {
IConstructor otherSym = ((NonTerminalType)other).symbol;
if (SymbolAdapter.isIterPlus(symbol) && SymbolAdapter.isIterStar(otherSym)) {
return this;
}
else if (SymbolAdapter.isIterPlus(otherSym) && SymbolAdapter.isIterStar(symbol)) {
return other;
}
else if (SymbolAdapter.isIterPlusSeps(symbol) && SymbolAdapter.isIterStarSeps(otherSym)) {
return this;
}
else if (SymbolAdapter.isIterPlusSeps(otherSym) && SymbolAdapter.isIterStarSeps(symbol)) {
return other;
}
return SymbolAdapter.isEqual(otherSym, symbol) ? other : TF.voidType();
}
@Override
public boolean equals(Object obj) {
if(obj == null) {
return false;
}
if (obj.getClass() == getClass()) {
NonTerminalType other = (NonTerminalType) obj;
return symbol.isEqual(other.symbol);
}
return false;
}
@Override
public int hashCode() {
return 133333331 + 1331 * symbol.hashCode();
}
@Override
public String toString() {
return SymbolAdapter.toString(symbol, false);
}
}