summaryrefslogtreecommitdiffstats
path: root/tcllib/modules/grammar_fa/dacceptor.tcl
diff options
context:
space:
mode:
Diffstat (limited to 'tcllib/modules/grammar_fa/dacceptor.tcl')
-rw-r--r--tcllib/modules/grammar_fa/dacceptor.tcl166
1 files changed, 166 insertions, 0 deletions
diff --git a/tcllib/modules/grammar_fa/dacceptor.tcl b/tcllib/modules/grammar_fa/dacceptor.tcl
new file mode 100644
index 0000000..479db0c
--- /dev/null
+++ b/tcllib/modules/grammar_fa/dacceptor.tcl
@@ -0,0 +1,166 @@
+# -*- tcl -*-
+# Grammar / Finite Automatons / Acceptance checker, DFA only
+
+# ### ### ### ######### ######### #########
+## Package description
+
+## A class whose instances take a FA and are able to check strings of
+## symbols for acceptance. This class is restricted to deterministic
+## FAs. The FA can be either a reference to some external FA container
+## object, or a copy of such. The latter makes the acceptor impervious
+## to changes in the original definition.
+
+# ### ### ### ######### ######### #########
+## Requisites
+
+package require snit ; # Tcllib | OO system used
+package require struct::set ; # Tcllib | Extended set operations.
+
+# ### ### ### ######### ######### #########
+## Implementation
+
+snit::type ::grammar::fa::dacceptor {
+ # ### ### ### ######### ######### #########
+ ## Type API.
+
+ # ### ### ### ######### ######### #########
+ ## Instance API.
+
+ #constructor {fa args} {}
+ #destructor {}
+
+ method accept? {symbolstring} {}
+
+ option -any {}
+
+ # ### ### ### ######### ######### #########
+ ## Internal data structures.
+
+ ## We take the relevant information from the FA specified during
+ ## construction, i.e. start state, final states, and transition
+ ## table in form for direct indexing and keep it local. No need to
+ ## access or even the full FA. We require a deterministic one, and
+ ## will complete it, if necessary.
+
+ variable start ; # Name of start state.
+ variable final ; # Array, existence = state is final.
+ variable trans ; # Transition array: state x symbol -> state
+ variable sym ; # Symbol set (as array), for checking existence.
+ variable any ; # Symbol to map any unknown symbol to. If not
+ # ; # specified (eq "") then unknown symbols will cause non-
+ # ; # acceptance.
+ variable stop ; # Stop state, causing immediate non-acceptance when entered.
+
+ # ### ### ### ######### ######### #########
+ ## Instance API Implementation.
+
+ constructor {fa args} {
+ set any {}
+ $self configurelist $args
+
+ if {![$fa is deterministic]} {
+ return -code error "Source FA is not deterministic"
+ }
+ if {($any ne "") && ![$fa symbol exists $any]} {
+ return -code error "Chosen any symbol \"$any\" does not exist"
+ }
+
+ if {![$fa is complete]} {
+ set istmp 1
+ set tmp [grammar::fa ${selfns}::fa = $fa]
+ set before [$tmp states]
+ $tmp complete
+ # Our sink is a stop state.
+ set stop [struct::set difference [$tmp states] $before]
+ } else {
+ set istmp 0
+ set tmp $fa
+ # We don't know if there is a sink, so no quickstop.
+ set stop {}
+ }
+
+ set start [lindex [$tmp startstates] 0]
+ foreach s [$tmp finalstates] {set final($s) .}
+ foreach s [set syms [$tmp symbols]] {set sym($s) .}
+
+ foreach s [$tmp states] {
+ foreach sy $syms {
+ set trans($s,$sy) [lindex [$tmp next $s $sy] 0]
+ }
+ }
+
+ if {$istmp} {$tmp destroy}
+ return
+ }
+
+ #destructor {}
+
+ onconfigure -any {value} {
+ set options(-any) $value
+ set any $value
+ return
+ }
+
+ # --- --- --- --------- --------- ---------
+
+ method accept? {symbolstring} {
+ set state $start
+
+ ## puts "\n====================== ($symbolstring)"
+
+ if {$any eq ""} {
+ # No any mapping of unknown symbols.
+
+ foreach sy $symbolstring {
+ if {![info exists sym($sy)]} {
+ # Bad symbol in input. String is not accepted,
+ # abort immediately.
+ ## puts " \[$state\] -- Unknown symbol ($sy)"
+ return 0
+ }
+
+ ## puts " \[$state\] --($sy)--> "
+
+ set state $trans($state,$sy)
+ # state == "" cannot happen, as our FA is complete.
+ if {$state eq $stop} {
+ # This is a known sink, we can stop processing input now.
+ ## puts " \[$state\] FULL STOP"
+ return 0
+ }
+ }
+
+ } else {
+ # Mapping of unknown symbols to any.
+
+ foreach sy $symbolstring {
+ if {![info exists sym($sy)]} {set sy $any}
+ ## puts " \[$state\] --($sy)--> "
+ set state $trans($state,$sy)
+ # state == "" cannot happen, as our FA is complete.
+ if {$state eq $stop} {
+ # This is a known sink, we can stop processing input now.
+ ## puts " \[$state\] FULL STOP"
+ return 0
+ }
+ }
+ }
+
+ ## puts " \[$state\][expr {[info exists final($state)] ? " ACCEPT" : ""}]"
+
+ return [info exists final($state)]
+ }
+
+ # ### ### ### ######### ######### #########
+ ## Type API implementation.
+
+ # ### ### ### ######### ######### #########
+ ## Type Internals.
+
+ # ### ### ### ######### ######### #########
+}
+
+# ### ### ### ######### ######### #########
+## Package Management
+
+package provide grammar::fa::dacceptor 0.1.1