debugger_cm.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. $(function () {
  2. var repl = new CodeMirrorREPL('debugger_cm', {
  3. mode: "python",
  4. theme: "solarized light",
  5. }),
  6. compilableLines = [],
  7. // Run code regex
  8. re_run = /\brun\b/,
  9. // Continue
  10. re_cont = /\bcont\b/,
  11. // Step instruction
  12. re_step = /\bstep\b/,
  13. //test for empty line.
  14. re_emptyline = /^\\s*$/,
  15. // test for view locals
  16. re_viewlocals = /view local(s*)( *)(\w*)/,
  17. // test for view globals
  18. re_viewglobals = /view global(s*)( *)(\w*)/,
  19. // test for help
  20. re_help = /\bhelp\b/,
  21. // test for set breakpoints
  22. re_break = /\bbreak\b (\d+)/,
  23. // test for temporary breakpoints
  24. re_tbreak = /\btbreak\b (\d+)/,
  25. // test for where command
  26. re_where = /\bwhere\b/,
  27. // test for view breakpoints
  28. re_view_bp = /\bview bp\b/,
  29. // test for clear breakpoints
  30. re_clear_bp = /\bclear\b( \d+)+/,
  31. // test for disable breakpoints
  32. re_disable_bp = /\bdisable\b( \d+)+/,
  33. // test for enable breakpoints
  34. re_enable_bp = /\benable\b( \d+)+/,
  35. // test for ignore count breakpoint
  36. re_ignore_count = /\bignore count\b (\d+) (\d+)/,
  37. // test for current execution line
  38. re_list = /\blist\b/,
  39. // test for going up the stack
  40. re_up = /up/,
  41. // test for going down the stack
  42. re_down = /down/,
  43. // editor filename
  44. editor_filename = "<stdin>",
  45. // cmd list
  46. cmd_list = {
  47. "help": "Display the list of commands available in the debugger",
  48. "where": "Print the stack trace",
  49. "down": "Move the current frame one level down in the stack trace (to a newer frame)",
  50. "up": "Move the current frame one level up in the stack trace (to an older frame)",
  51. "break <lineno>": "Set the breakpoint at specified line number",
  52. "tbreak <lineno>": "Temporary breakpoint, which is removed automatically when it is first hit. The arguments are the same as break.",
  53. "clear bpnumber [bpnumber ...]]": "If space separated breakpoints are specifed then that breakpoint is cleared otherwise all breakpoints are cleared",
  54. "disable [bpnumber [bpnumber ...]]": "Disables the breakpoints given as a space separated list of breakpoint numbers. Disabling a breakpoint means it cannot cause the program to stop execution",
  55. "enable [bpnumber [bpnumber ...]]": "Enables the breakpoints specified.",
  56. "ignore bpnumber [count]": "Sets the ignore count for the given breakpoint number. If count is omitted, the ignore count is set to 0",
  57. "run": "Run / Restart the current program in the editor",
  58. "step": "Step Over to the next instruction",
  59. "cont": "Continue execution till next breakpoint is hit or application terminates",
  60. "view local(s) <var>": "View all the locals at the current execution point. 'view locals' shows all locals. 'view local <var>' shows just one var",
  61. "view global(s) <var>": "View all the globals at the current execution point. 'view locals' shows all locals. 'view local <var>' shows just one var",
  62. "list": "List source code for the current file. Without arguments, list 11 lines around the current line or continue the previous listing."
  63. };
  64. // Debugger
  65. repl.sk_debugger = new Sk.Debugger(editor_filename, repl),
  66. // code editor
  67. repl.sk_code_editor = window.code_editor;
  68. repl.isBalanced = function (code) {
  69. var lines = code.split('\n'),
  70. depth = 0,
  71. mlsopened = false,
  72. l;
  73. for (l = 0; l < lines.length; l = l + 1) {
  74. if (lines[l].match(/'''/) !== null && lines[l].match(/'''/).length === 1) {
  75. mlsopened = !mlsopened;
  76. }
  77. if (!mlsopened && lines[l].substr(lines[l].length - 1) === ":") {
  78. depth = depth + 1;
  79. }
  80. if (!mlsopened && lines[l] === "" && depth > 0) {
  81. depth = 0 ;
  82. }
  83. }
  84. return depth === 0 && !mlsopened;
  85. };
  86. repl.setHeight(28 * 10);
  87. repl.get_source_line = function(lineno) {
  88. return repl.sk_code_editor.getLine(lineno);
  89. };
  90. repl.run_code = function(code) {
  91. Sk.configure({
  92. output: repl.print,
  93. debugout: window.jsoutf,
  94. read: window.builtinRead,
  95. yieldLimit: null,
  96. execLimit: null,
  97. debugging: true,
  98. breakpoints: repl.sk_debugger.check_breakpoints.bind(repl.sk_debugger),
  99. });
  100. Sk.canvas = "mycanvas";
  101. if (repl.sk_code_editor.getValue().indexOf('turtle') > -1 ) {
  102. $('#mycanvas').show()
  103. }
  104. Sk.pre = "edoutput";
  105. Sk.pre = "codeoutput";
  106. (Sk.TurtleGraphics || (Sk.TurtleGraphics = {})).target = 'mycanvas';
  107. try {
  108. var susp_handlers = {};
  109. susp_handlers["*"] = repl.sk_debugger.suspension_handler.bind(this);
  110. var promise = repl.sk_debugger.asyncToPromise(function() {
  111. return Sk.importMainWithBody(editor_filename, true, repl.sk_code_editor.getValue(),true);
  112. }, susp_handlers, this.sk_debugger);
  113. promise.then(this.sk_debugger.success.bind(this.sk_debugger), this.sk_debugger.error.bind(this.sk_debugger));
  114. } catch(e) {
  115. outf(e.toString() + "\n")
  116. }
  117. };
  118. repl.continue = function() {
  119. Sk.configure({
  120. output: repl.print,
  121. debugout: window.jsoutf,
  122. read: window.builtinRead,
  123. yieldLimit: null,
  124. execLimit: null,
  125. debugging: true,
  126. breakpoints: repl.sk_debugger.check_breakpoints.bind(repl.sk_debugger),
  127. });
  128. this.sk_debugger.disable_step_mode();
  129. this.sk_debugger.resume.call(this.sk_debugger);
  130. };
  131. repl.step = function() {
  132. this.sk_debugger.enable_step_mode();
  133. this.sk_debugger.resume.call(this.sk_debugger);
  134. };
  135. repl.set_breakpoint = function(bp, temporary) {
  136. this.sk_debugger.add_breakpoint(editor_filename + ".py", bp, "0", temporary);
  137. };
  138. repl.view_breakpoints = function() {
  139. var bps = this.sk_debugger.get_breakpoints_list();
  140. repl.print("Filename\t\tLineNo\t\tColNo\t\tEnabled\t\tCode");
  141. repl.print("--------\t\t------\t\t-----\t\t-------\t\t----");
  142. for (var bp in bps) {
  143. var bp_obj = bps[bp];
  144. var bp_str =
  145. (" " + bp_obj.filename).slice(-10) + "\t\t" +
  146. (" " + bp_obj.lineno).slice(-5) + "\t\t" +
  147. (" " + bp_obj.colno).slice(-5) + "\t\t" +
  148. (" " + bp_obj.enabled).slice(-5) + "\t\t" +
  149. repl.sk_code_editor.getLine(bp_obj.lineno - 1).trim();
  150. repl.print(bp_str);
  151. }
  152. };
  153. repl.clear_breakpoint = function(bp) {
  154. if (bp == "") {
  155. this.sk_debugger.clear_all_breakpoints();
  156. } else {
  157. var result = this.sk_debugger.clear_breakpoint(editor_filename + ".py", bp, "0");
  158. if (result != null) {
  159. repl.print(result);
  160. }
  161. }
  162. };
  163. repl.disable_breakpoint = function(bp) {
  164. if (bp == "") {
  165. repl.print("No breakpoints specified to be disabled");
  166. } else {
  167. var result = this.sk_debugger.disable_breakpoint(editor_filename + ".py", bp, "0");
  168. if (result != null) {
  169. repl.print(result);
  170. }
  171. }
  172. };
  173. repl.enable_breakpoint = function(bp) {
  174. if (bp == "") {
  175. repl.print("No breakpoints specified to be disabled");
  176. } else {
  177. var result = this.sk_debugger.enable_breakpoint(editor_filename + ".py", bp, "0");
  178. if (result != null) {
  179. repl.print(result);
  180. }
  181. }
  182. };
  183. repl.set_ignore_count = function(bp, count) {
  184. this.sk_debugger.set_ignore_count(editor_filename + ".py", bp, "0", count);
  185. };
  186. repl.debug_callback = function(suspension) {
  187. repl.suspension = suspension;
  188. };
  189. repl.list = function() {
  190. var suspension = this.sk_debugger.get_active_suspension();
  191. if (suspension != null) {
  192. var filename = suspension.filename;
  193. var lineno = suspension.lineno;
  194. var colno = suspension.colno;
  195. var minLineNo = Math.max(0, lineno - 5);
  196. var maxLineNo = Math.min(lineno + 5, repl.sk_code_editor.lineCount());
  197. repl.print("Broken at <" + filename + "> at line: " + lineno + " column: " + colno + "\n");
  198. repl.print("----------------------------------------------------------------------------------\n");
  199. for (var i = minLineNo; i <= maxLineNo; ++i) {
  200. var prefix = i + " ";
  201. if (i == lineno) {
  202. prefix = i + " => "
  203. }
  204. repl.print(prefix + repl.sk_code_editor.getLine(i - 1));
  205. }
  206. repl.print("----------------------------------------------------------------------------------\n");
  207. } else {
  208. repl.print("Program is currently not executing");
  209. }
  210. };
  211. repl.view_locals = function(variable) {
  212. var suspension = repl.sk_debugger.get_active_suspension();
  213. if (variable == "") {
  214. for (var local in suspension.$tmps) {
  215. repl.print(local + ": " + suspension.$tmps[local].v);
  216. }
  217. for (var local in suspension.$loc) {
  218. repl.print(local + ": " + suspension.$loc[local].v);
  219. }
  220. } else {
  221. if (hasOwnProperty(suspension.$loc, variable)) {
  222. repl.print(variable + ": " + suspension.$loc[variable].v);
  223. } else if (hasOwnProperty(suspension.$tmps, variable)) {
  224. repl.print(variable + ": " + suspension.$tmps[variable].v);
  225. } else {
  226. repl.print("No such local variable: " + variable);
  227. }
  228. }
  229. };
  230. repl.view_globals = function(variable) {
  231. var suspension = repl.sk_debugger.get_active_suspension();
  232. var globals = suspension.$gbl;
  233. if (variable == "") {
  234. for (var global in globals) {
  235. repl.print(global + ": " + globals[global].v);
  236. }
  237. } else {
  238. if (hasOwnProperty(globals, variable)) {
  239. repl.print(variable + ": " + globals[variable].v);
  240. } else {
  241. repl.print("No such local variable: " + variable);
  242. }
  243. }
  244. };
  245. repl.display_help = function() {
  246. repl.print("Help: refer to https://docs.python.org/2/library/pdb.html")
  247. for (var cmd in cmd_list) {
  248. repl.print(cmd + ": " + cmd_list[cmd]);
  249. }
  250. };
  251. repl.where = function() {
  252. var suspension_stack = repl.sk_debugger.get_suspension_stack();
  253. var active_suspension = repl.sk_debugger.get_active_suspension();
  254. var len = suspension_stack.length;
  255. for (var i = len - 1; i >= 0; --i) {
  256. var susp = suspension_stack[i];
  257. var code = repl.sk_code_editor.getLine(susp.lineno - 1);
  258. repl.print(" File \"" + susp.filename + "\", line " + susp.lineno + ", in <module>");
  259. code = code.trim();
  260. if (susp === active_suspension) {
  261. code = "=> " + code;
  262. } else {
  263. code = " " + code;
  264. }
  265. repl.print(code);
  266. }
  267. };
  268. repl.down = function() {
  269. repl.sk_debugger.move_down_the_stack();
  270. repl.where();
  271. };
  272. repl.up = function() {
  273. repl.sk_debugger.move_up_the_stack();
  274. repl.where();
  275. };
  276. //Loop
  277. repl.eval = function (code) {
  278. //split lines on linefeed
  279. var lines = code.split('\n'), index = -1, line = 0;
  280. var matches = null;
  281. var variable = null;
  282. var lineno = 0;
  283. try {
  284. //Evaluate
  285. if (!lines || /^\s*$/.test(lines)) {
  286. return;
  287. }
  288. else if (re_run.test(lines[0])) {
  289. this.run_code();
  290. } else if (re_cont.test(lines[0])) {
  291. this.continue();
  292. } else if (re_viewlocals.test(lines[0])) {
  293. // get the matches for this.
  294. matches = re_viewlocals.exec(lines[0]);
  295. variable = null;
  296. if (matches.length == 4) {
  297. variable = matches[3];
  298. }
  299. this.view_locals(variable);
  300. } else if (re_viewglobals.test(lines[0])) {
  301. // get the matches for this.
  302. matches = re_viewglobals.exec(lines[0]);
  303. variable = null;
  304. if (matches.length == 4) {
  305. variable = matches[3];
  306. }
  307. this.view_globals(variable);
  308. } else if (re_break.test(lines[0])) {
  309. matches = re_break.exec(lines[0]);
  310. var lineno = matches[1];
  311. this.set_breakpoint(lineno, false);
  312. } else if (re_view_bp.test(lines[0])) {
  313. this.view_breakpoints();
  314. } else if (re_clear_bp.test(lines[0])) {
  315. matches = lines[0].split(" ");
  316. for (var i = 1; i < matches.length; ++i)
  317. this.clear_breakpoint(matches[i]);
  318. } else if (re_list.test(lines[0])) {
  319. this.list();
  320. } else if (re_help.test(lines[0])) {
  321. this.display_help();
  322. } else if (re_where.test(lines[0])) {
  323. this.where();
  324. } else if (re_step.test(lines[0])) {
  325. this.step();
  326. } else if (re_tbreak.test(lines[0])) {
  327. matches = re_tbreak.exec(lines[0]);
  328. lineno = matches[1];
  329. this.set_breakpoint(lineno, true);
  330. } else if (re_disable_bp.test(lines[0])) {
  331. matches = lines[0].split(" ");
  332. for (var i = 1; i < matches.length; ++i)
  333. this.disable_breakpoint(matches[i]);
  334. } else if (re_enable_bp.test(lines[0])) {
  335. matches = lines[0].split(" ");
  336. for (var i = 1; i < matches.length; ++i)
  337. this.enable_breakpoint(matches[i]);
  338. } else if (re_ignore_count.test(lines[0])) {
  339. matches = re_ignore_count.exec(lines[0]);
  340. var bp = matches[1];
  341. var count = matches[2];
  342. this.set_ignore_count(bp, count);
  343. } else if (re_down.test(lines[0])) {
  344. this.down();
  345. } else if (re_up.test(lines[0])) {
  346. this.up();
  347. } else {
  348. repl.print("Invalid Command: " + lines[0]);
  349. }
  350. } catch (err) {
  351. repl.print(err);
  352. //find the line number
  353. if ((index = err.toString().indexOf("on line")) !== -1) {
  354. index = parseInt(err.toString().substr(index + 8), 10);
  355. }
  356. //print the accumulated code with a ">" before the broken line.
  357. //Don't add the last statement to the accumulated code
  358. lines.forEach(function (str) {
  359. repl.print(++line + (index === line ? ">" : " ") + ": " + str);
  360. });
  361. }
  362. };
  363. (function() {
  364. repl.print(" Skulpt Debugger");
  365. repl.print("-----------------------------------");
  366. repl.print("type 'help' for looking at commands");
  367. repl.print("-----------------------------------");
  368. })();
  369. });