(rontolisp) docs

データ型

TypeExampleDescription
Integer42, -5, 1,000, #xff, #o777, #b101064ビット符号付き整数。オーバーフロー時には自動的に多倍長整数へ昇格します(インタプリタおよびJVM)。WASMでは31ビット符号付き整数です。#x/#o/#b は16進/8進/2進リテラルとして読み込まれます
Ratio1/3, -2/5正確な有理数(Common Lispのratio)。常に正規化され、3つのバックエンドすべてでサポートされます
Double3.14, -0.5, 3,000.50, 1d0, 6.02e2364ビット浮動小数点数
String"hello"文字列リテラル
Character#\a, #\Space, #\Newline文字リテラル(#\ の後ろに字形または標準名: Space, Newline, Tab, Return, Page, Backspace, Nul, Rubout)。WASMバックエンドは文字列をバイト単位でインデックスするため、非ASCII文字は対象外です
Symbolx, foo識別子
Keyword:foo, :bar: で始まる自己評価シンボル
Nilnil偽 / 空リスト
Tt
Pipi定数 π。double値 3.141592653589793 として読み込まれます
Fixnum 範囲most-positive-fixnummost-negative-fixnumpi と同様に自己評価する整数として読み込まれます。値はバックエンド依存です (WASM の fixnum はアンボックスの 31 ビット参照、インタープリターと JVM バックエンドは 64 ビット long)
Cons(1 2 3), (a . 1)consセルで構築された連結リスト。(a . b) は単一セルを表すドット対記法
Function#'car, (lambda (x) x)#'/function/lambda で得られる関数オブジェクト
Array#(1 2 3), #2A((1 2) (3 4))任意の階数の固定サイズ配列(階数1はベクタ)。#(...)#nA(...) は自己評価される配列リテラルです
Hash table(make-hash-table)構造的(equal)なキーを持つ可変のキー/値テーブル

数値リテラルでは、整数部の桁の間に区切り文字として , を使用できます。たとえば 1,0001000 として読み込まれ、(+ 1,000 100)1100 に評価されます。カンマが区切り文字として扱われるのは2つの数字の間にある場合のみで、パース前に除去され、3つのバックエンドすべてに適用されます。これは , がunquote文字であるCommon Lisp(ここではサポートされません)とは異なります。

浮動小数点リテラルにはCommon Lispの指数マーカーを付けられます。仮数部の後ろに e, s, f, d, l のいずれか(大文字小文字を区別しません)、省略可能な符号、そして指数を続けます。例: 1d0, 1e0, 1.5d3(1500.0)、-2e-3, 6.02e23。これは3つのバックエンドすべてで機能します(リーダレベルの機能です)。Common Lispとは異なり、rontolispは浮動小数点型を1種類しか持たないため、すべてのマーカーは同じ 64ビットdoubleとして読み込まれます -- single/short/long-floatの区別(1d01e01f0)は保持されず、*read-default-float-format* もありません。指数の数字が後続しないマーカーは浮動小数点ではありません。1d1d0x は(1+ と同様に)数値ではなくシンボルとして読み込まれます。

インタプリタおよびJVMコンパイラでは、整数演算が暗黙のうちにラップすることはありません。long 演算(+, -, *, /, 1+, 1-, abs, ...)がオーバーフローする場合、結果は自動的に任意精度の多倍長整数へ昇格し、long より大きい整数リテラルは多倍長整数として読み込まれます。long に再び収まる多倍長整数の結果は再度降格されるため、値は単一の正規表現を保ちます。たとえば (defun fact (n) (if (= n 0) 1 (* n (fact (- n 1))))) の場合、(fact 32) は正確な値 263130836933693530167218012160000000 を返します。WASMコンパイラはこれをサポートしません。整数は31ビット(i31ref)に制限され、オーバーフローはラップします。

3つのバックエンドすべてがCommon Lispのratio(正確な有理数)をサポートします。1/3 はratioリテラルとして読み込まれ、割り切れない整数除算は切り捨てる代わりにratioを返します:

> 1/3
1/3
> (/ 1 2)
1/2
> (+ 1/2 1/3)
5/6
> (/ 1 2.0)
0.5
> (float 1/2)
0.5

ratioの結果は常に正規化されます。gcdで約分され符号は分子に付き(2/41/2 として読み込まれます)、分母が1に約分される場合は整数へ降格されます((/ 10 2)5(+ 1/2 1/2)1)。算術、比較(= < > <= >=)、eq/eqlabs/min/max/1+/1-/signum、述語(numberp, rationalp, zerop, plusp, minusp)、truncate/floor/ceiling/round、整数の指数を持つ expt((expt 2 -1)1/2)、numerator/denominator はすべてratioを扱います。浮動小数点が混ざると浮動小数点への伝播に切り替わります。単項の (/ x) は逆数です((/ 2)1/2)。

