-
Notifications
You must be signed in to change notification settings - Fork 149
/
print.cljc
105 lines (94 loc) · 3.48 KB
/
print.cljc
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
(ns instaparse.print
"Facilities for taking parsers and grammars, and converting them to strings.
Used for pretty-printing."
(:require [clojure.string :as str]))
(declare combinators->str) ; mutual recursion
(defn paren-for-tags [tag-set hidden? parser]
(if (and (not hidden?) (tag-set (parser :tag)))
(str "(" (combinators->str parser false) ")")
(combinators->str parser false)))
(def paren-for-compound
(partial paren-for-tags #{:alt :ord :cat}))
(defn regexp-replace
"Replaces whitespace characters with escape sequences for better printing"
[s]
(case s
"\n" "\\n"
"\b" "\\b"
"\f" "\\f"
"\r" "\\r"
"\t" "\\t"
s))
(defn regexp->str [r]
(str/replace
(str "#\""
#?(:clj (str r)
:cljs (subs (.-source r) 1))
"\"")
#"[\s]" regexp-replace))
#?(:clj
(defn char-range->str [{:keys [lo hi]}]
(if (= lo hi)
(format "%%x%04x" lo)
(format "%%x%04x-%04x" lo hi)))
:cljs
(do
(defn number->hex-padded [n]
(if (<= n 0xFFF)
(.substr (str "0000" (.toString n 16)) -4)
(.toString n 16)))
(defn char-range->str [{:keys [lo hi]}]
(if (= lo hi)
(str "%x" (number->hex-padded lo))
(str "%x" (number->hex-padded lo) "-" (number->hex-padded hi))))))
(defn combinators->str
"Stringifies a parser built from combinators"
([p] (combinators->str p false))
([{:keys [parser parser1 parser2 parsers tag] :as p} hidden?]
(if (and (not hidden?) (:hide p))
(str \< (combinators->str p true) \>)
(case tag
:epsilon "\u03b5"
:opt (str (paren-for-compound hidden? parser) "?")
:plus (str (paren-for-compound hidden? parser) "+")
:star (str (paren-for-compound hidden? parser) "*")
:rep (if (not= (:min p) (:max p))
(str (paren-for-compound hidden? parser) \{
(:min p) \, (:max p) \})
(str (paren-for-compound hidden? parser) \{
(:min p)\}))
:alt (str/join " | " (map (partial paren-for-tags #{:ord} hidden?) parsers))
:ord (str (paren-for-tags #{:alt} hidden? parser1)
" / "
(paren-for-tags #{:alt} hidden? parser2))
:cat (str/join " " (map (partial paren-for-tags #{:alt :ord} hidden?) parsers))
:string (with-out-str (pr (:string p)))
:string-ci (with-out-str (pr (:string p)))
:char (char-range->str p)
:regexp (regexp->str (:regexp p))
:nt (subs (str (:keyword p)) 1)
:look (str "&" (paren-for-compound hidden? parser))
:neg (str "!" (paren-for-compound hidden? parser))))))
(defn rule->str
"Takes a non-terminal symbol and a parser built from combinators,
and returns a string for the rule."
[non-terminal parser]
(if (= (-> parser :red :reduction-type) :raw)
(str \< (name non-terminal) \>
" = "
(combinators->str parser))
(str (name non-terminal)
" = "
(combinators->str parser))))
(defn Parser->str
"Takes a Parser object, i.e., something with a grammar map and a start
production keyword, and stringifies it."
[{grammar :grammar start :start-production}]
(str/join \newline
(cons
; Put starting production first
(rule->str start (grammar start))
; Then the others
(for [[non-terminal parser] grammar
:when (not= non-terminal start)]
(rule->str non-terminal parser)))))