feeley on master
CI: use gcc-11 on macOS (gcc-9 … (compare)
feeley on master
Create temp dir in /tmp when no… (compare)
gambiteer on master
Correct test directories and fi… (compare)
@twoplustwo:matrix.org Take number->string
… the first parameter (a number) is required, and the second parameter is optional (a number that is the base) and you cannot pass more than 2 parameters:
> (number->string 42)
"42"
> (number->string 42 2)
"101010"
> (number->string 42 2 111 222 333)
*** ERROR IN (stdin)@3.1 -- Wrong number of arguments passed to procedure
(number->string 42 2 111 222 333)
But sometimes you want to accept any number of parameters, for example the -
procedures requires one parameter, but can have any number of additional parameters:
> (- 5)
-5
> (- 5 2)
3
> (- 5 2 1)
2
> (- 5 2 1 111 222 333)
-664
For this situation you need to define the procedure using a rest parameter which will be a list of all the remaining parameters:
> (define (- first . rest) (list 'first= first 'rest= rest))
> (- 5)
(first= 5 rest= ())
> (- 5 2 1 111 222 333)
(first= 5 rest= (2 1 111 222 333))
> (define (a x y #!optional z) (list 'x= x 'y= y 'z= z))
> (define (b x y #!optional z #!rest rest) (list 'x= x 'y= y 'z= z 'rest= rest))
> (a 1 2)
(x= 1 y= 2 z= #f)
> (a 1 2 3)
(x= 1 y= 2 z= 3)
> (a 1 2 3 4)
*** ERROR IN (stdin)@5.1 -- Wrong number of arguments passed to procedure
(a 1 2 3 4)
1> ,t
> (b 1 2)
(x= 1 y= 2 z= #f rest= ())
> (b 1 2 3)
(x= 1 y= 2 z= 3 rest= ())
> (b 1 2 3 4)
(x= 1 y= 2 z= 3 rest= (4))
> (b 1 2 3 4 5)
(x= 1 y= 2 z= 3 rest= (4 5))
(define (h1 a #!rest r #!key k) (list a k r))
is this possible?
> (define (foo x #!key y z . rest) (list 'x= x 'y= y 'z= z 'rest= rest))
> (foo 1)
(x= 1 y= #f z= #f rest= ())
> (foo 1 z: 2)
(x= 1 y= #f z= 2 rest= ())
> (foo 1 z: 2 y: 3)
(x= 1 y= 3 z= 2 rest= ())
> (foo 1 z: 2 y: 3 4)
(x= 1 y= 3 z= 2 rest= (4))
> (foo 1 z: 2 y: 3 4 5)
(x= 1 y= 3 z= 2 rest= (4 5))
(define (string-substitute str delim proc-or-alist)
(define (index-of c start)
(let loop ((i start))
(if (fx< i (string-length str))
(if (char=? c (string-ref str i))
i
(loop (fx+ i 1)))
i)))
(let loop ((i 0) (j 0) (out '()))
(let ((start (index-of delim j)))
(if (fx< start (string-length str))
(let ((end (index-of delim (fx+ start 1))))
(if (fx< end (string-length str))
(if (fx= start (fx- end 1)) ;; two delimiters in a row?
(loop (fx+ end 1)
(fx+ end 1)
(cons (substring str i end)
out))
(let* ((var
(substring str (fx+ start 1) end))
(subst
(if (procedure? proc-or-alist)
(proc-or-alist var)
(let ((x (assoc var proc-or-alist)))
(and x (cdr x))))))
(if subst
(loop (fx+ end 1)
(fx+ end 1)
(cons subst
(cons (substring str i start)
out)))
(error "Unbound substitution variable in" str))))
(error "Unbalanced delimiter in" str)))
(string-concatenate
(reverse (cons (substring str i start) out)))))))
(pp (string-substitute "@a@ plus 1 is @b@" #\@ '(("a" ."two") ("b" . "three"))))
Hello, I'm trying gambit for the first time, starting with the javascript support in 4.9.4. I am able to reproduce the instructions at
http://www.gambitscheme.org/latest/manual/#Compiling-Modules
However, if I change much from that basic scenario, stuff stops working. For example, here is my attempt to extend to importing SRFI 78:
git clone https://github.com/scheme-requests-for-implementation/srfi-78
cat srfi-78/srfi-78.sld
(define-library (srfi-78)
(import (scheme base))
(export hello-world)
(include "check.scm")
(begin
(define (hello-world)
(display "hello world\n"))
))
cat hello-test/hello-test.sld
(define-library (hello-test)
(import (srfi-78) (scheme base) (scheme write))
(begin
(display
(cond-expand
((compilation-target C) "compiled to C\n")
((compilation-target (_)) "interpreted\n")
(else "compiled to other\n")))
(hello-world)
(newline)))
gsc -target js . srfi-78 hello-test
Building module: srfi-78
gsc -target js . srfi-78
<no output>
gsc -target js . hello-test
<no output>
gsc -target js -exe -nopreload . srfi-78/srfi-78.sld hello-test/hello-test.sld
srfi-78/srfi-78.sld:
/home/jeff/srchome/src/zipper/srfi-78/srfi-78.js:
hello-test/hello-test.sld:
/home/jeff/srchome/src/zipper/hello-test/hello-test.js:
/home/jeff/srchome/src/zipper/hello-test/hello-test_.js:
hello-test/hello-test
compiled to other
*** ERROR IN hello-test# -- Operator is not a PROCEDURE
(#!void "hello world\n")
Now:
gsc -target js . srfi-78 hello-test && \
gsc -target js -exe -nopreload . srfi-78/srfi-78.sld hello-test/hello-test.sld && \
node hello-test/hello-test
emits:
Building module: srfi-78
srfi-78/srfi-78.sld:
/home/jeff/srchome/src/zipper/srfi-78/srfi-78.js:
hello-test/hello-test.sld:
/home/jeff/srchome/src/zipper/hello-test/hello-test.js:
/home/jeff/srchome/src/zipper/hello-test/hello-test_.js:
compiled to other
hello world
(define-library (srfi-78)
(import (scheme base) (scheme write))
(export hello-world check check-passed? check-report check-reset! check-set-mode!
check:add-correct! check:add-failed! check:correct check:failed
check:mode check:proc check:proc-ec check:report-actual-result
check:report-correct check:report-expression check:report-failed
check:write)
(include "check.scm")
(begin
(define (hello-world)
(display "hello world\n"))
))
-warnings
option to gsc
is useful in this situation
I'm looking for a workflow to incrementally develop a library in the r7rs define-library style, including the ability to test in a repl. Currently using emacs scheme-mode for sending to the repl.
Suppose I want to change the library and then as quickly as possible test with the repl. I wrote a wrapper script to run gsi which finds the correct directory to pass with -:search so that my import will work. But after making a change I noticed when doing import as above in gsi that I can't seem to do import again and pick up the change. I'm okay with clearing out my environment to do so.
I suppose I could script this with a custom emacs command or subprocess tty hacks, but I was hoping to stay within gambit for editor independence. Is there any similar api available?
(display "hello $HOME/.gambini\n")
(define (dir-for-project)
(let loop ((d (path-strip-trailing-directory-separator (current-directory))))
(let ((files (directory-files (list (string->keyword "path") d (string->keyword "ignore-hidden") #f))))
(if (or (equal? 0 (string-length d))
(member ".gambini-local" files))
d
(loop (path-strip-trailing-directory-separator (path-directory d)))))))
(map display `(about to add ,(dir-for-project) "\n"))
(module-search-order-add! (dir-for-project))
;; TODO: look for project-specific modules to load in .gambini-local
;; For now, simulate by hard-coding:
;;(import (srfi-78))
;; =>
;; Cannot find library (srfi-78)
(eval "(define foo 123)")
;; does not cause error, but foo is not bound in the repl
eval
you want: (eval ‘(define foo 123))
(eval “a string”)
=> ”a string”
#|
Initializes project-specific path and imports for repl-based testing.
Place initialization code for your gambit based project in the project
root directory.
For example:
mkdir -p srfi-78
git clone https://github.com/scheme-requests-for-implementation/srfi-78 srfi-78/srfi-78
cat > srfi-78/srfi-78.sld
(define-library (srfi-78)
(import (scheme base) (scheme write))
(export hello-world check check-passed? check-report check-reset! check-set-mode!
check:add-correct! check:add-failed! check:correct check:failed
check:mode check:proc check:proc-ec check:report-actual-result
check:report-correct check:report-expression check:report-failed
check:write)
(include "srfi-78/check.scm")
(begin
(define (hello-world)
(display "hello world\n"))
))
cat > .gambini-local
(import (srfi-78))
(define bar 456)
gsi
hello HOME/.gambini
Found .gambini-local. Will module-search-order add ../src/zipper
About to eval: (begin (import (srfi-78)) (define bar 456))
Gambit v4.9.4
> bar
456
> (check (+ 2 2) => 4)
(+ 2 2) => 4 ; correct
>
|#
(import (srfi 28))
(define rfile-local ".gambini-local")
(define (dir-for-project)
(let loop ((d (path-strip-trailing-directory-separator (current-directory))))
(let ((files (directory-files (list (string->keyword "path") d (string->keyword "ignore-hidden") #f))))
(cond
((equal? 0 (string-length d)) #f)
((member rfile-local files) d)
(else
(loop (path-strip-trailing-directory-separator (path-directory d))))))))
(define (file-for-project)
(path-expand rfile-local (dir-for-project)))
(if (dir-for-project)
(begin
(display (format "Found .gambini-local. Will module-search-order add ~a\n" (dir-for-project)))
(module-search-order-add! (dir-for-project))
(let ((sexp-local `(begin ,@(with-input-from-file ".gambini-local" (lambda () (read-all (current-input-port)))))))
(display (format "About to eval: ~a\n" sexp-local))
(eval sexp-local))))
@jeffhhk The Gambit manual has this explanation:
Extensions which are meant to apply to a single user or to a specific working directory are best placed in the initialization file, which is a file containing Scheme code. In all modes, the interpreter first tries to locate the initialization file by searching the following locations: ‘.gambini’ and ‘ ̃/.gambini’ (with no extension, a ‘.sld’ extension, a ‘.scm’ extension, and a ‘.six’ extension in that order). The first file that is found is examined as though the expression (include initialization-file) had been entered at the read-eval-print loop where initialization-file is the file that was found. Note that by using an include the macros defined in the initialization file will be visible from the read-eval-print loop (this would not have been the case if load had been used). The initialization file is not searched for or examined when the ‘-f’ option is specified.
The .gambini file will be visited by gsi and gsc (unless the -f
option is specified) because the code it contains is meant to configure or extend the Gambit runtime library. There are various ways to detect if code is being run by gsi or gsc:
1) check for presence of compile-file
procedure which is defined only by gsc
:
(if (##unbound? (##global-var-ref (##make-global-var 'compile-file)))
(display “gsi\n”)
(display “gsc\n”))
2) check the name of the executable:
(if (member (path-strip-directory (executable-path)) '("gsi" "gsi-script”))
(display “gsi\n”)
(display “gsc\n”))
3) if you want to check if code is interpreted or compiled:
$ cat .gambini
(define-macro (interpreted-code?)
`(cond-expand ((compilation-target (_))
#t)
(else
#f)))
$ cat foo.scm
(if (interpreted-code?)
(display "interpreted code\n")
(display "compiled code\n"))
$ gsi foo.scm
interpreted code
$ gsc -exe foo.scm
$ ./foo
compiled code
$ gsc -i foo.scm
interpreted code
$ gsc
Gambit v4.9.4-39-g9a887b80
> (load "foo.scm")
interpreted code
"/Users/feeley/foo.scm"
> (compile-file "foo.scm")
"/Users/feeley/foo.o1"
> (load "foo.o1")
compiled code
"/Users/feeley/foo.o1"
Note that with #3 it is not the program (gsi
or gsc
) that is checked, but rather the way the code is executed, which is often a more robust test because gsc
can both compile and interpret code (when given the -i
option or no command line arguments).
Hi there!
While porting my json-rpc to Gambit, I found a possible bug:
> (alist->hash-table '())
*** ERROR IN (stdin)@2.1 -- PAIR LIST expected
(srfi/69#alist->hash-table '())
Trying to convert an empty list into a hash-table sounds lame, but I'm using it as a set-filter
for make-parameter
, like this:
(define json-rpc-handler-table
(make-parameter '() (lambda (alist) (alist->hash-table alist equal?))))
I'm using v4.9.4. BTW, I also noticed that gsi still shows v4.9.3
define-values
, the correct implementation of this form and other R7RS forms depends on the new macro system we are working on… in the meantime you can use this definition:(define-macro (define-values vars expr)
(cond ((null? vars)
(let ((temp (gensym)))
`(##define ,temp
(##call-with-values (##lambda () ,expr) ##list))))
((null? (cdr vars))
`(##define ,(car vars) ,expr))
(else
(let ((temp (gensym)))
`(##begin
(##define ,temp
(##call-with-values (##lambda () ,expr) ##list))
,@(map (lambda (var)
`(##define ,var
(##if (##pair? ,temp)
(##let ((val (##car ,temp)))
(##set! ,temp (##cdr ,temp))
val)
(##error "too few values"))))
vars))))))