aboutsummaryrefslogtreecommitdiff
path: root/src/cljcc/driver.clj
blob: ad9c3a478f4bdfea0f952a3990f76e5f9ab49610 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
(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]))

(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]
  (let [file-without-ext (remove-extension filename)
        assembly-file (make-file-name directory file-without-ext "s")
        output-file (str directory "/" file-without-ext)
        output (handle-sh "gcc" assembly-file "-o" output-file)]
    (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)]
    (if (p/parseable? (p/parse source))
      (log/info "Input file is succesfully parsed.")
      (throw (Exception. "Failed during parsing")))))

(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)
        assembled-source (c/run-compile source)
        out-file-path (make-file-name directory (remove-extension filename) "s")]
    (spit out-file-path assembled-source)
    (log/info (str "Succesfully generated assembly file.\n" assembled-source))))

(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 [base-steps [(partial validate-os)
                    (partial preprocessor-step directory filename)]
        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)]
    (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])
      :else (concat base-steps
                    [parser-step-fn compiler-step-fn assemble-step-fn cleanup-step-fn]))))

(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 "/Users/shagunagrawal/Development/c_tests/ex3.c" {})

 ,)