aboutsummaryrefslogtreecommitdiff
path: root/cljcc-compiler/src/cljcc/cljcc.clj
blob: e5c1e6960a1c3bd2f088def83caf409cf00c86b5 (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
(ns cljcc.cljcc
  (:require
   [cljcc.lexer :as lexer]
   [cljcc.parser :as parser]
   [cljcc.tacky :as tacky]
   [cljcc.config :as config]
   [cljcc.analyze.core :as analyzer]
   [cljcc.compiler :as codegen]
   [cljcc.emit :as emit])
  (:gen-class))

(set! *warn-on-reflection* true)

(def valid-os-targets #{:mac :linux})
(def valid-stages #{:lex :parse :validate :tacky :codegen :emit})

(defn run
  "Compiles source input using specified compiler options.

  Parameters:
    source - Source C file.
    options - Map of compiler configuration options.

  Options map:
    :target - Map of target platform settings
      :os - Target OS #{:linux :mac}
    :stage - Which stage to generate #{:lex :parse :validate :tacky :codegen :emit}

  Returns generated AST for specified stage."
  [source & {:keys [options] :or {options {}}}]
  (let [default-options {:target {:os :linux}
                         :stage :emit}
        merged-options (merge default-options options)
        stage (:stage merged-options)
        target-os (:os (:target merged-options))
        _ (assert (stage valid-stages) "Invalid stage for compilation.")
        _ (assert (target-os valid-os-targets) "Invalid operating system.")
        _ (config/set-os target-os)
        stages [lexer/lex parser/parse
                analyzer/validate tacky/tacky-generate
                codegen/assembly emit/emit]
        stage-idx (condp = stage
                    :lex 1
                    :parse 2
                    :validate 3
                    :tacky 4
                    :codegen 5
                    :emit 6)
        stages-to-run (vec (take stage-idx stages))]
    (reduce (fn [acc f] (f acc)) source stages-to-run)))

(defn- validate-os [os]
  (let [valid-os #{"mac" "linux"}]
    (assert (valid-os os) (str "OS: " os "is not valid." "OS type is not valid. Valid os: " (vec valid-os))))
  os)

(defn- validate-stage [stage]
  (let [valid-stages #{"lex" "parse" "validate" "tacky" "codegen" "emit"}]
    (assert (valid-stages stage) (str "Stage is not valid. Valid stages: " (vec valid-stages)))
    stage))

(defn -main [& args]
  (let [[source os stage] args
        source (or source "")
        os (or os "")
        stage (or stage "")]
    (validate-os os)
    (validate-stage stage)
    (println (run source {:options {:os (keyword os)
                                    :stage (keyword stage)}}))))

(comment

  (-main "int main(void) {return 42;}" "linux" "lex"))