OLD | NEW |
(Empty) | |
| 1 library petitparser.test.lisp_test; |
| 2 |
| 3 import 'package:test/test.dart'; |
| 4 |
| 5 import 'package:petitparser/lisp.dart'; |
| 6 |
| 7 void main() { |
| 8 var root = new Environment(); |
| 9 var native = Natives.import(root); |
| 10 var standard = Standard.import(native.create()); |
| 11 |
| 12 dynamic exec(String value, [Environment env]) { |
| 13 return evalString(lispParser, env != null ? env : standard.create(), value); |
| 14 } |
| 15 |
| 16 group('Cell', () { |
| 17 test('Name', () { |
| 18 var cell1 = new Name('foo'); |
| 19 var cell2 = new Name('foo'); |
| 20 var cell3 = new Name('bar'); |
| 21 expect(cell1, cell2); |
| 22 expect(cell1, same(cell2)); |
| 23 expect(cell1, isNot(cell3)); |
| 24 expect(cell1, isNot(same(cell3))); |
| 25 }); |
| 26 test('Cons', () { |
| 27 var cell = new Cons(1, 2); |
| 28 expect(cell.head, 1); |
| 29 expect(cell.tail, 2); |
| 30 cell.head = 3; |
| 31 expect(cell.head, 3); |
| 32 expect(cell.tail, 2); |
| 33 cell.tail = new Cons(4, 5); |
| 34 expect(cell.head, 3); |
| 35 expect(cell.tail.head, 4); |
| 36 expect(cell.tail.tail, 5); |
| 37 expect(cell == cell, isTrue); |
| 38 expect(cell.hashCode, isNonZero); |
| 39 expect(cell.toString(), '(3 4 . 5)'); |
| 40 }); |
| 41 }); |
| 42 group('Environment', () { |
| 43 var env = standard.create(); |
| 44 test('Standard', () { |
| 45 expect(env.owner, isNotNull); |
| 46 expect(env.keys, isEmpty); |
| 47 expect(env.owner.keys, isNot(isEmpty)); |
| 48 }); |
| 49 test('Create', () { |
| 50 var sub = env.create(); |
| 51 expect(sub.owner, same(env)); |
| 52 expect(sub.keys, isEmpty); |
| 53 }); |
| 54 }); |
| 55 group('Parser', () { |
| 56 var definition = new LispParserDefinition(); |
| 57 var atom = definition.build(start: definition.atom); |
| 58 test('Name', () { |
| 59 var cell = atom.parse('foo').value; |
| 60 expect(cell, new isInstanceOf<Name>()); |
| 61 expect(cell.toString(), 'foo'); |
| 62 }); |
| 63 test('Name for operator', () { |
| 64 var cell = atom.parse('+').value; |
| 65 expect(cell, new isInstanceOf<Name>()); |
| 66 expect(cell.toString(), '+'); |
| 67 }); |
| 68 test('Name for special', () { |
| 69 var cell = atom.parse('set!').value; |
| 70 expect(cell, new isInstanceOf<Name>()); |
| 71 expect(cell.toString(), 'set!'); |
| 72 }); |
| 73 test('String', () { |
| 74 var cell = atom.parse('"foo"').value; |
| 75 expect(cell, new isInstanceOf<String>()); |
| 76 expect(cell, 'foo'); |
| 77 }); |
| 78 test('String with escape', () { |
| 79 var cell = atom.parse('"\\""').value; |
| 80 expect(cell, '"'); |
| 81 }); |
| 82 test('Number integer', () { |
| 83 var cell = atom.parse('123').value; |
| 84 expect(cell, 123); |
| 85 }); |
| 86 test('Number negative integer', () { |
| 87 var cell = atom.parse('-123').value; |
| 88 expect(cell, -123); |
| 89 }); |
| 90 test('Number positive integer', () { |
| 91 var cell = atom.parse('+123').value; |
| 92 expect(cell, 123); |
| 93 }); |
| 94 test('Number floating', () { |
| 95 var cell = atom.parse('123.45').value; |
| 96 expect(cell, 123.45); |
| 97 }); |
| 98 test('Number floating exponential', () { |
| 99 var cell = atom.parse('1.23e4').value; |
| 100 expect(cell, 1.23e4); |
| 101 }); |
| 102 test('List empty', () { |
| 103 var cell = atom.parse('()').value; |
| 104 expect(cell, isNull); |
| 105 }); |
| 106 test('List empty []', () { |
| 107 var cell = atom.parse('[ ]').value; |
| 108 expect(cell, isNull); |
| 109 }); |
| 110 test('List empty {}', () { |
| 111 var cell = atom.parse('{ }').value; |
| 112 expect(cell, isNull); |
| 113 }); |
| 114 test('List one element', () { |
| 115 var cell = atom.parse('(1)').value; |
| 116 expect(cell, new isInstanceOf<Cons>()); |
| 117 expect(cell.head, 1); |
| 118 expect(cell.tail, isNull); |
| 119 }); |
| 120 test('List two elements', () { |
| 121 var cell = atom.parse('(1 2)').value; |
| 122 expect(cell, new isInstanceOf<Cons>()); |
| 123 expect(cell.head, 1); |
| 124 expect(cell.tail, new isInstanceOf<Cons>()); |
| 125 expect(cell.tail.head, 2); |
| 126 expect(cell.tail.tail, isNull); |
| 127 }); |
| 128 test('List three elements', () { |
| 129 var cell = atom.parse('(+ 1 2)').value; |
| 130 expect(cell, new isInstanceOf<Cons>()); |
| 131 expect(cell.head, new isInstanceOf<Name>()); |
| 132 expect(cell.head.toString(), '+'); |
| 133 expect(cell.tail, new isInstanceOf<Cons>()); |
| 134 expect(cell.tail.head, 1); |
| 135 expect(cell.tail.tail, new isInstanceOf<Cons>()); |
| 136 expect(cell.tail.tail.head, 2); |
| 137 expect(cell.tail.tail.tail, isNull); |
| 138 }); |
| 139 }); |
| 140 group('Natives', () { |
| 141 test('Define', () { |
| 142 expect(exec('(define a 1)'), 1); |
| 143 expect(exec('(define a 2) a'), 2); |
| 144 expect(exec('((define (a) 3))'), 3); |
| 145 expect(exec('(define (a) 4) (a)'), 4); |
| 146 expect(exec('((define (a x) x) 5)'), 5); |
| 147 expect(exec('(define (a x) x) (a 6)'), 6); |
| 148 }); |
| 149 test('Lambda', () { |
| 150 expect(exec('((lambda () 1) 2)'), 1); |
| 151 expect(exec('((lambda (x) x) 2)'), 2); |
| 152 expect(exec('((lambda (x) (+ x x)) 2)'), 4); |
| 153 expect(exec('((lambda (x y) (+ x y)) 2 4)'), 6); |
| 154 expect(exec('((lambda (x y z) (+ x y z)) 2 4 6)'), 12); |
| 155 }); |
| 156 test('Quote', () { |
| 157 expect(exec('(quote)'), null); |
| 158 expect(exec('(quote 1)'), new Cons(1, null)); |
| 159 expect(exec('(quote + 1)'), new Cons(new Name('+'), new Cons(1, null))); |
| 160 }); |
| 161 test('Quote (syntax)', () { |
| 162 expect(exec('\'()'), null); |
| 163 expect(exec('\'(1)'), new Cons(1, null)); |
| 164 expect(exec('\'(+ 1)'), new Cons(new Name('+'), new Cons(1, null))); |
| 165 }); |
| 166 test('Eval', () { |
| 167 expect(exec('(eval (quote + 1 2))'), 3); |
| 168 }); |
| 169 test('Apply', () { |
| 170 expect(exec('(apply + 1 2 3)'), 6); |
| 171 expect(exec('(apply + 1 2 3 (+ 2 2))'), 10); |
| 172 }); |
| 173 test('Let', () { |
| 174 expect(exec('(let ((a 1)) a)'), 1); |
| 175 expect(exec('(let ((a 1) (b 2)) a)'), 1); |
| 176 expect(exec('(let ((a 1) (b 2)) b)'), 2); |
| 177 expect(exec('(let ((a 1) (b 2)) (+ a b))'), 3); |
| 178 expect(exec('(let ((a 1) (b 2)) (+ a b) 4)'), 4); |
| 179 }); |
| 180 test('Set!', () { |
| 181 var env = standard.create(); |
| 182 env.define(new Name('a'), null); |
| 183 expect(exec('(set! a 1)', env), 1); |
| 184 expect(exec('(set! a (+ 1 2))', env), 3); |
| 185 expect(exec('(set! a (+ 1 2)) (+ a 1)', env), 4); |
| 186 }); |
| 187 test('Set! (undefined)', () { |
| 188 expect(() => exec('(set! a 1)'), throws); |
| 189 expect(() => standard[new Name('a')], throws); |
| 190 }); |
| 191 test('If', () { |
| 192 expect(exec('(if true)'), isNull); |
| 193 expect(exec('(if false)'), isNull); |
| 194 expect(exec('(if true 1)'), 1); |
| 195 expect(exec('(if false 1)'), isNull); |
| 196 expect(exec('(if true 1 2)'), 1); |
| 197 expect(exec('(if false 1 2)'), 2); |
| 198 }); |
| 199 test('If (lazyness)', () { |
| 200 expect(exec('(if (= 1 1) 3 4)'), 3); |
| 201 expect(exec('(if (= 1 2) 3 4)'), 4); |
| 202 }); |
| 203 test('While', () { |
| 204 var env = standard.create(); |
| 205 env.define(new Name('a'), 0); |
| 206 exec('(while (< a 3) (set! a (+ a 1)))', env); |
| 207 expect(env[new Name('a')], 3); |
| 208 }); |
| 209 test('True', () { |
| 210 expect(exec('true'), isTrue); |
| 211 }); |
| 212 test('False', () { |
| 213 expect(exec('false'), isFalse); |
| 214 }); |
| 215 test('And', () { |
| 216 expect(exec('(and)'), isTrue); |
| 217 expect(exec('(and true)'), isTrue); |
| 218 expect(exec('(and false)'), isFalse); |
| 219 expect(exec('(and true true)'), isTrue); |
| 220 expect(exec('(and true false)'), isFalse); |
| 221 expect(exec('(and false true)'), isFalse); |
| 222 expect(exec('(and false false)'), isFalse); |
| 223 expect(exec('(and true true true)'), isTrue); |
| 224 expect(exec('(and true true false)'), isFalse); |
| 225 expect(exec('(and true false true)'), isFalse); |
| 226 expect(exec('(and true false false)'), isFalse); |
| 227 expect(exec('(and false true true)'), isFalse); |
| 228 expect(exec('(and false true false)'), isFalse); |
| 229 expect(exec('(and false false true)'), isFalse); |
| 230 expect(exec('(and false false false)'), isFalse); |
| 231 }); |
| 232 test('And (lazyness)', () { |
| 233 var env = standard.create(); |
| 234 env.define(new Name('a'), null); |
| 235 exec('(and false (set! a true))', env); |
| 236 expect(env[new Name('a')], isNull); |
| 237 exec('(and true (set! a true))', env); |
| 238 expect(env[new Name('a')], isTrue); |
| 239 }); |
| 240 test('Or', () { |
| 241 expect(exec('(or)'), isFalse); |
| 242 expect(exec('(or true)'), isTrue); |
| 243 expect(exec('(or false)'), isFalse); |
| 244 expect(exec('(or true true)'), isTrue); |
| 245 expect(exec('(or true false)'), isTrue); |
| 246 expect(exec('(or false true)'), isTrue); |
| 247 expect(exec('(or false false)'), isFalse); |
| 248 expect(exec('(or true true true)'), isTrue); |
| 249 expect(exec('(or true true false)'), isTrue); |
| 250 expect(exec('(or true false true)'), isTrue); |
| 251 expect(exec('(or true false false)'), isTrue); |
| 252 expect(exec('(or false true true)'), isTrue); |
| 253 expect(exec('(or false true false)'), isTrue); |
| 254 expect(exec('(or false false true)'), isTrue); |
| 255 expect(exec('(or false false false)'), isFalse); |
| 256 }); |
| 257 test('Or (lazyness)', () { |
| 258 var env = standard.create(); |
| 259 env.define(new Name('a'), null); |
| 260 exec('(or true (set! a true))', env); |
| 261 expect(env[new Name('a')], isNull); |
| 262 exec('(or false (set! a true))', env); |
| 263 expect(env[new Name('a')], isTrue); |
| 264 }); |
| 265 test('Not', () { |
| 266 expect(exec('(not true)'), isFalse); |
| 267 expect(exec('(not false)'), isTrue); |
| 268 }); |
| 269 test('Add', () { |
| 270 expect(exec('(+ 1)'), 1); |
| 271 expect(exec('(+ 1 2)'), 3); |
| 272 expect(exec('(+ 1 2 3)'), 6); |
| 273 expect(exec('(+ 1 2 3 4)'), 10); |
| 274 }); |
| 275 test('Sub', () { |
| 276 expect(exec('(- 1)'), -1); |
| 277 expect(exec('(- 1 2)'), -1); |
| 278 expect(exec('(- 1 2 3)'), -4); |
| 279 expect(exec('(- 1 2 3 4)'), -8); |
| 280 }); |
| 281 test('Mul', () { |
| 282 expect(exec('(* 2)'), 2); |
| 283 expect(exec('(* 2 3)'), 6); |
| 284 expect(exec('(* 2 3 4)'), 24); |
| 285 }); |
| 286 test('Div', () { |
| 287 expect(exec('(/ 24)'), 24); |
| 288 expect(exec('(/ 24 3)'), 8); |
| 289 expect(exec('(/ 24 3 2)'), 4); |
| 290 }); |
| 291 test('Mod', () { |
| 292 expect(exec('(% 24)'), 24); |
| 293 expect(exec('(% 24 5)'), 4); |
| 294 expect(exec('(% 24 5 3)'), 1); |
| 295 }); |
| 296 test('Less', () { |
| 297 expect(exec('(< 1 2)'), isTrue); |
| 298 expect(exec('(< 1 1)'), isFalse); |
| 299 expect(exec('(< 2 1)'), isFalse); |
| 300 }); |
| 301 test('Less equal', () { |
| 302 expect(exec('(<= 1 2)'), isTrue); |
| 303 expect(exec('(<= 1 1)'), isTrue); |
| 304 expect(exec('(<= 2 1)'), isFalse); |
| 305 }); |
| 306 test('Equal', () { |
| 307 expect(exec('(= 1 1)'), isTrue); |
| 308 expect(exec('(= 1 2)'), isFalse); |
| 309 expect(exec('(= 2 1)'), isFalse); |
| 310 }); |
| 311 test('Not equal', () { |
| 312 expect(exec('(!= 1 1)'), isFalse); |
| 313 expect(exec('(!= 1 2)'), isTrue); |
| 314 expect(exec('(!= 2 1)'), isTrue); |
| 315 }); |
| 316 test('Larger', () { |
| 317 expect(exec('(> 1 1)'), isFalse); |
| 318 expect(exec('(> 1 2)'), isFalse); |
| 319 expect(exec('(> 2 1)'), isTrue); |
| 320 }); |
| 321 test('Larger equal', () { |
| 322 expect(exec('(>= 1 1)'), isTrue); |
| 323 expect(exec('(>= 1 2)'), isFalse); |
| 324 expect(exec('(>= 2 1)'), isTrue); |
| 325 }); |
| 326 test('Cons', () { |
| 327 expect(exec('(cons 1 2)'), new Cons(1, 2)); |
| 328 }); |
| 329 test('Car', () { |
| 330 expect(exec('(car null)'), isNull); |
| 331 expect(exec('(car (cons 1 2))'), 1); |
| 332 }); |
| 333 test('Car!', () { |
| 334 expect(exec('(car! null 3)'), isNull); |
| 335 expect(exec('(car! (cons 1 2) 3)'), new Cons(3, 2)); |
| 336 }); |
| 337 test('Cdr', () { |
| 338 expect(exec('(cdr null)'), isNull); |
| 339 expect(exec('(cdr (cons 1 2))'), 2); |
| 340 }); |
| 341 test('Cdr!', () { |
| 342 expect(exec('(cdr! null 3)'), isNull); |
| 343 expect(exec('(cdr! (cons 1 2) 3)'), new Cons(1, 3)); |
| 344 }); |
| 345 }); |
| 346 group('Library', () { |
| 347 test('Null', () { |
| 348 expect(exec('null'), isNull); |
| 349 }); |
| 350 test('Null? (true)', () { |
| 351 expect(exec('(null? \'())'), isTrue); |
| 352 expect(exec('(null? null)'), isTrue); |
| 353 }); |
| 354 test('Null? (false)', () { |
| 355 expect(exec('(null? 1)'), isFalse); |
| 356 expect(exec('(null? "a")'), isFalse); |
| 357 expect(exec('(null? (quote a))'), isFalse); |
| 358 expect(exec('(null? true)'), isFalse); |
| 359 expect(exec('(null? false)'), isFalse); |
| 360 }); |
| 361 test('Length', () { |
| 362 expect(exec('(length \'())'), 0); |
| 363 expect(exec('(length \'(1))'), 1); |
| 364 expect(exec('(length \'(1 1))'), 2); |
| 365 expect(exec('(length \'(1 1 1))'), 3); |
| 366 expect(exec('(length \'(1 1 1 1))'), 4); |
| 367 expect(exec('(length \'(1 1 1 1 1))'), 5); |
| 368 }); |
| 369 test('Append', () { |
| 370 expect(exec('(append \'() \'())'), isNull); |
| 371 expect(exec('(append \'(1) \'())'), exec('\'(1)')); |
| 372 expect(exec('(append \'() \'(1))'), exec('\'(1)')); |
| 373 expect(exec('(append \'(1) \'(2))'), exec('\'(1 2)')); |
| 374 expect(exec('(append \'(1 2) \'(3))'), exec('\'(1 2 3)')); |
| 375 expect(exec('(append \'(1) \'(2 3))'), exec('\'(1 2 3)')); |
| 376 }); |
| 377 test('List Head', () { |
| 378 expect(exec('(list-head \'(5 6 7) 0)'), 5); |
| 379 expect(exec('(list-head \'(5 6 7) 1)'), 6); |
| 380 expect(exec('(list-head \'(5 6 7) 2)'), 7); |
| 381 expect(exec('(list-head \'(5 6 7) 3)'), isNull); |
| 382 }); |
| 383 test('List Tail', () { |
| 384 expect(exec('(list-tail \'(5 6 7) 0)'), exec('\'(6 7)')); |
| 385 expect(exec('(list-tail \'(5 6 7) 1)'), exec('\'(7)')); |
| 386 expect(exec('(list-tail \'(5 6 7) 2)'), isNull); |
| 387 }); |
| 388 test('Map', () { |
| 389 expect(exec('(map \'() (lambda (x) (* 2 x)))'), isNull); |
| 390 expect(exec('(map \'(2) (lambda (x) (* 2 x)))'), exec('\'(4)')); |
| 391 expect(exec('(map \'(2 3) (lambda (x) (* 2 x)))'), exec('\'(4 6)')); |
| 392 expect(exec('(map \'(2 3 4) (lambda (x) (* 2 x)))'), exec('\'(4 6 8)')); |
| 393 }); |
| 394 test('Inject', () { |
| 395 expect(exec('(inject \'() 5 (lambda (s e) (+ s e 1)))'), 5); |
| 396 expect(exec('(inject \'(2) 5 (lambda (s e) (+ s e 1)))'), 8); |
| 397 expect(exec('(inject \'(2 3) 5 (lambda (s e) (+ s e 1)))'), 12); |
| 398 }); |
| 399 }); |
| 400 group('Examples', () { |
| 401 test('Fibonacci', () { |
| 402 var env = standard.create(); |
| 403 exec('(define (fib n)' |
| 404 ' (if (<= n 1)' |
| 405 ' 1' |
| 406 ' (+ (fib (- n 1)) (fib (- n 2)))))', env); |
| 407 expect(exec('(fib 0)', env), 1); |
| 408 expect(exec('(fib 1)', env), 1); |
| 409 expect(exec('(fib 2)', env), 2); |
| 410 expect(exec('(fib 3)', env), 3); |
| 411 expect(exec('(fib 4)', env), 5); |
| 412 expect(exec('(fib 5)', env), 8); |
| 413 }); |
| 414 test('Closure', () { |
| 415 var env = standard.create(); |
| 416 exec('(define (mul n)' |
| 417 ' (lambda (x) (* n x)))', env); |
| 418 expect(exec('((mul 2) 3)', env), 6); |
| 419 expect(exec('((mul 3) 4)', env), 12); |
| 420 expect(exec('((mul 4) 5)', env), 20); |
| 421 }); |
| 422 test('Object', () { |
| 423 var env = standard.create(); |
| 424 exec('(define (counter start)' |
| 425 ' (let ((count start))' |
| 426 ' (lambda ()' |
| 427 ' (set! count (+ count 1)))))', env); |
| 428 exec('(define a (counter 10))', env); |
| 429 exec('(define b (counter 20))', env); |
| 430 expect(exec('(a)', env), 11); |
| 431 expect(exec('(b)', env), 21); |
| 432 expect(exec('(a)', env), 12); |
| 433 expect(exec('(b)', env), 22); |
| 434 expect(exec('(a)', env), 13); |
| 435 expect(exec('(b)', env), 23); |
| 436 }); |
| 437 }); |
| 438 } |
OLD | NEW |