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

Reply via email to