wsx.bat: REPL, one line program processor supporting some NodeJS features

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

wsx.bat: REPL, one line program processor supporting some NodeJS features

#1 Post by siberia-man » 28 Feb 2020 18:19

PREAMBLE

Now I decided to publish this script here.

Dozen years ago I realized JSCmd.wsf, then extended it and renamed to wscmd.bat (the bat+js hybrid). The curent project called wsx.bat is their successor. Their main goal is to enable REPL and support some useful features like require(...) and console.log(...) existing in NodeJS (the famous and very popular JS engine), Rhino (Java-based implementation) and Chakra (another MS JS engine). Of course, you can say that I reinvented the wheels and you will be right. Nevetheless, due to absence of ability to run something simple as a one line program natively inspired me to continue activity in this direction.

FEATURES

As I said above the tool allows to run it interactively, write short one line programs without necessity to create temporary files and do something functional and flexible within one command. Internally it runs JS, which parses JS and/or VBS and evals. You can find it as a alternative to jrepl.bat, the tool popular here.

To the moment of announcing the script can to the following:

-- run external scripts
-- inject other external scripts with require(...) and variables with /let:..., /set:..., /get:...
-- run in the interactive mode
-- run one line programs

In the interactive mode it evaluates immediately commands reading them from STDIN. To enable multiple lines enter the double colon ::, enter commands and complete multiline mode with the second double colon. Everything entered between them will be evaluated as one set of commands.

TODO LIST

In the future I would like to implement the following features:

-- file globbing
-- argument file as a list of arguments
-- REPL for VBS
-- "compiling" to standalone JS/VBS/WSF/BAT files
-- most probably something more

EXAMPLES

Let me show some examples of the tool usage:

Count the number of lines (similar to wc -l, the unix tool)::

Code: Select all

rem as JS code
wsx /n /endfile:"echo(FLN, FILE)" /end:"echo(LN)" ...

rem as VBS code
wsx /n /endfile:vbs:"echo FLN, FILE" /end:vbs:"echo LN" ...
Numerate lines of each input file (similar to cat -bn, the unix tool):

Code: Select all

rem as JS code
wsx /p /e:"LINE = LN + ':' + LINE" ...

rem as VBS code
wsx /let:delim=":" /p /e:vbs:"LINE = LN & delim & LINE" ...
Print first 10 lines (similar to head -10, the unix tool):

Code: Select all

rem as JS code
wsx /let:limit=10 /p /e:"LN > limit && quit()"

rem as VBS code
wsx /use:vbs /let:limit=10 /p /e:"if LN > limit then exit : end if"
Filter lines (like grep; two different implementations for viewtopic.php?f=3&t=9473):

Code: Select all

wsx /n /e:"LINE.match(/^#/) && echo(LINE)" ...
wsx /p /e:"LINE.match(/^#/) || next()" ...
BUILD

Right now the project is hosted on github at https://github.com/ildar-shaimordanov/jsxt. There are original scripts there stored in the separate files.

To build the fully standalone tool from the sources you need to run the following command:

Code: Select all

cscript tools\compile-wsf2bat.js < wsx.wsf > wsx.bat
Another way is to use cmdize.bat. It creates cmd+wsf wrapper from the main script only and doesn't embed other included scripts. So it's not fully standalone but allows to test those scripts

Code: Select all

cmdize wsx.wsf
Create the separate wrapper file wsx.bat and use it:

Code: Select all

@echo off
cscript //nologo wsx.wsf %*
The latest version of the tool is below.

STANDALONE VERSION

Code: Select all

<?xml :
: version="1.0" encoding="utf-8" ?><!--
@echo off
cscript //nologo "%~f0?.wsf" %*
goto :EOF
: -->

<package>
<job id="wsx">
<?job error="false" debug="false" ?>

<script language="javascript"><![CDATA[

var NAME    = 'WSX';
var VERSION = '1.0.1 Alpha';

]]></script>

<runtime>
<description><![CDATA[
WSX: Version 1.0.1 Alpha
Copyright (C) 2009-2015, 2019, 2020 Ildar Shaimordanov

Run an external script file in the same way as it can be done traditionally via "cscript" or "wscript" with additional benefits making its usage closer to NodeJS, Perl, Python etc.

Run itself in the interactive mode. Type in the prompt any JS or VBS commands and execute them immediately. In this mode each entered line is evaluated immediately. To enable many lines executed as one you need to surround them with the double colons "::". The first double colon turns on the multiline mode, the second one turns it off. After that everything entered will be executed.

Run one line program from CLI and apply it on inputstream and other files. One line programs allow to estimate some code on the fly, without creating a temporary file. Writing one line programs you focus on the most important parts of the program implementation. Some implementation stuff -- like objects initialization, I/O operations etc -- are hidden on your eyes, however executed yet implicitly.

If the tool is launched with the one line program, everything after is assumed as a file name. Each argument is opened as a file and processed line by line until the end of file. Otherwise, if no any one line program is entered, the first item of the argument list is the script file and the rest of arguments are arguments for the script file. They could be everything and the script can use them accordingly its functionality.

For more convenience there are few predefined global definitions:

Common objects:

FSO     - The object "Scripting.FileSystemObject"
STDIN   - The reference to "WScript.StdIn"
STDOUT  - The reference to "WScript.StdOut"
STDERR  - The reference to "WScript.StdErr"

Common helper functions:

usage(), help()          - Display this help
echo(), print(), alert() - Print expressions
quit(), exit()           - Quit this shell
cmd(), shell()           - Run a command or DOS-session
sleep(n)                 - Sleep n milliseconds
clip()                   - Read from or write to clipboard
gc()                     - Run the JScript garbage collector

ERROR   - The variable keeping the last error
USE     - The instance of "Importer" class to import VBS easier
ARGV    - The CLI arguments

Used in the loop mode:

STREAM  - The reference to the stream of the current file
FILE    - The name of the current file
FILEFMT - The format to open files ("ascii", "unicode" or system "default")
LINE    - The current line
FLN     - The line number in the current file
LN      - The total line number

These special functions can be used on the loop mode only to cover the issue when we can't use "continue" and "break".

next()  - The "continue" operator
last()  - The "break" operator

Used in REPL:

The interactive mode provides the following useful properties for referencing to the history of the commands and REPL mode:

REPL.number  - the current line number
REPL.history - the list of all commands entered in the current session
REPL.quiet   - the current session mode

The CLI options supplying the program parts for execution could be infixed with the engine identifier ("js" or "vbs") supposed to be used for processing these options. See examples below.

The name explanation:

Following the old good tradition to explain acronyms recursively "WSX" means "WSX Simulates eXecutable".

]]></description>
<example><![CDATA[
Examples:

- Run interactively:
  wsx

- Count the number of lines (similar to "wc -l", the unix tool):
  wsx /n /endfile:"echo(FLN, FILE)" /end:"echo(LN)"
  wsx /n /endfile:vbs:"echo FLN, FILE" /end:vbs:"echo LN"
  wsx /use:vbs /n /endfile:"echo FLN, FILE" /end:"echo LN"

- Numerate lines of each input file (VScript example shows how to bypass the trouble with quotes within quotes):
  wsx /p /e:"LINE = LN + ':' + LINE"
  wsx /let:delim=":" /p /e:vbs:"LINE = LN & delim & LINE"

- Print first 10 lines (similar to "head", the unix tool):
  wsx /let:limit=10 /p /e:"LN > limit && quit()"
  wsx /use:vbs /let:limit=10 /p /e:"if LN > limit then exit : end if"

- Print last 10 lines (similar to "tail", the unix tool):
  wsx /let:limit=10 /n /beginfile:"lines=[]" /e:"lines.push(LINE); lines.length > limit && lines.shift()" /endfile:"echo(lines.join('\n'))"
]]></example>
<named
	name="help"
	helpstring="Print this help and exit (&#34;/h&#34; shortcut)"
	type="simple"
	required="false"
	/>
