Add macro support
[normand.git] / README.adoc
index c865f0faabb9f4c2e90163716a9805e0fb377896..e98e63703460cdb5d478f26b95f2ba9593c36f08 100644 (file)
@@ -29,7 +29,7 @@ _**Normand**_ is a text-to-binary processor with its own language.
 This package offers both a portable {py3} module and a command-line
 tool.
 
-WARNING: This version of Normand is 0.10, meaning both the Normand
+WARNING: This version of Normand is 0.11, meaning both the Normand
 language and the module/CLI interface aren't stable.
 
 ifdef::env-github[]
@@ -261,6 +261,38 @@ aa bb 7a 6f 6f 6d cc aa  bb 7a 6f 6f 6d cc aa bb  ┆ ••zoom•••zoom•
 6f 6d cc aa bb 7a 6f 6f  6d cc de de de de        ┆ om•••zoom•••••
 ----
 
+Macros::
++
+Input:
++
+----
+!macro hello(world)
+  "hello"
+  !if world " world" !end
+!end
+
+!repeat 17
+  ff ff ff ff
+  m:hello({ICITTE > 15 and ICITTE < 60})
+!end
+----
++
+Output:
++
+----
+ff ff ff ff 68 65 6c 6c  6f ff ff ff ff 68 65 6c  ┆ ••••hello••••hel
+6c 6f ff ff ff ff 68 65  6c 6c 6f 20 77 6f 72 6c  ┆ lo••••hello worl
+64 ff ff ff ff 68 65 6c  6c 6f 20 77 6f 72 6c 64  ┆ d••••hello world
+ff ff ff ff 68 65 6c 6c  6f 20 77 6f 72 6c 64 ff  ┆ ••••hello world•
+ff ff ff 68 65 6c 6c 6f  ff ff ff ff 68 65 6c 6c  ┆ •••hello••••hell
+6f ff ff ff ff 68 65 6c  6c 6f ff ff ff ff 68 65  ┆ o••••hello••••he
+6c 6c 6f ff ff ff ff 68  65 6c 6c 6f ff ff ff ff  ┆ llo••••hello••••
+68 65 6c 6c 6f ff ff ff  ff 68 65 6c 6c 6f ff ff  ┆ hello••••hello••
+ff ff 68 65 6c 6c 6f ff  ff ff ff 68 65 6c 6c 6f  ┆ ••hello••••hello
+ff ff ff ff 68 65 6c 6c  6f ff ff ff ff 68 65 6c  ┆ ••••hello••••hel
+6c 6f ff ff ff ff 68 65  6c 6c 6f                 ┆ lo••••hello
+----
+
 Precise error reporting::
 +
 ----
@@ -272,11 +304,11 @@ Precise error reporting::
 ----
 +
 ----
-/tmp/meow.normand:24:19 - Illegal (unknown or unreachable) variable/label name `meow` in expression `(meow - 45) // 8`; the legal names are {`mix`, `zoom`}.
+/tmp/meow.normand:24:19 - Illegal (unknown or unreachable) variable/label name `meow` in expression `(meow - 45) // 8`; the legal names are {`ICITTE`, `mix`, `zoom`}.
 ----
 +
 ----
-/tmp/meow.normand:18:9 - Value 315 is outside the 8-bit range when evaluating expression `end - ICITTE` at byte offset 45.
+/tmp/meow.normand:18:9 - Value 315 is outside the 8-bit range when evaluating expression `end - ICITTE`.
 ----
 
 You can use Normand to track data source files in your favorite VCS
@@ -330,8 +362,8 @@ the special `ICITTE` name in <<fixed-length-number,fixed-length
 number>>, <<leb-128-integer,LEB128 integer>>,
 <<variable-assignment,variable assignment>>,
 <<conditional-block,conditional block>>, <<repetition-block,repetition
-block>>, and <<post-item-repetition,post-item repetition>> expression
-evaluation.
+block>>, <<macro-expansion,macro expansion>>, and
+<<post-item-repetition,post-item repetition>> expression evaluation.
 
 Each generated byte increments the current offset.
 
@@ -399,6 +431,10 @@ This is similar to an assembly label.
 
 * A <<repetition-block,repetition block>>.
 
+* A <<macro-definition-block,macro definition block>>.
+
+* A <<macro-expansion,macro expansion>>.
+
 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>>.
@@ -755,9 +791,8 @@ expression may contain:
 +
 --
 * The name of any <<label,label>> defined before{nbsp}__**L**__.
-* The name of any <<variable-assignment,variable>> known at{nbsp}__**L**__
-  which doesn't, directly or indirectly, refer to a label
-  defined after{nbsp}__**L**__.
+* The name of any <<variable-assignment,variable>> known
+  at{nbsp}__**L**__.
 --
 +
 The value of the special name `ICITTE` (`int` type) in this expression
@@ -961,10 +996,14 @@ A variable assignment is:
   is `int`, `float`, or `bool` (automatically converted to `int`).
 +
 For a variable assignment at some source location{nbsp}__**L**__, this
-expression may contain the name of any accessible <<label,label>> (not
-within a nested group), including the name of a label defined
-after{nbsp}__**L**__, as well as the name of any
-<<variable-assignment,variable>> known at{nbsp}__**L**__.
+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>>.
@@ -1086,15 +1125,14 @@ A conditional block is:
    evaluation result type is `int` or `bool` (automatically converted to
    `int`), and the ``pass:[}]`` suffix.
 +
-For a repetition at some source location{nbsp}__**L**__, this expression
-may contain:
+For a conditional block 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**__ which doesn't, directly or indirectly, refer to a
-  label defined after{nbsp}__**L**__.
+  at{nbsp}__**L**__.
 --
 +
 The value of the special name `ICITTE` (`int` type) in this expression
@@ -1179,15 +1217,14 @@ A repetition block is:
    evaluation result type is `int` or `bool` (automatically converted to
    `int`), and the ``pass:[}]`` suffix.
 +
-For a repetition at some source location{nbsp}__**L**__, this expression
-may contain:
+For a repetition block 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**__ which doesn't, directly or indirectly, refer to a
-  label defined after{nbsp}__**L**__.
+  at{nbsp}__**L**__.
 --
 +
 The value of the special name `ICITTE` (`int` type) in this expression
@@ -1281,6 +1318,215 @@ ff ee ff ee ff ee ff ee  ff ee ff ee ff 11 22 33  ┆ ••••••••
 ----
 ====
 
+=== Macro definition block
+
+A _macro definition block_ associates a name and parameter names to
+a group of items.
+
+A macro definition block doesn't lead to generated bytes itself: a
+<<macro-expansion,macro expansion>> does so.
+
+A macro definition may only exist at the root level, that is, not within
+a <<group,group>>, a <<repetition-block,repetition block>>, a
+<<conditional-block,conditional block>>, or another
+<<macro-definition-block,macro definition block>>.
+
+All macro definitions must have unique names.
+
+A macro definition is:
+
+. The `!macro` or `!m` opening.
+
+. A valid {py3} name (the macro name).
+
+. The `(` parameter name list prefix.
+
+. A comma-separated list of zero or more unique parameter names,
+  each one being a valid {py3} name.
+
+. The `)` parameter name list suffix.
+
+. Zero or more items except, recursively, a macro definition block.
+
+. The `!end` closing.
+
+====
+----
+!macro bake()
+  {le} {ICITTE * 8 : 16}
+  u16le"predict explode"
+!end
+----
+====
+
+====
+----
+!macro nail(rep, with_extra, val)
+  {iter = 1}
+
+  !repeat rep
+    {val + iter : uleb128}
+    {0xdeadbeef : 32}
+    {iter = iter + 1}
+  !end
+
+  !if with_extra
+    "meow mix\0"
+  !end
+!end
+----
+====
+
+=== Macro expansion
+
+A _macro expansion_ expands the items of a defined
+<<macro-definition-block,macro>>.
+
+The macro to expand must be defined _before_ the expansion.
+
+The <<state,state>> before handling the first item of the chosen macro
+is:
+
+<<cur-offset,Current offset>>::
+    Unchanged.
+
+<<cur-bo,Current byte order>>::
+    Unchanged.
+
+Variables::
+    The only available variables initially are the macro parameters.
+
+Labels::
+    None.
+
+The state after having handled the last item of the chosen macro is:
+
+Current offset::
+    The one before handling the first item of the macro plus the size
+    of the generated data of the macro expansion.
++
+IMPORTANT: This means <<current-offset-setting,current offset setting>>
+items within the expanded macro don't impact the final current offset.
+
+Current byte order::
+    The one before handling the first item of the macro.
+
+Variables::
+    The ones before handling the first item of the macro.
+
+Labels::
+    The ones before handling the first item of the macro.
+
+A macro expansion is:
+
+. The `m:` prefix.
+
+. A valid {py3} name (the name of the macro to expand).
+
+. The `(` parameter value list prefix.
+
+. A comma-separated list of zero or more unique parameter values.
++
+The number of parameter values must match the number of parameter
+names of the definition of the chosen macro.
++
+A parameter value is one of:
++
+--
+* A positive integer (hexadecimal starting with `0x` or `0X` accepted).
+
+* The ``pass:[{]`` prefix, a valid {py3} expression of which the
+  evaluation result type is `int` or `bool` (automatically converted to
+  `int`), and the ``pass:[}]`` suffix.
++
+For a macro expansion 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 handling the items of the
+chosen macro).
+
+* A valid {py3} name.
++
+For the name `__NAME__`, this is equivalent to the
+`pass:[{]__NAME__pass:[}]` form above.
+--
+
+. The `)` parameter value list suffix.
+
+====
+Input:
+
+----
+!macro bake()
+  {le} {ICITTE * 8 : 16}
+  u16le"predict explode"
+!end
+
+"hello [" m:bake() "] world"
+
+m:bake() * 5
+----
+
+Output:
+
+----
+68 65 6c 6c 6f 20 5b 38  00 70 00 72 00 65 00 64  ┆ hello [8•p•r•e•d
+00 69 00 63 00 74 00 20  00 65 00 78 00 70 00 6c  ┆ •i•c•t• •e•x•p•l
+00 6f 00 64 00 65 00 5d  20 77 6f 72 6c 64 70 01  ┆ •o•d•e•] worldp•
+70 00 72 00 65 00 64 00  69 00 63 00 74 00 20 00  ┆ p•r•e•d•i•c•t• •
+65 00 78 00 70 00 6c 00  6f 00 64 00 65 00 70 02  ┆ e•x•p•l•o•d•e•p•
+70 00 72 00 65 00 64 00  69 00 63 00 74 00 20 00  ┆ p•r•e•d•i•c•t• •
+65 00 78 00 70 00 6c 00  6f 00 64 00 65 00 70 03  ┆ e•x•p•l•o•d•e•p•
+70 00 72 00 65 00 64 00  69 00 63 00 74 00 20 00  ┆ p•r•e•d•i•c•t• •
+65 00 78 00 70 00 6c 00  6f 00 64 00 65 00 70 04  ┆ e•x•p•l•o•d•e•p•
+70 00 72 00 65 00 64 00  69 00 63 00 74 00 20 00  ┆ p•r•e•d•i•c•t• •
+65 00 78 00 70 00 6c 00  6f 00 64 00 65 00 70 05  ┆ e•x•p•l•o•d•e•p•
+70 00 72 00 65 00 64 00  69 00 63 00 74 00 20 00  ┆ p•r•e•d•i•c•t• •
+65 00 78 00 70 00 6c 00  6f 00 64 00 65 00        ┆ e•x•p•l•o•d•e•
+----
+====
+
+====
+Input:
+
+----
+!macro A(val, is_be)
+  {le}
+
+  !if is_be
+    {be}
+  !end
+
+  {val : 16}
+!end
+
+!macro B(rep, is_be)
+  {iter = 1}
+
+  !repeat rep
+  m:A({iter * 3}, is_be)
+  {iter = iter + 1}
+  !end
+!end
+
+m:B(5, 1)
+m:B(3, 0)
+----
+
+Output:
+
+----
+00 03 00 06 00 09 00 0c  00 0f 03 00 06 00 09 00
+----
+====
+
 === Post-item repetition
 
 A _post-item repetition_ represents the bytes of an item repeated a
@@ -1294,6 +1540,7 @@ A post-item repetition is:
 ** A <<literal-string,literal string>>.
 ** A <<fixed-length-number,fixed-length number>>.
 ** An <<leb128-integer,LEB128 integer>>.
+** A <<macro-expansion,macro-expansion>>.
 ** A <<group,group>>.
 
 . The ``pass:[*]`` character.
@@ -1307,8 +1554,8 @@ A post-item repetition is:
    evaluation result type is `int` or `bool` (automatically converted to
    `int`), and the ``pass:[}]`` suffix.
 +
-For a repetition at some source location{nbsp}__**L**__, this expression
-may contain:
+For a post-item repetition at some source location{nbsp}__**L**__, this
+expression may contain:
 +
 --
 * The name of any <<label,label>> defined before{nbsp}__**L**__
@@ -1316,8 +1563,7 @@ may contain:
   which isn't part of the 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**__.
+  doesn't.
 --
 +
 The value of the special name `ICITTE` (`int` type) in this expression
This page took 0.026241 seconds and 4 git commands to generate.