chez-rs

macroで楽する

abc(v1,v2); の実行時間を、前回作った meas!( … ); で測定したい。 普段はemacs使いなので、先人に聞いてみた。

emacsのキーボードマクロとバインディングまで

キーボードの操作をレコーディングして、そいつに名前を付けるとな。関数の頭にカーソルを合わせてから、C-x add すれば良い。

meas!( の後、C-e で、最後に移動、1文字消してから新に ); を追加。何のひねりもない、キーボード操作だ。

(fset 'add
  (kmacro-lambda-form [?m ?e ?a ?s ?! ?\( ?\C-e ?\C-? ?\) ?\;] 0 "%d"))

同じ要領で、消す操作も定義出来るだろう。

dbg!

rustでは、簡単に変数の値を表示させるマクロが用意されてる。emacsから使っているなら、変数の所にカーソルを合わせて、C-c C-d すれば良い。表示を止める時は、dbg!(..) の、dの所に合わせて、C-c C-d だ。

error: expected identifier, found keyword `match`
  --> src/main.rs:10:9
   |
10 |     let dbg!(sd) = dump4gp("am.dat", am);
   |         ^^^^^^^^ expected identifier, found keyword
   |
   = note: this error originates in the macro `dbg` (in Nightly builds, run wit\
h -Z macro-backtrace for more info)

error: macro expansion ignores token `sd` and any following
   --> /home/sakae/.rustup/toolchains/stable-i686-unknown-linux-gnu/lib/rustlib\
/src/rust/library/std/src/macros.rs:297:15

仕込んだ場所が悪いのかな。左辺には設定出来無い? この例だど、sdを得て、それを13行目で使ってる。ここでは、値が決定されてるから大丈夫だろう。

run4gp(dbg!(sd));

ちゃんとコンパイル出来て、表示も得られた。

[src/main.rs:13] sd = "211123"

わーい、万歳ではもったいないので、dbg!を見ておく。エラーで、手の内を曝け出しているから、ひとっ飛びだ。

($val:expr $(,)?) => {
    // Use of `match` here is intentional because it affects the lifetimes
    // of temporaries - https://stackoverflow.com/a/48732525/1063961
    match $val {
        tmp => {
            $crate::eprintln!("[{}:{}] {} = {:#?}",
                $crate::file!(), $crate::line!(), $crate::stringify!($val), &tmp);
            tmp
        }
    }
};

勉強になるなあ。こういうのを盜むと佳ろし。目の前に沢山実例が有るから、盗まなければ損だ。

C-c C-d ってマクロ

dbg!(..)を付けたり、外したりするマクロって、オイラーがmeas!(..)のon/off させたかったのと一緒じゃん。パターンがマッチしました。だから、今度はemacs側のマクロを盜む番です。

キーシーケンスから、割り出そう。C-h k C-c C-d して、バインドされてる関数名とかを提示させる。

C-c C-d runs the command rust-dbg-wrap-or-unwrap (found in
rustic-mode-map), which is an autoloaded interactive compiled Lisp
function in ‘rust-utils.el’.

It is bound to C-c C-d.

(rust-dbg-wrap-or-unwrap)

Either remove or add the dbg! macro.

rust-utilis.elにカーソルを合わせてRETを叩くだけで、下記に到達。先人に感謝です。

