rebirth: Add viability test suite

This is an initial test suite that is by no means comprehensive, but is a
pretty good starting point.  At this point, it's important that we be
confident in certain aspects of the system to preempt difficulty in
debugging problems when using 3rd-party code (we'll be using psyntax at some
point, for example).

We also need to be able to have confidence in refactoring.  We do gain
limited confidence when Rebirth is able to successfully compile itself, but
finer details are missed, as has been demonstrated by recent commits (which
fix bugs found by this test suite).

This is also the first test of writing another program in Rebirth that isn't
Rebirth itself.  It's exciting, and helps make obvious certain pain points,
like the need to include core packages.  Further, we can't include important
definitions in `rebith.scm' itself.  Consequently, I think it may be in my
best interest to just add support to Birth for `include' so that the first
pass of Rebirth can benefit from it.  I didn't want to do so because I
wanted Birth to be a faithful re-implementation of Prebirth, but it's not
worth it.  This would be a minor addition and would save so much trouble.

I saw a couple mentions of JS Lisps today on HN (one a tiny Lisp that
compiles to JS, one an interpreter)---neither of them come close to what
Ulambda will be (and in many ways with Rebirth already is), so I'm excited
to keep development going.  There is a niche to be filled.  And I intend to
keep this project secret until it can actually be called "Scheme" (and maybe
beyond that too, since I have Gibble to work on as well).

Anyway, enjoy the first non-Rebirth Rebirth program!  Hello, world!

* bootstrap/Bootstrap.js (bootstrap): Include `#_testViable' call after
    Rebirth compilation.
  (_birth): Throw any error received during Birth compilation.
  (_makeCompiler): Accumulate output for each `console.log' in compiler,
    rather than overwriting with each call (that latter behavior was never
    intended in the first place).
  (_compileRebirth): Throw error on compilation failure.
  (_testViable): New method.
  (_strmap)[rebirthTestCompiling, rebirthTestCompiled, rebirthTestFailed]
    [rebirthTestDone]: New functions.
* bootstrap/rebirth.scm: Add comment clarifying that Rebirth should _not_ be
    used as a general-purpose little Lisp.
* bootstrap/rebirth/.dir-locals.el: New file.
* bootstrap/rebirth/es.scm: Add comment comparing environment implementation
    to a spaghetti stack (a term which I was unfamiliar with until recently
    while researching some Lisp history).
  (es:json-stringify): New procedure.
  (es:try): New macro.
* bootstrap/rebirth/relibprebirth.scm (es:null): New procedure for use by
    test suite.
  (fold): Fix argument naming order.
  (zero?): Correctly use `=' instead of `eq?'.
* bootstrap/rebirth/test.scm: New viability test suite.
master
Mike Gerwitz 2018-09-11 22:30:01 -04:00
parent 34518e1532
commit cf73eb4a31
Signed by: mikegerwitz
GPG Key ID: 8C917B7F5DC51BA2
6 changed files with 757 additions and 16 deletions

View File

@ -88,6 +88,7 @@ class Bootstrap
return this._birth()
.then( birth => this._rebirth( birth ) )
.then( rebirth => this._testViable( rebirth ) )
.catch( e => this._error( e ) )
.then( status =>
this._log( "=> " + this._doneMessage( status ) )
@ -137,7 +138,11 @@ class Bootstrap
"libprebirth.js": lib
} );
const birthout = birthf( scm );
const [ e, birthout ] = birthf( scm );
if ( e ) {
throw e;
}
this._verifyBirthOutput( birthout, birthjs );
@ -201,10 +206,10 @@ class Bootstrap
const birth = new Function(
'let __fsinit = this.__fsinit;' +
'let require = this.require;' +
'let birthout = "";\n' +
'const console = { log: str => birthout = str + "\\n" };\n' +
js +
"return birthout;"
'let out = "";\n' +
'const console = { log: str => out += str + "\\n" };\n' +
'try {' + js + '} catch( e ) { return [ e, out ]; }' +
'return [ null, out ];'
);
return scm =>
@ -267,19 +272,18 @@ class Bootstrap
* @param {number=} n target Rebirth generation id
* @param {string=} prev previous Rebirth generation
*
* @throws {Error} if compiler COMPILE produces no output
*
* @return {Promise<function(string):string>} final Rebirth generation
* or Error on error
*/
_compileRebirth( compile, scm, fs, n = 1, prev = "" )
{
this._strout( 'rebirthCompiling', n );
const birthout = compile( scm );
const [ e , birthout ] = compile( scm );
if ( birthout === '' ) {
if ( e || birthout === '' ) {
return Promise.reject(
Error( "Rebirth compilation yielded no output" )
e || Error( "Rebirth compilation yielded no output" )
);
}
@ -303,6 +307,64 @@ class Bootstrap
}
/**
* Compile and run the Rebirth Viability Test suite
*
* Once a Rebirth generation has been chosen, it can be used to compile
* and run a test suite. This suite not only aids in hacking the
* bootstrap process and preventing regressions, but also makes clear
* _what_ part of Rebirth might have problems.
*
* If the test suite succeeds, then Rebirth is said to be viable and can
* then be used to compile Ulambda core.
*
* The return value is a Promise that is either resolved with the given
* REBIRTH or rejected with an error. In either case, the output of the
* test suite will be logged to the console.
*
* @param {function(string):string} compile Rebirth compiler
*
* @return {Promise<function(string):string>} final Rebirth generation
* or Error on error
*/
_testViable( rebirth )
{
return this._loadPaths( [
[ "rebirth/test.scm", "Rebirth Viability Test" ],
] )
.then( ( [ scm ] ) =>
{
this._strout( 'rebirthTestCompiling' );
const [ e, testsuite ] = rebirth( scm );
if ( e || testsuite === '' ) {
return Promise.reject(
e || Error( "Rebirth test suite yielded no output" )
);
}
this._strout( 'rebirthTestCompiled', testsuite.length );
// invoke the compiled test suite, making note of both the
// error and the output
const [ te, output ] = this._makeCompiler( testsuite )();
this._log( output.trimRight() );
if ( te !== null ) {
this._strout( 'rebirthTestFailed' );
return Promise.reject( te );
}
this._strout( 'rebirthTestDone' );
// rebirth is good, so continue to propagate it
return Promise.resolve( rebirth );
} );
}
/**
* Produce a promise for the file contents of each of `path'
*
@ -521,7 +583,28 @@ Bootstrap._strmap = {
rebirthDone: n =>
"+ Rebirth stopped changing after Re" + Bootstrap._supmap[ n ] +
"birth, so that\n" +
"+ generation will serve as our final one. The last\n" +
"+ generation will serve as our final one. We will now\n" +
"+ use it to compile and run a primitive test suite that\n" +
"+ will determine its viability (whether it might be able\n" +
"+ to compile Ulambda). Running a test suite will allow\n" +
"+ us to clearly see any problems that may exist.",
rebirthTestCompiling: () =>
"Compiling Rebirth Viability Test...",
rebirthTestCompiled: len =>
`Compilation complete (len=${len}).`,
rebirthTestFailed: () =>
"+ Rebirth failed its test suite! This means that it is\n" +
"+ likely not suitable for compiling Ulambda, and so we\n" +
"+ cannot proceed with bootstrapping. If you are hacking\n" +
"+ the bootstrap process, the above failures will give you\n" +
"+ useful information; otherwise, please report this bug\n" +
"+ so that someone can help to fix it for you. Bummer.",
rebirthTestDone: () =>
"+ Rebirth appears to be a viable candidate! The last\n" +
"+ step is to use it to compile Ulambda.",
fatal: ( e ) =>

View File

@ -22,6 +22,10 @@
;;; artifact for those who wish to build Ulambda Scheme from scratch without
;;; using another version of Ulambda itself. This is called "self-hosting".
;;;
;;; REBIRTH SHOULD NOT BE USED AS A GENERAL-PURPOSE LISP. It is very
;;; narrowly tested and absolutely has bugs or misfeatures that were
;;; explicitly ignored (some of which are explicitly noted).
;;;
;;; This is the compiler for Rebirth Lisp---it builds off of Birth by
;;; first eliminating the need for libprebirth; this allows _all_
;;; development to happen in a Lisp dialect, which liberates the last

View File

@ -0,0 +1,9 @@
;; Per-directory local variables for GNU Emacs 23 and later.
((scheme-mode
. ((indent-tabs-mode . nil)
(eval . (put 'es:while 'scheme-indent-function 0))
(eval . (put 'topic 'scheme-indent-function 1))
(eval . (put 'verify 'scheme-indent-function 1))
(eval . (put 'skip 'scheme-indent-function 1))
(eval . (put 'expect 'scheme-indent-function 1)))))

View File

@ -56,9 +56,8 @@
;;; perfectly by ECMAScript's prototype model---a reference to a key on a
;;; prototype chain is transparently proxied up the chain until it is
;;; found. Therefore, environments are chained using a simple
;;; `Object.create'. For now, anyway---that's the easy solution for now, but
;;; in the future (Ulambda) we're likely to have a heap instead, once we have
;;; static analysis.
;;; `Object.create'. This creates a data structure similar to a spaghetti
;;; stack.
;;;
;;; Initially, everything here was a near-exact copy of the `fnmap-premacro'
;;; forms, re-arranged as needed for compilation (see limitations of
@ -79,6 +78,10 @@
(define-macro (es:error . args)
(`quote (%es:native-apply console.error (unquote@ args))))
(define (es:json-stringify value)
(string->es "JSON.stringify($$value)"))
;; Expand the body BODY into a new environment. Environments are
;; currently handled by the ES runtime, so this is easy.
(define-macro (es:envf env . body)
@ -102,6 +105,12 @@
;; Reference to current environment object.
(define-es-macro (%es:env) "_env")
;; Simple macro to encapsulate an expression in a try/catch and avoid
;; having to implement anything more complicated atm.
(define-es-macro (es:try try else)
"(function(){try{return " (sexp->es try) ";}"
"catch(e){return " (sexp->es else) "(e.message);}})()")
;; Don't worry---basic tail call support (at least for recursion) is
;; nearing, and then we can get rid of this ugly thing.
(define-es-macro (es:while pred . body)

View File

@ -53,6 +53,7 @@
(define (es:defined? x)
(let ((id (tname->id x)))
(string->es "eval('typeof ' + $$id) !== 'undefined'")))
(define es:null (string->es "null"))
(define (es:null? x)
(string->es "$$x === null"))
@ -137,7 +138,7 @@
;; R7RS math
(define (+ . xs)
(fold (lambda (y x)
(fold (lambda (x y)
(string->es "$$x + $$y"))
0
xs))
@ -149,7 +150,7 @@
(car xs)
(cdr xs))))
(define (zero? x)
(eq? x 0))
(= x 0))
;; SRFI-1
;; warning: fold here only supports one list

View File

@ -0,0 +1,635 @@
;;;; Viability suite for Rebirth Lisp
;;;;
;;;; Copyright (C) 2018 Mike Gerwitz
;;;;
;;;; This file is part of Ulambda Scheme.
;;;;
;;;; Ulambda Scheme is free software: you can redistribute it and/or modify
;;;; it under the terms of the GNU Affero General Public License as
;;;; published by the Free Software Foundation, either version 3 of the
;;;; License, or (at your option) any later version.
;;;;
;;;; This program 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 General Public License for more details.
;;;;
;;;; You should have received a copy of the GNU Affero General Public License
;;;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;;;
;;; Commentary:
;;; THIS IS BOOTSTRAP CODE INTENDED FOR USE ONLY IN REBIRTH.
;;;
;;;
;;; Rebirth has two major goals:
;;; 1. Be just good enough to begin writing Ulambda in a sane Lisp dialect
;;; without having to make too many negative compromises; and
;;; 2. To prove that certain implementation details are
;;; successful---Rebirth is an experiment in a number of concepts.
;;;
;;; Rebirth is to be considered _viable_ when it meets both of those
;;; goals. The viability of Rebirth is determined by this test suite.
;;;
;;; This file should be compiled using the last generation of Rebirth and
;;; directly executed. There are no external dependencies or fancy
;;; libraries here, since Rebirth is too primitive to support much of
;;; that. If successful, this compiled script should exit with a zero
;;; status code.
;;;
;;; Certain tests may be marked as skipped if Rebirth features are under
;;; development.
;;;
;;; Code:
;; TODO: These ought to be automatically included by Rebirth
(include "rebirth/es.scm")
(include "rebirth/relibprebirth.scm")
(include "rebirth/macro.scm")
(define (not x)
(if x #f #t))
;;; {Test Runner}
;;;
;; This runner uses an imperative style because Rebirth lacks convenient
;; functional primitives (and so this is far more concise). Mutators are
;; discouraged in Ulambda.
;;
;; These counts are increased during test execution.
(define testn 0)
(define testfail 0)
(define skipn 0)
;; Output message to standard out.
(define (output str)
(es:console str))
;; Run suite of tests described by TESTS.
;;
;; Test results are output as an S-expression.
(define-macro (test-suite . tests)
(`quote
(begin
(output "(test-results")
(unquote@ tests)
(test-report))))
;; Group tests TESTS into a topic with description DESC
;;
;; The description DESC will be output as a comment along with the number of
;; tests in TESTS. This is intended purely as a visual aid.
(define-macro (topic desc . tests)
(`quote
(begin
(output (string-append " ;; " (unquote desc)
" (" (unquote (length tests)) ")"))
(unquote@ tests))))
;; Verify that the given value VALUE matches the expected value EXPECTED.
;;
;; A line describing this test will be output as an S-expression (see
;; `test-result').
(define-macro (verify desc value expected)
(`quote
(let* ((result (es:try (unquote value)
(lambda (err) err)))
(ok (deep-eq? result (unquote expected))))
(test-result (unquote desc)
ok
(if ok "ok" "FAIL")
result)
(if (not ok) (set! testfail (+ 1 testfail))))))
;; Skip a test and output it as such.
;;
;; This should only ever be used when test cases are written to demonstrate
;; failing cases with the intent to fix them. It is dangerous to use this
;; template, since they could easily be forgotten about---skipped tests do
;; _not_ fail.
(define-macro (skip desc . _)
(`quote
(begin
(test-result (unquote desc) #t "SKIP" #f)
(set! skipn (+ 1 skipn)))))
;; Output result of a test.
;;
;; An S-expression will be output including the test number; result of the
;; test as a string TAG; a description DESC of the test; and, on failure, a
;; comment describing the bad value as a JSON-encoded string (because that's
;; convenient; Rebirth does not have a pretty-printer for S-expressions).
(define (test-result desc ok tag result)
(output (string-append " (" testn " '" tag " \"" desc "\")"
(if (not ok)
(string-append " ; " (es:json-stringify result)))))
(set! testn (+ 1 testn)))
;; Output a final test report with total, failure, and skip counts.
;;
;; This outputs a line as an S-expression and adds the closing parenthesis
;; for `test-suite'.
;;
;; If there are any failures, an error is thrown.
(define (test-report)
(output (string-append " (summary (tests . " testn ") "
"(failures . " testfail ") "
"(skipped . " skipn "))"))
(if (not (eq? testfail 0))
(error "Rebirth is not viable!")))
;; Primitive recursive eq? check.
(define (deep-eq? x y)
;; Universal quantifier over list LS for truthy values.
(define (everyt? ls)
(fold (lambda (x y) (and x y))
#t
ls))
(or (and (list? x)
(list? y)
(eq? (length x) (length y))
(everyt? (map deep-eq? x y)))
(and (not (or (list? x)
(list? y)))
(eq? x y))))
;;; {Test Cases}
;;;
(test-suite
(topic "car"
(verify "car of pair"
(car (list 1))
1)
(verify "car of multi-element pair"
(car (list 2 3))
2))
(topic "cdr"
(verify "cdr of pair"
(cdr (list 1))
(list))
(verify "cdr of multi-eement pair"
(cdr (list 1 2))
(list 2))
(skip "cdr of improper cons'd pair"
(cdr (cons 1 2))
2)
(skip "cdr of proper dotted pair"
(cdr (quote (1 . (2 3))))
(list 2 3))
(skip "cdr of improper dotted pair"
(cdr (quote (1 . 2)))
2))
(topic "lists"
(verify "list? with empty list"
(list? (list))
#t)
(verify "list? with non-empty list"
(list? (list 1 2))
#t)
(skip "list? with two-element improper list"
(list? (quote (1 . 2)))
#f)
(skip "list? with many-element improper list"
(list? (quote (1 2 3 . 4)))
#f)
(verify "list? with non-list object"
(or (list? 5)
(list? #t)
(list? #f)
(list? (quote symbol))
(list? "string"))
#f)
(verify "list? with proper cons"
(list? (cons 1 (list)))
#t)
(skip "list? with improper cons"
(list? (cons 1 2))
#f)
(verify "pair? with empty list"
(pair? (list))
#f)
(verify "pair? with single-element list"
(pair? (list 1))
#t)
(verify "pair? with multi-element list"
(pair? (list 1 2))
#t)
(skip "pair? with improper list"
(pair? (quote (1 . 2)))
#t)
(verify "pair? with proper cons"
(pair? (cons 1 (list)))
#t)
(skip "pair? with improper cons"
(pair? (cons 1 2))
#t)
(verify "length of list"
(length (list 1 2 3 4 5))
5))
(topic "lambda/define"
(verify "thunk"
(begin
(define f (lambda () (quote thunky)))
(f))
(quote thunky))
(verify "with parameters"
(begin
(define f (lambda (x y) (list x y)))
(f (quote a) (quote b)))
(list (quote a) (quote b)))
(verify "define procedure shorthand"
(begin
(define (f x y) (list x y))
(f (quote a) (quote b)))
(list (quote a) (quote b)))
(verify "define varadic"
(begin
(define (f x y . rest)
(list x y rest))
(f 1 2 3 4 5))
(list 1 2 (list 3 4 5)))
(verify "define value"
(begin
(define x 5)
x)
5))
(topic "let/let*"
(verify "let with no bindings"
(let () (quote ok))
(quote ok))
(verify "let with one binding"
(let ((x 1)) x)
1)
(verify "let with multiple bindings"
(let ((x 1)
(y 2))
(list x y))
(list 1 2))
(verify "let reference sibling binding"
(let ((x 1))
(let ((x 2)
(y x))
y))
1)
(verify "let with multiple body expressions"
(let ()
1
2
3)
3)
(verify "let* with no bindings"
(let* () (quote ok))
(quote ok))
(verify "let* with one binding"
(let* ((x 1)) x)
1)
(verify "let* with multiple bindings"
(let* ((x 1)
(y 2))
(list x y))
(list 1 2))
(verify "let* reference sibling binding"
(let* ((x 1))
(let* ((x 2)
(y x))
y))
2)
(verify "let* with multiple body expressions"
(let ()
2
3
4)
4))
(topic "begin"
(verify "begin with one expression"
(begin (quote ok))
(quote ok))
(verify "begin with multiple expressions"
(begin
(quote first)
(quote second)
(quote third))
(quote third))
(verify "begin with definition"
(begin
(define x 5)
x)
5))
(topic "strings"
(verify "substring with start and end"
(substring "foobar" 2 5)
"oba")
(verify "string-length"
(string-length "foo")
3)
(verify "string=? with differing strings"
(string=? "foo" "bar")
#f)
(verify "string=? with identical strings"
(string=? "baz" "baz")
#t)
(verify "string-append with no strings"
(string-append)
"")
(verify "string-append with one string"
(string-append "foo")
"foo")
(verify "string-append with many strings"
(string-append "foo" "bar" "baz" "quux")
"foobarbazquux"))
(topic "math"
(verify "+ with no argments"
(+)
0)
(verify "+ with one argument"
(+ 5)
5)
(verify "+ with two arguments"
(+ 5 4)
9)
(verify "+ with multiple arguments"
(+ 5 4 3 -2 -1)
9)
(verify "- with one argument"
(- 3)
-3)
(verify "- with two arguments"
(- 3 2)
1)
(verify "- with multiple arguments"
(- 3 2 1 -4)
4))
;; Rebirth comparison procedures support two arguments
(topic "comparison"
(verify "= with two arguments"
(and (= 5 5)
(not (= 4 3)))
#t)
(verify "< with two arguments"
(and (< 5 6)
(not (< 6 4)))
#t)
(verify "> with two arguments"
(and (> 5 4)
(not (> 3 4)))
#t)
(verify "zero? with two arguments"
(and (zero? 0)
(not (zero? 5)))
#t)
(verify "eq? given the same object"
(let ((x (list 1 2 3)))
(eq? x x))
#t)
(verify "eq? given different but similar objects"
(let ((x (list 1 2 3))
(y (list 1 2 3)))
(eq? x y))
#f))
(topic "if"
(verify "if #t is truthy"
(if #t 1)
1)
(verify "if #f is not truthy"
(if #f #f 2)
2)
(verify "if non-empty string is truthy"
(if "string" #t)
#t)
(verify "if empty string is truthy"
(if "" #t)
#t)
(verify "if symbol is truthy"
(if (quote symbol) #t)
#t)
(verify "if unspecified is truthy"
(if (if #f #f) #t)
#t)
(verify "if short-circuits when true"
(if #t #t (error "should not happen"))
#t)
(verify "if short-circuits when false"
(if #f (error "should not happen") #t)
#t))
(topic "and"
(verify "and nullary"
(and)
#t)
(verify "and unary truthy"
(and #t)
#t)
(verify "and unary false"
(and #f)
#f)
(verify "and nary all truthy"
(and #t "" 0 (lambda ()) (quote all))
(quote all))
(verify "and nary one false"
(and #t #t #f)
#f))
(topic "or"
(verify "or nullary"
(or)
#f)
(verify "or unary truthy"
(or #t)
#t)
(verify "or unary false"
(or #f)
#f)
(verify "or nary one truthy"
(or #f #f #f (quote any))
(quote any))
(verify "or nary all false"
(or #f #f #f)
#f))
(topic "case"
(verify "case with single clause predicate"
(case #t
((#t) (quote ok)))
(quote ok))
(verify "case with multiple clause predicates"
(case 5
((1 2 3 4 5) #t))
#t)
(verify "case with multiple clauses"
(case #f
((#t) (quote t))
((#f) (quote f)))
(quote f))
(verify "case with else"
(case 5
((#t) #f)
(else (quote else)))
(quote else)))
(topic "append"
(verify "append with no arguments"
(append)
(list))
(verify "append with one list"
(append (list 1 2))
(list 1 2))
(verify "append with multiple lists"
(append (list 1 2) (list) (list 3 4 5))
(list 1 2 3 4 5)))
(topic "map"
(verify "map with empty list"
(map (lambda (_))
(list))
(list))
(verify "map with non-empty list"
(map (lambda (x) (+ 1 x))
(list 1 2 3))
(list 2 3 4))
(verify "map with multiple lists"
(map (lambda (x y) (+ x y))
(list 1 2 3)
(list 11 12 13))
(list 12 14 16)))
(topic "fold"
(verify "fold with empty list"
(fold (lambda (_ __))
(list)
(list))
(list))
;; Rebirth's fold only supports one list
(verify "fold with non-empty list"
(fold (lambda (x y) (+ x y))
0
(list 1 2 3))
6))
;; certain things (like es:defined?) cannot be tested here because compiler
;; procedures are not available at runtime
(topic "es procedures/macros"
(verify "es:null?"
(and (es:null? es:null)
(not (es:null? "")))
#t)
(verify "es:array? given list"
(es:array? (list))
#t)
(verify "es:array? given non-list"
(or (es:array? "")
(es:array? #t)
(es:array? 5)
(es:array? es:array?))
#f)
(verify "es:error and es:try"
(es:try (error "test")
(lambda (e) (quote thrown)))
(quote thrown))
;; these also verify that the given Scheme object is properly recognized
;; by Rebirth (and produces the intended internal representation)
(verify "es:typeof on list"
(es:typeof (list 1 2))
"object")
(verify "es:typeof on string"
(es:typeof "string")
"string")
(verify "es:typeof on non-negative number"
(es:typeof 1)
"number")
(verify "es:typeof on negative number"
(es:typeof -1)
"number")
(verify "es:typeof on float"
(es:typeof 1.5)
"number")
(verify "es:typeof on float with no preceding digit"
(es:typeof .5)
"number")
(verify "es:typeof on negative float"
(es:typeof -0.5)
"number")
(verify "es:typeof on negative float with no preceding digit"
(es:typeof -.5)
"number")
(verify "es:typeof on #t"
(es:typeof #t)
"boolean")
(verify "es:typeof on #f"
(es:typeof #f)
"boolean")
(verify "es:typeof on procedure"
(es:typeof (lambda ()))
"function")
(verify "es:typeof on symbol"
(es:typeof (quote foo))
"symbol")
(verify "es:match with es:regexp"
(and (es:match (es:regexp "^foo") "foobar")
(not (es:match (es:regexp "^bar") "foobar")))
#t)
(verify "es:replace with string"
(es:replace "foo" "bar" "foobar")
"barbar")
(verify "es:replace with es:regexp"
(es:replace (es:regexp "^fo+") "q" "foobar")
"qbar")
(verify "es:while with predicate"
(let ((x 3))
(es:while (> x 1) (set! x (- x 1)))
x)
1)
(verify "es:while with es:break"
(let ((x 3))
(es:while (> x 1)
(set! x (- x 1))
(es:break))
x)
2))
(topic "eval and environment"
(skip "eval in new environment"
(eval (quote
(let ((x 5))
(+ x 5)))
(null-environment 5))
10)
(skip "eval inheriting environment"
(let ((x (quote xsym)))
(eval (list x)
(es:inherit-env)))
(quote xsym))))