我觉得这个问题看起来很有趣,所以我尝试了几种不同的实现方法。下面的代码是最有希望的方法。我认为它解决了所描述的问题,尽管我不确定一些细节。
基本上,它允许上下文敏感语法的一种形式,但只有一种非常简单的形式,其中每个产品只能依赖于前面的符号。下面的代码构建了一些组合器,允许将产品直接编码为“生成器”,并考虑场景后面的长度限制。
type sym = Xa | Xb | Xc // The terminal symbols
type word = sym list // A string of terminals
type gen = int -> sym list seq // Generate words up to the given length
/// Passes the previous symbol to a generator, after checking the length.
let (+>) : sym -> (sym -> gen) -> gen =
fun x g l -> if l<=0 then Seq.empty
else seq { for xs in g x (l-1) -> x :: xs }
let nil _ = seq { yield [] } // Generate just the empty word
let (|||) xs ys l = Seq.append (xs l) (ys l) // Union of two generators
let notAccepted _ = Seq.empty // Don't generate anything
let tryAll g = Xa +> g ||| Xb +> g ||| Xc +> g // Run g starting with any sym
// Generators for non-terminals. Each takes the previous symbol as an argument,
// and generates a (possibly empty) sequence of lists of symbols.
let rec gR = function Xa -> Xb +> gS ||| Xc +> gR ||| nil
| Xc -> Xb +> gS | _ -> notAccepted
and gS = function Xb -> Xa +> gR | _ -> notAccepted
let genTop = tryAll gR // The top level generator begins with gR with any sym
let words = genTop 4 // Generate words up to length 4
// val words : seq<sym list>
// = seq [[Xa; Xb; Xa]; [Xa; Xc; Xb; Xa]; [Xa]; [Xc; Xb; Xa]]