Begin C code generation.
This commit is contained in:
157
assembly.lisp
157
assembly.lisp
@@ -1,7 +1,5 @@
|
|||||||
(in-package :cl-forth)
|
(in-package :cl-forth)
|
||||||
|
|
||||||
(defparameter *operations* (make-hash-table :test 'equal))
|
|
||||||
|
|
||||||
(defparameter *psuedo-identifiers*
|
(defparameter *psuedo-identifiers*
|
||||||
'(syscall-1 syscall-2 syscall-3 syscall-4 syscall-5 syscall-6)
|
'(syscall-1 syscall-2 syscall-3 syscall-4 syscall-5 syscall-6)
|
||||||
"These do not map to operations directly, but are valid to lexer")
|
"These do not map to operations directly, but are valid to lexer")
|
||||||
@@ -122,7 +120,7 @@
|
|||||||
(reverse cur)))
|
(reverse cur)))
|
||||||
acc))))))
|
acc))))))
|
||||||
|
|
||||||
(defun expand-group (group out-stream &key (indent 4))
|
(defun expand-nasm-group (group out-stream &key (indent 4))
|
||||||
(destructuring-bind (syntax-type . forms) group
|
(destructuring-bind (syntax-type . forms) group
|
||||||
(case syntax-type
|
(case syntax-type
|
||||||
(:stack `((defop-format ,out-stream ,indent
|
(:stack `((defop-format ,out-stream ,indent
|
||||||
@@ -140,14 +138,27 @@
|
|||||||
forms)))))))
|
forms)))))))
|
||||||
|
|
||||||
(defun expand-nasm (out-stream indent body)
|
(defun expand-nasm (out-stream indent body)
|
||||||
(mapcan #'(lambda (group) (expand-group group out-stream
|
(mapcan #'(lambda (group) (expand-nasm-group group out-stream
|
||||||
:indent indent))
|
:indent indent))
|
||||||
(group-by-syntax body)))
|
(group-by-syntax body)))
|
||||||
|
|
||||||
|
(defun expand-c-group (group out-stream &key (indent 4))
|
||||||
|
(destructuring-bind (syntax-type . forms) group
|
||||||
|
(case syntax-type
|
||||||
|
((:stack :stack-extended) `((defop-format ,out-stream ,indent
|
||||||
|
,(cons 'list
|
||||||
|
(c-stack->string (car forms))))))
|
||||||
|
(:string `((defop-format ,out-stream ,indent
|
||||||
|
,(normalize-op-list forms)))))))
|
||||||
|
|
||||||
|
(defun expand-c (out-stream indent body)
|
||||||
|
(mapcan #'(lambda (group) (expand-c-group group out-stream :indent indent))
|
||||||
|
(group-by-syntax body)))
|
||||||
|
|
||||||
(defun expand-for-target (target out-stream body &optional (indent 4))
|
(defun expand-for-target (target out-stream body &optional (indent 4))
|
||||||
(case target
|
(case target
|
||||||
(:nasm (expand-nasm out-stream indent body))
|
(:nasm (expand-nasm out-stream indent body))
|
||||||
(:c '(nil))))
|
(:c (expand-c out-stream indent body))))
|
||||||
|
|
||||||
(defun expand-method (target out-stream indent op-name args body)
|
(defun expand-method (target out-stream indent op-name args body)
|
||||||
(with-gensyms (_op _args _target)
|
(with-gensyms (_op _args _target)
|
||||||
@@ -159,12 +170,67 @@
|
|||||||
,@(if (null args)
|
,@(if (null args)
|
||||||
(expand-for-target target out-stream body indent)
|
(expand-for-target target out-stream body indent)
|
||||||
`((destructuring-bind ,args ,_args
|
`((destructuring-bind ,args ,_args
|
||||||
,@(expand-for-target target out-stream body indent))))))))
|
,@(expand-for-target target out-stream body indent)))))))
|
||||||
|
|
||||||
|
;;hack, because there is no cl in C only nasm
|
||||||
|
(defun not-cl (sym)
|
||||||
|
(if (eq 'cl sym)
|
||||||
|
'rcx
|
||||||
|
sym))
|
||||||
|
|
||||||
|
(defparameter *c-ops* '((:add "+")
|
||||||
|
(:sub "-")
|
||||||
|
(:shl "<<")
|
||||||
|
(:shr ">>")
|
||||||
|
(:or "|")
|
||||||
|
(:and "&")
|
||||||
|
(:< "<")
|
||||||
|
(:> ">")
|
||||||
|
(:= "==")))
|
||||||
|
|
||||||
|
(defun c-stack->string (stack)
|
||||||
|
(multiple-value-bind (prev next) (split-stack stack)
|
||||||
|
(let ((*print-case* :downcase))
|
||||||
|
(append (iter (for x in (reverse prev))
|
||||||
|
(collect (format nil "~a = pop(&stack);" x)))
|
||||||
|
(iter (for x in next)
|
||||||
|
(if (and (consp x) (assoc (car x) *c-ops*))
|
||||||
|
(destructuring-bind (op arg1 arg2 . conds) x
|
||||||
|
(let ((opstr (cadr (assoc op *c-ops*))))
|
||||||
|
(if (null conds)
|
||||||
|
(collect (format nil "push(&stack, ~a ~a ~a);"
|
||||||
|
arg1 opstr (not-cl arg2)))
|
||||||
|
(collect (format nil
|
||||||
|
"push(&stack, (~a ~a ~a) ? ~a : ~a);"
|
||||||
|
arg1 opstr (not-cl arg2)
|
||||||
|
(getf conds :ise)
|
||||||
|
(getf conds :değilse))))))
|
||||||
|
(collect (format nil "push(&stack, ~a);" x)))))))))
|
||||||
|
|
||||||
|
(defun comment-safe-str (str)
|
||||||
|
"Handle newlines for comment"
|
||||||
|
(with-output-to-string (new-str)
|
||||||
|
(iter (for ch in-string str with-index i)
|
||||||
|
(cond ((> i 10)
|
||||||
|
(princ "..." new-str)
|
||||||
|
(finish))
|
||||||
|
((char= #\Newline ch)
|
||||||
|
(princ "\\n" new-str))
|
||||||
|
(t (write-char ch new-str))))))
|
||||||
|
|
||||||
(defgeneric write-op (target stream op args)
|
(defgeneric write-op (target stream op args)
|
||||||
(:documentation "Generate code for OP and ARGS and write it to STREAM according to the TARGET"))
|
(:documentation "Generate code for OP and ARGS and write it to STREAM according to the TARGET. WRITE-OP methods are defined by the DEFOP macro.")
|
||||||
|
(:method :before ((target (eql :nasm)) stream op args)
|
||||||
|
(format stream " ;; -- ~s --~%"
|
||||||
|
(mapcar (lambda (x) (if (stringp x) (comment-safe-str x) x))
|
||||||
|
(cons op args))))
|
||||||
|
(:method :before ((target (eql :c)) stream op args)
|
||||||
|
(format stream "~% /* ~s */~%"
|
||||||
|
;; comment-safe is probably not necessary with multiline comments
|
||||||
|
(mapcar (lambda (x) (if (stringp x) (comment-safe-str x) x))
|
||||||
|
(cons op args)))))
|
||||||
|
|
||||||
(defmacro defop (op-name+args (&key (indent 4) (lex t) (targets :nasm)
|
(defmacro defop (op-name+args (&key (indent 4) (lex t) (targets *targets*)
|
||||||
(as nil))
|
(as nil))
|
||||||
&body body)
|
&body body)
|
||||||
(declare (ignorable indent))
|
(declare (ignorable indent))
|
||||||
@@ -173,9 +239,7 @@
|
|||||||
`(progn
|
`(progn
|
||||||
,@(unless (null lex)
|
,@(unless (null lex)
|
||||||
`((push ',op-name *identifiers*)))
|
`((push ',op-name *identifiers*)))
|
||||||
,@(iter (for target in (if (eq :all targets)
|
,@(iter (for target in (mklist targets))
|
||||||
*targets*
|
|
||||||
(mklist targets)))
|
|
||||||
(collect (expand-method target out-stream indent
|
(collect (expand-method target out-stream indent
|
||||||
(if (null as) op-name as)
|
(if (null as) op-name as)
|
||||||
args body)))))))
|
args body)))))))
|
||||||
@@ -207,9 +271,6 @@
|
|||||||
(defop > ()
|
(defop > ()
|
||||||
(rax rbx -- (:> rax rbx :ise 1 :değilse 0)))
|
(rax rbx -- (:> rax rbx :ise 1 :değilse 0)))
|
||||||
|
|
||||||
(defop bel ()
|
|
||||||
( -- bel))
|
|
||||||
|
|
||||||
(defop üst ()
|
(defop üst ()
|
||||||
(rbx rax -- rbx rax rbx))
|
(rbx rax -- rbx rax rbx))
|
||||||
|
|
||||||
@@ -231,91 +292,70 @@
|
|||||||
(defop & ()
|
(defop & ()
|
||||||
(rbx rax -- (:and rbx rax)))
|
(rbx rax -- (:and rbx rax)))
|
||||||
|
|
||||||
(defop oku ()
|
|
||||||
|
|
||||||
|
;;; NASM operations
|
||||||
|
(defop bel (:targets :nasm)
|
||||||
|
( -- bel))
|
||||||
|
|
||||||
|
(defop oku (:targets :nasm)
|
||||||
(rax -- )
|
(rax -- )
|
||||||
(:xor rbx rbx)
|
(:xor rbx rbx)
|
||||||
(:mov bl [rax])
|
(:mov bl [rax])
|
||||||
( -- rbx))
|
( -- rbx))
|
||||||
|
|
||||||
(defop yaz ()
|
(defop yaz (:targets :nasm)
|
||||||
(rax rbx -- )
|
(rax rbx -- )
|
||||||
(:mov [rax] bl))
|
(:mov [rax] bl))
|
||||||
|
|
||||||
;; ( -- a)
|
;; ( -- a)
|
||||||
(defop (push-int a) (:lex nil)
|
(defop (push-int a) (:lex nil :targets :nasm)
|
||||||
("push ~d" a))
|
("push ~d" a))
|
||||||
|
|
||||||
(defop (push-str len addr str) (:lex nil)
|
(defop (push-str len addr str) (:lex nil :targets :nasm)
|
||||||
(progn (:write ("push ~d" len)
|
(progn (:write ("push ~d" len)
|
||||||
("push str_~d" addr))
|
("push str_~d" addr))
|
||||||
(list :string addr str)))
|
(list :string addr str)))
|
||||||
|
|
||||||
(defop dump ()
|
(defop dump (:targets :nasm)
|
||||||
"pop rdi"
|
"pop rdi"
|
||||||
"call dump")
|
"call dump")
|
||||||
|
|
||||||
(defop (exit code) (:lex nil)
|
(defop (exit code) (:lex nil :targets :nasm)
|
||||||
"mov rax, 60"
|
"mov rax, 60"
|
||||||
("mov rdi, ~a" code)
|
("mov rdi, ~a" code)
|
||||||
"syscall")
|
"syscall")
|
||||||
|
|
||||||
(defop (ise label-num) ()
|
(defop (ise label-num) (:targets :nasm)
|
||||||
"pop rax"
|
"pop rax"
|
||||||
"test rax, rax"
|
"test rax, rax"
|
||||||
("jz et_~a" label-num))
|
("jz et_~a" label-num))
|
||||||
|
|
||||||
(defop (yoksa yap-num ise-num) (:indent 0)
|
(defop (yoksa yap-num ise-num) (:indent 0 :targets :nasm)
|
||||||
(" jmp et_~a" yap-num)
|
(" jmp et_~a" yap-num)
|
||||||
("et_~a:" ise-num))
|
("et_~a:" ise-num))
|
||||||
|
|
||||||
(defop (yap label-num &optional döngü-num) (:indent 0)
|
(defop (yap label-num &optional döngü-num) (:indent 0 :targets :nasm)
|
||||||
(if (null döngü-num)
|
(if (null döngü-num)
|
||||||
(:write ("et_~a:" label-num))
|
(:write ("et_~a:" label-num))
|
||||||
(:write (" jmp et_~a" döngü-num)
|
(:write (" jmp et_~a" döngü-num)
|
||||||
("et_~a:" label-num))))
|
("et_~a:" label-num))))
|
||||||
|
|
||||||
(defop (iken label-num) ()
|
(defop (iken label-num) (:targets :nasm)
|
||||||
"pop rax"
|
"pop rax"
|
||||||
"test rax, rax"
|
"test rax, rax"
|
||||||
("jz et_~a" label-num))
|
("jz et_~a" label-num))
|
||||||
|
|
||||||
(defop (döngü label-num) (:indent 0)
|
(defop (döngü label-num) (:indent 0 :targets :nasm)
|
||||||
("et_~a:" label-num))
|
("et_~a:" label-num))
|
||||||
|
|
||||||
(defop (syscall num) (:lex nil)
|
(defop (syscall num) (:lex nil :targets :nasm)
|
||||||
(iter (with call-regs = #("rdi" "rsi" "rdx" "r10" "r8" "r9"))
|
(iter (with call-regs = #("rdi" "rsi" "rdx" "r10" "r8" "r9"))
|
||||||
(initially (:write "pop rax"))
|
(initially (:write "pop rax"))
|
||||||
(for i from (- num 1) downto 0)
|
(for i from (- num 1) downto 0)
|
||||||
(:write ("pop ~a" (aref call-regs i)))
|
(:write ("pop ~a" (aref call-regs i)))
|
||||||
(finally (:write "syscall"))))
|
(finally (:write "syscall"))))
|
||||||
|
|
||||||
(defun comment-safe-str (str)
|
|
||||||
"Handle newlines for asm comment"
|
|
||||||
(with-output-to-string (new-str)
|
|
||||||
(iter (for ch in-string str with-index i)
|
|
||||||
(cond ((> i 10)
|
|
||||||
(princ "..." new-str)
|
|
||||||
(finish))
|
|
||||||
((char= #\Newline ch)
|
|
||||||
(princ "\\n" new-str))
|
|
||||||
(t (write-char ch new-str))))))
|
|
||||||
|
|
||||||
(defun gen-header (op str)
|
|
||||||
(format str " ;; -- ~s --~%"
|
|
||||||
(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)
|
|
||||||
(gen-header op str)
|
|
||||||
(write-op :nasm str (car op) (cdr op)))
|
|
||||||
|
|
||||||
(defun gen-dump (str)
|
(defun gen-dump (str)
|
||||||
(format str "~{~a~%~}"
|
(format str "~{~a~%~}"
|
||||||
'("dump:"
|
'("dump:"
|
||||||
@@ -351,3 +391,14 @@
|
|||||||
" syscall"
|
" syscall"
|
||||||
" add rsp, 40"
|
" add rsp, 40"
|
||||||
" ret")))
|
" ret")))
|
||||||
|
|
||||||
|
|
||||||
|
;;; C operations
|
||||||
|
(defop (push-int a) (:lex nil :targets :c)
|
||||||
|
("push(&stack, ~d);" a))
|
||||||
|
|
||||||
|
;; (defop (push-str a) (:lex nil :targets :c)
|
||||||
|
;; ("push(&stack, ~d);" a))
|
||||||
|
|
||||||
|
(defop dump (:targets :c)
|
||||||
|
("printf(\"%d\\n\", pop(&stack));"))
|
||||||
|
|||||||
@@ -164,17 +164,19 @@
|
|||||||
(parse-tokens tokens)))
|
(parse-tokens tokens)))
|
||||||
|
|
||||||
;;; COMPILER
|
;;; COMPILER
|
||||||
(defun write-program (program out &key (mem-cap 640000))
|
;;(defgeneric write-program (target program stream))
|
||||||
|
(defmethod write-program ((target (eql :nasm)) program out
|
||||||
|
&key (mem-cap 640000))
|
||||||
(format out "~a~%" "segment .text")
|
(format out "~a~%" "segment .text")
|
||||||
(gen-dump out)
|
(gen-dump out)
|
||||||
(format out "~{~a~%~}" '("global _start"
|
(format out "~{~a~%~}" '("global _start"
|
||||||
"_start:"))
|
"_start:"))
|
||||||
(let ((strs nil))
|
(let ((strs nil))
|
||||||
(iter (for op in-sequence program)
|
(iter (for op in-sequence program)
|
||||||
(let ((gen-val (gen-code op out)))
|
(let ((gen-val (write-op target out (car op) (cdr op))))
|
||||||
(when (and (consp gen-val) (eq :string (car gen-val)))
|
(when (and (consp gen-val) (eq :string (car gen-val)))
|
||||||
(push (cdr gen-val) strs))))
|
(push (cdr gen-val) strs))))
|
||||||
(gen-code '(:exit 0) out)
|
(write-op target out :exit '(0))
|
||||||
(unless (null strs)
|
(unless (null strs)
|
||||||
(format out "segment .data~%")
|
(format out "segment .data~%")
|
||||||
(dolist (str strs)
|
(dolist (str strs)
|
||||||
@@ -184,18 +186,59 @@
|
|||||||
(format out "~a~%" "segment .bss")
|
(format out "~a~%" "segment .bss")
|
||||||
(format out "~a ~a~%" "bel: resb" mem-cap))
|
(format out "~a ~a~%" "bel: resb" mem-cap))
|
||||||
|
|
||||||
(defun generate-program (program &key (path "output.asm") (compile nil)
|
(defmethod write-program ((target (eql :c)) program out &key (mem-cap 640000))
|
||||||
(mem-cap 640000) (silence nil))
|
(declare (ignore mem-cap))
|
||||||
(with-open-file (out path :direction :output
|
(format out
|
||||||
:if-exists :supersede)
|
"#include <stdio.h>
|
||||||
(write-program program out :mem-cap mem-cap))
|
|
||||||
|
struct Stack {
|
||||||
|
int content[100];
|
||||||
|
int i;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct Stack Stack;
|
||||||
|
|
||||||
|
void push(Stack* stack, int val){
|
||||||
|
stack->content[stack->i] = val;
|
||||||
|
stack->i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pop(Stack* stack){
|
||||||
|
stack->i -= 1;
|
||||||
|
return stack->content[stack->i];
|
||||||
|
}
|
||||||
|
|
||||||
|
Stack stack;
|
||||||
|
int rax, rbx;
|
||||||
|
|
||||||
|
int main(void){
|
||||||
|
stack.i = 0;
|
||||||
|
")
|
||||||
|
(iter (for op in-sequence program)
|
||||||
|
(write-op target out (car op) (cdr op)))
|
||||||
|
(format out " return 0;~%}~%"))
|
||||||
|
|
||||||
|
(defun generate-program (program
|
||||||
|
&key (path "output.asm") (compile nil)
|
||||||
|
(mem-cap 640000) (silence nil) (target :nasm))
|
||||||
|
(with-open-file (out path :direction :output :if-exists :supersede)
|
||||||
|
(write-program target program out :mem-cap mem-cap))
|
||||||
(when compile
|
(when compile
|
||||||
|
(compile-program target path silence)))
|
||||||
|
|
||||||
|
(defgeneric compile-program (target path silence))
|
||||||
|
(setf (documentation #'compile-program 'function)
|
||||||
|
(format nil "Produces the executable from source code, targets are ~a"
|
||||||
|
*targets*))
|
||||||
|
|
||||||
|
(defmethod compile-program ((target (eql :nasm)) path silence)
|
||||||
(run `("nasm" "-felf64" ,path) :output t :silence silence)
|
(run `("nasm" "-felf64" ,path) :output t :silence silence)
|
||||||
(let ((name (first (uiop:split-string path :separator '(#\.)))))
|
(let ((name (first (uiop:split-string path :separator '(#\.)))))
|
||||||
(run `("ld" "-o" ,name ,(concatenate 'string name ".o"))
|
(run `("ld" "-o" ,name ,(concatenate 'string name ".o"))
|
||||||
:output t :silence silence))))
|
:output t :silence silence)))
|
||||||
|
|
||||||
|
(defmethod compile-program ((target (eql :c)) path silence)
|
||||||
|
(run `("gcc" ,path) :output t :silence silence))
|
||||||
|
|
||||||
(defun compile-program (path)
|
|
||||||
(generate-program (make-program path) :compile t))
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
;; (let ((program (prog-from-tokens tokens)))
|
;; (let ((program (prog-from-tokens tokens)))
|
||||||
;; (format t "~s~%" program)
|
;; (format t "~s~%" program)
|
||||||
;; (generate-program program :compile t)))
|
;; (generate-program program :compile t)))
|
||||||
(compile-program (second args)))
|
(generate-program (make-program (second args)) :compile t))
|
||||||
((string= flag "-i")
|
((string= flag "-i")
|
||||||
(interpret-program (make-program (second args))))
|
(interpret-program (make-program (second args))))
|
||||||
((string= flag "-p")
|
((string= flag "-p")
|
||||||
|
|||||||
Reference in New Issue
Block a user