www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - CTFE & code generators based on PEG grammars?

reply =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
I like the idea of DSL which are transpiled into the target language 
that gets compiled or are interpreted at run-time. Since D is compiled, 
I would like to transpile the DSL to D at compile-time.

The idea is, that I can write a string (or maybe even a scope block?) 
in my DSL and use a CTFE grammer to transpile the code.

Does anybody has any experience with such an approach? Are the CTFE 
facilities overall sufficient to implement something like this? Or is 
there a much better approach?

-- 
Robert M. Münch
http://www.saphirion.com
smarter | better | faster
Apr 06
next sibling parent Seb <seb wilzba.ch> writes:
On Saturday, 6 April 2019 at 12:06:22 UTC, Robert M. Münch wrote:
 I like the idea of DSL which are transpiled into the target 
 language that gets compiled or are interpreted at run-time. 
 Since D is compiled, I would like to transpile the DSL to D at 
 compile-time.

 The idea is, that I can write a string (or maybe even a scope 
 block?) in my DSL and use a CTFE grammer to transpile the code.

 Does anybody has any experience with such an approach? Are the 
 CTFE facilities overall sufficient to implement something like 
 this? Or is there a much better approach?
One of the most famous examples here is Vibe.d's Diet template: https://github.com/rejectedsoftware/diet-ng tl;dr: CTFE is sufficient, but the downside is that it will be run everytime you build your program which can increase the build time.
Apr 06
prev sibling next sibling parent reply Alex <AJ gmail.com> writes:
On Saturday, 6 April 2019 at 12:06:22 UTC, Robert M. Münch wrote:
 I like the idea of DSL which are transpiled into the target 
 language that gets compiled or are interpreted at run-time. 
 Since D is compiled, I would like to transpile the DSL to D at 
 compile-time.

 The idea is, that I can write a string (or maybe even a scope 
 block?) in my DSL and use a CTFE grammer to transpile the code.

 Does anybody has any experience with such an approach? Are the 
 CTFE facilities overall sufficient to implement something like 
 this? Or is there a much better approach?
Sure, there is no problem. PEG actually has several grammars. CTFE is CT, and CTFE is in general RT(as long as it depends on no RT inputs). That is CTFE is CT RTFE where runtime functions are executed at compile time when the inputs are CT. The issues is that you get no debugger support to help you and so it can be a real pain for complex languages.
Apr 06
parent reply ag0aep6g <anonymous example.com> writes:
On 06.04.19 16:19, Alex wrote:
 That is CTFE is CT RTFE where runtime functions are executed at compile 
 time when the inputs are CT.
You may have the right idea, but the last part of that sentence is wrong/misleading. CTFE happens when the result is required at CT. The inputs must be available at CT, but that alone doesn't trigger CTFE. Examples: ---- bool f(string s) { return __ctfe; } void main() { import std.stdio: readln; enum x = f("foo"); /* CTFE */ enum y = f(readln()); /* error, because "readln" can't be CTFEd */ auto z = f("foo"); /* not CTFE */ } ----
Apr 06
parent reply Alex <AJ gmail.com> writes:
On Saturday, 6 April 2019 at 14:59:18 UTC, ag0aep6g wrote:
 On 06.04.19 16:19, Alex wrote:
 That is CTFE is CT RTFE where runtime functions are executed 
 at compile time when the inputs are CT.
You may have the right idea, but the last part of that sentence is wrong/misleading. CTFE happens when the result is required at CT. The inputs must be available at CT, but that alone doesn't trigger CTFE. Examples: ---- bool f(string s) { return __ctfe; } void main() { import std.stdio: readln; enum x = f("foo"); /* CTFE */ enum y = f(readln()); /* error, because "readln" can't be CTFEd */ auto z = f("foo"); /* not CTFE */ } ----
readlin is not a CT function. You misinterpreted what I said. The function I am talking about is the runtime function(That is a function that can be run at RT) is to be CTFE'ed. readln is not to be CTFE'ed. There is nothing I said that contradicts what you wrote. The input to f, the RT function to be CTFE'ed cannot be CTFE'ed because one of it's inputs is not known at CT, that input is readln, which is another RT that could be CTFE'ed but it cannot because it's input is not known at CT. It is obvious that if any RT function(as opposed to a CT function which obviously is ran at CT. CT functions are pure templates) is CTFE'able only if all it's inputs are CTFE'able. This creates a chain reaction. Your chain fails at readln. Again, you misinterpreted what I said. I will be clear once more so we are not in disagreement:
 That is CTFE is CT RTFE where runtime functions are executed 
 at compile time when the inputs are CT.
