Parses command line arguments and performs the compilation process.

(ns mini-java.core
  (:require [mini-java.parser           :as    parser]
            [mini-java.static-semantics :as    static-semantics]
            [mini-java.code-gen         :as    code-gen]
            [clojure.tools.cli          :refer [parse-opts]])
  (:gen-class))
(def cli-options
  [["-d" "--directory DIR" "Destination directory for class files"
    :validate [#(.exists (clojure.java.io/file %))
               "Must be an existing directory"]
    :default "."]
   [nil "--syntax"
    "Stop after syntax checking"]
   [nil "--static-semantics"
    "Stop after static semantics checking"]
   ["-h" "--help"]])

Formats cli usage summary message.

(defn usage
  [options-summary]
  (->> ["Usage: mini-javac [options] filename"
        ""
        "Options:"
        options-summary]
       (clojure.string/join \newline)))

Formats cli parse error message.

(defn error-msg
  [errors]
  (str "The following errors occurred while parsing your command:\n\n"
       (clojure.string/join \newline errors)))

Exits the program, with an optional exit status and message. Default exit status is 0.

(defn exit
  ([]
     (System/exit 0))
  ([status]
     (System/exit status))
  ([status msg]
     (println msg)
     (System/exit status)))
(defn errors-occured [n]
  (str n " error"
       (if (= 1 n)  "s")
       " occurred."))

Parse the command line arguments and perform the compilation.

(defn -main
  [& args]
  (let [{:keys [options arguments errors summary]}
        (parse-opts args cli-options)]
    ;; check for anything which might cause the program to exit before
    ;; parsing the source file
    (cond
     ;; print help message
     (:help options) (exit 0 (usage summary))
     ;; only one positional argument is expected
     (not= (count arguments) 1) (exit 1 (usage summary))
     ;; errors in parsing command line options
     errors (exit 1 (error-msg errors)))
    ;; begin compilation process
    (let [source-file (first arguments)
          ;; parse AST from source file
          [ast parser errors] (parser/mini-java source-file)]
      ;; exit if there are syntax errors
      (when (pos? errors)
        (exit 1 (errors-occured errors)))
      ;; exit if only syntax checking is requested
      (when (:syntax options)
        (exit 0))
      ;; perform static semantics checking
      (let [[class-table errors]
            (static-semantics/class-table ast parser)]
        ;; exit if there are semantic errors
        (when-not (zero? errors)
          (exit 1 (errors-occured errors)))
        ;; exit if only static semantics checking is requested
        (when (:static-semantics options)
          (exit 0))
        ;; generate bytecode and write to files in the given directory
        (code-gen/write-classes class-table
                                (:directory options)))))
  nil)