« 2008年01月 | メイン | 2008年03月 »

2008年02月 アーカイブ

2008年02月02日

Arc のソースを読む #1

Arc のソースをつらつらと眺めているので、ついでにメモを書いてみる。チュートリアルArc Cross Reference も適宜参照してもらうとよいかも。

スタートアップファイル (as.scm)

まず、Arc を起動するときに最初に読み込む Scheme のスタートアップファイル as.scm から。(as.scm の as は Arc Startup の略?)

(require mzscheme) ; promise we won't redefine mzscheme bindings

(load "ac.scm") 
(require "brackets.scm")
(use-bracket-readtable)

(aload "arc.arc")
(aload "libs.arc") 

(tl)

いくつかのファイルが順にロードされていて、どうやら以下のように分かれているようだ。

ac.scm (ac は Arc Core の略?)は Arc の式を Scheme に変換して実行するコア部分。Arc から Scheme に変換するコンパイラと、プリミティブな関数が Scheme にて書かれている。

brackets.scm は Scheme のリードテーブルを拡張して [+ _ 1] のような式を扱えるようにしている("[" と "]" というトークンはもともと Scheme にはないため)。

arc.arc で様々な関数/マクロが Arc にて記述されている。

libs.arc では HTTP サーバやアプリケーションサーバ等のライブラリをロードしている。

read-eval-print ループ (ac.scm)

次に ac.scm (Arc Core?)。まず as.scm の最後で呼ばれている (tl) を見てみる。tl は Top Level の略か。

(define (tl)
  (display "Use (quit) to quit, (tl) to return here after an interrupt.\n")
  (tl2))

