The `require()` function loads the specified module, executes it and returns
the returned value to the caller.

The global array `REQUIRE_SEARCH_PATH` specifies the list of locations to
check for a matching module file.

The return value of a successfully loaded module is cached in a global
registry, subsequent require calls with the same name will return the
cached value.

Throws an exception if the global `REQUIRE_SEARCH_PATH` variable is unset or
not pointing to an array.

Throws an exception if the requested module name cannot be found.

Throws an exception if a module file could be found but not opened.

Throws an exception if a module file could not be compiled.

Returns the value returned by the invoked module code (typically an object).

-- Testcase --
{%
	push(REQUIRE_SEARCH_PATH, TESTFILES_PATH + '/*.uc');

	let mod1 = require("require.test.module");
	printf("require() #1 returned %.J\n\n", mod1);

	let mod2 = require("require.test.module");
	printf("require() #2 returned %.J\n\n", mod2);

	printf("Instances are identical: %s\n\n", mod1 === mod2);

	// deleting the entry from the global module registry forces reload
	delete global.modules["require.test.module"];

	let mod3 = require("require.test.module");
	printf("require() #3 returned %.J\n\n", mod3);

	printf("Instances are identical: %s\n\n", mod1 === mod3);
%}
-- End --

-- File require/test/module.uc --
{%
	print("This is require.test.module running!\n\n");

	return {
		greeting: function(name) {
			printf("Hello, %s!\n", name);
		}
	};
%}
-- End --

-- Expect stdout --
This is require.test.module running!

require() #1 returned {
	"greeting": "function(name) { ... }"
}

require() #2 returned {
	"greeting": "function(name) { ... }"
}

Instances are identical: true

This is require.test.module running!

require() #3 returned {
	"greeting": "function(name) { ... }"
}

Instances are identical: false

-- End --


A clobbered `REQUIRE_SEARCH_PATH` triggers an exception.

-- Testcase --
{%
	REQUIRE_SEARCH_PATH = null;

	require("test");
%}
-- End --

-- Expect stderr --
Runtime error: Global require search path not set
In line 4, byte 16:

 `    require("test");`
  Near here --------^


-- End --


A not found module triggers an exception.

-- Testcase --
{%
	require("test");
%}
-- End --

-- Expect stderr --
Runtime error: No module named 'test' could be found
In line 2, byte 16:

 `    require("test");`
  Near here --------^


-- End --


A compilation error in the module triggers an exception.

-- Testcase --
{%
	try {
		push(REQUIRE_SEARCH_PATH, TESTFILES_PATH + '/*.uc');

		require("require.test.broken");
	}
	catch (e) {
		// Catch and rethrow exception with modified message to
		// ensure stable test output.
		e.message = replace(e.message,
			/(compile module '.+require\/test\/broken\.uc')/,
			"compile module '.../require/test/broken.uc'");

		die(e);
	}
%}
-- End --

-- File require/test/broken.uc --
{%
	// Unclosed object to force syntax error
	return {
%}
-- End --

-- Expect stderr --
Unable to compile module '.../require/test/broken.uc':
Syntax error: Expecting label
In line 3, byte 11:

 `    return {`
  Near here --^



In line 14, byte 8:

 `        die(e);`
  Near here ---^


-- End --
