1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098
|
ECL: Enhanced CL Release Notes and User's Guide
================================================
Michael Fitzpatrick
NOAO/IRAF Group
12/12/04
Revised: 5/28/05
********************************************************************************
Release History:
02/10/05 ** Alpha Release for testing
05/06/05 ** 2nd Alpha Release for testing
06/07/05 ** 1st Beta Release for testing
********************************************************************************
Table of Contents
-----------------
Introduction
Installation and Use
To Install the CL
Determine CL Version Type
Error Handling
Introduction and Cautions
Example Descriptions
Reporting Errors
Traceback
Trapping Errors
The 'iferr' Syntax
The 'erract' Environment Variable
Error Handling: Then and Now
New CL parameters
What Errors Are NOT Trapped
Command-line History and BackSpace Revisions
Input Command Summary
New Builtin Functions and Variables
Error Functions
String Functions
Trig Functions
Utility Functions
Bitwise Operations
Defined Constants
Post-Release Notes
********************************************************************************
============
Introduction
============
The primary goals of the ECL project were to
o add an error-handling capability to the existing IRAF CL,
o include other functionality which could improve the
scripting environment (e.g. pre-defined language constants
such as 'PI') and add any other features we found lacking
(e.g. missing trig functions and string utilities), and
o add commonly requested features.
Where possible, small enhancements such as a new utility builtin function
will be implemented in the "old" CL as well, however as scripts begin to
use the more advanced features scripts will naturally become less backward
compatible. Future work will build on the version presented here with
the hope that users will migrate to the new system over a short time.
This is a work in progress. Users are encouraged to experiment with
features, request future enhancements, and to please report any errors or
problems to
iraf@noao.edu
New releases will be announced on the IRAF website (http://iraf.noao.edu)
following the addition of any new features or when critical bugs have been
fixed.
====================
Installation and Use
====================
The ECL is being distributed in a self-extracting script file
rather than the traditional IRAF external package since it is meant to
overlay an existing IRAF system until the time when it becomes part of
the core distribution. Since the script creates a new command link in
the unix system "local bin directory" and adds files to the IRAF source
tree, it MUST be run as the root user (the script will terminate or ask
if you wish to proceed with a no-op installation otherwise).
The installation script does the following to your system:
1) Replaces the existing hlib$cl.csh script with a modified
version after creating a hlib$cl.csh.ORIG backup file
2) Creates an "ecl" command link in the same directory as the
current "cl" IRAF command link. Both links point to the same
hlib$cl.csh script which checks for how it was called an
invokes the proper binary.
3) Moves the "ecl.e" binary to the proper iraf$bin.<arch> directory,
changing the ownership to the 'iraf' user and setting the execute
permissions on the file.
4) Creates a iraf$pkg/ecl directory and moves all ECL sources there.
The install script may be run from any directory on the system, it is
unpacked in /tmp and cleans up temp files when complete. A "personal
installation" option is not implemented at this time but could be considered
later for users who don't have write permission on their IRAF tree. Please
contact iraf@noao.edu for instructions on how to manually setup such a
system for personal use.
To Install the ECL
------------------
Step 1) Download the distribution file appropriate for your system. For
example,
% ftp iraf.noao.edu (140.252.1.1)
login: anonymous
password: [your email address]
ftp> cd pub
ftp> binary
ftp> get ecl_install_redhat.csh
ftp> quit
Step 2) Execute the script AS ROOT:
% su # become the root user
# ./ecl_install_redhat.csh
The script will prompt you for the local bin directory or any
iraf paths needed, simply accept the default values determined for
your system or override them with others.
Once executed, the ECL source and binaries will be installed in
the system as described above. The file you are reading right
now is available as iraf$pkg/ecl/Notes.ecl and will be updated
with post-release notes at the end of the file with each new
release.
Step 3) Start the ECL from your normal IRAF login directory as either
% ecl
or
% cl -ecl
The second form of the command is needed on systems which mount
IRAF from another machine since the CL command links are created
at IRAF install time. One reason for replacing the hlib$cl.csh
script is to allow for the "-ecl" argument to override the binary
to be used on systems where only the 'cl' command is available and
so that the installation isn't required on all machines mounting
a common IRAF.
The default ECL prompt is now "ecl>" in the new version as a visual
clue that the new system is being used. Additionally, package prompts
default to using the complete package name rather than the familiar
2-character prefix as another clue. This behavior can be changed
by adding the string "nolongprompt" to the CL 'ehinit' parameter,
e.g.
cl> cl.ehinit = cl.ehinit // " nolongprompt"
Except as described below, use of the ECL should be identical to the
traditional CL for most users.
Determining CL Version
----------------------
As users begin to make regular use of features found only in the
ECL, the first error to be checked is that the script is running using the
proper version of the CL. This needs to be done using features found in
both the ECL and traditional CL languages. The simplest test, for either
package loading scripts or within tasks, is something like
if (defpar ("$errno")) {
print ("You are using the ECL")
} else {
print ("You are using the old CL")
}
==============
Error Handling
==============
Introduction and Cautions
=========================
The error-handling enhancements are composed of two elements:
o the reporting of errors within scripts, and
o the ability to trap and recover those errors.
The first case addresses the long-standing problem in which an error message
returned by a script gives a line number that has no basis in reality, and
which gives no useful information about the underlying task that created it.
In the second case, one often wants scripts to be able to trap errors from
compiled tasks so that some sort of cleanup can be done in order to allow
the script to continue, or so that an error status code can be examined
and some specific action taken (which may simply be to ignore the error).
In the ECL, messages are now printed with the correct line number and
with a detailed traceback to the user's command-line showing more precisely
what was called at the time of the error. New language constructs are
available which allow scripts to conditionally check for errors from
tasks they call and branch to code to deal with those errors. Finally,
new ECL environment variables and builtin functions allow for limited
error-handling control over scripts already in the system which have not
been retrofitted to specifically trap errors. Details of each of these
capabilities and examples of how they may be used by developers and users
are given below. It is also worth discussing the types of errors which
can occur in a script task before getting into details about how they
might be handled by the user or script programmer.
Error conditions in the CL break down into roughly the following types:
Error Type Examples
---------- --------
Compiled Task Errors 1) A call to a compiled task in the system
dies unexpectedly with an exception (e.g.
FPE, segmentation violation, etc)
2) A task aborts due to an error condition the
task has trapped and cannot recover (e.g.
invalid parameters, out of memory, etc).
CL Internal Errors 1) Script code performs an illegal operation
causing an exception (e.g. "i = j / k"
where 'k' is zero.
2) Script code triggers a runtime error within
the CL itself (e.g. "log (string_value)")
CL Error Assertions 1) Script programmer forces the task to exit
with a call to the CL error() builtin.
2) Script programmer simply prints and error
message indicating a problem and returns
without further processing.
All of these errors can be detected at some level, however not all of
them can be handled in a way which allows a calling script to recover
and continue executing, nor would it always make sense to do so.
Errors such as a floating-point-exception (FPE) may be data-dependent,
a segmentation violation may indicate a coding error in a compiled task
or a platform-specific bug, or an error in another script task may be
beyond the control of the scripter to fix. Error assertions by a script
programmer are not meant to be recoverable, and in the second example
an arbitrary problem message cannot be trapped by the system.
An error-handling capability in the ECL (or any language) is not a
panacea for all error conditions one might encounter, the best a script
programmer can hope to do is to trap an error and take some reasonable
action at the time. The ECL offers a way for a script to print a more
meaningful error message, or at least abort gracefully after cleaning
itself up. However, depending on the type of error, *your* script may
still never run to completion until somebody else fixes *their* code.
Lastly, it is also important to note that trapping an error means the
script finds itself in an unnatural state. Proper recovery requires
that the script programmer understand the error condition as well as
the state of the script at that point of execution. The error-handling
code must restore the script to a state where it can continue running
(if possible) and avoid potential side-effects caused by e.g. forgetting
to clean up intermediate files or reset counter variables. New language
features mean new types of bugs can be introduced into a script, even if
the irony is that these new features are meant to trap bugs!
Example Descriptions
--------------------
In the examples to follow we will make use of an ERRTEST package
distributed with the ECL source and containing the following tasks used
in the examples to follow:
nested -- Test various error conditions from layered scripts
nest0 -- Dummy layer for nested testing
errtype -- Low-level script to test compiled and CL error conditions
fpe -- Compiled task producing an arithmetic exception
segvio -- Compiled task producing a segmentation violation
spperr -- Compiled task invoking the SPP error() function
Reporting of Errors
===================
Traceback
---------
The most obvious change to users will be in the traceback of errors
reported by the ECL. As an example, suppose we have a test script
called NESTED that calls several layers of other scripts until it gets
to a compiled task called FPE which simply triggers a divide-by-zero
arithmetic exception. The calling sequence we use is
NESTED (type) # toplevel test task
NEST0 (type) # hidden script task
ERRTYPE (type) # script task
FPE () # compiled task giving the error
(The 'type' argument here is a code used to test various types of system
errors but its value isn't important to the current discussion.) In the
traditional CL, executing this script results in the following and familiar
message:
cl> nested 1
ERROR on line 72: floating point divide by zero
errtype (type=1)
nested (type=1)
There are a number of issues with the error report here we wish to correct:
1) The error is reported to be on line 72, but none of the scripts
called invoke any task on that line, or even have that many lines,
and so it is clearly wrong.
2) Was it the ERRTYPE script that caused an error or something else?
3) There is no mention of the FPE task we know to be the culprit.
These problems are resolved in the ECL where the error report now looks like:
cl> nested 1
ERROR: floating point divide by zero
"fpe ()"
line 15: errtest$errtype.cl
called as: `errtype (type=1)'
"errtype (type)"
line 13: errtest$nest0.cl (hidden task)
called as: `nest0 (type=1)'
"nest0 (type)"
line 11: errtest$nested.cl
called as: `nested (type=1)'
The traceback is more complete and begins with the task which actually
throws the error. Checking the line numbers of the ERRTEST package
scripts we find that indeed FPE is called on line 15 of 'errtype.cl',
ERRTYPE is called from line 13 of 'nest0.cl', and so on.
For each task in the calling sequence the format of the traceback is
<script code fragment executing at the time of error>
LINE <number>: <script file containing line>
CALLED AS: <how this script was called>
The length of the traceback may be controlled with the new 'erract'
environment variable discussed in more detail below. In short, 'erract'
allows the traceback to be suppressed entirely, to print information only
at the level where the error occurred, or to print a full calling stack
trace (default).
Trapping Errors
===================
The 'iferr' Syntax
------------------
The ECL provides new language constructs to enable error actions, error
handling and recovery. This syntax will already be familiar to SPP programmers
and will quickly become obvious to even novice script programmers.
Error recovery is implemented using the IFERR and IFNOERR statements
to "post" an error handler that is called at the end of a block of code and
which checks for error conditions that may have occurred in that block.
The syntax for these statements is of the form:
iferr { <statement> } ifnoerr { <statement> }
<error action statement> <success action statement>
iferr { ifnoerr {
<block of statements> <block of statements>
} then } then
<error action statement> <success action statement>
iferr { ifnoerr {
<block of statements> <block of statements>
} then { } then {
<block of error stmts> <block of success action stmts>
} }
The IFERR is grammatically equivalent to the IF statement and means "if an
error occurs during the processing of the enclosed code, execute the error
action statement to follow". IFNOERR is the same except that the sense
of the test is reversed and the action statements are executed only if the
enclosed code completes without error. Additionally, these statements take
an ELSE clause allowing both forms of the test to be combined. For example,
iferr { ifnoerr {
<block of statements> <block of statements>
} then { } then {
<error stmts> <success stmts>
} else { } else {
<success stmts> <error stmts>
} }
In all cases
o Curly braces around the code to be checked are required,
o Curly braces are required when any action is a compound block
o The THEN statement is optional if a single statement is executed
as part of the action block
o The THEN statement is required for a compound action or when using
an ELSE clause
o It is a syntax error for a condition block to itself directly contain
an IFERR or IFNOERR statement and action statements, i.e. IFERR
statements may not be nested
To make effective use of these statements a few points need to be
kept in mind:
o The check for errors happens only after ALL statements in the
condition block are executed;
o Statements which generate errors did not execute to completion,
subsequent code relying on that result cannot be trusted
o Code in the condition block which executes following an initial
error may itself trigger errors due to the failure of a previous
statement or the resulting side-effects;
This implies that IFERR statements should be used to target only critical
pieces of code where a particular error condition might be expected, and/or
where an action block could reasonably react to that error. As an example
of how ignoring these points could be problematic consider the code snippet:
iferr {
task_a ()
task_b () | scan (x)
task_c (x)
} then {
error (99, "An error occurred\n")
}
All three tasks in the condition block will be executed, however the
behavior of the code being check depends on which task in the block fails;
If 'task_a' fails there may be no consequences for the remaining calls,
however if 'task_b' fails the value of 'x' may never be set and 'task_c'
may also fail (or at least produce spurious results). Cascading errors like
this will also be trapped and the action statement will still execute, but
the system error message strings will be incomplete (more about that below).
While it is possible to have a failure from each statement in a condition
block branch immediately to the action block by checking each statement
individually, doing so would permit poor programming practices such as
iteratively testing for the name of the failed task and taking different
recovery methods in the action block. If this is actually required for the
script to recover cleanly, the recommended method is to put an IFERR block
around smaller pieces of code where the recovery statements relate more
directly to the code being checked.
Errors trapped by IFERR statements include:
o System exceptions (FPE, segfault, etc) thrown by compiled tasks
o SPP error() returns from compiled tasks
o CL script error() assertions
Below we discuss errors which cannot be trapped using the IFERR syntax as
well as strategies for how to handle those errors which can be detected.
We'll also see how to determine which task in a condition block failed
and why.
The 'erract' Environment Variable
----------------------------------
The ECL has a new 'erract' environment variable used to control the
different aspects of the error handling. This is a whitespace-delimited
string comprised of the following options:
abort Script task should abort at an error and begin error
recovery back to the command-line
noabort Task should not abort, but continue execution if possible
trace Print a traceback of the calling sequence including all
line numbers and calling statements
notrace Print only the error message, no linenumbers or calls
clear Clear the error params (i.e. $errmsg, $errnum, $errtask)
at each new task call. This reseets the params with each
task invocation allowing them to be examined after each
call regardless of whether the code is in an IFERR block.
noclear Do not clear the CL error params at each new task call,
the params are only reset when an error is encountered.
flpr Automatically issue a 'flpr' when an error is seen. This
is used to flush any failed task from the process cache to
avoid potential future problems caused by a corrupted task.
noflpr Do not issue a 'flpr' when an error is seen, tasks remain
in the process cache, possibly in an error state.
full Print a complete traceback of the calling sequence.
nofull Print only the error report for the task causing the error
and none of its parents.
The default value is set as:
set erract = "abort trace flpr clear full"
Note that erract is implemented as an environment variable rather than
as a new CL parameter (similar to the ehinit/epinit params) in order to
minimize changes in the CL parameter file itself during the transition
to the ECL. The difference is that the 'set' (ore 'reset') command must
be used to define the values, whereas with ehinit/epinit they may be
assigned directly. For this variable it is also possible to (re)define
a single parameter without affecting other options, e.g.
cl> show erract # print options
abort trace flpr clear full
cl> set erract = "noabort" # reset one of them
cl> show erract # print options again
noabort trace flpr clear full
Error Handling: Then and Now
----------------------------
To better understand the new error detection and recovery behavior
(and to document this for future reference), let's look at the old error
mechanisms of the CL language: Any command called from the CL executes in a
context defined by the task hierarchy initiating the command, i.e. from the
command-line CL prompt one has a "first" task context, scripts calling child
(compiled or script) tasks push a new CL context inheriting the current
CL environment and who's 'parent' is the context that invoked the task.
In the traditional CL with an error occuring in a compiled task,
recovery first takes place in the SPP code who may choose to either handle
the error itself or may abort completely by doing a long-jump back to the
IRAF main() procedure (i.e. an EA_FATAL error type). In this latter case,
the process binary (running as a detached process from the CL) sends an
error() command back to the CL telling it the task has terminated abnormally
(a normal task shutdown leaves the executable simply waiting for more input
from the CL, e.g. another task to execute). This returned error() statement
is the same CL error() command one would use to abort a script task, and its
effect is to tell the CL to abort the current context and long-jump back
to the command-line after cleaning up running processes and freeing the
dictionary space (what the CL uses to catalog tasks/packages, parameters,
etc). [NOTE: Whether it is a system exception or a programmer-posted error,
the error sent back to the CL has always included both the error code and
message, it is just that the CL has never made use of these until now.]
Similarly, errors which occur while running script tasks (e.g. 'task not
found' errors, invalid use of string values, divide-by-zero from local
script variables, etc) also end up in the same CL error() procedure via
internal procedure calls made while executing the script.
Syntax errors are caught when the script is 'compiled' into the opcode
execution stack and are reported before the script begins to execute.
A script calling a child script containing a syntax error cannot trap
that error even though it will not be reported until the child script is
'compiled' just prior to execution. We assume that all script tasks are
well-formed and free of ntax errors.
ECL error recovery is somewhat simplified by the fact that errors,
either from external tasks or the execution of scripts, all converge in
a single procedure in the CL source code. The trick is to modify the
runtime behavior of the CL so that once we know we have an error we can
branch to conditional code instead of simply jumping all the way back to the
command line. Since we also wish to improve the error reporting we'd also
like make better use of information about how the failed code was called.
The first step is to realize that when executing a script the CL
language is "compiled" into a series of 'opcode' instructions comprising an
intermediate runtime language (similar to assembly language). Scripts are
run by executing the opcode instruction at the current 'program counter'
location, pushing task arguments or getting labels for jumps from the
dictionary, restoring a previous CL context, etc. The compilation stage
already has information about the script line being parsed so by adding
this line-number to the opcode instruction it is now possible to trace a
fault in any opcode back to the originating line of the script, and from
there back up to the command line through the calling tree. This extra
information makes the runtime size of the script slightly larger so
extremely large scripts may experience "dictionary full" problems not
previously seen (various CL buffer sizes were increased to help offset
this problem). This relatively minor change is all that is required to
address the problems mentioned above in error reporting.
Error trapping and recovery is done in a manner similar to the
implementation in SPP: The IFERR statement isn't actually an instruction
in the runtime script, rather it is used to tell the parser to insert
code around the block to be checked using traditional IF statements.
As an example, consider
iferr {
task1 (arg1, arg2)
task2 (arg1)
} then {
recovery ()
}
When compiled this is the equivalent of writing
_errpsh ()
task1 (arg1, arg2)
task2 (arg1)
if (_errpop () != 0) {
recovery ()
}
The _errpsh() is a hidden builtin function which "pushes" an error
structure onto the runtime stack, the _errpop() test at the end then
queries that structure to see whether any statement since the previous
push set the error flag and filled in the structure with the task name,
line number and other information. The push also temporarily deactivates
the behavior of the error() function so it no longer aborts entirely,
allowing the script to continue after cleaning up the current error.
In order to keep the model simple, nested iferr statements within
the same script are not currently implemented but are a possible future
enhancement. Complications arise from examples such as
iferr {
task1 (arg1, arg2)
iferr { task2 (arg1) } then
recovery2 ()
} then {
recovery1 ()
}
Consider the case where task1() succeeds and task2() fails and is
recovered properly with the recovery2() procedure. As far as the outer
IFERR block is concerned, did an error occur or not? If the remainder of
the script depends on task2() succeeding then the answer is possibly 'no'
(depending on what the recovery does) and we should additionally call
the recovery1() procedure (who is responsible for dealing with an error
condition in that block), if there is no dependency then we may want
*any* failure to be considered, or perhaps even have a way to "clear"
error conditions within the block. Now assume instead it is the first
task which fails and that triggers the second to fail because we depend
on the first succeeding, how should we post the error number/message for
the script? We simply disallow nested IFERR statements for the moment
to avoid dealing with these complex interactions
New CL parameters
===================
On order for script programmers to make use of errors that have
been trapped by the ECL, one generally needs access to the details of
that error, e.g. the message, task name, error number, etc. To this end
the ECL implements several new pseudo-parameters and builtin functions
containing this information. These include
Param Function Meaning
----- -------- -------
$errno errno() The system error number
$errmsg errmsg() The system error message string
$errtask errtask() Task which created the error
By default these parameters are re-defined as each task is called, in theory
allowing a script to trap errors without the IFERR by doing something like
mytask1 ()
if ($errno != 0) <statement>
mytask2 ()
if ($errno != 0) <statement>
:
This behavior can be modified by the 'erract' environment variable 'clear'
or 'noclear' settings so that they only change when an error condition is
found (i.e. erract set to 'noclear', tasks which complete successfully
do not modify variables).
Additionally, a new $err_dzvalue pseudo-parameter is defined to
be used by the CL interpreter when a divide-by-zero condition is encountered
in the CL itself. (This value has no builtin function equivalent.)
This is an integer and will be cast to floating-point automatically if
needed, the default value of 1 (one) was chosen to allow the script to
continue executing but it should be noted that this value is only used
when an error is found within an IFERR block. For example,
ecl> = 1 / 0
ERROR: integer divide by zero
ecl> = 1. / 0.
ERROR: floating divide by zero
However,
ecl> iferr {
>>> = 1 / 0
>>> } then ;;
Warning on line 31 of : integer divide by zero - using $err_dzvalue = 1
1
Note the warning message indicating the use of the parameter followed by the
result.
What Errors Are NOT Trapped
===========================
As mentioned above, not all CL errors can or should be trapped
by the new system. The (incomplete) list of error conditions which
CANNOT be trapped during task execution using the IFERR or other new
features includes:
o CL-language syntax errors
o CL internal errors, for example
- invalid procedure arguments (e.g. "parameter not found")
- improper usage of intrinsic procedures (e.g. log(-10) )
- operand type mis-matches (e.g. "s1 + x")
- parser errors (e.g. newline in string)
o CL runtime errors
- too many background jobs (e.g. "no available job slots")
- insufficient resource messages (e.g. out of memory)
- can't read/write/create files (e.g. permissions problem on uparm$)
- ambiguous task name
- scan/print string exceeds max length
o User-defined error messages and returns (i.e. the script writer
outputs an error message and returns from the procedure but
does not use something like thea CL error() function to abort.
For instance, a script prints "I can't run this on Tuesdays" and
returns to the command-line but does not otherwise post an error
condition for the calling context.
============================================
Command-line History and BackSpace Revisions
============================================
The ECL now implements the common GNU Readline interface for input
handing meaning that many familiar tcsh-like features such as Up/Down-Arrow
history, Left/Right cursor-position movement, and tab-filename completion
are now understood in the IRAF environment. It follows that many of
the problems encountered with the DEL/BS key to erase characters when
entering input on the commandline have also been eliminated on most
systems since the readline interface internally handles the delete-key
mappings imposed on most systems. Tab-completion of task/params names
was not implemented in this initial release but could be added later.
It is important to note that this implementation was done so as
to not interfere with the native IRAF ehist/epar cursor and history
mechanism. From the ECL prompt, all commands recognized by readline()
interface (including user mappings defined in an ".inputrc" file) will
be honored. If that command is ehist/epar or one of the recognized
IRAF history editing metacharacters then these will be processed in the
traditional IRAF manner.
Should a problem with readline input be found, it can be disabled
from the user's session by adding the string "noreadline" to the CL
'ehinit' parameter, e.g.
ecl> cl.ehinit = cl.ehinit // " noreadline"
Input Command Summary
---------------------
The following Control/Meta key sequences are understood by the
readline() interface for command input:
Basic Commands
Ctrl-b Move cursor back one character.
Ctrl-f Move cursor forward one character.
DEL Delete the character to the left of the cursor.
Backspace Delete the character to the left of the cursor.
Ctrl-d Delete the character underneath the cursor.
Ctrl-_ Undo the last editing command
Ctrl-x Ctrl-u Undo the last editing command
Up-Arrow Move up through the command-history list
Down-Arrow Move down through the command-history list
Left-Arrow Move cursor left one character on command line
Right-Arrow Move cursor right one character on command line
Cursor Movement Commands
Ctrl-a Move to the start of the line.
Ctrl-e Move to the end of the line.
Meta-f Move forward a word, where a word is composed of letters/digits.
Meta-b Move backward a word.
Ctrl-l Clear the screen, reprinting the current line at the top.
Text Deletion Commands
Ctrl-k Kill the text from the current cursor position to the end of
the line.
Meta-d Kill from the cursor to the end of the current word, or, if
between words, to the end of the next word. Word boundaries
are the same as those used by Meta-f.
Meta-DEL Kill from the cursor the start of the current word, or, if
between words, to the start of the previous word. Word
boundaries are the same as those used by Meta-b.
Ctrl-w Kill from the cursor to the previous whitespace. This is
different than Meta-DEL because the word boundaries differ.
To yank (copy the most-recently-killed text from the kill buffer) the text
back into the line:
Ctrl-y Yank the most recently killed text back into the buffer at
the cursor.
Meta-y Rotate the kill-ring, and yank the new top. You can only do
this if the prior command is Ctrl-y or Meta-y.
History Searching Commands
Ctrl-r Search backward through the history for a particular string
Ctrl-s Search forward through the history for a particular string
ESC Terminate the search
Ctrl-g Terminate the search and restore original line
As each character of the search string is typed, Readline displays
the next entry from the history matching the string typed so far. An
incremental search requires only as many characters as needed to
find the desired history entry. To find other matching entries in
the history list, type Ctrl-r or Ctrl-s as appropriate from the current
search position.
NOTE: In many terminal settings the Ctrl-s key is mapped to the tty
'stop' character and the window will appear to no longer accept
input. In these cases a Ctrl-q will normally return the terminal
to proper function and so the forward search mechanism isn't
generally recommended.
=====================
New Builtin Functions
=====================
Error-Handling Functions
------------------------
The following builtin functions were added as alternatives to the
matching CL parameters. The difference is almost entirely stylistic and
the rules about the longevity of the values described above apply in either
case.
errmsg () Return last error message string (i.e. cl.$errmsg)
errcode () Return last integer error code (i.e. cl.$errno)
errtask () Return taskname posting fatal error (i.e. cl.$errtask)
Examples:
iferr {
sometask (par1, ....)
} then {
printf ("Error in '%s': %s\n", errtask(), errmsg())
# or equivalently
printf ("Error in '%s': %s\n", $errtask, $errmsg)
}
String Functions
----------------
Beginning with V2.12.2 several new functions were added to the
CL to improve string handling and the provide complementary functions
to those which already exist. Items marked with a '*' first appeared in
V2.12.2, all others are new to this release.
New functions include:
isindef (expr) (*)
Can be used to check for INDEF values in expressions. INDEF
values may be tested for equality, however when otherwise used
in a boolean expression the result of the boolean is also
INDEF. This function can be used to trap this particular
case, or for INDEF strings/variable directly. Result is a
boolean yes/no.
Example:
cl> junk = fscan (threshold, tval)
cl> if (isindef (tval) == yes)
error (0, "INDEF 'threshold' parameter value")
strlwr (str) (*)
strupr (str) (*)
Convert the string to lower/upper case, returns a string.
Example:
cl> s1 = "test" ; s2 = "TEST"
cl> = strupr (s1) ; = strlwr (s2)
TEST
test
strstr (str1, str2) (*)
Search for first occurance of 'str1' in 'str2', returns index
of the start of 'str1' or zero if not found.
Example:
cl> = strstr ("imh", "imhead.imh")
1
cl> = strstr ("head", "imhead.imh")
3
strldx (chars, str) (*)
Complement to the stridx() which returns the last occurance of
any of 'chars' in 'str'. Returns index of last char or zero
if not found.
Example:
cl> = strldx (".", "junk.fits")
5
strlstr (str1, str2) (*)
Search for last occurance of 'str1' in 'str2', returns index
of the start of 'str1' or zero if not found.
Example:
cl> = strlstr ("imh", "imhead.imh")
8
[NOTE: String indices are 1-indexed in the CL]
trim (str [, trimchars])
triml (str [, trimchars])
trimr (str [, trimchars])
Trim any of the chars in 'trimchars' from the ends of 'str'.
The trim() function removes chars from both the front and back
of the string, triml() removes only from the left side of the
string, and trimr() removes only from the right side. If the
'trimchars' argument is not specified the whitespace chars
(tab and space) are assumed.
Example:
cl> printf ("'%s'\n", trim (" test "))
'test'
cl> = trimr ("/iraf/iraf///////", "/")
/iraf/iraf
To check for strings containing only whitespace:
if (trim (foo) == "")
error (0, "no legal value specified for 'foo'")
The new string functions are particularly useful for dealing with
pathnames where one needs to find and extension, separate a file from a
path prefix, trim trailing slashes. and so on.
Additionally, the existing substr() function has been modified to
allow a 'last' index greater than a 'first' index, in which case the return
string is reversed.
Trig Functions
--------------
The following trigonometric functions have been added as new builtins
to the CL. These complement existing functions as well as provide utility
versions to simplify degree/radian conversion.
asin (arg) Inverse SIN, result in radians
acos (arg) Inverse COS, result in radians
rad (rad_arg) Convert arg in radians to degrees
deg (deg_arg) Convert arg in degrees to radians
dsin (deg_arg) Sine function, arg in degrees
dcos (deg_arg) Cosine function, arg in degrees
dtan (deg_arg) Tangent function, arg in degrees
dasin (arg) Inverse sine function, result in degrees
dacos (arg) Inverse cosine function, result in degrees
datan2 (y, x) Inverse tangent function, result in degrees
Utility Functions
-----------------
The following utility functions have been added.
fp_equal (arg1, arg2) Floating point compare (w/in machine precision)
hypot (x, y) Euclidean distance (i.e. sqrt (x*x + y*y))
sign (arg) Sign of argument (-1 or 1)
Examples:
cl> = fp_equal (1.2345, 1.234)
0
cl> = hypot (3, 4) # may also take real arguments
5
cl> = sign (-23) # may also take real arguments
-1
Bitwise Operations
------------------
The following bitwise operands have been added in the V2.12.2b.
Note that these are bitwise operands and not logical operands. While there
is presently no direct need for these they are seen as potentially useful
in e.g. evaluating bit-flags stored in image header keywords and support the
goal of providing a richer scripting language.
not (arg1) Bitwise boolean NOT of an integer
and (arg1, arg2) Bitwise boolean AND of two integers
or (arg1, arg2) Bitwise boolean OR of two integers
xor (arg1, arg2) Bitwise exclusive OR of two integers
Examples:
cl> = radix (12, 2) # print bit pattern of number 12
1100
cl> = radix (13, 2) # print bit pattern of number 13
1101
cl> = and (12, 13) # 1100 & 1101 == 1100
12
cl> = or (12, 13) # 1100 | 1101 == 1101
13
cl> = xor (12, 13) # (1100 & ~1101) | (~1100 & 1101) == 1
1
cl> = not (12)
-13
cl> = radix (not(12), 2)
11111111111111111111111111110011
=================
Defined Constants
=================
The ECL also introduces the ability to use common numerical and
physical constants in scripts as part of the language keyword set. Constants
are, by convention, always upper case identifiers and are listed in the table
below:
Numerical constants
+---------------------------------------------------------------------+
| Name | Value | Units |
+---------------------------------------------------------------------+
| BASE_E | 2.7182818284590452353 | |
| FOURPI | 12.566370614359172953 | |
| GAMMA | .57721566490153286061 | |
| HALFPI | 1.5707963267948966192 | |
| LN_10 | 2.3025850929940456840 | |
| LN_2 | .69314718055994530942 | |
| LN_PI | 1.1447298858494001741 | |
| LOG_E | .43429448190325182765 | |
| PI | 3.1415926535897932385 | |
| RADIAN | 57.295779513082320877 | |
| SQRTOF2 | 1.4142135623730950488 | |
| SQRTOFPI | 1.7724538509055160273 | |
| TWOPI | 6.2831853071795864769 | |
+---------------------------------------------------------------------+
Physical constants
+---------------------------------------------------------------------+
| Name | Value | Units |
+---------------------------------------------------------------------+
| AU | 1.49597870691e11 | m |
| GRAV_ACCEL | 9.80665e0 | m / sec^2 |
| GRAV_CONST | 6.673e-11 | m^3 / kg s^2 |
| LIGHT_YEAR | 9.46053620707e15 | m |
| PARSEC | 3.08567758135e16 | m |
| SPEED_OF_LIGHT | 299792458.0 | m / sec |
| SOLAR_MASS | 1.98892e30 | kg |
+---------------------------------------------------------------------+
For example, these may be used in scripts as:
area = (PI * radius ** 2) # Compute area of circle.
rad = degrees / RADIAN # Convert degrees to radians
===============================================================================
# Post-Release Notes
===============================================================================
|