ツインテールdeエンジェルモード!!を触ってみる

# はじめに

「ツインテールdeエンジェルモード!!」というプログラミング言語を見つけました。

https://ja.osdn.net/projects/angelmode/wiki/FrontPage

~~ユニークな名前なのではじめはesolangだと思っていましたが、~~触ってみると使いやすくて、特にベクトル、行列計算が便利でした。ワンライナーで重宝しそうです。

```bash
$ tt -e '{{1, 2}, {3, 4}} @ {{2, 0}, {0, -2}}'
 = {{           2,          -4},
    {           6,          -8}}
```

公式ページによると

> この言語の話をするときは、ときどき語尾に「にゃん♪」をつけてほしいにゃん♪
> でも、これは強制ではないにゃん♪

とのことなので、以下触ってみた機能を軽く紹介するにゃん♪ :cat:

# バージョン

- OS: Ubuntu 20.04
- ツインテールdeエンジェルモード!! Ver.281

# インストール

公式ページからソースコードとマニュアルをダウンロードします。

https://ja.osdn.net/projects/angelmode/

後はマニュアルの通りにバイナリをビルドするだけで使えました。

```bash
# 依存ライブラリインストール（この他にgccも使用）
$ apt install flex bison
$ make
$ cp tt /usr/local/bin
$ tt -h

Usage: tt [Option] [FileName]

  -h | -? = Display Usage   Information ( This Page ) & Exit
  -v      = Display Version Information ( V281ILP64 ) & Exit
  -i      = Execute in Interactive Mode
  -e      = Execute in OneLiner    Mode
  -t | -x = Enable Runtime Trace for Debug [ -t -tt -ttt ... | -x ]
  -c | -m = {Color|Mono} Text Mode (Default=Auto)

  -: = Display Manual for :CMD < Runtime Command >
  -# = Display Manual for System関数＆System変数   #={#|0-9}

!!! Debug Option !!!
  "--"Dummy "-C"kOnly "-F"lex "-B"ison "-M"em "-G"c "-N"et "-^"Sig "-!"Ctr
```

# 特徴

- C言語製
- スクリプト言語（インタープリターを実行）
- 動的型付き言語

文法については、同封されているマニュアルで詳しく説明されていました。~~この記事を読むより分かりやすいです~~
以下、個人的に面白かった機能をいくつか紹介します。

## ワンライナーモード

```bash
$ tt -e 'print("Hello, world!\n")'
Hello, world!
```

## ベクトル、行列計算が簡単にできる

```
$ tt -i
tt> {3, 5} * {2, 4}
 = {           6,          20}
tt> {3, 5} * 2
 = {           6,          10}
tt> {1, 2} + 5
 = {           6,           7}
tt> {1, 2, 3} <> {2, 4, 6} # 内積
28
tt> {2, 0, 0} >< {0, 3, 0} # 外積
 = {           0,           0,           6}
tt> {{1}, {2}} @ {3, 4} # 行列積
 = {{           3,           4},
    {           6,           8}}
```

## 簡易表示

式文を書くと、副作用として値を表示してくれます。`print()` を書かなくて良い分ワンライナーが簡潔になります。

```
$ tt -e "1; 2; 3;"
1
2
3
```

配列の場合、簡易表示を使うとまるごと表示できるのでデバッグプリントに便利でした。

```tt:print_array.tt
a[0] = 1
print(a)
```
```
$ tt print_array.tt
tt: Master!! - Variable a has illegal type <A> [File="print_array.tt",Line=2]
> print(a)
        ^
```

```tt:show_array.tt
a[0] = 1
a
```

```bash:
$ tt show_array.tt 
a = {           1}
```

## 配列は要素を代入すると暗黙的に初期化できる

```
$ tt -i
tt> a[0] = 1 # a自体も暗黙的に初期化
tt> a
a = {           1}

# 途中の要素だけ作ると、連想配列のようになる
tt> b[2] = 10
tt> b
b[2]=10

# この状態ではベクトル演算できない
tt> b + {1, 2, 3}
tt: Master!! - Matrix element ?[0] is undefined [File="stdin",Line=3]

# 全ての要素を埋めると普通の配列になる
tt> b[0] = 8; b[1] = 9
tt> b
b = {           8,           9,          10}
```

もちろん、リテラルでまとめて初期化もできます。

```
tt> a = {1, 2, 3}
tt> a
a = {           1,           2,           3}
```

## 多値返却ができる

Goのようなエラー値を返す設計も可能です[^1]。

```tt:retn.tt
def div(x, y) {
	if(y == 0) {
		retn(NULL, "cannot be divided by zero")
	}
	retn(x / y, NULL)
}

res = div(6, 3) # 2番目の引数は無視される
print("%d\n", res) # 2

(res, err) = div(6, 0) # 全て受け取る
if(err != NULL) {
	print("err: %s\n", err)
	retn()
}
print("%d\n", res)
```

```bash
$ tt retn.tt
2
err: cannot be divided by zero
```

ちなみに戻り値は `retn()` とかっこがついていましたが、関数ではなく普通の戻り値キーワードでした。

## 未定義なものを参照してもエラーにならない

未定義変数、未定義メンバ、範囲外アクセスは全て `Undef` を返し、エラーにはなりません。

```
# 未定義変数を参照
tt> f
(Undef)

# 構造体の未定義メンバを参照
tt> x.a = 1
tt> x.b
(Undef)

# 配列の範囲外要素を参照
tt> a = {1, 2, 3}
tt> a[100]
(Undef)
```

例外として、**未定義関数の呼び出しのみエラーが発生**しました。

```
tt> f()
tt: Master!! - Function f() is undefined [File="stdin",Line=21]
> f()
  ^
```

## 文字列出力

- `${}` で埋め込みができる

```
tt> "2 + 3 = ${2 + 3}"
"2 + 3 = 5"

# スカラー値以外は表示できない？
tt> a = {1, 2, 3}
tt> "${a}"
"${a}"
```

- 色を付ける制御文字も定数化されている

```
tt> print("${C_GRE}hello${C_DEF}\n")
hello (緑色で表示される)
```

- C言語式のprint書式も使える

```
tt> print("%03d\n", 15)
015
```

## 関数を値として扱える

```tt:func.tt
def double(x) {
	retn(x * 2)
}

def map(arr, f) {
	for(i = 0; i < size(arr); i++){
		applied[i] = f(arr[i])
	}
	retn(applied)
}

print("%d\n", double(3))
each(e =  map({1, 2, 3}, double)) {
	print("%d,", e)
}
print("\n")
```

```
$ tt func.tt 
6
2,4,6,
```

残念ながらクロージャの定義はできないようです。

## コード領域、データ領域

`:data` 以降の行に文字列データを埋め込むことができます。設定のデフォルト値等を入れておくのに便利そうです。

```tt:
while (line=gets(:data)) {
        fields = split(chop(line), "=")
        config[fields[0]] = fields[1]
}

config // config["age"]="22",config["name"]="Taro",config["role"]="developer"

:data
name=Taro
age=22
role=developer
```

# おわりに

以上、「ツインテールdeエンジェルモード!!」を触ってみた紹介でした。
まだ触れていない機能がたくさんある（文字列操作、数学関連、TCPソケット等）ので、面白いものを見つけたらまた記事にしたいと思います。

ここまで読んでいただき、ありがとうございましたにゃん♪（思い出したように「にゃん」を付ける）

[^1]: 言語のお作法に沿っているかは分かりません...