This package offers both a portable {py3} module and a command-line
tool.
-WARNING: This version of Normand is 0.7, meaning both the Normand
+WARNING: This version of Normand is 0.8, meaning both the Normand
language and the module/CLI interface aren't stable.
ifdef::env-github[]
+
----
aa bb * 5 cc <zoom> "yeah\0" * {zoom * 3}
+
+!repeat 3
+ ff ee "juice"
+!end
----
+
Output:
61 68 00 79 65 61 68 00 79 65 61 68 00 79 65 61 ┆ ah•yeah•yeah•yea
68 00 79 65 61 68 00 79 65 61 68 00 79 65 61 68 ┆ h•yeah•yeah•yeah
00 79 65 61 68 00 79 65 61 68 00 79 65 61 68 00 ┆ •yeah•yeah•yeah•
+ff ee 6a 75 69 63 65 ff ee 6a 75 69 63 65 ff ee ┆ ••juice••juice••
+6a 75 69 63 65 ┆ juice
----
Alignment::
* A <<group,group>>, that is, a scoped sequence of items.
-Moreover, you can <<repetition,repeat>> any item above, except an offset
-or a label, a given fixed or variable number of times. This is called a
-repetition.
+* A <<repetition-block,repetition block>>.
+
+Moreover, you can repeat many items above a constant or variable number
+of times with the ``pass:[*]`` operator _after_ the item to repeat. This
+is called a <<post-item-repetition,post-item repetition>>.
A Normand comment may exist:
* Between items, possibly within a group.
* Between the nibbles of a constant hexadecimal byte.
* Between the bits of a constant binary byte.
-* Between the last item and the ``pass:[*]`` character of a repetition,
- and between that ``pass:[*]`` character and the following number
- or expression.
+* Between the last item and the ``pass:[*]`` character of a post-item
+ repetition, and between that ``pass:[*]`` character and the following
+ number or expression.
+* Between the ``!repeat``/``!r`` prefix and the following constant
+ integer, name, or expression of a repetition block.
A comment is anything between two ``pass:[#]`` characters on the same
line, or from ``pass:[#]`` until the end of the line. Whitespaces and
comment may exist:
----
-! / \ ? & : ; . , + [ ] _ = | -
+/ \ ? & : ; . , + [ ] _ = | -
----
The latter serve to improve readability so that you may write, for
The <<label,labels>> within a group aren't visible outside of it.
-The main purpose of a group is to <<repetition,repeat>> more than a
-single item.
+The main purpose of a group is to <<post-item-repetition,repeat>> more
+than a single item and to isolate labels.
A group is:
----
====
-=== Repetition
-
-A _repetition_ represents the bytes of an item repeated a given number
-of times.
+=== Repetition block
-A repetition is:
-
-. Any item except:
+A _repetition block_ represents the bytes of one or more items repeated
+a given number of times.
-** A <<current-byte-order-setting,current byte order setting>>.
-** A <<current-offset-setting,current offset setting>>.
-** A <<label,label>>.
-** A <<offset-alignment,offset alignment>>.
-** A <<variable-assignment,variable assignment>>.
+A repetition block is:
-. The ``pass:[*]`` character.
+. The `!repeat` or `!r` prefix.
. One of:
may contain:
+
--
-* The name of any <<label,label>> defined before{nbsp}__**L**__ and
- which isn't part of its repeated item.
+* The name of any <<label,label>> defined before{nbsp}__**L**__.
* The name of any <<variable-assignment,variable>> known
- at{nbsp}__**L**__, which isn't part of its repeated item, and which
- doesn't, directly or indirectly, refer to a label defined
- after{nbsp}__**L**__.
+ at{nbsp}__**L**__ which doesn't, directly or indirectly, refer to a
+ label defined after{nbsp}__**L**__.
--
+
-This expression must not contain the special name `ICITTE`.
+The value of the special name `ICITTE` (`int` type) in this expression
+is the <<cur-offset,current offset>> (before handling the items to
+repeat).
+
+** A valid {py3} name.
++
+For the name `__NAME__`, this is equivalent to the
+`pass:[{]__NAME__pass:[}]` form above.
+
+. Zero or more items.
+
+. The `!end` suffix.
+
+You may also use a <<post-item-repetition,post-item repetition>> after
+some items. The form ``!repeat{nbsp}__X__{nbsp}__ITEMS__{nbsp}!end``
+is equivalent to ``(__ITEMS__){nbsp}pass:[*]{nbsp}__X__``.
====
Input:
----
-{end - ICITTE - 1 : 8} * 0x100 <end>
+!repeat 0x100
+ {end - ICITTE - 1 : 8}
+!end
+
+<end>
----
Output:
----
{times = 1}
+
aa bb cc dd
-(
+
+!repeat 3
<here>
- (ee ff) * {here + 1}
- 11 22 33 * {times}
+
+ !repeat {here + 1}
+ ee ff
+ !end
+
+ 11 22 !repeat times 33 !end
+
{times = times + 1}
-) * 3
+!end
+
"coucou!"
----
====
====
-This example shows how to use a repetition as a conditional section
-depending on some predefined variable.
+This example shows how to use a repetition block as a conditional
+section depending on some predefined variable.
Input:
----
aa bb cc dd
-(ee ff "meow mix" 00) * {cond}
+
+!repeat cond
+ ee ff "meow mix" 00
+!end
+
{be} {-1993:16}
----
----
====
+=== Post-item repetition
+
+A _post-item repetition_ represents the bytes of an item repeated a
+given number of times.
+
+A post-item repetition is:
+
+. Any item except:
+
+** A <<current-byte-order-setting,current byte order setting>>.
+** A <<current-offset-setting,current offset setting>>.
+** A <<label,label>>.
+** A <<offset-alignment,offset alignment>>.
+** A <<variable-assignment,variable assignment>>.
+** A <<repetition-block,repetition block>>.
+
+. The ``pass:[*]`` character.
+
+. One of:
+
+** A positive integer (hexadecimal starting with `0x` or `0X` accepted)
+ which is the number of times to repeat the previous item.
+
+** The ``pass:[{]`` prefix, a valid {py3} expression, and the
+ ``pass:[}]`` suffix.
++
+For a repetition at some source location{nbsp}__**L**__, this expression
+may contain:
++
+--
+* The name of any <<label,label>> defined before{nbsp}__**L**__ and
+ which isn't part of its repeated item.
+* The name of any <<variable-assignment,variable>> known
+ at{nbsp}__**L**__, which isn't part of its repeated item, and which
+ doesn't, directly or indirectly, refer to a label defined
+ after{nbsp}__**L**__.
+--
++
+The value of the special name `ICITTE` (`int` type) in this expression
+is the <<cur-offset,current offset>> (before handling the items to
+repeat).
+
+** A valid {py3} name.
++
+For the name `__NAME__`, this is equivalent to the
+`pass:[{]__NAME__pass:[}]` form above.
+
+You may also use a <<repetition-block,repetition block>>. The form
+``__ITEM__{nbsp}pass:[*]{nbsp}__X__`` is equivalent to
+``!repeat{nbsp}__X__{nbsp}__ITEM__{nbsp}!end``.
+
+====
+Input:
+
+----
+{end - ICITTE - 1 : 8} * 0x100 <end>
+----
+
+Output:
+
+----
+ff fe fd fc fb fa f9 f8 f7 f6 f5 f4 f3 f2 f1 f0 ┆ ••••••••••••••••
+ef ee ed ec eb ea e9 e8 e7 e6 e5 e4 e3 e2 e1 e0 ┆ ••••••••••••••••
+df de dd dc db da d9 d8 d7 d6 d5 d4 d3 d2 d1 d0 ┆ ••••••••••••••••
+cf ce cd cc cb ca c9 c8 c7 c6 c5 c4 c3 c2 c1 c0 ┆ ••••••••••••••••
+bf be bd bc bb ba b9 b8 b7 b6 b5 b4 b3 b2 b1 b0 ┆ ••••••••••••••••
+af ae ad ac ab aa a9 a8 a7 a6 a5 a4 a3 a2 a1 a0 ┆ ••••••••••••••••
+9f 9e 9d 9c 9b 9a 99 98 97 96 95 94 93 92 91 90 ┆ ••••••••••••••••
+8f 8e 8d 8c 8b 8a 89 88 87 86 85 84 83 82 81 80 ┆ ••••••••••••••••
+7f 7e 7d 7c 7b 7a 79 78 77 76 75 74 73 72 71 70 ┆ •~}|{zyxwvutsrqp
+6f 6e 6d 6c 6b 6a 69 68 67 66 65 64 63 62 61 60 ┆ onmlkjihgfedcba`
+5f 5e 5d 5c 5b 5a 59 58 57 56 55 54 53 52 51 50 ┆ _^]\[ZYXWVUTSRQP
+4f 4e 4d 4c 4b 4a 49 48 47 46 45 44 43 42 41 40 ┆ ONMLKJIHGFEDCBA@
+3f 3e 3d 3c 3b 3a 39 38 37 36 35 34 33 32 31 30 ┆ ?>=<;:9876543210
+2f 2e 2d 2c 2b 2a 29 28 27 26 25 24 23 22 21 20 ┆ /.-,+*)('&%$#"!
+1f 1e 1d 1c 1b 1a 19 18 17 16 15 14 13 12 11 10 ┆ ••••••••••••••••
+0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00 ┆ ••••••••••••••••
+----
+====
+
+====
+Input:
+
+----
+{times = 1}
+aa bb cc dd
+(
+ <here>
+ (ee ff) * {here + 1}
+ 11 22 33 * {times}
+ {times = times + 1}
+) * 3
+"coucou!"
+----
+
+Output:
+
+----
+aa bb cc dd ee ff ee ff ee ff ee ff ee ff 11 22 ┆ •••••••••••••••"
+33 ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee ┆ 3•••••••••••••••
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee ┆ ••••••••••••••••
+ff ee ff ee ff 11 22 33 33 ee ff ee ff ee ff ee ┆ ••••••"33•••••••
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee ┆ ••••••••••••••••
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee ┆ ••••••••••••••••
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee ┆ ••••••••••••••••
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee ┆ ••••••••••••••••
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee ┆ ••••••••••••••••
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee ┆ ••••••••••••••••
+ff ee ff ee ff ee ff ee ff ee ff ee ff 11 22 33 ┆ ••••••••••••••"3
+33 33 63 6f 75 63 6f 75 21 ┆ 33coucou!
+----
+====
+
== Command-line tool
If you <<install-normand,installed>> the `normand` package, then you
== {py3} API
-The whole `normand` package/module API is:
+The whole `normand` package/module public API is:
[source,python]
----
+# Byte order.
class ByteOrder(enum.Enum):
# Big endian.
BE = ...
LE = ...
-class TextLoc:
+# Text location.
+class TextLocation:
# Line number.
@property
def line_no(self) -> int:
...
+# Parsing error.
class ParseError(RuntimeError):
# Source text location.
@property
- def text_loc(self) -> TextLoc:
+ def text_loc(self) -> TextLocation:
...
-SymbolsT = typing.Dict[str, int]
+# Variables dictionary type (for type hints).
+VariablesT = typing.Dict[str, typing.Union[int, float]]
+
+
+# Labels dictionary type (for type hints).
+LabelsT = typing.Dict[str, int]
+# Parsing result.
class ParseResult:
# Generated data.
@property
...
+# Parses the `normand` input using the initial state defined by
+# `init_variables`, `init_labels`, `init_offset`, and `init_byte_order`,
+# and returns the corresponding parsing result.
def parse(normand: str,
init_variables: typing.Optional[SymbolsT] = None,
init_labels: typing.Optional[SymbolsT] = None,
# Upstream repository: <https://github.com/efficios/normand>.
__author__ = "Philippe Proulx"
-__version__ = "0.7.0"
+__version__ = "0.8.0"
__all__ = [
"ByteOrder",
"parse",
"ParseError",
"ParseResult",
- "TextLoc",
- "SymbolsT",
+ "TextLocation",
+ "LabelsT",
+ "VariablesT",
"__author__",
"__version__",
]
import enum
import math
import struct
-from typing import (
- Any,
- Set,
- Dict,
- List,
- Union,
- Pattern,
- Callable,
- NoReturn,
- Optional,
-)
+import typing
+from typing import Any, Set, Dict, List, Union, Pattern, Callable, NoReturn, Optional
# Text location (line and column numbers).
-class TextLoc:
+class TextLocation:
@classmethod
def _create(cls, line_no: int, col_no: int):
self = cls.__new__(cls)
return self._col_no
def __repr__(self):
- return "TextLoc({}, {})".format(self._line_no, self._col_no)
+ return "TextLocation({}, {})".format(self._line_no, self._col_no)
# Any item.
class _Item:
- def __init__(self, text_loc: TextLoc):
+ def __init__(self, text_loc: TextLocation):
self._text_loc = text_loc
# Source text location.
# Single byte.
class _Byte(_ScalarItem, _RepableItem):
- def __init__(self, val: int, text_loc: TextLoc):
+ def __init__(self, val: int, text_loc: TextLocation):
super().__init__(text_loc)
self._val = val
# String.
class _Str(_ScalarItem, _RepableItem):
- def __init__(self, data: bytes, text_loc: TextLoc):
+ def __init__(self, data: bytes, text_loc: TextLocation):
super().__init__(text_loc)
self._data = data
# Byte order setting.
class _SetBo(_Item):
- def __init__(self, bo: ByteOrder, text_loc: TextLoc):
+ def __init__(self, bo: ByteOrder, text_loc: TextLocation):
super().__init__(text_loc)
self._bo = bo
# Label.
class _Label(_Item):
- def __init__(self, name: str, text_loc: TextLoc):
+ def __init__(self, name: str, text_loc: TextLocation):
super().__init__(text_loc)
self._name = name
# Offset setting.
class _SetOffset(_Item):
- def __init__(self, val: int, text_loc: TextLoc):
+ def __init__(self, val: int, text_loc: TextLocation):
super().__init__(text_loc)
self._val = val
# Offset alignment.
class _AlignOffset(_Item):
- def __init__(self, val: int, pad_val: int, text_loc: TextLoc):
+ def __init__(self, val: int, pad_val: int, text_loc: TextLocation):
super().__init__(text_loc)
self._val = val
self._pad_val = pad_val
# Variable assignment.
class _VarAssign(_Item, _ExprMixin):
def __init__(
- self, name: str, expr_str: str, expr: ast.Expression, text_loc: TextLoc
+ self, name: str, expr_str: str, expr: ast.Expression, text_loc: TextLocation
):
super().__init__(text_loc)
_ExprMixin.__init__(self, expr_str, expr)
# Fixed-length number, possibly needing more than one byte.
class _FlNum(_ScalarItem, _RepableItem, _ExprMixin):
def __init__(
- self, expr_str: str, expr: ast.Expression, len: int, text_loc: TextLoc
+ self, expr_str: str, expr: ast.Expression, len: int, text_loc: TextLocation
):
super().__init__(text_loc)
_ExprMixin.__init__(self, expr_str, expr)
# LEB128 integer.
class _Leb128Int(_Item, _RepableItem, _ExprMixin):
- def __init__(self, expr_str: str, expr: ast.Expression, text_loc: TextLoc):
+ def __init__(self, expr_str: str, expr: ast.Expression, text_loc: TextLocation):
super().__init__(text_loc)
_ExprMixin.__init__(self, expr_str, expr)
# Group of items.
class _Group(_Item, _RepableItem):
- def __init__(self, items: List[_Item], text_loc: TextLoc):
+ def __init__(self, items: List[_Item], text_loc: TextLocation):
super().__init__(text_loc)
self._items = items
# Repetition item.
class _Rep(_Item, _ExprMixin):
def __init__(
- self, item: _Item, expr_str: str, expr: ast.Expression, text_loc: TextLoc
+ self, item: _Item, expr_str: str, expr: ast.Expression, text_loc: TextLocation
):
super().__init__(text_loc)
_ExprMixin.__init__(self, expr_str, expr)
# A parsing error containing a message and a text location.
class ParseError(RuntimeError):
@classmethod
- def _create(cls, msg: str, text_loc: TextLoc):
+ def _create(cls, msg: str, text_loc: TextLocation):
self = cls.__new__(cls)
self._init(msg, text_loc)
return self
def __init__(self, *args, **kwargs): # type: ignore
raise NotImplementedError
- def _init(self, msg: str, text_loc: TextLoc):
+ def _init(self, msg: str, text_loc: TextLocation):
super().__init__(msg)
self._text_loc = text_loc
# Raises a parsing error, forwarding the parameters to the constructor.
-def _raise_error(msg: str, text_loc: TextLoc) -> NoReturn:
+def _raise_error(msg: str, text_loc: TextLocation) -> NoReturn:
raise ParseError._create(msg, text_loc) # pyright: ignore[reportPrivateUsage]
-# Variable/label dictionary type.
-SymbolsT = Dict[str, Union[int, float]]
+# Variables dictionary type (for type hints).
+VariablesT = Dict[str, Union[int, float]]
+
+
+# Labels dictionary type (for type hints).
+LabelsT = Dict[str, int]
# Python name pattern.
class _Parser:
# Builds a parser to parse the Normand input `normand`, parsing
# immediately.
- def __init__(self, normand: str, variables: SymbolsT, labels: SymbolsT):
+ def __init__(self, normand: str, variables: VariablesT, labels: LabelsT):
self._normand = normand
self._at = 0
self._line_no = 1
# Current text location.
@property
def _text_loc(self):
- return TextLoc._create( # pyright: ignore[reportPrivateUsage]
+ return TextLocation._create( # pyright: ignore[reportPrivateUsage]
self._line_no, self._col_no
)
# Pattern for _skip_ws_and_comments()
_ws_or_syms_or_comments_pat = re.compile(
- r"(?:[\s!/\\?&:;.,+[\]_=|-]|#[^#]*?(?:\n|#))*"
+ r"(?:[\s/\\?&:;.,+[\]_=|-]|#[^#]*?(?:\n|#))*"
)
# Skips as many whitespaces, insignificant symbol characters, and
# Returns a stripped expression string and an AST expression node
# from the expression string `expr_str` at text location `text_loc`.
- def _ast_expr_from_str(self, expr_str: str, text_loc: TextLoc):
+ def _ast_expr_from_str(self, expr_str: str, text_loc: TextLocation):
# Create an expression node from the expression string
expr_str = expr_str.strip().replace("\n", " ")
self._expect_pat(self._val_var_assign_set_bo_suffix_pat, "Expecting `}`")
return item
- # Pattern for _try_parse_set_offset_val() and _try_parse_rep()
+ # Common positive constant integer pattern
_pos_const_int_pat = re.compile(r"0[Xx][A-Fa-f0-9]+|\d+")
# Tries to parse an offset setting value (after the initial `<`),
# Return item
return _AlignOffset(val, pad_val, begin_text_loc)
+ # Patterns for _expect_rep_mul_expr()
+ _rep_expr_prefix_pat = re.compile(r"\{")
+ _rep_expr_pat = re.compile(r"[^}p]+")
+ _rep_expr_suffix_pat = re.compile(r"\}")
+
+ # Parses the multiplier expression of a repetition (block or
+ # post-item) and returns the expression string and AST node.
+ def _expect_rep_mul_expr(self):
+ expr_text_loc = self._text_loc
+
+ # Constant integer?
+ m = self._try_parse_pat(self._pos_const_int_pat)
+
+ if m is None:
+ # Name?
+ m = self._try_parse_pat(_py_name_pat)
+
+ if m is None:
+ # Expression?
+ if self._try_parse_pat(self._rep_expr_prefix_pat) is None:
+ # At this point it's invalid
+ self._raise_error(
+ "Expecting a positive integral multiplier, a name, or `{`"
+ )
+
+ # Expect an expression
+ expr_text_loc = self._text_loc
+ m = self._expect_pat(self._rep_expr_pat, "Expecting an expression")
+ expr_str = m.group(0)
+
+ # Expect `}`
+ self._expect_pat(self._rep_expr_suffix_pat, "Expecting `}`")
+ else:
+ expr_str = m.group(0)
+ else:
+ expr_str = m.group(0)
+
+ return self._ast_expr_from_str(expr_str, expr_text_loc)
+
+ # Pattern for _try_parse_rep_block()
+ _rep_block_prefix_pat = re.compile(r"!r(?:epeat)?\b\s*")
+ _rep_block_end_pat = re.compile(r"!end\b\s*")
+
+ # Tries to parse a repetition block, returning a repetition item on
+ # success.
+ def _try_parse_rep_block(self):
+ begin_text_loc = self._text_loc
+
+ # Match prefix
+ if self._try_parse_pat(self._rep_block_prefix_pat) is None:
+ # No match
+ return
+
+ # Expect expression
+ self._skip_ws_and_comments()
+ expr_str, expr = self._expect_rep_mul_expr()
+
+ # Parse items
+ self._skip_ws_and_comments()
+ items_text_loc = self._text_loc
+ items = self._parse_items()
+
+ # Expect end of block
+ self._skip_ws_and_comments()
+ self._expect_pat(
+ self._rep_block_end_pat, "Expecting an item or `!end` (end of repetition)"
+ )
+
+ # Return item
+ return _Rep(_Group(items, items_text_loc), expr_str, expr, begin_text_loc)
+
# Tries to parse a base item (anything except a repetition),
# returning it on success.
def _try_parse_base_item(self):
if item is not None:
return item
- # Pattern for _try_parse_rep()
- _rep_prefix_pat = re.compile(r"\*\s*")
- _rep_expr_prefix_pat = re.compile(r"\{")
- _rep_expr_pat = re.compile(r"[^}p]+")
- _rep_expr_suffix_pat = re.compile(r"\}")
+ # Repetition (block) item?
+ item = self._try_parse_rep_block()
- # Tries to parse a repetition, returning the expression string and
- # AST expression node on success.
- def _try_parse_rep(self):
+ if item is not None:
+ return item
+
+ # Pattern for _try_parse_rep_post()
+ _rep_post_prefix_pat = re.compile(r"\*")
+
+ # Tries to parse a post-item repetition, returning the expression
+ # string and AST expression node on success.
+ def _try_parse_rep_post(self):
# Match prefix
- if self._try_parse_pat(self._rep_prefix_pat) is None:
+ if self._try_parse_pat(self._rep_post_prefix_pat) is None:
# No match
return
- # Expect and return a decimal multiplier
+ # Return expression string and AST expression
self._skip_ws_and_comments()
-
- # Integer?
- m = self._try_parse_pat(self._pos_const_int_pat)
-
- if m is None:
- # Expression?
- if self._try_parse_pat(self._rep_expr_prefix_pat) is None:
- # At this point it's invalid
- self._raise_error("Expecting a positive integral multiplier or `{`")
-
- # Expect an expression
- expr_str_loc = self._text_loc
- m = self._expect_pat(self._rep_expr_pat, "Expecting an expression")
- expr_str = self._ast_expr_from_str(m.group(0), expr_str_loc)
-
- # Expect `}`
- self._expect_pat(self._rep_expr_suffix_pat, "Expecting `}`")
- expr_str = m.group(0)
- else:
- expr_str_loc = self._text_loc
- expr_str = m.group(0)
-
- return self._ast_expr_from_str(expr_str, expr_str_loc)
+ return self._expect_rep_mul_expr()
# Tries to parse an item, possibly followed by a repetition,
# returning `True` on success.
if isinstance(item, _RepableItem):
self._skip_ws_and_comments()
rep_text_loc = self._text_loc
- rep_ret = self._try_parse_rep()
+ rep_ret = self._try_parse_rep_post()
if rep_ret is not None:
item = _Rep(item, rep_ret[0], rep_ret[1], rep_text_loc)
def _create(
cls,
data: bytearray,
- variables: SymbolsT,
- labels: SymbolsT,
+ variables: VariablesT,
+ labels: LabelsT,
offset: int,
bo: Optional[ByteOrder],
):
def _init(
self,
data: bytearray,
- variables: SymbolsT,
- labels: SymbolsT,
+ variables: VariablesT,
+ labels: LabelsT,
offset: int,
bo: Optional[ByteOrder],
):
# Expression validator: validates that all the names within the
# expression are allowed.
class _ExprValidator(_NodeVisitor):
- def __init__(self, item: _ExprItemT, allowed_names: Set[str], icitte_allowed: bool):
+ def __init__(self, item: _ExprItemT, allowed_names: Set[str]):
super().__init__()
self._item = item
self._allowed_names = allowed_names
- self._icitte_allowed = icitte_allowed
def _visit_name(self, name: str):
# Make sure the name refers to a known and reachable
# variable/label name.
- if name == _icitte_name and not self._icitte_allowed:
- _raise_error(
- "Illegal reserved name `{}` in expression `{}`".format(
- _icitte_name, self._item.expr_str
- ),
- self._item.text_loc,
- )
- elif name != _icitte_name and name not in self._allowed_names:
+ if name != _icitte_name and name not in self._allowed_names:
msg = "Illegal (unknown or unreachable) variable/label name `{}` in expression `{}`".format(
name, self._item.expr_str
)
allowed_names = self._allowed_names.copy()
-
- if self._icitte_allowed:
- allowed_names.add(_icitte_name)
+ allowed_names.add(_icitte_name)
if len(allowed_names) > 0:
allowed_names_str = ", ".join(
class _GenState:
def __init__(
self,
- variables: SymbolsT,
- labels: SymbolsT,
+ variables: VariablesT,
+ labels: LabelsT,
offset: int,
bo: Optional[ByteOrder],
):
# The steps of generation are:
#
# 1. Validate that each repetition and LEB128 integer expression uses
-# only reachable names and not `ICITTE`.
+# only reachable names.
#
# 2. Compute and keep the effective repetition count and LEB128 integer
# value for each repetition and LEB128 integer instance.
def __init__(
self,
group: _Group,
- variables: SymbolsT,
- labels: SymbolsT,
+ variables: VariablesT,
+ labels: LabelsT,
offset: int,
bo: Optional[ByteOrder],
):
elif item.name in allowed_variable_names:
allowed_variable_names.remove(item.name)
elif isinstance(item, _Leb128Int):
- # Validate the expression (`ICITTE` allowed)
- _ExprValidator(
- item, allowed_label_names | allowed_variable_names, True
- ).visit(item.expr)
+ # Validate the expression
+ _ExprValidator(item, allowed_label_names | allowed_variable_names).visit(
+ item.expr
+ )
elif type(item) is _Rep:
- # Validate the expression first (`ICITTE` not allowed)
- _ExprValidator(
- item, allowed_label_names | allowed_variable_names, False
- ).visit(item.expr)
+ # Validate the expression first
+ _ExprValidator(item, allowed_label_names | allowed_variable_names).visit(
+ item.expr
+ )
# Validate inner item
_Gen._validate_vl_exprs(
# Evaluates the expression of `item` considering the current
# generation state `state`.
#
- # If `allow_icitte` is `True`, then the `ICITTE` name is available
- # for the expression to evaluate.
- #
# If `allow_float` is `True`, then the type of the result may be
# `float` too.
@staticmethod
def _eval_item_expr(
item: _ExprItemT,
state: _GenState,
- allow_icitte: bool,
allow_float: bool = False,
):
- syms = state.labels.copy()
+ syms = {} # type: VariablesT
+ syms.update(state.labels)
- # Set the `ICITTE` name to the current offset, if any
- if allow_icitte:
- syms[_icitte_name] = state.offset
+ # Set the `ICITTE` name to the current offset
+ syms[_icitte_name] = state.offset
# Add the current variables
syms.update(state.variables)
# Validate the node and its children
- _ExprValidator(item, set(syms.keys()), True).visit(item.expr)
+ _ExprValidator(item, set(syms.keys())).visit(item.expr)
# Compile and evaluate expression node
try:
if do_eval:
# Evaluate the expression and keep the result
- state.variables[item.name] = _Gen._eval_item_expr(
- item, state, True, True
- )
+ state.variables[item.name] = _Gen._eval_item_expr(item, state, True)
elif type(item) is _SetOffset:
state.offset = item.val
elif type(item) is _AlignOffset:
state.offset = _Gen._align_offset(state.offset, item)
elif isinstance(item, _Leb128Int):
# Evaluate the expression
- val = _Gen._eval_item_expr(item, state, True)
+ val = _Gen._eval_item_expr(item, state)
# Validate result
if type(item) is _ULeb128Int and val < 0:
state.offset += _Gen._leb128_size_for_val(val, type(item) is _SLeb128Int)
elif type(item) is _Rep:
# Evaluate the expression and keep the result
- val = _Gen._eval_item_expr(item, state, False)
+ val = _Gen._eval_item_expr(item, state)
# Validate result
if val < 0:
self, item: _VarAssign, state: _GenState, next_vl_instance: int
):
# Update variable
- state.variables[item.name] = self._eval_item_expr(item, state, True, True)
+ state.variables[item.name] = self._eval_item_expr(item, state, True)
return next_vl_instance
# Handles the fixed-length integer item `item`.
self, item: _FlNum, state: _GenState, next_vl_instance: int
):
# Compute value
- val = self._eval_item_expr(item, state, True, True)
+ val = self._eval_item_expr(item, state, True)
# Validate current byte order
if state.bo is None and item.len > 8:
# Raises `ParseError` on any parsing error.
def parse(
normand: str,
- init_variables: Optional[SymbolsT] = None,
- init_labels: Optional[SymbolsT] = None,
+ init_variables: Optional[VariablesT] = None,
+ init_labels: Optional[LabelsT] = None,
init_offset: int = 0,
init_byte_order: Optional[ByteOrder] = None,
):
# Returns a dictionary of string to integers from the list of strings
# `args` containing `NAME=VAL` entries.
def _dict_from_arg(args: Optional[List[str]]):
- d = {} # type: SymbolsT
+ d = {} # type: LabelsT
if args is None:
return d
normand = f.read()
# Variables and labels
- variables = _dict_from_arg(args.var)
+ variables = typing.cast(VariablesT, _dict_from_arg(args.var))
labels = _dict_from_arg(args.label)
# Validate offset
[tool.poetry]
name = 'normand'
-version = '0.7.0'
+version = '0.8.0'
description = 'Text-to-binary processor with its own language'
license = 'MIT'
authors = ['Philippe Proulx <eeppeliteloop@gmail.com>']
--- /dev/null
+!repeat { } 77 !end
+---
+1:10 - Invalid expression ``: invalid syntax
--- /dev/null
+!repeat { [1, 2, 3] } 77 !end
+---
+1:1 - Invalid expression `[1, 2, 3]`: expecting result type `int`, not `list`
--- /dev/null
+!repeat {3.4} 77 !end
+---
+1:1 - Invalid expression `3.4`: expecting result type `int`, not `float`
--- /dev/null
+aa !repeat { 23 + zoom(14) } bb !end
+---
+1:4 - Failed to evaluate expression `23 + zoom(14)`: name 'zoom' is not defined
+++ /dev/null
-77 * { }
----
-1:7 - Invalid expression ``: invalid syntax
+++ /dev/null
-77 * { [1, 2, 3] }
----
-1:4 - Invalid expression `[1, 2, 3]`: expecting result type `int`, not `list`
+++ /dev/null
-77 * { 3.4 }
----
-1:4 - Invalid expression `3.4`: expecting result type `int`, not `float`
+++ /dev/null
-aa bb * { 23 + zoom(14) }
----
-1:7 - Failed to evaluate expression `23 + zoom(14)`: name 'zoom' is not defined
+++ /dev/null
-99 * {ICITTE * 2}
----
-1:4 - Illegal reserved name `ICITTE` in expression `ICITTE * 2`
+++ /dev/null
-88 * salut
----
-1:6 - Expecting a positive integral multiplier or `{`
+++ /dev/null
-aa <meow> ff * {-meow}
----
-1:14 - Invalid expression `-meow`: unexpected negative result -1
--- /dev/null
+77 * { }
+---
+1:7 - Invalid expression ``: invalid syntax
--- /dev/null
+77 * { [1, 2, 3] }
+---
+1:4 - Invalid expression `[1, 2, 3]`: expecting result type `int`, not `list`
--- /dev/null
+77 * { 3.4 }
+---
+1:4 - Invalid expression `3.4`: expecting result type `int`, not `float`
--- /dev/null
+aa bb * { 23 + zoom(14) }
+---
+1:7 - Failed to evaluate expression `23 + zoom(14)`: name 'zoom' is not defined
--- /dev/null
+88 * "salut"
+---
+1:6 - Expecting a positive integral multiplier, a name, or `{`
--- /dev/null
+aa <meow> ff * {-meow}
+---
+1:14 - Invalid expression `-meow`: unexpected negative result -1
--- /dev/null
+<meow>
+aa bb {kilo = 23} cc
+dd * {mix}
+ee <mix>
+---
+3:4 - Illegal (unknown or unreachable) variable/label name `mix` in expression `mix`; the legal names are {`ICITTE`, `kilo`, `meow`}
--- /dev/null
+<meow>
+aa {kilo = 99} bb {kilo = mix} cc
+dd * {kilo}
+ee <mix>
+---
+3:4 - Illegal (unknown or unreachable) variable/label name `kilo` in expression `kilo`; the legal names are {`ICITTE`, `meow`}
--- /dev/null
+aa {mix = 23} bb <flake> cc
+(
+ dd ee * {meow}
+ ff
+ <meow>
+)
+"salut"
+---
+3:9 - Illegal (unknown or unreachable) variable/label name `meow` in expression `meow`; the legal names are {`ICITTE`, `flake`, `mix`}
+++ /dev/null
-<meow>
-aa bb {kilo = 23} cc
-dd * {mix}
-ee <mix>
----
-3:4 - Illegal (unknown or unreachable) variable/label name `mix` in expression `mix`; the legal names are {`kilo`, `meow`}
+++ /dev/null
-<meow>
-aa {kilo = 99} bb {kilo = mix} cc
-dd * {kilo}
-ee <mix>
----
-3:4 - Illegal (unknown or unreachable) variable/label name `kilo` in expression `kilo`; the legal names are {`meow`}
+++ /dev/null
-aa {mix = 23} bb <flake> cc
-(
- dd ee * {meow}
- ff
- <meow>
-)
-"salut"
----
-3:9 - Illegal (unknown or unreachable) variable/label name `meow` in expression `meow`; the legal names are {`flake`, `mix`}
-%!1/01\0?1&0:1;0 %.0,1+0[1]0_1=0|1 %1111-0000
-% 1! 0 / 1 \ 0 ? 1 & 0 : 1 ; 0
+%1/01\0?1&0:1;0 %.0,1+0[1]0_1=0|1 %1111-0000
+% 1 0 / 1 \ 0 ? 1 & 0 : 1 ; 0
% . 0 , 1 + 0 [ 1 ] 0 _ 1 = 0 | 1
% 1111 - 0000
---
-(0!12/3\4?5&6:7;8.9,a+b[c]d_e=f|0-1(!/\?&:;.,+[]_=|-))
+(012/3\4?5&6:7;8.9,a+b[c]d_e=f|0-1(/\?&:;.,+[]_=|-))
---
01 23 45 67 89 ab cd ef 01
-! / \ ? & : ; . , + [ ] _ = | -
-0!12/3\4?5&6:7;8.9,a+b[c]d_e=f|0-1
-0 ! 1 2 / 3 \ 4 ? 5 & 6 : 7 ; 8 . 9 , a + b [ c ] d_e = f | 0 - 1
+/ \ ? & : ; . , + [ ] _ = | -
+012/3\4?5&6:7;8.9,a+b[c]d_e=f|0-1
+0 1 2 / 3 \ 4 ? 5 & 6 : 7 ; 8 . 9 , a + b [ c ] d_e = f | 0 - 1
---
01 23 45 67 89 ab cd ef 01
01 23 45 67 89 ab cd ef 01
--- /dev/null
+# repetition
+ff/\?&:;.,+[]_=|-*/\?&:;.,+[]_=|-5
+---
+ff ff ff ff ff
+++ /dev/null
-# repetition
-ff!/\?&:;.,+[]_=|-*!/\?&:;.,+[]_=|-5
----
-ff ff ff ff ff
--- /dev/null
+ff#comment#*#comment#5
+---
+ff ff ff ff ff
+++ /dev/null
-ff#comment#*#comment#5
----
-ff ff ff ff ff
aa bb * 5 cc <zoom> "yeah\0" * {zoom * 3}
+
+!repeat 3
+ ff ee "juice"
+!end
---
aa bb bb bb bb bb cc 79 65 61 68 00 79 65 61 68
00 79 65 61 68 00 79 65 61 68 00 79 65 61 68 00
61 68 00 79 65 61 68 00 79 65 61 68 00 79 65 61
68 00 79 65 61 68 00 79 65 61 68 00 79 65 61 68
00 79 65 61 68 00 79 65 61 68 00 79 65 61 68 00
+ff ee 6a 75 69 63 65 ff ee 6a 75 69 63 65 ff ee
+6a 75 69 63 65
+++ /dev/null
-{end - ICITTE - 1 : 8} * 0x100 <end>
----
-ff fe fd fc fb fa f9 f8 f7 f6 f5 f4 f3 f2 f1 f0
-ef ee ed ec eb ea e9 e8 e7 e6 e5 e4 e3 e2 e1 e0
-df de dd dc db da d9 d8 d7 d6 d5 d4 d3 d2 d1 d0
-cf ce cd cc cb ca c9 c8 c7 c6 c5 c4 c3 c2 c1 c0
-bf be bd bc bb ba b9 b8 b7 b6 b5 b4 b3 b2 b1 b0
-af ae ad ac ab aa a9 a8 a7 a6 a5 a4 a3 a2 a1 a0
-9f 9e 9d 9c 9b 9a 99 98 97 96 95 94 93 92 91 90
-8f 8e 8d 8c 8b 8a 89 88 87 86 85 84 83 82 81 80
-7f 7e 7d 7c 7b 7a 79 78 77 76 75 74 73 72 71 70
-6f 6e 6d 6c 6b 6a 69 68 67 66 65 64 63 62 61 60
-5f 5e 5d 5c 5b 5a 59 58 57 56 55 54 53 52 51 50
-4f 4e 4d 4c 4b 4a 49 48 47 46 45 44 43 42 41 40
-3f 3e 3d 3c 3b 3a 39 38 37 36 35 34 33 32 31 30
-2f 2e 2d 2c 2b 2a 29 28 27 26 25 24 23 22 21 20
-1f 1e 1d 1c 1b 1a 19 18 17 16 15 14 13 12 11 10
-0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00
+++ /dev/null
-{times = 1}
-aa bb cc dd
-(
- <here>
- (ee ff) * {here + 1}
- 11 22 33 * {times}
- {times = times + 1}
-) * 3
-"coucou!"
----
-aa bb cc dd ee ff ee ff ee ff ee ff ee ff 11 22
-33 ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
-ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
-ff ee ff ee ff 11 22 33 33 ee ff ee ff ee ff ee
-ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
-ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
-ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
-ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
-ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
-ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
-ff ee ff ee ff ee ff ee ff ee ff ee ff 11 22 33
-33 33 63 6f 75 63 6f 75 21
+++ /dev/null
-{cond = 0}
-aa bb cc dd
-(ee ff "meow mix" 00) * {cond}
-{be} {-1993:16}
----
-aa bb cc dd f8 37
+++ /dev/null
-{cond = 1}
-aa bb cc dd
-(ee ff "meow mix" 00) * {cond}
-{be} {-1993:16}
----
-aa bb cc dd ee ff 6d 65 6f 77 20 6d 69 78 00 f8 37
--- /dev/null
+!repeat 0x100
+ {end - ICITTE - 1 : 8}
+!end
+
+<end>
+---
+ff fe fd fc fb fa f9 f8 f7 f6 f5 f4 f3 f2 f1 f0
+ef ee ed ec eb ea e9 e8 e7 e6 e5 e4 e3 e2 e1 e0
+df de dd dc db da d9 d8 d7 d6 d5 d4 d3 d2 d1 d0
+cf ce cd cc cb ca c9 c8 c7 c6 c5 c4 c3 c2 c1 c0
+bf be bd bc bb ba b9 b8 b7 b6 b5 b4 b3 b2 b1 b0
+af ae ad ac ab aa a9 a8 a7 a6 a5 a4 a3 a2 a1 a0
+9f 9e 9d 9c 9b 9a 99 98 97 96 95 94 93 92 91 90
+8f 8e 8d 8c 8b 8a 89 88 87 86 85 84 83 82 81 80
+7f 7e 7d 7c 7b 7a 79 78 77 76 75 74 73 72 71 70
+6f 6e 6d 6c 6b 6a 69 68 67 66 65 64 63 62 61 60
+5f 5e 5d 5c 5b 5a 59 58 57 56 55 54 53 52 51 50
+4f 4e 4d 4c 4b 4a 49 48 47 46 45 44 43 42 41 40
+3f 3e 3d 3c 3b 3a 39 38 37 36 35 34 33 32 31 30
+2f 2e 2d 2c 2b 2a 29 28 27 26 25 24 23 22 21 20
+1f 1e 1d 1c 1b 1a 19 18 17 16 15 14 13 12 11 10
+0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00
--- /dev/null
+{times = 1}
+
+aa bb cc dd
+
+!repeat 3
+ <here>
+
+ !repeat {here + 1}
+ ee ff
+ !end
+
+ 11 22 !repeat times 33 !end
+
+ {times = times + 1}
+!end
+
+"coucou!"
+---
+aa bb cc dd ee ff ee ff ee ff ee ff ee ff 11 22
+33 ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
+ff ee ff ee ff 11 22 33 33 ee ff ee ff ee ff ee
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
+ff ee ff ee ff ee ff ee ff ee ff ee ff 11 22 33
+33 33 63 6f 75 63 6f 75 21
--- /dev/null
+{cond = 0}
+
+aa bb cc dd
+
+!repeat cond
+ ee ff "meow mix" 00
+!end
+
+{be} {-1993:16}
+---
+aa bb cc dd f8 37
--- /dev/null
+{cond = 1}
+
+aa bb cc dd
+
+!repeat cond
+ ee ff "meow mix" 00
+!end
+
+{be} {-1993:16}
+---
+aa bb cc dd ee ff 6d 65 6f 77 20 6d 69 78 00 f8 37
--- /dev/null
+{end - ICITTE - 1 : 8} * 0x100 <end>
+---
+ff fe fd fc fb fa f9 f8 f7 f6 f5 f4 f3 f2 f1 f0
+ef ee ed ec eb ea e9 e8 e7 e6 e5 e4 e3 e2 e1 e0
+df de dd dc db da d9 d8 d7 d6 d5 d4 d3 d2 d1 d0
+cf ce cd cc cb ca c9 c8 c7 c6 c5 c4 c3 c2 c1 c0
+bf be bd bc bb ba b9 b8 b7 b6 b5 b4 b3 b2 b1 b0
+af ae ad ac ab aa a9 a8 a7 a6 a5 a4 a3 a2 a1 a0
+9f 9e 9d 9c 9b 9a 99 98 97 96 95 94 93 92 91 90
+8f 8e 8d 8c 8b 8a 89 88 87 86 85 84 83 82 81 80
+7f 7e 7d 7c 7b 7a 79 78 77 76 75 74 73 72 71 70
+6f 6e 6d 6c 6b 6a 69 68 67 66 65 64 63 62 61 60
+5f 5e 5d 5c 5b 5a 59 58 57 56 55 54 53 52 51 50
+4f 4e 4d 4c 4b 4a 49 48 47 46 45 44 43 42 41 40
+3f 3e 3d 3c 3b 3a 39 38 37 36 35 34 33 32 31 30
+2f 2e 2d 2c 2b 2a 29 28 27 26 25 24 23 22 21 20
+1f 1e 1d 1c 1b 1a 19 18 17 16 15 14 13 12 11 10
+0f 0e 0d 0c 0b 0a 09 08 07 06 05 04 03 02 01 00
--- /dev/null
+{times = 1}
+aa bb cc dd
+(
+ <here>
+ (ee ff) * {here + 1}
+ 11 22 33 * {times}
+ {times = times + 1}
+) * 3
+"coucou!"
+---
+aa bb cc dd ee ff ee ff ee ff ee ff ee ff 11 22
+33 ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
+ff ee ff ee ff 11 22 33 33 ee ff ee ff ee ff ee
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
+ff ee ff ee ff ee ff ee ff ee ff ee ff ee ff ee
+ff ee ff ee ff ee ff ee ff ee ff ee ff 11 22 33
+33 33 63 6f 75 63 6f 75 21
+++ /dev/null
-{ICITTE:8} * 256
----
-00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
-10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
-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 7f
-80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f
-90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f
-a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad 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
+!repeat 256 {ICITTE:8} !end
+---
+00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
+10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
+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 7f
+80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f
+90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f
+a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad 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
+!repeat 10
+ <beg>
+ aa bb cc dd
+ <end>
+ {include = int(beg >= 4 * (end - beg))}
+ !repeat include 11 22 33 44 !end
+!end
+---
+aa bb cc dd
+aa bb cc dd
+aa bb cc dd
+aa bb cc dd
+aa bb cc dd 11 22 33 44
+aa bb cc dd 11 22 33 44
+aa bb cc dd 11 22 33 44
+aa bb cc dd 11 22 33 44
+aa bb cc dd 11 22 33 44
+aa bb cc dd 11 22 33 44
--- /dev/null
+!repeat 5 %11001010 !end
+---
+ca ca ca ca ca
--- /dev/null
+!repeat 5 $-36 !end
+---
+dc dc dc dc dc
--- /dev/null
+!repeat 5 4b !end
+---
+4b 4b 4b 4b 4b
--- /dev/null
+11
+!repeat 2
+ aa bb
+ <meow>
+ cc !repeat meow dd !end
+ ee ff
+!end
+22
+---
+11
+
+aa bb
+cc dd dd dd
+ee ff
+
+aa bb
+cc dd dd dd dd dd dd dd dd dd dd dd
+ee ff
+
+22
--- /dev/null
+aa bb
+<meow>
+cc !repeat meow dd !end
+ee ff
+---
+aa bb
+cc dd dd
+ee ff
--- /dev/null
+aa bb
+{meow = 3}
+cc !repeat meow dd !end
+ee ff
+---
+aa bb
+cc dd dd dd
+ee ff
--- /dev/null
+aa <mix> bb
+{meow = 3}
+{meow = end}
+11 22
+!repeat 2
+ {meow = mix * 2}
+ cc !repeat meow dd !end
+!end
+ee ff
+!repeat meow 88 !end
+<end>
+---
+aa bb
+11 22
+cc dd dd
+cc dd dd
+ee ff
+88 88
--- /dev/null
+aa
+!repeat 0xa
+ bb cc
+ !repeat 3
+ dd ee
+ !end
+!end
+ff
+---
+aa
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ff
--- /dev/null
+aa bb cc
+!repeat ICITTE
+ dd
+!end
+ee ff
+---
+aa bb cc
+dd dd dd
+ee ff
--- /dev/null
+aa ff <meow> dd !repeat meow bb !end cc
+---
+aa ff dd bb bb cc
--- /dev/null
+!r 3
+ aa bb cc
+!end
+---
+aa bb cc
+aa bb cc
+aa bb cc
--- /dev/null
+!repeat 2
+ !repeat 8
+ !repeat 4
+ AA
+ !end
+ !end
+!end
+---
+aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
+aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
+aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
+aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
--- /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
+aa {meow = 5} dd !repeat meow bb !end cc
+---
+aa dd bb bb bb bb bb cc
--- /dev/null
+aa !repeat 0 bb !end cc
+---
+aa cc
+++ /dev/null
-(
- <beg>
- aa bb cc dd
- <end>
- {include = int(beg >= 4 * (end - beg))}
- (11 22 33 44) * {include}
-) * 10
----
-aa bb cc dd
-aa bb cc dd
-aa bb cc dd
-aa bb cc dd
-aa bb cc dd 11 22 33 44
-aa bb cc dd 11 22 33 44
-aa bb cc dd 11 22 33 44
-aa bb cc dd 11 22 33 44
-aa bb cc dd 11 22 33 44
-aa bb cc dd 11 22 33 44
+++ /dev/null
-%11001010*5
----
-ca ca ca ca ca
+++ /dev/null
-$-36*5
----
-dc dc dc dc dc
+++ /dev/null
-4b*5
----
-4b 4b 4b 4b 4b
+++ /dev/null
-11
-(
- aa bb
- <meow>
- cc dd * {meow}
- ee ff
-) * 2
-22
----
-11
-
-aa bb
-cc dd dd dd
-ee ff
-
-aa bb
-cc dd dd dd dd dd dd dd dd dd dd dd
-ee ff
-
-22
+++ /dev/null
-aa bb
-<meow>
-cc dd * {meow}
-ee ff
----
-aa bb
-cc dd dd
-ee ff
+++ /dev/null
-
-aa bb
-{meow = 3}
-cc dd * {meow}
-ee ff
----
-aa bb
-cc dd dd dd
-ee ff
+++ /dev/null
-aa <mix> bb
-{meow = 3}
-{meow = end}
-11 22
-(
- {meow = mix * 2}
- cc dd * {meow}
-) * 2
-ee ff
-88 * {meow}
-<end>
----
-aa bb
-11 22
-cc dd dd
-cc dd dd
-ee ff
-88 88
+++ /dev/null
-aa (bb cc (dd ee) * 3) * 0xa ff
----
-aa
- bb cc
- dd ee
- dd ee
- dd ee
- bb cc
- dd ee
- dd ee
- dd ee
- bb cc
- dd ee
- dd ee
- dd ee
- bb cc
- dd ee
- dd ee
- dd ee
- bb cc
- dd ee
- dd ee
- dd ee
- bb cc
- dd ee
- dd ee
- dd ee
- bb cc
- dd ee
- dd ee
- dd ee
- bb cc
- dd ee
- dd ee
- dd ee
- bb cc
- dd ee
- dd ee
- dd ee
- bb cc
- dd ee
- dd ee
- dd ee
-ff
+++ /dev/null
-aa bb * 1 cc
----
-aa bb cc
--- /dev/null
+{ICITTE:8} * 256
+---
+00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
+10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f
+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 7f
+80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f
+90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f
+a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad 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
+(
+ <beg>
+ aa bb cc dd
+ <end>
+ {include = int(beg >= 4 * (end - beg))}
+ (11 22 33 44) * {include}
+) * 10
+---
+aa bb cc dd
+aa bb cc dd
+aa bb cc dd
+aa bb cc dd
+aa bb cc dd 11 22 33 44
+aa bb cc dd 11 22 33 44
+aa bb cc dd 11 22 33 44
+aa bb cc dd 11 22 33 44
+aa bb cc dd 11 22 33 44
+aa bb cc dd 11 22 33 44
--- /dev/null
+%11001010*5
+---
+ca ca ca ca ca
--- /dev/null
+$-36*5
+---
+dc dc dc dc dc
--- /dev/null
+4b*5
+---
+4b 4b 4b 4b 4b
--- /dev/null
+11
+(
+ aa bb
+ <meow>
+ cc dd * {meow}
+ ee ff
+) * 2
+22
+---
+11
+
+aa bb
+cc dd dd dd
+ee ff
+
+aa bb
+cc dd dd dd dd dd dd dd dd dd dd dd
+ee ff
+
+22
--- /dev/null
+aa bb
+<meow>
+cc dd * {meow}
+ee ff
+---
+aa bb
+cc dd dd
+ee ff
--- /dev/null
+aa bb
+{meow = 3}
+cc dd * {meow}
+ee ff
+---
+aa bb
+cc dd dd dd
+ee ff
--- /dev/null
+aa <mix> bb
+{meow = 3}
+{meow = end}
+11 22
+(
+ {meow = mix * 2}
+ cc dd * {meow}
+) * 2
+ee ff
+88 * {meow}
+<end>
+---
+aa bb
+11 22
+cc dd dd
+cc dd dd
+ee ff
+88 88
--- /dev/null
+aa (bb cc (dd ee) * 3) * 0xa ff
+---
+aa
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ bb cc
+ dd ee
+ dd ee
+ dd ee
+ff
--- /dev/null
+aa bb cc dd*ICITTE ee ff
+---
+aa bb cc
+dd dd dd
+ee ff
--- /dev/null
+aa ff <meow> dd bb * meow cc
+---
+aa ff dd bb bb cc
--- /dev/null
+((AA * 4) * 8) * 2
+---
+aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
+aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
+aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
+aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
--- /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
+aa {meow = 5} dd bb * meow cc
+---
+aa dd bb bb bb bb bb cc
--- /dev/null
+aa bb * 0 cc
+---
+aa cc
+++ /dev/null
-((AA * 4) * 8) * 2
----
-aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
-aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
-aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
-aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa
+++ /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
-aa bb * 0 cc
----
-aa cc