I generate dot format files and call GraphViz to draw the graph. I use the symbol ',' to mean concatenation and enter 'a,a,b,b' instead of the traditional regexp 'aabb'. In the graphs, '.' refers to a null transition. Here are some sample inputs and outputs:
a*,b|c (a|b)* (a|b),(a|b) (Note that my handling of * is slightly incorrect because the expression must be present at least once.) Disjunction uses the '|' symbol, for example 'a|b' means a or b. Unlimited levels of nested parenthesis are supported, thanks to recursion.
I process the expression recursively by at each point dividing the input string into two parts, if possible. The string '(a|b),b' is split into (a|b) and b with the operation ',' , and the first part is then split into a and b with the operation |. If the expression is a or a*, then it will return, and work its way down. To my knowledge all complicated expressions are recognized successfully.
The Python program then will output Prolog code that can be consulted. Once consulted, the predicate parse can be used with a list to see if the string is recognized by the language.
Prolog code generated for (a|b),(a|b)
Sample output of this Prolog code recognizing (a|b),(a|b):
parse(L) :- start(S), trans(S,L). trans(X,[A|B]) :- delta(X,A,Y), write(X), write(' '), write([A|B]), nl, trans(Y,B). trans(X,[A|B]) :- nulltrans(X,Y), trans(Y,[A|B]). trans(X,):- nulltrans(X,Y), trans(Y,). trans(X,) :- final(X), write(X), write(' '), write(), nl. delta(0,a,1). delta(0,b,2). delta(3,a,4). delta(3,b,5). nulltrans(1,3). nulltrans(2,3). nulltrans(4,6). nulltrans(5,6). nulltrans(99,99). start(0). final(6).
| ?- parse([a,b]). 0 [a,b] 3 [b] 6  true ? yes | ?- parse([b,a]). 0 [b,a] 3 [a] 6  true ? (15 ms) yes | ?- parse([b,a,b]). 0 [b,a,b] 3 [a,b] no | ?- parse([b]). 0 [b]