FormulaParser.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-7.1.2).
003  * Copyright (c) 2007-2023 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  */
020 package io.jenetics.ext.internal.util;
021 
022 import static java.util.Objects.requireNonNull;
023 
024 import java.util.HashMap;
025 import java.util.List;
026 import java.util.Map;
027 import java.util.Map.Entry;
028 import java.util.Objects;
029 import java.util.Set;
030 import java.util.function.Consumer;
031 import java.util.function.Predicate;
032 import java.util.function.Supplier;
033 
034 import io.jenetics.ext.internal.parser.Parser;
035 import io.jenetics.ext.internal.parser.ParsingException;
036 import 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 categorised. 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 tokens 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  <pre>{@code
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  * }</pre>
095  * This parser allows you to parse the following token list
096  <pre>{@code
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  * }</pre>
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  <pre>{@code
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  * }</pre>
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  */
158 public 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 forms 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 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 forms the formula
364      @return the parsed formula as 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 forms 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 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 forms the formula
396      @return the parsed formula as 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 represents 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 represents 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 functions 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 functions 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 }