Skip to content

Commit b988788

Browse files
committed
rdmd: --loop and --eval with a last statement without effect should print it
1 parent 8df8eb7 commit b988788

File tree

3 files changed

+94
-15
lines changed

3 files changed

+94
-15
lines changed

changelog/rdmd-writeln.dd

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
In rdmd `--eval` and `--loop` the last statement without effect will be printed
2+
3+
`--eval` and `--loop` are two convenience features of `rdmd` that allow quick experimentation with D code.
4+
However, until
5+
6+
$(CONSOLE
7+
rdmd --eval='"hello"'
8+
/tmp/.rdmd-1000/eval.CFD785092747553A81673B51A318D6AE.d(18): $(RED Error): "hello" has no effect
9+
)
10+
11+
The new behavior will default to writeln if the last statement would have no effect:
12+
13+
$(CONSOLE
14+
rdmd --eval="hello"
15+
hello
16+
)
17+
18+
A few more examples of `rdmd` in action:
19+
20+
$(H4 Use `rdmd` as your calculator)
21+
22+
$(CONSOLE
23+
rdmd --eval="2 + 2"
24+
4
25+
)
26+
27+
$(CONSOLE
28+
rdmd --eval="2.pow(4)"
29+
16
30+
)
31+
32+
$(H4 Select the first column in a CSV file)
33+
34+
$(CONSOLE
35+
rdmd --loop='line.splitter(",").take(1)'
36+
16
37+
)
38+
39+
$(H4 Use `rdmd` as drop-in for Unix tools)
40+
41+
$(CONSOLE
42+
seq 5 | rdmd --loop='stdin.byLine.drop(2).joiner(newline)'
43+
2
44+
3
45+
4
46+
5
47+
)

rdmd.d

+18-1
Original file line numberDiff line numberDiff line change
@@ -828,11 +828,20 @@ Returns:
828828
*/
829829
string innerEvalCode(string[] eval)
830830
{
831+
import std.conv : text;
831832
import std.string : join, stripRight;
832833
// assumeSafeAppend just to avoid unnecessary reallocation
833834
string code = eval.join("\n").stripRight.assumeSafeAppend;
835+
auto lastSemicolon = code.lastIndexOf(";");
834836
if (code.length > 0 && code[$ - 1] != ';')
835-
code ~= ';';
837+
{
838+
if (code[lastSemicolon + 1 .. $].canFind("write"))
839+
code ~= ';';
840+
else if (lastSemicolon == -1)
841+
code = text("writeln(", code, ");");
842+
else
843+
code = text(code[0 .. lastSemicolon + 1], "writeln(", code[lastSemicolon + 1 .. $], ");");
844+
}
836845
return code;
837846
}
838847

@@ -852,6 +861,14 @@ unittest
852861
== "writeln(\"Hello!\"); \nwriteln(\"You!\");");
853862
}
854863

864+
unittest
865+
{
866+
assert(innerEvalCode(["2"]) == "writeln(2);");
867+
assert(innerEvalCode(["2 + 2"]) == "writeln(2 + 2);");
868+
assert(innerEvalCode(["2 + 2;"]) == "2 + 2;");
869+
assert(innerEvalCode(["2.pow(4)"]) == "writeln(2.pow(4));");
870+
}
871+
855872
/**
856873
Formats the code provided via `--eval` or `--loop` flags into a
857874
string of complete program code that can be written to a file

rdmd_test.d

+29-14
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,19 @@ void runTests(string rdmdApp, string compiler, string model)
183183
assert(res.status == 0, res.output);
184184
assert(res.output.canFind("eval_works")); // there could be a "DMD v2.xxx header in the output"
185185

186+
// Test automatic .writeln for --eval
187+
import std.conv : text;
188+
import std.typecons : tuple;
189+
foreach (t; [tuple(`"eval_works"`, "eval_works"),
190+
tuple("2 + 2", "4"),
191+
tuple("2.write; 2 + 2", "24")])
192+
{
193+
res = execute(rdmdArgs ~ ["--force", "-de", text("--eval=", t[0])]);
194+
assert(res.status == 0, res.output);
195+
// there could be a "DMD v2.xxx header in the output" (NB: only seems to be the case for GDC)
196+
assert(res.output.canFind(t[1]), text("got:", res.output, " expected:", t[1]));
197+
}
198+
186199
// compiler flags
187200
res = execute(rdmdArgs ~ ["--force", "-debug",
188201
"--eval=debug {} else assert(false);"]);
@@ -256,22 +269,24 @@ void runTests(string rdmdApp, string compiler, string model)
256269
{
257270
auto testLines = "foo\nbar\ndoo".split("\n");
258271

259-
auto pipes = pipeProcess(rdmdArgs ~ ["--force", "--loop=writeln(line);"], Redirect.stdin | Redirect.stdout);
260-
foreach (input; testLines)
261-
pipes.stdin.writeln(input);
262-
pipes.stdin.close();
263-
264-
while (!testLines.empty)
272+
// Test --loop with automatic writeln
273+
foreach (loopArg; ["--loop=writeln(line);", "--loop=line"])
265274
{
266-
auto line = pipes.stdout.readln.strip;
267-
if (line.empty || line.startsWith("DMD v")) continue; // git-head header
268-
assert(line == testLines.front, "Expected %s, got %s".format(testLines.front, line));
269-
testLines.popFront;
270-
}
271-
auto status = pipes.pid.wait();
272-
assert(status == 0);
273-
}
275+
auto pipes = pipeProcess(rdmdArgs ~ ["--force", loopArg], Redirect.stdin | Redirect.stdout);
276+
foreach (input; testLines)
277+
pipes.stdin.writeln(input);
278+
pipes.stdin.close();
274279

280+
while (!testLines.empty)
281+
{
282+
auto line = pipes.stdout.readln.strip;
283+
if (line.empty || line.startsWith("DMD v")) continue; // git-head header
284+
assert(line == testLines.front, "Expected %s, got %s".format(testLines.front, line));
285+
testLines.popFront;
286+
}
287+
auto status = pipes.pid.wait();
288+
assert(status == 0);
289+
}}
275290
// vs program file
276291
res = execute(rdmdArgs ~ ["--force",
277292
"--loop=assert(true);", voidMain]);

0 commit comments

Comments
 (0)