diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlTokenizer.java b/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlTokenizer.java index f3a5d61c942..e451570d31e 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlTokenizer.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlTokenizer.java @@ -641,7 +641,7 @@ private IdlToken tokenizeNewline() { } private IdlToken tokenizeSpace() { - parser.consumeUntilNoLongerMatches(c -> c == ' ' || c == '\t'); + parser.consumeWhile(c -> c == ' ' || c == '\t'); currentTokenEnd = parser.position(); return currentTokenType = IdlToken.SPACE; } diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/loader/ParserUtils.java b/smithy-model/src/main/java/software/amazon/smithy/model/loader/ParserUtils.java index 615da664bc9..b419fb39639 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/loader/ParserUtils.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/loader/ParserUtils.java @@ -46,13 +46,13 @@ public static String parseNumber(SimpleParser parser) { } } - parser.consumeUntilNoLongerMatches(ParserUtils::isDigit); + parser.consumeWhile(ParserUtils::isDigit); // Consume decimals. char peek = parser.peek(); if (peek == '.') { parser.skip(); - if (parser.consumeUntilNoLongerMatches(ParserUtils::isDigit) == 0) { + if (parser.consumeWhile(ParserUtils::isDigit) == 0) { throw parser.syntax(createInvalidString(parser, startPosition, "'.' must be followed by a digit")); } } @@ -65,7 +65,7 @@ public static String parseNumber(SimpleParser parser) { if (peek == '+' || peek == '-') { parser.skip(); } - if (parser.consumeUntilNoLongerMatches(ParserUtils::isDigit) == 0) { + if (parser.consumeWhile(ParserUtils::isDigit) == 0) { throw parser.syntax( createInvalidString(parser, startPosition, "'e', '+', and '-' must be followed by a digit")); } @@ -158,7 +158,7 @@ public static void consumeIdentifier(SimpleParser parser) { // Parse identifier_start char c = parser.peek(); if (c == '_') { - parser.consumeUntilNoLongerMatches(next -> next == '_'); + parser.consumeWhile(next -> next == '_'); if (!ParserUtils.isValidIdentifierCharacter(parser.peek())) { throw invalidIdentifier(parser); } @@ -170,7 +170,7 @@ public static void consumeIdentifier(SimpleParser parser) { parser.skip(); // Parse identifier_chars - parser.consumeUntilNoLongerMatches(ParserUtils::isValidIdentifierCharacter); + parser.consumeWhile(ParserUtils::isValidIdentifierCharacter); } private static RuntimeException invalidIdentifier(SimpleParser parser) { @@ -185,6 +185,16 @@ private static RuntimeException invalidIdentifier(SimpleParser parser) { * @return Returns true if the character is allowed in an identifier. */ public static boolean isValidIdentifierCharacter(char c) { + return isValidIdentifierCharacter((int) c); + } + + /** + * Returns true if the given character is allowed in an identifier. + * + * @param c Character to check. + * @return Returns true if the character is allowed in an identifier. + */ + public static boolean isValidIdentifierCharacter(int c) { return isIdentifierStart(c) || isDigit(c); } @@ -195,6 +205,16 @@ public static boolean isValidIdentifierCharacter(char c) { * @return Returns true if the character can start an identifier. */ public static boolean isIdentifierStart(char c) { + return isIdentifierStart((int) c); + } + + /** + * Returns true if the given character is allowed to start an identifier. + * + * @param c Character to check. + * @return Returns true if the character can start an identifier. + */ + public static boolean isIdentifierStart(int c) { return c == '_' || isAlphabetic(c); } @@ -205,6 +225,16 @@ public static boolean isIdentifierStart(char c) { * @return Returns true if the character is a digit. */ public static boolean isDigit(char c) { + return isDigit((int) c); + } + + /** + * Returns true if the given value is a digit 0-9. + * + * @param c Character to check. + * @return Returns true if the character is a digit. + */ + public static boolean isDigit(int c) { return c >= '0' && c <= '9'; } @@ -216,6 +246,17 @@ public static boolean isDigit(char c) { * @return Returns true if the character is an alphabetic character. */ public static boolean isAlphabetic(char c) { + return isAlphabetic((int) c); + } + + /** + * Returns true if the given character is an alphabetic character + * A-Z, a-z. This is a stricter version of {@link Character#isAlphabetic}. + * + * @param c Character to check. + * @return Returns true if the character is an alphabetic character. + */ + public static boolean isAlphabetic(int c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); } } diff --git a/smithy-model/src/main/java/software/amazon/smithy/model/validation/linters/EmitEachSelectorValidator.java b/smithy-model/src/main/java/software/amazon/smithy/model/validation/linters/EmitEachSelectorValidator.java index 5adf313034c..c28181cd535 100644 --- a/smithy-model/src/main/java/software/amazon/smithy/model/validation/linters/EmitEachSelectorValidator.java +++ b/smithy-model/src/main/java/software/amazon/smithy/model/validation/linters/EmitEachSelectorValidator.java @@ -233,7 +233,7 @@ private MessageTemplateParser(String expression) { MessageTemplate parse() { while (!eof()) { - consumeUntilNoLongerMatches(c -> c != '@'); + consumeWhile(c -> c != '@'); // '@' followed by '@' is an escaped '@", so keep parsing // the marked literal if that's the case. if (peek(1) == '@') { diff --git a/smithy-utils/src/main/java/software/amazon/smithy/utils/CodeFormatter.java b/smithy-utils/src/main/java/software/amazon/smithy/utils/CodeFormatter.java index abce9b502d6..8a7efaf9946 100644 --- a/smithy-utils/src/main/java/software/amazon/smithy/utils/CodeFormatter.java +++ b/smithy-utils/src/main/java/software/amazon/smithy/utils/CodeFormatter.java @@ -482,7 +482,7 @@ private void parseBracedArgument(int pendingTextStart) { if (parser.peek() == '@') { parser.skip(); int start = parser.position(); - parser.consumeUntilNoLongerMatches(this::isNameCharacter); + parser.consumeWhile(this::isNameCharacter); String sectionName = parser.sliceFrom(start); ensureNameIsValid(sectionName); operation = Operation.inlineSection(sectionName, operation); @@ -518,7 +518,7 @@ boolean parseStripTrailingWhitespace() { } void skipTrailingWhitespaceInParser() { - parser.consumeUntilNoLongerMatches(Character::isWhitespace); + parser.consumeWhile(Character::isWhitespace); } private boolean isAllLeadingWhitespaceOnLine(int startPosition, int startColumn) { @@ -549,13 +549,13 @@ private void pushBlock(int pendingTextStart, int startPosition, int startLine, i parser.expect('s'); parser.sp(); int startPos = parser.position(); - parser.consumeUntilNoLongerMatches(this::isNameCharacter); + parser.consumeWhile(this::isNameCharacter); keyPrefix = parser.sliceFrom(startPos); ensureNameIsValid(keyPrefix); parser.expect(','); parser.sp(); startPos = parser.position(); - parser.consumeUntilNoLongerMatches(this::isNameCharacter); + parser.consumeWhile(this::isNameCharacter); value = parser.sliceFrom(startPos); ensureNameIsValid(value); } @@ -646,7 +646,7 @@ private Operation parseNormalArgument() { private String parseArgumentName() { int start = parser.position(); - parser.consumeUntilNoLongerMatches(this::isNameCharacter); + parser.consumeWhile(this::isNameCharacter); String name = parser.sliceFrom(start); ensureNameIsValid(name); return name; @@ -694,7 +694,7 @@ private Function, Object> parsePositionalArgumentGetter() relativeIndex = -1; int startPosition = parser.position(); - parser.consumeUntilNoLongerMatches(Character::isDigit); + parser.consumeWhile(Character::isDigit); int index = Integer.parseInt(parser.sliceFrom(startPosition)) - 1; if (index < 0 || index >= arguments.length) { @@ -713,7 +713,7 @@ private void ensureNameIsValid(String name) { } } - private boolean isNameCharacter(char c) { + private boolean isNameCharacter(int c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') diff --git a/smithy-utils/src/main/java/software/amazon/smithy/utils/MediaType.java b/smithy-utils/src/main/java/software/amazon/smithy/utils/MediaType.java index 28215a91295..96492e0250a 100644 --- a/smithy-utils/src/main/java/software/amazon/smithy/utils/MediaType.java +++ b/smithy-utils/src/main/java/software/amazon/smithy/utils/MediaType.java @@ -225,7 +225,7 @@ private void parse() { private String parseToken() { int start = position(); - consumeUntilNoLongerMatches(TOKEN::contains); + consumeWhile(c -> TOKEN.contains((char) c)); // Fail if the token was empty. if (start == position()) { diff --git a/smithy-utils/src/main/java/software/amazon/smithy/utils/SimpleParser.java b/smithy-utils/src/main/java/software/amazon/smithy/utils/SimpleParser.java index a6da9af9140..74f009c9cce 100644 --- a/smithy-utils/src/main/java/software/amazon/smithy/utils/SimpleParser.java +++ b/smithy-utils/src/main/java/software/amazon/smithy/utils/SimpleParser.java @@ -17,6 +17,7 @@ import java.nio.CharBuffer; import java.util.Objects; +import java.util.function.IntPredicate; import java.util.function.Predicate; /** @@ -303,7 +304,11 @@ public void skip() { * the contents of the skipped characters using {@link #sliceFrom(int)}. */ public void consumeRemainingCharactersOnLine() { - consumeUntilNoLongerMatches(c -> c != '\n' && c != '\r'); + char ch = peek(); + while (ch != EOF && ch != '\n' && ch != '\r') { + skip(); + ch = peek(); + } } /** @@ -340,23 +345,30 @@ public final CharSequence borrowSliceFrom(int start, int removeRight) { return CharBuffer.wrap(input, start, position - removeRight); } + @Deprecated + public final int consumeUntilNoLongerMatches(Predicate predicate) { + int startPosition = position; + char ch = peek(); + while (ch != EOF && predicate.test(ch)) { + skip(); + ch = peek(); + } + return position - startPosition; + } + /** - * Reads a lexeme from the expression while the given {@code predicate} - * matches each peeked character. + * Reads a lexeme from the expression while the given {@code predicate} matches each peeked character. * * @param predicate Predicate that filters characters. * @return Returns the consumed lexeme (or an empty string on no matches). */ - public final int consumeUntilNoLongerMatches(Predicate predicate) { + public final int consumeWhile(IntPredicate predicate) { int startPosition = position; - while (!eof()) { - char peekedChar = peek(); - if (!predicate.test(peekedChar)) { - break; - } + char ch = peek(); + while (ch != EOF && predicate.test(ch)) { skip(); + ch = peek(); } - return position - startPosition; }