├── README.md ├── eval.l ├── evalify.rb ├── fizzbuzz.l ├── purelisp.rb ├── sedlisp.sed ├── sort.l ├── test.l └── test.rb /README.md: -------------------------------------------------------------------------------- 1 | SedLisp 2 | ======= 3 | 4 | Lisp implementation in (GNU) sed 5 | 6 | 7 | How to Use 8 | ---------- 9 | 10 | $ sed -f sedlisp.sed # '>' won't be shown. 11 | > (car (quote (a b c))) 12 | a 13 | > (cdr (quote (a b c))) 14 | (b c) 15 | > (cons 1 (cons 2 (cons 3 ()))) 16 | (1 2 3) 17 | > (defun fact (n) (if (eq n 0) 1 (* n (fact (- n 1))))) 18 | (lambda (n) (if (eq n 0) 1 (* n (fact (- n 1))))) 19 | > (fact 10) 20 | 3628800 21 | > (defun fib (n) (if (eq n 1) 1 (if (eq n 0) 1 (+ (fib (- n 1)) (fib (- n 2)))))) 22 | (lambda (n) (if (eq n 1) 1 (if (eq n 0) 1 (+ (fib (- n 1)) (fib (- n 2)))))) 23 | > (fib 12) 24 | 233 25 | > (defun gen (n) ((lambda (x y) y) (define G n) (lambda (m) (define G (+ G m))))) 26 | (lambda (n) ((lambda (x y) y) (define G n) (lambda (m) (define G (+ G m))))) 27 | > (define x (gen 100)) 28 | (lambda (m) (define G (+ G m))) 29 | > (x 10) 30 | 110 31 | > (x 90) 32 | 200 33 | > (x 300) 34 | 500 35 | 36 | 37 | Builtin Functions 38 | ----------------- 39 | 40 | - car 41 | - cdr 42 | - cons 43 | - eq 44 | - atom 45 | - +, -, *, /, mod 46 | - neg? 47 | - print 48 | 49 | 50 | Special Forms 51 | ------------- 52 | 53 | - quote 54 | - if 55 | - lambda 56 | - defun 57 | - define 58 | 59 | 60 | More Complicated Examples 61 | ------------------------- 62 | 63 | You can test a few more examples. 64 | 65 | FizzBuzz: 66 | 67 | $ cat fizzbuzz.l | sed -f sedlisp.sed 68 | (lambda (n) (if (eq n 101) nil (if (print (if (eq (mod n 15) 0) FizzBuzz (if (eq (mod n 5) 0) Buzz (if (eq (mod n 3) 0) Fizz n)))) (fizzbuzz (+ n 1)) nil))) 69 | PRINT: 1 70 | PRINT: 2 71 | PRINT: Fizz 72 | ... 73 | PRINT: 98 74 | PRINT: Fizz 75 | PRINT: Buzz 76 | nil 77 | 78 | Sort: 79 | 80 | $ cat sort.l /dev/stdin | sed -f sedlisp.sed 81 | ... 82 | (sort (quote (4 2))) 83 | (2 4) 84 | (sort (quote (4 2 99 12 -4 -7))) 85 | (-7 -4 2 4 12 99) 86 | 87 | Though this Lisp implementation does not support eval function, we can 88 | implement eval on top of this interpreter - eval.l is the 89 | implementation: 90 | 91 | $ cat eval.l /dev/stdin | sed -f sedlisp.sed 92 | (eval (quote (+ 4 38))) 93 | 42 94 | (eval (quote (defun fact (n) (if (eq n 0) 1 (* n (fact (- n 1))))))) 95 | (fact (lambda (n) (if (eq n 0) 1 (* n (fact (- n 1)))))) 96 | (eval (quote (fact 4))) ; Takes 10 seconds or so. 97 | 24 98 | 99 | This essentially means we have a Lisp interpreter in Lisp. evalify.rb 100 | is a helper script to convert a normal Lisp program into the Lisp in 101 | Lisp. You can run the FizzBuzz program like: 102 | 103 | $ ./evalify.rb fizzbuzz.l | sed -f sedlisp.sed 104 | ... 105 | PRINT: 1 106 | PRINT: 2 107 | PRINT: Fizz 108 | 109 | This takes very long time. For me, it took 45 minutes. 110 | 111 | Though sedlisp.sed does not support defmacro, eval.l also defines 112 | defmacro: 113 | 114 | $ ./evalify.rb | sed -f sedlisp.sed 115 | (defmacro let (l e) (cons (cons lambda (cons (cons (car l) nil) (cons e nil))) (cons (car (cdr l)) nil))) 116 | (let (x 42) (+ x 7)) ; Hit ^d after this. 117 | ... 118 | 49 119 | $ ./evalify.rb | sed -f sedlisp.sed 120 | (defun list0 (a) (cons a nil)) 121 | (defun cadr (a) (car (cdr a))) 122 | (defmacro cond (l) (if l (cons if (cons (car (car l)) (cons (cadr (car l)) (cons (cons (quote cond) (list0 (cdr l))))))) nil)) 123 | (defun fb (n) (cond (((eq (mod n 5) 0) "Buzz") ((eq (mod n 3) 0) "Fizz") (t n)))) 124 | (fb 18) ; Hit ^d after this. This will take about one minute. 125 | ... 126 | Fizz 127 | 128 | You can apply ./evalify.rb multiple times. However, sedlisp seems to 129 | be too slow to run the generated program. purelisp.rb, which is a 130 | reference implementation of sedlisp, can run it: 131 | 132 | $ ./evalify.rb fizzbuzz.l | ./evalify.rb | ruby purelisp.rb 133 | ... 134 | PRINT: 1 135 | PRINT: 2 136 | PRINT: Fizz 137 | PRINT: 4 138 | PRINT: Buzz 139 | PRINT: Fizz 140 | PRINT: 7 141 | PRINT: 8 142 | 143 | test.l is the test program I was using during the development. test.rb 144 | runs it with sedlisp.sed and purelisp.rb and compare their 145 | results. You can run the test with evalify.rb by passing -e: 146 | 147 | $ ./test.rb -e 148 | 149 | 150 | Limitations 151 | ----------- 152 | 153 | There should be a lot of limitations. sedlisp behaves very strangely 154 | when you pass a broken Lisp code. 155 | 156 | I don't know how many GNU extensions I used, so it would not be easy 157 | to port this to other sed implementations. 158 | -------------------------------------------------------------------------------- /eval.l: -------------------------------------------------------------------------------- 1 | (defun cadr (s) (car (cdr s))) 2 | (defun cddr (s) (cdr (cdr s))) 3 | 4 | ;(defun table-add (m k v) (if m (if (eq (car m) k) (cons k (cons v (cddr m))) (cons (car m) (cons (cadr m) (table-add (cddr m) k v)))) (cons k (cons v nil)))) 5 | (defun table-add (m k v) (cons k (cons v m))) 6 | 7 | (define gval-table ()) 8 | (defun gval-table-add (k v) (define gval-table (table-add gval-table k v))) 9 | 10 | (define val-table ()) 11 | 12 | (defun eval (s) (if (atom s) (eval-val s) (eval-if (car s) (cdr s)))) 13 | 14 | (defun eval-gval-1 (v s) (if v (if (eq (car v) s) (cadr v) (eval-gval-1 (cddr v) s)) s)) 15 | (defun eval-gval (s) (eval-gval-1 gval-table s)) 16 | 17 | (defun eval-val-1 (v s) (if v (if (eq (car v) s) (cadr v) (eval-val-1 (cddr v) s)) (eval-gval s))) 18 | (defun eval-val (s) (eval-val-1 val-table s)) 19 | 20 | (defun eval-if (o s) (if (eq o if) (run-if s) (eval-defmacro o s))) 21 | (defun eval-defmacro (o s) (if (eq o defmacro) (run-defmacro s) (eval-defun o s))) 22 | (defun eval-defun (o s) (if (eq o defun) (run-defun s) (eval-lambda o s))) 23 | (defun eval-lambda (o s) (if (eq o lambda) (run-lambda s) (eval-quote o s))) 24 | (defun eval-quote (o s) (if (eq o quote) (run-quote s) (eval-define o s))) 25 | (defun eval-define (o s) (if (eq o define) (run-define s) (eval-macro (eval o) s))) 26 | (defun is-macro (o) (if (atom o) nil (eq (car o) macro))) 27 | (defun eval-macro (o s) (if (is-macro o) (run-macro (cdr o) s) (eval-func o s))) 28 | (defun is-func (o) (if (atom o) nil (eq (car o) lambda))) 29 | (defun eval-func (o s) (if (is-func o) (run-func (cdr o) s) (eval-add o s))) 30 | (defun eval-add (o s) (if (eq o +) (run-add s) (eval-sub o s))) 31 | (defun eval-sub (o s) (if (eq o -) (run-sub s) (eval-mul o s))) 32 | (defun eval-mul (o s) (if (eq o *) (run-mul s) (eval-div o s))) 33 | (defun eval-div (o s) (if (eq o /) (run-div s) (eval-mod o s))) 34 | (defun eval-mod (o s) (if (eq o mod) (run-mod s) (eval-eq o s))) 35 | (defun eval-eq (o s) (if (eq o eq) (run-eq s) (eval-car o s))) 36 | (defun eval-car (o s) (if (eq o car) (run-car s) (eval-cdr o s))) 37 | (defun eval-cdr (o s) (if (eq o cdr) (run-cdr s) (eval-cons o s))) 38 | (defun eval-cons (o s) (if (eq o cons) (run-cons s) (eval-atom o s))) 39 | (defun eval-atom (o s) (if (eq o atom) (run-atom s) (eval-neg o s))) 40 | (defun eval-neg (o s) (if (eq o neg?) (run-neg s) (eval-print o s))) 41 | (defun eval-print (o s) (if (eq o print) (run-print s) (undefined-func o))) 42 | (defun undefined-func (o) (print (cons o (quote (undefined func))))) 43 | 44 | (defun run-if (s) (if (eval (car s)) (eval (cadr s)) (eval (car (cddr s))))) 45 | (defun run-defmacro (s) (gval-table-add (car s) (cons macro (cdr s)))) 46 | (defun run-defun (s) (gval-table-add (car s) (run-lambda (cdr s)))) 47 | (defun run-lambda (s) (cons lambda s)) 48 | (defun run-quote (s) (car s)) 49 | (defun run-define (s) (gval-table-add (car s) (eval (cadr s)))) 50 | 51 | (defun create-val-table (a p) (if a (cons (car a) (cons (eval (car p)) (create-val-table (cdr a) (cdr p)))) ())) 52 | (defun set-vals (a p) (define val-table (create-val-table a p))) 53 | 54 | (defun run-func-2 (a b c) b) 55 | (defun run-func-1 (v l s) (run-func-2 (set-vals (car l) s) (eval (cadr l)) (define val-table v))) 56 | (defun run-func (l s) (run-func-1 val-table l s)) 57 | 58 | (defun create-val-table-m (a p) (if a (cons (car a) (cons (car p) (create-val-table-m (cdr a) (cdr p)))) ())) 59 | (defun set-vals-m (a p) (define val-table (create-val-table-m a p))) 60 | 61 | (defun run-macro-2 (a b c) b) 62 | (defun run-macro-1 (v l s) (run-macro-2 (set-vals-m (car l) s) (eval (cadr l)) (define val-table v))) 63 | 64 | (defun run-macro (l s) (eval (run-macro-1 val-table l s))) 65 | 66 | (defun run-add (s) (+ (eval (car s)) (eval (cadr s)))) 67 | (defun run-sub (s) (- (eval (car s)) (eval (cadr s)))) 68 | (defun run-mul (s) (* (eval (car s)) (eval (cadr s)))) 69 | (defun run-div (s) (/ (eval (car s)) (eval (cadr s)))) 70 | (defun run-mod (s) (mod (eval (car s)) (eval (cadr s)))) 71 | (defun run-eq (s) (eq (eval (car s)) (eval (cadr s)))) 72 | (defun run-car (s) (car (eval (car s)))) 73 | (defun run-cdr (s) (cdr (eval (car s)))) 74 | (defun run-cons (s) (cons (eval (car s)) (eval (cadr s)))) 75 | (defun run-atom (s) (atom (eval (car s)))) 76 | (defun run-neg (s) (neg? (eval (car s)))) 77 | (defun run-print (s) (print (eval (car s)))) 78 | 79 | ; TEST 80 | 81 | ;(eval (quote (cons (quote (1 2)) (quote (3 ((5 6)) 4))))) 82 | ;(eval (quote (- 3 (+ 3 (if (eq 4 2) (+ 2 (+ 3 2)) (- (+ 4 1) (+ 3 9))))))) 83 | 84 | ;(define gval-table (table-add gval-table foo hoge)) 85 | ;(define gval-table (table-add gval-table bar fuga)) 86 | ;(define gval-table (table-add gval-table foo hige)) 87 | ;(table-get gval-table foo) 88 | 89 | ;(eval (quote (defun fizzbuzz (n) (if (eq n 101) nil (if (print (if (eq (mod n 15) 0) FizzBuzz (if (eq (mod n 5) 0) Buzz (if (eq (mod n 3) 0) Fizz n)))) (fizzbuzz (+ n 1)) nil))))) 90 | ;(eval (quote (fizzbuzz 1))) 91 | 92 | 93 | 94 | ;(eval (quote (* 2 3))) 95 | 96 | ;(eval (quote (defun f (n) n))) 97 | ;(eval (quote (f 42))) 98 | 99 | ;(eval (quote ((lambda (n) (+ n 4)) 42))) 100 | ;(eval (quote (lambda (n) (+ n 4)))) 101 | 102 | ;(eval (quote (define func (lambda (n) (+ n n))))) 103 | ;(eval (quote (func 42))) 104 | 105 | ;(eval (quote (define func (lambda () (print FOO))))) 106 | ;(eval (quote (func))) 107 | 108 | ;(print START) 109 | ;(eval (quote (defun mul (n m) (if (eq n 0) 0 (+ m (mul (- n 1) m)))))) 110 | ;(print DEFINED) 111 | ;(eval (quote (print (mul 2 3)))) 112 | 113 | ;(print (run-func-1 1 2 3)) 114 | 115 | ;(eval (quote (print (mul 2 3)))) 116 | 117 | ;(eval (quote (defun mul (n m) (if (eq n 0) 0 (+ m (mul (- n 1) m)))))) 118 | ;(print START) 119 | ;(eval (quote (* 2 3))) 120 | 121 | 122 | ;(define val-table (table-add nil foo hoge)) 123 | ;(push-vals) 124 | ;(print val-stack) 125 | ;(pop-vals) 126 | ;(print val-stack) 127 | 128 | ;(eval (quote (define foo 42))) 129 | ;(eval (quote foo)) 130 | 131 | ;(eval (quote (defun func (c) 3))) 132 | ;(eval (quote (defun func2 (b) (+ (func) b)))) 133 | ;(eval (quote (defun func2 (b) (+ b (func 99))))) 134 | ;(eval (quote (func2 42))) 135 | 136 | ;(eval (quote (defmacro let (l e) (cons (cons lambda (cons (cons (car l) nil) (cons e nil))) (cons (car (cdr l)) nil))))) 137 | ;(eval (quote (let (x 42) x))) 138 | -------------------------------------------------------------------------------- /evalify.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def evalify(c) 4 | o = File.read('eval.l').sub(/; TEST.*/ms, '') 5 | c.lines.each do |line| 6 | line.sub!(/;.*/, '') 7 | next if line =~ /^$/ 8 | o += "(eval (quote #{line.chomp}))\n" 9 | end 10 | o 11 | end 12 | 13 | if $0 == __FILE__ 14 | puts evalify($<.read) 15 | end 16 | -------------------------------------------------------------------------------- /fizzbuzz.l: -------------------------------------------------------------------------------- 1 | (defun fizzbuzz (n) (if (eq n 101) nil (if (print (if (eq (mod n 15) 0) FizzBuzz (if (eq (mod n 5) 0) Buzz (if (eq (mod n 3) 0) Fizz n)))) (fizzbuzz (+ n 1)) nil))) 2 | (fizzbuzz 1) 3 | -------------------------------------------------------------------------------- /purelisp.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | $vals = {} 4 | 5 | def parse_sexpr(s, orig) 6 | case s.strip 7 | when /^\(/ 8 | s = $' 9 | r = [] 10 | while s !~ /^\)/ 11 | raise "invalid sexpr: #{orig}" if s.empty? 12 | x, s = parse_sexpr(s, orig) 13 | r << x 14 | s.lstrip! 15 | end 16 | [r, $'] 17 | when /^-?\d+/ 18 | [$&.to_i, $'] 19 | when /^t\b/ 20 | [:t, $'] 21 | when /^nil\b/ 22 | [[], $'] 23 | when /^([^() ]+)/ 24 | [$1, $'] 25 | end 26 | end 27 | 28 | def atom?(sexpr) 29 | !sexpr.is_a?(Array) || sexpr == [] 30 | end 31 | 32 | def eval_sexpr(sexpr, vals) 33 | if atom?(sexpr) 34 | return vals[sexpr] || $vals[sexpr] || sexpr 35 | end 36 | 37 | op, *args = sexpr 38 | case op 39 | when 'if' 40 | raise "invalid if: #{stringify_sexpr(sexpr)}" if args.size != 3 41 | cond = eval_sexpr(args[0], vals) 42 | result = cond != [] ? eval_sexpr(args[1], vals) : eval_sexpr(args[2], vals) 43 | return result 44 | when 'defun' 45 | if (args.size != 3 || !args[0].is_a?(String) || !args[1].is_a?(Array) || 46 | !args[1].all?{|a|a.is_a?(String)}) 47 | raise "invalid defun: #{stringify_sexpr(sexpr)}" 48 | end 49 | $vals[args[0]] = {:args => args[1], :expr => args[2]} 50 | return args[0] 51 | when 'lambda' 52 | if (args.size != 2 || 53 | !args[0].is_a?(Array) || !args[0].all?{|a|a.is_a?(String)}) 54 | raise "invalid lambda: #{stringify_sexpr(sexpr)}" 55 | end 56 | return {:args => args[0], :expr => args[1]} 57 | when 'define' 58 | if args.size != 2 || !args[0].is_a?(String) 59 | raise "invalid define: #{stringify_sexpr(sexpr)}" 60 | end 61 | $vals[args[0]] = eval_sexpr(args[1], vals) 62 | return [] 63 | when 'quote' 64 | raise "invalid quote: #{stringify_sexpr(sexpr)}" if args.size != 1 65 | return args[0] 66 | end 67 | 68 | op = eval_sexpr(op, vals) 69 | args = args.map{|a|eval_sexpr(a, vals)} 70 | 71 | case op 72 | when Hash 73 | if op[:args].size != args.size 74 | raise "invalid number of args: #{stringify_sexpr(sexpr)}" 75 | end 76 | vals = {} 77 | op[:args].zip(args){|k, v|vals[k] = v} 78 | eval_sexpr(op[:expr], vals) 79 | when '+' 80 | raise "invalid add: #{stringify_sexpr(sexpr)}" if args.size != 2 81 | args[0] + args[1] 82 | when '-' 83 | raise "invalid sub: #{stringify_sexpr(sexpr)}" if args.size != 2 84 | args[0] - args[1] 85 | when '*' 86 | raise "invalid mul: #{stringify_sexpr(sexpr)}" if args.size != 2 87 | args[0] * args[1] 88 | when '/' 89 | raise "invalid div: #{stringify_sexpr(sexpr)}" if args.size != 2 90 | args[0] / args[1] 91 | when 'mod' 92 | raise "invalid mod: #{stringify_sexpr(sexpr)}" if args.size != 2 93 | args[0] % args[1] 94 | when 'eq' 95 | raise "invalid eq: #{stringify_sexpr(sexpr)}" if args.size != 2 96 | args[0] == args[1] ? :t : [] 97 | when 'car' 98 | if args.size != 1 || !args[0].is_a?(Array) 99 | raise "invalid car: #{stringify_sexpr(sexpr)}" 100 | end 101 | args[0][0] || [] 102 | when 'cdr' 103 | if args.size != 1 || !args[0].is_a?(Array) 104 | raise "invalid cdr: #{stringify_sexpr(sexpr)}" 105 | end 106 | args[0][1..-1] || [] 107 | when 'cons' 108 | if args.size != 2 || !args[1].is_a?(Array) 109 | raise "invalid cons: #{stringify_sexpr(sexpr)}" 110 | end 111 | [args[0]] + args[1] 112 | when 'atom' 113 | raise "invalid atom: #{stringify_sexpr(sexpr)}" if args.size != 1 114 | atom?(args[0]) ? :t : [] 115 | when 'neg?' 116 | raise "invalid neg?: #{stringify_sexpr(sexpr)}" if args.size != 1 117 | args[0] < 0 ? :t : [] 118 | when 'print' 119 | raise "invalid print: #{stringify_sexpr(sexpr)}" if args.size != 1 120 | puts "PRINT: #{stringify_sexpr(args[0])}" 121 | args[0] 122 | else 123 | raise "undefined function: #{op}" 124 | end 125 | end 126 | 127 | def stringify_sexpr(sexpr) 128 | if sexpr == [] 129 | 'nil' 130 | elsif sexpr == :t 131 | 't' 132 | elsif sexpr.is_a?(Array) 133 | '(' + sexpr.map{|s|stringify_sexpr(s)} * ' ' + ')' 134 | else 135 | sexpr.to_s 136 | end 137 | end 138 | 139 | $<.each do |line| 140 | line.sub!(/;.*/, '') 141 | next if line =~ /^$/ 142 | line.gsub!(/\s+/, ' ') 143 | sexpr, rest = parse_sexpr(line, line) 144 | raise "invalid sexpr: #{line}" if !rest.empty? 145 | puts stringify_sexpr(eval_sexpr(sexpr, {})) 146 | end 147 | -------------------------------------------------------------------------------- /sedlisp.sed: -------------------------------------------------------------------------------- 1 | x 2 | s/^/;mod;(lambda (n m) (- n (* (\/ n m) m)))$;\n/ 3 | # TODO: Handle negative divisions. 4 | s/^/;\/;(lambda (n m) (div-impl n m))$;\n/ 5 | s/^/;div-impl;(lambda (n m) (if (neg? n) -1 (+ 1 (div-impl (- n m) m))))$;\n/ 6 | s/^/;*;(lambda (n m) (if (eq n 0) 0 (+ m (* (- n 1) m))))$;\n/ 7 | y/() /{}_/ 8 | x 9 | 10 | :line_read 11 | 12 | s/ *;.*// 13 | s/\s\+/ /g 14 | s/(\s\+/(/g 15 | s/\s\+)/)/g 16 | s/\([^ ()]\)(/\1 (/g 17 | 18 | :loop 19 | 20 | s/ */ /g 21 | 22 | # i *** loop *** 23 | # p 24 | # x 25 | # i *** stack *** 26 | # p 27 | # x 28 | 29 | #s/( *)/nil/g 30 | 31 | /^(if /{ 32 | s/^(if \[\] /(if nil / 33 | /^(if nil /{ 34 | s/(if nil /_ifnil / 35 | 36 | /^_ifnil (/{ 37 | :remove_if_t 38 | s/([^()]*)/@/ 39 | /^_ifnil (/bremove_if_t 40 | } 41 | s/^\(_ifnil \) *\S\+/\1/ 42 | 43 | :parse_if_nil 44 | s/\(([^()]*)\)\([^\n]*\)/@\2\n\1/ 45 | #s/\(.*\n\)\(.*\)@\(.*\)\n\(.*\)/\1\2\4\3/ 46 | /^_ifnil *(/bparse_if_nil 47 | 48 | :resolve_if_nil_at 49 | s/^\(.*\n\)\(.*\)@\([^\n]*\)\n\([^\n]*\)/\1\2\4\3/ 50 | /^\(.*\n\)\(.*\)@/bresolve_if_nil_at 51 | 52 | s/^_ifnil/_ift/ 53 | bif_resolved 54 | } 55 | 56 | /^(if (/{ 57 | :parse_if 58 | s/\(([^()]*)\)\([^\n]*\)/@\2\n\1/ 59 | /^(if (/bparse_if 60 | 61 | :resolve_if_at 62 | s/^\(.*\n\)\(.*\)@\([^\n]*\)\n\([^\n]*\)/\1\2\4\3/ 63 | /^\(.*\n\)\(.*\)@/bresolve_if_at 64 | 65 | s/\(.*\)\n\(.*\)/\2@\1/ 66 | bpush_and_loop 67 | } 68 | 69 | /^(if \S\+ /{ 70 | s/(if \S\+ /_ift / 71 | :parse_if_t 72 | s/\(([^()]*)\)\([^\n]*\)/@\2\n\1/ 73 | #s/\(.*\n\)\(.*\)@\(.*\)\n\(.*\)/\1\2\4\3/ 74 | /^_ift (/bparse_if_t 75 | 76 | :resolve_if_t_at 77 | s/^\(.*\n\)\(.*\)@\([^\n]*\)\n\([^\n]*\)/\1\2\4\3/ 78 | /^\(.*\n\)\(.*\)@/bresolve_if_t_at 79 | 80 | /^_ift \S\+ (/{ 81 | :remove_if_nil 82 | s/([^()]*)// 83 | /^_ift \S\+ (/bremove_if_nil 84 | } 85 | 86 | :if_resolved 87 | /^_ift *@/{ 88 | s/.*\n// 89 | bloop 90 | } 91 | 92 | /^_ift *[^() ]\+/{ 93 | s/^_ift *\([^() ]\+\).*/\1/ 94 | bloop 95 | } 96 | 97 | s/^/invalid if?: / 98 | q 99 | } 100 | 101 | s/^/invalid if?: / 102 | q 103 | 104 | :push_and_loop 105 | H 106 | s/@.*// 107 | bloop 108 | } 109 | 110 | /^(defun /{ 111 | /^(defun \S\+ ([^()]*) .*)/!{ 112 | s/^/invalid defun: / 113 | q 114 | } 115 | 116 | s/(defun \(\S\+\) (\([^()]*\)) \(.*\))/;\1;(lambda (\2) \3)$;/ 117 | y/() /{}_/ 118 | H 119 | x 120 | s/\(.*;\)\(.*\)\(\n[^\n]*\)$/\1\3\2/ 121 | x 122 | s/;.*;\(.*\);/\1/ 123 | bloop 124 | } 125 | 126 | /^(define /{ 127 | /^(define \S\+ .*)/!{ 128 | s/^/invalid define: / 129 | q 130 | } 131 | 132 | s/(define /(define' '/ 133 | bloop 134 | } 135 | 136 | /^(quote /{ 137 | s/(quote \(.*\)).*/\1/ 138 | y/() /\[\],/ 139 | bloop 140 | } 141 | 142 | /^(lambda /{ 143 | /^(lambda ([^()]*) .*)$/!{ 144 | s/^/invalid lambda: / 145 | q 146 | } 147 | 148 | y/() /{}_/ 149 | s/$/$/ 150 | bloop 151 | } 152 | 153 | # Need to handle special forms? 154 | s/\(([^()]*)\)\(.*\)/@\2\n\1/ 155 | /^[^@]*(\(if\|defun\|quote\|lambda\|define\) /{ 156 | # Yes. 157 | s/@\(.*\)\n\(.*\)/\2\1/ 158 | s/(\(if\|defun\|quote\|lambda\|define\) /(@\1 / 159 | s/^\(.*\)(@\(.*\)/\2\n\1/ 160 | 161 | #i special 1 162 | #p 163 | /^[^()]*)/!{ 164 | :parse_special_form 165 | s/\(([^()]*)\)\([^\n]*\n[^\n]*\)/@\2\n;\1/ 166 | #i special 2 167 | #p 168 | 169 | :resolve_special_form_at 170 | s/\(;.*\)@\([^\n]*\)\n;\([^\n]*\)/\1\3\2/ 171 | /\(;.*\)@\([^\n]*\)\n;\([^\n]*\)/bresolve_special_form_at 172 | 173 | /^[^()]*)/!bparse_special_form 174 | } 175 | #i special 3 176 | #p 177 | 178 | s/\(^[^()]*)\)\([^\n]*\)\n\([^\n]*\)/(\1\n\3@\2/ 179 | 180 | :resolve_special_at 181 | s/^\([^@\n]*\)@\([^\n]*\)\(.*\)\n;\(.*\)/\1\4\2\3/ 182 | /^[^@\n]*@/bresolve_special_at 183 | 184 | s/\n/@/ 185 | H 186 | s/@.*// 187 | bloop 188 | } 189 | s/@\(.*\)\n\(.*\)/\2\1/ 190 | 191 | s/([^()]*)/@&@/ 192 | /@/!{ 193 | s/.*/(&) {}/ 194 | bhandle_var 195 | } 196 | 197 | s/\(.*\)@\(.*\)@/\2@\1@/ 198 | :start 199 | H 200 | 201 | ## i *** start *** 202 | ## p 203 | ## x 204 | ## i *** stack *** 205 | ## p 206 | ## x 207 | 208 | s/@.*// 209 | 210 | # This is unfortunate... 211 | /(\(if\|defun\|quote\|lambda\|define\) /bloop 212 | 213 | :handle_var 214 | G 215 | /^\([^\n]*[ ()]\)\([^0-9 ]\S*\)\([ ()\n]\).*\n;\2;/{ 216 | #i variable 217 | #p 218 | 219 | :subst_variable 220 | s/^\([^\n]*[ ()]\)\([^0-9 ]\S*\)\([ ()\n].*\n;\2;\([^;]*\)\)/\1\4\3/ 221 | #i var 222 | #p 223 | 224 | /^[^\n]*[ ()]\([^0-9 ]\S*\)[ ()\n].*\n;\1;/bsubst_variable 225 | 226 | #i result 227 | #p 228 | 229 | s/\n.*// 230 | 231 | / {}$/{ 232 | s/(\(.*\)) {}$/\1/ 233 | bmaybe_pop 234 | } 235 | 236 | bloop 237 | } 238 | 239 | s/\n.*// 240 | / {}$/{ 241 | s/(\(.*\)) {}$/\1/ 242 | bmaybe_pop 243 | } 244 | 245 | /({lambda/{ 246 | #i lambda! 247 | #p 248 | 249 | s/\$/;/ 250 | H 251 | s/;.*/;/ 252 | y/{}_/()@/ 253 | 254 | G 255 | s/;.*;/;/ 256 | 257 | 258 | /^[^$]*);\s*)$/!{ 259 | /^((lambda@*([^@)]\+/!binvalid_number_of_args 260 | 261 | :subst_args_lambda 262 | 263 | /^((lambda@*(@*)/binvalid_number_of_args 264 | 265 | :subst_arg_lambda 266 | 267 | s/^\(((lambda@*(@*\([^@);]\+\)[^);]*)[^;]*[()@]\)\2\([()@][^;]*)*;\s*\([^ ]\+\).*)\)$/\1\4\3/ 268 | 269 | /^((lambda@*(@*\([^@);]\+\)[^);]*)[^;]*[()@]\1[()@][^;]*)*;\s*[^ ]\+.*)$/bsubst_arg_lambda 270 | 271 | 272 | s/^\(((lambda@*(\)@*[^@);]\+\([^;]*);\s*\)[^ )]\+/\1\2/ 273 | /^[^;]*);\s*)$/!bsubst_args_lambda 274 | } 275 | 276 | /((lambda@*(@*)/!binvalid_number_of_args 277 | 278 | s/((lambda@(@*)@\(.*\));\s*)$/\1/ 279 | 280 | y/@/ / 281 | 282 | x 283 | s/\n[^\n]*$// 284 | x 285 | bpop_context 286 | 287 | :invalid_number_of_args 288 | x 289 | s/.*\n/invalid number of args: / 290 | y/{}_@/() / 291 | s/\$//g 292 | s/;//g 293 | q 294 | } 295 | 296 | /(+ /{ 297 | /--/{ 298 | s/^/invalid add: / 299 | q 300 | } 301 | /(+ -*[0-9]\+ -*[0-9]\+)/!{ 302 | s/^/invalid add: / 303 | q 304 | } 305 | 306 | s/(+ // 307 | s/)// 308 | /^-/{ 309 | / -/!{ 310 | s/-\(.*\) \(.*\)/\2 \1/ 311 | bsub 312 | } 313 | s/-//g 314 | s/$/\n-/ 315 | } 316 | / -/{ 317 | s/\(.*\) -/\1 / 318 | bsub 319 | } 320 | 321 | :add 322 | s/\(.*\) \([0-9]*\)\(\n\(-\)\)\?/\1@ \2@ 9876543210 9876543210\n\4/ 323 | 324 | :add_loop 325 | s/\(.\)@\(.*\)\(.\)@\(.\)\? \(.*\1\(.*\)\) .*\(\3.*\)\n/@\2; \4\6\7\5 \5 \5\n/ 326 | 327 | s/; .\{10\}\(.\)\([0-9]\{9\}\([0-9]\)\)\?[0-9]* \(.*\)\n\(.*\)/@\3 \4\n\1\5/ 328 | 329 | /^@ @/{ 330 | s/@ @. .*\n/\n1/ 331 | s/.*\n// 332 | :after_addsub 333 | s/\(.*\)-/-\1/ 334 | s/^-0$/0/ 335 | # wtf... 336 | s/^0*\([0-9]\)/\1/ 337 | bpop_context 338 | } 339 | 340 | s/^@/0@/ 341 | s/ @/ 0@/ 342 | 343 | badd_loop 344 | } 345 | 346 | /(- /{ 347 | /--/{ 348 | s/^/invalid sub: / 349 | q 350 | } 351 | /(- -*[0-9]\+ -*[0-9]\+)/!{ 352 | s/^/invalid sub: / 353 | q 354 | } 355 | 356 | s/(- // 357 | s/)// 358 | /^-/{ 359 | / -/!{ 360 | s/-// 361 | s/$/\n-/ 362 | badd 363 | } 364 | s/-//g 365 | s/\(.*\) \(.*\)/\2 \1/ 366 | } 367 | / -/{ 368 | s/-// 369 | badd 370 | } 371 | 372 | :sub 373 | s/\(.*\) \([0-9]*\)\(\n\(-\)\)\?/\1@ \2@x 9876543210 0123456789\n\4@\1 \2/ 374 | 375 | :sub_loop 376 | 377 | s/\(.\)@\(.*\)\(.\)@\(.\)\? \(.*\1\(.*\)\) \(.*\3\(.*\)\)\n/@\2; \4\8\1\6\5 \5 \7\n/ 378 | 379 | s/; .\{10\}\(.\)\([0-9]\{9\}\([^ ]\)\)\?[0-9]* \(.*\)\n\(.*\)/@\3 \4\n\1\5/ 380 | 381 | /^@ @ /{ 382 | s/.*\(-*\)@\(.*\) \(.*\)/\3 \2\n-\1/ 383 | s/--// 384 | bsub 385 | } 386 | 387 | /^@ @/{ 388 | s/@ @. .*\n/\n/ 389 | s/@.*// 390 | s/.*\n// 391 | s/^0*\([1-9]\)/\1/ 392 | bafter_addsub 393 | } 394 | 395 | s/^@/0@/ 396 | s/ @/ 0@/ 397 | 398 | bsub_loop 399 | } 400 | 401 | /(eq /{ 402 | /(eq \(\S\+\) \1)/{ 403 | s/.*/t/ 404 | bpop_context 405 | } 406 | /(eq \(\S\+\) \(\S\+\))/{ 407 | s/.*/nil/ 408 | bpop_context 409 | } 410 | s/^/invalid eq: / 411 | q 412 | } 413 | 414 | /(c[ad]r /{ 415 | s/\((c[ad]r \)\[ *\]/\1nil/ 416 | /(c[ad]r nil *)/{ 417 | s/.*/[]/ 418 | bpop_context 419 | } 420 | 421 | /(car \[\S\+ *)/{ 422 | s/(car \[\([^][,]\+\).*/\1/ 423 | /(car/!bpop_context 424 | 425 | s/(car \[/_car / 426 | :parse_car 427 | s/\(\[[^][]*\]\)\([^\n]*\)/@\2\n\1/ 428 | /_car *\[/bparse_car 429 | 430 | :resolve_car_at 431 | s/^\(.*\n\)\(.*\)@\([^\n]*\)\n\([^\n]*\)/\1\2\4\3/ 432 | /^\(.*\n\)\(.*\)@/bresolve_car_at 433 | 434 | s/.*\n// 435 | 436 | bpop_context 437 | } 438 | 439 | /(cdr \[\S\+ *)/{ 440 | s/(cdr \[\(.*\)\].*/\1/ 441 | /^\[/!{ 442 | s/^[^,]\+// 443 | :car_removed 444 | /^,/!{ 445 | s/.*/[]/ 446 | bpop_context 447 | } 448 | s/^,\(.*\)/[\1]/ 449 | bpop_context 450 | } 451 | 452 | :remove_car 453 | s/\[[^][]*\]// 454 | /^\[/bremove_car 455 | 456 | bcar_removed 457 | } 458 | 459 | /^(car/ s/^/invalid car: / 460 | /^(cdr/ s/^/invalid cdr: / 461 | q 462 | } 463 | 464 | /(cons /{ 465 | /(cons \S\+ \S\+)/{ 466 | s/\((cons \S\+ \)\[ *\]/\1nil/ 467 | /(cons \S\+ nil)/{ 468 | s/(cons \(\S\+\) .*/[\1]/ 469 | bpop_context 470 | } 471 | 472 | /(cons \S\+ [^[]/{ 473 | s/^/tuple is not supported: / 474 | q 475 | } 476 | 477 | s/(cons \(\S\+\) \[\(.*\)\])/[\1,\2]/ 478 | bpop_context 479 | } 480 | s/^/invalid cons: / 481 | q 482 | } 483 | 484 | /(atom /{ 485 | /\[\S\+\]/{ 486 | s/.*/[]/ 487 | bpop_context 488 | } 489 | s/.*/t/ 490 | bpop_context 491 | } 492 | 493 | /^(define' /{ 494 | /^(define' '\S\+ .*)/!{ 495 | s/^/invalid define: / 496 | q 497 | } 498 | 499 | s/(define' '\(\S\+\) \(.*\))/;\1;\2;/ 500 | H 501 | x 502 | s/\(.*;\)\(.*\)\(\n[^\n]*\)$/\1\3\2/ 503 | x 504 | s/;.*;\(.*\);/\1/ 505 | bpop_context 506 | } 507 | 508 | /(neg? /{ 509 | /(neg? -\S\+)/{ 510 | s/.*/t/ 511 | bpop_context 512 | } 513 | /(neg? \S\+)/{ 514 | s/.*/nil/ 515 | bpop_context 516 | } 517 | s/^/invalid neg?: / 518 | q 519 | } 520 | 521 | /(print /{ 522 | /(print \S\+)/{ 523 | s/(print // 524 | s/)// 525 | 526 | s/^/PRINT: / 527 | p 528 | s/PRINT: // 529 | 530 | bpop_context 531 | } 532 | s/^/invalid print: / 533 | q 534 | } 535 | 536 | /(\s*)/{ 537 | s/.*/nil/ 538 | bpop_context 539 | } 540 | 541 | s/^/unknown function: / 542 | q 543 | 544 | :maybe_pop 545 | 546 | x 547 | /@/{ 548 | x 549 | bpop_context 550 | } 551 | x 552 | 553 | bfinish 554 | 555 | :pop_context 556 | 557 | ## i *** pop_context *** 558 | ## p 559 | ## x 560 | ## i *** stack *** 561 | ## p 562 | ## i === 563 | ## x 564 | 565 | H 566 | x 567 | h 568 | s/\n[^\n]*\n[^\n]*$// 569 | x 570 | s/.*\n[^\n@]*@\([^\n@]*\)@\([^\n@]*\)\n\([^\n]*\)$/\1\3\2/ 571 | 572 | bloop 573 | 574 | :finish 575 | 576 | s/\[ *\]/nil/ 577 | y/\[\]{}_,/()() / 578 | s/\$//g 579 | 580 | N 581 | 582 | H 583 | s/\n.*// 584 | p 585 | g 586 | s/.*\n// 587 | x 588 | s/\n[^\n]*$// 589 | x 590 | 591 | bline_read 592 | -------------------------------------------------------------------------------- /sort.l: -------------------------------------------------------------------------------- 1 | (defun concat (a b) (if a (cons (car a) (concat (cdr a) b)) b)) 2 | (defun < (a b) (neg? (- a b))) 3 | (defun >= (a b) (if (< a b) nil t)) 4 | (defun filter-less1 (p h l) (if (< h p) (cons h l) l)) 5 | (defun filter-less (p l) (if l (filter-less1 p (car l) (filter-less p (cdr l))) nil)) 6 | (defun filter-more1 (p h l) (if (>= h p) (cons h l) l)) 7 | (defun filter-more (p l) (if l (filter-more1 p (car l) (filter-more p (cdr l))) nil)) 8 | (defun sort-1 (p l) (concat (sort (filter-less p l)) (cons p (sort (filter-more p l))))) 9 | (defun sort (l) (if l (sort-1 (car l) (cdr l)) nil)) 10 | 11 | (sort (quote (6 4 3 5 1 2 7))) 12 | -------------------------------------------------------------------------------- /test.l: -------------------------------------------------------------------------------- 1 | (+ 19 99) 2 | (+ 2 98) 3 | (+ 98 2 ) 4 | (+ 38 2 ) 5 | (+ -19 -99) 6 | (+ 99 99) 7 | 8 | (- 3 -17) 9 | (- -3 29) 10 | 11 | (- 10 10) 12 | (- -999 -999) 13 | 14 | (- 3 2) 15 | 16 | (- 1 3) 17 | 18 | (- 111 3) 19 | 20 | (- 100 3) 21 | 22 | (- 3 994) 23 | 24 | (+ 2 -3) 25 | 26 | (+ -2 3) 27 | (+ -9 2) 28 | (+ -2 9) 29 | 30 | 31 | (- -1 -3) 32 | (- -5 -3) 33 | 34 | (- 0 0) 35 | (- 0 0) 36 | 37 | (- 11 2) 38 | 39 | (- 8 9) 40 | 41 | ;(- 9 9) 42 | ;(- 9 0) 43 | ;(- 34 1) 44 | 45 | ;(- 34 7) 46 | 47 | ;(if 1 2 3) 48 | 1 49 | (+ 1(+ 1 1)) 50 | 51 | (+(- 9 2) 1) 52 | 53 | (- (- 9 2) (- 3 6)) 54 | 55 | (+ (+ (+ 1 2) 3) 4) 56 | (+ 1 (+ 2 (+ 3 4))) 57 | 58 | (eq 3 4) 59 | (eq 12 12) 60 | 61 | (if (eq 2 (+ 1 1)) (+ 3 4) (+ 4 2)) 62 | 63 | (if (eq 2 (+ 3 1)) (+ 3 4) (+ 2 4)) 64 | 65 | (if t 2 3) 66 | (if nil 2 3) 67 | 68 | (if (eq 4 2) 2 (+ (+ 4 1) 3)) 69 | 70 | (+ (if (eq 2 (+ 3 1)) (+ 3 4) (+ 2 4)) 4) 71 | (+ 4 (if (eq 2 (+ 3 1)) (+ 3 4) (+ 2 4))) 72 | 73 | (+ 3 (if (eq 4 4) (+ 2 (+ 3 2)) (+ 4 3))) 74 | (+ 3 (if (eq 4 2) (+ 2 (+ 3 2)) (+ (+ 4 1) 3))) 75 | 76 | (+ 3 (if t 2 3)) 77 | (+ 3 (if nil 2 3)) 78 | 79 | (- 3 (+ 3 (if (eq 4 2) (+ 2 (+ 3 2)) (- (+ 4 1) (+ 3 9))))) 80 | (if (eq (+ 1 2) (- 3 1)) (- (+ 2 3) (+ 3 9)) (+ (+ 2 3) (+ 3 9))) 81 | (if (eq (+ 1 2) (- 4 1)) (- (+ 2 3) (+ 3 9)) (+ (+ 2 3) (+ 3 9))) 82 | 83 | (defun f () 42) ;cont 84 | (f) 85 | (defun f () (+ 4 8)) ;cont 86 | (f) 87 | (defun f (n) (+ n n)) ;cont 88 | (f 42) 89 | 90 | (* 23 33) 91 | 92 | (/ 45 7) 93 | (mod 45 7) 94 | 95 | (quote ((1 2) (3 4))) 96 | (quote (())) 97 | 98 | () 99 | (car ()) 100 | (car nil) 101 | (car (quote ())) 102 | (car (quote (3))) 103 | (car (quote (42 99))) 104 | (car (quote ((42 99) (3 7)))) 105 | (car (quote ((42 (11 22) 99) (3 7)))) 106 | 107 | (cdr ()) 108 | (cdr nil) 109 | (cdr (quote ())) 110 | (cdr (quote (3))) 111 | (cdr (quote (42 99))) 112 | (cdr (quote ((42 99) (3 7)))) 113 | (cdr (quote ((42 (11 22) 99) (3 7)))) 114 | 115 | (cons 42 nil) 116 | (cons 42 ()) 117 | (cons nil nil) 118 | (cons 42 (quote ())) 119 | (cons 1 (quote (2))) 120 | (cons (quote (1 2)) (quote (3 ((5 6)) 4))) 121 | (cons 2 (cons 3 ())) 122 | 123 | (atom ()) 124 | (atom nil) 125 | (atom t) 126 | (atom 42) 127 | (atom (quote ())) 128 | (atom (quote (3))) 129 | (atom (quote (42 99))) 130 | (atom (quote ((42 99) (3 7)))) 131 | (atom (quote ((42 (11 22) 99) (3 7)))) 132 | 133 | ; TEST LAMBDA 134 | 135 | ((lambda (n) (+ 4 n)) 42) 136 | ((lambda (n m) (- m n)) 42 99) 137 | ((lambda (x) x) 42) 138 | 139 | (define foo 42) ;cont 140 | foo 141 | 142 | (define func (lambda (x y z) y)) ;cont 143 | (func 1 2 3) 144 | 145 | ((lambda (f) (f)) (lambda () 42)) 146 | 147 | (defun func (a b) b) ;cont 148 | (defun func2 (a b) (+ (func 2 3) b)) ;cont 149 | (func2 99 42) 150 | 151 | ; TEST EVAL 152 | 153 | (defmacro let (l e) (cons (cons lambda (cons (cons (car l) nil) (cons e nil))) (cons (car (cdr l)) nil))) ;cont 154 | (let (x 42) (+ x 7)) 155 | 156 | (defun list0 (a) (cons a nil)) ;cont 157 | (defun cadr (a) (car (cdr a))) ;cont 158 | (defmacro cond (l) (if l (cons if (cons (car (car l)) (cons (cadr (car l)) (cons (cons (quote cond) (list0 (cdr l))))))) nil)) ;cont 159 | (defun fb (n) (cond (((eq (mod n 5) 0) Buzz) ((eq (mod n 3) 0) Fizz) (t n)))) ;cont 160 | (fb 18) 161 | -------------------------------------------------------------------------------- /test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require './evalify' 4 | 5 | $evalify = false 6 | if ARGV[0] == '-e' 7 | $evalify = true 8 | ARGV.shift 9 | elsif ARGV[0] == '-E' 10 | $evalify = true 11 | $eval_test_only = true 12 | ARGV.shift 13 | end 14 | 15 | ref_lisp = ARGV[0] || 'purelisp.rb' 16 | test_lisp = ARGV[1] 17 | test_lisp ||= File.exist?('sedlisp.sed') ? 'sedlisp.sed' : 'beflisp.bef' 18 | 19 | COMMANDS = { 20 | 'purelisp.rb' => ['ruby', 'purelisp.rb'], 21 | 'rblisp.rb' => ['ruby', 'rblisp.rb'], 22 | 'sedlisp.sed' => ['sed', '-f', 'sedlisp.sed'], 23 | 'lisp.bef' => ['./befunge', 'lisp.bef'], 24 | 'beflisp.bef' => ['./befunge', 'beflisp.bef'], 25 | 'lisp' => ['./lisp'], 26 | 'makelisp.mk' => ['make', '-f', 'makelisp.mk'], 27 | 'makelisp2.mk' => ['make', '-f', 'makelisp2.mk'], 28 | } 29 | 30 | def getResult(cmd, line) 31 | pipe = IO.popen(cmd, 'r+') 32 | pipe.puts(line) 33 | pipe.close_write 34 | o = pipe.read 35 | if cmd[-1] == 'rblisp.rb' 36 | o.gsub!(/^> /, '') 37 | end 38 | o 39 | end 40 | 41 | num_tests = 0 42 | fails = [] 43 | 44 | lines = File.readlines('test.l') 45 | lineno = -1 46 | while line = lines[lineno += 1] 47 | if line.sub(/;.*/, '') =~ /^ *$/ 48 | if (/TEST LAMBDA/ =~ line && 49 | (ref_lisp == 'rblisp.rb' || test_lisp == 'rblisp.rb')) 50 | break 51 | elsif /TEST EVAL/ =~ line 52 | $eval_test = true 53 | if !$evalify 54 | break 55 | end 56 | end 57 | next 58 | end 59 | 60 | next if !$eval_test && $eval_test_only 61 | 62 | while line =~ /;cont/ 63 | line.sub!(/;cont/, '') 64 | line += lines[lineno += 1] 65 | end 66 | line.chomp! 67 | orig = line 68 | if $evalify 69 | line = evalify(line) 70 | end 71 | 72 | expected = getResult(COMMANDS[ref_lisp], $eval_test ? line : orig) 73 | expected = expected.lines.to_a[-1].to_s.chomp 74 | output = getResult(COMMANDS[test_lisp], line) 75 | actual = output.lines.to_a[-1].to_s.chomp 76 | 77 | if expected == actual 78 | puts "#{orig}: OK (#{expected})" 79 | else 80 | puts "#{orig}: FAIL expected=#{expected} actual=#{actual}" 81 | puts output 82 | fails << orig 83 | end 84 | num_tests += 1 85 | end 86 | 87 | if fails.empty? 88 | puts 'PASS' 89 | else 90 | puts "Failed tests:" 91 | puts fails.map{|f|f.inspect} 92 | puts "#{fails.size} / #{num_tests} FAIL" 93 | end 94 | --------------------------------------------------------------------------------