バックエンドごとに、各成分は整数表現に従います。インタプリタおよびJVMコンパイラは多倍長整数を使用し(巨大な分子/分母のratioも正確なまま)、WASMコンパイラは他のすべての整数演算と同様に、オーバーフロー昇格なしで31ビット i31 の範囲に保ちます。コンパイルされた read/load のために発行されるランタイムリーダはratioリテラルをパースしません(実行時に読まれる 1/3 トークンはシンボルになります)。また modevenp/oddpgcd/lcmisqrt は整数専用のままです。

コメント・フィーチャー条件・*features*

; の行コメントに加えて、リーダは Common Lisp の #| ... |# ブロックコメント(標準どおりネスト可)と #+/#- フィーチャー条件をサポートします。#+expr form はフィーチャー式が成立するときだけ form を残し、#-expr form は成立しないときだけ残します。フィーチャー式はフィーチャー名、または (and ...)/(or ...)/(not ...) の組み合わせです(裸のシンボルでもキーワードでもよく、大文字小文字を区別しません)。アクティブなフィーチャーはすべてのバックエンドで :rontolisp、加えてバックエンドを識別するフィーチャー — :rontolisp-interpreter:rontolisp-jvm:rontolisp-wasm — が1つで、1つのソースファイルでバックエンドごとのコードを選択できます。変数 *features* はアクティブなフィーチャーのリストとして読まれます(pi と同様に read 時に固定される quote されたキーワードのリストで、代入はできません)。

注意点:

  • 読み取りはフロントエンドで一度だけ行われます。インタプリタは :rontolisp-interpreter で読み、.class/.wasm へのコンパイルは :rontolisp-jvm/:rontolisp-wasm で読むため、コンパイル済みプログラムのフィーチャーセットはコンパイル時に固定されます。コンパイル時の load/require/asdf:load-system インクルードで取り込まれるファイルも、同じターゲットフィーチャーで読まれます。
  • 不成立の #+/#- ガードでスキップされるフォームは、パースされずに生の文字レベルでスキップされるため、rontolisp がサポートしない構文を使っていても構いません(それがガードの目的です)。
  • #. の read 時評価はサポート されず、明確な read エラーになります。唯一の例外は .asd ファイルで、そこでは #. フォームは警告付きでスキップされます(システムガイドを参照)。
  • コンパイル済みプログラムのランタイムリーダ(readread-from-string、実行時の load)は、backquote と同様に、ブロックコメントとフィーチャー条件を知りません — コンパイル済み read/load の制限を参照してください。
  • :common-lisp は意図的に *features*含まれません: rontolisp はサブセットであり、準拠実装ではないからです。

ドット対・連想リスト・属性リスト

リーダはCommon Lispのドット対記法をサポートします。(a . b) は car が a、cdr が b の単一のconsセルを表し、(a b . c) は最後の cdr が nil ではなく c になるリストです。連想リスト(alist)のリテラルはこの記法で書きます:

