PowerShell
TOML

便乗して PowerShell で汎用的に使えそうな設定ファイルとパーサ作ってみた

More than 1 year has passed since last update.

下記の記事が元ネタです。便乗して自分も作ってみました。実装はcodeの節参照。

なお、今回作った記法を以下ではPOMPと表記します。(TOMLの記法をパクりまくったので、POMP(Powershell de tOMl wo Pakutta の略)です。ネタです。)


だいたいTOMLと同じように書けるので、次節以降はTOMLと差異の大きい部分だけ説明します。詳細は、codeの節にある正常系テストを確認してください。


example.pomp.txt

# POMPドキュメントの例です。

title = POMP_Example

[owner]
name = "Lance Uppercut"
dob : datetime = 1979-05-27T07:32:00-08:00

[database]
server = 192.168.1.1
connection_max : int = 5000
enabled : bool = true

[[database.ports]] : int
8001
8001
8002

[clients]
[[clients.data]]
[[clients.data.]]
'gamma'
"delta"
[[clients.data.]]:int
1
2

# タブもしくはスペースで自由にインデントできますが、
# 必ずしも必要なものではありません。
[servers]
[servers.alpha]
ip = 10.0.0.1
dc = eqdc10

[servers.beta]
ip = 10.0.0.2
dc = eqdc10


上記POMPドキュメントは次のコマンドを用いて、

Get-Content .\example.pomp.txt -Raw | .\ConvertFrom-Pomp.ps1 | ConvertTo-Json -Depth 10

下記のJSONに変換できます。

{

"title": "POMP_Example",
"owner": {
"name": "Lance Uppercut",
"dob": "\/Date(296667120000)\/"
},
"database": {
"server": "192.168.1.1",
"connection_max": 5000,
"enabled": true,
"ports": [
8001,
8001,
8002
]
},
"clients": {
"data": [
[
"gamma",
"delta"
],
[
1,
2
]
]
},
"servers": {
"alpha": {
"ip": "10.0.0.1",
"dc": "eqdc10"
},
"beta": {
"ip": "10.0.0.2",
"dc": "eqdc10"
}
}
}


文字列

TOMLにある複数行文字列/複数行リテラル文字列は、POMPにはありません。

基本文字列、リテラル文字列はほとんどそのまま使えます。

なお、リテラル文字列内でシングルクォート(')を2個書くことで下記のようにエスケープされます。(そのため、複数行リテラル文字列のように書くとシングルクォートで囲まれた文字列として取得されます。)

PS > "str = '''escape'''" | .\ConvertFrom-Pomp.ps1

str
---
'escape'


裸リテラル文字列

POMPでは、クォート(")、シングルクォート(')で囲まれていないものについても、文字列として扱われる可能性があります。

クォートを使わない場合、何を持って文字列として扱うかは悩ましいところですが、実装としては、次の正規表現にマッチしたものを裸リテラル文字列として扱っています。


テーブル

TOMLは上位のテーブルの定義を省略できますが、POMPは上位のテーブル(または配列)の定義を省略できません。また、POMPはインラインテーブルを書くことはできません。


配列

POMPでは二重角括弧が配列の宣言になります。これは、一重角括弧がテーブルの宣言になることに対応しています。

TOMLとは二重角括弧の使い方が異なっているので注意してください。また、POMPにおいて、配列は名前付きの子要素が持てず、テーブルは名前付きの子要素しか持てません。

具体的には、下記のような記法になります。


.pomp

# 配列

[[arr1]]
One
Two
Three

# ジャグ配列
[[arr2]]
[[arr2.]]
1
2
[[arr2.]]
3
4
5

# テーブルの配列
[[arr3]]
[arr3.]
foo = val1
bar = val2
[arr3.]

[arr3.]
baz = val3


上記のPOMPドキュメントは下記のJSONと等価です。

{

"arr1": ["One", "Two", "Three"],
"arr2": [["1", "2"], ["3", "4", "5"]],
"arr3": [{"foo": "val1", "bar": "val2"}, {}, {"baz": "val3"}]
}


値の変換

as演算子を用いた、文字列から任意のデータ型への変換を実装しています。

なお、「任意のデータ型への変換を介して、パース時にヤバイ処理が実行されると、危険だよなー。」と思いながら実装しました。

値の変換は下記の通り、配列の宣言時か、Key/Valueペアを書く際に宣言し、コロン(:)の後に変換先の型を記載します。


.pomp

[[arr]]:bool

true
false

[table]
x:int = 100
y:float = 3.14
z:datetime = 1979-05-27T07:32:00-08:00



null値への変換

変換の結果としてnull値になる場合、通常はパースエラーとなります。

ただ、下記の通り、変換先の型としてnullを指定するか、もしくは変換先の型の末尾に疑問符(?)を指定した状態で、値として裸リテラル文字列でnullと記載した場合、null値が許容されます。

[table]

x:null = null
y:int? = null
z:string? = 'null'

上記のPOMPドキュメントは下記のJSONと等価です。

{"table" : {"x" : null, "y" : null, "z" : "null"}}


code

下記の通り、Gistに投稿した。