ELS’19, April 1–2 2018, Genova, Italy Jim E. Newton and Didier Verna
we calculate the derivative with respect each type calculated in the
maximal disjoint type decomposition as explained in [NVC17].
3.1 Constructing States and Transitions
The algorithm can be summarized as follows. Each state in the DFA
represents all the possible futures which are accepting. Moreover,
there is a (not necessarily unique) rte which expresses that set of
futures. For example, let:
P
1
= (:or (:cat number string) (:cat fixnum float))
be the rte representing all the sequences of either a
number
followed
by a
string
or a
fixnum
followed by a
float
. Suppose there is a
state in the DFA associated with this rte. Now we consider all the
possible types of the rst element of such a sequence. And for each
such rst element type, we calculate what the remaining future
would be given that the rst element of that type. If the rst element
is a
fixnum
, then the future is a sequence containing either a
string
or a
float
. Such a sequence is denoted by the rte
(:or string
float). In terms of the rational derivative we say:
P
2
= ∂
fixnum
P
1
= (:or string float) .
If, on the other hand, the rst element is not a
fixnum
but is a
number
, then the remaining sequence whose only element is a
string. That is to say:
P
3
= ∂
(and number (not fixnum))
P
1
= string .
Since there is no other possible rst element of
P
1
, we con-
struct two additional states,
P
2
and
P
3
and construct two transitions
P
1
→ P
2
labeled
fixnum
, and
P
1
→ P
3
labeled
(and number (not
fixnum)).
We continue this process until all the futures of each state have
been calculated, generating all the possible states, and all the possi-
ble transitions between the states.
3.2 Associating Code with Accepting States
DFAs used for matching pattern languages such as regular expres-
sions, normally represent Boolean functions; returning TRUE if the
sequence matches the expression, and FALSE otherwise. In our case
each accepting state of the DFAs in Figure 7 indicate which code
paths to take in the originating
rte-case
, Figure 3. This problem
is easily addressed. We have simply extended our
state
object
(Clos class [
GWB91
,
Kee89
]) to contain a slot indicating a piece of
continuation code to be serialized in the nal macro expansion.
3.3 Overlapping Clauses
The synchronized cross-product (SXP) of two or more given DFAs is
a single DFA whose behavior simultaneously emulates the behavior
of the given DFAs. Typically such a cross-product implements the
intersection or union languages of the input DFAs; however the
semantics of such a cross-product can be taken to be any Boolean
combination of the input.
For example, to implement the symmetric dierence language
we apply the Boolean XOR function; a state, X, in the SXP, cor-
responding to states A and B from two given DFAs, is marked as
an accepting state if A XOR B are accepting (if either but not both
are accepting). In our case we would like to select the code for
evaluation corresponding to the code appearing rst in the original
( r te-ca se expr es si on
((: cat fixn um fixnum )
: c lause -1 )
((: and (: cat fixnum i nte ger )
(: not (: cat f ixn um f ixn um )))
: c lause -2 )
((: and (: cat ( or string fi xnu m ) n umb er )
(: not (: cat f ixn um f ixn um ))
(: not (: cat f ixn um f ixn um )))
: c lause -3 ))
Figure 8: Example of rte-case with pairwise disjoint pat-
terns
destructuring-case
; so we need priority based selection, rather
than simply a Boolean function.
An important property of the behavior of
rte-case
is that if
more than one pattern matches the expression in question, then
the clause appearing rst has priority over the others. For example,
in the code in Figure 3, if the value of
expression
is the list
(1
2)
, then all three rtes match; nevertheless
:clause-1
must be the
return value.
An approach of addressing this ambiguity is to extend or aug-
ment the patterns so that they are mutually exclusive; i.e. assure
that no two patterns simultaneously match any candidate expres-
sion. The code shown in Figure 8 is equivalent to that in Figure 3
but any input expression,
(1 2)
, for example, matches at most
one pattern. This pattern augmentation can be accomplished as a
code transformation. The pattern corresponding to
:clause-1
is
unchanged, but the subsequent clauses have been augmented to
emphasize that those clauses are never reached if any prior pattern
matches.
These rtes correspond to the DFAs shown in Figure 9. The rst
DFA is exactly the same as before, but we notice in the second DFA
that the state labeled 2.2 is non-coäccessible; i.e., there is no path
from state 2.2 to any accepting state. This non-useful state corre-
sponds to
(:not (:cat fixnum fixnum))
in the input pattern,
and it enforces that a sequence consisting of two objects of type
fixnum
, is a rejected sequence rather than a matching sequence.
The third DFA in the gure contains a similar state, 3.4, but in
addition, contains two states 3.2 and 3.5 which are equivalent to
each other.
The disjoining process described here produces DFAs which
have redundant or non-coäccessible states. Despite this fact, these
slightly more complex DFAs play an important role in the SXP con-
struction, because the process guarantees that the SXP construction
will never encounter a situation where it must choose between two
dierent pieces of code to execute on reaching an acceptance condi-
tion. If attempting to calculate the union of the three DFAs shown
in Figure 7, the algorithm would have to deal with the fact that a
sequence of
(1 2)
at run time should return
:clause-1
rather than
:clause-2
. However, if calculating the union of the DFAs from
Figure 9, such ambiguity is averted. The union can be performed
purely algebraically, with no consideration or order of priority.