Get logical imports in "wren_modules" working.
There's a lot of changes here and surely some rough edges to iron out. Also, I need to update the docs. But I want to get closer to landing this so I can build on it.
This commit is contained in:
+8
-3
@@ -22,14 +22,19 @@ int main(int argc, const char* argv[])
|
||||
|
||||
osSetArguments(argc, argv);
|
||||
|
||||
WrenInterpretResult result;
|
||||
if (argc == 1)
|
||||
{
|
||||
runRepl();
|
||||
result = runRepl();
|
||||
}
|
||||
else
|
||||
{
|
||||
runFile(argv[1]);
|
||||
result = runFile(argv[1]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
// Exit with an error code if the script failed.
|
||||
if (result == WREN_RESULT_COMPILE_ERROR) return 65; // EX_DATAERR.
|
||||
if (result == WREN_RESULT_RUNTIME_ERROR) return 70; // EX_SOFTWARE.
|
||||
|
||||
return getExitCode();
|
||||
}
|
||||
|
||||
+100
-44
@@ -43,6 +43,75 @@ static void appendSlice(Path* path, Slice slice)
|
||||
path->chars[path->length] = '\0';
|
||||
}
|
||||
|
||||
static bool isSeparator(char c)
|
||||
{
|
||||
// Slash is a separator on POSIX and Windows.
|
||||
if (c == '/') return true;
|
||||
|
||||
// Backslash is only a separator on Windows.
|
||||
#ifdef _WIN32
|
||||
if (c == '\\') return true;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static bool isDriveLetter(char c)
|
||||
{
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
||||
}
|
||||
#endif
|
||||
|
||||
// Gets the length of the prefix of [path] that defines its absolute root.
|
||||
//
|
||||
// Returns 1 the leading "/". On Windows, also handles drive letters ("C:" or
|
||||
// "C:\").
|
||||
//
|
||||
// If the path is not absolute, returns 0.
|
||||
static size_t absolutePrefixLength(const char* path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Drive letter.
|
||||
if (isDriveLetter(path[0]) && path[1] == ':')
|
||||
{
|
||||
if (isSeparator(path->chars[2]))
|
||||
{
|
||||
// Fully absolute path.
|
||||
return 3;
|
||||
} else {
|
||||
// "Half-absolute" path like "C:", which is relative to the current
|
||||
// working directory on drive. It's absolute for our purposes.
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: UNC paths.
|
||||
|
||||
#endif
|
||||
|
||||
// POSIX-style absolute path or absolute path in the current drive on Windows.
|
||||
if (isSeparator(path[0])) return 1;
|
||||
|
||||
// Not absolute.
|
||||
return 0;
|
||||
}
|
||||
|
||||
PathType pathType(const char* path)
|
||||
{
|
||||
if (absolutePrefixLength(path) > 0) return PATH_TYPE_ABSOLUTE;
|
||||
|
||||
// See if it must be relative.
|
||||
if ((path[0] == '.' && isSeparator(path[1])) ||
|
||||
(path[0] == '.' && path[1] == '.' && isSeparator(path[2])))
|
||||
{
|
||||
return PATH_TYPE_RELATIVE;
|
||||
}
|
||||
|
||||
// Otherwise, we don't know.
|
||||
return PATH_TYPE_SIMPLE;
|
||||
}
|
||||
|
||||
Path* pathNew(const char* string)
|
||||
{
|
||||
Path* path = (Path*)malloc(sizeof(Path));
|
||||
@@ -67,7 +136,7 @@ void pathDirName(Path* path)
|
||||
// Find the last path separator.
|
||||
for (size_t i = path->length - 1; i < path->length; i--)
|
||||
{
|
||||
if (path->chars[i] == '/')
|
||||
if (isSeparator(path->chars[i]))
|
||||
{
|
||||
path->length = i;
|
||||
path->chars[i] = '\0';
|
||||
@@ -86,7 +155,7 @@ void pathRemoveExtension(Path* path)
|
||||
{
|
||||
// If we hit a path separator before finding the extension, then the last
|
||||
// component doesn't have one.
|
||||
if (path->chars[i] == '/') return;
|
||||
if (isSeparator(path->chars[i])) return;
|
||||
|
||||
if (path->chars[i] == '.')
|
||||
{
|
||||
@@ -98,7 +167,7 @@ void pathRemoveExtension(Path* path)
|
||||
|
||||
void pathJoin(Path* path, const char* string)
|
||||
{
|
||||
if (path->length > 0 && path->chars[path->length - 1] != '/')
|
||||
if (path->length > 0 && !isSeparator(path->chars[path->length - 1]))
|
||||
{
|
||||
pathAppendChar(path, '/');
|
||||
}
|
||||
@@ -106,37 +175,6 @@ void pathJoin(Path* path, const char* string)
|
||||
pathAppendString(path, string);
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
static bool isDriveLetter(char c)
|
||||
{
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
||||
}
|
||||
#endif
|
||||
|
||||
bool pathIsAbsolute(Path* path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Absolute path in the current drive.
|
||||
if (path->length >= 1 && path->chars[0] == '\\') return true;
|
||||
|
||||
// Drive letter.
|
||||
if (path->length >= 3 &&
|
||||
isDriveLetter(path->chars[0]) &&
|
||||
path->chars[1] == ':' &&
|
||||
path->chars[2] == '\\')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// UNC path.
|
||||
return path->length >= 2 && path->chars[0] == '\\' && path->chars[1] == '\\';
|
||||
#else
|
||||
// Otherwise, assume POSIX-style paths.
|
||||
return path->length >= 1 && path->chars[0] == '/';
|
||||
#endif
|
||||
}
|
||||
|
||||
void pathAppendChar(Path* path, char c)
|
||||
{
|
||||
ensureCapacity(path, path->length + 1);
|
||||
@@ -152,10 +190,8 @@ void pathAppendString(Path* path, const char* string)
|
||||
appendSlice(path, slice);
|
||||
}
|
||||
|
||||
Path* pathNormalize(Path* path)
|
||||
void pathNormalize(Path* path)
|
||||
{
|
||||
Path* result = pathNew("");
|
||||
|
||||
// Split the path into components.
|
||||
Slice components[MAX_COMPONENTS];
|
||||
int numComponents = 0;
|
||||
@@ -167,7 +203,7 @@ Path* pathNormalize(Path* path)
|
||||
int leadingDoubles = 0;
|
||||
for (;;)
|
||||
{
|
||||
if (*end == '\0' || *end == '/')
|
||||
if (*end == '\0' || isSeparator(*end))
|
||||
{
|
||||
// Add the current component.
|
||||
if (start != end)
|
||||
@@ -207,7 +243,7 @@ Path* pathNormalize(Path* path)
|
||||
}
|
||||
|
||||
// Skip over separators.
|
||||
while (*end != '\0' && *end == '/') end++;
|
||||
while (*end != '\0' && isSeparator(*end)) end++;
|
||||
|
||||
start = end;
|
||||
if (*end == '\0') break;
|
||||
@@ -216,13 +252,22 @@ Path* pathNormalize(Path* path)
|
||||
end++;
|
||||
}
|
||||
|
||||
// Preserve the absolute prefix, if any.
|
||||
// Preserve the path type. We don't want to turn, say, "./foo" into "foo"
|
||||
// because that changes the semantics of how that path is handled when used
|
||||
// as an import string.
|
||||
bool needsSeparator = false;
|
||||
if (path->length > 0 && path->chars[0] == '/')
|
||||
|
||||
Path* result = pathNew("");
|
||||
size_t prefixLength = absolutePrefixLength(path->chars);
|
||||
if (prefixLength > 0)
|
||||
{
|
||||
pathAppendChar(result, '/');
|
||||
// It's an absolute path, so preserve the absolute prefix.
|
||||
Slice slice;
|
||||
slice.start = path->chars;
|
||||
slice.end = path->chars + prefixLength;
|
||||
appendSlice(result, slice);
|
||||
}
|
||||
else
|
||||
else if (leadingDoubles > 0)
|
||||
{
|
||||
// Add any leading "..".
|
||||
for (int i = 0; i < leadingDoubles; i++)
|
||||
@@ -232,6 +277,13 @@ Path* pathNormalize(Path* path)
|
||||
needsSeparator = true;
|
||||
}
|
||||
}
|
||||
else if (path->chars[0] == '.' && isSeparator(path->chars[1]))
|
||||
{
|
||||
// Preserve a leading "./", since we use that to distinguish relative from
|
||||
// logical imports.
|
||||
pathAppendChar(result, '.');
|
||||
needsSeparator = true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < numComponents; i++)
|
||||
{
|
||||
@@ -242,7 +294,11 @@ Path* pathNormalize(Path* path)
|
||||
|
||||
if (result->length == 0) pathAppendChar(result, '.');
|
||||
|
||||
return result;
|
||||
// Copy back into the original path.
|
||||
free(path->chars);
|
||||
path->capacity = result->capacity;
|
||||
path->chars = result->chars;
|
||||
path->length = result->length;
|
||||
}
|
||||
|
||||
char* pathToString(Path* path)
|
||||
|
||||
+18
-5
@@ -16,6 +16,22 @@ typedef struct
|
||||
size_t capacity;
|
||||
} Path;
|
||||
|
||||
// Categorizes what form a path is.
|
||||
typedef enum
|
||||
{
|
||||
// An absolute path, starting with "/" on POSIX systems, a drive letter on
|
||||
// Windows, etc.
|
||||
PATH_TYPE_ABSOLUTE,
|
||||
|
||||
// An explicitly relative path, starting with "./" or "../".
|
||||
PATH_TYPE_RELATIVE,
|
||||
|
||||
// A path that has no leading prefix, like "foo/bar".
|
||||
PATH_TYPE_SIMPLE,
|
||||
} PathType;
|
||||
|
||||
PathType pathType(const char* path);
|
||||
|
||||
// Creates a new empty path.
|
||||
Path* pathNew(const char* string);
|
||||
|
||||
@@ -31,9 +47,6 @@ void pathRemoveExtension(Path* path);
|
||||
// Appends [string] to [path].
|
||||
void pathJoin(Path* path, const char* string);
|
||||
|
||||
// Return true if [path] is an absolute path for the host operating system.
|
||||
bool pathIsAbsolute(Path* path);
|
||||
|
||||
// Appends [c] to the path, growing the buffer if needed.
|
||||
void pathAppendChar(Path* path, char c);
|
||||
|
||||
@@ -43,8 +56,8 @@ void pathAppendString(Path* path, const char* string);
|
||||
// Simplifies the path string as much as possible.
|
||||
//
|
||||
// Applies and removes any "." or ".." components, collapses redundant "/"
|
||||
// characters, etc.
|
||||
Path* pathNormalize(Path* path);
|
||||
// characters, and normalizes all path separators to "/".
|
||||
void pathNormalize(Path* path);
|
||||
|
||||
// Allocates a new string exactly the right length and copies this path to it.
|
||||
char* pathToString(Path* path);
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#ifndef stat_h
|
||||
#define stat_h
|
||||
|
||||
// Utilities to smooth over working with stat() in a cross-platform way.
|
||||
|
||||
// Windows doesn't define all of the Unix permission and mode flags by default,
|
||||
// so map them ourselves.
|
||||
#if defined(WIN32) || defined(WIN64)
|
||||
#include <sys\stat.h>
|
||||
|
||||
// Map to Windows permission flags.
|
||||
#define S_IRUSR _S_IREAD
|
||||
#define S_IWUSR _S_IWRITE
|
||||
|
||||
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
||||
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
||||
|
||||
// Not supported on Windows.
|
||||
#define O_SYNC 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+159
-63
@@ -5,6 +5,7 @@
|
||||
#include "modules.h"
|
||||
#include "path.h"
|
||||
#include "scheduler.h"
|
||||
#include "stat.h"
|
||||
#include "vm.h"
|
||||
|
||||
// The single VM instance that the CLI uses.
|
||||
@@ -19,6 +20,7 @@ static uv_loop_t* loop;
|
||||
// TODO: This isn't currently used, but probably will be when package imports
|
||||
// are supported. If not then, then delete this.
|
||||
static char* rootDirectory = NULL;
|
||||
static Path* wrenModulesDirectory = NULL;
|
||||
|
||||
// The exit code to use unless some other error overrides it.
|
||||
int defaultExitCode = 0;
|
||||
@@ -61,46 +63,99 @@ static char* readFile(const char* path)
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static bool isDirectory(Path* path)
|
||||
{
|
||||
uv_fs_t request;
|
||||
uv_fs_stat(loop, &request, path->chars, NULL);
|
||||
// TODO: Check request.result value?
|
||||
|
||||
bool result = request.result == 0 && S_ISDIR(request.statbuf.st_mode);
|
||||
|
||||
uv_fs_req_cleanup(&request);
|
||||
return result;
|
||||
}
|
||||
|
||||
static Path* realPath(Path* path)
|
||||
{
|
||||
uv_fs_t request;
|
||||
uv_fs_realpath(loop, &request, path->chars, NULL);
|
||||
|
||||
Path* result = pathNew((char*)request.ptr);
|
||||
|
||||
uv_fs_req_cleanup(&request);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Starting at [rootDirectory], walks up containing directories looking for a
|
||||
// nearby "wren_modules" directory. If found, stores it in
|
||||
// [wrenModulesDirectory].
|
||||
//
|
||||
// If [wrenModulesDirectory] has already been found, does nothing.
|
||||
static void findModulesDirectory()
|
||||
{
|
||||
if (wrenModulesDirectory != NULL) return;
|
||||
|
||||
Path* searchDirectory = pathNew(rootDirectory);
|
||||
Path* lastPath = realPath(searchDirectory);
|
||||
|
||||
// Keep walking up directories as long as we find them.
|
||||
for (;;)
|
||||
{
|
||||
Path* modulesDirectory = pathNew(searchDirectory->chars);
|
||||
pathJoin(modulesDirectory, "wren_modules");
|
||||
|
||||
if (isDirectory(modulesDirectory))
|
||||
{
|
||||
pathNormalize(modulesDirectory);
|
||||
wrenModulesDirectory = modulesDirectory;
|
||||
pathFree(lastPath);
|
||||
break;
|
||||
}
|
||||
|
||||
pathFree(modulesDirectory);
|
||||
|
||||
// Walk up directories until we hit the root. We can tell that because
|
||||
// adding ".." yields the same real path.
|
||||
pathJoin(searchDirectory, "..");
|
||||
Path* thisPath = realPath(searchDirectory);
|
||||
if (strcmp(lastPath->chars, thisPath->chars) == 0)
|
||||
{
|
||||
pathFree(thisPath);
|
||||
break;
|
||||
}
|
||||
|
||||
pathFree(lastPath);
|
||||
lastPath = thisPath;
|
||||
}
|
||||
|
||||
pathFree(searchDirectory);
|
||||
}
|
||||
|
||||
// Applies the CLI's import resolution policy. The rules are:
|
||||
//
|
||||
// * If [name] starts with "./" or "../", it is a relative import, relative to
|
||||
// [importer]. The resolved path is [name] concatenated onto the directory
|
||||
// * If [module] starts with "./" or "../", it is a relative import, relative
|
||||
// to [importer]. The resolved path is [name] concatenated onto the directory
|
||||
// containing [importer] and then normalized.
|
||||
//
|
||||
// For example, importing "./a/./b/../c" from "d/e/f" gives you "d/e/a/c".
|
||||
//
|
||||
// * Otherwise, it is a "package" import. This isn't implemented yet.
|
||||
//
|
||||
// For example, importing "./a/./b/../c" from "./d/e/f" gives you "./d/e/a/c".
|
||||
static const char* resolveModule(WrenVM* vm, const char* importer,
|
||||
const char* name)
|
||||
const char* module)
|
||||
{
|
||||
size_t nameLength = strlen(name);
|
||||
// Logical import strings are used as-is and need no resolution.
|
||||
if (pathType(module) == PATH_TYPE_SIMPLE) return module;
|
||||
|
||||
// See if it's a relative import.
|
||||
if (nameLength > 2 &&
|
||||
((name[0] == '.' && name[1] == '/') ||
|
||||
(name[0] == '.' && name[1] == '.' && name[2] == '/')))
|
||||
{
|
||||
// Get the directory containing the importing module.
|
||||
Path* relative = pathNew(importer);
|
||||
pathDirName(relative);
|
||||
|
||||
// Add the relative import path.
|
||||
pathJoin(relative, name);
|
||||
Path* normal = pathNormalize(relative);
|
||||
pathFree(relative);
|
||||
|
||||
char* resolved = pathToString(normal);
|
||||
pathFree(normal);
|
||||
return resolved;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Implement package imports. For now, treat any non-relative import
|
||||
// as an import relative to the current working directory.
|
||||
}
|
||||
// Get the directory containing the importing module.
|
||||
Path* path = pathNew(importer);
|
||||
pathDirName(path);
|
||||
|
||||
return name;
|
||||
// Add the relative import path.
|
||||
pathJoin(path, module);
|
||||
|
||||
pathNormalize(path);
|
||||
char* resolved = pathToString(path);
|
||||
|
||||
pathFree(path);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
// Attempts to read the source for [module] relative to the current root
|
||||
@@ -110,23 +165,40 @@ static const char* resolveModule(WrenVM* vm, const char* importer,
|
||||
// module was found but could not be read.
|
||||
static char* readModule(WrenVM* vm, const char* module)
|
||||
{
|
||||
// Since the module has already been resolved, it should now be either a
|
||||
// valid relative path, or a package-style name.
|
||||
|
||||
// TODO: Implement package imports.
|
||||
Path* filePath;
|
||||
if (pathType(module) == PATH_TYPE_SIMPLE)
|
||||
{
|
||||
// If there is no "wren_modules" directory, then the only logical imports
|
||||
// we can handle are built-in ones. Let the VM try to handle it.
|
||||
findModulesDirectory();
|
||||
if (wrenModulesDirectory == NULL) return readBuiltInModule(module);
|
||||
|
||||
// TODO: Should we explicitly check for the existence of the module's base
|
||||
// directory inside "wren_modules" here?
|
||||
|
||||
// Look up the module in "wren_modules".
|
||||
filePath = pathNew(wrenModulesDirectory->chars);
|
||||
pathJoin(filePath, module);
|
||||
|
||||
// If the module is a single bare name, treat it as a module with the same
|
||||
// name inside the package. So "foo" means "foo/foo".
|
||||
if (strchr(module, '/') == NULL) pathJoin(filePath, module);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The module path is already a file path.
|
||||
filePath = pathNew(module);
|
||||
}
|
||||
|
||||
// Add a ".wren" file extension.
|
||||
Path* modulePath = pathNew(module);
|
||||
pathAppendString(modulePath, ".wren");
|
||||
|
||||
char* source = readFile(modulePath->chars);
|
||||
pathFree(modulePath);
|
||||
|
||||
if (source != NULL) return source;
|
||||
pathAppendString(filePath, ".wren");
|
||||
|
||||
// TODO: This used to look for a file named "<path>/module.wren" if
|
||||
// "<path>.wren" could not be found. Do we still want to support that with
|
||||
// the new relative import and package stuff?
|
||||
char* source = readFile(filePath->chars);
|
||||
pathFree(filePath);
|
||||
|
||||
// If we didn't find it, it may be a module built into the CLI or VM, so keep
|
||||
// going.
|
||||
if (source != NULL) return source;
|
||||
|
||||
// Otherwise, see if it's a built-in module.
|
||||
return readBuiltInModule(module);
|
||||
@@ -222,9 +294,11 @@ static void freeVM()
|
||||
wrenFreeVM(vm);
|
||||
|
||||
uv_tty_reset_mode();
|
||||
|
||||
if (wrenModulesDirectory != NULL) pathFree(wrenModulesDirectory);
|
||||
}
|
||||
|
||||
void runFile(const char* path)
|
||||
WrenInterpretResult runFile(const char* path)
|
||||
{
|
||||
char* source = readFile(path);
|
||||
if (source == NULL)
|
||||
@@ -233,19 +307,36 @@ void runFile(const char* path)
|
||||
exit(66);
|
||||
}
|
||||
|
||||
// If it looks like a relative path, make it explicitly relative so that we
|
||||
// can distinguish it from logical paths.
|
||||
// TODO: It might be nice to be able to run scripts from within a surrounding
|
||||
// "wren_modules" directory by passing in a simple path like "foo/bar". In
|
||||
// that case, here, we could check to see whether the give path exists inside
|
||||
// "wren_modules" or as a relative path and choose to add "./" or not based
|
||||
// on that.
|
||||
Path* module = pathNew(path);
|
||||
if (pathType(module->chars) == PATH_TYPE_SIMPLE)
|
||||
{
|
||||
Path* relative = pathNew(".");
|
||||
pathJoin(relative, path);
|
||||
|
||||
pathFree(module);
|
||||
module = relative;
|
||||
}
|
||||
|
||||
pathRemoveExtension(module);
|
||||
|
||||
// Use the directory where the file is as the root to resolve imports
|
||||
// relative to.
|
||||
Path* directory = pathNew(path);
|
||||
Path* directory = pathNew(module->chars);
|
||||
|
||||
pathDirName(directory);
|
||||
rootDirectory = pathToString(directory);
|
||||
pathFree(directory);
|
||||
|
||||
Path* moduleName = pathNew(path);
|
||||
pathRemoveExtension(moduleName);
|
||||
|
||||
initVM();
|
||||
|
||||
WrenInterpretResult result = wrenInterpret(vm, moduleName->chars, source);
|
||||
WrenInterpretResult result = wrenInterpret(vm, module->chars, source);
|
||||
|
||||
if (afterLoadFn != NULL) afterLoadFn(vm);
|
||||
|
||||
@@ -258,29 +349,29 @@ void runFile(const char* path)
|
||||
|
||||
free(source);
|
||||
free(rootDirectory);
|
||||
pathFree(moduleName);
|
||||
pathFree(module);
|
||||
|
||||
// Exit with an error code if the script failed.
|
||||
if (result == WREN_RESULT_COMPILE_ERROR) exit(65); // EX_DATAERR.
|
||||
if (result == WREN_RESULT_RUNTIME_ERROR) exit(70); // EX_SOFTWARE.
|
||||
|
||||
if (defaultExitCode != 0) exit(defaultExitCode);
|
||||
return result;
|
||||
}
|
||||
|
||||
int runRepl()
|
||||
WrenInterpretResult runRepl()
|
||||
{
|
||||
rootDirectory = ".";
|
||||
initVM();
|
||||
|
||||
printf("\\\\/\"-\n");
|
||||
printf(" \\_/ wren v%s\n", WREN_VERSION_STRING);
|
||||
|
||||
wrenInterpret(vm, "repl", "import \"repl\"\n");
|
||||
WrenInterpretResult result = wrenInterpret(vm, "<repl>", "import \"repl\"\n");
|
||||
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
if (result == WREN_RESULT_SUCCESS)
|
||||
{
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
}
|
||||
|
||||
freeVM();
|
||||
|
||||
return 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
WrenVM* getVM()
|
||||
@@ -293,6 +384,11 @@ uv_loop_t* getLoop()
|
||||
return loop;
|
||||
}
|
||||
|
||||
int getExitCode()
|
||||
{
|
||||
return defaultExitCode;
|
||||
}
|
||||
|
||||
void setExitCode(int exitCode)
|
||||
{
|
||||
defaultExitCode = exitCode;
|
||||
|
||||
+5
-4
@@ -5,12 +5,10 @@
|
||||
#include "wren.h"
|
||||
|
||||
// Executes the Wren script at [path] in a new VM.
|
||||
//
|
||||
// Exits if the script failed or could not be loaded.
|
||||
void runFile(const char* path);
|
||||
WrenInterpretResult runFile(const char* path);
|
||||
|
||||
// Runs the Wren interactive REPL.
|
||||
int runRepl();
|
||||
WrenInterpretResult runRepl();
|
||||
|
||||
// Gets the currently running VM.
|
||||
WrenVM* getVM();
|
||||
@@ -18,6 +16,9 @@ WrenVM* getVM();
|
||||
// Gets the event loop the VM is using.
|
||||
uv_loop_t* getLoop();
|
||||
|
||||
// Get the exit code the CLI should exit with when done.
|
||||
int getExitCode();
|
||||
|
||||
// Set the exit code the CLI should exit with when done.
|
||||
void setExitCode(int exitCode);
|
||||
|
||||
|
||||
+1
-16
@@ -4,28 +4,13 @@
|
||||
#include "uv.h"
|
||||
|
||||
#include "scheduler.h"
|
||||
#include "stat.h"
|
||||
#include "vm.h"
|
||||
#include "wren.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
// Windows doesn't define all of the Unix permission and mode flags by default,
|
||||
// so map them ourselves.
|
||||
#if defined(WIN32) || defined(WIN64)
|
||||
#include <sys\stat.h>
|
||||
|
||||
// Map to Windows permission flags.
|
||||
#define S_IRUSR _S_IREAD
|
||||
#define S_IWUSR _S_IWRITE
|
||||
|
||||
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
||||
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
||||
|
||||
// Not supported on Windows.
|
||||
#define O_SYNC 0
|
||||
#endif
|
||||
|
||||
typedef struct sFileRequestData
|
||||
{
|
||||
WrenHandle* fiber;
|
||||
|
||||
+2
-2
@@ -723,7 +723,7 @@ static Value resolveModule(WrenVM* vm, Value name)
|
||||
return name;
|
||||
}
|
||||
|
||||
Value wrenImportModule(WrenVM* vm, Value name)
|
||||
static Value importModule(WrenVM* vm, Value name)
|
||||
{
|
||||
name = resolveModule(vm, name);
|
||||
|
||||
@@ -1308,7 +1308,7 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
||||
{
|
||||
Value name = fn->constants.data[READ_SHORT()];
|
||||
|
||||
Value result = wrenImportModule(vm, name);
|
||||
Value result = importModule(vm, name);
|
||||
if (!IS_NULL(fiber->error)) RUNTIME_ERROR();
|
||||
|
||||
// Make a slot on the stack for the module's closure to place the return
|
||||
|
||||
Reference in New Issue
Block a user