The Common Lisp Cookbook – Alive で VSCode を使う

Table of Contents

The Common Lisp Cookbook – Alive で VSCode を使う

📢 🎓 ⭐ Learn Common Lisp efficiently in videos, by the Cookbook's main contributor. Learn more.

🖊️ Discover a new Common Lisp and Coalton editor for beginners: mine and a new VSCode extension for Common Lisp: OLIVE.

Alive extension は VSCode を強力な Common Lisp development platform にします。 target platform 上で動作する Common Lisp の version 以外に dependency はありません。現在は次を support しています。

Prerequisites

VSCode の Alive extension は ANSI Common Lisp と互換性があり、Alive REPL が正常に起動する限り、これらの手順はどの実装でも動くはずです。例ではすべて SBCL を使います。

VSCode を REPL に接続する

  1. VSCode 内で、編集したい lisp file を開きます。
    • まだない場合は、hello.lisp という新しい file を作成します。
  2. VSCode 内で、上部 menu の View/Command Palette から Command Palette を開き、 Alive: Start REPL And Attach を選んで、VSCode REPL に attach された Swank server を動かす SBCL instance を起動します。
    • REPL Connected と書かれた小さな window が pop up します。
    • REPL Connected message が出ない場合は、上部 menu の View:Output から VSCode の Output を開き、pulldown で Swank Trace を選びます。この output は実行中の lisp image からの output で、何がうまくいっていないのかを調べる出発点になります。

これで、稼働中の SBCL image の port 4005 で動く Swank server に attach された REPL を持つ VSCode instance ができました。file 内の statement を evaluate でき、それらは実行中の SBCL instance で処理されます。

REPL を disconnect し、SBCL instance を shut down するには、上部 menu の View/Command Palette から Command Palette を開き、Alive: Detach from REPL を選びます。

すべての操作には keybinding があります。必要に応じて explore し、変更してください。

Recipes

特に明記しない限り、すべての recipe は、VSCode で file を開き、REPL が attach されている状態を前提とします。

expression を evaluate するときは、evaluate したい s-expression の中、または直後のどこかに cursor を置いて、evaluate する expression を選びます。

statement を in-line で evaluate する

  1. editor window の開いている file に、次を入力します。
