# Quasi-quotation API extension?

**URL:** <https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060>\
**Category:** Help!\
**Created:** [February 25, 2022, 9:34pm UTC](https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060 "2022-02-25T21:34:03Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![ridgeworks](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/ridgeworks/32/886_2.png) [@ridgeworks](https://swi-prolog.discourse.group/u/ridgeworks)\
**Post date:** [February 25, 2022, 9:34pm UTC](https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060/1 "2022-02-25T21:34:03Z")

</div>

I have to admit I don’t fully understand all the ramifications of quasi-quotations, but:

The API for [quasi-quotations](https://www.swi-prolog.org/pldoc/man?section=quasiquotations) does not obviously allow for the content to be a normal string. Instead it seems to be some opaque term created by the builtin parser.

Would it be possible/desirable to extend this API so either the content could be a string, or to support a predicate to convert a normal string to such an opaque term? In essence, is there a way to decouple the builtin Prolog parser from the individual QQ parsers so QQ content could come from somewhere else?

---

<div class="post-metadata">

**Author:** ![peter.ludemann](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/peter.ludemann/32/48_2.png) [@peter.ludemann](https://swi-prolog.discourse.group/u/peter.ludemann)\
**Post date:** [February 25, 2022, 10:05pm UTC](https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060/2 "2022-02-25T22:05:38Z")

</div>

The quasi-quotation can be a string. See this comment from @jan in a PR discussion, in response to my musing about having a “raw” string in Prolog, similar to Python’s `r"..."`:

> For long and complicated regular expressions quasi quotations are an option. We already have the `string` quasi quotation. Making a dedicated one for regex would improve things (we can put the options in the re term, e.g.

```prolog
 {|re([caseless(true)])||aap|}

```

Source: [Prepare existing PCRE1 code for migration to PCRE2 by kamahen · Pull Request #3 · SWI-Prolog/packages-pcre · GitHub](https://github.com/SWI-Prolog/packages-pcre/pull/3#issuecomment-1034593804)

---

<div class="post-metadata">

**Author:** ![ridgeworks](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/ridgeworks/32/886_2.png) [@ridgeworks](https://swi-prolog.discourse.group/u/ridgeworks)\
**Post date:** [February 25, 2022, 10:27pm UTC](https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060/3 "2022-02-25T22:27:48Z")

</div>

Well that would be good news. But then I would expect the following to work:

```prolog
?- call(string,"abcd",[],[],R).
false.

```

What am I missing? (Using SWIP 8.4.1.)

---

<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:** [February 26, 2022, 8:05am UTC](https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060/4 "2022-02-26T08:05:45Z")

</div>

> [@ridgeworks](#):
>
> `call(string,"abcd",[],[],R).`

Who says this should work. The qq\_syntax/4 hook is not a DCG, but may choose between reading the content as either a stream or use a DCG. Anyway, to read a pure string you can use the QQ in library(strings) which defines various ways to get strings with nice layout, interpolation, etc.

The API can do anything I can think of, converting the input into an arbitrary Prolog term at compile time that can be a (dict) function to ensure further evaluation at runtime.

---

<div class="post-metadata">

**Author:** ![ridgeworks](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/ridgeworks/32/886_2.png) [@ridgeworks](https://swi-prolog.discourse.group/u/ridgeworks)\
**Post date:** [February 26, 2022, 4:17pm UTC](https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060/5 "2022-02-26T16:17:35Z")

</div>

> [@jan](#):
>
> Who says this should work.

Nobody, but this was the purpose of my original question.

> [@jan](#):
>
> The API can do anything I can think of,

This is what I’m trying to understand. To use the example in the manual, if I have a “Syntax” expression `html(Name, Address)` and the _string_ content “`<tr><td>Name<td>Address</tr>`”, is there an API that will do what the QQ hook does, but not at parse/compile time? One application might be a meta-interpreter for QQ’s.

`SyntaxName/4` appears to do this except that the `Content` is an “opaque term”. So is there a way of constructing such a term from a string? For example, if I wrote the string to a stream and used that somehow.

---

<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:** [February 26, 2022, 4:46pm UTC](https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060/6 "2022-02-26T16:46:55Z")

</div>

> [@ridgeworks](#):
>
> So is there a way of constructing such a term from a string? For example, if I wrote the string to a stream and used that somehow.

I don’t get the intend yet. You can of course use e.g. term\_string/2,3 to use the (QQ) parser with user generated content. What is your use case?

---

<div class="post-metadata">

**Author:** ![ridgeworks](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/ridgeworks/32/886_2.png) [@ridgeworks](https://swi-prolog.discourse.group/u/ridgeworks)\
**Post date:** [February 26, 2022, 7:11pm UTC](https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060/7 "2022-02-26T19:11:29Z")

</div>

At this point I’m in “exploratory” mode, just trying to find out what’s possible. The basic intent is to be dynamically call any defined QQ parser without invoking the Prolog parser.

As I understand the suggestion to use, e.g. [term\_string/2](https://www.swi-prolog.org/pldoc/doc_for?object=term_string/2),3, this would require me to reconstruct a QQ string from a `Syntax` term and a `Content` string. The parser would then deconstruct it again, then call the appropriate QQ parser. This seems a little wasteful but I’ll give it a try.

---

<div class="post-metadata">

**Author:** ![ridgeworks](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/ridgeworks/32/886_2.png) [@ridgeworks](https://swi-prolog.discourse.group/u/ridgeworks)\
**Post date:** [February 26, 2022, 8:46pm UTC](https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060/8 "2022-02-26T20:46:52Z")

</div>

OK. I think the following code fragment does what I want:

```prolog
	term_string(Syntax, QQsyntax, [variable_names(Vars)]),
	atomics_to_string(['{|', QQsyntax, '||', Content, '|}'], QQuote),
	term_string(Term, QQuote, [variable_names(Vars)]),

```

It doesn’t avoid using the builtin parser, but probably good enough.

But `term_string` seems a bit erratic:

```prolog
?- term_string(S, "{|string(To,From)||to:To from:From|}", [variable_names(['To' = To, 'From' = From])]).
S = strings{type:string}.exec(["to:To from:From"], ['To'=To, 'From'=From]).

?- term_string(S, "{|html(Name,Address)||<tr><td>Name<td>Address</tr>|}", [variable_names(['Address'=Add, 'Name'=Name])]).
false.

?- trace,term_string(S, "{|html(Name,Address)||<tr><td>Name<td>Address</tr>|}", [variable_names(['Address' = Add, 'Name' = Name])]).
   Call: (11) term_string(_32558, "{|html(Name,Address)||<tr><td>Name<td>Address</tr>|}", [variable_names(['Address'=_32526, 'Name'=_32538])]) ? creep
   Fail: (11) term_string(_32558, "{|html(Name,Address)||<tr><td>Name<td>Address</tr>|}", [variable_names(['Address'=_32526, 'Name'=_32538])]) ? creep
false.

```

Any idea what I’m doing wrong?

---

<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:** [February 26, 2022, 8:59pm UTC](https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060/9 "2022-02-26T20:59:53Z")

</div>

> [@ridgeworks](#):
>
> Any idea what I’m doing wrong?

I think it’s just a matter of wrong order of the `variable_names` argument, which should correspond to the linear order in which the variables are scanned.  
Try:

```prolog
?- term_string(S, "{|html(Name,Address)||<tr><td>Name<td>Address</tr>|}", [variable_names(['Name' = Name, 'Address' = Add])]).
S = [element(tr, [], [element(td, [], [Name]), element(td, [], [Add])])].

```

---

<div class="post-metadata">

**Author:** ![ridgeworks](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/ridgeworks/32/886_2.png) [@ridgeworks](https://swi-prolog.discourse.group/u/ridgeworks)\
**Post date:** [February 26, 2022, 9:24pm UTC](https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060/10 "2022-02-26T21:24:42Z")

</div>

Thanks for the hint.; that indeed is the issue. But seems like a bug to me; no hint of this in the doc and write\_term/2 has no such restriction:

```prolog
?- write_term(html(Address,Name),[variable_names([])]).
html(_51398,_51400)
true.

?- write_term(html(Address,Name),[variable_names(['Address'=Address, 'Name'=Name])]).
html(Address,Name)
true.

?- write_term(html(Address,Name),[variable_names(['Name'=Name, 'Address'=Address])]).
html(Address,Name)
true.

```

---

<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:** [February 27, 2022, 10:18am UTC](https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060/11 "2022-02-27T10:18:58Z")

</div>

> [@ridgeworks](#):
>
> But seems like a bug to me; no hint of this in the doc

Docs:

> variable\_names(Vars)  
> Unify Vars with a list of‘Name = Var’, where Name is an atom describing the variable name and Var is a variable that shares with the corresponding variable in Term. (ISO). The variables appear in the order they have been read.

That surely hints that the order matters I’d say … I don’t see a realistic case where you’d want this to handle any order. If you need that, just use a sort/2 after the read\_term/3 and you’ll have the variables in alphabetical order. Or, more common, use memberchk/2 to get the variables yoiu are looking for.

---

<div class="post-metadata">

**Author:** ![Boris](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/boris/32/7486_2.png) [@Boris](https://swi-prolog.discourse.group/u/Boris)\
**Post date:** [February 27, 2022, 11:08am UTC](https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060/12 "2022-02-27T11:08:16Z")

</div>

Forgive me if I am saying obvious things. I will surely make false claims and then someone could be so kind and correct me.

> [@ridgeworks](#):
>
> What am I missing?

Quasi-quotations are a mechanism for putting arbitrary content within a Prolog source file, capturing Prolog variables from the context, and converting it to a proper Prolog term at compile time. I am not sure what is the correct name for this: it is not compile-time or run-time, it is “writing the code”-time. (help, someone?)

You _can_ and usually _want to_ have content that can be parsed with a formal grammar. The SWI-Prolog implementation helps you by hooking _your parser_ to the compiler and running it at compile time.

Existing parsers:

On the informal end of the spectrum, you have the quasi-quotation syntax for plain strings provided by [library(strings)](https://www.swi-prolog.org/pldoc/man?section=strings), see the [source file, close to the top](https://github.com/SWI-Prolog/swipl-devel/blob/aaa6d7d962f92210d46b3cbbbaebb5c6f5a03bec/library/strings.pl). Jan was kind enough to implement it, maybe [provoked by my incessant questions](https://swi-prolog.discourse.group/t/quasi-quotations-again/2964).

You have already found the [HTML quasi-quotation parser](https://www.swi-prolog.org/pldoc/doc/_SWI_/library/http/html_quasiquotations.pl); just in case, [here is the source](https://github.com/SWI-Prolog/packages-http/blob/b254f185f10275186bb1c714265be4c5a64e5b47/html_quasiquotations.pl).

After this too long intro, I _think_ that nothing prevents you from reusing the parsers that quasi-quotations use, without the quasi-quotation mechanism: maybe using it on a user-provided string at run time? Or am I wrong again?

---

<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:** [February 27, 2022, 3:09pm UTC](https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060/13 "2022-02-27T15:09:17Z")

</div>

> [@Boris](#):
>
> After this too long intro, I _think_ that nothing prevents you from reusing the parsers that quasi-quotations use, without the quasi-quotation mechanism: maybe using it on a user-provided string at run time? Or am I wrong again?

I think it is more the other way around: given a parser using a DCG or something that reads from a stream you can implement a QQ handler. That still allows you to use the parser directly, i.e., without using read\_term/3 or some variant. The QQ handler talks to an opaque data structure that allows (re)using code that is not related to QQ. One of the roles of the intermediate representatiion is to forward error/warning locations properly.

---

<div class="post-metadata">

**Author:** ![ridgeworks](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/ridgeworks/32/886_2.png) [@ridgeworks](https://swi-prolog.discourse.group/u/ridgeworks)\
**Post date:** [February 27, 2022, 3:42pm UTC](https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060/14 "2022-02-27T15:42:12Z")

</div>

> [@jan](#):
>
> That surely hints that the order matters I’d say …

I now see what’s happening. In my use case, I already have a list of `Name = Var` bindings. When term-string/3 is invoked, it creates its own such list and tries to unify that list with the one I passed in as a `variable_names` option. So to ensure the same names refer to the same variables, I need to do a merge of the two lists after the call to term\_string:

```prolog
	term_string(Syntax,QQsyntax,[variable_names(Vars)]), % writes Syntax term to a string using pre-existing variable names?
	atomics_to_string(['{|',QQsyntax,'||',Content,'|}'],QQuote), % construct full QQ as string
	term_string(Term,QQuote,[variable_names(VarNames)]), % parse it and get new Var=Name bindings
	mergeNamedVars(VarNames,Vars). % uses memberchk to unify vars with the same name

```

This is certainly workable but seems like a roundabout way to call an individual QQ parser with a string as content using the documented QQ interface:

```
call(+SyntaxName, +Content, +SyntaxArgs, +VariableNames, -Result)

```

---

<div class="post-metadata">

**Author:** ![ridgeworks](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/ridgeworks/32/886_2.png) [@ridgeworks](https://swi-prolog.discourse.group/u/ridgeworks)\
**Post date:** [February 27, 2022, 3:59pm UTC](https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060/15 "2022-02-27T15:59:10Z")

</div>

> [@jan](#):
>
> I think it is more the other way around: given a parser using a DCG or something that reads from a stream you can implement a QQ handler. That still allows you to use the parser directly, i.e., without using [read\_term/3](https://www.swi-prolog.org/pldoc/doc_for?object=read_term/3) or some variant.

And I think this is what I’m trying to get my head around. How would I write a parser for some arbitrary syntax which could invoke such a QQ handler? Note that by using term\_string I’ve pretty much lost any information relevant to error/warning locations. Now if the Content term was transparent, I might be able to construct the appropriate “intermediate representation”, and keep everybody happy.

---

<div class="post-metadata">

**Author:** ![ridgeworks](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/ridgeworks/32/886_2.png) [@ridgeworks](https://swi-prolog.discourse.group/u/ridgeworks)\
**Post date:** [February 27, 2022, 4:23pm UTC](https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060/16 "2022-02-27T16:23:00Z")

</div>

> [@Boris](#):
>
> Quasi-quotations are a mechanism for putting arbitrary content within a Prolog source file, capturing Prolog variables from the context, and converting it to a proper Prolog term at compile time. I am not sure what is the correct name for this: it is not compile-time or run-time, it is “writing the code”-time.

Pretty much everything about Prolog is dynamic, including things like operator definitions, so while this might be the current state of Quasi-quotations, I’m trying to understand why that is the case and why it’s desirable that it should be so. Other than intellectual curiosity, I have no particular agenda that would motivate changing the status quo.

> [@Boris](#):
>
> I _think_ that nothing prevents you from reusing the parsers that quasi-quotations use,

I _think_ this is incorrect. The only way I see to access individual QQ parsers is by (re)constructing a QQ formatted string and giving it back to the builtin parser; workable, but a little awkward IMO.

---

<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:** [February 27, 2022, 4:52pm UTC](https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060/17 "2022-02-27T16:52:42Z")

</div>

> [@ridgeworks](#):
>
> The only way I see to access individual QQ parsers is by (re)constructing a QQ formatted string and giving it back to the builtin parser; workable, but a little awkward IMO.

One thing to note is that - if I understand correctly - the QQ interface allows existing Prolog parser/interpolators to be used _also_ at the time of reading source terms, but that means that every QQ parser is backed by some plain Prolog code that is either a DCG or a predicate which may read from the input stream, so you could use this Prolog code directly.

For example, the `string` QQ interface is backed by interpolate\_string/4, which my `debug_adapter` package calls directly to, well, interpolate strings with Prolog variables bound in a debugged frame (see code [here](https://github.com/eshelyaron/debug_adapter/blob/eb47a9d4821d54657e0114fa76557214c8653840/prolog/debug_adapter/breakpoint.pl#L17)).

---

<div class="post-metadata">

**Author:** ![ridgeworks](https://yyz2.discourse-cdn.com/free1/user_avatar/swi-prolog.discourse.group/ridgeworks/32/886_2.png) [@ridgeworks](https://swi-prolog.discourse.group/u/ridgeworks)\
**Post date:** [March 2, 2022, 4:07pm UTC](https://swi-prolog.discourse.group/t/quasi-quotation-api-extension/5060/18 "2022-03-02T16:07:50Z")

</div>

> [@oskardrums](#):
>
> that means that every QQ parser is backed by some plain Prolog code

This is true but I think it would be better if there were a standard way of accessing it. One possibility is to define an additional multifile QQ API predicate, e.g.,

```prolog
qq_parse(+Syntax,+String,+VariableNames,-Result).

```

It’s pretty much the same implementation as the current parser hook except there’s no need to use `with_quasi_quotation_input/3` as in, e.g., `string/4`:

```prolog
string(Content, Args, Binding, DOM) :-
    ...
    with_quasi_quotation_input(Content, Stream, read_string(Stream, _, String)),
    ...

```

Instead you could write:

```prolog
qq_parse(string(Args),String,Binding,DOM)

```

Now I can accomplish the same thing using `term_string` and re-invoking the builtin parser. It’s just more complicated and less efficient than the `qq_parse` (or equivalent) solution.
