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 --
{%
	push(REQUIRE_SEARCH_PATH, TESTFILES_PATH + '/*.uc');

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

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

-- Expect stderr --
Runtime error: Unable to compile source file './files/require/test/broken.uc':

  | Syntax error: Expecting label
  | In line 2, byte 10:
  |
  |  `return {`
  |           ^-- Near here

In line 4, byte 31:

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


-- End --