Skip to content

Commit 0f32b9a

Browse files
calculate nfa attempt
1 parent 49079cb commit 0f32b9a

File tree

3 files changed

+263
-2
lines changed

3 files changed

+263
-2
lines changed

Katydid/Expr/CalculateCompiler.lean

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
-- Based on: https://youtu.be/uMurx1a6Zck?t=20144
2+
-- We want:
3+
-- 1. A Compiler (comp)
4+
-- 2. A Virtual Machine (exec)
5+
-- 3. A proof that it is correct (comp, exec = eval)
6+
7+
inductive Regex where
8+
| emptyset: Regex
9+
| emptystr: Regex
10+
| char (c: Char): Regex
11+
| or (left: Regex) (right: Regex): Regex
12+
deriving BEq
13+
14+
def derive (r: Regex) (c: Char): Regex :=
15+
match r with
16+
| Regex.emptyset => Regex.emptyset
17+
| Regex.emptystr => Regex.emptyset
18+
| Regex.char a => if a == c then Regex.emptystr else Regex.emptystr
19+
| Regex.or a b => Regex.or (derive a c) (derive b c)
20+
21+
-- `eval :: Expr -> Int`
22+
-- `eval :: Expr -> m Int`
23+
-- `eval :: Expr -> State s Int`
24+
-- `newtype State s v = State (s -> (s, v))`
25+
-- `eval :: Expr -> s -> (s, v)`
26+
-- `eval :: Regex -> String -> (String, Regex)`
27+
def eval (r: Regex) (s: List Char): (List Char × Regex) :=
28+
(]
29+
, List.foldl derive r s)
30+
31+
-- The instruction set
32+
-- To Calculate: `data Code = ?`
33+
34+
-- The compiler
35+
-- Intuition: `comp :: Expr -> Code`
36+
-- Practically we need a continuation in the compiler:
37+
-- `comp :: Expr -> Code -> Code`, where initial Code is empty code
38+
-- `comp :: Regex -> NFA -> NFA`, where initial NFA is an empty NFA
39+
def comp (r: Regex) (d: NFA): NFA :=
40+
sorry
41+
42+
-- The virtual machine
43+
-- `type Stack :: [Int]`
44+
-- `exec :: Code -> Stack -> Stack`
45+
-- `exec :: Code -> Stack -> m Stack`
46+
-- `exec :: Code -> Stack -> State s Stack`
47+
-- `newtype State s v = State (s -> (s, v))`
48+
-- `exec :: NFA -> List Regex -> String -> (String, List Regex)`
49+
-- Regex = the NFA state, otherwise we cannot do an equality between eval and exec
50+
def exec (d: NFA) (current: List Regex) (s: String): (String × List Regex) :=
51+
sorry
52+
53+
-- Intuition: `comp, exec = eval`
54+
-- Intuition: `exec (comp e) s = eval e : s`
55+
-- Practically: `exec (comp e c) s = exec c (eval e : s)`
56+
-- With Effects: `exec (comp e c) s = do v <- eval e; exec c (v: s)`
57+
-- Solve this equation with 3 unknowns: comp, exec and code
58+
-- This equation has more unknowns that equations
59+
-- We solve this via constructive induction
60+
-- With Regex: `exec (comp initial_regex empty_dfa) empty_regex str`
61+
-- `= let (dstr, dregex) := eval initial_regex str in exec empty_dfa (dregex: empty_regex) dstr`
62+
-- c = any_dfa or empty_dfa
63+
-- e = initial_regex
64+
-- s = any_regexes or empty_regexs or []
65+
-- s has to be a list of states, to keep this formula general enough
66+
-- if s is a list then we compile to an NFA, instead of a DFA
67+
-- if we are compiling an automaton then our states have to regexes to be equal to eval and eval has to return a regex.
68+
-- So this means that derivatives are probably going to be evaluator.
69+
70+
-- e = Val n
71+
-- do v <- eval (Val n); exec c (v: s)
72+
-- ...
73+
-- = exec c' s
74+
-- c' = comp (Val n) c
75+
76+
-- initial_regex = char c
77+
-- `let (dstr, dregex) := eval (char c) str in exec empty_dfa (dregex: empty_regex) dstr`
78+
-- `let (dstr, dregex) := ("", String.foldl derive (char c) str) in exec empty_dfa (dregex: empty_regex) dstr`
79+
-- `let dregex := String.foldl derive (char c) str in exec empty_dfa (dregex: empty_regex) ""`
80+
-- `exec (comp initial_regex empty_dfa) empty_regex str`
81+
82+
83+
-- comp, exec = eval
84+
-- exec (comp regex dfa) char = derive regex char
85+
-- exec (comp regex dfa) (char:str) = exec (derive regex char) str
86+
-- exec (comp (Char c) dfa) char = derive (Char c) char
87+
-- if a == c then Regex.emptystr else Regex.emptystr
88+
-- (comp (Char c) dfa)
89+
-- dfa.add[Regex.char c, c] => Regex.emptystr
90+
-- dfa.add[Regex.char c, !c] => Regex.emptyset
91+
-- = exec c' char where c' = (comp (Char c) dfa)

