# Simple example of phrase\_from\_file/2?

**URL:** <https://swi-prolog.discourse.group/t/simple-example-of-phrase-from-file-2/3772>\
**Category:** Help!\
**Created:** [March 27, 2021, 2:21pm UTC](https://swi-prolog.discourse.group/t/simple-example-of-phrase-from-file-2/3772 "2021-03-27T14:21:14Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![stassa.p](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/stassa.p/32/712_2.png) [@stassa.p](https://swi-prolog.discourse.group/u/stassa.p)\
**Post date:** [March 27, 2021, 2:21pm UTC](https://swi-prolog.discourse.group/t/simple-example-of-phrase-from-file-2/3772/1 "2021-03-27T14:21:14Z")

</div>

I’m using: SWI-Prolog version 8.3.20

I have a file with the following line:

```prolog
% Target: ability/2

```

Suppose I’d like to parse it with the following grammar:

```prolog
:- use_module(library(dcg/basics)).

target(T) --> comment, blank, `Target:`, blanks, predicate_symbol(T).

comment --> `%`.

predicate_symbol(F/A) --> string(F), `/`, integer(A).

```

phrase/2 succeeds:

```prolog
?- phrase(target(_Cs/A), `% Target: ability/2`), atom_codes(T, _Cs).
A = 2,
T = ability .

```

phrase\_from\_file/2 fails:

```prolog
?- phrase_from_file(target(T), 'my_file.log').
false.

```

How can I make phrase\_from\_file/2 succeed?

Note that the example of ‘file\_contains/3’ in the documentation only shows how to count occurrences of a pattern but I want to bind variables in the output. Is this possible with phrase\_from\_file/2?

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 27, 2021, 2:57pm UTC](https://swi-prolog.discourse.group/t/simple-example-of-phrase-from-file-2/3772/2 "2021-03-27T14:57:13Z")

</div>

Just a guess: the content of the file may end with a newline? Your grammar doesn’t deal with that.

> [@stassa.p](#):
>
> but I want to bind variables in the output. Is this possible with [phrase\_from\_file/2](https://www.swi-prolog.org/pldoc/doc_for?object=phrase_from_file/2)?

Of course. It is just a grammar and can do anything a grammar can do.

P.s. Probably better use `"Target:"` rather than ``target``. Only for the input you need ``...``. The DCG compiler handles double quoted strings as string literals.

---

<div class="post-metadata">

**Author:** ![stassa.p](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/stassa.p/32/712_2.png) [@stassa.p](https://swi-prolog.discourse.group/u/stassa.p)\
**Post date:** [March 27, 2021, 4:48pm UTC](https://swi-prolog.discourse.group/t/simple-example-of-phrase-from-file-2/3772/3 "2021-03-27T16:48:09Z")

</div>

Thanks - but, no, the file doesn’t end wth a newline.

> [@jan](#):
>
> P.s. Probably better use `"Target:"` rather than `target` . Only for the input you need `...` . The DCG compiler handles double quoted strings as string literals.

A bit confused with all the different quotation schemes but I’ll manage.

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 27, 2021, 4:52pm UTC](https://swi-prolog.discourse.group/t/simple-example-of-phrase-from-file-2/3772/4 "2021-03-27T16:52:57Z")

</div>

> [@stassa.p](#):
>
> Thanks - but, no, the file doesn’t end wth a newline.

Next step is tracing, I guess ☹

---

<div class="post-metadata">

**Author:** ![stassa.p](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/stassa.p/32/712_2.png) [@stassa.p](https://swi-prolog.discourse.group/u/stassa.p)\
**Post date:** [March 27, 2021, 4:55pm UTC](https://swi-prolog.discourse.group/t/simple-example-of-phrase-from-file-2/3772/5 "2021-03-27T16:55:00Z")

</div>

Next step was to roll-my-own log parser with read\_line\_from\_file/2 (that’s what my program was tring to do, read a log) 🙂

But I’d still like to find out how phrase\_from\_file/2 works. Is there a way to ask the tracer nicely to render lists of codes as lists of chars?

---

<div class="post-metadata">

**Author:** ![jan](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/jan/32/4_2.png) [@jan](https://swi-prolog.discourse.group/u/jan)\
**Post date:** [March 27, 2021, 4:57pm UTC](https://swi-prolog.discourse.group/t/simple-example-of-phrase-from-file-2/3772/6 "2021-03-27T16:57:04Z")

</div>

> [@stassa.p](#):
>
> Is there a way to ask the tracer nicely to render lists of codes as lists of chars?

No, but using portray\_text/1 you can get them printed as strings.

---

<div class="post-metadata">

**Author:** ![stassa.p](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/stassa.p/32/712_2.png) [@stassa.p](https://swi-prolog.discourse.group/u/stassa.p)\
**Post date:** [March 27, 2021, 5:03pm UTC](https://swi-prolog.discourse.group/t/simple-example-of-phrase-from-file-2/3772/7 "2021-03-27T17:03:38Z")

</div>

That’s what I was looking for, thanks.

---

<div class="post-metadata">

**Author:** ![EricGT](https://avatars.discourse-cdn.com/v4/letter/e/f1d935/32.png) [@EricGT](https://swi-prolog.discourse.group/u/EricGT)\
**Post date:** [March 27, 2021, 5:06pm UTC](https://swi-prolog.discourse.group/t/simple-example-of-phrase-from-file-2/3772/8 "2021-03-27T17:06:28Z")

</div>

For others that might find this topic and want to know more ways to display info.

[When format, ansi\_format, print\_term and print\_message don’t seem right, try portray](https://swi-prolog.discourse.group/t/when-format-ansi-format-print-term-and-print-message-dont-seem-right-try-portray/2630)

---

<div class="post-metadata">

**Author:** ![swi](https://avatars.discourse-cdn.com/v4/letter/s/51bf81/32.png) [@swi](https://swi-prolog.discourse.group/u/swi)\
**Post date:** [March 28, 2021, 10:33am UTC](https://swi-prolog.discourse.group/t/simple-example-of-phrase-from-file-2/3772/9 "2021-03-28T10:33:38Z")

</div>

can you post `my_file.log`? my guess is that it contains more than one line, and your grammar only processes one string.

---

<div class="post-metadata">

**Author:** ![EricGT](https://avatars.discourse-cdn.com/v4/letter/e/f1d935/32.png) [@EricGT](https://swi-prolog.discourse.group/u/EricGT)\
**Post date:** [March 28, 2021, 11:18am UTC](https://swi-prolog.discourse.group/t/simple-example-of-phrase-from-file-2/3772/10 "2021-03-28T11:18:14Z")

</div>

I added `log` to the list of authorized file extensions for uploading.

![image](https://global.discourse-cdn.com/free1/uploads/swiprolog/original/2X/f/fe688c8f7e5956a516fa027dc87e51bb0bdf5825.png)

---

<div class="post-metadata">

**Author:** ![stassa.p](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/stassa.p/32/712_2.png) [@stassa.p](https://swi-prolog.discourse.group/u/stassa.p)\
**Post date:** [March 29, 2021, 9:15am UTC](https://swi-prolog.discourse.group/t/simple-example-of-phrase-from-file-2/3772/11 "2021-03-29T09:15:45Z")

</div>

Unfortunately I don’t have it anymore. It was an excerpt from a longer log with about 64 lines, but I don’t remember how many lines I copied in the excerpt- I think you’re right it was more than one line.

But I’m confused: what do you mean my grammar only processes one string? My understanding of phrase\_from\_file/2 is that it will nondeterministically process each string in a file and succeed for each line that contains a string that is accepted by the grammar rule. Is that not the case?

Edit: The above is my understanding after copying the file\_contains/2 example from the documentation into my source and running it against my full, 64-line log, whereupon it returned a count of 1, if I remember correctly.

---

<div class="post-metadata">

**Author:** ![stassa.p](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/stassa.p/32/712_2.png) [@stassa.p](https://swi-prolog.discourse.group/u/stassa.p)\
**Post date:** [March 29, 2021, 9:48am UTC](https://swi-prolog.discourse.group/t/simple-example-of-phrase-from-file-2/3772/12 "2021-03-29T09:48:57Z")

</div>

I was curious so I tried again and like you say the error on my part was that I expected phrase\_from\_file/2 to go through the entire file nondeterministically, treating each new line as a new input to the grammar.

Instead, it looks like phrase\_from\_file/2 treats the entire file as input to the grammar, i.e. from the first character of the first line on to the end-of-file marker. If the grammar does not accept the first line, phrase\_from\_file/2 fails deterministically (i.e. it doesn’t backtrack to get another line). [Edit: it didn’t help that I was writing a log parser, where it’s natural to want to ignore much of the file’s content and only be interested in some particular bit of information.]

I was confused by the aggregate\_all/3 call in the example program file\_contains/2 given in the documentation:

```prolog
:- use_module(library(dcg/basics)).

file_contains(File, Pattern) :-
        phrase_from_file(match(Pattern), File).

match(Pattern) -->
        string(_),
        string(Pattern),
        remainder(_).

match_count(File, Pattern, Count) :-
        aggregate_all(count, file_contains(File, Pattern), Count).

```

I didn’t realise that the `string(_)` call before `string(Pattern)` is meant to match _the entire file up to the sought-for pattern_, and thought it was supposed to match at the start of a line (and so ignore the input up to that pattern). My example grammar matches an entire log line so it fails when there are lines before and after it.

To be honest, I find the documentation for phrase\_from\_file/2 very confusing and it’s not the first time I’ve grappled with it.

I guess I have to propose a better example now?

---

<div class="post-metadata">

**Author:** ![stassa.p](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/stassa.p/32/712_2.png) [@stassa.p](https://swi-prolog.discourse.group/u/stassa.p)\
**Post date:** [March 29, 2021, 10:01am UTC](https://swi-prolog.discourse.group/t/simple-example-of-phrase-from-file-2/3772/13 "2021-03-29T10:01:10Z")

</div>

For the time being I’m uploading a very simple example grammar and two files, one with the target line as the first line, named “works.log” and one with a different first line named “not\_works.log”.

Hopefully that could help clear up the confusion to someone else trying to understand how phrase\_from\_file/2 works.

Source file with very simple grammar:  
[target.pl](https://swi-prolog.discourse.group/uploads/short-url/22lMX77pcGcM02Ipl5s1ZAazw6v.pl) (422 Bytes)

Text file with the target string as first line:  
[works.log](https://swi-prolog.discourse.group/uploads/short-url/vHVJUBlpbiiPXBcTxobygTwlC0e.log) (40 Bytes)

Text file with a different first line:  
[not\_works.log](https://swi-prolog.discourse.group/uploads/short-url/1JZ3xGdZO2Ljn51rAVh22ThIUBR.log) (53 Bytes)

Example queries:

```prolog
?- findall_targets(Ts,'works.log').
Ts = [move/2].

?- findall_targets(Ts,'not_works.log').
Ts = [].

```

I should also comment that the phrase\_from\_file/2 example is confusing because it doesn’t show (what I think is) the most natural application for such a predicate, i.e. to extract bits of text containing interesting information from a file. Instead, it shows how to count lines that contain a pattern, which is a bit of an edge case in general, I find. The first time I saw this bit of documentation I formed the impression that, perhaps, the point of the predicate was to confirm that a pattern exists in a file, rather than analyse a pattern as one would do with an ordinary DCG (hence my earlier question to Jan, whether phrase\_from\_file/2 can bind variables in the output).

---

<div class="post-metadata">

**Author:** ![swi](https://avatars.discourse-cdn.com/v4/letter/s/51bf81/32.png) [@swi](https://swi-prolog.discourse.group/u/swi)\
**Post date:** [March 29, 2021, 5:27pm UTC](https://swi-prolog.discourse.group/t/simple-example-of-phrase-from-file-2/3772/14 "2021-03-29T17:27:54Z")

</div>

There is no problem with phrase\_from\_file/2, what is happening is that your grammar does not reflect the structure of the file properly, the following will work:

```prolog
:-module(target, [findall_targets/2
                 ]).

:- use_module(library(dcg/basics)).

findall_targets(F, Ts):-
    phrase_from_file(lines(Entries), F),
    exclude(is_comment,Entries,Ts).

is_comment(comment(_)).

% Convert log to a list like this:
% [comment(SomeComment), P/A, ...]
lines([Entry|Entries]) -->
   line(Entry), !,
   lines(Entries).
lines([]) --> [].

% Line is either a comment or a comment with a target.
line(Entry) -->
   comment_with_target(Entry), !
   | comment(Entry).

comment_with_target(T) -->
   `%`, blank, `Target:`, blanks, predicate_symbol(T), blanks_to_nl.

comment(comment(C)) -->
   `%`, string_without("\n",C), `\n`.

predicate_symbol(F/A) --> string(F), `/`, integer(A).

```

Query:

```prolog
29 ?- findall_targets('works.log',Ts).
Ts = [`move`/2].

30 ?- findall_targets('not_works.log',Ts).
Ts = [`move`/2].

```

Remember the grammar (whether you use phrase/2 or phrase\_from\_file/2) must describe all possible contents, not just the bits you are interested in. The grammar is not a regex to extract the bits you want, but a description of all possible file contents.

EDIT: This is a more advanced topic, but after you are comfortable using grammars, and want a way to process the file using the grammar but without necessarily building a list, you can use the new [library(intercept)](https://www.swi-prolog.org/pldoc/man?section=intercept).