ドット末尾はバッククォートテンプレートでも機能し(`(a . ,x)cons の連鎖に展開されます)、コンパイルされたプログラムのランタイムリーダも同じ記法をパースするため、"(a . 1)"read/read-from-string はすべてのバックエンドで同一に動作します。リストの外に単独で現れる . はCommon Lispと同様に読み取りエラーです。またバッククォートテンプレート内では ,@ とドット末尾を組み合わせることはできません。呼び出し位置のドット末尾((+ 1 . 2) など)は3つのバックエンドすべてでエラーになります -- ドット対はデータとしてのみ意味を持ちます。

連想リスト関数群 -- assocassoc-ifrassocaconspairliscopy-alist -- は3つのバックエンドすべてで機能します。assocrassoc は既定では eql で比較し、member と同様に省略可能な :test/:key キーワード(:test は関数指定子。 文字列キーには #'equal など。:key は比較の前に各ペアの car/cdr へ適用される セレクタ)を受け付けます:

属性リスト(plist) -- (:a 1 :b 2) のようにインジケータと値が交互に並ぶ平坦なリスト -- は、キーワードを使うalistの親戚です。getf はインジケータに対応する値を読み取り(2引数のみ: &optional default はありません)、remf マクロは変数などの setf プレースに保持されたplistからインジケータと値の組を取り除きます。ラムダリストの &key パラメータも同じ形から解析されます。(setf (getf ...)) はサポートされるプレースではなく、シンボルの属性リスト(get/symbol-plist)もありません。エントリの追加や更新にはリストを作り直します(list* で先頭に追加するなど):

配列

make-arrayaref(setf (aref ...)) は3つのバックエンドすべてで機能します。 階数1以上の任意の階数の配列がサポートされます。dimensions引数は整数(階数1)または空でない整数のリストで、:initial-element はすべてのセルを設定します(デフォルトはnil)。要素は行優先で格納されO(1)でアクセスでき(階数に依存しないフラットなアクセスは row-major-aref / array-row-major-index)、配列は同一性(eq)で比較されるため、異なる2つの配列が equal になることはありません。length はベクタ(階数1の配列)の要素数を返します。多次元配列はシーケンスではないため、length はエラーを通知します。ハッシュテーブル演算子とは異なり、配列演算子は第一級の関数値として公開されないため、#'aref#'make-array は利用できません(直接呼び出してください)。 ベクタは vector で構築し、svref で読み取ることもできます。配列の形状は array-dimensions / array-rank / array-total-size で調べられ、 coerce はリスト・ベクタ・文字列を相互に変換します。 配列の上で numpy スタイルのベクトル・行列演算を行うには linalg パッケージを参照してください。 ネストしたループでインデックス参照する2次元配列:

#(...) リーダ構文は、要素がデータとして(評価されずに)読み込まれる自己評価される階数1のベクタリテラルを表します。例: #(1 2 3)#(a "b")。階数nの配列は、内容を深さnのネストしたリストとして #nA((...) ...) と書きます(行列は #2A、階数3の配列は #3A、...)。同じ深さのリストはすべて同じ長さでなければならず、 不揃いな内容は読み込みエラーになります。配列はすべてのバックエンドで同じ可読構文で印字され、prin1 は文字列要素を引用符で囲み、princ は囲みません:

パックド浮動小数点配列(#d / #f)

#d(...)#f(...)パックド浮動小数点配列を表します。これは要素をアン ボックスで格納する浮動小数点型の配列です。#d(...)double-float(f64)、 #f(...)single-float(f32 -- メモリは半分、SIMD レーン数は 2 倍)です。 #(...) と同じ記法で読み込まれますが、各要素は配列の浮動小数点型に強制変換される ため、#d(1 2 3)#d(1.0 2.0 3.0) は同じベクトルであり、 (array-element-type #d(1.0))double-float(#f なら single-float)になり ます。高階のリテラルはネストしたリストで書き(#d((1.0 2.0) (3.0 4.0)) は行列)、 実行時には (make-array n :element-type 'double-float)(または 'single-float) で構築できます。

スカラは double のままです。要素を読むと double に拡張され(single-float 要素は f32 -> f64 に拡張)、格納するときは配列の幅に丸められます(single-float 配列では f64 -> f32)。非実数を格納すると型エラーです(一般配列は任意の値を保持します)。 それ以外のすべての操作 -- aref(setf (aref ...))lengthrow-major-arefarray-rankarray-dimensionscoerce -- は同じ数値の一般配列と同じように機能し ますが、印字だけは独自の #d(...) / #f(...) リーダ構文を用いるため、その印字結果を 読み戻すと(アンボックス表現を保ったまま)同じ幅のパックド配列になり、一般配列に劣化 しません。数値カーネルが用いる、アンボックスで浮動小数点に特化した表現に過ぎないため、 フィルポインタ・可変長(adjustable)・ずらし配列(displaced)は利用できません(それ らには一般配列が必要です)。double-float の幅が既定で、linalg が生成するのもこの 幅です。パックド配列上の高速なベクトルカーネル(および任意のハードウェアアクセラレー ション)については vec パッケージを参照してくだ さい。

ハッシュテーブル

make-hash-tablegethash(setf (gethash ...))remhashclrhashhash-table-counthash-table-pmaphash は3つのバックエンドすべてで機能します。キーは構造的に(equal のように)比較されます。(list r c) のようなリストキーは等しいリストにマッチし、数値・シンボル・文字・文字列は値でマッチします。:test は馴染みのために受け付けられますがこの挙動を変えません -- eql テーブルでも構造的に等しい集約キーにマッチします。反復順序(maphash)はバックエンド間で保証されないため、ポータブルなコードはこれに依存すべきではありません。これらは3つのバックエンドすべてで第一級の関数値としても使用できます(#'gethash#'remhash#'clrhash#'hash-table-count#'hash-table-p#'maphash、および無引数形式の #'make-hash-table) -- 固定アリティのラッパー経由で渡されるため、gethash の省略可能なデフォルトや make-hash-table のキーワード引数は関数値経由では利用できません。典型的な使い方 -- placeに対する incf でのカウント: