001 /*
002 * Java Genetic Algorithm Library (jenetics-7.1.0).
003 * Copyright (c) 2007-2022 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 }
|