Your grammar seems more complex than needed, and you could simplify it using 'epsilon' (empty production, in DCG is []
). That apart, you should keep the 'program' more adherent to the specification.
atom --> letter, atom_part | letter.
atom_part --> letter | digit | letter, atom_part | digit, atom_part.
letter --> "a" | "b" | /* omissis... */ "Z".
digit --> [D], {memberchk(D, "0123456789")}.
You can see how similar is to the original specification. With that
?- phrase(atom, "a").
true ;
false.
?- phrase(atom, "3a").
false.
?- phrase(atom, "a3").
true ;
false.
letter
and digit
show different ways to match single characters. digit
it's simpler if you need to capture values from input, as done in your code. But because enumerating 26*2 characters it's error prone, please consider using code_type/2
atom(A) --> letter(L), atom_part(P), {A=[L|P]} | letter(L), {A=[L]}.
atom_part(P) --> letter(L), {P=[L]} | digit(D), {P=[D]} | letter(L), atom_part(A), {P=[L|A]} | digit(D), atom_part(A), {P=[D|A]}.
letter(L) --> [L], {code_type(L, alpha)}.
digit(D) --> [D], {memberchk(D, "0123456789")}.
Also consider that usually alternatives in Prolog are encoded in this way
atom([L|P]) --> letter(L), atom_part(P).
atom([L]) --> letter(L).
The simpler form allows moving the 'data construction' in head pattern.