Did you know ... Search Documentation:
Packs (add-ons) for SWI-Prolog

Package "redcg"

Title:EDCG surface syntax over a record-threaded (Ludemann) back-end
Rating:Not rated. Create the first rating!
Latest version:0.1.0
SHA1 sum:9e32ae68c3fdda7a9e6799cc2f9fa50701d301d3
Author:redcg contributors <redcg@localhost>
Maintainer:redcg contributors <redcg@localhost>
Requires:prolog>=9.0

Reviews

No reviews. Create the first review!.

Details by download location

VersionSHA1#DownloadsURL
0.1.01651f5c9f4392dd07573a3ada20897b79651926c1https://github.com/arizvisa/redcg.git
9e32ae68c3fdda7a9e6799cc2f9fa50701d301d31https://github.com/arizvisa/redcg.git

redcg — EDCG surface syntax over Ludemann's record-threaded back-end

redcg lets you write multi-accumulator predicates in EDCG's syntaxacc_info/5, pass_info/1, pred_info/3 declarations and -->> rules with the built-in dcg accumulator, bare-list terminals, [T]:Acc, X/Acc, Acc/X, X/Acc/Y and insert/2 — while the expansion runs on Peter Ludemann's record-threading technique instead of EDCG's per-accumulator argument threading.

Backwards-compatible with library(edcg): an EDCG program runs under redcg by swapping library(edcg)library(redcg) and the `edcg:redcg:` declaration prefix. EDCG's own synopsis, unique, and puzzle example programs pass verbatim under redcg (see prolog/redcg.plt).

Like library(edcg), the module exports nothing but its operators and an opt-in sentinel. You contribute declarations as `redcg:`-qualified multifile facts (note: facts, not directives), and you enable expansion in a module by importing redcg.

Install

% one-time, from the SWI toplevel:
?- pack_install(redcg).                       % from a registry/URL, or…
?- pack_install('file:///path/to/redcg').     % …from a local checkout (dir URL)

Then, e.g. in `~/.config/swi-prolog/init.pl`:

:- use_module(library(redcg)).

use_module/1 at the top of any file (or a module) is the opt-in signal — only importing modules get -->> expansion; everything else is left untouched (verified: a -->> term in a non-importing module stays a plain term).

Use

:- use_module(library(redcg)).

% declarations are redcg:-qualified FACTS (like edcg:acc_info/5 in EDCG):
redcg:acc_info(count, X, In, Out, plus(X, In, Out)).
redcg:acc_info(items, X, In, Out, Out = [X|In]).   % collecthead (reversed)
redcg:pass_info(limit).
redcg:pred_info(tag_e, 1, [count, items, limit]).

% -->> rules, exactly as in EDCG:
tag_e([])    -->> [].
tag_e([H|T]) -->> L/limit,                         % V/Acc reads the passed value
                  ( { H < L } -> [1]:count, [H]:items ; [] ),
                  tag_e(T).

% call with EDCG's per-accumulator In/Out arg pairs (acc = +2 args, pass = +1):
?- tag_e([10,30,20,40,5], 0, Count, [], Items, 25).
%    Count = 3, Items = [5,20,10].

Declarations must appear before the -->> rules that use them (the field layout is computed from the declared facts at expansion time).

Honest performance picture

Benchmarked on SWI 10.0.2, flattening a 200×200 matrix (straight accumulation):

approachrelativewhy
real library(edcg)fastestthreads only the fields a clause touches, each a bare arg
redcg (this pack)~2× slower than EDCGone :- record compound is rebuilt on every field write
dict-threaded DCG~5× slower than recordget_dict/`.put` call overhead

Ludemann's ~40% win was measured against dicts, not EDCG. So reach for redcg when you want EDCG ergonomics + named, inspectable library(record) state, not when the goal is to beat EDCG on raw accumulation speed. For a single piece of state, a plain DCG still beats all three.

How it works

  1. acc_info/5, pass_info/1, pred_info/3 are multifile predicates you populate with facts.
  2. begin_of_file term-expansion resets per-file emission flags so a re-consult re-emits everything (a persistent flag would survive reload and leave the generated wrappers missing — a real bug, now covered).
  3. On the first -->> rule in a file, redcg emits a `:- record redcg_state(...)` directive — library(record)'s own expansion then turns it into make_/get_/set_ accessors (a term_expansion-emitted :- record is re-expanded downstream). It also emits one public wrapper per predicate.
  4. -->> bodies expand by threading the single record Sin -> Sout, with field reads/writes inlined to plain unification (no accessor call survives).

The if-then-else / disjunction subtlety

Because state is a rigid record (not an open difference list), the branches of ;/-> must NOT share one output-record variable — unifying two differently-updated redcg_state(...) terms at expansion time cross-links their field vars. Each arm therefore threads its own fresh output record, reconciled to the shared output by a runtime = (like DCG translation of disjunction). This is the exact hazard EDCG's _merge_acc exists to handle.

Deliberate limits

  • Declarations must precede the -->> rules. redcg emits one shared :- record at the first rule in a file, so every acc_info/pass_info must be declared above it (EDCG's own example files all follow this).
  • One shared record layout per file. All accumulators/passed args in a file share the single redcg_state compound. (EDCG has the same per-file view.)
  • Acc/X (read-final) and X/Acc/Y (read-current-and-final) are supported by threading the clause-final record: Acc/X / the Y in X/Acc/Y unify with the accumulator's value at the end of the clause, matching EDCG.
  • Cuts inside -->> bodies inherit EDCG's own caveat; verify the expansion with listing/1 if a rule cuts (note: the SWI pretty-printer can garble a nested--> clause; write_canonical/1 shows the true term).

Tests

prolog/redcg.plt — a plunit suite (12 tests, incl. EDCG backwards-compat regressions). This is a pure-Prolog pack, so (like library(edcg)) the tests are not run automatically on install: pack_install/2's test(true) build step only fires for packs with foreign (C) components. Run them manually against a checkout:

?- [library(redcg)],
   consult('prolog/redcg.plt'),
   load_test_files([]),
   run_tests.
% % All 12 tests passed

Contents of pack "redcg"

Pack contains 4 files holding a total of 23.8K bytes.