(+ 2 2)
  1. 上部 menu の View/Command Palette から Command Palette を開き、Alive: Inline Eval を選びます。
  2. => 4 (3 bits, #x4, #o4, #b100) と書かれた小さな pop up が表示されます。これが result です。

    statement を in-line で evaluate することは、それを REPL に送ることとまったく同じです。違いは表示方法だけです。

statement を evaluate する

  1. editor window の開いている file に、次を入力します。
(+ 2 2)
  1. 上部 menu の View/Command Palette から Command Palette を開き、Alive: Send To REPL を選びます。
  2. expression が REPL に表示され、result も一緒に表示されます。
CL-USER>
(+ 2 2)
4
CL-USER>

file を compile する

  1. editor window の開いている file に、次を入力します。
(+ 2 2)
  1. 上部 menu の View/Command Palette から Command Palette を開き、Alive: Compile を選びます。
  2. repl に compile の詳細が表示され、filesystem に fasl file ができます。
CL-USER>

; compiling file "/Users/jason/Desktop/hello.lisp" (written 14 SEP 2021 04:24:37 AM):


; wrote /Users/jason/Desktop/hello.fasl

; compilation finished in 0:00:00.001

Interactive Debugger を使って abort する

  1. editor window の開いている file に、次を入力します。
(defun divide (x y)
  (/ x y))
  1. まだそうなっていなければ、最後の parenthesis の後に cursor を置きます。上部 menu の View/Command Palette から Command Palette を開き、Alive: Inline Eval を選んで、define function を image に load します。
  2. 開いている file に新しい行を追加し、次を入力します。
(divide 1 0)
  1. まだそうなっていなければ、最後の parenthesis の後に cursor を置きます。上部 menu の View/Command Palette から Command Palette を開き、Alive: Inline Eval を選んで、divide function を image 内で実行します。
  2. Interactive Debugger が pop up します。Restarts section で、option 2 を選んで Abort します。
  3. editor とまだ動いている REPL に戻り、何も起きなかったかのように続けられます。

Interactive Debugger を使って runtime に問題を修正する

  1. editor window の開いている file に、次を入力します。
(defun divide (x y)
  (assert (not (zerop y))
          (y)
          "The second argument can not be zero.")
  (/ x y))
  1. まだそうなっていなければ、最後の parenthesis の後に cursor を置きます。上部 menu の View/Command Palette から Command Palette を開き、Alive: Inline Eval を選んで、define function を image に load します。
  2. 開いている file に新しい行を追加し、次を入力します。
(divide 1 0)
  1. まだそうなっていなければ、最後の parenthesis の後に cursor を置きます。上部 menu の View/Command Palette から Command Palette を開き、Alive: Inline Eval を選んで、divide function を image 内で実行します。
  2. Interactive Debugger が pop up します。Restarts section で、option 0 の “Retry assertion with new value for Y” を選びます。
  3. popup menu に y' と入力します。
  4. 次の popup menu に 1 と入力します。
  5. => 1 (1 bit, #x1, #o1, #b1) と書かれた小さな pop up が表示されるはずです。これは新しい value の result です。debugger へ crash した後、crash の原因になった value を変更させてもらい、悪い 0 value を入力しなかったかのように proceeding した状態で、editor とまだ動いている REPL に戻っています。

debugger でできることのさらなる idea は error handling page にあります。

macro を expand する

  1. editor window の開いている file に、次を入力します。
(loop for x in '(a b c d e) do
     (print x))
  1. まだそうなっていなければ、最後の parenthesis の後に cursor を置きます。上部 menu の View/Command Palette から Command Palette を開き、Alive: Macro Expand を選んで for-loop macro を expand します。
  2. 次のようなものが見えるはずです。
(BLOCK NIL
  (LET ((X NIL)
        (#:LOOP-LIST-559
         (SB-KERNEL:THE* (LIST :USE-ANNOTATIONS T
                               :SOURCE-FORM '(A B C D E))
                         '(A B C D E))))
    (DECLARE (IGNORABLE #:LOOP-LIST-559)
             (IGNORABLE X))
    (TAGBODY
     SB-LOOP::NEXT-LOOP
      (SETQ X (CAR #:LOOP-LIST-559))
      (SETQ #:LOOP-LIST-559 (CDR #:LOOP-LIST-559))
      (PRINT X)
      (IF (ENDP #:LOOP-LIST-559)
          (GO SB-LOOP::END-LOOP))
      (GO SB-LOOP::NEXT-LOOP)
     SB-LOOP::END-LOOP)))

function を disassemble する

  1. editor window の開いている file に、次を入力します。
(defun hello (name)
  (format t "Hello, ~A~%" name))
  1. まだそうなっていなければ、最後の parenthesis の後に cursor を置きます。上部 menu の View/Command Palette から Command Palette を開き、Alive: Inline Eval を選んで function を image に load します。
  2. まだそうなっていなければ、最後の parenthesis の後に cursor を置きます。上部 menu の View/Command Palette から Command Palette を開き、Alive: Disassemble を選んで compiled function の machine code を print します。
  3. 次のように始まります。
; disassembly for HELLO
; Size: 172 bytes. Origin: #x70052478B4                       ; HELLO
; 8B4:       AC0A40F9         LDR R2, [THREAD, #16]           ; binding-stack-pointer
; 8B8:       4C0F00F9         STR R2, [CFP, #24]
; 8BC:       AC4642F9         LDR R2, [THREAD, #1160]         ; tls: *STANDARD-OUTPUT*
; 8C0:       9F8501F1         CMP R2, #97
; 8C4:       61000054         BNE L0
; 8C8:       4AFDFF58         LDR R0, #x7005247870            ; '*STANDARD-OUTPUT*
; 8CC:       4C1140F8         LDR R2, [R0, #1]
; 8D0: L0:   4C1700F9         STR R2, [CFP, #40]
; 8D4:       E0031BAA         MOV NL0, CSP
; 8D8:       7A0701F8         STR CFP, [CSP], #16
; 8DC:       EAFCFF58         LDR R0, #x7005247878            ; "Hello, "
; 8E0:       4B1740F9         LDR R1, [CFP, #40]
; 8E4:       B6FBFF58         LDR LEXENV, #x7005247858        ; #<SB-KERNEL:FDEFN WRITE-STRING>
; 8E8:       970080D2         MOVZ NARGS, #4
; 8EC:       FA0300AA         MOV CFP, NL0
; 8F0:       DE9240F8         LDR LR, [LEXENV, #9]
; 8F4:       C0033FD6         BLR LR
; 8F8:       3B039B9A         CSEL CSP, OCFP, CSP, EQ
; 8FC:       E0031BAA         MOV NL0, CSP
; 900:       7A0701F8         STR CFP, [CSP], #16
; 904:       4A2F42A9         LDP R0, R1, [CFP, #32]
; 908:       D6FAFF58         LDR LEXENV, #x7005247860        ; #<SB-KERNEL:FDEFN PRINC>
; 90C:       970080D2         MOVZ NARGS, #4
; 910:       FA0300AA         MOV CFP, NL0
; 914:       DE9240F8         LDR LR, [LEXENV, #9]
; 918:       C0033FD6         BLR LR
; 91C:       3B039B9A         CSEL CSP, OCFP, CSP, EQ
; 920:       E0031BAA         MOV NL0, CSP
; 924:       7A0701F8         STR CFP, [CSP], #16
; 928:       2A4981D2         MOVZ R0, #2633
; 92C:       4B1740F9         LDR R1, [CFP, #40]
; 930:       D6F9FF58         LDR LEXENV, #x7005247868        ; #<SB-KERNEL:FDEFN WRITE-CHAR>
; 934:       970080D2         MOVZ NARGS, #4
; 938:       FA0300AA         MOV CFP, NL0
; 93C:       DE9240F8         LDR LR, [LEXENV, #9]
; 940:       C0033FD6         BLR LR
; 944:       3B039B9A         CSEL CSP, OCFP, CSP, EQ
; 948:       EA031DAA         MOV R0, NULL
; 94C:       FB031AAA         MOV CSP, CFP
; 950:       5A7B40A9         LDP CFP, LR, [CFP]
; 954:       BF0300F1         CMP NULL, #0
; 958:       C0035FD6         RET
; 95C:       E00120D4         BRK #15                         ; Invalid argument count trap

Common Lisp system の skeleton を作成する

この recipe は新しい Common Lisp System を作成するため、running REPL は不要です。

  1. 新しい project 用に experiment という folder を作成します。
  2. newly created directory で vscode を開きます。
cd experiment
code .
  1. 新しい Common Lisp System を作成します。
    • VSCode 内で、上部 menu の View/Command Palette から Command Palette を開き、system skeleton を生成します: Alive: System Skeleton
    • 前の command は、次の directory structure を生成しているはずです。
    • experiment.asd
    • src/
      • app.lisp
    • test/
      • all.lisp

それらの file の内容は次のとおりです。

experiment.asd:

(in-package :asdf-user)

(defsystem "experiment"
  :class :package-inferred-system
  :depends-on ("experiment/src/app")
  :description ""
  :in-order-to ((test-op (load-op "experiment/test/all")))
  :perform (test-op (o c) (symbol-call :test/all :test-suite)))

(defsystem "experiment/test"
  :depends-on ("experiment/test/all"))

(register-system-packages "experiment/src/app" '(:app))
(register-system-packages "experiment/test/all" '(:test/all))

src/app.lisp:

(defpackage :app
  (:use :cl))

(in-package :app)

test/all.lisp:

(defpackage :test/all
  (:use :cl
        :app)
  (:export :test-suite))

(in-package :test/all)

(defun test-suite ()
  (format T "Test Suite~%"))

Optional Custom Configurations

Quicklisp と連携するよう VSCode Alive を configure する

quicklisp が install 済みで、init 時に load されるよう configure されていると仮定すると、quicklisp はそのまま動きます。

default context の CLPM と連携するよう VSCode Alive を configure する

CLPM が install 済みで configure 済みであると仮定し、vscode settings を変更 して次のようにします。

  1. VSCode settings に次を追加します。
  "alive.swank.startupCommand":[
    "clpm",
    "exec",
    "--",
    "sbcl",
    "--eval",
    "(asdf:load-system :swank)",
    "--eval",
    "(swank:create-server)"
  ],

これは default clpm context で sbcl を起動します。

bundle clpmfile を使う CLPM と連携するよう VSCode Alive を configure する

CLPM が install 済みで configure 済みであり、 home directory の root に swank を dev dependency として含む bundle が configure されていると仮定し、vscode settings を変更して次のようにします。

  1. VSCode settings に次を追加します。
  "alive.swank.startupCommand":[
    "clpm",
    "bundle",
    "exec",
    "--",
    "sbcl",
    "--eval",
    "(asdf:load-system :swank)",
    "--eval",
    "(swank:create-server)"
  ],

これは bundle の clpm context で sbcl を起動します。

Roswell と連携するよう VSCode Alive を configure する

Roswell が install 済みであると仮定し、 vscode settings を変更して次のようにします。

  "alive.swank.startupCommand": [
    "ros",
    "run",
    "--eval",
    "(require :asdf)",
    "--eval",
    "(asdf:load-system :swank)",
    "--eval",
    "(swank:create-server)"
  ]

VSCode Alive を Docker container に接続する

これらの手順は、remote connection、wsl connection、github Codespaces でも、それぞれ Remote - SSHRemote - WSLGithub Codespaces extension を使って動きます。extension が install されていることを前提とします。この例では、Containers extension が install 済みで configure 済み であることを確認してください。

  1. sbcl が install された docker image を pull します。この例では latest の clfoundations sbcl を使います。
docker pull clfoundation/sbcl
  1. docker image 内で bash を run し、起動して動かし続けます。
docker run -it clfoundation/sbcl bash
  1. VSCode Side Bar で Remote Explorer icon を click します。
  2. Dev Containers の list で clfoundation/sbcl を右 click し、Attach to Container を選びます。
  3. 開いた新しい VSCode window の VSCode Side Bar で、Explorer を click します。file がまだ表示されていない場合は、container 内の file を表示するよう伝える必要があるかもしれません。
  4. container 内の file を表示できたら、VSCode Side Bar 内で右 click し、New File を選びます。file 名を hello.lisp にします。
  5. VSCode Site Bar で Extensions icon を click します。
  6. Alive plugin の Install in Container... button を click します。
  7. hello.lisp file を開き、この recipe の冒頭にある “Connect VSCode to a REPL” の手順に従います。
  8. これで、docker container 内の SBCL image で動く Slime server に接続された REPL を持つ VSCode が 動いています。

Page source: ja/vscode-alive.md

T
O
C