(defun rust-dbg-wrap-or-unwrap ()
  "Either remove or add the dbg! macro."
  (interactive)
  (save-excursion
    (if (region-active-p)
        (rust-insert-dbg)
     :

後はこの関数と、rust-insert-dbgを適当な名前でコピペしてから修整。rust-mode.elにある

(defvar rust-mode-map
  (let ((map (make-sparse-keymap)))
    (define-key map (kbd "C-c C-d") 'rust-dbg-wrap-or-unwrap)

に、適当に追加。キーバインドは、C-c C-m ぐらいが、空いてて良さそう。

ちょいとした亊

上で使ったアプリは、前回登場した、血圧グラフのやつ。ぼっと眺めていたら、家庭教師clippyも気が付かない、修整点を見付けた。

let (am, pm) = csv_read("current.csv");
//    let am = rv.0;
//    let pm = rv.1;

csv_read は、タプルを返すんで、それをタプルで受ければ済むじゃん。これで多少無駄が省けた。まて、それってコードを書いた時点で知ってたけど、あえて、タプルのアクセス方法を後学の為に書いたのかも? 今となっては、忘却の彼方であります。

前回 .. によるレンジを使った。普通に設定すると、終値は含まれない。下のように ..=終値とすると、含まれるようになる。あれって思ったんで調べてみた。

fn main() {
    for x in 0..=2 {
        println!("val is {}", dbg!(x));
    }
}

dbg! を、for の所にあるxに指定すると、上でみたようなマクロエラーになる。しつこいようだけど、変数が使われる所で、printするように。

[src/main.rs:3] x = 0
val is 0
[src/main.rs:3] x = 1
val is 1
[src/main.rs:3] x = 2
val is 2

Ruscheme

rust scheme で探していたら、r7rs対応ってのを見付けた。 Ruscheme

早速、試練を与えてみよと、使い回しのスクリプトを取出したら、エラーですよ。実装レベルがまちまち。おまけにr7rsとなってるので、下記のように現場で調整。

sakae@deb:/tmp/Ruschm$ cat ever.scm
(import (scheme base) (scheme write))

(define (println x) (display x)(newline))
(define l '())
(define cnt 0)

(define (cs)
  (set! l (cons cnt l))
  (set! cnt (+ 1 cnt)))

(define (ever)
     ;  (and (= (modulo cnt 1000) 0) (println cnt))
  (println cnt)
  (cs)
  (ever))

(ever)

inportを追加。printlnがないので、でっちあげ。moduloがないので、垂れ流し。そこに至まで、使えそうな奴をリサーチしたんだけどね。

Ruschm Version 0.2.0
> (div 10 3)
unbound symbol div
> (/ 10 5)
2
> (/ 10 3)
10/3
>
Interrupted input by ctrl-c, use ctrl-d to exit.
>
exited. have a nice day.

/ を使って、moduloを実装しようとしたけど、割り切れないと、有理数になっちゃうと言う、割り切れなさですよ。まさに、打つ手なし。自分で拡張しろってのは取り敢えず無し。consがどうなってるか調べるのが、大目的ですから。

sakae@deb:/tmp/Ruschm$ cargo r ever.scm
 :
9986
9987

thread 'main' has overflowed its stack
fatal runtime error: stack overflow
Aborted

やっぱり、スタック不足だなあ。最適化されないのかな? 裏技は確か、clojure本に載ってた記憶が有るんだけど、取り敢えずうっちゃっておく。

consの定義場所を探してみる。例によってenumで型を閲覧、それらしいのが、 pair.rsに見付かった。

#[derive(Debug, Clone, PartialEq)]
pub enum GenericPair<T> {
    Some(T, T),
    Empty,
}
sakae@deb:/tmp/Ruschm/src$ grep 'fn cons' -rI .
./parser/pair.rs:    pub fn cons(car: T, cdr: T) -> Self {
./interpreter/library/native/base.rs:fn cons<R: RealNumberInternalTrait>(

旨い具合に引掛ってきた。

fn cons<R: RealNumberInternalTrait>(
    arguments: impl IntoIterator<Item = Value<R>>,
) -> Result<Value<R>> {
    let mut iter = arguments.into_iter();
    let car = iter.next().unwrap();
    let cdr = iter.next().unwrap();
    Ok(Value::Pair(Box::new(Pair::Some(car, cdr))))
}

gdbで追跡しようとしても、consの定義が深すぎて辿りつけず。変に階層化された奴は手出しが出禁な。

chez-sys

もう一つ、異色なものを見付けた。

chez-sys

ですって。どうも、 Section 4.8. C Library Routines をrust側から実現するっぽい。先月の終り頃出て来た新種。

/* a particularly silly way to multiply two floating-point numbers */
double mul(double x, double y) {
    ptr times = Stop_level_value(Sstring_to_symbol("*"));

    return Sflonum_value(Scall2(times, Sflonum(x), Sflonum(y)));
}

これをrust言語に置き換えれば、chezschemeを扱き使えるとな。

fn it_works() { ▶︎ Run Test|Debug
    unsafe {
        Sscheme_init(None);
        let ti = Stop_level_value(Sstring_to_symbol(
            "*".as_bytes().as_ptr() as *const i8));
        println!("{}",
                 Sflonum_value(Scall2(ti, Sflonum(1.1), Sflonum(2.2))));
    }
}

mulな関数を定義しておいて呼び出すようにすると、コンパイルエラーになったので、分解して埋め込んでみた。

sakae@pen:/tmp/chez-sys/src$ cargo t
    Finished test [unoptimized + debuginfo] target(s) in 0.01s
     Running unittests (/tmp/chez-sys/target/debug/deps/chez_sys-143b01c762533554)

running 1 test
error: test failed, to rerun pass '--lib'

Caused by:
  process didn't exit successfully: `/tmp/chez-sys/target/debug/deps/chez_sys-143b01c762533554` (signal: 8, SIGFPE: erroneous arithmetic operation)

けど、エラーだ。SIGFPEって亊は、信号がchezの演算装置まで届いたって亊かな? その後少し考えて、

fn tasu(x: i64, y: i64) -> i64 {
    unsafe {
        let ti = Stop_level_value(
            Sstring_to_symbol("+".as_bytes().as_ptr() as *const i8));
        Sfixnum_value(Scall2(ti, Sfixnum(x), Sfixnum(y)))
    }
}

#[test]
fn it_works() { ▶︎ Run Test|Debug
    unsafe {
        Sscheme_init(None);
        tasu(1, 4);
    }
}

こんな風に定義したら、コンパイル出来た。

  process didn't exit successfully: `/tmp/chez-sys/target/debug/deps/chez_sys-1\
43b01c762533554` (signal: 8, SIGFPE: erroneous arithmetic operation)

整数の足し算だけど、SIGFPEになる。しっちゃかめっちゃかだな。

i3le な、マシンでもやってみるかな。 210915.html 161216.html

normal

取り敢えず、chezscheme-{dev,doc}も入れて、ちゃんとしとく。man chezschemeはちゃんとしたURLの抜粋になってるんで、目を通しておくべき。

sakae@deb:/tmp$ scheme --verbose
trying /usr/lib/csv9.5.4/ti3le/scheme.boot...opened
version and machine type check
trying /usr/lib/csv9.5.4/ti3le/petite.boot...opened
version and machine type check
Chez Scheme Version 9.5.4
Copyright 1984-2020 Cisco Systems, Inc.

>

ふーんと頷いてからBOOTの説明をみる。

BOOT and HEAP FILES
       When  Chez  Scheme is run, it looks for one or more boot files to load.
       Boot files contain the compiled Scheme code that implements most of the
       Scheme system, including the interpreter, compiler, and most libraries.
       Boot files may be specified explicitly on the command line via “-b” op‐
       tions  or  implicitly.  In the simplest case, no “-b” options are given
       and the necessary boot files are loaded automatically based on the name
       of the executable.  For example, if the executable name is “myapp”, the
       system looks for “myapp.boot” in a set  of  standard  directories.   It
       also  looks  for  and  loads  any  subordinate  boot  files required by
       “myapp.boot”.  Subordinate boot files are also loaded automatically for
       the  first  boot  file explicitly specified via the command line.  When
       multiple boot files are specified via the command line  and  boot  each
       file must be listed before those that depend upon it.

で、真似亊ファイルを作成

#include "/usr/lib/csv9.5.4/ti3le/scheme.h"
#include <stdio.h>

double mul(double x, double y) {
    ptr times = Stop_level_value(Sstring_to_symbol("*"));
    return Sflonum_value(Scall2(times, Sflonum(x), Sflonum(y)));
}

int main(){
  Sscheme_init(NULL);
  printf("$f\n", mul(1.23, 4.56));
}

やっぱりエラーは、確信してましたよ。何をリンクすればいいの? それっぽいのがなかったぞ。

sakae@deb:/tmp$ cc hoge.c
/usr/bin/ld: /tmp/ccLczrjB.o: in function `mul':
hoge.c:(.text+0x36): undefined reference to `Sstring_to_symbol'
/usr/bin/ld: hoge.c:(.text+0x42): undefined reference to `Stop_level_value'
/usr/bin/ld: hoge.c:(.text+0x56): undefined reference to `Sflonum'
/usr/bin/ld: hoge.c:(.text+0x69): undefined reference to `Sflonum'
/usr/bin/ld: hoge.c:(.text+0x79): undefined reference to `Scall2'
/usr/bin/ld: /tmp/ccLczrjB.o: in function `main':
hoge.c:(.text+0xac): undefined reference to `Sscheme_init'
collect2: error: ld returned 1 exit status

Questions on embedding Chez Scheme into a C++ app

Sscheme_init(0);
Sregister_boot_file("/path/to/petite.boot");
Sregister_boot_file("/path/to/scheme.boot");
Sbuild_heap(0, 0);

このコードを、kernel.oと共にリンクせいとな。 そして、付帯するユーザーコードも追加し、対応するscheme側もxx.ssも用意せいとな。 詳しい亊は、 Chez Scheme で、質問しろ。それをやっちゃうと、chez沼となって、高橋君が司会してるNHKのあの番組から、出演依頼が來るぞ。

まあ、世界には、 https://github.com/tizoc/shen-scheme とか https://zhuanlan.zhihu.com/p/47923789 こういう人も居るって亊を、心に留めておいて、撤退する。

C言語からGaucheを使おう! (1) コンパイル環境を整える が、頭に有ったから、軽く考えていたってのは、内緒だ。GaucheのScheme処理系APIをC言語から利用する記述のサンプル こんなのも容易く見付かるね。間口が広い、資料豊富って亊か。

build.rs

chez-rsでは、build.rsが使われている。本筋は、こちらと思うのよ。 ビルドスクリプト に説明が有った。

Linking to system libraries ずばりの例が、解説されてた。

sakae@pen:/tmp/chez-sys$ cargo b -vv
       Fresh pkg-config v0.3.24
   Compiling chez-sys v0.1.0 (/tmp/chez-sys)
     Running `CARGO=/home/sakae/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin/cargo
        :
OUT_DIR=/tmp/chez-sys/target/debug/build/chez-sys-21--18/out
PETITE_BOOT_PATH=/tmp/chez-sys/target/debug/build/chez-sys-21--18/out/petite.bo\
ot
SCHEME_BOOT_PATH=/tmp/chez-sys/target/debug/build/chez-sys-21--18/out/scheme.boot
rustc --crate-name chez_sys --edition=2021 src/lib.rs --error-format=json
  --json=diagnostic-rendered-ansi --crate-type lib
  --emit=dep-info,metadata,link -C embed-bitcode=no
  -C debuginfo=2 -C metadata=a4938f4450e0570a
  -C extra-filename=-a4938f4450e0570a
  --out-dir  /tmp/chez-sys/target/debug/deps
  -C incremental=/tmp/chez-sys/target/debug/incremental
  -L dependency=/tmp/chez-sys/target/debug/deps
  -L native=/usr/lib/x86_64-linux-gnu
  -L /tmp/chez-sys/target/debug/build/chez-sys-21x18/out/ChezScheme/a6le/boot/a6le
  -L /tmp/chez-sys/target/debug/build/chez-sys-21x18/out/ChezScheme/a6le/lz4/lib
  -L /tmp/chez-sys/target/debug/build/chez-sys-21x18/out/ChezScheme/a6le/zlib
  -l uuid -l chez -l lz4 -l z -l uuid'

上のは、build.rsの実行内容(見やすい様に編集)。これらとbinding.rsがどう結び付くかと思ったら、

target/debug/libchez_sys.d

/tmp/chez-sys/target/debug/libchez_sys.rlib: 
  /tmp/chez-sys/build.rs
  /tmp/chez-sys/src/bindings.rs
  /tmp/chez-sys/src/lib.rs

こういう仕掛けで、最終成果物 libchez_sys.rlib が出来上がっていた。

このままだと、lib.rsにあるtestを実行するのが関の山なんだな。

gcc  -m64 -msse2 -Wpointer-arith -Wall -Wextra -Werror -Wno-implicit-fallthroug\
h -O2  -c -DX86_64 -I../boot/a6le -I../zlib -I../lz4/lib i3le.c
ar rc ../boot/a6le/libkernel.a statics.o segment.o alloc.o symbol.o intern.o gc\
wrapper.o gc-011.o gc-ocd.o gc-oce.o number.o schsig.o io.o new-io.o print.o fa\
sl.o stats.o foreign.o prim.o prim5.o flushcache.o schlib.o thread.o expeditor.\
o scheme.o compress-io.o i3le.o
gcc  -m64 -msse2 -Wpointer-arith -Wall -Wextra -Werror -Wno-implicit-fallthroug\
h -O2  -c -DX86_64 -I../boot/a6le -I../zlib -I../lz4/lib main.c
cp -p main.o ../boot/a6le/main.o
gcc  -m64 -msse2 -Wpointer-arith -Wall -Wextra -Werror -Wno-implicit-fallthroug\
h -O2  -rdynamic -o ../bin/a6le/scheme ../boot/a6le/main.o ../boot/a6le/libkern\
el.a -lm -ldl  -lrt -luuid ../zlib/libz.a ../lz4/lib/liblz4.a
(cd s && make bootstrap)
cargo:rustc-env=PETITE_BOOT_PATH=/tmp/chez-sys/target/debug/build/chez-sys-21f6\
1caf5a947518/out/petite.boot
cargo:rustc-env=SCHEME_BOOT_PATH=/tmp/chez-sys/target/debug/build/chez-sys-21f6\
1caf5a947518/out/scheme.boot
cargo:rustc-link-search=/tmp/chez-sys/target/debug/build/chez-sys-21f61caf5a947\
518/out/ChezScheme/a6le/boot/a6le
cargo:rustc-link-lib=chez
cargo:rustc-link-search=/tmp/chez-sys/target/debug/build/chez-sys-21f61caf5a947\
518/out/ChezScheme/a6le/lz4/lib
cargo:rustc-link-lib=lz4
cargo:rustc-link-search=/tmp/chez-sys/target/debug/build/chez-sys-21f61caf5a947\
518/out/ChezScheme/a6le/zlib
cargo:rustc-link-lib=z
cargo:rustc-link-lib=uuid
cargo:include=/tmp/chez-sys/target/debug/build/chez-sys-21f61caf5a947518/out/Ch\
ezScheme/a6le

target/debug/build/chez-sys-21x18/outputってファイルにログが残っていた。

getでchezのファイル群を取ってきて、configure,make後は必要なファイルの移動ってのが、build.rsの前半部分か。

build.rs から標準出力への出力内容は -vv フラグをつけると確認できる

-vv 磁力で見付ちゃったけど、本に載ってるのね。検索力が向上したって亊かな。

Rustもくもく会 で、build.rsからCコンパイラーを呼び出せる、ccって言う木箱が使われていた。蛇の道は蛇だなあ。

A little C with your Rust 組み込み屋ー 御用達。


This year's Index

Home