Nendo.ReferenceManual RSSPLAIN

Related pages: Nendo kiyoka.2010_05_27 kiyoka.2010_05_01 kiyoka.2010_04_13 !kiyoka.blog.2011_10 !kiyoka.blog.2010_11 !kiyoka.blog.2010_09 !kiyoka.blog.2010_07 !kiyoka.blog.2010_05 !kiyoka.blog.2010_04
555554555554555555555555555555555555555555555555555555555455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555255525552555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555355555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555554444444444444444444555555555555555555555555555555555555555555555555555555555555555551115551555555555555555555555555555550005555555555555555555555555555555555555555555555555444445444444444444444455555555555555555555555555555555555555555
5

Nendo programming language Reference Manual

5

This is a reference manual of Nendo programming language. This is available only in Japanese.

5

 

5

 

5

はじめに

4

本マニュアルはRubyかSchemeをある程度把握されている方向けに書かれています。

5

Nendoに興味をお持ちの方のほどんとが、どちらかの言語でプログラミングを経験済みでしょう。

5

従って、RubyかSchemeの知識をお持ちのであれば、手短かに仕様を把握するためのリファレンスとして使えるはずです。

5

SchemeやLispの経験が少ない方は、先にNendo.TutorialでLispでRubyプログラミングする雰囲気をつかむとよいでしょう。

5

 

5

対応バージョン

4

本マニュアルは Nendo version 0.6.5に対応しています。

5

 

5

 

5

表記

5

本マニュアルでは、以下の記号を使用します。

5

a → b

5
 意味: aという式がbに評価されます。
5
 例:  (* 5 8)  →  40
5

<式>

5
 意味: <式> という箇所には任意の式を適用可能です。
5
 例: (set! <シンボル> <式>)
5

[式]

5
 意味: [式] は省略可能です。
5
 例: (list [<式>])
5
 例: (get-keyword <key> <kv-list> [<fallback>])
5

TODO …改訂予定の事柄…

5
 意味: 本マニュアルの改訂予定のTODOを記載しています。
5

 

5

構文

5

Nendoは、Lisp の大多数の方言と同じく、プログラムとデータを表すために、省略なくカッコでくくられた前置記法を使用します。

5
(+ 1 2 3)
5
 → 6
5
'(1 2 3)
5
 → (1 2 3)
5
(define alist '((key1 . "data1")
5
                (key2 . "data2")
5
                (key3 . "data3")))
5
alist
5
 → ((key1 . "data1") (key2 . "data2") (key3 . "data3"))
5

 

5

命名規約

5

Nendoの組込関数は、ブーリアン値(真偽値)を返す関数は "?" で終ります。変数やデータの内容を破壊する関数は "!" で終わります。

5

NendoからはRubyのメソッドも呼び出せますが、Rubyには Stringクラスのclearメソッドのように、値を破壊するにもかかわらず "!" が付いていないメソッドもあるので注意してください。

5
# 以下はRubyプログラム例
5
"str".chop!               # 規約通り
5
"str".is_a?( String )     # 規約通り
5
"str".clear               # 例外
5

 

5

字句規約

5

 

5

識別子

5

識別子には以下の文字を使用することができます。

5
 ! $ % & * + - . / : < = > ? @ ^ _ ~
5

以下は全て識別子として許されます。

5

lambda q list->vector soup + V17a <=? a34kTMNs the-word-recursion-has-many-meanings

5

 

4

但し、大文字のAからZで始まる識別子は、コンスタントを示します。

5

以下は全てコンスタントの識別子として扱われます。

5

Class File Object Enumerable::Enumerator

5

 

5

TODO NendoとRubyの識別子の変換ルールを明記する

5

 

5

キーワード

5

":" で始まるシンボルはキーワードという特殊なオブジェクトとなります。

5

":a" は "a" という値を持つキーワードとなります。

5

":" で "" のようなゼロ文字のキーワードも表現可能です。

5

キーワードはRubyのLispKeyword型として扱われます。

5

LispKeyword型はSymbol型やString型などと継承関係に無いため専用のNendo標準関数を使って相互変換する必要があります。( make-keyword関数、keyword->string関数、keyword?関数 など )

5

 

5

文字列

5

"" で囲われた "aaa" のようなトークンは文字列となります。

5

文字列には改行を含めることができ、

5
"1行目
5
2行目
5
3行目
5
"
5

のような文字列リテラルも記述可能です。

5

文字列リテラルはRubyのString型のオブジェクトとなります。

5

 

5

空白文字

5

空白はトークンを分離します。スペースと改行文字、タブ文字が空白として扱われます。

5

ソースコードの可読性を向上させるためのインデントなどに使用可能です。

5

 

5

コメント

5

";"  文字から行末まではコメントとして扱われます。

5

"#!" 文字から行末まではコメントとして扱われます。

5

コメントは、Nendoからは不可視となります。

5

 

5

括弧

5

"(" ")" 丸括弧と "[" "]" 角括弧はグループ化とリストの表記のために使用します。

5

括弧の種類が対応していなくてもNendoはエラーなく処理します。(ソースコードの可読性は落ちるのでお勧めしません)

5
 例1: 下記二つは等価
5
'(((((a)))))
5
'[[[[[a]]]]]
5
 例2: 可読性を上げるために [ ] を上手に使う
5
(let [(a 1)
5
      (b 2)]
5
  (+ a b))
5

 

5

リテラル(クォート)

5

(quote a) は a と評価されます。 (quote a) は 'a と略記してもかまいません。

5

下記は全てリテラルとなります。クォートしなくても元々リテラルのデータに対してクォートしても無効となります。

5
(quote a)
5
 → a (シンボル)
5
'a
5
 → a (シンボル)
5
 1
5
 → 1 (数値リテラル)
