多段インデントについて。
成果物
前回まで
1層までのインデントができた。
多段インデント
今回は複数の階層を同時に作成したい。
インタフェース考察
メソッドチェーン
簡単に表現したい。たとえばメソッドチェーン的に。
"namespace MyProj" .child("class MyClass") .child("static void Main()") .child("Console.WriteLine("Hello World");")
namespace MyProj
{
class MyClass
{
static void Main()
{
Console.WriteLine("Hello World");
}
}
}
- 最後だけ
{,}がない。これをどう簡単に再現するか
もちろんbash構文においてメソッドチェーン構文など書けない。
パイプ
bashで表現できるとしたらパイプか。
indent 'namespace MyProj' | indent 'class MyClass' | indent 'static void Main()' | indent 'Console.WriteLine("Hello World");'
- 次の挿入位置である
{と}の間をどうやって探すか
末尾に追記していけばいいだけなら楽でよかったのだが……。ツリー構造への要素追加が難しいのはこのせい。
位置引数
位置引数をインデント階層と紐付けるようにすれば、以下のように書ける。
indent_pos 'namespace MyProj' 'class MyClass' 'static void Main()' 'Console.WriteLine("Hello World");'
複数行を書きたいなら以下。
indent_pos 'namespace MyProj' 'class MyClass' 'static void Main()' "$(echo -e "int i;\ni = 5;")"
- 複数行のところが冗長
- シェル文脈におけるエスケープがウザい(
',",\n) namespace,class,static,voidなどのキーワードを省略したい
インタフェースは位置引数を用いるべきだと判明した。
出力結果
namespace MyProj
class MyClass
static void Main()
int i;
i = 5;
{,}の囲み文字については未実装
実装
indent.sh
# stdin: インデントしたいテキスト $1: インデント用文字列(任意。省略時はタブ) indent() { echo "$(cat -)" | sed "s/^/${1:-\t}/g"; } # $1..9..{10}.. 位置引数の位置がそのままインデント階層である indent_pos() { local result="" local indent=0 # $1: 繰り返したいテキスト(省略時"") $2: 回数(省略時1) repeat() { local target=${1:-}; local count=${2:-2}; let count--; [ 0 -lt $count ] && printf "$target"'%*s' $count; } # ソフトインデントを返す。$1: インデント数(省略時0) $2: 1インデントあたりのスペース数(省略時4) soft_indent() { repeat ' ' $((${1:-0} * ${2:-4})); } # $1を$2だけインデントする indent_part() { local indent=${2:-0} [ 0 -eq $indent ] && echo "$1" || \ echo "$1" | indent "$(soft_indent $indent)"; } for code in "$@"; do result+="$(indent_part "$code" $indent)""\n" let indent++ done echo -e "$result" | head -c -1 }
細かい課題
細かい課題は内部関数にした。
repeat(): ソフトインデントするときのスペース文字列を取得したい- 何インデントする?
- 1インデントあたり何個のスペースにする?
soft_indent(): 指定した文字を指定した回数だけ繰り返した文字列を作成したいprintf ' %*s' 0- 0回のときは空文字列にしたいが、1個スペースが出力されてしまう
0回のときは何もしないよう分岐する1回以上のときはスペース数-1する
- 0回のときは空文字列にしたいが、1個スペースが出力されてしまう
indent_part(): 指定テキストを指定した数だけインデントする
もしrepeat()を別関数化せねばsoft_indent()が以下のように煩雑化してしまう。zshならrepeat()が存在するらしい。これくらい標準化してくれ。
soft_indent() { local indent=${1:-0}; local size=${2:-4}; local count=$((($indent * $size) - 1)); [ 0 -lt $indent ] && printf ' %*s' $count; }
まとめ
端末で以下コマンドを実行する。
git clone https://github.com/ytyaru/Shell.CSharp.Code.Generator.MultiIndent.20191104150156 cd Shell.CSharp.Code.Generator.MultiIndent.20191104150156/src/ . indent.sh indent_pos 'namespace MyProj' 'class MyClass' 'static void Main()' "$(echo -e "int i;\ni = 5;")"
出力結果。
namespace MyProj
class MyClass
static void Main()
int i;
i = 5;
所感
いい感じに書けた。次は{,}で囲みたい。
- インデントを表す囲み文字を指定したい
- インデントするからといって改行するとは限らない
- C#言語
- 構文の要素
- ブロック(block)
- 式: どこでも書ける。戻り値必須。(expression)
- 文: ブロック内にのみ書ける(statement)
;- ブロック
{,}末尾には;が不要int M() {}
- 式なら末尾に
;が必要int M() => {};int M(int a) a switch => { _ => 0 };
- ブロック
- 参考
- 構文の要素
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch 9.0 2018-11-13 ※
- bash 4.4.12(1)-release ※
- SQLite 3.29.0 ※
- C# dotnet 3.0.100 ※
$ uname -a Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux