diff options
Diffstat (limited to 'src/ast.lisp')
-rw-r--r-- | src/ast.lisp | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/src/ast.lisp b/src/ast.lisp new file mode 100644 index 0000000..c33de88 --- /dev/null +++ b/src/ast.lisp @@ -0,0 +1,207 @@ +(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)) |