/******************************************************************************* * 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 * * Anya Helene Bagge - UiB * * Paul Klint - Paul.Klint@cwi.nl - CWI * * Mark Hills - Mark.Hills@cwi.nl (CWI) * * Arnold Lankamp - Arnold.Lankamp@cwi.nl *******************************************************************************/ package org.rascalmpl.interpreter; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.rascalmpl.ast.Declaration; import org.rascalmpl.ast.Declaration.Alias; import org.rascalmpl.ast.Declaration.Data; import org.rascalmpl.ast.Declaration.DataAbstract; import org.rascalmpl.ast.KeywordFormal; import org.rascalmpl.ast.NullASTVisitor; import org.rascalmpl.ast.QualifiedName; import org.rascalmpl.ast.Toplevel; import org.rascalmpl.ast.Toplevel.GivenVisibility; import org.rascalmpl.ast.TypeArg; import org.rascalmpl.ast.TypeVar; import org.rascalmpl.ast.UserType; import org.rascalmpl.ast.Variant; import org.rascalmpl.interpreter.asserts.ImplementationError; import org.rascalmpl.interpreter.env.Environment; import org.rascalmpl.interpreter.result.ConstructorFunction; import org.rascalmpl.interpreter.result.Result; import org.rascalmpl.interpreter.staticErrors.IllegalQualifiedDeclaration; import org.rascalmpl.interpreter.staticErrors.RedeclaredField; import org.rascalmpl.interpreter.staticErrors.RedeclaredType; import org.rascalmpl.interpreter.staticErrors.SyntaxError; import org.rascalmpl.interpreter.staticErrors.UndeclaredType; import org.rascalmpl.interpreter.utils.Names; import org.rascalmpl.value.IValue; import org.rascalmpl.value.exceptions.FactTypeDeclarationException; import org.rascalmpl.value.exceptions.FactTypeRedeclaredException; import org.rascalmpl.value.exceptions.RedeclaredFieldNameException; import org.rascalmpl.value.type.Type; import org.rascalmpl.value.type.TypeFactory; public class TypeDeclarationEvaluator { private Evaluator eval; public TypeDeclarationEvaluator(Evaluator eval) { this.eval = eval; } private Environment env; public void evaluateDeclarations(List<Toplevel> decls, Environment env) { this.env = env; Set<UserType> abstractDataTypes = new HashSet<UserType>(); Set<Data> constructorDecls = new HashSet<Data>(); Set<Alias> aliasDecls = new HashSet<Alias>(); // this code is very much order dependent collectDeclarations(decls, abstractDataTypes, constructorDecls, aliasDecls); declareAbstractDataTypes(abstractDataTypes); declareAliases(aliasDecls); declareConstructors(constructorDecls); } private void declareConstructors(Set<Data> constructorDecls) { for (Data data : constructorDecls) { declareConstructor(data, env); } } public static Type computeKeywordParametersType(List<KeywordFormal> kws, IEvaluator<Result<IValue>> eval) { Type[] kwTypes = new Type[kws.size()]; String[] kwLabels = new String[kws.size()]; int i = 0; for (KeywordFormal kw : kws) { kwLabels[i] = Names.name(kw.getName()); kwTypes[i++] = kw.getType().typeOf(eval.getCurrentEnvt(), false, eval); } return TypeFactory.getInstance().tupleType(kwTypes, kwLabels); } public void declareConstructor(Data x, Environment env) { TypeFactory tf = TypeFactory.getInstance(); // needs to be done just in case the declaration came // from a shell instead of from a module Type adt = declareAbstractDataType(x.getUser(), env); List<KeywordFormal> common = x.getCommonKeywordParameters().isPresent() ? x.getCommonKeywordParameters().getKeywordFormalList() : Collections.<KeywordFormal>emptyList(); if (common.size() > 0) { env.declareGenericKeywordParameters(adt, computeKeywordParametersType(common, eval), common); } for (Variant var : x.getVariants()) { String altName = Names.name(var.getName()); if (var.isNAryConstructor()) { List<KeywordFormal> local = var.getKeywordArguments().hasKeywordFormalList() ? var.getKeywordArguments().getKeywordFormalList() : Collections.<KeywordFormal>emptyList(); List<TypeArg> args = var.getArguments(); int nAllArgs = args.size(); Type[] fields = new Type[nAllArgs]; String[] labels = new String[nAllArgs]; for (int i = 0; i < args.size(); i++) { TypeArg arg = args.get(i); fields[i] = arg.getType().typeOf(env, true, eval); if (fields[i] == null) { throw new UndeclaredType(arg.hasName() ? Names.name(arg.getName()) : "?", arg); } if (arg.hasName()) { labels[i] = Names.name(arg.getName()); } else { labels[i] = "arg" + java.lang.Integer.toString(i); } } try { ConstructorFunction cons = env.constructorFromTuple(var, eval, adt, altName, tf.tupleType(fields, labels), local); cons.setPublic(true); // TODO: implement declared visibility if (local.size() > 0) { Type kwType = computeKeywordParametersType(local, eval); for (String label : kwType.getFieldNames()) { env.getStore().declareKeywordParameter(cons.getConstructorType(), label, kwType.getFieldType(label)); } } } catch (org.rascalmpl.value.exceptions.RedeclaredConstructorException e) { throw new RedeclaredType(altName, var); } catch (RedeclaredFieldNameException e) { throw new RedeclaredField(e.getMessage(), var); } } } } public void declareAbstractADT(DataAbstract x, Environment env) { Type adt = declareAbstractDataType(x.getUser(), env); List<KeywordFormal> common = x.getCommonKeywordParameters().isPresent() ? x.getCommonKeywordParameters().getKeywordFormalList() : Collections.<KeywordFormal>emptyList(); if (common.size() > 0) { env.declareGenericKeywordParameters(adt, computeKeywordParametersType(common, eval), common); } } private void declareAliases(Set<Alias> aliasDecls) { List<Alias> todo = new LinkedList<Alias>(); todo.addAll(aliasDecls); int countdown = todo.size(); while (!todo.isEmpty()) { Alias trial = todo.remove(0); --countdown; try { declareAlias(trial, env); countdown = todo.size(); } catch (UndeclaredType e) { if (countdown == 0) { // Cycle throw e; } // Put at end of queue todo.add(trial); } } } public void declareAlias(Alias x, Environment env) { try { Type base = x.getBase().typeOf(env, true, eval); assert base != null; QualifiedName name = x.getUser().getName(); if (Names.isQualified(name)) { throw new IllegalQualifiedDeclaration(name); } env.aliasType(Names.typeName(name), base, computeTypeParameters(x.getUser(), env)); } catch (FactTypeRedeclaredException e) { throw new RedeclaredType(e.getName(), x); } catch (FactTypeDeclarationException e) { throw new ImplementationError("Unknown FactTypeDeclarationException: " + e.getMessage()); } } private void declareAbstractDataTypes(Set<UserType> abstractDataTypes) { // for (UserType decl : abstractDataTypes) { // declareAbstractDataType(decl, env); // } List<UserType> todo = new LinkedList<UserType>(); todo.addAll(abstractDataTypes); int countdown = todo.size(); while (!todo.isEmpty()) { UserType trial = todo.remove(0); --countdown; try { declareAbstractDataType(trial, env); countdown = todo.size(); } catch (UndeclaredType e) { if (countdown == 0) { // Cycle throw e; } // Put at end of queue todo.add(trial); } } } public Type declareAbstractDataType(UserType decl, Environment env) { QualifiedName name = decl.getName(); if (Names.isQualified(name)) { throw new IllegalQualifiedDeclaration(name); } return env.abstractDataType(Names.typeName(name), computeTypeParameters(decl, env)); } private Type[] computeTypeParameters(UserType decl, Environment env) { TypeFactory tf = TypeFactory.getInstance(); Type[] params; if (decl.isParametric()) { java.util.List<org.rascalmpl.ast.Type> formals = decl .getParameters(); params = new Type[formals.size()]; int i = 0; for (org.rascalmpl.ast.Type formal : formals) { if (!formal.isVariable()) { throw new SyntaxError( "Declaration of parameterized type with type instance " + formal + " is not allowed", formal.getLocation()); } TypeVar var = formal.getTypeVar(); Type bound = var.hasBound() ? var.getBound().typeOf(env, true, eval) : tf .valueType(); params[i++] = tf .parameterType(Names.name(var.getName()), bound); } } else { params = new Type[0]; } return params; } private void collectDeclarations(List<Toplevel> decls, Set<UserType> abstractDataTypes, Set<Data> constructorDecls, Set<Alias> aliasDecls) { DeclarationCollector collector = new DeclarationCollector( abstractDataTypes, constructorDecls, aliasDecls); for (Toplevel t : decls) { t.accept(collector); } } private static class DeclarationCollector extends NullASTVisitor<Declaration> { private Set<UserType> abstractDataTypes; private Set<Data> constructorDecls; private Set<Alias> aliasDecls; public DeclarationCollector(Set<UserType> abstractDataTypes, Set<Data> constructorDecls, Set<Alias> aliasDecls) { this.abstractDataTypes = abstractDataTypes; this.constructorDecls = constructorDecls; this.aliasDecls = aliasDecls; } @Override public Declaration visitToplevelGivenVisibility(GivenVisibility x) { return x.getDeclaration().accept(this); } @Override public Declaration visitDeclarationAlias(Alias x) { aliasDecls.add(x); return x; } @Override public Declaration visitDeclarationData(Data x) { abstractDataTypes.add(x.getUser()); constructorDecls.add(x); return x; } @Override public Declaration visitDeclarationDataAbstract(DataAbstract x) { abstractDataTypes.add(x.getUser()); return x; } } }