INTERNALS 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. 1. Language Implementation Details
  2. The ode input language is implemented with a bison-generated parser and a
  3. flex-generated lexical analyzer. The benefits of this approach are great.
  4. The original version of the basic recognizer was completed in about two
  5. days. And adding comments to the language required that exactly one line
  6. of the lexical analyzer source code be changed.
  7. Differential equations are compiled into linked lists of stack operations,
  8. one list per dynamic variable. The function evaluator (eval) traverses one
  9. of these lists, applying the indicated operations to a small operand stack.
  10. At the completion of the traverse, the new derivative is on top of the
  11. stack. The routine pops and returns this result. Another routine (field)
  12. is called by the numerical routines whenever a re-evaluation of the
  13. gradient is required. It calls eval() for each dynamic variable in turn.
  14. Changes to the language involve modifications to the bison grammar, the
  15. flex rules, and/or the semantics stored with the grammar and rules. The
  16. steps required to add a new builtin function, for example, are (aside from
  17. writing the function itself) as follows.
  18. (1) Invent a new terminal symbol name for the reserved word.
  19. (2) Add the new reserved word to the flex rules.
  20. (3) Add the new productions to the right-hand side of
  21. "expr" in the grammar, together with whatever semantic
  22. action is required (using the included macros if possible).
  23. (4) Define a new value for exper and add the stack code to eval.
  24. 2. Numerical Schemes
  25. The numerical and language procedures were designed in accordance with the
  26. Jesuit maxim. The major elements manipulated by both are the symbol table,
  27. and the functions field() and printq(). Addition of a new numerical scheme
  28. requires that the numerical routine be written, and perhaps that some
  29. fields inside the symbol table entry be expanded or added. None of these
  30. changes affects the performance or compromise the integrity of the parser,
  31. although they may change the space-cost of a dynamic variable. Similarly,
  32. modifications to the lexical analyzer or the grammar only affect the parse
  33. tables and related data structures, and have no effect on the numerical
  34. routines.
  35. At each step the numerical scheme fills each symbol table entry with
  36. relevant values. Then a call to field() evaluates every differential
  37. equation and puts the result in the symbol table element syrime. The
  38. evaluator gets current values for the dynamics from the field syalue. Both
  39. syalue and syrime are in some sense temporary variables. All correct
  40. results eventually find their way into syal[0] and syri[0]; these are never
  41. touched by the language processor. Thus the real interface between the
  42. language and numerical procedures is merely syalue and syrime. It is
  43. important to maintain meaningful values in syalue and syrime, since a
  44. floating-point exception may disrupt the control flow at any moment. It is
  45. recommended that the pointer fsp be used in traversing the symbol table, so
  46. that arithmetic exceptions can be diagnosed as accurately as possible.
  47. Fourth and fifth order schemes were chosen as the best compromise between
  48. speed and accuracy. The adaptive stepsize has proved a boon in both areas.
  49. 3. Details of Error and Fault Recovery
  50. Command-line errors are diagnosed at initialization time and are generally
  51. fatal.
  52. Errors in the input stream are diagnosed during the lexical scan or at
  53. parse time. They elicit an appropriate message. The error recovery method
  54. used in the parser is the vanilla Yacc scheme. (see S. Johnson, "Yacc: Yet
  55. Another Compiler-Compiler", 1978, pp. 16-18).
  56. Run-time errors are detected by the function evaluator, the numerical
  57. routines, or the floating-point unit hardware. In each case a message is
  58. printed describing the error, and ode returns to a known state awaiting
  59. input. The current step is abandoned, but all variable values are left
  60. reflecting the state of the machine just prior to the fault. All of the
  61. numerical schemes scan the symbol table, computing values for each dynamic
  62. variable, using a common pointer known to the error recovery routines named
  63. fsp. This pointer provides a way of identifying the offending dynamic
  64. variable when a trap occurs.
  65. 4. Space Utilization
  66. The data space used in numerically solving a system of ODEs consists of
  67. both fixed and dynamic regions. The fixed space is used efficiently
  68. regardless of the complexity of the problem being solved. The dynamic
  69. space contains the symbol table, the expression lists, and several other
  70. data structures whose sizes vary. The space for these structures comes out
  71. of a common pool; they play against each other.
  72. Every identifier in an ODE description requires 254 bytes; this space can
  73. never be reclaimed. Each operator in an equation costs 14 bytes. For the
  74. sake of speed, some operators are expanded into a sequence of simpler
  75. operations, each of which requires 14 bytes. In particular, some common
  76. powers are reduced to sequences of square roots, squares, cubes, and
  77. inversions. The extent of these transformations can be seen with the help
  78. of the `examine' statement. The operator space for an equation is
  79. reclaimed whenever a new equation is defined for that particular dynamic
  80. variable. Each lexical token in the most complex statement costs 10 bytes,
  81. but this space is reclaimed during the scan. Thus, the most complex
  82. statement circumscribes this requirement. Each element in a `print'
  83. statement costs 6 bytes. Upon execution of the next `print' statement this
  84. space is reclaimed.
  85. In addition to these absolute space limitations, the maximum depth of the
  86. operand stack used for evaluating derivatives is 30 cells. This limit
  87. could conceivably be exceeded for a very complex differential equation.
  88. The solution is to simplify the equation if possible, and where not, to
  89. recompile ode with a larger operand stack.