CTFE is compile time runtime function execution. That is, it exectues *RUNTIME functions* at compile time. I'm not saying that it executes all runtime functions at compile time, I'm saying it executes runtime functions. THat is PRECISELY what it does. It takes a function that is designed or to be run at RT(as far as the compiler's semantics see it) and it executes it. That, of course, may fail, but that is irrelevant. The CTFE engine executes RT functions. If you think "Well, it will then executes readln()", yes, it does... but it fails and so the output of readln is not known at CT and the chain breaks which is why I said the inputs have to be CT, the input to readln is CT, the input to f is not CT since the output of readln is not CT. (readln actually has a hidden input that is not known at CT, which is the keyboard). If you think of it the way I have said and understand what it means then it is much easier to understand CTFE because you really are just doing "normal" programming of RT functions but you just execute them at CT if possible(and the possibility simply is if the inputs are known at CT). By thinking this way there is really no confusion with CTFE, everyone that programs can program in CTFE without any complications about meta programming. CTFE is not meta programming(but one can use meta programming with it for more complex work). CTFE programming is RT programming but where one can optimize before RT. It becomes very simple to think about rather than trying to pretend there is some complex distinction.
Apr 06
next sibling parent reply zabruk <sorry noem.ail> writes:
On Sunday, 7 April 2019 at 03:32:45 UTC, Alex wrote:
 just execute them at CT if possible(and the possibility simply 
 is if the inputs are known at CT).
imho, Bastiaan Veelo want to say about citate above: not just "if possible", but "only if needed and possible"
Apr 06
parent Alex <AJ gmail.com> writes:
On Sunday, 7 April 2019 at 06:39:05 UTC, zabruk wrote:
 On Sunday, 7 April 2019 at 03:32:45 UTC, Alex wrote:
 just execute them at CT if possible(and the possibility simply 
 is if the inputs are known at CT).
imho, Bastiaan Veelo want to say about citate above: not just "if possible", but "only if needed and possible"
True, but the only if needed is vacuous. As the meta parser does it's thing it will come across RT function usage and it can easily tell if the arguments are known at CT and then will evaluate them. So, unless I'm mistaken about how the meta compiler works, it won't ever trigger CTFE on any CTFE'able functions that are not used. The reason being is that any time a function call is made, the arguments must be known(including hidden/global arguments) and if they are determined at CT already then a CT call can and must be made(that is what makes CTFE what it is). One can't CTFE if one doesn't have the inputs to the function... and the compiler doesn't just randomly compile RT functions without knowing the inputs... and the inputs are found at the use site. I don't even thing the "only if needed" makes a lot of sense. The only time they are needed is exactly when they are needed, there is no other use cause in CTFE. Can you give me a simple example that shows where a CTFE occurs that isn't needed and the compiler would try to CTFE it? By need, we mean with all optimizations, else CTFE is never needed.
Apr 07
prev sibling parent ag0aep6g <anonymous example.com> writes:
On 07.04.19 05:32, Alex wrote:
 readlin is not a CT function. You misinterpreted what I said.
Yeah, bad example from me. This would probably have been better: ---- auto v = "foo"; enum y = f(v); /* Error: variable v cannot be read at compile time */ ---- Also, the `readln` example wasn't meant to contradict anything you said. The one after it (`auto z = f("foo"); /* not CTFE */`) is supposed to clarify your statement that "CTFE [happens] when the inputs are CT". It's true that CTFE can only happen when the inputs are known at CT, but that alone doesn't force CTFE. [...]
 If you think of it the way I have said and understand what it means then 
 it is much easier to understand CTFE because you really are just doing 
 "normal" programming of RT functions but you just execute them at CT if 
 possible(and the possibility simply is if the inputs are known at CT).
There's that same slight unclarity again. You may have the right understanding of CTFE, but your phrasing is a bit misleading. CTFE doesn't kick in whenever it's possible to precompute something at CT. It only kicks in when the result is required at CT by language rules. [...]
 CTFE programming is RT 
 programming but where one can optimize before RT.
Optimization is not the only use for CTFE. It can also be used to generate compile-time constants, like lengths for static arrays or D code itself that is then mixed in. Can't do that stuff with run-time calls.
Apr 07
prev sibling parent reply Bastiaan Veelo <Bastiaan Veelo.net> writes:
On Saturday, 6 April 2019 at 12:06:22 UTC, Robert M. Münch wrote:
 The idea is, that I can write a string (or maybe even a scope 
 block?) in my DSL and use a CTFE grammer to transpile the code.
Are you aware of Pegged[1]? It’s for exactly that. [1] http://code.dlang.org/packages/pegged
Apr 06
parent =?utf-8?Q?Robert_M._M=C3=BCnch?= <robert.muench saphirion.com> writes:
On 2019-04-06 20:16:14 +0000, Bastiaan Veelo said:

 On Saturday, 6 April 2019 at 12:06:22 UTC, Robert M. Münch wrote:
 The idea is, that I can write a string (or maybe even a scope block?) 
 in my DSL and use a CTFE grammer to transpile the code.
Are you aware of Pegged[1]? It’s for exactly that. [1] http://code.dlang.org/packages/pegged
Yes, I'm but I didn't remember/thought about using a mixin(...) to use the parsed and transformed result at compiler time. Thanks. BTW: The reference for this specific use-case is here: https://github.com/PhilippeSigaud/Pegged/wiki/Generating-Code -- Robert M. Münch http://www.saphirion.com smarter | better | faster
Apr 06