Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Javascript

JavaScript REPL for Windows: Part 1 – Motivation, Choices and First Steps

5.00/5 (1 vote)
25 Aug 2011CPOL6 min read 14.7K  
JavaScript REPL for Windows: Part 1 – Motivation, Choices and First Steps

I earn a living writing C++ code. Code that twiddles pixels, mostly, and, while I write the occasional web app page, I am not a web application developer. Yet I am an avid user of JavaScript.

Long before node.js rose to its popularity, Microsoft Windows users had access to all the power of a scripting engine on a command line– cscript.exe. It is a very convenient and powerful language+host combination to develop simple utility scripts. Much faster than writing CLI (Command Line Interface) C++ utility. CScript.exe is on all modern flavors of Windows, thus it is as portable (Windows-vise) as an equivalent C++ EXE.

This post is a story in three parts of how I ended up implementing a JavaScript REPL console with simple debugger REPL and breakpoints. In this first part, I am going to explain how I arrived at the decision to write JSREPL and how it all started. The full source code of JSREPL is freely available under GPL 2 license on github. If you’d rather not follow my journey, you can directly jump there.

Motivation

Visual Studio is the default development and debugging tool for JavaScript, when working with cscript.exe. While it is a very powerful and full featured debugger, rapid development and debugging is hindered by two facts – first, there is no support for cscript.exe JavaScript projects in Visual Studio.

Second, the just-in-time debugging via //X command line option is cumbersome. It pops a pop-under dialog where you need to select a version and instance of Visual Studio to use for debugging every time a new debugging session is started. And what about that startup time of Visual Studio 2010?

To me, the power of JavaScript is in its immediate zero-compile-time nature. I would like to be able to move fast, in a rapid edit-run-fix succession. Such desire left me searching the internet for an alternative JavaScript debugger. There are several mentions of an older Microsoft Script Debugger, which is not as feature rich as Visual Studio and is not supported on newer versions of Windows, but nothing else along the lines of my hopes of simple REPL (read-evaluate-print-loop) style debugger.

Thus, I decided to write one myself.

Choices

I had written a trivial JavaScript REPL script a few years ago (see below) and I wanted to continue in a similar direction – command line interface, simple quick and reuse as much of JavaScript engines' capability as possible.

Figure 1. An example of my early JavaScript REPL.
JavaScript
1 WScript.StdOut.Write("# ");       
2 while ((cmdLine = WScript.StdIn.ReadLine()).toLowerCase() != "quit" ) {       
3     try {       
4          WScript.Echo("\n",(eval(cmdLine)));       
5     } catch (err) {       
6         WScript.Echo("\n", err.toString());       
7     }       
8     WScript.StdOut.Write("# ");       
9 }

The early REPL was useful to me in many ways – from debugging regular expressions to analyzing COM object API behavior. As the time went by, I have added more features – multiline mode (line continuation character) and some global utility functions (print, println, string prototypes, etc.) The script became more useful, but it was still severely lacking a debugging facility —at the very least, some ability to interrupt execution of the program and inspect variables.

So I had to write the debugger. Looking at the Windows Script Runtime documentation on MSDN, one choice became evident – write a full scale debugger in C++ using Script Runtime Hosting API. Even though I write C++ for living and COM is flowing in my blood, I was not too excited to write a full scale host for the Scripting Runtime, then have to register it on the target machine, etc. I decided to look for a simpler solution.

The simpler solution was to evolve my early REPL.

First Steps

Since I had a quite useful REPL going, I wanted to extend it rather than start from scratch. The inventory of the new functionality came down to the following:

  1. Facility to load script files into REPL – a load command
  2. Facility to list source code of a function – a list command
  3. Facility to set breakpoints and a debug REPL

The load command seemed simple enough:

Figure 2. Simple function to load script from file
JavaScript
1 function load(filePath) {       
2     var fso = new ActiveXObject("Scripting.FileSystemObject");       
3     if (fso.FileExists(filePath)) {       
4         return fso.OpenTextFile(filePath).ReadAll();       
5     }       
6     return null;       
7 }       
8

Then, just eval the returned text inside the main loop to create all the objects in the global scope:

Figure 3. Loading of a script via eval
JavaScript
1 eval(load("Test1.js"));

The load function could be further enhanced by searching the JSREPL folder and current PATH to load the file. An added benefit of the load function is that now I can easily break my REPL implementation into smaller files and not bother with WSF format.

A list command did not seem too hard either – just obtain a reference to function object, then do toString() on it to obtain the string representation, split on newline, etc. If you are following along the source code, this facility is in dbg.js – by this time, my JSREPL project has three files – js.cmd (more on .cmd part later), dbg.js and util.js.

Figure 4. Function to display source code
JavaScript
1 listFunction: function (fn) {       
2     var fnText =  fn.toString().split(/\n\r|\r\n/);       
3     for (l in fnText) {       
4             println(l.rJustify(4), " ", fnText[l]);       
5     }       
6 }

In the above code, there are a couple of utility functions – rJustify and println. Both are in util.js. The function is a method of the DBG class, hence the syntax.

The js.cmd is a JavaScript file, but it is executed by cmd.exe first, such that cscript.exe is invoked with specific command line options. This is done via some trickery:

Figure 5. Cmd to cscript bootstrap code
JavaScript
 1 @set @a=0 /*     
2 @set @a=     
3 @%js32path%cscript //E:JScript //I //Nologo %~dpnx0 %*     
4 @goto :EOF */

This code needs a bit of explanation. It is not my invention – I saw it somewhere circa 2004 – and it is very clever. Some scripting interpreters, such as Python or Perl, have a special command line option to skip one or more lines of source file. This allows to have batch file (shell script) invoke itself via the interpreter. Cscript.exe offers no such option. So the first line of the code serves the purpose of fooling cscript.exe into the JavaScript comment, while at the same time keeping cmd.exe quiet about it.

Cmd.exe reads the first line as “quietly assign 0 /* to variable @a”, while cscript.exe reads the line as “conditional compilation variable @set, followed by assignment to conditional compilation variable @a, then followed by a JavaScript comment”.

Lines 2 through 4 are ignored by cscript.exe, since that is JavaScript comment. However, cmd.exe reads line two as “quietly unassign variable @a." Then line three “quietly execute cscript.exe passing this file as first parameter”, and, finally, line four tells cmd.exe to “quietly exit”. Pretty neat.

Let’s take an inventory here. By now, JSREPL is quite a useful interactive environment – I can load scripts and even see exactly where I want to place the breakpoints. In part two, I will break down the break points (pun?) and debug REPL.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)