;;;;SMEVAL.SCM   Code for Sample Pset
;;;  EVAL-PRINT loop for Substitution Model 
                              ;;FOR THE COMPILER
(declare (usual-integrations))          
(declare (integrate-external "smsyntax"))
(declare (integrate-external "smscope"))
;; The main procedure of the Scheme Term Rewriting Model implementation
;; is ONE-STEP-BODY, which does a single rewrite step, if possible, on a
;; body.  ONE-STEP-BODY satisfies a simple "contract" which allows it to be
;; used effectively without studying, or even looking at, its definition:
                         ;;;CONTRACT FOR ONE-STEP-BODY
;; ONE-STEP-BODY: (
,List()) --> Tagged-Body
;; Tagged-Body  =  Labelled-Sum[stepped: ,
;;                                  val: ,
;;                                stuck: Error-info]
;;In a call (ONE-STEP-BODY body defines-from-context):
;;           BODY must be desugared.
;;           If a rewrite rule applies to BODY, using DEFINES-FROM-CONTEXT for
;;           lookup of undefined variables, then the call returns the tagged result
;;                  ('stepped, rewritten body);
;;           if body is a syntactic value, then it returns
;;                  ('val, garbage collected body);
;;           if body cannot be rewritten and is not a value, then it returns
;;                  ('stuck, info on the problem).
;;SMSTEP-LIST repeatedly calls ONE-STEP, cons'ing up selected Step's
;;until reaching a value or getting stuck or interrupted.
;;SMSTEP-LIST:  --> Tagged-Steps
;; Tagged-Steps  =  Step-Tag  x  List(Step)
;; Step-Tag = {interrupted, val, stuck}
;;constructor: MAKE-TAGGED-STEPS: (Step-Tag, List(Step)) --> Tagged-Steps
;;selector: DATUM-OF-TAGGED-STEPS: Tagged-Steps --> List(Step)
;; Step = Sch-Positive-Integer x ( U Error-info)
;; constructor MAKE-STEP: (Sch-Positive-Integer, ( U Error-Info)) --> Step
;; selectors   STEP-NUMBER-OF: Step --> Sch-Positive-Integer
;;             BODY-OR-INFO-OF-STEP: Step --> ( U Error-Info)
(define (smstep-list body)
  (define (do-steps step-number body steps-list)
    (let ((tagged-body (one-step-body body global-definitions)))
      (let ((tag (tag-of tagged-body))
            (next-body (maybe-collect-garbage
                        step-number
                        (datum-of-tagged-body tagged-body))))
	(let ((new-steps-list (add-step
                               tag
                               step-number
                               next-body
                               steps-list)))
          (cond ((memq tag '(val stuck))
                 (make-tagged-steps tag new-steps-list))
                ((interrupt-smeval? step-number next-body)
                 (make-tagged-steps 'interrupted new-steps-list))
                (else (do-steps (inc step-number) next-body new-steps-list)))))))
  (let ((desugared-body (desugar body)))
    (do-steps
     1
     desugared-body
     (list (make-step 0 desugared-body)))))
(define (maybe-collect-garbage number body)
  (if (garbage-collect-this-step? number body)
      (garbage-collect
       (defines-of-body body)
       (expression-of-body body))
      body))
(define (add-step tag number body steps)
  (if (or (memq tag '(val stuck))
	  (save-this-step? number body))
      (cons (make-step number body) steps)
      steps))
;;;THE GLOBAL DEFINITIONS: List()
;;;These are all user-definable, but are supplied for convenience
;;;and to minimize clutter in the printout.  By being installed here,
;;;they aren't printed.
;;;They must be desugared before being included in the list.
(define global-definitions
  '((define (list? obj)
      (if (null? obj)
          #t
          (if (pair? obj)
              (list? (cdr obj))
              #f)))
    (define (map f l)                   ;works only for f taking 1 argument
      (if (null? l)
          ()
          (cons (f (car l))
                (map f (cdr l)))))))
     ;;SAVED STEP PREDICATE
(define (save-this-step? n body)        ;cons at the rate
  (or (zero? n)
      (let ((sqn (round (sqrt n))))	;1/ sqrt(n)
	(zero? (modulo n sqn)))))
    ;;SUBMODEL INTERRUPT PREDICATE
(define (interrupt-smeval? step-number body)
  (> step-number 600))
    ;;GARBAGE COLLECTION IMPOSED-RATE PREDICATE
(define (garbage-collect-this-step? step-number body)   ;impose a garbage collection
  (zero? (modulo step-number 40)))                      ;every 40th step
                               ;;;;CONTROLLING THE PRINTOUT
;;PRINT-STEPS: Tagged-Steps --> 
;;SMEVAL:  --> 
;;nicely prints out (SMSTEP-LIST BODY), returning the final result of the rewriting
(define (smeval body) (print-steps (smstep-list body)))
(define (print-steps tagged-steplist)
    (let ((tag (tag-of tagged-steplist))
          (reversed-steps (datum-of-tagged-steps tagged-steplist)))
      (let ((final-body (body-or-info-of-step (car reversed-steps)))
            (steps (reverse reversed-steps)))
        (define (printlst stlst)
          (if (null? stlst)
              (print-final-message tag)
              (let ((current-step (car stlst)))
                (print-stepped-message
                 (step-number-of current-step)
                 (body-or-info-of-step current-step))
                (printlst (cdr stlst)))))
        (printlst steps)
        final-body)))
(define (print-stepped-message step-number body)
  (begin (newline)
         (newline)
         (display ";==(")
         (display step-number)
         (display ")==>")
         (pp body)))
(define (print-final-message tag)
  (newline)
  (display
   (cond
    ((eq? tag 'val) "Syntactic Value was returned")
    ((eq? tag 'interrupted) "Rewriting got interrupted")
    ((eq? tag 'stuck) "Rewriting got stuck"))))
    ;;PRINTABLE-VERSION:  --> Nested-List(Sch-Symbol + Sch-Num + Sch-Bool + '() )
     ;;Make a Sub Model value look more like what Scheme would print out
(define (printable-version value)
  (cond (((tagged-pair? 'cons) value)
         (if (submodel-null? (caddr value))
             (list (printable-version (cadr value)))
             (cons
              (printable-version (cadr value))
              (printable-version (caddr value)))))
        ((symbol-expression? value)
         (cadr value))
        ((submodel-null? value)
         '())
        ((or (lambda-expression? value)
             (primitive-procedure-variable? value)
             (rule-specified-procedure-variable? value))
         (cons 'procedure-object: value))
        ((and (not (expression? value))
              (pair? value)
              (pair? (car value))
              (define? (caar value))) ;VALUE is a body with defines
         (printable-version (expression-of-body value))) ;;don't print the defines
        (else value)))                  ;not sure what it is, so leave it alone