diff options
| -rw-r--r-- | assembly.lisp | 170 | ||||
| -rw-r--r-- | cl-forth.lisp | 115 | ||||
| -rw-r--r-- | main.lisp | 2 | 
3 files changed, 171 insertions, 116 deletions
| diff --git a/assembly.lisp b/assembly.lisp index c1efe45..422ea6c 100644 --- a/assembly.lisp +++ b/assembly.lisp @@ -15,6 +15,8 @@        (find sym *psuedo-identifiers* :test #'string=)))  (eval-always +  (defparameter *targets* '(:nasm :c)) +    (defun normalize-op-list (asm-list)      (cons 'list            (mapcar (lambda (el) (cond ((stringp el) el) @@ -67,12 +69,39 @@                              ((listp form)                               (appending (op->string form))))))))) +  (defparameter *stack-fn-assoc* '((:= :cmove) +                                   (:> :cmovg) +                                   (:< :cmovl))) +   +  (defun stack-unextend (stack) +    "Turns an extended stack to body of a defop, second part of an extended stack is in the form of (:op arg1 arg2 :then val1 :else val2) which is asserted." +    (multiple-value-bind (fst snd) (split-stack stack) +      (assert (= 1 (length snd))) +      (append (iter (for x in (reverse fst)) +                    (let ((*print-case* :downcase)) +                      (collect (format nil "pop ~a" x)))) +              (let* ((push-part (car snd)) +                     (ifs (nthcdr 3 push-part))) +                (list `(:mov rdx ,(getf ifs :ise)) +                      `(:mov rcx ,(getf ifs :değilse)) +                      `(:cmp ,(second push-part) ,(third push-part)) +                      `(,(cadr (assoc (car push-part) *stack-fn-assoc*)) +                        rcx rdx) +                      (format nil "push rcx")))))) +      (defun syntax-of (form)      (cond ((or (stringp form)                 (and (consp form) (stringp (car form))))             :string)            ((and (listp form) (find '-- form)) -           :stack) +           (if (multiple-value-bind (fst snd) (split-stack form) +                 (declare (ignore fst)) +                 (and (consp snd) +                      (consp (car snd)) +                      (find (caar snd) *stack-fn-assoc* +                            :key #'car))) +               :stack-extended +               :stack))            ((and (listp form) (keywordp (car form)))             :op)            (t :general))) @@ -96,29 +125,60 @@    (defun expand-group (group out-stream &key (indent 4))      (destructuring-bind (syntax-type . forms) group        (case syntax-type -        (:stack `(defop-format ,out-stream ,indent -                   ,(cons 'list (mapcan (lambda (form) (stack->string form)) -                                        forms)))) -        (:op `(defop-format ,out-stream ,indent -                ,(cons 'list (mapcar (lambda (form) (op->string form :push? nil)) -                                     forms)))) -        (:string `(defop-format ,out-stream ,indent -                    ,(normalize-op-list forms))) -        (:general `(progn ,@(mapcar (lambda (form) (replace-write out-stream indent -                                                             form)) -                                    forms))))))) - -(defmacro defop (op-name+args (&key (indent 4) (lex t)) &body body) +        (:stack `((defop-format ,out-stream ,indent +                     ,(cons 'list (mapcan (lambda (form) (stack->string form)) +                                          forms))))) +        (:stack-extended (expand-nasm out-stream indent +                                      (stack-unextend (car forms)))) +        (:op `((defop-format ,out-stream ,indent +                  ,(cons 'list (mapcar (lambda (form) (op->string form :push? nil)) +                                       forms))))) +        (:string `((defop-format ,out-stream ,indent +                      ,(normalize-op-list forms)))) +        (:general `((progn ,@(mapcar (lambda (form) (replace-write out-stream indent +                                                               form)) +                                      forms))))))) + +  (defun expand-nasm (out-stream indent body) +    (mapcan #'(lambda (group) (expand-group group out-stream +                                       :indent indent)) +            (group-by-syntax body))) +   +  (defun expand-for-target (target out-stream body &optional (indent 4)) +    (case target +      (:nasm (expand-nasm out-stream indent body)) +      (:c '(nil)))) +   +  (defun expand-method (target out-stream indent op-name args body) +    (with-gensyms (_op _args _target) +      (declare (ignorable _args)) +      `(defmethod write-op +           ((,_target (eql ,target)) ,out-stream +            (,_op (eql ,(intern (string op-name) "KEYWORD"))) +            ,_args) +         ,@(if (null args) +               (expand-for-target target out-stream body indent) +               `((destructuring-bind ,args ,_args +                   ,@(expand-for-target target out-stream body indent)))))))) + +(defgeneric write-op (target stream op args) +  (:documentation "Generate code for OP and ARGS and write it to STREAM according to the TARGET")) + +(defmacro defop (op-name+args (&key (indent 4) (lex t) (targets :nasm) +                                 (as nil)) +                 &body body) +  (declare (ignorable indent))    (with-gensyms (out-stream)      (destructuring-bind (op-name . args) (mklist op-name+args)        `(progn           ,@(unless (null lex)               `((push ',op-name *identifiers*))) -         (setf (gethash ,(string op-name) *operations*) -               (lambda (,out-stream ,@args) -                 ,@(mapcar #'(lambda (group) (expand-group group out-stream -                                                      :indent indent)) -                           (group-by-syntax body)))))))) +         ,@(iter (for target in (if (eq :all targets) +                                    *targets* +                                    (mklist targets))) +                 (collect (expand-method target out-stream indent +                                         (if (null as) op-name as) +                                         args body)))))))  ;;; TODO: Turn stack operation comments to DEFOP option,  ;;;       which then can be used by the user as a documentation @@ -126,15 +186,6 @@  ;;;       the stack op documentation (this seems easily doable)  ;;; Hopefully these two are done, need testing... -;; ( -- a) -(defop (push-int a) (:lex nil) -  ("push ~d" a)) - -(defop (push-str len addr str) (:lex nil) -  (progn (:write ("push ~d" len) -                 ("push str_~d" addr)) -         (list :string addr str))) -  (defop + ()    (rbx rax -- (:add rax rbx))) @@ -142,11 +193,7 @@    (rbx rax -- (:sub rbx rax)))  (defop = () -  (:mov rcx 0) -  (:mov rdx 1) -  (rbx rax -- ) -  (:cmp rax rbx) -  ( -- (:cmove rcx rdx))) +  (rbx rax -- (:= rbx rax :ise 1 :değilse 0)))  (defop eş ()    (rax -- rax rax)) @@ -155,32 +202,14 @@    (rax -- ))  (defop < () -  (:mov rcx 0) -  (:mov rdx 1) -  (rax rbx -- ) -  (:cmp rax rbx) -  ( -- (:cmovl rcx rdx))) +  (rax rbx -- (:< rax rbx :ise 1 :değilse 0)))  (defop > () -  (:mov rcx 0) -  (:mov rdx 1) -  (rax rbx -- ) -  (:cmp rax rbx) -  ( -- (:cmovg rcx rdx))) +  (rax rbx -- (:> rax rbx :ise 1 :değilse 0)))  (defop bel ()    ( -- bel)) -(defop oku () -  (rax -- ) -  (:xor rbx rbx) -  (:mov bl [rax]) -  ( -- rbx)) - -(defop yaz () -  (rax rbx -- ) -  (:mov [rax] bl)) -  (defop üst ()    (rbx rax -- rbx rax rbx)) @@ -196,12 +225,31 @@  (defop >> ()    (rbx rcx -- (:shr rbx cl))) -(defop "|" () +(defop "|" (:as pipe)    (rbx rax -- (:or rbx rax)))  (defop & ()    (rbx rax -- (:and rbx rax))) +(defop oku () +  (rax -- ) +  (:xor rbx rbx) +  (:mov bl [rax]) +  ( -- rbx)) + +(defop yaz () +  (rax rbx -- ) +  (:mov [rax] bl)) + +;; ( -- a) +(defop (push-int a) (:lex nil) +  ("push ~d" a)) + +(defop (push-str len addr str) (:lex nil) +  (progn (:write ("push ~d" len) +                 ("push str_~d" addr)) +         (list :string addr str))) +  (defop dump ()    "pop rdi"    "call dump") @@ -257,12 +305,16 @@            (mapcar (lambda (x) (if (stringp x) (comment-safe-str x) x))                     op))) +;; (defun gen-code (op str) +;;   (let ((op-fn (gethash (string (car op)) *operations*))) +;;     (when (null op-fn) +;;       (error "~s is not a valid op" op)) +;;     (gen-header op str) +;;     (apply op-fn str (cdr op)))) +  (defun gen-code (op str) -  (let ((op-fn (gethash (string (car op)) *operations*))) -    (when (null op-fn) -      (error "~s is not a valid op" op)) -    (gen-header op str) -    (apply op-fn str (cdr op)))) +  (gen-header op str) +  (write-op :nasm str (car op) (cdr op)))  (defun gen-dump (str)    (format str "~{~a~%~}" diff --git a/cl-forth.lisp b/cl-forth.lisp index 1026253..ec7927f 100644 --- a/cl-forth.lisp +++ b/cl-forth.lisp @@ -28,40 +28,41 @@                  (t (write-char ch str))))))  (defun lex-line (line &optional (line-num 0)) -  (iter (with line-stream = (make-string-input-stream line)) -        (with col = 0) -        (with has-err = nil) -        (for next-char = (peek-char nil line-stream nil nil)) -        (until (null next-char)) -        (let ((flag t)) -          (cond ;; ((char= #\. next-char) -            ;;  (collect (make-token '|.| line-num col) into tokens) -            ;;  (read-char line-stream)) -            ((char= #\|  next-char) -             (read-char line-stream) -             (collect (make-token "|" line-num col :identifier) into tokens)) -            ((char= #\Space next-char) (read-char line-stream)) -            ((char= #\; next-char) ;; and not in string -             (finish)) -            ((char= #\" next-char) -             (read-char line-stream) -             (collect (make-token (read-string line-stream) -                                  line-num col) -               into tokens)) -            (t (setf flag nil))) -          (when flag -            (incf col) -            (next-iteration))) -        (for next-sym in-stream line-stream -             using #'read-preserving-whitespace) -        (multiple-value-bind (token err) -            (make-token next-sym line-num col) -          (collect token into tokens) -          (when err ;; skip line on error and continue lexing -            (setf has-err t) -            (finish)) -          (incf col (length (princ-to-string next-sym)))) -        (finally (return (values tokens has-err))))) +  (let ((*package* (find-package "KEYWORD"))) +    (iter (with line-stream = (make-string-input-stream line)) +          (with col = 0) +          (with has-err = nil) +          (for next-char = (peek-char nil line-stream nil nil)) +          (until (null next-char)) +          (let ((flag t)) +            (cond ;; ((char= #\. next-char) +              ;;  (collect (make-token '|.| line-num col) into tokens) +              ;;  (read-char line-stream)) +              ((char= #\|  next-char) +               (read-char line-stream) +               (collect (make-token :pipe line-num col :identifier) into tokens)) +              ((char= #\Space next-char) (read-char line-stream)) +              ((char= #\; next-char) ;; and not in string +               (finish)) +              ((char= #\" next-char) +               (read-char line-stream) +               (collect (make-token (read-string line-stream) +                                    line-num col) +                 into tokens)) +              (t (setf flag nil))) +            (when flag +              (incf col) +              (next-iteration))) +          (for next-sym in-stream line-stream +               using #'read-preserving-whitespace) +          (multiple-value-bind (token err) +              (make-token next-sym line-num col) +            (collect token into tokens) +            (when err ;; skip line on error and continue lexing +              (setf has-err t) +              (finish)) +            (incf col (length (princ-to-string next-sym)))) +          (finally (return (values tokens has-err))))))  (defun lex-file (file-name &optional report-errors)    (let ((has-error nil)) @@ -119,39 +120,39 @@          (let ((op (token-op token))                (op-type (getf (cdr token) :type)))            (cond ((eq :number op-type) -                 (vector-push-extend `(push-int ,op) ops)) +                 (vector-push-extend `(:push-int ,op) ops))                  ((eq :string op-type) -                 (vector-push-extend `(push-str ,(length op) ,i ,op) +                 (vector-push-extend `(:push-str ,(length op) ,i ,op)                                       ops)) -                ((string= 'ise op) -                 (push (list 'ise i) stack) -                 (vector-push-extend (list 'ise nil) ops)) -                ((string= 'yoksa op) +                ((string= :ise op) +                 (push (list :ise i) stack) +                 (vector-push-extend (list :ise nil) ops)) +                ((string= :yoksa op)                   (let ((top (pop stack))) -                   (assert (string= 'ise (car top))) +                   (assert (string= :ise (car top)))                     (setf (second (aref ops (cadr top))) i) -                   (push (list 'yoksa i) stack) -                   (vector-push-extend (list 'yoksa nil i) ops))) -                ((string= 'yap op) +                   (push (list :yoksa i) stack) +                   (vector-push-extend (list :yoksa nil i) ops))) +                ((string= :yap op)                   (let ((top (pop stack))) -                   (cond ((find (car top) (list 'yoksa 'ise)) +                   (cond ((find (car top) (list :yoksa :ise))                            (setf (second (aref ops (cadr top))) i) -                          (vector-push-extend (list 'yap i) ops)) -                         ((string= 'iken (car top)) +                          (vector-push-extend (list :yap i) ops)) +                         ((string= :iken (car top))                            (setf (second (aref ops (cadr top))) i) -                          (vector-push-extend (list 'yap i (third top)) ops)) +                          (vector-push-extend (list :yap i (third top)) ops))                           (t (error "yap cannot reference: ~a" (car top)))))) -                ((string= 'döngü op) -                 (push (list 'döngü i) stack) -                 (vector-push-extend (list 'döngü i) ops)) -                ((string= 'iken op) +                ((string= :döngü op) +                 (push (list :döngü i) stack) +                 (vector-push-extend (list :döngü i) ops)) +                ((string= :iken op)                   (let ((top (pop stack))) -                   (assert (string= 'döngü (car top))) -                   (push (list 'iken i (cadr top)) stack) -                   (vector-push-extend (list 'iken nil) ops))) +                   (assert (string= :döngü (car top))) +                   (push (list :iken i (cadr top)) stack) +                   (vector-push-extend (list :iken nil) ops)))                  ((search "syscall" (string-downcase (string op)))                   (let ((syscall-num (parse-integer (subseq (string op) 8)))) -                   (vector-push-extend (list 'syscall syscall-num) ops))) +                   (vector-push-extend (list :syscall syscall-num) ops)))                  (t (vector-push-extend (list op) ops))))          (finally (return ops)))) @@ -173,7 +174,7 @@            (let ((gen-val (gen-code op out)))              (when (and (consp gen-val) (eq :string (car gen-val)))                (push (cdr gen-val) strs)))) -         (gen-code '(exit 0) out) +         (gen-code '(:exit 0) out)           (unless (null strs)             (format out "segment .data~%")             (dolist (str strs) @@ -15,6 +15,8 @@               (compile-program (second args)))              ((string= flag "-i")               (interpret-program (make-program (second args)))) +            ((string= flag "-p") +             (format t "~a" (make-program (second args))))              ((string= flag "-t")               (run-tests))              (t (format t "~a is not a valid flag~%" flag)))))) | 
