ncss(自作プログラミング言語)
2023年01月06日
![thumbnail](https://img.esa.io/uploads/production/attachments/21347/2024/01/25/148142/8b333775-dd3f-46a7-b5bc-3d5caf9ec95e.png)
![banner.png (48.7 kB)](https://img.esa.io/uploads/production/attachments/21347/2024/01/25/148142/8b333775-dd3f-46a7-b5bc-3d5caf9ec95e.png)
なぜ
今までの会話でこんなことがありました
A. 使ったことあるプログラミング言語は?
B. うーん... HTML と CSS かな?
経験者なら違和感を感じると思います
どっちもプログラミング言語じゃなくね?
厳密に言うと HTML は マークアップ言語で CSS は スタイルシート言語 です。
プログラミング言語だ、と言う人もいれば違うと言う人もいます。
そこで私は思いつきました。
「プログラミング言語にすればいい!」
ということで作りました
目標は
CSSをプログラミング言語にする
はじめに
プログラミング言語を作るには、他のプログラミング言語を使用します
- Ruby は C言語
- Python は C言語, Java...
やはりC言語で実装するのが基本的ぽい、が、めんどくさい!
そこで私は JavaScript で実装することにしました。
決め手は以下の通りです
- 使い慣れている
- ブラウザで実行できる
- 公開しやすい
パクれる参考になるリポジトリがあった
自作言語について
言語仕様は この (GitHub) ようにしました。
サンプルコードは こちら (GitHub) です。
言語名は
CSS(カスケーディング・スタイル・シート) ではない
ということで
NCSS (not CSS)
としました。SCSSを意識してますね
工夫点はcssらしくしたことです。(目標だから当然)
さらに scss らしいあの うるさい ネストを再現できるように、
グループ というものを追加しました。
1/* scssらしいうるささの再現 */ 2#main { 3 width: 100vw; 4 5 .container { 6 margin: 10px; 7 8 .content { 9 font-size: 1.2rem; 10 background-color: aqua; 11 } 12 } 13}
実装には JavaScriptを使った と書きましたが、制作は TypeScript に切り替えました。
型が無いとあまりにも不便だったからです。型を定義すれば文字列でも選択肢を絞れるから楽でした。
公開する前に JavaScript にコンパイルしています。
使い方
node が入っている場合は↓でインストールできます
npm i -g @satooru65536/ncss
↓ でncssを実行できます。
ncss ./main.ncss
ncss を使うためにVScodeの拡張機能も作りました。
難しかった点
こぴぺしたので あまり苦労しませんでしたが、return や berak の扱いが厄介でした。
関数の中で同じ関数をなん度も呼び出しているので、どこまで遡って中断すればいいか分からなかったからです。
何言ってるか分からない場合は ↓ を見て察してください。(簡単に説明する方法わからない)
1/* mian.ncss */ 2#main { 3 sub: ""; 4 content: "main!!"; 5 return: ""; 6} 7 8#sub { 9 --i: 0; 10 .while [--i < 3] { 11 .if [--i == 0] { 12 return: "sub return"; 13 } 14 transform: --i var(--i) + 1; 15 } 16 content: "error"; 17}
1// ブロック内の処理を順番に実行する関数 2function eval_statement_list(/* ... */) { 3 for (/* ブロック内の処理を一つずつ */) { 4 eal_statement(/* ... */); 5 } 6} 7 8// 処理を実行する関数 9function eval_statement(/* ... */) { 10 ... 11 else if (/* 関数を呼び出す場合 */) eval_statement_list(/* ... */); 12 else if (/* 代入の場合 */) eval_assign(/* ... */); 13 else if (/* 変数宣言の場合 */) eval_assign(/* ... */); 14 ... 15}
1[処理手順はイメージこんな感じ] 2 3#main 内のブロックを処理 eval_statement_list() 4 #sub 内のブロックを処理 eval_statement_list() 5 eal_statement(--i: 0;) 6 eal_statement(.while) 7 .while 内のブロックを処理 eval_statement_list() 8 eal_statement(.if) 9 .if 内のブロックを処理 eval_statement_list() 10 eal_statement(return) // eval_statement_list を何回遡れば...? 11 // if ブロック > 中断 12 // eal_statement(transform) 13 14 // while ブロック > 中断 15 // eal_statement(.if) 16 // eal_statement(.if) 17 18 // #sub ブロック > 中断 19 // eal_statement(content) 20 21// #main ブロック > 続行 22 eal_statement(content) 23 eal_statement(return) 24// #main ブロック > 中断 25// プログラム終了 26 27/* ------------------------------------ */ 28 29// 中断と続行の判断基準は? 30// => https://github.com/ncss-project/ncss/blob/main/src/evaluator.ts#L70 31// called_global_func フラッグがポイント 32// 関数のブロックから直接呼び出されているかを判断(if/else/group...のブロックはfalse) 33// false なら中断する 34// => https://github.com/ncss-project/ncss/blob/main/src/evaluator.ts#L78
おわりに
ちゃんとしたプログラミング言語は難しくても、この程度ならとても簡単でした。
ぜひ ソースコード を解読してみてください