aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--TODO20
-rw-r--r--minimal.scm2
-rw-r--r--prelude.scm3
-rw-r--r--rust/TODO10
-rw-r--r--rust/notes.txt3
-rw-r--r--rust/spectrum.rs35
-rw-r--r--test/r5rs_pitfall.scm332
-rw-r--r--test/simple.scm48
-rw-r--r--test/unicode.scm3
-rw-r--r--test/unicode_lambda.scm6
10 files changed, 431 insertions, 31 deletions
diff --git a/TODO b/TODO
index 644128b..2996558 100644
--- a/TODO
+++ b/TODO
@@ -1,22 +1,16 @@
-- rust:
- - define/set! support (at the REPL?)
- - refactor out lifetime specifiers
- - add file input support (eg, comments in parser, multi-line, tty detect)
- - multi-part lambdas
- - begin (?)
- - map, filter
- - add numerical comparisons (>, >=)
- - basic testing
-
- pseudo unittest; makefile?
+ => good tests at:
+ http://swiss.csail.mit.edu/~jaffer/Scheme
- find r7rs-small; try a full/compliant implementation in scheme/python?
eg, macros
-- define a larger sub-set with let, letrc, etc
- - include a parser
- - should be able to run minimal.scm
- of interest:
julia
c
javascript
haskell
go
+
+links:
+http://www.defmacro.org/ramblings/lisp-in-haskell.html
+https://github.com/mbutterick/mal
+http://norvig.com/lispy2.html
diff --git a/minimal.scm b/minimal.scm
index 5c953e6..218eb5b 100644
--- a/minimal.scm
+++ b/minimal.scm
@@ -1,4 +1,3 @@
-
; cyclic dependency graph (sigh)
;
; meaning
@@ -248,3 +247,4 @@
(lambda (e)
(meaning e (quote ()))))
;(value '((lambda (a b) (a (add1 b))) (lambda (c) (add1 c)) 4)) ; 6
+;(value (quote (lambda (a b) (a (add1 b))) (lambda (c) (add1 c)) 4)) ; 6
diff --git a/prelude.scm b/prelude.scm
index a7469b5..9721c33 100644
--- a/prelude.scm
+++ b/prelude.scm
@@ -27,5 +27,8 @@
(cond ((null? l) ())
(else (cons (f (car l)) (map f (cdr l)))))))
+(define true #t)
+(define false #f)
+
(define for-each map)
;(define compose (lambda (f g) (lambda args (f (apply g args)))))
diff --git a/rust/TODO b/rust/TODO
new file mode 100644
index 0000000..6198755
--- /dev/null
+++ b/rust/TODO
@@ -0,0 +1,10 @@
+- basic tests
+- fold in norvig's "lispy2" additions http://norvig.com/lispy2.html
+- fix quote (should take an expr, not a list?)
+- fix cond/cdr behavior working right; also empty tuple
+- get minimal.scm to run
+- if let
+- use getopt
+? multi-part lambdas
+? apply
+? re-write runge kutta with lists, not vectors
diff --git a/rust/notes.txt b/rust/notes.txt
new file mode 100644
index 0000000..cc35cdf
--- /dev/null
+++ b/rust/notes.txt
@@ -0,0 +1,3 @@
+https://mgattozzi.github.io/2016/11/08/scheme-input.html
+
+see also: tail recursion notes impl notes from norvig
diff --git a/rust/spectrum.rs b/rust/spectrum.rs
index 8325798..73ba59d 100644
--- a/rust/spectrum.rs
+++ b/rust/spectrum.rs
@@ -562,7 +562,7 @@ fn apply_action(list: &Vec<SchemeExpr>,
}
},
_ => Err(format!("cdr only takes lists and quotes (got {})",
- scheme_repr(&args[0]):unwrap()))
+ scheme_repr(&args[0]).unwrap()))
}
},
"cons" => {
@@ -757,8 +757,9 @@ fn repl(verbose: bool, top_env: &mut HashMap<String, SchemeExpr>) {
}
}
-/* This loads and evals the hard-coded prelude file (which is just scheme expressions compiled into
- * the executable), saving the resulting defines in top_env.
+/* Loads and evals the hard-coded prelude file (which is just scheme
+ * expressions compiled into the executable), saving the resulting defines in
+ # top_env.
*/
fn import_prelude(top_env: &mut HashMap<String, SchemeExpr>) -> Result<(), String> {
@@ -778,8 +779,10 @@ fn import_file(fpath: &Path, top_env: &mut HashMap<String, SchemeExpr>) -> Resul
let mut f = File::open(fpath)
.expect(&format!("couldn't open file: {}", &fpath.to_str().unwrap()));
+
f.read_to_end(&mut raw_bytes)
.expect(&format!("couldn't read file: {}", &fpath.to_str().unwrap()));
+
let contents = String::from_utf8(raw_bytes)
.expect(&format!("UTF-8 decode error reading file: {}", &fpath.to_str().unwrap()));
@@ -792,13 +795,13 @@ fn import_file(fpath: &Path, top_env: &mut HashMap<String, SchemeExpr>) -> Resul
}
fn usage() {
- println!("usage:\tspectrum [-h] [-v] [--no-repl] [--no-prelude] [<files>]");
- println!("");
- println!("Files will be loaded in order, then drop to REPL (unless \"--no-repl\" is passed).");
- println!("Verbose flag (\"-v\") will result in lexed tokens and parsed AST \
- being dumped to stdout (when on REPL).");
- println!("A \"prelude\" of common Scheme/LISP functions (eg, cdaddr) will \
- be loaded before any files (unless \"--no-prelude\" is passed).");
+ println!(
+r#"usage:\tspectrum [-h] [-v] [--no-repl] [--no-prelude] [<files>]
+
+Files will be loaded in order, then drop to REPL (unless "--no-repl" is passed).
+Verbose flag ("-v") will result in lexed tokens and parsed AST being dumped to stdout (when on REPL).
+A "prelude" of common Scheme/LISP functions (eg, cdaddr) will be loaded before any files (unless "--no-prelude" is passed).
+"#);
}
fn main() {
@@ -806,7 +809,6 @@ fn main() {
let mut verbose: bool = false;
let mut no_repl: bool = false;
let mut no_prelude: bool = false;
- let mut top_env = HashMap::<String, SchemeExpr>::new();
let mut file_list = Vec::<String>::new();
@@ -828,6 +830,8 @@ fn main() {
}
}
+ let mut top_env = HashMap::<String, SchemeExpr>::new();
+
if !no_prelude {
import_prelude(&mut top_env).unwrap();
}
@@ -839,12 +843,9 @@ fn main() {
return;
}
println!("Loading {}...", fname);
- match import_file(&fpath, &mut top_env) {
- Err(e) => {
- println!("Error loading file: {}\n {}", fname, e);
- return;
- },
- Ok(_) => ()
+ if let Err(e) = import_file(&fpath, &mut top_env) {
+ println!("Error loading file: {}\n {}", fname, e);
+ return;
}
}
diff --git a/test/r5rs_pitfall.scm b/test/r5rs_pitfall.scm
new file mode 100644
index 0000000..87ca3cf
--- /dev/null
+++ b/test/r5rs_pitfall.scm
@@ -0,0 +1,332 @@
+;; From: http://sisc-scheme.org/r5rs_pitfall.scm
+;;
+;; r5rs_pitfalls.scm
+;;
+;; This program attempts to test a Scheme implementation's conformance
+;; to various subtle edge-cases and consequences of the R5RS Scheme standard.
+;; Code was collected from public forums, and is hereby placed in the public domain.
+;;
+;;
+(define-syntax should-be
+ (syntax-rules ()
+ ((_ test-id value expression)
+ (let ((return-value expression))
+ (if (not (equal? return-value value))
+ (for-each (lambda (v) (display v))
+ `("Failure: " test-id ", expected '"
+ value "', got '" ,return-value "'." #\newline))
+ (for-each (lambda (v) (display v))
+ '("Passed: " test-id #\newline)))))))
+
+(define call/cc call-with-current-continuation)
+
+;; Section 1: Proper letrec implementation
+
+;;Credits to Al Petrofsky
+;; In thread:
+;; defines in letrec body
+;; http://groups.google.com/groups?selm=87bsoq0wfk.fsf%40app.dial.idiom.com
+(should-be 1.1 0
+ (let ((cont #f))
+ (letrec ((x (call-with-current-continuation (lambda (c) (set! cont c) 0)))
+ (y (call-with-current-continuation (lambda (c) (set! cont c) 0))))
+ (if cont
+ (let ((c cont))
+ (set! cont #f)
+ (set! x 1)
+ (set! y 1)
+ (c 0))
+ (+ x y)))))
+
+;;Credits to Al Petrofsky
+;; In thread:
+;; Widespread bug (arguably) in letrec when an initializer returns twice
+;; http://groups.google.com/groups?selm=87d793aacz.fsf_-_%40app.dial.idiom.com
+(should-be 1.2 #t
+ (letrec ((x (call/cc list)) (y (call/cc list)))
+ (cond ((procedure? x) (x (pair? y)))
+ ((procedure? y) (y (pair? x))))
+ (let ((x (car x)) (y (car y)))
+ (and (call/cc x) (call/cc y) (call/cc x)))))
+
+;;Credits to Alan Bawden
+;; In thread:
+;; LETREC + CALL/CC = SET! even in a limited setting
+;; http://groups.google.com/groups?selm=19890302162742.4.ALAN%40PIGPEN.AI.MIT.EDU
+(should-be 1.3 #t
+ (letrec ((x (call-with-current-continuation
+ (lambda (c)
+ (list #T c)))))
+ (if (car x)
+ ((cadr x) (list #F (lambda () x)))
+ (eq? x ((cadr x))))))
+
+;; Section 2: Proper call/cc and procedure application
+
+;;Credits to Al Petrofsky, (and a wink to Matthias Blume)
+;; In thread:
+;; Widespread bug in handling (call/cc (lambda (c) (0 (c 1)))) => 1
+;; http://groups.google.com/groups?selm=87g00y4b6l.fsf%40radish.petrofsky.org
+(should-be 2.1 1
+ (call/cc (lambda (c) (0 (c 1)))))
+
+;; Section 3: Hygienic macros
+
+;; Eli Barzilay
+;; In thread:
+;; R5RS macros...
+;; http://groups.google.com/groups?selm=skitsdqjq3.fsf%40tulare.cs.cornell.edu
+(should-be 3.1 4
+ (let-syntax ((foo
+ (syntax-rules ()
+ ((_ expr) (+ expr 1)))))
+ (let ((+ *))
+ (foo 3))))
+
+
+;; Al Petrofsky again
+;; In thread:
+;; Buggy use of begin in r5rs cond and case macros.
+;; http://groups.google.com/groups?selm=87bse3bznr.fsf%40radish.petrofsky.org
+(should-be 3.2 2
+ (let-syntax ((foo (syntax-rules ()
+ ((_ var) (define var 1)))))
+ (let ((x 2))
+ (begin (define foo +))
+ (cond (else (foo x)))
+ x)))
+
+;;Al Petrofsky
+;; In thread:
+;; An Advanced syntax-rules Primer for the Mildly Insane
+;; http://groups.google.com/groups?selm=87it8db0um.fsf@radish.petrofsky.org
+
+(should-be 3.3 1
+ (let ((x 1))
+ (let-syntax
+ ((foo (syntax-rules ()
+ ((_ y) (let-syntax
+ ((bar (syntax-rules ()
+ ((_) (let ((x 2)) y)))))
+ (bar))))))
+ (foo x))))
+
+;; Al Petrofsky
+;; Contributed directly
+(should-be 3.4 1
+ (let-syntax ((x (syntax-rules ()))) 1))
+
+;; Setion 4: No identifiers are reserved
+
+;;(Brian M. Moore)
+;; In thread:
+;; shadowing syntatic keywords, bug in MIT Scheme?
+;; http://groups.google.com/groups?selm=6e6n88%248qf%241%40news.cc.ukans.edu
+(should-be 4.1 '(x)
+ ((lambda lambda lambda) 'x))
+
+(should-be 4.2 '(1 2 3)
+ ((lambda (begin) (begin 1 2 3)) (lambda lambda lambda)))
+
+(should-be 4.3 #f
+ (let ((quote -)) (eqv? '1 1)))
+;; Section 5: #f/() distinctness
+
+;; Scott Miller
+(should-be 5.1 #f
+ (eq? #f '()))
+(should-be 5.2 #f
+ (eqv? #f '()))
+(should-be 5.3 #f
+ (equal? #f '()))
+
+;; Section 6: string->symbol case sensitivity
+
+;; Jens Axel S?gaard
+;; In thread:
+;; Symbols in DrScheme - bug?
+;; http://groups.google.com/groups?selm=3be55b4f%240%24358%24edfadb0f%40dspool01.news.tele.dk
+(should-be 6.1 #f
+ (eq? (string->symbol "f") (string->symbol "F")))
+
+;; Section 7: First class continuations
+
+;; Scott Miller
+;; No newsgroup posting associated. The gist of this test and 7.2
+;; is that once captured, a continuation should be unmodified by the
+;; invocation of other continuations. This test determines that this is
+;; the case by capturing a continuation and setting it aside in a temporary
+;; variable while it invokes that and another continuation, trying to
+;; side effect the first continuation. This test case was developed when
+;; testing SISC 1.7's lazy CallFrame unzipping code.
+(define r #f)
+(define a #f)
+(define b #f)
+(define c #f)
+(define i 0)
+(should-be 7.1 28
+ (let ()
+ (set! r (+ 1 (+ 2 (+ 3 (call/cc (lambda (k) (set! a k) 4))))
+ (+ 5 (+ 6 (call/cc (lambda (k) (set! b k) 7))))))
+ (if (not c)
+ (set! c a))
+ (set! i (+ i 1))
+ (case i
+ ((1) (a 5))
+ ((2) (b 8))
+ ((3) (a 6))
+ ((4) (c 4)))
+ r))
+
+;; Same test, but in reverse order
+(define r #f)
+(define a #f)
+(define b #f)
+(define c #f)
+(define i 0)
+(should-be 7.2 28
+ (let ()
+ (set! r (+ 1 (+ 2 (+ 3 (call/cc (lambda (k) (set! a k) 4))))
+ (+ 5 (+ 6 (call/cc (lambda (k) (set! b k) 7))))))
+ (if (not c)
+ (set! c a))
+ (set! i (+ i 1))
+ (case i
+ ((1) (b 8))
+ ((2) (a 5))
+ ((3) (b 7))
+ ((4) (c 4)))
+ r))
+
+;; Credits to Matthias Radestock
+;; Another test case used to test SISC's lazy CallFrame routines.
+(should-be 7.3 '((-1 4 5 3)
+ (4 -1 5 3)
+ (-1 5 4 3)
+ (5 -1 4 3)
+ (4 5 -1 3)
+ (5 4 -1 3))
+ (let ((k1 #f)
+ (k2 #f)
+ (k3 #f)
+ (state 0))
+ (define (identity x) x)
+ (define (fn)
+ ((identity (if (= state 0)
+ (call/cc (lambda (k) (set! k1 k) +))
+ +))
+ (identity (if (= state 0)
+ (call/cc (lambda (k) (set! k2 k) 1))
+ 1))
+ (identity (if (= state 0)
+ (call/cc (lambda (k) (set! k3 k) 2))
+ 2))))
+ (define (check states)
+ (set! state 0)
+ (let* ((res '())
+ (r (fn)))
+ (set! res (cons r res))
+ (if (null? states)
+ res
+ (begin (set! state (car states))
+ (set! states (cdr states))
+ (case state
+ ((1) (k3 4))
+ ((2) (k2 2))
+ ((3) (k1 -)))))))
+ (map check '((1 2 3) (1 3 2) (2 1 3) (2 3 1) (3 1 2) (3 2 1)))))
+
+;; Modification of the yin-yang puzzle so that it terminates and produces
+;; a value as a result. (Scott G. Miller)
+(should-be 7.4 '(10 9 8 7 6 5 4 3 2 1 0)
+ (let ((x '())
+ (y 0))
+ (call/cc
+ (lambda (escape)
+ (let* ((yin ((lambda (foo)
+ (set! x (cons y x))
+ (if (= y 10)
+ (escape x)
+ (begin
+ (set! y 0)
+ foo)))
+ (call/cc (lambda (bar) bar))))
+ (yang ((lambda (foo)
+ (set! y (+ y 1))
+ foo)
+ (call/cc (lambda (baz) baz)))))
+ (yin yang))))))
+
+;; Miscellaneous
+
+;;Al Petrofsky
+;; In thread:
+;; R5RS Implementors Pitfalls
+;; http://groups.google.com/groups?selm=871zemtmd4.fsf@app.dial.idiom.com
+(should-be 8.1 -1
+ (let - ((n (- 1))) n))
+
+(should-be 8.2 '(1 2 3 4 1 2 3 4 5)
+ (let ((ls (list 1 2 3 4)))
+ (append ls ls '(5))))
+
+;; This example actually illustrates a bug in R5RS. If a Scheme system
+;; follows the letter of the standard, 1 should be returned, but
+;; the general agreement is that 2 should instead be returned.
+;; The reason is that in R5RS, let-syntax always introduces new scope, thus
+;; in the following test, the let-syntax breaks the definition section
+;; and begins the expression section of the let.
+;;
+;; The general agreement by the implementors in 1998 was that the following
+;; should be possible, but isn't:
+;;
+;; (define ---)
+;; (let-syntax (---)
+;; (define ---)
+;; (define ---))
+;; (define ---)
+;;
+;; Scheme systems based on the Portable syntax-case expander by Dybvig
+;; and Waddell do allow the above, and thus often violate the letter of
+;; R5RS. In such systems, the following will produce a local scope:
+;;
+;; (define ---)
+;; (let-syntax ((a ---))
+;; (let ()
+;; (define ---)
+;; (define ---)))
+;; (define ---)
+;;
+;; Credits to Matthias Radestock and thanks to R. Kent Dybvig for the
+;; explanation and background
+(should-be 8.3 1
+ (let ((x 1))
+ (let-syntax ((foo (syntax-rules () ((_) 2))))
+ (define x (foo))
+ 3)
+ x))
+
+;;Not really an error to fail this (Matthias Radestock)
+;;If this returns (0 1 0), your map isn't call/cc safe, but is probably
+;;tail-recursive. If its (0 0 0), the opposite is true.
+(let ((result
+ (let ()
+ (define executed-k #f)
+ (define cont #f)
+ (define res1 #f)
+ (define res2 #f)
+ (set! res1 (map (lambda (x)
+ (if (= x 0)
+ (call/cc (lambda (k) (set! cont k) 0))
+ 0))
+ '(1 0 2)))
+ (if (not executed-k)
+ (begin (set! executed-k #t)
+ (set! res2 res1)
+ (cont 1)))
+ res2)))
+ (if (equal? result '(0 0 0))
+ (display "Map is call/cc safe, but probably not tail recursive or inefficient.")
+ (display "Map is not call/cc safe, but probably tail recursive and efficient."))
+ (newline))
+
diff --git a/test/simple.scm b/test/simple.scm
new file mode 100644
index 0000000..41babb5
--- /dev/null
+++ b/test/simple.scm
@@ -0,0 +1,48 @@
+
+; This comment should be skipped
+
+(begin
+ (display #t)
+ (newline))
+
+; anonymous lambda
+(define add1 (lambda (x) (+ 1 x)))
+(display (eq? (add1 4) 5))
+(newline)
+
+; named/defined lambda, spread over multiple lines
+(define
+ add1
+ (lambda
+ (x)
+ (+ 1 x)))
+(display (eq? (add1 4) 5))
+(newline)
+
+; this checks that cond is a special form
+(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))
+(display (eq? 479001600 (fact 12)))
+(newline)
+
+(display (eq? (add1 (car (cons 4 (cons 0 ())))) 5))
+(newline)
+
+(car (car (quote (lambda (x) (+ 1 x)))))
+
+(define dummy-var 5)
+(set! dummy-var 6)
+(display (eq? dummy-var 6))
+(newline)
+
+(display (string? "short string with spaces"))
+(newline)
+
+(display (pair? (cons 1 (cons 2 ()))))
+(newline)
+
+(display (symbol? 'lambda))
+(newline)
+
+(display (procedure? add1))
+(newline)
+
diff --git a/test/unicode.scm b/test/unicode.scm
new file mode 100644
index 0000000..9ea74eb
--- /dev/null
+++ b/test/unicode.scm
@@ -0,0 +1,3 @@
+
+(display (eq? "αβγ" "αβγ"))
+(newline)
diff --git a/test/unicode_lambda.scm b/test/unicode_lambda.scm
new file mode 100644
index 0000000..6c8b17f
--- /dev/null
+++ b/test/unicode_lambda.scm
@@ -0,0 +1,6 @@
+
+(display (eq? (quote λ) 'λ))
+(newline)
+
+(display (eq? 5 ((λ (x) (+ x 4)) 1)))
+(newline)