aboutsummaryrefslogtreecommitdiff
path: root/src/cljcc
diff options
context:
space:
mode:
Diffstat (limited to 'src/cljcc')
-rw-r--r--src/cljcc/cljcc.clj1
-rw-r--r--src/cljcc/compiler.clj40
-rw-r--r--src/cljcc/driver.clj36
-rw-r--r--src/cljcc/parser.clj19
-rw-r--r--src/cljcc/tacky.clj118
5 files changed, 168 insertions, 46 deletions
diff --git a/src/cljcc/cljcc.clj b/src/cljcc/cljcc.clj
index 8c8211d..0ebe609 100644
--- a/src/cljcc/cljcc.clj
+++ b/src/cljcc/cljcc.clj
@@ -19,6 +19,7 @@
(def cli-options
[[nil "--parse" "Runs parser. Does not emit any files."]
[nil "--codegen" "Runs compiler. Does not emit any files."]
+ [nil "--tacky" "Runs tacky generation. Does not emit any files."]
["-h" "--help"]])
(defn validate-args [args]
diff --git a/src/cljcc/compiler.clj b/src/cljcc/compiler.clj
index 9e0a9a4..eececdd 100644
--- a/src/cljcc/compiler.clj
+++ b/src/cljcc/compiler.clj
@@ -12,17 +12,17 @@
:body body})
(defn ast->compile [ast]
- (insta/transform
- {:function transform-function
- :identifier str
- :constant (comp edn/read-string str)
- :exp (fn [v]
- {:op :movl
- :src v
- :dst :eax})
+ (insta/transform
+ {:function transform-function
+ :identifier str
+ :constant (comp edn/read-string str)
+ :exp (fn [v]
+ {:op :movl
+ :src v
+ :dst :eax})
:statement (fn [_ v]
[v {:op :ret}])}
- ast))
+ ast))
(defn handle-function-name [name]
(if (= :mac (get-os))
@@ -37,8 +37,8 @@
(defn statement-fn [stmt]
(condp = (:op stmt)
- :ret (emit-instruction :ret)
- :movl (emit-instruction (:op stmt) (:src stmt) (:dst stmt))))
+ :ret (emit-instruction :ret)
+ :movl (emit-instruction (:op stmt) (:src stmt) (:dst stmt))))
(statement-fn {:op :movl :src 1 :dst :eax})
@@ -71,19 +71,17 @@
(comment
- (def ex "int main(void) {return 2;}")
+ (def ex "int main(void) {return 2;}")
- (-> ex
+ (-> ex
p/parse)
- (-> ex
+ (-> ex
p/parse
ast->compile)
- (-> ex
- p/parse
- ast->compile
- il->assembly
- join-assembly)
-
- ,)
+ (-> ex
+ p/parse
+ ast->compile
+ il->assembly
+ join-assembly))
diff --git a/src/cljcc/driver.clj b/src/cljcc/driver.clj
index e3c4822..80e4096 100644
--- a/src/cljcc/driver.clj
+++ b/src/cljcc/driver.clj
@@ -1,10 +1,12 @@
(ns cljcc.driver
(:require
- [clojure.java.io :as io]
- [cljcc.compiler :as c]
- [cljcc.log :as log]
- [cljcc.util :refer [get-os handle-sh mac-aarch64? make-file-name]]
- [cljcc.parser :as p]))
+ [clojure.java.io :as io]
+ [cljcc.compiler :as c]
+ [cljcc.tacky :as t]
+ [clojure.pprint :as pp]
+ [cljcc.log :as log]
+ [cljcc.util :refer [get-os handle-sh mac-aarch64? make-file-name]]
+ [cljcc.parser :as p]))
(defn validate-os []
(let [os (get-os)]
@@ -45,6 +47,15 @@
(log/info "Input file is succesfully parsed.")
(throw (Exception. "Failed during parsing")))))
+(defn tacky-step [directory filename]
+ (let [preprocessed-file-path (make-file-name directory (remove-extension filename) "i")
+ file (io/file preprocessed-file-path)
+ source (slurp file)
+ output (t/tacky-generate (p/parse source))]
+ (log/info (str
+ "Successfully generated Tacky IR.\n"
+ (with-out-str (pp/pprint output))))))
+
(defn compiler-step [directory filename]
(let [preprocessed-file-path (make-file-name directory (remove-extension filename) "i")
file (io/file preprocessed-file-path)
@@ -65,14 +76,13 @@
parser-step-fn (partial parser-step directory filename)
compiler-step-fn (partial compiler-step directory filename)
assemble-step-fn (partial assemble-step directory filename)
- cleanup-step-fn (partial cleanup-step directory filename)]
+ tacky-step-fn (partial tacky-step directory filename)]
(cond
- (:parse options) (concat base-steps
- [parser-step-fn cleanup-step-fn])
- (:codegen options) (concat base-steps
- [parser-step-fn compiler-step-fn cleanup-step-fn])
+ (:parse options) (concat base-steps [parser-step-fn])
+ (:tacky options) (concat base-steps [parser-step-fn tacky-step-fn])
+ (:codegen options) (concat base-steps [parser-step-fn tacky-step-fn compiler-step-fn])
:else (concat base-steps
- [parser-step-fn compiler-step-fn assemble-step-fn cleanup-step-fn]))))
+ [parser-step-fn tacky-step-fn compiler-step-fn assemble-step-fn]))))
(defn run-steps [options directory filename]
(let [steps (create-steps options directory filename)]
@@ -91,6 +101,4 @@
(comment
- (run "./test-programs/ex1.c" {})
-
- ,)
+ (run "./test-programs/ex1.c" {}))
diff --git a/src/cljcc/parser.clj b/src/cljcc/parser.clj
index d19ddd1..8852d91 100644
--- a/src/cljcc/parser.clj
+++ b/src/cljcc/parser.clj
@@ -11,10 +11,11 @@
(def c-parser
(insta/parser
"<program> = function+
- function = #'int\\b' identifier <'('> #'void\\b' <')'> <'{'> statement+ <'}'>
+ function = #'int\\b' identifier <'('> #'void\\b' <')'> <'{'> statement <'}'>
statement = #'return\\b' exp <';'>
exp = exp-prime
- <exp-prime> = constant | unop exp-prime | <'('> exp-prime <')'>
+ <exp-prime> = constant | unop-exp | <'('> exp-prime <')'>
+ unop-exp = unop exp
unop = #'-' | #'~'
identifier = #'[a-zA-Z_]\\w*\\b'
constant = #'[0-9]+\\b'
@@ -29,21 +30,17 @@
(comment
- (parse "int main(void) {return 2;}")
+ (parse "int main(void) {return 2;}")
- (parse "
+ (parse "
int main(void) {
return 2;
- return 2;
}")
- (parse "int main(void) {
+ (parse "int main(void) {
return -(((((10)))));
}")
- (parse "int main(void) {
- return --2;
+ (parse "int main(void) {
return -(((((10)))));
- }")
-
- ,)
+ }"))
diff --git a/src/cljcc/tacky.clj b/src/cljcc/tacky.clj
new file mode 100644
index 0000000..e25c87f
--- /dev/null
+++ b/src/cljcc/tacky.clj
@@ -0,0 +1,118 @@
+(ns cljcc.tacky
+ (:require
+ [clojure.pprint :as pp]
+ [instaparse.core :as insta]
+ [cljcc.parser :as p]))
+
+(def counter "Global integer counter for generating unique identifier names." (atom 0))
+
+(defn create-identifier
+ "Returns a unique identifier. Used for generating tacky variable names."
+ ([]
+ (create-identifier "tmp"))
+ ([identifier]
+ (let [n @counter
+ _ (swap! counter #(+ % 1))]
+ (str identifier "." n))))
+
+(defn variable
+ ([]
+ {:type :variable
+ :value (create-identifier)})
+ ([^String identifier]
+ {:type :variable
+ :value (create-identifier identifier)}))
+
+(defn constant [^String v]
+ {:type :constant
+ :value (Long. v)})
+
+(defn unop-operator [^String unop]
+ (condp = unop
+ "~" :complement
+ "-" :negate))
+
+(defn unary-instruction [unop src dst]
+ {:type :unary
+ :unary-operator unop
+ :dst dst
+ :src src})
+
+(defn return-instruction [val]
+ {:type :return
+ :val val})
+
+(defn exp-instructions [exp]
+ (when-let [expr (second exp)]
+ (condp = (first expr)
+ :constant {:val (constant (second expr))}
+ :unop-exp (let [inner (exp-instructions (nth expr 2))
+ dst (variable)
+ src (:val inner)
+ unop (unop-operator (second (second expr)))
+ inst (unary-instruction unop src dst)]
+ {:val dst
+ :instructions (conj (:instructions inner) inst)}))))
+
+(defn ret-instructions [exp]
+ (let [e (exp-instructions exp)
+ val (:val e)
+ instructions (:instructions e)]
+ (conj instructions (return-instruction val))))
+
+(defn statement-transform [_ret-keyword exp]
+ (reverse (ret-instructions exp)))
+
+(defn tacky-generate [ast]
+ (insta/transform {:statement statement-transform} ast))
+
+(comment
+
+ (reset! counter 0)
+
+ (pp/pprint (tacky-generate (p/parse "int main(void) {return -~-8;}")))
+
+ (pp/pprint
+ (exp-instructions [:exp [:constant "2"]]))
+
+ (pp/pprint
+ (exp-instructions [:exp [:constant "2"]]))
+
+ (pp/pprint
+ (exp-instructions [:exp [:unop-exp [:unop "-"] [:exp [:constant "2"]]]]))
+
+ (def ex-exp
+ [:exp
+ [:unop-exp
+ [:unop "-"]
+ [:exp
+ [:unop-exp
+ [:unop "~"]
+ [:exp [:unop-exp [:unop "-"] [:exp [:constant "8"]]]]]]]])
+
+ (def ex-ret
+ [:statement "return"
+ [:exp
+ [:unop-exp
+ [:unop "-"]
+ [:exp
+ [:unop-exp
+ [:unop "~"]
+ [:exp [:unop-exp [:unop "-"] [:exp [:constant "8"]]]]]]]]])
+
+ (pp/pprint
+ (exp-instructions ex-exp))
+
+ (pp/pprint
+ (ret-instructions ex-ret))
+
+ (def exprg
+ "int main(void) {
+ return -(~(-8));
+ }")
+
+ (pp/pprint (parse "int main(void) {return 2;}"))
+
+ (pp/pprint (parse exprg))
+
+ ,)