Visual LISP 入門

統合開発環境 Visual LISP

Visual LISP は AutoLISP での開発を支援するツール群、すなわち統合開発環境(IDE=Integrated Development Environment)です。Visual LISP は AutoCAD に組み込まれており、AutoCAD が使用できれば Visual LISP も使用することができます。

AutoLISP のプログラム自体は、一般のテキストエディタでも書くことができます。しかし Visual LISP 環境を使用すると次のような利点があります。

  1. プログラム開発中、AutoCAD にプログラムを簡単にロードしてテストすることができる。
  2. プログラムのステップ実行や変数値の監視などデバックに欠かせない機能を利用することができる。
  3. テキストエディタには、関数の検索機能や、コードを見やすくするための色分け、カッコの対応などプログラマを支援する機能が含まれている。
  4. AutoCAD の図面データベースの内容を表示させることができる。
  5. プロジェクト機能を使って、複数のファイルにまたがるプログラムの管理が容易に行える。
  6. VLX アプリケーションといった扱いが容易なプログラムにパッケージ化することができる。

Visual Studio Code

AutoCAD 2021 から AutoLISP の開発は Microsoft が MIT ライセンス下で作成している Visual Studio Code を使う方針に変更になりました。このエディターはフリーでオープンのため拡張機能が豊富で、そのため AutoCAD にも適用できる拡張機能が新たに用意されるに至ったわけですが、外部の独立したエディターであり AutoCAD とは通信を介してやり取りします。このことから AutoCAD と一体化している Visual LISP の機能を全て叶えるものにはなりません。

一方で、Visual Studio Code は汎用のエディターとして今も開発が続いている最新の開発環境です。既にこちらが優れている面も多々ありますし、Visual LISP ではできたのにと思う機能を惜しみながらもこちらが将来的には主流になる流れは確実でしょう。そして現在でも既に開発者が Visual Studio Code に移行しなければならない理由が、日本語などのマルチバイトバイト文字が UNICODE をベースにする処理に AutoCAD 2021 全体でなっているというものです。新しい Visual LISP ではフォントの設定をしても日本語の入力はできなくなりました。また、SHIFT_JIS といった従前の文字コードは AutoCAD 2021 からは表示することができないで文字化けします。

このようなことから、AutoCAD 2021 以降の開発環境は Visual Studio Code をメインとして活用し、どうしても Visual LISP の機能が必要なときは切り替えてプログラム作成を行うというスタイルで、特に英語圏以外のマルチバイト文字を使うプログラム開発では、面倒ですが、やっていかなければならないでしょう。Visual LISP の依然として優れている機能として、プロジェクト機能、図形データーベースの内容の表示、シンボルサービスからのソースコードの表示、デバッグのトレース機能といったものが挙げられます。

Visual LISP から Visual Studio Code に開発環境が変わっても、プログラム開発の基本の流れは共通するものがあります。これから Visual Studio Code とともに AutoLISP に挑戦する方でも、このコラムで説明する開発の流れのあらましをイメージとして捉えてもらうとともに、特にコンソールの存在とロードの概念を理解してください。既に Visual LISP を使って AutoLISP の開発を行っている方は別コラム「 Visual Studio Code への移行」を参照ください。

AutoCAD 2020 以前で Visual LISP で日本語が文字化けする場合は

Visual LISP のメニューから【ツール】>【ウィンドウの属性】>【フォント】を選んで、表示されるダイアログから【MS ゴシック】などの日本語のフォントを選んだあと、再起動してください。

Visual LISP を表⽰する

プログラミングを始めるにあたって AutoCAD から Visual LISP を表示します。Visual LISPを表示する方法は、インターフェースのスタイルによってリボンやメニューなどの方法がいくつかありますが、共通して簡単なのが【コマンドライン】に「vlisp」または「vlide」のコマンドを入力します。どちらのコマンドも同じです。

コマンド: vlisp⏎

AutoCAD 2021 以降で Visual LISP を表示するには

AutoCAD 2021 以降で Visual LISP を表示するには、AutoCAD の新しいシステム変数 LISPSYS を 0 に設定して AutoCAD を再起動し、従前どおり vlisp コマンドを使用します。反対に Visual Studio Code を使うようにするにはシステム変数 LISPSYS を 1 または 2 に変更して AutoCAD を再起動し、vlisp コマンドを使用します。

