karino2 / karino2.github.io

This site is auto generated from issues.
7 stars 2 forks source link

FileScriptingアプリ #163

Open karino2 opened 6 years ago

karino2 commented 6 years ago

粗筋

Android上で、ファイル関連の雑務を片付けるスクリプト環境が欲しくなる。

具体的にはmicroSDを128GBに乗り換えた事で、以前ScanSnapでスキャンしたpdfファイルを幾つか突っ込める余裕が出てきた。 この中身は無圧縮のjpegが埋め込まれているだけなので、単純にjpegのヘッダをseekしてmemcpyするだけでjpegファイルは取り出せる。 で、これが50ページ単位くらいのpdfに分かれているのを、一つのフォルダに連番jpegとして突っ込んでzipで固めたりしたい。

こういう類のファイル関連の雑務を軽くこなすスクリプト環境をAndroid上にも欲しくなってきた。

数日前にそういう話も書いた。

blog: Android上の日常的な雑務をやるスクリプト環境が欲しい

という事で作ろう。
まず名前をつける。名前はFileScriptingとした。

karino2 commented 6 years ago

コンセプト

キーボードはつなげる前提で考えている。 PowerShell ISVみたいな環境があれば良い。

karino2 commented 6 years ago

karino2 commented 6 years ago

イメージ的にはこういう感じで。

まず言語はとりあえずBeanShellというので行ってみようかな、と思っている。 luaでもいいかな、と思っていて、どっちにするかはまだ未定。

コマンドラインの所ではcdとかlsとかするし、パイプも作りたい。 ここはスクリプト言語とは別の簡易言語にするしか無いかな、と思っている。 cdするとグローバル変数のCWDとかが変更されて、上の窓のスクリプトと真ん中のコマンドラインの環境は共有されている。

イコールの左辺に変数をドル付きで書けて、これは上のスクリプトのドルなしグローバル変数として扱われる。 イコールの右辺を全て実行した最終的結果が左辺に入る。 ストリーミングも全部arrayに変換して突っ込む。

出来たらpowershellのwhenに相当する物とか作りたいが、まぁ無理にとは言わない。 適当にlsとかの結果を変数に突っ込んで、細かい事は上のスクリプトでやる。 ただlsはワイルドカードくらいは頑張りたい。catも出来たら文字列のリストにする位はやりたいなぁ。

スクリプトは今の所SQLiteに突っ込んで、ListViewから適当に選んで開く、でタブで複数開いて、このスクリプトを複数組み合わせて手動でevalしていく事で目的を達成していく。

karino2 commented 6 years ago

もともとの目的からバイナリを扱う必要があるので、下のJavaをそのまま触れるのがいいかな、と思う。 権限的には今の所ネットワークはなしでいいかなぁ。 EXTERNAL_STORAGEのwritableとreadableくらいでなんとかならないかなぁ。

karino2 commented 6 years ago

実装順序

  1. 単発コマンドでcdとlsくらいだけをサポートしたやる気無いコマンドラインで、上のスクリプトウィンドウと下のコンソール出力をとりあえず動かす
  2. コマンドラインをまともなパーサーコンビネータで書き直し、イコールをサポート
  3. 過去スクリプトのListViewと複数タブをサポート
  4. パイプをサポート
  5. スクリプトのexportをサポート

3くらいまで終われば使い物にはなる気がする。 1さえ終われば3まではイケる気がするので、使う所までは行くんじゃないか。

5まで終われば一般にも公開していいかなぁ。

最終的にはimportとかも必要だろうけれど、とりあえずバックアップさえ出来ればimportは必要になったらやればいいか、とは思っている。

karino2 commented 6 years ago

ソースコード置き場

ということで作り始めた。 https://github.com/karino2/FileScripting

karino2 commented 6 years ago

lsとかprintはオリジナルのBeanShellとは変えたい

BeanShellはコマンドが.bshファイルでいろいろ定義されてて、それが必要に応じて自動でロードされるという仕組みとなっているっぽい。 だからAndroidならassets下に置いてそれをロードするようにした方が良いのだろうな、 という気はするのだが、ローダー的な物を差し替えないと行けなくて、これが意外とめんどいので、最初は.bshファイルの中身をraw stringにコピペして起動時にevalしている。

で、dirは全部テキストをprintする感じのコマンドになっている。 だが、PowerShell民としてはこれはFileのIterableであって欲しい。 まぁ実用上はFileのArrayListでも良い。

で、そうすると出力の所も書き換える必要があって、formatterが要る、という話になると思う。 まずはハードコードでいいかなぁ、という気がするが。

そうすると、そもそもprintの実装もその辺いろいろやってしまっているが、 スクリプトレイヤでやるんじゃなくて、Kotlin側でやる方が筋が良いよな。 そうしよう。

ついでにAndroidだとdirじゃなくてlsだよな、という気もするので、コマンド名も変えよう。

パイプはIterable的なのにするかListにしてしまうか、ちょっと悩む。まぁパイプまで行ってから考えよう。

そう考えるとcatもIterable<String>だよな。 そういう感じにしておこう。

karino2 commented 6 years ago

一行コマンドラインのインタープリタ文法

一行コマンドラインは、やはり実用的には独自の言語を作る必要がありそう。 カッコとか書くのかったるいし。やはりワンライナーに特化してcdとかlsするだけ言語は要るだろう。

なんか名前が欲しいなぁ。OneLineScript、略称olsとしよう。

karino2 commented 6 years ago

空白区切りで引数は文字列

まず、引数はデフォルトでダブルクォートなしで文字列とみなされるべきだろう。 で、例えば以下は、

mv file1 file2

以下のBeanShellスクリプトとして実行されるべき。

mv("file1", "file2")

intとかも全部文字列になるのはダサいか。floatは使う予定は無いが、intくらいはサポートしよう。 で、16進とかは使わなくてよかろう。

だが、BeanShellは可変長引数をサポートしていない!まじか!イマドキ!

という事で、ols側でその辺は面倒みてやって、

mv(String, String)

が無かったら、

mv(Object[])

を呼ぶ事にしよう。Object配列は決め打ちで、Stringの可変長配列とかはサポートしない。 ちょっとかったるいが、BeanShell側でサポートしてないのをそこまで頑張って補う気も起こらないのでいいでしょう。

karino2 commented 6 years ago

ワイルドカード

ワイルドカードの展開はolsのレベルではやらないで、文字列をそのまま渡す事にする。 ただし、展開してList<File>を返すユーティリティを提供しよう。 だからmvとかを複数ファイル対応にしたいなら、最初からmv(Object[])で実装して、 配列の最後の要素をdestinationとするようにコードを書く必要がある。

ワイルドカード以外の複数ファイルのmvとかはまぁ最初は要らないだろう。 この辺は必要になる都度やっていこう。

karino2 commented 6 years ago

catやlsの出力はIterable

lsはIterable<File>を返す。ただ最終的にreplがprintする所でそれっぽいformatterで出力される。

catもIterable<String>を返すようにしよう。

変数と代入とIterable

さて、変数が欲しい。変数はドル記号で始めよう。ドル記号で始まっている変数は、その値に置き換えられて関数が呼び出される。

イコールで変数に代入出来るとする。

代入する時は、IterableをArrayListに変換して代入する事にしよう。 PowerShellもそう振る舞っていたので。

イコールの右辺はパイプまで含めて全てのコマンドを実行した結果を変数に入れる事にする。

で、ドル付き変数はBeanShellのグローバル変数にされる。 逆にBeanShell側からもグローバル変数として触れるようにしよう。

現状はfor文回したい時はBeanShell側で回すようにする。

karino2 commented 6 years ago

とりあえず入出力回りだけをやって、他はBeanShell側で

あまりいろいろと頑張らずに、とりあえずlsとかcdとかしつつ変数に突っ込んで、 そこから先はBeanShell側でへこへこ書けばいいかなぁ、と思っている。 まずは動いて使える所までやってからいろいろ追加していきたい。

パイプも一応考えてはおく

パイプライニングしようと思えば、オプションの引数とパイプラインから流れてくるオブジェクト列の両方を対応する必要がある。 だから専用のインターフェースを作ってしまう方が手早いだろう。

で、このインターフェースは引数とパイプからの受け取りをちゃんと区別出来る感じにする必要がある。 パイプからの受け取りはIterable<Object>で良かろう。

とりあえずlsした結果を正規表現とかでフィルタリングしたいので、matchくらいは実装する必要があるかなぁ。

ただこの辺は実装が面倒な割に、別に変数に突っ込んでからBeanShellでfor文回してもそんなに変わらんので、やはり後回しが筋が良さそう。

karino2 commented 6 years ago

近況

上記実装予定のうち、2に相当するあたりまで終わった。 cdとlsで移動して、lsの結果を変数に入れてスクリプトから触ったり出来る。 最初はvarargsサポートが無くてやる気を失いかけていたが、overloadは動いている事に気づいてlsは引数なし版も実装したら実用上は問題なくなってやる気が出た。

パーサーはbetter parseというのを使っているが、これがいい感じ。 たいしてドキュメントが無いが、こう動いて欲しい、という感じで書くとその通りに動くので、何も不満が無い。 Kotlinはライブラリもセンスが良くて触ってて楽しいね。

アプリの方はというと、少し触った感じ結構いいのが出来たな、と思っている。 poor man's PowerShell ISVくらいにはなっている。

次はスクリプトを保存したり読み出したりする所を実装したら、 ドッグフード開始かな。SQLite周辺はAnkoというのを使ってみる予定。

karino2 commented 6 years ago

近況2

AnkoはCursorをロードしてSimpleCursorAdapterに渡す、 という基本的なシナリオが実現出来ないと知って割と衝撃を受ける。 ただ、SQLiteDatabaseのextentionは使えるので、 それだけ使っている。

で、タブのタイトルがいつも「New」のままだが、 それ以外は一通りの構成要素は出来た。上記実装予定の3までが終わった、という事になる。
ちょっと仕上げやってアイコン書けばPlayに公開も出来るレベルだが、 公開よりも前に自分で使ってみようかなぁ、と今は思っている。

さて、いざスクリプトを書いてみると、バグってたらファイルが消滅してしまうので、 ひとまずtmpとかフォルダを掘って、その中にコピーして作業したい、 と思うが、これがまだ出来ない。 やはりワイルドカードに対応したmvやcpが要るな。

あとはついでにsortも欲しいかなぁ。

この辺は割と簡単なので、とりあえず使える所まではあと一歩、という所までは来た気がする。
当初考えていたjpeg埋め込みpdfをバラすコードを書いてみて、使い心地を確かめたい。

ここまで触っている印象だと、結構いい線いってる気がする。 もう作業系のスクリプトはPCじゃなくてもいいかなぁ。 ここ一年で、だいぶPCでの作業をAndroidタブに持っていくのには成功した気がする。

karino2 commented 6 years ago

近況3

ワイルドカードを実装して、ls, cp, mv, rmあたりが実用的に動くようになった。

これでとりあえず複数に分かれた自炊pdfからjpeg抜き出してフォルダに吐く、は作れるようになったはず。

今の所はextSdCardの方は書き込み出来ない。これはどうするのがいいかなぁ。 DocumentTree使うようにコードを書いてもいいのだが、internalのストレージとコードが2つになるのもださいし、でもinternalもいちいち許可取る、というのも馬鹿らしい。
SAFはシェルと相性が悪いのだよな、そもそも。

という事で、当面はinternalのsdにコピーして作業する事にする。コピーは無駄だが、当面はそこが耐えられないほどの事はやらないだろうし。

結構出来てきたなぁ。 なかなか革新的なアプリになった。よしよし。さすが俺。

karino2 commented 6 years ago

近況4

もうやりたい事は出来るかなぁ、という事でドッグフードしてた所、使ってみるといろいろ辛い所が出てくる。

やはり使ってみると必要と思う物は、使ってない時に思うのとは違うよなー。 ドッグフード重要。

という事で一通り直した。 また、保存されない作業場所が欲しいと思ったので、最初のバッファはスクラッチバッファとして特別扱いする事にした。

かなりいい感じになったが、直ってみるともっと細かい所が気になる。

もう少し使ってみたらこの辺も直そう。

karino2 commented 6 years ago

もう少し使ってみた所、選択範囲を実行がキーボードの選択では出来ないので、shift enterのショートカットで選択範囲を実行するようにした。

あとscratchがタブ切り替えると消えてたのを直した。

そろそろ後回しのバグが増えてきたので、issue trackを使い始める。

この過程で、pdfからjpeg抜き出すコードを書いてみた所、なんか一枚あたり30秒くらいかかる。 300ページくらいの本なら2時間以上掛かる計算になる。

さすがにこれは遅すぎだと思うのだか、なんでこんなかかるのだろう?

try catchもバグってるし、BeanShellいまいちだなぁ。もうちょっと手頃なインタープリタ、無いものか。

karino2 commented 6 years ago

snapscriptに乗り換え

Bean Shellはバイナリ処理がやけに遅いのと、try catchとかのバグが結構でかいので、他の言語を探す事にした。

groovyなどはあまりAndroidサポートは乗り気では無いらしく最近は進んでない、との事と、そもそもコンパイルする、という方針がAndroid的でないとおもうので、もうちょっと違う言語を探していた。

するとインタープリタとしてはsnapscriptがいいぜ、と言ってる書き込みを見かけた。 これか

レポジトリを見ると全然他の人に使われてる形跡は無いが、最近もコミットはされてる。 ふむ、使ってみるか、と触り始めた。

karino2 commented 6 years ago

導入

あとで書く。

karino2 commented 6 years ago

どうもREPLを前提としてない気がする

snapscriptはファイルをロードすると、ファイル名に対応したモジュールという物になるっぽい。 文字列リテラルを食わせる時は、default.snapというファイルに書いてあると思って動くっぽい。

で、このモジュールは要素が動的に追加されたり削除されたり、という事を想定してない気がする。

具体的には、同じ変数名で別の型の物を定義しようとするとエラーになる。 関数も同じシグニチャの中身が別の関数を作って評価するとエラーになる。 これはちょっといじってeval、ちょっといじってeval、というREPL的な使い方が出来ない事になる。

とりあえずソースコードを突っ込んでしまったので、適当にいじって自分の用途なら問題無いように変えた。

ロード時にキャッシュを適当にパージして関数作る時同じシグニチャのがあれば置き換えるようにし、変数も再定義は置き換えるようにかた。 少し違うが互換のシグニチャとかは置き換えたりしないが、ある程度無理そうなのは立ち上げ直せば良いか、という事で割り切る。

そしてエラーも一回エラーが出たあとにも、次の実行前にクリアするようにした。

最後の結果を返すようにした。

この位の変更で、だいたい使えるくらいまでは来た。

karino2 commented 6 years ago

ファイル書き出しスクリプトを移植してみる

BeanShellでは一枚につき30秒くらいかかっていたのをSnapScriptに移植してみた所、一枚6〜7秒くらいになった。 感覚的には0.5secくらいで終わっても良いコードのはずだが、この位なら一冊の本のjpeg取り出すのに30分以下とかなので、仕掛けておいて別の事やってればいいか、という範囲。

でもどこが遅いんだろうね? ファイル読んで、jpegっぽいマーカー探して、その範囲だけファイルに書き出すだけなのだが。

BuffererdOutputReaderかましたら気持ち早くなった(気のせいかも?)。 for文とかは自分で回さすkotlin側で実装しようかなぁ。

karino2 commented 6 years ago

SnapScript雑感

一通りコードを書いて使っていこう、という気がする所まで来たので書いてみる。

処理系のコードはクリーン

処理系のコードは小クラス主義で綺麗に書かれている。 これだけきっちり言語処理系書ける人もそうは居ないだろう、というレベルで良く書けている。

しかも遅そうな所はいろいろ工夫も入ってて、ちゃんと使い物になるレベルに仕上がっている。 割と一瞬で実行は開始されるし、速度もまぁまぁ早いし、結構良い処理系に思う。

そういう訳で、驚く程良く出来ている。

テストも結構良く書かれている。

karino2 commented 6 years ago

ドキュメントとサンプルは無い

サンプルが実質無い。 そしてドキュメントもほとんど無い。 唯一あるのが言語の説明が一ページ。(learn.htmlの事)。

このドキュメントの無さは異常で、どのレポジトリが何かすら分からない。 インタープリタのmainがどこにあるのかも分からない。酷い。

せめてどのjarを使えばいいのかくらい教えてよ…

結局良く分からなかったので、ソースを取ってきて突っ込む事にした。 これはあとから思えば正しい方針だった。

UnitTestは豊富にあって、これとコードが綺麗なのでこの2つを手がかりに使い方を探っていく感じだ。UnitTestはかなりいろいろな使い方をしてるので参考になる。

だが、もしUnitTestに無い使い方があったとすると、発見は出来そうも無い。 具体的にはインタープリタとして使う正しい使い方が全然分からない。(自分は内部に手を入れてコンパイラをインタープリタ化して使ってる)

自分の理解ではEvaluatorはexpressionしか評価出来なくて、statementsはcopilerとassemblerを使う必要かある、という物だが、Evaluator 的に使えるstatements評価器がどこかにあるのでは無いか?と未だに疑っている。(見つけられてないが)

karino2 commented 6 years ago

特定のユースケース以外で使った形跡が無い

REPLで使えないように見える事から考えて、どうもコンパイルしたバイナリをロードする使い方でしか使った事が無いように見える。 動的に一部をかきかえたりした形跡が無い。

別にそんな難しくもないのだが、やってないのが、やった形跡が無い、と感じる所。 技術的に面倒という訳でも無いので、本当にただやってないだけ、という感じ。

しかも普通はそういう使い方をしたくなると思うので、本当に本人達以外誰も使って無いんじゃないか。 githubもスターとか全然ついてないし。

karino2 commented 6 years ago

コミットはされてる

アクティブに開発されてる、という訳では無いが、去年もちょこちょこコミットはされてて、死んでる訳じゃなさそう。

動かしていればとりあえず動きはするので、触る気にもなる。実際内部の方は結構ちゃんと動いている。

これ、どっかクローズドな所で結構ハードに使ってるんじゃないかな? そうでなければこんなに処理系のコードが良くできているのは考えづらい。

ただ、その用途が特殊過ぎて普通の使い方とは大分違うんじゃないかなぁ。 で、内部にはもっとドキュメントとかあるが、外に出すコストは払ってない、でも一応誰かが公開する所まではやった、とかじゃないか。 分からないけど。

karino2 commented 6 years ago

言語はなかなか良い

System.arraycopyとか変態的な奴を使おうとするとリストとboxingなByte型の配列とbyteの配列がややこしくてハマったが、それ以外は素直に書ける。

しかも結構ちゃんと型チェックしてて、割といろいろ見つけてくれる。 それでいてJavaとのインターオペラビリティも良い。

文法的にはJSとかTypeScriptからなんとなく想像出来る感じで、それでいてJavaっぽくもあるので、割とすぐ使い始められる。 learn.htmlの内容はちょっとしか書いてないが、あれで確かにだいたい十分で書けるようにはなる。

最初はfunctionとかタイプするのだるい!と思ってたけど、型チェックがちゃんと動くのでわりと試行錯誤も少なく、あんま気にならなくなった。

表面上は全然違うが書いてる感覚はgolangに近いね。あんな感覚で書ける。 言語的にはかなり良いんじゃないか。 BeanShellよりは100倍良い。

karino2 commented 6 years ago

結論: かなり良い言語と処理系だが手を入れずに使う方法が謎

そういう訳で使える処理系だが、正しい使い方は良く分からない。 コード持っていって自分でいじって使うならかなり良いと思う。

ただここまで出来てるならちょっと整えるだけでjar参照するだけに出来ると思うんですけど…ここまでやっておいて、なんでそうなってないの!?