# Editing emacs buffer regions by Prolog scripts

**URL:** <https://swi-prolog.discourse.group/t/editing-emacs-buffer-regions-by-prolog-scripts/7439>\
**Category:** General\
**Created:** [May 6, 2024, 4:46pm UTC](https://swi-prolog.discourse.group/t/editing-emacs-buffer-regions-by-prolog-scripts/7439 "2024-05-06T16:46:14Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![kuniaki.mukai](https://avatars.discourse-cdn.com/v4/letter/k/c67d28/32.png) [@kuniaki.mukai](https://swi-prolog.discourse.group/u/kuniaki.mukai)\
**Post date:** [May 6, 2024, 4:46pm UTC](https://swi-prolog.discourse.group/t/editing-emacs-buffer-regions-by-prolog-scripts/7439/1 "2024-05-06T16:46:14Z")

</div>

Objective of this post to share ideas to apply Prolog to edit the emacs buffer regions. First,  
I would like to explain an example case. As I posted I had written a “simple minded prolog-like prover”.

> [@Needing help with call\_with\_depth\_limit/3](https://swi-prolog.discourse.group/t/needing-help-with-call-with-depth-limit-3/7398/9):
>
> So I got curious of writing a (toy) simple-minded resolution prover, which is based on:
> 
> - iterated deepening ( hand made on simple limited purpose ).
> - prolog like proof search:
> - topdown
> - left-to-right
> 
> - history of literals relolved upon (tabling ?).
> 
> Fortunately, functionality of my old codes was  
> fully recovered. some example queries follows.

So far more than one hundred small valid formulae and also also about 20 invalid ones, it returns as expeced.

```prolog
% ?- time(test_prove(valid_formula)).
%@ all(A,p==f(A))==(p==all(B,f(B)))
%@ Proved.
%@ p==all(A,f(A))->all(B,(f(B)->p))
%@ Proved.

<snip>

%@ ** 91 formulae tested.

%@ % 487,006 inferences, 0.041 CPU in 0.106 seconds (38% CPU, 11995517 Lips)
%@ true.

% ?- time(test_prove(invalid_formula)).

%@ some(A,p(A))->all(A,p(A))
%@ NOT provable.
%@
%@ all(A,some(B,p(A,B)))->some(B,all(A,p(A,B)))
%@ NOT provable.

<snip>

%@ ** 19 formulae tested.
%@ % 14,070 inferences, 0.002 CPU in 0.002 seconds (91% CPU, 8653137 Lips)
%@ true.

```

However I have still no idea about “timeout” formulae.

```prolog
timeout_formula(all(X, p(X))-> some(Y, (p(Y), p(f(Y))))

```

Now this is the place I used prolog to edit emacs buffer region.  
I noticed that the above predicates `valid_formula/invalid_formula`  
were redundant. In fact many redundant formulae like this.

```prolog
valid_formula(A->A).
valid_formula(B->B).

```

So I wrote an emacs buffer handler in prolog

```prolog
handle([free, variant])--> region, % get region in codes.
	split, % split by `\n`
	remove([]), % remove empty lines.
	maplist(pred(([X, Y-S]:- string_codes(S, X),	% parse each line as a term.
				 term_string(Y, S, [module(fol_prover)])))),
	peek(Terms, []),	% get current contents, and put [] as a inital value.
	add_non_variant(Terms),	% remove terms so that no variant pairs there.
	maplist(pred([_-S, [S,"\n"]])),	% put orginal terms with new line code at end.
	reverse,
	overwrite. % replace the input region with the slimmed result.

% ?- add_non_variant([A-1, B-2, A-1], [], R).
%@ R = [A-1].
% ?- add_non_variant([f(A)-1, g(B)-2, f(A)-2], [], R).
%@ R = [g(B)-2, f(A)-1].

add_non_variant([], X, X).
add_non_variant([P|U], X, Y):-add_non_variant_one(P, X, Z),
	add_non_variant(U, Z, Y).
%
add_non_variant_one(A-B, X, Y):-
	(	member(C-_, X), variant(A, C) -> Y = X
	;	Y = [A-B|X]
	).

```

For example, suppose the current region with

```prolog
valid_formula(A->A).
valid_formula(B->B).
valid_formula(C->C).

```

becomes this with variants formula removed.

```prolog
valid_formula(A->A).

```

The “handle” critically uses swi-prolog builtins `term_string/3`, and `variant/2.`

Now I am wondering how ones edit such region in a handy way. So far I never  
heard about such use of prolog to edit emacs buffer. I believe there maybe  
smarter ways to edit emacs buffer by prolog. However, as I have some experiences on this and still several basic questions I would like to ask emacs experts, I would like to share idea and experiences.

---

<div class="post-metadata">

**Author:** ![oskardrums](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/oskardrums/32/3594_2.png) [@oskardrums](https://swi-prolog.discourse.group/u/oskardrums)\
**Post date:** [May 6, 2024, 5:06pm UTC](https://swi-prolog.discourse.group/t/editing-emacs-buffer-regions-by-prolog-scripts/7439/2 "2024-05-06T17:06:44Z")

</div>

Hi there,

FWIW, with Sweep you can call Elisp functions (such as `insert` and `delete-region` and other buffer editing functions) from your SWI-Prolog code. See [Sweep: SWI-Prolog Embedded in Emacs](https://eshelyaron.com/sweep.html#Call-Back-to-Elisp)

HTH

EDIT: to be clear, Sweep also lets you invoke Prolog from Elisp (and vice versa, as mentioned above), so you can define an Emacs command which calls Prolog, which then calls Elisp to edit an Emacs buffer. Although TBH, for the purpose of editing Emacs buffers, I’d just stick to Elisp most probably 🙂

---

<div class="post-metadata">

**Author:** ![kuniaki.mukai](https://avatars.discourse-cdn.com/v4/letter/k/c67d28/32.png) [@kuniaki.mukai](https://swi-prolog.discourse.group/u/kuniaki.mukai)\
**Post date:** [May 6, 2024, 6:07pm UTC](https://swi-prolog.discourse.group/t/editing-emacs-buffer-regions-by-prolog-scripts/7439/3 "2024-05-06T18:07:34Z")

</div>

> [@oskardrums](#):
>
> FWIW, with Sweep you can call Elisp functions (such as `insert` and `delete-region` and other buffer editing functions) from your [SWI-Prolog](https://www.swi-prolog.org/pldoc/doc_for?object=manual) code. See [Sweep: SWI-Prolog Embedded in Emacs](https://eshelyaron.com/sweep.html#Call-Back-to-Elisp)

I would like to try again later. In fact, once I tried but I failed for even simple  
lisp function call from Prolog, despite reading reference. I installed and updates  
sweep daily base via package manager of Emacs.

---

<div class="post-metadata">

**Author:** ![kuniaki.mukai](https://avatars.discourse-cdn.com/v4/letter/k/c67d28/32.png) [@kuniaki.mukai](https://swi-prolog.discourse.group/u/kuniaki.mukai)\
**Post date:** [May 8, 2024, 12:27pm UTC](https://swi-prolog.discourse.group/t/editing-emacs-buffer-regions-by-prolog-scripts/7439/4 "2024-05-08T12:27:07Z")

</div>

> [@kuniaki.mukai](#):
>
> However I have still no idea about “timeout” formulae.
> 
> ```prolog
> timeout_formula(all(X, p(X))-> some(Y, (p(Y), p(f(Y))))
> 
> ```

I have tried an ad hoc idea. For given formula A,  
run two processes (threads) in parallel, with iterated deepening method having the depth limit  
in common, one is for A, the other is for negation of A. The output is (1) A is valid, (2) A is unsatisfiable, (3) A is invalid, or (4) A is NOT unsatisfiable. Note that A is invalid when A is not unsatisfiable. Although there is of course no guarantee in theory on FOL prover that this parallel processes terminate, this simple  
“parallel prover” seems work on the “timeout” formula in a way of “conservative extension” of  
the prolog-like prover.

```prolog
 % ?- time(prove(all(X, p(X))-> some(Y, (p(Y) * p(f(Y)))))).
%@ valid
%@ % 1,181 inferences, 0.000 CPU in 0.000 seconds (93% CPU, 3393678 Lips)
%@ true.

% ?- time(prove(all(X, p(X))-> all(Y, (p(Y) * p(f(Y)))))).
%@ valid
%@ % 1,259 inferences, 0.000 CPU in 0.000 seconds (89% CPU, 3195431 Lips)
%@ true.

% ?- time(prove(all(X, p(X))-> -some(Y, (p(Y) * p(f(Y)))))).
%@ invalid
%@ % 1,274 inferences, 0.000 CPU in 0.000 seconds (95% CPU, 3241730 Lips)
%@ true.

```

I should be familiar with thread programming of swi-prolog, which seems fit what I have in mind  
as parallelism but I am doing in the usual single thread.

**EDIT** I was confused with my scattered test formulae. The following  
was really the timeout example. For human it should be obvious that  
this is false, but my “simple-minded” prover continue endless search to find  
refutation in iterated deepening search.

```prolog
%@ 22 : all(A,p(A))->some(B,(p(B) * -p(f(B))))

```

like prolog goes into similar sittiuation for

```prolog
?- p(A).
p(A):- p(f(A)).

```

In fact, I checked by hand that CNF of negation of formula 22 becomes similar to the above prolog clause

**EDIT** I “discovered” the formula by writing a simple emacs editing snipet  
for numbering terms in the whole file on the current buffer. I am personally surprised  
with how fast prolog works enough on emacs buffer despite of asynchronous communication in utf8 encoding.

---

<div class="post-metadata">

**Author:** ![kuniaki.mukai](https://avatars.discourse-cdn.com/v4/letter/k/c67d28/32.png) [@kuniaki.mukai](https://swi-prolog.discourse.group/u/kuniaki.mukai)\
**Post date:** [May 14, 2024, 7:00pm UTC](https://swi-prolog.discourse.group/t/editing-emacs-buffer-regions-by-prolog-scripts/7439/5 "2024-05-14T19:00:25Z")

</div>

> [@oskardrums](#):
>
> EDIT: to be clear, Sweep also lets you invoke Prolog from Elisp (and vice versa, as mentioned above), so you can define an Emacs command which calls Prolog, which then calls Elisp to edit an Emacs buffer. Although TBH, for the purpose of editing Emacs buffers, I’d just stick to Elisp most probably 🙂

It still sounds nice and like what I wish to have, but I have only lengthy concrete codes emacs-lisp and prolog codes to implements such communication between them, and more worse, details of which it is now difficult for me to remember. Fortunately, it still works as I expect, and here is a new simplified example “handle” on `fill-paragraph` which uses internally such mentioned basic codes. Of course such `fill-paragraph` must have been implemented in emacs-lisp, my interest is what kind of prolog codes for such `fill-paragraph` provided that the region is given as a list of character codes of text. The code below may have a careless bug, but I hope it shows some aspect of DCG-like approaches to edit  
emacs region by prolog. I know there are many things for me to write what are behind this codes, but I have to say still they are poor and, worse, scattered in the pack library pac.

> **Listing of handle(fill, paragraph)**
>
> ```prolog
> %!	handle([fill, paragraph])//2 is det.
> %	Asssuming a global variable paragraph_width is set,
> %	the region is filled so that the width of each line
> %	fits the specified width.
> 
> % ?- nb_setval(paragraph_width, 3).
> 
> % for exampe, the region of this content
> % a
> % b c
> % d e t
> 
> % ==> is filled as this:
> % a b
> % c d
> % e t
> 
> handle([fill, paragraph]) --> region,
> current(X),
> {	words(Words, [], X, []),
> maplist(string_codes, SWords, Words),
> (	nb_current(paragraph_width, Width)->true
> ;	Width = 10
> ),
> fill_paragraph(Width, P, [], SWords) 
> },
> peek(P),
> maplist(insert(" ")),
> insert("\n"),
> overwrite.
> 
> % ?- fill_string_paragraph(5, "a b c d", P).
> %@ P = [["a", "b", "c"], ["d"]].
> % ?- fill_string_paragraph(5, "a\t\t\r\n b\n c\n d\n", P).
> %@ P = [["a", "b", "c"], ["d"]].
> 
> fill_string_paragraph(K, X, P):- string_codes(X, Y),
> words(Words_in_codes, [], Y, []),
> maplist(string_codes, Words_in_string, Words_in_codes),
> fill_paragraph(K, P, [], Words_in_string).
> 
> % ?-words(X, [], `abc`, []), maplist(flip(string_codes), X, R).
> % ?-words(X, [], `\t\t`,[]), maplist(flip(string_codes), X, R).
> % ?-words(X, [], ` ab c\td \n`,[]), maplist(flip(string_codes), X, R).
> 
> words([X|Y], Z) --> wl("[^\s\t\n\r]+", X, []), !, words(Y, Z).
> words(X, Y) --> wl("[\s\t\n\r]+"), !, words(X, Y).
> words(X, X) --> [].
> 
> % ?- fill_paragraph(3, P, [], [a,a,a,a,a,a,a]).
> 
> fill_paragraph(_, P, P, []):-!.
> fill_paragraph(W, P, Q, R):-
> fill_line(W, W, P, U, R, U),
> fill_paragraph(W, U, Q, U).
> 
> % ?- fill_line(5, 5, X, [], [a, a, a, a, a, a, a, a, a], R).
> % ?- fill_line(5, 5, X, [], [a, a, a, a, a, a], R).
> % ?- fill_line(1, 1, X, [], [a, a, a], R).
> % ?- fill_line(10, 10, X, [], [ab, cd, e, f, ghi, a, a, a, a], R).
> % ?- fill_line(10, 10, X, [], [ab], R).
> 
> fill_line(W, W, X, X, [], []):-!.
> fill_line(_, _, [[]|X], X, [], []):-!.
> fill_line(Width, K, X, Y, [Word|U], V):-
> insert_word_at_end(Width, K, K0, X, Z, Word),
> fill_line(Width, K0, Z, Y, U, V).
> 
> %
> insert_word_at_end(Width, K, K0, X, Y, Word):-!,
> string_length(Word, A),
> compare(C, A, K),
> (	C = (=) ->
> X = [[Word]|Y],
> K0 = Width
> ;	C = (<) ->
> X = [[Word|P]|X0],
> Y = [P|X0],
> K0 is K-(A + 1)
> ;	Width =< A ->
> X = [[]|[[Word]|Y]],
> K0 is Width
> ; X = [[],[Word|P] | Q],
> Y = [P|Q],
> K0 is Width -(A + 1)
> ).
> 
> ```
