summaryrefslogtreecommitdiff
path: root/src/ast.lisp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ast.lisp')
-rw-r--r--src/ast.lisp207
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))