1 # The MIT License (MIT)
3 # Copyright (c) 2023 Philippe Proulx <eeppeliteloop@gmail.com>
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be
14 # included in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 # This module is the portable Normand processor. It offers both the
25 # parse() function and the command-line tool (run the module itself)
26 # without external dependencies except a `typing` module for Python 3.4.
28 # Feel free to copy this module file to your own project to use Normand.
30 # Upstream repository: <https://github.com/efficios/normand>.
32 __author__
= "Philippe Proulx"
65 # Text location (line and column numbers).
68 def _create(cls
, line_no
: int, col_no
: int):
69 self
= cls
.__new
__(cls
)
70 self
._init
(line_no
, col_no
)
73 def __init__(*args
, **kwargs
): # type: ignore
74 raise NotImplementedError
76 def _init(self
, line_no
: int, col_no
: int):
77 self
._line
_no
= line_no
91 return "TextLoc({}, {})".format(self
._line
_no
, self
._col
_no
)
96 def __init__(self
, text_loc
: TextLoc
):
97 self
._text
_loc
= text_loc
99 # Source text location.
102 return self
._text
_loc
106 class _ScalarItem(_Item
):
107 # Returns the size, in bytes, of this item.
110 def size(self
) -> int:
120 class _Byte(_ScalarItem
, _RepableItem
):
121 def __init__(self
, val
: int, text_loc
: TextLoc
):
122 super().__init
__(text_loc
)
135 return "_Byte({}, {})".format(hex(self
._val
), repr(self
._text
_loc
))
139 class _Str(_ScalarItem
, _RepableItem
):
140 def __init__(self
, data
: bytes
, text_loc
: TextLoc
):
141 super().__init
__(text_loc
)
151 return len(self
._data
)
154 return "_Str({}, {})".format(repr(self
._data
), repr(self
._text
_loc
))
159 class ByteOrder(enum
.Enum
):
167 # Byte order setting.
169 def __init__(self
, bo
: ByteOrder
, text_loc
: TextLoc
):
170 super().__init
__(text_loc
)
178 return "_SetBo({}, {})".format(repr(self
._bo
), repr(self
._text
_loc
))
183 def __init__(self
, name
: str, text_loc
: TextLoc
):
184 super().__init
__(text_loc
)
193 return "_Label({}, {})".format(repr(self
._name
), repr(self
._text
_loc
))
197 class _SetOffset(_Item
):
198 def __init__(self
, val
: int, text_loc
: TextLoc
):
199 super().__init
__(text_loc
)
202 # Offset value (bytes).
208 return "_SetOffset({}, {})".format(repr(self
._val
), repr(self
._text
_loc
))
212 class _AlignOffset(_Item
):
213 def __init__(self
, val
: int, pad_val
: int, text_loc
: TextLoc
):
214 super().__init
__(text_loc
)
216 self
._pad
_val
= pad_val
218 # Alignment value (bits).
223 # Padding byte value.
229 return "_AlignOffset({}, {}, {})".format(
230 repr(self
._val
), repr(self
._pad
_val
), repr(self
._text
_loc
)
234 # Mixin of containing an AST expression and its string.
236 def __init__(self
, expr_str
: str, expr
: ast
.Expression
):
237 self
._expr
_str
= expr_str
243 return self
._expr
_str
245 # Expression node to evaluate.
251 # Variable assignment.
252 class _VarAssign(_Item
, _ExprMixin
):
254 self
, name
: str, expr_str
: str, expr
: ast
.Expression
, text_loc
: TextLoc
256 super().__init
__(text_loc
)
257 _ExprMixin
.__init
__(self
, expr_str
, expr
)
266 return "_VarAssign({}, {}, {}, {})".format(
268 repr(self
._expr
_str
),
270 repr(self
._text
_loc
),
274 # Fixed-length number, possibly needing more than one byte.
275 class _FlNum(_ScalarItem
, _RepableItem
, _ExprMixin
):
277 self
, expr_str
: str, expr
: ast
.Expression
, len: int, text_loc
: TextLoc
279 super().__init
__(text_loc
)
280 _ExprMixin
.__init
__(self
, expr_str
, expr
)
290 return self
._len
// 8
293 return "_FlNum({}, {}, {}, {})".format(
294 repr(self
._expr
_str
),
297 repr(self
._text
_loc
),
302 class _Leb128Int(_Item
, _RepableItem
, _ExprMixin
):
303 def __init__(self
, expr_str
: str, expr
: ast
.Expression
, text_loc
: TextLoc
):
304 super().__init
__(text_loc
)
305 _ExprMixin
.__init
__(self
, expr_str
, expr
)
308 return "{}({}, {}, {})".format(
309 self
.__class
__.__name
__,
310 repr(self
._expr
_str
),
312 repr(self
._text
_loc
),
316 # Unsigned LEB128 integer.
317 class _ULeb128Int(_Leb128Int
, _RepableItem
, _ExprMixin
):
321 # Signed LEB128 integer.
322 class _SLeb128Int(_Leb128Int
, _RepableItem
, _ExprMixin
):
327 class _Group(_Item
, _RepableItem
):
328 def __init__(self
, items
: List
[_Item
], text_loc
: TextLoc
):
329 super().__init
__(text_loc
)
338 return "_Group({}, {})".format(repr(self
._items
), repr(self
._text
_loc
))
342 class _Rep(_Item
, _ExprMixin
):
344 self
, item
: _Item
, expr_str
: str, expr
: ast
.Expression
, text_loc
: TextLoc
346 super().__init
__(text_loc
)
347 _ExprMixin
.__init
__(self
, expr_str
, expr
)
356 return "_Rep({}, {}, {}, {})".format(
358 repr(self
._expr
_str
),
360 repr(self
._text
_loc
),
364 # Expression item type.
365 _ExprItemT
= Union
[_FlNum
, _Leb128Int
, _VarAssign
, _Rep
]
368 # A parsing error containing a message and a text location.
369 class ParseError(RuntimeError):
371 def _create(cls
, msg
: str, text_loc
: TextLoc
):
372 self
= cls
.__new
__(cls
)
373 self
._init
(msg
, text_loc
)
376 def __init__(self
, *args
, **kwargs
): # type: ignore
377 raise NotImplementedError
379 def _init(self
, msg
: str, text_loc
: TextLoc
):
380 super().__init
__(msg
)
381 self
._text
_loc
= text_loc
383 # Source text location.
386 return self
._text
_loc
389 # Raises a parsing error, forwarding the parameters to the constructor.
390 def _raise_error(msg
: str, text_loc
: TextLoc
) -> NoReturn
:
391 raise ParseError
._create
(msg
, text_loc
) # pyright: ignore[reportPrivateUsage]
394 # Variable/label dictionary type.
395 SymbolsT
= Dict
[str, Union
[int, float]]
398 # Python name pattern.
399 _py_name_pat
= re
.compile(r
"[a-zA-Z_][a-zA-Z0-9_]*")
404 # The constructor accepts a Normand input. After building, use the `res`
405 # property to get the resulting main group.
407 # Builds a parser to parse the Normand input `normand`, parsing
409 def __init__(self
, normand
: str, variables
: SymbolsT
, labels
: SymbolsT
):
410 self
._normand
= normand
414 self
._label
_names
= set(labels
.keys())
415 self
._var
_names
= set(variables
.keys())
418 # Result (main group).
423 # Current text location.
426 return TextLoc
._create
( # pyright: ignore[reportPrivateUsage]
427 self
._line
_no
, self
._col
_no
430 # Returns `True` if this parser is done parsing.
432 return self
._at
== len(self
._normand
)
434 # Returns `True` if this parser isn't done parsing.
435 def _isnt_done(self
):
436 return not self
._is
_done
()
438 # Raises a parse error, creating it using the message `msg` and the
439 # current text location.
440 def _raise_error(self
, msg
: str) -> NoReturn
:
441 _raise_error(msg
, self
._text
_loc
)
443 # Tries to make the pattern `pat` match the current substring,
444 # returning the match object and updating `self._at`,
445 # `self._line_no`, and `self._col_no` on success.
446 def _try_parse_pat(self
, pat
: Pattern
[str]):
447 m
= pat
.match(self
._normand
, self
._at
)
452 # Skip matched string
453 self
._at
+= len(m
.group(0))
456 self
._line
_no
+= m
.group(0).count("\n")
458 # Update column number
459 for i
in reversed(range(self
._at
)):
460 if self
._normand
[i
] == "\n" or i
== 0:
462 self
._col
_no
= self
._at
+ 1
464 self
._col
_no
= self
._at
- i
468 # Return match object
471 # Expects the pattern `pat` to match the current substring,
472 # returning the match object and updating `self._at`,
473 # `self._line_no`, and `self._col_no` on success, or raising a parse
474 # error with the message `error_msg` on error.
475 def _expect_pat(self
, pat
: Pattern
[str], error_msg
: str):
477 m
= self
._try
_parse
_pat
(pat
)
481 self
._raise
_error
(error_msg
)
483 # Return match object
486 # Pattern for _skip_ws_and_comments()
487 _ws_or_syms_or_comments_pat
= re
.compile(
488 r
"(?:[\s!/\\?&:;.,+[\]_=|-]|#[^#]*?(?:\n|#))*"
491 # Skips as many whitespaces, insignificant symbol characters, and
492 # comments as possible.
493 def _skip_ws_and_comments(self
):
494 self
._try
_parse
_pat
(self
._ws
_or
_syms
_or
_comments
_pat
)
496 # Pattern for _try_parse_hex_byte()
497 _nibble_pat
= re
.compile(r
"[A-Fa-f0-9]")
499 # Tries to parse a hexadecimal byte, returning a byte item on
501 def _try_parse_hex_byte(self
):
502 begin_text_loc
= self
._text
_loc
504 # Match initial nibble
505 m_high
= self
._try
_parse
_pat
(self
._nibble
_pat
)
511 # Expect another nibble
512 self
._skip
_ws
_and
_comments
()
513 m_low
= self
._expect
_pat
(
514 self
._nibble
_pat
, "Expecting another hexadecimal nibble"
518 return _Byte(int(m_high
.group(0) + m_low
.group(0), 16), begin_text_loc
)
520 # Patterns for _try_parse_bin_byte()
521 _bin_byte_bit_pat
= re
.compile(r
"[01]")
522 _bin_byte_prefix_pat
= re
.compile(r
"%")
524 # Tries to parse a binary byte, returning a byte item on success.
525 def _try_parse_bin_byte(self
):
526 begin_text_loc
= self
._text
_loc
529 if self
._try
_parse
_pat
(self
._bin
_byte
_prefix
_pat
) is None:
534 bits
= [] # type: List[str]
537 self
._skip
_ws
_and
_comments
()
538 m
= self
._expect
_pat
(self
._bin
_byte
_bit
_pat
, "Expecting a bit (`0` or `1`)")
539 bits
.append(m
.group(0))
542 return _Byte(int("".join(bits
), 2), begin_text_loc
)
544 # Patterns for _try_parse_dec_byte()
545 _dec_byte_prefix_pat
= re
.compile(r
"\$\s*")
546 _dec_byte_val_pat
= re
.compile(r
"(?P<neg>-?)(?P<val>\d+)")
548 # Tries to parse a decimal byte, returning a byte item on success.
549 def _try_parse_dec_byte(self
):
550 begin_text_loc
= self
._text
_loc
553 if self
._try
_parse
_pat
(self
._dec
_byte
_prefix
_pat
) is None:
558 m
= self
._expect
_pat
(self
._dec
_byte
_val
_pat
, "Expecting a decimal constant")
561 val
= int(m
.group("val")) * (-1 if m
.group("neg") == "-" else 1)
564 if val
< -128 or val
> 255:
565 _raise_error("Invalid decimal byte value {}".format(val
), begin_text_loc
)
571 return _Byte(val
, begin_text_loc
)
573 # Tries to parse a byte, returning a byte item on success.
574 def _try_parse_byte(self
):
576 item
= self
._try
_parse
_hex
_byte
()
582 item
= self
._try
_parse
_bin
_byte
()
588 item
= self
._try
_parse
_dec
_byte
()
593 # Patterns for _try_parse_str()
594 _str_prefix_pat
= re
.compile(r
'(?:u(?P<len>16|32)(?P<bo>be|le))?\s*"')
595 _str_suffix_pat
= re
.compile(r
'"')
596 _str_str_pat
= re
.compile(r
'(?:(?:\\.)|[^"])*')
598 # Strings corresponding to escape sequence characters
599 _str_escape_seq_strs
= {
613 # Tries to parse a string, returning a string item on success.
614 def _try_parse_str(self
):
615 begin_text_loc
= self
._text
_loc
618 m
= self
._try
_parse
_pat
(self
._str
_prefix
_pat
)
627 if m
.group("len") is not None:
628 encoding
= "utf_{}_{}".format(m
.group("len"), m
.group("bo"))
631 m
= self
._expect
_pat
(self
._str
_str
_pat
, "Expecting a literal string")
633 # Expect end of string
634 self
._expect
_pat
(self
._str
_suffix
_pat
, 'Expecting `"` (end of literal string)')
636 # Replace escape sequences
639 for ec
in '0abefnrtv"\\':
640 val
= val
.replace(r
"\{}".format(ec
), self
._str
_escape
_seq
_strs
[ec
])
643 data
= val
.encode(encoding
)
646 return _Str(data
, begin_text_loc
)
648 # Patterns for _try_parse_group()
649 _group_prefix_pat
= re
.compile(r
"\(")
650 _group_suffix_pat
= re
.compile(r
"\)")
652 # Tries to parse a group, returning a group item on success.
653 def _try_parse_group(self
):
654 begin_text_loc
= self
._text
_loc
657 if self
._try
_parse
_pat
(self
._group
_prefix
_pat
) is None:
662 items
= self
._parse
_items
()
664 # Expect end of group
665 self
._skip
_ws
_and
_comments
()
667 self
._group
_suffix
_pat
, "Expecting an item or `)` (end of group)"
671 return _Group(items
, begin_text_loc
)
673 # Returns a stripped expression string and an AST expression node
674 # from the expression string `expr_str` at text location `text_loc`.
675 def _ast_expr_from_str(self
, expr_str
: str, text_loc
: TextLoc
):
676 # Create an expression node from the expression string
677 expr_str
= expr_str
.strip().replace("\n", " ")
680 expr
= ast
.parse(expr_str
, mode
="eval")
683 "Invalid expression `{}`: invalid syntax".format(expr_str
),
687 return expr_str
, expr
689 # Patterns for _try_parse_num_and_attr()
690 _val_expr_pat
= re
.compile(r
"([^}:]+):\s*")
691 _fl_num_len_attr_pat
= re
.compile(r
"8|16|24|32|40|48|56|64")
692 _leb128_int_attr_pat
= re
.compile(r
"(u|s)leb128")
694 # Tries to parse a value and attribute (fixed length in bits or
695 # `leb128`), returning a value item on success.
696 def _try_parse_num_and_attr(self
):
697 begin_text_loc
= self
._text
_loc
700 m_expr
= self
._try
_parse
_pat
(self
._val
_expr
_pat
)
706 # Create an expression node from the expression string
707 expr_str
, expr
= self
._ast
_expr
_from
_str
(m_expr
.group(1), begin_text_loc
)
710 m_attr
= self
._try
_parse
_pat
(self
._fl
_num
_len
_attr
_pat
)
714 m_attr
= self
._try
_parse
_pat
(self
._leb
128_int
_attr
_pat
)
717 # At this point it's invalid
719 "Expecting a length (multiple of eight bits), `uleb128`, or `sleb128`"
722 # Return LEB128 integer item
723 cls
= _ULeb128Int
if m_attr
.group(1) == "u" else _SLeb128Int
724 return cls(expr_str
, expr
, begin_text_loc
)
726 # Return fixed-length number item
730 int(m_attr
.group(0)),
734 # Patterns for _try_parse_num_and_attr()
735 _var_assign_pat
= re
.compile(
736 r
"(?P<name>{})\s*=\s*(?P<expr>[^}}]+)".format(_py_name_pat
.pattern
)
739 # Tries to parse a variable assignment, returning a variable
740 # assignment item on success.
741 def _try_parse_var_assign(self
):
742 begin_text_loc
= self
._text
_loc
745 m
= self
._try
_parse
_pat
(self
._var
_assign
_pat
)
752 name
= m
.group("name")
754 if name
== _icitte_name
:
756 "`{}` is a reserved variable name".format(_icitte_name
), begin_text_loc
759 if name
in self
._label
_names
:
760 _raise_error("Existing label named `{}`".format(name
), begin_text_loc
)
762 # Add to known variable names
763 self
._var
_names
.add(name
)
765 # Create an expression node from the expression string
766 expr_str
, expr
= self
._ast
_expr
_from
_str
(m
.group("expr"), begin_text_loc
)
776 # Pattern for _try_parse_set_bo()
777 _bo_pat
= re
.compile(r
"[bl]e")
779 # Tries to parse a byte order name, returning a byte order setting
781 def _try_parse_set_bo(self
):
782 begin_text_loc
= self
._text
_loc
785 m
= self
._try
_parse
_pat
(self
._bo
_pat
)
791 # Return corresponding item
792 if m
.group(0) == "be":
793 return _SetBo(ByteOrder
.BE
, begin_text_loc
)
795 assert m
.group(0) == "le"
796 return _SetBo(ByteOrder
.LE
, begin_text_loc
)
798 # Patterns for _try_parse_val_or_bo()
799 _val_var_assign_set_bo_prefix_pat
= re
.compile(r
"\{\s*")
800 _val_var_assign_set_bo_suffix_pat
= re
.compile(r
"\s*}")
802 # Tries to parse a value, a variable assignment, or a byte order
803 # setting, returning an item on success.
804 def _try_parse_val_or_var_assign_or_set_bo(self
):
806 if self
._try
_parse
_pat
(self
._val
_var
_assign
_set
_bo
_prefix
_pat
) is None:
810 # Variable assignment item?
811 item
= self
._try
_parse
_var
_assign
()
815 item
= self
._try
_parse
_num
_and
_attr
()
818 # Byte order setting item?
819 item
= self
._try
_parse
_set
_bo
()
822 # At this point it's invalid
824 "Expecting a fixed-length number, a variable assignment, or a byte order setting"
828 self
._expect
_pat
(self
._val
_var
_assign
_set
_bo
_suffix
_pat
, "Expecting `}`")
831 # Pattern for _try_parse_set_offset_val() and _try_parse_rep()
832 _pos_const_int_pat
= re
.compile(r
"0[Xx][A-Fa-f0-9]+|\d+")
834 # Tries to parse an offset setting value (after the initial `<`),
835 # returning an offset item on success.
836 def _try_parse_set_offset_val(self
):
837 begin_text_loc
= self
._text
_loc
840 m
= self
._try
_parse
_pat
(self
._pos
_const
_int
_pat
)
847 return _SetOffset(int(m
.group(0), 0), begin_text_loc
)
849 # Tries to parse a label name (after the initial `<`), returning a
850 # label item on success.
851 def _try_parse_label_name(self
):
852 begin_text_loc
= self
._text
_loc
855 m
= self
._try
_parse
_pat
(_py_name_pat
)
864 if name
== _icitte_name
:
866 "`{}` is a reserved label name".format(_icitte_name
), begin_text_loc
869 if name
in self
._label
_names
:
870 _raise_error("Duplicate label name `{}`".format(name
), begin_text_loc
)
872 if name
in self
._var
_names
:
873 _raise_error("Existing variable named `{}`".format(name
), begin_text_loc
)
875 # Add to known label names
876 self
._label
_names
.add(name
)
879 return _Label(name
, begin_text_loc
)
881 # Patterns for _try_parse_label_or_set_offset()
882 _label_set_offset_prefix_pat
= re
.compile(r
"<\s*")
883 _label_set_offset_suffix_pat
= re
.compile(r
"\s*>")
885 # Tries to parse a label or an offset setting, returning an item on
887 def _try_parse_label_or_set_offset(self
):
889 if self
._try
_parse
_pat
(self
._label
_set
_offset
_prefix
_pat
) is None:
893 # Offset setting item?
894 item
= self
._try
_parse
_set
_offset
_val
()
898 item
= self
._try
_parse
_label
_name
()
901 # At this point it's invalid
902 self
._raise
_error
("Expecting a label name or an offset setting value")
905 self
._expect
_pat
(self
._label
_set
_offset
_suffix
_pat
, "Expecting `>`")
908 # Patterns for _try_parse_align_offset()
909 _align_offset_prefix_pat
= re
.compile(r
"@\s*")
910 _align_offset_val_pat
= re
.compile(r
"(\d+)\s*")
911 _align_offset_pad_val_prefix_pat
= re
.compile(r
"~\s*")
913 # Tries to parse an offset alignment, returning an offset alignment
915 def _try_parse_align_offset(self
):
916 begin_text_loc
= self
._text
_loc
919 if self
._try
_parse
_pat
(self
._align
_offset
_prefix
_pat
) is None:
923 align_text_loc
= self
._text
_loc
924 m
= self
._expect
_pat
(
925 self
._align
_offset
_val
_pat
,
926 "Expecting an alignment (positive multiple of eight bits)",
930 val
= int(m
.group(1))
932 if val
<= 0 or (val
% 8) != 0:
934 "Invalid alignment value {} (not a positive multiple of eight)".format(
943 if self
._try
_parse
_pat
(self
._align
_offset
_pad
_val
_prefix
_pat
) is not None:
944 pad_val_text_loc
= self
._text
_loc
945 m
= self
._expect
_pat
(self
._pos
_const
_int
_pat
, "Expecting a byte value")
948 pad_val
= int(m
.group(0), 0)
952 "Invalid padding byte value {}".format(pad_val
),
957 return _AlignOffset(val
, pad_val
, begin_text_loc
)
959 # Tries to parse a base item (anything except a repetition),
960 # returning it on success.
961 def _try_parse_base_item(self
):
963 item
= self
._try
_parse
_byte
()
969 item
= self
._try
_parse
_str
()
974 # Value, variable assignment, or byte order setting item?
975 item
= self
._try
_parse
_val
_or
_var
_assign
_or
_set
_bo
()
980 # Label or offset setting item?
981 item
= self
._try
_parse
_label
_or
_set
_offset
()
986 # Offset alignment item?
987 item
= self
._try
_parse
_align
_offset
()
993 item
= self
._try
_parse
_group
()
998 # Pattern for _try_parse_rep()
999 _rep_prefix_pat
= re
.compile(r
"\*\s*")
1000 _rep_expr_prefix_pat
= re
.compile(r
"\{")
1001 _rep_expr_pat
= re
.compile(r
"[^}p]+")
1002 _rep_expr_suffix_pat
= re
.compile(r
"\}")
1004 # Tries to parse a repetition, returning the expression string and
1005 # AST expression node on success.
1006 def _try_parse_rep(self
):
1008 if self
._try
_parse
_pat
(self
._rep
_prefix
_pat
) is None:
1012 # Expect and return a decimal multiplier
1013 self
._skip
_ws
_and
_comments
()
1016 m
= self
._try
_parse
_pat
(self
._pos
_const
_int
_pat
)
1020 if self
._try
_parse
_pat
(self
._rep
_expr
_prefix
_pat
) is None:
1021 # At this point it's invalid
1022 self
._raise
_error
("Expecting a positive integral multiplier or `{`")
1024 # Expect an expression
1025 expr_str_loc
= self
._text
_loc
1026 m
= self
._expect
_pat
(self
._rep
_expr
_pat
, "Expecting an expression")
1027 expr_str
= self
._ast
_expr
_from
_str
(m
.group(0), expr_str_loc
)
1030 self
._expect
_pat
(self
._rep
_expr
_suffix
_pat
, "Expecting `}`")
1031 expr_str
= m
.group(0)
1033 expr_str_loc
= self
._text
_loc
1034 expr_str
= m
.group(0)
1036 return self
._ast
_expr
_from
_str
(expr_str
, expr_str_loc
)
1038 # Tries to parse an item, possibly followed by a repetition,
1039 # returning `True` on success.
1041 # Appends any parsed item to `items`.
1042 def _try_append_item(self
, items
: List
[_Item
]):
1043 self
._skip
_ws
_and
_comments
()
1046 item
= self
._try
_parse
_base
_item
()
1052 # Parse repetition if the base item is repeatable
1053 if isinstance(item
, _RepableItem
):
1054 self
._skip
_ws
_and
_comments
()
1055 rep_text_loc
= self
._text
_loc
1056 rep_ret
= self
._try
_parse
_rep
()
1058 if rep_ret
is not None:
1059 item
= _Rep(item
, rep_ret
[0], rep_ret
[1], rep_text_loc
)
1064 # Parses and returns items, skipping whitespaces, insignificant
1065 # symbols, and comments when allowed, and stopping at the first
1066 # unknown character.
1067 def _parse_items(self
) -> List
[_Item
]:
1068 items
= [] # type: List[_Item]
1070 while self
._isnt
_done
():
1071 # Try to append item
1072 if not self
._try
_append
_item
(items
):
1073 # Unknown at this point
1078 # Parses the whole Normand input, setting `self._res` to the main
1079 # group item on success.
1081 if len(self
._normand
.strip()) == 0:
1082 # Special case to make sure there's something to consume
1083 self
._res
= _Group([], self
._text
_loc
)
1086 # Parse first level items
1087 items
= self
._parse
_items
()
1089 # Make sure there's nothing left
1090 self
._skip
_ws
_and
_comments
()
1092 if self
._isnt
_done
():
1094 "Unexpected character `{}`".format(self
._normand
[self
._at
])
1097 # Set main group item
1098 self
._res
= _Group(items
, self
._text
_loc
)
1101 # The return type of parse().
1107 variables
: SymbolsT
,
1110 bo
: Optional
[ByteOrder
],
1112 self
= cls
.__new
__(cls
)
1113 self
._init
(data
, variables
, labels
, offset
, bo
)
1116 def __init__(self
, *args
, **kwargs
): # type: ignore
1117 raise NotImplementedError
1122 variables
: SymbolsT
,
1125 bo
: Optional
[ByteOrder
],
1128 self
._vars
= variables
1129 self
._labels
= labels
1130 self
._offset
= offset
1138 # Dictionary of updated variable names to their last computed value.
1140 def variables(self
):
1143 # Dictionary of updated main group label names to their computed
1154 # Updated byte order.
1156 def byte_order(self
):
1160 # Raises a parse error for the item `item`, creating it using the
1162 def _raise_error_for_item(msg
: str, item
: _Item
) -> NoReturn
:
1163 _raise_error(msg
, item
.text_loc
)
1166 # The `ICITTE` reserved name.
1167 _icitte_name
= "ICITTE"
1170 # Base node visitor.
1172 # Calls the _visit_name() method for each name node which isn't the name
1174 class _NodeVisitor(ast
.NodeVisitor
):
1176 self
._parent
_is
_call
= False
1178 def generic_visit(self
, node
: ast
.AST
):
1179 if type(node
) is ast
.Call
:
1180 self
._parent
_is
_call
= True
1181 elif type(node
) is ast
.Name
and not self
._parent
_is
_call
:
1182 self
._visit
_name
(node
.id)
1184 super().generic_visit(node
)
1185 self
._parent
_is
_call
= False
1188 def _visit_name(self
, name
: str):
1192 # Expression validator: validates that all the names within the
1193 # expression are allowed.
1194 class _ExprValidator(_NodeVisitor
):
1195 def __init__(self
, item
: _ExprItemT
, allowed_names
: Set
[str], icitte_allowed
: bool):
1198 self
._allowed
_names
= allowed_names
1199 self
._icitte
_allowed
= icitte_allowed
1201 def _visit_name(self
, name
: str):
1202 # Make sure the name refers to a known and reachable
1203 # variable/label name.
1204 if name
== _icitte_name
and not self
._icitte
_allowed
:
1206 "Illegal reserved name `{}` in expression `{}`".format(
1207 _icitte_name
, self
._item
.expr_str
1209 self
._item
.text_loc
,
1211 elif name
!= _icitte_name
and name
not in self
._allowed
_names
:
1212 msg
= "Illegal (unknown or unreachable) variable/label name `{}` in expression `{}`".format(
1213 name
, self
._item
.expr_str
1216 allowed_names
= self
._allowed
_names
.copy()
1218 if self
._icitte
_allowed
:
1219 allowed_names
.add(_icitte_name
)
1221 if len(allowed_names
) > 0:
1222 allowed_names_str
= ", ".join(
1223 sorted(["`{}`".format(name
) for name
in allowed_names
])
1225 msg
+= "; the legal names are {{{}}}".format(allowed_names_str
)
1229 self
._item
.text_loc
,
1233 # Expression visitor getting all the contained names.
1234 class _ExprNamesVisitor(_NodeVisitor
):
1236 self
._parent
_is
_call
= False
1237 self
._names
= set() # type: Set[str]
1243 def _visit_name(self
, name
: str):
1244 self
._names
.add(name
)
1251 variables
: SymbolsT
,
1254 bo
: Optional
[ByteOrder
],
1256 self
.variables
= variables
.copy()
1257 self
.labels
= labels
.copy()
1258 self
.offset
= offset
1262 # Generator of data and final state from a group item.
1264 # Generation happens in memory at construction time. After building, use
1265 # the `data`, `variables`, `labels`, `offset`, and `bo` properties to
1266 # get the resulting context.
1268 # The steps of generation are:
1270 # 1. Validate that each repetition and LEB128 integer expression uses
1271 # only reachable names and not `ICITTE`.
1273 # 2. Compute and keep the effective repetition count and LEB128 integer
1274 # value for each repetition and LEB128 integer instance.
1276 # 3. Generate bytes, updating the initial state as it goes which becomes
1277 # the final state after the operation.
1279 # During the generation, when handling a `_Rep` or `_Leb128Int` item,
1280 # we already have the effective repetition count or value of the
1283 # When handling a `_Group` item, first update the current labels with
1284 # all the immediate (not nested) labels, and then handle each
1285 # contained item. This gives contained item access to "future" outer
1286 # labels. Then remove the immediate labels from the state so that
1287 # outer items don't have access to inner labels.
1292 variables
: SymbolsT
,
1295 bo
: Optional
[ByteOrder
],
1297 self
._validate
_vl
_exprs
(group
, set(variables
.keys()), set(labels
.keys()))
1298 self
._vl
_instance
_vals
= self
._compute
_vl
_instance
_vals
(
1299 group
, _GenState(variables
, labels
, offset
, bo
)
1301 self
._gen
(group
, _GenState(variables
, labels
, offset
, bo
))
1308 # Updated variables.
1310 def variables(self
):
1311 return self
._final
_state
.variables
1313 # Updated main group labels.
1316 return self
._final
_state
.labels
1321 return self
._final
_state
.offset
1323 # Updated byte order.
1326 return self
._final
_state
.bo
1328 # Returns the set of used, non-called names within the AST
1329 # expression `expr`.
1331 def _names_of_expr(expr
: ast
.Expression
):
1332 visitor
= _ExprNamesVisitor()
1334 return visitor
.names
1336 # Validates that all the repetition and LEB128 integer expressions
1337 # within `group` don't refer, directly or indirectly, to subsequent
1340 # The strategy here is to keep a set of allowed label names, per
1341 # group, initialized to `allowed_label_names`, and a set of allowed
1342 # variable names initialized to `allowed_variable_names`.
1344 # Then, depending on the type of `item`:
1347 # Add its name to the local allowed label names: a label
1348 # occurring before a repetition, and not within a nested group,
1349 # is always reachable.
1352 # If all the names within its expression are allowed, then add
1353 # its name to the allowed variable names.
1355 # Otherwise, remove its name from the allowed variable names (if
1356 # it's in there): a variable which refers to an unreachable name
1357 # is unreachable itself.
1359 # `_Rep` and `_Leb128`:
1360 # Make sure all the names within its expression are allowed.
1363 # Call this function for each contained item with a _copy_ of
1364 # the current allowed label names and the same current allowed
1367 def _validate_vl_exprs(
1368 item
: _Item
, allowed_variable_names
: Set
[str], allowed_label_names
: Set
[str]
1370 if type(item
) is _Label
:
1371 allowed_label_names
.add(item
.name
)
1372 elif type(item
) is _VarAssign
:
1373 # Check if this variable name is allowed
1376 for name
in _Gen
._names
_of
_expr
(item
.expr
):
1378 allowed_label_names | allowed_variable_names | {_icitte_name}
1385 allowed_variable_names
.add(item
.name
)
1386 elif item
.name
in allowed_variable_names
:
1387 allowed_variable_names
.remove(item
.name
)
1388 elif isinstance(item
, _Leb128Int
):
1389 # Validate the expression (`ICITTE` allowed)
1391 item
, allowed_label_names | allowed_variable_names
, True
1393 elif type(item
) is _Rep
:
1394 # Validate the expression first (`ICITTE` not allowed)
1396 item
, allowed_label_names | allowed_variable_names
, False
1399 # Validate inner item
1400 _Gen
._validate
_vl
_exprs
(
1401 item
.item
, allowed_variable_names
, allowed_label_names
1403 elif type(item
) is _Group
:
1404 # Copy `allowed_label_names` so that this frame cannot
1405 # access the nested label names.
1406 group_allowed_label_names
= allowed_label_names
.copy()
1408 for subitem
in item
.items
:
1409 _Gen
._validate
_vl
_exprs
(
1410 subitem
, allowed_variable_names
, group_allowed_label_names
1413 # Evaluates the expression of `item` considering the current
1414 # generation state `state`.
1416 # If `allow_icitte` is `True`, then the `ICITTE` name is available
1417 # for the expression to evaluate.
1419 # If `allow_float` is `True`, then the type of the result may be
1422 def _eval_item_expr(
1426 allow_float
: bool = False,
1428 syms
= state
.labels
.copy()
1430 # Set the `ICITTE` name to the current offset, if any
1432 syms
[_icitte_name
] = state
.offset
1434 # Add the current variables
1435 syms
.update(state
.variables
)
1437 # Validate the node and its children
1438 _ExprValidator(item
, set(syms
.keys()), True).visit(item
.expr
)
1440 # Compile and evaluate expression node
1442 val
= eval(compile(item
.expr
, "", "eval"), None, syms
)
1443 except Exception as exc
:
1444 _raise_error_for_item(
1445 "Failed to evaluate expression `{}`: {}".format(item
.expr_str
, exc
),
1449 # Validate result type
1450 expected_types
= {int}
# type: Set[type]
1454 expected_types
.add(float)
1455 type_msg
+= " or `float`"
1457 if type(val
) not in expected_types
:
1458 _raise_error_for_item(
1459 "Invalid expression `{}`: expecting result type {}, not `{}`".format(
1460 item
.expr_str
, type_msg
, type(val
).__name
__
1467 # Returns the size, in bytes, required to encode the value `val`
1468 # with LEB128 (signed version if `is_signed` is `True`).
1470 def _leb128_size_for_val(val
: int, is_signed
: bool):
1472 # Equivalent upper bound.
1474 # For example, if `val` is -128, then the full integer for
1475 # this number of bits would be [-128, 127].
1478 # Number of bits (add one for the sign if needed)
1479 bits
= val
.bit_length() + int(is_signed
)
1484 # Seven bits per byte
1485 return math
.ceil(bits
/ 7)
1487 # Returns the offset `offset` aligned according to `item`.
1489 def _align_offset(offset
: int, item
: _AlignOffset
):
1490 align_bytes
= item
.val
// 8
1491 return (offset
+ align_bytes
- 1) // align_bytes
* align_bytes
1493 # Computes the effective value for each repetition and LEB128
1494 # integer instance, filling `instance_vals` (if not `None`) and
1495 # returning `instance_vals`.
1497 # At this point it must be known that, for a given variable-length
1498 # item, its expression only contains reachable names.
1500 # When handling a `_Rep` item, this function appends its effective
1501 # multiplier to `instance_vals` _before_ handling its repeated item.
1503 # When handling a `_VarAssign` item, this function only evaluates it
1504 # if all its names are reachable.
1506 def _compute_vl_instance_vals(
1507 item
: _Item
, state
: _GenState
, instance_vals
: Optional
[List
[int]] = None
1509 if instance_vals
is None:
1512 if isinstance(item
, _ScalarItem
):
1513 state
.offset
+= item
.size
1514 elif type(item
) is _Label
:
1515 state
.labels
[item
.name
] = state
.offset
1516 elif type(item
) is _VarAssign
:
1517 # Check if all the names are reachable
1520 for name
in _Gen
._names
_of
_expr
(item
.expr
):
1522 name
!= _icitte_name
1523 and name
not in state
.variables
1524 and name
not in state
.labels
1526 # A name is unknown: cannot evaluate
1531 # Evaluate the expression and keep the result
1532 state
.variables
[item
.name
] = _Gen
._eval
_item
_expr
(
1533 item
, state
, True, True
1535 elif type(item
) is _SetOffset
:
1536 state
.offset
= item
.val
1537 elif type(item
) is _AlignOffset
:
1538 state
.offset
= _Gen
._align
_offset
(state
.offset
, item
)
1539 elif isinstance(item
, _Leb128Int
):
1540 # Evaluate the expression
1541 val
= _Gen
._eval
_item
_expr
(item
, state
, True)
1544 if type(item
) is _ULeb128Int
and val
< 0:
1545 _raise_error_for_item(
1546 "Invalid expression `{}`: unexpected negative result {:,} for a ULEB128 encoding".format(
1552 # Add the evaluation result to the to variable-length item
1554 instance_vals
.append(val
)
1557 state
.offset
+= _Gen
._leb
128_size
_for
_val
(val
, type(item
) is _SLeb128Int
)
1558 elif type(item
) is _Rep
:
1559 # Evaluate the expression and keep the result
1560 val
= _Gen
._eval
_item
_expr
(item
, state
, False)
1564 _raise_error_for_item(
1565 "Invalid expression `{}`: unexpected negative result {:,}".format(
1571 # Add to repetition instance values
1572 instance_vals
.append(val
)
1574 # Process the repeated item `val` times
1575 for _
in range(val
):
1576 _Gen
._compute
_vl
_instance
_vals
(item
.item
, state
, instance_vals
)
1577 elif type(item
) is _Group
:
1578 prev_labels
= state
.labels
.copy()
1581 for subitem
in item
.items
:
1582 _Gen
._compute
_vl
_instance
_vals
(subitem
, state
, instance_vals
)
1584 state
.labels
= prev_labels
1586 return instance_vals
1588 def _update_offset_noop(self
, item
: _Item
, state
: _GenState
, next_vl_instance
: int):
1589 return next_vl_instance
1591 def _dry_handle_scalar_item(
1592 self
, item
: _ScalarItem
, state
: _GenState
, next_vl_instance
: int
1594 state
.offset
+= item
.size
1595 return next_vl_instance
1597 def _dry_handle_leb128_int_item(
1598 self
, item
: _Leb128Int
, state
: _GenState
, next_vl_instance
: int
1600 # Get the value from `self._vl_instance_vals` _before_
1601 # incrementing `next_vl_instance` to honor the order of
1602 # _compute_vl_instance_vals().
1603 state
.offset
+= self
._leb
128_size
_for
_val
(
1604 self
._vl
_instance
_vals
[next_vl_instance
], type(item
) is _SLeb128Int
1607 return next_vl_instance
+ 1
1609 def _dry_handle_group_item(
1610 self
, item
: _Group
, state
: _GenState
, next_vl_instance
: int
1612 for subitem
in item
.items
:
1613 next_vl_instance
= self
._dry
_handle
_item
(subitem
, state
, next_vl_instance
)
1615 return next_vl_instance
1617 def _dry_handle_rep_item(self
, item
: _Rep
, state
: _GenState
, next_vl_instance
: int):
1618 # Get the value from `self._vl_instance_vals` _before_
1619 # incrementing `next_vl_instance` to honor the order of
1620 # _compute_vl_instance_vals().
1621 mul
= self
._vl
_instance
_vals
[next_vl_instance
]
1622 next_vl_instance
+= 1
1624 for _
in range(mul
):
1625 next_vl_instance
= self
._dry
_handle
_item
(item
.item
, state
, next_vl_instance
)
1627 return next_vl_instance
1629 def _dry_handle_align_offset_item(
1630 self
, item
: _AlignOffset
, state
: _GenState
, next_vl_instance
: int
1632 state
.offset
= self
._align
_offset
(state
.offset
, item
)
1633 return next_vl_instance
1635 def _dry_handle_set_offset_item(
1636 self
, item
: _SetOffset
, state
: _GenState
, next_vl_instance
: int
1638 state
.offset
= item
.val
1639 return next_vl_instance
1641 # Updates `state.offset` considering the generated data of `item`,
1642 # without generating any, and returns the updated next
1643 # variable-length item instance.
1644 def _dry_handle_item(self
, item
: _Item
, state
: _GenState
, next_vl_instance
: int):
1645 return self
._dry
_handle
_item
_funcs
[type(item
)](item
, state
, next_vl_instance
)
1647 # Handles the byte item `item`.
1648 def _handle_byte_item(self
, item
: _Byte
, state
: _GenState
, next_vl_instance
: int):
1649 self
._data
.append(item
.val
)
1650 state
.offset
+= item
.size
1651 return next_vl_instance
1653 # Handles the string item `item`.
1654 def _handle_str_item(self
, item
: _Str
, state
: _GenState
, next_vl_instance
: int):
1655 self
._data
+= item
.data
1656 state
.offset
+= item
.size
1657 return next_vl_instance
1659 # Handles the byte order setting item `item`.
1660 def _handle_set_bo_item(
1661 self
, item
: _SetBo
, state
: _GenState
, next_vl_instance
: int
1663 # Update current byte order
1665 return next_vl_instance
1667 # Handles the variable assignment item `item`.
1668 def _handle_var_assign_item(
1669 self
, item
: _VarAssign
, state
: _GenState
, next_vl_instance
: int
1672 state
.variables
[item
.name
] = self
._eval
_item
_expr
(item
, state
, True, True)
1673 return next_vl_instance
1675 # Handles the fixed-length integer item `item`.
1676 def _handle_fl_int_item(self
, val
: int, item
: _FlNum
, state
: _GenState
):
1678 if val
< -(2 ** (item
.len - 1)) or val
> 2**item
.len - 1:
1679 _raise_error_for_item(
1680 "Value {:,} is outside the {}-bit range when evaluating expression `{}` at byte offset {:,}".format(
1681 val
, item
.len, item
.expr_str
, state
.offset
1686 # Encode result on 64 bits (to extend the sign bit whatever the
1687 # value of `item.len`).
1690 ">" if state
.bo
in (None, ByteOrder
.BE
) else "<",
1691 "Q" if val
>= 0 else "q",
1696 # Keep only the requested length
1697 len_bytes
= item
.len // 8
1699 if state
.bo
in (None, ByteOrder
.BE
):
1700 # Big endian: keep last bytes
1701 data
= data
[-len_bytes
:]
1703 # Little endian: keep first bytes
1704 assert state
.bo
== ByteOrder
.LE
1705 data
= data
[:len_bytes
]
1707 # Append to current bytes and update offset
1710 # Handles the fixed-length integer item `item`.
1711 def _handle_fl_float_item(self
, val
: float, item
: _FlNum
, state
: _GenState
):
1713 if item
.len not in (32, 64):
1714 _raise_error_for_item(
1715 "Invalid {}-bit length for a fixed-length floating point number (value {:,})".format(
1722 self
._data
+= struct
.pack(
1724 ">" if state
.bo
in (None, ByteOrder
.BE
) else "<",
1725 "f" if item
.len == 32 else "d",
1730 # Handles the fixed-length number item `item`.
1731 def _handle_fl_num_item(
1732 self
, item
: _FlNum
, state
: _GenState
, next_vl_instance
: int
1735 val
= self
._eval
_item
_expr
(item
, state
, True, True)
1737 # Validate current byte order
1738 if state
.bo
is None and item
.len > 8:
1739 _raise_error_for_item(
1740 "Current byte order isn't defined at first fixed-length number (`{}`) to encode on more than 8 bits".format(
1746 # Handle depending on type
1747 if type(val
) is int:
1748 self
._handle
_fl
_int
_item
(val
, item
, state
)
1750 assert type(val
) is float
1751 self
._handle
_fl
_float
_item
(val
, item
, state
)
1754 state
.offset
+= item
.size
1756 return next_vl_instance
1758 # Handles the LEB128 integer item `item`.
1759 def _handle_leb128_int_item(
1760 self
, item
: _Leb128Int
, state
: _GenState
, next_vl_instance
: int
1762 # Get the precomputed value
1763 val
= self
._vl
_instance
_vals
[next_vl_instance
]
1766 size
= self
._leb
128_size
_for
_val
(val
, type(item
) is _SLeb128Int
)
1769 for _
in range(size
):
1770 # Seven LSBs, MSB of the byte set (continue)
1771 self
._data
.append((val
& 0x7F) |
0x80)
1774 # Clear MSB of last byte (stop)
1775 self
._data
[-1] &= ~
0x80
1777 # Consumed this instance
1778 return next_vl_instance
+ 1
1780 # Handles the group item `item`, only removing the immediate labels
1781 # from `state.labels` if `remove_immediate_labels` is `True`.
1782 def _handle_group_item(
1786 next_vl_instance
: int,
1787 remove_immediate_labels
: bool = True,
1789 # Compute the values of the immediate (not nested) labels. Those
1790 # labels are reachable by any expression within the group.
1791 tmp_state
= _GenState({}, {}, state
.offset
, None)
1792 immediate_label_names
= set() # type: Set[str]
1793 tmp_next_vl_instance
= next_vl_instance
1795 for subitem
in item
.items
:
1796 if type(subitem
) is _Label
:
1797 # New immediate label
1798 state
.labels
[subitem
.name
] = tmp_state
.offset
1799 immediate_label_names
.add(subitem
.name
)
1801 tmp_next_vl_instance
= self
._dry
_handle
_item
(
1802 subitem
, tmp_state
, tmp_next_vl_instance
1805 # Handle each item now with the actual state
1806 for subitem
in item
.items
:
1807 next_vl_instance
= self
._handle
_item
(subitem
, state
, next_vl_instance
)
1809 # Remove immediate labels if required so that outer items won't
1810 # reach inner labels.
1811 if remove_immediate_labels
:
1812 for name
in immediate_label_names
:
1813 del state
.labels
[name
]
1815 return next_vl_instance
1817 # Handles the repetition item `item`.
1818 def _handle_rep_item(self
, item
: _Rep
, state
: _GenState
, next_vl_instance
: int):
1819 # Get the precomputed repetition count
1820 mul
= self
._vl
_instance
_vals
[next_vl_instance
]
1822 # Consumed this instance
1823 next_vl_instance
+= 1
1825 for _
in range(mul
):
1826 next_vl_instance
= self
._handle
_item
(item
.item
, state
, next_vl_instance
)
1828 return next_vl_instance
1830 # Handles the offset setting item `item`.
1831 def _handle_set_offset_item(
1832 self
, item
: _SetOffset
, state
: _GenState
, next_vl_instance
: int
1834 state
.offset
= item
.val
1835 return next_vl_instance
1837 # Handles offset alignment item `item` (adds padding).
1838 def _handle_align_offset_item(
1839 self
, item
: _AlignOffset
, state
: _GenState
, next_vl_instance
: int
1841 init_offset
= state
.offset
1842 state
.offset
= self
._align
_offset
(state
.offset
, item
)
1843 self
._data
+= bytes([item
.pad_val
] * (state
.offset
- init_offset
))
1844 return next_vl_instance
1846 # Handles the label item `item`.
1847 def _handle_label_item(self
, item
: _Label
, state
: _GenState
, next_vl_instance
: int):
1848 return next_vl_instance
1850 # Handles the item `item`, returning the updated next repetition
1852 def _handle_item(self
, item
: _Item
, state
: _GenState
, next_vl_instance
: int):
1853 return self
._item
_handlers
[type(item
)](item
, state
, next_vl_instance
)
1855 # Generates the data (`self._data`) and final state
1856 # (`self._final_state`) from `group` and the initial state `state`.
1857 def _gen(self
, group
: _Group
, state
: _GenState
):
1859 self
._data
= bytearray()
1862 self
._item
_handlers
= {
1863 _AlignOffset
: self
._handle
_align
_offset
_item
,
1864 _Byte
: self
._handle
_byte
_item
,
1865 _FlNum
: self
._handle
_fl
_num
_item
,
1866 _Group
: self
._handle
_group
_item
,
1867 _Label
: self
._handle
_label
_item
,
1868 _Rep
: self
._handle
_rep
_item
,
1869 _SetBo
: self
._handle
_set
_bo
_item
,
1870 _SetOffset
: self
._handle
_set
_offset
_item
,
1871 _SLeb128Int
: self
._handle
_leb
128_int
_item
,
1872 _Str
: self
._handle
_str
_item
,
1873 _ULeb128Int
: self
._handle
_leb
128_int
_item
,
1874 _VarAssign
: self
._handle
_var
_assign
_item
,
1875 } # type: Dict[type, Callable[[Any, _GenState, int], int]]
1877 # Dry item handlers (only updates the state offset)
1878 self
._dry
_handle
_item
_funcs
= {
1879 _AlignOffset
: self
._dry
_handle
_align
_offset
_item
,
1880 _Byte
: self
._dry
_handle
_scalar
_item
,
1881 _FlNum
: self
._dry
_handle
_scalar
_item
,
1882 _Group
: self
._dry
_handle
_group
_item
,
1883 _Label
: self
._update
_offset
_noop
,
1884 _Rep
: self
._dry
_handle
_rep
_item
,
1885 _SetBo
: self
._update
_offset
_noop
,
1886 _SetOffset
: self
._dry
_handle
_set
_offset
_item
,
1887 _SLeb128Int
: self
._dry
_handle
_leb
128_int
_item
,
1888 _Str
: self
._dry
_handle
_scalar
_item
,
1889 _ULeb128Int
: self
._dry
_handle
_leb
128_int
_item
,
1890 _VarAssign
: self
._update
_offset
_noop
,
1891 } # type: Dict[type, Callable[[Any, _GenState, int], int]]
1893 # Handle the group item, _not_ removing the immediate labels
1894 # because the `labels` property offers them.
1895 self
._handle
_group
_item
(group
, state
, 0, False)
1897 # This is actually the final state
1898 self
._final
_state
= state
1901 # Returns a `ParseResult` instance containing the bytes encoded by the
1902 # input string `normand`.
1904 # `init_variables` is a dictionary of initial variable names (valid
1905 # Python names) to integral values. A variable name must not be the
1906 # reserved name `ICITTE`.
1908 # `init_labels` is a dictionary of initial label names (valid Python
1909 # names) to integral values. A label name must not be the reserved name
1912 # `init_offset` is the initial offset.
1914 # `init_byte_order` is the initial byte order.
1916 # Raises `ParseError` on any parsing error.
1919 init_variables
: Optional
[SymbolsT
] = None,
1920 init_labels
: Optional
[SymbolsT
] = None,
1921 init_offset
: int = 0,
1922 init_byte_order
: Optional
[ByteOrder
] = None,
1924 if init_variables
is None:
1927 if init_labels
is None:
1931 _Parser(normand
, init_variables
, init_labels
).res
,
1937 return ParseResult
._create
( # pyright: ignore[reportPrivateUsage]
1938 gen
.data
, gen
.variables
, gen
.labels
, gen
.offset
, gen
.bo
1942 # Parses the command-line arguments.
1943 def _parse_cli_args():
1947 ap
= argparse
.ArgumentParser()
1954 help="initial offset (positive)",
1960 choices
=["be", "le"],
1962 help="initial byte order (`be` or `le`)",
1968 help="add an initial variable (may be repeated)",
1975 help="add an initial label (may be repeated)",
1978 "--version", action
="version", version
="Normand {}".format(__version__
)
1985 help="input path (none means standard input)",
1989 return ap
.parse_args()
1992 # Raises a command-line error with the message `msg`.
1993 def _raise_cli_error(msg
: str) -> NoReturn
:
1994 raise RuntimeError("Command-line error: {}".format(msg
))
1997 # Returns a dictionary of string to integers from the list of strings
1998 # `args` containing `NAME=VAL` entries.
1999 def _dict_from_arg(args
: Optional
[List
[str]]):
2000 d
= {} # type: SymbolsT
2006 m
= re
.match(r
"({})=(\d+)$".format(_py_name_pat
.pattern
), arg
)
2009 _raise_cli_error("Invalid assignment {}".format(arg
))
2011 d
[m
.group(1)] = int(m
.group(2))
2016 # CLI entry point without exception handling.
2021 args
= _parse_cli_args()
2024 if args
.path
is None:
2025 normand
= sys
.stdin
.read()
2027 with
open(args
.path
) as f
:
2030 # Variables and labels
2031 variables
= _dict_from_arg(args
.var
)
2032 labels
= _dict_from_arg(args
.label
)
2036 _raise_cli_error("Invalid negative offset {}")
2038 # Validate and set byte order
2039 bo
= None # type: Optional[ByteOrder]
2041 if args
.byte_order
is not None:
2042 if args
.byte_order
== "be":
2045 assert args
.byte_order
== "le"
2050 res
= parse(normand
, variables
, labels
, args
.offset
, bo
)
2051 except ParseError
as exc
:
2054 if args
.path
is not None:
2055 prefix
= "{}:".format(os
.path
.abspath(args
.path
))
2058 "{}{}:{} - {}".format(
2059 prefix
, exc
.text_loc
.line_no
, exc
.text_loc
.col_no
, str(exc
)
2064 sys
.stdout
.buffer.write(res
.data
)
2067 # Prints the exception message `msg` and exits with status 1.
2068 def _fail(msg
: str) -> NoReturn
:
2069 if not msg
.endswith("."):
2072 print(msg
, file=sys
.stderr
)
2080 except Exception as exc
:
2084 if __name__
== "__main__":