# KSP拡張・独自文法 ###### tags: `ksp` `kontakt` `vscode` ## メリット - 標準のちょっと微妙な文法のしがらみから脱却 - 効率が上がる(*スクリプティングにおいて) 多分これぐらい。十分恩恵はあるはず…。 ## デメリット - 独自の構文のために再学習 - 純粋なKSPの文法と互換がなくなる - 常にツールを経由してトランスパイルする必要がある - 該当ツールがサポート、更新をやめたらそれまで - 乗り換え先のKSPツールで再び修正し直し+学習し直し ## 対応コスト 内蔵コンパイラ側(外部プログラム)をゼロベースで見直し。(*1) (BNF、一部ロジックの再利用ができるぐらい) > *1: vscode拡張機能の本体(Typescript)とは別に存在するコンパイルプログラムのこと。 > vscode拡張機能の本体としては、独自文法に対するハイライト、ホバー表示の管理回り 完全な再設計を考えているので、安定版に達するまでに1年は必要。 普段の仕事もあるので、合間での作業。状況によっては完全に停滞する場合もある。 ## 拡張する文法など よくあるもの、某SublimeKSPで既に実装されているものと大体同じ - プリプロセッサ(C Preprocessor風) - 候補: https://github.com/shevek/jcpp - ローカル変数 - 引数ありユーザ定義関数 - ステートメント拡張 - for - foreach - return <戻り値> - etc. - 式拡張 - x++ → inc(x) *後置インクリメントのみ - x-- → dec(x) *後置デクリメントのみ ### プロセス 現状のオブファスケートの処理フロー同様に - 拡張構文込みの - 字句解析/構文解析 - 意味解析 - 意味解析をパスしたら - ASTをトラバースし、拡張構のASTをKSPの文法に展開(オブファスケートはここのフェーズで処理している) プリプロセスを介さず、文法としてパースを行う。こうすることで文法と統合させ行番号の乖離をなくすことが出来る ### ローカル変数表現 コストに見合うのであれば、ローカル変数は既存の某SublimeKSPとは違うアプローチを取りたい。 某SublimeKSP  ローカル変数1つにつき、対になるようにグローバル変数に展開 関数間で同じ変数名が存在しても、使い回しはされない(当然そうなる) ローカル変数の数=on initでの行カウントを消費する。 巨大なスクリプトの場合、変数宣言だけで行を消費してしまうとon initの上限に達し、Kontakt側がスタックオーバーフローを起こす。 独自対応するとしたら、このようば不安要素は抱えたくないため、ローカルスタックをちゃんと実現することで回避を図りたい。 これにより行数問題やグローバル変数宣言(on init)の肥大化は回避できるが、弱点は速度。スタック操作で管理するので配列アクセスが基本になるため。 ### 独自文法で記述 ~~~ def pow($x) return $x * $x end def on init declare $X $x := POW( 8 ) end on ~~~ ### 標準のKSPに展開 ~~~ on init declare %__transpile__stack__[ 4096 ] declare $__transpile__sp__ := -1 declare $__transpile__fp__ := 0 declare $__transpile__lsp__ := 0 declare $__transpile__lfp__ := 0 declare $__transpile__lret__ := 0 {ローカルを表現する変数。これ以上は増えない} {引数} %__transpile__stack__[ $__transpile__sp__ + 1 ] := 8 inc( $__transpile__sp__ ) { JSR 関数呼び出しは基本的にこの流れ 前段までで引数のプッシュ 復帰後のSPをプッシュ( = 引数が積まれる前の位置 ) 復帰後のFPをプッシュ KONTAKT標準のユーザー関数呼び出し } {局所変数に変更前のSP・FPを書き込み} $__transpile__lsp__ := $__transpile__sp__ $__transpile__lfp__ := $__transpile__fp__ { 関数から復帰後のSPをプッシュ( = 引数が積まれる前の位置 ) } %__transpile__stack__[ $__transpile__sp__ + 1 ] := $__transpile__lsp__ - 1 inc( $__transpile__sp__ ) {復帰後のFPをプッシュ} %__transpile__stack__[ $__transpile__sp__ + 1 ] := $__transpile__lfp__ inc( $__transpile__sp__ ) { ジャンプ演算+KONTAKT標準のユーザー関数呼び出し } $__transpile__fp__ := $__transpile__lsp__ - 1 + 1 {-1: 引数分, +1: FPの開始位置(1つ上)} call POW {on init 内ではcallが使用不可なので、実際はインライン展開} { ユーザー関数内でプッシュされた戻り値を取り出す } declare $x $x := %__transpile__stack__[ $__transpile__sp__ ] dec( $__transpile__sp__ ) message( $x ) {-> 64} end on { def POW($x) } function POW { return $x * $x } { $x*$x の計算結果をローカルスタックにプッシュ } %__transpile__stack__[ $__transpile__sp__ + 1 ] := %__transpile__stack__[ $__transpile__fp__ + 0 ] * %__transpile__stack__[ $__transpile__fp__ + 0 ] inc( $__transpile__sp__ ) $__transpile__lret__ := %__transpile__stack__[ $__transpile__sp__ - 0 ] { 戻り値 } $__transpile__fp__ := %__transpile__stack__[ $__transpile__sp__ - 1 ] { 復帰先フレームポインタ } $__transpile__sp__ := %__transpile__stack__[ $__transpile__sp__ - 2 ] { 復帰先スタックポインタ } {---- 呼び出し元に復帰 ----} %__transpile__stack__[ $__transpile__sp__ + 1 ] := $__transpile__lret__ { 戻り地をプッシュ } inc($__transpile__sp__) end function ~~~ ### 難点 CPU、VM同様に関数呼び出しのコストが高い。 スタックフレーム操作のエミュレーションをKSPで表現しているため。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up