Merge pull request #303 from WebDrake/prevent-eval-loop-empty-statements

Prevent empty statements at the end of --eval or --loop code
This commit is contained in:
Sebastian Wilzbach 2018-02-04 16:23:31 +01:00 committed by GitHub
commit a8d282e618
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

105
rdmd.d
View file

@ -17,7 +17,7 @@
import std.algorithm, std.array, core.stdc.stdlib, std.datetime, import std.algorithm, std.array, core.stdc.stdlib, std.datetime,
std.digest.md, std.exception, std.file, std.getopt, std.digest.md, std.exception, std.file, std.getopt,
std.parallelism, std.path, std.process, std.range, std.regex, std.parallelism, std.path, std.process, std.range, std.regex,
std.stdio, std.string, std.typetuple; std.stdio, std.string, std.typecons, std.typetuple;
version (Posix) version (Posix)
{ {
@ -180,18 +180,14 @@ int main(string[] args)
{ {
enforce(programPos == args.length, "Cannot have both --loop and a " ~ enforce(programPos == args.length, "Cannot have both --loop and a " ~
"program file ('" ~ args[programPos] ~ "')."); "program file ('" ~ args[programPos] ~ "').");
root = makeEvalFile(importWorld ~ "void main(char[][] args) { " root = makeEvalFile(makeEvalCode(loop, Yes.loop));
~ "foreach (line; std.stdio.stdin.byLine()) {\n"
~ std.string.join(loop, "\n")
~ ";\n} }");
argsBeforeProgram ~= "-d"; argsBeforeProgram ~= "-d";
} }
else if (eval.ptr) else if (eval.ptr)
{ {
enforce(programPos == args.length, "Cannot have both --eval and a " ~ enforce(programPos == args.length, "Cannot have both --eval and a " ~
"program file ('" ~ args[programPos] ~ "')."); "program file ('" ~ args[programPos] ~ "').");
root = makeEvalFile(importWorld ~ "void main(char[][] args) {\n" root = makeEvalFile(makeEvalCode(eval, No.loop));
~ std.string.join(eval, "\n") ~ ";\n}");
argsBeforeProgram ~= "-d"; argsBeforeProgram ~= "-d";
} }
else if (programPos < args.length) else if (programPos < args.length)
@ -839,6 +835,101 @@ import std.stdio, std.algorithm, std.array, std.ascii, std.base64,
std.zlib; std.zlib;
"; ";
/**
Joins together the code provided via an `--eval` or `--loop`
flag, ensuring a trailing `;` is added if not already provided
by the user
Params:
eval = array of strings generated by the `--eval`
or `--loop` rdmd flags
Returns:
string of code to be evaluated, corresponding to the
inner code of either the program or the loop
*/
string innerEvalCode(string[] eval)
{
import std.string : join, stripRight;
// assumeSafeAppend just to avoid unnecessary reallocation
string code = eval.join("\n").stripRight.assumeSafeAppend;
if (code.length > 0 && code[$ - 1] != ';')
code ~= ';';
return code;
}
unittest
{
assert(innerEvalCode([`writeln("Hello!")`]) == `writeln("Hello!");`);
assert(innerEvalCode([`writeln("Hello!");`]) == `writeln("Hello!");`);
// test with trailing whitespace
assert(innerEvalCode([`writeln("Hello!") `]) == `writeln("Hello!");`);
assert(innerEvalCode([`writeln("Hello!"); `]) == `writeln("Hello!");`);
// test with multiple entries
assert(innerEvalCode([`writeln("Hello!"); `, `writeln("You!") `])
== "writeln(\"Hello!\"); \nwriteln(\"You!\");");
assert(innerEvalCode([`writeln("Hello!"); `, `writeln("You!"); `])
== "writeln(\"Hello!\"); \nwriteln(\"You!\");");
}
/**
Formats the code provided via `--eval` or `--loop` flags into a
string of complete program code that can be written to a file
and then compiled
Params:
eval = array of strings generated by the `--eval` or
`--loop` rdmd flags
loop = set to `Yes.loop` if this code comes from a
`--loop` flag, `No.loop` if it comes from an
`--eval` flag
Returns:
string of code to be evaluated, corresponding to the
inner code of either the program or the loop
*/
string makeEvalCode(string[] eval, Flag!"loop" loop)
{
import std.format : format;
immutable codeFormat = importWorld
~ "void main(char[][] args) {%s%s\n%s}";
immutable innerCodeOpening =
loop ? " foreach (line; std.stdio.stdin.byLine()) {\n"
: "\n";
immutable innerCodeClosing = loop ? "} " : "";
return format(codeFormat,
innerCodeOpening,
innerEvalCode(eval),
innerCodeClosing);
}
unittest
{
// innerEvalCode already tests the cases for different
// contents in `eval` array, so let's focus on testing
// the difference based on the `loop` flag
assert(makeEvalCode([`writeln("Hello!") `], No.loop) ==
importWorld
~ "void main(char[][] args) {\n"
~ "writeln(\"Hello!\");\n}");
assert(makeEvalCode([`writeln("What!"); `], No.loop) ==
importWorld
~ "void main(char[][] args) {\n"
~ "writeln(\"What!\");\n}");
assert(makeEvalCode([`writeln("Loop!") ; `], Yes.loop) ==
importWorld
~ "void main(char[][] args) { "
~ "foreach (line; std.stdio.stdin.byLine()) {\n"
~ "writeln(\"Loop!\") ;\n} }");
}
string makeEvalFile(string todo) string makeEvalFile(string todo)
{ {
auto pathname = myOwnTmpDir; auto pathname = myOwnTmpDir;