Skip to content

Instantly share code, notes, and snippets.

@amachang
Last active August 7, 2023 07:44
Show Gist options
  • Save amachang/22f7f3cccbed00c0aa776a3c6f1271b6 to your computer and use it in GitHub Desktop.
Save amachang/22f7f3cccbed00c0aa776a3c6f1271b6 to your computer and use it in GitHub Desktop.
nushell main calling process

Issue

nushell/nushell#9939

Current Call Tree

  • src/command.rs: gather_commandline_args

    • crates/nu-parser/src/deparse.rs: escape_for_script_arg
      • crates/nu-parser/src/deparse.rs: escape_quote_string
  • src/run.rs: run_file

    • crates/nu-cli/src/eval_file: evaluate_file
      • crates/nu-parser/src/parser.rs: parse
      • crates/nu-cli/src/util.rs: eval_source (source code)
      • crates/nu-cli/src/util.rs: eval_source (generated main calling code)
  • crates/nu-cli/src/util.rs: eval_source

    • crates/nu-parser/src/parser.rs: parse
    • crates/nu-engine/src/eval.rs: eval_block

script file の main を呼び出すコード生成をもっとよくするにはどうしたらいいの?

run_file で script file を parse したら、 args の quote 処理の前に AST を辿っていって main の parameters の宣言を読み込む必要がある。

なので、 fn main() のかなり最初の方で行われる gather_commandline_args の中で quote を行っている現状を変えなければならない。

やはりそもそも与えられた引数が何かに使われる可能性がある限り、 quote や escape はすべきではなく値がプログラムの外部に渡される時に quote をすべきだろう。こういう原則に名前はあるのだろうか。

この辺の原則が大事: https://lukeplant.me.uk/blog/posts/why-escape-on-input-is-a-bad-idea/

quote は main calling の code generation への埋め込み時にだけ行うようにコードを書くべき。つまり format!("{}", args.join()) のところで args に map で適用すべき

普通の postional parameter もすべて quote and escape する必要があるし、 optional positional parameter の仕様は理解していないが name value 双方に必要な quote や escape が行われていないのは間違いないので、それをする必要がある。

自分が学ばなければならないこと

  • ` このキャラクタの使い方がわかってないので理解する
  • optional positional parameter の名前をどのように script file に転送すべきか。特に space などの区切り文字の場合エラーとすべきかなど。
  • nu の文法上 optional positional parameter の value の部分に ('10' | into int) のような式をかけるのか調べる
  • custom command の parameter に指定できる type と into で指定できる type は同じものか。

上を学べば以下の仕様が決められる

  • parameter の正しい escape or encode 方法
  • optional positional parameter の名前を nu の文法がどの程度許容しているか。名前に他の文法が injection されないようにするためには何をエラーとすれば良いか。
  • optional positional parameter の value は普通の parameter と同じ escape or encode を取り入れても良いか
  • main の parameter の  type を見て into を挿入するのはいい戦略か
  • parameter の type から挿入される into の仕様を定めることはできるか?

  • gather_commandline_args がやってること全部書き下す
    • nu が -nu とか実行された場合は --log を args_to_nushell に追加
    • ハイフンが出てこない最初の引数が出てくるまで nu のオプションとして args_to_nushell に追加。 オプション名によって quote が必要なものは quote する。   - 個人的な意見としては quote するのは何かプログラムの外部との出口だけにとどめるべきだと思うので、この部分何かおかしい気はするが今回関係ないので無視
    • 上記の処理でハイフンが出てこない最初の引数は script_name として返される
    • それ以降の引数は args_to_script として返されるが、すべての文字列に escape_for_script_arg が適用される。これが今回の問題を起こしてる部分
  • escape_for_script_arg がやってること全部書き下す
    • input が -- から始まってる場合で = を含む場合
      • = で name と value にセパレートする
      • if: value がスペースを含む場合、 value を `{value}` に変換する(value に ` が含まれてたらどうなるんだ?)
      • else if: value が "\ を含む場合、 value を escape_quote_string する
      • else if: value が空文字列の場合、 '' に変換する
      • else: 何もしない
      • name と value を = で単純に結合して返す(name に space 含んでたらどうなるんだ?)
    • input が -- から始まっていないか、 -- で始まっているけど = を含まない
      • if: input がスペースを含む場合、 input を `{input}` に変換する(input に ` が含まれてたらどうなるんだ?)
      • else if: input が "\ を含む場合、 input を escape_quote_string する
      • else if: input が空文字列の場合、 '' に変換する
      • else: 何もしない
      • input を返す
  • escape_for_script_arg の影響範囲
    • test を除くと gather_commandline_args から呼ばれているだけ
  • 個人的に shell script のパースにパフォーマンスを求めてはいないが、 script file を実行すると main を持つかどうか確認するためにパースして、あったらもう一回 eval_source を呼び出してもう一回 parse するという無駄がある。
  • parse
    • 作られる AST のルートのノードは Block struct で block と言う変数に入れられることが多い。で parse -> eval_block のコードはよく出てくる
    • parse は StateWorkingSet にも影響を与え、 StateDelta の merge を伴うこともある。グローバル
    • block はレキシカルスコープで変数をキャプチャするっぽい
      • キャプチャした変数を参照可能な状態にする部分も parse に含まれている
    • parse と言いつつ構文解析だけではなく、ブロックと言う実行可能な構造を生成していて、 parse 時に現在の実行環境の変数がキャプチャされる。
      • 実行しながらパースされるシェルならではの動き
  • よく使われる struct
    • StateWorkingSet
      • external_commands: コマンド名のみ
      • StateDelta を持つ
      • 何か GlobalEngine を変更するときにそのコミットの単位のようなものか
    • StateDelta (crates/nu-protocol/src/engine/engine_state.rs)
      • scope の最小単位、名前空間をパッケージ化したもの?
      • decls: commands, only internal?, only parsed command
      • vars
      • blocks: code blocks
      • modules: package or something
    • EngineState
      • 環境変数とか設定とか基本コマンドとか、その他いろんなコマンドの実行に影響するグローバルな状態を持っている
      • StateDelta をマージすることによって様々な変更を同時に行う
      • まあ、マージされないまま drop される StateWorkingSet や StateDelta はいわゆるスコープや名前空間と考えて差し支えないだろう
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment