Add io.include() function to Nasal base library
io.inlude() loads and executes a Nasal file in place, effectively embedding the script in the calling namespace. The function adds a symbol mark in the namespace to avoid duplicate loading. Additionally, in this path: + io.basename() & io.dirname(): Convenience functions for managing path strings. Designed after their unix counterparts. + string.normpath(): Improved support for relative paths. It can now handle paths starting with double dots, like ../../Directory
This commit is contained in:
parent
b6f3e32212
commit
6ae3fae393
2 changed files with 65 additions and 13 deletions
53
Nasal/io.nas
53
Nasal/io.nas
|
@ -8,6 +8,57 @@ var readfile = func(file) {
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# basename(<path>), dirname(<path>)
|
||||||
|
#
|
||||||
|
# Work like standard Unix commands: basename returns the file name from a given
|
||||||
|
# path, and dirname returns the directory part.
|
||||||
|
|
||||||
|
var basename = func(path) {
|
||||||
|
split("/", string.normpath(path))[-1];
|
||||||
|
};
|
||||||
|
|
||||||
|
var dirname = func(path) {
|
||||||
|
path = string.normpath(path);
|
||||||
|
substr(path, 0, size(path) - size(basename(path)));
|
||||||
|
};
|
||||||
|
|
||||||
|
# include(<filename>)
|
||||||
|
#
|
||||||
|
# Loads and executes a Nasal file in place. The file is searched for in the
|
||||||
|
# calling script directory and in standard FG directories (in that order).
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
#
|
||||||
|
# io.include("Aircraft/Generic/library.nas");
|
||||||
|
# io.include("my_other_file.nas");
|
||||||
|
|
||||||
|
var include = func(file) {
|
||||||
|
|
||||||
|
file = string.normpath(file);
|
||||||
|
var clr = caller();
|
||||||
|
var (ns, fn, fl) = clr;
|
||||||
|
|
||||||
|
var local_file = dirname(fl) ~ file;
|
||||||
|
var path = (stat(local_file) != nil)? local_file : resolvepath(file);
|
||||||
|
|
||||||
|
if (path == "") die("File not found: ", file);
|
||||||
|
|
||||||
|
var module = "__" ~ path ~ "__";
|
||||||
|
if (contains(ns, module))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ns[module] = "included";
|
||||||
|
|
||||||
|
var code = call(compile, [readfile(path), path], var err = []);
|
||||||
|
if (size(err)) {
|
||||||
|
if (find("Parse error:", err[0]) < 0)
|
||||||
|
die(err[0]);
|
||||||
|
else
|
||||||
|
die(sprintf("%s\n in included file: %s", err[0], path));
|
||||||
|
}
|
||||||
|
|
||||||
|
call(bind(code, ns, fn), [], nil, ns);
|
||||||
|
}
|
||||||
|
|
||||||
# Loads Nasal file into namespace and executes it. The namespace
|
# Loads Nasal file into namespace and executes it. The namespace
|
||||||
# (module name) is taken from the optional second argument, or
|
# (module name) is taken from the optional second argument, or
|
||||||
|
@ -33,8 +84,6 @@ var load_nasal = func(file, module = nil) {
|
||||||
|
|
||||||
var code = call(func compile(readfile(file), file), nil, var err = []);
|
var code = call(func compile(readfile(file), file), nil, var err = []);
|
||||||
if (size(err)) {
|
if (size(err)) {
|
||||||
(func nil)(); # FIXME hack around Nasal bug
|
|
||||||
|
|
||||||
if (substr(err[0], 0, 12) == "Parse error:") { # hack around Nasal feature
|
if (substr(err[0], 0, 12) == "Parse error:") { # hack around Nasal feature
|
||||||
var e = split(" at line ", err[0]);
|
var e = split(" at line ", err[0]);
|
||||||
if (size(e) == 2)
|
if (size(e) == 2)
|
||||||
|
|
|
@ -191,25 +191,28 @@ match = func(str, patt) {
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Removes superfluous slashes, empty and "." elements, expands
|
# Removes superfluous slashes, empty and "." elements,
|
||||||
# all ".." elements, and turns all backslashes into slashes.
|
# expands all ".." elements keeping relative paths,
|
||||||
# The result will start with a slash if it started with a slash
|
# and turns all backslashes into slashes.
|
||||||
# or backslash, it will end without slash. Should be applied to
|
# The result will start with a slash if it started with a slash or backslash,
|
||||||
# absolute property or file paths, otherwise ".." elements might
|
# it will end without slash.
|
||||||
# be resolved wrongly.
|
|
||||||
#
|
#
|
||||||
normpath = func(path) {
|
normpath = func(path) {
|
||||||
path = replace(path, "\\", "/");
|
path = replace(path, "\\", "/");
|
||||||
var prefix = size(path) and path[0] == `/` ? "/" : "";
|
var prefix = size(path) and path[0] == `/` ? "/" : "";
|
||||||
|
|
||||||
var stack = [];
|
var stack = [];
|
||||||
|
var relative = 1;
|
||||||
|
|
||||||
foreach (var e; split("/", path)) {
|
foreach (var e; split("/", path)) {
|
||||||
if (e == "." or e == "")
|
if (e == "." or e == "")
|
||||||
continue;
|
continue;
|
||||||
elsif (e == "..")
|
elsif (e == ".." and !relative)
|
||||||
pop(stack);
|
pop(stack);
|
||||||
else
|
else {
|
||||||
append(stack, e);
|
append(stack, e);
|
||||||
|
relative = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return size(stack) ? prefix ~ join("/", stack) : "/";
|
return size(stack) ? prefix ~ join("/", stack) : "/";
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue