/******************************************************************************* * 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 - added type parameters *******************************************************************************/ package org.rascalmpl.library.cobra; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.rascalmpl.interpreter.IEvaluatorContext; import org.rascalmpl.interpreter.result.AbstractFunction; import org.rascalmpl.interpreter.result.OverloadedFunction; import org.rascalmpl.interpreter.types.ReifiedType; import org.rascalmpl.interpreter.utils.RuntimeExceptionFactory; import org.rascalmpl.library.cobra.util.NullOutputStream; import org.rascalmpl.value.IBool; import org.rascalmpl.value.IInteger; import org.rascalmpl.value.IString; import org.rascalmpl.value.IValue; import org.rascalmpl.value.IValueFactory; import org.rascalmpl.value.type.Type; public class Cobra { public static final String TRIES = "tries"; public static final String MAXDEPTH = "maxDepth"; public static Type reifyType(IValue type) { Type reified = type.getType(); if (!(reified instanceof ReifiedType)) { throw RuntimeExceptionFactory.illegalArgument(type, null, null, "A reified type is required instead of " + reified); } return reified.getTypeParameters().getFieldType(0); } final IValueFactory vf; private final QuickCheck quickcheck; public Cobra(IValueFactory vf) { this.vf = vf; this.quickcheck = QuickCheck.getInstance(); } public static int readIntTag(AbstractFunction test, String key, int defaultVal) { if (test.hasTag(key)) { int result = Integer.parseInt(((IString) test.getTag(key)).getValue()); if (result < 1) { throw new IllegalArgumentException(key + " smaller than 1"); } return result; } else { return defaultVal; } } public IValue _quickcheck(IValue function, IBool verbose, IBool maxVerbose, IEvaluatorContext eval) { return _quickcheck(function, verbose, maxVerbose, null, null, eval); } public IValue _quickcheck(IValue function, IBool verbose, IBool maxVerbose, IInteger maxDepth, IInteger tries, IEvaluatorContext eval) { NullOutputStream nullStream = new NullOutputStream(); PrintWriter printer = new PrintWriter(nullStream); PrintWriter out = (verbose.getValue()) ? eval.getStdOut() : printer; ArrayList<AbstractFunction> functions = extractFunctions(function, eval); if (!isReturnTypeBool(functions)) { if(out == printer){ out.close(); printer.close(); } throw RuntimeExceptionFactory.illegalArgument(function, eval.getCurrentAST(), null, "Return type should be bool."); } try { boolean result = true; for (AbstractFunction f : functions) { int _maxDepth, _tries; _maxDepth = getAnnotation(eval, f, Cobra.MAXDEPTH, maxDepth, 5); _tries = getAnnotation(eval, f, Cobra.TRIES, tries, 5); result = result && quickcheck.quickcheck(f, _maxDepth, _tries, maxVerbose.getValue(), out); } return eval.getValueFactory().bool(result); } catch (IllegalArgumentException e) { throw RuntimeExceptionFactory.illegalArgument(maxDepth, eval.getCurrentAST(), null, e.getMessage()); } finally { out.flush(); } } private int getAnnotation(IEvaluatorContext eval, AbstractFunction f, String tag, IInteger override, int defaultVal) { try { return (override == null) ? readIntTag(f, tag, defaultVal) : override.intValue(); } catch (IllegalArgumentException e) { throw RuntimeExceptionFactory.illegalArgument(vf.string(tag), eval.getCurrentAST(), null, "Annotation: " + e.getMessage()); } } public IValue arbitrary(IValue type, IInteger depthLimit, IEvaluatorContext eval) { try { TypeParameterVisitor tpvisit = new TypeParameterVisitor(); Type requestedType = Cobra.reifyType(type); HashMap<Type, Type> tpbindings = tpvisit.bindTypeParameters(requestedType); IValue result = quickcheck.arbitrary(requestedType, depthLimit.intValue(), eval.getCurrentEnvt().getRoot(), eval.getValueFactory(), tpbindings); return result; } catch (IllegalArgumentException e) { throw RuntimeExceptionFactory.illegalArgument(depthLimit, eval.getCurrentAST(), null, "No construction possible at this depth or less."); } } private ArrayList<AbstractFunction> extractFunctions(IValue function, IEvaluatorContext eval) { ArrayList<AbstractFunction> functions = new ArrayList<AbstractFunction>(); if (function instanceof AbstractFunction) { functions.add((AbstractFunction) function); } else if (function instanceof OverloadedFunction) { for (AbstractFunction f : ((OverloadedFunction) function) .getFunctions()) { functions.add(f); } } else { throw RuntimeExceptionFactory.illegalArgument(function, eval.getCurrentAST(), null, "Argument should be function."); } return functions; } public IValue getGenerator(IValue t, IEvaluatorContext eval) { return quickcheck.getGenerator(t, eval); } private boolean isReturnTypeBool(List<AbstractFunction> functions) { for(AbstractFunction f: functions){ if (!f.getReturnType().isBool()) { return false; } } return true; } public void resetGenerator(IValue type) { quickcheck.resetGenerator(type); } public void setGenerator(IValue f) { quickcheck.setGenerator(f); } }