$30 off During Our Annual Pro Sale. View Details »

scheme-small-linter.pdf

Niyarin
April 30, 2020

 scheme-small-linter.pdf

Niyarin

April 30, 2020
Tweet

More Decks by Niyarin

Other Decks in Programming

Transcript

  1. Scheme Linterを作る
    Niyarin

    View Slide

  2. Linter for Scheme
    ・作法に反するコードを教えてくれるやつ
    (if (not (some-pred? a)) (write ‘hello))
      Linter 「 に変更しよう」
    (unless (some-pred? a) (write ‘hello))


    ・(捕捉)Schemeコーディングスタイル派閥
    関数定義(define (foo arg) ...) VS (define foo (lambda (arg) …)
    あとはifを使わずcond+else勢とか
    ・Schemeは決まったコーディングスタイルは特にないが、
      明らかに冗長なものは潰せる
    1

    View Slide

  3. Linter実装方針
    kibit/kibit/src/kibit/rules/control_structures.clj
    ・パターンベースのやつ
        → ユーザがルールを追加しやすい
    S式言語はこういうの楽で良いね
    ・ClojureのKibitもそういう方針
    core.logicベース
    2

    View Slide

  4. 作ったもの
    ・プロトタイプとしてRed-parenという小さいLinterを作った
    → syntax-rulesライクなルール記述ができる
      
      行数取得とかできるreaderがないので入出力が未完全
    ・没案としてmini-kanrenとかの利用も検討していた
    → Kibitがcore.logic使っているようだったので
    ・syntax-rulesライクなパターンマッチング(のようなことをする)ライブラリ
    ・rules (最初に作ったやつ)
      ・rules+ (記述力が弱かったので↑を拡張した)
    niyarin/red-paren
    niyarin/rules
    2

    View Slide

  5. 最初に作ったライブラリ、Rules
    ・rulesの例
    実質syntax-rulesの関数版のようなもの
      
      inputが第一引数にマッチしたなら第二引数に展開される
    ・schemeでschemeを書く時にsyntax-rulesの実装にも役立つ
    er-macro/ir-macro(ハイジニックな評価を使うマクロ)があればすぐ実装できる
    ・あまり強力ではなかった
    ・型の判定等は苦手
    定数(0,#t,#fなど)の判定はできるが、
        数値かどうかの判定などはできない
    ・繰り返し記号の1listに1回だけという制約
    (match-expand
    (when (not pred) body ...)
     (unless pred body ...)
    input)
    3

    View Slide

  6. rulesの繰り返し記号の制約
    ・1つのリストに繰り返し記号2回使うルールが書けない
    0を含む+演算
    (syntax-rulesでは再帰的にマクロルールを書くことで対処している)
    ・メリットとしては計算は少なくてすむ
    ・バックトラックしなくて良い
    繰り返し回数が(入力リストの長さ - 非繰り返し記号)で定まる
    (+ num1 … 0 num2 ...)
    (a b c … d e)に(1 2 3 4 5 6)を入力した場合、
    入力長6からa,b,d,eの4つを引く
    4

    View Slide

  7. rulesの実装は必要だったのか
    ・syntax-rulesのような操作を関数実行時に行う一番楽な方法
    ・ほとんど使われないのに必要な時に忘れられるeval
    ・10行足らずで書けてしまったよ
    (define (match-expand rule template input)
    (eval
    `(let-syntax ((expand (syntax-rules ()
    ((_ ,rule) (quote ,template))
    ((_ _ …) #f))))
    (expand ,input))
    (environment '(scheme base))))
    5

    View Slide

  8. rules+ (1) 繰り返し数の制限を無くした
    ・ … 記号は最長マッチの繰り返しとして扱うように変更した
    ・継続でバックトラックするだけなのでそれほど労力はかからなかった。
    6
    (or (call/cc (lambda (break)
    繰り返しにもう一回マッチする))
    繰り返しおわり、次に進む)
    (break #f)
    マッチ失敗した場合

    View Slide

  9. rules+ (2) 述語埋め込み
    述語を埋め込めるようにした
    挙動
    ・述語に入力を適用して#tならば述語:入力の連想リストで登録される
    ・複数回使う場合はラップして別ポインタに配置される必要がある
    手続きのポインタ配置は仕様の外なので
           コンパイラの最適化にひっかからないようにする
    `((assv (quote ,symbol?) als)
    (assq (quote ,symbol?) als))
    (let ((symbol1? (lambda (x) (and (symbol? x) 1)))
    (symbol2? (lambda (x) (and (symbol? x) 2))))
    `((some-rule ,symbol1? ,symbol2?)
    (some-template ,symbol1? ,symbol2?)))
    7

    View Slide

  10. Red-parenの実行例
    ----------
    (if (not (and (> x 0) (= (modulo (+ x y) 2) 0))) (display (car (car y))))
    => (unless (and (> x 0) (= (modulo (+ x y) 2) 0)) (display (car (car y))))
    ----------
    (> x 0)
    => (positive? x)
    ----------
    (= (modulo (+ x y) 2) 0)
    => (even? (+ x y))
    ----------
    (car (car y))
    => (caar y)
    (if (not (and (> x 0) (= (modulo (+ x y) 2) 0)))
    (display (car (car y))))
    > gosh rparen.scm badcode1.scm
    結果
    入力
    8

    View Slide

  11. Red-parenのチェック項目例
    ・SRFI/R7RS large用のリストとか
    append-map、concatenate、remove
    `((apply append (map f ls ...)) (append-map f ls ...))
    ((apply append list-of-list) (concatenate list-of-list))
    ((filter (lambda (x) (not (pred x)) body) ls)
    (remove pred ls)))
    9

    View Slide

  12. おわり/あとでやること
    今後実装する手続き(ライブラリ)
    ・行数とか頭の空白情報を保持するreader
      ・いいかんじに成形するwriter
    出力のフィルタリング
    やってみたいこと
    ・初心者が書いたSchemeコードを入力してみたい
    AtcoderとかQiitaに転がってそう
    典型的な冗長なコードからチェックパターンを増やせると思う
     
    10

    View Slide