aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cljcc/analyzer.clj98
-rw-r--r--src/cljcc/parser.clj110
-rw-r--r--src/cljcc/token.clj5
3 files changed, 199 insertions, 14 deletions
diff --git a/src/cljcc/analyzer.clj b/src/cljcc/analyzer.clj
index b8b0141..16124e6 100644
--- a/src/cljcc/analyzer.clj
+++ b/src/cljcc/analyzer.clj
@@ -34,6 +34,11 @@
(resolve-exp (:right e) mp))
(throw (ex-info "Analyzer error. Invalid expression type" {:exp e}))))
+(defn- resolve-optional-exp [e var-mp]
+ (if (nil? e)
+ e
+ (resolve-exp e var-mp)))
+
(defn- resolve-declaration [d mp]
(if (and (contains? mp (:identifier d)) (:from-current-block (get mp (:identifier d))))
(throw (ex-info "Analyzer error. Duplicate variable declaration." {:declaration d}))
@@ -48,6 +53,17 @@
{:declaration (p/declaration-node unique-name)
:variable-map updated-mp}))))
+(defn- copy-variable-map [var-mp]
+ (zipmap (keys var-mp)
+ (map (fn [m]
+ (update m :from-current-block (fn [_] false)))
+ (vals var-mp))))
+
+(defn- resolve-for-init [for-init var-mp]
+ (if (= (:type for-init) :declaration)
+ (resolve-declaration for-init var-mp)
+ (resolve-optional-exp for-init var-mp)))
+
(defn- resolve-statement [s mp]
(condp = (:statement-type s)
:return (p/return-statement-node (resolve-exp (:value s) mp))
@@ -58,10 +74,23 @@
(resolve-statement (:else-statement s) mp))
(p/if-statement-node (resolve-exp (:condition s) mp)
(resolve-statement (:then-statement s) mp)))
- :compound (let [updated-mp (zipmap (keys mp)
- (map (fn [m]
- (update m :from-current-block (fn [_] false)))
- (vals mp)))]
+ :break s
+ :continue s
+ :while (p/while-statement-node (resolve-exp (:condition s) mp)
+ (resolve-statement (:body s) mp))
+ :do-while (p/do-while-statement-node (resolve-exp (:condition s) mp)
+ (resolve-statement (:body s) mp))
+ :for (let [new-var-map (copy-variable-map mp)
+ for-init (resolve-for-init (:init s) new-var-map)
+ new-var-map (if (:declaration for-init)
+ (:variable-map for-init)
+ new-var-map) ; updates new-var-map so that include possible
+ ; variable declaration
+ condition (resolve-optional-exp (:condition s) new-var-map)
+ post (resolve-optional-exp (:post s) new-var-map)
+ body (resolve-statement (:body s) new-var-map)]
+ (p/for-statement-node for-init condition post body))
+ :compound (let [updated-mp (copy-variable-map mp)]
(p/compound-statement-node (:block (resolve-block (:block s) updated-mp))))
:empty (p/empty-statement-node)
(throw (ex-info "Analyzer error. Invalid statement." {:statement s}))))
@@ -88,9 +117,61 @@
:variable-map var-mp}
block)))
+(defn- annotate-label [n l]
+ (assoc n :label l))
+
+(defn- label-statement
+ ([s]
+ (label-statement s nil))
+ ([s current-label]
+ (let [s-type (:statement-type s)]
+ (cond
+ (= s-type :break) (if (nil? current-label)
+ (throw (ex-info "break statement outside of loop" {}))
+ (p/break-statement-node current-label))
+ (= s-type :continue) (if (nil? current-label)
+ (throw (ex-info "continue statement outside of loop" {}))
+ (p/continue-statement-node current-label))
+ (= s-type :while) (let [new-label (unique-identifier "while_label")
+ l-body (label-statement (:body s) new-label)
+ l-while (p/while-statement-node (:condition s) l-body)]
+ (annotate-label l-while new-label))
+ (= s-type :do-while) (let [new-label (unique-identifier "do_while_label")
+ l-body (label-statement (:body s) new-label)
+ l-do-while (p/do-while-statement-node (:condition s) l-body)]
+ (annotate-label l-do-while new-label))
+ (= s-type :for) (let [new-label (unique-identifier "for_label")
+ l-body (label-statement (:body s) new-label)
+ l-for (p/for-statement-node (:init s) (:condition s) (:post s) l-body)]
+ (annotate-label l-for new-label))
+ (= s-type :if) (if (:else-statement s)
+ (p/if-statement-node (:condition s)
+ (label-statement (:then-statement s) current-label)
+ (label-statement (:else-statement s) current-label))
+ (p/if-statement-node (:condition s)
+ (label-statement (:then-statement s) current-label)))
+ (= s-type :compound) (let [update-block-f (fn [item]
+ (if (= (:type item) :statement)
+ (label-statement item current-label)
+ item))
+ new-block (map update-block-f (:block s))]
+ (p/compound-statement-node new-block))
+ (= s-type :return) s
+ (= s-type :expression) s
+ (= s-type :empty) s
+ :else (throw (ex-info "invalid statement reached during loop labelling." {}))))))
+
+(defn- resolve-loop-labels [body]
+ (let [f (fn [item]
+ (if (= :statement (:type item))
+ (label-statement item)
+ item))
+ new-body (map f body)]
+ new-body))
+
(defn- validate-function [f]
- (let [updated-body (resolve-block (:body f))]
- (assoc f :body (:block updated-body))))
+ (let [body (resolve-loop-labels (:block (resolve-block (:body f))))]
+ (assoc f :body body)))
(comment
@@ -123,6 +204,11 @@ int a = 3;
{
int a = a = 4;
}
+
+while (a < 10) {
+break;
+}
+
return a;
}"))
diff --git a/src/cljcc/parser.clj b/src/cljcc/parser.clj
index f1fac64..ce83f12 100644
--- a/src/cljcc/parser.clj
+++ b/src/cljcc/parser.clj
@@ -4,7 +4,7 @@
[cljcc.token :as t]
[clojure.pprint :as pp]))
-(declare parse parse-exp parse-statement parse-block)
+(declare parse parse-exp parse-statement parse-block expect parse-declaration)
(defn- parse-repeatedly [tokens parse-f end-kind]
(loop [tokens tokens
@@ -14,6 +14,14 @@
(let [[node rst] (parse-f tokens)]
(recur rst (conj res node))))))
+(defn- parse-optional-expression [[{kind :kind} :as tokens] parse-f end-kind]
+ (if (= kind end-kind)
+ (let [[_ tokens] (expect end-kind tokens)]
+ [nil tokens]) ; end kind seen, so expression not found
+ (let [[e tokens] (parse-f tokens)
+ [_ tokens] (expect end-kind tokens)]
+ [e tokens])))
+
(defn- keyword->type [k]
(condp = k
:kw-int "int"
@@ -99,6 +107,8 @@
(recur [(binary-exp-node left right kind)] rst)))
[left tokens])))))
+;;;; Statements
+
(defn return-statement-node [e]
{:type :statement
:statement-type :return
@@ -109,6 +119,20 @@
:statement-type :expression
:value e})
+(defn break-statement-node
+ ([] (break-statement-node nil))
+ ([label]
+ {:type :statement
+ :statement-type :break
+ :label label}))
+
+(defn continue-statement-node
+ ([] (continue-statement-node nil))
+ ([label]
+ {:type :statement
+ :statement-type :continue
+ :label label}))
+
(defn empty-statement-node []
{:type :statement
:statement-type :empty})
@@ -128,6 +152,33 @@
:then-statement then
:else-statement else}))
+(defn while-statement-node [cond-exp body-statement]
+ {:type :statement
+ :statement-type :while
+ :condition cond-exp
+ :body body-statement})
+
+(defn do-while-statement-node [cond-exp body-statement]
+ {:type :statement
+ :statement-type :do-while
+ :condition cond-exp
+ :body body-statement})
+
+(defn for-statement-node [for-init cond-exp post-exp body-statement]
+ {:type :statement
+ :statement-type :for
+ :condition cond-exp
+ :post post-exp
+ :init for-init
+ :body body-statement})
+
+(defn for-init-node [decl exp]
+ {:type :for-initializer
+ :init-declaration decl
+ :init-exp exp})
+
+;;;; Parse statement nodes
+
(defn- parse-return-statement [tokens]
(let [[_ rst] (expect :kw-return tokens)
[exp-node rst] (parse-exp rst)
@@ -145,6 +196,48 @@
(let [[_ rst] (expect :semicolon tokens)]
[(empty-statement-node) rst]))
+(defn- parse-break-statement [tokens]
+ (let [[_ tokens] (expect :kw-break tokens)
+ [_ tokens] (expect :semicolon tokens)]
+ [(break-statement-node) tokens]))
+
+(defn- parse-continue-statement [tokens]
+ (let [[_ tokens] (expect :kw-continue tokens)
+ [_ tokens] (expect :semicolon tokens)]
+ [(continue-statement-node) tokens]))
+
+(defn- parse-while-statement [tokens]
+ (let [[_ tokens] (expect :kw-while tokens)
+ [_ tokens] (expect :left-paren tokens)
+ [e tokens] (parse-exp tokens)
+ [_ tokens] (expect :right-paren tokens)
+ [s tokens] (parse-statement tokens)]
+ [(while-statement-node e s) tokens]))
+
+(defn- parse-do-while-statement [tokens]
+ (let [[_ tokens] (expect :kw-do tokens)
+ [s tokens] (parse-statement tokens)
+ [_ tokens] (expect :kw-while tokens)
+ [_ tokens] (expect :left-paren tokens)
+ [e tokens] (parse-exp tokens)
+ [_ tokens] (expect :right-paren tokens)
+ [_ tokens] (expect :semicolon tokens)]
+ [(do-while-statement-node e s) tokens]))
+
+(defn- parse-for-init-statement [[{kind :kind} :as tokens]]
+ (if (= kind :kw-int)
+ (parse-declaration tokens)
+ (parse-optional-expression tokens parse-exp :semicolon)))
+
+(defn- parse-for-statement [tokens]
+ (let [[_ tokens] (expect :kw-for tokens)
+ [_ tokens] (expect :left-paren tokens)
+ [for-init-node tokens] (parse-for-init-statement tokens)
+ [cond-exp tokens] (parse-optional-expression tokens parse-exp :semicolon)
+ [post-exp tokens] (parse-optional-expression tokens parse-exp :right-paren)
+ [stmt tokens] (parse-statement tokens)]
+ [(for-statement-node for-init-node cond-exp post-exp stmt) tokens]))
+
(defn- parse-if-statement [tokens]
(let [[_ tokens] (expect :kw-if tokens)
[_ tokens] (expect :left-paren tokens)
@@ -169,6 +262,11 @@
(= kind :semicolon) (parse-empty-statement tokens)
(= kind :kw-return) (parse-return-statement tokens)
(= kind :kw-if) (parse-if-statement tokens)
+ (= kind :kw-break) (parse-break-statement tokens)
+ (= kind :kw-continue) (parse-continue-statement tokens)
+ (= kind :kw-for) (parse-for-statement tokens)
+ (= kind :kw-while) (parse-while-statement tokens)
+ (= kind :kw-do) (parse-do-while-statement tokens)
(= kind :left-curly) (parse-compound-statement tokens)
:else (parse-expression-statement tokens)))
@@ -237,13 +335,9 @@
(pp/pprint (parse-from-src "
int main(void) {
int a = 1;
- {
-int a = 2;
- {
-int a = 4;
-}
-}
-return 0;
+do {
+ a += 2;
+} while (a < 10);
}"))
(pp/pprint
diff --git a/src/cljcc/token.clj b/src/cljcc/token.clj
index 2da74fd..a703373 100644
--- a/src/cljcc/token.clj
+++ b/src/cljcc/token.clj
@@ -176,6 +176,11 @@
"int" :kw-int
"if" :kw-if
"else" :kw-else
+ "do" :kw-do
+ "while" :kw-while
+ "for" :kw-for
+ "break" :kw-break
+ "continue" :kw-continue
:identifier))
(defn create