/* * © Copyright 2015 - SourceClear Inc */ package com.sourceclear.gramtest; import org.antlr.v4.runtime.RuleContext; import java.util.*; /** * */ public class GeneratorVisitor extends bnfBaseVisitor { ///////////////////////////// Class Attributes \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ////////////////////////////// Class Methods \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ //////////////////////////////// Attributes \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ private final Map<String,bnfParser.RhsContext> productionsMap = new HashMap<>(); private int maxNum = 100; private int maxDepth = 2; private int maxSize = 4; private int minSize = 1; private boolean useMinimalGenerator = true; private List<String> tests = new LinkedList<>(); private final Stack<String> prodHist = new Stack<>(); /////////////////////////////// Constructors \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ GeneratorVisitor() { } GeneratorVisitor(int num, int depth, int min, int max, boolean useMinimalGenerator) { this.maxNum = num; this.maxDepth = depth; this.minSize = min; this.maxSize = max; this.useMinimalGenerator = useMinimalGenerator; } ////////////////////////////////// Methods \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ //------------------------ Implements: //------------------------ Overrides: @Override public Object visitId(bnfParser.IdContext ctx) { return super.visitId(ctx); } @Override public String visitText(bnfParser.TextContext ctx) { return unquote(ctx.getText()); } @Override public String visitCaptext(bnfParser.CaptextContext ctx) { return ctx.getText(); } @Override public Object visitOneormore(bnfParser.OneormoreContext ctx) { return super.visitOneormore(ctx); } @Override public Object visitZeroormore(bnfParser.ZeroormoreContext ctx) { return super.visitZeroormore(ctx); } @Override public Object visitOptional(bnfParser.OptionalContext ctx) { return super.visitOptional(ctx); } @Override public List<String> visitElement(bnfParser.ElementContext ctx) { List<String> result = new LinkedList<>(); if(ctx.zeroormore() != null) { result = visitAlternatives(ctx.zeroormore().alternatives()); // one time List<String> twoLs = combineTwoLists(result, Collections.singletonList("")); // zero time result.addAll(twoLs); } else if(ctx.oneormore() != null) { result = visitAlternatives(ctx.oneormore().alternatives()); // one time List<String> twoLs = combineTwoLists(result,result); //two times result.addAll(twoLs); } else if(ctx.optional() != null) { result = visitAlternatives(ctx.optional().alternatives()); // one time result.add(""); // zero time } else if(ctx.captext() != null) { result.add(visitCaptext(ctx.captext())); } else if(ctx.text() != null) { result.add(visitText(ctx.text())); } else { String lhs = ctx.id().getText(); prodHist.push(lhs); bnfParser.RhsContext rhs = productionsMap.get(lhs); if(rhs.alternatives() == null || rhs.alternatives().alternative().stream().allMatch(ac -> isTerminalContext(ac.element())) || prodHist.size() < productionsMap.size()) { return visitRhs(productionsMap.get(lhs)); } else { int choices = rhs.alternatives().alternative().size(); int rand = new Random().nextInt(choices); result = visitAlternative(rhs.alternatives().alternative(rand)); if(!prodHist.empty()) prodHist.pop(); return result; } } return result; } @Override public List<String> visitAlternative(bnfParser.AlternativeContext ctx) { List<List<String>> comStr = new LinkedList<>(); for(bnfParser.ElementContext ec1 : ctx.element()) { if(prodHist.size() < maxDepth) { List<String> slist = visitElement(ec1); if (!slist.isEmpty()) comStr.add(slist); } } List<String> emptyStr = new LinkedList<>(); emptyStr.add(""); return generateAllStrings(comStr, emptyStr); } @Override public List<String> visitAlternatives(bnfParser.AlternativesContext ctx) { List<String> altStrs = new LinkedList<>(); List<bnfParser.AlternativeContext> acList = ctx.alternative(); Collections.shuffle(acList); for(bnfParser.AlternativeContext ac : acList) { altStrs.addAll(visitAlternative(ac)); } return altStrs; } @Override public List<String> visitRhs(bnfParser.RhsContext ctx) { List<String> tmp = visitAlternatives(ctx.alternatives()); if(!prodHist.empty()) prodHist.pop(); return tmp; } @Override public Object visitLhs(bnfParser.LhsContext ctx) { return super.visitLhs(ctx); } @Override public List<String> visitRule_(bnfParser.Rule_Context ctx) { return visitRhs(ctx.rhs()); } @Override public Object visitRulelist(bnfParser.RulelistContext ctx) { List<String> sentences = new LinkedList<>(); for(bnfParser.Rule_Context rc : ctx.rule_()) { productionsMap.put(rc.lhs().id().getText(), rc.rhs()); } maxDepth = maxDepth + productionsMap.size(); // Should not visit all the rules, but start from the top one /* for(bnfParser.Rule_Context rc : ctx.rule_()) { sentences.addAll(visitRule_(rc)); } */ sentences.addAll(visitRule_(ctx.rule_(0))); //sentences.removeIf(s -> s.length() < minSize); if(sentences.size() > maxNum) tests = sentences.subList(0, maxNum); // return only top maxNum test cases else tests = sentences; return super.visitRulelist(ctx); } //---------------------------- Abstract Methods ----------------------------- //---------------------------- Utility Methods ------------------------------ private String unquote(String s) { if (s != null && ((s.startsWith("\"") && s.endsWith("\"")) || (s.startsWith("'") && s.endsWith("'")))) { s = s.substring(1, s.length() - 1); } return s; } private List<String> generateAllStrings(List<List<String>> strList, List<String> result) { if(strList.size() > 0) { List<String> newResult = combineTwoLists(result,strList.remove(0)); if(newResult.isEmpty()) return result; else return generateAllStrings(strList, newResult); } return result; } private List<String> combineTwoLists(List<String> preList, List<String> postList) { List<String> combList = new LinkedList<>(); if(useMinimalGenerator) { Stack<String> preStack = new Stack<>(); preStack.addAll(preList); Stack<String> postStack = new Stack<>(); postStack.addAll(postList); while(!preStack.empty() && !postStack.empty()) { String s1 = preStack.pop(); String s2 = postStack.pop(); if(combList.size() < maxNum && (s1.length() + s2.length()) > minSize && !(s1.isEmpty() && s2.isEmpty())) combList.add(s1+s2); } } for(String s1 : preList) { for(String s2 : postList) { if(combList.size() < maxNum && (s1.length() + s2.length()) < maxSize && !(s1.isEmpty() && s2.isEmpty())) combList.add(s1+s2); } } return combList; } private Boolean isTerminalContext(List<bnfParser.ElementContext> eclist) { for(bnfParser.ElementContext ec : eclist) { if (ec.text() == null && ec.captext() == null) return false; } return true; } //---------------------------- Property Methods ----------------------------- public List<String> getTests() { return tests; } }