Add toyscheme, Scheme interpreter written in Scheme.
authorDavid Lee <live4thee@gmail.com>
Thu Jul 17 14:48:33 2008 +0800 (2 years ago)
changeset 20dd34aa263590
parent 199c3bc61c8959
child 216b5968e4af44
Add toyscheme, Scheme interpreter written in Scheme.
toyscheme/toyscheme.scm
       1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
       2 +++ b/toyscheme/toyscheme.scm	Thu Jul 17 14:48:33 2008 +0800
       3 @@ -0,0 +1,345 @@
       4 +;; toyscheme.scm
       5 +;;
       6 +;; The scheme interpreter implemented in SICP, chapter 4, with
       7 +;; slightly modifications.
       8 +;;
       9 +;; Li Qun <liqun82@users.sf.net>
      10 +;; Wednesday, 16 Jul 2008, 04:57pm
      11 +;;
      12 +;; Run this script with:
      13 +;; guile -e main toyscheme.scm
      14 +
      15 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      16 +;; Built-in special forms:
      17 +;;   lambda, define, if, cond, begin, set!, let, quote
      18 +;;
      19 +;; Primitive procedures loaded on startup:
      20 +;;   +, -, *, /
      21 +;;   car, cdr, cons
      22 +;;   not, null?, >, <, =
      23 +;;
      24 +;; Supported types:
      25 +;;   number, string, symbol
      26 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
      27 +
      28 +(define (self-evaluating? exp)
      29 +  (cond ((number? exp) #t)
      30 +	((string? exp) #t)
      31 +	(else #f)))
      32 +
      33 +(define (variable? exp) (symbol? exp))
      34 +
      35 +(define (quoted? exp)
      36 +  (taggled-list? exp 'quote))
      37 +
      38 +(define (text-of-quotation exp) (cadr exp))
      39 +
      40 +(define (taggled-list? exp tag)
      41 +  (if (pair? exp)
      42 +      (eq? (car exp) tag)
      43 +      #f))
      44 +
      45 +(define (assignment? exp)
      46 +  (taggled-list? exp 'set!))
      47 +
      48 +(define (assignment-variable exp) (cadr exp))
      49 +(define (assignment-value exp) (caddr exp))
      50 +
      51 +(define (definition? exp)
      52 +  (taggled-list? exp 'define))
      53 +
      54 +(define (definition-variable exp)
      55 +  (if (symbol? (cadr exp)) ;; (define foo ...)
      56 +      (cadr exp)
      57 +      (caadr exp))) ;; (define (foo x) (...))
      58 +
      59 +(define (definition-value exp)
      60 +  (if (symbol? (cadr exp))
      61 +      (caddr exp)
      62 +      (make-lambda (cdadr exp) ;; formal parameters
      63 +		   (cddr exp)))) ;; body
      64 +
      65 +(define (lambda? exp) (taggled-list? exp 'lambda))
      66 +(define (lambda-parameters exp) (cadr exp))
      67 +(define (lambda-body exp) (cddr exp))
      68 +
      69 +(define (make-lambda parameters body)
      70 +  (cons 'lambda (cons parameters body)))
      71 +
      72 +(define (let? exp) (taggled-list? exp 'let))
      73 +(define (let->lambda exp)
      74 +  (let ((vars (map car (cadr exp)))
      75 +        (vals (map cadr (cadr exp)))
      76 +        (body (caddr exp)))
      77 +    (cons (list 'lambda vars body) vals)))
      78 +
      79 +(define (if? exp) (taggled-list? exp 'if))
      80 +(define (if-predicate exp) (cadr exp))
      81 +(define (if-consequent exp) (caddr exp))
      82 +
      83 +(define (if-alternative exp)
      84 +  (if (not (null? (cdddr exp)))
      85 +      (cadddr exp)
      86 +      #f))
      87 +
      88 +(define (make-if predicate consequent alternative)
      89 +  (list 'if predicate consequent alternative))
      90 +
      91 +(define (begin? exp) (taggled-list? exp 'begin))
      92 +(define (begin-actions exp) (cdr exp))
      93 +(define (last-exp? seq) (null? (cdr seq)))
      94 +(define (first-exp seq) (car seq))
      95 +(define (rest-exps seq) (cdr seq))
      96 +
      97 +(define (sequence->exp seq)
      98 +  (cond ((null? seq) seq)
      99 +	((last-exp? seq) (first-exp seq))
     100 +	(else (make-begin seq))))
     101 +
     102 +(define (make-begin seq)
     103 +  (cons 'begin seq))
     104 +
     105 +(define (appliation? exp) (pair? exp))
     106 +(define (operator exp) (car exp))
     107 +(define (operands exp) (cdr exp))
     108 +(define (no-operands? ops) (null? ops))
     109 +(define (first-operand ops) (car ops))
     110 +(define (rest-operands ops) (cdr ops))
     111 +
     112 +(define (cond? exp) (taggled-list? exp 'cond))
     113 +(define (cond-clauses exp) (cdr exp))
     114 +
     115 +(define (cond-else-clause? clause)
     116 +  (eq? (cond-predicate clause) 'else))
     117 +
     118 +(define (cond-predicate clause) (car clause))
     119 +(define (cond-actions clause) (cdr clause))
     120 +
     121 +(define (cond->if exp)
     122 +  (expand-clauses (cond-clauses exp)))
     123 +
     124 +(define (expand-clauses clauses)
     125 +  (if (null? clauses)
     126 +      #f
     127 +      (let ((first (car clauses))
     128 +	    (rest (cdr clauses)))
     129 +	(if (cond-else-clause? first)
     130 +	    (if (null? rest)
     131 +		(sequence->exp (cond-actions first))
     132 +		(error "ELSE clause isn't last -- COND->IF"
     133 +		       clauses))
     134 +	    (make-if (cond-predicate first)
     135 +		     (sequence->exp (cond-actions first))
     136 +		     (expand-clauses rest))))))
     137 +
     138 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     139 +
     140 +(define (make-procedure parameters body env)
     141 +  (list 'procedure parameters body env))
     142 +
     143 +(define (compound-procedure? p)
     144 +  (taggled-list? p 'procedure))
     145 +
     146 +(define (procedure-parameters p) (cadr p))
     147 +(define (procedure-body p) (caddr p))
     148 +(define (procedure-environment p) (cadddr p))
     149 +
     150 +;; `env' is a list of frames
     151 +(define (enclosing-environment env) (cdr env))
     152 +(define (first-frame env) (car env))
     153 +(define the-empty-environment '())
     154 +
     155 +(define (make-frame variables values)
     156 +  (cons variables values))
     157 +
     158 +(define (frame-variables frame) (car frame))
     159 +(define (frame-values frame) (cdr frame))
     160 +
     161 +(define (add-binding-to-frame! var val frame)
     162 +  (set-car! frame (cons var (car frame)))
     163 +  (set-cdr! frame (cons val (cdr frame))))
     164 +
     165 +(define (extend-environment vars vals base-env)
     166 +  (if (= (length vars) (length vals))
     167 +      (cons (make-frame vars vals) base-env)
     168 +      (if (< (length vars) (length vals))
     169 +	  (error "Too many arguments supplied" vars vals)
     170 +	  (error "Too few arguments supplied" vals vals))))
     171 +
     172 +(define (lookup-variable-value var env)
     173 +  (define (env-loop env)
     174 +    (define (scan vars vals)
     175 +      (cond ((null? vars)
     176 +	     (env-loop (enclosing-environment env)))
     177 +	    ((eq? var (car vars))
     178 +	     (car vals))
     179 +	    (else (scan (cdr vars) (cdr vals)))))
     180 +    (if (eq? env the-empty-environment)
     181 +	(error "Unbound variable" var)
     182 +	(let ((frame (first-frame env)))
     183 +	  (scan (frame-variables frame)
     184 +		(frame-values frame)))))
     185 +  (env-loop env))
     186 +
     187 +(define (set-variable-value! var val env)
     188 +  (define (env-loop env)
     189 +    (define (scan vars vals)
     190 +      (cond ((null? vars)
     191 +	     (env-loop (enclosing-environment env)))
     192 +	    ((eq? var (car vars))
     193 +	     (set-car! vals val))
     194 +	    (else (scan (cdr vars) (cdr vals)))))
     195 +    (if (eq? env the-empty-environment)
     196 +	(error "Unbound variable -- SET!" var)
     197 +	(let ((frame (first-frame env)))
     198 +	  (scan (frame-variables frame)
     199 +		(frame-values frame)))))
     200 +  (env-loop env))
     201 +
     202 +(define (define-variable! var val env)
     203 +  (let ((frame (first-frame env)))
     204 +    (define (scan vars vals)
     205 +      (cond ((null? vars)
     206 +	     (add-binding-to-frame! var val frame))
     207 +	    ((eq? var (car vars))
     208 +	     (set-car! vals val))
     209 +	    (else (scan (cdr vars) (cdr vals)))))
     210 +    (scan (frame-variables frame)
     211 +	  (frame-values frame))))
     212 +
     213 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     214 +(define (primitive-procedure? proc)
     215 +  (taggled-list? proc 'primitive))
     216 +
     217 +(define (primitive-implementation proc)
     218 +  (cadr proc))
     219 +
     220 +(define primitive-procedures
     221 +  (list (list 'car car)			; list operations
     222 +	(list 'cdr cdr)
     223 +	(list 'cons cons)
     224 +	(list 'null? null?)		; prediction
     225 +	(list '> >)
     226 +	(list '< <)
     227 +	(list '= =)
     228 +	(list 'not not)
     229 +	(list '+ +)			; basic arithmetics
     230 +	(list '- -)
     231 +	(list '* *)
     232 +	(list '/ /)
     233 +	))
     234 +
     235 +(define (primitive-procedure-names)
     236 +  (map car primitive-procedures))
     237 +
     238 +(define (primitive-procedure-objects)
     239 +  (map (lambda (proc) (list 'primitive (cadr proc)))
     240 +       primitive-procedures))
     241 +
     242 +(define (apply-primitive-procedure proc args)
     243 +  (apply (primitive-implementation proc) args))
     244 +
     245 +(define (setup-environment)
     246 +  (let ((initial-env
     247 +	 (extend-environment (primitive-procedure-names)
     248 +			     (primitive-procedure-objects)
     249 +			     the-empty-environment)))
     250 +    initial-env))
     251 +
     252 +(define the-global-environment (setup-environment))
     253 +
     254 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     255 +
     256 +(define (list-of-values exps env)
     257 +  (if (no-operands? exps)
     258 +      '()
     259 +      (cons (toyscheme-eval (first-operand exps) env)
     260 +	    (list-of-values (rest-operands exps) env))))
     261 +
     262 +(define (eval-if exp env)
     263 +  (if (toyscheme-eval (if-predicate exp) env)
     264 +      (toyscheme-eval (if-consequent exp) env)
     265 +      (toyscheme-eval (if-alternative exp) env)))
     266 +
     267 +(define (eval-sequence exps env)
     268 +  (cond ((last-exp? exps) (toyscheme-eval (first-exp exps) env))
     269 +	(else (toyscheme-eval (first-exp exps) env)
     270 +	      (eval-sequence (rest-exps exps) env))))
     271 +
     272 +(define (eval-assignment exp env)
     273 +  (set-variable-value! (assignment-variable exp)
     274 +		       (toyscheme-eval (assignment-value exp) env)
     275 +		       env)
     276 +  'ok)
     277 +
     278 +(define (eval-definition exp env)
     279 +  (define-variable! (definition-variable exp)
     280 +    (toyscheme-eval (definition-value exp) env)
     281 +    env)
     282 +  'ok)
     283 +
     284 +(define (toyscheme-eval exp env)
     285 +  (cond ((self-evaluating? exp) exp)
     286 +	((variable? exp) (lookup-variable-value exp env))
     287 +	((quoted? exp) (text-of-quotation exp))
     288 +	((assignment? exp) (eval-assignment exp env))
     289 +	((definition? exp) (eval-definition exp env))
     290 +	((if? exp) (eval-if exp env))
     291 +	((lambda? exp)
     292 +	 (make-procedure (lambda-parameters exp)
     293 +			 (lambda-body exp)
     294 +			 env))
     295 +	((let? exp) (toyscheme-eval (let->lambda exp) env))
     296 +	((begin? exp)
     297 +	 (eval-sequence (begin-actions exp) env))
     298 +	((cond? exp) (toyscheme-eval (cond->if exp) env))
     299 +	((appliation? exp)
     300 +	 (toyscheme-apply (toyscheme-eval (operator exp) env)
     301 +			  (list-of-values (operands exp) env)))
     302 +	(else
     303 +	 (error "unknown expression type -- TOYSCHEME-EVAL" exp))))
     304 +
     305 +(define (toyscheme-apply procedure arguments)
     306 +  (cond ((primitive-procedure? procedure)
     307 +	 (apply-primitive-procedure procedure arguments))
     308 +	((compound-procedure? procedure)
     309 +	 (eval-sequence
     310 +	  (procedure-body procedure)
     311 +	  (extend-environment
     312 +	   (procedure-parameters procedure)
     313 +	   arguments
     314 +	   (procedure-environment procedure))))
     315 +	(else
     316 +	 (error "unknown procedure type -- TOYSCHEME-APPLY" procedure))))
     317 +
     318 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     319 +
     320 +(define input-prompt "Scheme> ")
     321 +(define output-prompt "Answer: ")
     322 +
     323 +(define (prompt-for-input string)
     324 +  (newline) (newline) (display string))
     325 +
     326 +(define (announce-output string)
     327 +  (newline) (display string))
     328 +
     329 +(define (user-print object)
     330 +  (if (compound-procedure? object)
     331 +      (display (list 'compound-procedure
     332 +		     (procedure-parameters object)
     333 +		     (procedure-body object)
     334 +		     '<procedure-env>))
     335 +      (display object)))
     336 +
     337 +(define (driver-loop)
     338 +  (prompt-for-input input-prompt)
     339 +  (let ((input (read)))
     340 +    (if (eof-object? input)
     341 +	(begin (newline) (exit 0))
     342 +	(let ((output (toyscheme-eval input the-global-environment)))
     343 +	  (announce-output output-prompt)
     344 +	  (user-print output))))
     345 +  (driver-loop))
     346 +
     347 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     348 +(define (main args) (driver-loop))