Erg言語パーサー内部のざっくり解説(1)

ドキュメントだけでなく実際の処理をRustを使って実装するようになってきた

Ergは構文がシンプルだが、パースでは複雑な処理をしている

そのためパーサーで簡単な実装を加えるだけで割と簡単に正常な構文もエラーになるなどの問題が発生する

それで自分が理解できているかの整理を兼ねて，Erg言語を開発してみたい人向けの内容を書いてみようと思った

Rustと再帰下降構文解析の話は省く

現在進行形(2022/1/25)でエラーの種類分けをしているのでもしかしたらエラーの位置だったり種類が変わっている可能性があるので注意

Lexerは軽く、Parserは以下のメソッドに絞って説明をする

- try_reduce_module()
- try_reduce_chunk()
- try_reduce_expr()
- try_reduce_bin_lhs()
- try_reduce_call_or_acc()
- try_reduce_acc_lhs()
- try_reduce_acc_chain()
- try_reduce_args()

## Lexer

ファイルを文字列として取得し，Erg言語で使われる`token`に分類する

ここで文字列や数値、Ergで使われる構文の記号などに分類する

### コマンド実行方法

```
erg --mode lex
erg --mode lex <script.er>
cargo lex
cargo lex <script.er>
```

実際に分析してみると以下の様な出力を得られる

[token名 解析された文字, ..., EOF ]

最後に必ず`EOF`が挿入される

```
>>> a = 1
[Symbol a, Equal =, NatLit 1, EOF ]
>>> add(x, y) = x + y
[Symbol add, LParen (, Symbol x, Comma ,, Symbol y, RParen ), Equal =, Symbol x, Plus +, Symbol y, EOF ]
```

ここで，例えば[ProcArrow `=>`]が[Equal `=`]、[Gre `>`]に分割されたりすれば構文のバグになる

`token`の種類やカテゴリは全て`token.rs`に集約されているので、新しい記号などを追加する際にはそこに定義してから利用する

### 解析方法

これら構文解析は以下で処理される

```rust:lex.rs
impl Iterator for Lexer /*<'a>*/ {
    type Item = LexResult<Token>;

    fn next(&mut self) -> Option<Self::Item> {
    ...
    }
...
}
```

スクリプトやREPLなどで得た文字列を全て`Vec<char>`に変換して，上記の`next()`で前から順番に解析していく

```rust:lex.rs
    pub fn new(input: Input) -> Self {
        let normed = normalize_newline(&input.read());
        Lexer {
            chars: normed.chars().collect::<Vec<char>>(),
            ...
        }
    }
```

文字列は消費せずに解析するスタート位置を更新してそこから解析する文字を見る

解析の際によく使うメソッドの一覧

```rust:lex.rs
fn emit_token()   // ここでtoken化と解析済みの位置の更新
fn consume()      // 解析する位置を一つ進める
fn peek_cur_ch()  // 現在の位置の文字を見る
fn peek_next_ch() // 次の文字を見る
```

自分が実装した箇所だからわかりやすいので，例として`comment`のところを見る

`next()`なので返り値が`Option<char>`となっている

それをErgの構文とマッチングさせている

今回の`comment`の場合だと`#`と`#[~]#`になる

```rust:lex.rs
    fn next(&mut self) -> Option<Self::Item> {
        ...
        if let Some('#') = self.peek_cur_ch() {
            if let Some('[') = self.peek_next_ch() {
                if let Err(e) = self.lex_multi_line_comment() {
                    return Some(Err(e));
                }
            } else if let Err(e) = self.lex_comment() {
                return Some(Err(e));
            }
        }
        ...
```

現在の位置が`self.peek_cur_ch()`でわかるのでそれで`#`かどうかをマッチングをかける

次の文字が`[`なら複数行のコメントになるので`self.lex_multi_line_comment()`で解析させる

`[`ではないなら一行のコメントになるので`self.lex_comment()`で一行の解析をさせる

ここで大切なのは先に複数行のコメントかどうかを見るということ

一行コメントとして解析を始めてしまうと，それ以降の改行まで文字列が全てコメントとしてスキップされていくので，複数行コメントの構文になる`[`を見逃すバグになってしまう

また、最近見つけたバグだと`else if`になっていなくて、複数行コメントが呼ばれた後にさらに追加で普通のコメントが呼ばれるようになってしまっていた

新しい`token`を追加する際には順番に気を付けて実装をする

他にも複数文字列と一行の文字列のときに逆していたのを発見して直したりもした

```
"""
Error[#0000]: File <stdin>, line 1,

1 | """
  :   -

SyntaxError: the string is not closed by "
```

`""`と`"`で解析されており`"""`で解析されていなかった

なのでこれも`"""`を先にしないといけなかった

このように，順番がおかしい時にはそれはバグとなる

`lex_multi_line_comment()`は以下の通り

```rust:lex.rs
    fn lex_multi_line_comment(&mut self) -> LexResult<()> {
        // エラーを起こした時に無視されるコメントを表示するため
        let mut s = "".to_string();
        let mut nest_level = 0;
        while let Some(c) = self.peek_cur_ch() {
            if let Some(next_c) = self.peek_next_ch() {
                match (c, next_c) {
                    ('#', '[') => {
                        nest_level += 1;
                    }
                    (']', '#') => {
                        nest_level -= 1;
                        if nest_level == 0 {
                            self.consume() // ]
                            self.consume() // #
                            return Ok(());
                        }
                    }
                    _ => {}
                }
                if c == '\n' {
                    self.lineno_token_starts += 1;
                    self.col_token_starts = 0;
                }
            }
            if Self::is_bidi(self.peek_cur_ch().unwrap()) {
                let comment = self.emit_token(Illegal, &s);
                return Err(LexError::syntax_error(
                    0,
                    comment.loc(),
                    switch_lang!(
                        "japanese" => "不正なユニコード文字(双方向オーバーライド)がコメント中に使用されています",
                        "simplified_chinese" => "注释中使用了非法的unicode字符（双向覆盖）",
                        "traditional_chinese" => "註釋中使用了非法的unicode字符（雙向覆蓋）",
                        "english" => "invalid unicode character (bi-directional override) in comments",
                    ),
                    None,
                ));
            }
            s.push(self.consume().unwrap());
        }
        if nest_level >= 0 {
            return Ok(());
        }
        let comment = self.emit_token(Illegal, &s);
        Err(LexError::syntax_error(
            0,
            comment.loc(),
            switch_lang!(
            "japanese" => "複数行コメントが]#で閉じられていません",
            "simplified_chinese" => "未用]#号结束的多处评论",
            "traditional_chinese" => "多條評論未用]#關閉",
            "english" => "Multi-comment is not closed with ]#",
            ),
            None,
        ))
    }
```

やっていることは簡単で、`]#`が来るまでループして見る位置を`self.consume()`している

少し厄介なのは、`#[`が再度コメント内で呼ばれたりしてそれの閉じるための`]#`があればそれもコメントにしないといけないという問題がある

```
#[
    #[
        #[
            コメント
        ]#
    ]#
]#
```

なので、その入子になっているかどうかを`nest_level`で加算し，`]#`で減算して0になったときにOk(())になるようにしている

それまでにNoneになったら解析エラーとして返す

---

このような関数がいくつかあり、最終的に`next()`がNoneで文字を全て読み終わったら解析が終了となり，これらが`TokenStream`として上記の様な形で`Parser`に渡される

## Parser

ここからが非常に厄介なので注意

Erg言語には予約語が一つもなく、組み込みの定数などを含め多くの文字(print, Class, True, etc.)がParseする段階ではどれも`Symbol`扱いになる

なので、token種類と並びのみで関数であったり変数の宣言かどうかを判断している

この`Symbol`の扱い方を覚えればとりあえずParserの難しいところは分かっていくと思う

```python
a          # 変数呼び出し、関数オブジェト呼び出し、高階関数オブジェクト呼び出しなどなど
.a         # パブリック
::a        # 明示的なプライベート
a + 1      # 二項演算
a.0        # タプル添え字アクセス
a.method() # メソッド呼び出し
a.         # クラスアクセス(本来はaではなく大文字のA)
a::        # クラスアクセス(これも)
a::{data}  # データパック宣言
a: Type    # 型指定
a :> Type  # 汎化型指定
a <: Type  # 部分型指定
a|T: Type| # 型変数宣言
a[0]       # 配列添え字アクセス
a x:= 1    # 関数キーワード引数呼び出し
a -> a     # 無名関数宣言
a => a     # 無名プロシージャ宣言
a = 1      # 変数代入
a x = x    # 関数宣言
a = x -> x # 無名関数宣言と代入
a = x => x # 無名プロシージャ宣言と代入
a x = x    # 関数宣言
a 1        # 関数呼び出し
a x->x = x # 高階関数宣言
a 1 1      # 高階関数呼び出し
a(1) 1     # こんなのとかもa(1)(1)
```

このように、`Symbol`(この場合は`a`)が始めに来たら次の`token`によって様々に分岐が発生する

