Some basic DCG example predicates because I am always looking for these examples in my code. Now they are in one place for all to see.
% one or more - Accumulator returns closed list
%! 'digit+'(-Codes:list(code))// is semidet.
'digit+'([H|T]) -->
digit(H), !,
'digit*'(T).
% zero or more - Accumulator returns closed list
%! 'digit*'(-Codes:list(code))// is semidet.
'digit*'([H|T]) -->
digit(H), !,
'digit*'(T).
'digit*'([]) --> [].
% one or more - Accumulator returns difference list
%! 'digit+'(-Codes:list(code), -Tail:list(code))// is semidet.
'digit+'([H|T0],T) -->
digit(H), !,
'digit*'(T0,T).
% zero or more - Accumulator returns difference list
%! 'digit*'(-Codes:list(code), -Tail:list(code))// is semidet.
'digit*'([H|T0],T) -->
digit(H), !,
'digit*'(T0,T).
'digit*'(T,T) --> [].
% Recognizer
%! digit// is semidet.
digit -->
digit(_).
%! digit(-Code:code)// is semidet.
digit(0'0) --> "0", !.
digit(0'1) --> "1", !.
digit(0'2) --> "2", !.
digit(0'3) --> "3", !.
digit(0'4) --> "4", !.
digit(0'5) --> "5", !.
digit(0'6) --> "6", !.
digit(0'7) --> "7", !.
digit(0'8) --> "8", !.
digit(0'9) --> "9".
% Note other alpha predicates are not created because they are similar to digit predicates.
% zero or more - Accumulator returns difference list
%! 'alpha*'(-Codes:list(code), -Tail:list(code))// is semidet.
'alpha*'([H|T0],T) -->
alpha(H), !,
'alpha*'(T0,T).
'alpha*'(T,T) --> [].
%! alpha(-Code:code)// is semidet.
alpha(C) -->
[C],
{
between(0'a,0'z,C), !
;
between(0'A,0'Z,C)
}.
%! c_identifier_start(-Code:code)// is semidet.
c_identifier_start(C) -->
alpha(C), !.
c_identifier_start(0'_) -->
[0'_].
% Accumulator returns difference list
%! c_identifier_rest(-Codes:list(code), -Tail:list(code))// is semidet.
c_identifier_rest([C|T0],T) -->
(
c_identifier_start(C), !
;
digit(C)
), !,
c_identifier_rest(T0,T).
c_identifier_rest(T,T) --> [].
%! c_identifier(@Value)// is semidet.
c_identifier(identifier(Identifier)) -->
c_identifier_start(H), % Returns a single character code.
c_identifier_rest(T,[]), % Ues a character code difference list.
{ atom_codes(Identifier,[H|T]) }.
% -----------------------------------------------------------------------------
:- begin_tests(dcg).
digit_test( success, "0", 0'0, [] ).
digit_test( success, "1", 0'1, [] ).
digit_test( success, "2", 0'2, [] ).
digit_test( success, "3", 0'3, [] ).
digit_test( success, "4", 0'4, [] ).
digit_test( success, "5", 0'5, [] ).
digit_test( success, "6", 0'6, [] ).
digit_test( success, "7", 0'7, [] ).
digit_test( success, "8", 0'8, [] ).
digit_test( success, "9", 0'9, [] ).
digit_test( success, "0a", 0'0, [0'a] ).
digit_test( success, "1a", 0'1, [0'a] ).
digit_test( success, "2a", 0'2, [0'a] ).
digit_test( success, "3a", 0'3, [0'a] ).
digit_test( success, "4a", 0'4, [0'a] ).
digit_test( success, "5a", 0'5, [0'a] ).
digit_test( success, "6a", 0'6, [0'a] ).
digit_test( success, "7a", 0'7, [0'a] ).
digit_test( success, "8a", 0'8, [0'a] ).
digit_test( success, "9a", 0'9, [0'a] ).
digit_test( fail, "a" ).
digit_test( fail, "z" ).
digit_test( fail, "A" ).
digit_test( fail, "Z" ).
digit_test( fail, "~" ).
digit_test( fail, "+" ).
digit_test( fail, "@" ).
test(digit_success,[forall(digit_test(success,Input,C,Rest))]) :-
string_codes(Input,Codes),
phrase(digit(C),Codes,Rest).
test(digit_fail,[fail,forall(digit_test(fail,Input))]) :-
string_codes(Input,Codes),
phrase(digit(_),Codes,[]).
test(digit_recognizer_success,[forall(digit_test(success,Input,_,_))]) :-
string_codes(Input,Codes),
phrase(digit,Codes,_).
test(digit_recognizer_fail,[fail,forall(digit_test(fail,Input))]) :-
string_codes(Input,Codes),
phrase(digit,Codes,[]).
'digit*_test'( success, "", [], [] ).
'digit*_test'( success, "0", [0'0], [] ).
'digit*_test'( success, "1", [0'1], [] ).
'digit*_test'( success, "2", [0'2], [] ).
'digit*_test'( success, "3", [0'3], [] ).
'digit*_test'( success, "4", [0'4], [] ).
'digit*_test'( success, "5", [0'5], [] ).
'digit*_test'( success, "6", [0'6], [] ).
'digit*_test'( success, "7", [0'7], [] ).
'digit*_test'( success, "8", [0'8], [] ).
'digit*_test'( success, "9", [0'9], [] ).
'digit*_test'( success, "00", [0'0,0'0], [] ).
'digit*_test'( success, "09", [0'0,0'9], [] ).
'digit*_test'( success, "90", [0'9,0'0], [] ).
'digit*_test'( success, "99", [0'9,0'9], [] ).
'digit*_test'( success, "000", [0'0,0'0,0'0], [] ).
'digit*_test'( success, "009", [0'0,0'0,0'9], [] ).
'digit*_test'( success, "090", [0'0,0'9,0'0], [] ).
'digit*_test'( success, "099", [0'0,0'9,0'9], [] ).
'digit*_test'( success, "900", [0'9,0'0,0'0], [] ).
'digit*_test'( success, "909", [0'9,0'0,0'9], [] ).
'digit*_test'( success, "990", [0'9,0'9,0'0], [] ).
'digit*_test'( success, "999", [0'9,0'9,0'9], [] ).
'digit*_test'( success, "a", [], [0'a] ).
'digit*_test'( success, "0a", [0'0], [0'a] ).
'digit*_test'( success, "1a", [0'1], [0'a] ).
'digit*_test'( success, "2a", [0'2], [0'a] ).
'digit*_test'( success, "3a", [0'3], [0'a] ).
'digit*_test'( success, "4a", [0'4], [0'a] ).
'digit*_test'( success, "5a", [0'5], [0'a] ).
'digit*_test'( success, "6a", [0'6], [0'a] ).
'digit*_test'( success, "7a", [0'7], [0'a] ).
'digit*_test'( success, "8a", [0'8], [0'a] ).
'digit*_test'( success, "9a", [0'9], [0'a] ).
'digit*_test'( success, "00a", [0'0,0'0], [0'a] ).
'digit*_test'( success, "000a", [0'0,0'0,0'0], [0'a] ).
'digit*_test'( fail, "a" ).
'digit*_test'( fail, "z" ).
'digit*_test'( fail, "A" ).
'digit*_test'( fail, "Z" ).
'digit*_test'( fail, "~" ).
'digit*_test'( fail, "+" ).
'digit*_test'( fail, "@" ).
test(digit_close_list_accumulator_success,[forall('digit*_test'(success,Input,Digit_codes,Rest))]) :-
string_codes(Input,Codes),
phrase('digit*'(Digit_codes),Codes,Rest).
test(digit_close_list_accumulator_fail,[fail,forall('digit*_test'(fail,Input))]) :-
string_codes(Input,Codes),
phrase('digit*'(_),Codes,[]).
test(digit_difference_list_accumulator_success,[forall('digit*_test'(success,Input,Digit_codes,Rest))]) :-
string_codes(Input,Codes),
phrase('digit*'(Digit_codes,[]),Codes,Rest).
test(digit_difference_list_accumulator_fail,[fail,forall('digit*_test'(fail,Input))]) :-
string_codes(Input,Codes),
phrase('digit*'(_,_),Codes,[]).
'digit+_test'( success, "0", [0'0], [] ).
'digit+_test'( success, "1", [0'1], [] ).
'digit+_test'( success, "2", [0'2], [] ).
'digit+_test'( success, "3", [0'3], [] ).
'digit+_test'( success, "4", [0'4], [] ).
'digit+_test'( success, "5", [0'5], [] ).
'digit+_test'( success, "6", [0'6], [] ).
'digit+_test'( success, "7", [0'7], [] ).
'digit+_test'( success, "8", [0'8], [] ).
'digit+_test'( success, "9", [0'9], [] ).
'digit+_test'( success, "00", [0'0,0'0], [] ).
'digit+_test'( success, "09", [0'0,0'9], [] ).
'digit+_test'( success, "90", [0'9,0'0], [] ).
'digit+_test'( success, "99", [0'9,0'9], [] ).
'digit+_test'( success, "000", [0'0,0'0,0'0], [] ).
'digit+_test'( success, "009", [0'0,0'0,0'9], [] ).
'digit+_test'( success, "090", [0'0,0'9,0'0], [] ).
'digit+_test'( success, "099", [0'0,0'9,0'9], [] ).
'digit+_test'( success, "900", [0'9,0'0,0'0], [] ).
'digit+_test'( success, "909", [0'9,0'0,0'9], [] ).
'digit+_test'( success, "990", [0'9,0'9,0'0], [] ).
'digit+_test'( success, "999", [0'9,0'9,0'9], [] ).
'digit+_test'( success, "0a", [0'0], [0'a] ).
'digit+_test'( success, "1a", [0'1], [0'a] ).
'digit+_test'( success, "2a", [0'2], [0'a] ).
'digit+_test'( success, "3a", [0'3], [0'a] ).
'digit+_test'( success, "4a", [0'4], [0'a] ).
'digit+_test'( success, "5a", [0'5], [0'a] ).
'digit+_test'( success, "6a", [0'6], [0'a] ).
'digit+_test'( success, "7a", [0'7], [0'a] ).
'digit+_test'( success, "8a", [0'8], [0'a] ).
'digit+_test'( success, "9a", [0'9], [0'a] ).
'digit+_test'( success, "00a", [0'0,0'0], [0'a] ).
'digit+_test'( success, "000a", [0'0,0'0,0'0], [0'a] ).
'digit+_test'( fail, "" ).
'digit+_test'( fail, "a" ).
'digit+_test'( fail, "z" ).
'digit+_test'( fail, "A" ).
'digit+_test'( fail, "Z" ).
'digit+_test'( fail, "~" ).
'digit+_test'( fail, "+" ).
'digit+_test'( fail, "@" ).
test(digit_close_list_accumulator_success,[forall('digit+_test'(success,Input,Digit_codes,Rest))]) :-
string_codes(Input,Codes),
phrase('digit+'(Digit_codes),Codes,Rest).
test(digit_close_list_accumulator_fail,[fail,forall('digit+_test'(fail,Input))]) :-
string_codes(Input,Codes),
phrase('digit+'(_),Codes,[]).
test(digit_difference_list_accumulator_success,[forall('digit+_test'(success,Input,Digit_codes,Rest))]) :-
string_codes(Input,Codes),
phrase('digit+'(Digit_codes,[]),Codes,Rest).
test(digit_difference_list_accumulator_fail,[fail,forall('digit+_test'(fail,Input))]) :-
string_codes(Input,Codes),
phrase('digit+'(_,_),Codes,[]).
alpha_test( success, "a", 0'a, [] ).
alpha_test( success, "z", 0'z, [] ).
alpha_test( success, "A", 0'A, [] ).
alpha_test( success, "Z", 0'Z, [] ).
alpha_test( fail, "0" ).
alpha_test( fail, "9" ).
alpha_test( fail, "~" ).
alpha_test( fail, "+" ).
alpha_test( fail, "_" ).
test(alpha_success,[forall(alpha_test(success,Input,Alpha_code,Rest))]) :-
string_codes(Input,Codes),
phrase(alpha(Alpha_code),Codes,Rest).
test(alpha_fail,[fail,forall(alpha_test(fail,Input))]) :-
string_codes(Input,Codes),
phrase(alpha(_),Codes,_).
c_identifier_start_test( success, "a", 0'a, [] ).
c_identifier_start_test( success, "z", 0'z, [] ).
c_identifier_start_test( success, "A", 0'A, [] ).
c_identifier_start_test( success, "Z", 0'Z, [] ).
c_identifier_start_test( success, "_", 0'_, [] ).
c_identifier_start_test( fail, "0" ).
c_identifier_start_test( fail, "9" ).
c_identifier_start_test( fail, "~" ).
c_identifier_start_test( fail, "+" ).
test(c_identifier_start_success,[forall(c_identifier_start_test(success,Input,C_identifier_start_code,Rest))]) :-
string_codes(Input,Codes),
phrase(c_identifier_start(C_identifier_start_code),Codes,Rest).
test(c_identifier_start_fail,[fail,forall(c_identifier_start_test(fail,Input))]) :-
string_codes(Input,Codes),
phrase(c_identifier_start(_),Codes,_).
c_identifier_rest_test(success, "", [], [] ).
c_identifier_rest_test(success, "a", [0'a], [] ).
c_identifier_rest_test(success, "z", [0'z], [] ).
c_identifier_rest_test(success, "A", [0'A], [] ).
c_identifier_rest_test(success, "Z", [0'Z], [] ).
c_identifier_rest_test(success, "0", [0'0], [] ).
c_identifier_rest_test(success, "9", [0'9], [] ).
c_identifier_rest_test(success, "_", [0'_], [] ).
c_identifier_rest_test(success, "~", [], [0'~] ).
c_identifier_rest_test(success, "+", [], [0'+] ).
c_identifier_rest_test(success, "aa", [0'a,0'a], [] ).
c_identifier_rest_test(success, "a1", [0'a,0'1], [] ).
c_identifier_rest_test(success, "a~", [0'a], [0'~] ).
c_identifier_rest_test(success, "aaa", [0'a,0'a,0'a], [] ).
c_identifier_rest_test(success, "a1a", [0'a,0'1,0'a], [] ).
c_identifier_rest_test(success, "a11", [0'a,0'1,0'1], [] ).
c_identifier_rest_test(success, "aa~", [0'a,0'a], [0'~] ).
c_identifier_rest_test(success, "aaa~",[0'a,0'a,0'a], [0'~] ).
test(c_identifier_rest_success,[forall(c_identifier_rest_test(success,Input,C_identifier_rest_codes,Rest))]) :-
string_codes(Input,Codes),
phrase(c_identifier_rest(C_identifier_rest_codes,[]),Codes,Rest).
c_identifier_test( success, "a", identifier( a), [] ).
c_identifier_test( success, "z", identifier( z), [] ).
c_identifier_test( success, "A", identifier('A'), [] ).
c_identifier_test( success, "Z", identifier('Z'), [] ).
c_identifier_test( success, "_", identifier('_'), [] ).
c_identifier_test( success, "aa", identifier( aa), [] ).
c_identifier_test( success, "a1", identifier( a1), [] ).
c_identifier_test( success, "a~", identifier( a), [0'~] ).
c_identifier_test( success, "aaa", identifier(aaa), [] ).
c_identifier_test( success, "a1a", identifier(a1a), [] ).
c_identifier_test( success, "a11", identifier(a11), [] ).
c_identifier_test( success, "aa~", identifier( aa), [0'~] ).
c_identifier_test( success, "aaa~",identifier(aaa), [0'~] ).
c_identifier_test( fail, "").
c_identifier_test( fail, "0").
c_identifier_test( fail, "9").
c_identifier_test( fail, "~").
c_identifier_test( fail, "+").
test(c_identifier_success,[forall(c_identifier_test(success,Input,Identifier,Rest))]) :-
string_codes(Input,Codes),
phrase(c_identifier(Identifier),Codes,Rest).
test(c_identifier_fail,[fail,forall(c_identifier_test(fail,Input))]) :-
string_codes(Input,Codes),
phrase(c_identifier(_),Codes,_).
:- end_tests(dcg).
Note: If some of this looks exactly like the code by Wouter Beek in dcg.pl it is because
- If you write lots of DCG code you will eventually refactor it down to almost the same code.
- I did borrow his use of
*
meaningzero or more
and+
meaningone or more
in the predicate names, but even the use of*
and+
are standard.