<named
	name="version"
	helpstring="Print version information and exit"
	type="simple"
	required="false"
	/>
<named
	name="dry-run"
	helpstring="Show in pseudocode what is going to be executed"
	type="simple"
	required="false"
	/>
<!--
<named
	name="compile"
	helpstring="Compile and store to another file without execution"
	type="simple"
	required="false"
	/>
-->
<named
	name="quiet"
	helpstring="Be quiet in the interactive mode (&#34;/q&#34; shortcut)"
	type="simple"
	required="false"
	/>
<named
	name="use"
	helpstring="Use the engine (&#34;js&#34; or &#34;vbs&#34;)"
	type="string"
	required="false"
	/>
<named
	name="m"
	helpstring="Load the module (similar to &#34;require(...)&#34; in NodeJS)"
	type="string"
	required="false"
	/>
<named
	name="let"
	helpstring="Assign the value: &#34;name=value&#34;"
	type="string"
	required="false"
	/>
<named
	name="set"
	helpstring="Create the object: &#34;name=CreateObject(object)&#34;"
	type="string"
	required="false"
	/>
<named
	name="get"
	helpstring="Get the object: &#34;name=GetObject(object)&#34;"
	type="string"
	required="false"
	/>
<named
	name="e"
	helpstring="One line program (multiple &#34;/e&#34;'s supported)"
	type="string"
	required="false"
	/>
<named
	name="n"
	helpstring="Apply a program in a loop &#34;while read LINE { ... }&#34;"
	type="simple"
	required="false"
	/>
<named
	name="p"
	helpstring="Apply a program in a loop &#34;while read LINE { ... print }&#34;"
	type="simple"
	required="false"
	/>
<named
	name="begin"
	helpstring="The code for executing before the loop"
	type="string"
	required="false"
	/>
<named
	name="end"
	helpstring="The code for executing after the loop"
	type="string"
	required="false"
	/>
<named
	name="beginfile"
	helpstring="The code for executing before each file"
	type="string"
	required="false"
	/>
<named
	name="endfile"
	helpstring="The code for executing after each file"
	type="string"
	required="false"
	/>
<unnamed
	name="scriptfile"
	helpstring="The script file"
	required="false"
	/>
<!--
<named
	name="@"
	helpstring="Read arguments from the specified file"
	type="string"
	required="false"
	/>
-->
<named
	name="f"
	helpstring="Open a file as &#34;ascii&#34;, &#34;unicode&#34; or using system &#34;default&#34;"
	type="string"
	required="false"
	/>
<unnamed
	name="arguments"
	helpstring="Other arguments to be passed to the program"
	required="false"
	/>
</runtime>

<!-- <script language="javascript" src="./wsx/Helpers.js"></script> -->
<script language="javascript"><![CDATA[
//
// Set of useful and convenient definitions
// This script is the part of the wsx
//
// Copyright (c) 2019, 2020 by Ildar Shaimordanov
//

var FSO = new ActiveXObject('Scripting.FileSystemObject');

var STDIN = WScript.StdIn;
var STDOUT = WScript.StdOut;
var STDERR = WScript.StdErr;

var usage = help = (function() {
	var helpMsg = [
		  'Commands                 Descriptions'
		, '========                 ============'
		, 'usage(), help()          Display this help'
		, 'echo(), print(), alert() Print expressions'
		, 'quit(), exit()           Quit this shell'
		, 'cmd(), shell()           Run a command or DOS-session'
		, 'sleep(n)                 Sleep n milliseconds'
		, 'clip()                   Read from or write to clipboard'
		, 'gc()                     Run the JScript garbage collector'
	].join('\n');

	return function() {
		WScript.Echo(helpMsg);
	};
})();

var echo = print = alert = (function() {
	var slice = Array.prototype.slice;

	return function() {
		WScript.Echo(slice.call(arguments));
	};
})();

var quit = exit = function(exitCode) {
	WScript.Quit(exitCode);
};

var cmd = shell = function(command) {
	var shell = new ActiveXObject('WScript.Shell');
	shell.Run(command || '%COMSPEC%');
};

var sleep = function(time) {
	return WScript.Sleep(time);
};

var clip = function(text) {
	if ( typeof text == 'undefined' ) {
		return new ActiveXObject('htmlfile').parentWindow.clipboardData.getData('Text');
	}

	// Validate a value is integer in the range 1..100
	// Otherwise, defaults to 20
	var clamp = function(x) {
		x = Number(x);
		if ( isNaN(x) || x < 1 || x > 100 ) {
			x = 20;
		}
		return x;
	};

	var WAIT1 = clamp(clip.WAIT_READY);
	var WAIT2 = clamp(clip.WAIT_LOADED);

	// Borrowed from https://stackoverflow.com/a/16216602/3627676
	var msie = new ActiveXObject('InternetExplorer.Application');
	msie.silent = true;
	msie.Visible = false;
	msie.Navigate('about:blank');

	// Wait until MSIE ready
	while ( msie.ReadyState != 4 ) {
		WScript.Sleep(WAIT1);
	}

	// Wait until document loaded
	while ( msie.document.readyState != 'complete' ) {
		WScript.Sleep(WAIT2);
	}

	msie.document.body.innerHTML = '<textarea id="area" wrap="off" />';
	var area = msie.document.getElementById('area');
	area.value = text;
	area.select();
	area = null;

	// 12 - "Edit" menu, "Copy" command
	//  0 - the default behavior
	msie.ExecWB(12, 0);
	msie.Quit();
	msie = null;
};

var gc = CollectGarbage;

if ( typeof exports != "undefined" ) {
	exports.FSO = FSO;
	exports.STDIN = STDIN;
	exports.STDOUT = STDOUT;
	exports.STDERR = STDERR;

	exports.usage = usage;
	exports.help = help;

	exports.echo = echo;
	exports.alert = alert;
	exports.print = print;

	exports.quit = quit;
	exports.exit = exit;

	exports.cmd = cmd;
	exports.shell = shell;

	exports.sleep = sleep;
	exports.clip = clip;
	exports.gc = gc;
}
]]></script>

