This patch improves the string features of Normand.
Specifically, this patch:
• Adds support for the Latin-1 to Latin-10 string encodings:
s:latin1"bonne journée!"
62 6f 6e 6e 65 20 6a 6f 75 72 6e e9 65 21 ┆ bonne journ•e!
The `s:` prefix is needed and also works with existing UTF encoding
names:
s:u32le "yo la gang 👋"
• Adds the variable string item.
The syntax is one of:
u16le{some_expr}
{some_expr : s:u16le}
Any `bool`, `int`, `float`, or `str` variable/label is accepted in the
expression.
For the suffix encoding form, the `s:` prefix is required.
• Makes it possible to use a string, possibly literal, as a macro
expansion parameter:
m:meow(42, "mix")
• Adds the `--var-str` CLI option to add a variable having a string
value:
normand '--var-str=my_var=salut la gang' [...]
See the updated `README.adoc` for more details.
Adding tests and updating existing ones.
Change-Id: I01426bb5ffa53097bb8c85937f54f1b4b498ec06
Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Reviewed-on: https://review.lttng.org/c/normand/+/11019
Tested-by: jenkins <jenkins@lttng.org>
This package offers both a portable {py3} module and a command-line
tool.
-WARNING: This version of Normand is 0.18, meaning both the Normand
+WARNING: This version of Normand is 0.19, meaning both the Normand
language and the module/CLI interface aren't stable.
ifdef::env-github[]
aa bb f7 a7 32 da
----
-UTF-8, UTF-16, and UTF-32 literal strings::
+Strings::
+
Input:
+
----
"hello world!" 00
u16le"stress\nverdict 🤣"
+s:latin3{hex(ICITTE)}
----
+
Output:
----
68 65 6c 6c 6f 20 77 6f 72 6c 64 21 00 73 00 74 ┆ hello world!•s•t
00 72 00 65 00 73 00 73 00 0a 00 76 00 65 00 72 ┆ •r•e•s•s•••v•e•r
-00 64 00 69 00 63 00 74 00 20 00 3e d8 23 dd ┆ •d•i•c•t• •>•#•
+00 64 00 69 00 63 00 74 00 20 00 3e d8 23 dd 30 ┆ •d•i•c•t• •>•#•0
+78 32 66 ┆ x2f
----
Labels: special variables holding the offset where they're defined::
|
The current offset has an effect on the value of <<label,labels>> and of
the special `ICITTE` name in <<fixed-length-number,fixed-length
-number>>, <<leb-128-integer,LEB128 integer>>,
+number>>, <<leb-128-integer,LEB128 integer>>, <<string,string>>,
<<filling,filling>>, <<variable-assignment,variable assignment>>,
<<conditional-block,conditional block>>, <<repetition-block,repetition
block>>, <<macro-expansion,macro expansion>>, and
|<<variable-assignment,Variables>>
|Mapping of variable names to integral or floating point number values.
|`init_variables` parameter of the `parse()` function.
-|One or more `--var` options.
+|One or more `--var` or `--var-str` options.
|===
The available items are:
* A <<byte-constant,constant integer>> representing one or more
constant bytes.
-* A <<literal-string,literal string>> representing a sequence of bytes
- encoding UTF-8, UTF-16, or UTF-32 data.
+* A <<literal-string,literal string>> representing a constant sequence
+ of bytes encoding UTF-8, UTF-16, UTF-32, or Latin-1 to Latin-10 data.
* A <<current-byte-order-setting,current byte order setting>> (big or
little endian).
* An <<leb128-integer,LEB128 integer>> of which the value is the result
of a {py3} expression.
+* A <<string,string>> representing a sequence of bytes encoding UTF-8,
+ UTF-16, UTF-32, or Latin-1 to Latin-10 data, and of which the value is
+ the result of a {py3} expression.
+
* A <<current-offset-setting,current offset setting>>.
* A <<current-offset-alignment,current offset alignment>>.
=== Literal string
-A _literal string_ represents the UTF-8-, UTF-16-, or UTF-32-encoded
-bytes of a string.
+A _literal string_ represents the encoded bytes of a literal string
+using the UTF-8, UTF-16, UTF-32, or Latin-1 to Latin-10 encoding.
The string to encode isn't implicitly null-terminated: use `\0` at the
end of the string to add a null character.
A literal string is:
-. **Optional**: one of the following encodings instead of UTF-8:
+. **Optional**: one of the following encodings instead of the default
+ UTF-8:
+
--
[horizontal]
-`u16be`:: UTF-16BE.
-`u16le`:: UTF-16LE.
-`u32be`:: UTF-32BE.
-`u32le`:: UTF-32LE.
+`s:u8`::
+`u8`::
+ UTF-8.
+
+`s:u16be`::
+`u16be`::
+ UTF-16BE.
+
+`s:u16le`::
+`u16le`::
+ UTF-16LE.
+
+`s:u32be`::
+`u32be`::
+ UTF-32BE.
+
+`s:u32le`::
+`u32le`::
+ UTF-32LE.
+
+`s:latin1`::
+ ISO/IEC 8859-1.
+
+`s:latin2`::
+ ISO/IEC 8859-2.
+
+`s:latin3`::
+ ISO/IEC 8859-3.
+
+`s:latin4`::
+ ISO/IEC 8859-4.
+
+`s:latin5`::
+ ISO/IEC 8859-9.
+
+`s:latin6`::
+ ISO/IEC 8859-10.
+
+`s:latin7`::
+ ISO/IEC 8859-13.
+
+`s:latin8`::
+ ISO/IEC 8859-14.
+
+`s:latin9`::
+ ISO/IEC 8859-15.
+
+`s:latin10`::
+ ISO/IEC 8859-16.
--
. The ``pass:["]`` prefix.
Input:
----
-u32be "\"illusion is the first\nof all pleasures\" 🦉"
+s:u32be "\"illusion is the first\nof all pleasures\" 🦉"
----
Output:
----
====
+====
+Input:
+
+----
+s:latin1 "Paul Piché"
+----
+
+Output:
+
+----
+50 61 75 6c 20 50 69 63 68 e9 ┆ Paul Pich•
+----
+====
+
=== Current byte order setting
This special item sets the <<cur-bo,_current byte order_>>.
----
====
+=== String
+
+A _string_ represents a variable number of bytes encoding a string which
+is the result of evaluating a {py3} expression using the UTF-8, UTF-16,
+UTF-32, or Latin-1 to Latin-10 encoding.
+
+A string has two possible forms:
+
+Encoding prefix form:: {empty}
++
+. An encoding amongst:
++
+--
+[horizontal]
+`s:u8`::
+`u8`::
+ UTF-8.
+
+`s:u16be`::
+`u16be`::
+ UTF-16BE.
+
+`s:u16le`::
+`u16le`::
+ UTF-16LE.
+
+`s:u32be`::
+`u32be`::
+ UTF-32BE.
+
+`s:u32le`::
+`u32le`::
+ UTF-32LE.
+
+`s:latin1`::
+ ISO/IEC 8859-1.
+
+`s:latin2`::
+ ISO/IEC 8859-2.
+
+`s:latin3`::
+ ISO/IEC 8859-3.
+
+`s:latin4`::
+ ISO/IEC 8859-4.
+
+`s:latin5`::
+ ISO/IEC 8859-9.
+
+`s:latin6`::
+ ISO/IEC 8859-10.
+
+`s:latin7`::
+ ISO/IEC 8859-13.
+
+`s:latin8`::
+ ISO/IEC 8859-14.
+
+`s:latin9`::
+ ISO/IEC 8859-15.
+
+`s:latin10`::
+ ISO/IEC 8859-16.
+--
+
+. The ``pass:[{]`` prefix.
+
+. A valid {py3} expression of which the evaluation result type
+ is `bool`, `int`, `float`, or `str` (the first three automatically
+ converted to `str`).
++
+For a string at some source location{nbsp}__**L**__, this expression may
+contain:
++
+--
+* The name of any <<label,label>> defined before{nbsp}__**L**__
+ which isn't within a nested group.
+* The name of any <<variable-assignment,variable>> known
+ at{nbsp}__**L**__.
+--
++
+The value of the special name `ICITTE` (`int` type) in this expression
+is the <<cur-offset,current offset>> (before encoding the string).
+
+. The `}` suffix.
+
+Encoding suffix form:: {empty}
++
+. The ``pass:[{]`` prefix.
+
+. A valid {py3} expression of which the evaluation result type
+ is `bool`, `int`, `float`, or `str` (the first three automatically
+ converted to `str`).
++
+For a string at some source location{nbsp}__**L**__, this expression may
+contain:
++
+--
+* The name of any <<label,label>> defined before{nbsp}__**L**__
+ which isn't within a nested group.
+* The name of any <<variable-assignment,variable>> known
+ at{nbsp}__**L**__.
+--
++
+The value of the special name `ICITTE` (`int` type) in this expression
+is the <<cur-offset,current offset>> (before encoding the string).
+
+. The `:` character.
+
+. A string encoding amongst:
++
+--
+[horizontal]
+`s:u8`::
+ UTF-8.
+
+`s:u16be`::
+ UTF-16BE.
+
+`s:u16le`::
+ UTF-16LE.
+
+`s:u32be`::
+ UTF-32BE.
+
+`s:u32le`::
+ UTF-32LE.
+
+`s:latin1`::
+ ISO/IEC 8859-1.
+
+`s:latin2`::
+ ISO/IEC 8859-2.
+
+`s:latin3`::
+ ISO/IEC 8859-3.
+
+`s:latin4`::
+ ISO/IEC 8859-4.
+
+`s:latin5`::
+ ISO/IEC 8859-9.
+
+`s:latin6`::
+ ISO/IEC 8859-10.
+
+`s:latin7`::
+ ISO/IEC 8859-13.
+
+`s:latin8`::
+ ISO/IEC 8859-14.
+
+`s:latin9`::
+ ISO/IEC 8859-15.
+
+`s:latin10`::
+ ISO/IEC 8859-16.
+--
+
+. The `}` suffix.
+
+====
+Input:
+
+----
+{iter = 1}
+
+!repeat 10
+ {iter : s:u8} " "
+ {iter = iter + 1}
+!end
+----
+
+Output:
+
+----
+31 20 32 20 33 20 34 20 35 20 36 20 37 20 38 20 ┆ 1 2 3 4 5 6 7 8
+39 20 31 30 20 ┆ 9 10
+----
+====
+
+====
+Input:
+
+----
+{meow = 'salut jérémie'}
+{meow.upper() : s:latin1}
+----
+
+Output:
+
+----
+53 41 4c 55 54 20 4a c9 52 c9 4d 49 45 ┆ SALUT J•R•MIE
+----
+====
+
=== Current offset setting
This special item sets the <<cur-offset,_current offset_>>.
. The `=` character.
-. A valid {py3} expression of which the evaluation result type
- is `int`, `float`, or `bool` (automatically converted to `int`).
+. A valid {py3} expression of which the evaluation result type is `int`,
+ `float`, or `bool` (automatically converted to `int`), or `str`.
+
For a variable assignment at some source location{nbsp}__**L**__, this
expression may contain:
** A <<literal-string,literal string>>.
** A <<fixed-length-number,fixed-length number>>.
** An <<leb128-integer,LEB128 integer>>.
+** A <<string,string>>.
** A <<macro-expansion,macro-expansion>>.
** A <<group,group>>.
# Upstream repository: <https://github.com/efficios/normand>.
__author__ = "Philippe Proulx"
-__version__ = "0.18.0"
+__version__ = "0.19.0"
__all__ = [
"__author__",
"__version__",
return "_Byte({}, {})".format(hex(self._val), repr(self._text_loc))
-# String.
-class _Str(_ScalarItem, _RepableItem):
+# Literal string.
+class _LitStr(_ScalarItem, _RepableItem):
def __init__(self, data: bytes, text_loc: TextLocation):
super().__init__(text_loc)
self._data = data
return len(self._data)
def __repr__(self):
- return "_Str({}, {})".format(repr(self._data), repr(self._text_loc))
+ return "_LitStr({}, {})".format(repr(self._data), repr(self._text_loc))
# Byte order.
pass
+# String.
+class _Str(_Item, _RepableItem, _ExprMixin):
+ def __init__(
+ self, expr_str: str, expr: ast.Expression, codec: str, text_loc: TextLocation
+ ):
+ super().__init__(text_loc)
+ _ExprMixin.__init__(self, expr_str, expr)
+ self._codec = codec
+
+ # Codec name.
+ @property
+ def codec(self):
+ return self._codec
+
+ def __repr__(self):
+ return "_Str({}, {}, {}, {})".format(
+ self.__class__.__name__,
+ repr(self._expr_str),
+ repr(self._expr),
+ repr(self._codec),
+ repr(self._text_loc),
+ )
+
+
# Group of items.
class _Group(_Item, _RepableItem):
def __init__(self, items: List[_Item], text_loc: TextLocation):
return s
+# Encodes the string `s` using the codec `codec`, raising `ParseError`
+# with `text_loc` on encoding error.
+def _encode_str(s: str, codec: str, text_loc: TextLocation):
+ try:
+ return s.encode(codec)
+ except UnicodeEncodeError:
+ _raise_error(
+ "Cannot encode `{}` with the `{}` encoding".format(s, codec), text_loc
+ )
+
+
# Variables dictionary type (for type hints).
-VariablesT = Dict[str, Union[int, float]]
+VariablesT = Dict[str, Union[int, float, str]]
# Labels dictionary type (for type hints).
if item is not None:
return item
- # Patterns for _try_parse_str()
- _str_prefix_pat = re.compile(r'(?:u(?P<len>16|32)(?P<bo>be|le))?\s*"')
- _str_suffix_pat = re.compile(r'"')
- _str_str_pat = re.compile(r'(?:(?:\\.)|[^"])*')
-
# Strings corresponding to escape sequence characters
- _str_escape_seq_strs = {
+ _lit_str_escape_seq_strs = {
"0": "\0",
"a": "\a",
"b": "\b",
'"': '"',
}
- # Tries to parse a string, returning a string item on success.
- def _try_parse_str(self):
- begin_text_loc = self._text_loc
+ # Patterns for _try_parse_lit_str()
+ _lit_str_prefix_suffix_pat = re.compile(r'"')
+ _lit_str_contents_pat = re.compile(r'(?:(?:\\.)|[^"])*')
- # Match prefix
- m = self._try_parse_pat(self._str_prefix_pat)
+ # Parses a literal string between double quotes (without an encoding
+ # prefix) and returns the resulting string.
+ def _try_parse_lit_str(self, with_prefix: bool):
+ # Match prefix if needed
+ if with_prefix:
+ if self._try_parse_pat(self._lit_str_prefix_suffix_pat) is None:
+ # No match
+ return
- if m is None:
+ # Expect literal string
+ m = self._expect_pat(self._lit_str_contents_pat, "Expecting a literal string")
+
+ # Expect end of string
+ self._expect_pat(
+ self._lit_str_prefix_suffix_pat, 'Expecting `"` (end of literal string)'
+ )
+
+ # Replace escape sequences
+ val = m.group(0)
+
+ for ec in '0abefnrtv"\\':
+ val = val.replace(r"\{}".format(ec), self._lit_str_escape_seq_strs[ec])
+
+ # Return string
+ return val
+
+ # Patterns for _try_parse_utf_str_encoding()
+ _str_encoding_utf_prefix_pat = re.compile(r"u")
+ _str_encoding_utf_pat = re.compile(r"(?:8|(?:(?:16|32)(?:[bl]e)))\b")
+
+ # Tries to parse a UTF encoding specification, returning the Python
+ # codec name on success.
+ def _try_parse_utf_str_encoding(self):
+ # Match prefix
+ if self._try_parse_pat(self._str_encoding_utf_prefix_pat) is None:
# No match
return
- # Get encoding
- encoding = "utf8"
+ # Expect UTF specification
+ m = self._expect_pat(
+ self._str_encoding_utf_pat,
+ "Expecting `8`, `16be`, `16le`, `32be` or `32le`",
+ )
+
+ # Convert to codec name
+ return {
+ "8": "utf_8",
+ "16be": "utf_16_be",
+ "16le": "utf_16_le",
+ "32be": "utf_32_be",
+ "32le": "utf_32_le",
+ }[m.group(0)]
+
+ # Patterns for _try_parse_str_encoding()
+ _str_encoding_gen_prefix_pat = re.compile(r"s")
+ _str_encoding_colon_pat = re.compile(r":")
+ _str_encoding_non_utf_pat = re.compile(r"latin(?:[1-9]|10)\b")
+
+ # Tries to parse a string encoding specification, returning the
+ # Python codec name on success.
+ #
+ # Requires the general prefix (`s:`) if `req_gen_prefix` is `True`.
+ def _try_parse_str_encoding(self, req_gen_prefix: bool = False):
+ # General prefix?
+ if self._try_parse_pat(self._str_encoding_gen_prefix_pat) is not None:
+ # Expect `:`
+ self._skip_ws()
+ self._expect_pat(self._str_encoding_colon_pat, "Expecting `:`")
- if m.group("len") is not None:
- encoding = "utf_{}_{}".format(m.group("len"), m.group("bo"))
+ # Expect encoding specification
+ self._skip_ws()
- # Actual string
- m = self._expect_pat(self._str_str_pat, "Expecting a literal string")
+ # UTF?
+ codec = self._try_parse_utf_str_encoding()
- # Expect end of string
- self._expect_pat(self._str_suffix_pat, 'Expecting `"` (end of literal string)')
+ if codec is not None:
+ return codec
- # Replace escape sequences
- val = m.group(0)
+ # Expect Latin
+ m = self._expect_pat(
+ self._str_encoding_non_utf_pat,
+ "Expecting `u8`, `u16be`, `u16le`, `u32be`, `u32le`, or `latin1` to `latin10`",
+ )
+ return m.group(0)
- for ec in '0abefnrtv"\\':
- val = val.replace(r"\{}".format(ec), self._str_escape_seq_strs[ec])
+ # UTF?
+ if not req_gen_prefix:
+ return self._try_parse_utf_str_encoding()
- # Encode
- data = val.encode(encoding)
+ # Patterns for _try_parse_str()
+ _lit_str_prefix_pat = re.compile(r'"')
+ _str_prefix_pat = re.compile(r'"|\{')
+ _str_expr_pat = re.compile(r"[^}]+")
+ _str_expr_suffix_pat = re.compile(r"\}")
- # Return item
- return _Str(data, begin_text_loc)
+ # Tries to parse a string, returning a literal string or string item
+ # on success.
+ def _try_parse_str(self):
+ begin_text_loc = self._text_loc
+
+ # Encoding
+ codec = self._try_parse_str_encoding()
+
+ # Match prefix (expect if there's an encoding specification)
+ self._skip_ws()
+
+ if codec is None:
+ # No encoding: only a literal string (UTF-8) is legal
+ m_prefix = self._try_parse_pat(self._lit_str_prefix_pat)
+
+ if m_prefix is None:
+ return
+ else:
+ # Encoding present: expect a string prefix
+ m_prefix = self._expect_pat(self._str_prefix_pat, 'Expecting `"` or `{`')
+
+ # Literal string or expression?
+ prefix = m_prefix.group(0)
+
+ if prefix == '"':
+ # Expect literal string
+ str_text_loc = self._text_loc
+ val = self._try_parse_lit_str(False)
+
+ if val is None:
+ self._raise_error("Expecting a literal string")
+
+ # Encode string
+ data = _encode_str(val, "utf_8" if codec is None else codec, str_text_loc)
+
+ # Return item
+ return _LitStr(data, begin_text_loc)
+ else:
+ # Expect expression
+ self._skip_ws()
+ expr_text_loc = self._text_loc
+ m = self._expect_pat(self._str_expr_pat, "Expecting an expression")
+
+ # Expect `}`
+ self._expect_pat(self._str_expr_suffix_pat, "Expecting `}`")
+
+ # Create an expression node from the expression string
+ expr_str, expr = self._ast_expr_from_str(m.group(0), expr_text_loc)
+
+ # Return item
+ assert codec is not None
+ return _Str(expr_str, expr, codec, begin_text_loc)
# Common right parenthesis pattern
_right_paren_pat = re.compile(r"\)")
return expr_str, expr
- # Patterns for _try_parse_num_and_attr()
+ # Patterns for _try_parse_val()
_val_expr_pat = re.compile(r"([^}:]+):\s*")
- _fl_num_len_attr_pat = re.compile(r"8|16|24|32|40|48|56|64")
- _leb128_int_attr_pat = re.compile(r"(u|s)leb128")
+ _fl_num_len_fmt_pat = re.compile(r"8|16|24|32|40|48|56|64")
+ _leb128_int_fmt_pat = re.compile(r"(u|s)leb128")
- # Tries to parse a value and attribute (fixed length in bits or
- # `leb128`), returning a value item on success.
- def _try_parse_num_and_attr(self):
+ # Tries to parse a value (number or string) and format (fixed length
+ # in bits, `uleb128`, `sleb128`, or `s:` followed with an encoding
+ # name), returning an item on success.
+ def _try_parse_val(self):
begin_text_loc = self._text_loc
# Match
# Create an expression node from the expression string
expr_str, expr = self._ast_expr_from_str(m_expr.group(1), begin_text_loc)
- # Length?
- m_attr = self._try_parse_pat(self._fl_num_len_attr_pat)
+ # Fixed length?
+ m_fmt = self._try_parse_pat(self._fl_num_len_fmt_pat)
- if m_attr is None:
+ if m_fmt is None:
# LEB128?
- m_attr = self._try_parse_pat(self._leb128_int_attr_pat)
+ m_fmt = self._try_parse_pat(self._leb128_int_fmt_pat)
- if m_attr is None:
- # At this point it's invalid
- self._raise_error(
- "Expecting a length (multiple of eight bits), `uleb128`, or `sleb128`"
- )
+ if m_fmt is None:
+ # String encoding?
+ codec = self._try_parse_str_encoding(True)
+
+ if codec is None:
+ # At this point it's invalid
+ self._raise_error(
+ "Expecting a fixed length (multiple of eight bits), `uleb128`, `sleb128`, or `s:` followed with a valid encoding (`u8`, `u16be`, `u16le`, `u32be`, `u32le`, or `latin1` to `latin10`)"
+ )
+ else:
+ # Return string item
+ return _Str(expr_str, expr, codec, begin_text_loc)
# Return LEB128 integer item
- cls = _ULeb128Int if m_attr.group(1) == "u" else _SLeb128Int
+ cls = _ULeb128Int if m_fmt.group(1) == "u" else _SLeb128Int
return cls(expr_str, expr, begin_text_loc)
else:
# Return fixed-length number item
return _FlNum(
expr_str,
expr,
- int(m_attr.group(0)),
+ int(m_fmt.group(0)),
begin_text_loc,
)
# Patterns for _try_parse_var_assign()
- _var_assign_name_equal_pat = re.compile(r"({})\s*=".format(_py_name_pat.pattern))
+ _var_assign_name_equal_pat = re.compile(
+ r"({})\s*=(?!=)".format(_py_name_pat.pattern)
+ )
_var_assign_expr_pat = re.compile(r"[^}]+")
# Tries to parse a variable assignment, returning a variable
item = self._try_parse_var_assign()
if item is None:
- # Number item?
- item = self._try_parse_num_and_attr()
+ # Value item?
+ item = self._try_parse_val()
if item is None:
# Byte order setting item?
if item is None:
# At this point it's invalid
self._raise_error(
- "Expecting a fixed-length number, a variable assignment, or a byte order setting"
+ "Expecting a fixed-length number, a string, a variable assignment, or a byte order setting"
)
# Expect suffix
accept_const_int: bool = False,
allow_neg_int: bool = False,
accept_const_float: bool = False,
+ accept_lit_str: bool = False,
):
begin_text_loc = self._text_loc
if m is not None:
return self._ast_expr_from_str(m.group(0), begin_text_loc)
+ # Literal string
+ if accept_lit_str:
+ val = self._try_parse_lit_str(True)
+
+ if val is not None:
+ return self._ast_expr_from_str(repr(val), begin_text_loc)
+
# Expect `{`
msg_accepted_parts = ["a name", "or `{`"]
+ if accept_lit_str:
+ msg_accepted_parts.insert(0, "a literal string")
+
if accept_const_float:
msg_accepted_parts.insert(0, "a constant floating point number")
# End
break
- # Expect a Value
+ # Expect a value
if expect_comma:
self._expect_pat(self._macro_params_comma_pat, "Expecting `,`")
accept_const_int=True,
allow_neg_int=True,
accept_const_float=True,
+ accept_lit_str=True,
),
text_loc=param_text_loc
)
# `expr_str` at the location `text_loc` considering the current
# generation state `state`.
#
- # If `allow_float` is `True`, then the type of the result may be
+ # If `accept_float` is `True`, then the type of the result may be
# `float` too.
+ #
+ # If `accept_str` is `True`, then the type of the result may be
+ # `str` too.
@staticmethod
def _eval_expr(
expr_str: str,
expr: ast.Expression,
text_loc: TextLocation,
state: _GenState,
- allow_float: bool = False,
+ accept_float: bool = False,
+ accept_str: bool = False,
):
syms = {} # type: VariablesT
syms.update(state.labels)
# Validate result type
expected_types = {int} # type: Set[type]
- type_msg = "`int`"
- if allow_float:
+ if accept_float:
expected_types.add(float)
- type_msg += " or `float`"
+
+ if accept_str:
+ expected_types.add(str)
if type(val) not in expected_types:
+ expected_types_str = sorted(
+ ["`{}`".format(t.__name__) for t in expected_types]
+ )
+
+ if len(expected_types_str) == 1:
+ msg_expected = expected_types_str[0]
+ elif len(expected_types_str) == 2:
+ msg_expected = " or ".join(expected_types_str)
+ else:
+ expected_types_str[-1] = "or {}".format(expected_types_str[-1])
+ msg_expected = ", ".join(expected_types_str)
+
_raise_error(
"Invalid expression `{}`: expecting result type {}, not `{}`".format(
- expr_str, type_msg, type(val).__name__
+ expr_str, msg_expected, type(val).__name__
),
text_loc,
)
return val
- # Evaluates the expression of `item` considering the current
- # generation state `state`.
- #
- # If `allow_float` is `True`, then the type of the result may be
- # `float` too.
+ # Forwards to _eval_expr() with the expression and text location of
+ # `item`.
@staticmethod
def _eval_item_expr(
- item: Union[_FlNum, _Leb128Int, _FillUntil, _VarAssign, _Rep, _Cond],
+ item: Union[_Cond, _FillUntil, _FlNum, _Leb128Int, _Rep, _Str, _VarAssign],
state: _GenState,
- allow_float: bool = False,
+ accept_float: bool = False,
+ accept_str: bool = False,
):
return _Gen._eval_expr(
- item.expr_str, item.expr, item.text_loc, state, allow_float
+ item.expr_str, item.expr, item.text_loc, state, accept_float, accept_str
)
# Handles the byte item `item`.
self._data.append(item.val)
state.offset += item.size
- # Handles the string item `item`.
- def _handle_str_item(self, item: _Str, state: _GenState):
+ # Handles the literal string item `item`.
+ def _handle_lit_str_item(self, item: _LitStr, state: _GenState):
self._data += item.data
state.offset += item.size
# Handles the variable assignment item `item`.
def _handle_var_assign_item(self, item: _VarAssign, state: _GenState):
# Update variable
- state.variables[item.name] = self._eval_item_expr(item, state, True)
+ state.variables[item.name] = self._eval_item_expr(
+ item, state, accept_float=True, accept_str=True
+ )
# Handles the fixed-length number item `item`.
def _handle_fl_num_item(self, item: _FlNum, state: _GenState):
# Handles the LEB128 integer item `item`.
def _handle_leb128_int_item(self, item: _Leb128Int, state: _GenState):
# Compute value
- val = self._eval_item_expr(item, state, False)
+ val = self._eval_item_expr(item, state)
# Size in bytes
size = self._leb128_size_for_val(val, type(item) is _SLeb128Int)
# Update offset
state.offset += size
+ # Handles the string item `item`.
+ def _handle_str_item(self, item: _Str, state: _GenState):
+ # Compute value
+ val = str(self._eval_item_expr(item, state, accept_float=True, accept_str=True))
+
+ # Encode
+ data = _encode_str(val, item.codec, item.text_loc)
+
+ # Add to data
+ self._data += data
+
+ # Update offset
+ state.offset += len(data)
+
# Handles the group item `item`, removing the immediate labels from
# `state` at the end if `remove_immediate_labels` is `True`.
def _handle_group_item(
for param_name, param in zip(macro_def.param_names, item.params):
exp_state.variables[param_name] = _Gen._eval_expr(
- param.expr_str, param.expr, param.text_loc, init_state, True
+ param.expr_str,
+ param.expr,
+ param.text_loc,
+ init_state,
+ accept_float=True,
+ accept_str=True,
)
return exp_state
_FlNum: self._handle_fl_num_item,
_Group: self._handle_group_item,
_Label: self._handle_label_item,
+ _LitStr: self._handle_lit_str_item,
_MacroExp: self._handle_macro_exp_item,
_Rep: self._handle_rep_item,
_SetBo: self._handle_set_bo_item,
# Returns a dictionary of string to numbers from the list of strings
# `args` containing `NAME=VAL` entries.
-def _dict_from_arg(args: Optional[List[str]], is_label: bool):
+def _dict_from_arg(args: Optional[List[str]], is_label: bool, is_str_only: bool):
d = {} # type: VariablesT
if args is None:
return d
for arg in args:
- m = re.match(r"({})\s*=\s*(.+)$".format(_py_name_pat.pattern), arg)
+ m = re.match(r"({})\s*=\s*(.*)$".format(_py_name_pat.pattern), arg)
if m is None:
_raise_cli_error("Invalid assignment `{}`".format(arg))
- d[m.group(1)] = _val_from_assign_val_str(m.group(2), is_label)
+ if is_str_only:
+ val = m.group(2)
+ else:
+ val = _val_from_assign_val_str(m.group(2), is_label)
+
+ d[m.group(1)] = val
return d
"--var",
metavar="NAME=VAL",
action="append",
- help="add an initial variable (may be repeated)",
+ help="add an initial numeric variable (may be repeated)",
+ )
+ ap.add_argument(
+ "-s",
+ "--var-str",
+ metavar="NAME=VAL",
+ action="append",
+ help="add an initial string variable (may be repeated)",
)
ap.add_argument(
"-l",
normand = f.read()
# Variables and labels
- variables = _dict_from_arg(args.var, False)
- labels = _dict_from_arg(args.label, True)
+ variables = _dict_from_arg(args.var, False, False)
+ variables.update(_dict_from_arg(args.var_str, False, True))
+ labels = _dict_from_arg(args.label, True, False)
# Validate offset
if args.offset < 0:
[tool.poetry]
name = 'normand'
-version = '0.18.0'
+version = '0.19.0'
description = 'Text-to-binary processor with its own language'
license = 'MIT'
authors = ['Philippe Proulx <eeppeliteloop@gmail.com>']
{ : 8 }
---
-1:3 - Expecting a fixed-length number, a variable assignment, or a byte order setting
+1:3 - Expecting a fixed-length number, a string, a variable assignment, or a byte order setting
{ 'salut' : 8 }
---
-1:3 - Invalid expression `'salut'`: expecting result type `int` or `float`, not `str`
+1:3 - Invalid expression `'salut'`: expecting result type `float` or `int`, not `str`
{ 23 : 17 }
---
-1:8 - Expecting a length (multiple of eight bits), `uleb128`, or `sleb128`
+1:8 - Expecting a fixed length (multiple of eight bits), `uleb128`, `sleb128`, or `s:` followed with a valid encoding (`u8`, `u16be`, `u16le`, `u32be`, `u32le`, or `latin1` to `latin10`)
{ 23 : }
---
-1:9 - Expecting a length (multiple of eight bits), `uleb128`, or `sleb128`
+1:9 - Expecting a fixed length (multiple of eight bits), `uleb128`, `sleb128`, or `s:` followed with a valid encoding (`u8`, `u16be`, `u16le`, `u32be`, `u32le`, or `latin1` to `latin10`)
--- /dev/null
+"the string
+---
+2:1 - Expecting `"` (end of literal string)
+++ /dev/null
-!macro salut(a, b, c)
- "meow" 23 42
-!end
-
-aa bb m:salut(42, "yo", 12) ff
----
-5:19 - Expecting a constant integer, a constant floating point number, a name, or `{`
{ : sleb128 }
---
-1:3 - Expecting a fixed-length number, a variable assignment, or a byte order setting
+1:3 - Expecting a fixed-length number, a string, a variable assignment, or a byte order setting
--- /dev/null
+ff u16be ee
+---
+1:10 - Expecting `"` or `{`
+++ /dev/null
-"the string
----
-2:1 - Expecting `"` (end of literal string)
--- /dev/null
+{ : s:u8}
+---
+1:3 - Expecting a fixed-length number, a string, a variable assignment, or a byte order setting
--- /dev/null
+{"a string" : s:latin11}
+---
+1:17 - Expecting `u8`, `u16be`, `u16le`, `u32be`, `u32le`, or `latin1` to `latin10`
--- /dev/null
+{"a string" : s:u24be}
+---
+1:18 - Expecting `8`, `16be`, `16le`, `32be` or `32le`
--- /dev/null
+{"a string" : slatin1}
+---
+1:16 - Expecting `:`
--- /dev/null
+{"a string" : }
+---
+1:15 - Expecting a fixed length (multiple of eight bits), `uleb128`, `sleb128`, or `s:` followed with a valid encoding (`u8`, `u16be`, `u16le`, `u32be`, `u32le`, or `latin1` to `latin10`)
--- /dev/null
+{"a string" : latin1}
+---
+1:15 - Expecting a fixed length (multiple of eight bits), `uleb128`, `sleb128`, or `s:` followed with a valid encoding (`u8`, `u16be`, `u16le`, `u32be`, `u32le`, or `latin1` to `latin10`)
--- /dev/null
+{"a string" : s:u}
+---
+1:18 - Expecting `8`, `16be`, `16le`, `32be` or `32le`
--- /dev/null
+s:latin11{"a string"}
+---
+1:3 - Expecting `u8`, `u16be`, `u16le`, `u32be`, `u32le`, or `latin1` to `latin10`
--- /dev/null
+s:latin3 {"hello " 23}
+---
+1:11 - Invalid expression `"hello " 23`: invalid syntax
--- /dev/null
+u24be{"a string"}
+---
+1:2 - Expecting `8`, `16be`, `16le`, `32be` or `32le`
--- /dev/null
+slatin1{"a string"}
+---
+1:2 - Expecting `:`
--- /dev/null
+{"a string"}
+---
+1:2 - Expecting a fixed-length number, a string, a variable assignment, or a byte order setting
--- /dev/null
+latin1{"a string"}
+---
+1:1 - Unexpected character `l`
--- /dev/null
+s:latin5 {some_string
+---
+2:1 - Expecting `}`
--- /dev/null
+u{"a string"}
+---
+1:2 - Expecting `8`, `16be`, `16le`, `32be` or `32le`
{ : uleb128 }
---
-1:3 - Expecting a fixed-length number, a variable assignment, or a byte order setting
+1:3 - Expecting a fixed-length number, a string, a variable assignment, or a byte order setting
+++ /dev/null
-{ meow = 'salut' }
----
-1:3 - Invalid expression `'salut'`: expecting result type `int` or `float`, not `str`
{ hello }
---
-1:3 - Expecting a fixed-length number, a variable assignment, or a byte order setting
+1:3 - Expecting a fixed-length number, a string, a variable assignment, or a byte order setting
--- /dev/null
+u16be"\0 \a \b \e \f \n \r \t \v \" \\"
+---
+00 00 00 20 00 07 00 20 00 08 00 20 00 1b 00 20
+00 0c 00 20 00 0a 00 20 00 0d 00 20 00 09 00 20
+00 0b 00 20 00 22 00 20 00 5c
--- /dev/null
+u16le"\0 \a \b \e \f \n \r \t \v \" \\"
+---
+00 00 20 00 07 00 20 00 08 00 20 00 1b 00 20 00
+0c 00 20 00 0a 00 20 00 0d 00 20 00 09 00 20 00
+0b 00 20 00 22 00 20 00 5c 00
--- /dev/null
+u32be"\0 \a \b \e \f \n \r \t \v \" \\"
+---
+00 00 00 00 00 00 00 20 00 00 00 07 00 00 00 20
+00 00 00 08 00 00 00 20 00 00 00 1b 00 00 00 20
+00 00 00 0c 00 00 00 20 00 00 00 0a 00 00 00 20
+00 00 00 0d 00 00 00 20 00 00 00 09 00 00 00 20
+00 00 00 0b 00 00 00 20 00 00 00 22 00 00 00 20
+00 00 00 5c
--- /dev/null
+u32le"\0 \a \b \e \f \n \r \t \v \" \\"
+---
+00 00 00 00 20 00 00 00 07 00 00 00 20 00 00 00
+08 00 00 00 20 00 00 00 1b 00 00 00 20 00 00 00
+0c 00 00 00 20 00 00 00 0a 00 00 00 20 00 00 00
+0d 00 00 00 20 00 00 00 09 00 00 00 20 00 00 00
+0b 00 00 00 20 00 00 00 22 00 00 00 20 00 00 00
+5c 00 00 00
--- /dev/null
+"\0 \a \b \e \f \n \r \t \v \" \\"
+---
+00 20 07 20 08 20 1b 20 0c 20 0a 20 0d 20 09 20 0b 20 22 20 5c
--- /dev/null
+s:latin1 " !\"#$%&'()*+,-./"
+s:latin1 "0123456789:;<=>?"
+s:latin1 "@ABCDEFGHIJKLMNO"
+s:latin1 "PQRSTUVWXYZ[\]^_"
+s:latin1 "`abcdefghijklmno"
+s:latin1 "pqrstuvwxyz{|}~¡"
+s:latin1 "¢£¤¥¦§¨©ª«¬®¯°±²"
+s:latin1 "³´µ¶·¸¹º»¼½¾¿ÀÁÂ"
+s:latin1 "ÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒ"
+s:latin1 "ÓÔÕÖ×ØÙÚÛÜÝÞßàáâ"
+s:latin1 "ãäåæçèéêëìíîïðñò"
+s:latin1 "óôõö÷øùúûüýþÿ"
+---
+20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
+30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
+40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
+50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
+60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
+70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e a1
+a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ae af b0 b1 b2
+b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2
+c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2
+d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2
+e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2
+f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
--- /dev/null
+s:latin10 " !\"#$%&'()*+,-./"
+s:latin10 "0123456789:;<=>?"
+s:latin10 "@ABCDEFGHIJKLMNO"
+s:latin10 "PQRSTUVWXYZ[\]^_"
+s:latin10 "`abcdefghijklmno"
+s:latin10 "pqrstuvwxyz{|}~Ą"
+s:latin10 "ąŁ€„Š§š©Ș«ŹźŻ°±Č"
+s:latin10 "łŽ”¶·žčș»ŒœŸżÀÁÂ"
+s:latin10 "ĂÄĆÆÇÈÉÊËÌÍÎÏĐŃÒ"
+s:latin10 "ÓÔŐÖŚŰÙÚÛÜĘȚßàáâ"
+s:latin10 "ăäćæçèéêëìíîïđńò"
+s:latin10 "óôőöśűùúûüęțÿ"
+---
+20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
+30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
+40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
+50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
+60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
+70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e a1
+a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ae af b0 b1 b2
+b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2
+c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2
+d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2
+e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2
+f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
--- /dev/null
+s:latin2 " !\"#$%&'()*+,-./"
+s:latin2 "0123456789:;<=>?"
+s:latin2 "@ABCDEFGHIJKLMNO"
+s:latin2 "PQRSTUVWXYZ[\]^_"
+s:latin2 "`abcdefghijklmno"
+s:latin2 "pqrstuvwxyz{|}~Ą"
+s:latin2 "˘Ł¤ĽŚ§¨ŠŞŤŹŽŻ°ą˛"
+s:latin2 "ł´ľśˇ¸šşťź˝žżŔÁÂ"
+s:latin2 "ĂÄĹĆÇČÉĘËĚÍÎĎĐŃŇ"
+s:latin2 "ÓÔŐÖ×ŘŮÚŰÜÝŢßŕáâ"
+s:latin2 "ăäĺćçčéęëěíîďđńň"
+s:latin2 "óôőö÷řůúűüýţ˙"
+---
+20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
+30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
+40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
+50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
+60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
+70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e a1
+a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ae af b0 b1 b2
+b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2
+c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2
+d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2
+e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2
+f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
--- /dev/null
+s:latin3 " !\"#$%&'()*+,-./"
+s:latin3 "0123456789:;<=>?"
+s:latin3 "@ABCDEFGHIJKLMNO"
+s:latin3 "PQRSTUVWXYZ[\]^_"
+s:latin3 "`abcdefghijklmno"
+s:latin3 "pqrstuvwxyz{|}~Ħ"
+s:latin3 "˘£¤Ĥ§¨İŞĞĴŻ°ħ²³´"
+s:latin3 "µĥ·¸ışğĵ½żÀÁÂÄĊĈ"
+s:latin3 "ÇÈÉÊËÌÍÎÏÑÒÓÔĠÖ×"
+s:latin3 "ĜÙÚÛÜŬŜßàáâäċĉçè"
+s:latin3 "éêëìíîïñòóôġö÷ĝù"
+s:latin3 "úûüŭŝ˙"
+---
+20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
+30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
+40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
+50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
+60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
+70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e a1
+a2 a3 a4 a6 a7 a8 a9 aa ab ac af b0 b1 b2 b3 b4
+b5 b6 b7 b8 b9 ba bb bc bd bf c0 c1 c2 c4 c5 c6
+c7 c8 c9 ca cb cc cd ce cf d1 d2 d3 d4 d5 d6 d7
+d8 d9 da db dc dd de df e0 e1 e2 e4 e5 e6 e7 e8
+e9 ea eb ec ed ee ef f1 f2 f3 f4 f5 f6 f7 f8 f9
+fa fb fc fd fe ff
--- /dev/null
+s:latin4 " !\"#$%&'()*+,-./"
+s:latin4 "0123456789:;<=>?"
+s:latin4 "@ABCDEFGHIJKLMNO"
+s:latin4 "PQRSTUVWXYZ[\]^_"
+s:latin4 "`abcdefghijklmno"
+s:latin4 "pqrstuvwxyz{|}~Ą"
+s:latin4 "ĸŖ¤ĨĻ§¨ŠĒĢŦŽ¯°ą˛"
+s:latin4 "ŗ´ĩļˇ¸šēģŧŊžŋĀÁÂ"
+s:latin4 "ÃÄÅÆĮČÉĘËĖÍÎĪĐŅŌ"
+s:latin4 "ĶÔÕÖ×ØŲÚÛÜŨŪßāáâ"
+s:latin4 "ãäåæįčéęëėíîīđņō"
+s:latin4 "ķôõö÷øųúûüũū˙"
+---
+20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
+30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
+40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
+50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
+60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
+70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e a1
+a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ae af b0 b1 b2
+b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2
+c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2
+d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2
+e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2
+f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
--- /dev/null
+s:latin5 " !\"#$%&'()*+,-./"
+s:latin5 "0123456789:;<=>?"
+s:latin5 "@ABCDEFGHIJKLMNO"
+s:latin5 "PQRSTUVWXYZ[\]^_"
+s:latin5 "`abcdefghijklmno"
+s:latin5 "pqrstuvwxyz{|}~¡"
+s:latin5 "¢£¤¥¦§¨©ª«¬®¯°±²"
+s:latin5 "³´µ¶·¸¹º»¼½¾¿ÀÁÂ"
+s:latin5 "ÃÄÅÆÇÈÉÊËÌÍÎÏĞÑÒ"
+s:latin5 "ÓÔÕÖ×ØÙÚÛÜİŞßàáâ"
+s:latin5 "ãäåæçèéêëìíîïğñò"
+s:latin5 "óôõö÷øùúûüışÿ"
+---
+20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
+30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
+40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
+50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
+60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
+70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e a1
+a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ae af b0 b1 b2
+b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2
+c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2
+d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2
+e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2
+f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
--- /dev/null
+s:latin6 " !\"#$%&'()*+,-./"
+s:latin6 "0123456789:;<=>?"
+s:latin6 "@ABCDEFGHIJKLMNO"
+s:latin6 "PQRSTUVWXYZ[\]^_"
+s:latin6 "`abcdefghijklmno"
+s:latin6 "pqrstuvwxyz{|}~Ą"
+s:latin6 "ĒĢĪĨĶ§ĻĐŠŦŽŪŊ°ąē"
+s:latin6 "ģīĩķ·ļđšŧž―ūŋĀÁÂ"
+s:latin6 "ÃÄÅÆĮČÉĘËĖÍÎÏÐŅŌ"
+s:latin6 "ÓÔÕÖŨØŲÚÛÜÝÞßāáâ"
+s:latin6 "ãäåæįčéęëėíîïðņō"
+s:latin6 "óôõöũøųúûüýþĸ"
+---
+20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
+30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
+40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
+50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
+60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
+70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e a1
+a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ae af b0 b1 b2
+b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2
+c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2
+d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2
+e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2
+f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
--- /dev/null
+s:latin7 " !\"#$%&'()*+,-./"
+s:latin7 "0123456789:;<=>?"
+s:latin7 "@ABCDEFGHIJKLMNO"
+s:latin7 "PQRSTUVWXYZ[\]^_"
+s:latin7 "`abcdefghijklmno"
+s:latin7 "pqrstuvwxyz{|}~”"
+s:latin7 "¢£¤„¦§Ø©Ŗ«¬®Æ°±²"
+s:latin7 "³“µ¶·ø¹ŗ»¼½¾æĄĮĀ"
+s:latin7 "ĆÄÅĘĒČÉŹĖĢĶĪĻŠŃŅ"
+s:latin7 "ÓŌÕÖ×ŲŁŚŪÜŻŽßąįā"
+s:latin7 "ćäåęēčéźėģķīļšńņ"
+s:latin7 "óōõö÷ųłśūüżž’"
+---
+20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
+30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
+40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
+50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
+60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
+70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e a1
+a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ae af b0 b1 b2
+b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2
+c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2
+d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2
+e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2
+f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
--- /dev/null
+s:latin8 " !\"#$%&'()*+,-./"
+s:latin8 "0123456789:;<=>?"
+s:latin8 "@ABCDEFGHIJKLMNO"
+s:latin8 "PQRSTUVWXYZ[\]^_"
+s:latin8 "`abcdefghijklmno"
+s:latin8 "pqrstuvwxyz{|}~Ḃ"
+s:latin8 "ḃ£ĊċḊ§Ẁ©ẂḋỲ®ŸḞḟĠ"
+s:latin8 "ġṀṁ¶ṖẁṗẃṠỳẄẅṡÀÁÂ"
+s:latin8 "ÃÄÅÆÇÈÉÊËÌÍÎÏŴÑÒ"
+s:latin8 "ÓÔÕÖṪØÙÚÛÜÝŶßàáâ"
+s:latin8 "ãäåæçèéêëìíîïŵñò"
+s:latin8 "óôõöṫøùúûüýŷÿ"
+---
+20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
+30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
+40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
+50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
+60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
+70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e a1
+a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ae af b0 b1 b2
+b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2
+c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2
+d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2
+e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2
+f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
--- /dev/null
+s:latin9 " !\"#$%&'()*+,-./"
+s:latin9 "0123456789:;<=>?"
+s:latin9 "@ABCDEFGHIJKLMNO"
+s:latin9 "PQRSTUVWXYZ[\]^_"
+s:latin9 "`abcdefghijklmno"
+s:latin9 "pqrstuvwxyz{|}~¡"
+s:latin9 "¢£€¥Š§š©ª«¬®¯°±²"
+s:latin9 "³Žµ¶·ž¹º»ŒœŸ¿ÀÁÂ"
+s:latin9 "ÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒ"
+s:latin9 "ÓÔÕÖ×ØÙÚÛÜÝÞßàáâ"
+s:latin9 "ãäåæçèéêëìíîïðñò"
+s:latin9 "óôõö÷øùúûüýþÿ"
+---
+20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f
+30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f
+40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f
+50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
+60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f
+70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e a1
+a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ae af b0 b1 b2
+b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2
+c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2
+d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2
+e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2
+f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
--- /dev/null
+"begin
+end"
+---
+62 65 67 69 6e 0a 65 6e 64
--- /dev/null
+u16be"sébastien diaz 🌻"
+---
+00 73 00 e9 00 62 00 61 00 73 00 74 00 69 00 65
+00 6e 00 20 00 64 00 69 00 61 00 7a 00 20 d8 3c
+df 3b
--- /dev/null
+u16le "sébastien diaz 🌻"
+---
+73 00 e9 00 62 00 61 00 73 00 74 00 69 00 65 00
+6e 00 20 00 64 00 69 00 61 00 7a 00 20 00 3c d8
+3b df
--- /dev/null
+u32be "sébastien diaz 🌻"
+---
+00 00 00 73 00 00 00 e9 00 00 00 62 00 00 00 61
+00 00 00 73 00 00 00 74 00 00 00 69 00 00 00 65
+00 00 00 6e 00 00 00 20 00 00 00 64 00 00 00 69
+00 00 00 61 00 00 00 7a 00 00 00 20 00 01 f3 3b
--- /dev/null
+u32le "sébastien diaz 🌻"
+---
+73 00 00 00 e9 00 00 00 62 00 00 00 61 00 00 00
+73 00 00 00 74 00 00 00 69 00 00 00 65 00 00 00
+6e 00 00 00 20 00 00 00 64 00 00 00 69 00 00 00
+61 00 00 00 7a 00 00 00 20 00 00 00 3b f3 01 00
--- /dev/null
+"sébastien" u8" diaz 🌻"
+---
+73 c3 a9 62 61 73 74 69 65 6e 20 64 69 61 7a 20 f0 9f 8c bb
--- /dev/null
+!macro salut(zoom)
+ "meow " {zoom : s:latin1}
+!end
+
+{be}
+aa bb
+m:salut("mix\n")
+m:salut("meow")
+ff
+---
+aa bb
+
+6d 65 6f 77 20 6d 69 78 0a
+6d 65 6f 77 20 6d 65 6f 77
+
+ff
"hello world!" 00
u16le"stress\nverdict 🤣"
+s:latin3{hex(ICITTE)}
---
68 65 6c 6c 6f 20 77 6f 72 6c 64 21 00 73 00 74
00 72 00 65 00 73 00 73 00 0a 00 76 00 65 00 72
-00 64 00 69 00 63 00 74 00 20 00 3e d8 23 dd
+00 64 00 69 00 63 00 74 00 20 00 3e d8 23 dd 30
+78 32 66
--- /dev/null
+"coucou tout le monde!"
+---
+63 6f 75 63 6f 75 20 74 6f 75 74 20 6c 65 20 6d
+6f 6e 64 65 21
--- /dev/null
+u16le"I am not young enough to know everything."
+---
+49 00 20 00 61 00 6d 00 20 00 6e 00 6f 00 74 00
+20 00 79 00 6f 00 75 00 6e 00 67 00 20 00 65 00
+6e 00 6f 00 75 00 67 00 68 00 20 00 74 00 6f 00
+20 00 6b 00 6e 00 6f 00 77 00 20 00 65 00 76 00
+65 00 72 00 79 00 74 00 68 00 69 00 6e 00 67 00
+2e 00
--- /dev/null
+s:u32be "\"illusion is the first\nof all pleasures\" 🦉"
+---
+00 00 00 22 00 00 00 69 00 00 00 6c 00 00 00 6c
+00 00 00 75 00 00 00 73 00 00 00 69 00 00 00 6f
+00 00 00 6e 00 00 00 20 00 00 00 69 00 00 00 73
+00 00 00 20 00 00 00 74 00 00 00 68 00 00 00 65
+00 00 00 20 00 00 00 66 00 00 00 69 00 00 00 72
+00 00 00 73 00 00 00 74 00 00 00 0a 00 00 00 6f
+00 00 00 66 00 00 00 20 00 00 00 61 00 00 00 6c
+00 00 00 6c 00 00 00 20 00 00 00 70 00 00 00 6c
+00 00 00 65 00 00 00 61 00 00 00 73 00 00 00 75
+00 00 00 72 00 00 00 65 00 00 00 73 00 00 00 22
+00 00 00 20 00 01 f9 89
--- /dev/null
+s:latin1 "Paul Piché"
+---
+50 61 75 6c 20 50 69 63 68 e9
-"coucou tout le monde!"
+{iter = 1}
+
+!repeat 10
+ {iter : s:u8} " "
+ {iter = iter + 1}
+!end
---
-63 6f 75 63 6f 75 20 74 6f 75 74 20 6c 65 20 6d
-6f 6e 64 65 21
+31 20 32 20 33 20 34 20 35 20 36 20 37 20 38 20
+39 20 31 30 20
-u16le"I am not young enough to know everything."
+{meow = 'salut jérémie'}
+{meow.upper() : s:latin1}
---
-49 00 20 00 61 00 6d 00 20 00 6e 00 6f 00 74 00
-20 00 79 00 6f 00 75 00 6e 00 67 00 20 00 65 00
-6e 00 6f 00 75 00 67 00 68 00 20 00 74 00 6f 00
-20 00 6b 00 6e 00 6f 00 77 00 20 00 65 00 76 00
-65 00 72 00 79 00 74 00 68 00 69 00 6e 00 67 00
-2e 00
+53 41 4c 55 54 20 4a c9 52 c9 4d 49 45
+++ /dev/null
-u32be "\"illusion is the first\nof all pleasures\" 🦉"
----
-00 00 00 22 00 00 00 69 00 00 00 6c 00 00 00 6c
-00 00 00 75 00 00 00 73 00 00 00 69 00 00 00 6f
-00 00 00 6e 00 00 00 20 00 00 00 69 00 00 00 73
-00 00 00 20 00 00 00 74 00 00 00 68 00 00 00 65
-00 00 00 20 00 00 00 66 00 00 00 69 00 00 00 72
-00 00 00 73 00 00 00 74 00 00 00 0a 00 00 00 6f
-00 00 00 66 00 00 00 20 00 00 00 61 00 00 00 6c
-00 00 00 6c 00 00 00 20 00 00 00 70 00 00 00 6c
-00 00 00 65 00 00 00 61 00 00 00 73 00 00 00 75
-00 00 00 72 00 00 00 65 00 00 00 73 00 00 00 22
-00 00 00 20 00 01 f9 89
--- /dev/null
+!repeat 3 u16be"yo" !end
+---
+00 79 00 6f
+00 79 00 6f
+00 79 00 6f
--- /dev/null
+!repeat 3 u16le"yo" !end
+---
+79 00 6f 00
+79 00 6f 00
+79 00 6f 00
--- /dev/null
+!repeat 3 u32be"yo" !end
+---
+00 00 00 79 00 00 00 6f
+00 00 00 79 00 00 00 6f
+00 00 00 79 00 00 00 6f
--- /dev/null
+!repeat 3 u32le"yo" !end
+---
+79 00 00 00 6f 00 00 00
+79 00 00 00 6f 00 00 00
+79 00 00 00 6f 00 00 00
--- /dev/null
+!repeat 3 "yo" !end
+---
+79 6f
+79 6f
+79 6f
+++ /dev/null
-!repeat 3 u16be"yo" !end
----
-00 79 00 6f
-00 79 00 6f
-00 79 00 6f
+++ /dev/null
-!repeat 3 u16le"yo" !end
----
-79 00 6f 00
-79 00 6f 00
-79 00 6f 00
+++ /dev/null
-!repeat 3 u32be"yo" !end
----
-00 00 00 79 00 00 00 6f
-00 00 00 79 00 00 00 6f
-00 00 00 79 00 00 00 6f
+++ /dev/null
-!repeat 3 u32le"yo" !end
----
-79 00 00 00 6f 00 00 00
-79 00 00 00 6f 00 00 00
-79 00 00 00 6f 00 00 00
+++ /dev/null
-!repeat 3 "yo" !end
----
-79 6f
-79 6f
-79 6f
--- /dev/null
+u16be"yo"*3
+---
+00 79 00 6f
+00 79 00 6f
+00 79 00 6f
--- /dev/null
+u16le"yo"*3
+---
+79 00 6f 00
+79 00 6f 00
+79 00 6f 00
--- /dev/null
+u32be"yo"*3
+---
+00 00 00 79 00 00 00 6f
+00 00 00 79 00 00 00 6f
+00 00 00 79 00 00 00 6f
--- /dev/null
+u32le"yo"*3
+---
+79 00 00 00 6f 00 00 00
+79 00 00 00 6f 00 00 00
+79 00 00 00 6f 00 00 00
--- /dev/null
+"yo"*3
+---
+79 6f
+79 6f
+79 6f
+++ /dev/null
-u16be"yo"*3
----
-00 79 00 6f
-00 79 00 6f
-00 79 00 6f
+++ /dev/null
-u16le"yo"*3
----
-79 00 6f 00
-79 00 6f 00
-79 00 6f 00
+++ /dev/null
-u32be"yo"*3
----
-00 00 00 79 00 00 00 6f
-00 00 00 79 00 00 00 6f
-00 00 00 79 00 00 00 6f
+++ /dev/null
-u32le"yo"*3
----
-79 00 00 00 6f 00 00 00
-79 00 00 00 6f 00 00 00
-79 00 00 00 6f 00 00 00
+++ /dev/null
-"yo"*3
----
-79 6f
-79 6f
-79 6f
+++ /dev/null
-u16be"\0 \a \b \e \f \n \r \t \v \" \\"
----
-00 00 00 20 00 07 00 20 00 08 00 20 00 1b 00 20
-00 0c 00 20 00 0a 00 20 00 0d 00 20 00 09 00 20
-00 0b 00 20 00 22 00 20 00 5c
+++ /dev/null
-u16le"\0 \a \b \e \f \n \r \t \v \" \\"
----
-00 00 20 00 07 00 20 00 08 00 20 00 1b 00 20 00
-0c 00 20 00 0a 00 20 00 0d 00 20 00 09 00 20 00
-0b 00 20 00 22 00 20 00 5c 00
+++ /dev/null
-u32be"\0 \a \b \e \f \n \r \t \v \" \\"
----
-00 00 00 00 00 00 00 20 00 00 00 07 00 00 00 20
-00 00 00 08 00 00 00 20 00 00 00 1b 00 00 00 20
-00 00 00 0c 00 00 00 20 00 00 00 0a 00 00 00 20
-00 00 00 0d 00 00 00 20 00 00 00 09 00 00 00 20
-00 00 00 0b 00 00 00 20 00 00 00 22 00 00 00 20
-00 00 00 5c
+++ /dev/null
-u32le"\0 \a \b \e \f \n \r \t \v \" \\"
----
-00 00 00 00 20 00 00 00 07 00 00 00 20 00 00 00
-08 00 00 00 20 00 00 00 1b 00 00 00 20 00 00 00
-0c 00 00 00 20 00 00 00 0a 00 00 00 20 00 00 00
-0d 00 00 00 20 00 00 00 09 00 00 00 20 00 00 00
-0b 00 00 00 20 00 00 00 22 00 00 00 20 00 00 00
-5c 00 00 00
+++ /dev/null
-"\0 \a \b \e \f \n \r \t \v \" \\"
----
-00 20 07 20 08 20 1b 20 0c 20 0a 20 0d 20 09 20 0b 20 22 20 5c
+++ /dev/null
-"begin
-end"
----
-62 65 67 69 6e 0a 65 6e 64
--- /dev/null
+{False : s:latin1}
+{True : s:latin1}
+---
+30
+31
--- /dev/null
+{meow = 'LÉGIFÉRER'}
+
+{meow.lower() : s:latin1}
+{ICITTE == len(meow) : 8}
+---
+6c e9 67 69 66 e9 72 65 72
+01
--- /dev/null
+{"ãäåæçèéêëìíîïðñò" : s:latin1}
+{"ăäĺćçčéęëěíîďđńň" : s:latin2}
+{"µĥ·¸ışğĵ½żÀÁÂÄĊĈ" : s:latin3}
+{"ãäåæįčéęëėíîīđņō" : s:latin4}
+{"ãäåæçèéêëìíîïğñò" : s:latin5}
+{"ÃÄÅÆĮČÉĘËĖÍÎÏÐŅŌ" : s:latin6}
+{"ÓŌÕÖ×ŲŁŚŪÜŻŽßąįā" : s:latin7}
+{"ãäåæçèéêëìíîïŵñò" : s:latin8}
+{"ÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒ" : s:latin9}
+{"łŽ”¶·žčș»ŒœŸżÀÁÂ" : s:latin10}
+---
+e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2
+e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2
+b5 b6 b7 b8 b9 ba bb bc bd bf c0 c1 c2 c4 c5 c6
+e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2
+e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2
+c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2
+d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2
+e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2
+c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2
+b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2
--- /dev/null
+{zoom = 'hello 🦊'}
+
+{zoom : s:u8}
+{zoom : s:u16be}
+{zoom : s:u16le}
+{zoom : s:u32be}
+{zoom : s:u32le}
+---
+68 65 6c 6c 6f 20 f0 9f a6 8a 00 68 00 65 00 6c
+00 6c 00 6f 00 20 d8 3e dd 8a 68 00 65 00 6c 00
+6c 00 6f 00 20 00 3e d8 8a dd 00 00 00 68 00 00
+00 65 00 00 00 6c 00 00 00 6c 00 00 00 6f 00 00
+00 20 00 01 f9 8a 68 00 00 00 65 00 00 00 6c 00
+00 00 6c 00 00 00 6f 00 00 00 20 00 00 00 8a f9
+01 00
--- /dev/null
+s:latin1{False}
+s:latin1{True}
+---
+30
+31
--- /dev/null
+{meow = 'légiférer'}
+
+u8{meow.upper()}
+---
+4c c3 89 47 49 46 c3 89 52 45 52
--- /dev/null
+{-23.42 : s:latin1}
+---
+2d 32 33 2e 34 32
--- /dev/null
+{-42 : s:latin1}
+---
+2d 34 32
--- /dev/null
+s:latin1 {"ãäåæçèéêëìíîïðñò"}
+s:latin2 {"ăäĺćçčéęëěíîďđńň"}
+s:latin3 {"µĥ·¸ışğĵ½żÀÁÂÄĊĈ"}
+s:latin4 {"ãäåæįčéęëėíîīđņō"}
+s:latin5 {"ãäåæçèéêëìíîïğñò"}
+s:latin6 {"ÃÄÅÆĮČÉĘËĖÍÎÏÐŅŌ"}
+s:latin7 {"ÓŌÕÖ×ŲŁŚŪÜŻŽßąįā"}
+s:latin8 {"ãäåæçèéêëìíîïŵñò"}
+s:latin9 {"ÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒ"}
+s:latin10 {"łŽ”¶·žčș»ŒœŸżÀÁÂ"}
+---
+e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2
+e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2
+b5 b6 b7 b8 b9 ba bb bc bd bf c0 c1 c2 c4 c5 c6
+e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2
+e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2
+c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2
+d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2
+e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2
+c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2
+b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2
--- /dev/null
+{zoom = 'hello 🦊'}
+
+u8{zoom}
+u16be{zoom}
+u16le{zoom}
+u32be{zoom}
+u32le{zoom}
+---
+68 65 6c 6c 6f 20 f0 9f a6 8a 00 68 00 65 00 6c
+00 6c 00 6f 00 20 d8 3e dd 8a 68 00 65 00 6c 00
+6c 00 6f 00 20 00 3e d8 8a dd 00 00 00 68 00 00
+00 65 00 00 00 6c 00 00 00 6c 00 00 00 6f 00 00
+00 20 00 01 f9 8a 68 00 00 00 65 00 00 00 6c 00
+00 00 6c 00 00 00 6f 00 00 00 20 00 00 00 8a f9
+01 00
+++ /dev/null
-u16be"sébastien diaz 🌻"
----
-00 73 00 e9 00 62 00 61 00 73 00 74 00 69 00 65
-00 6e 00 20 00 64 00 69 00 61 00 7a 00 20 d8 3c
-df 3b
+++ /dev/null
-u16le "sébastien diaz 🌻"
----
-73 00 e9 00 62 00 61 00 73 00 74 00 69 00 65 00
-6e 00 20 00 64 00 69 00 61 00 7a 00 20 00 3c d8
-3b df
+++ /dev/null
-u32be "sébastien diaz 🌻"
----
-00 00 00 73 00 00 00 e9 00 00 00 62 00 00 00 61
-00 00 00 73 00 00 00 74 00 00 00 69 00 00 00 65
-00 00 00 6e 00 00 00 20 00 00 00 64 00 00 00 69
-00 00 00 61 00 00 00 7a 00 00 00 20 00 01 f3 3b
+++ /dev/null
-u32le "sébastien diaz 🌻"
----
-73 00 00 00 e9 00 00 00 62 00 00 00 61 00 00 00
-73 00 00 00 74 00 00 00 69 00 00 00 65 00 00 00
-6e 00 00 00 20 00 00 00 64 00 00 00 69 00 00 00
-61 00 00 00 7a 00 00 00 20 00 00 00 3b f3 01 00
+++ /dev/null
-"sébastien diaz 🌻"
----
-73 c3 a9 62 61 73 74 69 65 6e 20 64 69 61 7a 20 f0 9f 8c bb