/** * A scanner that succeeds and consumes the current character if it equals to any character in * {@code chars}. */ public static Parser<Void> among(String chars) { if (chars.length() == 0) return isChar(CharPredicates.NEVER); if (chars.length() == 1) return isChar(chars.charAt(0)); return isChar(CharPredicates.among(chars)); }
/** * A scanner that succeeds and consumes the current character if it is equal to {@code ch}. * * @param ch the expected character. * @return the scanner. */ public static Parser<Void> isChar(char ch) { return isChar(CharPredicates.isChar(ch)); }
/** * A scanner that succeeds and consumes the current character if it is equal to {@code ch}. * * @param ch the expected character. * @param name the name of what's expected logically. Is used in error message. * @return the scanner. * @deprecated Use {@link #notChar(char)}. */ @Deprecated public static Parser<Void> notChar(char ch, String name) { return isChar(CharPredicates.notChar(ch), name); }
/** * A scanner that succeeds and consumes the current character if it is not equal to {@code ch}. * * @param ch the expected character. * @return the scanner. */ public static Parser<Void> notChar(char ch) { return isChar(CharPredicates.notChar(ch)); }
/** * A scanner that succeeds and consumes the current character if it is not equal to any character * in {@code chars}. * * @param chars the characters. * @param name the name of what's expected logically. Is used in error message. * @return the scanner. * @deprecated Use {@code Patterns.among(chars).not().toScanner(name)}, * or {@code isChar(CharPredicates.notAmong(chars), name)}. */ @Deprecated public static Parser<Void> notAmong(String chars, String name) { return isChar(CharPredicates.notAmong(chars), name); }
private static <T> Parser<T> op(char ch, T value) { return isChar(ch).retn(value); }
/** * A scanner that succeeds and consumes the current character if it is equal to {@code ch}. * * @param ch the expected character. * @param name the name of what's expected logically. Is used in error message. * @return the scanner. * @deprecated Use {@link #isChar(char)} instead * or use {@code Patterns.isChar(ch).toScanner(name)}. */ @Deprecated public static Parser<Void> isChar(char ch, String name) { return isChar(CharPredicates.isChar(ch), name); }
/** * A scanner that succeeds and consumes the current character if it equals to any character in * {@code chars}. * * @param chars the characters. * @param name the name of what's expected logically. Is used in error message. * @return the scanner. * @deprecated Use {@code Patterns.among(chars).toScanner(name)}. */ @Deprecated public static Parser<Void> among(String chars, String name) { return isChar(CharPredicates.among(chars), name); }
/** * A scanner that succeeds and consumes the current character if it is not equal to any character * in {@code chars}. */ public static Parser<Void> notAmong(String chars) { if (chars.length() == 0) return ANY_CHAR; if (chars.length() == 1) return notChar(chars.charAt(0)); return isChar(CharPredicates.notAmong(chars)); }
/** * Gets a {@link Lexicon} instance with {@link Tokens#reserved(String)} as each operator's value * and a lexer that strives to try the shortest operator first. * * <p> Safely speaking, we can always start from the longest operator and falls back to shorter * ones. Yet shorter operators are more often used than longer ones and the scanning of them is * faster. However, scanning shorter operators first has the chance that a "==" is mistakenly * scanned as "=" followed by another "=". In order to avoid this, we analyze the prefix * relationship and make sure that prefixes are scanned after prefixes. */ static Lexicon lexicon(final Collection<String> operatorNames) { final Map<String, Object> operators = new HashMap<String, Object>(); final String[] ops = sort(operatorNames.toArray(new String[operatorNames.size()])); final Parser<?>[] lexers = new Parser<?>[ops.length]; for (int i = 0; i < ops.length; i++) { String s = ops[i]; Parser<?> scanner = s.length() == 1 ? Scanners.isChar(s.charAt(0)) : Scanners.string(s); Object value = Tokens.reserved(s); operators.put(s, value); lexers[i] = scanner.retn(value); } return new Lexicon(operators::get, Parsers.or(lexers)); }
/** * A scanner for a quoted string that starts with character {@code begin} and ends with character * {@code end}. */ public static Parser<String> quoted(char begin, char end) { Pattern beforeClosingQuote = Patterns.isChar(begin).next(Patterns.many(CharPredicates.notChar(end))); return beforeClosingQuote.toScanner(Character.toString(begin)).next(isChar(end)).source(); }
/** * A {@link Parser} that greedily runs {@code tokenizer}, and translates line feed characters * ({@code '\n'}) to {@code indent} and {@code outdent} tokens. * Return values are wrapped in {@link Token} objects and collected in a {@link List}. * Patterns recognized by {@code delim} are ignored. */ public Parser<List<Token>> lexer(Parser<?> tokenizer, Parser<?> delim) { Parser<?> lf = Scanners.isChar('\n').retn(Punctuation.LF); return Parsers.or(tokenizer, lf).lexer(delim) .map(tokens -> analyzeIndentations(tokens, Punctuation.LF)); }
static Parser<Integer> parser() { Parser.Reference<Integer> ref = Parser.newReference(); Parser<Integer> term = ref.lazy().between(isChar('('), isChar(')')).or(NUMBER); Parser<Integer> parser = new OperatorTable<Integer>() .prefix(op('-', NEG), 100) .infixl(op('+', PLUS), 10) .infixl(op('-', MINUS), 10) .infixl(op('*', MUL), 20) .infixl(op('/', DIV), 20) .infixl(op('%', MOD), 20) .build(term); ref.set(parser); return parser; } }