├── .gitignore ├── History.md ├── LICENSE ├── README.md ├── pack.pl ├── prolog ├── acc-pass.pl ├── edcg.pl ├── generic-util.pl ├── messages.pl └── special-util.pl └── t ├── examples.pl ├── synopsis.pl └── unique.pl /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | dev.pl 4 | doc/ 5 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | # v0.9.0 (2015-02-16) 2 | 3 | * First release as an SWI-Prolog pack 4 | 5 | # Origin 6 | 7 | * Originally part of [Aquarius Prolog 1.0](https://www.info.ucl.ac.be/~pvr/aquarius.html) 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 1992 Peter Van Roy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Synopsis 2 | 3 | ```prolog 4 | :- use_module(library(edcg)). 5 | 6 | % Declare accumulators 7 | edcg:acc_info(adder, X, In, Out, plus(X,In,Out)). 8 | 9 | % Declare predicates using these hidden arguments 10 | edcg:pred_info(len,0,[adder,dcg]). 11 | edcg:pred_info(increment,0,[adder]). 12 | 13 | increment -->> 14 | [1]:adder. % add one to the accumulator 15 | 16 | 17 | len(Xs,N) :- 18 | len(0,N,Xs,[]). 19 | 20 | len -->> 21 | [_], % 'dcg' accumulator has an element 22 | !, 23 | increment, % increment the 'adder' accumulator 24 | len. 25 | len -->> 26 | []. 27 | ``` 28 | 29 | # Introduction 30 | 31 | DCG notation gives us a single, hidden accumulator. Extended DCG notation (implemented by this library) lets predicates have arbitrarily many hidden accumulators. As demonstrated by the Synopsis above, those accumulators can be implemented with arbitrary goals (like plus/3). 32 | 33 | Benefits of this library: 34 | 35 | * avoid tedium and errors from manually threading accumulators through your predicates 36 | * add or remove accumulators with a single declaration 37 | * change accumulator implementation with a single declaration (ex, switching from ordsets to rbtrees) 38 | 39 | # Syntax 40 | 41 | Extended DCG syntax is very similar to DCG notation. An EDCG is created with clauses whose neck is the `-->>` operator. The following syntax is supported inside an EDCG clause: 42 | 43 | * `{Goal}` - don't expand any hidden arguments of `Goal` 44 | * `Goal` - expand all hidden arguments of Goal that are also in the head. Those hidden arguments not in the head are given default values. 45 | * `Goal:L` - If `Goal` has no hidden arguments then force the expansion of all arguments in `L` in the order given. If `Goal` has hidden arguments then expand all of them, using the contents of `L` to override the expansion. `L` is either a term of the form `Acc`, `Acc(Left,Right)`, `Pass`, `Pass(Value)`, or a list of such terms. When present, the arguments `Left`, `Right`, and `Value` override the default values of arguments not in the head. 46 | * `List:Acc` - Accumulate a list of terms in the accumulator `Acc` 47 | * `List` - Accumulate a list of terms in the accumulator `dcg` 48 | * `X/Acc` - Unify `X` with the left term for the accumulator `Acc` 49 | * `Acc/X` - Unify `X` with the right term for the accumulator `Acc` 50 | * `X/Acc/Y` - Unify `X` with the left and `Y` with the right term for the accumulator `Acc` 51 | * `insert(X,Y):Acc` - Insert the arguments `X` and `Y` into the chain implementing the accumulator `Acc`. This is useful when the value of the accumulator changes radically because `X` and `Y` may be the arguments of an arbitrary relation 52 | * `insert(X,Y)` - Insert the arguments `X` and `Y` into the chain implementing the accumulator `dcg`. This inserts the difference list X-Y into the accumulated list 53 | 54 | # Declarations 55 | 56 | ## Declaration of Predicates 57 | 58 | Predicates are declared with facts of the following form: 59 | 60 | ```prolog 61 | pred_info(Name, Arity, List) 62 | ``` 63 | 64 | The predicate `Name/Arity` has the hidden parameters given in `List`. The parameters are added in the order given by `List` and their names must be atoms. 65 | 66 | ## Declaration of Accumulators 67 | 68 | Accumulators are declared with facts in one of two forms. The short form is: 69 | 70 | ```prolog 71 | acc_info(Acc, Term, Left, Right, Joiner) 72 | ``` 73 | 74 | The long form is: 75 | 76 | ``` 77 | acc_info(Acc, Term, Left, Right, Joiner, LStart, RStart) 78 | ``` 79 | 80 | In most cases the short form gives sufficient information. It declares the accumulator `Acc`, which must be an atom, along with the accumulating function, `Joiner`, and its arguments `Term`, the term to be accumulated, and `Left` & `Right`, the variables used in chaining. 81 | 82 | The long form of `acc_info` is useful in more complex programs. It contains two additional arguments, `LStart` and `RStart`, that are used to give default starting values for an accumulator occurring in a body goal that does not occur in the head. The starting values are given to the unused accumulator to ensure that it will execute correctly even though its value is not used. Care is needed to give correct values for `LStart` and `RStart`. For DCG-like list accumulation both may remain unbound. 83 | 84 | Two conventions are used for the two variables used in chaining depending on which direction the accumulation is done. For forward accumulation, `Left` is the input and `Right` is the output. For reverse accumulation, `Right` is the input and `Left` is the output. 85 | 86 | ## Declaration of Passed Arguments 87 | 88 | Passed arguments are conceptually the same as accumulators with `=/2` as the joiner function. Passed arguments are declared as facts in one of two forms. The short form is: 89 | 90 | ```prolog 91 | pass_info(Pass) 92 | ``` 93 | 94 | The long form is: 95 | 96 | ```prolog 97 | pass_info(Pass, PStart) 98 | ``` 99 | 100 | In most cases the short form is sufficient. It declares a passed argument `Pass`, that must be an atom. The long form also contains the starting value `PStart` that is used to give a default value for a passed argument in a body goal that does not occur in the head. Most of the time this situation does not occur. 101 | 102 | # Installation 103 | 104 | Using SWI-Prolog 7.1 or later: 105 | 106 | ?- pack_install(edcg). 107 | 108 | This module uses [semantic versioning](http://semver.org/). 109 | 110 | Source code available and pull requests accepted at 111 | http://github.com/mndrix/edcg 112 | 113 | @license mit 114 | 115 | # Additional documentation 116 | 117 | Peter Van Roy's page: [Declarative Programming with State](https://www.info.ucl.ac.be/~pvr/edcg.html) 118 | 119 | Technical Report UCB/CSD-90-583 [Extended DCG Notation: A Tool for Applicative Programming in Prolog](https://www2.eecs.berkeley.edu/Pubs/TechRpts/1990/5471.html) by Peter Van Roy 120 | 121 | * The Tech Report's PDF is [here](https://www2.eecs.berkeley.edu/Pubs/TechRpts/1990/CSD-90-583.pdf) 122 | 123 | A short [Wikipedia article](https://en.wikipedia.org/wiki/Definite_clause_grammar#Extensions) on DCGs and extensions. 124 | -------------------------------------------------------------------------------- /pack.pl: -------------------------------------------------------------------------------- 1 | name(edcg). 2 | 3 | title('Extended DCG'). 4 | version('0.9.0'). 5 | download('https://github.com/mndrix/edcg/archive/v0.9.0.zip'). 6 | 7 | author('Peter Van Roy','peter.vanroy@uclouvain.be'). 8 | packager('Michael Hendricks','michael@ndrix.org'). 9 | maintainer('Michael Hendricks','michael@ndrix.org'). 10 | home('https://github.com/mndrix/edcg'). 11 | -------------------------------------------------------------------------------- /prolog/acc-pass.pl: -------------------------------------------------------------------------------- 1 | % Operations on the Acc and Pass data structures: 2 | 3 | :- ['generic-util.pl']. 4 | :- ['special-util.pl']. 5 | 6 | % Create the Acc and Pass data structures: 7 | % Acc contains terms of the form acc(A,LeftA,RightA) where A is the name of an 8 | % accumulator, and RightA and LeftA are the accumulating parameters. 9 | % Pass contains terms of the form pass(A,Arg) where A is the name of a passed 10 | % argument, and Arg is the argument. 11 | '_create_acc_pass'([], _, _, [], []). 12 | '_create_acc_pass'([A|AList], Index, TGoal, [acc(A,LeftA,RightA)|Acc], Pass) :- 13 | '_is_acc'(A), !, 14 | Index1 is Index+1, 15 | arg(Index1, TGoal, LeftA), 16 | Index2 is Index+2, 17 | arg(Index2, TGoal, RightA), 18 | '_create_acc_pass'(AList, Index2, TGoal, Acc, Pass). 19 | '_create_acc_pass'([A|AList], Index, TGoal, Acc, [pass(A,Arg)|Pass]) :- 20 | '_is_pass'(A), !, 21 | Index1 is Index+1, 22 | arg(Index1, TGoal, Arg), 23 | '_create_acc_pass'(AList, Index1, TGoal, Acc, Pass). 24 | '_create_acc_pass'([A|_AList], _Index, _TGoal, _Acc, _Pass) :- 25 | \+'_is_acc'(A), 26 | \+'_is_pass'(A), 27 | print_message(error,not_a_hidden_param(A)). 28 | 29 | 30 | % Use the Acc and Pass data structures to create the arguments of a body goal: 31 | % Add the hidden parameters named in GList to the goal. 32 | '_use_acc_pass'([], _, _, Acc, Acc, _). 33 | % 1a. The accumulator A is used in the head: 34 | '_use_acc_pass'([A|GList], Index, TGoal, Acc, NewAcc, Pass) :- 35 | '_replace_acc'(A, LeftA, RightA, MidA, RightA, Acc, MidAcc), !, 36 | Index1 is Index+1, 37 | arg(Index1, TGoal, LeftA), 38 | Index2 is Index+2, 39 | arg(Index2, TGoal, MidA), 40 | '_use_acc_pass'(GList, Index2, TGoal, MidAcc, NewAcc, Pass). 41 | % 1b. The accumulator A is not used in the head: 42 | '_use_acc_pass'([A|GList], Index, TGoal, Acc, NewAcc, Pass) :- 43 | '_acc_info'(A, LStart, RStart), !, 44 | Index1 is Index+1, 45 | arg(Index1, TGoal, LStart), 46 | Index2 is Index+2, 47 | arg(Index2, TGoal, RStart), 48 | '_use_acc_pass'(GList, Index2, TGoal, Acc, NewAcc, Pass). 49 | % 2a. The passed argument A is used in the head: 50 | '_use_acc_pass'([A|GList], Index, TGoal, Acc, NewAcc, Pass) :- 51 | '_is_pass'(A), 52 | member(pass(A,Arg), Pass), !, 53 | Index1 is Index+1, 54 | arg(Index1, TGoal, Arg), 55 | '_use_acc_pass'(GList, Index1, TGoal, Acc, NewAcc, Pass). 56 | % 2b. The passed argument A is not used in the head: 57 | '_use_acc_pass'([A|GList], Index, TGoal, Acc, NewAcc, Pass) :- 58 | '_pass_info'(A, AStart), !, 59 | Index1 is Index+1, 60 | arg(Index1, TGoal, AStart), 61 | '_use_acc_pass'(GList, Index1, TGoal, Acc, NewAcc, Pass). 62 | % 3. Defaulty case when A does not exist: 63 | '_use_acc_pass'([A|_GList], _Index, _TGoal, Acc, Acc, _Pass) :- 64 | print_message(error,not_a_hidden_param(A)). 65 | 66 | % Finish the Acc data structure: 67 | % Link its Left and Right accumulation variables together in pairs: 68 | '_finish_acc'([]). 69 | '_finish_acc'([acc(_,Link,Link)|Acc]) :- '_finish_acc'(Acc). 70 | 71 | % Replace elements in the Acc data structure: 72 | % Succeeds iff replacement is successful. 73 | '_replace_acc'(A, L1, R1, L2, R2, Acc, NewAcc) :- 74 | member(acc(A,L1,R1), Acc), !, 75 | '_replace'(acc(A,_,_), acc(A,L2,R2), Acc, NewAcc). 76 | 77 | % Combine two accumulator lists ('or'ing their values) 78 | '_merge_acc'([], [], G1, G1, [], G2, G2, []) :- !. 79 | '_merge_acc'([acc(Acc,OL,R)|Accs], [acc(Acc,L1,R)|Accs1], G1, NG1, 80 | [acc(Acc,L2,R)|Accs2], G2, NG2, [acc(Acc,NL,R)|NewAccs]) :- !, 81 | ( ( OL == L1, OL \== L2 ) -> 82 | MG1 = (G1,L1=L2), MG2 = G2, NL = L2 83 | ; ( OL == L2, OL \== L1 ) -> 84 | MG2 = (G2,L2=L1), MG1 = G1, NL = L1 85 | ; MG1 = G1, MG2 = G2, L1 = L2, L2 = NL ), 86 | '_merge_acc'(Accs, Accs1, MG1, NG1, Accs2, MG2, NG2, NewAccs). 87 | -------------------------------------------------------------------------------- /prolog/edcg.pl: -------------------------------------------------------------------------------- 1 | :- module( edcg, [ 2 | op(1200, xfx, '-->>'), % Similar to '-->' 3 | edcg_import_sentinel/0 4 | ]). 5 | 6 | :- use_module(library(lists), [member/2]). 7 | 8 | :- ['acc-pass.pl']. 9 | :- ['special-util.pl']. 10 | :- ['generic-util.pl']. 11 | 12 | 13 | % these predicates define extra arguments 14 | :- multifile 15 | acc_info/5, 16 | acc_info/7, 17 | pass_info/1, 18 | pass_info/2. 19 | 20 | 21 | % True if the module being read has opted-in to EDCG macro expansion. 22 | wants_edcg_expansion :- 23 | prolog_load_context(module, Module), 24 | Module \== edcg, % don't expand macros in our own library 25 | predicate_property(Module:edcg_import_sentinel, imported_from(edcg)). 26 | 27 | % dummy predicate exported to detect which modules want EDCG expansion 28 | edcg_import_sentinel. 29 | 30 | 31 | % Perform EDCG macro expansion 32 | user:term_expansion((H-->>B), (TH:-TB)) :- 33 | wants_edcg_expansion, 34 | functor(H, Na, Ar), 35 | '_has_hidden'(H, HList), 36 | debug(edcg,'Expanding ~w',[H]), 37 | '_new_goal'(H, HList, HArity, TH), 38 | '_create_acc_pass'(HList, HArity, TH, Acc, Pass), 39 | '_expand_goal'(B, TB, Na/Ar, HList, Acc, NewAcc, Pass), 40 | '_finish_acc'(NewAcc), !. 41 | 42 | % Expand a goal: 43 | '_expand_goal'((G1,G2), (TG1,TG2), NaAr, HList, Acc, NewAcc, Pass) :- 44 | '_expand_goal'(G1, TG1, NaAr, HList, Acc, MidAcc, Pass), 45 | '_expand_goal'(G2, TG2, NaAr, HList, MidAcc, NewAcc, Pass). 46 | '_expand_goal'((G1->G2;G3), (TG1->TG2;TG3), NaAr, HList, Acc, NewAcc, Pass) :- 47 | '_expand_goal'(G1, TG1, NaAr, HList, Acc, MidAcc, Pass), 48 | '_expand_goal'(G2, MG2, NaAr, HList, MidAcc, Acc1, Pass), 49 | '_expand_goal'(G3, MG3, NaAr, HList, Acc, Acc2, Pass), 50 | '_merge_acc'(Acc, Acc1, MG2, TG2, Acc2, MG3, TG3, NewAcc). 51 | '_expand_goal'((G1;G2), (TG1;TG2), NaAr, HList, Acc, NewAcc, Pass) :- 52 | '_expand_goal'(G1, MG1, NaAr, HList, Acc, Acc1, Pass), 53 | '_expand_goal'(G2, MG2, NaAr, HList, Acc, Acc2, Pass), 54 | '_merge_acc'(Acc, Acc1, MG1, TG1, Acc2, MG2, TG2, NewAcc). 55 | '_expand_goal'((G1->G2), (TG1->TG2), NaAr, HList, Acc, NewAcc, Pass) :- 56 | '_expand_goal'(G1, TG1, NaAr, HList, Acc, MidAcc, Pass), 57 | '_expand_goal'(G2, TG2, NaAr, HList, MidAcc, NewAcc, Pass). 58 | '_expand_goal'((\+G), (\+TG), NaAr, HList, Acc, Acc, Pass) :- 59 | '_expand_goal'(G, TG, NaAr, HList, Acc, _TempAcc, Pass). 60 | '_expand_goal'({G}, G, _, _, Acc, Acc, _) :- !. 61 | '_expand_goal'(insert(X,Y), LeftA=X, _, _, Acc, NewAcc, _) :- 62 | '_replace_acc'(dcg, LeftA, RightA, Y, RightA, Acc, NewAcc), !. 63 | '_expand_goal'(insert(X,Y):A, LeftA=X, _, _, Acc, NewAcc, _) :- 64 | '_replace_acc'(A, LeftA, RightA, Y, RightA, Acc, NewAcc), 65 | debug(edcg,'Expanding accumulator goal: ~w',[insert(X,Y):A]), 66 | !. 67 | % Force hidden arguments in L to be appended to G: 68 | '_expand_goal'((G:A), TG, _, _HList, Acc, NewAcc, Pass) :- 69 | \+'_list'(G), 70 | '_has_hidden'(G, []), !, 71 | '_make_list'(A, AList), 72 | '_new_goal'(G, AList, GArity, TG), 73 | '_use_acc_pass'(AList, GArity, TG, Acc, NewAcc, Pass). 74 | % Use G's regular hidden arguments & override defaults for those arguments 75 | % not in the head: 76 | '_expand_goal'((G:A), TG, _, _HList, Acc, NewAcc, Pass) :- 77 | \+'_list'(G), 78 | '_has_hidden'(G, GList), GList\==[], !, 79 | '_make_list'(A, L), 80 | '_new_goal'(G, GList, GArity, TG), 81 | '_replace_defaults'(GList, NGList, L), 82 | '_use_acc_pass'(NGList, GArity, TG, Acc, NewAcc, Pass). 83 | '_expand_goal'((L:A), Joiner, NaAr, _, Acc, NewAcc, _) :- 84 | '_list'(L), !, 85 | '_joiner'(L, A, NaAr, Joiner, Acc, NewAcc). 86 | '_expand_goal'(L, Joiner, NaAr, _, Acc, NewAcc, _) :- 87 | '_list'(L), !, 88 | '_joiner'(L, dcg, NaAr, Joiner, Acc, NewAcc). 89 | '_expand_goal'((X/A), true, _, _, Acc, Acc, _) :- 90 | atomic(A), 91 | member(acc(A,X,_), Acc), 92 | debug(edcg,'Expanding accumulator goal: ~w',[X/A]), 93 | !. 94 | '_expand_goal'((X/A), true, _, _, Acc, Acc, Pass) :- 95 | atomic(A), 96 | member(pass(A,X), Pass), 97 | debug(edcg,'Expanding passed argument goal: ~w',[X/A]), 98 | !. 99 | '_expand_goal'((A/X), true, _, _, Acc, Acc, _) :- 100 | atomic(A), 101 | member(acc(A,_,X), Acc), !. 102 | '_expand_goal'((X/A/Y), true, _, _, Acc, Acc, _) :- 103 | var(X), var(Y), atomic(A), 104 | member(acc(A,X,Y), Acc), !. 105 | '_expand_goal'((X/Y), true, NaAr, _, Acc, Acc, _) :- 106 | print_message(warning,missing_hidden_parameter(NaAr,X/Y)). 107 | % Defaulty cases: 108 | '_expand_goal'(G, TG, _HList, _, Acc, NewAcc, Pass) :- 109 | '_has_hidden'(G, GList), !, 110 | '_new_goal'(G, GList, GArity, TG), 111 | '_use_acc_pass'(GList, GArity, TG, Acc, NewAcc, Pass). 112 | -------------------------------------------------------------------------------- /prolog/generic-util.pl: -------------------------------------------------------------------------------- 1 | % Generic utilities 2 | 3 | % Match arguments L, L+1, ..., H of the predicates P and Q: 4 | '_match'(L, H, _, _) :- L>H, !. 5 | '_match'(L, H, P, Q) :- L= 4 | ['In ~w the accumulator ''~w'' does not exist'-[Predicate,Accumulator]]. 5 | prolog:message(missing_hidden_parameter(Predicate,Term)) --> 6 | ['In ~w the term ''~w'' uses a non-existent hidden parameter.'-[Predicate,Term]]. 7 | prolog:message(not_a_hidden_param(Name)) --> 8 | ['~w is not a hidden parameter'-[Name]]. 9 | -------------------------------------------------------------------------------- /prolog/special-util.pl: -------------------------------------------------------------------------------- 1 | % Specialized utilities: 2 | 3 | :- ['generic-util.pl']. 4 | :- ['messages.pl']. 5 | 6 | % Given a goal Goal and a list of hidden parameters GList 7 | % create a new goal TGoal with the correct number of arguments. 8 | % Also return the arity of the original goal. 9 | '_new_goal'(Goal, GList, GArity, TGoal) :- 10 | functor(Goal, Name, GArity), 11 | '_number_args'(GList, GArity, TArity), 12 | functor(TGoal, Name, TArity), 13 | '_match'(1, GArity, Goal, TGoal). 14 | 15 | % Add the number of arguments needed for the hidden parameters: 16 | '_number_args'([], N, N). 17 | '_number_args'([A|List], N, M) :- 18 | '_is_acc'(A), !, 19 | N2 is N+2, 20 | '_number_args'(List, N2, M). 21 | '_number_args'([A|List], N, M) :- 22 | '_is_pass'(A), !, 23 | N1 is N+1, 24 | '_number_args'(List, N1, M). 25 | '_number_args'([_|List], N, M) :- !, 26 | % error caught elsewhere 27 | '_number_args'(List, N, M). 28 | 29 | % Give a list of G's hidden parameters: 30 | '_has_hidden'(G, GList) :- 31 | functor(G, GName, GArity), 32 | pred_info(GName, GArity, GList). 33 | '_has_hidden'(G, []) :- 34 | functor(G, GName, GArity), 35 | \+pred_info(GName, GArity, _). 36 | 37 | % Succeeds if A is an accumulator: 38 | '_is_acc'(A) :- atomic(A), !, '_acc_info'(A, _, _, _, _, _, _). 39 | '_is_acc'(A) :- functor(A, N, 2), !, '_acc_info'(N, _, _, _, _, _, _). 40 | 41 | % Succeeds if A is a passed argument: 42 | '_is_pass'(A) :- atomic(A), !, '_pass_info'(A, _). 43 | '_is_pass'(A) :- functor(A, N, 1), !, '_pass_info'(N, _). 44 | 45 | % Get initial values for the accumulator: 46 | '_acc_info'(AccParams, LStart, RStart) :- 47 | functor(AccParams, Acc, 2), 48 | '_is_acc'(Acc), !, 49 | arg(1, AccParams, LStart), 50 | arg(2, AccParams, RStart). 51 | '_acc_info'(Acc, LStart, RStart) :- 52 | '_acc_info'(Acc, _, _, _, _, LStart, RStart). 53 | 54 | % Isolate the internal database from the user database: 55 | '_acc_info'(Acc, Term, Left, Right, Joiner, LStart, RStart) :- 56 | acc_info(Acc, Term, Left, Right, Joiner, LStart, RStart). 57 | '_acc_info'(Acc, Term, Left, Right, Joiner, _, _) :- 58 | acc_info(Acc, Term, Left, Right, Joiner). 59 | '_acc_info'(dcg, Term, Left, Right, Left=[Term|Right], _, []). 60 | 61 | % Get initial value for the passed argument: 62 | % Also, isolate the internal database from the user database. 63 | '_pass_info'(PassParam, PStart) :- 64 | functor(PassParam, Pass, 1), 65 | '_is_pass'(Pass), !, 66 | arg(1, PassParam, PStart). 67 | '_pass_info'(Pass, PStart) :- 68 | pass_info(Pass, PStart). 69 | '_pass_info'(Pass, _) :- 70 | pass_info(Pass). 71 | 72 | % Calculate the joiner for an accumulator A: 73 | '_joiner'([], _, _, true, Acc, Acc). 74 | '_joiner'([Term|List], A, NaAr, (Joiner,LJoiner), Acc, NewAcc) :- 75 | '_replace_acc'(A, LeftA, RightA, MidA, RightA, Acc, MidAcc), 76 | '_acc_info'(A, Term, LeftA, MidA, Joiner, _, _), !, 77 | '_joiner'(List, A, NaAr, LJoiner, MidAcc, NewAcc). 78 | % Defaulty case: 79 | '_joiner'([_Term|List], A, NaAr, Joiner, Acc, NewAcc) :- 80 | print_message(warning, missing_accumulator(NaAr,A)), 81 | '_joiner'(List, A, NaAr, Joiner, Acc, NewAcc). 82 | 83 | % Replace hidden parameters with ones containing initial values: 84 | '_replace_defaults'([], [], _). 85 | '_replace_defaults'([A|GList], [NA|NGList], AList) :- 86 | '_replace_default'(A, NA, AList), 87 | '_replace_defaults'(GList, NGList, AList). 88 | 89 | '_replace_default'(A, NewA, AList) :- % New initial values for accumulator. 90 | functor(NewA, A, 2), 91 | member(NewA, AList), !. 92 | '_replace_default'(A, NewA, AList) :- % New initial values for passed argument. 93 | functor(NewA, A, 1), 94 | member(NewA, AList), !. 95 | '_replace_default'(A, NewA, _) :- % Use default initial values. 96 | A=NewA. 97 | -------------------------------------------------------------------------------- /t/examples.pl: -------------------------------------------------------------------------------- 1 | :- use_module(library(edcg)). 2 | 3 | % Declare accumulators 4 | edcg:acc_info(castor,_,_,_,true). 5 | edcg:acc_info(fwd, T, In, Out, Out=[T|In]). % forward accumulator 6 | edcg:acc_info(rev, T, Out, In, Out=[T|In]). % reverse accumulator 7 | edcg:acc_info(adder, I, In, Out, plus(I,In,Out)). % adder 8 | 9 | % Declare passed arguments 10 | edcg:pass_info(pollux). 11 | 12 | % Declare predicates using these hidden arguments 13 | edcg:pred_info(p,1,[castor,pollux]). 14 | edcg:pred_info(q,1,[castor,pollux]). 15 | edcg:pred_info(r,1,[castor,pollux]). 16 | edcg:pred_info(flist,1,[fwd]). 17 | edcg:pred_info(rlist,1,[rev]). 18 | edcg:pred_info(sum_first_n,1,[adder]). 19 | edcg:pred_info(sum,0,[adder,dcg]). 20 | 21 | 22 | % flist(N,[],List) creates the list [1,2,...,N] 23 | flist(0) -->> 24 | !, 25 | []. 26 | flist(N) -->> 27 | N>0, 28 | [N]:fwd, 29 | N1 is N-1, 30 | flist(N1). 31 | 32 | % rlist(N,List,[]) creates the list [N,...,2,1] 33 | rlist(0) -->> 34 | !, 35 | []. 36 | rlist(N) -->> 37 | N>0, 38 | [N]:rev, 39 | N1 is N-1, 40 | rlist(N1). 41 | 42 | 43 | % sum(N,0,Sum) adds the numbers 1,2,...,N 44 | sum_first_n(0) -->> 45 | !, 46 | []. 47 | sum_first_n(N) -->> 48 | N>0, 49 | [N]:adder, 50 | N1 is N-1, 51 | sum_first_n(N1). 52 | 53 | sum(Xs,Sum) :- 54 | sum(0,Sum,Xs,[]). 55 | sum -->> 56 | [X], 57 | !, 58 | [X]:adder, 59 | sum. 60 | sum -->> 61 | []. 62 | 63 | 64 | :- use_module(library(tap)). 65 | 66 | 'flist solutions' :- 67 | flist(7,[],L), 68 | L == [1,2,3,4,5,6,7]. 69 | 70 | 71 | 'rlist solutions' :- 72 | rlist(7,L,[]), 73 | L == [7,6,5,4,3,2,1]. 74 | 75 | 'sum_first_n: trivial' :- 76 | sum_first_n(0,0,Sum), 77 | Sum == 0. 78 | 79 | 'sum_first_n: four' :- 80 | sum_first_n(4,0,Sum), 81 | Sum is 4+3+2+1. 82 | 83 | 'sum [2,2,3]' :- 84 | sum([2,2,3],Sum), 85 | Sum is 2+2+3. 86 | -------------------------------------------------------------------------------- /t/synopsis.pl: -------------------------------------------------------------------------------- 1 | :- use_module(library(edcg)). 2 | 3 | % Declare accumulators 4 | edcg:acc_info(adder, X, In, Out, plus(X,In,Out)). 5 | 6 | % Declare predicates using these hidden arguments 7 | edcg:pred_info(len,0,[adder,dcg]). 8 | edcg:pred_info(increment,0,[adder]). 9 | 10 | increment -->> 11 | [1]:adder. 12 | 13 | 14 | len(Xs,N) :- 15 | len(0,N,Xs,[]). 16 | 17 | len -->> 18 | [_], 19 | !, 20 | increment, 21 | len. 22 | len -->> 23 | []. 24 | 25 | 26 | :- use_module(library(tap)). 27 | 28 | len([],0). 29 | len([a],1). 30 | len([a,b,a],3). 31 | -------------------------------------------------------------------------------- /t/unique.pl: -------------------------------------------------------------------------------- 1 | :- use_module(library(edcg)). 2 | :- use_module(library(rbtrees), [rb_insert/4]). 3 | 4 | % Declare accumulators 5 | edcg:acc_info(set, X, In, Out, rb_insert(In,X,seen,Out)). 6 | 7 | % Declare predicates using these hidden arguments 8 | edcg:pred_info(unique,0,[dcg,set]). 9 | 10 | 11 | %% unique(+Xs:list, -Count:integer, -Unique:list) 12 | unique(Xs,Unique) :- 13 | rb_empty(Empty), 14 | unique(Xs,[],Empty,Final), 15 | rb_keys(Final, Unique). 16 | 17 | unique -->> 18 | [X], % X present in the list 19 | [X]:set, % and present in the set 20 | !, 21 | unique. % same for the rest of the list 22 | unique -->> 23 | []. 24 | 25 | 26 | :- use_module(library(tap)). 27 | 28 | unique([],[]). 29 | unique([a],[a]). 30 | unique([a,b,a],[a,b]). 31 | --------------------------------------------------------------------------------