;;;; srfi-119.test --- Test suite for Guile's SRFI-119 reader. -*- scheme -*- ;;;; ;;;; Copyright (C) 2023 Free Software Foundation, Inc. ;;;; ;;;; This library is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU Lesser General Public ;;;; License as published by the Free Software Foundation; either ;;;; version 3 of the License, or (at your option) any later version. ;;;; ;;;; This library is distributed in the hope that it will be useful, ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;;; Lesser General Public License for more details. ;;;; ;;;; You should have received a copy of the GNU Lesser General Public ;;;; License along with this library; if not, write to the Free Software ;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA (define-module (test-srfi-119) #:use-module (test-suite lib) #:use-module (srfi srfi-1) #:use-module (srfi srfi-26) ;; cut #:use-module (language wisp)) (define (read-string s) (with-input-from-string s read)) (define (with-read-options opts thunk) (let ((saved-options (read-options))) (dynamic-wind (lambda () (read-options opts)) thunk (lambda () (read-options saved-options))))) (define (wisp->list str) (wisp-scheme-read-string str)) (define (scheme->list str) (with-input-from-string str (λ () (let loop ((result '())) (if (eof-object? (peek-char)) (reverse! result) (loop (cons (read) result))))))) (with-test-prefix "wisp-read-simple" (pass-if-equal '((<= n 5)) (wisp->list "<= n 5")) (pass-if-equal '(5) (wisp->list ". 5")) (pass-if-equal '((+ 1 (* 2 3))) (wisp->list "+ 1 : * 2 3"))) (with-test-prefix "wisp-read-complex" (pass-if-equal '( (a b c d e f g h i j k) (concat "I want " (getwish from me) " - " username)) (wisp->list " a b c d e . f g h . i j k concat \"I want \" getwish from me . \" - \" username ")) (pass-if-equal '( (define (a b c) (d e (f) (g h) i)) (define (_) (display "hello\n")) (_)) (wisp->list " define : a b c _ d e ___ f ___ g h __ . i define : _ _ display \"hello\n\" \\_")) ;; nesting with pairs (pass-if-equal '((1 . 2)(3 4 (5 . 6))) (wisp->list "1 . 2\n3 4\n 5 . 6"))) (with-test-prefix "wisp-source-properties" ;; has properties (pass-if (every pair? (map source-properties (wisp->list "1 . 2\n3 4\n 5 . 6")))) (pass-if (every pair? (map source-properties (wisp->list "1 2\n3 4\n 5 6")))) ;; has the same properties (pass-if-equal (map source-properties (scheme->list "(1 . 2)\n(3 4\n (5 . 6))\n(1 4)\n\n(7 8)")) (map (cut cons '(filename . #f) <>) (map source-properties (wisp->list "1 . 2\n3 4\n 5 . 6\n1 4\n\n7 8"))))) (with-test-prefix "btest" (pass-if-equal '((display "b") (newline) ) (wisp->list " display \"b\" newline "))) (with-test-prefix "continuation" (pass-if-equal '((a b c d e f g h i j k) (concat "I want " (getwish from me) " - " username) ) (wisp->list " a b c d e . f g h . i j k concat \"I want \" getwish from me . \" - \" username "))) (with-test-prefix "dotted-pair" (pass-if-equal '((use-modules ((ice-9 popen) #:select ((open-input-pipe . oip)))) ) (wisp->list " use-modules : (ice-9 popen) #:select ((open-input-pipe . oip)) "))) (with-test-prefix "example" (pass-if-equal '((defun a (b c) (let ( (d "i am a string do not break me!") ( ; comment: 0 (f) ; comment : 1 `(g )); comment " : " 2 ( (h (I am in brackets: do not : change "me")) i))) ,('j k) l ; comment (a c)) (defun b (:n o) "second defun : with a docstring!" (message "I am here") t) (defun c (e f) ((g)) ( (h (i)) (j)) '(()) (k) l (m)) (defun _ (:) :) (_ b) (defun d () (let ((a b) (c d)))) (a (((c)))) (let ((a b) (c))) (let ((a b))) a ) (wisp->list " defun a (b c) let : d \"i am a string do not break me!\" : ; comment: 0 f ; comment : 1 ` g ; comment \" : \" 2 : h (I am in brackets: do not : change \"me\") . i , 'j k . l ; comment a c defun b : :n o . \"second defun : with a docstring!\" message \"I am here\" . t defun c : e f : g : h i j ' : k . l . : m defun _ : \\: __ __ . \\: \\_ b defun d : let : a b c d a : : : c let : a b c let : : a b . a "))) (with-test-prefix "factorial" (pass-if-equal '(;; short version ; note: once you use one inline colon, all the following forms on that ; line will get closed at the end of the line (define (factorial n) (if (zero? n) 1 (* n (factorial (- n 1))))) (display (factorial 5 )) ;; more vertical space, less colons (define (factorial n) (if (zero? n) 1 (* n (factorial (- n 1))))) (display (factorial 5 )) ) (wisp->list " ;; short version ; note: once you use one inline colon, all the following forms on that ; line will get closed at the end of the line define : factorial n if : zero? n . 1 * n : factorial : - n 1 display : factorial 5 ;; more vertical space, less colons define : factorial n if : zero? n . 1 * n factorial - n 1 display : factorial 5 "))) (with-test-prefix "fast-sum" (pass-if-equal '((use-modules (srfi srfi-1)) ; only for the nice test #!curly-infix (define-syntax fast-sum (syntax-rules (iota) ((fast-sum (iota count start)) (+ 1 (apply - (map (lambda (x) (/ {x * {x + 1} } 2)) (list {count + {start - 1}} start))))) ((fast-sum e) (apply + e)))) ) (wisp->list " use-modules : srfi srfi-1 ; only for the nice test . #!curly-infix define-syntax fast-sum syntax-rules : iota : fast-sum : iota count start + 1 apply - map : lambda (x) : / {x * {x + 1} } 2 list {count + {start - 1}} start : fast-sum e apply + e "))) (with-test-prefix "flexible-parameter-list" (pass-if-equal '(; Test using a . as first parameter on a line by prefixing it with a second . (define (a i . b) (unless (>= i (length b)) (display (number->string (length b ))) (display (list-ref b i)) (newline) (apply a ( + i 1 ) b))) (a 0 "123" "345" "567") ) (wisp->list " ; Test using a . as first parameter on a line by prefixing it with a second . define a i . . b unless : >= i : length b display : number->string : length b display : list-ref b i newline apply a ( + i 1 ) b a 0 \"123\" \"345\" \"567\" "))) (with-test-prefix "hello" (pass-if-equal '((define (hello who) ;; include the newline (format #t "~A ~A!\n" "Hello" who)) (hello "Wisp") ) (wisp->list " define : hello who ;; include the newline format #t \"~A ~A!\\n\" . \"Hello\" who hello \"Wisp\" "))) (with-test-prefix "mtest" (pass-if-equal '(#!/home/arne/wisp/wisp-multiline.sh !# (display 1) ) (wisp->list " #!/home/arne/wisp/wisp-multiline.sh !# display 1 "))) (with-test-prefix "multiline-string" (pass-if-equal '((display " This is a \"multi-line\" string. ") ) (wisp->list " display \" This is a \\\"multi-line\\\" string. \" "))) (with-test-prefix "namedlet" (pass-if-equal '(#!/home/arne/wisp/wisp-multiline.sh ; !# (define (hello who) (display who)) (let hello ((who 0)) (if (= who 5) (display who) (hello (+ 1 who)))) ) (wisp->list " #!/home/arne/wisp/wisp-multiline.sh ; !# define : hello who display who let hello : who 0 if : = who 5 display who hello : + 1 who "))) ;; the following is no error, but produces a warning because indentation is inconsistent. (with-test-prefix "partial-indent" (pass-if-equal '((write (list (+ 1 2) (+ 2 3))) (newline) (write (list (+ 1 2 (+ 3 4)) (+ 2 3))) (newline) ) (wisp->list " write list + 1 2 + 2 3 newline write list + 1 2 + 3 4 + 2 3 newline "))) (with-test-prefix "quotecolon" (pass-if-equal '(#!/home/arne/wisp/wisp-multiline.sh ; !# (define a 1 ); test whether ' : correctly gets turned into '( ; and whether brackets in commments are treated correctly. (define a '(1 2 3)) (define (a b) (c)) (define a (quasiquote ,(+ 2 2))) ) (wisp->list " #!/home/arne/wisp/wisp-multiline.sh ; !# define a 1 ; test whether ' : correctly gets turned into '( ; and whether brackets in commments are treated correctly. define a ' : 1 2 3 define a b c define a : quasiquote , : + 2 2 "))) (with-test-prefix "range" (pass-if-equal '((import (rnrs)) (define range (case-lambda ((n ); one-argument syntax (range 0 n 1)) ((n0 n ); two-argument syntax (range n0 n 1)) ((n0 n s ); three-argument syntax (assert (and (for-all number? (list n0 n s)) (not (zero? s)))) (let ((cmp (if (positive? s) >= <= ))) (let loop ((i n0 ) (acc '())) (if (cmp i n ) (reverse acc) (loop (+ i s) (cons i acc)))))))) (display (apply string-append "" (map number->string (range 5)))) (newline) ) (wisp->list " import : rnrs define range case-lambda : n ; one-argument syntax range 0 n 1 : n0 n ; two-argument syntax range n0 n 1 : n0 n s ; three-argument syntax assert and for-all number? : list n0 n s not : zero? s let : : cmp : if (positive? s) >= <= let loop : i n0 acc '() if cmp i n reverse acc loop (+ i s) (cons i acc) display : apply string-append \"\" : map number->string : range 5 newline"))) (with-test-prefix "readable-tests" (pass-if-equal '((define (fibfast n) (if (< n 2)) n (fibup n 2 1 0 )) (define (fibup maxnum count n-1 n-2) (if (= maxnum count) (+ n-1 n-2) (fibup maxnum (+ count 1 ) (+ n-1 n-2 ) n-1))) (define (factorial n) (if (<= n 1) 1 (* n (factorial (- n 1))))) (define (gcd x y) (if (= y 0)) x (gcd y (rem x y))) (define (add-if-all-numbers lst) (call/cc (lambda (exit) (let loop ( (lst lst ) (sum 0)) (if (null? lst) sum (if (not (number? (car lst))) (exit #f) (+ (car lst) (loop (cdr lst))))))))) ) (wisp->list " define : fibfast n if : < n 2 . n fibup n 2 1 0 define : fibup maxnum count n-1 n-2 if : = maxnum count + n-1 n-2 fibup maxnum + count 1 + n-1 n-2 . n-1 define : factorial n if : <= n 1 . 1 * n factorial : - n 1 define (gcd x y) if (= y 0) . x gcd y rem x y define : add-if-all-numbers lst call/cc lambda : exit let loop : lst lst sum 0 if : null? lst . sum if : not : number? : car lst exit #f + : car lst loop : cdr lst"))) (with-test-prefix "receive" (pass-if-equal '((import (ice-9 receive) (srfi srfi-1)) (write (receive (car cdr) (car+cdr '(car . cdr)) car)) ) (wisp->list " import (ice-9 receive) (srfi srfi-1) write receive : car cdr car+cdr '(car . cdr) . car "))) (with-test-prefix "self-referencial" (pass-if-equal '(; http://stackoverflow.com/questions/23167464/scheme-self-reference-lambda-macro ; because this is as cool as things get (define-syntax slambda (lambda (x) (syntax-case x () ((slambda formals body0 body1 ...) (with-syntax ((self (datum->syntax #'slambda 'self))) #'(letrec ((self (lambda formals body0 body1 ...))) self)))))) ( (slambda (x) (+ x 1)) 10) ((slambda () self)) ) (wisp->list " ; http://stackoverflow.com/questions/23167464/scheme-self-reference-lambda-macro ; because this is as cool as things get define-syntax slambda lambda : x syntax-case x : : slambda formals body0 body1 ... with-syntax : self : datum->syntax #'slambda 'self #' letrec : : self : lambda formals body0 body1 ... . self : slambda (x) : + x 1 . 10 : slambda () self "))) (with-test-prefix "shebang" (pass-if-equal '(#!/usr/bin/wisp.py # !# ; This tests shebang lines ) (wisp->list " #!/usr/bin/wisp.py # !# ; This tests shebang lines "))) (with-test-prefix "strangecomments" (pass-if-equal '((use-modules (wisp-scheme)) ; works (display (call-with-input-string "foo ; bar\n ; nop \n\n; nup\n; nup \n \n\n\n foo : moo \"\n\" \n___ . goo . hoo" wisp-scheme-read-chunk)) (newline) (display (call-with-input-string "foo \n___ . goo . hoo" wisp-scheme-read-chunk)) (newline) ) (wisp->list " use-modules : wisp-scheme ; works display call-with-input-string \"foo ; bar\\n ; nop \\n\\n; nup\\n; nup \\n \\n\\n\\n foo : moo \\\"\\n\\\" \\n___ . goo . hoo\" wisp-scheme-read-chunk newline display call-with-input-string \"foo \\n___ . goo . hoo\" wisp-scheme-read-chunk newline "))) (with-test-prefix "sublist" (pass-if-equal '(; sublists allow to start single line function calls with a colon ( : ). (defun a (b c) (let ((e . f)) g)) ) (wisp->list " ; sublists allow to start single line function calls with a colon ( : ). ; defun a : b c let : : e . f . g "))) (with-test-prefix "sxml" (pass-if-equal '((use-modules (sxml simple)) (use-modules (ice-9 match)) ; define a template (define template (quote (html (head (title "test")) (body (h1 "test") (message "the header") (p "it " (em "works!") (br) (" it actually works!")))))) ; transform it (define template2 (let loop ((l template)) (match l (('message a ...) `(p (@ (style "margin-left: 2em")) (strong ,(map loop a)))) ((a ...) (map loop a )) (a a)))) ; write xml to the output port (sxml->xml template2) (newline) ) (wisp->list " use-modules : sxml simple use-modules : ice-9 match ; define a template define template quote html head : title \"test\" body h1 \"test\" message \"the header\" p \"it \" : em \"works!\" br \" it actually works!\" ; transform it define template2 let loop : l template match l : 'message a ... ` p : @ : style \"margin-left: 2em\" strong ,(map loop a) : a ... map loop a a . a ; write xml to the output port sxml->xml template2 newline "))) (with-test-prefix "syntax-colon" (pass-if-equal '((let ( (a 1) (b 2)) (let ( ( c 3)) (format #t "a: ~A, b: ~A, c: ~A" a b c))) ((a)) (define (hello) (display "hello\n")) (let ((a 1) (b 2)) (format #t "a: ~A, b: ~A" a b)) (let ((a '()))) (let ( ; foo (a '()))) ( (a)) (define (:) (hello)) (:) ) (wisp->list " let : a 1 b 2 let : : . c 3 format #t \"a: ~A, b: ~A, c: ~A\" . a b c : a define : hello display \"hello\\n\" let : a 1 b 2 format #t \"a: ~A, b: ~A\" . a b let : : a ' : let : ; foo a ' : a define : \\: hello \\: "))) (with-test-prefix "syntax-dot" (pass-if-equal '((define (foo) "bar") (define (bar) '(1 . 2 )); pair (display (foo)) (newline) (display (bar)) (newline) ) (wisp->list " define : foo . \"bar\" define : bar ' 1 . . 2 ; pair display : foo newline display : bar newline "))) (with-test-prefix "syntax-empty" (pass-if-equal '() (wisp->list " "))) (with-test-prefix "syntax-indent" (pass-if-equal '((define (hello who) (format #t "Hello ~A\n" who)) (define (let ( (a 1) (b 2) (c 3)) (format #t "a: ~A, b: ~A, c: ~A" (+ a 2) b c))) ) (wisp->list "define hello who format #t \"Hello ~A\\n\" who define let : a 1 b 2 c 3 format #t \"a: ~A, b: ~A, c: ~A\" + a 2 . b c "))) (with-test-prefix "syntax-strings-parens" (pass-if-equal '(; Test linebreaks in strings and brackets "flubbub flabbab" (hrug (nadda madda gadda "shoktom mee" " sep ka" hadda) (gom)) (flu) (sum [foo bar] barz {1 + [* 2 2]}) (mara { li + lo - (mabba) }) ) (wisp->list " ; Test linebreaks in strings and brackets . \"flubbub flabbab\" hrug (nadda madda gadda \"shoktom mee\" \" sep ka\" hadda) gom flu sum [foo bar] barz {1 + [* 2 2]} mara { li + lo - (mabba) } "))) (with-test-prefix "syntax-underscore" (pass-if-equal '((define (a b c) (d e (f) (g h) i)) (define (_) (display "hello\n")) (_) ) (wisp->list " define : a b c _ d e ___ f ___ g h __ . i define : _ _ display \"hello\\n\" \\_ ")))