Package org.rascalmpl.library.cobra

Source Code of org.rascalmpl.library.cobra.RandomValueTypeVisitor

/*******************************************************************************
* 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:

*   * Wietse Venema - wietsevenema@gmail.com - CWI
*   * Paul Klint - Paul.Klint@cwi.nl - CWI
*******************************************************************************/
package org.rascalmpl.library.cobra;

import java.net.URI;
import java.net.URISyntaxException;
import java.text.Normalizer;
import java.text.Normalizer.Form;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Random;

import org.eclipse.imp.pdb.facts.IConstructor;
import org.eclipse.imp.pdb.facts.IList;
import org.eclipse.imp.pdb.facts.IListWriter;
import org.eclipse.imp.pdb.facts.IMap;
import org.eclipse.imp.pdb.facts.IMapWriter;
import org.eclipse.imp.pdb.facts.ISet;
import org.eclipse.imp.pdb.facts.ISetWriter;
import org.eclipse.imp.pdb.facts.ISourceLocation;
import org.eclipse.imp.pdb.facts.IValue;
import org.eclipse.imp.pdb.facts.IValueFactory;
import org.eclipse.imp.pdb.facts.type.ITypeVisitor;
import org.eclipse.imp.pdb.facts.type.Type;
import org.eclipse.imp.pdb.facts.type.TypeFactory;
import org.rascalmpl.interpreter.control_exceptions.Throw;
import org.rascalmpl.interpreter.env.ModuleEnvironment;
import org.rascalmpl.interpreter.result.ICallableValue;
import org.rascalmpl.interpreter.result.Result;
import org.rascalmpl.library.cobra.util.RandomUtil;
import org.rascalmpl.uri.URIUtil;

public class RandomValueTypeVisitor implements ITypeVisitor<IValue, RuntimeException> {

  private static final Random stRandom = new Random();

  private final IValueFactory vf;
  private final TypeFactory tf = TypeFactory.getInstance();
  private final ModuleEnvironment rootEnv;
  private final int maxDepth;
  private final HashMap<Type, ICallableValue> generators;
  private final Map<Type, Type> typeParameters;

  public RandomValueTypeVisitor(IValueFactory vf, ModuleEnvironment rootEnv,
      int maxDepth, HashMap<Type, ICallableValue> generators, Map<Type, Type> typeParameters) {
    this.vf = vf;
    this.rootEnv = rootEnv;
    this.maxDepth = maxDepth;
    this.generators = generators;
    this.typeParameters = typeParameters;
  }

  private IValue callGenerator(Type type, int depthLimit) {
    if (depthLimit < 0) {
      return null;
    }
    TypeFactory tf = TypeFactory.getInstance();

    ICallableValue generator = generators.get(type);
    Result<IValue> result = generator.call(new Type[] { tf.integerType() },
        new IValue[] { vf.integer(depthLimit) }, null);
    return result.getValue();
  }

  private RandomValueTypeVisitor descend() {
    RandomValueTypeVisitor visitor = new RandomValueTypeVisitor(vf,
        rootEnv, maxDepth - 1, generators, typeParameters);
    return visitor;
  }

  public IValue generate(Type t) {
    if (generators.containsKey(t)) {
      return callGenerator(t, maxDepth);
    } else {
      return t.accept(this);
    }
  }

  private IValue genSet(Type type) {
    ISetWriter writer = vf.setWriter(); // type.writer(vf);

    if (maxDepth <= 0 || (stRandom.nextInt(2) == 0)) {
      return writer.done();
    } else {
      RandomValueTypeVisitor visitor = descend();
      ISet set = (ISet) visitor.generate(type);

      IValue element = null;
      int recursionGuard = 0; // Domain of set can be small.
      while ((element == null || set.contains(element))
          && recursionGuard < 1000) {
        recursionGuard += 1;
        element = visitor.generate(type.getElementType());
      }

      writer.insertAll(set);

      if (element != null) {
        writer.insert(element);
      }
      return writer.done();
    }
  }

  @Override
  public IValue visitAbstractData(Type type) {
    LinkedList<Type> alternatives = new LinkedList<Type>();
    alternatives.addAll(this.rootEnv.lookupAlternatives(type));
    Collections.shuffle(alternatives);
    for (Type pick : alternatives) {
      IConstructor result = (IConstructor) this.generate(pick);
      if (result != null) {
        RandomValueTypeVisitor visitor = descend();
        Map<String, Type> annotations = rootEnv.getStore()
            .getAnnotations(type);
        for (Map.Entry<String, Type> entry : annotations.entrySet()) {
          IValue value = visitor.generate(entry.getValue());
          if (value == null) {
            return null;

          }
          result = result.asAnnotatable().setAnnotation(entry.getKey(), value);
        }

        return result;

      }
    }
    return null;
  }

