多段インデントについて。
成果物
前回まで
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