なお、システム変数 LISPSYS が 0 の際に、Visual LISP を使うとともに文字列関係の関数が UNICODE を考慮しない従前の動作になるとヘルプには書かれていますが、AutoCAD 全体が SHIFT_JIS を表示できないものに変更になっているので、まったく以前の環境に戻るわけではありません。

コンソールを使ってみる

Visual LISP を表示させると、【コンソール】ウィンドウが表示されています。

プログラムと言えば、テキストエディタでコードを書いていくイメージですが、AutoLISP はインタープリタなので、【コンソール】に直接プログラムを書いて実行させることもできます。

【コンソール】に書いたプログラムはその場限りなので、プログラムとしてファイルに保存されませんが、一時的な関数のテストや、変数の値の表示など、多くの場面で使うものなので、まずこれに慣れてください。

【コンソール】はAutoCAD の作図ウィンドウに表示される【コマンドライン】と大変よく似たものですが、入力したコマンドは LISP として解釈されるので、【コマンドライン】と異なる部分があります。最初は、以下のようにプロンプト「_$」が表示されて入力を待っています。

_$

次にLISP のコマンドを入力し、⏎キーを押して実行させます。LISP のプログラムは、大文字小文字を区別しません。

_$ (expt 3 2)⏎
9
_$

「(expt 3 2)」は 3 の 2 乗を計算する関数の呼び出しです。答えの 9 が次の行に表示されて、再びプロンプトが表示されます。【コンソール】では、コマンドを実行すると、関数の戻り値がコマンドの次の行に表示されることを理解してください。このままでは、計算結果が表示されるだけなので、戻り値を変数 a に代入してみます。

_$ (setq a (expt 3 2)) ⏎
9

変数 a の内容を表示してみます。変数名だけを入力して、⏎キーを押します。

_$ a⏎
9

内容が次の行に表示されました。文字列を表示するコマンドを試してみます。二回文字列が表示されました。2 回目の文字列は"(ダブルクォーテーション)で囲まれている点が異なります。

_$ (princ "Hello,World!") ⏎
Hello,World!"Hello,World!"

princ という関数は、与えられた引数を標準出力に出力し、一方で引数をそのまま返す関数です。そのため、1 回目の「Hello,World!」は princ 関数が標準出力に出力したもの、2 回目の「"Hello,World!"」は princ 関数が返した値を【コンソール】が表示したものとなります。ここでは文字列が返ってきたので、" で囲まれています。

【コンソール】には、複数行にまたがるコマンドを入力することもできます。コマンドをカッコで閉じる前に⏎キーを押すとプロンプトが変化します。