Katydid/Expr/Recurse.lean

+152
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
-- Haskell
2+
-- data RegexF r = EmptySet
3+
-- | EmptyString
4+
-- | Character Char
5+
-- | Concat r r
6+
-- | ZeroOrMore r
7+
-- | Or r r
8+
-- deriving Functor
9+
-- Ocaml
10+
-- type 'r expr_node =
11+
-- | Const of int
12+
-- | Var of Id.t
13+
-- | Add of 'r * 'r
14+
-- | ...
15+
-- Lean
16+
inductive RegexF (r: Type u) where
17+
| emptyset: RegexF r
18+
| emptystr: RegexF r
19+
| char: Char -> RegexF r
20+
| concat: r -> r -> RegexF r
21+
| star: r -> RegexF r
22+
| or: r -> r -> RegexF r
23+
24+
def RegexF.map {α β : Type u} (f: α → β) (r: RegexF α): RegexF β :=
25+
match r with
26+
| emptyset => emptyset
27+
| emptystr => emptystr
28+
| char c => char c
29+
| concat p q => concat (f p) (f q)
30+
| star p => star (f p)
31+
| or p q => or (f p) (f q)
32+
33+
instance : Functor RegexF where
34+
map := RegexF.map
35+
36+
-- Haskell
37+
-- type Regex = Fix RegexF
38+
-- Ocaml
39+
-- type expr = { unfix : expr expr_node }
40+
-- Lean
41+
inductive Regex where
42+
| unfix: RegexF Regex -> Regex
43+
44+
-- Haskell
45+
-- newtype Fix f = Fix (f (Fix f))
46+
-- Ocaml
47+
-- let fix : expr expr_node -> expr = fun e -> { unfix = e }
48+
-- Lean
49+
def fix: RegexF Regex -> Regex :=
50+
fun (e) => Regex.unfix e
51+
52+
-- Haskell
53+
-- wreckit :: Fix f -> f (Fix f)
54+
-- wreckit (Fix x) = x
55+
-- wreckit :: Regex -> RegexF Regex
56+
-- Ocaml
57+
-- let unfix r = r.unfix
58+
-- Lean
59+
def unfix: Regex -> RegexF Regex :=
60+
fun r =>
61+
match r with
62+
| Regex.unfix rf => rf
63+
64+
def RegexF.sizeof [SizeOf a] (r: RegexF a): Nat :=
65+
match r with
66+
| RegexF.emptyset => 0
67+
| RegexF.emptystr => 1
68+
| RegexF.char _ => 2
69+
| RegexF.concat p q => 1 + sizeOf p + sizeOf q
70+
| RegexF.star p => 1 + sizeOf p
71+
| RegexF.or p q => 1 + sizeOf p + sizeOf q
72+
73+
instance [SizeOf r]: SizeOf (RegexF r) where
74+
sizeOf := RegexF.sizeof
75+
76+
def Regex.sizeof (r: Regex): Nat :=
77+
match r with
78+
| Regex.unfix rf =>
79+
match rf with
80+
| RegexF.emptyset => 0
81+
| RegexF.emptystr => 1
82+
| RegexF.char _ => 2
83+
| RegexF.concat p q => 1 + sizeof p + sizeof q
84+
| RegexF.star p => 1 + sizeof p
85+
| RegexF.or p q => 1 + sizeof p + sizeof q
86+
87+
instance : SizeOf Regex where
88+
sizeOf := Regex.sizeof
89+
90+
def emptyset: Regex :=
91+
fix RegexF.emptyset
92+
93+
def emptystr: Regex :=
94+
fix RegexF.emptystr
95+
96+
def char (c: Char): Regex :=
97+
fix (RegexF.char c)
98+
99+
def concat (p q: Regex): Regex :=
100+
fix (RegexF.concat p q)
101+
102+
def star (p: Regex): Regex :=
103+
fix (RegexF.star p)
104+
105+
def or' (p q: Regex): Regex :=
106+
fix (RegexF.or p q)
107+
108+
def nullableAlg (r: RegexF Bool): Bool :=
109+
match r with
110+
| RegexF.emptyset => false
111+
| RegexF.emptystr => true
112+
| RegexF.char _ => false
113+
| RegexF.concat p q => p && q
114+
| RegexF.star _ => true
115+
| RegexF.or p q => p || q
116+
117+
-- type FAlgebra f r = f r -> r
118+
-- type NullableFAlgebra = FAlgebra RegexF Bool
119+
-- cata :: NullableFAlgebra -> Regex -> Bool
120+
-- cata nullableAlg regex =
121+
-- wreckit regex
122+
-- |> fmap (cata nullableAlg)
123+
-- |> nullableAlg
124+
def cata (f: RegexF Bool -> Bool) (regex: Regex): Bool :=
125+
let uregex := unfix regex
126+
let cf := fun (rr: Regex) => cata f rr
127+
f (Functor.map cf uregex)
128+
termination_by regex
129+
decreasing_by sorry
130+
131+
132+
133+
134+
135+
136+
137+
138+
139+
140+
141+
142+
143+
144+
145+
146+
-- nullable :: Regex -> Bool
147+
-- nullable = cata nullableAlg
148+
def nullable (r: Regex): Bool :=
149+
cata nullableAlg r
150+
151+
-- def cata [Functor f] : FAlgebra f r -> Fix f -> r
152+
-- cata alg = alg . fmap (cata alg) . wreckit

