aboutsummaryrefslogtreecommitdiff
path: root/src/cljcc
diff options
context:
space:
mode:
Diffstat (limited to 'src/cljcc')
-rw-r--r--src/cljcc/cljcc.clj16
-rw-r--r--src/cljcc/parser.clj105
-rw-r--r--src/cljcc/schema.clj16
-rw-r--r--src/cljcc/token.clj6
-rw-r--r--src/cljcc/util.clj34
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})))