LuaへのトランスパイラYuescriptの紹介

- Yuescript は lua に変換するトランスコンパイラ。
  [Lua へのトランスパイラ集](https://github.com/hengestone/lua-languages)にあった。
- 他言語にある内包表記、クラス型 OOP、分解代入ほか、`?.`(null チェックしてアクセス)など便利な記法を取り入れる
  独自の prelude は持ってない(すべて Lua 本体にある機能に変換する)
  型の導入、他言語の哲学の導入はしていない。
- インデントでブロックを作り最後の式を評価して返す
  複文を横に並べて書けない

## インストールと使い方

- `sudo luarocks install yuescript`でインストール。
  `yue -c -l --target=5.1 program.yue`で program.lua を生成(シンプル！)
- 周辺ツールは文法の色付けのみ。(トランスパイルして lua のものを使えればつかう)
- shebangを書いてコマンドにできないっぽい。

## よく使う機能

### クラス型 OOP

```.moon
class ゲッソー extends _Enemy
	new: (x, y)=>
		super(x+1, y+8, 14, 16, Sprite({{0x14a, 0x15a, ox: -1, oy: -8, z: 1}, {0x14b, 0x15b}}))
		@gravity, @sprites.freq, @vx = 0, 0.8, 0
		Scene.current.world\add(@, @x, @y, @w, @h)

	update: (dt)=>
		super(dt)
		if @time > @sprites.freq*2 then @time, @direction = @time-@sprites.freq*2, if Scene.current.player.x < @x then 1 else 3
		if @time % (@sprites.freq*2) < @sprites.freq then @vy, @vx = -0.4, _Enemy.Speed*2
		else @vy, @vx = 0.4, 0
	Playerにぶつかった: ジュゲム.__base.Playerにぶつかった

```

- クラス宣言型の単一継承、privateはない。
- インスタンス変数やメソッドは、クラス宣言にインデントをつけた書き方のほか、(最近の言語のように)クラス宣言の外側から追加する方法がある
- クラス変数は`@`または`@@`をつけて宣言、使うときは`@@`を付ける
- クラス内クラスを作りたいとき、インデントをつけた書き方ではうまく展開されない。外側から追加する方法は大丈夫
  呼び出すとき`@@ClassName()`とすると、第一引数にクラスが渡ってしまうので`@@.ClassName()`とする必要がある。
- インスタンス変数は素直にテーブルのキーと値になるが、メソッドはindexメタメソッドで探す位置にある。
- `__class`で同一クラスか比較できたり、`__class.__name`で型名を取ったり、`__base.メソッド名`を得たりする。
  newは`__init`という名前になる
  生成は`型名()`
- インタプリタなのでクラス自体も実行中、値として存在する
- issubclassが必要

  ```.moon
  -- 引数は両方class
  export issubclass = (A, B)->
      assert(A.__init)
      assert(B.__init)
      A == B or (A.__parent and issubclass(A.__parent, B))
  ```

### 内包表記

`cells = [ [0 for i=1,4] for j=1,4]`

- forをネストさせる時順序に注意。外側から書く
- 関数の最後の行でない場所に、受け手のない内包表記を書いた場合、正しく展開されない
- 関数の最後の行が、内包表記の場合、受け手があるものとして return される
- [bug?] 内包表記をパイプで関数に繋げた場合、受け手として認識されない？→luacheck が empty if branch と報告するので気付く

### 分解代入

- テーブルを分解して変数に代入する機能 `{name: name, price: price} = data`
  配列の場合`{a1, a2} = array`
- for文でローカル変数に使うときが便利
- [bug] yue v23 現在、多値代入と分解代入を組み合わせると、① 左辺、② 添字、③ 代入の計算順序が狂う。
  `{x, y}, list = lume.first(list), lume.slice(list, 2)` breaks Before the assignment rule.

### `local *`で前方宣言を自動でする

- つまりクラスや関数の記述順序を前後させられる

### if for while switch do などが式になれる

- (if then else) を３項演算子として使える
- for は明確に受け手があるときのみ式になる(関数の最後ではreturnが必要)
- ネストした for 文は必ず２次元の配列を返す(１次元に出来ない)。対して、for が２つある内包表記は１次元の配列を作れる

### do 式で組み立て

- スコープを作るので自然な(短い)変数名が使える

### withでオブジェクト生成後に変更を加える

- constructor は不完全なものでよくなる

```.moon
    tween = with tween.new(@@【新たな数字が大きくなる時間○秒】, anim, {scale: 1}, 'outBack')
        .clock = -delay
```

### パイプ `|>`

- 記述順と動作順を同じにできる
- パイプの右側は関数呼び出しのみ。よって関数呼び出しのカッコを省略することも可能
- テーブル生成子や内包表記につなげたいとき、それを関数にすれば良い`(=> {@, @})`

### テンプレート文字列`"text#{exp}"`

- ダブルクォート文字列が複数行と式埋め込みに対応。トランスパイル時に分割される。式には`tostring()`が付く
  [spec?] 埋め込む式は複数行になってはいけない

### ?で存在チェック

loveのイベントを現在フォーカスを持つウィジットに渡すために、
他の言語ではinterfaceや空の実装を用意して何もしないメソッドを呼ぶだろうところ、
yueでは同名のメソッドが存在するときのみ呼ぶという形になった

```ui.moon
love.keypressed = (key, scancode, isrepeat)-> Scene.current\keypressed?(key, scancode, isrepeat)
love.keyreleased = (key, scancode)-> Scene.current\keyreleased?(key, scancode)
love.mousepressed = (x, y, button, istouch, presses)-> Scene.current\mousepressed?(x, y, button, istouch, presses)
love.mousereleased = (x, y, button, istouch , presses)-> Scene.current\mousereleased?(x, y, button, istouch, presses)
love.mousemoved = (x, y, dx, dy, istouch )-> Scene.current\mousemoved?(x, y, dx, dy, istouch)
love.wheelmoved = (dx, dy)-> Scene.current\wheelmoved?(dx, -dy) -- 反転
```

## おもしろい機能

### 仮引数の初期値

- トランスパイル時に関数本体に入るので
  - 毎回初期値が評価される
  - 右から詰める必要もない
  - より左の仮引数の値を使える(`...`も可能か？)

```2048.moon
-- spawn: (n = if math.random(100)<10 then 4 else 2)=>
spawn: (n = lume.weightedchoice({[2]: 90, [4]: 10}))=>
    try -- すべて埋まっているときにrandomchoiceがエラーを返すため
        {x, y} = [{i, j} for j, line in ipairs(@field) for i, cell in ipairs(line) when cell == 0] |> lume.randomchoice -- 空いてるマスを集めて１つ選ぶ
        @field[y][x] = n
```

### if の条件式の場所で変数宣言と初期化/または再代入文

- [注意] 外側に同じ名前の変数がある時、変数宣言にならずに再代入になる
- lua だから多値にも対応、一番左のものをifの条件の対象とする
- if not のときは`unless`を使う
- and/or の右側には書けず。
- [注意] and/or で複数の条件式を書けない。and で合わせたものが代入されてしまう
- while の条件式ではできない

### マクロでデータ成形

macroの実行はシステムのlua(5.4)で行われる。
必要なライブラリはマクロのなかで読む
戻り値は一つ、多値はできない。戻り値は文字列にして返す(文字列という値を返したい時２重の囲みになる)

```.moon
export macro DATE = -> os.date("'%Y-%m-%d'")
```

関数型しかないため引数０個の時呼び出しカッコを省略して変数のように使える
数字と文字列の自動変換があるため数字も渡せる

- 引数の扱い

  - 複数の引数を渡すことが可能
  - 引数は文字列として渡る、シングルクォート文字列、ダブルクォート文字列も周囲のクォート文字含めて渡る
    例外としてダブルカリー文字列が、先頭、末尾の改行とスペースをトリムして渡る(←これはやりすぎ。luaに合わせて！スペースが使えない！)

- 使いみち

  - トランスパイル時計算
    luaでできることは何でもできる(shellを呼ぶことも) mml2oggやzigをコンパイルすることもできた(ただしコンパイルキャッシュ機構が必要)
    データをluaのソース形式にすると実行時に変換する部分がなくなり速い。`luajit -b`すると更に速い

    ```.moon
    -- 先頭、末尾のスペースが渡ってこないことに注意
    export macro to2d = (data, nwidth)->
        import 'lib.lume'
            import 'lib.inspect'
                -- "#{inspect([ [(if 0x20==c then 0 else c-0x30) for _,c in utf8.codes(row\sub(1,nwidth))] for row in *lume.split(data,'\n')])}"
                    "#{inspect([ [(if 0x20==c then 0 else c) for _, c in utf8.codes(row\sub(1, nwidth))] for row in *lume.split(data,'\n')])}"
    ```

  - 決まった形のデータに展開する

    ```.moon
    export macro palette = (s)->
        import 'lib.lume'
        import 'lib.inspect'
        "with #{s\gsub('\'(#%x%x%x%x%x%x)\'', (c)->inspect{lume.color(c)})}\n\t.<> = #{s}"
    ```

- その他

  - [request] テーブルのキーの順序を保存するマクロがほしい

    ```.moon
    -- <>._orderedkeysをつけるマクロ、簡易的なもの(完全なものはparseが必要？)
    export macro orderedkeys = (dict)->
        import 'inspect'
        import 'lume'
        import 'yue'
        keys = lume.keys load(yue.to_lua dict)()
        "{#{dict\sub(2,-2)}, <>: {_orderedkeys: #{inspect [m for m in dict\gmatch("[%{,]%s?(.-): ") when m in keys]}} }"
    ```

### Unicode 識別子

- 日本語の変数名関数名ファイル名(=モジュール名)が使える
- 全角記号やπや㍍なども使える(lpegでspaceを␣と表記できる)
- lua5.2からパースが通らなかったが、`['＋']`と表記すれば使える機能だった。
- マングルされないためには`export 型名 = class 型名`と２回型名を書く必要がある
  マングルされているとimportで見つからない

`import 'combine' as {combn: 順列, permute: 組み合わせ}`

### close アトリビュート(luajit でも使える)

- スコープを抜けた時実行されるものを順序を前後して書いて置ける
- 書き方: `close _ = {<close>: => donothing()}`
- 一方、const アトリビュートは①テーブルの中身をいじれる。もう一度constと書くと再代入できる。ため役に立つとは思えない

## 危険な機能

### local が初期値になったので省略できることの弊害

lua では変数宣言、関数宣言に local と付けなければ global になってしまい、よく付け忘れられてしまっていた
Yue では local がデフォルトになったためそれを省略できるようになった。(local/const と書くこともできる)
global/export のときのみ書いたほうが書き忘れがない

しかし、lua のすべてに local と書いていたものを省略する感じで使うと
ファイルローカルと関数ローカルに同名の変数があったばあいlocalとつけていたときは別変数だったが、同一になってしまう

これはif文の条件式の部分での変数宣言でも同じ、複数の変数を宣言したときでも外側に同じ名前の変数が１つあればその変数だけ、新しく宣言と初期化にならない

つまり、その文だけを見た時『変数を宣言して初期値を代入している』のか、『外側の変数に再代入をしている』のか区別がつきにくくなった。(特にコピペ移植で)
他の言語が var とか let とかを書かせる理由がわかった。

### 関数呼び出しのカッコ省略

- 省略したほうが見やすくなるところは使う
- [[注意]] 関数名の後にスペースを付けないと lua の括弧省略記法(文字列/テーブル１つのみ渡す形)になる。２つ目以降の引数が渡らない
- `type x == 'number'`は間違いでカッコが必要

### 予約語が増えているので lua のつもりで書いたら parse 通らない

- as を変数名に使うと字下げがおかしいという変なエラーになる
- 一方、`self`は予約語ではない(1引数関数を`=>`と書くこともできる)

## その他

- 数値の間に`_`を挟める
- not equalが`!=`
- `a += 1`がある(多値代入にはできない)
- in 演算子
- `??`演算子はorのnil専用だが、使い分ける場面がない
- `--target=5.1`したとき整数除算`//`は使えない(使えたほうが便利)
- テーブルリテラルの別の表記方法がある(が余り使ってない)
- 配列を`[1,]`と表記できるが中身が１つの時カンマが必要(pythonのタプルのよう)
- テーブル内では`=`を `: `に書き換え、メソッド呼び出しを`:`から`\`に書き換えが必要
- テーブルの行末のカンマなくても良い、関数引数のカンマはなくてはいけない？
- テーブル展開`...`はテーブルリテラルの中だけで使えて、テーブルの部分更新に使える
- メタテーブルのキーを他の値と並べて書ける(活用例はない)
- switch文/式
- 一行に書かない限りelseifとelse if は同じ意味になる
- try/catch
- スライスは、①for文で②ipairsの代わりにアスタリスクを使ったときだけ使える
- 仮引数名をインスタンス変数にすることができる。その時の仮引数名は@を除いたもの
- exportアトリビュート
- [request?] 関数内の記述順序を前後させたい markdownのfootnoteのような
- 後置if/forは前に書いたものと全く同じ。スコープも作るし
- [注意] `y -2`は関数呼び出しと認識される。２項演算子式と認識させるには、スペースなしか、両方あり、にする
- backcallわからない