  @Override
  public IValue visitAlias(Type type) {
    // Het is niet mogelijk om een circulaire alias te maken dus dit kost
    // geen diepte.
    return this.generate(type.getAliased());
  }

  @Override
  public IValue visitBool(Type boolType) {
    return vf.bool(stRandom.nextBoolean());
  }

  @Override
  public IValue visitConstructor(Type type) {
    /*
     * Following the common definition of depth of tree, the depth of an
     * algebraic datatype with zero arguments is 0 and the depth of an
     * alternative with more than 0 arguments is defined as the maximum
     * depth of the list of arguments plus 1.
     */

    if (type.getArity() == 0 && !type.hasKeywordParameters()) {
      return vf.constructor(type);
    } else if (this.maxDepth <= 0) {
      return null;
    }

    RandomValueTypeVisitor visitor = descend();

    LinkedList<IValue> values = new LinkedList<IValue>();
    for (int i = 0; i < type.getArity(); i++) {
      Type fieldType = type.getFieldType(i);
      IValue argument = visitor.generate(fieldType);
      if (argument == null) {
        return null;
        /*
         * Het is onmogelijk om de constructor te bouwen als ������n
         * argument null is.
         */
      }
      values.add(argument);
    }
    IValue[] params = values.toArray(new IValue[values.size()]);
    if (stRandom.nextBoolean() && type.getKeywordParameterTypes().getArity() > 0) {
      Map<String, IValue> kwParams = new HashMap<>();
      for (String kw:  type.getKeywordParameters()) {
        if (stRandom.nextBoolean()) continue;
        Type fieldType = type.getKeywordParameterType(kw);
        IValue argument = visitor.generate(fieldType);
        if (argument == null) {
          return null;
        }
        kwParams.put(kw, argument);
      }
      return vf.constructor(type, params, kwParams);
    }
    else {
      return vf.constructor(type, params);
    }

  }

  @Override
  public IValue visitDateTime(Type type) {
    Calendar cal = Calendar.getInstance();
    int milliOffset = stRandom.nextInt(1000) * (stRandom.nextBoolean() ? -1 : 1);
    cal.roll(Calendar.MILLISECOND, milliOffset);
    int second = stRandom.nextInt(60) * (stRandom.nextBoolean() ? -1 : 1);
    cal.roll(Calendar.SECOND, second);
    int minute = stRandom.nextInt(60) * (stRandom.nextBoolean() ? -1 : 1);
    cal.roll(Calendar.MINUTE, minute);
    int hour = stRandom.nextInt(60) * (stRandom.nextBoolean() ? -1 : 1);
    cal.roll(Calendar.HOUR_OF_DAY, hour);
    int day = stRandom.nextInt(30) * (stRandom.nextBoolean() ? -1 : 1);
    cal.roll(Calendar.DAY_OF_MONTH, day);
    int month = stRandom.nextInt(12) * (stRandom.nextBoolean() ? -1 : 1);
    cal.roll(Calendar.MONTH, month);
   
    // make sure we do not go over the 4 digit year limit, which breaks things
    int year = stRandom.nextInt(5000) * (stRandom.nextBoolean() ? -1 : 1);
   
    // make sure we don't go into negative territory
    if (cal.get(Calendar.YEAR) + year < 1)
      cal.add(Calendar.YEAR, 1);
    else
      cal.add(Calendar.YEAR, year);
   
    return vf.datetime(cal.getTimeInMillis());
  }

  @Override
  public IValue visitExternal(Type externalType) {
    throw new Throw(vf.string("Can't handle ExternalType."),
        (ISourceLocation) null, null);
  }

  @Override
  public IValue visitInteger(Type type) {
    return vf.integer(stRandom.nextInt());
  }
 
  private IValue genList(Type type){
    IListWriter writer = vf.listWriter(); // type.writer(vf);

    if (maxDepth <= 0 || (stRandom.nextInt(2) == 0)) {
      return writer.done();
    } else {
      RandomValueTypeVisitor visitor = descend();
      IValue element = visitor.generate(type.getElementType());
      if (element != null) {
        writer.append(element);
      }
      writer.appendAll((IList) visitor.generate(type));
      return writer.done();
    }
  }

  @Override
  public IValue visitList(Type type) {
    return genList(type);
  }

  @Override
  public IValue visitMap(Type type) {
    IMapWriter writer = vf.mapWriter(); // type.writer(vf);

    if (maxDepth <= 0 || (stRandom.nextInt(2) == 0)) {
      return writer.done();
    } else {

      RandomValueTypeVisitor visitor = descend();
      IValue key = visitor.generate(type.getKeyType());
      IValue value = visitor.generate(type.getValueType());

      if (key != null && value != null) {
        writer.put(key, value);
      }

      writer.putAll((IMap) visitor.generate(type));
      return writer.done();
    }
  }

