How to create compile-time container?

Andrey Zherikov andrey.zherikov at gmail.com
Wed Sep 2 09:56:11 UTC 2020


On Tuesday, 1 September 2020 at 19:38:29 UTC, Steven 
Schveighoffer wrote:
> On 9/1/20 3:09 PM, Andrey Zherikov wrote:
>> Unfortunately this won't work if there is a function 'bar' in 
>> different module that calls 'foo':
>
> You should post a full example you expect to work or not work, 
> then we can discuss.
>
> I think it should work (I've tried it), but there are several 
> problems that could possibly happen with your code, and it's 
> hard to tell what you mean by "won't work" with the incomplete 
> code that you posted.
>
> -Steve

Sorry for confusion. I'm trying to implement compile-time 
scripting and the idea is pretty simple: I have a script file and 
I want to convert it to D code during compilation. I've done some 
things but stuck at the point when script includes another script.

For simplicity, let's say that script file has commands (one per 
line) with syntax "<command><space><parameter>" and only two 
commands available= "msg" to print text and "include" to include 
another script.
Here is my code:
==============
void parseFile(string file)()
{
     enum script = import(file);
     mixin(parseScript(script));
}
string parseScript(string script)
{
     string code;

     foreach(line; script.lineSplitter())
     {
         auto idx = line.indexOf(' ');

         switch(line[0..idx])
         {
             case "msg"=
                 code ~= "writeln(\"" ~ line[idx+1..$] ~ "\");";
                 break;
             case "include"=
                 code ~= "parseFile!\"" ~ line[idx+1..$] ~ "\";";
                 break;
             default= break;
         }
     }
     return code;
}
void main()
{
     parseFile!"script";
}
==============
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.


More information about the Digitalmars-d-learn mailing list