215 lines
7.2 KiB
Racket
215 lines
7.2 KiB
Racket
#lang scribble/doc
|
|
@(require scribble/manual
|
|
scribble/bnf
|
|
scribble/eval
|
|
unstable/scribblings/utils
|
|
(for-label racket/base
|
|
racket/contract
|
|
racket/list))
|
|
|
|
@(define our-eval (make-base-eval))
|
|
|
|
@title{Automata: Compiling State Machines}
|
|
@unstable[@author+email["Jay McCarthy" "jay@racket-lang.org"]]
|
|
|
|
@defmodule[unstable/automata]
|
|
|
|
This package provides macros and functions for writing state machines over @racketmodname[racket/match] patterns (as opposed to concrete characters.)
|
|
|
|
@section[#:tag "machine"]{Machines}
|
|
|
|
@defmodule[unstable/automata/machine]
|
|
@(require (for-label unstable/automata/machine))
|
|
@interaction-eval[#:eval our-eval (require unstable/automata/machine)]
|
|
|
|
Each of the subsequent macros compile to instances of the machines provided by this module. This is a documented feature of the modules, so these functions should be used to, for example, determine if the machine is currently accepting.
|
|
|
|
@defstruct*[machine ([next (any/c . -> . machine?)])]{
|
|
An applicable structure for machines. When the structure is applied, the @racket[next] field is used as the procedure.
|
|
}
|
|
|
|
@defstruct*[(machine-accepting machine) ([next (any/c . -> . machine?)])]{
|
|
A sub-structure of @racket[machine] that is accepting.
|
|
}
|
|
|
|
@defproc[(machine-accepts? [m machine?] [i (listof any/c)])
|
|
boolean?]{
|
|
Returns @racket[#t] if @racket[m] ends in an accepting state after consuming every element of @racket[i].
|
|
}
|
|
|
|
@defproc[(machine-accepts?/prefix-closed [m machine?] [i (listof any/c)])
|
|
boolean?]{
|
|
Returns @racket[#t] if @racket[m] stays in an accepting state during the consumption of every element of @racket[i].
|
|
}
|
|
|
|
@defthing[machine-null machine?]{
|
|
A machine that is never accepting.
|
|
}
|
|
|
|
@defthing[machine-epsilon machine?]{
|
|
A machine that is initially accepting and never accepting afterwards.
|
|
}
|
|
|
|
@defthing[machine-sigma* machine?]{
|
|
A machine that is always accepting.
|
|
}
|
|
|
|
@defproc[(machine-complement [m machine?])
|
|
machine?]{
|
|
A machine that inverts the acception criteria of @racket[m].
|
|
}
|
|
|
|
@defproc[(machine-star [m machine?])
|
|
machine?]{
|
|
A machine that simulates the Kleene star of @racket[m]. @racket[m] may be invoked many times.
|
|
}
|
|
|
|
@defproc[(machine-union [m0 machine?] [m1 machine?])
|
|
machine?]{
|
|
A machine that simulates the union of @racket[m0] and @racket[m1].
|
|
}
|
|
|
|
@defproc[(machine-intersect [m0 machine?] [m1 machine?])
|
|
machine?]{
|
|
A machine that simulates the intersection of @racket[m0] and @racket[m1].
|
|
}
|
|
|
|
@defproc[(machine-seq [m0 machine?] [m1 machine?])
|
|
machine?]{
|
|
A machine that simulates the sequencing of @racket[m0] and @racket[m1]. @racket[m1] may be invoked many times.
|
|
}
|
|
|
|
@defproc[(machine-seq* [m0 machine?] [make-m1 (-> machine?)])
|
|
machine?]{
|
|
A machine that simulates the sequencing of @racket[m0] and @racket[(make-m1)].
|
|
@racket[(make-m1)] may be invoked many times.
|
|
}
|
|
|
|
|
|
@section[#:tag "dfa"]{Deterministic Finite Automata}
|
|
|
|
@defmodule[unstable/automata/dfa]
|
|
@(require (for-label unstable/automata/dfa))
|
|
@interaction-eval[#:eval our-eval (require unstable/automata/dfa)]
|
|
|
|
This module provides a macro for deterministic finite automata.
|
|
|
|
@defform[(dfa start
|
|
(end ...)
|
|
[state ([evt next-state]
|
|
...)]
|
|
...)
|
|
#:contracts
|
|
([start identifier?]
|
|
[end identifier?]
|
|
[state identifier?]
|
|
[next-state identifier?])]{
|
|
A @racket[machine] that starts in state @racket[start] where each state behaves as specified in the rules. If a @racket[state] is in @racket[(end ...)], then it is constructed with @racket[machine-accepting]. @racket[next-state] need not be a state from this DFA.
|
|
|
|
@defexamples[#:eval our-eval
|
|
(define M
|
|
(dfa s1 (s1)
|
|
[s1 ([0 s2]
|
|
[(? even?) s1])]
|
|
[s2 ([0 s1]
|
|
[(? even?) s2])]))
|
|
(machine-accepts? M (list 2 0 4 0 2))
|
|
(machine-accepts? M (list 0 4 0 2 0))
|
|
(machine-accepts? M (list 2 0 2 2 0 8))
|
|
(machine-accepts? M (list 0 2 0 0 10 0))
|
|
(machine-accepts? M (list))
|
|
(machine-accepts? M (list 4 0))]
|
|
}
|
|
|
|
@section[#:tag "nfa"]{Non-Deterministic Finite Automata}
|
|
|
|
@defmodule[unstable/automata/nfa]
|
|
@(require (for-label unstable/automata/nfa))
|
|
@interaction-eval[#:eval our-eval (require unstable/automata/nfa)]
|
|
|
|
This module provides a macro for non-deterministic finite automata.
|
|
|
|
@defform[(nfa (start:id ...)
|
|
(end:id ...)
|
|
[state:id ([evt:expr (next-state:id ...)]
|
|
...)]
|
|
...)
|
|
#:contracts
|
|
([start identifier?]
|
|
[end identifier?]
|
|
[state identifier?]
|
|
[next-state identifier?])]{
|
|
A @racket[machine] that starts in state @racket[(set start ...)] where each state behaves as specified in the rules. If a state is in @racket[(end ...)], then the machine is accepting. @racket[next-state] must be a state from this NFA.
|
|
|
|
These machines are efficiently compiled to use the smallest possible bit-string as a set representation and unsafe numeric operations where appropriate for inspection and adjusting the sets.
|
|
|
|
@defexamples[#:eval our-eval
|
|
(define M
|
|
(nfa (s1 s3) (s1 s3)
|
|
[s1 ([0 (s2)]
|
|
[1 (s1)])]
|
|
[s2 ([0 (s1)]
|
|
[1 (s2)])]
|
|
[s3 ([0 (s3)]
|
|
[1 (s4)])]
|
|
[s4 ([0 (s4)]
|
|
[1 (s3)])]))
|
|
(machine-accepts? M (list 1 0 1 0 1))
|
|
(machine-accepts? M (list 0 1 0 1 0))
|
|
(machine-accepts? M (list 1 0 1 1 0 1))
|
|
(machine-accepts? M (list 0 1 0 0 1 0))
|
|
(machine-accepts? M (list))
|
|
(machine-accepts? M (list 1 0))]
|
|
}
|
|
|
|
@section[#:tag "nfa-ep"]{Non-Deterministic Finite Automata (with epsilon transitions)}
|
|
|
|
@defmodule[unstable/automata/nfa-ep]
|
|
@(require (for-label unstable/automata/nfa-ep))
|
|
@interaction-eval[#:eval our-eval (require unstable/automata/nfa-ep)]
|
|
|
|
This module provides a macro for non-deterministic finite automata with epsilon transitions.
|
|
|
|
@defidform[epsilon]{
|
|
A binding for use in epsilon transitions.
|
|
}
|
|
|
|
@defform[#:literals (epsilon)
|
|
(nfa/ep (start:id ...)
|
|
(end:id ...)
|
|
[state:id ([epsilon (epsilon-state:id ...)]
|
|
...
|
|
[evt:expr (next-state:id ...)]
|
|
...)]
|
|
...)
|
|
#:contracts
|
|
([start identifier?]
|
|
[end identifier?]
|
|
[state identifier?]
|
|
[epsilon-state identifier?]
|
|
[next-state identifier?])]{
|
|
Extends @racket[nfa] with epsilon transitions, which must be listed first for each state.
|
|
|
|
@defexamples[#:eval our-eval
|
|
(define M
|
|
(nfa/ep (s0) (s1 s3)
|
|
[s0 ([epsilon (s1)]
|
|
[epsilon (s3)])]
|
|
[s1 ([0 (s2)]
|
|
[1 (s1)])]
|
|
[s2 ([0 (s1)]
|
|
[1 (s2)])]
|
|
[s3 ([0 (s3)]
|
|
[1 (s4)])]
|
|
[s4 ([0 (s4)]
|
|
[1 (s3)])]))
|
|
(machine-accepts? M (list 1 0 1 0 1))
|
|
(machine-accepts? M (list 0 1 0 1 0))
|
|
(machine-accepts? M (list 1 0 1 1 0 1))
|
|
(machine-accepts? M (list 0 1 0 0 1 0))
|
|
(machine-accepts? M (list))
|
|
(machine-accepts? M (list 1 0))]
|
|
}
|
|
|
|
@include-section["re.scrbl"]
|