2009/09/26

Parrot についてのメモ

とりあえずメモ。

http://www.parrot.org/

PIR

PIR(Parrot Intermediate Representation)は Parrot の中間レベルのアセンブリ言語。

使える機能としては

  • ガーベッジ・コレクション
  • コンティニュエーション
  • レキシカルバインディング
  • オブジェクト指向
  • 多値
  • 例外
  • テイルコール
  • コルーチン
  • UTF-8 UTF-16 のサポート

などがある。

.sub hello
.local pmc en, jp
$P0 = newclass "EnHello"
$P1 = newclass "JpHello"
en = new "EnHello"
jp = new "JpHello"
en.'hello'("Parrot")
jp.'hello'(utf8:unicode:"おーむちゃん")
.end

.namespace [ "EnHello" ]

.sub 'hello' :method
.param string name
$S0 = "Hello, "
$S0 .= name
$S0 .= "!"
say $S0
.end

.namespace [ "JpHello" ]

.sub 'hello' :method
.param string name
$S0 = utf8:unicode:"こんにちは、"
$S0 .= name
$S0 .= utf8:unicode:"♪"
say $S0
.end
変数

レジスタ

$[INSP]\d+

変数(名前付き変数)

named variables: int, num, string, pmc

pmc = "PolyMorphic Container".

= "Parrot Magic Cookies"?

named variables のスコープはサブルーチン。

symbol variables のスコープもサブルーチン。

グローバルにしたい場合は set_global と get_global を使う。

= でキャストが可能

$P0 = new 'Integer'
$P0 = 5
$S0 = $P0

PIR のコードは全て .sub と .end の中(サブルーチン)に書く。

メイン関数は :main で指定。指定がなければファイルの先頭の .sub がメイン関数となる。

say できるためには .sub get_string :vtable :method

$S0 = self.'symbol-name'() .return($S0) .end を実装する。

ネームスペースとスコープ
  • .HLL 小文字で指定する
  • set_hll_global
  • get_hll_global
  • get_hll_namespace
  • get_root_namespace
  • 'export_to'

異なる namespace の sub は直接呼べない。ただし root namespace(.namespace [])の sub は直に呼べる。

.sub main
## 呼べない fb()
## 呼べない $P0 = get_global "fb"
$P0 = get_global ["a";"b"], "fb"
$P0()
froot()
.end

.namespace []
.sub froot
say "in root"
.end

.namespace ["a"]
.sub fa
say "in a"
froot()
.end

.namespace ["a";"b"]
.sub fb
say "in b"
## 呼べない fa()
## 呼べない $P0 = get_global "fa"
## 呼べない $P0 = get_global ["a"], "fa"
## 呼べない $P0 = get_root_global "fa"
## 呼べない $P0 = get_root_global ["a"], "fa"
## 呼べない $P0 = get_hll_global "fa"
$P0 = get_hll_global ["a"], "fa"
$P0()
froot() # root ネームスペースは直に呼べる
.end
サブルーチン

初期化サブルーチンの雛形。

.namespace []
.sub '' :anon :init :load
# ...
.end

:anon はネームスペースに登録されない。gensym に似てる。

クロージャ

:outer で環境を指定した sub を .const 'Sub' でつかまえて newclosure する。

.lex するレジスタは pmc でないといけない。

他のネームスペースの sub をつかまえるときは find_name を使う。

.sub main
$P0 = make_closure(10)
$P1 = make_closure(100)
$P0() # 11
$P1() # 101
$P0() # 12
$P1() # 102
.end

.sub make_closure
.param pmc n
.lex 'n', n
.const 'Sub' foo_sub = "foo"
$P1 = newclosure foo_sub
.return($P1)
.end

.sub foo :outer(make_closure)
$P0 = find_lex 'n'
$P0 += 1
say $P0
.end
ダイナミックスコープ

outer の指定は不要で find_dynamic_lex と store_dynamic_lex でアクセスする。

.sub foo
.local pmc var
var = box "foo value"
.lex 'var', var
say var
bar()
say var
.end

.sub bar
.local pmc var
var = find_dynamic_lex 'var'
say var
$P1 = box "new value"
store_dynamic_lex 'var', $P1
.end
多値

.return に複数の値を指定し、複数の変数を括弧で左辺とする。

.sub main
($P0, $P1, $P2) = foo()
say $P0
say $P1
say $P2
.end

.sub foo
.return(1, "a", utf8:unicode:"")
.end
文字列

ユニコードが使える。

utf8:unicode:"UTF-8 の文字列"
utf8:unicode 意外にも utf16:unicode, ascii, binary も使える。
マクロ

.macro から .endm で定義する。引数は . を付けると展開される。呼び出し時は .macro_name(arg1,...) と、やはり . を付ける。

.macro macro_name(arg1, arg2, ... argn)
中身
.endm
インクルードとロード
  • .include
  • load_bytecode
Evaluating a Code String
.sub main
.local pmc compiler, generated1, generated2
.local string source1, source2
compiler = compreg "PIR"
source1 = ".sub foo\n$S1 = 'in eval 1'\nsay $S1\n.end"
source2 = ".sub foo\n$S1 = 'in eval 2'\nsay $S1\n.end"
generated1 = compiler(source1)
generated2 = compiler(source2)
generated1() # in eval 1
generated2() # in eval 2
foo() # in eval 2
$P1 = generated1
$P1() # in eval 1
.end
トレース
PASM を出力する

-o オプションで PASM を出力できる。-o - で標準出力に PASM をはく。

parrot -o - a.pir

2009/09/19

Prattor の namespace

異なる namespace の sub は直接呼べない。ただし root namespace(.namespace [])の sub は直に呼べる。

で、あってる?

.sub main
## 呼べない fb()
## 呼べない $P0 = get_global "fb"
$P0 = get_global ["a";"b"], "fb"
$P0()
froot()
.end

.namespace []
.sub froot
say "in root"
.end

.namespace ["a"]
.sub fa
say "in a"
froot()
.end

.namespace ["a";"b"]
.sub fb
say "in b"
## 呼べない fa()
## 呼べない $P0 = get_global "fa"
## 呼べない $P0 = get_global ["a"], "fa"
## 呼べない $P0 = get_root_global "fa"
## 呼べない $P0 = get_root_global ["a"], "fa"
## 呼べない $P0 = get_hll_global "fa"
$P0 = get_hll_global ["a"], "fa"
$P0()
froot() # root ネームスペースは直に呼べる
.end

2009/09/16

Parrot でダイナミックスコープ

Parrot 1.6.0 がリリースされてダイナミックスコープが使えるようになった♪

.sub foo
.local pmc var
var = box "foo value"
.lex 'var', var
say var
bar()
say var
.end

.sub bar
.local pmc var
var = find_dynamic_lex 'var'
say var
$P1 = box "new value"
store_dynamic_lex 'var', $P1
.end

2009/09/07

Forth の direct threaded code

こんなぐあいに r15 に _doLIST のアドレスをいれておいて call してる。これだと無駄にレジスタ使っちゃうのが問題(Forth の実装でそんなにレジスタ使ったりしないけどね)。call _doLIST としたいところなんだけど、そのやり方が分からない。

        ;; nop x 5 call r15
DOCOL_CODE EQU 0xd7ff419090909090

%macro NEXT 0
lodsq
jmp rax
%endmacro

defcode "EXIT", 0, exit
POPRSP rsi
NEXT

defword ":", 0, colon
dq _WORD
dq header
;; あらかじめ mov r15, _doLIST してある。
dq lit
dq DOCOL_CODE
dq comma
dq latest, fetch, hidden
dq rbrac
dq exit

_doLIST:
PUSHRSP rsi
pop rsi
NEXT

_start:
mov r15, _doLIST

Clojure-contrib のインストールとビルド

http://richhickey.github.com/clojure-contrib/

Clojure 1.0 を使う場合は clojure-1.0-compatible ブランチにしなければいけない。ブランチのかえかたはこれでいいのか? git 難しいな。

git clone git://github.com/richhickey/clojure-contrib.git
cd clojure-contrib
git checkout origin/clojure-1.0-compatible
git checkout -b clojure-1.0-compatible
ant -Dclojure.jar=/usr/share/java/clojure.jar

~/.emacs で swank-clojure-extra-classpaths を指定する。

(setq swank-clojure-extra-classpaths
(list "~/letter/clojure/lib/clojure-contrib/clojure-contrib.jar"))

