(in-package :ast) (defclass node () ((token :accessor token :initarg :token :type token))) (defgeneric stringify (node) (:method ((node node)) (with-output-to-string (out) (emit node out)))) (defparameter *indent* 0) (defun emit-new-line (stream) (write-char #\Newline stream) (loop :for i :from 0 :below *indent* :do (write-char #\Space stream))) (defgeneric emit (node stream) (:method ((node node) stream) (write-string (token-literal node) stream))) (defgeneric token-literal (node) (:method ((node node)) (token:literal (token node)))) (defclass statement (node) ()) (defclass expression (node) ()) (defclass program (node) ((statements :accessor statements :initform (make-array 10 :adjustable t :fill-pointer 0)))) (defmethod token-literal ((node program)) (if (> (length (statements node)) 0) (token-literal (aref (statements node) 0)) "")) (defmethod emit ((node program) stream) (loop :for stmt :across (statements node) :do (emit stmt stream))) (defclass identifier (expression) ()) (defclass let-statement (statement) ((name :accessor name :initarg :name :type identifier) (value :accessor value :initarg :value :type expression))) (defmethod emit ((node let-statement) stream) (write-string (token-literal node) stream) (write-char #\Space stream) (emit (name node) stream) (write-string " = " stream) (emit (value node) stream) (write-char #\; stream) (write-char #\Newline stream)) (defclass return-statement (statement) ((return-value :accessor return-value :initarg :return-value :type expression))) (defmethod emit ((node return-statement) stream) (write-string (token-literal node) stream) (write-char #\Space stream) (emit (return-value node) stream) (write-char #\; stream) (write-char #\Newline stream)) (defclass integer-literal (expression) ((value :accessor value :initarg :value :type integer))) (defclass prefix-expression (expression) ((operator :accessor operator :initarg :operator :type token-type) (right :accessor right :initarg :right :type expression))) (defmethod emit ((node prefix-expression) stream) (write-char #\( stream) (write-string (token-literal node) stream) (emit (right node) stream) (write-char #\) stream)) (defclass expression-statement (statement) ((expression :accessor expression :initarg :expression :type expression))) (defmethod emit ((node expression-statement) stream) (emit (expression node) stream) (write-char #\; stream) (write-char #\Newline stream)) (defclass boolean-expression (expression) ((value :accessor value :initarg :value :type boolean))) (defclass infix-expression (expression) ((left :accessor left :initarg :left :type expression) (operator :accessor operator :initarg :operator :type token-type) (right :accessor right :initarg :right :type expression))) (defmethod emit ((node infix-expression) stream) (write-char #\( stream) (emit (left node) stream) (let ((tok (token:as-token (operator node)))) (format stream " ~a " (token:literal tok))) (emit (right node) stream) (write-char #\) stream)) (defclass if-expression (expression) ((con :accessor con :initarg :condition :type expression) (consequence :accessor consequence :initarg :consequence :type block-statement) (alternative :accessor alternative :initform nil :initarg :alternative :type (or block-statement null)))) (defmethod emit ((node if-expression) stream) (write-string "if (" stream) (emit (con node) stream) (write-string ") " stream) (emit (consequence node) stream) (unless (null (alternative node)) (write-string "else " stream) (emit (alternative node) stream))) (defclass block-statement (statement) ((statements :accessor statements :initarg statements :initform (make-array 0 :element-type 'statement :adjustable t :fill-pointer 0) :type (vector statement)))) (defmethod emit ((node block-statement) stream) (write-char #\{ stream) (let ((*indent* (+ 4 *indent*))) (emit-new-line stream) (loop :for stmt :across (statements node) :do (emit stmt stream))) (format stream "}")) (defclass function-literal (expression) ((parameters :accessor parameters :initarg :parameters :initform (make-array 0 :element-type 'identifier :adjustable t :fill-pointer 0) :type (vector identifier)) (body :accessor body :initarg :body :type block-statement))) (defmethod emit ((node function-literal) stream) (write-string (token-literal node) stream) (write-char #\( stream) (loop :for param :across (parameters node) :for i :from 1 :do (write-string (token-literal param) stream) (when (< i (length (parameters node))) (write-string ", " stream))) (write-char #\) stream) (emit (body node) stream)) (defclass call-expression (expression) ((fn-expr :accessor fn-expr :initarg :function :type expression) (args :accessor args :initarg :args :initform (make-array 0 :element-type 'expression :adjustable t :fill-pointer 0) :type (vector expression)))) (defmethod emit ((node call-expression) stream) (emit (fn-expr node) stream) (write-char #\( stream) (loop :for arg :across (args node) :for i :from 1 :do (emit arg stream) (when (< i (length (args node))) (write-string ", " stream))) (write-char #\) stream))