# -*- tcl -*-
# -------------------------------------------------------------------------
# Helper for tests: Validation of serializations.

proc validate_serial {value fa} {
    # value is list/3 ('grammar::fa' symbols states)
    # !("" in symbols)
    # states is ordered dict (key is state, value is statedata)
    # statedata is list/3 (start final trans|"")
    # start is boolean
    # final is boolean
    # trans is dict (key in symbols, value is list (state))

    # Output for debug ...
    ##puts "$fa => ($value)"

    # symbols set-equal symbols(fa)
    # states  set-equal states(fa)
    # finalstates set-equal finalstates(fa)
    # startstates set-equal startstates(fa)

    set prefix "error in serialization:"
    if {[llength $value] != 3} {
	return "$prefix list length not 3"
    }

    struct::list assign $value type symbols statedata

    if {$type ne "grammar::fa"} {
	return "$prefix unknown type \"$type\""
    }
    if {[struct::set contains $symbols ""]} {
	return "$prefix empty symbol is not legal"
    }
    if {![struct::set equal $symbols [$fa symbols]]} {
	return "$prefix set of symbols does not match"
    }
    if {[llength $statedata] % 2 == 1} {
	return "$prefix state data is not a dictionary"
    }
    array set _states $statedata
    if {[llength $statedata] != (2*[array size _states])} {
	return "$prefix state data contains duplicate states"
    }
    if {![struct::set equal [array names _states] [$fa states]]} {
	return "$prefix set of states does not match"
    }
    foreach {k v} $statedata {
	if {[llength $v] != 3} {
	    return "$prefix state list length not 3"
	}
	struct::list assign $v start final trans
	if {![string is boolean -strict $start]} {
	    return "$prefix expected boolean for start, got \"$start\", for state \"$k\""
	}
	if {($start && ![$fa start? $k]) || (!$start && [$fa start? $k])} {
	    return "$prefix start does not match for state \"$k\""
	}
	if {![string is boolean -strict $final]} {
	    return "$prefix expected boolean for final, got \"$final\", for state \"$k\""
	}
	if {($final && ![$fa final? $k]) || (!$final && [$fa final? $k])} {
	    return "$prefix final does not match for state \"$k\""
	}
	if {[llength $trans] % 2 == 1} {
	    return "$prefix transition data is not a dictionary for state \"$k\""
	}
	array set _trans $trans
	if {[llength $trans] != (2*[array size _trans])} {
	    return "$prefix transition data contains duplicate symbols for state \"$k\""
	}
	# trans keys set-equal to trans/symbols(fa,k)
	if {![struct::set equal [$fa symbols@ $k] [array names _trans]]} {
	    return "$prefix transition symbols do not match for state \"$k\""
	}
	unset _trans

	foreach {sym destinations} $trans {
	    if {($sym ne "") && ![struct::set contains $symbols $sym]} {
		return "$prefix illegal symbol \"$sym\" in transition for state \"$k\""
	    }
	    foreach dest $destinations {
		if {![info exists _states($dest)]} {
		    return "$prefix illegal destination state \"$dest\" for state \"$k\""
		}
	    }
	    if {![struct::set equal $destinations [$fa next $k $sym]]} {
		return "$prefix destination set does not match for state \"$k\""
	    }
	}
    }
    return ok
}

# -------------------------------------------------------------------------
# Helper for tests: Serialization of empty FA.

set fa_empty {grammar::fa {} {}}

# -------------------------------------------------------------------------
# Helper for tests: Predefined graphs for use in tests.
# (Properties and such). Number of graphs: 30.

array set fa_pre {}

proc gen {code} {
    global      fa_pre
    uplevel #0 $fa_pre($code)
    return
}
proc def {code script} {
    global fa_pre
    set fa_pre($code) $script
    return
}


