Пример #1
0
bool Parser::parseGenericArguments(SmallVectorImpl<TypeRepr*> &Args,
                                   SourceLoc &LAngleLoc,
                                   SourceLoc &RAngleLoc) {
  // Parse the opening '<'.
  assert(startsWithLess(Tok) && "Generic parameter list must start with '<'");
  LAngleLoc = consumeStartingLess();

  do {
    ParserResult<TypeRepr> Ty = parseType(diag::expected_type);
    if (Ty.isNull() || Ty.hasCodeCompletion()) {
      // Skip until we hit the '>'.
      RAngleLoc = skipUntilGreaterInTypeList();
      return true;
    }

    Args.push_back(Ty.get());
    // Parse the comma, if the list continues.
  } while (consumeIf(tok::comma));

  if (!startsWithGreater(Tok)) {
    checkForInputIncomplete();
    diagnose(Tok, diag::expected_rangle_generic_arg_list);
    diagnose(LAngleLoc, diag::opening_angle);

    // Skip until we hit the '>'.
    RAngleLoc = skipUntilGreaterInTypeList();
    return true;
  } else {
    RAngleLoc = consumeStartingGreater();
  }

  return false;
}
Пример #2
0
/// Parse an optional type annotation on a pattern.
///
///  pattern-type-annotation ::= (':' type)?
///
ParserResult<Pattern> Parser::
parseOptionalPatternTypeAnnotation(ParserResult<Pattern> result,
                                   bool isOptional) {

  // Parse an optional type annotation.
  if (!consumeIf(tok::colon))
    return result;

  Pattern *P;
  if (result.isNull())  // Recover by creating AnyPattern.
    P = new (Context) AnyPattern(Tok.getLoc());
  else
    P = result.get();

  ParserResult<TypeRepr> Ty = parseType();
  if (Ty.hasCodeCompletion())
    return makeParserCodeCompletionResult<Pattern>();

  TypeRepr *repr = Ty.getPtrOrNull();
  if (!repr)
    repr = new (Context) ErrorTypeRepr(PreviousLoc);

  // In an if-let, the actual type of the expression is Optional of whatever
  // was written.
  if (isOptional)
    repr = new (Context) OptionalTypeRepr(repr, Tok.getLoc());

  return makeParserResult(new (Context) TypedPattern(P, repr));
}
Пример #3
0
/// Parse a pattern with an optional type annotation.
///
///  typed-pattern ::= pattern (':' type)?
///
ParserResult<Pattern> Parser::parseTypedPattern() {
  auto result = parsePattern();
  
  // Now parse an optional type annotation.
  if (Tok.is(tok::colon)) {
    SourceLoc colonLoc = consumeToken(tok::colon);
    
    if (result.isNull())  // Recover by creating AnyPattern.
      result = makeParserErrorResult(new (Context) AnyPattern(colonLoc));
    
    ParserResult<TypeRepr> Ty = parseType();
    if (Ty.hasCodeCompletion())
      return makeParserCodeCompletionResult<Pattern>();
    if (!Ty.isNull()) {
      // Attempt to diagnose initializer calls incorrectly written
      // as typed patterns, such as "var x: [Int]()".
      if (Tok.isFollowingLParen()) {
        BacktrackingScope backtrack(*this);
        
        // Create a local context if needed so we can parse trailing closures.
        LocalContext dummyContext;
        Optional<ContextChange> contextChange;
        if (!CurLocalContext) {
          contextChange.emplace(*this, CurDeclContext, &dummyContext);
        }
        
        SourceLoc lParenLoc, rParenLoc;
        SmallVector<Expr *, 2> args;
        SmallVector<Identifier, 2> argLabels;
        SmallVector<SourceLoc, 2> argLabelLocs;
        Expr *trailingClosure;
        ParserStatus status = parseExprList(tok::l_paren, tok::r_paren,
                                            /*isPostfix=*/true,
                                            /*isExprBasic=*/false,
                                            lParenLoc, args, argLabels,
                                            argLabelLocs, rParenLoc,
                                            trailingClosure);
        if (status.isSuccess()) {
          backtrack.cancelBacktrack();
          
          // Suggest replacing ':' with '='
          diagnose(lParenLoc, diag::initializer_as_typed_pattern)
            .highlight({Ty.get()->getStartLoc(), rParenLoc})
            .fixItReplace(colonLoc, " = ");
          result.setIsParseError();
        }
      }
    } else {
      Ty = makeParserResult(new (Context) ErrorTypeRepr(PreviousLoc));
    }
    
    result = makeParserResult(result,
                            new (Context) TypedPattern(result.get(), Ty.get()));
  }
  
  return result;
}
Пример #4
0
bool SceneGraph::add(const char *filename, bool bInitialize, void (*callbackFn)(int nLine, void *info), void *callbackFnInfo)
{
	int fileFormat = GetFileFormat(filename);

	Parser *parser = NULL;
	ParserResult *presult = getParserResult();

	presult->init();
	presult->setParserResult(false);

	switch (fileFormat) {
	case FILE_FORMAT_VRML:
		parser = new VRML97Parser();
		break;
#ifdef CX3D_SUPPORT_X3D
	case FILE_FORMAT_XML:
		parser = new X3DParser();
		break;
#endif
#ifdef CX3D_SUPPORT_OBJ
	case FILE_FORMAT_OBJ:
		parser = new OBJParser();
		break;
#endif
	}

	if (parser == NULL)
		return false;

	SetParserResultObject(presult);

	bool parserRet = parser->load(filename, callbackFn, callbackFnInfo);
	presult->setParserResult(parserRet);
	if (parserRet == false) {
		delete parser;
		return false;
	}

	moveParserNodes(parser);
	moveParserRoutes(parser);

        delete parser;

	if (bInitialize)
		initialize();

	setBackgroundNode(findBackgroundNode(), true);
	setFogNode(findFogNode(), true);
	setNavigationInfoNode(findNavigationInfoNode(), true);
	setViewpointNode(findViewpointNode(), true);

	return true;
}
Пример #5
0
/// parseTypeComposition
///   
///   type-composition:
///     'protocol' '<' type-composition-list? '>'
///
///   type-composition-list:
///     type-identifier (',' type-identifier)*
///
ParserResult<ProtocolCompositionTypeRepr> Parser::parseTypeComposition() {
  SourceLoc ProtocolLoc = consumeToken(tok::kw_protocol);
 
  // Check for the starting '<'.
  if (!startsWithLess(Tok)) {
    diagnose(Tok, diag::expected_langle_protocol);
    return nullptr;
  }
  SourceLoc LAngleLoc = consumeStartingLess();
  
  // Check for empty protocol composition.
  if (startsWithGreater(Tok)) {
    SourceLoc RAngleLoc = consumeStartingGreater();
    return makeParserResult(new (Context) ProtocolCompositionTypeRepr(
                                             ArrayRef<IdentTypeRepr *>(),
                                             ProtocolLoc,
                                             SourceRange(LAngleLoc,
                                                         RAngleLoc)));
  }
  
  // Parse the type-composition-list.
  ParserStatus Status;
  SmallVector<IdentTypeRepr *, 4> Protocols;
  do {
    // Parse the type-identifier.
    ParserResult<IdentTypeRepr> Protocol = parseTypeIdentifier();
    Status |= Protocol;
    if (Protocol.isNonNull())
      Protocols.push_back(Protocol.get());
  } while (consumeIf(tok::comma));
  
  // Check for the terminating '>'.
  SourceLoc EndLoc = PreviousLoc;
  if (startsWithGreater(Tok)) {
    EndLoc = consumeStartingGreater();
  } else {
    if (Status.isSuccess()) {
      diagnose(Tok, diag::expected_rangle_protocol);
      diagnose(LAngleLoc, diag::opening_angle);
      Status.setIsParseError();
    }

    // Skip until we hit the '>'.
    EndLoc = skipUntilGreaterInTypeList(/*protocolComposition=*/true);
  }

  return makeParserResult(Status, ProtocolCompositionTypeRepr::create(
      Context, Protocols, ProtocolLoc, SourceRange(LAngleLoc, EndLoc)));
}
Пример #6
0
SAWYER_EXPORT std::string
PodFormatter::toNroff(const ParserResult &parsed) {
    // Generate POD documentation into a temporary file
    TempFile tmpfile(tempFileName(".pod"));
    parsed.emit(tmpfile.stream, sharedFromThis());
    tmpfile.stream.close();

    std::string cmd = "pod2man"
                      " --center='" + escapeSingleQuoted(chapterName_) + "'"
                      " --date='" + escapeSingleQuoted(dateString_) + "'"
                      " --name='" + escapeSingleQuoted(pageName_) + "'"
                      " --release='" + escapeSingleQuoted(versionString_) + "'"
                      " --section='" + escapeSingleQuoted(chapterNumber_) + "'"
                      " " + tmpfile.name;

    FILE *f = popen(cmd.c_str(), "r");
    if (!f) {
#include <Sawyer/WarningsOff.h>                         // suppress strerror unsafe warning from Microsoft C++
        throw std::runtime_error(std::string("cannot run command: ") + strerror(errno) + "\ncommand: " + cmd);
#include <Sawyer/WarningsRestore.h>
    }
    
    std::string result;
    while (1) {
        std::string line = readOneLine(f);
        if (line.empty())
            break;
        result += line;
    }

    if (-1 == Sawyer::pclose(f))
        throw std::runtime_error("command failed: " + cmd);

    return result;
}
Пример #7
0
ParserResult<TypeRepr> Parser::parseTypeCollection() {
  // Parse the leading '['.
  assert(Tok.is(tok::l_square));
  Parser::StructureMarkerRAII parsingCollection(*this, Tok);
  SourceLoc lsquareLoc = consumeToken();

  // Parse the element type.
  ParserResult<TypeRepr> firstTy = parseType(diag::expected_element_type);

  // If there is a ':', this is a dictionary type.
  SourceLoc colonLoc;
  ParserResult<TypeRepr> secondTy;
  if (Tok.is(tok::colon)) {
    colonLoc = consumeToken();

    // Parse the second type.
    secondTy = parseType(diag::expected_dictionary_value_type);
  }

  // Parse the closing ']'.
  SourceLoc rsquareLoc;
  parseMatchingToken(tok::r_square, rsquareLoc,
                     colonLoc.isValid()
                       ? diag::expected_rbracket_dictionary_type
                       : diag::expected_rbracket_array_type, 
                     lsquareLoc);

  if (firstTy.hasCodeCompletion() || secondTy.hasCodeCompletion())
    return makeParserCodeCompletionStatus();

  // If we couldn't parse anything for one of the types, propagate the error.
  if (firstTy.isNull() || (colonLoc.isValid() && secondTy.isNull()))
    return makeParserError();

  // Form the dictionary type.
  SourceRange brackets(lsquareLoc, rsquareLoc);
  if (colonLoc.isValid())
    return makeParserResult(ParserStatus(firstTy) | ParserStatus(secondTy),
                            new (Context) DictionaryTypeRepr(firstTy.get(),
                                                             secondTy.get(),
                                                             colonLoc,
                                                             brackets));
    
  // Form the array type.
  return makeParserResult(firstTy,
                          new (Context) ArrayTypeRepr(firstTy.get(),
                                                      brackets));
}
Пример #8
0
/// Parse a pattern with an optional type annotation.
///
///  typed-pattern ::= pattern (':' type)?
///
ParserResult<Pattern> Parser::parseTypedPattern() {
  auto result = parsePattern();
  
  // Now parse an optional type annotation.
  if (consumeIf(tok::colon)) {
    if (result.isNull())  // Recover by creating AnyPattern.
      result = makeParserErrorResult(new (Context) AnyPattern(PreviousLoc));
    
    ParserResult<TypeRepr> Ty = parseType();
    if (Ty.hasCodeCompletion())
      return makeParserCodeCompletionResult<Pattern>();
    if (Ty.isNull())
      Ty = makeParserResult(new (Context) ErrorTypeRepr(PreviousLoc));
    
    result = makeParserResult(result,
                            new (Context) TypedPattern(result.get(), Ty.get()));
  }
  
  return result;
}
Пример #9
0
/// Parse an element of a tuple pattern.
///
///   pattern-tuple-element:
///     (identifier ':')? pattern
std::pair<ParserStatus, Optional<TuplePatternElt>>
Parser::parsePatternTupleElement() {
  // If this element has a label, parse it.
  Identifier Label;
  SourceLoc LabelLoc;

  // If the tuple element has a label, parse it.
  if (Tok.is(tok::identifier) && peekToken().is(tok::colon)) {
    LabelLoc = consumeIdentifier(&Label);
    consumeToken(tok::colon);
  }

  // Parse the pattern.
  ParserResult<Pattern>  pattern = parsePattern();
  if (pattern.hasCodeCompletion())
    return std::make_pair(makeParserCodeCompletionStatus(), None);
  if (pattern.isNull())
    return std::make_pair(makeParserError(), None);

  auto Elt = TuplePatternElt(Label, LabelLoc, pattern.get());
  return std::make_pair(makeParserSuccess(), Elt);
}
Пример #10
0
ParserResult<Pattern> Parser::parseMatchingPatternAsLetOrVar(bool isLet,
                                                             SourceLoc varLoc,
                                                             bool isExprBasic) {
  // 'var' and 'let' patterns shouldn't nest.
  if (InVarOrLetPattern == IVOLP_InLet ||
      InVarOrLetPattern == IVOLP_InVar)
    diagnose(varLoc, diag::var_pattern_in_var, unsigned(isLet));
  
  // 'let' isn't valid inside an implicitly immutable context, but var is.
  if (isLet && InVarOrLetPattern == IVOLP_ImplicitlyImmutable)
    diagnose(varLoc, diag::let_pattern_in_immutable_context);

  // In our recursive parse, remember that we're in a var/let pattern.
  llvm::SaveAndRestore<decltype(InVarOrLetPattern)>
    T(InVarOrLetPattern, isLet ? IVOLP_InLet : IVOLP_InVar);

  ParserResult<Pattern> subPattern = parseMatchingPattern(isExprBasic);
  if (subPattern.isNull())
    return nullptr;
  auto *varP = new (Context) VarPattern(varLoc, isLet, subPattern.get());
  return makeParserResult(varP);
}
Пример #11
0
/// matching-pattern ::= 'is' type
/// matching-pattern ::= matching-pattern-var
/// matching-pattern ::= expr
///
ParserResult<Pattern> Parser::parseMatchingPattern(bool isExprBasic) {
  // TODO: Since we expect a pattern in this position, we should optimistically
  // parse pattern nodes for productions shared by pattern and expression
  // grammar. For short-term ease of initial implementation, we always go
  // through the expr parser for ambiguous productions.

  // Parse productions that can only be patterns.
  if (Tok.isAny(tok::kw_var, tok::kw_let)) {
    assert(Tok.isAny(tok::kw_let, tok::kw_var) && "expects var or let");
    bool isLet = Tok.is(tok::kw_let);
    SourceLoc varLoc = consumeToken();
    
    return parseMatchingPatternAsLetOrVar(isLet, varLoc, isExprBasic);
  }
  
  // matching-pattern ::= 'is' type
  if (Tok.is(tok::kw_is)) {
    SourceLoc isLoc = consumeToken(tok::kw_is);
    ParserResult<TypeRepr> castType = parseType();
    if (castType.isNull() || castType.hasCodeCompletion())
      return nullptr;
    return makeParserResult(new (Context) IsPattern(isLoc, castType.get(),
                                                    nullptr));
  }

  // matching-pattern ::= expr
  // Fall back to expression parsing for ambiguous forms. Name lookup will
  // disambiguate.
  ParserResult<Expr> subExpr =
    parseExprImpl(diag::expected_pattern, isExprBasic);
  if (subExpr.hasCodeCompletion())
    return makeParserCodeCompletionStatus();
  if (subExpr.isNull())
    return nullptr;
  
  // The most common case here is to parse something that was a lexically
  // obvious pattern, which will come back wrapped in an immediate
  // UnresolvedPatternExpr.  Transform this now to simplify later code.
  if (auto *UPE = dyn_cast<UnresolvedPatternExpr>(subExpr.get()))
    return makeParserResult(UPE->getSubPattern());
  
  return makeParserResult(new (Context) ExprPattern(subExpr.get()));
}
Пример #12
0
ParserResult Parser::parse(string input){
	vector<string> output;
	ParserResult parserResult;

	try {
		string userInput = input;
		LOG(INFO) << "Parser-Initial input:" << userInput;
		if (userInput.empty()) {
			resetAll();
			throw std::out_of_range(ERROR_EMPTY_INPUT);
		}

		userInput = removeExtraSpacePadding(userInput);
		setCommand(userInput);
		LOG(INFO) << "Parser-After setCommand:" << userInput;
		setDateAndTime(userInput);
		LOG(INFO) << "Parser-After setDateAndTime:" << userInput;
		setIndex(userInput);
		LOG(INFO) << "Parser-After setIndex:" << userInput;
		setDescription(userInput);
		LOG(INFO) << "Parser-After setDescription:" << userInput;
	}

	catch (const out_of_range& error) {
		LOG(INFO) << "Parser-Exception:" << error.what();
		resetAll();
		throw std::out_of_range(error.what());
	}

	parserResult.setUserCommand(_userCommand);
	parserResult.setDescription(_description);
	parserResult.setIndex(_index);
	parserResult.setEntryType(_entryType);
	parserResult.setStartDate(_startYear, _startMonth, _startDay);
	parserResult.setStartTime(_startTime);
	parserResult.setEndDate(_endYear, _endMonth, _endDay);
	parserResult.setEndTime(_endTime);
	resetAll();

	return parserResult;
}
Пример #13
0
SAWYER_EXPORT void
PodFormatter::emit(const ParserResult &parsed) {
    // Generate POD documentation into a temporary file.  Since perldoc doesn't support the "name" property, but rather
    // uses the file name, we create a temporary directory and place a POD file inside with the name we want.
    TempDir tmpdir(tempFileName());
    std::string fileName = tmpdir.name + pageName_ + ".pod";
    {
        std::ofstream stream(fileName.c_str());
        parsed.emit(stream, sharedFromThis());
    }

    std::string cmd = "perldoc "
                      " -o man"
                      " -w 'center:" + escapeSingleQuoted(chapterName_) + "'"
                      " -w 'date:" + escapeSingleQuoted(dateString_) + "'"
                      // " -w 'name:" + escapeSingleQuoted(pageName_) + "'"
                      " -w 'release:" + escapeSingleQuoted(versionString_) + "'"
                      " -w 'section:" + escapeSingleQuoted(chapterNumber_) + "'"
                      " " + fileName;
    system(cmd.c_str());
}
Пример #14
0
/// parseTypeTupleBody
///   type-tuple:
///     '(' type-tuple-body? ')'
///   type-tuple-body:
///     type-tuple-element (',' type-tuple-element)* '...'?
///   type-tuple-element:
///     identifier ':' type
///     type
ParserResult<TupleTypeRepr> Parser::parseTypeTupleBody() {
  Parser::StructureMarkerRAII ParsingTypeTuple(*this, Tok);
  SourceLoc RPLoc, LPLoc = consumeToken(tok::l_paren);
  SourceLoc EllipsisLoc;
  unsigned EllipsisIdx;
  SmallVector<TypeRepr *, 8> ElementsR;

  // We keep track of the labels separately, and apply them at the end.
  SmallVector<std::tuple<Identifier, SourceLoc, Identifier, SourceLoc>, 4>
    Labels;

  ParserStatus Status = parseList(tok::r_paren, LPLoc, RPLoc,
                                  tok::comma, /*OptionalSep=*/false,
                                  /*AllowSepAfterLast=*/false,
                                  diag::expected_rparen_tuple_type_list,
                                  [&] () -> ParserStatus {
    // If this is a deprecated use of the inout marker in an argument list,
    // consume the inout.
    SourceLoc InOutLoc;
    bool hasAnyInOut = false;
    bool hasValidInOut = false;
    if (consumeIf(tok::kw_inout, InOutLoc)) {
      hasAnyInOut = true;
      hasValidInOut = false;
    }
    // If the tuple element starts with a potential argument label followed by a
    // ':' or another potential argument label, then the identifier is an
    // element tag, and it is followed by a type annotation.
    if (Tok.canBeArgumentLabel() &&
        (peekToken().is(tok::colon) || peekToken().canBeArgumentLabel())) {
      // Consume the name
      Identifier name;
      if (!Tok.is(tok::kw__))
        name = Context.getIdentifier(Tok.getText());
      SourceLoc nameLoc = consumeToken();

      // If there is a second name, consume it as well.
      Identifier secondName;
      SourceLoc secondNameLoc;
      if (Tok.canBeArgumentLabel()) {
        if (!Tok.is(tok::kw__))
          secondName = Context.getIdentifier(Tok.getText());
        secondNameLoc = consumeToken();
      }

      // Consume the ':'.
      if (!consumeIf(tok::colon))
        diagnose(Tok, diag::expected_parameter_colon);

      SourceLoc postColonLoc = Tok.getLoc();

      // Consume 'inout' if present.
      if (!hasAnyInOut && consumeIf(tok::kw_inout, InOutLoc)) {
        hasValidInOut = true;
      }

      SourceLoc extraneousInOutLoc;
      while (consumeIf(tok::kw_inout, extraneousInOutLoc)) {
        diagnose(Tok, diag::parameter_inout_var_let_repeated)
          .fixItRemove(extraneousInOutLoc);
      }

      // Parse the type annotation.
      ParserResult<TypeRepr> type = parseType(diag::expected_type);
      if (type.hasCodeCompletion())
        return makeParserCodeCompletionStatus();
      if (type.isNull())
        return makeParserError();

      if (!hasValidInOut && hasAnyInOut) {
        diagnose(Tok.getLoc(), diag::inout_as_attr_disallowed)
          .fixItRemove(InOutLoc)
          .fixItInsert(postColonLoc, "inout ");
      }

      // If an 'inout' marker was specified, build the type.  Note that we bury
      // the inout locator within the named locator.  This is weird but required
      // by sema apparently.
      if (InOutLoc.isValid())
        type = makeParserResult(new (Context) InOutTypeRepr(type.get(),
                                                            InOutLoc));

      // Record the label. We will look at these at the end.
      if (Labels.empty()) {
        Labels.assign(ElementsR.size(),
                      std::make_tuple(Identifier(), SourceLoc(),
                                      Identifier(), SourceLoc()));
      }
      Labels.push_back(std::make_tuple(name, nameLoc,
                                       secondName, secondNameLoc));

      ElementsR.push_back(type.get());
    } else {
      // Otherwise, this has to be a type.
      ParserResult<TypeRepr> type = parseType();
      if (type.hasCodeCompletion())
        return makeParserCodeCompletionStatus();
      if (type.isNull())
        return makeParserError();
      if (InOutLoc.isValid())
        type = makeParserResult(new (Context) InOutTypeRepr(type.get(),
                                                            InOutLoc));

      if (!Labels.empty()) {
        Labels.push_back(std::make_tuple(Identifier(), SourceLoc(),
                                         Identifier(), SourceLoc()));
      }

      ElementsR.push_back(type.get());
    }

    // Parse '= expr' here so we can complain about it directly, rather
    // than dying when we see it.
    if (Tok.is(tok::equal)) {
      SourceLoc equalLoc = consumeToken(tok::equal);
      auto init = parseExpr(diag::expected_init_value);
      auto inFlight = diagnose(equalLoc, diag::tuple_type_init);
      if (init.isNonNull())
        inFlight.fixItRemove(SourceRange(equalLoc, init.get()->getEndLoc()));
    }

    if (Tok.isEllipsis()) {
      if (EllipsisLoc.isValid()) {
        diagnose(Tok, diag::multiple_ellipsis_in_tuple)
          .highlight(EllipsisLoc)
          .fixItRemove(Tok.getLoc());
        (void)consumeToken();
      } else {
        EllipsisLoc = consumeToken();
        EllipsisIdx = ElementsR.size() - 1;
      }
    }
    return makeParserSuccess();
  });

  if (EllipsisLoc.isValid() && ElementsR.empty()) {
    EllipsisLoc = SourceLoc();
  }

  if (EllipsisLoc.isInvalid())
    EllipsisIdx = ElementsR.size();

  // If there were any labels, figure out which labels should go into the type
  // representation.
  if (!Labels.empty()) {
    assert(Labels.size() == ElementsR.size());
    bool isFunctionType = Tok.isAny(tok::arrow, tok::kw_throws,
                                    tok::kw_rethrows);
    for (unsigned i : indices(ElementsR)) {
      auto &currentLabel = Labels[i];

      Identifier firstName = std::get<0>(currentLabel);
      SourceLoc firstNameLoc = std::get<1>(currentLabel);
      Identifier secondName = std::get<2>(currentLabel);
      SourceLoc secondNameLoc = std::get<3>(currentLabel);

      // True tuples have labels.
      if (!isFunctionType) {
        // If there were two names, complain.
        if (firstNameLoc.isValid() && secondNameLoc.isValid()) {
          auto diag = diagnose(firstNameLoc, diag::tuple_type_multiple_labels);
          if (firstName.empty()) {
            diag.fixItRemoveChars(firstNameLoc, ElementsR[i]->getStartLoc());
          } else {
            diag.fixItRemove(
              SourceRange(Lexer::getLocForEndOfToken(SourceMgr,firstNameLoc),
                          secondNameLoc));
          }
        }

        // Form the named type representation.
        ElementsR[i] = new (Context) NamedTypeRepr(firstName, ElementsR[i],
                                                   firstNameLoc);
        continue;
      }

      // If there was a first name, complain; arguments in function types are
      // always unlabeled.
      if (firstNameLoc.isValid() && !firstName.empty()) {
        auto diag = diagnose(firstNameLoc, diag::function_type_argument_label,
                             firstName);
        if (secondNameLoc.isInvalid())
          diag.fixItInsert(firstNameLoc, "_ ");
        else if (secondName.empty())
          diag.fixItRemoveChars(firstNameLoc, ElementsR[i]->getStartLoc());
        else
          diag.fixItReplace(SourceRange(firstNameLoc), "_");
      }

      if (firstNameLoc.isValid() || secondNameLoc.isValid()) {
        // Form the named parameter type representation.
        ElementsR[i] = new (Context) NamedTypeRepr(secondName, ElementsR[i],
                                                   secondNameLoc, firstNameLoc);
      }
    }
  }

  return makeParserResult(Status,
                          TupleTypeRepr::create(Context, ElementsR,
                                                SourceRange(LPLoc, RPLoc),
                                                EllipsisLoc, EllipsisIdx));
}
Пример #15
0
/// parseTypeSimple
///   type-simple:
///     type-identifier
///     type-tuple
///     type-composition
///     'Any'
///     type-simple '.Type'
///     type-simple '.Protocol'
///     type-simple '?'
///     type-simple '!'
///     type-collection
ParserResult<TypeRepr> Parser::parseTypeSimple(Diag<> MessageID,
                                               bool HandleCodeCompletion) {
  ParserResult<TypeRepr> ty;
  // If this is an "inout" marker for an identifier type, consume the inout.
  SourceLoc InOutLoc;
  consumeIf(tok::kw_inout, InOutLoc);

  switch (Tok.getKind()) {
  case tok::kw_Self:
    ty = parseTypeIdentifier();
    break;
  case tok::identifier:
  case tok::kw_protocol:
  case tok::kw_Any:
    ty = parseTypeIdentifierOrTypeComposition();
    break;
  case tok::l_paren:
    ty = parseTypeTupleBody();
    break;
  case tok::code_complete:
    if (!HandleCodeCompletion)
      break;
    if (CodeCompletion)
      CodeCompletion->completeTypeSimpleBeginning();
    // Eat the code completion token because we handled it.
    consumeToken(tok::code_complete);
    return makeParserCodeCompletionResult<TypeRepr>();
  case tok::kw_super:
  case tok::kw_self:
    // These keywords don't start a decl or a statement, and thus should be
    // safe to skip over.
    diagnose(Tok, MessageID);
    ty = makeParserErrorResult(new (Context) ErrorTypeRepr(Tok.getLoc()));
    consumeToken();
    // FIXME: we could try to continue to parse.
    return ty;
  case tok::l_square:
    ty = parseTypeCollection();
    break;
  default:
    checkForInputIncomplete();
    diagnose(Tok, MessageID);
    return nullptr;
  }
  
  // '.Type', '.Protocol', '?', and '!' still leave us with type-simple.
  while (ty.isNonNull()) {
    if ((Tok.is(tok::period) || Tok.is(tok::period_prefix))) {
      if (peekToken().isContextualKeyword("Type")) {
        consumeToken();
        SourceLoc metatypeLoc = consumeToken(tok::identifier);
        ty = makeParserResult(ty,
          new (Context) MetatypeTypeRepr(ty.get(), metatypeLoc));
        continue;
      }
      if (peekToken().isContextualKeyword("Protocol")) {
        consumeToken();
        SourceLoc protocolLoc = consumeToken(tok::identifier);
        ty = makeParserResult(ty,
          new (Context) ProtocolTypeRepr(ty.get(), protocolLoc));
        continue;
      }
    }

    if (!Tok.isAtStartOfLine()) {
      if (isOptionalToken(Tok)) {
        ty = parseTypeOptional(ty.get());
        continue;
      }
      if (isImplicitlyUnwrappedOptionalToken(Tok)) {
        ty = parseTypeImplicitlyUnwrappedOptional(ty.get());
        continue;
      }
    }
    break;
  }

  // If we parsed an inout modifier, prepend it.
  if (InOutLoc.isValid())
    ty = makeParserResult(new (Context) InOutTypeRepr(ty.get(),
                                                      InOutLoc));

  return ty;
}
Пример #16
0
/// parseTypeIdentifierOrTypeComposition
/// - Identifiers and compositions both start with the same identifier
///   token, parse it and continue constructing a composition if the
///   next token is '&'
///
///   type-composition:
///     type-identifier ('&' type-identifier)*
///     'protocol' '<' type-composition-list-deprecated? '>'
///
///   type-composition-list-deprecated:
///     type-identifier (',' type-identifier)*
ParserResult<TypeRepr> Parser::parseTypeIdentifierOrTypeComposition() {
  
  // Handle deprecated case
  if (Tok.getKind() == tok::kw_protocol && startsWithLess(peekToken())) {
    SourceLoc ProtocolLoc = consumeToken(tok::kw_protocol);
    SourceLoc LAngleLoc = consumeStartingLess();
    
    // Parse the type-composition-list.
    ParserStatus Status;
    SmallVector<IdentTypeRepr *, 4> Protocols;
    bool IsEmpty = startsWithGreater(Tok);
    if (!IsEmpty) {
      do {
        // Parse the type-identifier.
        ParserResult<TypeRepr> Protocol = parseTypeIdentifier();
        Status |= Protocol;
        if (auto *ident = dyn_cast_or_null<IdentTypeRepr>(
                                                       Protocol.getPtrOrNull()))
          Protocols.push_back(ident);
      } while (consumeIf(tok::comma));
    }

    // Check for the terminating '>'.
    SourceLoc RAngleLoc = PreviousLoc;
    if (startsWithGreater(Tok)) {
      RAngleLoc = consumeStartingGreater();
    } else {
      if (Status.isSuccess()) {
        diagnose(Tok, diag::expected_rangle_protocol);
        diagnose(LAngleLoc, diag::opening_angle);
        Status.setIsParseError();
      }
      
      // Skip until we hit the '>'.
      RAngleLoc = skipUntilGreaterInTypeList(/*protocolComposition=*/true);
    }

    auto composition = ProtocolCompositionTypeRepr::create(
      Context, Protocols, ProtocolLoc, {LAngleLoc, RAngleLoc});

    if (Status.isSuccess()) {
      // Only if we have complete protocol<...> construct, diagnose deprecated.

      SmallString<32> replacement;
      if (Protocols.empty()) {
        replacement = "Any";
      } else {
        auto extractText = [&](IdentTypeRepr *Ty) -> StringRef {
          auto SourceRange = Ty->getSourceRange();
          return SourceMgr.extractText(
            Lexer::getCharSourceRangeFromSourceRange(SourceMgr, SourceRange));
        };
        auto Begin = Protocols.begin();
        replacement += extractText(*Begin);
        while (++Begin != Protocols.end()) {
          replacement += " & ";
          replacement += extractText(*Begin);
        }
      }

      // Copy trailing content after '>' to the replacement string.
      // FIXME: lexer should smartly separate '>' and trailing contents like '?'.
      StringRef TrailingContent = L->getTokenAt(RAngleLoc).getRange().str().
        substr(1);
      if (!TrailingContent.empty()) {
        if (Protocols.size() > 1) {
          replacement.insert(replacement.begin(), '(');
          replacement += ")";
        }
        replacement += TrailingContent;
      }

      // Replace 'protocol<T1, T2>' with 'T1 & T2'
      diagnose(ProtocolLoc,
        IsEmpty              ? diag::deprecated_any_composition :
        Protocols.size() > 1 ? diag::deprecated_protocol_composition :
                               diag::deprecated_protocol_composition_single)
        .highlight(composition->getSourceRange())
        .fixItReplace(composition->getSourceRange(), replacement);
    }

    return makeParserResult(Status, composition);
  }
  
  SourceLoc FirstTypeLoc = Tok.getLoc();
  
  // Parse the first type
  ParserResult<TypeRepr> FirstType = parseTypeIdentifier();
  if (!Tok.isContextualPunctuator("&"))
    return FirstType;
  
  SmallVector<IdentTypeRepr *, 4> Protocols;
  ParserStatus Status;

  // If it is not 'Any', add it to the protocol list
  if (auto *ident = dyn_cast_or_null<IdentTypeRepr>(FirstType.getPtrOrNull()))
    Protocols.push_back(ident);
  Status |= FirstType;
  auto FirstAmpersandLoc = Tok.getLoc();
  
  while (Tok.isContextualPunctuator("&")) {
    consumeToken(); // consume '&'
    ParserResult<TypeRepr> Protocol = parseTypeIdentifier();
    Status |= Protocol;
    if (auto *ident = dyn_cast_or_null<IdentTypeRepr>(Protocol.getPtrOrNull()))
      Protocols.push_back(ident);
  };
  
  return makeParserResult(Status, ProtocolCompositionTypeRepr::create(
    Context, Protocols, FirstTypeLoc, {FirstAmpersandLoc, PreviousLoc}));
}
Пример #17
0
/// parseTypeTupleBody
///   type-tuple:
///     '(' type-tuple-body? ')'
///   type-tuple-body:
///     type-tuple-element (',' type-tuple-element)* '...'?
///   type-tuple-element:
///     identifier ':' type
///     type
ParserResult<TupleTypeRepr> Parser::parseTypeTupleBody() {
  Parser::StructureMarkerRAII ParsingTypeTuple(*this, Tok);
  SourceLoc RPLoc, LPLoc = consumeToken(tok::l_paren);
  SourceLoc EllipsisLoc;
  unsigned EllipsisIdx;
  SmallVector<TypeRepr *, 8> ElementsR;

  ParserStatus Status = parseList(tok::r_paren, LPLoc, RPLoc,
                                  tok::comma, /*OptionalSep=*/false,
                                  /*AllowSepAfterLast=*/false,
                                  diag::expected_rparen_tuple_type_list,
                                  [&] () -> ParserStatus {
    // If this is an inout marker in an argument list, consume the inout.
    SourceLoc InOutLoc;
    consumeIf(tok::kw_inout, InOutLoc);

    // If the tuple element starts with "ident :", then
    // the identifier is an element tag, and it is followed by a type
    // annotation.
    if (Tok.canBeArgumentLabel() && peekToken().is(tok::colon)) {
      // Consume the name
      Identifier name;
      if (!Tok.is(tok::kw__))
        name = Context.getIdentifier(Tok.getText());
      SourceLoc nameLoc = consumeToken();

      // Consume the ':'.
      consumeToken(tok::colon);

      // Parse the type annotation.
      ParserResult<TypeRepr> type = parseType(diag::expected_type);
      if (type.hasCodeCompletion())
        return makeParserCodeCompletionStatus();
      if (type.isNull())
        return makeParserError();

      // If an 'inout' marker was specified, build the type.  Note that we bury
      // the inout locator within the named locator.  This is weird but required
      // by sema apparently.
      if (InOutLoc.isValid())
        type = makeParserResult(new (Context) InOutTypeRepr(type.get(),
                                                            InOutLoc));

      
      ElementsR.push_back(
          new (Context) NamedTypeRepr(name, type.get(), nameLoc));
    } else {
      // Otherwise, this has to be a type.
      ParserResult<TypeRepr> type = parseType();
      if (type.hasCodeCompletion())
        return makeParserCodeCompletionStatus();
      if (type.isNull())
        return makeParserError();
      if (InOutLoc.isValid())
        type = makeParserResult(new (Context) InOutTypeRepr(type.get(),
                                                            InOutLoc));

      ElementsR.push_back(type.get());
    }

    // Parse '= expr' here so we can complain about it directly, rather
    // than dying when we see it.
    if (Tok.is(tok::equal)) {
      SourceLoc equalLoc = consumeToken(tok::equal);
      auto init = parseExpr(diag::expected_init_value);
      auto inFlight = diagnose(equalLoc, diag::tuple_type_init);
      if (init.isNonNull())
        inFlight.fixItRemove(SourceRange(equalLoc, init.get()->getEndLoc()));
    }

    if (Tok.isEllipsis()) {
      if (EllipsisLoc.isValid()) {
        diagnose(Tok, diag::multiple_ellipsis_in_tuple)
          .highlight(EllipsisLoc)
          .fixItRemove(Tok.getLoc());
        (void)consumeToken();
      } else {
        EllipsisLoc = consumeToken();
        EllipsisIdx = ElementsR.size() - 1;
      }
    }
    return makeParserSuccess();
  });

  if (EllipsisLoc.isValid() && ElementsR.empty()) {
    EllipsisLoc = SourceLoc();
  }

  if (EllipsisLoc.isInvalid())
    EllipsisIdx = ElementsR.size();

  return makeParserResult(Status,
                          TupleTypeRepr::create(Context, ElementsR,
                                                SourceRange(LPLoc, RPLoc),
                                                EllipsisLoc, EllipsisIdx));
}
Пример #18
0
ParserResult<TypeRepr> Parser::parseTypeIdentifierWithRecovery(
    Diag<> MessageID, Diag<TypeLoc> NonIdentifierTypeMessageID) {
  ParserResult<TypeRepr> Ty = parseType(MessageID);

  if (!Ty.isParseError() && !isa<IdentTypeRepr>(Ty.get()) &&
      !isa<ErrorTypeRepr>(Ty.get())) {
    diagnose(Ty.get()->getStartLoc(), NonIdentifierTypeMessageID, Ty.get())
        .highlight(Ty.get()->getSourceRange());
    Ty.setIsParseError();
    Ty = makeParserResult(
        Ty, new (Context) ErrorTypeRepr(Ty.get()->getSourceRange()));
  }

  assert(Ty.isNull() ||
         isa<IdentTypeRepr>(Ty.get()) ||
         isa<ErrorTypeRepr>(Ty.get()));
  return Ty;
}
Пример #19
0
/// parseType
///   type:
///     attribute-list type-function
///     attribute-list type-array
///
///   type-function:
///     type-simple '->' type
///
ParserResult<TypeRepr> Parser::parseType(Diag<> MessageID,
                                         bool HandleCodeCompletion) {
  // Parse attributes.
  TypeAttributes attrs;
  parseTypeAttributeList(attrs);

  // Parse Generic Parameters. Generic Parameters are visible in the function
  // body.
  GenericParamList *generics = nullptr;
  if (isInSILMode()) {
    generics = maybeParseGenericParams().getPtrOrNull();
  }

  ParserResult<TypeRepr> ty = parseTypeSimple(MessageID, HandleCodeCompletion);
  if (ty.hasCodeCompletion())
    return makeParserCodeCompletionResult<TypeRepr>();

  if (ty.isNull())
    return nullptr;

  // Parse a throws specifier.  'throw' is probably a typo for 'throws',
  // but in local contexts we could just be at the end of a statement,
  // so we need to check for the arrow.
  ParserPosition beforeThrowsPos;
  SourceLoc throwsLoc;
  bool rethrows = false;
  if (Tok.isAny(tok::kw_throws, tok::kw_rethrows) ||
      (Tok.is(tok::kw_throw) && peekToken().is(tok::arrow))) {
    if (Tok.is(tok::kw_throw)) {
      diagnose(Tok.getLoc(), diag::throw_in_function_type)
        .fixItReplace(Tok.getLoc(), "throws");
    }

    beforeThrowsPos = getParserPosition();
    rethrows = Tok.is(tok::kw_rethrows);
    throwsLoc = consumeToken();
  }

  // Handle type-function if we have an arrow.
  SourceLoc arrowLoc;
  if (consumeIf(tok::arrow, arrowLoc)) {
    ParserResult<TypeRepr> SecondHalf =
      parseType(diag::expected_type_function_result);
    if (SecondHalf.hasCodeCompletion())
      return makeParserCodeCompletionResult<TypeRepr>();
    if (SecondHalf.isNull())
      return nullptr;
    if (rethrows) {
      // 'rethrows' is only allowed on function declarations for now.
      diagnose(throwsLoc, diag::rethrowing_function_type);
    }
    auto fnTy = new (Context) FunctionTypeRepr(generics, ty.get(),
                                               throwsLoc,
                                               arrowLoc,
                                               SecondHalf.get());
    return makeParserResult(applyAttributeToType(fnTy, attrs));
  } else if (throwsLoc.isValid()) {
    // Don't consume 'throws', so we can emit a more useful diagnostic when
    // parsing a function decl.
    restoreParserPosition(beforeThrowsPos);
    return ty;
  }
  
  // Only function types may be generic.
  if (generics) {
    auto brackets = generics->getSourceRange();
    diagnose(brackets.Start, diag::generic_non_function);
  }

  // Parse legacy array types for migration.
  while (ty.isNonNull() && !Tok.isAtStartOfLine()) {
    if (Tok.is(tok::l_square)) {
      ty = parseTypeArray(ty.get());
    } else {
      break;
    }
  }

  if (ty.isNonNull() && !ty.hasCodeCompletion()) {
    ty = makeParserResult(applyAttributeToType(ty.get(), attrs));
  }
  return ty;
}
Пример #20
0
/// Parse a function definition signature.
///   func-signature:
///     func-arguments func-throws? func-signature-result?
///   func-signature-result:
///     '->' type
///
/// Note that this leaves retType as null if unspecified.
ParserStatus
Parser::parseFunctionSignature(Identifier SimpleName,
                               DeclName &FullName,
                               SmallVectorImpl<ParameterList*> &bodyParams,
                               DefaultArgumentInfo &defaultArgs,
                               SourceLoc &throwsLoc,
                               bool &rethrows,
                               TypeRepr *&retType) {
  SmallVector<Identifier, 4> NamePieces;
  NamePieces.push_back(SimpleName);
  FullName = SimpleName;
  
  ParserStatus Status;
  // We force first type of a func declaration to be a tuple for consistency.
  if (Tok.is(tok::l_paren)) {
    ParameterContextKind paramContext;
    if (SimpleName.isOperator())
      paramContext = ParameterContextKind::Operator;
    else
      paramContext = ParameterContextKind::Function;

    Status = parseFunctionArguments(NamePieces, bodyParams, paramContext,
                                    defaultArgs);
    FullName = DeclName(Context, SimpleName, 
                        llvm::makeArrayRef(NamePieces.begin() + 1,
                                           NamePieces.end()));

    if (bodyParams.empty()) {
      // If we didn't get anything, add a () pattern to avoid breaking
      // invariants.
      assert(Status.hasCodeCompletion() || Status.isError());
      bodyParams.push_back(ParameterList::createEmpty(Context));
    }
  } else {
    diagnose(Tok, diag::func_decl_without_paren);
    Status = makeParserError();

    // Recover by creating a '() -> ?' signature.
    bodyParams.push_back(ParameterList::createEmpty(Context, PreviousLoc,
                                                    PreviousLoc));
    FullName = DeclName(Context, SimpleName, bodyParams.back());
  }
  
  // Check for the 'throws' keyword.
  rethrows = false;
  if (Tok.is(tok::kw_throws)) {
    throwsLoc = consumeToken();
  } else if (Tok.is(tok::kw_rethrows)) {
    throwsLoc = consumeToken();
    rethrows = true;
  } else if (Tok.is(tok::kw_throw)) {
    throwsLoc = consumeToken();
    diagnose(throwsLoc, diag::throw_in_function_type)
      .fixItReplace(throwsLoc, "throws");
  }

  SourceLoc arrowLoc;
  // If there's a trailing arrow, parse the rest as the result type.
  if (Tok.isAny(tok::arrow, tok::colon)) {
    if (!consumeIf(tok::arrow, arrowLoc)) {
      // FixIt ':' to '->'.
      diagnose(Tok, diag::func_decl_expected_arrow)
          .fixItReplace(SourceRange(Tok.getLoc()), "->");
      arrowLoc = consumeToken(tok::colon);
    }

    ParserResult<TypeRepr> ResultType =
      parseType(diag::expected_type_function_result);
    if (ResultType.hasCodeCompletion())
      return ResultType;
    retType = ResultType.getPtrOrNull();
    if (!retType) {
      Status.setIsParseError();
      return Status;
    }
  } else {
    // Otherwise, we leave retType null.
    retType = nullptr;
  }

  // Check for 'throws' and 'rethrows' after the type and correct it.
  if (!throwsLoc.isValid()) {
    if (Tok.is(tok::kw_throws)) {
      throwsLoc = consumeToken();
    } else if (Tok.is(tok::kw_rethrows)) {
      throwsLoc = consumeToken();
      rethrows = true;
    }

    if (throwsLoc.isValid()) {
      assert(arrowLoc.isValid());
      assert(retType);
      auto diag = rethrows ? diag::rethrows_after_function_result
                           : diag::throws_after_function_result;
      SourceLoc typeEndLoc = Lexer::getLocForEndOfToken(SourceMgr,
                                                        retType->getEndLoc());
      SourceLoc throwsEndLoc = Lexer::getLocForEndOfToken(SourceMgr, throwsLoc);
      diagnose(Tok, diag)
        .fixItInsert(arrowLoc, rethrows ? "rethrows " : "throws ")
        .fixItRemoveChars(typeEndLoc, throwsEndLoc);
    }
  }

  return Status;
}
Пример #21
0
ParserStatus
Parser::parseGenericParametersBeforeWhere(SourceLoc LAngleLoc,
                        SmallVectorImpl<GenericTypeParamDecl *> &GenericParams) {
  ParserStatus Result;
  SyntaxParsingContext GPSContext(SyntaxContext, SyntaxKind::GenericParameterList);
  bool HasNextParam;
  do {
    SyntaxParsingContext GParamContext(SyntaxContext, SyntaxKind::GenericParameter);
    // Note that we're parsing a declaration.
    StructureMarkerRAII ParsingDecl(*this, Tok.getLoc(),
                                    StructureMarkerKind::Declaration);
    
    if (ParsingDecl.isFailed()) {
      return makeParserError();
    }

    // Parse attributes.
    DeclAttributes attributes;
    if (Tok.hasComment())
      attributes.add(new (Context) RawDocCommentAttr(Tok.getCommentRange()));
    bool foundCCTokenInAttr;
    parseDeclAttributeList(attributes, foundCCTokenInAttr);

    // Parse the name of the parameter.
    Identifier Name;
    SourceLoc NameLoc;
    if (parseIdentifier(Name, NameLoc,
                        diag::expected_generics_parameter_name)) {
      Result.setIsParseError();
      break;
    }

    // Parse the ':' followed by a type.
    SmallVector<TypeLoc, 1> Inherited;
    if (Tok.is(tok::colon)) {
      (void)consumeToken();
      ParserResult<TypeRepr> Ty;

      if (Tok.isAny(tok::identifier, tok::code_complete, tok::kw_protocol,
                    tok::kw_Any)) {
        Ty = parseType();
      } else if (Tok.is(tok::kw_class)) {
        diagnose(Tok, diag::unexpected_class_constraint);
        diagnose(Tok, diag::suggest_anyobject)
        .fixItReplace(Tok.getLoc(), "AnyObject");
        consumeToken();
        Result.setIsParseError();
      } else {
        diagnose(Tok, diag::expected_generics_type_restriction, Name);
        Result.setIsParseError();
      }

      if (Ty.hasCodeCompletion())
        return makeParserCodeCompletionStatus();

      if (Ty.isNonNull())
        Inherited.push_back(Ty.get());
    }

    // We always create generic type parameters with an invalid depth.
    // Semantic analysis fills in the depth when it processes the generic
    // parameter list.
    auto Param = new (Context) GenericTypeParamDecl(CurDeclContext, Name, NameLoc,
                                            GenericTypeParamDecl::InvalidDepth,
                                                    GenericParams.size());
    if (!Inherited.empty())
      Param->setInherited(Context.AllocateCopy(Inherited));
    GenericParams.push_back(Param);

    // Attach attributes.
    Param->getAttrs() = attributes;

    // Add this parameter to the scope.
    addToScope(Param);

    // Parse the comma, if the list continues.
    HasNextParam = consumeIf(tok::comma);
  } while (HasNextParam);

  return Result;
}
Пример #22
0
/// parseGenericWhereClause - Parse a 'where' clause, which places additional
/// constraints on generic parameters or types based on them.
///
///   where-clause:
///     'where' requirement (',' requirement) *
///
///   requirement:
///     conformance-requirement
///     same-type-requirement
///
///   conformance-requirement:
///     type-identifier ':' type-identifier
///     type-identifier ':' type-composition
///
///   same-type-requirement:
///     type-identifier '==' type
ParserStatus Parser::parseGenericWhereClause(
               SourceLoc &WhereLoc,
               SmallVectorImpl<RequirementRepr> &Requirements,
               bool &FirstTypeInComplete) {
  ParserStatus Status;
  // Parse the 'where'.
  WhereLoc = consumeToken(tok::kw_where);
  FirstTypeInComplete = false;
  do {
    // Parse the leading type-identifier.
    ParserResult<TypeRepr> FirstType = parseTypeIdentifier();
    if (FirstType.isNull()) {
      Status.setIsParseError();
      if (FirstType.hasCodeCompletion()) {
        Status.setHasCodeCompletion();
        FirstTypeInComplete = true;
      }
      break;
    }

    if (Tok.is(tok::colon)) {
      // A conformance-requirement.
      SourceLoc ColonLoc = consumeToken();

      // Parse the protocol or composition.
      ParserResult<TypeRepr> Protocol = parseTypeIdentifierOrTypeComposition();
      
      if (Protocol.isNull()) {
        Status.setIsParseError();
        if (Protocol.hasCodeCompletion())
          Status.setHasCodeCompletion();
        break;
      }

      // Add the requirement.
      Requirements.push_back(RequirementRepr::getTypeConstraint(FirstType.get(),
                                                     ColonLoc, Protocol.get()));
    } else if ((Tok.isAnyOperator() && Tok.getText() == "==") ||
               Tok.is(tok::equal)) {
      // A same-type-requirement
      if (Tok.is(tok::equal)) {
        diagnose(Tok, diag::requires_single_equal)
          .fixItReplace(SourceRange(Tok.getLoc()), "==");
      }
      SourceLoc EqualLoc = consumeToken();

      // Parse the second type.
      ParserResult<TypeRepr> SecondType = parseType();
      if (SecondType.isNull()) {
        Status.setIsParseError();
        if (SecondType.hasCodeCompletion())
          Status.setHasCodeCompletion();
        break;
      }

      // Add the requirement
      Requirements.push_back(RequirementRepr::getSameType(FirstType.get(),
                                                      EqualLoc,
                                                      SecondType.get()));
    } else {
      diagnose(Tok, diag::expected_requirement_delim);
      Status.setIsParseError();
      break;
    }
    // If there's a comma, keep parsing the list.
  } while (consumeIf(tok::comma));

  return Status;
}
Пример #23
0
/// Parse a pattern.
///   pattern ::= identifier
///   pattern ::= '_'
///   pattern ::= pattern-tuple
///   pattern ::= 'var' pattern
///   pattern ::= 'let' pattern
///
ParserResult<Pattern> Parser::parsePattern() {
  switch (Tok.getKind()) {
  case tok::l_paren:
    return parsePatternTuple();
    
  case tok::kw__:
    return makeParserResult(new (Context) AnyPattern(consumeToken(tok::kw__)));
    
  case tok::identifier: {
    Identifier name;
    SourceLoc loc = consumeIdentifier(&name);
    bool isLet = InVarOrLetPattern != IVOLP_InVar;
    return makeParserResult(createBindingFromPattern(loc, name, isLet));
  }
    
  case tok::code_complete:
    if (!CurDeclContext->isNominalTypeOrNominalTypeExtensionContext()) {
      // This cannot be an overridden property, so just eat the token. We cannot
      // code complete anything here -- we expect an identifier.
      consumeToken(tok::code_complete);
    }
    return nullptr;
    
  case tok::kw_var:
  case tok::kw_let: {
    bool isLet = Tok.is(tok::kw_let);
    SourceLoc varLoc = consumeToken();
    
    // 'var' and 'let' patterns shouldn't nest.
    if (InVarOrLetPattern == IVOLP_InLet ||
        InVarOrLetPattern == IVOLP_InVar)
      diagnose(varLoc, diag::var_pattern_in_var, unsigned(isLet));
    
    // 'let' isn't valid inside an implicitly immutable context, but var is.
    if (isLet && InVarOrLetPattern == IVOLP_ImplicitlyImmutable)
      diagnose(varLoc, diag::let_pattern_in_immutable_context);
    
    // In our recursive parse, remember that we're in a var/let pattern.
    llvm::SaveAndRestore<decltype(InVarOrLetPattern)>
    T(InVarOrLetPattern, isLet ? IVOLP_InLet : IVOLP_InVar);
    
    ParserResult<Pattern> subPattern = parsePattern();
    if (subPattern.hasCodeCompletion())
      return makeParserCodeCompletionResult<Pattern>();
    if (subPattern.isNull())
      return nullptr;
    return makeParserResult(new (Context) VarPattern(varLoc, isLet,
                                                     subPattern.get()));
  }
      
  default:
    if (Tok.isKeyword() &&
        (peekToken().is(tok::colon) || peekToken().is(tok::equal))) {
      diagnose(Tok, diag::expected_pattern_is_keyword, Tok.getText());
      SourceLoc Loc = Tok.getLoc();
      consumeToken();
      return makeParserErrorResult(new (Context) AnyPattern(Loc));
    }
    diagnose(Tok, diag::expected_pattern);
    return nullptr;
  }
}
Пример #24
0
GenericParamList *Parser::parseGenericParameters(SourceLoc LAngleLoc) {
  // Parse the generic parameter list.
  SmallVector<GenericTypeParamDecl *, 4> GenericParams;
  bool Invalid = false;
  do {
    // Parse the name of the parameter.
    Identifier Name;
    SourceLoc NameLoc;
    if (parseIdentifier(Name, NameLoc, diag::expected_generics_parameter_name)) {
      Invalid = true;
      break;
    }

    // Parse the ':' followed by a type.
    SmallVector<TypeLoc, 1> Inherited;
    if (Tok.is(tok::colon)) {
      (void)consumeToken();
      ParserResult<TypeRepr> Ty;
      if (Tok.getKind() == tok::identifier) {
        Ty = parseTypeIdentifier();
      } else if (Tok.getKind() == tok::kw_protocol) {
        Ty = parseTypeComposition();
      } else {
        diagnose(Tok, diag::expected_generics_type_restriction, Name);
        Invalid = true;
      }

      // FIXME: code completion not handled.
      if (Ty.isNonNull())
        Inherited.push_back(Ty.get());
    }

    // We always create generic type parameters with a depth of zero.
    // Semantic analysis fills in the depth when it processes the generic
    // parameter list.
    auto Param = new (Context) GenericTypeParamDecl(CurDeclContext, Name,
                                                    NameLoc, /*Depth=*/0,
                                                    GenericParams.size());
    if (!Inherited.empty())
      Param->setInherited(Context.AllocateCopy(Inherited));
    GenericParams.push_back(Param);

    // Add this parameter to the scope.
    addToScope(Param);

    // Parse the comma, if the list continues.
  } while (consumeIf(tok::comma));

  // Parse the optional where-clause.
  SourceLoc WhereLoc;
  SmallVector<RequirementRepr, 4> Requirements;
  if (Tok.is(tok::kw_where) &&
      parseGenericWhereClause(WhereLoc, Requirements)) {
    Invalid = true;
  }
  
  // Parse the closing '>'.
  SourceLoc RAngleLoc;
  if (!startsWithGreater(Tok)) {
    if (!Invalid) {
      diagnose(Tok, diag::expected_rangle_generics_param);
      diagnose(LAngleLoc, diag::opening_angle);
      
      Invalid = true;
    }
    
    // Skip until we hit the '>'.
    skipUntilGreaterInTypeList();
    if (startsWithGreater(Tok))
      RAngleLoc = consumeStartingGreater();
    else
      RAngleLoc = Tok.getLoc();
  } else {
    RAngleLoc = consumeStartingGreater();
  }

  if (GenericParams.empty())
    return nullptr;

  return GenericParamList::create(Context, LAngleLoc, GenericParams,
                                  WhereLoc, Requirements, RAngleLoc);
}
Пример #25
0
int main(int argc, char *argv[]) {

    // Command-line parsing will "push" parsed values into members of this struct when ParserResult::apply is called.
    // Alternatively, we could have used a "pull" paradigm in which we query the ParserResult to obtain the values. In
    // fact, nothing prevents us from doing both, except if we use a pull paradigm we would probably want to set some
    // of the Switch::key properties so related switches (like -E, -F, -G, -P) all store their result in the same place
    // within the ParserResult.
    Options opt;

    // We split the switches into individual groups only to make the documentation better.  The name supplied to the
    // switch group constructor is the subsection under which the documentation for those switches appears.
    SwitchGroup generic("Generic Program Information");
    generic.switchOrder(INSERTION_ORDER);
    generic.doc("This paragraph is attached to a switch group and appears before the switches that this "
                "group contains.  It may be as many paragraphs as you like and may contain markup like for the "
                "individual switches.\n\n"
                "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum "
                "mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. "
                "Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta "
                "lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum "
                "dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a "
                "semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur "
                "dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna "
                "ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. "
                "Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum "
                "accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est.");
    generic.insert(Switch("help")
                   .action(showHelpAndExit(0))
                   .doc("Print a usage message briefly summarizing these command-line options and the bug-reporting "
                        "address, then exit."));
    generic.insert(Switch("version", 'V')
                   .action(showVersion(VERSION_STRING))
                   .doc("Print the version number of @prop{programName} to the standard output stream. This version "
                        "number should be included in all bug reports (see below)."));
    generic.insert(Switch("log")
                   .action(configureDiagnostics("log", Sawyer::Message::mfacilities))
                   .argument("config")
                   .whichValue(SAVE_ALL)
                   .doc("Controls Sawyer diagnostics.  See Sawyer::Message::Facilities::control for details."));

    SwitchGroup matcher("Matcher Selection");
    matcher.switchOrder(INSERTION_ORDER);
    matcher.insert(Switch("extended-regexp", 'E')
                   .intrinsicValue(MATCHER_EXTENDED, opt.matcher)
                   .doc("Interpret @v{pattern} as an extended regular expression (ERE, see below). (@s{E} is specified "
                        "by POSIX.)"));
    matcher.insert(Switch("fixed-strings", 'F')
                   .intrinsicValue(MATCHER_STRINGS, opt.matcher)
                   .doc("Interpret @v{pattern} as a list of fixed strings, separated by newlines, any of which is to "
                        "be matched. (@s{F} is specified by POSIX.)"));
    matcher.insert(Switch("basic-regexp", 'G')
                   .intrinsicValue(MATCHER_BASIC, opt.matcher)
                   .doc("Interpret @v{pattern} as a basic regular expression (BRE, see below).  This is the default."));
    matcher.insert(Switch("perl-regexp", 'P')
                   .intrinsicValue(MATCHER_PERL, opt.matcher)
                   .doc("Interpret @v{pattern} as a Perl regular expression.  This is highly experimental and "
                        "@prop{programName} @s{P} may warn of unimplemented features."));

    SwitchGroup control("Matching Control");
    control.switchOrder(INSERTION_ORDER);
    control.insert(Switch("regexp", 'e')
                   .argument("pattern", anyParser(opt.pattern))
                   .doc("Use @v{pattern} as the pattern.  This can be used to specify multiple search patterns, or to "
                        "protect a pattern beginning with a hyphen (\"-\"). (@s{e} is specified by POSIX.)"));
    control.insert(Switch("file", 'f')
                   .argument("file", anyParser(opt.patternFile))
                   .doc("Obtain patterns from @v{file}, one per line.  The empty file contains zero patterns, and "
                        "therefore matches nothing. (@s{f} is specified by POSIX.)"));
    control.insert(Switch("ignore-case", 'i')
                   .intrinsicValue(true, opt.ignoreCase)
                   .doc("Ignore case distinctions in both the @v{pattern} and the input files. (@s{i} is specified "
                        "by POSIX.)"));
    control.insert(Switch("invert-match", 'v')
                   .intrinsicValue(true, opt.invert)
                   .doc("Invert the sense of matching, to select non-matching lines. (@s{v} is specified by POSIX.)"));
    control.insert(Switch("word-regexp", 'w')
                   .intrinsicValue(true, opt.wordRegexp)
                   .doc("Select only those lines containing matches that form whole words.  The test is that the "
                        "matching substring must either be at the beginning of the line, or preceded by a non-word "
                        "constituent character.  Similarly, it must be either at the end of the line or followed by "
                        "a non-word constituent character.  Word-constituent characters are letters, digits, and "
                        "the underscore."));
    control.insert(Switch("line-regexp", 'x')
                   .intrinsicValue(true, opt.lineRegexp)
                   .doc("Select only those matches that exactly match the whole line. (@s{x} is specified by POSIX.)"));
    control.insert(Switch("", 'y')
                   .whichValue(SAVE_NONE)
                   .doc("Obsolete synonym for @s{i}."));

    SwitchGroup output("General Output Control");
    output.name("output");
    output.doc("In this example, we've given this group of switches a name, \"output\", to demonstrate how switch group "
               "name spaces work.  The switches here can be invoked by the name they were given in the C++ source code, "
               "or they can include the switch group name, as in @s{count} or @s{output-count}.");
    output.switchOrder(INSERTION_ORDER);
    output.insert(Switch("count", 'c')
                  .intrinsicValue(true, opt.count)
                  .doc("Suppress normal output; instead print a count of matching lines for each input file.  With "
                       "the @s{v}, @s{invert-match} option (see below), count non-matching lines. (@s{c} is specified "
                       "by POSIX.)"));
    output.insert(Switch("color")
                  .longName("colour")
                  .argument("when",
                            enumParser(opt.colorWhen)
                            ->with("never", COLOR_NEVER)
                            ->with("always", COLOR_ALWAYS)
                            ->with("auto", COLOR_AUTO),
                            "auto")
                  .doc("Surround the matched (non-empty) strings, matching lines, context lines, file names, line "
                       "numbers, byte offsets, and separators (for fields and groups of context lines) with escape "
                       "sequences to display them in color on the terminal.  The colors are defined by the "
                       "environment variable \"GREP_COLORS\".  The deprecated environment variable \"GREP_COLOR\" "
                       "is still supported, but its setting does not have priority. @v{when} is \"never\", \"always\", "
                       "or \"auto\"."));
    output.insert(Switch("files-without-match", 'L')
                  .intrinsicValue(true, opt.filesWithoutMatch)
                  .doc("Suppress normal output; instead print the name of each input file from which no output "
                       "would normally have been printed.  The scanning will stop on the first match."));
    output.insert(Switch("files-with-matches", 'l')
                  .intrinsicValue(true, opt.filesWithMatches)
                  .doc("Suppress normal output; instead print the name of each input file from which output would "
                       "normally have been printed.  The scanning will stop on the first match. (@s{l} is specified "
                       "by POSIX.)"));
    output.insert(Switch("max-count", 'm')
                  .argument("num", nonNegativeIntegerParser(opt.maxCount))
                  .doc("Stop reading a file after @v{num} matching lines.  If the input is standard input from a "
                       "regular file, and @v{num} matching lines are output, @prop{programName} ensures that the "
                       "standard input is positioned to just after the last matching line before exiting, "
                       "regardless of the presence of trailing context lines.  This enables a calling process "
                       "to resume a search.  When @prop{programName} stops after @v{num} matching lines, it "
                       "outputs any trailing context lines.  When the @s{c} or @s{count} options is also used, "
                       "@prop{programName} does not output a count greater than @v{num}.  When the @s{v} or "
                       "@s{invert-match} option is also used, @prop{programName} stops after outputting @v{num} "
                       "non-matching lines."));
    output.insert(Switch("only-matching", 'o')
                  .intrinsicValue(true, opt.onlyMatching)
                  .doc("Print only the matched (non-empty) parts of a matching line, with each such part on a "
                       "separate output line."));
    output.insert(Switch("quiet", 'q')
                  .longName("silent")
                  .intrinsicValue(true, opt.quiet)
                  .doc("Quiet; do not write anything to standard output.  Exit immediately with zero status "
                       "if any match is found, even if an error was detected.  Also see the @s{s} or "
                       "@s{no-messages} option. (@s{q} is specified by POSIX.)"));
    output.insert(Switch("no-messages", 's')
                  .intrinsicValue(true, opt.noMessages)
                  .doc("Suppress error messages about nonexistent or unreadable files.  Portability note: "
                       "unlike GNU grep, 7th Edition Unix grep did not conform to POSIX, because it lacked "
                       "@s{q} and its @s{s} option behaved like GNU grep's @s{q} option.  USG-style grep "
                       "also lacked @s{q} but its @s{s} option behaved like GNU grep.  Portable shell "
                       "scripts should avoid both @s{q} and @s{s} and should redirect standard and error "
                       "output to /dev/null instead.  (@s{s} is specified by POSIX.)"));

    SwitchGroup prefix("Output Line Prefix Control");
    prefix.switchOrder(INSERTION_ORDER);
    prefix.insert(Switch("byte-offset", 'b')
                  .intrinsicValue(true, opt.byteOffset)
                  .doc("Print the 0-based byte offset within the input file before each line of output. "
                       "If @s{o} (@s{only-matching}) is specified, print the offset of the matching part "
                       "itself."));
    prefix.insert(Switch("with-filename", 'H')
                  .intrinsicValue(true, opt.withFileName)
                  .doc("Print the file name for each match.  This is the default when there is more than one "
                       "file to search."));
    prefix.insert(Switch("no-filename", 'h')
                  .intrinsicValue(false, opt.withFileName)
                  .doc("Suppress the prefixing of file names on output.  This is the default when there is only "
                       "one file (or only standard input) to search."));
    prefix.insert(Switch("label")
                  .argument("label", anyParser(opt.label))
                  .doc("Display input actually coming from standard input as input coming from file @v{label}. "
                       "This is especially useful when implementing tools like zgep, e.g., \"gzip -cd foo.gz | "
                       "grep --label=foo -H something\".  See also the @s{H} option."));
    prefix.insert(Switch("line-number", 'n')
                  .intrinsicValue(true, opt.lineNumbers)
                  .doc("Prefix each line of output with the 1-based line number within its input file. (@s{n} "
                       "is specified by POSIX.)"));
    prefix.insert(Switch("initial-tab", 'T')
                  .intrinsicValue(true, opt.initialTab)
                  .doc("Make sure that the first character of actual line content lies on a tab stop, so that "
                       "the alignment of tabs looks normal.  This is useful with options that prefix their "
                       "output to the actual content: @s{H}, @s{n}, and @s{b}.  In order to improve the "
                       "probability that lines from a single file will all start at the same column, this "
                       "also causes the line number and byte offset (if present) to be printed in a minimum "
                       "size field width."));
    prefix.insert(Switch("unix-byte-offsets", 'u')
                  .intrinsicValue(true, opt.unixByteOffsets)
                  .doc("Report Unix-style byte offsets.  This switch causes @prop{programName} to report byte "
                       "offsets as if the file were a Unix-style text file, i.e., with CR characters stripped "
                       "off.  This will produce results identical to running @prop{programName} on a Unix "
                       "machine.  This option has no effect unless @s{b} option is also used;  it has no "
                       "effect on platforms other than MS-DOS and MS-Windows."));
    prefix.insert(Switch("null", 'Z')
                  .intrinsicValue(true, opt.nullAfterName)
                  .doc("Output a zero byte (the ASCII NUL character) instead of the character that normally "
                       "follows a file name.  For example, \"grep -lZ\" outputs a zero byte after each file "
                       "name instead of the usual newline.  This option makes the output unambiguous, even in "
                       "the presence of file name containing unusual characters like newlines.  This option "
                       "can be used with commands like \"find -print0\", \"perl -0\", \"sort -z\", and \"xargs "
                       "-0\" to process arbitrary file names, even those that contain newline characters."));

    SwitchGroup context("Context Line Control");
    context.switchOrder(INSERTION_ORDER);
    context.insert(Switch("after-context", 'A')
                   .argument("num", nonNegativeIntegerParser(opt.afterContext))
                   .doc("Print @v{num} lines of trailing context after matching lines.  Places a line containing "
                        "a group separator (\"--\") between contiguous groups of matches.  With the @s{o} or "
                        "@s{only-matching} option, this has no effect and a warning is given."));
    context.insert(Switch("before-context", 'B')
                   .argument("num", nonNegativeIntegerParser(opt.beforeContext))
                   .doc("Print @v{num} lines of leading context before matching lines.  Places a line containing "
                        "a group separator (\"--\") between contigous groups of matches.  With the @s{o} or "
                        "@s{only-matching} option, this has no effect and a warning is given."));
    context.insert(Switch("context", 'C')               // FIXME[Robb Matzke 2014-03-15]: allow "-NUM"
                   .argument("num", nonNegativeIntegerParser(opt.contextLines))
                   .doc("Print @v{num} lines of output context.  Places a line containing a group separator "
                        "(\"--\") between contigous groups of matches.  With the @s{o} or @s{only-matching} "
                        "option, this has no effect and a warning is given."));

    SwitchGroup selection("File and Directory Selection");
    selection.switchOrder(INSERTION_ORDER);
    selection.insert(Switch("text", 'a')
                     .intrinsicValue(BIN_TEXT, opt.binaryFile)
                     .doc("Process a binary file as if it were text; this is the equivalent to the "
                          "\"@s{binary-files}=text\" option."));
    selection.insert(Switch("binary-files")
                     .argument("type",
                               enumParser(opt.binaryFile)
                               ->with("binary", BIN_BINARY)
                               ->with("without-match", BIN_SKIP)
                               ->with("text", BIN_TEXT))
                     .doc("If the first few bytes of a file indicate that the file contains binary data, assume "
                          "that the file is of type @v{type}.  By default, @v{type} is \"binary\", and "
                          "@prop{programName} normally outputs either a one-line message saying that a binary "
                          "file matches, or no message if there is no match.  If @v{type} is \"without-match"
                          "@prop{programName} assumes that a binary file does not match; this is equivalent to "
                          "the @s{I} option.  If @v{type} is \"text\", @prop{programName} processes a binary "
                          "file as if it were text; ths is equivalent to the @s{a} option.  @b{Warning:} "
                          "\"@prop{programName} @s{binary-files}=text\" might output binary garbage, which can "
                          "have nasty side effects if the output is a terminal and if the terminal driver "
                          "interprets some of it as commands."));
    selection.insert(Switch("devices", 'D')
                     .argument("action",
                               enumParser(opt.deviceAction)
                               ->with("read", ACTION_READ)
                               ->with("skip", ACTION_SKIP))
                     .doc("If an input file is a device, FIFO, or socket, use @v{action} to process it.  By "
                          "default, @v{action} is \"read\", which means that devices are read just as if they "
                          "were ordinary files.  If @v{action} is \"skip\", devices are silently skipped."));
    selection.insert(Switch("directories", 'd')
                     .argument("action",
                               enumParser(opt.directoryAction)
                               ->with("read", ACTION_READ)
                               ->with("skip", ACTION_SKIP)
                               ->with("recurse", ACTION_RECURSE))
                     .doc("If an input file is a directory, use @v{action} to process it.  By default, "
                          "@v{action} is \"read\", which means that directories are read just as if they were "
                          "ordinary files.  If @v{action} is \"skip\", directories are silently skipped. If "
                          "@v{action} is \"recurse\", @prop{programName} reads all files under each directory, "
                          "recursively; this is equivalent to the @s{r} option."));
    selection.insert(Switch("exclude")
                     .argument("glob", anyParser(opt.excludeGlob))
                     .doc("Skip files whose base name matches @v{glob} (using wildcard matching). A file-name "
                          "glob can use \"*\", \"?\", and \"[...]\" as wildcards, and \"\\\" to quote a wildcard "
                          "or backslash character literally."));
    selection.insert(Switch("exclude-from")
                     .argument("file", anyParser(opt.excludeFromFile))
                     .doc("Skip files whose base name matches any of the file-name globs read from @v{file} "
                          "(using wildcard matching as described under @s{exclude})."));
    selection.insert(Switch("", 'I')
                     .intrinsicValue(BIN_SKIP, opt.binaryFile)
                     .doc("Process a binary file as if it did not contain matching data; this is equivalent "
                          "to the \"@s{binary-files}=without-match\" option."));
    selection.insert(Switch("include")
                     .argument("glob", anyParser(opt.includeGlob))
                     .doc("Search only files whose base names match @v{glob} (using wildcard matching as "
                          "described under @s{exclude})."));
    selection.insert(Switch("recursive", 'R')
                     .shortName('r')
                     .intrinsicValue(ACTION_RECURSE, opt.directoryAction)
                     .doc("Read all files under each directory, recursively; this is equivalent to the "
                          "\"@s{d} recurse\" option."));

    SwitchGroup misc("Other Options");
    misc.switchOrder(INSERTION_ORDER);
    misc.insert(Switch("line-buffered")
                .intrinsicValue(true, opt.lineBuffered)
                .doc("Use line buffering on output.  This can cause a performance penalty."));
    misc.insert(Switch("mmap")
                .intrinsicValue(true, opt.useMmap)
                .doc("If possible, use the @man{mmap}{2} system call to read input, instead of the default "
                     "@man{read}{2} system call.  In some situations, @s{mmap} yields better performance.  "
                     "However, @s{mmap} can cause undefined behavior (including core dumps) if an input "
                     "file shrinks while @prop{programName} is operating, or if an I/O error occurs."));
    misc.insert(Switch("binary", 'U')
                .intrinsicValue(true, opt.openAsBinary)
                .doc("Treat the file(s) as binary.  By default, under MS-DOS and MS-Windows, "
                     "@prop{programName} guesses the file type by looking at the contents of the first 32kB "
                     "read from the file.  If @prop{programName} decides the file is a text file, it strips "
                     "the CR characters from the original file contents (to make regular expressions with "
                     "\"^\" and \"$\" work correctly).  Specifying @s{U} overrules this guesswork, causing all "
                     "files to be read and passed to the matching mechanism verbatim; if the file is a text file "
                     "with CR/LF pairs at the end of each line, this will cause some regular expressions to "
                     "fail.  This option has no effect on platforms other than MS-DOS and MS-Windows."));
    misc.insert(Switch("null-data", 'Z')
                .intrinsicValue(true, opt.nulTerminatedLines)
                .doc("Treat the input as a set of lines, each terminated by a zero byte (the ASCII NUL character) "
                     "instead of a newline.  Like the @s{Z} or @s{null} option, this option can be used with "
                     "commands like \"sort -z\" to process arbitrary file names."));


    // Build the parser
    Parser parser;
    parser
        .with(generic)
        .with(matcher)
        .with(control)
        .with(output)
        .with(prefix)
        .with(context)
        .with(selection)
        .with(misc);

    // Add some top-level documentation.
    parser
        .programName("demoGrep")                        // override the real command name
        .purpose("print lines matching a pattern")
        .version(VERSION_STRING)
        .doc("Synopsis",
             "@b{@prop{programName}} [@v{options}] @v{pattern} [@v{file}...]\n\n"
             "@b{@prop{programName}} [@v{options}] [@s{e} @v{pattern} | @s{f} @v{file}] [@v{file}...]")
        .doc("Description",
             "@prop{programName} searches the named input @v{file}s (or standard input if no files are named, or if "
             "a single hyphen-minus (\"-\") is given as the file name) for lines containing a match to the given "
             "@v{pattern}.  By default, @prop{programName} prints the matching lines."
             "\n\n"
             "In addition, three variant programs egrep, fgrep, and rgrep are available.  egrep is the same as "
             "\"@prop{programName} @s{E}\"; fgrep is the same as \"@prop{programName} @s{F}\"; rgrep is the same as "
             "\"@prop{programName} @s{r}\".  Direct invocation as either \"egrep\" or \"fgrep\" is deprecated, but "
             "is provided to allow historical applications that rely on them to run unmodified.")
        .doc("Regular Expressions",
             "A regular expression is a pattern that describes a set of strings.  Regular expressions are "
             "constructed analogously to arithmetic expressions, by using various operators to combine smaller "
             "expressions."
             "\n\n"
             "@prop{programName} understands three different versions of regular expression syntax: \"basic,\" "
             "\"extended\" and \"perl.\"  In @prop{programName}, there is no difference in available functionality "
             "between basic and extended syntaxes.  In other implementations, basic regular expressions are less "
             "powerful.  The following description applies to extended regular expressions; differences for basic "
             "regular expressions are summarized afterwards.  Perl regular expressions give additional functionality, "
             "and are documented in @man{pcresyntax}{3} and @man{pcrepattern}{3}, but may not be available on every "
             "system."
             "\n\n"
             "The fundamental building blocks are the regular expressions that match a single character.  Most "
             "characters, including all letters and digits, are regular expressions that  match  themselves.   Any "
             "meta-character with special meaning may be quoted by preceding it with a backslash."
             "\n\n"
             "The period \".\" matches any single character.");


    // Parse the command-line
    ParserResult cmdline = parser.parse(argc, argv);

    // Apply the parser results, causing values to be saved and actions to be executed.
    cmdline.apply();

}
Пример #26
0
static ParserStatus parseDefaultArgument(Parser &P,
                                   Parser::DefaultArgumentInfo *defaultArgs,
                                   unsigned argIndex,
                                   ExprHandle *&init,
                                 Parser::ParameterContextKind paramContext) {
  SourceLoc equalLoc = P.consumeToken(tok::equal);

  // Enter a fresh default-argument context with a meaningless parent.
  // We'll change the parent to the function later after we've created
  // that declaration.
  auto initDC =
    P.Context.createDefaultArgumentContext(P.CurDeclContext, argIndex);
  Parser::ParseFunctionBody initScope(P, initDC);

  ParserResult<Expr> initR = P.parseExpr(diag::expected_init_value);

  // Give back the default-argument context if we didn't need it.
  if (!initScope.hasClosures()) {
    P.Context.destroyDefaultArgumentContext(initDC);

  // Otherwise, record it if we're supposed to accept default
  // arguments here.
  } else if (defaultArgs) {
    defaultArgs->ParsedContexts.push_back(initDC);
  }

  Diag<> diagID = { DiagID() };
  switch (paramContext) {
  case Parser::ParameterContextKind::Function:
  case Parser::ParameterContextKind::Operator:
  case Parser::ParameterContextKind::Initializer:
    break;
  case Parser::ParameterContextKind::Closure:
    diagID = diag::no_default_arg_closure;
    break;
  case Parser::ParameterContextKind::Subscript:
    diagID = diag::no_default_arg_subscript;
    break;
  case Parser::ParameterContextKind::Curried:
    diagID = diag::no_default_arg_curried;
    break;
  }
  
  assert(((diagID.ID != DiagID()) == !defaultArgs ||
          // Sometimes curried method parameter lists get default arg info.
          // Remove this when they go away.
          paramContext == Parser::ParameterContextKind::Curried) &&
         "Default arguments specified for an unexpected parameter list kind");
  
  if (diagID.ID != DiagID()) {
    auto inFlight = P.diagnose(equalLoc, diagID);
    if (initR.isNonNull())
      inFlight.fixItRemove(SourceRange(equalLoc, initR.get()->getEndLoc()));
    return ParserStatus();
  }
  
  defaultArgs->HasDefaultArgument = true;

  if (initR.hasCodeCompletion())
    return makeParserCodeCompletionStatus();

  if (initR.isNull())
    return makeParserError();

  init = ExprHandle::get(P.Context, initR.get());
  return ParserStatus();
}
Пример #27
0
ParserResult<GenericParamList>
Parser::parseGenericParameters(SourceLoc LAngleLoc) {
  // Parse the generic parameter list.
  SmallVector<GenericTypeParamDecl *, 4> GenericParams;
  bool Invalid = false;
  do {
    // Note that we're parsing a declaration.
    StructureMarkerRAII ParsingDecl(*this, Tok.getLoc(),
                                    StructureMarkerKind::Declaration);

    // Parse attributes.
    DeclAttributes attributes;
    if (Tok.hasComment())
      attributes.add(new (Context) RawDocCommentAttr(Tok.getCommentRange()));
    bool foundCCTokenInAttr;
    parseDeclAttributeList(attributes, foundCCTokenInAttr);

    // Parse the name of the parameter.
    Identifier Name;
    SourceLoc NameLoc;
    if (parseIdentifier(Name, NameLoc,
                        diag::expected_generics_parameter_name)) {
      Invalid = true;
      break;
    }

    // Parse the ':' followed by a type.
    SmallVector<TypeLoc, 1> Inherited;
    if (Tok.is(tok::colon)) {
      (void)consumeToken();
      ParserResult<TypeRepr> Ty;
      
      if (Tok.isAny(tok::identifier, tok::code_complete, tok::kw_protocol, tok::kw_Any)) {
        Ty = parseTypeIdentifierOrTypeComposition();
      } else if (Tok.is(tok::kw_class)) {
        diagnose(Tok, diag::unexpected_class_constraint);
        diagnose(Tok, diag::suggest_anyobject, Name)
          .fixItReplace(Tok.getLoc(), "AnyObject");
        consumeToken();
        Invalid = true;
      } else {
        diagnose(Tok, diag::expected_generics_type_restriction, Name);
        Invalid = true;
      }

      if (Ty.hasCodeCompletion())
        return makeParserCodeCompletionStatus();

      if (Ty.isNonNull())
        Inherited.push_back(Ty.get());
    }

    // We always create generic type parameters with a depth of zero.
    // Semantic analysis fills in the depth when it processes the generic
    // parameter list.
    auto Param = new (Context) GenericTypeParamDecl(CurDeclContext, Name,
                                                    NameLoc, /*Depth=*/0,
                                                    GenericParams.size());
    if (!Inherited.empty())
      Param->setInherited(Context.AllocateCopy(Inherited));
    GenericParams.push_back(Param);

    // Attach attributes.
    Param->getAttrs() = attributes;

    // Add this parameter to the scope.
    addToScope(Param);

    // Parse the comma, if the list continues.
  } while (consumeIf(tok::comma));

  // Parse the optional where-clause.
  SourceLoc WhereLoc;
  SmallVector<RequirementRepr, 4> Requirements;
  bool FirstTypeInComplete;
  if (Tok.is(tok::kw_where) &&
      parseGenericWhereClause(WhereLoc, Requirements,
                              FirstTypeInComplete).isError()) {
    Invalid = true;
  }
  
  // Parse the closing '>'.
  SourceLoc RAngleLoc;
  if (!startsWithGreater(Tok)) {
    if (!Invalid) {
      diagnose(Tok, diag::expected_rangle_generics_param);
      diagnose(LAngleLoc, diag::opening_angle);
      
      Invalid = true;
    }
    
    // Skip until we hit the '>'.
    skipUntilGreaterInTypeList();
    if (startsWithGreater(Tok))
      RAngleLoc = consumeStartingGreater();
    else
      RAngleLoc = Tok.getLoc();
  } else {
    RAngleLoc = consumeStartingGreater();
  }

  if (GenericParams.empty())
    return nullptr;

  return makeParserResult(GenericParamList::create(Context, LAngleLoc,
                                                   GenericParams, WhereLoc,
                                                   Requirements, RAngleLoc));
}
Пример #28
0
/// parseGenericWhereClause - Parse a 'where' clause, which places additional
/// constraints on generic parameters or types based on them.
///
///   where-clause:
///     'where' requirement (',' requirement) *
///
///   requirement:
///     conformance-requirement
///     same-type-requirement
///
///   conformance-requirement:
///     type-identifier ':' type-identifier
///     type-identifier ':' type-composition
///
///   same-type-requirement:
///     type-identifier '==' type
ParserStatus Parser::parseGenericWhereClause(
               SourceLoc &WhereLoc,
               SmallVectorImpl<RequirementRepr> &Requirements,
               bool &FirstTypeInComplete,
               bool AllowLayoutConstraints) {
  SyntaxParsingContext ClauseContext(SyntaxContext,
                                     SyntaxKind::GenericWhereClause);
  ParserStatus Status;
  // Parse the 'where'.
  WhereLoc = consumeToken(tok::kw_where);
  FirstTypeInComplete = false;
  SyntaxParsingContext ReqListContext(SyntaxContext,
                                      SyntaxKind::GenericRequirementList);
  bool HasNextReq;
  do {
    SyntaxParsingContext ReqContext(SyntaxContext, SyntaxContextKind::Syntax);
    // Parse the leading type. It doesn't necessarily have to be just a type
    // identifier if we're dealing with a same-type constraint.
    ParserResult<TypeRepr> FirstType = parseType();

    if (FirstType.hasCodeCompletion()) {
      Status.setHasCodeCompletion();
      FirstTypeInComplete = true;
    }

    if (FirstType.isNull()) {
      Status.setIsParseError();
      break;
    }

    if (Tok.is(tok::colon)) {
      // A conformance-requirement.
      SourceLoc ColonLoc = consumeToken();
      ReqContext.setCreateSyntax(SyntaxKind::ConformanceRequirement);
      if (Tok.is(tok::identifier) &&
          getLayoutConstraint(Context.getIdentifier(Tok.getText()), Context)
              ->isKnownLayout()) {
        // Parse a layout constraint.
        Identifier LayoutName;
        auto LayoutLoc = consumeIdentifier(&LayoutName);
        auto LayoutInfo = parseLayoutConstraint(LayoutName);
        if (!LayoutInfo->isKnownLayout()) {
          // There was a bug in the layout constraint.
          Status.setIsParseError();
        }
        auto Layout = LayoutInfo;
        // Types in SIL mode may contain layout constraints.
        if (!AllowLayoutConstraints && !isInSILMode()) {
          diagnose(LayoutLoc,
                   diag::layout_constraints_only_inside_specialize_attr);
        } else {
          // Add the layout requirement.
          Requirements.push_back(RequirementRepr::getLayoutConstraint(
              FirstType.get(), ColonLoc,
              LayoutConstraintLoc(Layout, LayoutLoc)));
        }
      } else {
        // Parse the protocol or composition.
        ParserResult<TypeRepr> Protocol = parseType();

        if (Protocol.isNull()) {
          Status.setIsParseError();
          if (Protocol.hasCodeCompletion())
            Status.setHasCodeCompletion();
          break;
        }

        // Add the requirement.
        Requirements.push_back(RequirementRepr::getTypeConstraint(
            FirstType.get(), ColonLoc, Protocol.get()));
      }
    } else if ((Tok.isAnyOperator() && Tok.getText() == "==") ||
               Tok.is(tok::equal)) {
      ReqContext.setCreateSyntax(SyntaxKind::SameTypeRequirement);
      // A same-type-requirement
      if (Tok.is(tok::equal)) {
        diagnose(Tok, diag::requires_single_equal)
          .fixItReplace(SourceRange(Tok.getLoc()), "==");
      }
      SourceLoc EqualLoc = consumeToken();

      // Parse the second type.
      ParserResult<TypeRepr> SecondType = parseType();
      if (SecondType.isNull()) {
        Status.setIsParseError();
        if (SecondType.hasCodeCompletion())
          Status.setHasCodeCompletion();
        break;
      }

      // Add the requirement
      Requirements.push_back(RequirementRepr::getSameType(FirstType.get(),
                                                      EqualLoc,
                                                      SecondType.get()));
    } else {
      diagnose(Tok, diag::expected_requirement_delim);
      Status.setIsParseError();
      break;
    }
    HasNextReq = consumeIf(tok::comma);
    // If there's a comma, keep parsing the list.
  } while (HasNextReq);

  if (Requirements.empty())
    WhereLoc = SourceLoc();

  return Status;
}