001/* 002 * Java Genetic Algorithm Library (jenetics-7.2.0). 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 */ 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 * <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 */ 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}