| 546 | |
| 547 | ######################################################################## |
| 548 | # |
| 549 | # Automatic Creation of Variable Names |
| 550 | # |
| 551 | # See the docstring for automatic_names below for an explanation of how |
| 552 | # this works. |
| 553 | # |
| 554 | ######################################################################## |
| 555 | |
| 556 | _automatic_names = False |
| 557 | # We wrap everything in a try/catch, in case this is being imported |
| 558 | # without the sage library present, e.g., in FEMhub. |
| 559 | try: |
| 560 | from sage.symbolic.all import Expression, SR |
| 561 | class AutomaticVariable(Expression): |
| 562 | """ |
| 563 | An automatically created symbolic variable with an additional |
| 564 | :meth:`__call__` method designed so that doing self(foo,...) |
| 565 | results in foo.self(...). |
| 566 | """ |
| 567 | def __call__(self, *args, **kwds): |
| 568 | """ |
| 569 | Call method such that self(foo, ...) is transformed into |
| 570 | foo.self(...). Note that self(foo=...,...) is not |
| 571 | transformed, it is treated as a normal symbolic |
| 572 | substitution. |
| 573 | """ |
| 574 | if len(args) == 0: |
| 575 | return Expression.__call__(self, **kwds) |
| 576 | return args[0].__getattribute__(str(self))(*args[1:], **kwds) |
| 577 | |
| 578 | def automatic_name_eval(s, globals, max_names=10000): |
| 579 | """ |
| 580 | Exec the string ``s`` in the scope of the ``globals`` |
| 581 | dictionary, and if any :exc:`NameError`\ s are raised, try to |
| 582 | fix them by defining the variable that caused the error to be |
| 583 | raised, then eval again. Try up to ``max_names`` times. |
| 584 | |
| 585 | INPUT: |
| 586 | |
| 587 | - ``s`` -- a string |
| 588 | - ``globals`` -- a dictionary |
| 589 | - ``max_names`` -- a positive integer (default: 10000) |
| 590 | """ |
| 591 | # This entire automatic naming system really boils down to |
| 592 | # this bit of code below. We simply try to exec the string s |
| 593 | # in the globals namespace, defining undefined variables and |
| 594 | # functions until everything is defined. |
| 595 | for _ in range(max_names): |
| 596 | try: |
| 597 | exec s in globals |
| 598 | return |
| 599 | except NameError, msg: |
| 600 | # Determine if we hit a NameError that is probably |
| 601 | # caused by a variable or function not being defined: |
| 602 | if len(msg.args) == 0: raise # not NameError with |
| 603 | # specific variable name |
| 604 | v = msg.args[0].split("'") |
| 605 | if len(v) < 2: raise # also not NameError with |
| 606 | # specific variable name We did |
| 607 | # find an undefined variable: we |
| 608 | # simply define it and try |
| 609 | # again. |
| 610 | nm = v[1] |
| 611 | globals[nm] = AutomaticVariable(SR, SR.var(nm)) |
| 612 | raise NameError, "Too many automatic variable names and functions created (limit=%s)"%max_names |
| 613 | |
| 614 | def automatic_name_filter(s): |
| 615 | """ |
| 616 | Wrap the string ``s`` in a call that will cause evaluation of |
| 617 | ``s`` to automatically create undefined variable names. |
| 618 | |
| 619 | INPUT: |
| 620 | |
| 621 | - ``s`` -- a string |
| 622 | |
| 623 | OUTPUT: |
| 624 | |
| 625 | - a string |
| 626 | """ |
| 627 | return '_support_.automatic_name_eval(_support_.base64.b64decode("%s"),globals())'%base64.b64encode(s) |
| 628 | |
| 629 | def automatic_names(state=None): |
| 630 | """ |
| 631 | Turn automatic creation of variables and functional calling of |
| 632 | methods on or off. Returns the current ``state`` if no |
| 633 | argument is given. |
| 634 | |
| 635 | This ONLY works in the Sage notebook. It is not supported on |
| 636 | the command line. |
| 637 | |
| 638 | INPUT: |
| 639 | |
| 640 | - ``state`` -- a boolean (default: None); whether to turn |
| 641 | automatic variable creation and functional calling on or off |
| 642 | |
| 643 | OUTPUT: |
| 644 | |
| 645 | - a boolean, if ``state`` is None; otherwise, None |
| 646 | |
| 647 | EXAMPLES:: |
| 648 | |
| 649 | sage: automatic_names(True) # not tested |
| 650 | sage: x + y + z # not tested |
| 651 | x + y + z |
| 652 | |
| 653 | Here, ``trig_expand``, ``y``, and ``theta`` are all |
| 654 | automatically created:: |
| 655 | |
| 656 | sage: trig_expand((2*x + 4*y + sin(2*theta))^2) # not tested |
| 657 | 4*(sin(theta)*cos(theta) + x + 2*y)^2 |
| 658 | |
| 659 | IMPLEMENTATION: Here's how this works, internally. We define |
| 660 | an :class:`AutomaticVariable` class derived from |
| 661 | :class:`~sage.symbolic.all.Expression`. An instance of |
| 662 | :class:`AutomaticVariable` is a specific symbolic variable, |
| 663 | but with a special :meth:`~AutomaticVariable.__call__` method. |
| 664 | We overload the call method so that ``foo(bar, ...)`` gets |
| 665 | transformed to ``bar.foo(...)``. At the same time, we still |
| 666 | want expressions like ``f^2 - b`` to work, i.e., we don't want |
| 667 | to have to figure out whether a name appearing in a |
| 668 | :exc:`NameError` is meant to be a symbolic variable or a |
| 669 | function name. Instead, we just make an object that is both! |
| 670 | |
| 671 | This entire approach is very simple---we do absolutely no |
| 672 | parsing of the actual input. The actual real work amounts to |
| 673 | only a few lines of code. The primary catch to this approach |
| 674 | is that if you evaluate a big block of code in the notebook, |
| 675 | and the first few lines take a long time, and the next few |
| 676 | lines define 10 new variables, the slow first few lines will |
| 677 | be evaluated 10 times. Of course, the advantage of this |
| 678 | approach is that even very subtle code that might inject |
| 679 | surprisingly named variables into the namespace will just work |
| 680 | correctly, which would be impossible to guarantee with static |
| 681 | parsing, no matter how sophisticated it is. Finally, given |
| 682 | the target audience: people wanting to simplify use of Sage |
| 683 | for Calculus for undergrads, I think this is an acceptable |
| 684 | tradeoff, especially given that this implementation is so |
| 685 | simple. |
| 686 | """ |
| 687 | global _automatic_names |
| 688 | if state is None: |
| 689 | return _automatic_names |
| 690 | _automatic_names = bool(state) |
| 691 | |
| 692 | except ImportError: |
| 693 | pass |
| 694 | |