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

nanopass-compiler-frameworkを使ってみました

Niyarin
March 25, 2021

 nanopass-compiler-frameworkを使ってみました

nanopass-compiler-frameworkを使ってみたので、それがどういうものかを紹介した。

Niyarin

March 25, 2021
Tweet

More Decks by Niyarin

Other Decks in Programming

Transcript

  1. Nanopass compiler framework
    を使ってみました
    Niyarin

    View Slide

  2. Nanopass compiler frameworkとは
    - コンパイラを書くためのフレームワーク
    - コンパイラの中間表現の記述とその変換方法を楽に書け
    る枠組みを提供してくれる。
    - ただし、S式言語 → S式言語
    正確には、Schemeの外部表現で記述できる言語?
    - Chez Scheme の実装に使われている?
    - Indiana大(Chezの開発元)のAndy Keepさんのプロダクト

    View Slide

  3. 前提知識:Multi-pass compiler
    - いくつかのフェーズからなるコンパイラ
    - ↔ one-pass compiler
    - 部品として切り出せるのが利点
    - 入力言語が複数ある場合、Parserの次だけ共通にするとか
    一例

    View Slide

  4. 前提知識:nano-pass compiler
    - 1フェーズを最小単位にしたmulti-pass compiler
    - 利点: 手を入れやすくなる
       バグ発見が容易になる
    - 欠点: 時間、空間計算量の増加

    View Slide

  5. 前提知識:nano-pass compilerの各パスに必要なもの
    入出力言語とその変換方法

    View Slide

  6. nano-pass compiler framework
    各言語定義と変換の方法を容易にする枠組みの提供
    - 各パスでの入出力言語の定義を容易にする枠組み
    - 各パスの変換を容易にする枠組み
    入出力はS式
    - 字句解析や構文解析は別でやる必要がある(非S式言語の場合)

    View Slide

  7. 例 説明に使うコンパイルパスの例
    - Piyo言語と名付けたSchemeからほとんどの機能を削った言語
    次のページで詳細を書く
    - Piyo言語lambda式のbodyに複数の式が含まれる形式を消す
    beginでbody包む
    (lambda (x) (display x) (newline) (+ x 1))
    → (lambda (x) (begin (display x) (newline) (+ x 1)))

    View Slide

  8. 拡張BNFでPiyo言語の文法を記述する
    :: =
        |
      |
    | (lambda ( ...) ...)
       | (begin ...)
       | ( ...)
    - 拡張BNF: 0回以上の繰り返しを “...” で表記したBNF
    プリミティブ手続き(cons, car, cdr, display) シンボル、ブーリアン
    lambda式 begin式 手続き適用
    左辺に出てこないやつ ( 、 、 ) を終端記号と呼ぶ
    左辺に出てくるやつ ()を非終端記号と呼ぶ

    View Slide

  9. Nanopass compiler frameworkでPiyo言語を表現する1
    言語の文法をS式の拡張BNFで記述する
    (define-language piyo-lang
    (terminals
    (primitive-procedure (proc))
    (boolean (b))
    (symbol (x)))
    (Expression (e)
    proc
    b
    x
    (lambda (x ...) e e* ...)
    (begin e e* ... )
    (e e* ...)))

    View Slide

  10. Nanopass compiler frameworkでPiyo言語を表現する1
    言語の文法をS式の拡張BNFで記述する
    (define-language piyo-lang
    (terminals
    (primitive-procedure (proc))
    (boolean (b))
    (symbol (x)))
    (Expression (e)
    proc
    b
    x
    (lambda (x ...) e e* ...)
    (begin e e* ... )
    (e e* ...)))
    ・終端記号の定義
     非終端記号名とメタ変数のリストを並べる
     メタ変数とは、非終端記号の定義で使われる
     変数のようなもの
    例えば、symbolのメタ変数はxとして定義している

    View Slide

  11. Nanopass compiler frameworkでPiyo言語を表現する1
    言語の文法をS式の拡張BNFで記述する
    (define-language piyo-lang
    (terminals
    (primitive-procedure (proc))
    (boolean (b))
    (symbol (x)))
    (Expression (e)
    proc
    b
    x
    (lambda (x ...) e e* ...)
    (begin e e* ... )
    (e e* ...)))
    非終端記号の定義
     非終端記号とメタ変数のリストと定義を書く
     拡張BNFをS式にしただけ
     あるメタ変数に”*”や数字を付けても同じように使える

    View Slide

  12. Nanopass compiler frameworkでPiyo言語を表現する2
    終端記号の定義も必要
    ・ primitive-procedureやsymbol、booleanについて
    ・終端記号名に”?”を付けた名前の述語を終端記号の定義としている
    symbol?とboolean?はSchemeがすでに提供している
    (define (primitive-procedure? x)
    (or (eq? x 'cons)
    (eq? x 'car)
    (eq? x 'cdr)
    (eq? x 'display)))

    View Slide

  13. lambdaから複数bodyが消えたPiyo言語を表現する
    (define-language single-body-lambda-piyo-lang
    (extends piyo-lang)
    (Expression (e)
    (- (lambda (x ...) e e* ...))
    (+ (lambda (x ...) e))))
    Piyo言語との差分を書くだけで済む

    View Slide

  14. lambdaから複数bodyが消えたPiyo言語を表現する
    (define-language single-body-lambda-piyo-lang
    (extends piyo-lang)
    (Expression (e)
    (- (lambda (x ...) e e* ...))
    (+ (lambda (x ...) e))))
    Piyo言語との差分を書くだけで済む
    拡張したい言語名を指定する

    View Slide

  15. lambdaから複数bodyが消えたPiyo言語を表現する
    (define-language single-body-lambda-piyo-lang
    (extends piyo-lang)
    (Expression (e)
    (- (lambda (x ...) e e* ...))
    (+ (lambda (x ...) e))))
    Piyo言語との差分を書くだけで済む
    “-”を頭に付けると削除できる
    “+”を頭に付けると追加できる

    View Slide

  16. beginでbodyを包む部分を書く
    syntax-case風の構文で変換規則を書く
    パターンに当てはまった構文を指定した方法で展開する
    (define-pass remove-multi-body-lambda :
    piyo-lang (e) -> single-body-lambda-piyo-lang ()
    (Expression : Expression (ir) -> Expression ()
    ((lambda (,x ...) ,(e) ,(e*) ...) `(lambda (,x ...) (begin ,e ,e* ...)))))

    View Slide

  17. beginでbodyを包む部分を書く
    syntax-case風の構文で変換規則を書く
    (define-pass remove-multi-body-lambda :
    piyo-lang (e) -> single-body-lambda-piyo-lang ()
    (Expression : Expression (ir) -> Expression ()
    ((lambda (,x ...) ,(e) ,(e*) ...) `(lambda (,x ...) (begin ,e ,e* ...)))))
    本体
    パターン
    ・ 与えた式の外側から見ていき、パターンにマッチしたものを展開部で置き換え

    ・パターン内のメタ変数にマッチしたものは、本体部で参照できる
    ・この展開を再帰的に行うには、パターンのその部分をリストで囲う

    View Slide

  18. beginでbodyを包む部分を書く
    syntax-case風の構文で変換規則を書く
    (define-pass remove-multi-body-lambda :
    piyo-lang (e) -> single-body-lambda-piyo-lang ()
    (Expression : Expression (ir) -> Expression ()
    ((lambda (,x ...) ,(e) ,(e*) ...) `(lambda (,x ...) (begin ,e ,e* ...)))))
    この辺のごたっとしたのは自信がないのでまた今度。
    途中まで書いているので、ブログ等でそれを説明するつもり

    View Slide

  19. 不正な入力に対して怒ってくれる(その他の良い点)
    入出力言語の文法を定めているため、間違いは指摘してくれる
    コンパイラの変換規則の記述の間違い
    コンパイラへの入力の間違い
    とても親切というほどではないが。
    例えば、Piyo言語は数値を受理しないが、それを入れたら怒ってくれ

    Exception in parse-piyo-lang: invalid syntax 3

    View Slide

  20. コンパイルする
    変換器に渡すにはパーサーを使って内部表現に変換する必要がある
    (※ここでのパーサーは、構文解析器のことではない)
    言語名とパーサ名を与えれば、パーサとアンパーサができる
    アンパーサは、パーサ名の頭にunがついたもの
    (例: un-parse-piyo-lang)
    (define-parser parse-piyo-lang piyo-lang)
    (define input '(cons (lambda (x y) (display x) (lambda (z) (cons x y))) foo )))
    (unparse-single-body-lambda-piyo-lang
    (remove-multi-body-lambda
    (parse-piyo-lang input))
    ; → (cons (lambda (x y) (begin (display x) (lambda (z) (begin (cons x y))))) foo)

    View Slide

  21. おわり
    ・Nanopass compiler frameworkはすごくよいソフトウェアでした
    構文を楽に書ける記法
    +とか-とかで拡張できる点
    syntax-case風の変換方法
    コンパイラの記述やあるパスへの入力が間違っていれば怒ってくれる
    入出力言語の文法を与えているため

    View Slide