diff options
Diffstat (limited to 'src/cljcc')
| -rw-r--r-- | src/cljcc/analyzer.clj | 98 | ||||
| -rw-r--r-- | src/cljcc/parser.clj | 110 | ||||
| -rw-r--r-- | src/cljcc/token.clj | 5 |
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 |
