aboutsummaryrefslogtreecommitdiff
path: root/src/cljcc/parser.clj
blob: 0feb388add2241ad0b14af6fdd096597ef1f5411 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
(ns cljcc.parser
  (:require
   [cljcc.lexer :as l]
   [clojure.pprint :as pp]))

(defn- expect [expected-kind [token & rst]]
  (if (= expected-kind (:kind token))
    [token rst]
    (throw (ex-info "Parser Error." {:expected expected-kind
                                     :actual (:kind token)}))))

(defn- parse-exp [tokens]
  (let [[t rst] (expect :number tokens)]
    [{:type :exp
      :value {:type :constant-exp
              :value (:literal t)}} rst]))

(defn- parse-return-statement [tokens]
  (let [[_ rst] (expect :kw-return tokens)
        [constant-node rst] (parse-exp rst)]
    [{:type :statement
      :statement-type :return
      :value constant-node}
     rst]))

(defn- parse-statement
  "Parses a single statement. Expects a semicolon at the end."
  [[token :as tokens]]
  (let [[statement rst]
        (cond
          (= (:kind token) :kw-return) (parse-return-statement tokens)
          :else (throw (ex-info "Parser Error. Unexpected statement. " {:token token})))
        [_ rst] (expect :semicolon rst)]
    [statement rst]))

(defn- keyword->type [k]
  (condp = k
    :kw-int "int"
    (throw (ex-info "Parser Error. Unsupported type." {:keyword k}))))

(defn- parse-function [tokens]
  (let [[fn-type-token rst] (expect :kw-int tokens)
        [fn-identifier-token rst] (expect :identifier rst)
        [_ rst] (expect :left-paren rst)
        [fn-parameter-token rst] (expect :kw-void rst)
        [_ rst] (expect :right-paren rst)
        [_ rst] (expect :left-curly rst)
        [statement rst] (parse-statement rst)
        [_ rst] (expect :right-curly rst)]
    [{:type :function
      :return-type (keyword->type (:kind fn-type-token))
      :identifier (:literal fn-identifier-token)
      :parameters (:kind fn-parameter-token)
      :statements [statement]}
     rst]))

(defn- parse-program [tokens]
  (let [[ast rst] (parse-function tokens)
        _ (expect :eof rst)]
    [ast]))

(defn parse [tokens]
  (-> tokens
      :tokens
      parse-program))

(comment

  (parse "int main(void) {return 2;}")

  (pp/pprint (parse (l/lex "
  int main(void) {
  return 2;
  }")))

  (pp/pprint
   (l/lex "
  int main(void) {
    return 2;
  }"))

  (parse "int main(void) {
   return -(((((10)))));
   }")

  (pp/pprint (parse "int main(void) {
   return 1 & 2 + 6 & 6;
   }"))

  ())