因みに、私が分かっている範囲での宣言なのでまだある

なので、Parserを修正するのにはErgの構文知識とParserのアルゴリズムの理解が必要になる

私もまだ完全には理解できていないので、試行錯誤しながら実装だったり修正をしている最中

Parserで使われている略語は大まかなのはこんな感じ

|略語| 単語 | 訳 |
| --- | ---  | --- |
| acc | accessor | アクセッサ― |
| asc | ascription | 帰属 |
| bin| binary | 二項 |
| def | definition | 定義式 |
| elem | element | 要素 |
| expr| expression | 式 |
| kw | Keyword | キーワード |
| ident | identifier | 識別子 |
| op | operator | 演算子 |
| sig| signature | シグネチャ |
| spec | specifier | 指定子 |
| subr | subroutine | サブルーチン |

メソッド名や変数名などにこれらが良く使われているので参考までに

### Parserコマンド

ここからデバッグコマンドを利用をすることができ、呼び出されたメソッドやエラーが発生した場所などを情報として取得できる

一応注意点としては重い処理になるため、大きいファイルでやるとかなり処理に時間がかかる

また、`cargo install erg`でインストールした場合は再度`erg install erg --features debug`が必要になる

`degub`featureフラグとコマンドは`--mode parse`を利用する

```
erg --mode parser
erg --mode parser <script.er>
cargo run --features debug -- --mode parse
cargo run --features debug -- --mode parse <script.er>
cargo dprs # ショートカットもある
```

`cargo run --features debug -- --mode parse`で実行すると以下の様な結果が得られるようになる

``` 
>>> a = 1
[DEBUG] compiler\erg_parser\parse.rs:0307: the parsing process has started.
[DEBUG] compiler\erg_parser\parse.rs:0308: token stream: [Symbol a, Equal =, NatLit 1, EOF ]
[DEBUG] compiler\erg_parser\parse.rs:0334:
 (1) entered try_reduce_module, cur: Symbol a
[DEBUG] compiler\erg_parser\parse.rs:1065:
 (2) entered try_reduce_chunk, cur: Symbol a
[DEBUG] compiler\erg_parser\parse.rs:1605:
 (3) entered try_reduce_bin_lhs, cur: Symbol a
[DEBUG] compiler\erg_parser\parse.rs:1797:
･ (4) entered try_reduce_call_or_acc, cur: Symbol a
[DEBUG] compiler\erg_parser\parse.rs:0526:
･ (5) entered try_reduce_acc_lhs, cur: Symbol a
[DEBUG] compiler\erg_parser\parse.rs:0548:
･ (4) exit try_reduce_acc_lhs, cur: Equal =
[DEBUG] compiler\erg_parser\parse.rs:1818:
･ (5) entered try_reduce_acc_chain, cur: Equal =
[DEBUG] compiler\erg_parser\parse.rs:1937:
･ (4) exit try_reduce_acc_chain, cur: Equal =
[DEBUG] compiler\erg_parser\parse.rs:0644:
･ (5) entered opt_reduce_args, cur: Equal =
[DEBUG] compiler\erg_parser\parse.rs:1811:
･ (4) exit try_reduce_call_or_acc, cur: Equal =
[DEBUG] compiler\erg_parser\parse.rs:1677:
 (3) exit try_reduce_bin_lhs, cur: Equal =
[DEBUG] compiler\erg_parser\convert.rs:0013:
･ (4) entered convert_rhs_to_sig, cur: NatLit 1
[DEBUG] compiler\erg_parser\convert.rs:0078:
･ (5) entered convert_accessor_to_var_sig, cur: NatLit 1
[DEBUG] compiler\erg_parser\parse.rs:1367:
･ (4) entered try_reduce_expr, cur: NatLit 1
[DEBUG] compiler\erg_parser\parse.rs:1605:
･ (5) entered try_reduce_bin_lhs, cur: NatLit 1
[DEBUG] compiler\erg_parser\parse.rs:2404:
･ (6) entered try_reduce_lit, cur: NatLit 1
[DEBUG] compiler\erg_parser\parse.rs:2405:
･ (5) exit try_reduce_lit, cur: NatLit 1
[DEBUG] compiler\erg_parser\parse.rs:1630:
･ (4) exit try_reduce_bin_lhs, cur: EOF
[DEBUG] compiler\erg_parser\parse.rs:1536:
 (3) exit try_reduce_expr, cur: EOF
[DEBUG] compiler\erg_parser\parse.rs:1099:
 (2) exit try_reduce_chunk, cur: EOF
[DEBUG] compiler\erg_parser\parse.rs:0366:
 (1) exit try_reduce_module, cur: EOF
[DEBUG] compiler\erg_parser\parse.rs:0321: the parsing process has completed (errs: 0).
[DEBUG] compiler\erg_parser\parse.rs:0322: AST:
::a =
    1
::a =
    1
```

再帰の`深さ`と実行された`メソッド名`，解析している`token`が一行で表示されるようになる

最終的に脱糖衣された抽象構文木が出力されるようになっている

因みに`･`一つで深さ四つ分になる

以下からは[DEBUG]は過剰なので省略する

### 基本的な解析の流れ

重要なメソッドはいくつかあるが，ほぼ必ず呼び出されるのは以下の通り

```rust
fn try_reduce_module()  // ファイル(module)やREPLの一行とかがこれになる
fn try_reduce_chunk()   // 一行やブロックがこの塊になる
fn try_reduce_expr()    // ergでは基本的には式なのでこれが呼び出される
fn try_reduce_bin_lhs() // 一番小さな要素でchunk()やexpr()の始めにこれが呼ばれる
```

解析されていく感じとしては以下の様な感じだと思われる(まだ調べている最中)

再帰下降で左側から順番に解析がされていく

```
|  a  =  (x, y)  ->    x + y 
| lhs |         rhs
| sym |    lhs   |      rhs
| sym | arg, arg |      rhs
| sym | arg, arg | lit binOp lit 
```

## try_reduce_module()

ファイルにある一つの行やブロック、REPLの一行がいわゆるmoduleにあたる

なのでこれが必ず`Parser`が起動したときにこれが呼ばれる

ここから呼ばれてSeparator(`;`や`\n`)の時にはスキップして毎度`try_reduce_chunk()`を呼び出している

ここで`token`が`EOF`になればパースが完了になる

```rust:parse.rs
    fn try_reduce_module(&mut self) -> ParseResult<Module> {
        debug_call_info!(self);
        let mut chunks = Module::empty();
        loop {
            match self.peek_kind() {
                Some(Newline | Semi) => {
                    self.skip();
                }
                Some(EOF) => {
                    break;
                }
                Some(_) => {
                    if let Ok(expr) = self.try_reduce_chunk(true, false) {
                        chunks.push(expr);
                        // 一行の複数のchunkがある場合はエラー
                        if !self.cur_is(EOF) && !self.cur_category_is(TC::Separator) {
                            let err = self.skip_and_throw_invalid_chunk_err(
                                caused_by!(),
                                chunks.last().unwrap().loc(),
                            );
                            self.errs.push(err);
                        }
                    }
                }
                None => {
                    if !self.errs.is_empty() {
                        debug_exit_info!(self);
                        return Err(());
                    } else {
                        switch_unreachable!()
                    }
                }
            }
        }
        debug_exit_info!(self);
        Ok(chunks)
    }
```

## try_reduce_chunk()

expr + defが一つのchunkらしい(まだ調べている最中)

ブロック単位や式全般がこのchunkとなり、大抵は一行だったりブロックだったりする

```python
a = 1 # これ一行がchunk
b = 2 # これも一つのchunk
main!() =
    print! "hi"
    log "there" # ここまでのひとまとまりがblockのchunk
```

`try_reduce_bin_lhs()`は後で説明する

ここで一つ目の`token`を読み込んで以降の処理が決定するのでここの処理が非常に大切になる

```rust:parse.rs
        let mut stack = Vec::<ExprOrOp>::new();
        stack.push(ExprOrOp::Expr(
            self.try_reduce_bin_lhs(false, in_brace)
                .map_err(|_| self.stack_dec())?,
        ));
```

`try_reduce_bin_lhs()`で始めの`token`を読み込んでそれが次のトークンを示唆する場合には次の解析に移り，なければここに戻って来る

REPLだと基本的に一行とブロックがこの`chunk`になるのでファイルで複数行にならなければ`module`と同様に一回しか呼ばれない

次に、loopで現在のトークンの解析を始めて、各メソッドでトークンを消費して再度ここに戻ってくるということをしている

これらスタックされた式、演算子は再度取り出されてsignatureとして変換される

ここで小さな要素群を塊の式に直している感じ

必ずここで一つはExprがスタックされるはずなので、次のループで場合によっては取り出して再度式に変換するなどをしている

実際のループ処理を見てみる

### 引数全般

`self.peek()`で現在の要素を見て`Symbol`か`Literal`だったら`try_reduce_args`が呼ばれる

引数が返ってきたら、スタックされているExprを取り出して引数を取る`Call式`に変換する

