[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

optional user-specified end-delimiters



This is a proposed syntax for optional user-specified
end tokens for both SRFI-108 and SRFI-109.
First the proposal, then a discussion comparing alternatives.

PROPOSAL

A string quasi-literal may have an optional end-label,
specified by a '!' followed by an label.  For example:

&!END{
  &|line1
  &|line2
}!END

Syntax:

label ::= tagname-subsequent+

It seems reasonable to allow an integer label, so we allow
starting with a digit.  Thus for simplicity we also allow
hyphen, underscore, or period.
(In an unrelated change, I think tagname-subsequent should
allow periods.  XML compatibility is one reason.)

extended-string-literal ::= "&" string-starttag?
   "{" initial-ignored? string-literal-part* "}!" string-endtag?
string-starttag ::= "!" label
string-endtag ::= "!" label

The string-endtag is required if the string-starttag is specified,
and of course the labels must much.
Note this chance means that an extended-string-literal must be
followed by a delimiter or end of input.

For a named quasi-literal, we can use the constructor-name as an end-tag:

&example{
  &|line1
  &|line2
}example

DISCUSSION: Should we require a final "!" if there is no
explicit label?  Make it optional?  I.e.:

}example!

You can add an explicit label:

&example!23{
  &|line1
  &|line2
}example!23

or:

&example!23{
  &|line1
  &|line2
}!23

(It's not clear all these options are all that useful.)

extended-datum-body ::=
"&" cname datum-start-label? "{" initial-ignored? named-literal-part* "}" datum-end-label? | "&" cname datum-start-label? "[" expression* "]{" initial-ignored? named-literal-part* "}" datum-end-label?

datum-start-label ::= "!" label
datum-end-label :=
    cname "!"?
  | cname? "!" label

If may occasionally be useful to make labels available for
semantic information.  One  example is as implicit "id" attributes.
To do that we modify the translation:

&cname!label[init-args...]{content}cname!label ==>
($construct$:cname init-args... $>>$ "content" ($construct-label$ "label"))

I.e. we add ($construct-label$ "label") as the final operand
of the $construct$:foo.  We also add a standard definition:
  (define ($construct-label$ label) "")
This way the $construct-label$ by default becomes a no-op,
including when using define-simple-constructor.

ALTERNATIVES

There are some plausible alternatives.  For example we can put the
end-tag just before the right brace.
For named constructors we could use:

&cname{
line1
line2
&cname}

This works and parses unambiguously.  However, it's difficult
to come up with a "compatible" syntax for strings,
as discussed below.  Also, the start-of-literal and the
end-of-literal both start with the same prefix "&cname", which
makes visual scanning slower.

We could add the "!" as also used for strings:

&!cname}

or:

&cname!}

The former appears incompatible with optional explicit
labels (i.e. "cname!tag"), while the latter doesn't
help much with the visual scanning problem.

For strings we don't need the "&" since the end-delimiter
is required if specified:

&!END{
  &|line1
  &|line2
!END}

This is a little tricky to parse, since you don't know that !END if
an end-tag until you see the right-brace.  (For the same reason
it's not very human-readable.)  However, it doesn't feel very
consistent with the named-constructor case.

We could avoid this by requiring a '&' before the end-tag, either:

&END}

or:

&!END}

The former feels inconsistent with named constructors (it seems
to preclude explicit labels); the latter seems excessive.
--
	--Per Bothner
per@xxxxxxxxxxx   http://per.bothner.com/