<!-- <script language="javascript" src="./core/console.js"></script> -->
<script language="javascript"><![CDATA[
//
// console.js
// Imitation of the NodeJS console in the Windows Scripting Host
//
// Copyright (c) 2012, 2013, 2019, 2020 by Ildar Shaimordanov
//

/*

The module adds the useful NodeJS console features to WSH

"console.log(), console.debug(), ..." can be used the same way as used in NodeJS.

Log messages with custom icon depending on the function used.
	console.log(object[, object, ...])
	console.debug(object[, object, ...])
	console.info(object[, object, ...])
	console.warn(object[, object, ...])
	console.error(object[, object, ...])

Test if the expression. If it is false, the info will be logged in the console
	console.assert(expression[, object, ...])

Starts and stops a timer and writes the time elapsed
	console.time(name)
	console.timeEnd(name)

Customizing the console
	console.fn

Checks that the object is a formatting string
	console.fn.isFormat(object)

The simplest formatting function immitating C-like format
	console.fn.format(pattern, objects)

Details for the complex object
	console.fn.inspect(object)

The low-level printing function
	console.fn.print(msgType, msg)

The deep of nestion for complex structures (default is 5)
	console.fn.deep

The initial value of indentation (4 whitespaces, by default).
A numeric value defines indentaion size, the number of space chars.
	console.fn.space

Numeric values controls the visibility of functions. Defaults to 0.
(0 - do not show function, 1 - show [Function] string, 2 - show a details)
	console.fn.func

The visibility properties from the prototype of the oject. Defaults to 0.
(0 - do not show properties from prototype, 1 - show then)
	console.fn.proto

The string to glue the set of arguments when output them
	console.fn.separator = ' '

The following functions are not implemented:
	console.clear
	console.count
	console.dir
	console.dirxml
	console.group
	console.groupCollapsed
	console.groupEnd
	console.profile
	console.profileEnd
	console.table
	console.trace

*/

var console = console || (function() {

	var entities = {
		'&': '&amp;', 
		'"': '&quot;', 
		'<': '&lt;', 
		'>': '&gt;'
	};

	var escaped = /[\x00-\x1F\"\\]/g;
	var special = {
		'"': '\\"', 
		'\r': '\\r', 
		'\n': '\\n', 
		'\t': '\\t', 
		'\b': '\\b', 
		'\f': '\\f', 
		'\\': '\\\\'
	};

	var space;
	var indent = '';

	var deep;

	var proto;
	var func;

	function _quote(value) {
		var result = value.replace(escaped, function($0) {
			return special[$0] || $0;
		});
		return '"' + result + '"';
	};

	// The main method for printing objects
	var inspect = function(object) {
		switch (typeof object) {
		case 'string':
			return _quote(object);

		case 'boolean':
		case 'number':
		case 'undefined':
		case 'null':
			return String(object);

		case 'function':
			if ( func == 1 ) {
				return '[Function]';
			}
			if ( func > 1 ) {
				return object.toString();
			}
			return '';

		case 'object':
			if ( object === null ) {
				return String(object);
			}

			// Assume win32 COM objects
			if ( object instanceof ActiveXObject ) {
				return '[ActiveXObject]';
			}

			var t = Object.prototype.toString.call(object);

			// Assume the RegExp object
			if ( t == '[object RegExp]' ) {
				return String(object);
			}

			// Assume the Date object
			if ( t == '[object Date]' ) {
				return object.toUTCString();
			}

			// Stop the deeper nestings
			if ( ! deep ) {
				return '[...]';
			}

			var saveDeep = deep;
			deep--;

			var saveIndent = indent;
			indent += space;

			var result = [];
			for (var k in object) {
				if ( ! object.hasOwnProperty(k) && ! proto ) {
					continue;
				}

				var v;

				if ( object[k] === object ) {
					v = '[Recursive]';
				} else {
					v = inspect(object[k]);
					if ( v === '' ) {
						// Sure that any property will return non-empty string
						// Only functions can return an empty string when func == 0
						continue;
					}
				}

				result.push(k + ': ' + v);
			}

			var pred;
			var post;

			if ( t == '[object Array]' ) {
				pred = 'Array(' + object.length + ') [';
				post = ']';
			} else {
				pred = 'Object {';
				post = '}';
			}

			result = result.length == 0 
				? '\n' + saveIndent 
				: '\n' + indent + result.join('\n' + indent) + '\n' + saveIndent;

			indent = saveIndent;
			deep = saveDeep;

			return pred + result + post;

		default:
			return '[Unknown]';
		}
	};

	// This regular expression is used to recongnize a formatting string 
	var reFormat = /%%|%(\d+)?([idfxso])/g;

	// Checks that the object is a formatting string
	var isFormat = function(object) {
		return Object.prototype.toString.call(object) == '[object String]' && object.match(reFormat);
	};

	var formatters = {};
	formatters.i = function(v) { return Number(v).toFixed(0); };
	formatters.d = 
	formatters.f = function(v) { return Number(v).toString(10); };
	formatters.x = function(v) { return Number(v).toString(16); }, 
	formatters.o = inspect;
	formatters.s = function(v) { return String(v); };

	// The formatting function immitating C-like "printf"
	var format = function(pattern, objects) {
		var i = 0;
		return pattern.replace(reFormat, function(format, width, id) {
			if ( format == '%%' ) {
				return '%';
			}

			i++;

			var r = formatters[id](objects[i]);

			return r;
		});
	};

	// The low-level printing function
	var print = function(msgType, msg) {
		WScript.Echo(msg);
	};


	// The core function
	var printMsg = function(msgType, objects) {
		// Get the actual configuration of the console
		var fn = console.fn || {};

		var sep = fn.separator || ' ';

		space = fn.space;
		deep = Number(fn.deep) > 0 ? fn.deep : 5;
		proto = fn.proto || 0;
		func = fn.func || 0;

		var t = Object.prototype.toString.call(space);
		if ( t == '[object Number]' && space >= 0 ) {
			space = new Array(space + 1).join(' ');
		} else if ( t != '[object String]' ) {
			space = '    ';
		}

		if ( typeof fn.isFormat == 'function' ) {
			isFormat = fn.isFormat;
		}
		if ( typeof fn.format == 'function' ) {
			format = fn.format;
		}
		if ( typeof fn.inspect == 'function' ) {
			inspect = fn.inspect;
		}
		if ( typeof fn.print == 'function' ) {
			print = fn.print;
		}

		var result;

		if ( isFormat(objects[0]) ) {
			//result = format(objects[0], Array.prototype.slice.call(objects, 1));
			result = format(objects[0], objects);
		} else {
			result = [];
			for (var i = 0; i < objects.length; i++) {
				result.push(inspect(objects[i]));
			}
			result = result.join(sep);
		}

		print(msgType, result);
	};


	var log = function() {
		printMsg(0, arguments);
	};

	var debug = log;

	var info = function() {
		printMsg(64, arguments);
	};

	var warn = function() {
		printMsg(48, arguments);
	};

	var error = function() {
		printMsg(16, arguments);
	};


	// If the expression is false, the rest of arguments will be logged in 
	// the console
	var assert = function(expr) {
		if ( expr ) {
			return;
		}
		error.apply(console, arguments.length < 2 ? ['Assertion error'] : Array.prototype.slice.call(arguments, 1));
	};


	// Processing of timers start/stop
	var timeNames = {};

	var timeStart = function(name) {
		if ( ! name ) {
			return;
		}

		timeNames[name] = new Date();
		log(name + ': Timer started');
	};

	var timeEnd = function(name) {
		if ( ! name || ! timeNames.hasOwnProperty(name) ) {
			return;
		}

		var t = new Date() - timeNames[name];
		delete timeNames[name];

		log(name + ': ' + t + 'ms');
	};


	return {
		// Implemented methods
		log: log, 
		debug: debug, 
		info: info, 
		warn: warn, 
		error: error, 

		assert: assert, 

		time: timeStart, 
		timeEnd: timeEnd, 

		// Not implemented methods
		clear: function() {}, 

		dir: function() {}, 
		dirxml: function() {}, 

		group: function() {}, 
		groupCollapsed: function() {}, 
		groupEnd: function() {}, 

		profile: function() {}, 
		profileEnd: function() {}, 

		table: function() {}, 

		trace: function() {}, 

		// Customizing the console
		fn: {
			space: 4,
			deep: 5,
			proto: 0,
			func: 0,
			separator: ' '
		}
	};

})();

if ( typeof module != "undefined" ) {
	module.exports = console;
}
]]></script>

<!-- <script language="javascript" src="./core/require.js"></script> -->
<script language="javascript"><![CDATA[
//
// require.js
// Imitation of the NodeJS function require in the Windows Scripting Host
//
// Copyright (c) 2019, 2020 by Ildar Shaimordanov
//

/*

The module adds to WSH the "require" function, the useful NodeJS feature, and some additional extensions.

Load a module by name of filename
	require(id[, options])

Resolve a location of the module
	require.resolve(id[, options])

The list of of paths used to resolve the module location
	require.paths

Cache for the imported modules
	require.cache

*/

var require = require || (function() {

	var fso = WScript.CreateObject("Scripting.FileSystemObject");

	function loadFile(file, format) {
		var stream = fso.OpenTextFile(file, 1, false, format || 0);
		var text = stream.ReadAll();
		stream.Close();

		return text;
	};

	/**
	 * Load a module by name of filename
	 *
	 * @param	<String>	module name or path
	 * @param	<Object>	options
	 * @return	<Object>	exported module content
	 *
	 * Available options:
	 * - paths	<Array>		Paths to resolve the module location
	 * - format	<Integer>	format of the opened file (-2 - system default, -1 - Unicode file, 0 - ASCII file)
	 */
	function require(id, options) {
		if ( ! id ) {
			throw new TypeError("Missing path");
		}

		if ( typeof id != "string" ) {
			throw new TypeError("Path must be a string");
		}

		options = options || {};

		var file = require.resolve(id, options);

		require.cache = require.cache || {};

		if ( ! require.cache[file] || ! require.cache[file].loaded ) {
			var text = loadFile(file, options.format);

			var code
				= "(function(module) {\n"
				+ "var exports = module.exports;\n"
				+ text + ";\n"
				+ "return module.exports || {};\n"
				+ "})({ exports: {} })";

			var evaled = eval(code);

			require.cache[file] = {
				exports: evaled,
				loaded: true
			};
		}

		return require.cache[file].exports;
	};

	function absolutePath(file) {
		if ( fso.FileExists(file) ) {
			return fso.GetAbsolutePathName(file);
		}
	}

	/**
	 * Resolve a location of the module
	 *
	 * @param	<String>	module name or path
	 * @param	<Object>	options
	 * @return	<String>	resolved location of the module
	 *
	 * Available options:
	 * - paths	<Array>		Paths to resolve the module location
	 */
	require.resolve = function(id, options) {
		options = options || {};

		var file = /[\\\/]|\.js$/i.test(id)

			// module looks like a path
			? absolutePath(/\.[^.\\\/]+$/.test(id) ? id : id + ".js")

			// attempt to find a librarian module
			: (function() {
				var paths = [].concat(options.paths || [], require.paths);
				for (var i = 0; i < paths.length; i++) {
					var file = absolutePath(paths[i] + "\\" + id + ".js");
					if ( file ) {
						return file;
					}
				}
			})();

		if ( ! file ) {
			throw new Error("Cannot find module '" + id + "'");
		}

		return file;
	};

	var myDir = fso.GetParentFolderName(WScript.ScriptFullName);
	var me = WScript.ScriptName.replace(/(\.[^.]+\?)?\.[^.]+$/, '');

	var shell = WScript.CreateObject ("WScript.Shell");
	var cwd = shell.CurrentDirectory;

	require.paths = [
		  myDir + "\\js"
		, myDir + "\\" + me
		, myDir + "\\" + me + "\\js"
		, myDir + "\\lib"
		, cwd
	];

	if ( fso.GetBaseName(myDir) == "bin" ) {
		require.paths.push(myDir + "\\..\\lib");
	}

	return require;
})();
]]></script>
<!-- <script language="vbscript" src="./core/importer.vbs"></script> -->
<script language="vbscript"><![CDATA[
'
' importer.js
' Import modules by name or filename similar to the NodeJS "require"
'
' Copyright (c) 2019, 2020 by Ildar Shaimordanov
'
' @see
' https://blog.ctglobalservices.com/scripting-development/jgs/include-other-files-in-vbscript/
' https://helloacm.com/include-external-files-in-vbscriptjscript-wsh/
' https://www.planetcobalt.net/sdb/importvbs.shtml
' https://stackoverflow.com/a/316169/3627676
' https://stackoverflow.com/a/43957897/3627676
' https://riptutorial.com/vbscript/topic/8345/include-files


' Create and return an instance of the Importer class
' Can be useful to import VBScript modules from JScript
'
' @return	<Importer>
Function CreateImporter
	Set CreateImporter = New Importer
End Function


Class Importer
	' For caching already loaded modules
	Private cache

	' File system object, used internally
	Private fso

	' The list of of paths used to resolve the module location
	Public paths

	' format of the opened file
	' (-2 - system default, -1 - Unicode file, 0 - ASCII file)
	Public format

	' Initialize importer
	Private Sub Class_Initialize
		Set cache = CreateObject("Scripting.Dictionary")
		Set fso = CreateObject("Scripting.FileSystemObject")

		Dim re
		Set re = New RegExp
		re.Pattern = "(\.[^.]+\?)?\.[^.]+$"

		Dim mydir, myself

		mydir = fso.GetParentFolderName(WScript.ScriptFullName)
		myself = re.Replace(WScript.ScriptName, "")

		Dim shell, cwd

		Set shell = WScript.CreateObject("WScript.Shell")
		cwd = shell.CurrentDirectory

		paths = Array( _
			  mydir & "\vbs" _
			, mydir & "\" & myself _
			, mydir & "\" & myself & "\vbs" _
			, mydir & "\lib" _
			, cwd _
		)
		If fso.GetBaseName(mydir) = "bin" Then
			ReDim Preserve paths(UBound(paths) + 1)
			paths(UBound(paths)) = mydir & "\..\lib"
		End If
	End Sub

	' Destroy importer
	Private Sub Class_Terminate
		Set paths = Nothing
		Set fso = Nothing
		Set cache = Nothing
	End Sub

	Private Sub PathIncrement(insert, apath)
		Dim gain
		If IsArray(apath) Then
			gain = UBound(apath) + 1
		Else
			gain = 1
		End If

		Dim count
		count = UBound(paths)

		Redim Preserve paths(count + gain)

		Dim i

		If insert = 1 Then
			For i = count To 0 Step -1
				paths(i + gain) = paths(i)
			Next
			count = 0
		End If

		If IsArray(apath) Then
			For i = 0 To gain - 1
				paths(count + i) = apath(i)
			Next
		Else
			paths(count) = apath
		End If
	End Sub

	' Add a path or array of paths to the begin of the list of paths
	'
	' @param	<String|Array>	Path or array of paths
	Public Sub PathInsert(apath)
		PathIncrement 1, apath
	End Sub

	' Add a path or array of paths to the end of the list of paths
	'
	' @param	<String|Array>	Path or array of paths
	Public Sub PathAppend(apath)
		PathIncrement 0, apath
	End Sub

	' Load a text
	'
	' @param	<String>	a text to be executed
	Public Sub Execute(text)
		On Error GoTo 0
		ExecuteGlobal text
		On Error Resume Next
	End Sub

	' Load a module by name of filename
	'
	' @param	<String>	module name or path
	Public Sub Import(id)
		On Error GoTo 0

		Dim Name

		Dim ErrStr

		Select Case VarType(id)
		Case vbString
			Name = id
		Case vbEmpty
			ErrStr = "Missing path (Empty)"
		Case vbNull
			ErrStr = "Missing path (Null)"
		Case Else
			ErrStr = "Path must be a string"
		End Select

		If ErrStr <> "" Then
			Err.Description = ErrStr
			Err.Raise vbObjectError
		End If

		Dim file
		file = Resolve(id)

		If Not cache.Exists(file) Then
			Dim text
			text = ReadFile(file)
			ExecuteGlobal text

			cache.Add file, 1
		End If

		On Error Resume Next
	End Sub

	' Read a file
	Private Function ReadFile(file)
		Dim stream
		Set stream = fso.OpenTextFile(file, 1, False, format)
		ReadFile = stream.ReadAll
		stream.Close
		Set stream = Nothing
	End Function

	' Resolve a location of the module
	'
	' @param	<String>	module name or path
	' @return	<String>	resolved location of the module
	Public Function Resolve(id)
		Dim file

		Dim re
		Set re = New RegExp

		re.Pattern = "[\\\/]|\.vbs$"
		re.IgnoreCase = True
		If re.Test(id) Then
			' module looks like a path
			re.Pattern = "\.[^.\\\/]+$"
			If re.Test(id) Then
				file = id
			Else
				file = id & ".vbs"
			End If
			Resolve = AbsolutePath(file)
		Else
			' attempt to load a librarian module
			Dim path
			For Each path In paths
				file = path & "\" & id & ".vbs"
				Resolve = AbsolutePath(file)
				If Resolve <> "" Then
					Exit For
				End If
			Next
		End If

		If Resolve = "" Then
			Err.Description = "Cannot find module '" & id & "'"
			Err.Raise vbObjectError
		End If
	End Function

	' Return the absolute path if file exists, otherwise - empty string
	Private Function AbsolutePath(file)
		AbsolutePath = ""
		If fso.FileExists(file) Then
			AbsolutePath = fso.GetAbsolutePathName(file)
		End If
	End Function

End Class
]]></script>

<!-- <script language="javascript" src="./wsx/Program.js"></script> -->
<script language="javascript"><![CDATA[
//
// Program
// This script is the part of the wsx
//
// Copyright (c) 2019, 2020 by Ildar Shaimordanov
//

var Program = {
	dryRun: false,
	quiet: false,
	inLoop: 0,

	engine: "js",

	modules: [],
	vars: [],

	main: [],

	begin: [],
	end: [],
	beginfile: [],
	endfile: [],

	setEngine: function(engine) {
		this.engine = engine;
	},
	getEngine: function(engine) {
		return (engine || this.engine).toLowerCase();
	},

	setMode: function(mode) {
		var loopTypes = { i: 0, n: 1, p: 2 };
		this.inLoop = loopTypes[mode];
	},

	setQuiet: function() {
		this.quiet = true;
	},

	addScript: function(engine, name) {
		name = name.replace(/\\/g, '\\\\');
		var result = '';
		if ( this.getEngine(engine) == "vbs" ) {
			result = 'USE.Import("' + name + '")';
		} else {
			result = 'require("' + name + '")';
		}
		return result;
	},
	addModule: function(engine, name) {
		this.modules.push(this.addScript(engine, name));
	},

	vbsVar: function(name, value, setter) {
		var result = 'Dim ' + name;
		if ( value ) {
			switch( setter.toLowerCase() ) {
			case 'let': result += ' : ' + name + ' = \\"' + value + '\\"'; break;
			case 'set': result += ' : Set ' + name + ' = CreateObject(\\"' + value + '\\")'; break;
			case 'get': result += ' : Set ' + name + ' = GetObject(\\"' + value + '\\")'; break;
			}
		}
		return 'USE.Execute("' + result + '")';
	},
	jsVar: function(name, value, setter) {
		//var result = 'var ' + name;
		var result = ''
		if ( value ) {
			result = name;
			switch( setter.toLowerCase() ) {
			case 'let': result += ' = "' + value + '"'; break;
			case 'set': result += ' = new ActiveXObject("' + value + '")'; break;
			case 'get': result += ' = GetObject("' + value + '")'; break;
			}
		}
		return result;
	},
	addVar: function(engine, name, value, setter) {
		var result;
		if ( this.getEngine(engine) == "vbs" ) {
			result = this.vbsVar(name, value, setter);
		} else {
			result = this.jsVar(name, value, setter);
		}
		this.vars.push(result);
	},

	addCode: function(engine, code, region) {
		var result = '';
		if ( this.getEngine(engine) == "vbs" ) {
			result = 'USE.Execute("' + code + '")';
		} else {
			result = code;
		}
		this[region || 'main'].push(result);
	},

	detectScriptFile: function(args) {
		if ( this.main.length ) {
			return;
		}
		if ( this.inLoop ) {
			return;
		}
		if ( args.length ) {
			var scriptFile = args.shift();
			var engine = /\.vbs$/.test(scriptFile) ? 'vbs' : 'js';
			this.main.push(this.addScript(engine, scriptFile));
		}
	}
};
]]></script>
<!-- <script language="javascript" src="./wsx/Runner.js"></script> -->
<script language="javascript"><![CDATA[
//
// Code processor: Runner
// This script is the part of the wsx
//
// Copyright (c) 2019, 2020 by Ildar Shaimordanov
//

var Runner = function(Program, argv) {
	if ( Program.dryRun ) {
		Runner.dump(Program);
		return;
	}

	var modules = Program.modules.join(';\n');
	var vars = Program.vars.join(';\n');
	var begin = Program.begin.join(';\n');
	var beginfile = Program.beginfile.join(';\n');
	var main = Program.main.join(';\n');
	var endfile = Program.endfile.join(';\n');
	var end = Program.end.join(';\n');

	/*
	The following variables are declared without the keyword "var". So
	they become global and available for all codes in JScript and VBScript.
	*/

	// Helper to simplify VBS importing
	USE = CreateImporter();

	// Keep a last exception
	ERROR = null;

	// Reference to CLI arguments
	ARGV = argv;

	/*
	Load provided modules
	Set user-defined variables
	*/
	eval(modules);
	eval(vars);

	if ( Program.main.length == 0 && Program.inLoop == false ) {
		/*
		Run REPL
		*/
		REPL.quiet = Program.quiet;
		REPL();
		return;
	}

	if ( ! Program.inLoop ) {
		/*
		Load the main script and do nothing more.
		*/
		eval(main);
		return;
	}

	// The currently open stream
	STREAM = null;

	// The current filename, file format and file number
	FILE = '';
	FILEFMT = 0;

	// The current line read from the stream
	LINE = '';

	// The line number in the current file
	FLN = 0;

	// The total line number
	LN = 0;

	// Emulate the "continue" operator
	next = function() {
		throw new EvalError('next');
	};

	// Emulate the "break" operator
	last = function() {
		throw new EvalError('last');
	};

	/*
	Execute the code before starting to process any file.
	This is good place to initialize.
	*/
	eval(begin);

	if ( ! ARGV.length ) {
		ARGV.push('con');
	}

	while ( ARGV.length ) {
		FILE = ARGV.shift();

		var m = FILE.match(/^\/f:(ascii|unicode|default)$/i);

		if ( m ) {
			var fileFormats = { ascii: 0, unicode: -1, 'default': -2 };
			FILEFMT = fileFormats[ m[1] ];
			continue;
		}

		FLN = 0;

		/*
		Execute the code before starting to process the file.
		We can do here something while the file is not opened.
		*/
		eval(beginfile);

		try {
			STREAM = FILE.toLowerCase() == 'con'
				? STDIN
				: FSO.OpenTextFile(FILE, 1, false, FILEFMT);
		} catch (ERROR) {
			WScript.Echo(ERROR.message + ': ' + FILE);
			continue;
		}

		/*
		Prevent failure of reading out of STDIN stream
		The real exception number is 800a005b (-2146828197)
		"Object variable or With block variable not set"
		*/
		//// NEED MORE INVESTIGATION
		//try {
		//	stream.AtEndOfStream;
		//} catch (ERROR) {
		//	WScript.StdErr.WriteLine('Out of stream: ' + file);
		//	continue;
		//}

		while ( ! STREAM.AtEndOfStream ) {
			FLN++;
			LN++;

			LINE = STREAM.ReadLine();

			/*
			Execute the main code per each input line.
			*/
			try {
				eval(main);
			} catch (ERROR) {
				if ( ERROR instanceof EvalError && ERROR.message == 'next' ) {
					continue;
				}
				if ( ERROR instanceof EvalError && ERROR.message == 'last' ) {
					break;
				}
				throw ERROR;
			}

			if ( Program.inLoop == 2 ) {
				WScript.Echo(LINE);
			}
		}

		if ( STREAM != STDIN ) {
			STREAM.Close();
		}

		/*
		Execute the code when the file is already closed. We can do
		some finalization (i.e.: print the number of lines in the file).
		*/
		eval(endfile);
	}

	/*
	Execute the code when everything is completed.
	We can finalize the processing (i.e.: print the total number of lines).
	*/
	eval(end);
};

Runner.dump = function(Program) {
	var s = [];

	function dumpCode(code) {
		if ( code.length ) {
			s.push(code.join(';\n'));
		}
	}

	dumpCode(Program.modules);
	dumpCode(Program.vars);

	if ( Program.inLoop ) {
		dumpCode(Program.begin);
		s.push('::foreach FILE do');
		dumpCode(Program.beginfile);
		s.push('::while read LINE do');
	}

	if ( Program.main.length == 0 && Program.inLoop == false ) {
		s = s.concat([
			'::while read',
			'::eval',
			'::print',
			'::loop while'
		]);
	}

	dumpCode(Program.main);

	if ( Program.inLoop == 2 ) {
		s.push('::print LINE');
	}

	if ( Program.inLoop ) {
		s.push('::loop while');
		dumpCode(Program.endfile);
		s.push('::loop foreach');
		dumpCode(Program.end);
	}

	WScript.Echo(s.join('\n'));
};
]]></script>
<!-- <script language="javascript" src="./wsx/REPL.js"></script> -->
<script language="javascript"><![CDATA[
//
// Code processor: REPL
// This script is the part of the wsx
//
// Copyright (c) 2019, 2020 by Ildar Shaimordanov
//

var REPL = function() {
	if ( ! WScript.FullName.match(/cscript.exe$/i) ) {
		WScript.Echo('REPL works with cscript only');
		WScript.Quit();
	}

	while ( true ) {

		try {

			(function(storage, result) {
				/*
				A user can modify the codes of these methods so
				to prevent the script malfunctioning we have
				to keep their original codes and restore them later
				*/
				eval = storage.eval;
				REPL = storage.REPL;

				if ( result === void 0 ) {
					return;
				}

				if ( result && typeof result == 'object'
				&& console && typeof console == 'object'
				&& typeof console.log == 'function' ) {
					console.log(result);
				} else {
					WScript.StdOut.WriteLine('' + result);
				}
			})({
				eval: eval,
				REPL: REPL
			},
			eval((function(PS1, PS2) {

				if ( REPL.quiet ) {
					PS1 = '';
					PS2 = '';
				} else {
					var me = WScript.ScriptName;
					PS1 = me + ' js > ';
					PS2 = me + ' js :: ';
				}

				/*
				The REPL.history can be changed by the user as he
				can. We should prevent a concatenation with the one
				of the empty values such as undefined, null, etc.
				*/
				if ( ! REPL.history || ! ( REPL.history instanceof [].constructor ) ) {
					REPL.history = [];
				}

				/*
				The REPL.number can be changed by the user as he can.
				We should prevent an incrementing of non-numeric values.
				*/
				if ( ! REPL.number || typeof REPL.number != 'number' ) {
					REPL.number = 0;
				}

				/*
				The line consisting of two colons only switches the
				multiline mode. The first entry of double colons
				means turn on the multiline mode. The next entry
				turns off. In the multiline mode it's possible to
				type a code of few lines without inpterpreting.
				*/
				var multiline = false;

				/*
				Storages for:
				-- one input line
				-- one or more input lines as array
				   (more than one are entered in multiline mode)
				-- the resulting string of all entered lines
				   (leading and trailing whitespaces are trimmed)
				*/
				var input = [];
				var inputs = [];
				var result = '';

				WScript.StdOut.Write(PS1);

				while ( true ) {

					try {
						REPL.number++;
						input = [];
						while ( ! WScript.StdIn.AtEndOfLine ) {
							input[input.length] = WScript.StdIn.Read(1);
						}
						WScript.StdIn.ReadLine();
					} catch (ERROR) {
						input = [ 'WScript.Quit()' ];
					}

					if ( input.length == 2 && input[0] + input[1] == '::' ) {
						input = [];
						multiline = ! multiline;
					}

					if ( inputs.length ) {
						inputs[inputs.length] = '\n';
					}
					for (var i = 0; i < input.length; i++) {
						inputs[inputs.length] = input[i];
					}

					if ( ! multiline ) {
						break;
					}

					WScript.StdOut.Write(PS2);

				} // while ( true )

				// Trim left
				var k = 0;
				while ( inputs[k] <= ' ' ) {
					k++;
				}
				// Trim right
				var m = inputs.length - 1;
				while ( inputs[m] <= ' ' ) {
					m--;
				}

				var result = '';
				for (var i = k; i <= m; i++) {
					result += inputs[i];
				}

				if ( result == '' ) {
					return '';
				}

				REPL.history[REPL.history.length] = result;

				return result;

			})()));

		} catch (ERROR) {

			WScript.StdErr.WriteLine(WScript.ScriptName
				+ ': "<stdin>", line ' + REPL.number
				+ ': ' + ERROR.name
				+ ': ' + ERROR.message);

		}

	} // while ( true )
};
]]></script>
<!-- <script language="javascript" src="./wsx/CommandLine.js"></script> -->
<script language="javascript"><![CDATA[
//
// Command Line processor
// This script is the part of the wsx
//
// Copyright (c) 2019, 2020 by Ildar Shaimordanov
//

(function(Program, Runner, REPL) {

	var argv = [];

	function ShowVersion() {
		var me = WScript.ScriptName.replace(/(\.[^.]+\?)?\.[^.]+$/, '');
		var name = typeof NAME == 'string' ? NAME : me;
		var version = typeof VERSION == 'string' ? VERSION : '0.0.1';
		WScript.Echo(name + ' (' + me + '): Version ' + version
			+ '\n' + WScript.Name
			+ ': Version ' + WScript.Version
			+ ', Build ' + WScript.BuildVersion);
	}

	// Walk through all named and unnamed arguments because
	// we have to handle each of them even if they duplicate
	for (var i = 0; i < WScript.Arguments.length; i++) {

		var arg = WScript.Arguments.Item(i);

		var m;

		m = arg.match(/^\/h(?:elp)?$/i);
		if ( m ) {
			WScript.Arguments.ShowUsage();
			WScript.Quit();
		}

		m = arg.match(/^\/version$/i);
		if ( m ) {
			ShowVersion();
			WScript.Quit();
		}

		m = arg.match(/^\/dry-run$/i);
		if ( m ) {
			Program.dryRun = true;
			continue;
		}

		m = arg.match(/^\/q(?:uiet)?$/i);
		if ( m ) {
			Program.setQuiet();
			continue;
		}

		m = arg.match(/^\/use:(js|vbs)$/i);
		if ( m ) {
			Program.setEngine(m[1]);
			continue;
		}

		m = arg.match(/^\/m(?::(js|vbs))?:(.+)$/i);
		if ( m ) {
			Program.addModule(m[1], m[2]);
			continue;
		}

		m = arg.match(/^\/(let|set|get)(?::(js|vbs))?:(\w+)=(.*)$/i);
		if ( m ) {
			Program.addVar(m[2], m[3], m[4], m[1]);
			continue;
		}

		m = arg.match(/^\/(?:e|((?:begin|end)(?:file)?))(?::(js|vbs))?(?::(.*))?$/i);
		if ( m ) {
			Program.addCode(m[2], m[3], m[1]);
			continue;
		}

		m = arg.match(/^\/([np])$/i);
		if ( m ) {
			Program.setMode(m[1]);
			continue;
		}

		/*
		This looks like ugly, but it works and reliable enough to
		stop looping over the rest of the CLI options. From this
		point we allow end users to specify their own options even,
		if their names intersect with names of our options.
		*/
		break;

	}

	for ( ; i < WScript.Arguments.length; i++) {
		var arg = WScript.Arguments.Item(i);
		argv.push(arg);
	}

	Program.detectScriptFile(argv);

	Runner(Program, argv);

})(Program, Runner, REPL);
]]></script>

</job>
</package>
Last edited by siberia-man on 09 Sep 2020 07:13, edited 7 times in total.

aGerman
Expert
Posts: 4654
Joined: 22 Jan 2010 18:01
Location: Germany

Re: wsx.bat: REPL, one line program processor supporting some NodeJS features

#2 Post by aGerman » 29 Feb 2020 05:16

This tool seems to be quite handy. Just one question before I try to dive in a little deeper - where does the file come from in your examples? It seems to be reading from stdin in the first place. So, provided I'd like to print the first 10 lines of "test.txt" how do I do that?

Steffen

siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

Re: wsx.bat: REPL, one line program processor supporting some NodeJS features

#3 Post by siberia-man » 29 Feb 2020 05:40

It works in the same manner as other CLI tools. It can read both STDIN and files. If no file is specified, STDIN is assumed as the input. So you can use it as follows:

reading from a pipe

Code: Select all

do something | wsx /n /e:...
reading from a file or file(s)

Code: Select all

wsx /n /e:... FILE

siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

Re: wsx.bat: REPL, one line program processor supporting some NodeJS features

#4 Post by siberia-man » 29 Feb 2020 06:28

The first post was updated with separating on the sections and adding more descriptions.

aGerman
Expert
Posts: 4654
Joined: 22 Jan 2010 18:01
Location: Germany

Re: wsx.bat: REPL, one line program processor supporting some NodeJS features

#5 Post by aGerman » 29 Feb 2020 10:19

Oh, I see. I was somehow afraid that this would be treated as to belonging to the /e: option then :lol: Sorry for my ignorance.

Steffen

siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

Re: wsx.bat: REPL, one line program processor supporting some NodeJS features

#6 Post by siberia-man » 29 Feb 2020 18:26

No problem. Asking question is OK. They help to improve description. Let me know about the issues you experienced, if they have occurred.

Squashman
Expert
Posts: 4465
Joined: 23 Dec 2011 13:59

Re: wsx.bat: REPL, one line program processor supporting some NodeJS features

#7 Post by Squashman » 29 Feb 2020 19:09

I kind of felt the same way as aGerman. I think it would be better to have a switch for the input and output file names.

siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

Re: wsx.bat: REPL, one line program processor supporting some NodeJS features

#8 Post by siberia-man » 29 Feb 2020 19:39

to have a switch for the input and output
Not agree. Additional switches make usage more complicated. But this is not fully your issue -- I think it is on my side in majority of cases because I can't immediately give complete and/or comprehensive explanation.

In my vision the good tool is a pipe compatible tool. It should be able to read data from any arguments or from the pipe in the absence of any arguments. F.i., using grep we don't complain about switches. Grep'ing STDOUT of another tool we just use a command like this:

Code: Select all

comman1 ... | grep "something" options
or reading data from a particular file we use this one:

Code: Select all

grep "something" options FILENAME
I understand that my descriptions above are not well enough and talking with you I see ways what to say next time for better explanation of the tool (this one at least).

Anyway thank you for your feedbacks. I believe all of them help me to make the tool better.

aGerman
Expert
Posts: 4654
Joined: 22 Jan 2010 18:01
Location: Germany

Re: wsx.bat: REPL, one line program processor supporting some NodeJS features

#9 Post by aGerman » 01 Mar 2020 05:22

Not such a big problem. Only for people that never used the tool and try to figure out how it works. If you run wsx /? you'll get
[...]
Syntax: wsx.bat?.wsf [/help] [/version] [/dry-run] [/quiet] [/use:value] [/m:value] [/let:value] [/set:value] [/get:value] [/e:value] [/n] [/p] [/begin:value] [/end:value] [/beginfile:value] [/endfile:value] [scriptfile] [/f:value] [arguments]
[...]
Now I was a little lost in the first place of where to place the file name. And still I'm confused if the file that shall be processed is the [scriptfile] (which would be quite misleading because usually a script file is a file containing executable code, and I already know that this is the case here) or if it falls in the category of other [arguments] :lol:

However, I certainly didn't even discover the half of the functionality of the tool, but from what I've seen until now it works pretty well.

Steffen

siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

Re: wsx.bat: REPL, one line program processor supporting some NodeJS features

#10 Post by siberia-man » 02 Mar 2020 02:41

As you can find -- everything is optional.

If you enter an one line program, everything after is assumed as a file for processing. Each argument is opened as a file and processed line by line until EOF.

Code: Select all

wsx /n /e:"..." file1 file2 ...
Otherwise. If you don't enter one line programs, the first item of argument list is a scriptfile and the rest of arguments are arguments for the scriptfile. They could be everything and the scriptfile can use them accordingly its functionality:

Code: Select all

wsx scriptfile argument1 argument2 ...
aGerman wrote:
01 Mar 2020 05:22
Now I was a little lost in the first place of where to place the file name. And still I'm confused if the file that shall be processed is the [scriptfile] (which would be quite misleading because usually a script file is a file containing executable code, and I already know that this is the case here) or if it falls in the category of other [arguments] :lol:
As far as I understand you correctly, you are talking about the case:

Code: Select all

wsx /n /e:"..." scriptfile file1 file2 ...
I don't see any problems here. There is one line program above processing everything and a scriptfile as well.

What if I am looking for something? In this example below I look for the string "argv" in all listed files. The one liner is declared so all other arguments are files, so the first argument is not scriptfile at all:

Code: Select all

wsx /n /e:"LINE.match(/argv/) && echo(FILE + ':' + LINE)" wx\REPL.js wsx\CommandLine.js wsx\Program.js wsx\Runner.js wsx.wsf

aGerman
Expert
Posts: 4654
Joined: 22 Jan 2010 18:01
Location: Germany

Re: wsx.bat: REPL, one line program processor supporting some NodeJS features

#11 Post by aGerman » 02 Mar 2020 12:05

After you told me that I just have to pass the files at the end of the command line it was all clear to me. But when I initially read the help message I couldn't figure out that [arguments] is related to the files that should be processed. So, it's of course not an issue of the functionality of the wsx tool. Don't worry too much about that. But maybe consider to update the help message in order to make more intelligible what [arguments] means :wink: Current explanation is "Other arguments for processing" which is quite vague, at least for me.

Steffen

siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

Re: wsx.bat: REPL, one line program processor supporting some NodeJS features

#12 Post by siberia-man » 02 Mar 2020 12:57

I see your point. I need to step back and try to explain everything clearly as much as possible. I'll come back with the updated version of the tool in the first post.

siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

Re: wsx.bat: REPL, one line program processor supporting some NodeJS features

#13 Post by siberia-man » 05 Mar 2020 05:13

Updates came to the first post. The tool version is the same but description is extended.

siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

Re: wsx.bat: REPL, one line program processor supporting some NodeJS features

#14 Post by siberia-man » 08 Sep 2020 10:33

Version is 1.0.1 (still Alpha). I updated the first post in the topic. Most of changes are cosmetic and some of them slightly improve description.

Post Reply