def x {
    a state add x
}
def x- {
    a state add x
    a symbol add @
    a next x @ --> x
}
def xe {
    a state add x
    a next x "" --> x
}
def xy {
    a state add x y
}
def xy- {
    a state add x y
    a symbol add @
    a next x @ --> y
}
def xye {
    a state add x y
    a next x "" --> y
}
def xyee {
    a state add x y
    a next x "" --> y
    a next y "" --> x
}
def xye- {
    a state add x y
    a symbol add @
    a next x "" --> y
    a next y @  --> x
}
def xy-- {
    a state add x y
    a symbol add @
    a next x @ --> y
    a next y @ --> x
}
def xy-= {
    a state add x y
    a symbol add @ =
    a next x @ --> y
    a next y = --> x
}
def xyz/ee {
    a state add x y z
    a next x "" --> y
    a next x "" --> z
}
def xyz/e- {
    a state add x y z
    a symbol add @
    a next x @  --> y
    a next x "" --> z
}
def xyz/-- {
    a state add x y z
    a symbol add @
    a next x @ --> y
    a next x @ --> z
}
def xyz/-= {
    a state add x y z
    a symbol add @ =
    a next x @ --> y
    a next x = --> z
}
def xyz|ee {
    a state add x y z
    a next x "" --> z
    a next y "" --> z
}
def xyz|e- {
    a state add x y z
    a symbol add @
    a next x @  --> z
    a next y "" --> z
}
def xyz|-- {
    a state add x y z
    a symbol add @
    a next x @ --> z
    a next y @ --> z
}
def xyz|-= {
    a state add x y z
    a symbol add @ =
    a next x @ --> z
    a next y = --> z
}
def xyz+eee {
    a state add x y z
    a next x "" --> y
    a next y "" --> z
    a next z "" --> x
}
def xyz+ee- {
    a state add x y z
    a symbol add @
    a next x "" --> y
    a next y "" --> z
    a next z @  --> x
}
def xyz+e-- {
    a state add x y z
    a symbol add @
    a next x "" --> y
    a next y @  --> z
    a next z @  --> x
}
def xyz+e-= {
    a state add x y z
    a symbol add @ =
    a next x "" --> y
    a next y @  --> z
    a next z =  --> x
}
def xyz+--- {
    a state add x y z
    a symbol add @
    a next x @ --> y
    a next y @ --> z
    a next z @ --> x
}
def xyz+--= {
    a state add x y z
    a symbol add @ =
    a next x @ --> y
    a next y @ --> z
    a next z = --> x
}
def xyz+-=_ {
    a state add x y z
    a symbol add @ = %
    a next x @ --> y
    a next y = --> z
    a next z % --> x
}
def xyz&eee {
    a state add x y z
    a next x "" --> y
    a next x "" --> z
    a next y "" --> z
}
def xyz&ee- {
    a state add x y z
    a symbol add @
    a next x "" --> y
    a next x "" --> z
    a next y @  --> z
}
def xyz&e-- {
    a state add x y z
    a symbol add @
    a next x "" --> y
    a next x @  --> z
    a next y @  --> z
}
def xyz&e-= {
    a state add x y z
    a symbol add @ =
    a next x "" --> y
    a next x @  --> z
    a next y =  --> z
}
def xyz&--- {
    a state add x y z
    a symbol add @
    a next x @ --> y
    a next x @ --> z
    a next y @ --> z
}
def xyz&--= {
    a state add x y z
    a symbol add @ =
    a next x @ --> y
    a next x @ --> z
    a next y = --> z
}
def xyz&-=_ {
    a state add x y z
    a symbol add @ = %
    a next x @ --> y
    a next x = --> z
    a next y % --> z
}
def xyz!ee {
    a state add x y z
    a next x "" --> y
    a next y "" --> z
}
def xyz!e- {
    a state add x y z
    a symbol add @
    a next x "" --> y
    a next y @  --> z
}
def xyz!-- {
    a state add x y z
    a symbol add @
    a next x @ --> y
    a next y @ --> z
}
def xyz!-= {
    a state add x y z
    a symbol add @ = %
    a next x @ --> y
    a next y = --> z
}
def xyz!-e {
    a state add x y z
    a symbol add @
    a next x @  --> y
    a next y "" --> z
}

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

def datom {
    a state add x y
    a symbol add @
    a next x @ --> y
}
def dalt {
    a state add u v w x y z
    a symbol add @ =
    a next u "" --> v ; a next v @ --> x ; a next x "" --> z
    a next u "" --> w ; a next w = --> y ; a next y "" --> z
}
def daltb {
    a state add u v w x y z
    a symbol add @ =
    a next u "" --> v ; a next v @ --> x ; a next x "" --> z
    a next u "" --> w ; a next w = --> y ; a next y "" --> z
    a next z "" --> u
}
def dopt {
    a state add u v w x
    a symbol add @
    a next u "" --> v ; a next v @ --> w ; a next w "" --> x
    a next u "" --> x
}
def drep {
    a state add u v w x
    a symbol add @
    a next u "" --> v ; a next v @ --> w ; a next w "" --> x
    a next u "" --> x
    a next x "" --> u
}

# -------------------------------------------------------------------------
