joとの比較で負けている気になったのでmkdictにblockを入れてみた

github.com

昨日の記事joと比較してみたのだけれど。ネストが深いJSONを作るのは楽でも、同じ階層の複数フィールドに値をもたせたJSONを作るのが大変だった。癪だったのでblockという概念を導入してみた。

同じ階層に複数のフィールド

例えば以下の様なJSONを作りたいとする。obの階層にname,ageという2つのフィールドがある。

{
  "ob": {
    "age": 20,
    "name": "foo"
  }
}

このとき今までのmkdictでは以下のように記述する必要があった。

$ dictknife mkdict ob/age 20 ob/name foo

joではシェル上で直接ネストして呼び出すことで以下の様に書ける。

$ jo -p ob=$(jo age=20 name=foo)

一方mkdictでは、深い階層のフィールドが単一であれば"/"で繋げていけば良いので ob/name foo みたいに手軽に記述できるのだけれど(よりネストした階層についても ob/father/name bar みたいに)、同じ階層に複数のフィールドをもたせたい時にちょっと不便だった。例えば今回はob/ageob/nameと毎回共通のprefix(?)を書く必要が出てしまう。

block

そんなわけでblockという概念を導入した。blockは"{"と"}"で記述されるもの。例えば先程のコードをblockを使って記述すると以下の様になる。

# 先程のコード
$ dictknife mkdict ob/age 20 ob/name foo

# blockを使ったコード
$ dictknife mkdict ob { age 20 name foo }

注意点として、"{"と"}"と他の引数との間には必ず空白が必要。シェルのtestコマンドと同様のイメージ(if [ -z "foo" ]のようなもののこと)。

なので以下の様なコードはNG。ただし現状の実装では構文エラーにはならない。

$ dictknife mkdict ob {age 20 name foo }
dictknife mkdict ob { age 20 name foo}

複雑なネストも

もちろん複雑なネストにも対応している。

深いネストでも。

$ dictknife mkdict a/b/c/ob { name foo age 20 }
{
  "a": {
    "b": {
      "c": {
        "ob": {
          "name": "foo",
          "age": 20
        }
      }
    }
  }
}

blockの中にblockがあっても。

$ dictknife mkdict a/b { x/ob { name foo age 20 } y/ob { name boo } } id 1
{
  "a": {
    "b": {
      "x": {
        "ob": {
          "name": "foo",
          "age": 20
        }
      },
      "y": {
        "ob": {
          "name": "boo"
        }
      }
    }
  },
  "id": 1
}

変数の参照と組わせる

変数の参照と組み合わせると便利。

$ dictknife mkdict @father { name A age 20 } @mother { name B age 20 } { name X age 0 father "&father" mother "&mother" } ";" { name Y age 1 father "&father" mother "&mother" }
[
  {
    "name": "X",
    "age": 0,
    "father": {
      "name": "A",
      "age": 20
    },
    "mother": {
      "name": "B",
      "age": 20
    }
  },
  {
    "name": "Y",
    "age": 1,
    "father": {
      "name": "A",
      "age": 20
    },
    "mother": {
      "name": "B",
      "age": 20
    }
  }
]

ヒアドキュメントでも。

$ dictknife mkdict << EOS
@father { name A age 20 } @mother { name B age 20 }
name X age 0 father "&father" mother "&mother"
name Y age 1 father "&father" mother "&mother"
EOS

まぁヒアドキュメントを使えるような状況なら素直にJSONを書いたほうが良いかもしれない。

導入に伴い変数にスコープを導入

blockの導入に伴い変数にスコープを導入した。一応blockの内部で同名の変数はシャドウイングされるし、外側の環境の変数にアクセスできる。

$ dictknife mkdict @v 10 x0 "&v" x1 { v "&v" } x2 { @v 20 v "&v" } x3 { v "&v" } x4 { v "&v" @v 100 y { v "&v" } } @v 11 x5 "&v"
{
  "x0": 10,
  "x1": {
    "v": 10
  },
  "x2": {
    "v": 20
  },
  "x3": {
    "v": 10
  },
  "x4": {
    "v": 10,
    "y": {
      "v": 100
    }
  },
  "x5": 11
}