```rust:parse.rs
                // aやxとかの`Symbol`と 1, True, "sample"などの`Literal` 
                Some(arg) if arg.is(Symbol) || arg.category_is(TC::Literal) => {                   
                    let args = self.try_reduce_args(false).map_err(|_| self.stack_dec())?;
                    let obj = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_)));
                    stack.push(ExprOrOp::Expr(obj.call_expr(args)));
                }
```
### 代入全般

`=`が来たら一般的な代入表現に還元する

代入式はブロックを持ってくることもできるので，途中で`Newline`かどうかを求めて`try_reduce_block`をやっていたりする

ブロックの時には複数の式が来る可能性があるが，ブロックじゃなければ一行の式になるのでそれを判断している

取り出したexpr(lhs)をシグネチャに変換している

これで右辺に式を持つ形に変換している

```rust
                Some(op) if op.category_is(TC::DefOp) => {
                    let op = self.lpop(); // =が取り出される
                    // =の次に改行があればブロックになる
                    let is_multiline_block = self.cur_is(Newline);
                    let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_)));
                    // 代入式のシグネチャに式を直す lhs = rhs
                    let sig = self.convert_rhs_to_sig(lhs).map_err(|_| self.stack_dec())?;
                    // =は定義になるのでIDを付与する
                    self.counter.inc();
                    // ブロックだったら複数行の表現が可能なのでそちらに任せる
                    let block = if is_multiline_block {
                        self.try_reduce_block().map_err(|_| self.stack_dec())?
                    } else {
                        // precedence: `=` < `,`
                        let expr = self
                            .try_reduce_expr(true, false, false, false) // =で代入する式を求める
                            .map_err(|_| self.stack_dec())?;
                        Block::new(vec![expr])
                    };
                    // IDと代入するブロック(exprs)を代入式に変換する
                    let body = DefBody::new(op, block, self.counter); // lhs = block <-こんな感じにあるように変換
                    self.level -= 1;
                    return Ok(Expr::Def(Def::new(sig, body)));
                }
```

### 無名関数・プロシージャ

`=>`か`->`が来たら無名関数・プロシージャに還元する

```python
(x, y) -> log x + y
(x: Int, y: Int): Int => print! x + y
x -> x
!y => y
```

Parseする段階では副作用のあるなしを判断できないので、両方とも同じ処理をしている

これもブロックを定義できるのでNewlineかどうかを先に見る

その上で式をblockメソッドかexprメソッドで取得するようになっている

```rust
                Some(op) if op.category_is(TC::LambdaOp) => {
                    let op = self.lpop(); // =>, ->のどちらか
                    let is_multiline_block = self.cur_is(Newline);
                    let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_)));
                    // 引数が左辺にあるはずなので，それを抽象構文木で右辺を取れる形に変換する
                    let sig = self
                        .convert_rhs_to_lambda_sig(lhs)
                        .map_err(|_| self.stack_dec(fn_name!()))?;
                    self.counter.inc();
                    let block = if is_multiline_block {
                        self.try_reduce_block()
                            .map_err(|_| self.stack_dec(fn_name!()))?
                    } else {
                        // precedence: `->` > `,`
                        let expr = self
                            .try_reduce_expr(false, false, false, false)
                            .map_err(|_| self.stack_dec(fn_name!()))?;
                        Block::new(vec![expr])
                    };
                    stack.push(ExprOrOp::Expr(Expr::Lambda(Lambda::new(
                        sig,
                        op,
                        block,
                        self.counter,
                    ))));
                }
```


### 型指定

`:`が来た時には型指定(型帰属)に還元する

今までのはExprだが、これはOperatorになる

コロンがありかつ次が改行でなければ型を指定した形(`var: Type`, `var :> Type`, `var <: Type`)になる

```rust
                Some(op)
                    if (op.is(Colon) && !self.nth_is(1, Newline))
                        || (op.is(SubtypeOf) || op.is(SupertypeOf)) =>
                {
                    // "a": 1 (key-value pair)
                    if in_brace {
                        while stack.len() >= 3 {
                            collect_last_binop_on_stack(&mut stack);
                        }
                        break;
                    }
                    let op = self.lpop(); // colon
                    let lhs = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_)));
                    let t_spec = self // typeを求める
                        .try_reduce_expr(false, false, false, false)
                            .map_err(|_| self.stack_dec(fn_name!()))?,
                    // ただの式から型指定に変換する
                    let t_spec = Self::expr_to_type_spec(t_spec).map_err(|e| self.errs.push(e))?;
                    // 最終的に既にスタックされていた要素の型指定の形に変換する
                    let expr = lhs.type_asc_expr(op, t_spec);
                    stack.push(ExprOrOp::Expr(expr));
                }
```

### 二項演算子

これもOperator

これは+-とかの二項演算子のパターン

掛け算や割り算などの優先順位を数値で受け取って評価をする

その上で今のBinOpの順番を決める

そのために、スタックの中身を取り出して変換する作業を別でやっている

```rust
                Some(op) if op.category_is(TC::BinOp) => {
                    // 演算子を優先順位を計れるようにusizeに変換
                    let op_prec = op.kind.precedence();
                    if stack.len() >= 2 {
                        while let Some(ExprOrOp::Op(prev_op)) = stack.get(stack.len() - 2) { // "a * b + 1"とかの*を見る時
                            // 前の演算子の方が優先度が高ければ、そちらを優先するように変換する
                            if prev_op.category_is(TC::BinOp)
                                && prev_op.kind.precedence() >= op_prec 
                            {
                                // ここでstackの中身が消費される
                                collect_last_binop_on_stack(&mut stack);
                            } else {
                                break;
                            }
                            if stack.len() <= 1 {
                                break;
                            }
                        }
                    }
                    stack.push(ExprOrOp::Op(self.lpop())); // 今の演算子
                    stack.push(ExprOrOp::Expr(
                        self.try_reduce_bin_lhs(false, in_brace) // 右辺を求めてスタック
                            .map_err(|_| self.stack_dec(fn_name!()))?,
                    ));
                }
```


### 可視性プライベート

- 明示的なプライベート変数
- クラスのプライベート属性
- データパック

私が間違えているかもしれないのでここは注意

可視性パブリックのセクションで`method`を使っているが，ここで使われていないのはミスかもしれない

```rust
                Some(t) if t.is(DblColon) => {
                    let vis = self.lpop(); // ::
                    match self.lpop() {
                        symbol if symbol.is(Symbol) => {
                            // objが取り出せなかったらそもそも定義できないので構文エラーになる
                            let Some(ExprOrOp::Expr(obj)) = stack.pop() else {
                                let err = self.skip_and_throw_syntax_err(caused_by!());
                                self.errs.push(err);
                                debug_exit_info!(self);
                                return Err(());
                            };
                            // 引数のあるなしでアトリビュートかメソッドなどの呼び出しかを判断している
                            if let Some(args) = self
                                .opt_reduce_args(false) 
                                .transpose()
                                .map_err(|_| self.stack_dec())?
                            {
                                let ident = Identifier::new(None, VarName::new(symbol));
                                let call = Call::new(obj, Some(ident), args);
                                stack.push(ExprOrOp::Expr(Expr::Call(call)));
                            } else {
                                let ident = Identifier::new(None, VarName::new(symbol));
                                stack.push(ExprOrOp::Expr(obj.attr_expr(ident)));
                            }
                        }
                        // ClassName::\n
                        // クラスのプライベート属性の宣言
                        line_break if line_break.is(Newline) => {
                            // maybeなのは無効なシンボルの可能性があるから(組み込み定数や小文字ならだめなど)
                            let maybe_class = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_)));
                            // クラス属性を定義している、methodとあるが属性の定義もある
                            let defs = self
                                .try_reduce_method_defs(maybe_class, vis)
                                .map_err(|_| self.stack_dec(fn_name!()))?;
                            let expr = Expr::Methods(defs);
                            assert_eq!(stack.len(), 0); // どうしてassert_eqしているか分かっていない
                            debug_exit_info!(self);
                            return Ok(expr);
                        }
                        // DataPack::{ここの中身}
                        l_brace if l_brace.is(LBrace) => {
                            let maybe_class = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_)));
                            self.restore(l_brace);
                            // これでレコードが来ないといけない
                            let container = self
                                .try_reduce_brace_container()
                                .map_err(|_| self.stack_dec(fn_name!()))?;
                            match container {
                                BraceContainer::Record(args) => { // ここでレコードを確定させる
                                    let pack = DataPack::new(maybe_class, vis, args);
                                    stack.push(ExprOrOp::Expr(Expr::DataPack(pack)));
                                }
                                other => { // 辞書型や集合型なら中身が違うのでエラー
                                    let err = ParseError::invalid_data_class_container(
                                        line!() as usize,
                                        other.loc(),
                                        &other.to_string(),
                                    );
                                    self.errs.push(err);
                                    debug_exit_info!(self);
                                    return Err(());
                                }
                            }
                        }
                        other => {
                            self.restore(other);
                            let err = self.skip_and_throw_syntax_err(caused_by!());
                            self.errs.push(err);
                            debug_exit_info!(self);
                            return Err(());
                        }
                    }
                }
```

### 可視性パブリック

これもプライベートと似たようなことをしている

― パブリック変数
- パブリッククラス属性

ただ，DetaPack式はない

```rust
                Some(t) if t.is(Dot) => {
                    let vis = self.lpop(); // .が取り出される
                    match self.lpop() {
                        // 可視性がパブリックの変数になる
                        symbol if symbol.is(Symbol) => {
                            let Some(ExprOrOp::Expr(obj)) = stack.pop() else {
                                let err = self.skip_and_throw_syntax_err(caused_by!());
                                self.errs.push(err);
                                debug_exit_info!(self);
                                return Err(());
                            };
                            if let Some(args) = self
                                .opt_reduce_args(false) // 引数があれば関数，なければ変数
                                .transpose() // Ok()とSome()をひっくり返す
                                .map_err(|_| self.stack_dec())?
                            {
                                let ident = Identifier::new(Some(vis), VarName::new(symbol)); // ここでパブリックを指定している
                                let call = Expr::Call(Call::new(obj, Some(ident), args));
                                stack.push(ExprOrOp::Expr(call));
                            } else {
                                let ident = Identifier::new(Some(vis), VarName::new(symbol));
                                stack.push(ExprOrOp::Expr(obj.attr_expr(ident)));
                            }
                        }
                        // クラスのパブリック属性が定義される
                        line_break if line_break.is(Newline) => {
                            // maybeなのは無効なシンボルの可能性があるから(組み込み定数や小文字ならだめなど)
                            let maybe_class = enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_)));
                            let defs = self
                                .try_reduce_method_defs(maybe_class, vis)
                            .map_err(|_| self.stack_dec(fn_name!()))?;
                            return Ok(Expr::Methods(defs));
                        }
                        other => {
                            self.restore(other);
                            let err = self.skip_and_throw_syntax_err(caused_by!());
                            self.errs.push(err);
                            debug_exit_info!(self);
                            return Err(());
                        }
                    }
                }
```

### 配列の添え字アクセス

配列は式なのでchunkで呼ばれることはないので，`[index]`の添え字アクセスになる

```rust
                Some(t) if t.is(LSqBr) => {
                    let Some(ExprOrOp::Expr(obj)) = stack.pop() else {
                        let err = self.skip_and_throw_syntax_err(caused_by!());
                        self.errs.push(err);
                        debug_exit_info!(self);
                        return Err(());
                    };
                    self.skip(); // [がスキップされる
                    let index = self
                        .try_reduce_expr(false, false, in_brace, false)
                        .map_err(|_| self.stack_dec())?;
                    let r_sqbr = self.lpop();
                    // ]ではなければ括弧で閉じられていないか別の式か何かが来ているのでエラー
                    if !r_sqbr.is(RSqBr) {
                        self.restore(r_sqbr);
                        let err = self.skip_and_throw_syntax_err(caused_by!());
                        self.errs.push(err);
                        debug_exit_info!(self);
                        return Err(());
                    }
                    let acc = Accessor::subscr(obj, index, r_sqbr);
                    stack.push(ExprOrOp::Expr(Expr::Accessor(acc)));
                }
```

### 括弧なし引数

Tuple型で括弧なしで引数宣言するとき

```python
print! 1, True, "sample"
```

windingが基本的にはfalseになっているが，moduleとblockの時にはtrueになる

```rust
                // elem, ←ここから始まっている
                Some(t) if t.is(Comma) && winding => {
                    // カンマが来るということは初めの要素が解析されたことが前提となる
                    // なので、式が来ることが期待されている
                    let first_elem = PosOrKwArg::Pos(PosArg::new(
                        enum_unwrap!(stack.pop(), Some:(ExprOrOp::Expr:(_))),
                    ));
                    let tup = self
                        .try_reduce_nonempty_tuple(first_elem, false) // 最初の要素が必ずあるので，noneempty_setが呼ばれる
                        .map_err(|_| self.stack_dec())?;
                    stack.push(ExprOrOp::Expr(Expr::Tuple(tup)));
                }
```

### デフォルト引数

仮引数のデフォルト代入の構文

引数は全てTuple型なので最終的にTupleに変換されている

```rust
                Some(t) if t.is(Walrus) && winding => {
                    let tuple = self
                        .try_reduce_default_parameters(&mut stack, in_brace)
                        .map_err(|_| self.stack_dec())?;
                    stack.push(ExprOrOp::Expr(Expr::Tuple(tuple)));
                }
```

### 予約(~~反転~~)

~~^と&のビット演算子だと思うが定かではない~~
~~正直一番ここがわからない~~
~~恐らく，これら演算子が来た時点でchunkにならないから構文エラーなのだと思う~~

[edit(2023/02/10)]
予約しているトークンのことだった

```rust
                Some(t) if t.category_is(TC::Reserved) => {
                    let err = self.skip_and_throw_syntax_err(caused_by!());
                    self.errs.push(err);
                    debug_exit_info!(self);
                    return Err(());
                }
```

### プレースホルダ―

一つの塊になったら終了で，スタックされている数が多ければそもそも塊になっていないはずなので塊に直すことをしている

ここで塊になるまでループが続く

コメントアウトされている2個の場合が気になるがどうなのだろうか

```rust
                _ => {
                    if stack.len() <= 1 {
                        break;
                    }
                    // else if stack.len() == 2 { switch_unreachable!() }
                    else {
                        while stack.len() >= 3 {
                            collect_last_binop_on_stack(&mut stack);
                        }
                    }
                }
```


### chunkの終了

塊が一つだけのはずなのでそれを取り出している

```rust
        match stack.pop() {
            Some(ExprOrOp::Expr(expr)) if stack.is_empty() => {
                debug_exit_info!(self);
                Ok(expr)
            }
            Some(ExprOrOp::Expr(expr)) => {
                let extra = stack.pop().unwrap();
                let loc = match extra {
                    ExprOrOp::Expr(expr) => expr.loc(),
                    ExprOrOp::Op(op) => op.loc(),
                };
                // 余分なのはwarning行き
                self.warns
                    .push(ParseError::compiler_bug(0, loc, fn_name!(), line!()));
                debug_exit_info!(self);
                Ok(expr)
            }
            // オペレータだけの場合はエラー、必ず二項演算になっているはず
            Some(ExprOrOp::Op(op)) => {
                self.errs
                    .push(ParseError::compiler_bug(0, op.loc(), fn_name!(), line!()));
                debug_exit_info!(self);
                Err(())
            }
            _ => switch_unreachable!(),
        }
```

## try_reduce_bin_lhs()

exprやchunkの塊の元になる要素に還元する

try_reduce_chunkやtry_reduce_exprでかならず初めにこれが呼ばれる

これを解析して初めの要素が何かで次の解析に移る

分かりやすいのが、Literalになると思う

数値や文字列といったのがそのまま`IntLit`とか`StrLit`にここで変換される

```
>>> -1
(1) entered try_reduce_module, cur: IntLit -1
 (2) entered try_reduce_chunk, cur: IntLit -1
 (3) entered try_reduce_bin_lhs, cur: IntLit -1 <- ここ
･ (4) entered try_reduce_lit, cur: IntLit -1
>>> "sample"
 (1) entered try_reduce_module, cur: StrLit "sample"
 (2) entered try_reduce_chunk, cur: StrLit "sample"
 (3) entered try_reduce_bin_lhs, cur: StrLit "sample" <- ここ
･ (4) entered try_reduce_lit, cur: StrLit "sample"
```

moduleからchunkへ行ってそこからlhs_binからのlitの流れになっている

このように小さなユニットに還元する

もう少し見るとこうなる

```
>>> a = 1
 (1) entered try_reduce_module, cur: Symbol a
 (2) entered try_reduce_chunk, cur: Symbol a
 (3) entered try_reduce_bin_lhs, cur: Symbol a // <- ここ
･ (4) entered try_reduce_call_or_acc, cur: Symbol a
･ (5) entered try_reduce_acc_lhs, cur: Symbol a
･ (4) exit try_reduce_acc_lhs, cur: Equal =
･ (5) entered try_reduce_acc_chain, cur: Equal =
･ (4) exit try_reduce_acc_chain, cur: Equal =
･ (5) entered opt_reduce_args, cur: Equal =
･ (4) exit try_reduce_call_or_acc, cur: Equal =
 (3) exit try_reduce_bin_lhs, cur: Equal =
･ (4) entered convert_rhs_to_sig, cur: NatLit 1
･ (5) entered convert_accessor_to_var_sig, cur: NatLit 1
･ (4) entered try_reduce_expr, cur: NatLit 1
･ (5) entered try_reduce_bin_lhs, cur: NatLit 1 // <- ここ
･ (6) entered try_reduce_lit, cur: NatLit 1
･ (5) exit try_reduce_lit, cur: NatLit 1
･ (4) exit try_reduce_bin_lhs, cur: EOF
 (3) exit try_reduce_expr, cur: EOF
 (2) exit try_reduce_chunk, cur: EOF
 (1) exit try_reduce_module, cur: EOF
```

