(in-package #:user-side-compiler) ;;; S-Printer (the S stood for S-expression, but that's no longer true). ;;; It reprints a valid program out of a syntax tree. Start it by passing ;;; S-PRINT a NODE-PROGRAM and a stream. Otherwise, be sure to properly bind ;;; the dynamic variables. (defparameter *s-print-indent-size* 4) (defvar *s-print-indent* 0) (defvar *s-print-indent-p* nil) (defun s-indent (stream) (when *s-print-indent-p* (loop :repeat (* *s-print-indent* *s-print-indent-size*) :do (write-char #\Space stream)))) (defgeneric s-print (node stream)) (defmethod s-print ((node null) stream) (format stream "~%~%END OF FILE~%")) (defmethod s-print ((node node) stream) (s-print (normal-next node))) (defmethod s-print :around (node (stream symbol)) (cond ((eql stream t) (s-print node *standard-output*)) ((eql stream nil) (with-output-to-string (s) (s-print node s))) (t (error "~A is not a valid stream. Must be T, NIL, or a STREAM." stream)))) (defmethod s-print ((node node-expr-grouping) stream) (format stream "(~A)" (s-print (expression node) nil))) (defmethod s-print ((node node-expr) stream) (let ((operator (car (rassoc (class-name (class-of (operator-token node))) *operator-token-classes* :key #'car)))) (assert (= (length (operands node)) 2)) (format stream "~A ~A ~A" (s-print (first (operands node)) nil) operator (s-print (second (operands node)) nil)))) (defmethod s-print ((node node-assignment) stream) (s-indent stream) (format stream "~A = ~A~%" (s-print (dst-variable node) nil) (s-print (value node) nil))) (defmethod s-print ((node node-conditional) stream) (s-indent stream) (format stream "IF ~A THEN~%~A" (s-print (test-node node) nil) (prog2 (incf *s-print-indent*) (s-print (then-node node) nil) (decf *s-print-indent*))) (s-indent stream) (if (null (else-node node)) (format stream "END~%") (progn (format stream "ELSE~%~A" (prog2 (incf *s-print-indent*) (s-print (else-node node) nil) (decf *s-print-indent*))) (s-indent stream) (format stream "END~%")))) (defmethod s-print ((node reference-variable) stream) (write-string (name node) stream)) (defmethod s-print ((node reference-constant) stream) (format stream "~D" (ref-value node))) (defmethod s-print ((node node-dotimes) stream) (s-indent stream) (format stream "FOR ~A DO ~A TIMES~%" (s-print (counter-ref node) nil) (s-print (stop-ref node) nil)) (incf *s-print-indent*) (s-print (loopee-node node) stream) (decf *s-print-indent*) (s-indent stream) (format stream "END~%")) (defmethod s-print ((node token-name) stream) (format stream "~A" (name node))) (defmethod s-print ((node token-number) stream) (format stream "~A" (value node))) (defmethod s-print ((node node-block) stream) (dolist (s (statements node)) (s-print s stream))) (defmethod s-print ((node node-program) stream) (let ((*s-print-indent* 0) (*s-print-indent-p* t)) (dolist (s (statements node)) (s-print s stream)))) (defmethod s-print ((node node-call) stream) (s-indent stream) (format stream "~A(~A~{, ~A~})~%" (s-print (callee node) nil) (s-print (first (arguments node)) nil) (mapcar (lambda (a) (s-print a nil)) (rest (arguments node))))) (defmethod s-print ((node asm-function) stream) (format stream "~A" (string-upcase (name node))))