@Test
  public void testAggregateRule() {
    Parser<AggregateSlot> sut =
        TagSpaceTerminalParser.buildParser(TagSpaceRuleParser.AGGREGATE_SLOT_RULE);
    List<ValueDefinition> valueDefinitions =
        new LinkedList<>(
            Arrays.asList(
                vDef("clear", ""),
                vDef("encrypt", ""),
                vDef("clientSide", ""),
                vDef("doubleEncrypt", "")));

    assertEquals(
        new AggregateSlot("Transfer", "", valueDefinitions),
        sut.parse("Transfer: some of clear, encrypt, clientSide, doubleEncrypt."));
    assertEquals(
        new AggregateSlot("Transfer", "How we transfer the data", valueDefinitions),
        sut.parse(
            "Transfer [How we transfer the data]: some of clear, encrypt, clientSide, doubleEncrypt."));
    assertEquals(
        new AggregateSlot("Transfer", "How we transfer the data", valueDefinitions),
        sut.parse(
            "Transfer [How we transfer the data]<* err, what?*>: some of clear, encrypt, clientSide, doubleEncrypt. <--- la la la"));

    valueDefinitions.add(
        new ValueDefinition("superEncrypt", "does not really exist but sounds good"));
    assertEquals(
        new AggregateSlot("Transfer", "How we transfer the data", valueDefinitions),
        sut.parse(
            "Transfer [How we transfer the data]: some of clear, encrypt, clientSide, doubleEncrypt, superEncrypt [does not really exist but sounds good]."));
  }
  @Test
  public void testValueDefinition() {
    Parser<ValueDefinition> sut =
        TagSpaceTerminalParser.buildParser(TagSpaceRuleParser.VALUE_DEFINITION_RULE);
    assertEquals(new ValueDefinition("clear", ""), sut.parse("clear"));

    ValueDefinition expected = new ValueDefinition("clear", "a value that is not encrypted at all");
    assertEquals(expected, sut.parse("clear [a value that is not encrypted at all]"));
    assertEquals(
        expected,
        sut.parse("clear [a value that is not encrypted at all] <-- This is a line comment"));
    assertEquals(expected, sut.parse("<*un*>clear [a value that is not encrypted at all]"));
  }
 @Test
 public void testRule() {
   Parser<? extends AbstractSlot> sut =
       TagSpaceTerminalParser.buildParser(TagSpaceRuleParser.RULE);
   assertEquals(new ToDoSlot("Test", ""), sut.parse("Test: TODO."));
   assertEquals(
       new AtomicSlot("Test", "a test slot", Arrays.asList(vDef("A"), vDef("B", "value b"))),
       sut.parse("Test [a test slot]: one of A, B[value b]."));
   assertEquals(
       new AggregateSlot("Test", "", Arrays.asList(vDef("A"), vDef("B", "value b"))),
       sut.parse("Test: some of A, B[value b]."));
   assertEquals(
       new CompoundSlot("Test", "", Arrays.asList("A", "B", "C")),
       sut.parse("Test: consists of A, B, C."));
 }
  @Test
  public void testCompoundRule() {
    Parser<CompoundSlot> sut =
        TagSpaceTerminalParser.buildParser(TagSpaceRuleParser.COMPOUND_SLOT_RULE);
    List<String> subSlots = Arrays.asList("HIPAA", "FERPA", "PrivacyAct", "JUnit");

    assertEquals(
        new CompoundSlot("Legal", "", subSlots),
        sut.parse("Legal: consists of HIPAA, FERPA, PrivacyAct, JUnit."));
    assertEquals(
        new CompoundSlot("Legal", "Aspects of the dataset by act", subSlots),
        sut.parse(
            "Legal [Aspects of the dataset by act]: consists of HIPAA, FERPA, PrivacyAct, JUnit."));
    assertEquals(
        new CompoundSlot("Legal", "Aspects of the dataset by act", subSlots),
        sut.parse(
            "Legal<*please ignore me!*> [Aspects of the dataset by act]: consists of HIPAA, FERPA, PrivacyAct, JUnit. <--- la la la"));
  }
  @Test
  public void testAtomicRule() {
    Parser<AtomicSlot> sut =
        TagSpaceTerminalParser.buildParser(TagSpaceRuleParser.ATOMIC_SLOT_RULE);
    List<ValueDefinition> valueDefinitions =
        Arrays.asList(
            vDef("clear", ""),
            vDef("encrypt", ""),
            vDef("clientSide", ""),
            vDef("doubleEncrypt", ""));

    assertEquals(
        new AtomicSlot("Transfer", "", valueDefinitions),
        sut.parse("Transfer: one of clear, encrypt, clientSide, doubleEncrypt."));
    assertEquals(
        new AtomicSlot("Transfer", "How we transfer the data", valueDefinitions),
        sut.parse(
            "Transfer [How we transfer the data]: one of clear, encrypt, clientSide, doubleEncrypt."));
    assertEquals(
        new AtomicSlot("Transfer", "How we transfer the data", valueDefinitions),
        sut.parse(
            "Transfer [How we transfer the data]<* err, what?*>: one of clear, encrypt, clientSide, doubleEncrypt. <--- la la la"));
  }
 @Test
 public void testTodoRule() {
   Parser<ToDoSlot> sut = TagSpaceTerminalParser.buildParser(TagSpaceRuleParser.TODO_RULE);
   assertEquals(new ToDoSlot("Procrastination", ""), sut.parse("Procrastination: TODO."));
   assertEquals(new ToDoSlot("Procrastination", ""), sut.parse("Procrastination: TODO   ."));
   assertEquals(new ToDoSlot("Procrastination", ""), sut.parse("Procrastination: TODO   \n."));
   assertEquals(
       new ToDoSlot("Procrastination", ""),
       sut.parse("\t \t Procrastination: \n \t \t \tTODO<*!!!*>   \n."));
   assertEquals(
       new ToDoSlot("Procrastination", "I'll get to this later"),
       sut.parse("Procrastination [I'll get to this later]: TODO."));
   assertEquals(
       new ToDoSlot("Procrastination", "I'll get to this later"),
       sut.parse("Procrastination [I'll get to this later] <--- Yeah, right\n\t: TODO."));
 }
/**
 * Plugin for string related literals, operations, and functions.
 *
 * @author Mashaal Memon, Roozbeh Farahbod
 */
public class StringPlugin extends Plugin
    implements ParserPlugin, InterpreterPlugin, OperatorProvider, VocabularyExtender {

  public static final VersionInfo VERSION_INFO = new VersionInfo(0, 4, 1, "");

  public static final String STRING_TOKEN = "STRING";

  public static final String STRINGCONCAT_OP = "+";

  public static final String PLUGIN_NAME = StringPlugin.class.getSimpleName();

  // TODO why do we have this as a static field?
  // public static StringBackgroundElement STRING_BACKGROUND_ELEMENT;

  private StringBackgroundElement stringBackgroundElement;

  private Map<String, FunctionElement> funcs = null;
  private Map<String, BackgroundElement> backgroundElements = null;

  private Map<String, GrammarRule> parsers = null;
  private Set<Parser<? extends Object>> lexers = null;

  // private final Parser<Node>[] stringTermParserArray = new Parser[1];
  // private final Parser<Node> stringTermParser = ParserTools.lazy("StringTerm",
  // stringTermParserArray);
  Parser.Reference<Node> refStringTermParser = Parser.newReference();

  Parser<String> tokenizer_str = null;

  private final String[] keywords = {};
  private final String[] operators = {"+"};

  private CompilerPlugin compilerPlugin = new CompilerStringPlugin(this);

  @Override
  public CompilerPlugin getCompilerPlugin() {
    return compilerPlugin;
  }

  public String[] getKeywords() {
    return keywords;
  }

  public String[] getOperators() {
    return operators;
  }

  /* (non-Javadoc)
   * @see org.coreasm.engine.Plugin#interpret(org.coreasm.engine.interpreter.Node)
   */
  public ASTNode interpret(Interpreter interpreter, ASTNode pos) {

    ASTNode nextPos = pos;
    String x = pos.getToken();
    String gClass = pos.getGrammarClass();

    // if number related expression
    if (gClass.equals(ASTNode.EXPRESSION_CLASS)) {
      // it is a number constant
      if (x != null) {

        // get new string element representing lexeme from string background
        StringElement se = stringBackgroundElement.getNewValue(x);

        // result of this node is the string element produced
        pos.setNode(null, null, se);
      }
    }

    return nextPos;
  }

  public Set<Parser<? extends Object>> getLexers() {
    if (lexers == null) {
      lexers = new HashSet<Parser<? extends Object>>();

      tokenizer_str = Terminals.StringLiteral.DOUBLE_QUOTE_TOKENIZER;
      lexers.add(tokenizer_str);
    }
    return lexers;
  }

  /*
   * @see org.coreasm.engine.plugin.ParserPlugin#getParser(java.lang.String)
   */
  public Parser<Node> getParser(String nonterminal) {
    if (nonterminal.equals("StringTerm")) return refStringTermParser.lazy();
    else return null;
  }

  public Map<String, GrammarRule> getParsers() {
    if (parsers == null) {
      // org.coreasm.engine.parser.Parser parser = capi.getParser();
      parsers = new HashMap<String, GrammarRule>();

      Parser<Node> stringParser =
          Terminals.StringLiteral.PARSER
              .token()
              .map(
                  new org.codehaus.jparsec.functors.Map<Token, Node>() {
                    @Override
                    public Node map(Token from) {
                      return new StringNode(
                          StringElement.processEscapeCharacters(from.toString()),
                          new ScannerInfo(from));
                    }
                  });

      refStringTermParser.set(stringParser);
      parsers.put(
          "ConstantTerm",
          new GrammarRule("StringConstantTerm", "STRING", refStringTermParser.lazy(), PLUGIN_NAME));
    }

    return parsers;
  }

  /* (non-Javadoc)
   * @see org.coreasm.engine.Plugin#initialize()
   */
  @Override
  public void initialize() {}

  // --------------------------------
  // Vocabulary Extender Interface
  // --------------------------------

  /** @see org.coreasm.engine.plugin.VocabularyExtender#getFunctions() */
  public Map<String, FunctionElement> getFunctions() {
    if (funcs == null) {
      funcs = new HashMap<String, FunctionElement>();
      funcs.put(ToStringFunctionElement.TOSTRING_FUNC_NAME, new ToStringFunctionElement());
      funcs.put(StringLengthFunctionElement.STRLENGTH_FUNC_NAME, new StringLengthFunctionElement());
      funcs.put(
          StringMatchingFunction.STRING_MATCHES_FUNCTION_NAME, new StringMatchingFunction(capi));
      funcs.put(
          StringSubstringFunction.STRING_SUBSTRING_FUNCTION_NAME, new StringSubstringFunction());
    }
    return funcs;
  }

  public Set<String> getRuleNames() {
    return Collections.emptySet();
  }

  public Map<String, RuleElement> getRules() {
    return null;
  }

  /** @see org.coreasm.engine.plugin.VocabularyExtender#getUniverses() */
  public Map<String, UniverseElement> getUniverses() {
    // no universes
    return Collections.emptyMap();
  }

  /** @see org.coreasm.engine.plugin.VocabularyExtender#getBackgrounds() */
  public Map<String, BackgroundElement> getBackgrounds() {
    if (backgroundElements == null) {
      backgroundElements = new HashMap<String, BackgroundElement>();
      stringBackgroundElement = new StringBackgroundElement();
      backgroundElements.put(
          StringBackgroundElement.STRING_BACKGROUND_NAME, stringBackgroundElement);
    }
    return backgroundElements;
  }

  // --------------------------------
  // Operator Implementor Interface
  // --------------------------------

  public Collection<OperatorRule> getOperatorRules() {

    ArrayList<OperatorRule> opRules = new ArrayList<OperatorRule>();

    opRules.add(new OperatorRule(STRINGCONCAT_OP, OpType.INFIX_LEFT, 750, PLUGIN_NAME));

    return opRules;
  }

  public Element interpretOperatorNode(Interpreter interpreter, ASTNode opNode)
      throws InterpreterException {
    Element result = null;
    String x = opNode.getToken();
    String gClass = opNode.getGrammarClass();

    // if class of operator is binary
    if (gClass.equals(ASTNode.BINARY_OPERATOR_CLASS)) {

      // get operand nodes
      ASTNode alpha = opNode.getFirst();
      ASTNode beta = alpha.getNext();

      // get operand values
      Element l = alpha.getValue();
      Element r = beta.getValue();

      // if both operands are undef, the result is undef
      if (l.equals(Element.UNDEF) && r.equals(Element.UNDEF)) {
        capi.warning(
            PLUGIN_NAME,
            "Both operands of the '" + x + "' operator were undef.",
            opNode,
            interpreter);
        result = Element.UNDEF;
      } else
      // confirm that at least one of the operands is a string elements, otherwise throw an error
      if ((l instanceof StringElement || r instanceof StringElement)) {
        if (x.equals(STRINGCONCAT_OP))
          result = stringBackgroundElement.getNewValue(l.toString() + r);
      }
      // otherwise
      //    throw new InterpreterException("At least one operand must be strings for '"+x+"'
      // operation.");
    }

    return result;
  }

  /** This plugin requires "NumberPlugin". */
  public Set<String> getDependencyNames() {
    Set<String> names = new HashSet<String>(super.getDependencyNames());
    names.add("NumberPlugin");
    return names;
  }

  public Set<String> getBackgroundNames() {
    return backgroundElements.keySet();
  }

  public Set<String> getFunctionNames() {
    return funcs.keySet();
  }

  public Set<String> getUniverseNames() {
    return Collections.emptySet();
  }

  public VersionInfo getVersionInfo() {
    return VERSION_INFO;
  }

  /** A pattern to match string litterals. */
  //	public static final class StringPattern extends Pattern {
  //
  //		private static final long serialVersionUID = 1L;
  //
  //		public int match(final CharSequence src, final int len, final int from) {
  //	        if(from >= len)
  //	        	return Pattern.MISMATCH;
  //	        if (src.charAt(from) != '"')
  //	        	return Pattern.MISMATCH;
  //
  //	        for (int i=from+1; i < len; i++) {
  //	        	char c = src.charAt(i);
  //	        	if (c == '"' && src.charAt(i-1) != '\\')
  //	        		return i+1 - from;
  //	        }
  //
  //	        return Pattern.MISMATCH;
  //		}
  //
  //		public String toString(){
  //			return "string constant";
  //		}
  //
  //	}

  /** Creates string tokens. */
  //	@SuppressWarnings("serial")
  //	public static class StringTokenizer implements Tokenizer {
  //
  //		public Object toToken(CharSequence cs, int from, int len) {
  //			TypedToken<StringTokenType> tToken =
  //				new TypedToken<StringTokenType>(cs.subSequence(from, from + len).toString(),
  //						StringTokenType.String);
  //			return tToken;
  //		}
  //
  //	}

  /**
   * Type of string tokens.
   *
   * @author trident
   */
  public static enum StringTokenType {
    String
  }
}