tag:crieit.net,2005:https://crieit.net/tags/%E3%81%AA%E3%81%A7%E3%81%97%E3%81%93/feed
「なでしこ」の記事 - Crieit
Crieitでタグ「なでしこ」に投稿された最近の記事
2021-12-21T07:47:50+09:00
https://crieit.net/tags/%E3%81%AA%E3%81%A7%E3%81%97%E3%81%93/feed
tag:crieit.net,2005:PublicArticle/17847
2021-12-13T06:56:59+09:00
2021-12-21T07:47:50+09:00
https://crieit.net/posts/nadesiko3-simple-compiler
なでしこ3でかんたんな自作言語のコンパイラを書いた
<p>日本語プログラミング言語「なでしこ」 Advent Calendar 2021<br />
<a target="_blank" rel="nofollow noopener" href="https://qiita.com/advent-calendar/2021/nadesiko">https://qiita.com/advent-calendar/2021/nadesiko</a><br />
の13日目の記事です。</p>
<p><a href="https://crieit.now.sh/upload_images/d022495e2d320868f0b13d07c98d3f9861b66f943faf6.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/d022495e2d320868f0b13d07c98d3f9861b66f943faf6.png?mw=700" alt="image" /></a></p>
<p>ライフゲームのコンパイルが通ればヨシ、という程度の雑なものです。</p>
<p><a target="_blank" rel="nofollow noopener" href="https://github.com/sonota88/vm2gol-v2-nadesiko3">https://github.com/sonota88/vm2gol-v2-nadesiko3</a></p>
<h1 id="移植元"><a href="#%E7%A7%BB%E6%A4%8D%E5%85%83">移植元</a></h1>
<p><a target="_blank" rel="nofollow noopener" href="https://github.com/sonota88/vm2gol-v2">https://github.com/sonota88/vm2gol-v2</a></p>
<p>なでしこ3版のベースになっているバージョンは <a target="_blank" rel="nofollow noopener" href="https://github.com/sonota88/vm2gol-v2/tree/62">tag:62</a> のあたり</p>
<p><自作言語処理系の説明用テンプレ></p>
<p>自分がコンパイラ実装に入門するために作った素朴なトイ言語とその処理系です。簡単に概要を書くと下記のような感じ。</p>
<ul>
<li>小規模: コンパイラ部分は 1,000 行程度</li>
<li>pure Ruby / 標準ライブラリ以外のライブラリ不要</li>
<li>独自VM向けにコンパイルする</li>
<li>ライフゲームのために必要な機能だけ
<ul>
<li>変数宣言、代入、反復、条件分岐、関数定義</li>
<li>演算子: <code>+</code>, <code>*</code>, <code>==</code>, <code>!=</code> のみ(優先順位は <code>(</code> <code>)</code> で明示)</li>
<li>型なし(値は符号付き整数のみ)</li>
</ul></li>
<li>作ったときに書いた備忘記事
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sonota88/items/f9cb3fc4a496b354b729">RubyでオレオレVMとアセンブラとコード生成器を2週間で作ってライフゲームを動かした話</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sonota88/items/2b95378b43a22109513c">Rubyでかんたんな自作言語のコンパイラを作った</a></li>
</ul></li>
<li><a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/vm2gol_v2_additional_features">本体には含めていない後付けの機能など</a>
<ul>
<li>真偽値リテラル / break / if/else / 単項マイナス / Racc などを使って書いたパーサの別実装</li>
</ul></li>
<li><a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/2020/08/30/132314">Ruby 以外の言語への移植</a>(コンパイラ部分のみ)</li>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sonota88/items/1e683276541cf1b87b76">セルフホスト版</a></li>
<li>製作過程を知りたい場合は<a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/2019/05/04/234516">製作メモ</a>を見てください</li>
</ul>
<p><説明用テンプレおわり></p>
<h1 id="メモ"><a href="#%E3%83%A1%E3%83%A2">メモ</a></h1>
<p>なでしこ3版のサイズ(行数)はこれくらい。1,000行切ってますね。</p>
<pre><code>$ wc -l *.nako3 lib/*.nako3
365 codegen.nako3
110 lexer.nako3
422 parser.nako3
49 lib/utils.nako3
946 合計
</code></pre>
<ul>
<li>そんなに苦労しなかった
<ul>
<li>書き味は普通にスクリプト言語っぽい感じ</li>
<li>標準でJSON読み書きできる、ネストした配列が作れる</li>
<li><code>[1, "a", []]</code> のように JavaScript っぽくリテラルが書ける</li>
</ul></li>
<li>ファイル分割できない?
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://nadesi.com/v3/doc/index.php?plugin_system%2F%E3%83%8A%E3%83%87%E3%82%B7%E3%82%B3&show">ナデシコ命令</a>を使って分割したファイルの取込み(というか読み込んだコードの評価)ができないか軽く試したのですが、期待した動きにならなかったので、諦めて適当なスクリプト(<code>preproc.rb</code>)で前処理することに</li>
</ul></li>
</ul>
<hr />
<p><code>引数+助詞、引数+助詞、 ... 関数。</code> のように書くのだ、ということをまず最初に知ると基本的な読み書きができるようになります。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://nadesi.com/v3/doc/index.php?%E6%96%87%E6%B3%95%2F%E5%9F%BA%E6%9C%AC&show">なでしこさん マニュアル - 文法/基本</a></li>
</ul>
<blockquote>
<p>命令文の基本的な文形 *<br />
そして、なでしこのプログラムの構造は、以下のような形式になっています。<br />
<code>引数+助詞、引数+助詞、 ... 関数。</code><br />
『「こんにちは」と表示』という命令文で言えば、「こんにちは」が引数で、「表示」が関数となります。</p>
</blockquote>
<p>「読んで」が <code>読</code> と <code>んで</code> に別れる、みたいなのもあって最初はよく分からなかった気がするので、助詞一覧にも目を通しておくとよいです。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://nadesi.com/v3/doc/index.php?%E6%96%87%E6%B3%95%2F%E5%AD%97%E5%8F%A5%E8%A7%A3%E6%9E%90&show">なでしこさん マニュアル - 文法/字句解析</a></li>
</ul>
<p>予約語や助詞はハイライトがあるといいなと思って、 Emacs の generic-mode で色を付けて書いていました。最初は助詞一覧が頭に入ってないので、エディタのサポートがあると入門がスムーズになるように思います。Qiita のコードブロックでもサポートされるといいですね。</p>
<p><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/234055/f73841fa-ac65-add9-98f5-ac5492581138.png" alt="0044.png" /></p>
<ul>
<li>参考: <a target="_blank" rel="nofollow noopener" href="https://qiita.com/tm_tn/items/3b40b5ab5e72750ffd7f">Emacs でオレオレ言語用モードを簡単作成! (ヘルプもあるよ!) - Qiita</a></li>
</ul>
<hr />
<p>最初は慣れてなくて試行錯誤が少しありました、という例をメモ。</p>
<pre><code># 「変数宣言パース」はトークン列をパースして変数宣言文オブジェクトを返す自作関数
# 「文リスト」は配列
変数宣言パースして文リストに配列追加
</code></pre>
<p><code>変数宣言パース</code> 関数の戻り値を <code>文リスト</code> に追加したいのですが、これは文法エラーになります。</p>
<p>マニュアルの <a target="_blank" rel="nofollow noopener" href="https://nadesi.com/v3/doc/index.php?plugin_system%2F%E9%85%8D%E5%88%97%E8%BF%BD%E5%8A%A0&show">「配列追加」命令</a> の説明に書かれているように、<code>AにBを</code> または <code>Aへ</code> の形で引数を渡さなければいけない(関数定義で定められた通りに助詞を添えなければいけない)からです。</p>
<p>ふーむ、なるほど、ということで次のように一時変数を補って2行(2文?)に分けると <code>AにBを配列追加</code> の形になり、動くようになります。</p>
<pre><code>変数宣言パースして文に代入
文リストに文を配列追加
</code></pre>
<p>1行で書けないでしょうか?</p>
<pre><code># () はなくてもOK
文リストに(変数宣言パース)を配列追加
</code></pre>
<p>1行で書けるようになりました。でもなんか微妙ですね……:thinking:</p>
<p>こういう場合は、「それ」を使うと <code>AにBを配列追加</code> の形に持ち込むことができます。</p>
<pre><code>変数宣言パースして、文リストにそれを配列追加
# 「Aに」「Bを」の順番は入れ替えてもよい
変数宣言パースして、それを文リストに配列追加
</code></pre>
<p>自然になりました。なるほど、こう書けばいいのかー。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://nadesi.com/v3/doc/index.php?%E6%96%87%E6%B3%95%2F%E3%81%9D%E3%82%8C&show">なでしこさん マニュアル - 文法/それ</a></li>
</ul>
<hr />
<p>なでしこ3には敬語があります。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://nadesi.com/v3/doc/index.php?%E6%96%87%E6%B3%95%2F%E6%95%AC%E8%AA%9E&show">なでしこさん マニュアル - 文法/敬語</a></li>
</ul>
<blockquote>
<p>なでしこv3.1.14以降、なでしこで礼節をわきまえたプログラムを書くことができるようになりました。</p>
</blockquote>
<p>使ってみました。なでしこ3では礼節のあるコンパイラを書くことができます。</p>
<pre><code>パースして抽象構文木に代入してください。
抽象構文木をJSONエンコードして、それを表示してください。お願いします。
</code></pre>
<h1 id="この記事を読んだ人はこちらも(たぶん)読んでいます"><a href="#%E3%81%93%E3%81%AE%E8%A8%98%E4%BA%8B%E3%82%92%E8%AA%AD%E3%82%93%E3%81%A0%E4%BA%BA%E3%81%AF%E3%81%93%E3%81%A1%E3%82%89%E3%82%82%EF%BC%88%E3%81%9F%E3%81%B6%E3%82%93%EF%BC%89%E8%AA%AD%E3%82%93%E3%81%A7%E3%81%84%E3%81%BE%E3%81%99">この記事を読んだ人はこちらも(たぶん)読んでいます</a></h1>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/sonota88/items/d43c9618498062863e58">なでしこ3で簡単なcatコマンドを作る</a></li>
</ul>
sonota486
tag:crieit.net,2005:PublicArticle/17834
2021-12-07T21:52:40+09:00
2021-12-07T21:52:40+09:00
https://crieit.net/posts/nadesiko3-cat-command
なでしこ3で簡単なcatコマンドを作る
<p><a target="_blank" rel="nofollow noopener" href="https://nadesi.com/doc3/">なでしこ3</a> に入門しています。</p>
<pre><code>$ cnako3 -v
3.2.30
</code></pre>
<p>新しくプログラミング言語を覚えるとき、入出力や反復などの基本的な機能の下調べも兼ねて、最近はよく cat コマンドを書いています。なでしこでもやってみます。</p>
<ul>
<li>参考: <a target="_blank" rel="nofollow noopener" href="https://atmarkit.itmedia.co.jp/ait/articles/1602/25/news034.html">【 cat 】コマンド――設定ファイルの内容を簡単に確認する:Linux基本コマンドTips(1) - @IT</a></li>
</ul>
<p>ちなみに、なでしこ3は Node.js ベースなので Linux でも使えます。この記事も Docker コンテナ内で動かして書いています。</p>
<ul>
<li><p><a target="_blank" rel="nofollow noopener" href="https://nadesi.com/doc3/index.php?%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%ABPC%E3%81%A7%E3%83%90%E3%83%83%E3%83%81%E5%87%A6%E7%90%86%E3%81%AB%E4%BD%BF%E3%81%86">ローカルPCでバッチ処理に使う - 日本語プログラミング言語「なでしこ3」</a></p>
<ul>
<li><code>npm install -g nadesiko3</code> でインストールできます</li>
</ul></li>
<li><p><a target="_blank" rel="nofollow noopener" href="https://qiita.com/kujirahand/items/2a940c14fd2b5ac0db29">Chromebookになでしこをインストールする - Qiita</a></p></li>
</ul>
<h1 id="標準出力に出力する"><a href="#%E6%A8%99%E6%BA%96%E5%87%BA%E5%8A%9B%E3%81%AB%E5%87%BA%E5%8A%9B%E3%81%99%E3%82%8B">標準出力に出力する</a></h1>
<p>まずは基本ということで。</p>
<pre><code>「こんにちは」と表示。
</code></pre>
<p>これは簡単。チュートリアルの最初に出てきますね。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://nadesi.com/v3/doc/index.php?%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB&show">なでしこさん マニュアル - チュートリアル</a></li>
</ul>
<h1 id="標準入力から読む"><a href="#%E6%A8%99%E6%BA%96%E5%85%A5%E5%8A%9B%E3%81%8B%E3%82%89%E8%AA%AD%E3%82%80">標準入力から読む</a></h1>
<p>マニュアルで探して <code>標準入力取得時</code> という命令を見つけました。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://nadesi.com/v3/doc/index.php?plugin_node%2F%E6%A8%99%E6%BA%96%E5%85%A5%E5%8A%9B%E5%8F%96%E5%BE%97%E6%99%82&show">なでしこさん マニュアル - plugin_node/標準入力取得時</a></li>
</ul>
<p>使ってみます。</p>
<pre><code># cat1.nako3
標準入力取得時には(行)
行を表示
ここまで
</code></pre>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://nadesi.com/v3/doc/index.php?%E6%96%87%E6%B3%95%2F%E7%84%A1%E5%90%8D%E9%96%A2%E6%95%B0&show">なでしこさん マニュアル - 文法/無名関数 / 「…には」構文 / 暗黙的な無名関数の指定</a></li>
</ul>
<p>動かしてみます。</p>
<pre><code>$ cat cat1.nako3 | cnako3 cat1.nako3
標準入力取得時には(行)
標準入力取得時には(行)
行を表示
行を表示
ここまで
ここまで
$
</code></pre>
<p>各行が2回表示されていますが、これはインストールしたなでしこに含まれている <code>src/plugin_node.js</code> を修正して createInterface のオプションとして <code>terminal: false</code> を指定することで解消できました。</p>
<pre><code>$ cat cat1.nako3 | cnako3 cat1.nako3 | cat
標準入力取得時には(行)
行を表示
ここまで
$
</code></pre>
<p>参考:</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/kujirahand/nadesiko3/blob/3.2.30/src/plugin_node.js">https://github.com/kujirahand/nadesiko3/blob/3.2.30/src/plugin_node.js</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://github.com/nodejs/node/issues/30510">readline duplicates input sent from the console/terminal · Issue #30510 · nodejs/node</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://nodejs.org/api/readline.html#readlinepromisescreateinterfaceoptions">Readline | Node.js v17.2.0 Documentation / readlinePromises.createInterface(options)</a></li>
</ul>
<p>はい、これで cat コマンドっぽいものができました。簡単ですね。</p>
<hr />
<p>さすがにこれだけだとさびしいので、もうちょっとあれこれやってみます。</p>
<h1 id="末尾に改行を付けない版の「表示」が使いたい"><a href="#%E6%9C%AB%E5%B0%BE%E3%81%AB%E6%94%B9%E8%A1%8C%E3%82%92%E4%BB%98%E3%81%91%E3%81%AA%E3%81%84%E7%89%88%E3%81%AE%E3%80%8C%E8%A1%A8%E7%A4%BA%E3%80%8D%E3%81%8C%E4%BD%BF%E3%81%84%E3%81%9F%E3%81%84">末尾に改行を付けない版の「表示」が使いたい</a></h1>
<p><a target="_blank" rel="nofollow noopener" href="https://www.nadesi.com/man/index.php?%E7%B6%99%E7%B6%9A%E8%A1%A8%E7%A4%BA">なでしこ v1 には「継続表示」という命令があり</a>、末尾に改行を付けずに出力できるようなのですが、v3 では用意されていないようです(v3.2.30 時点)。</p>
<p>cat コマンドの基本の動作としては入力の内容を変えずにそのまま出力したいのですが、「表示」命令だと末尾に改行がない入力が来た場合に内容が変わってしまいます。</p>
<p>そこで、「JSメソッド実行」という命令を利用して「継続表示」関数を作ってみました。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://nadesi.com/v3/doc/index.php?plugin_system%2FJS%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E5%AE%9F%E8%A1%8C&show">なでしこさん マニュアル - plugin_system/JSメソッド実行</a></li>
</ul>
<pre><code>●(値を)継続表示とは
「process.stdout」の「write」を["{値}"]でJSメソッド実行
ここまで
123を継続表示
「継続表示の」を継続表示
「テスト」を継続表示
「です。{LF}」を継続表示
</code></pre>
<pre><code>$ cnako3 sample_keizoku_hyouji.nako3
123継続表示のテストです。
$
</code></pre>
<p>よしよし。ちなみに <code>stdout</code> を <code>stderr</code> にすれば標準エラー出力に出力できます。</p>
<p>このように、なでしこ自体に命令が用意されていない場合でも JavaScript(Node.js)の機能を使うことで自分でなんとかすることができます。</p>
<p>この例では「JSメソッド実行」を使いましたが、他にも似た命令として「JS実行」などがあります。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://nadesi.com/v3/doc/index.php?plugin_system%2F%E7%89%B9%E6%AE%8A%E5%91%BD%E4%BB%A4&show">なでしこさん マニュアル - plugin_system/特殊命令</a></li>
</ul>
<hr />
<p>余談ですが eval がありますね!</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://nadesi.com/v3/doc/index.php?plugin_system%2F%E3%83%8A%E3%83%87%E3%82%B7%E3%82%B3&show">なでしこさん マニュアル - plugin_system/ナデシコ</a></li>
</ul>
<p>↓のようなことができるそうです。</p>
<pre><code>AAA=0
『AAA=123』をナデシコする
AAAを表示
</code></pre>
<h1 id="標準入力から全部読みたい"><a href="#%E6%A8%99%E6%BA%96%E5%85%A5%E5%8A%9B%E3%81%8B%E3%82%89%E5%85%A8%E9%83%A8%E8%AA%AD%E3%81%BF%E3%81%9F%E3%81%84">標準入力から全部読みたい</a></h1>
<p><code>標準入力取得時</code> だと <code>EOF</code> (入力ストリームの終端)が検出できないため、「標準入力からとりあえず全部読んで何かしたい」「標準入力から読み終わったタイミングで何かしたい」といったことができないようでした。</p>
<p>なでしこの機能だけで簡単に済ます方法を思いつけなかったので、標準入力からの入力をシェルスクリプトで一時ファイルに出力して、なでしこではその一時ファイルから読む、という方式にしてみました。<br />
ファイル名は <code>stdin</code> で決め打ち。</p>
<pre><code class="sh">#!/bin/bash
STDIN_FILE=stdin
if [ -e $STDIN_FILE ]; then
rm $STDIN_FILE
fi
if [ -p /dev/stdin ]; then
# 標準入力の内容をすべてファイルに保存
cat > $STDIN_FILE
fi
cnako3 "$@"
</code></pre>
<p>これを <code>cnako3.sh</code> として保存して、以降は <code>cnako3</code> コマンドを置き換える形で</p>
<pre><code>$ ls | ./cnako3.sh sample.nako3
</code></pre>
<p>のように使っていきます。</p>
<p>参考: <a target="_blank" rel="nofollow noopener" href="https://qiita.com/xtetsuji/items/d9800864c23af47c1443">標準入力にデータを与えられているかどうか調べる方法 - Qiita</a></p>
<p>なでしこ側では普通にファイルを読めばよいですね。</p>
<pre><code># cat2.nako3
●標準入力全部読とは
もし、「stdin」が存在するならば
「stdin」を読んで戻す
違えば
「標準入力からの読み込みに失敗」のエラー発生
ここまで
ここまで
標準入力全部読んでテキストに代入
テキストを表示
</code></pre>
<p><code>標準入力全部読んでテキストに代入</code> したあとは、普通の文字列の処理です。</p>
<pre><code>$ cat sample_end_with_newline.txt | ./cnako3.sh cat2.nako3
UTF-8 テキストファイル
ファイル終端の改行あり
行末にスペース→
$
</code></pre>
<h1 id="改造してみる"><a href="#%E6%94%B9%E9%80%A0%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">改造してみる</a></h1>
<p>cat コマンドなんか作って何が面白いんや? と思われるかもしれませんが、 cat ができるとそれを改造していろいろ遊べます。</p>
<h2 id="LF を "<改行>" + LF に置換してファイル終端も表示"><a href="#LF+%E3%82%92+%26quot%3B%EF%BC%9C%E6%94%B9%E8%A1%8C%EF%BC%9E%26quot%3B+%2B+LF+%E3%81%AB%E7%BD%AE%E6%8F%9B%E3%81%97%E3%81%A6%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E7%B5%82%E7%AB%AF%E3%82%82%E8%A1%A8%E7%A4%BA">LF を "<改行>" + LF に置換してファイル終端も表示</a></h2>
<pre><code># cat3.nako3
# 継続表示、標準入力全部読 は同じなので省略
標準入力全部読んでテキストに代入
テキストのLFを「<改行>{LF}」に置換して継続表示
「<ファイル終端>」を表示
</code></pre>
<p>入力の最後に改行がある場合:</p>
<pre><code>$ cat sample_end_with_newline.txt | ./cnako3.sh cat3.nako3
UTF-8 テキストファイル<改行>
ファイルの最後に改行あり<改行>
行末にスペース→ <改行>
<ファイル終端>
$
</code></pre>
<p>入力の最後に改行がない場合:</p>
<pre><code>$ cat sample_end_without_newline.txt | ./cnako3.sh cat3.nako3
UTF-8 テキストファイル<改行>
ファイルの最後に改行なし<改行>
行末にスペース→ <ファイル終端>
$
</code></pre>
<p>これで行末の余分なスペースを見つけやすくなりますね!<br />
<code>cat --show-all</code> みたいな感じ。<br />
最後に <code><ファイル終端></code> を付けて、最後の改行やスペースがあるかどうかも分かりやすくしてみました。</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://nadesi.com/v3/doc/index.php?plugin_system%2F%E7%BD%AE%E6%8F%9B&show">なでしこさん マニュアル - plugin_system/置換</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://nadesi.com/v3/doc/index.php?plugin_system%2F%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E5%AE%9A%E6%95%B0&show">なでしこさん マニュアル - plugin_system/システム定数</a>
<ul>
<li><code>LF</code> はシステム定数として定義されています</li>
</ul></li>
</ul>
<h2 id="行番号を表示"><a href="#%E8%A1%8C%E7%95%AA%E5%8F%B7%E3%82%92%E8%A1%A8%E7%A4%BA">行番号を表示</a></h2>
<p><code>cat -n</code> みたいなの。</p>
<pre><code># cat4.nako3
# 継続表示、標準入力全部読 は同じなので省略
標準入力全部読んでテキストに代入
テキストをLFで区切って行リストに代入
行数 = 行リストの配列要素数
行番号を1から(行数)まで繰り返す
「{行番号}: {行リスト@(行番号 - 1)}」を表示
ここまで
</code></pre>
<pre><code>$ cat sample_end_with_newline.txt | ./cnako3.sh cat4.nako3
1: UTF-8 テキストファイル
2: ファイルの最後に改行あり
3: 行末にスペース→
4:
$
</code></pre>
<p>行番号が分かってべんり!</p>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://nadesi.com/v3/doc/index.php?%E6%96%87%E6%B3%95%2F%E9%85%8D%E5%88%97%E5%A4%89%E6%95%B0&show">なでしこさん マニュアル - 文法/配列変数</a></li>
</ul>
<h2 id="1文字ずつ処理"><a href="#1%E6%96%87%E5%AD%97%E3%81%9A%E3%81%A4%E5%87%A6%E7%90%86">1文字ずつ処理</a></h2>
<p>1文字ずつ処理するバージョンも書いてみました。</p>
<pre><code># cat5.nako3
# 継続表示、標準入力全部読 は同じなので省略
●(テキストから位置の)文字取得
文字配列 = テキストを文字列分解
文字配列@位置を戻す
ここまで
標準入力全部読んでテキストに代入
テキスト文字数はテキストの文字数
位置は0
位置が(テキスト文字数 - 1)以下の間繰り返す
テキストから位置の文字取得して文字に代入
文字を継続表示
位置 = 位置 + 1
ここまで
</code></pre>
<p>「文字取得」関数を呼び出すたびに文字列分解していて効率悪いですが、おためしなので気にしない。</p>
<pre><code>$ cat sample_end_with_newline.txt | ./cnako3.sh cat5.nako3
UTF-8 テキストファイル
ファイルの最後に改行あり
行末にスペース→
$
</code></pre>
<h2 id="行をソート"><a href="#%E8%A1%8C%E3%82%92%E3%82%BD%E3%83%BC%E3%83%88">行をソート</a></h2>
<p>cat コマンドを改造すると、たとえば行をソートするコマンドなんかも作れるようになります。</p>
<pre><code># sort.nako3
# 継続表示、標準入力全部読 は同じなので省略
標準入力全部読んでテキストに代入
テキストをLFで区切って行リストに代入
行リストを配列ソートしてソート済行リストに代入
ソート済行リストを反復
対象を表示
ここまで
</code></pre>
<pre><code>$ cat sort.nako3 | ./cnako3.sh sort.nako3
「stdin」を読んで戻す
「標準入力からの読み込みに失敗」のエラー発生
「process.stdout」の「write」を["{値}"]でJSメソッド実行
ここまで
もし、「stdin」が存在するならば
対象を表示
違えば
●標準入力全部読とは
●(値を)継続表示とは
ここまで
ここまで
ここまで
ソート済行リストを反復
テキストをLFで区切って行リストに代入
標準入力全部読んでテキストに代入
行リストを配列ソートしてソート済行リストに代入
$
</code></pre>
<hr />
<p>というわけで cat コマンドっぽいものを作っていろいろ試してみました。この記事は以上です。</p>
<h1 id="この記事を読んだ人は(ひょっとしたら)こちらも読んでいます"><a href="#%E3%81%93%E3%81%AE%E8%A8%98%E4%BA%8B%E3%82%92%E8%AA%AD%E3%82%93%E3%81%A0%E4%BA%BA%E3%81%AF%EF%BC%88%E3%81%B2%E3%82%87%E3%81%A3%E3%81%A8%E3%81%97%E3%81%9F%E3%82%89%EF%BC%89%E3%81%93%E3%81%A1%E3%82%89%E3%82%82%E8%AA%AD%E3%82%93%E3%81%A7%E3%81%84%E3%81%BE%E3%81%99">この記事を読んだ人は(ひょっとしたら)こちらも読んでいます</a></h1>
<ul>
<li><a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/2020/09/19/084602">Zig: 1バイトごとに読み書きするだけのcatコマンドを書いてみた</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/2020/07/18/154534">Deno: 標準入力を読んで行ごとに処理</a></li>
<li><a target="_blank" rel="nofollow noopener" href="https://memo88.hatenablog.com/entry/20200711_kotlin_read_stdin_each_line">Kotlin: 標準入力を読んで行ごとに処理</a></li>
</ul>
sonota486