/
failure.cljc
83 lines (76 loc) · 2.64 KB
/
failure.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
(ns instaparse.failure
"Facilities for printing and manipulating error messages."
#?(:clj (:import java.io.BufferedReader java.io.StringReader))
(:require [instaparse.print :as print]))
(defn index->line-column
"Takes an index into text, and determines the line and column info"
[index text]
(loop [line 1, col 1, counter 0]
(cond
(= index counter) {:line line :column col}
(= \newline (get text counter)) (recur (inc line) 1 (inc counter))
:else (recur line (inc col) (inc counter)))))
#?(:clj
(defn get-line
"Returns nth line of text, 1-based"
[n text]
(try (nth (line-seq (BufferedReader. (StringReader. (str text)))) (dec n))
(catch Exception e "")))
:cljs
(defn get-line
[n text]
(loop [chars (seq (clojure.string/replace text "\r\n" "\n"))
n n]
(cond
(empty? chars) ""
(= n 1) (apply str (take-while (complement #{\newline}) chars))
(= \newline (first chars)) (recur (next chars) (dec n))
:else (recur (next chars) n)))))
(defn marker
"Creates string with caret at nth position, 1-based"
[n]
(when (integer? n)
(if (<= n 1) "^"
(apply str (concat (repeat (dec n) \space) [\^])))))
(defn augment-failure
"Adds text, line, and column info to failure object."
[failure text]
(let [lc (index->line-column (:index failure) text)]
(merge failure
lc
{:text (get-line (:line lc) text)})))
(defn print-reason
"Provides special case for printing negative lookahead reasons"
[r]
(cond
(:NOT r)
(do (print "NOT ")
(print (:NOT r))),
(:char-range r)
(print (print/char-range->str r))
(instance? #?(:clj java.util.regex.Pattern
:cljs js/RegExp)
r)
(print (print/regexp->str r))
:else
(pr r)))
(defn pprint-failure
"Takes an augmented failure object and prints the error message"
[{:keys [line column text reason]}]
(println (str "Parse error at line " line ", column " column ":"))
(println text)
(println (marker column))
(let [full-reasons (distinct (map :expecting
(filter :full reason)))
partial-reasons (distinct (map :expecting
(filter (complement :full) reason)))
total (+ (count full-reasons) (count partial-reasons))]
(cond (zero? total) nil
(= 1 total) (println "Expected:")
:else (println "Expected one of:"))
(doseq [r full-reasons]
(print-reason r)
(println " (followed by end-of-string)"))
(doseq [r partial-reasons]
(print-reason r)
(println))))