Katydid/Std/Ltac.lean

+20-2
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,33 @@ def run (t: Lean.Elab.Tactic.TacticM (Lean.TSyntax `tactic)): Lean.Elab.Tactic.T
8080
let t' ← t
8181
Lean.Elab.Tactic.evalTactic t'
8282

83-
-- an example tactic that applies a hypothesis to the goal if it matches a pattern
84-
local elab "example_apply_hypothesis" : tactic => do
83+
-- an example tactic that applies a hypothesis to the goal if it matches a pattern using the Qq library.
84+
local elab "example_apply_hypothesis_prop" : tactic => do
8585
let hyps ← getHypothesesProp
8686
for (name, ty) in hyps do
8787
if let ~q((((($a : Prop)) → $b) : Prop)) := ty then
8888
run `(tactic| apply $name )
8989

9090
example {A B: Prop} (P: A -> B) (a: A): B := by
91+
example_apply_hypothesis_prop
92+
assumption
93+
94+
local elab "example_apply_hypothesis" : tactic => do
95+
let hyps ← getHypotheses
96+
for (name, ty) in hyps do
97+
match ty with
98+
| Lean.Expr.forallE _ _ _ _ =>
99+
run `(tactic| apply $name )
100+
return ()
101+
| _ =>
102+
continue
103+
104+
example : 1 = 1 :=
105+
rfl
106+
107+
theorem example100 {C D: Prop} (Q: C -> D) (a: C): D := by
91108
example_apply_hypothesis
109+
try apply Q
92110
assumption
93111

94112
-- Returns the main goal as a Q(Prop), such that it can be used in a pattern match.

0 commit comments

Comments
 (0)