Skip to content

Commit 1fbb5e2

Browse files
authored
[ fix #835 ] Text.Regex (#1391)
1 parent de671f6 commit 1fbb5e2

28 files changed

+1407
-26
lines changed

CHANGELOG.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,31 @@ New modules
291291
Algebra.Morphism.LatticeMonomorphism
292292
```
293293

294+
* New ternary relation on lists:
295+
```
296+
Data.List.Relation.Ternary.Appending
297+
Data.List.Relation.Ternary.Appending.Properties
298+
Data.List.Relation.Ternary.Appending.Propositional
299+
Data.List.Relation.Ternary.Appending.Propositional.Properties
300+
Data.List.Relation.Ternary.Appending.Setoid
301+
Data.List.Relation.Ternary.Appending.Setoid.Properties
302+
```
303+
304+
* New modules formalising regular expressions:
305+
```
306+
Text.Regex
307+
Text.Regex.Base
308+
Text.Regex.Derivative.Brzozowski
309+
Text.Regex.Properties.Core
310+
Text.Regex.Properties
311+
Text.Regex.Search
312+
Text.Regex.SmartConstructors
313+
Text.Regex.String
314+
Text.Regex.String.Unsafe
315+
316+
README.Text.Regex
317+
```
318+
294319
Other major changes
295320
-------------------
296321

@@ -694,6 +719,14 @@ Other minor additions
694719
```agda
695720
linesBy : Decidable P → String → List String
696721
lines : String → List String
722+
723+
_≤_ : Rel String zero
724+
```
725+
726+
* Added new proofs to `Data.String.Properties`:
727+
```agda
728+
≤-isDecPartialOrder-≈ : IsDecPartialOrder _≈_ _≤_
729+
≤-decPoset-≈ : DecPoset _ _ _
697730
```
698731

699732
* Added new function to `Data.Maybe.Base`:
@@ -757,6 +790,22 @@ Other minor additions
757790
resp : (P : Pred A ℓ) → P Respects _≡_
758791
```
759792

793+
* Added new proof to `Data.List.Relation.Binary.Lex.Strict`:
794+
```agda
795+
≤-isDecPartialOrder : IsStrictTotalOrder _≈_ _≺_ → IsDecPartialOrder _≋_ _≤_
796+
≤-decPoset : StrictTotalOrder a ℓ₁ ℓ₂ → DecPoset _ _ _
797+
```
798+
799+
* Added new function to `Data.List.Relation.Binary.Prefix.Heterogeneous`:
800+
```agda
801+
_++ᵖ_ : Prefix R as bs → ∀ suf → Prefix R as (bs ++ suf)
802+
```
803+
804+
* Added new function to `Data.List.Relation.Binary.Suffix.Heterogeneous`:
805+
```agda
806+
_++ˢ_ : ∀ pre → Suffix R as bs → Suffix R as (pre ++ bs)
807+
```
808+
760809
* Added new function to `Data.Fin` (the inverse of `splitAt`:
761810
```agda
762811
join : ∀ m n → Fin m ⊎ Fin n → Fin (m ℕ.+ n)

GenerateEverything.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ withKModules = map modToFile
7373
, "Relation.Binary.PropositionalEquality.TrustMe"
7474
, "Text.Pretty.Core"
7575
, "Text.Pretty"
76+
, "Text.Regex.String.Unsafe"
7677
]
7778

7879
isWithKModule :: FilePath -> Bool

README.agda

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ import README.Data
9191
-- • Tactic
9292
-- Tactics for automatic proof generation
9393

94+
-- ∙ Text
95+
-- Format-based printing, Pretty-printing, and regular expressions
96+
9497

9598
------------------------------------------------------------------------
9699
-- Library design
@@ -184,6 +187,29 @@ import Codata.Thunk
184187

185188
import IO
186189

190+
-- ∙ Text
191+
192+
-- Explaining string formats and the behaviour of printf
193+
194+
import README.Text.Printf
195+
196+
-- Showcasing the pretty printing module
197+
198+
import README.Text.Pretty
199+
200+
-- Demonstrating the regular expression matching
201+
202+
import README.Text.Regex
203+
204+
-- Explaining how to display tables of strings:
205+
206+
import README.Text.Tabular
207+
208+
-- Explaining how to display a tree:
209+
210+
import README.Text.Tree
211+
212+
187213

188214
------------------------------------------------------------------------
189215
-- More documentation
@@ -213,22 +239,6 @@ import README.Nary
213239

214240
import README.Inspect
215241

216-
-- Explaining string formats and the behaviour of printf
217-
218-
import README.Text.Printf
219-
220-
-- Showcasing the pretty printing module
221-
222-
import README.Text.Pretty
223-
224-
-- Explaining how to display tables of strings:
225-
226-
import README.Text.Tabular
227-
228-
-- Explaining how to display a tree:
229-
230-
import README.Text.Tree
231-
232242
-- Explaining how to use the automatic solvers
233243

234244
import README.Tactic.MonoidSolver

README/Text/Regex.agda

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
------------------------------------------------------------------------
2+
-- The Agda standard library
3+
--
4+
-- Examples of regular expressions and matching
5+
------------------------------------------------------------------------
6+
7+
{-# OPTIONS --with-K #-}
8+
9+
module README.Text.Regex where
10+
11+
open import Data.Bool using (true; false)
12+
open import Data.List using (_∷_; [])
13+
open import Data.String
14+
open import Function.Base using () renaming (_$′_ to _$_)
15+
open import Relation.Nullary using (yes)
16+
open import Relation.Nullary.Decidable using (True; False; from-yes)
17+
18+
-- Our library available via the Text.Regex module is safe but it works on
19+
-- lists of characters.
20+
21+
-- To use it on strings we have to rely on unsafe theorems about the
22+
-- conversions between strings and lists of characters being inverses.
23+
-- For convenience we use the following unsafe module for this README.
24+
open import Text.Regex.String.Unsafe
25+
26+
------------------------------------------------------------------------
27+
-- Defining regular expressions
28+
29+
-- The type of regular expressions is Exp.
30+
31+
-- Some examples of regular expressions using:
32+
33+
-- [_] for the union of ranges it contains
34+
-- _─_ for a range
35+
-- singleton for an exact character
36+
-- _∙_ for the concatenation of two regular expressions
37+
-- _∣_ for the sum of two regular expressions
38+
-- _⋆ for the Kleene star (zero or more matches of the regular expression)
39+
-- _⁇ for an optional regular expression
40+
41+
ℕ* : Exp
42+
ℕ* = [ '1''9' ∷ [] ] -- a non-zero digit
43+
∙ [ '0''9' ∷ [] ] ⋆ -- followed by zero or more digits
44+
45+
: Exp
46+
= ℕ* ∣ singleton '0' -- ℕ* or exactly 0
47+
48+
: Exp
49+
= ((singleton '-') ⁇ ∙ ℕ*) -- an optional minus sign followed by a ℕ*
50+
∣ singleton '0' -- or exactly 0
51+
52+
------------------------------------------------------------------------
53+
-- An expression's semantics
54+
55+
-- The semantics of these regular expression is defined in terms of the
56+
-- lists of characters they match. The type (str ∈ e) states that the
57+
-- string str matches the expression e.
58+
59+
-- It is decidable, and the proof is called _∈?_.
60+
-- We can run it on a few examples to check that it matches our intuition:
61+
62+
-- Valid: starts with a non-zero digit, followed by 3 digits
63+
_ : True ("1848" ∈? ℕ*)
64+
_ = _
65+
66+
-- Valid: exactly 0
67+
_ : True ("0" ∈? ℕ)
68+
_ = _
69+
70+
-- Invalid: starts with a leading 0
71+
_ : False ("007" ∈? ℕ)
72+
_ = _
73+
74+
-- Invalid: no negative ℕ number
75+
_ : False ("-666" ∈? ℕ)
76+
_ = _
77+
78+
-- Valid: a negative integer
79+
_ : True ("-666" ∈? ℤ)
80+
_ = _
81+
82+
-- Invalid: no negative 0
83+
_ : False ("-0" ∈? ℤ)
84+
_ = _
85+
86+
------------------------------------------------------------------------
87+
-- Matching algorithms
88+
89+
-- The proof that _∈_ is decidable gives us the ability to check whether
90+
-- a whole string matches a regular expression. But we may want to use
91+
-- other matching algorithms detecting a prefix, infix, or suffix of the
92+
-- input string that matches the regular expression.
93+
94+
-- This is what the Regex type gives us.
95+
96+
-- For instance, the following value corresponds to finding an infix
97+
-- substring matching the string "agda" or "agdai"
98+
99+
agda : Exp
100+
agda = singleton 'a'
101+
∙ singleton 'g'
102+
∙ singleton 'd'
103+
∙ singleton 'a'
104+
∙ (singleton 'i' ⁇)
105+
106+
infixAgda : Regex
107+
infixAgda = record
108+
{ fromStart = false
109+
; tillEnd = false
110+
; expression = agda
111+
}
112+
113+
-- The search function gives us the ability to look for matches
114+
115+
-- Valid: agda in the middle
116+
_ : True (search "Maria Magdalena" infixAgda)
117+
_ = _
118+
119+
-- By changing the value of fromStart and tillEnd we can control where the
120+
-- substring should be. We can insist on the match being at the end of the
121+
-- input for instance:
122+
123+
suffixAgda : Regex
124+
suffixAgda = record
125+
{ fromStart = false
126+
; tillEnd = true
127+
; expression = agda
128+
}
129+
130+
-- Invalid: agda is in the middle
131+
_ : False (search "Maria Magdalena" suffixAgda)
132+
_ = _
133+
134+
-- Valid: agda as a suffix
135+
_ : True (search "README.agda" suffixAgda)
136+
_ = _
137+
138+
-- Valid: agdai as a suffix
139+
_ : True (search "README.agdai" suffixAgda)
140+
_ = _
141+
142+
143+
------------------------------------------------------------------------
144+
-- Advanced uses
145+
146+
-- Search does not just return a boolean, it returns an informative answer.
147+
-- Infix matches are for instance represented using the `Infix` relation on
148+
-- list. Such a proof pinpoints the exact position of the match:
149+
150+
open import Data.List.Relation.Binary.Infix.Heterogeneous
151+
open import Data.List.Relation.Binary.Infix.Heterogeneous.Properties
152+
open import Data.List.Relation.Binary.Pointwise using (≡⇒Pointwise-≡)
153+
open import Relation.Binary.PropositionalEquality
154+
155+
-- Here is an example of a match: it gives back the substring, the inductive
156+
-- proof that it is accepted by the regular expression and its precise location
157+
-- inside the input string
158+
mariamAGDAlena : Match "Maria Magdalena" infixAgda
159+
mariamAGDAlena = record
160+
{ string = "agda" -- we have found "agda"
161+
; match = from-yes ("agda" ∈? agda) -- a proof of the match
162+
; related = proof -- and its location
163+
}
164+
165+
where
166+
167+
proof : Infix _≡_ (toList "agda") (toList "Maria Magdalena")
168+
proof = toList "Maria M"
169+
++ⁱ fromPointwise (≡⇒Pointwise-≡ refl)
170+
ⁱ++ toList "lena"
171+
172+
173+
-- And here is the proof that search returns such an object
174+
_ : search "Maria Magdalena" infixAgda ≡ yes mariamAGDAlena
175+
_ = refl

src/Data/List/Relation/Binary/Infix/Heterogeneous.agda

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ module Data.List.Relation.Binary.Infix.Heterogeneous where
1010

1111
open import Level
1212
open import Relation.Binary using (REL; _⇒_)
13-
open import Data.List.Base as List using (List; []; _∷_)
13+
open import Data.List.Base as List using (List; []; _∷_; _++_)
1414
open import Data.List.Relation.Binary.Pointwise
1515
using (Pointwise)
1616
open import Data.List.Relation.Binary.Prefix.Heterogeneous
17-
as Prefix using (Prefix; []; _∷_)
17+
as Prefix using (Prefix; []; _∷_; _++ᵖ_)
1818

1919
private
2020
variable
@@ -34,6 +34,16 @@ module _ {A : Set a} {B : Set b} (R : REL A B r) where
3434
MkView : pref {inf} Pointwise R as inf suff
3535
View as (pref List.++ inf List.++ suff)
3636

37+
infixr 5 _++ⁱ_ _ⁱ++_
38+
39+
_++ⁱ_ : xs {as bs} Infix R as bs Infix R as (xs ++ bs)
40+
[] ++ⁱ rs = rs
41+
(x ∷ xs) ++ⁱ rs = there (xs ++ⁱ rs)
42+
43+
_ⁱ++_ : {as bs} Infix R as bs xs Infix R as (bs ++ xs)
44+
here rs ⁱ++ xs = here (rs ++ᵖ xs)
45+
there rs ⁱ++ xs = there (rs ⁱ++ xs)
46+
3747
map : R ⇒ S Infix R ⇒ Infix S
3848
map R⇒S (here pref) = here (Prefix.map R⇒S pref)
3949
map R⇒S (there inf) = there (map R⇒S inf)

src/Data/List/Relation/Binary/Lex/Strict.agda

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,14 @@ module _ {a ℓ₁ ℓ₂} {A : Set a} where
196196
}
197197
where open IsStrictPartialOrder spo
198198

199+
≤-isDecPartialOrder : IsStrictTotalOrder _≈_ _≺_
200+
IsDecPartialOrder _≋_ _≤_
201+
≤-isDecPartialOrder sto = record
202+
{ isPartialOrder = ≤-isPartialOrder isStrictPartialOrder
203+
; _≟_ = Pointwise.decidable _≟_
204+
; _≤?_ = ≤-decidable _≟_ _<?_
205+
} where open IsStrictTotalOrder sto
206+
199207
≤-isTotalOrder : IsStrictTotalOrder _≈_ _≺_ IsTotalOrder _≋_ _≤_
200208
≤-isTotalOrder sto = record
201209
{ isPartialOrder = ≤-isPartialOrder isStrictPartialOrder
@@ -222,6 +230,13 @@ module _ {a ℓ₁ ℓ₂} {A : Set a} where
222230
{ isPartialOrder = ≤-isPartialOrder isStrictPartialOrder
223231
} where open StrictPartialOrder spo
224232

233+
≤-decPoset : {a ℓ₁ ℓ₂} StrictTotalOrder a ℓ₁ ℓ₂
234+
DecPoset _ _ _
235+
≤-decPoset sto = record
236+
{ isDecPartialOrder = ≤-isDecPartialOrder isStrictTotalOrder
237+
} where open StrictTotalOrder sto
238+
239+
225240
≤-decTotalOrder : {a ℓ₁ ℓ₂} StrictTotalOrder a ℓ₁ ℓ₂
226241
DecTotalOrder _ _ _
227242
≤-decTotalOrder sto = record

src/Data/List/Relation/Binary/Prefix/Heterogeneous.agda

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ module _ {a b r s} {A : Set a} {B : Set b} {R : REL A B r} {S : REL A B s} where
4545

4646
module _ {a b r} {A : Set a} {B : Set b} {R : REL A B r} where
4747

48+
_++ᵖ_ : {as bs} Prefix R as bs suf Prefix R as (bs List.++ suf)
49+
[] ++ᵖ suf = []
50+
(r ∷ rs) ++ᵖ suf = r ∷ (rs ++ᵖ suf)
51+
4852
toView : {as bs} Prefix R as bs PrefixView R as bs
4953
toView [] = [] ++ _
5054
toView (r ∷ rs) with toView rs

src/Data/List/Relation/Binary/Suffix/Heterogeneous.agda

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ module _ {a b r} {A : Set a} {B : Set b} {R : REL A B r} where
3131
tail (here (_ ∷ rs)) = there (here rs)
3232
tail (there x) = there (tail x)
3333

34+
_++ˢ_ : pre {as bs} Suffix R as bs Suffix R as (pre List.++ bs)
35+
[] ++ˢ rs = rs
36+
(x ∷ xs) ++ˢ rs = there (xs ++ˢ rs)
37+
3438
module _ {a b r s} {A : Set a} {B : Set b} {R : REL A B r} {S : REL A B s} where
3539

3640
map : R ⇒ S Suffix R ⇒ Suffix S

0 commit comments

Comments
 (0)