aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--deps.edn1
-rw-r--r--src/cljcc/cljcc.clj40
-rw-r--r--src/cljcc/driver.clj2
-rw-r--r--src/cljcc/log.clj29
-rw-r--r--src/cljcc/parser.clj3
-rw-r--r--src/cljcc/util.clj9
6 files changed, 73 insertions, 11 deletions
diff --git a/deps.edn b/deps.edn
index e6b7e08..c0a34f4 100644
--- a/deps.edn
+++ b/deps.edn
@@ -1,5 +1,6 @@
{:paths ["src" "resources"]
:deps {org.clojure/clojure {:mvn/version "1.11.1"}
+ org.clojure/tools.cli {:mvn/version "1.1.230"}
instaparse/instaparse {:mvn/version "1.5.0"}
com.github.clj-easy/graal-build-time {:mvn/version "1.0.5"}}
:aliases
diff --git a/src/cljcc/cljcc.clj b/src/cljcc/cljcc.clj
index e687ae9..6708173 100644
--- a/src/cljcc/cljcc.clj
+++ b/src/cljcc/cljcc.clj
@@ -1,18 +1,42 @@
(ns cljcc.cljcc
(:require
+ [clojure.tools.cli :refer [parse-opts]]
+ [clojure.string :as string]
+ [cljcc.log :as log]
+ [cljcc.util :refer [exit]]
[cljcc.driver :as d])
(:gen-class))
(set! *warn-on-reflection* true)
+(defn usage [options-summary]
+ (->>
+ ["Usage: ./cljcc path/to/file.c [options]"
+ ""
+ "Options:"
+ options-summary]
+ (string/join \newline)))
+
+(def cli-options
+ [[nil "--parse" "Runs parser. Does not emit any files."]
+ [nil "--codegen" "Runs compiler. Does not emit any files."]
+ ["-h" "--help"]])
+
+(defn validate-args [args]
+ (let [{:keys [options arguments summary]} (parse-opts args cli-options)]
+ (cond
+ (:help options) {:exit-message (usage summary) :ok? true}
+ (= 1 (count arguments)) {:file-path (first arguments)
+ :options options}
+ :else {:exit-message (usage summary)})))
+
(defn -main
"Main entrypoint for cljcc compiler."
[& args]
- (let [input-file-path (first args)]
- (try
- (d/run input-file-path)
- (catch Exception e
- (println "Error: " (.getMessage e))
- (System/exit 1))
- (finally
- (System/exit 0)))))
+ (let [{:keys [file-path exit-message ok? options]} (validate-args args)]
+ (if exit-message
+ (exit (if ok? 0 1) exit-message)
+ (try
+ (d/run file-path options)
+ (catch Exception e (exit 1 (.getMessage e)))
+ (finally (exit 0 "Succesfully ran compiler."))))))
diff --git a/src/cljcc/driver.clj b/src/cljcc/driver.clj
index dede930..a06c9a3 100644
--- a/src/cljcc/driver.clj
+++ b/src/cljcc/driver.clj
@@ -62,7 +62,7 @@
(defn run
"Runs the compiler driver with the given input source file."
- [^String file-path]
+ [^String file-path options]
(let [file (io/file ^String file-path)
filename (.getName file)
directory (.getParent file)]
diff --git a/src/cljcc/log.clj b/src/cljcc/log.clj
new file mode 100644
index 0000000..394e4a4
--- /dev/null
+++ b/src/cljcc/log.clj
@@ -0,0 +1,29 @@
+(ns cljcc.log
+ (:require [clojure.string :as str]
+ [cljcc.log :as log]))
+
+(def ^:private log-colors
+ {:debug "\u001b[36m" ; Cyan
+ :info "\u001b[32m" ; Green
+ :warn "\u001b[33m" ; Yellow
+ :error "\u001b[31m" ; Red
+ :reset "\u001b[0m"}) ; Reset color
+
+(def reset-color (get log-colors :reset))
+
+(defn- log-message [level message]
+ (let [color (get log-colors level)
+ formatted-message (str color "[" (str/upper-case (name level)) "] " message reset-color)]
+ (println formatted-message)))
+
+(defn debug [msg]
+ (log-message :debug msg))
+
+(defn info [msg]
+ (log-message :info msg))
+
+(defn warn [msg]
+ (log-message :warn msg))
+
+(defn error [msg]
+ (log-message :error msg))
diff --git a/src/cljcc/parser.clj b/src/cljcc/parser.clj
index 860fcd5..a4d7492 100644
--- a/src/cljcc/parser.clj
+++ b/src/cljcc/parser.clj
@@ -11,7 +11,8 @@
"<program> = function+
function = #'int\\b' identifier <'('> #'void\\b' <')'> <'{'> statement <'}'>
statement = #'return\\b' exp <';'>
- exp = constant
+ exp = constant | unop exp | <'('> exp <')'>
+ unop = #'-\\b' | #'~\\b'
identifier = #'[a-zA-Z_]\\w*\\b'
constant = #'[0-9]+\\b'
keyword = #'int\\b' | #'return\\b' | #'void\\b'"
diff --git a/src/cljcc/util.clj b/src/cljcc/util.clj
index 1087029..81419e4 100644
--- a/src/cljcc/util.clj
+++ b/src/cljcc/util.clj
@@ -1,5 +1,6 @@
(ns cljcc.util
- (:require [clojure.java.shell :refer [sh]]))
+ (:require [clojure.java.shell :refer [sh]]
+ [cljcc.log :as log]))
(defn get-os []
(let [os-name (.toLowerCase (System/getProperty "os.name"))]
@@ -17,3 +18,9 @@
(if (mac-aarch64?)
(apply sh "arch" "-x86_64" command args)
(apply sh command args)))
+
+(defn exit [status msg]
+ (if (= status 0)
+ (log/info msg)
+ (log/error msg))
+ (System/exit status))