Upgrade to Pro — share decks privately, control downloads, hide ads and more …

tree-sitter-rbsで作って学ぶRBSとパーサージェネレーター

 tree-sitter-rbsで作って学ぶRBSとパーサージェネレーター

RubyKaigi AfterイベントのLT

Tomohiro Hashidate

May 28, 2024
Tweet

More Decks by Tomohiro Hashidate

Other Decks in Technology

Transcript

  1. tree-sitterのDSLについて 数字の足し算が読める簡単なパーサーを書いてみる。 module.exports = grammar({ name: "sample", // spaceや改行を読み飛ばす extras:

    $ => [ /\s/, /\r?\n/ ], rules: { program: $ => repeat(choice($.hello, $.expr)), hello: $ => "hello", // この文字列の並びにのみマッチする number: $ => /\d+/, // 終端は正規表現でも書ける expr: $ => choice($.number, $.plus), // どちらかにマッチする plus: $ => prec.left(seq($.expr, "+", $.expr)), // (A + B) + Cなのか、A + (B + C)なのか } }); see. https://tree-sitter.github.io/tree-sitter/creating-parsers
  2. BNFだとこんな感じ 0 頭をどうするかとかは省略。 digit ::= "0" | "1" | "2"|

    "3"| "4"| "5"| "6"| "7"| "8"| "9" integer ::= <digit>+ expr ::= <integer> | <plus> plus ::= <expr> "+" <expr> tree-sitterのDSLだと終端定義を正規表現で楽できる。
  3. input: 123 hello 123 + 234 + 512 output: (program

    [0, 0] - [3, 0] (expr [0, 0] - [0, 3] (number [0, 0] - [0, 3])) (hello [1, 0] - [1, 5]) (expr [2, 0] - [2, 15] (plus [2, 0] - [2, 15] (expr [2, 0] - [2, 9] (plus [2, 0] - [2, 9] (expr [2, 0] - [2, 3] (number [2, 0] - [2, 3])) (expr [2, 6] - [2, 9] (number [2, 6] - [2, 9])))) (expr [2, 12] - [2, 15] (number [2, 12] - [2, 15])))))
  4. メソッド引数のパターン ドキュメントだとざっくりこう書かれている。 _parameters_ ::= `(` _required-positionals_ _optional-positionals_ _rest-positional_ _trailing-positionals_ _keywords_

    `)` しかし、これは順番がこうであるというだけで、それぞれ省略が効く。 ドキュメントはドキュメントであって実装ではない。
  5. DSLに展開すると…… parameters: $ => seq( "(", optional(choice( seq($.required_positionals), seq($.required_positionals, ",",

    choice($.optional_positionals, $.rest_positional, $.keywords)), seq($.required_positionals, ",", $.optional_positionals, ",", choice($.rest_positional, $.trailing_positionals, $.keywords)), seq($.required_positionals, ",", $.optional_positionals, ",", $.rest_positional, ",", choice($.trailing_positionals, $.keywords)), seq($.required_positionals, ",", $.optional_positionals, ",", $.rest_positional, ",", $.trailing_positionals, ",", $.keywords), seq($.required_positionals, ",", $.optional_positionals, ",", $.trailing_positionals, ",", optional($.keywords)), seq($.required_positionals, ",", $.rest_positional, ",", choice($.trailing_positionals, $.keywords)), seq($.required_positionals, ",", $.rest_positional, ",", $.trailing_positionals, ",", $.keywords), seq($.optional_positionals), seq($.optional_positionals, ",", choice($.rest_positional, $.trailing_positionals, $.keywords)), seq($.optional_positionals, ",", $.rest_positional, ",", choice($.trailing_positionals, $.keywords)), seq($.optional_positionals, ",", $.rest_positional, ",", $.trailing_positionals, ",", $.keywords), seq($.optional_positionals, ",", $.trailing_positionals, ",", $.keywords), seq($.rest_positional), seq($.rest_positional, ",", choice($.trailing_positionals, $.keywords)), seq($.rest_positional, ",", $.trailing_positionals, ",", $.keywords), seq($.keywords), )), ")" ), 本当に合ってんのか……?
  6. methodのoverloadとunion typeが衝突する def foo: () -> String | Integer |

    (Symbol) -> Symbol () が常にあるなら簡単なんだけど……。