diff options
Diffstat (limited to 'src/cljcc')
| -rw-r--r-- | src/cljcc/cljcc.clj | 16 | ||||
| -rw-r--r-- | src/cljcc/parser.clj | 105 | ||||
| -rw-r--r-- | src/cljcc/schema.clj | 16 | ||||
| -rw-r--r-- | src/cljcc/token.clj | 6 | ||||
| -rw-r--r-- | src/cljcc/util.clj | 34 |
5 files changed, 121 insertions, 56 deletions
diff --git a/src/cljcc/cljcc.clj b/src/cljcc/cljcc.clj index 3aeea48..e36658e 100644 --- a/src/cljcc/cljcc.clj +++ b/src/cljcc/cljcc.clj @@ -45,3 +45,19 @@ (exit 0 "Successfully executed.") (catch Exception e (exit 1 (ex-message e) e)))))) + +(comment + + (require '[io.github.humbleui.ui :as ui]) + + (ui/defcomp app [] + [ui/center + [ui/label "Hello, world"]]) + + (defn -main [& args] + (ui/start-app! + (ui/window #'app))) + + (-main) + + ()) diff --git a/src/cljcc/parser.clj b/src/cljcc/parser.clj index 3cfaa9d..37d1c39 100644 --- a/src/cljcc/parser.clj +++ b/src/cljcc/parser.clj @@ -3,14 +3,19 @@ [cljcc.lexer :as l] [cljcc.token :as t] [malli.core :as m] + [clojure.set :refer [union]] [malli.dev.pretty :as pretty] - [clojure.math :refer [pow]] [cljcc.schema :as s] [cljcc.exception :as exc] - [clojure.string :as str])) + [cljcc.util :as u])) (declare parse parse-exp parse-statement parse-block expect parse-declaration parse-variable-declaration) +(set! *warn-on-reflection* true) + +(def valid-declaration-starts + (union t/type-specifier-keywords t/storage-specifier-keywords)) + (defn- parse-repeatedly "Repeatedly runs given parse function on input until end-kind encountered. @@ -98,12 +103,25 @@ :right r}) (defn- parse-type [specifiers] - (condp = (mapv :specifier-type specifiers) - [:int] :int - [:long] :long - [:int :long] :long - [:long :int] :long - (exc/parser-error "Invalid specifiers" specifiers))) + (let [specifiers (mapv :specifier-type specifiers) + has-duplicates? (fn [coll] (some (fn [[_ c]] (> c 1)) (frequencies coll))) + spec-set (set specifiers)] + (cond + (has-duplicates? specifiers) (exc/parser-error "Invalid specifiers" {:specifiers specifiers}) + (empty? specifiers) (exc/parser-error "Invalid specifiers" {:specifiers specifiers}) + (and (spec-set :signed) + (spec-set :unsigned)) (exc/parser-error "Invalid specifiers" {:specifiers specifiers}) + (and (spec-set :unsigned) + (spec-set :long)) :ulong + (spec-set :unsigned) :uint + (spec-set :long) :long + :else :int))) + +(comment + + (parse-type '(:long :int :int :signed :unsigned)) + + ()) (defn specifier-node [{:keys [kind] :as token}] (let [specifier-type (condp = kind @@ -111,17 +129,19 @@ :kw-long :long :kw-static :static :kw-extern :extern + :kw-unsigned :unsigned + :kw-signed :signed (exc/parser-error "Parser Error. Invalid specifier." {:specifier-token token}))] {:type :specifier :specifier-type specifier-type})) (defn- parse-type-specifier [[{:keys [kind] :as token} & rst]] - (if-not (contains? #{:kw-int :kw-long} kind) + (if-not (t/type-specifier-keywords kind) (exc/parser-error "Invalid token for type specifier" {:token token}) [(specifier-node token) rst])) (defn- parse-specifier [[{:keys [kind] :as token} & rst]] - (if-not (contains? #{:kw-int :kw-long :kw-static :kw-extern} kind) + (if-not (valid-declaration-starts kind) (exc/parser-error "Invalid token for specifier" {:token token}) [(specifier-node token) rst])) @@ -135,18 +155,39 @@ [_ tokens] (expect :right-paren tokens)] [(into [e-node] (vec rest-arguments)) tokens])) -(defn- parse-const - "Expects a stringified number." - [v] - (let [long? (or (= \l (last v)) - (= \L (last v))) - n (if long? - (Long/parseLong (str/join (subvec (vec v) 0 (dec (count v))))) - (Long/parseLong v)) - int-range? (and (not long?) - (<= n (- (long (pow 2 31)) 1)))] - {:type (if int-range? :int :long) - :value n})) +(defn- parse-signed-const [v] + (let [n (re-find #"[0-9]+" v) + long? (u/matches-regex u/signed-long-re v) + in-long-range? (try (Long/parseLong n) (catch Exception _e false)) + in-int-range? (<= (Long/parseLong n) Integer/MAX_VALUE) + _ (when (not in-long-range?) + (exc/parser-error "Constant is too large to represent in int or long." {:number v}))] + (if (and (not long?) in-int-range?) + {:type :int + :value (Long/parseLong n)} + {:type :long + :value (Long/parseLong n)}))) + +(defn- parse-unsigned-const [v] + (let [n (re-find #"[0-9]+" v) + ulong? (u/matches-regex u/unsigned-long-re v) + in-ulong-range? (try (Long/parseUnsignedLong n) (catch Exception _e false)) + in-uint-range? (<= (Long/compareUnsigned (Long/parseUnsignedLong n) (Long/parseUnsignedLong "4294967295")) 0) + _ (when (not in-ulong-range?) + (exc/parser-error "Constant is too large to represent in unsigned int or unsigned long." {:number v}))] + (if (and (not ulong?) in-uint-range?) + {:type :uint + :value (Long/parseUnsignedLong n)} + {:type :ulong + :value (Long/parseUnsignedLong n)}))) + +(defn- parse-const [^String v] + (cond + (or (u/matches-regex u/unsigned-long-re v) + (u/matches-regex u/unsigned-int-re v)) (parse-unsigned-const v) + (or (u/matches-regex u/signed-long-re v) + (u/matches-regex u/signed-int-re v)) (parse-signed-const v) + :else (exc/parser-error "Invalid constant." {:constant v}))) (defn- parse-factor [[{kind :kind :as token} :as tokens]] (cond @@ -155,7 +196,7 @@ [e rst] (parse-factor (rest tokens))] [(unary-exp-node op e) rst]) (= kind :left-paren) (let [next-token-kind (:kind (first (rest tokens))) - type-specifier? (contains? #{:kw-int :kw-long} next-token-kind)] + type-specifier? (t/type-specifier-keywords next-token-kind)] (if type-specifier? (let [[specifiers tokens] (parse-repeatedly (rest tokens) parse-type-specifier :right-paren) ptype (parse-type specifiers) @@ -175,7 +216,7 @@ (let [[arguments tokens] (parse-argument-list tokens)] [(function-call-exp-node f-name arguments) tokens]))) [(variable-exp-node (:literal token)) (rest tokens)]) - :else (throw (ex-info "Parser Error. Malformed token." {:token token})))) + :else (exc/parser-error "Invalid token to parse factor." {:token token}))) (defn- parse-exp ([tokens] @@ -316,7 +357,7 @@ [(do-while-statement-node e s) tokens])) (defn- parse-for-init-statement [[{kind :kind} :as tokens]] - (if (contains? #{:kw-int :kw-static :kw-long :kw-extern} kind) + (if (valid-declaration-starts kind) (parse-declaration tokens) (parse-optional-expression tokens parse-exp :semicolon))) @@ -447,7 +488,7 @@ :else (throw (ex-info "Parser error. Not able to parse variable declaration." {}))))) (defn- parse-type-and-storage-class [specifiers] - (let [valid-types #{:int :long} + (let [valid-types #{:int :long :signed :unsigned} {types true, storage-classes false} (group-by #(contains? valid-types (:specifier-type %)) specifiers) type-specifier (parse-type types) storage-class (if (> (count storage-classes) 1) @@ -465,7 +506,7 @@ (parse-variable-declaration type-specifier storage-class tokens)))) (defn- parse-block-item [[token :as tokens]] - (if (contains? #{:kw-int :kw-static :kw-extern :kw-long} (:kind token)) + (if (valid-declaration-starts (:kind token)) (parse-declaration tokens) (parse-statement tokens))) @@ -477,7 +518,8 @@ (defn- parse-program [tokens] (let [[declarations tokens] (parse-repeatedly tokens parse-declaration :eof) - _ (expect :eof tokens)] + _ (expect :eof tokens) + _ (m/coerce #'s/Program declarations)] declarations)) (defn parse [tokens] @@ -502,9 +544,8 @@ (pretty/explain s/Program - (parse-from-src - "int main(void) { -return (long) 42; -}")) + (-> file-path + slurp + parse-from-src)) ()) diff --git a/src/cljcc/schema.clj b/src/cljcc/schema.clj index 0d86453..936a0ea 100644 --- a/src/cljcc/schema.clj +++ b/src/cljcc/schema.clj @@ -9,10 +9,18 @@ [:map [:type [:= :int]]]) +(def UIntType + [:map + [:type [:= :uint]]]) + (def LongType [:map [:type [:= :long]]]) +(def ULongType + [:map + [:type [:= :ulong]]]) + (def FunType [:map [:type [:= :function]] @@ -22,16 +30,20 @@ (def Type [:schema {:registry {::mtype-int #'IntType ::mtype-long #'LongType + ::mtype-uint #'UIntType + ::mtype-ulong #'ULongType ::mtype-function #'FunType}} [:multi {:dispatch :type} [:int #'IntType] [:long #'LongType] + [:uint #'UIntType] + [:ulong #'ULongType] [:function #'FunType]]]) (def Const [:map - [:type [:enum :int :long]] - [:value int?]]) + [:type [:enum :int :long :uint :ulong]] + [:value number?]]) (def ConstantExp [:map diff --git a/src/cljcc/token.clj b/src/cljcc/token.clj index 6796c0b..3d08010 100644 --- a/src/cljcc/token.clj +++ b/src/cljcc/token.clj @@ -207,6 +207,12 @@ "unsigned" :kw-unsigned :identifier)) +(def type-specifier-keywords + #{:kw-int :kw-long :kw-signed :kw-unsigned}) + +(def storage-specifier-keywords + #{:kw-static :kw-extern}) + (defn create ([kind line col] {:kind kind diff --git a/src/cljcc/util.clj b/src/cljcc/util.clj index f75dd5f..eb77ad6 100644 --- a/src/cljcc/util.clj +++ b/src/cljcc/util.clj @@ -6,6 +6,8 @@ (def ^:private counter "Global integer counter for generating unique identifier names." (atom 0)) +(set! *warn-on-reflection* true) + (defn create-identifier! "Returns a unique identifier. Used for generating unique identifier. @@ -74,36 +76,24 @@ (defn whitespace? [^Character ch] (Character/isWhitespace ch)) -(defn- valid-long? - "Validates string to be of form [0-9]+[lL]\b. - - Verifies that `l` or `L` occurs only once, and at the end." - [s] - (try - (let [strip-l-or-L (if-let [_ (or (str/ends-with? s "l") - (str/ends-with? s "L"))] - (subs s 0 (dec (count s))) - s) - _ (-> strip-l-or-L - Long/parseLong - Long/toString)] - s) - (catch Exception _e - false))) - -(defn- matches-regex [re s] +(defn matches-regex [re s] (not (nil? (re-matches re s)))) +(def unsigned-long-re #"[0-9]+([lL][uU]|[uU][lL])") +(def signed-long-re #"[0-9]+[lL]") +(def unsigned-int-re #"[0-9]+[uU]") +(def signed-int-re #"[0-9]+") + (defn read-number "Returns number in string form. Checks whether number is valid long. If no, checks if it valid int. Otherwise error." [s line col] - (if-let [_ (or (matches-regex #"[0-9]+" s) - (matches-regex #"[0-9]+[lL]" s) - (matches-regex #"[0-9]+[uU]" s) - (matches-regex #"[0-9]+([lL][uU]|[uU][lL])" s))] + (if-let [_ (or (matches-regex signed-int-re s) + (matches-regex signed-long-re s) + (matches-regex unsigned-int-re s) + (matches-regex unsigned-long-re s))] s (exc/lex-error {:line line :col col}))) |
