diff options
Diffstat (limited to 'cljcc-compiler/src/cljcc/driver.clj')
| -rw-r--r-- | cljcc-compiler/src/cljcc/driver.clj | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/cljcc-compiler/src/cljcc/driver.clj b/cljcc-compiler/src/cljcc/driver.clj new file mode 100644 index 0000000..20d2d22 --- /dev/null +++ b/cljcc-compiler/src/cljcc/driver.clj @@ -0,0 +1,139 @@ +(ns cljcc.driver + (:require + [clojure.java.io :as io] + [cljcc.compiler :as c] + [cljcc.tacky :as t] + [cljcc.lexer :as l] + [cljcc.emit :as e] + [cljcc.analyze.core :as a] + [clojure.pprint :as pp] + [cljcc.log :as log] + [cljcc.util :refer [get-os handle-sh mac-aarch64? make-file-name]] + [cljcc.parser :as p] + [clojure.string :as str])) + +(defn- validate-os [] + (let [os (get-os)] + (condp = os + :linux (log/info "Running on Linux.") + :mac (if (mac-aarch64?) + (log/info "Running on Mac ARM64.") + (log/info "Running on Mac x86_64.")) + :unsupported (throw (Exception. (str os " is not currently supported.")))))) + +(defn- remove-extension [^String filename] + (if (.contains filename ".") + (.substring filename 0 (.lastIndexOf filename ".")) + filename)) + +(defn- preprocessor-step [directory filename] + (let [input-file-path (make-file-name directory (remove-extension filename) "c") + preprocessed-file-path (make-file-name directory (remove-extension filename) "i") + output (handle-sh "gcc" "-E" "-P" input-file-path "-o" preprocessed-file-path)] + (if (= 1 (:exit output)) + (throw (Exception. ^String (:err output))) + (log/info (str "Successfully preprocessed file: " preprocessed-file-path))))) + +(defn- assemble-step [directory filename options] + (let [file-without-ext (remove-extension filename) + assembly-file (make-file-name directory file-without-ext "s") + preprocessed-file-path (make-file-name directory (remove-extension filename) "i") + file (io/file preprocessed-file-path) + source (slurp file) + assembly-ast (c/assembly-from-src source) + assembly-output (e/emit assembly-ast) + assembly-out-file-path (make-file-name directory (remove-extension filename) "s") + _ (spit assembly-out-file-path assembly-output) + output-file (if (:generate-object-file options) + (str directory "/" (str file-without-ext ".o")) + (str directory "/" file-without-ext)) + libs (str/join " " (:libs options)) + output (if (:generate-object-file options) + (handle-sh "gcc" "-c" assembly-file "-o" output-file libs) + (handle-sh "gcc" assembly-file "-o" output-file libs))] + (if (= 1 (:exit output)) + (throw (Exception. ^String (:err output))) + (log/info (str "Successfully created executable at: " output-file))))) + +(defn- parser-step [directory filename] + (let [preprocessed-file-path (make-file-name directory (remove-extension filename) "i") + file (io/file preprocessed-file-path) + source (slurp file) + ast (p/parse (l/lex source))] + (log/info "Input file is succesfully parsed.") + (pp/pprint ast))) + +(defn- semantic-analyzer-step [directory filename] + (let [preprocessed-file-path (make-file-name directory (remove-extension filename) "i") + file (io/file preprocessed-file-path) + source (slurp file) + ast (a/validate (p/parse (l/lex source)))] + (log/info "Input file is succesfully validated.") + (pp/pprint ast))) + +(defn- lexer-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 (l/lex source)] + (log/info "Input file is succesfully lexed.") + (pp/pprint output))) + +(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 (a/validate (p/parse (l/lex 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) + source (slurp file) + assembly-ast (c/assembly-from-src source)] + (log/info (str "Succesfully generated assembly ast.\n" assembly-ast)))) + +(defn- cleanup-step [directory filename] + (let [file-without-ext (remove-extension filename)] + (io/delete-file (make-file-name directory file-without-ext "i") true) + (io/delete-file (make-file-name directory file-without-ext "s") true))) + +(defn- create-steps [options directory filename] + (let [steps [(partial validate-os) + (partial preprocessor-step directory filename) + (partial lexer-step directory filename) + (partial parser-step directory filename) + (partial semantic-analyzer-step directory filename) + (partial tacky-step directory filename) + (partial compiler-step directory filename) + (partial assemble-step directory filename options)]] + (cond + (:lex options) (subvec steps 0 3) + (:parse options) (subvec steps 0 4) + (:validate options) (subvec steps 0 5) + (:tacky options) (subvec steps 0 6) + (:codegen options) (subvec steps 0 7) + :else steps))) + +(defn- run-steps [options directory filename] + (let [steps (create-steps options directory filename)] + (run! #(apply % []) steps))) + +(defn run + "Runs the compiler driver with the given input source file." + [^String file-path options] + (let [file (io/file ^String file-path) + filename (.getName file) + directory (.getParent file)] + (try + (run-steps options directory filename) + (finally + (cleanup-step directory filename))))) + +(comment + + (run "./test-programs/ex1.c" {}) + + ()) |