_$ (setq a (expt 3 2⏎
((_>

「((_>」は、開きカッコ二つに対する閉じかっこが、まだ入力されていないことを示しています。ここで閉じカッコを一つだけ入力すると、プロンプトの開きカッコが一つ減ります。もう一度閉じカッコを入力すると、すべてのカッコが閉じて、コマンドが実行されます。

_$ (setq a (expt 3 2⏎
((_> ) ⏎
(_> ) ⏎
9

コマンドを複数行にまたがって入力する正式な方法は、改行で⏎キーを押す代わりに、SHIFT+⏎キーを押します。最後は⏎キーでコマンドを確定して実行させます。

_$ (setq a [SHIFT+⏎]
(expt 3 2) [SHIFT+⏎]
) ⏎
9

入力したコマンドや実行しているプログラムにエラーが発生した場合に、設定によってはブレークループに入って実行が一時停止します。

エラーを発生させてみます。変数 b に 0(ゼロ)を代入し、10 を変数 b で割ることでエラーを発生させています。

_$ (setq b 0) ⏎
0
_$ (/ 10 b) ⏎
_1$

プロンプトが「_1$」に変わりました。これはブレークループの1 段目であることを示し、プログラムが一時停止していることを示します。この状態でも、コマンドの実行を行うことができます。ここでは再びエラーを起こしてみます。ブレークループが1段深くなりました。変数名を入力することで、変数の値の確認もできます。

_1$ (/ 5 b) ⏎
_2$ b
0
_2$

エラーを起こしたコマンドやプログラムを、正常に復帰することはできませんので、結局はリセットすることになります。リセットするには CTRL+R を押します。一時停止していたコマンドは終了し、プロンプトは元の状態に戻ります。

もし無限ループに入ってしまった等、プログラムの実行を強制的に終了させたいときは、VisualLISP のメニューより【デバッグ】>【評価を中止】を選んでください。

テキストエディタを使ってみる

【コンソール】は、関数のテストや変数の値の確認に重宝するものですが、まとまったプログラムを書くとなると、エディタで書き、ファイルに保存する必要があります。

テキストエディタには高機能なものが各種あり、自分の手慣れたものを使うという考え方もありますが、Visual LISP のエディタを使うことで、プログラムのロードやデバッグが容易になるのでこちらを使うことをお勧めします。

テキストエディタを使うには、ツールバーから行うのが最も容易です。エディタなどでおなじみのボタンが並んでいます。

【新しいファイルを作成】ボタンを押すと、エディタが開き、プログラムを書くことができます。

プログラムを書いていくと、インデントなど整形を行いたい場合があります。整形はツールバーの以下のボタンで行うことができます。左がファイル全体を、右が選択範囲を整形します。

プログラムにコメントを含める方法はいくつかありますが、行単位でコメントアウトを行いたい場合、または元に戻したい場合は以下のボタンを使用します。

プログラムを書いてみる

AutoCAD でプログラムすることは、AutoCAD に新しいコマンドを追加することです。新しいコマンドは、ユーザーから呼び出せる LISP の関数を AutoCAD にロードすることで追加することができます。

簡単なコマンドの例を、以下に示します。このプログラムは、ユーザーがクリックした場所に、円で囲まれた連番の数字を次々と描きます。簡単のために、円の半径は2.5、文字の高さは3 に固定とします。

(defun drawEnclosedText (pt text /)
    (command "._TEXT" "j" "mc" pt textHeight "" text)
    (command "._CIRCLE" pt radius)
)

(defun userInput (/)
    (initget 1 "Number eXit")
    (getpoint (strcat "¥n<次の番号: "
                      (itoa counter)
                      "> - 点を指定 [番号設定(N)/終了(X)]:"
             )
    )
)

(defun initCounter (/)
    (initget (+ 1 2 4))
    (getint "¥n 正の整数を入力:")
)

(defun c:enclosedNumber (/ com textHeight radius counter)
    (setq textHeight 3.0
          radius 2.5
         counter 1
    )
    (while (/= com "eXit")
        (setq com (vl-catch-all-apply 'userInput nil))
        (if (not (vl-catch-all-error-p com))
            (cond ((= (type com) 'LIST)
                   (drawEnclosedText com (itoa counter))
                   (setq counter (1+ counter))
                  )
                  ((= com "Number")
                   (setq com (vl-catch-all-apply 'initCounter nil))
                   (if (not (vl-catch-all-error-p com))
                   (setq counter com)
                  )
                )
            )
            (setq com "eXit")
        )
    )
    (princ)
)

ここでは詳細を説明しませんが、このプログラムは、単純ながらもコマンドプログラムの基本的な構造を持っています。ここには4 つの関数が定義されています。

drawEnclosedText 関数は、円とテキストを描く関数です。プログラムの出力を引き受けます。同じようにuserInput 関数とinitCounter 関数は、ユーザーからの入力を受け取る関数です。これらは、いわゆる副作用をともなう関数です。

そして、c:enclosedNumber 関数がコマンドのメイン関数です。ユーザーからの入力を受け取り、描画を繰り返します。ユーザーの入力が不正だった場合に備えて、例外を使用しています。

c:enclosedNumber 関数の名前に「c:」と付いている点に注目してください。関数名の頭に「c:」と付けると、AutoCAD はそれにつづく関数名を通常のコマンドのように扱います。すなわち、AutoCAD ユーザーは【コマンドライン】に「(c:enclosedNumber)」のようにLISP の関数呼び出しのように入力しても良いのですが、他のコマンドのようにカッコを付けずに「enclosedNumber」と入力してもこの関数が呼ばれます。大文字小文字は区別されません。

コマンド登録関数 vlax-add-cmd

AutoLISP の関数をコマンドのように使えるようにする方法として関数名の頭に「c:」と付ける方法以外に、vlax-add-cmd 関数を使ってコマンド名と対応する関数を宣言する方法もあります。こちらの方法を使ったコマンドは、他の組み込みコマンドと同じように別の所から command 関数で呼び出せるコマンドになります。このことは言い換えますと、関数名の頭に「c:」と付けたコマンドは他の所から command 関数で呼び出すことはできません。他にも vlax-add-cmd 関数は割り込みコマンドを定義できたり、コマンド名のローカライズ - コマンドの別名を付けることができますので、本格的にコマンドを作るようになったらこちらも使ってみることをお勧めします。

vlax-add-cmd 関数の説明はこちらを参照してください。

また、c:enclosedNumber 関数の最後に「(princ)」というコマンドが書かれている点に注目してください。【コンソール】の例でみたように、コマンドを実行すると返り値が表示されます。これは、AutoCAD ユーザーが【コマンドライン】でコマンドを実行していても同じです。princ 関数は標準出力に表示するとともに、引数を返す関数でした。引数なしの princ 関数は何も表示しませんし何も返しません(正確には長さが 0 のため見ることができないシンボル名が返っています)。これを最後に置くことによって、c:enclosedNumber 関数の戻り値を、強制的に空にしています。最後に「(princ)」を書くのは、コマンド作成の定石なので覚えておいてください。

プログラムをロードして実⾏する

プログラムが書けたら、この辺りでテキストエディタの内容をファイルにひとまず保存してください。

しかしこの段階では LISP のコードが書かれたテキストファイルが作成されただけです。このプログラムを AutoCAD にロードすることでコマンドが実行可能状態になります。ロードする方法はいくつかあり、Visual LISP を使っているならば、エディタウィンドウをアクティブにして、以下のツールボタンで簡単にロードできます。左がファイル全体の内容をすべてロードします。右が選択部分だけをロードします。なお、AutoCAD にロードされるのはテキストエディタの内容であり、先ほど保存したファイルの内容ではありませんので、適時ディスク上のファイルを更新してください。

全てをロードさせると【コンソール】に次のように表示されます。

_$
; 4 フォーム (#<editor "D:/Users/mChiar/Documents/AutoCAD/lisp/sample.LSP"> からロード)
_$

「4 フォーム」はファイルに四つのコマンドが含まれていたことを示します。今回の場合、関数定義のコマンドが四つありましたので、その四つです。

選択部分のみロードさせると、例えば、「(setq a 0)」などというコマンドを選択していた場合は、この部分を【コンソール】で実行したように、戻り値が表示されます。

_$
0
_$

関数定義を選択してロードさせると、例えば「DRAWENCLOSEDTEXT」のように、関数名が表示されます。関数定義の defun 関数は、関数定義を行うとともに、その関数名を返す関数です。

_$
DRAWENCLOSEDTEXT
_$

これらは、ロードという行為が、AutoCAD のどこかにプログラムを格納するという行為だけではなく、読み込みと同時に実行されていることを表します。先ほどのファイル全体の場合、ロードとともに四つの関数定義が実行され、これらの関数が実行可能状態になったのです。

ロードの段階で、うまくいかない場合は、以下のツールボタンでプログラムが正しいか確認してください。左がファイル全体をチェックします。右が選択部分をチェックします。

チェックを行うと、【作成出力】というウィンドウが新しく開き、エラーの内容を表示します。水色の部分をダブルクリックすると、エディタウィンドウに飛んでエラーの箇所が選択されます。ロードで失敗する場合は、だいたい LISP のカッコの対応が取れていません。

ロードを無事に行えたら、コマンドを実行してテストします。【コンソール】からならば、LISP のコマンドとして関数を呼び出します。

_$ (c:enclosedNumber) ⏎

AutoCAD ユーザーが使う【コマンドライン】からならば、LISP のコマンド「(c:enclosedNumber)」でも可能ですが、「enclosedNumber」だけでも実行されます。大文字小文字は区別されません。

例えプログラムが意図どおり動かなかったり、エラーとなっても、Visual LISP ならば、ブレークポイントを置いてステップ実行をしたり、変数の値をウォッチすることができます。プログラムを修正したら、再度ロードして、AutoCAD 内の以前の内容を上書きして再びテストします。

Visual LISP 上での開発について、雰囲気、概要について見てきました。Visual LISP の詳しい使い方については、別稿の Visual LISP ガイド をご参照ください。