[gambit-list] Is it a bug?

Marc Feeley feeley at iro.umontreal.ca
Tue Jun 20 03:08:12 EDT 2023


Hello. Gambit uses a module local scope for macro definitions. This means that a macro defined at the top-level of a module will only be visible in the rest of that module. The definition is not automatically added to the interaction environment.

There are a few ways to get what you want.  You could run your program as though everything was typed at the REPL by doing

   gsi < foobar.scm

and in that case the REPL is doing the evaluation so each macro definition is added to the interaction environment. Note that you must remove the shebang line which the REPL does not understand.

A better approach is to redefine the define-syntax form so that it automatically adds the macro definition to the interaction environment like this:

 (##define-syntax define-syntax
   (syntax-rules ()
     ((_ name expander)
      (begin
        (eval '(##define-syntax name expander) (interaction-environment))
        (##define-syntax name expander)))))

This could be added to the top of your file (after the shebang line). You can also add this definition to your .gambini.scm file so that you don’t have to change your program’s source code.

My favourite approach is to change the definition of the test-cases macro so that it does not use eval, which is the source of the problem:

 (define-syntax test-cases
   (syntax-rules ()
     ((_)
      (display "all tests passed!\n"))
     ((_ (expr expect) case2 ...)
      (begin
        (display-objects "Runing case:" 'expr "=>" 'expect)
        (newline)
        (let ((val expr))
          (if (equal? val 'expect)
              (display "true")
              (display "false"))
          (newline))
        (test-cases case2 ...)))))

By avoiding eval all the test expressions will be evaluated in the environment of the module. This has the added bonus that you can compile your code to verify that the test cases also work correctly in compiled code (if you use eval then you are only testing that the evaluation of the expression using the interpreter gives the expected result).

Marc



> On Jun 20, 2023, at 4:17 AM, Pan Xie <xiepan at skyguard.com.cn> wrote:
> 
> Hello
> 
> I am accessing the possibility of using gambit in our new project.
> However, I just encounter an issue which make me uneasy.
> 
> The following code sample demonstrates the issue. When run it, I just
> get the error "*** ERROR -- Unbound variable: mlist". It seems the
> environment used by "eval" does not have `mlist' defined.
> 
> #+begin_src scheme
> #!/usr/bin/gsi
> 
> (define display-objects
>   (lambda args
>     (let loop ((args args))
>       (cond
>         ((null? args) #f)
>         ((null? (cdr args)) (display (car args)))
>         (else
>          (display (car args))
>          (display " ")
>          (loop (cdr args)))))))
> 
> (define-syntax mlist
>   (syntax-rules ()
>     ((_ x1 ...)
>      (apply list '(x1 ...)))))
> 
> (define-syntax test-cases
>   (syntax-rules ()
>     ((_ case1 case2 ...)
>      (for-each
>       (lambda (case)
>         (display-objects "Runing case:" (car case) "=>" (cadr case))
>         (newline)
>         (let ((expr (car case)) (val (cadr case)))
>           (if (equal? (eval expr (interaction-environment)) val)
>               (display "true")
>               (display "false"))
>           (newline)))
>       '(case1 case2 ...)))))
> 
> (test-cases
>  ((mlist 1 2 3) (1 2 3)))
> #+end_src
> 
> I tested the same codes with other schemes (chez, chibi, guile, gauche,
> chicken and racket), and they all works flawlessly:
> 
> (My "run-as.scm" script will deal with the incompatible issues among
> various implementations, for example, it will define
> `interaction-environment' for racket, since it lacks the procedure.)
> 
> #+begin_example
> $ ./run-as.scm all ~/gambit-issue.scm
> =========== Running as chez ... ============
> 
> Runing case: (mlist 1 2 3) => (1 2 3)
> true
> 
> =========== Running as chibi ... ============
> 
> Runing case: (mlist 1 2 3) => (1 2 3)
> true
> 
> =========== Running as chicken ... ============
> 
> Runing case: (mlist 1 2 3) => (1 2 3)
> true
> 
> =========== Running as guile ... ============
> 
> ;;; note: source file /tmp/foobar.scm
> ;;;       newer than compiled /home/pxie/.cache/guile/ccache/3.0-LE-8-4.6/tmp/foobar.scm.go
> ;;; note: auto-compilation is enabled, set GUILE_AUTO_COMPILE=0
> ;;;       or pass the --no-auto-compile argument to disable.
> ;;; compiling /tmp/foobar.scm
> ;;; compiled /home/pxie/.cache/guile/ccache/3.0-LE-8-4.6/tmp/foobar.scm.go
> Runing case: (mlist 1 2 3) => (1 2 3)
> true
> 
> =========== Running as gauche ... ============
> 
> Runing case: (mlist 1 2 3) => (1 2 3)
> true
> 
> =========== Running as gambit ... ============
> 
> Runing case: (mlist 1 2 3) => (1 2 3)
> *** ERROR -- Unbound variable: mlist
> 
> =========== Running as racket ... ============
> 
> Runing case: (mlist 1 2 3) => (1 2 3)
> true
> #+end_example
> 
> So, please advise me what is going wrong here? Is it a bug of gambit, or
> am I missing something?
> 
> I am running the latest version of gambit on a ArchLinux:
> 
> #+begin_example
> gsi -v
> v4.9.4 20220102232246 x86_64-pc-linux-gnu "./configure '--docdir=/usr/share/doc/gambit-c' '--enable-gcc-opts' '--enable-single-host' '--infodir=/usr/share/info' '--libdir=/usr/lib/gambit-c' '--prefix=/usr' 'CFLAGS=-march=x86-64 -mtune=generic -O2 -pipe -fno-plt -fexceptions -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -fstack-clash-protection -fcf-protection -g -ffile-prefix-map=/build/gambit-c/src=/usr/src/debug/gambit-c' 'LDFLAGS=-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now' 'CXXFLAGS=-march=x86-64 -mtune=generic -O2 -pipe -fno-plt -fexceptions         -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security         -fstack-clash-protection -fcf-protection -Wp,-D_GLIBCXX_ASSERTIONS -g -ffile-prefix-map=/build/gambit-c/src=/usr/src/debug/gambit-c'"
> #+end_example
> 
> Thanks
> Pan
> 
> 




More information about the Gambit-list mailing list