blob: f0509ec194528eb15592da82ae2c8b8c6441ac44 (
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
;; Copyright (c) Eduardo Julian. All rights reserved.
;; This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
;; If a copy of the MPL was not distributed with this file,
;; You can obtain one at http://mozilla.org/MPL/2.0/.
(ns lux.reader
(:require [clojure.string :as string]
clojure.core.match
clojure.core.match.array
[lux.base :as & :refer [defvariant |do return* return fail fail* |let |case]]))
;; [Tags]
(defvariant
("No" 1)
("Done" 1)
("Yes" 2))
;; [Utils]
(defn ^:private with-line [body]
(fn [state]
(|case (&/get$ &/$source state)
(&/$Nil)
(fail* "[Reader Error] EOF")
(&/$Cons [[file-name line-num column-num] line]
more)
(|case (body file-name line-num column-num line)
($No msg)
(fail* msg)
($Done output)
(return* (&/set$ &/$source more state)
output)
($Yes output line*)
(return* (&/set$ &/$source (&/$Cons line* more) state)
output))
)))
(defn ^:private with-lines [body]
(fn [state]
(|case (body (&/get$ &/$source state))
(&/$Right reader* match)
(return* (&/set$ &/$source reader* state)
match)
(&/$Left msg)
(fail* msg)
)))
(defn ^:private re-find! [^java.util.regex.Pattern regex column ^String line]
(let [matcher (doto (.matcher regex line)
(.region column (.length line))
(.useAnchoringBounds true))]
(when (.find matcher)
(.group matcher 0))))
;; [Exports]
(defn read-regex [regex]
(with-line
(fn [file-name line-num column-num ^String line]
(if-let [^String match (re-find! regex column-num line)]
(let [match-length (.length match)
column-num* (+ column-num match-length)]
(if (= column-num* (.length line))
($Done (&/T [(&/T [file-name line-num column-num]) true match]))
($Yes (&/T [(&/T [file-name line-num column-num]) false match])
(&/T [(&/T [file-name line-num column-num*]) line]))))
($No (str "[Reader Error] Pattern failed: " regex))))))
(defn read-regex+ [regex]
(with-lines
(fn [reader]
(loop [prefix ""
reader* reader]
(|case reader*
(&/$Nil)
(&/$Left "[Reader Error] EOF")
(&/$Cons [[file-name line-num column-num] ^String line]
reader**)
(if-let [^String match (re-find! regex column-num line)]
(let [match-length (.length match)
column-num* (+ column-num match-length)
prefix* (if (= 0 column-num)
(str prefix "\n" match)
(str prefix match))]
(if (= column-num* (.length line))
(recur prefix* reader**)
(&/$Right (&/T [(&/$Cons (&/T [(&/T [file-name line-num column-num*]) line])
reader**)
(&/T [(&/T [file-name line-num column-num]) prefix*])]))))
(&/$Left (str "[Reader Error] Pattern failed: " regex))))))))
(defn read-text [^String text]
(with-line
(fn [file-name line-num column-num ^String line]
(if (.startsWith line text column-num)
(let [match-length (.length text)
column-num* (+ column-num match-length)]
(if (= column-num* (.length line))
($Done (&/T [(&/T [file-name line-num column-num]) true text]))
($Yes (&/T [(&/T [file-name line-num column-num]) false text])
(&/T [(&/T [file-name line-num column-num*]) line]))))
($No (str "[Reader Error] Text failed: " text))))))
(defn from [^String name ^String source-code]
(let [lines (string/split-lines source-code)
indexed-lines (map (fn [line line-num]
(&/T [(&/T [name (inc line-num) 0])
line]))
lines
(range (count lines)))]
(reduce (fn [tail head] (&/$Cons head tail))
&/$Nil
(reverse indexed-lines))))
|