`(3) try_reduce_bin_lhs, cur: Symbol a`のここから連続する要素を見ている感じ

`Symbol`があると`call_or_acc`と`acc_chain`が呼ばれている

その中で数値などがあれば，またこれが呼ばれる(後半の方)

このように`try_reduce_bin_lhs`が呼ばれるようになっている

実際の処理を見ていくが，ここでは最小の要素に直していく処理をしているので単純にメソッドが呼ばれてそれを返す処理が多い

気になるところがあればそれを取り上げるようにする

### Lambdaの糖衣構文

Lambdaは`->`と`=>`だが，これらの糖衣構文として`do`と`do!`がある

```rust
            Some(t) if &t.inspect()[..] == "do" || &t.inspect()[..] == "do!" => {
                let lambda = self.try_reduce_do_block().map_err(|_| self.stack_dec())?;
                debug_exit_info!(self);
                Ok(Expr::Lambda(lambda))
            }
```

これらは`Symbol`の中にある文字列なので、Rustでの文字列として読み取るために`inspect()[..]`をしている

`[..]`がされている理由は、`token`の文字列は以下のように定義されているので，`Str`から&strとして取り出すためにこうなっている

```rust
pub type RcStr = std::rc::Rc<str>;
#[derive(Debug, Clone, Eq)]
pub enum Str {
    Rc(RcStr),
    Static(&'static str),
}
```

### デコレータ

pythonと同じでデコレータは`@`が使われる

複数の場合があるのでdecoratorsとなっている

```rust
            Some(t) if t.is(AtSign) => {
                // @以降のパースは別で処理をする
                let decos = self.opt_reduce_decorators()?;
                // デコレートされているクラスや関数の宣言を確認して違う式ならエラーとしている
                let expr = self.try_reduce_chunk(false, in_brace)?;
                let Some(mut def) = option_enum_unwrap!(expr, Expr::Def) else {
                    let err = self.skip_and_throw_syntax_err(caused_by!());
                    self.errs.push(err);
                    debug_exit_info!(self);
                    return Err(());
                };
                // var = something
                // func(parms) = something
                // どちらかになっているはずなのでそれを判断している
                match def.sig {
                    // 関数(サブルーチン)
                    Signature::Subr(mut subr) => {
                        subr.decorators = decos;
                        let expr = Expr::Def(Def::new(Signature::Subr(subr), def.body));
                        debug_exit_info!(self);
                        Ok(expr)
                    }
                    // 変数
                    Signature::Var(var) => {
                        let mut last = def.body.block.pop().unwrap();
                        for deco in decos.into_iter() {
                            last = deco.into_expr().call_expr(Args::new(
                                vec![PosArg::new(last)],
                                vec![],
                                None,
                            ));
                        }
                        def.body.block.push(last);
                        let expr = Expr::Def(Def::new(Signature::Var(var), def.body));
                        debug_exit_info!(self);
                        Ok(expr)
                    }
                }
            }

```

### Tuple型

括弧が付いているTuple型

一行の場合と改行の場合の2通りある

```python
(1, True, "sample")
(
    1,
    True,
    "sample"
)
(
    1, True, "sample"
)
```

ここでは形がどうなっているかを判断して結果を返すだけ

中身はexprであれば何でも取れるのがTuple型なので，そのままtry_reduce_expr()で中身を処理している

```rust
            Some(t) if t.is(LParen) => {
                let lparen = self.lpop(); // (を取り出す
                while self.cur_is(Newline) {
                    self.skip();
                }
                // 改行で宣言されているかどうか
                let line_break = if self.cur_is(Indent) {
                    self.skip();
                    true
                } else {
                    false
                };
                // 空のTuple
                if self.cur_is(RParen) {
                    let rparen = self.lpop(); // )を取り出す
                    let args = Args::new(vec![], vec![], Some((lparen, rparen)));
                    let unit = Tuple::Normal(NormalTuple::new(args));
                    debug_exit_info!(self);
                    return Ok(Expr::Tuple(unit));
                }
                // 中身の処理をexprに任せる(引数のboolはexprの方で説明する)
                let mut expr = self
                    .try_reduce_expr(true, false, false, line_break)
                    .map_err(|_| self.stack_dec(fn_name!()))?;
                // 最後の要素までが，exprで取り出される
                // その後に空行あっても文法上は無視される
                while self.cur_is(Newline) {
                    self.skip();
                }
                // Tupleの宣言時に改行で宣言した場合にはIndentが付くので，それのDedent
                if self.cur_is(Dedent) {
                    self.skip();
                }
                let rparen = self.lpop(); // )を取り出す
                if let Expr::Tuple(Tuple::Normal(tup)) = &mut expr {
                    tup.elems.paren = Some((lparen, rparen));
                }
                debug_exit_info!(self);
                Ok(expr)
            }
```

### 辞書型、レコード型、集合型

ここで`{`が来た時に変換をしている

中身の処理は`try_reduce_brace_container()`にまかせて、返って式をそれぞれでマッチングをかけている

```rust
            Some(t) if t.is(LBrace) => {
                match self
                    .try_reduce_brace_container()
                    .map_err(|_| self.stack_dec(fn_name!()))?
                {
                    BraceContainer::Dict(dic) => {
                        debug_exit_info!(self);
                        Ok(Expr::Dict(dic))
                    }
                    BraceContainer::Record(rec) => {
                        debug_exit_info!(self);
                        Ok(Expr::Record(rec))
                    }
                    BraceContainer::Set(set) => {
                        debug_exit_info!(self);
                        Ok(Expr::Set(set))
                    }
                }
            }
```

### 多相関数型?

