Skip to content

Commit

Permalink
Escaping special characters in string annotations
Browse files Browse the repository at this point in the history
The value of a string annotation uses the `;` and `:` characters to allow
platform- and feature-specific values to be defined.  These special
characters can now be escaped using a leading `\\`.  This capability has
now been documented.

Resolves #59
  • Loading branch information
philthompson10 committed Dec 19, 2024
1 parent a619d79 commit a0d45a9
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 7 deletions.
12 changes: 11 additions & 1 deletion docs/annotations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,17 @@ Annotations can have one of the following types:
Python scope.

*string*
The value is a double quoted string.
The value is a double quoted string. The value is interpreted as a
sequence of ``;``-separated fields. Each field may contain a
``:``-separated selector:value pair. The selector is the name of either a
platform (defined by the :directive:`%Platforms` directive) or a feature
(defined by the :directive:`%Feature` directive). If the selector refers
to the current platform or to an enabled feature then the corresponding
value is used as the value of the annotation. The selector may be preceded
by ``!`` to invert the selection. The selector of each field is
evaluated in turn until a value is found to be selected. ``;`` and ``:``
may be escaped using a leading ``\\``. Normally a string annotation is a
simple string.

The following example shows argument and function annotations::

Expand Down
44 changes: 38 additions & 6 deletions sipbuild/generator/parser/annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,19 +136,51 @@ def validate_string(pm, p, symbol, name, value, optional):
raise InvalidAnnotation(name, "must be a quoted string", use='')

# Handle any embedded selectors.
for part in value.split(';'):
if ':' not in part:
return part.strip()

selector, subvalue = part.split(':', maxsplit=1)
fields = []

# Break into a list of ';' separated fields allowing for escaped ';'s.
field = ''
needs_appending = True
for ch in value:
if ch == ';':
if field and field[-1] == '\\':
# Remove the escape.
field = field[:-1]
else:
fields.append(field.strip())
field = ''
needs_appending = False
continue

field += ch
needs_appending = True

if needs_appending:
fields.append(field.strip())

# Go through each field looking for ':' separated selector/value pairs.
for fields in fields:
# A missing selector is treated as 'true'.
parts = field.split(':', maxsplit=1)
if len(parts) != 2:
return field

selector, field_value = parts

# An escaped ':' means the selector is missing.
if selector and selector[-1] == '\\':
# Remove the escape.
return field.replace('\\', '', 1)

# See if the selector is inverted.
if selector.startswith('!'):
selector = selector[1:]
inverted = True
else:
inverted = False

if pm.evaluate_feature_or_platform(p, symbol, selector, inverted):
return subvalue.strip()
return field_value.strip()

# No value was selected so ignore the annotation completely.
return None
Expand Down

0 comments on commit a0d45a9

Please sign in to comment.