diff --git a/rdmd.d b/rdmd.d index 153ad2d..250f3f2 100755 --- a/rdmd.d +++ b/rdmd.d @@ -17,7 +17,7 @@ import std.algorithm, std.array, core.stdc.stdlib, std.datetime, std.digest.md, std.exception, std.file, std.getopt, 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) { @@ -180,18 +180,14 @@ int main(string[] args) { enforce(programPos == args.length, "Cannot have both --loop and a " ~ "program file ('" ~ args[programPos] ~ "')."); - root = makeEvalFile(importWorld ~ "void main(char[][] args) { " - ~ "foreach (line; std.stdio.stdin.byLine()) {\n" - ~ std.string.join(loop, "\n") - ~ ";\n} }"); + root = makeEvalFile(makeEvalCode(loop, Yes.loop)); argsBeforeProgram ~= "-d"; } else if (eval.ptr) { enforce(programPos == args.length, "Cannot have both --eval and a " ~ "program file ('" ~ args[programPos] ~ "')."); - root = makeEvalFile(importWorld ~ "void main(char[][] args) {\n" - ~ std.string.join(eval, "\n") ~ ";\n}"); + root = makeEvalFile(makeEvalCode(eval, No.loop)); argsBeforeProgram ~= "-d"; } else if (programPos < args.length) @@ -839,6 +835,101 @@ import std.stdio, std.algorithm, std.array, std.ascii, std.base64, 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) { auto pathname = myOwnTmpDir;