恐らく[多相関数型](https://github.com/erg-lang/erg/blob/main/doc/JA/syntax/type/15_quantified.md)?のパース

```python
f: |T: Type| T -> T
f: |Y, Z: Type, X <: Add(Y, O1), O1 <: Add(Z, O2), O2 <: Add(X, O3)| (X, Y, Z) -> O3
```

`|~|`中身を`try_reduce_type_app_args()`でパースする

そして，残りの`T->T`だったり`(X, Y, Z) -> O3`をLambdaシグネチャに変換していると思われる

```rust
            Some(t) if t.is(VBar) => {
                // |type|これを判断しているはず
                let type_args = self
                    .try_reduce_type_app_args()
                    .map_err(|_| self.stack_dec())?;
                let bounds = self
                    .convert_type_args_to_bounds(type_args)
                    .map_err(|_| self.stack_dec())?;
                let args = self.try_reduce_args(false).map_err(|_| self.stack_dec())?;
                // |の後の引数のパース
                let params = self
                    .convert_args_to_params(args)
                    .map_err(|_| self.stack_dec())?;
                if !self.cur_category_is(TC::LambdaOp) {
                    let err = self.skip_and_throw_syntax_err(caused_by!());
                    self.errs.push(err);
                    debug_exit_info!(self);
                    return Err(());
                }
                // ここでシグネチャをlmabdaに変換する
                let sig = LambdaSignature::new(params, None, bounds);
                let op = self.lpop(); // ->をスキップ
                // lambdaはブロックを持てるので，そのままblockの解析をする
                let block = self.try_reduce_block().map_err(|_| self.stack_dec())?;
                self.counter.inc();
                debug_exit_info!(self);
                let lambda = Lambda::new(sig, op, block, self.counter);
                Ok(Expr::Lambda(lambda))
            }
```

## try_reduce_expr

Ergで使われる式(experesion)をパースするための起点となるメソッド

- windingは丸括弧なしのTuple

Tuple型は中身にそれぞれ異なる式を宣言することができるので、専用の引数を持っている

`a = True, a => a, "sample", !a => print! a, (a, b, c)`みたいなこともできてしまう

- in_type_argsは型指定付き

これも基本的にはTuple型で関数などの仮引数を定義するときの条件

`func(x: Int, y: Int) = somthing`みたいな引数と型を指定するときのTuple型の中身を処理するとき

- in_braceは`{}`の中身

辞書型時は`key: value`それぞれが式を取ることができる

ただし、これは`{}`で囲まれているときだけなので、それを判断するため

- line_brakは改行で式を宣言しているかどうか

詳細はTuple型の方で説明をしている

```rust
    fn try_reduce_expr(
        &mut self,
        winding: bool,
        in_type_args: bool,
        in_brace: bool,
        line_break: bool,
    ) -> ParseResult<Expr> {
```

式は以下の通り

```rust
pub enum Expr {
    Lit(Literal), // 1, "sapmle", True, 1e-1
    Accessor(Accessor), // [1, 2, 3][i], (1, 2, 3).i, .func
    Array(Array), // [1, 2, 3], ["True", "False", "Even"], [Nat; 10]
    Tuple(Tuple), // (1, True, 1.0)
    Dict(Dict), // {"a": 1, "b": 2, "c":3}
    Set(Set), // {1, 2, 3}, {1; 10}
    Record(Record), // {x; y}, {x = 1; y}, {x = 1; y = 1}
    BinOp(BinOp), // +, -, *, //
    UnaryOp(UnaryOp), // +, -, *, /, //, %
    Call(Call), // func, proc!
    DataPack(DataPack), // datapack::{recordのどれか}
    Lambda(Lambda), // do, do!, ->, =>
    TypeAsc(TypeAscription), // var: type, var :> type, var <: typeだと思う
    Def(Def), // 基本的な=の式
    Methods(Methods), // .method(parm) = somehting
    ClassDef(ClassDef), // Class., Class::
    PatchDef(PatchDef), // Patch(type)
    ReDef(ReDef), // 
    /// for mapping to Python AST
    Dummy(Dummy),
}
```

`try_reduce_chunk`と`try_reduce_bin_lhs`と被るので省略

## try_reduce_call_or_acc

`try_reduce_bin_lhs()`に`Symbol`，`Dot(.)`，`UBar(_)`が来た時に必ず呼ばれる重要なメソッド

ここで`acc_lhs`と`acc_chain`で`token(この場合は基本的にSymbol)`がどのように連続しているかを見ている

```
a
a 1
a 1, True, "sample"
a.0
a[0]
a.attr
a.method(args)
# これらにの前に.が付いたバージョン
.a
.a 1
.a 1, True, "sample"
.a.0
.a[0]
.a.attr
.a.method(args)
# クラス
C.
    attr = 1
    method(parms) = something
```

```
>>> a
  (1) entered try_reduce_module, cur: Symbol a
   (2) entered try_reduce_chunk, cur: Symbol a
    (3) entered try_reduce_bin_lhs, cur: Symbol a
     (4) entered try_reduce_call_or_acc, cur: Symbol a <- ここ
      (5) entered try_reduce_acc_lhs, cur: Symbol a
      (5) entered try_reduce_acc_chain, cur: EOF
      (5) entered opt_reduce_args, cur: EOF
```

```
>>> a 1
  (1) entered try_reduce_module, cur: Symbol a
   (2) entered try_reduce_chunk, cur: Symbol a
    (3) entered try_reduce_bin_lhs, cur: Symbol a
     (4) entered try_reduce_call_or_acc, cur: Symbol a <- ここ
      (5) entered try_reduce_acc_lhs, cur: Symbol a
      (5) entered try_reduce_acc_chain, cur: NatLit 1
      (5) entered opt_reduce_args, cur: NatLit 1
       (6) entered try_reduce_args, cur: NatLit 1
        (7) entered try_reduce_arg, cur: NatLit 1
         (8) entered try_reduce_expr, cur: NatLit 1
          (9) entered try_reduce_bin_lhs, cur: NatLit 1
           (10) entered try_reduce_lit, cur: NatLit 1
       (6) entered opt_reduce_args, cur: EOF
```

関数の宣言か関数などへのアクセスかどうかを見ている

```rust
    fn try_reduce_call_or_acc(&mut self, in_type_args: bool) -> ParseResult<Expr> {
        debug_call_info!(self);
        let acc = self
            .try_reduce_acc_lhs()
            .map_err(|_| self.stack_dec(fn_name!()))?;
        let mut call_or_acc = self.try_reduce_acc_chain(acc, in_type_args)?;
        while let Some(res) = self.opt_reduce_args(in_type_args) {
            let args = res.map_err(|_| self.stack_dec(fn_name!()))?;
            let (receiver, attr_name) = match call_or_acc {
                Expr::Accessor(Accessor::Attr(attr)) => (*attr.obj, Some(attr.ident)),
                other => (other, None),
            };
            let call = Call::new(receiver, attr_name, args);
            call_or_acc = Expr::Call(call);
        }
        debug_exit_info!(self);
        Ok(call_or_acc)
    }
```

## try_reuduce_acc_lhs

単純にパブリック・プライベート変数の確認をしているだけ

```python
# private(local)
a = 1
f()
p!()
# public
.a = 1
.f()
.p!()
```

```rust
    fn try_reduce_acc_lhs(&mut self) -> ParseResult<Accessor> {
        debug_call_info!(self);
        let acc = match self.peek_kind() {
            Some(Symbol | UBar) => Accessor::local(self.lpop()), // 非公開変数化
            Some(Dot) => {
                let dot = self.lpop();
                let maybe_symbol = self.lpop();
                if maybe_symbol.is(Symbol) {
                    Accessor::public(dot, maybe_symbol) // 公開変数化
                } else {
                    let err = self.skip_and_throw_syntax_err(caused_by!());
                    self.errs.push(err);
                    debug_exit_info!(self);
                    return Err(());
                }
            }
            _ => {  // これが来たらParserのバグ
                let err = self.skip_and_throw_syntax_err(caused_by!());
                self.errs.push(err);
                debug_exit_info!(self);
                return Err(());
            }
        };
        debug_exit_info!(self);
        Ok(acc)
    }
```

## try_reduce_acc_chain

先ほどの`acc_lhs`で公開・非公開変数を求めて，ここでその次の`token`を見て関数呼び出しかアクセッサ―かの確認している

長いので分割してこれも説明をしていく

### 配列の添え字アクセス

一般的な配列に対する添え字のアクセス

今のところ，一要素のアクセスしかできないが将来的には複数のアクセスが可能になるはず

```python
a = [1, 2, 3, 4, 5]
one = a[0]
slice = a[2..3]
one_two = a[1, 2]
```

```rust
                Some(t) if t.is(LSqBr) && obj.col_end() == t.col_begin() => {
                    let _l_sqbr = self.lpop();
                    // これでスライスや添え字が取り出される
                    // bin_lhsやlitじゃないのは恐らく上記のスライスとかに対応するためだと思う
                    let index = self
                        .try_reduce_expr(true, false, false, false)
                        .map_err(|_| self.stack_dec(fn_name!()))?;
                    let r_sqbr = if self.cur_is(RSqBr) {
                        self.lpop()
                    } else {
                        // ]が無ければ，閉じられていないからエラー
                        let err = self.skip_and_throw_syntax_err(caused_by!());
                        self.errs.push(err);
                        debug_exit_info!(self);
                        return Err(());
                    };
                    obj = Expr::Accessor(Accessor::subscr(obj, index, r_sqbr));
                }
```

### 配列の中身の添え字アクセス

多分こういうのだと思う

```python
a = [(1, 2), (3, 4)]
three = a[1].0
```

.0とかの数字は`Ratio`の整数省略だと解釈されてしまう

なのでここではRatioLitで明示的にアクセスするように直している

```rust
                Some(t) if t.is(RatioLit) && obj.col_end() == t.col_begin() => {
                    let mut token = self.lpop();
                    token.content = Str::rc(&token.content[1..]); // 小数点を切り離してる
                    token.kind = NatLit; // 添え字に直す
                    token.col_begin += 1;
                    obj = obj.tuple_attr_expr(Literal::from(token));
                }
```

### 可視性パブリックへのアクセス

obj.col_end()とt.col_begin()が呼ばれているのは改行でのアクセスかどうかを見るためだと思う

```python
a.0
a.attr
C. # クラス定義
```

これも今現在は一行だけになっているが、複数行でできるようにする予定

```python
(0..10)
    .map x -> x * 3
    .filter x -> x % 2 == 0 #[6, 12, 18, 24, 30]
```

```rust
                Some(t) if t.is(Dot) && obj.col_end() == t.col_begin() => {
                    let vis = self.lpop();
                    match self.peek() {
                        // ojb.attrもしくはobj.method
                        Some(t) if t.is(Symbol) => {
                            let ident = Identifier::new(Some(vis), VarName::new(self.lpop()));
                            obj = obj.attr_expr(ident);
                        }
                        //  tuple.iの添え字アクセス
                        Some(t) if t.is(NatLit) => {
                            let index = Literal::from(self.lpop());
                            obj = obj.tuple_attr_expr(index);
                        }
                        // クラスの定義なのでやり直す
                        Some(t) if t.is(Newline) => {
                            self.restore(vis);
                            break;
                        }
                        // それ以外は無効な構文なのでエラー
                        Some(other) => {
                            let err = ParseError::invalid_acc_chain(
                                line!() as usize,
                                other.loc(),
                                &other.inspect()[..],
                            );
                            self.errs.push(err);
                            debug_exit_info!(self);
                            return Err(());
                        }
                        // これが来たらParserのバグ
                        None => {
                            self.errs.push(self.unexpected_none(line!(), caused_by!()));
                            debug_exit_info!(self);
                            return Err(());
                        }
                    }
                }
```

### 可視性プライベートへのアクセス

```rust
                Some(t) if t.is(DblColon) && obj.col_end() == t.col_begin() => {
                    let vis = self.lpop();
                    let token = self.lpop();
                    match token.kind {
                        Symbol => { // private(local)属性アクセス
                            let ident = Identifier::new(None, VarName::new(token));
                            obj = obj.attr_expr(ident);
                        }
                        LBrace => { // 
                            self.restore(token);
                            let args = self
                                .try_reduce_brace_container()
                                .map_err(|_| self.stack_dec(fn_name!()))?;
                            match args {
                                BraceContainer::Record(args) => {
                                    obj = Expr::DataPack(DataPack::new(obj, vis, args));
                                }
                                other => {
                                    let err = ParseError::simple_syntax_error(
                                        line!() as usize,
                                        other.loc(),
                                    );
                                    self.errs.push(err);
                                    debug_exit_info!(self);
                                    return Err(());
                                }
                            }
                        }
                        // MethodDefs
                        Newline => {
                            self.restore(token);
                            self.restore(vis);
                            break;
                        }
                        _ => {
                            self.restore(token);
                            let err = self.skip_and_throw_syntax_err(caused_by!());
                            self.errs.push(err);
                            debug_exit_info!(self);
                            return Err(());
                        }
                    }
                }
```

### 引数

ここは一度ループが来たことが前提となっている

`ojb.attr`で属性があり，それが`ojb.attr(args)`となっていたら引数を求めてメソッドの呼び出しに変換する

`obj.attr`→`obj.method(args)`

```rust
                Some(t) if t.is(LParen) && obj.col_end() == t.col_begin() => {
                    let args = self
                        .try_reduce_args(false)
                        .map_err(|_| self.stack_dec(fn_name!()))?;
                    let (receiver, attr_name) = match obj {
                        // 引数があるということは直前のobjに属性があり
                        // その属性にも識別子があるはずなのでそれを取り出す
                        Expr::Accessor(Accessor::Attr(attr)) => (*attr.obj, Some(attr.ident)),
                        other => (other, None),
                    };
                    // メソッド呼び出しに変換
                    let call = Call::new(receiver, attr_name, args);
                    obj = Expr::Call(call);
                }
```

### 全称量化?

バーティカルバーはまだ正直わかっていないのでこれも推測になる

多分，[全称量化](https://github.com/erg-lang/erg/blob/main/doc/JA/syntax/type/15_quantified.md)のパースだと思っている

```python
add|T <: Add| l: T, r: T = l + r
```

```rust
                Some(t) if t.is(VBar) && !in_type_args => {
                    let type_args = self
                        .try_reduce_type_app_args()
                        .map_err(|_| self.stack_dec(fn_name!()))?;
                    obj = Expr::Accessor(Accessor::TypeApp(TypeApp::new(obj, type_args)));
                }
```

後はloopをbreakして今までの内容を適用させたobjを返している


## try_reduce_args

引数全般を解析するためのメソッド

Ergは数値だけではなく、関数も簡単に引数にすることができる

また，引数は一行で書くこともできるが，改行を使って書くこともできる

更に，実引数(argument)と仮引数(parameter)はパースする段階ではどちらかを判断できないため，同じ処理をすることになっていしまう

なので、すごく複雑な処理をしている

```python
# argment
x = if True, 1, 2 # SingleCommaNoParen
x = if True: # Colon
    1
    2
# parameter
f(x, y, z) = something # SingleCommaWithParen
f x, y, z = something # SingleCommaNoParen
f( # MultiComma
    x,
    y,
    z
)
```

これも長いので区切りながら見ていく

### 初めの処理

引数は必ずタプル型になる

しかし，タプル型は丸括弧を省略することができたり，改行で宣言できたりする(上記の例)

そのための`style`を`Enum`で分けている

```rust
        let mut lp = None; // left parenthese
        let rp; // right parentheses
        if self.cur_is(LParen) {
            lp = Some(self.lpop());
        }
        // とりあえず一行で，丸括弧があるかどうかを見ている
        // 後のループの処理で複数行になるかを決めている
        let mut style = if lp.is_some() {
            ArgsStyle::SingleCommaWithParen
        } else {
            ArgsStyle::SingleCommaNoParen
        };
        match self.peek_kind() {
            // 引数無し
            Some(RParen) => {
                rp = Some(self.lpop());
                debug_exit_info!(self);
                return Ok(Args::new(vec![], vec![], Some((lp.unwrap(), rp.unwrap()))));
            }
            // 正直ここはわかっていない(空になるのはわかる)
            // 配列やセット，辞書，レコード型は別で処理されているはずなので，
            // Dedent以外は必要ない気がする
            Some(RBrace | RSqBr | Dedent) => {
                debug_exit_info!(self);
                return Ok(Args::new(vec![], vec![], None));
            }
            // 丸括弧ありで改行を使った宣言
            Some(Newline) if style.needs_parens() => {
                self.skip();
                if self.cur_is(Indent) {
                    self.skip();
                }
                style = ArgsStyle::MultiComma;
            }
            _ => {}
        }
```

### 最初の引数

基本的には`try_reduce_arg`を使って個々の引数をパースをしてく

ただ、初めの引数の処理をしてコロンスタイルの場合は改行が次に来るが，それ以外は一行での処理になるはず

そのために初めの引数を見ている

```rust
        let mut args = match self
            .try_reduce_arg(in_type_args)
            .map_err(|_| self.stack_dec(fn_name!()))?
        {
            PosOrKwArg::Pos(arg) => Args::new(vec![arg], vec![], None),
            PosOrKwArg::Kw(arg) => Args::new(vec![], vec![arg], None),
        };
```

以降はループをして、最後の引数が来るまでこの`args`に引数を`push`している

### 実引数のエラー

これは最初のループでは来ることがない

二回目以降に来る可能性があり，その上でコロンスタイルだったり丸括弧が既にある場合にはエラーになる

```python
if (True: 1, 2) # コロンがあるときには改行がされていたりするはず
if True: # 一回目のループでこれが処理される
    :1 # 上記のコロンがあるときにこういうコロンが来ることがない
    do: # これはできるが，doが必ずargで処理されるはず
        log 2
```

```rust
                Some(Colon) if style.is_colon() || lp.is_some() => {
                    self.skip();
                    let err = self.skip_and_throw_syntax_err(line!(), caused_by!());
                    self.errs.push(err);
                    debug_exit_info!(self);
                    return Err(());
                }
```

### コロン改行スタイル

以下のように改行がきたらブロックが期待される

更にその中でも式が来たり，様々な要素が来る可能性がある

それら要素は再度ループを回して`arg`に任せる

```python
if True:


    do:
        log 1



    do:
        log 2
```

```rust
                Some(Colon) => {
                    self.skip(); // :をスキップ
                    style = ArgsStyle::Colon;
                    // 改行のみはスキップしている(上記の例)
                    while self.cur_is(Newline) {
                        self.skip();
                    }
                    // 改行が来たら次はブロックが期待されるのでインデントが必要になる
                    if !self.cur_is(Indent) {
                        let caused_by = caused_by!();
                        log!(err "error caused by: {caused_by}");
                        let err = ParseError::no_indention(
                            line!() as usize,
                            self.lpop().loc(),
                            switch_lang!(
                                "japanese" => "実引数",
                                "traditional_chinese" => "参数",
                                "simplified_chinese" => "参数",
                                "english" => "argument",
                            ),
                        );
                        self.errs.push(err);
                        let caused_by = caused_by!();
                        log!(err "error caused by: {caused_by}");
                        self.next_expr();
                        debug_exit_info!(self);
                        return Err(());
                    }
                    self.skip();
                }
```

### カンマスタイル

こんな感じ

```
f 1, 2, 3
f(1, 2, 3)
f(

    1,
    2,


    3
)
```

```rust
                Some(Comma) => {
                    self.skip(); // ,はスキップ
                    // コロンスタイルの時カンマは来ない
                    if style.is_colon() || self.cur_is(Comma) {
                        let err = self.skip_and_throw_syntax_err(line!(), caused_by!());
                        self.errs.push(err);
                        debug_exit_info!(self);
                        return Err(());
                    }
                    // 改行スタイルなら改行は無視
                    if style.is_multi_comma() {
                        while self.cur_is(Newline) {
                            self.skip();
                        }
                        if self.cur_is(Dedent) {
                            self.skip();
                        }
                    }
                    // 括弧で始まったときにはこれが必ずtrueになっているはず
                    // そのうえで右の括弧}が来た時
                    if style.needs_parens() && self.cur_is(RParen) {
                        let rp = self.lpop();
                        let (pos_args, kw_args, _) = args.deconstruct();
                        args = Args::new(pos_args, kw_args, Some((lp.unwrap(), rp)));
                        break;
                    }
                    if !args.kw_is_empty() {
                        args.push_kw(
                            self.try_reduce_kw_arg(in_type_args)
                                .map_err(|_| self.stack_dec(fn_name!()))?,
                        );
                    } else {
                        // 引数は必ずポジショナル引数はキーワード引数になる
                        match self
                            .try_reduce_arg(in_type_args)
                            .map_err(|_| self.stack_dec(fn_name!()))?
                        {
                            PosOrKwArg::Pos(arg) => {
                                args.push_pos(arg);
                            }
                            PosOrKwArg::Kw(arg) => {
                                args.push_kw(arg);
                            }
                        }
                    }
                }
```

### 引数パース終了

括弧があるときの引数の終了

```rust
                Some(RParen) => {
                    if let Some(lp) = lp {
                        let rp = self.lpop();
                        let (pos_args, kw_args, _) = args.deconstruct();
                        args = Args::new(pos_args, kw_args, Some((lp, rp)));
                    } else {
                        // 括弧内で括弧を省略した実引数を取るとき
                        // e.g. f(g 1)
                        let (pos_args, kw_args, _) = args.deconstruct();
                        args = Args::new(pos_args, kw_args, None);
                    }
                    break;
                }
```

### 改行スタイル

```
f(
    1,
    2
)
# or
if True:
    1
    2
```

```rust
                Some(Newline) => {
                    if !style.is_colon() {
                        if style.is_multi_comma() {
                            self.skip();
                            while self.cur_is(Dedent) {
                                self.skip();
                            }
                            let rp = self.lpop();
                            if !rp.is(RParen) {
                                let err = self.skip_and_throw_syntax_err(line!(), caused_by!());
                                self.errs.push(err);
                                debug_exit_info!(self);
                                return Err(());
                            }
                            let (pos_args, kw_args, _) = args.deconstruct();
                            args = Args::new(pos_args, kw_args, Some((lp.unwrap(), rp)));
                        }
                        break;
                    }
                    let last = self.lpop();
                    if self.cur_is(Dedent) {
                        self.skip();
                        self.restore(last);
                        break;
                    }
                }
```

### 一行引数のパース

一行の引数のパースがここで行われる

```python
# キーワード実引数が来たらその後ろの引数は全てキーワード引数にしないといけない
f(True, "sample", x:=1, y:=2, z:=3)
```

キーワードがあるときには必ず，その後の引数もキーワード引数にする必要がある

それ以外は単純なポジショナル引数になるが，途中からキーワード引数をとることもできる

```rust
                Some(_) if style.is_colon() => {
                    if !args.kw_is_empty() {
                        args.push_kw(
                            self.try_reduce_kw_arg(in_type_args)
                                .map_err(|_| self.stack_dec(fn_name!()))?,
                        );
                    } else {
                        match self
                            .try_reduce_arg(in_type_args)
                            .map_err(|_| self.stack_dec(fn_name!()))?
                        {
                            PosOrKwArg::Pos(arg) => {
                                args.push_pos(arg);
                            }
                            PosOrKwArg::Kw(arg) => {
                                args.push_kw(arg);
                            }
                        }
                    }
                }
```
## try_reduce_arg

`try_redcuce_args`から呼ばれて、一つの引数だけをパースする

### キーワード引数

パースする段階ではデフォルト仮引数とキーワード実引数は区別できない

```python
f(x:=1, y:=[2]) # キーワード実引数
f(x:=1, y:=[1, 2, 3], z:={name="John", age=21})=
    x
    y
    print! z # デフォルト引数
```

これらは右辺があるかどうかでしか判断が付けないので，ここではあくまでもキーワード引数として処理される

```rust
            Some(Symbol) => {
                // シンボルの次に:=が来た時
                if self.nth_is(1, Walrus) {
                    let acc = self
                        .try_reduce_acc_lhs()
                        .map_err(|_| self.stack_dec(fn_name!()))?;
                    debug_power_assert!(self.cur_is(Walrus));
                    self.skip();
                    // 識別子が来ないといけない
                    let kw = if let Accessor::Ident(n) = acc {
                        n.name.into_token()
                    } else {
                        let err = ParseError::simple_syntax_error(0, acc.loc());
                        self.errs.push(err);
                        self.next_expr();
                        debug_exit_info!(self);
                        return Err(());
                    };
                    // キーワードに代入する式の解析
                    let expr = self
                        .try_reduce_expr(false, in_type_args, false, false)
                        .map_err(|_| self.stack_dec(fn_name!()))?;
                    debug_exit_info!(self);
                    Ok(PosOrKwArg::Kw(KwArg::new(kw, None, expr)))
                } else {
```

### 関数が引数

シンボルが来て更に別のが来るのは関数を引数にするときなど

```python
f x = x
g y = y

f(g(x))
f g 1
f g y:=1
```

関数を取れるのでここで式としてパースする

内容は上記のキーワード引数と同じ

```rust
                    let expr = self
                        .try_reduce_expr(false, in_type_args, false, false)
                        .map_err(|_| self.stack_dec(fn_name!()))?;
                    if self.cur_is(Walrus) {
                        self.skip();
                        let (kw, t_spec) = match expr {
                            Expr::Accessor(Accessor::Ident(n)) => (n.name.into_token(), None),
                            Expr::TypeAsc(tasc) => {
                                if let Expr::Accessor(Accessor::Ident(n)) = *tasc.expr {
                                    let t_spec = TypeSpecWithOp::new(tasc.op, tasc.t_spec);
                                    (n.name.into_token(), Some(t_spec))
                                } else {
                                    let err = ParseError::simple_syntax_error(0, tasc.loc());
                                    self.errs.push(err);
                                    self.next_expr();
                                    debug_exit_info!(self);
                                    return Err(());
                                }
                            }
                            _ => {
                                let err = ParseError::simple_syntax_error(0, expr.loc());
                                self.errs.push(err);
                                self.next_expr();
                                debug_exit_info!(self);
                                return Err(());
                            }
                        };
                        let expr = self
                            .try_reduce_expr(false, in_type_args, false, false)
                            .map_err(|_| self.stack_dec(fn_name!()))?;
                        debug_exit_info!(self);
                        Ok(PosOrKwArg::Kw(KwArg::new(kw, t_spec, expr)))
                    } else {
                        debug_exit_info!(self);
                        Ok(PosOrKwArg::Pos(PosArg::new(expr)))
                    }
                }
            }
```

### ポジショナル引数

いわゆる一般的な引数のパース

引数には式が持ってこれるのでそれを解析し、`PosArg`として返す

```python
f(1)
f {x = 1; y =2}
```

```rust
            Some(_) => {
                let expr = self
                    .try_reduce_expr(false, in_type_args, false, false)
                    .map_err(|_| self.stack_dec(fn_name!()))?;
                debug_exit_info!(self);
                Ok(PosOrKwArg::Pos(PosArg::new(expr)))
            }
```

## 終わり

いくつかわからないところがあったので何とかわかるように努めたい

以下の内容について大雑把であるが説明をしてきた

- Lexer
    - lex_multi_line_comment()
- Parser
    - try_reduce_module()
    - try_reduce_chunk()
    - try_reduce_bin_lhs()
    - try_reduce_expr()
    - try_reduce_call_or_acc()
    - try_reduce_acc_lhs()
    - try_reduce_arg()
    - try_reduce_args()

今回はパースの幹になるところの説明を行ったのでこれをしっかりと理解できればdebugの情報を読み取れるようになると思う

後はこれらから呼び出されるtry_reudce系のメソッドをより詳しく見ていけばより詳しく理解できると思う

## パースでまだできていないところ

これを読んでちょっと興味が出た人向けの内容を軽く紹介する

しかし、多分初心者の人は難しいとは思うので質問をすることをお勧めする

### 配列の改行宣言

割と簡単だと思う

他のセット型やレコード型などなどでできているので、これらを参考にするとできると思う

`try_reduce_elems`のところ

```python
a = [1, # 現在のパーサーではエラー
2]
↓
a = [
2, 1,
3, 2,
4, 3,
]
```

### バックスラッシュによる途中改行

https://github.com/erg-lang/erg/issues/234

簡単だと思う

```python
# x + y + z
x \
+ y\
+ z
```

### 簡易乗算

https://github.com/erg-lang/erg/issues/235

これも簡単だと思う

```python
x = 1
assert 2x + 1 == 3 # 2*x + 1
assert 2(x+1) == 4 # 2*(x + 1)
assert 2.2x == 2.2
1m / 1s # (1*m)/(1*s)
```

### 改行によるメソッドチェーン

恐らくこれも簡単だと思う

Issueは作っていないがこれもできれば嬉しい機能なのでぜひ誰か

```python
# 途中改行(現状)
(0..10)\
    .map x -> x + 1\
    .filter x % 2 == 0\
# 改行
(0..10)
    .map x -> x + 1
    .filter x % 2 = 1
```

### 配列の連続アクセス

これもIssueが作られていないが一応[API]()にはあるので載せておく

```python
a = [1, 2, 3, 4]
a[1, 2] # 現在エラー
↓
a[1, 2] # [2, 3]
```

### 内包表記

難しいので、もしチャレンジしたい人はGithubかDiscord質問をした方が良い


```python
[expr | (name <- iter) + (predicate)] # セット型や辞書型も同様にできる
[i*2 | i <- [0, 1, 2]] # [0, 2, 4]
```

### 篩型

これはパースの実装の難しさは普通くらいだと思う

ただ、抽象木(ast)を作成する必要があるのでその分だけ難しいと思う

```python
{name: Type | predicate}
{I: Int | I >= 0}
```