On 9/2/20 5:56 AM, Andrey Zherikov wrote:
==============
Everything works well until I have included scripts in subdirectories:
├── dir1
│ ├── dir2
│ │ └── script
│ └── script
└── script
Content:
============== script
msg hello
include dir1/script
============== dir1/script
msg hello from dir1
include dir2/script
============== dir1/dir2/script
msg hello from dir1/dir2
==============
Compilation fails with "Error: file `"dir2/script"` cannot be found or
not in a path specified with `-J`" (I used simple dmd -J. -run parser.d)
which is expected because parse* functions do not track the directory
where the script is located.
In this simple example the issue can be fixed by passing path to script
as a parameter to parseScript function. But this doesn't seem to be
flexible and extendable solution because there can be other commands
that might call parseFile indirectly (they can even be in other modules).
Theoretically this can be solved by doing something like this but it
doesn't work because "static variable `paths` cannot be read at compile
time":
==============
string[] paths;
void parseFile(string file)()
{
enum path = paths.length > 0 ? buildPath(paths[$-1],
file.dirName()) : file.dirName();
paths ~= path;
scope(exit) paths = paths[0..$-1];
enum script = import(buildPath(path, file));
mixin(parseScript(script));
}
==============
Note that the whole point is to do this parsing at compile time.
OK, NOW I see where your code is coming from. The issue is that you need
the directory of the script imported to be the "local directory". Your
solution will not work -- you can't mixin code that is not available at
compile time.
Here is what I would do instead:
string parseScript(string filename, string script)
{
string code;
string base = dirName(filename);
if(base[$-1] != '/') base ~= '/';
foreach(line; script.lineSplitter())
{
auto idx = line.indexOf(' ');
switch(line[0..idx])
{
case "msg":
code ~= "writeln(\"" ~ line[idx+1..$] ~ "\");";
break;
case "include":
{
code ~= `parseFile!"`;
string importfile = line[idx+1 .. $];
if(!importfile.startsWith('/')) // relative path
code ~= base;
code ~= importfile ~ `";`;
break;
}
default: break;
}
}
return code;
}
And pass the filename to this function in addition to the script source.
-Steve