From 6474f4a6862045d1a92194ad398a2c50e560122d Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Wed, 9 Feb 2022 11:54:29 +0000 Subject: [PATCH 1/6] [articles/mixin.dd] Add concatenation, statements, types, expressions Add 4 sections. Split statement spec examples into 2. Use foreach and show mixin reading variable declared in current scope. Move mixin variable declaration example to MixinDeclaration from statement.dd. --- articles/mixin.dd | 87 +++++++++++++++++++++++++++++++++++++++++------ spec/module.dd | 7 ++++ spec/statement.dd | 26 ++++++-------- 3 files changed, 94 insertions(+), 26 deletions(-) diff --git a/articles/mixin.dd b/articles/mixin.dd index d061663cb3..affa2063f3 100644 --- a/articles/mixin.dd +++ b/articles/mixin.dd @@ -2,31 +2,96 @@ Ddoc $(D_S String Mixins, +$(HEADERNAV_TOC) + $(P String Mixins (not to be confused with $(DDLINK spec/template-mixin, Template Mixins, template mixins)) enable string constants to be compiled as regular D code and inserted into the program. + Mixins can be used to generate:) + + $(UL + $(LI Declarations) + $(LI Statements) + $(LI Types) + $(LI Expressions) + ) + $(P Combining this with compile time manipulation of strings enables the creation of domain-specific languages. + For example, we can generate a declaration `struct Foo { int bar; }` + using string constants for the identifiers: ) - $(P For example, here we can create a template that generates - a struct with the named members: - ) +--- +enum name = "Foo"; +enum m1 = "bar"; +mixin("struct " ~ name ~ + "{ int " ~ m1 ~ "; }"); +enum f = Foo(2); +static assert(f.bar == 2); +--- + $(P This can be encapsulated in an enum template:) +--- +enum string genStruct(string name, string m1) = + "struct " ~ name ~ "{ int " ~ m1 ~ "; }"; + +mixin(genStruct!("Foo", "bar")); +--- + +$(H2 $(LNAME2 multiple-args, Concatenation of Multiple Arguments)) + + $(P `mixin()` can also concatenate multiple arguments, converting any + non-strings to strings:) +--- +enum name = "Foo"; +enum m1 = "bar"; +$(CODE_HIGHLIGHT enum val = 4;) +mixin("struct ", name, + "{ int ", m1, " = ", $(CODE_HIGHLIGHT val), "; }"); + +enum f = Foo(); +static assert(f.bar == 4); --- -template GenStruct(string Name, string M1) -{ - const char[] GenStruct = "struct " ~ Name ~ "{ int " ~ M1 ~ "; }"; -} -mixin(GenStruct!("Foo", "bar")); +$(H2 $(LNAME2 declarations, Generating Declarations)) + +--- +enum s = "int y;"; +mixin(s); +y = 4; // ok, mixin declared y +--- + +$(H2 $(LNAME2 statements, Generating Statements)) + +--- +int x = 3; +mixin(" + foreach (i; 0 .. 3) + writeln(x + i); + "); +--- + $(P See also: $(GLINK2 statement, MixinStatement).) + +$(H2 $(LNAME2 types, Generating Types)) + --- - $(P which generates:) +mixin("int")* p; // int* p +mixin("int")[] a; // int[] a; +mixin("int[]") b; // int[] b; --- -struct Foo { int bar; } + +$(H2 $(LNAME2 expressions, Generating Expressions)) + +--- +int foo(int x) +{ + return mixin("x +", 1) * 7; // same as `return (x + 1) * 7` +} --- +$(H2 $(LNAME2 cpp, Comparison with C preprocessor)) $(P Superficially, since D mixins can manipulate text and compile the result, it has some similar properties to the C preprocessor. @@ -61,7 +126,7 @@ END This monkey business is impossible with mixins. Mixed in text must form complete declarations, - statements, or expressions. + statements, types, or expressions. ) $(LI C macros will affect everything following that has diff --git a/spec/module.dd b/spec/module.dd index b9e822f014..db6060f33c 100644 --- a/spec/module.dd +++ b/spec/module.dd @@ -643,6 +643,13 @@ $(GNAME MixinDeclaration): $(GLINK DeclDefs), and is compiled as such. ) +$(SPEC_RUNNABLE_EXAMPLE_COMPILE +--- +enum s = "int y;"; +mixin(s); +y = 4; // ok, mixin declared y +--- +) $(H2 $(LEGACY_LNAME2 PackageModule, package-module, Package Module)) diff --git a/spec/statement.dd b/spec/statement.dd index 99e1ce4c5c..447d58d7d5 100644 --- a/spec/statement.dd +++ b/spec/statement.dd @@ -1999,30 +1999,26 @@ $(GNAME MixinStatement): ) $(SPEC_RUNNABLE_EXAMPLE_RUN ---- -import std.stdio; - -void main() -{ - int i = 0; + --- + int x = 3; mixin(" - int x = 3; - for (; i < 3; i++) - writeln(x + i, i); + foreach (i; 0 .. 3) + writeln(x + i); "); // ok + --- +) - enum s = "int y;"; - mixin(s); // ok - y = 4; // ok, mixin declared y +$(SPEC_RUNNABLE_EXAMPLE_COMPILE + --- + int y; string t = "y = 3;"; //mixin(t); // error, t is not evaluatable at compile time - //mixin("y =") 4; // error, string must be complete statement + //mixin("y =") 4; // error, string must be complete statement mixin("y =" ~ "4;"); // ok mixin("y =", 2+2, ";"); // ok -} ---- + --- ) $(SPEC_SUBNAV_PREV_NEXT expression, Expressions, arrays, Arrays) From 75f20924224f607bbaa20864b5436eb58ce182ea Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Thu, 10 Feb 2022 18:08:38 +0000 Subject: [PATCH 2/6] String mixins 'can be used in place of' --- articles/mixin.dd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/articles/mixin.dd b/articles/mixin.dd index affa2063f3..e02f4b97b6 100644 --- a/articles/mixin.dd +++ b/articles/mixin.dd @@ -4,11 +4,11 @@ $(D_S String Mixins, $(HEADERNAV_TOC) - $(P String Mixins (not to be confused with + $(P String mixins (not to be confused with $(DDLINK spec/template-mixin, Template Mixins, template mixins)) enable string constants to be compiled as regular D code and inserted into the program. - Mixins can be used to generate:) + String mixins can be used in place of:) $(UL $(LI Declarations) From 00666ec2cb77d75916b672db28ec70f292c18f2d Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Thu, 10 Feb 2022 18:27:52 +0000 Subject: [PATCH 3/6] Add 'Domain Specific Languages' section --- articles/mixin.dd | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/articles/mixin.dd b/articles/mixin.dd index e02f4b97b6..f85cc887aa 100644 --- a/articles/mixin.dd +++ b/articles/mixin.dd @@ -17,8 +17,6 @@ $(HEADERNAV_TOC) $(LI Expressions) ) $(P - Combining this with compile time manipulation of strings - enables the creation of domain-specific languages. For example, we can generate a declaration `struct Foo { int bar; }` using string constants for the identifiers: ) @@ -91,6 +89,35 @@ int foo(int x) } --- +$(H2 $(LNAME2 dsl, Domain Specific Languages)) + + $(P + Combining compile-time function execution with `mixin()` + enables the use of domain-specific languages:) + --- + mixin(convertJS(q{ + var msg = "Hello world!" + console.log(msg) + msg = 5 + console.log(msg) + })); + --- + $(P `convertJS` would generate a string that would be compiled + to the following D code:) + --- + import std.variant; + + Variant msg = "Hello world!"; + writeln(msg); + msg = 5; + writeln(msg); + --- + + $(NOTE Text editors can syntax highlight the contents of `q{}` + strings as D tokens rather than a string, which is useful with + `mixin()`. The above JS code happens to be token compatible + with D.) + $(H2 $(LNAME2 cpp, Comparison with C preprocessor)) $(P Superficially, since D mixins can manipulate text and compile From 3355c37b47225535489dd3950b44c38eb2b78aeb Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Thu, 10 Feb 2022 18:39:20 +0000 Subject: [PATCH 4/6] Move 'Generating Declarations' section to top There's already a struct declaration mixin there. --- articles/mixin.dd | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/articles/mixin.dd b/articles/mixin.dd index f85cc887aa..6fed530e49 100644 --- a/articles/mixin.dd +++ b/articles/mixin.dd @@ -16,8 +16,17 @@ $(HEADERNAV_TOC) $(LI Types) $(LI Expressions) ) - $(P - For example, we can generate a declaration `struct Foo { int bar; }` + +$(H2 $(LNAME2 declarations, Generating Declarations)) + + $(P A mixin can be used for a variable declaration:) +--- +enum s = "int y;"; +mixin(s); +y = 4; // ok, mixin declared y +--- + + $(P We can generate a declaration `struct Foo { int bar; }` using string constants for the identifiers: ) @@ -30,7 +39,7 @@ mixin("struct " ~ name ~ enum f = Foo(2); static assert(f.bar == 2); --- - $(P This can be encapsulated in an enum template:) + $(P This can be encapsulated in an enum template for reuse:) --- enum string genStruct(string name, string m1) = "struct " ~ name ~ "{ int " ~ m1 ~ "; }"; @@ -53,14 +62,6 @@ enum f = Foo(); static assert(f.bar == 4); --- -$(H2 $(LNAME2 declarations, Generating Declarations)) - ---- -enum s = "int y;"; -mixin(s); -y = 4; // ok, mixin declared y ---- - $(H2 $(LNAME2 statements, Generating Statements)) --- From a98779b72fa565d453e61f3a92a7954aea99e8ea Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Thu, 10 Feb 2022 18:49:44 +0000 Subject: [PATCH 5/6] Show output for mixin statement --- articles/mixin.dd | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/articles/mixin.dd b/articles/mixin.dd index 6fed530e49..20a614eea4 100644 --- a/articles/mixin.dd +++ b/articles/mixin.dd @@ -71,6 +71,12 @@ mixin(" writeln(x + i); "); --- + $(P This outputs:) +$(CONSOLE +3 +4 +5 +) $(P See also: $(GLINK2 statement, MixinStatement).) $(H2 $(LNAME2 types, Generating Types)) From afd699b04f8a026b493cc4ce55f220d0510d7e8b Mon Sep 17 00:00:00 2001 From: Nick Treleaven Date: Wed, 16 Feb 2022 13:01:30 +0000 Subject: [PATCH 6/6] Fix missing NOTE macro --- articles/mixin.dd | 1 + 1 file changed, 1 insertion(+) diff --git a/articles/mixin.dd b/articles/mixin.dd index 20a614eea4..2e27fddead 100644 --- a/articles/mixin.dd +++ b/articles/mixin.dd @@ -192,3 +192,4 @@ END Macros: TITLE=String Mixins SUBNAV=$(SUBNAV_ARTICLES) + NOTE= $(DIVC note, $(B Note:) $0)