001/*
002 * Java Genetic Algorithm Library (jenetics-8.0.0).
003 * Copyright (c) 2007-2024 Franz Wilhelmstötter
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 * Author:
018 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019 */
020package io.jenetics.ext.internal.util;
021
022import static java.util.Objects.requireNonNull;
023
024import java.util.HashMap;
025import java.util.List;
026import java.util.Map;
027import java.util.Map.Entry;
028import java.util.Objects;
029import java.util.Set;
030import java.util.function.Consumer;
031import java.util.function.Predicate;
032import java.util.function.Supplier;
033
034import io.jenetics.ext.internal.parser.Parser;
035import io.jenetics.ext.internal.parser.ParsingException;
036import io.jenetics.ext.util.TreeNode;
037
038/**
039 * This class allows you to convert a sequence of <em>tokens</em>, which
040 * represents some kind of (mathematical) formula, into a tree structure. To do
041 * this, it is assumed that the given tokens can be categorized. The two main
042 * categories are <em>structural</em> tokens and <em>operational</em> tokens.
043 *
044 * <p><b>Structural tokens</b></p>
045 * Structural tokens are used to influence the hierarchy of the parsed tokens
046 * and are also part of function definitions. This kind of token will not be
047 * part of the generated tree representation.
048 * <ol>
049 *     <li><em>lparen</em>: Represents left parentheses, which starts
050 *     sub-trees or opens function argument lists.</li>
051 *     <li><em>rparen</em>: Represents right parentheses, which closes
052 *     sub-trees or function argument lists. <em>lparen</em> and
053 *     <em>rparen</em> must be balanced.</li>
054 *     <li><em>comma</em>: Separator token for function arguments.</li>
055 * </ol>
056 *
057 * <p><b>Operational tokens</b></p>
058 * Operational tokens define the actual <em>behaviour</em> of the created tree.
059 * <ol>
060 *     <li><em>identifier</em>: This kind of tokens usually represents variable
061 *     names or numbers.</li>
062 *     <li><em>function</em>: Function tokens represents identifiers for
063 *     functions. Valid functions have the following form: {@code 'fun' 'lparen'
064 *     arg ['comma' args]* 'rparen'}</li>
065 *     <li><em>binary operator</em>: Binary operators are defined in infix
066 *     order and have a precedence. Typical examples are the arithmetic
067 *     operators '+' and '*', where the '*' have a higher precedence than '+'.</li>
068 *     <li><em>unary operator</em>: Unary operators are prefix operators. A
069 *     typical example is the arithmetic negation operator '-'. Unary
070 *     operators have all the same precedence, which is higher than the
071 *     precedence of all binary operators.</li>
072 * </ol>
073 *
074 * This class is only responsible for the parsing step. The tokenization must
075 * be implemented separately. Another possible token source would be a generating
076 * grammar, where the output is already a list of tokens (aka sentence). The
077 * following example parser can be used to parse arithmetic expressions.
078 *
079 * {@snippet lang="java":
080 * final FormulaParser<String> parser = FormulaParser.<String>builder()
081 *     // Structural tokens.
082 *     .lparen("(")
083 *     .rparen(")")
084 *     .separator(",")
085 *     // Operational tokens.
086 *     .unaryOperators("+", "-")
087 *     .binaryOperators(ops -> ops
088 *         .add(11, "+", "-")
089 *         .add(12, "*", "/")
090 *         .add(14, "^", "**"))
091 *     .identifiers("x", "y", "z")
092 *     .functions("pow", "sin", "cos")
093 *     .build();
094 * }
095 * This parser allows you to parse the following token list
096 * {@snippet lang="java":
097 * final List<String> tokens = List.of(
098 *     "x", "*", "x", "+", "sin", "(", "z", ")", "-", "cos", "(", "x",
099 *     ")", "+", "y", "/", "z", "-", "pow", "(", "z", ",", "x", ")"
100 * );
101 * final Tree<String, ?> tree = parser.parse(tokens);
102 * }
103 * which will result in the following parsed tree:
104 * <pre>{@code
105 * "-"
106 * ├── "+"
107 * │   ├── "-"
108 * │   │   ├── "+"
109 * │   │   │   ├── "*"
110 * │   │   │   │   ├── "x"
111 * │   │   │   │   └── "x"
112 * │   │   │   └── "sin"
113 * │   │   │       └── "z"
114 * │   │   └── "cos"
115 * │   │       └── "x"
116 * │   └── "/"
117 * │       ├── "y"
118 * │       └── "z"
119 * └── "pow"
120 *     ├── "z"
121 *     └── "x"
122 * }</pre>
123 * Note that the generated (parsed) tree is of type {@code Tree<String, ?>}. To
124 * <em>evaluate</em> this tree, additional steps are necessary. If you want to
125 * create an <em>executable</em> tree, you have to use the
126 * {@link #parse(Iterable, TokenConverter)} function for parsing the tokens.
127 * <p>
128 * The following code snippet shows how to create an <em>executable</em> AST
129 * from a token list. The {@code MathExpr} class in the {@code io.jenetics.prog}
130 * module uses a similar {@link TokenConverter}.
131 * {@snippet lang="java":
132 * final Tree<Op<Double>, ?> tree = formula.parse(
133 *     tokens,
134 *     (token, type) -> switch (token) {
135 *         case "+" -> type == TokenType.UNARY_OPERATOR ? MathOp.ID : MathOp.ADD;
136 *         case "-" -> type == TokenType.UNARY_OPERATOR ? MathOp.NEG : MathOp.SUB;
137 *         case "*" -> MathOp.MUL;
138 *         case "/" -> MathOp.DIV;
139 *         case "^", "**", "pow" -> MathOp.POW;
140 *         case "sin" -> MathOp.SIN;
141 *         case "cos" -> MathOp.COS;
142 *         default -> type == TokenType.IDENTIFIER
143 *             ? Var.of(token)
144 *             : throw new IllegalArgumentException("Unknown token: " + token);
145 *     }
146 * );
147 * }
148 *
149 * @param <T> the token type used as input for the parser
150 *
151 * @implNote
152 * This class is immutable and thread-safe.
153 *
154 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
155 * @since 7.1
156 * @version 7.1
157 */
158public final class FormulaParser<T> {
159
160        /**
161         * The token types the parser recognizes during the parsing process.
162         */
163        public enum TokenType {
164
165                /**
166                 * Indicates an unary operator.
167                 */
168                UNARY_OPERATOR,
169
170                /**
171                 * Indicates a binary operator.
172                 */
173                BINARY_OPERATOR,
174
175                /**
176                 * Indicates a function token.
177                 */
178                FUNCTION,
179
180                /**
181                 * Indicates an identifier token.
182                 */
183                IDENTIFIER
184        }
185
186        /**
187         * Conversion function which is used for converting tokens into another
188         * type.
189         *
190         * @param <T> the token type
191         * @param <V> the converted value type
192         */
193        @FunctionalInterface
194        public interface TokenConverter<T, V> {
195
196                /**
197                 * Convert the given {@code token} into another value. The conversion
198                 * can use the token type, recognized during the parsing process.
199                 *
200                 * @param token the token value to convert
201                 * @param type the token type, recognized during the parsing process
202                 * @return the converted value
203                 */
204                V convert(final T token, final TokenType type);
205        }
206
207
208        private final Predicate<? super T> _lparen;
209        private final Predicate<? super T> _rparen;
210        private final Predicate<? super T> _separator;
211        private final Predicate<? super T> _uops;
212        private final Predicate<? super T> _identifiers;
213        private final Predicate<? super T> _functions;
214
215        // The processed binary operators.
216        private final Term<T> _term;
217
218        /**
219         * Creates a new general expression parser object. The parser is not bound
220         * to a specific source and target type or concrete token types.
221         *
222         * @param lparen the token type specifying the left parentheses, '('
223         * @param rparen the token type specifying the right parentheses, ')'
224         * @param separator the token type specifying the function parameter
225         *        separator, ','
226         * @param bops the list of binary operators, according its
227         *        precedence. The first list element contains the operations with
228         *        the lowest precedence, and the last list element contains the
229         *        operations with the highest precedence.
230         * @param uops the token types representing the unary operations
231         * @param identifiers the token type representing identifier, like variable
232         *        names, constants or numbers
233         * @param functions predicate which tests whether a given identifier value
234         *        represents a known function name
235         */
236        private FormulaParser(
237                final Predicate<? super T> lparen,
238                final Predicate<? super T> rparen,
239                final Predicate<? super T> separator,
240                final List<? extends Predicate<? super T>> bops,
241                final Predicate<? super T> uops,
242                final Predicate<? super T> identifiers,
243                final Predicate<? super T> functions
244        ) {
245                _lparen = requireNonNull(lparen);
246                _rparen = requireNonNull(rparen);
247                _separator = requireNonNull(separator);
248                _uops = requireNonNull(uops);
249                _identifiers = requireNonNull(identifiers);
250                _functions = requireNonNull(functions);
251
252                final Term<T> oterm = BopTerm.build(bops);
253                final Term<T> fterm = new Term<>() {
254                        @Override
255                        <V> TreeNode<V> term(
256                                final Parser<T> parser,
257                                final TokenConverter<? super T, ? extends V> mapper
258                        ) {
259                                return function(parser, mapper);
260                        }
261                };
262                if (oterm != null) {
263                        oterm.append(fterm);
264                        _term = oterm;
265                } else {
266                        _term = fterm;
267                }
268        }
269
270        private <V> TreeNode<V> function(
271                final Parser<T> parser,
272                final TokenConverter<? super T, ? extends V> mapper
273        ) {
274                final var token = parser.LT(1);
275
276                if (_functions.test(token)) {
277                        parser.consume();
278                        final TreeNode<V> node = TreeNode
279                                .of(mapper.convert(token, TokenType.FUNCTION));
280
281                        parser.match(_lparen);
282                        node.attach(_term.expr(parser, mapper));
283                        while (_separator.test(parser.LT(1))) {
284                                parser.consume();
285                                node.attach(_term.expr(parser, mapper));
286                        }
287                        parser.match(_rparen);
288
289                        return node;
290                } else if (_lparen.test(token)) {
291                        parser.consume();
292                        final TreeNode<V> node = _term.expr(parser, mapper);
293                        parser.match(_rparen);
294                        return node;
295                } else {
296                        return unary(() -> atom(parser, mapper), parser, mapper);
297                }
298        }
299
300        private <V> TreeNode<V> atom(
301                final Parser<T> parser,
302                final TokenConverter<? super T, ? extends V> mapper
303        ) {
304                final var token = parser.LT(1);
305
306                if (_identifiers.test(token)) {
307                        parser.consume();
308                        return TreeNode.of(mapper.convert(token, TokenType.IDENTIFIER));
309                } else if (token == null) {
310                        throw new ParsingException("Unexpected end of input.");
311                } else {
312                        throw new ParsingException(
313                                "Unexpected symbol found: %s.".formatted(parser.LT(1))
314                        );
315                }
316        }
317
318        private <V> TreeNode<V> unary(
319                final Supplier<TreeNode<V>> other,
320                final Parser<T> parser,
321                final TokenConverter<? super T, ? extends V> mapper
322        ) {
323                final var token = parser.LT(1);
324
325                if (_uops.test(token)) {
326                        parser.consume();
327                        return TreeNode
328                                .<V>of(mapper.convert(token, TokenType.UNARY_OPERATOR))
329                                .attach(other.get());
330                } else {
331                        return other.get();
332                }
333        }
334
335        /**
336         * Parses the given token sequence according {@code this} formula definition.
337         * If the given {@code tokens} supplier returns null, no further token is
338         * available.
339         *
340         * @param tokens the tokens which form the formula
341         * @param mapper the mapper function which maps the token type to the parse
342         *        tree value type
343         * @return the parsed formula as a tree
344         * @throws NullPointerException if one of the arguments is {@code null}
345         * @throws IllegalArgumentException if the given {@code tokens} can't be
346         *         parsed
347         */
348        public <V> TreeNode<V> parse(
349                final Supplier<? extends T> tokens,
350                final TokenConverter<? super T, ? extends V> mapper
351        ) {
352                requireNonNull(tokens);
353                requireNonNull(mapper);
354
355                return _term.expr(new Parser<T>(tokens::get, 1), mapper);
356        }
357
358        /**
359         * Parses the given token sequence according {@code this} formula definition.
360         * If the given {@code tokens} supplier returns null, no further token is
361         * available.
362         *
363         * @param tokens the tokens which form the formula
364         * @return the parsed formula as a tree
365         * @throws NullPointerException if the arguments is {@code null}
366         * @throws IllegalArgumentException if the given {@code tokens} can't be
367         *         parsed
368         */
369        public TreeNode<T> parse(final Supplier<? extends T> tokens) {
370                return parse(tokens, (token, type) -> token);
371        }
372
373        /**
374         * Parses the given token sequence according {@code this} formula definition.
375         *
376         * @param tokens the tokens which form the formula
377         * @param mapper the mapper function which maps the token type to the parse
378         *        tree value type
379         * @return the parsed formula as a tree
380         * @throws NullPointerException if one of the arguments is {@code null}
381         * @throws IllegalArgumentException if the given {@code tokens} can't be
382         *         parsed
383         */
384        public <V> TreeNode<V> parse(
385                final Iterable<? extends T> tokens,
386                final TokenConverter<? super T, ? extends V> mapper
387        ) {
388                final var it = tokens.iterator();
389                return parse(() -> it.hasNext() ? it.next() : null, mapper);
390        }
391
392        /**
393         * Parses the given token sequence according {@code this} formula definition.
394         *
395         * @param tokens the tokens which form the formula
396         * @return the parsed formula as a tree
397         * @throws NullPointerException if the arguments is {@code null}
398         * @throws IllegalArgumentException if the given {@code tokens} can't be
399         *         parsed
400         */
401        public TreeNode<T> parse(final Iterable<? extends T> tokens) {
402                return parse(tokens, (token, type) -> token);
403        }
404
405        /**
406         * Return a new builder class for building new formula parsers.
407         *
408         * @param <T> the token type
409         * @return a new formula parser builder
410         */
411        public static <T> Builder<T> builder() {
412                return new Builder<>();
413        }
414
415
416        /* *************************************************************************
417         * FormulaParser helper classes
418         * ************************************************************************/
419
420        /**
421         * General term object to be parsed.
422         *
423         * @param <T> the token value type used as input for the parser
424         */
425        private static abstract class Term<T> {
426                Term<T> _next;
427                Term<T> _last;
428
429                <V> TreeNode<V> op(
430                        final TreeNode<V> expr,
431                        final Parser<T> parser,
432                        final TokenConverter<? super T, ? extends V> mapper
433                ) {
434                        return expr;
435                }
436
437                abstract <V> TreeNode<V> term(
438                        final Parser<T> parser,
439                        final TokenConverter<? super T, ? extends V> mapper
440                );
441
442                <V> TreeNode<V> expr(
443                        final Parser<T> parser,
444                        final TokenConverter<? super T, ? extends V> mapper
445                ) {
446                        return op(term(parser, mapper), parser, mapper);
447                }
448
449                void append(final Term<T> term) {
450                        if (_next == null) {
451                                _next = term;
452                                _last = term;
453                        } else {
454                                _last.append(term);
455                        }
456                }
457        }
458
459        /**
460         * Represents a binary (mathematical) operation.
461         *
462         * @param <T> the token value type used as input for the parser
463         */
464        private static class BopTerm<T> extends Term<T> {
465                private final Predicate<? super T> _tokens;
466
467                BopTerm(final Predicate<? super T> tokens) {
468                        _tokens = requireNonNull(tokens);
469                }
470
471                @Override
472                <V> TreeNode<V> op(
473                        final TreeNode<V> expr,
474                        final Parser<T> parser,
475                        final TokenConverter<? super T, ? extends V> mapper
476                ) {
477                        var result = expr;
478
479                        final var token = parser.LT(1);
480                        if (token != null && _tokens.test(token)) {
481                                parser.consume();
482
483                                final TreeNode<V> node = TreeNode
484                                        .<V>of(mapper.convert(token, TokenType.BINARY_OPERATOR))
485                                        .attach(expr)
486                                        .attach(term(parser, mapper));
487
488                                result = op(node, parser, mapper);
489                        }
490                        return result;
491                }
492
493                @Override
494                <V> TreeNode<V> term(
495                        final Parser<T> parser,
496                        final TokenConverter<? super T, ? extends V> mapper
497                ) {
498                        return _next.op(_next.term(parser, mapper), parser, mapper);
499                }
500
501                /**
502                 * Builds a linked chain of binary operations. Operations with lower
503                 * <em>precedence</em> are at the beginning of the chain and operations
504                 * with higher <em>precedence</em> are appended to the end of the linked
505                 * operation term chain.
506                 *
507                 * @param bops the list of binary operations with a given precedence
508                 * @param <T> the token value type used as input for the parser
509                 * @return the linked operation term
510                 */
511                static <T> BopTerm<T> build(final List<? extends Predicate<? super T>> bops) {
512                        BopTerm<T> start = null;
513                        for (var tokens : bops) {
514                                final BopTerm<T> term = new BopTerm<>(tokens);
515                                if (start == null) {
516                                        start = term;
517                                } else {
518                                        start.append(term);
519                                }
520                        }
521
522                        return start;
523                }
524        }
525
526        /* *************************************************************************
527         * FormulaParser builder class
528         * ************************************************************************/
529
530
531        /**
532         * Builder for building new {@link FormulaParser} instances.
533         *
534         * @param <T> the token type
535         */
536        public static final class Builder<T> {
537
538                private Predicate<? super T> _lparen = token -> false;
539                private Predicate<? super T> _rparen = token -> false;
540                private Predicate<? super T> _separator = token -> false;
541                private List<? extends Predicate<? super T>> _bops = List.of();
542                private Predicate<? super T> _uops = token -> false;
543                private Predicate<? super T> _identifiers = token -> false;
544                private Predicate<? super T> _functions = token -> false;
545
546
547                private Builder() {
548                }
549
550                /**
551                 * Set the predicate which defines {@code lparen} tokens. If the given
552                 * predicate returns {@code true} for a token, it is treated as
553                 * <em>lparen</em>.
554                 *
555                 * @param lparen the {@code lparen} token
556                 * @return {@code this} builder, for method chaining
557                 * @throws NullPointerException if the {@code lparen} is {@code null}
558                 */
559                public Builder<T> lparen(final Predicate<? super T> lparen) {
560                        _lparen = requireNonNull(lparen);
561                        return this;
562                }
563
564                /**
565                 * Set the <em>prototype</em> for the {@code lparen} token. A given
566                 * token is treated as  {@code lparen} if {@code Objects.equals(token, lparen)}
567                 * returns {@code true}.
568                 *
569                 * @param lparen the {@code lparen} prototype
570                 * @return {@code this} builder, for method chaining
571                 */
572                public Builder<T> lparen(final T lparen) {
573                        return lparen(token -> Objects.equals(token, lparen));
574                }
575
576                /**
577                 * Set the predicate which defines {@code rparen} tokens. If the given
578                 * predicate returns {@code true} for a token, it is treated as
579                 * <em>rparen</em>.
580                 *
581                 * @param rparen the {@code rparen} token
582                 * @return {@code this} builder, for method chaining
583                 * @throws NullPointerException if the {@code rparen} is {@code null}
584                 */
585                public Builder<T> rparen(final Predicate<? super T> rparen) {
586                        _rparen = requireNonNull(rparen);
587                        return this;
588                }
589
590                /**
591                 * Set the <em>prototype</em> for the {@code rparen} token. A given
592                 * token is treated as  {@code rparen} if {@code Objects.equals(token, rparen)}
593                 * returns {@code true}.
594                 *
595                 * @param rparen the {@code rparen} prototype
596                 * @return {@code this} builder, for method chaining
597                 */
598                public Builder<T> rparen(final T rparen) {
599                        return rparen(token -> Objects.equals(token, rparen));
600                }
601
602                /**
603                 * Set the predicate which defines {@code separator} tokens. If the given
604                 * predicate returns {@code true} for a token, it is treated as
605                 * <em>separator</em>.
606                 *
607                 * @param separator the {@code separator} token
608                 * @return {@code this} builder, for method chaining
609                 * @throws NullPointerException if the {@code separator} is {@code null}
610                 */
611                public Builder<T> separator(final Predicate<? super T> separator) {
612                        _separator = requireNonNull(separator);
613                        return this;
614                }
615
616                /**
617                 * Set the <em>prototype</em> for the {@code separator} token. A given
618                 * token is treated as  {@code separator} if {@code Objects.equals(token, separator)}
619                 * returns {@code true}.
620                 *
621                 * @param separator the {@code separator} prototype
622                 * @return {@code this} builder, for method chaining
623                 */
624                public Builder<T> separator(final T separator) {
625                        return separator(token -> Objects.equals(token, separator));
626                }
627
628                /**
629                 * Set the predicate which defines the unary operator tokens. If the
630                 * given predicate returns {@code true} for a token, it is treated as
631                 * unary operator.
632                 *
633                 * @param ops the {@code comma} token
634                 * @return {@code this} builder, for method chaining
635                 * @throws NullPointerException if the {@code ops} is {@code null}
636                 */
637                public Builder<T> unaryOperators(final Predicate<? super T> ops) {
638                        _uops = requireNonNull(ops);
639                        return this;
640                }
641
642                /**
643                 * Set all unary operator tokens.
644                 *
645                 * @param ops the unary operator tokens
646                 * @return {@code this} builder, for method chaining
647                 * @throws NullPointerException if the {@code ops} is {@code null}
648                 */
649                public Builder<T> unaryOperators(final Set<? extends T> ops) {
650                        return unaryOperators(Set.copyOf(ops)::contains);
651                }
652
653                /**
654                 * Set all unary operator tokens.
655                 *
656                 * @param ops the unary operator tokens
657                 * @return {@code this} builder, for method chaining
658                 * @throws NullPointerException if the {@code ops} is {@code null}
659                 */
660                @SafeVarargs
661                public final Builder<T> unaryOperators(final T... ops) {
662                        return unaryOperators(Set.of(ops));
663                }
664
665                /**
666                 * Set the list of predicates which defines the binary ops. The
667                 * predicate indexes of the list represent the precedence of the binary
668                 * ops. {@code ops.get(0)} has the lowest precedence and
669                 * {@code ops.get(ops.size() - 1)} has the highest precedence
670                 *
671                 * @param ops the predicates defining the binary operator tokens
672                 * @return {@code this} builder, for method chaining
673                 * @throws NullPointerException if the {@code ops} is {@code null}
674                 */
675                public Builder<T> binaryOperators(final List<? extends Predicate<? super T>> ops) {
676                        _bops = List.copyOf(ops);
677                        return this;
678                }
679
680                /**
681                 * Set the list of predicates which defines the binary ops. The
682                 * predicate indexes of the list represent the precedence of the binary
683                 * ops. {@code ops.get(0)} has the lowest precedence and
684                 * {@code ops.get(ops.size() - 1)} has the highest precedence
685                 *
686                 * @param ops the predicates defining the binary operator tokens
687                 * @return {@code this} builder, for method chaining
688                 * @throws NullPointerException if the {@code ops} is {@code null}
689                 */
690                @SafeVarargs
691                public final Builder<T> binaryOperators(final Predicate<? super T>... ops) {
692                        _bops = List.of(ops);
693                        return this;
694                }
695
696                /**
697                 * Method for defining the binary operators and its precedence.
698                 *
699                 * @param ops the predicates defining the binary operator tokens
700                 * @return {@code this} builder, for method chaining
701                 */
702                public Builder<T> binaryOperators(final Consumer<? super Bops<T>> ops) {
703                        final var builder = new Bops<T>();
704                        ops.accept(builder);
705                        _bops = builder.build();
706                        return this;
707                }
708
709                /**
710                 * Set the predicate which defines identifier tokens.
711                 *
712                 * @param identifiers the identifier predicate
713                 * @return {@code this} builder, for method chaining
714                 * @throws NullPointerException if the {@code identifiers} is {@code null}
715                 */
716                public Builder<T> identifiers(final Predicate<? super T> identifiers) {
717                        _identifiers = requireNonNull(identifiers);
718                        return this;
719                }
720
721                /**
722                 * Set all identifier tokens.
723                 *
724                 * @param identifiers the identifier tokens
725                 * @return {@code this} builder, for method chaining
726                 * @throws NullPointerException if the {@code identifiers} is {@code null}
727                 */
728                public Builder<T> identifiers(final Set<? extends T> identifiers) {
729                        return identifiers(Set.copyOf(identifiers)::contains);
730                }
731
732                /**
733                 * Set all identifier tokens.
734                 *
735                 * @param identifiers the identifier tokens
736                 * @return {@code this} builder, for method chaining
737                 * @throws NullPointerException if the {@code identifiers} is {@code null}
738                 */
739                @SafeVarargs
740                public final Builder<T> identifiers(final T... identifiers) {
741                        return identifiers(Set.of(identifiers));
742                }
743
744                /**
745                 * Set the predicate which defines function tokens.
746                 *
747                 * @param functions the function predicate
748                 * @return {@code this} builder, for method chaining
749                 * @throws NullPointerException if the {@code functions} is {@code null}
750                 */
751                public Builder<T> functions(final Predicate<? super T> functions) {
752                        _functions = requireNonNull(functions);
753                        return this;
754                }
755
756                /**
757                 * Set all functions tokens.
758                 *
759                 * @param functions the function tokens
760                 * @return {@code this} builder, for method chaining
761                 * @throws NullPointerException if the {@code functions} is {@code null}
762                 */
763                public Builder<T> functions(final Set<? extends T> functions) {
764                        return functions(Set.copyOf(functions)::contains);
765                }
766
767                /**
768                 * Set all functions tokens.
769                 *
770                 * @param functions the function tokens
771                 * @return {@code this} builder, for method chaining
772                 * @throws NullPointerException if the {@code functions} is {@code null}
773                 */
774                @SafeVarargs
775                public final Builder<T> functions(final T... functions) {
776                        return functions(Set.of(functions));
777                }
778
779                /**
780                 * Create a new formula parser with the defined values.
781                 *
782                 * @return a new formula parser
783                 */
784                public FormulaParser<T> build() {
785                        return new FormulaParser<>(
786                                _lparen,
787                                _rparen,
788                                _separator,
789                                _bops,
790                                _uops,
791                                _identifiers,
792                                _functions
793                        );
794                }
795
796                /**
797                 * Builder class for building binary operators with its precedence.
798                 *
799                 * @param <T> the token type
800                 */
801                public static final class Bops<T> {
802                        private final Map<Integer, Predicate<? super T>> _operations = new HashMap<>();
803
804                        private Bops() {
805                        }
806
807                        /**
808                         * Add a new operator predicate with its precedence.
809                         *
810                         * @param precedence the precedence of the operators
811                         * @param operators the operators predicate
812                         * @return {@code this} builder, for method chaining
813                         */
814                        public Bops<T> add(
815                                final int precedence,
816                                final Predicate<? super T> operators
817                        ) {
818                                Predicate<? super T> ops = _operations.get(precedence);
819                                if (ops != null) {
820                                        final Predicate<? super T> prev = ops;
821                                        ops = token -> prev.test(token) || operators.test(token);
822                                } else {
823                                        ops = operators;
824                                }
825                                _operations.put(precedence, ops);
826
827                                return this;
828                        }
829
830                        /**
831                         * Add a new operator tokens with its precedence.
832                         *
833                         * @param precedence the precedence of the operators
834                         * @param operators the operators
835                         * @return {@code this} builder, for method chaining
836                         */
837                        @SafeVarargs
838                        public final Bops<T> add(
839                                final int precedence,
840                                final T... operators
841                        ) {
842                                return add(precedence, Set.of(operators)::contains);
843                        }
844
845                        private List<? extends Predicate<? super T>> build() {
846                                return _operations.entrySet().stream()
847                                        .sorted(Entry.comparingByKey())
848                                        .map(Entry::getValue)
849                                        .toList();
850                        }
851
852                }
853        }
854
855}