/* DCG utils in Picat. Some utilities for string parsing using DCG. Please note that moste of the utils are not generating DCGs, just for parsing/identifying. See dcg_utils_test.pi for some example how to use this. This model was created by Hakan Kjellerstrand, hakank@gmail.com See also my Picat page: http://www.hakank.org/picat/ */ module dcg_utils. import util. % % Some general DCG utilities. % (Perhaps they should have a better name, e.g. akin to SNOBOL4 or Icon/Unicon.) % alpha() = "abcdefghijklmnopqrstuvwxyz0123456789". % Matches exactly one character in CharList one_of(C,CharList) --> [C], {membchk(C,CharList)}. one_of(CharList) --> [C], {membchk(C,CharList)}. % no capture % Maches many characters in CharList (no capture) many_of(CharList) --> [C], {membchk(C,CharList)}, many_of(CharList). many_of(_CharList) --> []. % Maches many characters in CharList, with capture many_of([C|Cs],CharList) --> [C], {membchk(C,CharList)}, many_of(Cs,CharList). many_of([],_CharList) --> []. % get and ignore many space characters space --> many_of(" \n\t\r"). % Matches anything, nothing is captured. any --> [] ; [_], any. % Maches anything (as much as possible) with capture. any([C|Rest]) --> [C], any(Rest). any([]) --> []. any_of(Chars) --> [] ; [C], {membchk(C, Chars)}. % Accept any characters that are in the list Valid. any_chars([C|Rest],Valid) --> [C], { char(C), membchk(C,Valid)} , any_chars(Rest,Valid). any_chars([],_) --> []. % Accept any character except characters in the list Except. any_except([C|Rest],Except) --> [C], { char(C), not membchk(C,Except) }, any_except(Rest,Except). any_except([],_Except) --> [] . % Accept only elements (strings) from the list Accepted. any_member(Cs,Accepted) --> any(Cs), { membchk(Cs,Accepted)}. % Accept at least one character, no capture some --> any(Cs), {Cs.len > 0}. % Accept at least one character, with capture some(Cs) --> any(Cs), {Cs.len > 0}. % Accept at least character except characters in the list Except. some_except([C|Rest],Except) --> [C], { char(C), not membchk(C,Except) }, some_except(Rest,Except). some_except([],_Except) --> [] . % Accept strings with a length of at leat Lower and at most Upper some(Cs,Lower,Upper) --> any(Cs), {Cs.len >= Lower, Cs.len <= Upper}. % Accept strings of valid character of a certain length some(Cs,Valid,Lower,Upper) --> any_chars(Cs,Valid), {Cs.len >= Lower, Cs.len <= Upper}. % Accept any string except when it contain Target string. % See split_split2 for an example. any_except_string(Str,Target) --> any(Str), {not once(append(_,Target,_,Str))}. % % Specific types % % Accept string of chars a..z (lower case) lower([C|Rest]) --> [C], {ascii_lowercase(C)}, lower(Rest). lower([]) --> []. % Accept string of chars a..z (lower case) upper([C|Rest]) --> [C], {ascii_uppercase(C)}, upper(Rest). upper([]) --> []. % Accept string of digits 0..9 digits([C|Rest]) --> [C], {ascii_digit(C)}, digits(Rest). digits([]) --> []. % Accept string of chars 0..9a..f hex_digits(Cs) --> any_chars(Cs,"0123456789abcdef"). % Accept string of char 01 bin_digits(Cs) --> any_chars(Cs,"01"). % % Some more utilities using DCG % % % Generalized version of replacing a _string_ with a character or string % (which is not possible with Picat's replace/3). % Example: % Picat> S=replace_string("xabc_nd_abc","abc","xyz") % S = [x,x,y,z,'_',n,d,'_',x,y,z] % replace_string(String,From,To) = Result.flatten => replace_string(Result,From,To,String,[]). replace_string([To|Cs],From,To) --> some(From), replace_string(Cs,From,To), !. % First string replace_string([C,To|Cs],From,To) --> [C], some(From), replace_string(Cs,From,To), !. replace_string([C|Cs],From,To) --> [C], replace_string(Cs,From,To). replace_string([],_From,_To) --> []. % This is splitting on a string. % Note: On failure it might find another way of splitting the string. % % I don't like that one have to use some/1 here. It would be nice with [Split]. % I settle with: any(Tmp2) + Tmp2 == Split split_string(S,Split) = Res => split_string(Res,Split,S,[]). split_string1(Tmp,Split) --> any_except_string(Tmp,Split), any(Tmp2), {Tmp2 == Split} . % some(Split). % [Split] split_string([Tmp|Tmps],Split) --> split_string1(Tmp,Split), split_string(Tmps,Split). split_string([Tmp],Split) --> any_except_string(Tmp,Split). split_string([],_) --> []. % % Get all strings generated by a DCG. % Note: This uses bp.expand_term/2. % dcg_generate(DCG) = dcg_generate(DCG,false). dcg_generate(DCG,PrintClause) = All => bp.expand_term($('-->'(test_xxxyz,DCG)),Clause), if PrintClause then bp.portray_clause(Clause) end, bp.assert(Clause), % Assert the DCG All = findall(S,bp.test_xxxyz(S,[])), bp.retract(Clause). % remove it again