blob: 14b9e18f1b79deb003ac5d12b90332238e50381c (
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
(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.analyzer :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/generate-assembly source)
assembly-output (e/emit assembly-ast)
assembly-out-file-path (make-file-name directory (remove-extension filename) "s")
_ (println assembly-output)
_ (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))
output (if (:generate-object-file options)
(handle-sh "gcc" "-c" assembly-file "-o" output-file)
(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)
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/generate-assembly 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" {}))
|