home *** CD-ROM | disk | FTP | other *** search
/ ftp.cs.arizona.edu / ftp.cs.arizona.edu.tar / ftp.cs.arizona.edu / icon / historic / v941.tgz / icon.v941src.tar / icon.v941src / ipl / packs / itweak / dbg_run.icn next >
Text File  |  2000-07-29  |  67KB  |  2,291 lines

  1. ############################################################################
  2. #
  3. #    File:     dbg_run.icn
  4. #
  5. #    Subject:  Icon interactive debugging.
  6. #          Contains an interactive debugging run-time system.
  7. #
  8. #    Author:   Hakan Soderstrom
  9. #
  10. #    Revision: $Revision: 2.21 $
  11. #
  12. ###########################################################################
  13. #
  14. # Copyright (c) 1994 Hakan Soderstrom and
  15. # Soderstrom Programvaruverkstad AB, Sweden
  16. # Permission to use, copy, modify, distribute, and sell this software
  17. # and its documentation for any purpose is hereby granted without fee,
  18. # provided that the above copyright notice and this permission notice
  19. # appear in all copies of the software and related documentation.
  20. # THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
  21. # EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
  22. # WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
  23. #
  24. # IN NO EVENT SHALL HAKAN SODERSTROM OR SODERSTROM PROGRAMVARUVERKSTAD
  25. # AB BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL
  26. # DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
  27. # OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY
  28. # OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN
  29. # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  30. #
  31. ###########################################################################
  32. #
  33. # General note: all names are prefixed in an elaborate way in order to
  34. # avoid name collisions with the debugged program.
  35. # The default prefix for all globally visible names is '__dbg_'.
  36. #
  37. # This is the reason why lists are frequently used instead of records
  38. # (whose field names clutter the global name space).
  39. #
  40. ###########################################################################
  41.  
  42. #
  43. #-------- Constants --------
  44. #
  45.  
  46. # Versions (this program and 'itweak').
  47. $define PROGRAM_VERSION    "$Revision: 2.21 $"
  48.  
  49. # Components of a breakpoint descriptor (list).
  50. # Breakpoint id (integer).
  51. $define BRKP_ID        1
  52. # Source file (string).
  53. $define BRKP_FILE    2
  54. # File index.
  55. $define BRKP_FIDX    3
  56. # First line number.
  57. $define BRKP_LINE1    4
  58. # Second line number.
  59. $define BRKP_LINE2    5
  60. # Ignore counter (integer).
  61. $define BRKP_IGNORE    6
  62. # Condition for breaking.
  63. $define BRKP_COND    7
  64. # Commands to perform on break.
  65. $define BRKP_DO        8
  66.  
  67. # Constants for 'the current breakpoint' and 'the last breakpoint'.
  68. $define BRKP_CURRENT    -1
  69. $define BRKP_LAST    -2
  70.  
  71. # Keywords for the 'clear' command.
  72. # Definitions must match list in compilation procedure.
  73. $define CLEAR_BREAKPOINT    1
  74. $define CLEAR_COND        2
  75. $define CLEAR_DO        3
  76. $define CLEAR_ECHO        4
  77. $define CLEAR_MACRO        5
  78.  
  79. # Keywords for the 'info' command.
  80. # Definitions must match list in compilation procedure.
  81. $define INFO_BREAKPOINT        1
  82. $define INFO_ECHO        2
  83. $define INFO_FILES        3
  84. $define INFO_GLOBALS        4
  85. $define INFO_LOCALS        5
  86. $define INFO_MACROS        6
  87. $define INFO_TRACE        7
  88. $define INFO_VERSION        8
  89.  
  90. # Keywords for the 'set' command.
  91. # Definitions must match list in compilation procedure.
  92. $define SET_ECHO        1
  93. $define SET_PRELUDE        2
  94. $define SET_POSTLUDE        3
  95.  
  96. # Components of a command definition (list).
  97. # Used for built-in commands as well as user-defined macros.
  98. # Unabbreviated command/macro name (string).
  99. $define CMD_NAME    1
  100. # Command code (an integer corresponding to the name).
  101. $define CMD_CODE    2
  102. # Help text (list of string).
  103. $define CMD_HELP    3
  104. # Compilation procedure; null if macro.
  105. $define CMD_COMPILE    4
  106. # Macro definition (list of command instances, list of list).
  107. # Null if built-in command.
  108. $define CMD_MACRO    5
  109. # Executing procedure, if built-in. Null otherwise.
  110. $define CMD_EXEC    6
  111.  
  112. # Command codes.
  113. $define BREAK_CMD    1
  114. $define CLEAR_CMD    2
  115. $define COMMENT_CMD    3
  116. $define CONDITION_CMD    4
  117. $define DO_CMD        5
  118. $define END_CMD        6
  119. $define EPRINT_CMD    7
  120. $define FAIL_CMD    8
  121. $define FPRINT_CMD    9
  122. $define FRAME_CMD    10
  123. $define GOON_CMD    11
  124. $define HELP_CMD    12
  125. $define INFO_CMD    13
  126. $define IGNORE_CMD    14
  127. $define MACRO_CMD    15
  128. $define NEXT_CMD    16
  129. $define PRINT_CMD    17
  130. $define SET_CMD        18
  131. $define SOURCE_CMD    19
  132. $define STOP_CMD    20
  133. $define TRACE_CMD    21
  134. $define WHERE_CMD    22
  135. $define USERDEF_CMD    23
  136.  
  137. # Environment variable for defining the input file (must be a string value).
  138. $define DBG_INPUT_ENV    "DBG_INPUT"
  139.  
  140. # Environment variable for defining the primary output file
  141. # (must be a string value).
  142. $define DBG_OUTPUT_ENV    "DBG_OUTPUT"
  143.  
  144. # Prefix for debugging run-time global names.
  145. $define DBG_PREFIX    "__dbg_"
  146.  
  147. # Maximum source nesting levels.
  148. $define MAX_SOURCE_NESTING    12
  149.  
  150. # File index is obtained by shifting a small integer left a number of
  151. # positions.
  152. $define FIDX_SHIFT    10
  153.  
  154. # Prompt string to use in initialization mode.
  155. $define INIT_PROMPT    "debug init $ "
  156.  
  157. # Execution return status.
  158. # Normal return.
  159. $define OK_STATUS    0
  160. # Break the command loop, resume execution.
  161. $define RESUME_STATUS    1
  162. # Break the command loop, terminate the session.
  163. $define STOP_STATUS    2
  164. # Break the command loop, make the current procedure fail.
  165. $define FAIL_STATUS    3
  166.  
  167. # Index into '__dbg_g_where'.
  168. $define WHERE_FILE    1
  169. $define WHERE_LINE    2
  170. $define WHERE_PROC    3
  171. $define WHERE_BRKP    4
  172. $define WHERE_PRELUDE    5
  173. $define WHERE_POSTLUDE    6
  174.  
  175. #
  176. #-------- Record types --------
  177. #
  178.  
  179. #
  180. #-------- Globals --------
  181. #
  182.  
  183. global __dbg_default_prelude, __dbg_default_postlude
  184. # The source text for the default pre/postlude (single command assumed).
  185.  
  186. global __dbg_g_automacro
  187. # The 'prelude' and 'postlude' macros.
  188. # List of two components:
  189. # (1) prelude commands,
  190. # (2) postlude commands.
  191. # Both are lists of compiled commands, not complete macros.
  192.  
  193. global __dbg_g_brkpcnt
  194. # Counter incremented each break.
  195. # Used to identify the file written by 'display' which is used by several
  196. # commands.
  197. # In this way we can check if we have to write the file anew.
  198.  
  199. global __dbg_g_brkpdef
  200. # Lookup table for breakpoints.
  201. # Entry key is a breakpoint id (integer).
  202. # Entry value is a breakpoint descriptor (list).
  203.  
  204. global __dbg_g_brlookup
  205. # Lookup table for breakpoints.
  206. # Entry key is a file index or'ed with a line number (integer).
  207. # Entry value is a breakpoint descriptor (list).
  208.  
  209. global __dbg_g_brkpid
  210. # Id of the latest breakpoint created (integer).
  211.  
  212. global __dbg_g_cmd
  213. # Table of command and macro definitions.
  214. # Entry key is an unabbreviated command/macro name.
  215. # Entry value is a command descriptor (list).
  216.  
  217. global __dbg_g_display
  218. # Name of temporary file used by '__dbg_x_opendisplay' and others.
  219.  
  220. global __dbg_g_fileidx
  221. # Table mapping source file names on (large) integers.
  222. # Entry key is a source file name (string).
  223. # Entry value is a file index (integer).
  224.  
  225. global __dbg_g_in
  226. # The file through which debugging input is taken.
  227.  
  228. global __dbg_g_level
  229. # Value of &level for the interrupted procedure.
  230. # Calculated as &level for the breakpoint procedure - 1.
  231.  
  232. global __dbg_g_local
  233. # Table containing local variables.
  234. # Entry key is variable name (string).
  235. # Entry value is the value of the variable (any type).
  236.  
  237. global __dbg_g_out1
  238. # Primary file for debugging output.
  239.  
  240. global __dbg_g_out2, __dbg_g_out2name
  241. # Secondary file for debugging output; used for 'set echo'.
  242. # Null when no echoing is not active.
  243. # The name of this file.
  244.  
  245. global  __dbg_g_src
  246. # Stack of input files used by the 'source' command (list of file).
  247. # Empty list when no 'source' command is active.
  248.  
  249. global __dbg_g_trace
  250. # Current trace level (passed to &trace when resuming execution).
  251.  
  252. global __dbg_g_where
  253. # A list with data about the current breakpoint.
  254. # Contents (symbolic names below):
  255. # (1) Source file name (string).
  256. # (2) Source line number (integer).
  257. # (3) Procedure name (string).
  258. # (4) The breakpoint causing this break (breakpoint descriptor, a list).
  259.  
  260. global __dbg_g_white
  261. # This program's definition of white space.
  262.  
  263. # A note on the use of global '__dbg_test' (defined in 'dbg_init.icn').
  264. # The runtime system assigns this variable one of the following values.
  265. # ** Function 'member' for ordinary testing against the breakpoint sets.
  266. # ** Function 'integer' (which is guaranteed to always fail, given a
  267. # set as its first parameter) in the 'nobreak' mode; execution continues
  268. # without break until the program completes.
  269. # ** Integer '2' which causes a break at every intercept point.
  270. # (Returns the second parameter which is the line number.)
  271.  
  272. #
  273. #-------- Globals for Icon functions used by the debuggin runtime --------
  274. # In an excruciating effort to avoid being hit by bad manners from the
  275. # program under test we use our own variables for Icon functions.
  276.  
  277. global __dbg_fany, __dbg_fclose, __dbg_fdelete, __dbg_fexit, __dbg_ffind
  278. global __dbg_fgetenv, __dbg_fimage, __dbg_finsert, __dbg_finteger, __dbg_fior
  279. global __dbg_fishift, __dbg_fkey, __dbg_fmany, __dbg_fmatch
  280. global __dbg_fmove, __dbg_fpop, __dbg_fpos, __dbg_fproc, __dbg_fpush
  281. global __dbg_fput, __dbg_fread, __dbg_fremove, __dbg_freverse, __dbg_fright
  282. global __dbg_fsort, __dbg_fstring, __dbg_ftab, __dbg_ftable, __dbg_ftrim
  283. global __dbg_ftype, __dbg_fupto, __dbg_fwrite, __dbg_fwrites
  284.  
  285. #
  286. #-------------- Expression management globals -----------        
  287. #
  288.  
  289. global __dbg_ge_message
  290. # Holds message if there is a conflict in expression compilation or
  291. # evaluation
  292.  
  293. global __dbg_ge_singular
  294. # Value used as default for the local variable table.
  295. # Must be initialized to an empty list (or other suitable value).
  296.  
  297. #
  298. #-------- Main --------
  299. #
  300.  
  301. procedure __dbg_proc (file, line, proc_name, var_name, var_val[])
  302. # This procedure is invoked a first time during initialization with parameters
  303. # all null.
  304. # Then it is called every time we hit a breakpoint during a debugging session.
  305. # The parameters define the breakpoint, as follows,
  306. # 'file': source file name (string).
  307. # 'line': source line number (integer).
  308. # 'proc_name': name of the current procedure (string).
  309. # 'var_name': names of variables local to the current procedure
  310. # (list of string).
  311. # The list is sorted alphabetically.
  312. # 'Local' variables include parameters and static variables.
  313. # 'var_val': The current values of the local variables (list).
  314. # The values occur in the same order as the names in 'var_name'.
  315. # NOTE: In order not to affect the logic of the debugged program this
  316. # procedure MUST FAIL.
  317. # If it returns anything the current procedure will fail immediately.
  318. local bdescr, cond, cmd, idx, tfname
  319.     # Save trace level; turn tracing off.
  320.     __dbg_g_trace := &trace
  321.     &trace := 0
  322.  
  323.     if \file then { # Not the first-time invocation from "dbg_init".
  324.         # Increment the global breakpoint counter.
  325.         __dbg_g_brkpcnt +:= 1
  326.  
  327.         # Compute the procedure nesting level.
  328.         __dbg_g_level := &level - 1
  329.  
  330.         # Begin setting up the 'where' structure.
  331.         __dbg_g_where := [file, line, proc_name, &null]
  332.  
  333.         # We get here either because of a 'next', or because we hit a
  334.         # breakpoint.
  335.         # If we break because of a 'next' we should not treat this as
  336.         # a breakpoint, even if there is one on this source line.
  337.         if __dbg_test === member then {
  338.             # This is a breakpoint; get it.
  339.             if bdescr := __dbg_g_brlookup[__dbg_fior (__dbg_g_fileidx[file],
  340.                         line)] then {
  341.                 # Check ignore count.
  342.                 ((bdescr[BRKP_IGNORE] -:= 1) = -1) | fail
  343.                 bdescr[BRKP_IGNORE] := 0
  344.                 }
  345.             else
  346.                 __dbg_io_cfl ("Mysterious break: %1 (%2:%3).",
  347.                     proc_name, file, line)
  348.             }
  349.         else {  # Break caused by 'next'.
  350.             # By convention treated as breakpoint number 0.
  351.             bdescr := __dbg_g_brkpdef[0]
  352.             # Check ignore count.
  353.             ((bdescr[BRKP_IGNORE] -:= 1) = -1) | fail
  354.             bdescr[BRKP_IGNORE] := 0
  355.             }
  356.         __dbg_g_where[WHERE_BRKP] := bdescr
  357.  
  358.         # Create table of locals.
  359.         __dbg_g_local := __dbg_ftable (__dbg_ge_singular)
  360.         every idx := 1 to *var_name do
  361.             __dbg_g_local[var_name[idx]] := var_val[idx]
  362.  
  363.         # Evaluate the condition of the breakpoint, if any.
  364.         if cond := \(bdescr)[BRKP_COND] then {
  365.             idx := 0
  366.             __dbg_e_eval (cond[1]) & (idx +:= 1)
  367.             # Check for conflict.
  368.             # Make sure we don't resume in such case.
  369.             __dbg_io_cfl ("[%1] condition '%2'\n   %3",
  370.                 bdescr[BRKP_ID], cond[2], \__dbg_ge_message) &
  371.                 (idx +:= 1)
  372.             (idx > 0) | fail
  373.             }
  374.  
  375.         # Reset the test procedure (effective if this is a 'next' break).
  376.         __dbg_test := member
  377.  
  378.         # The first command to execute is the macro attached to the
  379.         # breakpoint, if any; otherwise the prelude.
  380.         cmd := (\(\bdescr)[BRKP_DO] | __dbg_g_automacro[1])
  381.         }
  382.     else {    # Initialize global variables for Icon functions.
  383.         __dbg_func_init ()
  384.         # Initialize breakpoint globals.
  385.         __dbg_g_brkpcnt := 0
  386.         __dbg_g_brkpdef := __dbg_ftable ()
  387.         __dbg_g_brlookup := __dbg_ftable ()
  388.         __dbg_g_brkpid := 0
  389.  
  390.         # Compute the procedure nesting level.
  391.         __dbg_g_level := &level - 2
  392.  
  393.         # Create breakpoint number 0, used for 'next' breaks.
  394.         __dbg_g_brkpdef[0] := [0, "*any*", 0, 0, 0, 0, , ]
  395.  
  396.         # Display file name.
  397.         __dbg_g_display := "_DBG" || &clock[4:6] || &clock[7:0] || ".tmp"
  398.  
  399.         # More globals.
  400.         __dbg_g_src := []
  401.         __dbg_g_white := ' \t'
  402.         __dbg_ge_singular := []
  403.  
  404.         # Create file index table.
  405.         idx := -1
  406.         __dbg_g_fileidx := __dbg_ftable ()
  407.         every __dbg_g_fileidx[key(__dbg_file_map)] :=
  408.             __dbg_fishift ((idx +:= 1), FIDX_SHIFT)
  409.  
  410.         # Open input and output files.
  411.         if tfname := __dbg_fgetenv (DBG_INPUT_ENV) then
  412.             __dbg_g_in := __dbg_x_openfile (tfname)
  413.         (/__dbg_g_in := &input) | __dbg_fpush (__dbg_g_src, &input)
  414.  
  415.         if tfname := __dbg_fgetenv (DBG_OUTPUT_ENV) then
  416.             __dbg_g_out1 := __dbg_x_openfile (tfname, 1)
  417.         /__dbg_g_out1 := &errout
  418.  
  419.         # Initialize command definitions.
  420.         __dbg_cmd_init ()
  421.  
  422.         # Set up the breakpoint data structure.
  423.         # This is not a breakpoint; the following keeps some commands from
  424.         # crashing.
  425.         __dbg_g_local := __dbg_ftable ()
  426.         __dbg_g_where := [&null, 0, "main", &null]
  427.         __dbg_default_prelude :=
  428.         "fprint \"[%1] %2 (%3:%4)\\n\";&bp;&proc;&file;&line"
  429.         __dbg_default_postlude := ""
  430.         __dbg_g_automacro := [[__dbg_c_compile (__dbg_default_prelude)],
  431.             []]
  432.         cmd := []
  433.         }
  434.  
  435.     # Command processing.
  436.     repeat {
  437.         case __dbg_c_interp (cmd) of {
  438.         RESUME_STATUS: break
  439.         STOP_STATUS: {
  440.             __dbg_fremove (__dbg_g_display)
  441.             __dbg_io_note ("Debug session terminates.")
  442.             __dbg_fexit (0)
  443.             }
  444.         }
  445.         # Get input until it compiles OK.
  446.         repeat {
  447.             (*__dbg_g_src > 0) | __dbg_fwrites ("$ ")
  448.             if cmd := [__dbg_c_compile (__dbg_io_getline ())] then
  449.                 break
  450.             }
  451.         }
  452.     # Run the postlude, if any; status discarded.
  453.     __dbg_c_interp (__dbg_g_automacro[2])
  454.     &trace := __dbg_g_trace
  455. end
  456.  
  457. #
  458. #-------- Command processing procedures --------
  459. #
  460.  
  461. procedure __dbg_c_compile (str, macro_def)
  462. # Compiles a command.
  463. # 'str' must be a command to compile (string).
  464. # 'macro_def' must be non-null to indicate a macro is being defined.
  465. # RETURNS a command instance (list), or
  466. # FAILS on conflict.
  467. local cmd, keywd
  468.     str ? {
  469.         __dbg_ftab (__dbg_fmany (__dbg_g_white))
  470.         keywd := __dbg_ftab (__dbg_fupto (__dbg_g_white) | 0)
  471.         if *keywd = 0 then # empty line treated as comment
  472.             return [__dbg_cx_NOOP, COMMENT_CMD]
  473.         __dbg_ftab (__dbg_fmany (__dbg_g_white))
  474.         (cmd := __dbg_c_findcmd (keywd)) | fail
  475.         return cmd[CMD_COMPILE] (cmd, macro_def)
  476.         }
  477. end
  478.  
  479. procedure __dbg_c_brkpt (not_zero)
  480. # Extracts a breakpoint id from a command.
  481. # A breakpoint id is either an integer, or one of the special forms
  482. # '.' (current), '$' (last defined).
  483. # 'not_zero' may be non-null to indicate that breakpoint number zero
  484. # is not accepted.
  485. # RETURNS a breakpoint identifier (integer) on success;
  486. # FAILS with a suitable conflict message otherwise.
  487. local id, res
  488.     __dbg_ftab (__dbg_fmany (__dbg_g_white))
  489.     (res := (__dbg_finteger (__dbg_ftab (__dbg_fmany (&digits))) |
  490.         2(id := =".", BRKP_CURRENT) |
  491.         2(id := ="$", BRKP_LAST))) | {
  492.         __dbg_io_cfl ("Breakpoint id (integer, '.', '$') expected.")
  493.         fail
  494.         }
  495.     (res > 0) | /not_zero | {
  496.         __dbg_io_cfl ("Breakpoint number 0 not accepted here.")
  497.         fail
  498.         }
  499.     return res
  500. end
  501.  
  502. procedure __dbg_c_interp (clist)
  503. # Command interpreter.
  504. # 'clist' must be a list of command instances.
  505. # The interpreter may call itself indirectly through commands.
  506. # RETURNS a status code, or
  507. # FAILS on conflict, abandoning its command list.
  508. local cmd, code
  509.     every cmd := !clist do {
  510.         (code := cmd[1]!cmd) | fail
  511.         (code = OK_STATUS) | return code
  512.         }
  513.     return OK_STATUS
  514. end
  515.  
  516. procedure __dbg_c_findcmd (keywd)
  517. # Finds a command descriptor given a keyword.
  518. # 'keywd' must be a command keyword candidate, possibly abbreviated (string).
  519. # RETURNS a command definition, or
  520. # FAILS with a message on conflict.
  521. local count, cmd, mstr, sep, try
  522.     count := 0
  523.     sep := mstr := ""
  524.     every __dbg_fmatch (keywd, (try := !__dbg_g_cmd)[CMD_NAME], 1, 0) do {
  525.         cmd := try
  526.         count +:= 1
  527.         mstr ||:= sep || cmd[CMD_NAME]
  528.         sep := ", "
  529.         }
  530.     case count of {
  531.     0: {
  532.         __dbg_io_cfl ("%1: unrecognized command.", keywd)
  533.         fail
  534.         }
  535.     1: return cmd
  536.     default : {
  537.         __dbg_io_cfl ("'%1': ambiguous (matches %2).", keywd, mstr)
  538.         fail
  539.         }
  540.     }
  541. end
  542.  
  543. procedure __dbg_c_findkey (keywd, keylist)
  544. # Finds a command descriptor given a keyword.
  545. # 'keywd' must be a keyword candidate, possibly abbreviated (string).
  546. # 'keylist' must be a list of available keywords.
  547. # RETURNS an integer index into 'keylist', or
  548. # FAILS with a message on conflict.
  549. local count, cmd, idx, mstr, sep
  550.     count := 0
  551.     sep := mstr := ""
  552.     every __dbg_fmatch (keywd, keylist[idx := 1 to *keylist], 1, 0) do {
  553.         count +:= 1
  554.         mstr ||:= sep || keylist[cmd := idx]
  555.         sep := ", "
  556.         }
  557.     case count of {
  558.     0: {
  559.         __dbg_io_cfl ("%1: unrecognized keyword.", keywd)
  560.         fail
  561.         }
  562.     1: return cmd
  563.     default : {
  564.         __dbg_io_cfl ("'%1': ambiguous (matches %2).", keywd, mstr)
  565.         fail
  566.         }
  567.     }
  568. end
  569.  
  570. procedure __dbg_c_mcompile (fname)
  571. # Compiles a macro.
  572. # 'fname' must contain a file name (string) if the macro definition should
  573. # be read from a file; otherwise null.
  574. # If 'fname' is defined and can be opened, a null value is pushed on the file
  575. # stack before the file, as a mark.
  576. # RETURNS a macro, i.e. a list of compiled commands -- on success.
  577. # FAILS if a conflict arises during the macro definition.
  578. local cfl_count, cmd, f, line, macro
  579.     cfl_count := 0
  580.     macro := []
  581.     if \fname then {
  582.         if f := __dbg_x_openfile (fname) then {
  583.             __dbg_fpush (__dbg_g_src, __dbg_g_in)
  584.             __dbg_fpush (__dbg_g_src, &null)
  585.             __dbg_g_in := f
  586.             }
  587.         else
  588.             fail
  589.         }
  590.     repeat {
  591.         (*__dbg_g_src > 0) | __dbg_fwrites ("> ")
  592.         (line := __dbg_io_getline ()) | break
  593.         if cmd := __dbg_c_compile (line, 1) then {
  594.             if cmd[CMD_CODE] = END_CMD then
  595.                 break
  596.             else
  597.                 __dbg_fput (macro, cmd)
  598.             }
  599.          else
  600.             cfl_count +:= 1
  601.         (cfl_count < 30) | break
  602.         }
  603.     /__dbg_g_in := __dbg_fpop (__dbg_g_src)
  604.     if cfl_count = 0 then
  605.         return macro
  606.     else {
  607.         __dbg_io_note ("The definition did not take effect.")
  608.         fail
  609.         }
  610. end
  611.  
  612. procedure __dbg_c_msource ()
  613. # Checks if the source of a macro is a file.
  614. # RETURNS a file name if there is a '<' followed by a file name.
  615. # RETURNS null if there is nothing but white space.
  616. # FAILS with a message on conflict.
  617. local fname
  618.     __dbg_ftab (__dbg_fmany (__dbg_g_white))
  619.     if ="<" then {
  620.         __dbg_ftab (__dbg_fmany (__dbg_g_white))
  621.         if __dbg_fpos (0) then {
  622.             __dbg_io_cfl ("File name expected.")
  623.             fail
  624.             }
  625.         fname := __dbg_ftrim (__dbg_ftab (0))
  626.         }
  627.     return fname
  628. end
  629.  
  630. procedure __dbg_x_brkpt (id)
  631. # RETURNS a breakpoint descriptor, given a breakpoint id ('id', integer).
  632. # FAILS with a diagnostic message on conflict.
  633. local bdescr
  634.     bdescr := case id of {
  635.     BRKP_CURRENT:    \__dbg_g_where[WHERE_BRKP] |
  636.             (__dbg_io_cfl ("No current breakpoint."), &null)
  637.     BRKP_LAST:    \__dbg_g_brkpdef[__dbg_g_brkpid] |
  638.         (__dbg_io_cfl ("Breakpoint [%1] undefined.", __dbg_g_brkpid),
  639.             &null)
  640.     default:    \__dbg_g_brkpdef[id] |
  641.             (__dbg_io_cfl ("Breakpoint [%1] undefined.", id), &null)
  642.     }
  643.     return \bdescr
  644. end
  645.  
  646. procedure __dbg_x_dispglob (f, pat)
  647. # Essentially performs the 'info globals' command.
  648. # 'f' must be a display file open for input.
  649. # 'pat' must be a substring that variable names must contain.
  650. local fchanged, line, word
  651. static func
  652. initial {
  653.     func := set ()
  654.     # A set containing all function names.
  655.     every insert (func, function ())
  656.     }
  657.     fchanged := []
  658.     until __dbg_fread (f) == "global identifiers:"
  659.     repeat {
  660.         (line := __dbg_fread (f)) | break
  661.         word := []
  662.         line ? repeat {
  663.             __dbg_ftab (__dbg_fmany (__dbg_g_white))
  664.             if __dbg_fpos (0) then
  665.                 break
  666.             __dbg_fput (word, __dbg_ftab (__dbg_fupto (__dbg_g_white) | 0))
  667.             }
  668.         __dbg_fmatch (DBG_PREFIX, word[1]) | (word[1] == word[-1]) |
  669.         if __dbg_ffind (pat, word[1]) then
  670.             __dbg_io_info ("%1", word[1])
  671.  
  672.         # Check if function name has been used for other things.
  673.         if member (func, word[1]) then {
  674.             (word[-2] == "function" & word[-1] == word[1]) |
  675.             put (fchanged, word[1])
  676.             }
  677.         }
  678.     if *fchanged > 0 then {
  679.         __dbg_io_note ("The following global(s) no longer hold their usual Icon functions:")
  680.         every __dbg_io_wrline ("  " || !fchanged)
  681.         }
  682. end
  683.  
  684. procedure __dbg_x_dispinit (f)
  685. # Reads the display file, skipping over lines caused by the debugger.
  686. # 'f' must be the display file, open for input.
  687. # RETURNS the first 'significant' line.
  688. # NOTE that you must take care of the 'co-expression' line before calling
  689. # this procedure.
  690. local line
  691.     until __dbg_fmatch (DBG_PREFIX, line := __dbg_fread (f))
  692.     while line[1] == " " | __dbg_fmatch (DBG_PREFIX, line) do
  693.         line := __dbg_fread (f)
  694.     return line
  695. end
  696.  
  697. procedure __dbg_x_lbreak (bdescr)
  698. # Lists the nominal definition of a breakpoint.
  699. # 'bdescr' may be a breakpoint descriptor, or null.
  700. # If null all breakpoints are listed.
  701. local bd, blist, cond, dodef, tmplist
  702.     (blist := [\bdescr]) | {
  703.         tmplist := __dbg_fsort (__dbg_g_brkpdef)
  704.         blist := []
  705.         every __dbg_fput (blist, (!tmplist)[2])
  706.         }
  707.     every bd := !blist do {
  708.         dodef := if \bd[BRKP_DO] then "  DO defined" else ""
  709.         __dbg_io_info ("[%1] %2 %3:%4%5", bd[BRKP_ID], bd[BRKP_FILE],
  710.             bd[BRKP_LINE1], bd[BRKP_LINE2], dodef)
  711.         if cond := \bd[BRKP_COND] then
  712.             __dbg_io_info ("   CONDITION: %1", cond[2])
  713.         }
  714. end
  715.  
  716. procedure __dbg_x_openfile (fname, output, quiet)
  717. # Opens a file.
  718. # 'fname' must be the name of the file to open.
  719. # 'output' must be non-null if the file is to be opened for output.
  720. # 'quiet' must be non-null to prevent a conflict from generating a message.
  721. # RETURNS an open file on success;
  722. # FAILS with a message otherwise, unless 'quiet' is set.
  723. # FAILS silently if 'quiet' is set.
  724. local f, mode, modestr
  725.     if \output then {
  726.         mode := "w"
  727.         modestr := "output"
  728.         }
  729.     else {
  730.         mode := "r"
  731.         modestr := "input"
  732.         }
  733.     (f := open (fname, mode)) | (\quiet & fail) |
  734.     __dbg_io_cfl ("Cannot open '%1' for %2.", fname, modestr)
  735.     return \f
  736. end
  737.  
  738. procedure __dbg_x_opendisplay ()
  739. # Opens the display file for reading; writes it first, if necessary.
  740. # RETURNS a file open for input on success.
  741. # FAILS with a message on conflict.
  742. local f, res
  743.     if f := __dbg_x_openfile (__dbg_g_display,, 1) then {
  744.         if __dbg_finteger (__dbg_fread (f)) = __dbg_g_brkpcnt then
  745.             res := f
  746.         else
  747.             __dbg_fclose (f)
  748.         }
  749.     \res | {
  750.         (f := __dbg_x_openfile (__dbg_g_display, 1)) | fail
  751.         __dbg_fwrite (f, __dbg_g_brkpcnt)
  752.         display (, f)
  753.         __dbg_fclose (f)
  754.         (f := __dbg_x_openfile (__dbg_g_display)) | fail
  755.         __dbg_fread (f) # Throw away breakpoint counter.
  756.         res := f
  757.         }
  758.     return res
  759. end
  760.  
  761. #-------- Command compilation procedures --------
  762. # 'macro_def' must be non-null to indicate that a macro is being defined.
  763. # The command compilation procedures must return a list representing the
  764. # compiled command, or fail on conflict.
  765. # When they are invoked the keyword and any following white space has been
  766. # parsed.
  767.  
  768.  
  769. procedure __dbg_cc_break (cmd, macro_def)
  770. local fidx, fname, line1, line2
  771.     __dbg_fany (&digits) | (fname := __dbg_ftab (__dbg_fupto (__dbg_g_white))) | {
  772.         __dbg_io_cfl ("File name and/or line number expected.")
  773.         fail
  774.         }
  775.  
  776.     # Get file name.
  777.     if \fname then {
  778.         (fidx := \__dbg_g_fileidx[fname]) | {
  779.             __dbg_io_cfl ("File name '%1' not recognized.", fname)
  780.             fail
  781.             }
  782.         }
  783.     else if fname := \__dbg_g_where[WHERE_FILE] then
  784.         fidx := __dbg_g_fileidx[fname]
  785.     else { # init mode
  786.         __dbg_io_cfl ("File name required.")
  787.         fail
  788.         }
  789.  
  790.     # Get line number(s).
  791.     __dbg_ftab (__dbg_fmany (__dbg_g_white))
  792.     (line1 := __dbg_finteger (__dbg_ftab (__dbg_fmany (&digits)))) | {
  793.         __dbg_io_cfl ("Line number expected.")
  794.         fail
  795.         }
  796.     __dbg_ftab (__dbg_fmany (__dbg_g_white))
  797.     if =":" then {
  798.         __dbg_ftab (__dbg_fmany (__dbg_g_white))
  799.             (line2 := __dbg_finteger (__dbg_ftab (__dbg_fmany (&digits)))) | {
  800.                 __dbg_io_cfl ("Line number expected.")
  801.                 fail
  802.                 }
  803.         }
  804.     else
  805.         line2 := line1
  806.     (line1 <= line2 < 1000000) | {
  807.         __dbg_io_cfl ("Weird line number.")
  808.         fail
  809.         }
  810.  
  811.     # Create an almost finished breakpoint descriptor (id is missing).
  812.     return [cmd[CMD_EXEC], cmd[CMD_CODE], [ , fname, fidx, line1, line2, 0, ,]]
  813. end
  814.  
  815. procedure __dbg_cc_clear (cmd, macro_def)
  816. # A compound command.
  817. local keyidx, parm
  818. static ckey
  819. initial ckey := ["breakpoint", "condition", "do", "echo", "macro"]
  820.     (keyidx := __dbg_c_findkey (__dbg_ftab (__dbg_fupto (__dbg_g_white) | 0), ckey)) |
  821.         fail
  822.     __dbg_ftab (__dbg_fmany (__dbg_g_white))
  823.     case keyidx of {
  824.     CLEAR_BREAKPOINT:
  825.         (parm := __dbg_c_brkpt (1)) | fail
  826.     (CLEAR_COND | CLEAR_DO):
  827.         (parm := __dbg_c_brkpt ()) | fail
  828.     CLEAR_MACRO:
  829.         (parm := __dbg_e_idf ()) | {
  830.             __dbg_io_cfl ("Macro name expected.")
  831.             fail
  832.             }
  833.     }
  834.     return [cmd[CMD_EXEC], cmd[CMD_CODE], ckey, keyidx, parm]
  835. end
  836.  
  837. procedure __dbg_cc_condition (cmd, macro_def)
  838. local brkpt, expr
  839.     (brkpt := __dbg_c_brkpt ()) | fail
  840.     # This makes the expression cleaner, but not necessary.
  841.     __dbg_ftab (__dbg_fmany (__dbg_g_white))
  842.     (expr := __dbg_e_compile (__dbg_ftab (0))) | {
  843.         __dbg_io_cfl (__dbg_ge_message)
  844.         fail
  845.         }
  846.     (*expr = 1) | __dbg_io_note ("Last %1 expressions ignored.", *expr - 1)
  847.     return [cmd[CMD_EXEC], cmd[CMD_CODE], brkpt, expr[1]]
  848. end
  849.  
  850. procedure __dbg_cc_do (cmd, macro_def)
  851. local brkpt, fname
  852.     /macro_def | {
  853.         __dbg_io_cfl ("Sorry, nested macros not accepted.")
  854.         fail
  855.         }
  856.     (brkpt := __dbg_c_brkpt ()) | fail
  857.     (fname := __dbg_c_msource ()) | fail
  858.     return [cmd[CMD_EXEC], cmd[CMD_CODE], brkpt, fname]
  859. end
  860.  
  861. procedure __dbg_cc_end (cmd, macro_def)
  862.     \macro_def | {
  863.         __dbg_io_cfl ("'end' out of context.")
  864.         fail
  865.         }
  866.     return [cmd[CMD_EXEC], cmd[CMD_CODE]]
  867. end
  868.  
  869. procedure __dbg_cc_eprint (cmd, macro_def)
  870. local expr
  871.     (expr := __dbg_e_compile (__dbg_ftab (0))) | {
  872.         __dbg_io_cfl (__dbg_ge_message)
  873.         fail
  874.         }
  875.     (*expr = 1) | __dbg_io_note ("Last %1 expressions ignored.", *expr - 1)
  876.     return [cmd[CMD_EXEC], cmd[CMD_CODE], expr[1]]
  877. end
  878.  
  879. procedure __dbg_cc_frame (cmd, macro_def)
  880. local frame_no
  881.     __dbg_fpos (0) | (frame_no := __dbg_finteger (__dbg_ftab (__dbg_fmany (&digits ++ '-')))) | {
  882.         __dbg_io_cfl ("Frame number expected.")
  883.         fail
  884.         }
  885.     return [cmd[CMD_EXEC], cmd[CMD_CODE], frame_no]
  886. end
  887.  
  888. procedure __dbg_cc_goon (cmd, macro_def)
  889. local opt
  890.     __dbg_fpos (0) | __dbg_fmatch (opt := __dbg_ftab (__dbg_fmany (&lcase)), "nobreak", 1, 0) | {
  891.         __dbg_io_cfl ("Expected 'nobreak', found '%1'.", opt)
  892.         fail
  893.         }
  894.     return [cmd[CMD_EXEC], cmd[CMD_CODE], opt]
  895. end
  896.  
  897. procedure __dbg_cc_help (cmd, macro_def)
  898. local keywd
  899.     __dbg_fpos (0) | (keywd := __dbg_ftab (__dbg_fmany (&lcase))) | {
  900.         __dbg_io_cfl ("Command keyword expected.")
  901.         fail
  902.         }
  903.     return [cmd[CMD_EXEC], cmd[CMD_CODE], keywd]
  904. end
  905.  
  906. procedure __dbg_cc_ignore (cmd, macro_def)
  907. local brkpt, count
  908.     (brkpt := __dbg_c_brkpt ()) | fail
  909.     __dbg_ftab (__dbg_fmany (__dbg_g_white))
  910.     (count := __dbg_finteger (__dbg_ftab (__dbg_fmany (&digits ++ '+-')))) | {
  911.         __dbg_io_cfl ("Integer ignore count expected.")
  912.         fail
  913.         }
  914.     return [cmd[CMD_EXEC], cmd[CMD_CODE], brkpt, count]
  915. end
  916.  
  917. procedure __dbg_cc_info (cmd, macro_def)
  918. # A compound command.
  919. local keyidx, parm
  920. static ckey
  921. initial ckey := ["breakpoint", "echo", "files", "globals", "locals", "macros",
  922.     "trace", "version"]
  923.     (keyidx := __dbg_c_findkey (__dbg_ftab (__dbg_fupto (__dbg_g_white) | 0), ckey)) |
  924.         fail
  925.     __dbg_ftab (__dbg_fmany (__dbg_g_white))
  926.     if keyidx = INFO_BREAKPOINT then
  927.         __dbg_fpos (0) | (parm := __dbg_c_brkpt ()) | fail
  928.     else if keyidx = INFO_GLOBALS then
  929.         __dbg_fpos (0) | (parm := __dbg_ftab (__dbg_fupto (__dbg_g_white) | 0))
  930.     return [cmd[CMD_EXEC], cmd[CMD_CODE], ckey, keyidx, parm]
  931. end
  932.  
  933. procedure __dbg_cc_macro (cmd, macro_def)
  934. local fname, idf
  935.     /macro_def | {
  936.         __dbg_io_cfl ("Sorry, nested macros not accepted.")
  937.         fail
  938.         }
  939.     (idf := __dbg_ftab (__dbg_fmany (&lcase))) | {
  940.         __dbg_io_cfl ("Macro name expected.")
  941.         fail
  942.         }
  943.     (fname := __dbg_c_msource ()) | fail
  944.     return [cmd[CMD_EXEC], cmd[CMD_CODE], idf, fname]
  945. end
  946.  
  947. procedure __dbg_cc_next (cmd, macro_def)
  948. local count
  949.     __dbg_ftab (__dbg_fmany (__dbg_g_white))
  950.     __dbg_fpos (0) | (count := __dbg_finteger (__dbg_ftab (__dbg_fmany (&digits ++ '+-')))) | {
  951.         __dbg_io_cfl ("Integer ignore count expected.")
  952.         fail
  953.         }
  954.     return [cmd[CMD_EXEC], cmd[CMD_CODE], count]
  955. end
  956.  
  957. procedure __dbg_cc_print (cmd, macro_def)
  958. # Used to compile 'fprint' and 'print'.
  959. local expr
  960.     (expr := __dbg_e_compile (__dbg_ftab (0))) | {
  961.         __dbg_io_cfl (__dbg_ge_message)
  962.         fail
  963.         }
  964.     return [cmd[CMD_EXEC], cmd[CMD_CODE], expr]
  965. end
  966.  
  967. procedure __dbg_cc_set (cmd, macro_def)
  968. # A compound command.
  969. local keyidx, parm
  970. static ckey
  971. initial ckey := ["echo", "prelude", "postlude"]
  972.     (keyidx := __dbg_c_findkey (__dbg_ftab (__dbg_fupto (__dbg_g_white) | 0), ckey)) |
  973.         fail
  974.     __dbg_ftab (__dbg_fmany (__dbg_g_white))
  975.     case keyidx of {
  976.     SET_ECHO: {
  977.         parm := __dbg_ftrim (__dbg_ftab (__dbg_fupto (__dbg_g_white) | 0))
  978.         (*parm > 0) | {
  979.             __dbg_io_cfl ("File name expected.")
  980.             fail
  981.             }
  982.         }
  983.     (SET_PRELUDE | SET_POSTLUDE):
  984.         (parm := __dbg_c_msource ()) | fail
  985.     }
  986.     return [cmd[CMD_EXEC], cmd[CMD_CODE], ckey, keyidx, parm]
  987. end
  988.  
  989. procedure __dbg_cc_source (cmd, macro_def)
  990. # The 'source' command is different from other commands, because it is not
  991. # really compiled; it takes effect immediately.
  992. # In contrast to macro compilation, no null marker is pushed on the file stack.
  993. # RETURNS a dummy 'source' command.
  994. local f, fname, res
  995.     __dbg_ftab (__dbg_fmany (__dbg_g_white))
  996.     if __dbg_fpos (0) then
  997.         __dbg_io_cfl ("File name expected.")
  998.     else {
  999.         fname := __dbg_ftrim (__dbg_ftab (0))
  1000.         if *__dbg_g_src >= MAX_SOURCE_NESTING then
  1001.             __dbg_io_cfl ("%1: Too deeply nested 'source' file.", fname)
  1002.         else if f := __dbg_x_openfile (fname) then {
  1003.             __dbg_fpush (__dbg_g_src, __dbg_g_in)
  1004.             __dbg_g_in := f
  1005.             res := [cmd[CMD_EXEC], cmd[CMD_CODE], fname]
  1006.             }
  1007.         }
  1008.     return \res
  1009. end
  1010.  
  1011. procedure __dbg_cc_trace (cmd, macro_def)
  1012. local tlevel
  1013.     (tlevel := __dbg_finteger (__dbg_ftab (__dbg_fmany (&digits ++ '+-')))) | {
  1014.         __dbg_io_cfl ("Integer value expected.")
  1015.         fail
  1016.         }
  1017.     return [cmd[CMD_EXEC], cmd[CMD_CODE], \tlevel]
  1018. end
  1019.  
  1020. procedure __dbg_cc_SIMPLE (cmd, macro_def)
  1021. # Used to compile all keyword-only commands, including macros.
  1022.     return [cmd[CMD_EXEC], cmd[CMD_CODE], cmd[CMD_MACRO]]
  1023. end
  1024.  
  1025. #-------- Command executing procedures --------
  1026. # The first parameter of these procedures is the procedure itself.
  1027. # (Not a very interesting parameter.)
  1028. # The command executing procedures must return a return code on success.
  1029. # Return codes are defined among the symbolic constants.
  1030. # The procedures must fail on conflict.
  1031.  
  1032.  
  1033. procedure __dbg_cx_break (proced, ccode, brkp)
  1034. local id, bpset, fidx, line1, line2
  1035.     # Add the breakpoint id to the descriptor.
  1036.     brkp[BRKP_ID] := id := (__dbg_g_brkpid +:= 1)
  1037.     __dbg_io_wrline ("[" || id || "]")
  1038.     # Make sure we can find the breakpint descriptor, given its id.
  1039.     __dbg_g_brkpdef[id] := brkp
  1040.     # Install the breakpoint lines in the lookup table.
  1041.     fidx := brkp[BRKP_FIDX]
  1042.     line1 := brkp[BRKP_LINE1]
  1043.     line2 := brkp[BRKP_LINE2]
  1044.     every __dbg_g_brlookup[__dbg_fior (fidx, line1 to line2)] := brkp
  1045.     # Add the line numbers to the breakpoint set.
  1046.     bpset := __dbg_file_map[brkp[BRKP_FILE]]
  1047.     every __dbg_finsert (bpset, line1 to line2)
  1048.     return OK_STATUS
  1049. end
  1050.  
  1051. procedure __dbg_cx_clear (proced, ccode, ckey, keyidx, parm)
  1052. # 'ckey' will be a list containing all the possible keywords to 'clear'.
  1053. # 'keyidx' is an index into that list, indicating a subcommand.
  1054. local bdescr, bpset, cmd, fidx, lcode, line, line1, line2
  1055.     if keyidx = (CLEAR_BREAKPOINT | CLEAR_COND | CLEAR_DO) then
  1056.         (bdescr := __dbg_x_brkpt (parm)) | fail
  1057.     else if keyidx = CLEAR_MACRO then
  1058.         (cmd := __dbg_c_findcmd (parm)) | fail
  1059.     case keyidx of {
  1060.     CLEAR_BREAKPOINT: {
  1061.         __dbg_fdelete (__dbg_g_brkpdef, bdescr[BRKP_ID])
  1062.         fidx := bdescr[BRKP_FIDX]
  1063.         line1 := bdescr[BRKP_LINE1]
  1064.         line2 := bdescr[BRKP_LINE2]
  1065.         bpset := __dbg_file_map[bdescr[BRKP_FILE]]
  1066.         # The range of lines once defined for the breakpoint might
  1067.         # have been overwritten by later breakpoints.
  1068.         every lcode := __dbg_fior (fidx, line := line1 to line2) do {
  1069.             if __dbg_g_brlookup[lcode] === bdescr then {
  1070.                 __dbg_fdelete (__dbg_g_brlookup, lcode)
  1071.                 __dbg_fdelete (bpset, line)
  1072.                 }
  1073.             }
  1074.         }
  1075.     CLEAR_COND:    bdescr[BRKP_COND] := &null
  1076.     CLEAR_DO:    bdescr[BRKP_DO] := &null
  1077.     CLEAR_ECHO: {
  1078.         __dbg_fclose (\__dbg_g_out2)
  1079.         __dbg_g_out2 := &null
  1080.         }
  1081.     CLEAR_MACRO: {
  1082.         (cmd := __dbg_c_findcmd (parm)) | fail
  1083.         __dbg_fdelete (__dbg_g_cmd, cmd[CMD_NAME])
  1084.         }
  1085.     }
  1086.     return OK_STATUS
  1087. end
  1088.  
  1089. procedure __dbg_cx_condition (proced, ccode, brkpt, expr)
  1090. local bdescr
  1091.     (bdescr := __dbg_x_brkpt (brkpt)) | fail
  1092.     bdescr[BRKP_COND] := expr
  1093.     return OK_STATUS
  1094. end
  1095.  
  1096. procedure __dbg_cx_do (proced, ccode, brkpt, fname)
  1097. local bdescr
  1098.     (bdescr := __dbg_x_brkpt (brkpt)) | fail
  1099.     (bdescr[BRKP_DO] := __dbg_c_mcompile (fname)) | fail
  1100.     return OK_STATUS
  1101. end
  1102.  
  1103. procedure __dbg_cx_eprint (proced, ccode, expr)
  1104. local count, val
  1105.     __dbg_io_wrline ("{" || expr[2] || "}")
  1106.     count := 0
  1107.     every val := __dbg_fimage (__dbg_e_eval (expr[1])) do {
  1108.         if __dbg_io_cfl (\__dbg_ge_message) then
  1109.             fail
  1110.         else
  1111.             __dbg_io_wrline ("" || __dbg_fright ((count +:= 1), 3) ||
  1112.                 ": " || val)
  1113.         }
  1114.     return OK_STATUS
  1115. end
  1116.  
  1117. procedure __dbg_cx_fprint (proced, ccode, elist)
  1118. # 'elist' must be a list on the format returned by '__dbg_e_compile'.
  1119. local expr, fmt, idx, sval, val
  1120.     val := []
  1121.     every expr := !elist do {
  1122.         __dbg_fput (val, __dbg_e_eval (expr[1]) | "&fail")
  1123.         if __dbg_io_cfl (\__dbg_ge_message) then
  1124.             fail
  1125.         }
  1126.     (fmt := __dbg_fstring (val[1])) | {
  1127.         __dbg_io_cfl ("Expected format string; got '%1'.", __dbg_fimage (val[1]))
  1128.         fail
  1129.         }
  1130.     sval := []
  1131.     every idx := 2 to *val do {
  1132.         __dbg_fput (sval, __dbg_fstring (val[idx])) | {
  1133.         __dbg_io_cfl ("Expression not string-convertible: {%1} %2",
  1134.             elist[idx][2], __dbg_fimage (val[idx]))
  1135.             fail
  1136.             }
  1137.         }
  1138.     __dbg_io_wrstr (__dbg_x_subst (fmt, sval))
  1139.     return OK_STATUS
  1140. end
  1141.  
  1142. procedure __dbg_cx_frame (proced, ccode, frame_spec)
  1143. local f, frame_no, idx, line
  1144.     frame_no := if \frame_spec then {
  1145.         if frame_spec < 0 then __dbg_g_level + frame_spec else frame_spec
  1146.         } else __dbg_g_level
  1147.     (1 <= frame_no <= __dbg_g_level) | {
  1148.         __dbg_io_cfl ("Invalid frame number.")
  1149.         fail
  1150.         }
  1151.     (f := __dbg_x_opendisplay ()) | fail
  1152.     line := __dbg_x_dispinit (f)
  1153.     idx := __dbg_g_level
  1154.     while idx > frame_no do {
  1155.         repeat if (line := __dbg_fread (f))[1] ~== " " then
  1156.                 break
  1157.         idx -:= 1
  1158.         }
  1159.     __dbg_io_info ("(%1) %2", frame_no, line)
  1160.     repeat {
  1161.         if (line := __dbg_fread (f))[1] ~== " " then
  1162.             break
  1163.         line ? {
  1164.             __dbg_ftab (__dbg_fmany (__dbg_g_white))
  1165.             =DBG_PREFIX | __dbg_io_info ("%1", line, *line > 0)
  1166.             }
  1167.         }
  1168.     __dbg_fclose (f)
  1169.     return OK_STATUS
  1170. end
  1171.  
  1172. procedure __dbg_cx_goon (proced, ccode, nobreak)
  1173.     if \nobreak then {
  1174.         __dbg_test := integer
  1175.         __dbg_fremove (__dbg_g_display)
  1176.         }
  1177.     return RESUME_STATUS
  1178. end
  1179.  
  1180. procedure __dbg_cx_help (proced, ccode, keywd)
  1181. # 'keywd' will be an identifier if the command had a keyword.
  1182. local cmd, hstr
  1183.     if cmd := __dbg_c_findcmd (\keywd) then {
  1184.         if hstr := \cmd[CMD_HELP] then
  1185.             __dbg_io_wrline (hstr)
  1186.         else
  1187.             __dbg_io_note ("No help available for '%1'.", cmd[CMD_NAME])
  1188.         }
  1189.     else
  1190. __dbg_io_wrline ("Available commands: (all keywords may be abbreviated)\n_
  1191. break        (set breakpoint)\n_
  1192. clear        (clear breakpoint or debugger parameter)\n_
  1193. condition    (attach condition to breakpoint)\n_
  1194. do        (attach macro to breakpoint)\n_
  1195. end        (terminate macro definition)\n_
  1196. eprint        (print every value from expression)\n_
  1197. fprint        (formatted print)\n_
  1198. frame        (inspect procedure call chain)\n_
  1199. goon        (resume execution)\n_
  1200. help        (print explanatory text)\n_
  1201. ignore        (set ignore counter on breakpoint)\n_
  1202. info        (print information about breakpoint or debugger parameter)\n_
  1203. macro        (define new command)\n_
  1204. next        (resume execution, break on every line)\n_
  1205. print        (print expressions)\n_
  1206. set        (set a debugger parameter)\n_
  1207. source        (read debugging commands from file)\n_
  1208. stop        (terminate program and debugging session)\n_
  1209. trace        (set value of Icon &trace)\n_
  1210. where        (print procedure call chain)\n\n_
  1211. An expression may be formed from a large subset of Icon operators; integer,\n_
  1212. string, list literals; locals from the current procedure, and globals.\n_
  1213. Procedure/function invocation, subscripting, record field reference is\n_
  1214. supported. Several keywords are also included.\n\n_
  1215. New/altered keywords,\n_
  1216. \    &bp, &breakpoint    current breakpoint id (integer)\n_
  1217. \    &file            current breakpoint source file name (string)\n_
  1218. \    &line            current breakpoint line number (integer)\n_
  1219. \    &proc            current breakpoint procedure name (string)")
  1220.     return OK_STATUS
  1221. end
  1222.  
  1223. procedure __dbg_cx_ignore (proced, ccode, brkpt, count)
  1224. local bdescr
  1225.     (bdescr := __dbg_x_brkpt (brkpt)) | fail
  1226.     bdescr[BRKP_IGNORE] := count
  1227.     return OK_STATUS
  1228. end
  1229.  
  1230. procedure __dbg_cx_info (proced, ccode, ckey, keyidx, parm)
  1231. # 'ckey' will be a list containing all the possible keywords to 'info'.
  1232. # 'keyidx' is an index into that list, indicating a subcommand.
  1233. local cmd, bdescr, f, nlist, version
  1234.     case keyidx of {
  1235.     INFO_BREAKPOINT:
  1236.         if \parm then {
  1237.             (bdescr := __dbg_x_brkpt (parm)) | fail
  1238.             __dbg_x_lbreak (bdescr)
  1239.             }
  1240.         else
  1241.             __dbg_x_lbreak ()
  1242.     INFO_ECHO:
  1243.         if \__dbg_g_out2 then
  1244.             __dbg_io_info ("Echo file: %1.", __dbg_g_out2name)
  1245.         else
  1246.             __dbg_io_info ("No echo file.")
  1247.     INFO_FILES: {
  1248.         nlist := []
  1249.         every __dbg_fput (nlist, __dbg_fkey (__dbg_file_map))
  1250.         nlist := __dbg_fsort (nlist)
  1251.         __dbg_io_info ("Tweaked source files in this program:")
  1252.         every __dbg_io_info ("   %1", !nlist)
  1253.         }
  1254.     INFO_GLOBALS: {
  1255.         (f := __dbg_x_opendisplay ()) | fail
  1256.         if \parm then
  1257.             __dbg_x_dispglob (f, parm)
  1258.         else
  1259.             __dbg_x_dispglob (f, "")
  1260.         __dbg_fclose (f)
  1261.         }
  1262.     INFO_LOCALS: {
  1263.         nlist := []
  1264.         every __dbg_fput (nlist, __dbg_fkey (__dbg_g_local))
  1265.         nlist := __dbg_fsort (nlist)
  1266.         __dbg_io_info ("Local identifiers in the current procedure:",
  1267.             *nlist > 0)
  1268.         every __dbg_io_info ("   %1", !nlist)
  1269.         }
  1270.     INFO_MACROS: {
  1271.         nlist := []
  1272.         every \(cmd := !__dbg_g_cmd)[CMD_MACRO] do
  1273.             __dbg_fput (nlist, cmd[CMD_NAME])
  1274.         nlist := __dbg_fsort (nlist)
  1275.         __dbg_io_info ("Currently defined macros:", *nlist > 0)
  1276.         every __dbg_io_info ("   %1", !nlist)
  1277.         }
  1278.     INFO_TRACE:
  1279.         __dbg_io_info ("Current trace level: %1.", __dbg_g_trace)
  1280.     INFO_VERSION: {
  1281.         version := (PROGRAM_VERSION ? (__dbg_ftab (__dbg_fupto (&digits)),
  1282.             __dbg_ftab (__dbg_fmany (&digits++'.'))))
  1283.         __dbg_io_info ("Program tweaked by itweak version %1.\n_
  1284.         This is runtime version %2.", __dbg_itweak_ver, version)
  1285.         }
  1286.     }
  1287.     return OK_STATUS
  1288. end
  1289.  
  1290. procedure __dbg_cx_macro (proced, ccode, idf, fname)
  1291. # Executes a 'macro' statement (not the resulting macro).
  1292. # 'fname' contains a file name (string) if the macro definition should be
  1293. # read from a file; otherwise null.
  1294. # SIDE EFFECT: Adds a command definition to '__dbg_g_cmd' on success.
  1295. local count, macro, mstr, sep, try
  1296.     count := 0
  1297.     mlist := []
  1298.     # Macro name must not be an abbreviation of an existing command.
  1299.     every __dbg_fmatch (idf, try := (!__dbg_g_cmd)[CMD_NAME], 1, 0) do {
  1300.         count +:= 1
  1301.         __dbg_fput (mlist, try)
  1302.         }
  1303.     # Check that no existing command is an abbreviation of macro name.
  1304.     every __dbg_fmatch (try := (!__dbg_g_cmd)[CMD_NAME], idf, 1, 0) do {
  1305.         count +:= 1
  1306.         (try == !mlist) | __dbg_fput (mlist, try)
  1307.         }
  1308.     (count = 0) | {
  1309.         mstr := sep := ""
  1310.         every mstr ||:= sep || !mlist do
  1311.             sep := ", "
  1312.         __dbg_io_cfl ("'%1' clashes with existing command (%2).", idf, mstr)
  1313.         fail
  1314.         }
  1315.     (macro := __dbg_c_mcompile (fname)) | fail
  1316.     __dbg_g_cmd[idf] := [idf, USERDEF_CMD, , __dbg_cc_SIMPLE, macro, __dbg_cx_userdef]
  1317.     return OK_STATUS
  1318. end
  1319.  
  1320. procedure __dbg_cx_next (proced, ccode, count)
  1321. # 'count' may be an ignore count.
  1322.     __dbg_g_brkpdef[0][BRKP_IGNORE] := \count
  1323.     __dbg_test := 2
  1324.     return RESUME_STATUS
  1325. end
  1326.  
  1327. procedure __dbg_cx_print (proced, ccode, elist)
  1328. # 'elist' must be a list on the format returned by '__dbg_e_compile'.
  1329. local expr, val
  1330.     every expr := !elist do {
  1331.         val := (__dbg_fimage (__dbg_e_eval (expr[1])) | "&fail")
  1332.         if __dbg_io_cfl (\__dbg_ge_message) then
  1333.             fail
  1334.         else
  1335.             __dbg_io_wrline ("{" || expr[2] || "} " || val)
  1336.         }
  1337.     return OK_STATUS
  1338. end
  1339.  
  1340. procedure __dbg_cx_set (proced, ccode, ckey, keyidx, parm)
  1341. # 'ckey' will be a list containing all the possible keywords to 'set'.
  1342. # 'keyidx' is an index into that list, indicating a subcommand.
  1343.     case keyidx of {
  1344.     SET_ECHO: {
  1345.         (__dbg_g_out2 := __dbg_x_openfile (parm, 1)) | fail
  1346.         __dbg_g_out2name := parm
  1347.         }
  1348.     SET_PRELUDE:
  1349.         (__dbg_g_automacro[1] := __dbg_c_mcompile (parm)) | fail
  1350.     SET_POSTLUDE:
  1351.         (__dbg_g_automacro[2] := __dbg_c_mcompile (parm)) | fail
  1352.     }
  1353.     return OK_STATUS
  1354. end
  1355.  
  1356. procedure __dbg_cx_stop (proced, ccode)
  1357.     return STOP_STATUS
  1358. end
  1359.  
  1360. procedure __dbg_cx_trace (proced, ccode, tlevel)
  1361.     __dbg_g_trace := tlevel
  1362.     return OK_STATUS
  1363. end
  1364.  
  1365. procedure __dbg_cx_where (proced, ccode)
  1366. local f, idf, idx, line
  1367.     (f := __dbg_x_opendisplay ()) | fail
  1368.     __dbg_io_info ("Current call stack in %1:", __dbg_fread (f))
  1369.     idx := __dbg_g_level
  1370.     line := __dbg_x_dispinit (f)
  1371.     repeat {
  1372.         idf := (line ? __dbg_ftab (__dbg_fupto (__dbg_g_white)))
  1373.         if idf == "global" then
  1374.             break
  1375.         if *idf > 0 then {
  1376.             __dbg_io_info ("(%1) %2", idx, idf)
  1377.             idx -:= 1
  1378.             }
  1379.         (line := __dbg_fread (f)) | break # Sanity.
  1380.         }
  1381.     __dbg_fclose (f)
  1382.     return OK_STATUS
  1383. end
  1384.  
  1385. procedure __dbg_cx_userdef (proced, ccode, macro)
  1386.     return __dbg_c_interp (macro)
  1387. end
  1388.  
  1389. procedure __dbg_cx_NOOP (proced, ccode)
  1390.     return OK_STATUS
  1391. end
  1392.  
  1393. #
  1394. #-------- General-purpose procedures --------
  1395. #
  1396.  
  1397. procedure __dbg_x_fld_adj (str)
  1398. # Part of 'subst' format string parsing.
  1399. # 'str' must be a parameter string identified by the beginning part of a
  1400. # placeholder ('%n').
  1401. # This procedure checks if the placeholder contains a fixed field width
  1402. # specifier.
  1403. # A fixed field specifier begins with '<' or '>' and continues with the field
  1404. # width expressed as a decimal literal.
  1405. # RETURNS 'str' possibly inserted in a fixed width field.
  1406. local just, init_p, res, wid
  1407. static fwf
  1408. initial fwf := '<>'
  1409.     init_p := &pos
  1410.     if (just := if ="<" then left else if =">" then right) &
  1411.         (wid := __dbg_finteger (__dbg_ftab (__dbg_fmany (&digits)))) then
  1412.         res := just (str, wid)
  1413.     else {
  1414.         res := str
  1415.         &pos := init_p
  1416.         }
  1417.     return res
  1418. end
  1419.  
  1420. procedure __dbg_x_subst (msg, parm)
  1421. # Substitutes parameters in a message template.
  1422. # 'msg' must be a message template (string).
  1423. # 'parm' must be a list of parameters (list of string-convertible), or null.
  1424. # It may also be a string.
  1425. local esc, res, sub
  1426. static p_digit
  1427. initial p_digit := '123456789'
  1428.     \parm | return msg
  1429.     parm := [__dbg_fstring (parm)]
  1430.     res := ""
  1431.     msg ? until __dbg_fpos (0) do {
  1432.         res ||:= __dbg_ftab (__dbg_fupto ('%\\') | 0)
  1433.         if ="%" then res ||:= {
  1434.             if __dbg_fany (p_digit) then {
  1435.                 sub := (\parm[__dbg_finteger (__dbg_fmove (1))] | "")
  1436.                 __dbg_x_fld_adj (sub)    
  1437.                 }
  1438.             else if __dbg_fany ('%') then
  1439.                 __dbg_fmove (1)
  1440.             else ""
  1441.             }
  1442.         else if ="\\" then res ||:= case esc := __dbg_fmove (1) of {
  1443.             "n": "\n"
  1444.             "t": "\t"
  1445.             default: esc
  1446.             }
  1447.         }
  1448.     return res
  1449. end
  1450.  
  1451. #
  1452. #-------- Input/Output procedures --------
  1453. #
  1454.  
  1455. procedure __dbg_io_cfl (format, parm[])
  1456. # Writes a conflict message to debugging output.
  1457. # 'format' must be a format string.
  1458. # 'parm' must be string-convertibles to insert into placeholders in the
  1459. # format string, if any.
  1460. # RETURNS 1 (i.e. always succeeds).
  1461.     __dbg_io_wrline ("[debug CONFLICT] " || __dbg_x_subst (format, parm))
  1462.     return 1
  1463. end
  1464.  
  1465. procedure __dbg_io_getline ()
  1466. # RETURNS the next line from debugging input, or
  1467. # FAILS on end of file.
  1468. local line
  1469.     (line := __dbg_fread (__dbg_g_in)) | {
  1470.         __dbg_fclose (__dbg_g_in)
  1471.         # Check for a macro definition marker.
  1472.         \(__dbg_g_in := __dbg_fpop (__dbg_g_src)) | fail
  1473.         if *__dbg_g_src > 0 then
  1474.             return __dbg_io_getline ()
  1475.         }
  1476.     __dbg_fwrite (\__dbg_g_out2, "$ ", \line)
  1477.     return \line
  1478. end
  1479.  
  1480. procedure __dbg_io_info (format, parm[])
  1481. # Writes an info message to debugging output.
  1482. # 'format' must be a format string.
  1483. # 'parm' must be string-convertibles to insert into placeholders in the
  1484. # format string, if any.
  1485.     __dbg_io_wrline (__dbg_x_subst (format, parm))
  1486. end
  1487.  
  1488. procedure __dbg_io_note (format, parm[])
  1489. # Writes a note to debugging output.
  1490. # 'format' must be a format string.
  1491. # 'parm' must be string-convertibles to insert into placeholders in the
  1492. # format string, if any.
  1493.     __dbg_io_wrline ("[debug NOTE] " || __dbg_x_subst (format, parm))
  1494. end
  1495.  
  1496. procedure __dbg_io_wrline (line)
  1497. # Writes a string and a newline to debugging output.
  1498. # 'line' must be the string to write.
  1499. # It may contains additional newlines.
  1500.     __dbg_fwrite (__dbg_g_out1, line)
  1501.     __dbg_fwrite (\__dbg_g_out2, line)
  1502. end
  1503.  
  1504. procedure __dbg_io_wrstr (line)
  1505. # Writes a string without a newline to debugging output.
  1506. # 'line' must be the string to write.
  1507. # It may contains additional newlines.
  1508.     __dbg_fwrites (__dbg_g_out1, line)
  1509.     __dbg_fwrites (\__dbg_g_out2, line)
  1510. end
  1511.  
  1512. #
  1513. #-------- Function initialization ---------
  1514. #
  1515. procedure __dbg_func_init ()
  1516.     __dbg_fany    := any
  1517.     __dbg_fclose    := close
  1518.     __dbg_fdelete    := delete
  1519.     __dbg_fexit    := exit
  1520.     __dbg_ffind    := find
  1521.     __dbg_fgetenv    := getenv
  1522.     __dbg_fimage    := image
  1523.     __dbg_finsert    := insert
  1524.     __dbg_finteger    := integer
  1525.     __dbg_fior    := ior
  1526.     __dbg_fishift    := ishift
  1527.     __dbg_fkey    := key
  1528.     __dbg_fmany    := many
  1529.     __dbg_fmatch    := match
  1530.     __dbg_fmove    := move
  1531.     __dbg_fpop    := pop
  1532.     __dbg_fpos    := pos
  1533.     __dbg_fproc    := proc
  1534.     __dbg_fpush    := push
  1535.     __dbg_fput    := put
  1536.     __dbg_fread    := read
  1537.     __dbg_fremove    := remove
  1538.     __dbg_freverse    := reverse
  1539.     __dbg_fright    := right
  1540.     __dbg_fsort    := sort
  1541.     __dbg_fstring    := string
  1542.     __dbg_ftab    := tab
  1543.     __dbg_ftable    := table
  1544.     __dbg_ftrim    := trim
  1545.     __dbg_ftype    := type
  1546.     __dbg_fupto    := upto
  1547.     __dbg_fwrite    := write
  1548.     __dbg_fwrites    := writes
  1549. end
  1550.  
  1551. #
  1552. #-------- Command initialization ---------
  1553. #
  1554.  
  1555. procedure __dbg_cmd_init ()
  1556. # Initialize command definitions.
  1557.     __dbg_g_cmd := __dbg_ftable ()
  1558. ### break
  1559.         __dbg_g_cmd["break"] := ["break", BREAK_CMD,
  1560. "    break [file] [line [: line]]\n_
  1561. Sets a breakpoint on a line or a range of lines. The file name (if present)\n_
  1562. must be one of the tweaked files (cf. the 'info files' command). If omitted\n_
  1563. the file of the current breakpoint is assumed. The identity of the new\n_
  1564. breakpoint (an integer) is displayed. It may be used in other commands.\n_
  1565. Besides an integer there are two other ways to identify a breakpoint,\n_
  1566. \    .    (dot) the current breakpoint,\n_
  1567. \    $    (dollar) the last breakpoint defined by a 'break' command.\n_
  1568. Breakpoint 0 (zero) is special; see the 'next' command.\n\n_
  1569. As a rule a breakpoint takes effect AFTER the breakpointed line has been\n_
  1570. executed. If two breakpoints are defined on the same line, only the latest\n_
  1571. is in effect.",
  1572. __dbg_cc_break, , __dbg_cx_break]
  1573. ### clear
  1574.         __dbg_g_cmd["clear"] := ["clear", CLEAR_CMD,
  1575. "    clear breakpoint brkpt\n_
  1576. Deletes breakpoint identified by 'brkpt'.\n_
  1577. \    clear condition brkpt\n_
  1578. Removes condition from breakpoint 'brkpt'. The breakpoint becomes\n_
  1579. unconditional.\n_
  1580. \    clear do brkpt\n_
  1581. Removes commands associated with breakpoint 'brkpt'.\n_
  1582. \    clear echo\n_
  1583. Stops output to echo file.\n_
  1584. \    clear macro name\n_
  1585. Removes macro identified by 'name'.",
  1586. __dbg_cc_clear, , __dbg_cx_clear]
  1587. ### comment
  1588.         __dbg_g_cmd["#"] := ["#", COMMENT_CMD,
  1589. "    # comment text\n_
  1590. A line beginning with '#' is ignored.",
  1591. __dbg_cc_SIMPLE, , __dbg_cx_NOOP]
  1592. ### condition
  1593.         __dbg_g_cmd["condition"] := ["condition", CONDITION_CMD,
  1594. "    condition brkpt expr\n_
  1595. Attaches a condition to breakpoint 'brkpt'. The expression 'expr' must\n_
  1596. succeed for a break to occur.",
  1597. __dbg_cc_condition, , __dbg_cx_condition]
  1598. ### do
  1599.         __dbg_g_cmd["do"] := ["do", DO_CMD,
  1600. "    do brkpt [<filename]\n_
  1601. Attaches commands to the breakpoint identified by 'brkpt'. The commands\n_
  1602. are entered interactively (terminate with 'end'), or are read from a file.",
  1603. __dbg_cc_do, , __dbg_cx_do]
  1604. ### end
  1605.         __dbg_g_cmd["end"] := ["end", END_CMD,
  1606. "    end\n_
  1607. Terminates a macro definition.",
  1608. __dbg_cc_end, , __dbg_cx_NOOP]
  1609. ### eprint
  1610.         __dbg_g_cmd["eprint"] := ["eprint", EPRINT_CMD,
  1611. "    eprint expr\n_
  1612. Prints image of every value generated by expression 'expr'.",
  1613. __dbg_cc_eprint, , __dbg_cx_eprint]
  1614. ### fprint
  1615.         __dbg_g_cmd["fprint"] := ["fprint", FPRINT_CMD,
  1616. "    fprint format-expr {; expr}\n_
  1617. Formatted print. The first expression must evaluate to a format string,\n_
  1618. possibly containing placeholders (%1, %2, etc). The result of evaluating\n_
  1619. remaining expressions will be substituted for the placeholders. You must\n_
  1620. make sure their values are string-convertible (the 'image' function is\n_
  1621. available). Insert '\\n' in format string to obtain newline.",
  1622. __dbg_cc_print, , __dbg_cx_fprint]
  1623. ### frame
  1624.         __dbg_g_cmd["frame"] := ["frame", FRAME_CMD,
  1625. "    frame [n]\n_
  1626. Shows a call frame. 'n' may be an integer frame number (obtained from\n_
  1627. the 'where' command), or may be omitted. Omitted frame number = current\n_
  1628. procedure. Negative frame number is relative to the current procedure.\n_
  1629. The command prints the image of all local variables.",
  1630. __dbg_cc_frame, , __dbg_cx_frame]
  1631. ### goon
  1632.         __dbg_g_cmd["goon"] := ["goon", GOON_CMD,
  1633. "    goon [nobreak]\n_
  1634. Resumes execution. With 'nobreak': lets the program run to completion\n_
  1635. without breaking.",
  1636. __dbg_cc_goon, , __dbg_cx_goon]
  1637. ### help
  1638.         __dbg_g_cmd["help"] := ["help", HELP_CMD,
  1639. "    help [command]\n_
  1640. Displays information. Prints short command description if command keyword\n_
  1641. is included. Otherwise prints list of available commands.",
  1642. __dbg_cc_help, , __dbg_cx_help]
  1643. ### ignore
  1644.         __dbg_g_cmd["ignore"] := ["ignore", IGNORE_CMD,
  1645. "    ignore brkpt count\n_
  1646. Sets the ignore counter of breakpoint 'brkpt'. 'count' may be a positive\n_
  1647. or negative integer. It replaces the previous ignore counter value.\n_
  1648. A breakpoint with a non-zero ignore count does not cause a break, but the\n_
  1649. ignore count is decremented by 1.",
  1650. __dbg_cc_ignore, , __dbg_cx_ignore]
  1651. ### info
  1652.         __dbg_g_cmd["info"] := ["info", INFO_CMD,
  1653. "    info breakpoint [brkpt]\n_
  1654. Prints info about breakpoint identified by 'brkpt', or about all\n_
  1655. breakpoints if 'brkpt' is omitted.\n_
  1656. \    info echo\n_
  1657. Prints the current 'echo' file name, if any.\n_
  1658. \    info files\n_
  1659. Prints names of source files with tweaked ucode in this program.\n_
  1660. \    info globals [substr]\n_
  1661. Prints names of global variables. The optional substring limits output\n_
  1662. to global names containing this substring.\n_
  1663. \    info locals\n_
  1664. Prints names of all local variables in current procedure.\n_
  1665. \    info macros\n_
  1666. Prints names of all currently defined macros.\n_
  1667. \    info trace\n_
  1668. Prints the current value of &trace.\n_
  1669. \    info version\n_
  1670. Prints itweak and runtime versions.",
  1671. __dbg_cc_info, , __dbg_cx_info]
  1672. ### macro
  1673.         __dbg_g_cmd["macro"] := ["macro", MACRO_CMD,
  1674. "    macro name\n_
  1675. Creates a new command called 'name'. The command will consist of\n_
  1676. subsequent lines, up to a line containing 'end'.\n_
  1677. \    macro name <filename\n_
  1678. As above, but macro definition read from a file. 'end' command optional.",
  1679. __dbg_cc_macro, , __dbg_cx_macro]
  1680. ### next
  1681.         __dbg_g_cmd["next"] := ["next", NEXT_CMD,
  1682. "    next [count]\n_
  1683. Resumes execution as if a breakpoint were defined on every line. An\n_
  1684. ignore count may be included (see the 'ignore' command). A break\n_
  1685. caused by 'next' is considered breakpoint 0 (zero), even if an\n_
  1686. ordinary breakpoint is in effect on the same line. The 'condition',\n_
  1687. 'do', 'info' commands accept 0 as a breakpoint number.",
  1688. __dbg_cc_next, , __dbg_cx_next]
  1689. ### print
  1690.         __dbg_g_cmd["print"] := ["print", PRINT_CMD,
  1691. "    print expr {; expr}\n_
  1692. Evaluates and print image of expression(s). Only the first value from\n_
  1693. each expression is printed. '&fail' printed if an expression fails.",
  1694. __dbg_cc_print, , __dbg_cx_print]
  1695. ### set
  1696.         __dbg_g_cmd["set"] := ["set", SET_CMD,
  1697. "    set echo filename\n_
  1698. Starts echoing output to a file.\n_
  1699. \    set prelude [<file]\n_
  1700. Defines a macro to be exeucted at breaks. The default prelude is\n_
  1701. \    fprint \"[%1] %2 (%3:%4)\\n\";&bp;&proc;&file;&line\n_
  1702. It prints breakpoint number, procedure name, source file name, and\n_
  1703. line number.\n_
  1704. \    set postlude [<file]\n_
  1705. Defines a macro to be executed when resuming execution. The default\n_
  1706. postlude does nothing.",
  1707. __dbg_cc_set, , __dbg_cx_set]
  1708. ### source
  1709.         __dbg_g_cmd["source"] := ["source", SOURCE_CMD,
  1710. "    source filename\n_
  1711. Reads commands from a file. Takes effect immediately when used in a macro\n_
  1712. definition.",
  1713. __dbg_cc_source, , __dbg_cx_NOOP]
  1714. ### stop
  1715.         __dbg_g_cmd["stop"] := ["stop", STOP_CMD,
  1716. "    stop\n_
  1717. Stops the program and terminates the debugging session.",
  1718. __dbg_cc_SIMPLE, , __dbg_cx_stop]
  1719. ### trace
  1720.         __dbg_g_cmd["trace"] := ["trace", TRACE_CMD,
  1721. "    trace count\n_
  1722. Sets the value of the Icon trace counter (&trace) to 'count'.",
  1723. __dbg_cc_trace, , __dbg_cx_trace]
  1724. ### where
  1725.         __dbg_g_cmd["where"] := ["where", WHERE_CMD,
  1726. "    where\n_
  1727. Prints the call chain leading up to the current procedure.\n_
  1728. Displays frame numbers which may be used by the 'frame' command.",
  1729. __dbg_cc_SIMPLE, , __dbg_cx_where]
  1730. end
  1731.  
  1732. ############### EXPRESSIONS ##############################
  1733. #
  1734. # Parses a fair subset of Icon expressions.
  1735. # Compiles them into a linear post-fix representation.
  1736. # Evaluates.
  1737. # Somewhat adapted to the debugging environment, but
  1738. # generally useful with small modifications.
  1739. #
  1740. ##########################################################
  1741.  
  1742. #
  1743. #-------------- Expression management constants ----------
  1744. #
  1745.  
  1746. $define IDENT_T        1
  1747. $define INTEGER_T    2
  1748. $define STRING_T    3
  1749. $define SPECIAL_T    4
  1750. $define FIELD_T        5
  1751. $define LIST_T        6
  1752. $define EXPR_T        8
  1753. $define ELIST_T        9
  1754. $define UNOP_T        10
  1755. $define BINOP_T        11
  1756. $define TEROP_T        12
  1757. $define INVOKE_T    13
  1758.  
  1759. $define NOTN_OP        901
  1760. $define ISN_OP        902
  1761. $define SIZ_OP        903
  1762. $define BNG_OP        904
  1763. $define NEG_OP        905
  1764.  
  1765. $define ALT_OP        1501
  1766. $define CNJ_OP        1401
  1767. # N -- numerical comparison.
  1768. $define NEQ_OP        1301
  1769. $define NNE_OP        1302
  1770. $define NLE_OP        1303
  1771. $define NLT_OP        1304
  1772. $define NGE_OP        1305
  1773. $define NGT_OP        1306
  1774. # L -- lexical comparison.
  1775. $define LLT_OP        1307
  1776. $define LLE_OP        1308
  1777. $define LEQ_OP        1309
  1778. $define LNE_OP        1310
  1779. $define LGE_OP        1311
  1780. $define LGT_OP        1312
  1781. $define EQ_OP        1313
  1782. $define NE_OP        1314
  1783. $define ADD_OP        1201
  1784. $define SUBTR_OP    1202
  1785. $define UNION_OP    1203
  1786. $define DIFF_OP        1204
  1787. $define CAT_OP        1101
  1788. $define LCAT_OP        1102
  1789. $define MUL_OP        1001
  1790. $define DIV_OP        1002
  1791. $define REM_OP        1003
  1792. $define ISCT_OP        1004
  1793. $define EXP_OP        1001
  1794. $define INVOKE_OP    801
  1795. $define SSC_OP        802
  1796. $define PART_OP        803
  1797. $define FLD_OP        804
  1798.  
  1799. $define CLOCK_SP    1
  1800. $define CURRENT_SP    2
  1801. $define DATE_SP        3
  1802. $define DATELINE_SP    4
  1803. $define POS_SP        5
  1804. $define REGIONS_SP    6
  1805. $define SOURCE_SP    7
  1806. $define STORAGE_SP    8
  1807. $define SUBJECT_SP    9
  1808. $define VERSION_SP    10
  1809.  
  1810. $define BREAK_SP    101
  1811. $define FILE_SP        102
  1812. $define LEVEL_SP    103
  1813. $define LINE_SP        104
  1814. $define PROC_SP        105
  1815. $define TRACE_SP    106
  1816.  
  1817. #
  1818. #-------------- Expression parsing ----------------------        
  1819. #
  1820.  
  1821. procedure __dbg_e_compile (str)
  1822. # Compiles one or more expressions separated by a semicolon.
  1823. # 'str' must be the candidate expression (string).
  1824. # RETURNS a list of lists where each sublist has the following components:
  1825. # (1) The compiled expression in postfix representation (list).
  1826. # This representation can be used with the '__dbg_e_eval' procedure.
  1827. # (2) The expression source string.
  1828. # FAILS on conflict.
  1829. # SIDE EFFECT: Assigns a message (string) to '__dbg_ge_message' on conflict;
  1830. # assigns &null otherwise.
  1831. local elist, res1, res2, pos1, pos2
  1832.     elist := []
  1833.     # Parse the expression(s).
  1834.     str ? repeat {
  1835.         pos1 := &pos
  1836.         (res1 := 1(__dbg_e_expr(), pos2:= &pos, __dbg_e_ws (),
  1837.             (__dbg_fpos (0) | __dbg_fany (';')))) | {
  1838.             __dbg_ge_message := "Expression syntax error."
  1839.             fail
  1840.             }
  1841.         # Linearize, convert to postfix.
  1842.         __dbg_ge_message := &null
  1843.         res2 := []
  1844.         __dbg_e_ecode (res1, res2)
  1845.         # Check for conflict.
  1846.         /__dbg_ge_message | fail
  1847.         __dbg_fput (elist, [res2, str[pos1:pos2]])
  1848.         if __dbg_fpos (0) then
  1849.             break
  1850.         else {
  1851.             __dbg_fmove (1)
  1852.             __dbg_e_ws ()
  1853.             }
  1854.         }
  1855.     return elist
  1856. end
  1857.  
  1858. procedure __dbg_e_expr()
  1859.     __dbg_ftab (__dbg_fmany (' \t'))
  1860.     suspend [__dbg_e_term()] |
  1861.          ([__dbg_e_term(), __dbg_e_bin()] ||| __dbg_e_expr())
  1862. end
  1863.  
  1864. procedure __dbg_e_term()
  1865.     __dbg_ftab (__dbg_fmany (' \t'))
  1866.     suspend [__dbg_e_factor()] |
  1867.         [__dbg_e_factor(), __dbg_e_form()] |
  1868.         [__dbg_e_un(), __dbg_e_factor()] |
  1869.         [__dbg_e_un(), __dbg_e_factor(), __dbg_e_form()]
  1870. end
  1871.     
  1872. procedure __dbg_e_form()
  1873.     __dbg_ftab (__dbg_fmany (' \t'))
  1874.     suspend 2(=".", [FLD_OP, [FIELD_T, __dbg_e_idf()]]) |
  1875.         2(="[", [SSC_OP, __dbg_e_expr()], ="]") |
  1876.         2(="(", [INVOKE_OP, __dbg_e_elist()], =")") |
  1877.         2(="[", [PART_OP, __dbg_e_expr(),
  1878.             3(__dbg_e_ws(), =":", __dbg_e_expr())], ="]") |
  1879.          (2(=".", [FLD_OP, [FIELD_T, __dbg_e_idf()]]) ||| __dbg_e_form()) |
  1880.         (2(="[", [SSC_OP, __dbg_e_expr()], ="]") ||| __dbg_e_form()) |
  1881.         (2(="(", [INVOKE_OP, __dbg_e_elist()], =")") ||| __dbg_e_form()) |
  1882.         (2(="[", [PART_OP, __dbg_e_expr(),
  1883.             3(__dbg_e_ws(), =":", __dbg_e_expr())], ="]") |||
  1884.             __dbg_e_form())
  1885. end
  1886.  
  1887. procedure __dbg_e_elist()
  1888.     __dbg_ftab (__dbg_fmany (' \t'))
  1889.     suspend [] |
  1890.         [__dbg_e_expr()] |
  1891.         [__dbg_e_expr()] ||| 3(__dbg_e_ws(), =",", __dbg_e_elist())
  1892. end
  1893.  
  1894. procedure __dbg_e_factor()
  1895.     __dbg_ftab (__dbg_fmany (' \t'))
  1896.     suspend [IDENT_T, __dbg_e_idf()] |
  1897.         [INTEGER_T, __dbg_e_ilit()] |
  1898.         [STRING_T, __dbg_e_slit()] |
  1899.         [SPECIAL_T, (="&", __dbg_e_idf())] |
  1900.         2(="(", [EXPR_T, __dbg_e_expr()], __dbg_e_ws(), =")") |
  1901.         2(="[", [LIST_T, __dbg_e_elist()], __dbg_e_ws(), ="]")
  1902. end
  1903.  
  1904. procedure __dbg_e_idf()
  1905. static char1, char2
  1906. initial {
  1907.     char1 := &ucase ++ &lcase ++ '_'
  1908.     char2 := char1 ++ &digits
  1909.     }
  1910.     suspend __dbg_ftab (__dbg_fmany (char1)) || (__dbg_ftab (__dbg_fmany (char2)) | "")
  1911. end
  1912.  
  1913. procedure __dbg_e_ilit()
  1914.     suspend __dbg_ftab (__dbg_fmany (&digits))
  1915. end
  1916.  
  1917. procedure __dbg_e_strend()
  1918. static signal, nonsignal
  1919. initial {
  1920.     signal := '\"\\'
  1921.     nonsignal := ~signal
  1922.     }
  1923.     suspend 2(="\"", "") |
  1924.         1(__dbg_e_stresc(), ="\"") |
  1925.         (__dbg_e_stresc() || __dbg_ftab (__dbg_fmany (nonsignal)) || __dbg_e_strend()) |
  1926.         (__dbg_e_stresc() || __dbg_e_strend())
  1927. end
  1928.  
  1929. procedure __dbg_e_stresc()
  1930.     suspend (="\\n", "\n") |
  1931.         (="\\t", "\t") |
  1932.         (="\\r", "\r") |
  1933.         (="\\", __dbg_fmove (1))
  1934. end
  1935.  
  1936. procedure __dbg_e_slit()
  1937. static signal, nonsignal
  1938. initial {
  1939.     signal := '\"\\'
  1940.     nonsignal := ~signal
  1941.     }
  1942.     suspend 2(="\"",
  1943.         (__dbg_ftab (__dbg_fmany (nonsignal)) || __dbg_e_strend()) | __dbg_e_strend())
  1944. end
  1945.  
  1946. procedure __dbg_e_un()
  1947. # Sequence of unary operators.
  1948. # Always succeeds.
  1949. # NOTE: Assumes no space between operators.
  1950. static unop
  1951. initial unop := '\\/*!-'
  1952.     __dbg_ftab (__dbg_fmany (' \t'))
  1953.     suspend [UNOP_T, __dbg_ftab (__dbg_fmany (unop))]
  1954. end
  1955.  
  1956. procedure __dbg_e_bin()
  1957. # Binary operators.
  1958. static optab
  1959. initial {
  1960.     # Table of operators.
  1961.     # Operators are coded as decimal integers where the hundreds
  1962.     # digit defines precedence.
  1963.     optab := table()
  1964.     optab["|"] :=        ALT_OP
  1965.     optab["&"] :=        CNJ_OP
  1966.     optab["="] :=        NEQ_OP
  1967.     optab["~="] :=        NNE_OP
  1968.     optab["<="] :=        NLE_OP
  1969.     optab["<"] :=        NLT_OP
  1970.     optab[">="] :=        NGE_OP
  1971.     optab[">"] :=        NGT_OP
  1972.     optab["<<"] :=        LLT_OP
  1973.     optab["<<="] :=        LLE_OP
  1974.     optab["=="] :=        LEQ_OP
  1975.     optab["~=="] :=        LNE_OP
  1976.     optab[">>="] :=        LGE_OP
  1977.     optab[">>"] :=        LGT_OP
  1978.     optab["==="] :=        EQ_OP
  1979.     optab["~==="] :=    NE_OP
  1980.     optab["+"] :=        ADD_OP
  1981.     optab["-"] :=        SUBTR_OP
  1982.     optab["++"] :=        UNION_OP
  1983.     optab["--"] :=        DIFF_OP
  1984.     optab["||"] :=        CAT_OP
  1985.     optab["|||"] :=        LCAT_OP
  1986.     optab["*"] :=        MUL_OP
  1987.     optab["/"] :=        DIV_OP
  1988.     optab["%"] :=        REM_OP
  1989.     optab["**"] :=        ISCT_OP
  1990.     optab["^"] :=        EXP_OP
  1991.     }
  1992.     __dbg_ftab (__dbg_fmany (' \t'))
  1993.     suspend \optab[__dbg_fmove (3)] |
  1994.         \optab[__dbg_fmove (2)] |
  1995.         \optab[__dbg_fmove (1)] |
  1996.         \optab[=("~===")]
  1997. end
  1998.  
  1999. procedure __dbg_e_ws()
  2000. # Removes optional white space.
  2001. # The point is that it always succeeds.
  2002.     __dbg_ftab (__dbg_fmany (' \t'))
  2003.     return 1
  2004. end
  2005.  
  2006. #-------------- Linearization ----------------------        
  2007.  
  2008. procedure __dbg_e_ecode (ex, res)
  2009. # 'Evaluates' the list resulting from pattern matching.
  2010. # Produces a single list with everything in postfix order.
  2011. # 'ex' must be an expression in the form that '__dbg_e_compile' generates.
  2012. # 'res' must be an (empty) list where the expression elements are to
  2013. # be inserted.
  2014. # Always FAILS.
  2015. # SIDE EFFECT: Adds elements to 'res'.
  2016. # Assigns a message string to '__dbg_ge_message' on conflict.
  2017. local opnd, oprt, op_stack
  2018.     if *ex = 1 then
  2019.         __dbg_e_tcode (ex[1], res)
  2020.     else {
  2021.         op_stack := []
  2022.         opnd := create !ex
  2023.         __dbg_e_tcode (@opnd, res)
  2024.         while oprt := @opnd do {
  2025.             while (op_stack[1]/100) <= (oprt/100) do
  2026.                 __dbg_fput (res, __dbg_e_proc ([BINOP_T, __dbg_fpop (op_stack),]))
  2027.             __dbg_fpush (op_stack, oprt)
  2028.             __dbg_e_tcode (@opnd, res)
  2029.             }
  2030.         while __dbg_fput (res, __dbg_e_proc ([BINOP_T, __dbg_fpop (op_stack),]))
  2031.         }
  2032. end
  2033.  
  2034. procedure __dbg_e_tcode (tm, res)
  2035. # Disentangles a term.
  2036. local comp, unary
  2037. static special, unop
  2038. initial {
  2039.     special := __dbg_ftable ()
  2040.     # The 'normal' keywords.
  2041.     special["clock"] :=    CLOCK_SP
  2042.     special["current"] :=    CURRENT_SP
  2043.     special["date"] :=    DATE_SP
  2044.     special["dateline"] :=    DATELINE_SP
  2045.     special["pos"] :=    POS_SP
  2046.     special["regions"] :=    REGIONS_SP
  2047.     special["source"] :=    SOURCE_SP
  2048.     special["storage"] :=    STORAGE_SP
  2049.     special["subject"] :=    SUBJECT_SP
  2050.     special["trace"] :=    TRACE_SP
  2051.     special["version"] :=    VERSION_SP
  2052.     
  2053.     # The special keywords.
  2054.     special["bp"] :=BREAK_SP
  2055.     special["breakpoint"] :=BREAK_SP
  2056.     special["file"]    :=    FILE_SP
  2057.     special["level"] :=    LEVEL_SP
  2058.     special["line"] :=    LINE_SP
  2059.     special["proc"] :=    PROC_SP
  2060.  
  2061.     unop := __dbg_ftable ()
  2062.     unop["\\"] :=    NOTN_OP
  2063.     unop["/"] :=    ISN_OP
  2064.     unop["*"] :=    SIZ_OP
  2065.     unop["!"] :=    BNG_OP
  2066.     unop["-"] :=    NEG_OP
  2067.     }
  2068.     every comp := !tm do case comp[1] of {
  2069.     UNOP_T:    unary := comp    # Save for later.
  2070.     INTEGER_T: {
  2071.         comp[2] := __dbg_finteger (comp[2])
  2072.         __dbg_fput (res, comp)
  2073.         }
  2074.     SPECIAL_T: {
  2075.         if comp[2] := \special[comp[2]] then
  2076.             __dbg_fput (res, comp)
  2077.         else
  2078.             __dbg_ge_message := "'" || comp[2] ||
  2079.                 "': unrecognized special identifier."
  2080.         }
  2081.     EXPR_T:        __dbg_e_ecode (comp[2], res)
  2082.     LIST_T:    {
  2083.         every __dbg_e_ecode (!comp[2], res)
  2084.         __dbg_fput (res, [LIST_T, *comp[2]])
  2085.         }
  2086.     (FLD_OP | SSC_OP | INVOKE_OP | PART_OP) :
  2087.         __dbg_e_fcode (comp, res)
  2088.     default:    __dbg_fput (res, comp)
  2089.     # This includes: IDENT_T, STRING_T
  2090.     }
  2091.     every __dbg_fput (res, __dbg_e_proc ([UNOP_T, unop[!__dbg_freverse ((\unary)[2])],]))
  2092. end
  2093.  
  2094. procedure __dbg_e_fcode (fm, res)
  2095. # Disentangles a form.
  2096. # The operators have the same precedence; stack not needed.
  2097. local comp, opnd, oprt
  2098.     comp := create !fm
  2099.     while oprt := @comp do {
  2100.         opnd := @comp    # There is at least one operand.
  2101.         case oprt of {
  2102.         FLD_OP:    {
  2103.             __dbg_fput (res, opnd)
  2104.             __dbg_fput (res, [BINOP_T, oprt, __dbg_e_field])
  2105.         }
  2106.         SSC_OP:    {
  2107.             __dbg_e_ecode (opnd, res)
  2108.             __dbg_fput (res, [BINOP_T, oprt, __dbg_fproc ("[]", 2)])
  2109.         }
  2110.         INVOKE_OP: {
  2111.             every __dbg_e_ecode (!opnd, res)
  2112.             __dbg_fput (res, [INVOKE_T, *opnd])
  2113.         }
  2114.         PART_OP: {
  2115.             __dbg_e_ecode (opnd, res)
  2116.             __dbg_e_ecode (@comp, res)
  2117.             __dbg_fput (res, [TEROP_T, oprt, __dbg_fproc ("[:]", 3)])
  2118.         }
  2119.         default: __dbg_ge_message := __dbg_fimage (oprt) || ": weird operator."
  2120.         }
  2121.         }
  2122. end
  2123.  
  2124. procedure __dbg_e_proc (op_d)
  2125. # 'op_d' must be an operator descriptor (list(3)).
  2126. # RETURNS the descriptor with the 3rd component filled in by a
  2127. # procedure/function.
  2128. static opt
  2129. initial {
  2130.     opt := __dbg_ftable ()
  2131.     opt[NOTN_OP] :=        __dbg_fproc ("\\", 1)
  2132.     opt[ISN_OP] :=        __dbg_fproc ("/", 1)
  2133.     opt[SIZ_OP] :=        __dbg_fproc ("*", 1)
  2134.     opt[BNG_OP] :=        __dbg_fproc ("!", 1)
  2135.     opt[NEG_OP] :=        __dbg_fproc ("-", 1)
  2136.     opt[ALT_OP] :=        __dbg_e_alt
  2137.     opt[CNJ_OP] :=        __dbg_e_cnj
  2138.     opt[NEQ_OP] :=        __dbg_fproc ("=", 2)
  2139.     opt[NNE_OP] :=        __dbg_fproc ("~=", 2)
  2140.     opt[NLE_OP] :=        __dbg_fproc ("<=", 2)
  2141.     opt[NLT_OP] :=        __dbg_fproc ("<", 2)
  2142.     opt[NGE_OP] :=        __dbg_fproc (">=", 2)
  2143.     opt[NGT_OP] :=        __dbg_fproc (">", 2)
  2144.     opt[LLT_OP] :=        __dbg_fproc ("<<", 2)
  2145.     opt[LLE_OP] :=        __dbg_fproc ("<<=", 2)
  2146.     opt[LEQ_OP] :=        __dbg_fproc ("==", 2)
  2147.     opt[LNE_OP] :=        __dbg_fproc ("~==", 2)
  2148.     opt[LGE_OP] :=        __dbg_fproc (">>=", 2)
  2149.     opt[LGT_OP] :=        __dbg_fproc (">>", 2)
  2150.     opt[EQ_OP] :=        __dbg_fproc ("===", 2)
  2151.     opt[NE_OP] :=        __dbg_fproc ("~===", 2)
  2152.     opt[ADD_OP] :=        __dbg_fproc ("+", 2)
  2153.     opt[SUBTR_OP] :=    __dbg_fproc ("-", 2)
  2154.     opt[UNION_OP] :=    __dbg_fproc ("++", 2)
  2155.     opt[DIFF_OP] :=        __dbg_fproc ("--", 2)
  2156.     opt[CAT_OP] :=        __dbg_fproc ("||", 2)
  2157.     opt[LCAT_OP] :=        __dbg_fproc ("|||", 2)
  2158.     opt[MUL_OP] :=        __dbg_fproc ("*", 2)
  2159.     opt[DIV_OP] :=        __dbg_fproc ("/", 2)
  2160.     opt[REM_OP] :=        __dbg_fproc ("%", 2)
  2161.     opt[ISCT_OP] :=        __dbg_fproc ("**", 2)
  2162.     opt[EXP_OP] :=        __dbg_fproc ("^", 2)
  2163.     opt[SSC_OP] :=        __dbg_fproc ("[]", 2)
  2164.     opt[PART_OP] :=        __dbg_fproc ("[:]", 2)
  2165.     opt[FLD_OP] :=        __dbg_e_field
  2166.     }
  2167.     op_d[3] := opt[op_d[2]]
  2168.     return op_d
  2169. end
  2170.  
  2171. #-------------- Evaluation ----------------------        
  2172.  
  2173. procedure __dbg_e_eval (expr)
  2174. # Evaluates a compiled expression.
  2175. # 'expr' must be an expression using the representation created by
  2176. # '__dbg_e_compile' (list).
  2177. # GENERATES all expression values.
  2178. # SIDE EFFECT: Assigns a message (string) to '__dbg_ge_message' on conflict;
  2179. # assigns &null otherwise.
  2180. local val
  2181.     __dbg_ge_message := &null
  2182.     &error := -1
  2183.     every val := __dbg_e_eval1 (expr, []) do {
  2184.         &error := 0
  2185.         suspend val
  2186.         __dbg_ge_message := &null
  2187.         &error := -1
  2188.         }
  2189.     if &error < -1 then
  2190.         __dbg_ge_message := "Error number " || &errornumber || ": " ||
  2191.         &errortext || "." ||
  2192.         (("\nOffending value: " || __dbg_fimage (\&errorvalue) || ".") | "")
  2193.     &error := 0
  2194. end
  2195.  
  2196. procedure __dbg_e_alt (opnd1, opnd2)
  2197. # Our version of alternation.
  2198.     suspend (opnd1 | opnd2)
  2199. end
  2200.  
  2201. procedure __dbg_e_cnj (opnd1, opnd2)
  2202. # Our version of conjunction.
  2203.     suspend (opnd1 & opnd2)
  2204. end
  2205.  
  2206. procedure __dbg_e_field (opnd1, opnd2)
  2207. # Record field access.
  2208. # Any better way to determine if a value is a record of any type?
  2209. static builtin
  2210. initial {
  2211.     builtin := __dbg_ftable ()
  2212.     builtin["co-expression"] := 1
  2213.     builtin["cset"] := 1
  2214.     builtin["file"] := 1
  2215.     builtin["integer"] := 1
  2216.     builtin["list"] := 1
  2217.     builtin["null"] := 1
  2218.     builtin["procedure"] := 1
  2219.     builtin["real"] := 1
  2220.     builtin["set"] := 1
  2221.     builtin["string"] := 1
  2222.     builtin["table"] := 1
  2223.     }
  2224.     if \builtin[__dbg_ftype (opnd1)] then {
  2225.         __dbg_ge_message := "Record expected; found " || __dbg_fimage (opnd1)
  2226.         fail
  2227.         }
  2228.     suspend opnd1[opnd2]
  2229. end
  2230.  
  2231. procedure __dbg_e_ident (idf)
  2232. # Evaluates an identifier.
  2233. local val
  2234.     (val := ((__dbg_ge_singular ~=== __dbg_g_local[idf]) | variable (idf))) | {
  2235.         __dbg_ge_message := "Identifier '" || idf || "' not visible."
  2236.         fail
  2237.         }
  2238.     suspend val
  2239. end
  2240.  
  2241. procedure __dbg_e_special (sp_code)
  2242. # Evaluates a special identifier.
  2243.     suspend case sp_code of {
  2244.     # Regular Icon keyword variables.
  2245.     CLOCK_SP:    &clock
  2246.     CURRENT_SP:    ¤t
  2247.     DATE_SP:    &date
  2248.     DATELINE_SP:    &dateline
  2249.     POS_SP:        &pos
  2250.     REGIONS_SP:    ®ions
  2251.     SOURCE_SP:    &source
  2252.     STORAGE_SP:    &storage
  2253.     SUBJECT_SP:    &subject
  2254.     VERSION_SP:    &version
  2255.     # Special keywords.
  2256.     BREAK_SP:    (\__dbg_g_where[WHERE_BRKP])[BRKP_ID]
  2257.     FILE_SP:    __dbg_g_where[WHERE_FILE]
  2258.     LEVEL_SP:    __dbg_g_level
  2259.     LINE_SP:    __dbg_g_where[WHERE_LINE]
  2260.     PROC_SP:    __dbg_g_where[WHERE_PROC]
  2261.     TRACE_SP:    __dbg_g_trace
  2262.     default: {
  2263.         __dbg_ge_message := __dbg_fimage (sp_code) ||
  2264.             ": weird special identifier code."
  2265.         fail
  2266.         }
  2267.     }
  2268. end
  2269.  
  2270. procedure __dbg_e_eval1 (expr, stack)
  2271. # Evaluates an expression.
  2272. # 'stack' must be the current evaluation stack (list).
  2273. # The procedure is recursive; the initial invocation must supply an
  2274. # empty list.
  2275. local comp
  2276.     (comp := expr[1]) | while suspend __dbg_fpop (stack) | fail
  2277.     suspend __dbg_e_eval1 (expr[2:0], case comp[1] of {
  2278.     IDENT_T:    stack ||| [__dbg_e_ident (comp[2])]
  2279.     SPECIAL_T:    stack ||| [__dbg_e_special (comp[2])]
  2280.     LIST_T:        stack[1:-comp[2]] ||| [stack[-comp[2]:0]]
  2281.     UNOP_T:        stack[1:-1] ||| [comp[3](stack[-1])]
  2282.     BINOP_T:    stack[1:-2] ||| [comp[3]!stack[-2:0]]
  2283.     TEROP_T:    stack[1:-3] ||| [comp[3]!stack[-3:0]]
  2284.     INVOKE_T:    stack[1:-(comp[2]+1)] |||
  2285.                 [stack[-(comp[2]+1)]!stack[-comp[2]:0]]
  2286.     default:    stack ||| [comp[2]]
  2287.     })
  2288. end
  2289.