pythonでgoを書くためのライブラリを作り始めました
pythonでgoを書くためのユーティリティ的なライブラリを作り始めました。良い名前が見つかったので進捗がありました。
できること
できることは少しずつ増えていく予定です。
structの定義
例えば、以下の様な感じでstructを定義できます。
from goaway import get_repository r = get_repository() f = r.package("value").file("value.go") value = f.struct("Value", comment="value type") value.define_field("Name", type=f.string) value.define_field("Value", type=f.int) print(r.writer.write(f))
以下のようなgoのコードを生成します。
package value // Value : value type type Value struct { Name string Value int }
ちなみに現状ではwithを使って以下の様にも書けるようにしていますが。これは将来変更される可能性があります。
from goaway import get_repository r = get_repository() f = r.package("value").file("value.go") with f.struct("Value", comment="value type") as field: field("Name", type=f.string) field("Value", type=f.int) print(r.writer.write(f))
あと、この記事の例には載せていないですが。埋め込みやinterfaceも使えます。
enum的なconstの定義
enum的なconstの定義も出来るようにしました。こういうものを手軽に書いていきたい感じです。 (とは言え、enumレベルのかんたんなコード生成の場合には、テンプレートエンジンベースの出力で十分だったり、go側でのAST変換の方が楽な気がします)
例えば、トランプを模した定義は以下のような感じになります。
from goaway import get_repository def define(f): suit = f.enum("Cardsuit", f.string, comment="記号") suit.define_member("spadeds", "♠", comment="スペード") suit.define_member("hearts", "♥", comment="ハート") suit.define_member("diamonds", "♦", comment="ダイヤ") suit.define_member("clubs", "♣", comment="クラブ") card = f.struct("Card", comment="カード") card.define_field("Suit", type=suit) card.define_field("Value", type=f.int8) def main(): r = get_repository() f = r.package("card").file("card.go") define(f) print(r.writer.write(f)) if __name__ == "__main__": main()
結果は以下の様になります。
package card import ( "fmt" ) // Cardsuit : 記号 type Cardsuit string const ( // CardsuitSpadeds : スペード CardsuitSpadeds = Cardsuit("♠") // CardsuitHearts : ハート CardsuitHearts = Cardsuit("♥") // CardsuitDiamonds : ダイヤ CardsuitDiamonds = Cardsuit("♦") // CardsuitClubs : クラブ CardsuitClubs = Cardsuit("♣") ) // String : stringer implementation func (c Cardsuit) String() string { switch c { case CardsuitSpadeds: return "spadeds" case CardsuitHearts: return "hearts" case CardsuitDiamonds: return "diamonds" case CardsuitClubs: return "clubs" default: panic(fmt.Sprintf("unexpected Cardsuit %s, in string()", string(c))) } } // ParseCardsuit : parse func ParseCardsuit(c string) Cardsuit { switch c { case "♠": return CardsuitSpadeds case "♥": return CardsuitHearts case "♦": return CardsuitDiamonds case "♣": return CardsuitClubs default: panic(fmt.Sprintf("unexpected Cardsuit %v, in parse()", c)) } } // Card : カード type Card struct { Suit Cardsuit Value int8 }
出力先のファイルを分けたい場合
出力先のファイルを分けたい場合もあると思います。その場合は以下の様に書き換えると良いです。
# emit.py from goaway import get_repository def define(package): f = package.file("suit.go") suit = f.enum("Cardsuit", f.string, comment="記号") suit.define_member("spadeds", "♠", comment="スペード") suit.define_member("hearts", "♥", comment="ハート") suit.define_member("diamonds", "♦", comment="ダイヤ") suit.define_member("clubs", "♣", comment="クラブ") f = package.file("card.go") card = f.struct("Card", comment="カード") card.define_field("Suit", type=suit) card.define_field("Value", type=f.int8) def main(): r = get_repository() package = r.package("card") define(package) import logging logging.basicConfig(level=logging.INFO) r.emitter.emit_package(package, d="./card") if __name__ == "__main__": main()
今度はcard.goとsuit.goに分割されて出力されます。
$ python emit.py INFO:goaway.emitter:write: ./card/suit.go INFO:goaway.emitter:write: ./card/card.go $ tree card card ├── card.go └── suit.go
enum2go
ところで、変換先のgoの定義とほとんど同様の定義をpython上に直接記述するような形で書ける様になってもあんまり嬉しくありません。それなら直接go側で記述すれば良いだけなので。なので別種の定義ファイルを受け取ってコード生成するという形にしてみましょう。
enum2goというコマンドを作ってみました(インストールすると使える様になります)。
これは以下のようなyamlファイルを渡すと対応するgoのコードを出力するというようなものです。
# card.yaml suit.go: cardsuit: type: string description: 記号 enum: spadeds: value: ♠ description: スペード hearts: value: ♥ description: ハート diamonds: value: ♦ description: ダイヤ clubs: value: ♣ description: クラブ
これで先程のenumの定義と同様のものが生成されます。
$ enum2go --package card --position=. card.yaml INFO:goaway.emitter:write: ./card/suit.go # 通常は enum2go --package github.com/foo/bar bar.yaml みたいな形で使う