  @Override
  public IValue visitNode(Type type) {
    String str = stRandom.nextBoolean() ? RandomUtil.string(stRandom, stRandom.nextInt(5)) : RandomUtil.stringAlpha(stRandom, stRandom.nextInt(5));

    int arity = maxDepth <= 0 ? 0: stRandom.nextInt(5);
    IValue[] args = new IValue[arity];
    for (int i = 0; i < arity; i++) {
      args[i] = descend().generate(tf.valueType());
    }
   
    if (stRandom.nextBoolean()) {
      int kwArity = 1 + stRandom.nextInt(5);
      Map<String, IValue> kwParams = new HashMap<>(kwArity);
      for (int i = 0; i < kwArity; i++) {
        String name = "";
        while (name.isEmpty()) {
          // names have to start with alpha character
          name = RandomUtil.stringAlpha(stRandom, 3);
        }
        name += RandomUtil.stringAlphaNumeric(stRandom, 4);
        IValue argument = descend().generate(tf.valueType());
        if (argument == null) {
          return null;
        }
        kwParams.put(name, argument);
      }
      return vf.node(str, args, kwParams);
    }
   
    return vf.node(str, args)
  }

  @Override
  public IValue visitNumber(Type type) {
    switch (stRandom.nextInt(3)) {
    case 0:
      return this.visitInteger(type);
    case 1:
      return this.visitReal(type);
    default:
      return this.visitRational(type);
    }
  }

  @Override
  public IValue visitParameter(Type parameterType) {
    // FIXME Type parameters binden aan echte type van actual value in call.
    Type type = typeParameters.get(parameterType);
    if(type == null){
      throw new IllegalArgumentException("Unbound type parameter " + parameterType);
    }
    return this.generate(type);
  }

  @Override
  public IValue visitRational(Type type) {
    return vf.rational(stRandom.nextInt(), stRandom.nextInt());
  }

  @Override
  public IValue visitReal(Type type) {
    return vf.real(stRandom.nextDouble());
  }

  @Override
  public IValue visitSet(Type type) {
    return genSet(type);
  }

  @Override
  public IValue visitSourceLocation(Type type) {
    if (maxDepth <= 0) {
      return vf.sourceLocation(URIUtil.assumeCorrect("tmp:///"));
    }
    else {
      try {
        String path = stRandom.nextDouble() < 0.9 ? RandomUtil.stringAlphaNumeric(stRandom, stRandom.nextInt(5)) : RandomUtil.string(stRandom, stRandom.nextInt(5));
        String nested = "";
        URI uri = URIUtil.assumeCorrect("tmp:///");
       
        if (stRandom.nextDouble() > 0.5) {
          RandomValueTypeVisitor visitor = descend();
          ISourceLocation loc = (ISourceLocation) visitor.generate(type);
          uri = loc.getURI();
          nested = uri.getPath();
        }
       
        path = path.startsWith("/") ? path : "/" + path;
        uri = URIUtil.changePath(uri, nested.length() > 0 && !nested.equals("/") ? nested + path : path);
       
        return vf.sourceLocation(uri);
      } catch (URISyntaxException e) {
        // generated illegal URI?
        return vf.sourceLocation(URIUtil.assumeCorrect("tmp:///"));
      }
    }
  }
 

  @Override
  public IValue visitString(Type type) {
    if (stRandom.nextBoolean() || maxDepth <= 0) {
      return vf.string("");
    }
    String result = null;
    if (generators.containsKey(type)) {
      // we have a custom string generator,  call that.
      RandomValueTypeVisitor visitor = descend();
      result = visitor.generate(type).toString();
    }
    else {
      // no custom generator so lets generate a string
      result = RandomUtil.string(stRandom, 1 + stRandom.nextInt(maxDepth + 3));
    }
    // make sure we are not generating very strange sequences
    result = Normalizer.normalize(result, Form.NFC);
    return vf.string(result);
  }


  @Override
  public IValue visitTuple(Type type) {
    RandomValueTypeVisitor visitor = descend();

    IValue[] elems = new IValue[type.getArity()];
    for (int i = 0; i < type.getArity(); i++) {
      Type fieldType = type.getFieldType(i);
      IValue element = visitor.generate(fieldType);
      if (element == null) {
        return null;
      }
      elems[i] = visitor.generate(fieldType);
    }
    return vf.tuple(elems);
  }

  @Override
  public IValue visitValue(Type type) {
    RandomType rt = new RandomType();
    return this.generate(rt.getType(maxDepth));
  }

  @Override
  public IValue visitVoid(Type type) {
    throw new Throw(vf.string("void has no values."),
        (ISourceLocation) null, null);
  }

}
TOP

Related Classes of org.rascalmpl.library.cobra.RandomValueTypeVisitor

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.