2009/09/05

Clojure のセットアップ

Clojure をセットアップする。 Debian にはパッケージがあったので apt-get する。 Emacs + SLIME のために git clone を2つ。

sudo apt-get install clojure
cd ~/letter/clojure/lib
git clone git://github.com/jochu/clojure-mode.git
git clone git://github.com/jochu/swank-clojure.git

~/.emacs に clojure-mode と swank-clojure の設定を追加。

(setq slime-net-coding-system 'utf-8-unix)
;;;; Clojure
;; clojure-mode
(add-path "~/letter/clojure/lib/clojure-mode")
(require 'clojure-mode)
;; swank-clojure
(add-path "~/letter/clojure/lib/swank-clojure/src/emacs")
(setq swank-clojure-jar-path "~/letter/clojure/git/clojure/clojure.jar")
(require 'swank-clojure-autoload)
(setq swank-clojure-extra-classpaths
(list "~/letter/clojure/git/clojure-contrib/clojure-contrib.jar"))

SLIME 自体の設定は省略、と思ったけどついでにのせておこう。

~/.emacs

;;;;;SLIME
(setq common-lisp-hyperspec-root "file:/usr/share/doc/hyperspec/")
(add-path "~/letter/lisp/clbuild/source/slime")
(add-path "~/letter/lisp/clbuild/source/slime/contrib")
;;(setq slime-communication-style :fd-handler)
;;(setq slime-communication-style :spawn)
;;(setq slime-communication-style :sigio)
(setq slime-net-coding-system 'utf-8-unix)
(setq slime-lisp-implementations
`((sbcl ("sbcl") :coding-system utf-8-unix)
(ccl ("/home/ancient/local/opt/ccl/lx86cl64")
:coding-system utf-8-unix)
;; (ccl ("/home/ancient/letter/lisp/clbuild/clbuild"
;; "--implementation" "ccl" "lisp")
;; :coding-system utf-8-unix)
(deb-sbcl ("/usr/bin/sbcl") :coding-system utf-8-unix)
(clisp ("/home/ancient/letter/lisp/clbuild/clbuild"
"--implementation" "clisp" "lisp")
:coding-system utf-8-unix)
(abcl ("/home/ancient/letter/lisp/abcl/abcl")
:coding-system utf-8-unix)
(acl ("/home/ancient/local/opt/acl81_express/alisp")
:coding-system utf-8-unix)
(cmucl ("lisp"))))
(require 'slime-autoloads)
;;(add-hook 'lisp-mode-hook
;; (lambda ()
;; (cond ((not (featurep 'slime))
;; (require 'slime)
;; (normal-mode)))))
(setq slime-truncate-lines nil)
(setq slime-enable-evaluate-in-emacs t)
(defun my-slime-complete-form ()
(interactive)
(condition-case nil
(slime-complete-form)
(error
(save-excursion
(insert
(multiple-value-bind (ops arg-indices points)
(slime-enclosing-form-specs)
(run-hook-with-args 'slime-autodoc-hook ops arg-indices points)
(multiple-value-bind (cache-key retrieve-form)
(slime-compute-autodoc-rpc-form ops arg-indices points)
(slime-eval retrieve-form))))))))
(add-hook
'slime-mode-hook
(lambda ()
(global-set-key [(control ?\;)] 'slime-selector)
(define-key slime-mode-map "\C-c\C-s" 'my-slime-complete-form)
(define-key slime-repl-mode-map "\C-c\C-s" 'my-slime-complete-form)
(loop for (key command) in
'(([(control ?c) ?\;] slime-insert-balanced-comments)
([(control ?u) (control ?c) ?\;] slime-remove-balanced-comments)
([(control ?c) ?\;] slime-insert-balanced-comments)
("\C-m" newline-and-indent)
("\C-i" slime-indent-and-complete-symbol))
do (define-key slime-mode-map key command))))
(add-to-list 'auto-mode-alist '("\\.asd$" . common-lisp-mode))
(eval-after-load "slime"
'(progn
(slime-setup '(slime-repl
slime-asdf
slime-fancy
slime-indentation
slime-references
slime-tramp
slime-banner))
(setq slime-complete-symbol*-fancy t)
(setq slime-complete-symbol-function 'slime-fuzzy-complete-symbol)
(setq lisp-indent-function 'cl-indent:function)
;; g000001 さん作
(progn
(defun gcode-lookup ()
"カーソル位置のシンボルをGoogle Codeで検索(lisp決め打ち)"
(interactive)
(browse-url
(format "http://www.google.com/codesearch?q=%s+lang:%s+file:\\.%s$&hl=ja&num=20"
(thing-at-point 'symbol) "lisp" "lisp")))
(define-key slime-mode-map
[(control ?c) (control ?d) ?g] 'gcode-lookup))))

C-- M-x slime clojure

teepeedee2

teepeedee2 をちょこっと動かしてみた。

(eval-when (:compile-toplevel :load-toplevel :execute)
(require :teepeedee2))

(defpackage :try-teepeedee2
(:use :cl :tpd2 :tpd2.ml.html))

(in-package :try-teepeedee2)

(defpage
"/hello" ; URL
((name "Friend")) ; リクエストパラメータ
(values ; ボディとヘッダの多値を返す
(<html ; タグはマクロ
(<body
(<h1 "こんにちは " name)
(<div "まみむめも♪")))
(byte-vector-cat ; ヘッダはバイトベクタで返す
tpd2.webapp::+http-header-html-content-type+ ; "Content-Type: text/html;charset=utf-8"
"Xxx: Aaa" tpd2.io:+newline+))) ; 他のヘッダも付けるときはこんなふう

(defun start ()
"Visit http://localhost:8080/hello"
(http-start-server 8080)
(sb-thread:make-thread #'event-loop :name "tpd2"))
;;(start)

私の環境は clbulid で用意しているため、いくつかのライブラリてバージョンがあってなかった模様。以下のように適当にソースをいじった。


diff --git a/src/lib/utils.lisp b/src/lib/utils.lisp
index f937fe0..6e5f718 100644
--- a/src/lib/utils.lisp
+++
b/src/lib/utils.lisp
@@ -49,5 +49,6 @@
(with-input-from-string (*standard-input* (force-string string)) (read-safely)))

(defun backtrace-description (err)
- (format nil "ERROR ~A:~&~A" (with-output-to-string (*standard-output*) (describe err))
- (trivial-backtrace:backtrace-string)))
+ (format nil "ERROR ~A:~&~A" (with-output-to-string (*standard-output*) (describe err))
+ (with-output-to-string (out)
+ (trivial-backtrace:print-backtrace-to-stream out))))
diff --git a/src/webapp/default-site.lisp b/src/webapp/default-site.lisp
index 2d8fc33..ac3a660 100644
--- a/src/webapp/default-site.lisp
+++
b/src/webapp/default-site.lisp
@@ -3,3 +3,4 @@
(defsite *default-site*)
(setf *current-site* *default-site*)

+
\ No newline at end of file
diff --git a/src/webapp/js-library.lisp b/src/webapp/js-library.lisp
index 9a1899f..05aa768 100644
--- a/src/webapp/js-library.lisp
+++
b/src/webapp/js-library.lisp
@@ -50,7 +50,7 @@

(defun channels-get-param ()
(let ((lines (make-array)))
- (ps:doeach (channel *channels*)
+ (ps:dolist (channel *channels*)
(lines.push (+ (encode-U-R-I-component channel) "|" (aref *channels* channel))))
(return (+ ".channels.=" (lines.join ";")))))

2009/09/03

もうひと月もの間ブログ投稿してなかった。

  • 『Lisp in Small Pieces』を読んで Parrot で Lisp を実装しようとした。
  • その息抜きにアセンブリで Forth を実装しようとした。
  • 夏休みが終った。
  • 祖父が他界した。
  • 仕事は出口がなさそう。
  • ふと思えば去年の秋からずっと鬱のような気がする。

いま気づいたけどこんなのあるんだ。

cl-rdbms

(eval-when (:compile-toplevel :load-toplevel :execute)
(require :cl-rdbms))

(defvar *test-database*
(make-instance 'rdbms:postgresql-postmodern
:connection-specification '(:database "junk"
:user-name "ancient"
:password "password")))

(rdbms:with-transaction* (:database *test-database*)
(rdbms:execute "select 1, 'まみむめも♪'"))

htmlize なおってる♪