Last active
December 17, 2015 23:59
-
-
Save k3kaimu/5693467 to your computer and use it in GitHub Desktop.
//##$ dmc %src%
//## %src.exe%
みたいに使う。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** ソースファイルの先頭に書かれた"//##"行を読み取って、コマンドを打ち込みます。 | |
* "//##"行は複数でも構いません。複数行の場合には、上から順番に実行されます。 | |
//##$ <commands> このようにすると、shellでコマンドを打ち込みます。 | |
commandsの中に"%identifier%"があれば、展開されます。 | |
commandsはスペース区切りで入力する必要があります。 | |
//##& set waitTime <long> プロセスを待つ最大の時間を設定します。 | |
//##& let <identifier> = <string> <identifier>を<string>に束縛します。 | |
<string>には%src%や%arg[n]%など%<identifier>%を入れることもでき、それらは展開されます。 | |
//## <string> 現在のプロセスの標準入力に<string>を入れ込みます。 | |
"%identifier%"の種類 | |
%src% ソースファイル(パスあり) | |
%src.<extension>% ソースファイルの拡張子を<extension>に変更した場合のファイル名(パスあり) | |
%src.name.<extension>% ソースファイルの拡張子を<extension>に変更した場合のファイル名(パスなし) | |
%arg[n]% nは0~引数の個数+1, n=0はアプリ名, n=1は%src%に等しい | |
%arg[n].<extension>% %arg[n]%の拡張子を<extension>に変更した場合のファイル名(パスあり) | |
%arg[n].name.<extension>% %arg[n]%の拡張子を<extension>に変更した場合のファイル名(パスなし) | |
%<identifier>% let命令で宣言された<identifier> | |
Example: | |
---- | |
//##$ dmc %src% | |
//##$ %src.exe% | |
//##& set waitTime 10000 | |
//## 0 | |
//## 1 | |
//## 2 | |
//## 3 | |
---- | |
*/ | |
import std.algorithm, | |
std.array, | |
std.ascii, | |
std.conv, | |
std.exception, | |
std.format, | |
std.process, | |
std.range, | |
std.regex, | |
std.stdio, | |
std.string, | |
std.utf; | |
import core.thread; | |
void main(string[] args) | |
{ | |
enforce(args.length > 1); | |
auto srcfile = args[1]; | |
long waitTimemsecs = 1000; | |
string[] cmdlines; | |
string delegate(string)[] argRegexReplaceDlg; | |
string[string] idents = ["src" : srcfile]; | |
{ | |
auto srcFile = File(srcfile); | |
foreach(line; srcFile.byLine) | |
if(line.startsWith("//##")){ | |
immutable idup = line.dup; | |
cmdlines ~= idup; | |
}else | |
break; | |
} | |
if(cmdlines.length == 0) | |
throw new Exception(`Error: Input file doesn't have some command scripts. File name is "%s"`.toFormattedString(srcfile)); | |
Pipe cmdPipe; | |
Pid cmdProcess; | |
string lastProcessCode; | |
static string toNameExtensionName(string filename) | |
{ | |
return filename.match(regex(`(\w+)\.\w+`)).captures[1] ~ ".$1"; | |
} | |
static string toExtensionName(string filename) | |
{ | |
return filename.match(regex(`\.\w+`)).captures.pre ~ ".$1"; | |
} | |
{ | |
//argRegexReplaceDlg ~= (string str) => str.replace(regex(`%src%`), srcfile); | |
argRegexReplaceDlg ~= (string str) => str.replace(regex(`%src\.name\.(\w+)%`), toNameExtensionName(srcfile)); | |
argRegexReplaceDlg ~= (string str) => str.replace(regex(`%src\.(\w+)%`), toExtensionName(srcfile)); | |
argRegexReplaceDlg ~= (string str){ | |
while(1){ | |
if(auto m = str.match(regex(`%arg\[(\d+)\]%`))){ | |
auto c = m.captures; | |
debug writeln(c); | |
immutable n = c[1].to!sizediff_t(); | |
enforce(n < args.length); | |
str = str.replace(regex(`%arg\[(\d+)\]%`, "g"), args[n]); | |
}else | |
return str; | |
} | |
}; | |
argRegexReplaceDlg ~= (string str){ | |
while(1){ | |
if(auto m = str.match(regex(`%arg\[(\d+)\]\.name\.(\w+)%`))){ | |
auto c = m.captures; | |
debug writeln(c); | |
immutable n = c[1].to!sizediff_t(); | |
enforce(n < args.length && n > 0); | |
if(auto marg = args[n].match(regex(`(\w+)\.\w+`))){ | |
auto carg = marg.captures; | |
debug writeln(carg); | |
immutable toName = carg[1] ~ "." ~ c[2]; | |
debug writeln(toName); | |
str = str.replace(regex(`%arg\[\d+\]\.name\.\w+%`), toName); | |
}else | |
throw new Exception(`"` ~ args[n] ~ `" does not have a extension, eval of ` ~ lastProcessCode); | |
}else | |
return str; | |
} | |
}; | |
auto argnextReplaceDlg = (string str){ | |
while(1){ | |
if(auto m = str.match(regex(`%arg\[(\d+)\]\.(\w+)%`))){ | |
auto c = m.captures; | |
debug writeln(c); | |
immutable n = c[1].to!sizediff_t(); | |
enforce(n < args.length && n > 0); | |
if(auto marg = args[n].match(regex(`\.\w+`))){ | |
immutable toName = args[n].replace(regex(`.\w+`), `.` ~ c[2]); | |
str = str.replace(regex(`%arg\[\d+\]\.\w+%`), toName); | |
}else | |
throw new Exception(`"` ~ args[n] ~ `" does not have a extension, eval of ` ~ lastProcessCode); | |
}else | |
return str; | |
} | |
}; | |
argRegexReplaceDlg ~= (string str){ | |
while(1){ | |
if(auto m = str.match(regex(`%(\w+)%`))){ | |
auto c = m.captures; | |
debug writeln(c); | |
if(auto p = c[1] in idents) | |
return str.replace(regex(`%\w+%`, "g"), *p); | |
else | |
throw new Exception("Invalid identifier " ~ c[0] ~ ", eval of " ~ lastProcessCode); | |
}else | |
return str; | |
} | |
}; | |
} | |
//現在のプロセスの終了を待つ | |
void finishProcess() | |
{ | |
if(cmdProcess !is null){ | |
cmdPipe.writeEnd.flush(); | |
auto status = cmdProcess.tryWait(); | |
if(!status.terminated){ | |
long cnt = waitTimemsecs / 100; | |
do{ | |
core.thread.Thread.sleep(dur!"msecs"(100)); | |
status = cmdProcess.tryWait(); | |
--cnt; | |
if(cnt <= 0) | |
throw new Exception("Error: Process is not ended about %s".toFormattedString(lastProcessCode)); | |
}while(!status.terminated); | |
} | |
//auto code = status.status; | |
if(immutable returnCode = status.status) | |
throw new Exception(`Error: %s, eval of "%s"`.toFormattedString(returnCode, lastProcessCode)); | |
} | |
} | |
//コマンドの"%<identifier>%"を置換する | |
string commandReplace(string command) | |
{ | |
foreach(immutable dlg; argRegexReplaceDlg) | |
command = dlg(command); | |
return command; | |
} | |
foreach(immutable line; cmdlines){ | |
if(line.startsWith("//##$")){ | |
finishProcess(); | |
lastProcessCode = line.chomp(); | |
cmdPipe = pipe(); | |
debug writeln(line); | |
auto command = line.split()[1 .. $].map!commandReplace().array(); | |
debug writeln(command); | |
try | |
cmdProcess = command.spawnProcess(cmdPipe.readEnd); | |
catch(Exception ex) | |
{ | |
throw new Exception("Failed to spawn new process, eval of " ~ lastProcessCode, ex); | |
} | |
}else if(line.startsWith("//##&")){ | |
auto splits = line.split(); | |
enforce(splits.length >= 2); | |
switch(splits[1]){ | |
case "set": | |
switch(splits[2]){ | |
case "waitTime": | |
enforce(splits.length > 3); | |
waitTimemsecs = splits[3].to!long; | |
break; | |
default: | |
throw new Exception(`Invalid script, eval of "%s"`.toFormattedString(lastProcessCode)); | |
} | |
break; | |
case "let": | |
enforce(splits.length > 4); | |
enforce(splits[3] == "=", `Invalid script, eval of %s`.toFormattedString(lastProcessCode)); | |
immutable str = commandReplace(splits[4]); | |
idents[splits[2]] = str; | |
break; | |
default: | |
throw new Exception("Invalid script, eval of %s".toFormattedString(lastProcessCode)); | |
} | |
}else if(line.startsWith("//##")){ | |
if(cmdProcess !is null){ | |
immutable output = line[4 .. $].find!(a => !a.isWhite)(); | |
cmdPipe.writeEnd.writeln(output); | |
debug writeln(output); | |
}else | |
throw new Exception(`No process is running, eval of "%s"`.toFormattedString(lastProcessCode)); | |
} | |
} | |
finishProcess(); | |
} | |
string toFormattedString(T...)(string format, T args) | |
{ | |
auto writer = appender!string(); | |
writer.formattedWrite(format, args); | |
return writer.data; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment