racket/collects/unstable/automata/scribblings/automata.scrbl

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"]