docs: @Override
    public CcgExample parseFrom(String exampleString) {
      String[] parts = exampleString.split("###");
      List<String> words = Arrays.asList(parts[0].split("\\s+"));
  
      Set<DependencyStructure> dependencies = Sets.newHashSet();
      String[] dependencyParts = new CsvParser(CsvParser.DEFAULT_SEPARATOR,
          CsvParser.DEFAULT_QUOTE, CsvParser.NULL_ESCAPE).parseLine(parts[1]);
      for (int i = 0; i < dependencyParts.length; i++) {
        if (dependencyParts[i].trim().length() == 0) {
          continue;
        }
        String[] dep = dependencyParts[i].split("\\s+");
        Preconditions.checkState(dep.length >= 6, "Illegal dependency string: " + dependencyParts[i]);
  
        dependencies.add(new DependencyStructure(dep[0], Integer.parseInt(dep[2]), 
            HeadedSyntacticCategory.parseFrom(dep[1]).getCanonicalForm(), dep[4],
            Integer.parseInt(dep[5]), Integer.parseInt(dep[3])));
      }
  
      // Parse out a CCG syntactic tree, if one is provided.
      CcgSyntaxTree tree = null;
      List<String> posTags = null;
      if (parts.length >= 3 && parts[2].length() > 0) {
        tree = syntaxTreeReader.parseFrom(parts[2]);
        posTags = tree.getAllSpannedPosTags();
      } else {
        posTags = Collections.nCopies(words.size(), ParametricCcgParser.DEFAULT_POS_TAG);
      }
  
      // Parse out a logical form, if one is provided.
      Expression2 logicalForm = null;
      if (parts.length >= 4 && parts[3].length() > 0) {
        logicalForm = ExpressionParser.expression2().parse(parts[3]);
      }
  
      if (!ignoreSemantics) {
        return new CcgExample(new AnnotatedSentence(words, posTags), dependencies, tree,
            logicalForm);
      } else {
        return new CcgExample(new AnnotatedSentence(words, posTags), null, tree,
            logicalForm);
      }
    }