summaryrefslogtreecommitdiff
path: root/edit-lens/src/Control/Edit/Container/Transducer.lhs
blob: 9db26db93ddcb1fe4f016273edfdfa70e8596078 (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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
\begin{code}
{-# LANGUAGE ScopedTypeVariables #-}

module Control.Edit.Container.Transducer
  ( ContainerTransducer(..), ctStates
  , runCT, stepCT
  ) where

import Control.Edit.Container
import Data.Map.Strict (Map, (!?))
import qualified Data.Map.Strict as Map
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Sequence (Seq(..), (><))
import qualified Data.Sequence as Seq

import Control.Monad.RWS.Strict
  
import Control.Monad.Writer.Class
import Control.Monad.Reader.Class
import Control.Monad.State.Class

import Control.Lens
import Control.Lens.Edit

import Data.Maybe
import Data.Foldable


data ContainerTransducer state input output = ContainerTransducer
  { ctInitialState :: state
  , ctTransition :: Map (state, Content input) (ContainerEdits output, state)
    -- ^ States are stable (and produce no output) by default
  }

ctStates :: Ord state => ContainerTransducer state input output -> Set state
ctStates ContainerTransducer{..} = Map.foldrWithKey' (\(s1, _) (_, s2) -> Set.insert s1 . Set.insert s2) (Set.singleton ctInitialState) ctTransition

stepCT :: ( Ord state, Ord (Content input)
          , MonadReader (ContainerTransducer state input output) m
          , MonadState state m
          , MonadWriter (ContainerEdits output) m
          ) => Content input -> m ()
stepCT x = do
  ContainerTransducer{ ctTransition } <- ask
  state <- get
  let (output, newState) = fromMaybe (mempty, state) $ ctTransition !? (state, x)
  put newState
  tell output

runCT :: ( Ord state, Ord (Content input)
         , FoldableContainer input
         ) => ContainerTransducer state input output
           -> input
           -> ContainerEdits output
runCT ct@ContainerTransducer{ ctInitialState } input = runRWS (forMOf_ containerContent input stepCT) ct ctInitialState ^. _3
\end{code}

\begin{code}
class Ord a => HasRanges a where
  data Range a :: *
  bounds :: Getter (Range a) (a, a)
  range :: Iso' (Set a) (Range a)
  within :: a -> Range a -> Bool
  within x xs = Set.member x $ xs ^. from range
  below :: a -> Range a -> Bool
  below x xs = x < min
    where (min, _) = xs ^. bounds
  above :: a -> Range a -> Bool
  above x xs = x > max
    where (_, max) = xs ^. bounds
  insert :: Range a -> a -> Range a
  insert xs x = xs & under range (Set.insert x)
  singleton :: a -> Range a
  singleton = view range . Set.singleton

type PositionAction state output = Map state (ContainerEdits output, state)
  
data ContainerAction state input output
  = CALeaf { caPos :: Position input
           , caContent :: Content input
           , caAction :: PositionAction state output
           , caOutputPos :: Range Natural
           }
  | CABranch { caRange :: Range (Position input)
             , caAction :: PositionAction state output
             , caLeft :: ContainerAction state input output
             , caRight :: ContainerAction state input output
             , caOutputPos :: Range Natural
             }
  | CATip

-- type ContainerAction state input output = Map ((Position input, Seq (Content input), Position input)) (Map state (ContainerEdits output, state))

instance HasRanges Natural where
  data Range Natural = NatInterval Natural Natural -- @NatInterval x y@ is the interval of natural numbers z such that x <= z < y
                     | NatSet (Set Natural)
  bounds = to bounds'
    where
      bounds' (NatInterval min max) = (min, max)
      bounds' (NatSet set) = (Set.findMin set, Set.findMax set)
  range = iso fromSet toSet
    where
      fromSet set
        | Set.null set = NatInterval 0 0
        | otherwise = Set.foldl' insert (Set.empty ^. range) set
      toSet (NatInterval x y)
        | x < y = Set.fromAscList [x..pred y]
        | otherwise = Set.empty
  insert i@(NatInterval x y) z
    | x < y
    , z == pred x = NatInterval z y
    | x < y
    , z == succ y = NatInterval x z
    | x >= y = NatInterval z (succ z)
    | otherwise = NatSet $ (i ^. from range) `Set.union` Set.singleton z
  insert (NatSet set) z = fromMaybe (NatSet $ Set.insert z set) $ foldlM insert' (NatInterval 0 0) set
    where
      insert' r x = case insert r x of
        r'@(NatInterval _ _) -> Just r
        _ -> Nothing

caRange' :: HasRanges (Position input) => ContainerAction state input output -> Range (Position input)
caRange' (CALeaf tPos _ _) = singleton tPos
caRange' (CABranch r _ _ _) = r
caRange' CATip = Set.empty ^. range

caAction' :: ContainerAction state input output -> PositionAction state output
caAction' CATip = Map.empty
caAction' x = caAction x

conc :: Ord state
     => PositionAction state output
     -> PositionAction state output
     -> PositionAction state output
f `conc` g = Map.fromList [ (st, fromMaybe (mempty, st') $ f !? st') | (st, x@(_, st')) <- Map.toAscList g ]

\end{code}

Für die Zwecke eines Container-Transducers sehen wir Container als eine Sequenz von edits auf $\text{init}$.

Das Komplement der transducer-edit-linse ist ein binärbaum dessen Zweige beschriftet sind mit der vom Teilbaum betrachteten input Container-Positionen, der Wirkung des gesamten Teilbaums (d.h. die Transitionsfunktion für den Zustand und den produzierten Output (eine Teilsequenz der edits des output containers)), und den betroffenen output Positionen.
Die Blätter beschriften wir mit einer einzigen betrachteten Position im input-Container, dem beim verarbeiten angetroffenen Inhalt, der Wirkung dieses konkreten Inhalts, und den betroffenen output Positionen.

Zur Verarbeitung eines edits auf dem input lokalisieren wir zunächst dessen Wirkung in unserem Komplement-Baum.
Handelt es sich um einen edit auf einer Position im input können wir für das betroffene Blatt eine neue Wirkung berechnen, anhand der Transitionsfunktion des transducers und dem im komplement gespeicherten alten Wert.
Wir können nun nicht vermeiden von den output edits zu fordern eine Gruppe zu sein, da wir gezwungen sind einen edit zu produzieren, der auf dem Resultat des vorherigen Laufes, zumindest den alten, bei der betroffenen transition edit rückgängig zu machen.

Beweisskizze:
 Betrachte als input den trivialen container mit nur einer shape und darin belegten position.
 Als edits auf dem Inhalt jenes containers nehmen wir Replace.
 Nimm $e_o$ einen edit auf dem output-Container.
 Obda ist $e_o$ die Wirkung von $\text{Replace}(c)$ unter $\text{diffContTrans}$ auf $(\text{init}, \text{init_C}, \text{init})$ (Konstruiere hierfür einen geeigneten Transducer)
 Betrachte nun $\text{Replace}(c)$ gefolgt von $\text{Replace}(\text{init})$ unter $\text{diffContTrans}$ auf $\text{init}$.
 Für das Ergebnis $e_o^{-1}$ muss gelten, dass $e_o^{-1} \circ e_o = \text{id}$.
 Wir haben also Inverse für beliebige Elemente des edit-monoiden auf dem Output; also eine Gruppe auf edits auf dem Output die kompatibel ist mit der bestehenden monoid struktur.
 
\begin{code}
diffContTrans :: forall state input output.
                 ( Container input, Container output
                 , Ord state, Ord (Content input)
                 , HasRanges (Position input)
                 )
              => ContainerTransducer state input output
              -> EditLens (ContainerAction state input output) (ContainerEdits input) (ContainerEdits output)
diffContTrans ContainerTransducer{..} = EditLens CATip forward backward
  where
    forward (cAct, Seq.Empty) = (cAct, Seq.empty)
    forward (cAct, xs) = foldr' (\x (cAct', ys) -> over _2 (ys ><) $ go cAct' x) (cAct, Seq.empty) xs
      where
        go :: ContainerAction state input output
           -> ContainerEdit input
           -> (ContainerAction state input output, ContainerEdits output)
        go ca Fail = (ca, Seq.singleton Fail)
        go CATip _ = (CATip, mempty)
        go ca@(CALeaf tPos content pAct) (ModContent pos cEdit)
          | pos == tPos
          , Just content' <- content `apply` cEdit
          = (CALeaf tPos content' $ Map.fromAscList [ (s, a) | ((s, c), a) <- Map.toAscList ctTransition, c == content' ], undefined)
          | otherwise = (ca, Seq.singleton Fail)
        go ca@(CABranch range act left right) e@(ModContent pos _)
          | pos `within` caRange' left
          = let (left', es) = go left e in (CABranch range (caAction' right `conc` caAction' left') left' right, es)
          | pos `within` caRange' right
          = let (right', es) = go right e in (CABranch range (caAction' right' `conc` caAction' left) left right', es)
          | otherwise = (ca, Seq.singleton Fail)
        go cAct (ModShape cShape perm) = undefined

    backward = undefined
\end{code}