Skip to content

Commit a8d282e

Browse files
authored
Merge pull request #303 from WebDrake/prevent-eval-loop-empty-statements
Prevent empty statements at the end of --eval or --loop code
2 parents 6840d41 + 1a29cdb commit a8d282e

1 file changed

Lines changed: 98 additions & 7 deletions

File tree

rdmd.d

Lines changed: 98 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import std.algorithm, std.array, core.stdc.stdlib, std.datetime,
1818
std.digest.md, std.exception, std.file, std.getopt,
1919
std.parallelism, std.path, std.process, std.range, std.regex,
20-
std.stdio, std.string, std.typetuple;
20+
std.stdio, std.string, std.typecons, std.typetuple;
2121

2222
version (Posix)
2323
{
@@ -180,18 +180,14 @@ int main(string[] args)
180180
{
181181
enforce(programPos == args.length, "Cannot have both --loop and a " ~
182182
"program file ('" ~ args[programPos] ~ "').");
183-
root = makeEvalFile(importWorld ~ "void main(char[][] args) { "
184-
~ "foreach (line; std.stdio.stdin.byLine()) {\n"
185-
~ std.string.join(loop, "\n")
186-
~ ";\n} }");
183+
root = makeEvalFile(makeEvalCode(loop, Yes.loop));
187184
argsBeforeProgram ~= "-d";
188185
}
189186
else if (eval.ptr)
190187
{
191188
enforce(programPos == args.length, "Cannot have both --eval and a " ~
192189
"program file ('" ~ args[programPos] ~ "').");
193-
root = makeEvalFile(importWorld ~ "void main(char[][] args) {\n"
194-
~ std.string.join(eval, "\n") ~ ";\n}");
190+
root = makeEvalFile(makeEvalCode(eval, No.loop));
195191
argsBeforeProgram ~= "-d";
196192
}
197193
else if (programPos < args.length)
@@ -839,6 +835,101 @@ import std.stdio, std.algorithm, std.array, std.ascii, std.base64,
839835
std.zlib;
840836
";
841837

838+
/**
839+
Joins together the code provided via an `--eval` or `--loop`
840+
flag, ensuring a trailing `;` is added if not already provided
841+
by the user
842+
843+
Params:
844+
eval = array of strings generated by the `--eval`
845+
or `--loop` rdmd flags
846+
847+
Returns:
848+
string of code to be evaluated, corresponding to the
849+
inner code of either the program or the loop
850+
*/
851+
string innerEvalCode(string[] eval)
852+
{
853+
import std.string : join, stripRight;
854+
// assumeSafeAppend just to avoid unnecessary reallocation
855+
string code = eval.join("\n").stripRight.assumeSafeAppend;
856+
if (code.length > 0 && code[$ - 1] != ';')
857+
code ~= ';';
858+
return code;
859+
}
860+
861+
unittest
862+
{
863+
assert(innerEvalCode([`writeln("Hello!")`]) == `writeln("Hello!");`);
864+
assert(innerEvalCode([`writeln("Hello!");`]) == `writeln("Hello!");`);
865+
866+
// test with trailing whitespace
867+
assert(innerEvalCode([`writeln("Hello!") `]) == `writeln("Hello!");`);
868+
assert(innerEvalCode([`writeln("Hello!"); `]) == `writeln("Hello!");`);
869+
870+
// test with multiple entries
871+
assert(innerEvalCode([`writeln("Hello!"); `, `writeln("You!") `])
872+
== "writeln(\"Hello!\"); \nwriteln(\"You!\");");
873+
assert(innerEvalCode([`writeln("Hello!"); `, `writeln("You!"); `])
874+
== "writeln(\"Hello!\"); \nwriteln(\"You!\");");
875+
}
876+
877+
/**
878+
Formats the code provided via `--eval` or `--loop` flags into a
879+
string of complete program code that can be written to a file
880+
and then compiled
881+
882+
Params:
883+
eval = array of strings generated by the `--eval` or
884+
`--loop` rdmd flags
885+
loop = set to `Yes.loop` if this code comes from a
886+
`--loop` flag, `No.loop` if it comes from an
887+
`--eval` flag
888+
889+
Returns:
890+
string of code to be evaluated, corresponding to the
891+
inner code of either the program or the loop
892+
*/
893+
string makeEvalCode(string[] eval, Flag!"loop" loop)
894+
{
895+
import std.format : format;
896+
immutable codeFormat = importWorld
897+
~ "void main(char[][] args) {%s%s\n%s}";
898+
899+
immutable innerCodeOpening =
900+
loop ? " foreach (line; std.stdio.stdin.byLine()) {\n"
901+
: "\n";
902+
903+
immutable innerCodeClosing = loop ? "} " : "";
904+
905+
return format(codeFormat,
906+
innerCodeOpening,
907+
innerEvalCode(eval),
908+
innerCodeClosing);
909+
}
910+
911+
unittest
912+
{
913+
// innerEvalCode already tests the cases for different
914+
// contents in `eval` array, so let's focus on testing
915+
// the difference based on the `loop` flag
916+
assert(makeEvalCode([`writeln("Hello!") `], No.loop) ==
917+
importWorld
918+
~ "void main(char[][] args) {\n"
919+
~ "writeln(\"Hello!\");\n}");
920+
921+
assert(makeEvalCode([`writeln("What!"); `], No.loop) ==
922+
importWorld
923+
~ "void main(char[][] args) {\n"
924+
~ "writeln(\"What!\");\n}");
925+
926+
assert(makeEvalCode([`writeln("Loop!") ; `], Yes.loop) ==
927+
importWorld
928+
~ "void main(char[][] args) { "
929+
~ "foreach (line; std.stdio.stdin.byLine()) {\n"
930+
~ "writeln(\"Loop!\") ;\n} }");
931+
}
932+
842933
string makeEvalFile(string todo)
843934
{
844935
auto pathname = myOwnTmpDir;

0 commit comments

Comments
 (0)