5
 "ABC"
5
 → "ABC" (文字列リテラル)
5
 '1
5
 → 1 (数値リテラル)
5
 '"ABC"
5
 → "ABC" (文字列リテラル)
5
 ''a
5
 → 'a    (クォート自身もクォートされることに注意)
5

 

5

正規表現リテラル

5

#/abc/ は Rubyの正規表現リテラル /abc/ と等価です。

5

大文字小文字判定を無視する #/abc/i というサフィックス指定もサポートしています。

5
#/[a-z]/
5
 → #/[a-z]/
5
#/Engine/i
5
 → #/Engine/i
5
(. #/[a-z]/ class)
5
 → Regexp
5

 

5

その他

5

 

5

ブーリアン値

5

#t、#f は真偽値の定数として扱われ、それぞれRubyのtrue,falseに対応します。

5

入力するS式としては#t,#f,true,falseのどの表記でもread可能です。

5

Nendoの標準の外部表現は#t,#fなので、writeは #t,#fで出力します。

5
'(#t true #f false)
5
 → (#t #t #f #f)
5

 

5

nil

5

Rubyのnilと等価です。nilは何度評価してもnilです。

5
nil
5
 → nil
5

 

5

デバッグ出力

5

#?=式  は式に副作用を与えずにデバッグ表示呼び出しを埋め込みます。

5

デバッグ表示は #?= で始まり、ソースコード上の位置(ファイル名と行番号)と評価結果を表示します。

5
replで実行した例
5
nendo> (define a 1)
5
1
5
nendo> #?=a
5
#?="(stdin)":2:a
5
#?-    1
5
1
5
nendo> #?=(+ a #?=a)
5
#?="(stdin)":3:(+ a (debug-print a "(stdin)" 3 'a))
5
#?="(stdin)":3:a
5
#?-    1
5
#?-    2
5
2
5
nendo> 
5

 

5

 

5

5

 

5

手続き呼び出し

5

(<関数> <引数1> <引数2> ...)

5

<関数>はNendoのビルトイン関数とユーザ定義関数を指定します。

5

例:

5
(+ 1 2 3)
5
 → 6
5
(define (func arg1) (+ 1 arg1))
5
 → #<Proc:0x7ef35c@(stdin):9>
5
(func 5)
5
 → 6
5

 

5

Rubyのメソッド呼び出し

5

(<クラスorインスタンス>.メソッド 引数1 引数2 ...)

5
 もしくは
5

(. <クラスorインスタンス> メソッド 引数1 引数2 ...)

5

 

5

例:

5
(require "date")
5
 → false
5
(Date.new 0)
5
 → 0000-01-01
5
(. Date new 0)
5
 → 0000-01-01
5
(. (Date.new 0) strftime "%x") 
5
 → "01/01/00"
5

 

5
(require "digest/md5")
5
 → false
5
(. (Digest::MD5.hexdigest "source text") split "")
5
 → ["2", "0", "f", "7", "9", "a", "1", "4", "1", "6", "6", "2", "6",
5
 "e", "e", "a", "c", "c", "0", "b", "d", "9", "a", "8", "d", "b", "8",
5
 "7", "f", "a", "a", "2"]
5

 

5

手続き

5

(lambda <仮引数部> <本体>)

5

R5RS Schemeの規定通り、固定個数の引数、任意個数の引数、n個以上の手続きを定義して返します。

5

実態はRubyのProc型のインスタンスです。

5

手続きはオブジェクトであるため、任意の変数に束縛したり、関数の返す値として扱えます。

5
(lambda (a) a)
5
 → #<Proc:0x000001010d9ab0@(stdin):15>
5
(define foo (lambda (a) a.to_s))
5
 → #<Proc:0x00000100c32de0@(stdin):21>
5
(foo.class)
5
 → Proc
5
((lambda (a) (+ 10 a)) 20)
5
 → 30
5

 

5

<仮引数部>に (a b . c) のように dot対を指定すると    N個〜無限個の引数を取る関数を定義可能です。

5

<仮引数部>に    x      のように シンボルを指定すると 0個〜無限個の引数を取る関数を定義可能です。

5

※ 詳細はR5RS Schemeの仕様を参照してください。

5

 

5

(define (関数名 <仮引数部>) <本体>)

5

指定した関数名に <仮引数部>と<本体>から作成された手続きを定義してバインドします。(lambda式に展開されます)

5

この関数定義は、関数内でもネストして仕様可能です。(関数内関数)

5

 

5

<仮引数部>は前述の lambda 構文でのルールに加えて、以下のオプショナル引数が指定可能です。

5

<仮引数部>に (a b :optional (c #f) (d 50)) のように指定すると 2個から4個の引数を取る関数を定義可能です。

5
nendo> (define (func1 a b :optional (c #f) (d 50))
5
         (list a b c d))
5
 → #<Proc:0x8fa18d0@(stdin):8>
5
nendo> (func1 1 2)
5
 → (1 2 #f 50)
5
nendo> (func1 1 2 3)
5
 → (1 2 3 50)
5
nendo> (func1 1 2 3 4)
5
 → (1 2 3 4)
5
nendo> (func1 1)
5
 → Error: wrong number of arguments for closure `func1'
5

 

5

 

5

束縛式

5

(define <シンボル> <式>)

5

トップレベルで評価されると、Nendoのグローバル変数が定義されます。

5

例:

5
(define symbol-1 100)
5
 → 100
5
symbol-1
5
 → 100
5

 

5

lambda内でdefineが評価されると、ローカル変数として束縛されます。仕組みは以下の通りです。

5

例:

5
(define (foo x)
5
  (define result '())
5
  (define (local-foo1 arg) arg)
5
  (define a 100)
5
  (define (local-foo2 arg) arg)
5
5
  (+ (local-foo1 1)))
5

コンパイル時にlambdaの内側のdefineはletrecに変換され、ローカル変数への束縛となります。

5
(define foo (lambda (x)
5
  (letrec ((result '())
5
           (local-foo1 (lambda (arg) arg))
5
           (a 100)
5
           (local-foo2 (lambda (arg) arg)))
5
    (+ (local-foo1 1)))))
5

 

5

(set! <シンボル> <式>)

5

定義済みの変数に代入(値の更新)を行ないます。

5

未定義の変数に適用しようとするとエラーとなります。

5

 

5

ローカル変数を束縛して、その変数が見えるブロックを作り出します。

5

let let* letrec let1 if-let1が使用できます。

5

※ 詳細はR5RS Schemeの仕様を参照してください。

5

TODO 詳細な解説追加

5

 

5

条件式

5

if cond case and or cond when unless が使用できます。

5

 

5

(if <条件> <式1> [<式2>])

5

条件が真(#fかnil以外)の場合 <式1> が評価され、偽の場合 <式2> が評価されます。

5

最後に評価された <式1> か <式2> が if の値となります。

5

条件が偽で <式2> が省略された場合は、nilが返ります。

5

 

5

(cond clause1 clause2 ...)

5

TODO 詳細な解説追加

5

 

5

(case key clause1 clause2 ...)

5

TODO 詳細な解説追加

5

 

5

(and <式> ...)

5

R5RS Schemeの仕様通りです。

5

 

5

(or <式> ...)

5

R5RS Schemeの仕様通りです。

5

 

5

(when <式> ...)

5

TODO 詳細な解説追加

5

 

5

(unless <式> ...)

5

TODO 詳細な解説追加

5

 

5

 

5

逐次式

5

(begin <式1> <式2> ...)

5

各 <式> が逐次的に左から右へと評価され,最後の <式> の (1個または複数個の) 値が返されます。

5

この式型は,入出力 などの副作用を順序どおりに起こすために使われます。

5

 

5

繰り返し

5

while until が使用できます。

5

TODO 詳細な解説追加

5

 

5

準引用 (準クォート)

5

Scheme R5RSの規定通りです。

5

"`"  "," ",@" をサポートしており、ネストも可能です。ネストの段数に制限はありません。

5

 

5

R5RS Scheme仕様書中の例:

5
`(list ,(+ 1 2) 4)
5
 → (list 3 4)
5
(let ((name 'a)) `(list ,name ',name))
5
 → (list a 'a)
5
`(a ,(+ 1 2) ,@(map abs '(4 -5 6)) b)
5
 → (a 3 4 5 6 b)
5

 

5

 

5

組み込みライブラリ

5

等価

5

(eq? <obj> <obj>)

5

objにはNendoのオブジェクトの他にもRubyのオブジェクトを指定することも可能です。

2

内部はRubyの演算子 equal? が使用されています。

5

 

5

(eqv? <obj> <obj>)

5

objにはNendoのオブジェクトの他にもRubyのオブジェクトを指定することも可能です。

2

内部はRubyの演算子 == が使用されています。

5

 

5

(equal? <obj> <obj>)

5

objにはNendoのオブジェクトの他にもRubyのオブジェクトを指定することも可能です。

2

objがlistなどの場合、再帰的に equal? を適用して比較します。そうでなければ eqv? 相当の Ruby演算子 == が使用されます。

5

 

5

数値

5

整数、実数を扱うことができます。

5

数値リテラルはそれぞれ、RubyのFixnum型、Float型になります。

5
nendo> (. 10 class)
5
 → Fixnum
5
nendo> (. 0.1 class)
5
 → Float
5

 

5

10進数

5

1 2 3 4 1000 45093245908 のような表記で10進数の整数リテラルをあらわします。

5

#dで始まる数値も10進数を表します。

5
nendo> -1
5
 → -1
5
nendo> +1
5
 → 1
5
nendo> #d10
5
 → 10
5
nendo> #d100
5
 → 100
5
nendo> #d0123456789
5
 → 123456789
5

 

5

2進数

5
nendo> #b0
5
 → 0
5
nendo> #b01
5
 → 1
5
nendo> #b10
5
 → 2
5
nendo> #b1111
5
 → 15
5

 

5

8進数

5
nendo> #o0
5
 → 0
5
nendo> #o7
5
 → 7
5
nendo> #o10
5
 → 8
5
nendo> #o11
5
 → 9
5

 

5

16進数

5
nendo> #xf
5
 → 15
5
nendo> #x10
5
 → 16
5
nendo> #xffff
5
 → 65535
5
nendo> #x10000
5
 → 65536
5

 

5

 

5

論理値

5

(not <obj>)

5

<obj>が#fの時のみ#tを返し、それ以外の場合は#fを返します。 

5

 

5

ペアとリスト

5

(list? <obj>)

5

R5RS Schemeの仕様通りです。

5

 

5

(pair? <obj>)

5

R5RS Schemeの仕様通りです。

5

 

5

(null? <obj>)

5

R5RS Schemeの仕様通りです。

5

 

5

(pair? <obj>)

5

R5RS Schemeの仕様通りです。

5

 

5

(cons <obj> <obj>)

5

R5RS Schemeの仕様通りです。

5

 

5

(cons* <list> . <obj>)

5

list* は cons* の別名です。

5
(list* 1 2 3)
5
 → (1 2 . 3)
5
(list* 1)
5
 → 1
5

 

5

(iota <数> [<開始値>])

5

rangeはiotaの別名です。

5

<開始値> から指定した数までの整数リストをリストにして返します。

5

<開始値>を省略すると ゼロを指定したこととみなされます。

5
(iota 10)
5
 → (0 1 2 3 4 5 6 7 8 9)
5
(iota 10 1)
5
 → (1 2 3 4 5 6 7 8 9 10)
5
(range 10 1)
5
 → (1 2 3 4 5 6 7 8 9 10)
5

 

5

(car <pair>)

5

consセルのcar部を返します。

5

 

5

(cdr <pair>)

5

consセルのcdr部を返します。

5

 

5

(set-car! <pair> <exp>)

5

consセルのcar部にexpを代入します。

5

 

5

(set-cdr! <pair> <exp>)

5

consセルのcdr部にexpを代入します。

5

 

5

(length <list>)

5

リストの長さを返します。

5

 

5

(last-pair <list>)

5

リストの最後のペアを返します。

5

 

5

(append <list>)

5

 

5

(reverse <list>)

5

 

5

memq   <obj> <list>

5

memv   <obj> <list>

5

member <obj> <list>

5

 

5

assq  <obj> <list>

5

assv  <obj> <list>

5

assoc <obj> <list>

5

 

5

assq-ref  <obj> <list>

5

assv-ref  <obj> <list>

5

assoc-ref <obj> <list>

5

 

5

 

5

シンボル

5

(symbol? <obj>)

5

objがシンボルなら #t を返します。

5

 

5

(string->symbol <obj>)

5

(intern <obj>) の別名です。

5

文字列をシンボルに変換します。

5

 

5

(symbol->string <obj>)

5

シンボルを文字列に変換します。

5

 

5

(gensym)

5

システムでユニークなシンボルを自動的に生成します。

5

実際には内部で ___gemsym__シーケンス番号 というシンボルが生成され、シーケンス番号がインクリメントされています。

5

 

5

 

5

キーワード

5

":" で始まるシンボルはキーワードという特殊なオブジェクトとなります。

5

":a" は "a" という値を持つキーワードとなります。

5

":" で "" のようなゼロ文字のキーワードも表現可能です。

5

参考: Gauche ユーザリファレンス: キーワードEXT

5

(keyword? <obj>)

5

(make-keyword <name>)

5

(keyword->string <keyword>)

5

(get-keyword <key> <kv-list> [<fall-back>])

5

 

5

 

5

文字列

5

Nendoでは文字列に関する組み込み関数は少ししか用意していません。

5

RubyのStringクラスのメソッドが豊富にあるため、それを使って文字列操作してください。

5

※ 将来、Schemeの文字列操作ライブラリをポーティングかもしれません。

5

 

5

(sprintf <フォーマット> <引数1> <引数2> ...)

5

 

5

(x->string <obj>)

5

objを文字列に変換します。

5

objにはRubyのオブジェクトを指定することもできます。その場合、メソッド to_s が適用されます。

5

 

5

(string-join <list> [<delim>])

5

文字列のリスト<list> を <delim> ではさんで連結します。

5

<delim> は省略可能です。省略時は空文字列が指定されたとみなされます。

5

 

5
(string-join '("aaa" "bbb" "ccc") "/")
5
 → "aaa/bbb/ccc"
5
(string-join '("aaa" "bbb" "ccc") "")
5
 → "aaabbbccc"
5
(string-join '("aaa" "bbb" "ccc"))
5
 → "aaabbbccc"
5

 

5

 

5

ベクタ

5

実態はRubyのArrayです。

5

R5RS Schemeの以下の関数群が使用可能です。

3
 vector? make-vector vector-length vector-copy vector-ref vector-set! vector-equal? list->vector vector-fill!vector->list
5

vector-refだけはR5RSから拡張されています。

5

参考: Gauche ユーザリファレンス: ベクタEXT

5

 

5

(vector-ref <vec> <index> [<fallback>])

5

<vec>から<index>で指定した要素を取り出だします。

5

<fallback>が指定されている場合、<index>が<vec>の範囲外であったなら <fallback>に指定した値が返ります。

5

<fallback>が指定されていなければ、RuntimeError例外が発生します。

5

 

5

 

5

ハッシュ

5

実体はRubyのHashです。

5

以下のHash操作関数群はHashから継承されたクラスに対しても適用可能です。

5

GaucheのAPI ( Gauche ユーザリファレンス: ハッシュテーブルEXT )のサブセットです。

5

 

5

(make-hash-table)

5

Hashのインスタンスを生成します。

5

 

5

TODO 他の関数も追記すること。

5

 

5

 

5

正規表現

5

GaucheのAPI ( Gauche ユーザリファレンス: 正規表現EXT )のサブセットとなっています。

5

 

5

(rxmatch <正規表現> <文字列>)

5

正規表現を文字列にマッチさせ、結果をRubyのMatchDataインスタンスで返します。

5
(rxmatch #/X(abc)X/ "XXXXabcXXXX")
5
 → XabcX
5
(define m (rxmatch #/X(abc)X/ "XXXXabcXXXX")) (m.class)
5
 → MatchData
5

 

5

(rxmatch-substring <MatchDataインスタンス> [<index>])

5

rxmatch関数で得られたMatchDataインスタンスから部分マッチを取りだします。

5
(define m (rxmatch #/X(abc)X/ "XXXXabcXXXX"))
5
 → XabcX
5
(rxmatch-substring m 0)
5
 → "XabcX"
5
(rxmatch-substring m 1)
5
 → "abc"
5

 

5

TODO 他の関数も追記すること。

5

 

5

 

5

例外

5

(raise <例外クラス> [<メッセージ>])

5

内部で Rubyの raise <例外クラス>,<メッセージ> に変換されます。

5

Rubyが持つ全ての例外クラスを指定可能です。

5

<メッセージ>を省略すると、"ファイル名:行番号 raised 例外クラス名"というメッセージを作り出します。

5

「ファイル名:行番号」はraiseの呼出元のファイル名と行番号に置換されます。

5

 

5

(error <"エラーメッセージ文字列"> [<S式>])

5

明示的にエラーを通知したい時に使用します。

5

エラーメッセージ文字列を表示して、RuntimeError例外を発生させます。

5

S式が指定されている場合は、そのS式を最後に表示します。

5
nendo> (error "Error: message 1")
5
 → Error: message 1
5

 

5
nendo> (error "Error: message 2" '(a b c d))
5
 → Error: message 2 (a b c d)
5

 

5

(errorf <"sprintfのフォーマット文字列"> [<値1>] [<値2>] ...)

5

明示的にエラーを通知したい時に使用します。

5

エラーメッセージ文字列を表示して、RuntimeError例外を発生させます。

5

sprintfと同様のフォーマット文字列を使用できます。

5
nendo> (errorf "Error: %s and %s" "A" "B")
5
 → Error: A and B
5

 

5

(guard (<変数> ((<条件1> <処理1>)  ... (else <処理n>))))

5

Gaucheの高水準のエラー処理フォームguradに似せたAPIです。

5

参考: Gauche ユーザリファレンス: 6.18 例外EXT

5

 

5

Gaucheと違い、組み込みコンディションクラスは全てRubyの例外クラスを使用してください。(RuntimeErrorやTypeErrorなど)

5

例:

5
(guard
5
 (exc (else (sprintf "Type is [%s]" (exc.class))))
5
 (error "This is RuntimeError"))
5
 → "Type is [RuntimeError]"
5

 

5
(guard
5
 (exc (else (sprintf "Type is [%s]" exc.class)))
5
 (+ (Array.new) 1))
5
 → "Type is [TypeError]"
5

 

5
(guard
5
    (exc ((exc.is_a? RuntimeError)
5
          "Type is [RuntimeError]")
5
         ((exc.is_a? TypeError)
5
          "Type is [TypeError]"))
5
  (+ (Array.new) 1)
5
  (error "This is RuntimeError"))
5
 → "Type is [TypeError]"
5

 

5
(let1 lst '()
5
  (guard
5
      (exc (else (push! lst 2)))
5
    (guard
5
        (exc (else (push! lst 1)
5
                   (error "Error occur(2)")))
5
      (error "Error occur(1)")))
5
  lst)
5
 → (2 1)
5

 

5

 

5

(unwind-protect <body> <cleanup>)

5

Gaucheの高水準のエラー処理フォームunwind-protectに似せたAPIです。

5

参考: Gauche ユーザリファレンス: 6.18 例外EXT

5

 

5

<body>、<cleanup>の順番で実行し、<body>の結果を返します。

5

body内で例外が挙がった場合、その例外がunwind-protectフォームを抜ける前に、cleanupが実行されます。

5

例外はunwind-protectフォームの外にはraiseされません。

5

 

5

例:

5
(unwind-protect
5
    1
5
  2))
5
 → 1
5

 

5
(unwind-protect
5
    (begin 1 2)
5
  3))
5
 → 2
5

 

5
(let* ([cnt 0]
5
       [result (unwind-protect
5
                   (begin
5
                     (set! cnt (+ cnt 1))
5
                     (set! cnt (+ cnt 2))
5
                     (+ 1.1 "str")
5
                     (set! cnt (+ cnt 3)))
5
                 (set! cnt (+ cnt 100)))])
5
  (list result cnt))
5
 → (#f 103)
5

 

5
(let* ([cnt 0]
5
       [result (unwind-protect
5
                   (begin
5
                     (set! cnt (+ cnt 10))
5
                     (set! cnt (+ cnt 01))
5
                     (guard (exc (else (set! cnt (+ cnt 100))))
5
                       (error "[RuntimeError]"))
5
                     (set! cnt (+ cnt 02)))
5
                 (set! cnt (+ cnt 1000)))])
5
  (list result cnt))
5
 → (113 1113)
5

 

5

 

5

入出力

5

 

5

(read)

5

(read-from-string)

5

(write <式>)

5

(write-to-string <式>)

5

read と write でオブジェクトの出力と入力が可能です。

5

writeで出力した外部表現は、readで元のデータを復元可能です。

5

すなわち、Nendoで作成したツリー構造のデータをデーターベースなどに保存・復元する手段として read と write 関数が利用できます。

5

但し、Rubyのオブジェクトについては、readで 元のデータを復元することはできません。

5

 

5

例:

5
nendo> (define v (read))
5
(a b c)
5
(a b c)
5
nendo> v
5
(a b c)
5
nendo> (write-to-string v)
5
"(a b c)"
5
nendo> (cdr (read-from-string "(symbol1 . symbol2)"))
5
(symbol1 . symbol2)
5
nendo> (cdr (read-from-string "(symbol1 . symbol2)"))
5
symbol2
5

 

5

(load <ファイルパス>)

5
 ファイルをNendoプログラムとしてロードして評価します。
5

 

5

(use <ライブラリ名>)

5
 ライブラリをNendoライブラリとしてロードして評価します。
5
 useはマクロ展開されます。例: (use a.b.c) は (load "a/b/c") にストレートに展開されます。
5

 

5

表記例:

5
(load "./myscript")
5
(load "srfi-1")
5
(load "text/html-lite")
5
(load "text/tree")
5

 

5
(use srfi-1)
5
(use text.html-lite)
5
(use text.tree)
5

 

5

Nnedoのソースコードの拡張子が省略された場合は、".nndc"(コンパイル済)ファイル ".nnd"(Nendoスクリプト)ファイル 両方の存在チェックが行われ、".nndc"が優先してロードします。

5

ファイルパスが "/" "./" "../"  で始まる場合は、*load-path* を参照せず、指定パスからロードされます。

5

それ以外の場合は *load-path* の先頭から該当するファイルを検索して最初に見つかったものをロードします。 

5

※ コンパイル済ファイルは 変換済みのRubyコードになっています。Rubyのevalで一気に評価するだけですのでロードが高速です。

5

 

5

*load-path*

5
 ロードパス文字列のリストです。グローバル変数です。
5
 load関数 と useマクロ が参照します。
5
 Nendoの起動時にRubyの$LOAD_PATHの情報がコピーされて作られます。
5

 

5

(add-load-path <パス名> [<afterp>])

5
 *load-path* に検索パス名を追加します。
5
 afterpが#fか、未指定の場合は、*load-path*の先頭に追加されます。 
5
 afterpが#tの場合は、*load-path*の末尾に追加されます。
5

 

5

例:

5
*load-path*
5
 →("/a" "/b" "/c")
5
(add-load-path "/first")
5
 →("/first" "/a" "/b" "/c")
5
(add-load-path "/last" #t)
5
 →("/first" "/a" "/b" "/c" "/last")
5
*load-path*
5
 →("/first" "/a" "/b" "/c" "/last")
5

 

5

 

5

マクロ

5

Nendoでは「伝統的なマクロ」と「健全なマクロ」の両方をサポートしています。

5

局所的なマクロ(let-syntax)もサポートしています。

5

マクロはコンパイルフェーズで適用されます。

5

1つのS式に対して全てのマクロ展開が完了するまで、再帰的にマクロ展開が行われます。

5

 

5

伝統的なマクロの定義

5

参考: Gauche ユーザリファレンス: 5.3 マクロの展開EXT

5

(macro <仮引数部> <本体>)

5

固定個数の引数、任意個数の引数、n個以上の引数が定義可能です。

5

 

5

マクロ展開

5

(maroexpand-1 <式>)

5

<式>に含まれるマクロのうち、最初に見つかったマクロを1回だけ展開し、結果を式で返します。

5

 

5

(maroexpand <式>)

5

<式>に含まれるマクロのうち、全てのマクロ展開が完了するまで再帰的にマクロ展開し、結果を式で返します。

5

 

5

例:

5
(set! twice (macro (x) (list 'begin x x)))
5
 → #<Nendo::LispMacro:0x000001011a6998@(stdin):8>
5
(macroexpand-1 '(twice (+ 1 1)))
5
 → (begin (+ 1 1) (+ 1 1))
5
(set! inc (macro (x) (list 'set! x (list '+ x 1))))
5
 → #<Nendo::LispMacro:0x000001015dd4a8@(stdin):10>
5
(macroexpand-1 '(inc a))
5
 → (set! a (+ a 1))
5
(macroexpand-1 '(twice (twice (inc a))))
5
 → (begin (twice (inc a)) (twice (inc a)))
5
(macroexpand   '(twice (twice (inc a))))
5
 → (begin (begin (set! a (+ a 1)) (set! a (+ a 1))) (begin (set! a (+ a 1)) (set! a (+ a 1))))
5

 

5

健全なマクロの定義

5

syntax-rulesはchibi-scheme 0.3の移植です。以下は、chibi-scheme 0.3のREADMEからの抜粋です。

5
 SYNTAX-RULES macros are provided by default, with the extensions from
5
 SRFI-46.  In addition, low-level hygienic macros are provided with
5
 a syntactic-closures interface, including SC-MACRO-TRANSFORMER,
5
 RSC-MACRO-TRANSFORMER, and ER-MACRO-TRANSFORMER.  A good introduction
5
 to syntactic-closures can be found at:
5
 
5
  http://community.schemewiki.org/?syntactic-closures
5
 
5
 IDENTIFIER?, IDENTIFIER->SYMBOL, IDENTIFIER=?, and
5
 MAKE-SYNTACTIC-CLOSURE and STRIP-SYNTACTIC-CLOSURES are provided.
5

 

5

(define-syntax  (syntax-rules <リテラル> <ルール1> [<ルール2>] ... )

5

大域的なマクロを定義します。

5

TODO 詳細な解説

5

 

5

(let-syntax  ((<変数1> (syntax-rules <リテラル> <ルール1> [<ルール2>] ... )) <変数2> (syntax-rules ...)) <式>)

5

局所的なマクロを定義します。

5

TODO 詳細な解説

5

 

5

 

5

コンパイル結果の表示

5

disasm

5

TODO 詳細な解説

5

 

5

Rubyライブラリのアクセス

5

NendoのLispコードは動的にRubyのコードに変換されながら動作しています。

5

そのため、容易にNendoからRubyの機能にアクセス可能です。

5

ここでは、RubyのライブラリをNendoからアクセスする方法を解説します。

5

 

5

準備

5

(require <Rubyのライブラリパス名>)

5

Rubyでプログラミングする時と同様に、利用するライブラリをrequireします。

5

常に #f を返します。

5
(require "pp")
5
 → #f
5

 

5

Rubyライブラリへのアクセス

5

Rubyライブラリへのアクセスは、.(ドット)演算子を使います。

5

.(ドット)の表記ルールには二種類あります。

5

 

5

([<Rubyのオブジェクト>].<メソッド名> <メソッド引数1> <メソッド引数2> ...)

5

Rubyのオブジェクトにメッセージを送ります。

5

なお、<Rubyのインスタンス> が省略された場合は、Kernel:: を指定したものとみなされます。

5
(define str "abc")
5
 → "abc"
5
(str.size)
5
 → 3
5
(str.upcase)
5
 → "ABC"
5
(str.chop)
5
 → "ab"
5
(.printf "%04d\n" 10)
5
0010
5
 → nil
5
(Digest::MD5.hexdigest "buzz buzz buzz")
5
 → "75ced500ba12b8e25475b9fce170a914"
5

<Rubyのインスタンス>には、識別子しか指定できません。("abc" や 100 のような数値リテラルは指定できません)

5

 

5

(. <Rubyのオブジェクト> <メソッド名> <メソッド引数1> <メソッド引数2> ...)

5

.(ドット)マクロは、第一引数に<Rubyのオブジェクト>を指定することができるため、上記の (<オブジェクト>.<メソッド>) 形式の表記に比べて柔軟なプログラミングが可能です。

5
(. "str" size)
5
 → 3
5
(. (symbol->string 'strstr) size)
5
 → 6
5
(. 100 to_s)
5
 → "100"
5
(. (. 1234 to_s) size)
5
 → 4
5

 

5

 

4

 

4

Lazy

4

Ruby 2.0からRubyのEnumerator::Lazyが使えます。

4

 

4

(ruby-lazy-enabled-platform?)

4

Enumerator::Lazyが使えるプラットフォームかどうかを判定します。

4

 

4

(ruby-lazy <ベクタ>)

4

vectorをEnumerator::Lazy化します。

4

無効なプラットフォームでは何もせず引数をそのまま返します。

4

 

4

(ruby-lazy? <ベクタ>)

4

vectorをEnumerator::Lazyかどうか判定します。

4

無効なプラットフォームでは常に #f です。

4

 

4

 

4

 

4

 

4

 

5

Rubyプログラムへの組み込み

5

RubyからNendoで書かれたプログラムを簡単に呼び出すことができます。

5

nendoモジュールをrequireします。

5
require 'nendo'
5

 

5

最初にRubyプログラムで Nendo::Coreクラスのインスタンスを生成し、Nendo::Core#loadInitFileメソッドで初期化します。

5
  core = Nendo::Core.new()
5
  core.loadInitFile
5

 

5

次に、Nendo::Core#loadメソッドでNendoで書かれたソースファイルを読み込むか、Nendo::Core#evalStr()メソッドでS式を直接評価することでNendoのプログラムを評価します。

5
  core.evalStr( '(define (plus10 num) (+ num 10))' )
5

 

5

例えば、上記のplus10という関数をRubyから呼び出すには、export-to-rubyを使って外から呼び出せるメソッドとして定義します。(Rubyの特異メソッド化)

5
  core.evalStr( '(export-to-ruby plus10)' )
5

 

5

exportしたメソッドを呼び出します。

5
  core.plus10( 1 )
5

 

5

 

5

完全なRubyプログラム例: [sample.rb]

5
#!/usr/local/bin/ruby
5
5
require 'nendo'
5
5
def main
5
  core = Nendo::Core.new()
5
  core.loadInitFile
5
  lst = [ "a", "b", "c",
5
          [ "d", "e" ].to_list
5
        ].to_list
5
  core.evalStr( '(define (sexpToString sexp) (write-to-string sexp))' )
5
  core.evalStr( '(export-to-ruby sexpToString)' )
5
  core.evalStr( '(use text.tree)' )
5
  core.evalStr( '(define (treeToString sexp) (tree->string sexp))' )
5
  core.evalStr( '(export-to-ruby treeToString)' )
5
  puts core.sexpToString( lst )
5
  puts core.treeToString( lst )
5
end
5
5
main
5

 

5

コマンドラインからの実行例

5
$ ruby sample.rb 
5
("a" "b" "c" ("d" "e"))
5
abcde
5

 

5

 

5

ライブラリ

5

ライブラリの読み込み

5

例えば、srfi-1を使う場合は下記のように記述します。

5
(load-library "srfi-1")
5
(load-library "text.tree")
5

別の書きかたとして、useを使うこともできます。

5
(use srfi-1)
5
(use text.tree)
5

 

5

サポートライブラリ

5
 (参考: SRFI 日本語訳EXTGauche ユーザリファレンス: TopEXT)
5

 

5

srfi-1

5

SRFI 1: リスト ライブラリEXT

5

 

5

srfi-2

5

SRFI 2: AND-LET*: ローカル束縛を伴う AND、条件付き LET* 特殊フォームEXT

5

 

1

srfi-9

1

SRFI 9: レコード型の定義EXT

1

 

5

srfi-26

5

SRFI 26: カリー化を伴わないパラメータ特殊化の記法EXT ( cut cute )

5

 

1

 

5

text.html-lite

5

Gaucheのtext.html-liteをポーティングしたものです。

5

Gauche ユーザリファレンス: text.html-lite - シンプルなHTMLドキュメントの構築EXT

5

 

5

text.tree

5

Gaucheのtext.treeをポーティングしたものです。

5

Gauche ユーザリファレンス: text.tree - 怠惰なテキスト構築EXT

5

 

5

debug.syslog

5

#?=式による出力先を syslog に変更します。

5

Webアプリケーションのデバッグ等に利用することを想定しています。

5

useすると無条件にデバッグ出力先がsyslogに変更されます。

5
(use debug.syslog)
5

 

5

debug.null

5

#?=式によるデバッグ出力を抑制します。

5

useを記述した以降の#?=式はデバッグ出力されなくなります。

5

#?=式を残したまま、ソースファイル単位でデバッグ出力を抑制したい場合に使います。

5
(use debug.null)
5

 

5

nendo.test

5

gauche.testのサブセットです。

5

nendo.testはgauche.testと違い、(test-error <例外>)部分にRubyの例外クラスを指定します。

5

Gauche ユーザリファレンス: gauche.test - 単体テストEXT

5

 

5

rfc.json

5

Gaucheのrfc.jsonのサブセットです。Gaucheのライブラリでportで受けとる部分が、NendoではFileオブジェクトに置き換わっています。

5

Gauche ユーザリファレンス: rfc.json - JSONのパーズと構築EXT

5

 

0

rfc.yaml

0

rfc.jsonとほぼ同様のAPIを持つYAMLサポートライブラリです。

0

 

5

util.list

5

Gaucheのutil.listのサブセットです。Nendoはマルチメソッドディスパッチ機能が無いので、assoc-refの引数指定順序が逆バージョン等は外してあります。

5

take* drop* take-right* drop-right* split-at* slices intersperse cond-list alist->hashtable hash-table->hash-table が使えます。

5

Gauche ユーザリファレンス: util.list - その他のリストライブラリEXT

5

 

5

util.match

5

chibi-schemeの(chibi match)のポーティング版です。

5

ほとんどGaucheのutil.matchと互換であるため、util.matchという名前で置いています。

5

Gauche ユーザリファレンス: util.match - パターンマッチングEXT

5

 

5

util.combinations

5

Gacuheのutil.combinationsのポーティング版です。

5

util.matchを使って実装されており、GaucheとAPI互換です。

5

Gauche ユーザリファレンス: util.combinations - 組み合わせEXT

5

 

5

 

5

Nendo専用ライブラリ

5

(with-open <ファイル名> <手続> [<openモード>])

5

ファイルをオープンして、ファイルオブジェクトを<手続>の引数に渡します。

5

<手続>から処理が戻ってきたら、自動的にファイルをクローズします。

5

内部はRubyのFile#openで実現されているため、<ファイル名>と<openモード>の仕様はRubyのリファレンスマニュアルを確認してください。

5

 

5

例:

5
nendo> (with-open "VERSION.yml" (lambda (f) (f.read)))
5
 → "---
5
 :major: 0
5
 :minor: 5
5
 :patch: 3
5
 "
5

 

5
nendo> (with-open "VERSION.yml"
5
          (lambda (f)
5
             (map
5
               (lambda (line) (line.chomp))
5
               (f.readlines))))
5
 → #("# ---" "# :major: 0" "# :minor: 5" "# :patch: 3")
5

 

5
nendo> (with-open "VERSION.yml"
5
          (lambda (f)
5
             (map
5
               (lambda (line) (line.chomp))
5
               f)))
5
 → #("# ---" "# :major: 0" "# :minor: 5" "# :patch: 3")
5

 

5
nendo> (require "open-uri")
5
 → !#f
5
nendo> (with-open "http://www.google.com/" (lambda (f) (f.read)))
5
 → #("<!doctype html><html><head><meta http-equiv=\"content-type\" ...以下略)
5

 

4

(readlines <IO型>)

4

RubyのFile#openや`with-open'関数でオープンしたファイルから1行ごとに読み込んでvectorとして返します。

4

例:

4
nendo> (with-open "VERSION.yml" (lambda (f) (vector->list (readlines f))))
4
 → ("---" ":major: 0" ":minor: 6" ":patch: 5")
5

 

4

※ Ruby 1.9.xでは、readlinesは一度にファイルの最後まで読み込みvectorを返しますが、

4

Ruby 2.0では readlines 全て読み込まず、Enumerator::Lazy 型の遅延vectorを返します。

4

 

4

(readchars <IO型>)

4

RubyのFile#openや`with-open'関数でオープンしたファイルから1文字ごとに読み込んでvectorとして返します。

4

例:

4
nendo> (with-open "VERSION.yml" (lambda (f) (vector->list (readlines f))))
4
 → ("-" "-" "-" "
4
    " ":" "m" "a" "j" "o" "r" ":" " " "0" "
4
    " ":" "m" "i" "n" "o" "r" ":" " " "6" "
4
    " ":" "p" "a" "t" "c" "h" ":" " " "5" "
4
    ")
4

※ Ruby 1.9.xでは、readcharsは一度にファイルの最後まで読み込みvectorを返しますが、

4

Ruby 2.0では readchars 全て読み込まず、Enumerator::Lazy 型の遅延vectorを返します。

4

 

4

 

5

その他Scheme R5RSとの違い

5

キーワードのサポート

5

Gaucheと同様に :abcde のような表記のキーワード型をサポートしています。

5

参考: Gauche ユーザリファレンス: キーワードEXT

5

 

5

+演算子

5

数値同士だけでなく、文字列同士の連結にも使用できます。

5

 

5

未実装のスペシャルフォーム

5

letrec-syntax

5

 

5

遅延評価

5

make-promise delay force は実装されていません。

5

 

5

継続

5

継続は実装されていません。すなわち、以下の機能は使えません。

5

call-with-current-continuation dynamic-wind

5

 

5

eval

5

(eval <式>) の形式しかありません。

5

環境は1種類しかないため、環境を指定する第二引数は指定できません。

5

 

5

ポート

5

portという概念は未実装です。

5

NendoではRubyのファイルという機能があるので、それをportの代わりに使ってプログラミングします。

5

以下のような例でportと同様のコーディングが可能です。

5
(STDOUT.print)
5
(let1 f (StringIO.new "abc")  f.read)
5

 

5

 

5

未決定事項

5

char- 系の関数は実装されないかも

5

TODO 文字列(string)と文字(char)の扱いは検討中。SchemeとライブラリAPI互換にすることは可能だと考えられるが、Rubyが1.9で文字の扱いを変更したのに伴い、どのような整合を取るのが良いのかは要検討。

5

 

5

Rubyのオブジェクトの外部表現

5

TODO RubyのHashオブジェクトなどを含むデータをNendoからwriteした場合、どのような外部表現にするかは未定。

5

当然 read すれば元のオブジェクトの状態を極力復元する必要がある。

5

最悪は、Rubyのオブジェクトの外部表現はNendoでは責任を持たないという案もありえる。

5

 

5

 

5

...comment disabled...