Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 98 additions & 7 deletions rdmd.d
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idiomatic D would just use .byLine - you could use this opportunity to change this bit.

: "\n";

immutable innerCodeClosing = loop ? "} " : "";

return format(codeFormat,
innerCodeOpening,
innerEvalCode(eval),
innerCodeClosing);
}

unittest
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be @safe

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@safe won't go with the use of assumeSafeAppend inside innerEvalCode. That might be possible to drop, but OTOH, I'm not sure I care to overly restrict how anyone edits or revises these functions in future. It's not like rdmd.d makes common use of these kinds of constraints.

{
// 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;
Expand Down