function parse(sourceLiterals, values, doParse, cache) {
  let tree = cache.get(sourceLiterals);
  if (!tree) {
    let source = insertPlaceholderIdentifiers(sourceLiterals);
    let errorReporter = new CollectingErrorReporter();
    let parser = getParser(source, errorReporter);
    tree = doParse(parser);
    if (errorReporter.hadError() || !tree || !parser.isAtEnd()) {
      throw new Error(
          `Internal error trying to parse:\n\n${source}\n\n${
              errorReporter.errorsAsString()}`);
    }
    cache.set(sourceLiterals, tree);
  }
  if (!values.length)
    return tree;

  // We allow either a ParseTree or an Array as the result of doParse. An
  // array is returned for parseStatements.
  if (tree instanceof ParseTree)
    return new PlaceholderTransformer(values).transformAny(tree);
  return new PlaceholderTransformer(values).transformList(tree);
}
@param {Array.<string>} sourceLiterals
@param {Array} values An array containing values or parse trees.
@return {ParseTree}