(define (tl2)
  (display "arc> ")
  (on-err (lambda (c) 
            (set! last-condition* c)
            (display "Error: ")
            (write (exn-message c))
            (newline)
            (tl2))
    (lambda ()
      (let ((expr (read)))
        (if (eqv? expr ':a)
            'done
            (let ((val (arc-eval expr)))
              (write (ac-denil val))
              (namespace-set-variable-value! '_that val)
              (namespace-set-variable-value! '_thatexpr expr)
              (newline)
              (tl2)))))))

これは典型的な read-eval-print loop (repl)。on-err の部分はエラー処理で、エラーが起きたらそっちに飛んで、最後にまた tl2 を呼びだしている。

read は Scheme のをそのまま使っている(前述の "[" と "]" を扱うためにリードテーブルはいじってある)。Arc も Scheme も S 式なので楽できるね :D

eval は当然 Arc から Scheme に変換しないと評価できないので arc-eval という関数でやってる。arc-eval については後述。

で、評価結果を write で表示。ここで、ac-denil って関数でラップしてるんだけど、この ac-denil や ac-niltree 等の関数は他の部分でも沢山ある。Arc をも含めた一般的な Lisp では、真偽値は t と nil で空リストも nil で表わすことになっている一方、Scheme だと真偽値は #t と #f で、空リストは '() で表わすことになっている。で、その辺の差異を ac-denil 等の関数を使って吸収してると思っておけば OK。

コードを見るとわかるけど、Arc のプロンプトで :a と入力すると Scheme に戻ってこれる。で再度 Arc に入るには (tl)。

eval/コンパイル (ac.scm)

次は arc-eval。arc-eval は単に Arc の式(expr)を ac という関数で Scheme の式に変換して Scheme の eval に渡しているだけ。つまり ac が Arc→Scheme のコンパイラになっている。ac は Arc Compile(r) の略?

(define (arc-eval expr) 
  (eval (ac expr '()) (interaction-environment)))

(define (ac s env)
  (cond ((string? s) (string-copy s))  ; to avoid immutable strings
        ((literal? s) s)
        ((eqv? s 'nil) (list 'quote 'nil))
        ((ssyntax? s) (ac (expand-ssyntax s) env))
        ((symbol? s) (ac-var-ref s env))
        ((ssyntax? (xcar s)) (ac (cons (expand-ssyntax (car s)) (cdr s)) env))
        ((eq? (xcar s) 'quote) (list 'quote (ac-niltree (cadr s))))
        ((eq? (xcar s) 'quasiquote) (ac-qq (cadr s) env))
        ((eq? (xcar s) 'if) (ac-if (cdr s) env))
        ((eq? (xcar s) 'fn) (ac-fn (cadr s) (cddr s) env))
        ((eq? (xcar s) 'set) (ac-set (cdr s) env))
        ; this line could be removed without changing semantics
        ((eq? (xcar (xcar s)) 'compose) (ac (decompose (cdar s) (cdr s)) env))
        ((pair? s) (ac-call (car s) (cdr s) env))
        (#t (err "Bad object in expression" s))))

で ac は Shiro Kawai さんの 2008/01/30 の日記にある通り、

よく、「Schemeはdefine, quote, if, lambda, set!の基本構文があれば残りの構文はそれで書ける」と言うけれど、ほんとにそれをやっちゃった。コアで定義されている構文は quote, quasiquote, if, fn (lambdaに相当), set (defineとset!に相当)だけ。

数値等のリテラルならそのまま。シンボルなら ac-var-ref で値を返す。シンタックスなら expand-ssyntax で展開して再度コンパイル。式がペアなら関数呼び出し。で、あとは quote, quasiquote, if, fn, set をそれぞれ専用の関数(ac-*)で処理してる。

シンプルな Lisp ですね :D

もし、Arc→Scheme の変換結果を見てみたければ、以下のように arc-eval をちょっと変更してやって、Scheme のコードを表示させるようにしてみるとよいかも。

#|
(define (arc-eval expr) 
  (eval (ac expr '()) (interaction-environment)))
|#

(define (arc-eval expr) 
  (let ((scm (ac expr '())))
    (display "scm: ")
    (newline)
    (pretty-print scm)
    (eval scm (interaction-environment))))

実行してみるとこんな感じ。

arc> 1
scm: 
1
1

arc> (+ 1 2)
scm: 
(ar-funcall2 _+ 1 2)
3

arc> (= a 1)
scm: 
((lambda ()
   'nil
   (begin (let ((| a| 1)) (namespace-set-variable-value! '_a | a|) | a|))))
1

arc> a
scm: 
_a
1

arc> (def fact (n)  
       (if (<= n 1) 1 (* n (fact (- n 1))))))
scm: 
((lambda ()
   'nil
   (ar-funcall3 _sref _sig '(n . nil) 'fact)
   ((lambda ()
      'nil
      (if (not (ar-false? (ar-funcall1 _bound 'fact)))
        ((lambda ()
           'nil
           (ar-funcall1 _disp "*** redefining ")
           (ar-funcall1 _disp 'fact)
           (ar-funcall1 _writec #\newline)))
        'nil)
      (begin
        (let ((| fact|
               (lambda (n)
                 'nil
                 (if (not (ar-false? (ar-funcall2 _<= n 1)))
                   1
                   (ar-funcall2
                     _*
                     n
                     (ar-funcall1 _fact (ar-funcall2 _- n 1)))))))
          (namespace-set-variable-value! '_fact | fact|)
          | fact|))))))
#<procedure: fact>

arc> (fact 5)
scm: 
(ar-funcall1 _fact 5)
120

とりあえず今日はここまで。次回は、個別のコンパイル関数(ac-fn等)やマクロ関連を見ていくつもり :D

Arc 開発環境 on Emacs (Meadow)

arc_emacs.png

Arc のフォーラムで見つけたEmacs での Arc開発環境の話。いずれ近いうちに Arc 専用のモードが出てくると思うんですが、それまでの一時しのぎとして lisp-mode を乗っ取る形で対応する方法です。以下は Meadow でのやり方ですが、Unix 系では .bat を .sh に置き換えてください。

Arc 起動用のバッチファイルを作成。例) C:\MzScheme\arc0\arc.bat

@echo off
cd /d C:\MzScheme\arc0
..\MzScheme -m -f as.scm

~/.emacs.el に

;;;
;;; for Arc
;;; see http://www.robblackwell.org.uk/?p=88
;;;
(setq inferior-lisp-program "C:\\MzScheme\\arc0\\arc.bat")
(setq inferior-lisp-prompt "^arc>+ *")
(add-to-list 'auto-mode-alist '("\\.arc$" . lisp-mode))
(put 'def 'lisp-indent-function 'defun)
(put 'if 'lisp-indent-function 0)
(put 'do 'lisp-indent-function 0)
;; TODO: more indent definitions

などと書くと普通の Lisp のように M-x run-lisp でArc を立ち上げたり、ソースコード中で C-x C-e で直前の S 式を評価したり、C-M-x で def を評価したりできます。インデントについてはやりかけなので、もっと追加する必要があります。

2008年02月05日

QStat Win32 binary (SVN rev.292 with ET:QW 1.4 fix)

qstat_win32bin.png

需要があるかどうかわかりませんが、QStat の Windows 32bit 用の exe を置いておきます。ET:QW 1.4 対応の patch をあててあります。 SVN で新しいバージョンが出ても zip 中の src/qstat.vcproj をコピーして、Visual Studio で開いてビルドすれば OK だとおもいます。こちらでは Microsoft Visual C++ 2008 Express Edition でコンパイルしました。Unix 系 OS でのコンパイル方法は GyaASE:Compiling Qstat を参照してください。

2008年02月09日

GyaASE: Added RtCW and TC:E

GyaASERtCWTC:E を追加しました。 マスターサーバ等の情報は、Wiki にまとめてあります。

それと、id 系のゲームのカラーコード (^数字) を表示できるように修正しました。カラーコードの実装は このサイト がとても役に立ちました。thx ayunyan さん :D

gyaase_rtcw_tce.jpg

2008年02月13日

Arc New Version - arc1

Arc新しいバージョンが出ました。arc1.tar ダウンロードして、MzScheme のディレクトリに展開で OK です。

今回の目玉?は

The most dramatic change is probably the ability to use x.y and x!y as abbreviations for (x y) and (x 'y) respectively.
ということなので、ちょっといじってみました。
Use (quit) to quit, (tl) to return here after an interrupt.
arc> prn.1
1
1
arc> =.x.1
1
arc> x
1
arc> +.1.2
3
arc> prn!hello
hello
hello
arc> (= lst '(1 2 (3 4) 5 (6)))
(1 2 (3 4) 5 (6))
arc> lst.0
1
arc> lst.2
(3 4)
arc> lst.2.1
(3 4)
arc> (lst.2 1)
4
うーん、なんかこれはやりすぎのような気がしないでもない...

2008年02月29日

Warsow 0.42 released!

Warsow 0.42 でたよ :D

Download Now!

Have fun! :D