Index: packages/petitparser/test/lisp_test.dart |
diff --git a/packages/petitparser/test/lisp_test.dart b/packages/petitparser/test/lisp_test.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2c88884ce90638b51a148135ecec3cbd13697e22 |
--- /dev/null |
+++ b/packages/petitparser/test/lisp_test.dart |
@@ -0,0 +1,438 @@ |
+library petitparser.test.lisp_test; |
+ |
+import 'package:test/test.dart'; |
+ |
+import 'package:petitparser/lisp.dart'; |
+ |
+void main() { |
+ var root = new Environment(); |
+ var native = Natives.import(root); |
+ var standard = Standard.import(native.create()); |
+ |
+ dynamic exec(String value, [Environment env]) { |
+ return evalString(lispParser, env != null ? env : standard.create(), value); |
+ } |
+ |
+ group('Cell', () { |
+ test('Name', () { |
+ var cell1 = new Name('foo'); |
+ var cell2 = new Name('foo'); |
+ var cell3 = new Name('bar'); |
+ expect(cell1, cell2); |
+ expect(cell1, same(cell2)); |
+ expect(cell1, isNot(cell3)); |
+ expect(cell1, isNot(same(cell3))); |
+ }); |
+ test('Cons', () { |
+ var cell = new Cons(1, 2); |
+ expect(cell.head, 1); |
+ expect(cell.tail, 2); |
+ cell.head = 3; |
+ expect(cell.head, 3); |
+ expect(cell.tail, 2); |
+ cell.tail = new Cons(4, 5); |
+ expect(cell.head, 3); |
+ expect(cell.tail.head, 4); |
+ expect(cell.tail.tail, 5); |
+ expect(cell == cell, isTrue); |
+ expect(cell.hashCode, isNonZero); |
+ expect(cell.toString(), '(3 4 . 5)'); |
+ }); |
+ }); |
+ group('Environment', () { |
+ var env = standard.create(); |
+ test('Standard', () { |
+ expect(env.owner, isNotNull); |
+ expect(env.keys, isEmpty); |
+ expect(env.owner.keys, isNot(isEmpty)); |
+ }); |
+ test('Create', () { |
+ var sub = env.create(); |
+ expect(sub.owner, same(env)); |
+ expect(sub.keys, isEmpty); |
+ }); |
+ }); |
+ group('Parser', () { |
+ var definition = new LispParserDefinition(); |
+ var atom = definition.build(start: definition.atom); |
+ test('Name', () { |
+ var cell = atom.parse('foo').value; |
+ expect(cell, new isInstanceOf<Name>()); |
+ expect(cell.toString(), 'foo'); |
+ }); |
+ test('Name for operator', () { |
+ var cell = atom.parse('+').value; |
+ expect(cell, new isInstanceOf<Name>()); |
+ expect(cell.toString(), '+'); |
+ }); |
+ test('Name for special', () { |
+ var cell = atom.parse('set!').value; |
+ expect(cell, new isInstanceOf<Name>()); |
+ expect(cell.toString(), 'set!'); |
+ }); |
+ test('String', () { |
+ var cell = atom.parse('"foo"').value; |
+ expect(cell, new isInstanceOf<String>()); |
+ expect(cell, 'foo'); |
+ }); |
+ test('String with escape', () { |
+ var cell = atom.parse('"\\""').value; |
+ expect(cell, '"'); |
+ }); |
+ test('Number integer', () { |
+ var cell = atom.parse('123').value; |
+ expect(cell, 123); |
+ }); |
+ test('Number negative integer', () { |
+ var cell = atom.parse('-123').value; |
+ expect(cell, -123); |
+ }); |
+ test('Number positive integer', () { |
+ var cell = atom.parse('+123').value; |
+ expect(cell, 123); |
+ }); |
+ test('Number floating', () { |
+ var cell = atom.parse('123.45').value; |
+ expect(cell, 123.45); |
+ }); |
+ test('Number floating exponential', () { |
+ var cell = atom.parse('1.23e4').value; |
+ expect(cell, 1.23e4); |
+ }); |
+ test('List empty', () { |
+ var cell = atom.parse('()').value; |
+ expect(cell, isNull); |
+ }); |
+ test('List empty []', () { |
+ var cell = atom.parse('[ ]').value; |
+ expect(cell, isNull); |
+ }); |
+ test('List empty {}', () { |
+ var cell = atom.parse('{ }').value; |
+ expect(cell, isNull); |
+ }); |
+ test('List one element', () { |
+ var cell = atom.parse('(1)').value; |
+ expect(cell, new isInstanceOf<Cons>()); |
+ expect(cell.head, 1); |
+ expect(cell.tail, isNull); |
+ }); |
+ test('List two elements', () { |
+ var cell = atom.parse('(1 2)').value; |
+ expect(cell, new isInstanceOf<Cons>()); |
+ expect(cell.head, 1); |
+ expect(cell.tail, new isInstanceOf<Cons>()); |
+ expect(cell.tail.head, 2); |
+ expect(cell.tail.tail, isNull); |
+ }); |
+ test('List three elements', () { |
+ var cell = atom.parse('(+ 1 2)').value; |
+ expect(cell, new isInstanceOf<Cons>()); |
+ expect(cell.head, new isInstanceOf<Name>()); |
+ expect(cell.head.toString(), '+'); |
+ expect(cell.tail, new isInstanceOf<Cons>()); |
+ expect(cell.tail.head, 1); |
+ expect(cell.tail.tail, new isInstanceOf<Cons>()); |
+ expect(cell.tail.tail.head, 2); |
+ expect(cell.tail.tail.tail, isNull); |
+ }); |
+ }); |
+ group('Natives', () { |
+ test('Define', () { |
+ expect(exec('(define a 1)'), 1); |
+ expect(exec('(define a 2) a'), 2); |
+ expect(exec('((define (a) 3))'), 3); |
+ expect(exec('(define (a) 4) (a)'), 4); |
+ expect(exec('((define (a x) x) 5)'), 5); |
+ expect(exec('(define (a x) x) (a 6)'), 6); |
+ }); |
+ test('Lambda', () { |
+ expect(exec('((lambda () 1) 2)'), 1); |
+ expect(exec('((lambda (x) x) 2)'), 2); |
+ expect(exec('((lambda (x) (+ x x)) 2)'), 4); |
+ expect(exec('((lambda (x y) (+ x y)) 2 4)'), 6); |
+ expect(exec('((lambda (x y z) (+ x y z)) 2 4 6)'), 12); |
+ }); |
+ test('Quote', () { |
+ expect(exec('(quote)'), null); |
+ expect(exec('(quote 1)'), new Cons(1, null)); |
+ expect(exec('(quote + 1)'), new Cons(new Name('+'), new Cons(1, null))); |
+ }); |
+ test('Quote (syntax)', () { |
+ expect(exec('\'()'), null); |
+ expect(exec('\'(1)'), new Cons(1, null)); |
+ expect(exec('\'(+ 1)'), new Cons(new Name('+'), new Cons(1, null))); |
+ }); |
+ test('Eval', () { |
+ expect(exec('(eval (quote + 1 2))'), 3); |
+ }); |
+ test('Apply', () { |
+ expect(exec('(apply + 1 2 3)'), 6); |
+ expect(exec('(apply + 1 2 3 (+ 2 2))'), 10); |
+ }); |
+ test('Let', () { |
+ expect(exec('(let ((a 1)) a)'), 1); |
+ expect(exec('(let ((a 1) (b 2)) a)'), 1); |
+ expect(exec('(let ((a 1) (b 2)) b)'), 2); |
+ expect(exec('(let ((a 1) (b 2)) (+ a b))'), 3); |
+ expect(exec('(let ((a 1) (b 2)) (+ a b) 4)'), 4); |
+ }); |
+ test('Set!', () { |
+ var env = standard.create(); |
+ env.define(new Name('a'), null); |
+ expect(exec('(set! a 1)', env), 1); |
+ expect(exec('(set! a (+ 1 2))', env), 3); |
+ expect(exec('(set! a (+ 1 2)) (+ a 1)', env), 4); |
+ }); |
+ test('Set! (undefined)', () { |
+ expect(() => exec('(set! a 1)'), throws); |
+ expect(() => standard[new Name('a')], throws); |
+ }); |
+ test('If', () { |
+ expect(exec('(if true)'), isNull); |
+ expect(exec('(if false)'), isNull); |
+ expect(exec('(if true 1)'), 1); |
+ expect(exec('(if false 1)'), isNull); |
+ expect(exec('(if true 1 2)'), 1); |
+ expect(exec('(if false 1 2)'), 2); |
+ }); |
+ test('If (lazyness)', () { |
+ expect(exec('(if (= 1 1) 3 4)'), 3); |
+ expect(exec('(if (= 1 2) 3 4)'), 4); |
+ }); |
+ test('While', () { |
+ var env = standard.create(); |
+ env.define(new Name('a'), 0); |
+ exec('(while (< a 3) (set! a (+ a 1)))', env); |
+ expect(env[new Name('a')], 3); |
+ }); |
+ test('True', () { |
+ expect(exec('true'), isTrue); |
+ }); |
+ test('False', () { |
+ expect(exec('false'), isFalse); |
+ }); |
+ test('And', () { |
+ expect(exec('(and)'), isTrue); |
+ expect(exec('(and true)'), isTrue); |
+ expect(exec('(and false)'), isFalse); |
+ expect(exec('(and true true)'), isTrue); |
+ expect(exec('(and true false)'), isFalse); |
+ expect(exec('(and false true)'), isFalse); |
+ expect(exec('(and false false)'), isFalse); |
+ expect(exec('(and true true true)'), isTrue); |
+ expect(exec('(and true true false)'), isFalse); |
+ expect(exec('(and true false true)'), isFalse); |
+ expect(exec('(and true false false)'), isFalse); |
+ expect(exec('(and false true true)'), isFalse); |
+ expect(exec('(and false true false)'), isFalse); |
+ expect(exec('(and false false true)'), isFalse); |
+ expect(exec('(and false false false)'), isFalse); |
+ }); |
+ test('And (lazyness)', () { |
+ var env = standard.create(); |
+ env.define(new Name('a'), null); |
+ exec('(and false (set! a true))', env); |
+ expect(env[new Name('a')], isNull); |
+ exec('(and true (set! a true))', env); |
+ expect(env[new Name('a')], isTrue); |
+ }); |
+ test('Or', () { |
+ expect(exec('(or)'), isFalse); |
+ expect(exec('(or true)'), isTrue); |
+ expect(exec('(or false)'), isFalse); |
+ expect(exec('(or true true)'), isTrue); |
+ expect(exec('(or true false)'), isTrue); |
+ expect(exec('(or false true)'), isTrue); |
+ expect(exec('(or false false)'), isFalse); |
+ expect(exec('(or true true true)'), isTrue); |
+ expect(exec('(or true true false)'), isTrue); |
+ expect(exec('(or true false true)'), isTrue); |
+ expect(exec('(or true false false)'), isTrue); |
+ expect(exec('(or false true true)'), isTrue); |
+ expect(exec('(or false true false)'), isTrue); |
+ expect(exec('(or false false true)'), isTrue); |
+ expect(exec('(or false false false)'), isFalse); |
+ }); |
+ test('Or (lazyness)', () { |
+ var env = standard.create(); |
+ env.define(new Name('a'), null); |
+ exec('(or true (set! a true))', env); |
+ expect(env[new Name('a')], isNull); |
+ exec('(or false (set! a true))', env); |
+ expect(env[new Name('a')], isTrue); |
+ }); |
+ test('Not', () { |
+ expect(exec('(not true)'), isFalse); |
+ expect(exec('(not false)'), isTrue); |
+ }); |
+ test('Add', () { |
+ expect(exec('(+ 1)'), 1); |
+ expect(exec('(+ 1 2)'), 3); |
+ expect(exec('(+ 1 2 3)'), 6); |
+ expect(exec('(+ 1 2 3 4)'), 10); |
+ }); |
+ test('Sub', () { |
+ expect(exec('(- 1)'), -1); |
+ expect(exec('(- 1 2)'), -1); |
+ expect(exec('(- 1 2 3)'), -4); |
+ expect(exec('(- 1 2 3 4)'), -8); |
+ }); |
+ test('Mul', () { |
+ expect(exec('(* 2)'), 2); |
+ expect(exec('(* 2 3)'), 6); |
+ expect(exec('(* 2 3 4)'), 24); |
+ }); |
+ test('Div', () { |
+ expect(exec('(/ 24)'), 24); |
+ expect(exec('(/ 24 3)'), 8); |
+ expect(exec('(/ 24 3 2)'), 4); |
+ }); |
+ test('Mod', () { |
+ expect(exec('(% 24)'), 24); |
+ expect(exec('(% 24 5)'), 4); |
+ expect(exec('(% 24 5 3)'), 1); |
+ }); |
+ test('Less', () { |
+ expect(exec('(< 1 2)'), isTrue); |
+ expect(exec('(< 1 1)'), isFalse); |
+ expect(exec('(< 2 1)'), isFalse); |
+ }); |
+ test('Less equal', () { |
+ expect(exec('(<= 1 2)'), isTrue); |
+ expect(exec('(<= 1 1)'), isTrue); |
+ expect(exec('(<= 2 1)'), isFalse); |
+ }); |
+ test('Equal', () { |
+ expect(exec('(= 1 1)'), isTrue); |
+ expect(exec('(= 1 2)'), isFalse); |
+ expect(exec('(= 2 1)'), isFalse); |
+ }); |
+ test('Not equal', () { |
+ expect(exec('(!= 1 1)'), isFalse); |
+ expect(exec('(!= 1 2)'), isTrue); |
+ expect(exec('(!= 2 1)'), isTrue); |
+ }); |
+ test('Larger', () { |
+ expect(exec('(> 1 1)'), isFalse); |
+ expect(exec('(> 1 2)'), isFalse); |
+ expect(exec('(> 2 1)'), isTrue); |
+ }); |
+ test('Larger equal', () { |
+ expect(exec('(>= 1 1)'), isTrue); |
+ expect(exec('(>= 1 2)'), isFalse); |
+ expect(exec('(>= 2 1)'), isTrue); |
+ }); |
+ test('Cons', () { |
+ expect(exec('(cons 1 2)'), new Cons(1, 2)); |
+ }); |
+ test('Car', () { |
+ expect(exec('(car null)'), isNull); |
+ expect(exec('(car (cons 1 2))'), 1); |
+ }); |
+ test('Car!', () { |
+ expect(exec('(car! null 3)'), isNull); |
+ expect(exec('(car! (cons 1 2) 3)'), new Cons(3, 2)); |
+ }); |
+ test('Cdr', () { |
+ expect(exec('(cdr null)'), isNull); |
+ expect(exec('(cdr (cons 1 2))'), 2); |
+ }); |
+ test('Cdr!', () { |
+ expect(exec('(cdr! null 3)'), isNull); |
+ expect(exec('(cdr! (cons 1 2) 3)'), new Cons(1, 3)); |
+ }); |
+ }); |
+ group('Library', () { |
+ test('Null', () { |
+ expect(exec('null'), isNull); |
+ }); |
+ test('Null? (true)', () { |
+ expect(exec('(null? \'())'), isTrue); |
+ expect(exec('(null? null)'), isTrue); |
+ }); |
+ test('Null? (false)', () { |
+ expect(exec('(null? 1)'), isFalse); |
+ expect(exec('(null? "a")'), isFalse); |
+ expect(exec('(null? (quote a))'), isFalse); |
+ expect(exec('(null? true)'), isFalse); |
+ expect(exec('(null? false)'), isFalse); |
+ }); |
+ test('Length', () { |
+ expect(exec('(length \'())'), 0); |
+ expect(exec('(length \'(1))'), 1); |
+ expect(exec('(length \'(1 1))'), 2); |
+ expect(exec('(length \'(1 1 1))'), 3); |
+ expect(exec('(length \'(1 1 1 1))'), 4); |
+ expect(exec('(length \'(1 1 1 1 1))'), 5); |
+ }); |
+ test('Append', () { |
+ expect(exec('(append \'() \'())'), isNull); |
+ expect(exec('(append \'(1) \'())'), exec('\'(1)')); |
+ expect(exec('(append \'() \'(1))'), exec('\'(1)')); |
+ expect(exec('(append \'(1) \'(2))'), exec('\'(1 2)')); |
+ expect(exec('(append \'(1 2) \'(3))'), exec('\'(1 2 3)')); |
+ expect(exec('(append \'(1) \'(2 3))'), exec('\'(1 2 3)')); |
+ }); |
+ test('List Head', () { |
+ expect(exec('(list-head \'(5 6 7) 0)'), 5); |
+ expect(exec('(list-head \'(5 6 7) 1)'), 6); |
+ expect(exec('(list-head \'(5 6 7) 2)'), 7); |
+ expect(exec('(list-head \'(5 6 7) 3)'), isNull); |
+ }); |
+ test('List Tail', () { |
+ expect(exec('(list-tail \'(5 6 7) 0)'), exec('\'(6 7)')); |
+ expect(exec('(list-tail \'(5 6 7) 1)'), exec('\'(7)')); |
+ expect(exec('(list-tail \'(5 6 7) 2)'), isNull); |
+ }); |
+ test('Map', () { |
+ expect(exec('(map \'() (lambda (x) (* 2 x)))'), isNull); |
+ expect(exec('(map \'(2) (lambda (x) (* 2 x)))'), exec('\'(4)')); |
+ expect(exec('(map \'(2 3) (lambda (x) (* 2 x)))'), exec('\'(4 6)')); |
+ expect(exec('(map \'(2 3 4) (lambda (x) (* 2 x)))'), exec('\'(4 6 8)')); |
+ }); |
+ test('Inject', () { |
+ expect(exec('(inject \'() 5 (lambda (s e) (+ s e 1)))'), 5); |
+ expect(exec('(inject \'(2) 5 (lambda (s e) (+ s e 1)))'), 8); |
+ expect(exec('(inject \'(2 3) 5 (lambda (s e) (+ s e 1)))'), 12); |
+ }); |
+ }); |
+ group('Examples', () { |
+ test('Fibonacci', () { |
+ var env = standard.create(); |
+ exec('(define (fib n)' |
+ ' (if (<= n 1)' |
+ ' 1' |
+ ' (+ (fib (- n 1)) (fib (- n 2)))))', env); |
+ expect(exec('(fib 0)', env), 1); |
+ expect(exec('(fib 1)', env), 1); |
+ expect(exec('(fib 2)', env), 2); |
+ expect(exec('(fib 3)', env), 3); |
+ expect(exec('(fib 4)', env), 5); |
+ expect(exec('(fib 5)', env), 8); |
+ }); |
+ test('Closure', () { |
+ var env = standard.create(); |
+ exec('(define (mul n)' |
+ ' (lambda (x) (* n x)))', env); |
+ expect(exec('((mul 2) 3)', env), 6); |
+ expect(exec('((mul 3) 4)', env), 12); |
+ expect(exec('((mul 4) 5)', env), 20); |
+ }); |
+ test('Object', () { |
+ var env = standard.create(); |
+ exec('(define (counter start)' |
+ ' (let ((count start))' |
+ ' (lambda ()' |
+ ' (set! count (+ count 1)))))', env); |
+ exec('(define a (counter 10))', env); |
+ exec('(define b (counter 20))', env); |
+ expect(exec('(a)', env), 11); |
+ expect(exec('(b)', env), 21); |
+ expect(exec('(a)', env), 12); |
+ expect(exec('(b)', env), 22); |
+ expect(exec('(a)', env), 13); |
+ expect(exec('(b)', env), 23); |
+ }); |
+ }); |
+} |