Nim: transpilerとして使う(2) 〜一通り終了〜
ここ1年ぐらいjavascript書くのにpythonを使ってきました。:
python -> mypy -> transcrypt -> javascript
世の中はtypescriptとか言われてますすが、 そのへんは調べてません。
javascriptよりもpythonで記述できるのはよかったのですが、 \ あまり良くないこと気がついてきました。
mypyとtranscryptが非常に遅い
そもそも動的なpythonだと、mypyでもタイプチェックが弱い
そこで静的型付けで、javascriptに変換できそうな言語を 使ってみました
genie: もともと使い始めてた
nim: genie→javascriptが難しそうだったのでこちら
C#: ソフトのインストールとか面倒だったのでやめ
nimですが、いろいろな機能を使わなければ シンプルでとっつきは良かったです。
自分が手持ちのjavascriptはだいたい変換終わりました。
変換したときのポイント等を書いておきます。
jQueryを使う
jQueryを使うために宣言を作っていきます。
ただ、元々のjQueryが引数が柔らかすぎて完全に対応してしまうと、 \ 静的型付けやってる意味がないような気もします。
下のようになります。どんどん増えていくので \ 使うmethodをある程度限定してしまった方がよさそうです。:
proc jq*(doc: Document): jQuerySelector {.importc: "jQuery", nodecl.} proc jq*(nod: Node): jQuerySelector {.importc: "jQuery", nodecl.} proc jq*(nodes: seq[Node]): jQuerySelector {.importc: "jQuery", nodecl.} proc jq*(selector: cstring): jQuerySelector {.importc: "jQuery", nodecl.} proc jq*(selector: cstring, sel: Node): jQuerySelector {.importc: "jQuery", nodecl.} proc jq*(selector: cstring, sel: jQuerySelector): jQuerySelector {.importc: "jQuery", nodecl.}
セレクタだけでこんなにもバリエーションができます。 実際、ここまで必要ないと思います。特に私が確認した中では:
proc jq*(selector: cstring, sel: jQuerySelector): jQuerySelector {.importc: "jQuery", nodecl.}
これなんか、jQuery内の実体もid結合するだけだったりします。 \ そんなのなら使わなくてもよさそうです。
また、戻り値が変わる場合は関数名変えた方が無難かなと思います。:
proc prop_bool*(jq: jQuerySelector, name: cstring): bool {.importcpp: "#.prop(#)".}
同名で戻り値の型が違うとか、C++などでは対応できないですし...
こういう記述はファイルにまとめていきました。
nimでは使えないkeywordへの対応: whenとか
whenはnimのkeywordで使えませんでした。:
proc jqwhen*(jq: JQuery, src: cstring): JsPromise {.importcpp: "when" .} proc main(ev: Event): void = window.location.href = "http://www.google.com" jQuery.jqwhen(jQuery.ready).then(main)
importcppで変換してしまいます。
nim既存のjsffi.nimについて (謎のJsAssoc
nimに付属しているjsffiですが、 \ このmoduleで JsObject というのが使えるようになります。
JsObjectは {} のことで、これがないとjavascriptは書けないのですが、 \ nimの制限で書き方は少し煩雑になりました:
proc ajax*(jq: JQuery, prm: JsObject): JsPromise
この関数の場合は以下のようになります。:
var dat = newJsObject() dat["url"] = url dat["type"] = cstring("POST") dat["data"] = data dat["dataType"] = dataType dat["timeout"] = timeout return jQuery.ajax(dat)
macroとか書けばタイプ量を減らせそうですが、nimはmacroが難しいので 私は素直に書くことにしました。
あと、 JsAssoc という型を指定できる JsObject があったのですが、 これの使い方がいまいち分かっていません。使用例も見つからないし...
string型だとうまくいかないことがある→macroで対応
文字列の配列を簡単に記述したい場面がありました:
var obj = newJsObject() obj["monthsFull"] = @["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"] libraryA_func(obj)
これをすると、 monthFull は文字列のarrayではなく、 \ byte配列の配列と認識されます。 (nimのstringはそういう扱い)
そこで、ちゃんとcstringと書いてにやればいいですが、以下のようになります:
var ary: seq[cstring] = @[] for i in @["1月", "2月", "3月", "4月", "5月", "6月", ...]: ary.add(cstring(i)) obj["monthsFull"] = ary
これは面倒くさい... \ ここだけはmacroで乗りきろうと考えました
macro js_array*(src: varargs[string]): untyped = # {{{1 result = newNimNode(nnkBracket) for i in src.children: let t1 = newIdentNode("cstring") let t2 = newNimNode(nnkCast) t2.add(t1) t2.add(i) result.add(t2)
このmacroを作るのに何度か挫折しそうになりましたが、 \ 今はちゃんと動いているのでまぁよし。 \ 次のように書くことができるようになりました:
var obj = newJsObject() obj["monthsFull"] = js_array("1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月")
javascriptへの変換後のコードも綺麗です。
thisの対処
nimはシンプルなので、thisが引数にあるとかないとかは うまく扱えなさそうです。
思い切って、thisのことは忘れたいところですが、 イベント処理などではthis使わないと記述量うまく扱えなさそうです。
私はとりあえずセレクタだけは定義してみました:
proc jq_this*(): jQuerySelector {.importcpp: "jQuery(this)", nodecl.}
vim+ALEでjavascript用nimを使う
本筋ではないですが、 vim+ALE だと \ nim js とは条件が違ってしまって、いいメッセージが得られません。
どうもALEのnimはそんなに使われていないようで、 \ nimに関する記述が他の言語と違ってそんなに充実していない感じです。
私の場合は javascript 用のエラーが見たいのですが \ vim-ALEのスクリプトは nim check ... となっていて js では \ でないチェックも見えたりして vim-ALE の機能が逆に邪魔になりました。
それで nim check の部分を変更したいんですが、 \ 他の言語なら変数修正ぐらいでなんとかなりますが、 \ nim はそこまで整備はされていませんでした。
まずALEのファイルの場所ですが、私の場合は \ $HOME/.vim/bundle/dein.vim/.cache/.vimrc/.dein/ale_linters/nim/nimcheck.vim \ という場所にありました。 .cacheに気がつくのに時間がかかった...
nimcechk.vim スクリプトを修正します。:
return 'nim check --verbosity:0 --colors:off --listFullPaths %s'
ここをこんな感じに。:
return 'nim js -p:stub --verbosity:0 --colors:off --listFullPaths %s'
他の言語なら、この辺は変数からいじれるようになってるんですけどね...
JSON
JSONのparseは以下の定義を作ってみましたが、
proc parse_seq*(self: JsonClass, src: cstring): seq[JsObject] {. importcpp: "#.parse(#)" .}
失敗時に例外が出ます。 \ その処理はまだできていません。
コメント
Comments powered by Disqus