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"
66 # Text location (line and column numbers).
69 def _create(cls
, line_no
: int, col_no
: int):
70 self
= cls
.__new
__(cls
)
71 self
._init
(line_no
, col_no
)
74 def __init__(*args
, **kwargs
): # type: ignore
75 raise NotImplementedError
77 def _init(self
, line_no
: int, col_no
: int):
78 self
._line
_no
= line_no
92 return "TextLoc({}, {})".format(self
._line
_no
, self
._col
_no
)
97 def __init__(self
, text_loc
: TextLoc
):
98 self
._text
_loc
= text_loc
100 # Source text location.
103 return self
._text
_loc
107 class _ScalarItem(_Item
):
108 # Returns the size, in bytes, of this item.
111 def size(self
) -> int:
121 class _Byte(_ScalarItem
, _RepableItem
):
122 def __init__(self
, val
: int, text_loc
: TextLoc
):
123 super().__init
__(text_loc
)
136 return "_Byte({}, {})".format(hex(self
._val
), self
._text
_loc
)
140 class _Str(_ScalarItem
, _RepableItem
):
141 def __init__(self
, data
: bytes
, text_loc
: TextLoc
):
142 super().__init
__(text_loc
)
152 return len(self
._data
)
155 return "_Str({}, {})".format(repr(self
._data
), self
._text
_loc
)
160 class ByteOrder(enum
.Enum
):
168 # Byte order setting.
170 def __init__(self
, bo
: ByteOrder
, text_loc
: TextLoc
):
171 super().__init
__(text_loc
)
179 return "_SetBo({}, {})".format(repr(self
._bo
), self
._text
_loc
)
184 def __init__(self
, name
: str, text_loc
: TextLoc
):
185 super().__init
__(text_loc
)
194 return "_Label({}, {})".format(repr(self
._name
), self
._text
_loc
)
198 class _SetOffset(_Item
):
199 def __init__(self
, val
: int, text_loc
: TextLoc
):
200 super().__init
__(text_loc
)
209 return "_SetOffset({}, {})".format(repr(self
._val
), self
._text
_loc
)
212 # Mixin of containing an AST expression and its string.
214 def __init__(self
, expr_str
: str, expr
: ast
.Expression
):
215 self
._expr
_str
= expr_str
221 return self
._expr
_str
223 # Expression node to evaluate.
229 # Variable assignment.
230 class _VarAssign(_Item
, _ExprMixin
):
232 self
, name
: str, expr_str
: str, expr
: ast
.Expression
, text_loc
: TextLoc
234 super().__init
__(text_loc
)
235 _ExprMixin
.__init
__(self
, expr_str
, expr
)
244 return "_VarAssign({}, {}, {}, {})".format(
245 repr(self
._name
), repr(self
._expr
_str
), repr(self
._expr
), self
._text
_loc
249 # Fixed-length integer, possibly needing more than one byte.
250 class _FlInt(_ScalarItem
, _RepableItem
, _ExprMixin
):
252 self
, expr_str
: str, expr
: ast
.Expression
, len: int, text_loc
: TextLoc
254 super().__init
__(text_loc
)
255 _ExprMixin
.__init
__(self
, expr_str
, expr
)
265 return self
._len
// 8
268 return "_FlInt({}, {}, {}, {})".format(
269 repr(self
._expr
_str
), repr(self
._expr
), repr(self
._len
), self
._text
_loc
274 class _Leb128Int(_Item
, _RepableItem
, _ExprMixin
):
275 def __init__(self
, expr_str
: str, expr
: ast
.Expression
, text_loc
: TextLoc
):
276 super().__init
__(text_loc
)
277 _ExprMixin
.__init
__(self
, expr_str
, expr
)
280 return "{}({}, {}, {})".format(
281 self
.__class
__.__name
__,
282 repr(self
._expr
_str
),
288 # Unsigned LEB128 integer.
289 class _ULeb128Int(_Leb128Int
, _RepableItem
, _ExprMixin
):
293 # Signed LEB128 integer.
294 class _SLeb128Int(_Leb128Int
, _RepableItem
, _ExprMixin
):
299 class _Group(_Item
, _RepableItem
):
300 def __init__(self
, items
: List
[_Item
], text_loc
: TextLoc
):
301 super().__init
__(text_loc
)
310 return "_Group({}, {})".format(repr(self
._items
), self
._text
_loc
)
314 class _Rep(_Item
, _ExprMixin
):
316 self
, item
: _Item
, expr_str
: str, expr
: ast
.Expression
, text_loc
: TextLoc
318 super().__init
__(text_loc
)
319 _ExprMixin
.__init
__(self
, expr_str
, expr
)
328 return "_Rep({}, {}, {}, {})".format(
329 repr(self
._item
), repr(self
._expr
_str
), repr(self
._expr
), self
._text
_loc
333 # Expression item type.
334 _ExprItemT
= Union
[_FlInt
, _Leb128Int
, _VarAssign
, _Rep
]
337 # A parsing error containing a message and a text location.
338 class ParseError(RuntimeError):
340 def _create(cls
, msg
: str, text_loc
: TextLoc
):
341 self
= cls
.__new
__(cls
)
342 self
._init
(msg
, text_loc
)
345 def __init__(self
, *args
, **kwargs
): # type: ignore
346 raise NotImplementedError
348 def _init(self
, msg
: str, text_loc
: TextLoc
):
349 super().__init
__(msg
)
350 self
._text
_loc
= text_loc
352 # Source text location.
355 return self
._text
_loc
358 # Raises a parsing error, forwarding the parameters to the constructor.
359 def _raise_error(msg
: str, text_loc
: TextLoc
) -> NoReturn
:
360 raise ParseError
._create
(msg
, text_loc
) # pyright: ignore[reportPrivateUsage]
363 # Variable/label dictionary type.
364 SymbolsT
= Dict
[str, int]
367 # Python name pattern.
368 _py_name_pat
= re
.compile(r
"[a-zA-Z_][a-zA-Z0-9_]*")
373 # The constructor accepts a Normand input. After building, use the `res`
374 # property to get the resulting main group.
376 # Builds a parser to parse the Normand input `normand`, parsing
378 def __init__(self
, normand
: str, variables
: SymbolsT
, labels
: SymbolsT
):
379 self
._normand
= normand
383 self
._label
_names
= set(labels
.keys())
384 self
._var
_names
= set(variables
.keys())
387 # Result (main group).
392 # Current text location.
395 return TextLoc
._create
( # pyright: ignore[reportPrivateUsage]
396 self
._line
_no
, self
._col
_no
399 # Returns `True` if this parser is done parsing.
401 return self
._at
== len(self
._normand
)
403 # Returns `True` if this parser isn't done parsing.
404 def _isnt_done(self
):
405 return not self
._is
_done
()
407 # Raises a parse error, creating it using the message `msg` and the
408 # current text location.
409 def _raise_error(self
, msg
: str) -> NoReturn
:
410 _raise_error(msg
, self
._text
_loc
)
412 # Tries to make the pattern `pat` match the current substring,
413 # returning the match object and updating `self._at`,
414 # `self._line_no`, and `self._col_no` on success.
415 def _try_parse_pat(self
, pat
: Pattern
[str]):
416 m
= pat
.match(self
._normand
, self
._at
)
421 # Skip matched string
422 self
._at
+= len(m
.group(0))
425 self
._line
_no
+= m
.group(0).count("\n")
427 # Update column number
428 for i
in reversed(range(self
._at
)):
429 if self
._normand
[i
] == "\n" or i
== 0:
431 self
._col
_no
= self
._at
+ 1
433 self
._col
_no
= self
._at
- i
437 # Return match object
440 # Expects the pattern `pat` to match the current substring,
441 # returning the match object and updating `self._at`,
442 # `self._line_no`, and `self._col_no` on success, or raising a parse
443 # error with the message `error_msg` on error.
444 def _expect_pat(self
, pat
: Pattern
[str], error_msg
: str):
446 m
= self
._try
_parse
_pat
(pat
)
450 self
._raise
_error
(error_msg
)
452 # Return match object
455 # Pattern for _skip_ws_and_comments()
456 _ws_or_syms_or_comments_pat
= re
.compile(
457 r
"(?:[\s!@/\\?&:;.,+[\]_=|-]|#[^#]*?(?:\n|#))*"
460 # Skips as many whitespaces, insignificant symbol characters, and
461 # comments as possible.
462 def _skip_ws_and_comments(self
):
463 self
._try
_parse
_pat
(self
._ws
_or
_syms
_or
_comments
_pat
)
465 # Pattern for _try_parse_hex_byte()
466 _nibble_pat
= re
.compile(r
"[A-Fa-f0-9]")
468 # Tries to parse a hexadecimal byte, returning a byte item on
470 def _try_parse_hex_byte(self
):
471 begin_text_loc
= self
._text
_loc
473 # Match initial nibble
474 m_high
= self
._try
_parse
_pat
(self
._nibble
_pat
)
480 # Expect another nibble
481 self
._skip
_ws
_and
_comments
()
482 m_low
= self
._expect
_pat
(
483 self
._nibble
_pat
, "Expecting another hexadecimal nibble"
487 return _Byte(int(m_high
.group(0) + m_low
.group(0), 16), begin_text_loc
)
489 # Patterns for _try_parse_bin_byte()
490 _bin_byte_bit_pat
= re
.compile(r
"[01]")
491 _bin_byte_prefix_pat
= re
.compile(r
"%")
493 # Tries to parse a binary byte, returning a byte item on success.
494 def _try_parse_bin_byte(self
):
495 begin_text_loc
= self
._text
_loc
498 if self
._try
_parse
_pat
(self
._bin
_byte
_prefix
_pat
) is None:
503 bits
= [] # type: List[str]
506 self
._skip
_ws
_and
_comments
()
507 m
= self
._expect
_pat
(self
._bin
_byte
_bit
_pat
, "Expecting a bit (`0` or `1`)")
508 bits
.append(m
.group(0))
511 return _Byte(int("".join(bits
), 2), begin_text_loc
)
513 # Patterns for _try_parse_dec_byte()
514 _dec_byte_prefix_pat
= re
.compile(r
"\$\s*")
515 _dec_byte_val_pat
= re
.compile(r
"(?P<neg>-?)(?P<val>\d+)")
517 # Tries to parse a decimal byte, returning a byte item on success.
518 def _try_parse_dec_byte(self
):
519 begin_text_loc
= self
._text
_loc
522 if self
._try
_parse
_pat
(self
._dec
_byte
_prefix
_pat
) is None:
527 m
= self
._expect
_pat
(self
._dec
_byte
_val
_pat
, "Expecting a decimal constant")
530 val
= int(m
.group("val")) * (-1 if m
.group("neg") == "-" else 1)
533 if val
< -128 or val
> 255:
534 _raise_error("Invalid decimal byte value {}".format(val
), begin_text_loc
)
540 return _Byte(val
, begin_text_loc
)
542 # Tries to parse a byte, returning a byte item on success.
543 def _try_parse_byte(self
):
545 item
= self
._try
_parse
_hex
_byte
()
551 item
= self
._try
_parse
_bin
_byte
()
557 item
= self
._try
_parse
_dec
_byte
()
562 # Patterns for _try_parse_str()
563 _str_prefix_pat
= re
.compile(r
'(?:u(?P<len>16|32)(?P<bo>be|le))?\s*"')
564 _str_suffix_pat
= re
.compile(r
'"')
565 _str_str_pat
= re
.compile(r
'(?:(?:\\.)|[^"])*')
567 # Strings corresponding to escape sequence characters
568 _str_escape_seq_strs
= {
582 # Tries to parse a string, returning a string item on success.
583 def _try_parse_str(self
):
584 begin_text_loc
= self
._text
_loc
587 m
= self
._try
_parse
_pat
(self
._str
_prefix
_pat
)
596 if m
.group("len") is not None:
597 encoding
= "utf_{}_{}".format(m
.group("len"), m
.group("bo"))
600 m
= self
._expect
_pat
(self
._str
_str
_pat
, "Expecting a literal string")
602 # Expect end of string
603 self
._expect
_pat
(self
._str
_suffix
_pat
, 'Expecting `"` (end of literal string)')
605 # Replace escape sequences
608 for ec
in '0abefnrtv"\\':
609 val
= val
.replace(r
"\{}".format(ec
), self
._str
_escape
_seq
_strs
[ec
])
612 data
= val
.encode(encoding
)
615 return _Str(data
, begin_text_loc
)
617 # Patterns for _try_parse_group()
618 _group_prefix_pat
= re
.compile(r
"\(")
619 _group_suffix_pat
= re
.compile(r
"\)")
621 # Tries to parse a group, returning a group item on success.
622 def _try_parse_group(self
):
623 begin_text_loc
= self
._text
_loc
626 if self
._try
_parse
_pat
(self
._group
_prefix
_pat
) is None:
631 items
= self
._parse
_items
()
633 # Expect end of group
634 self
._skip
_ws
_and
_comments
()
636 self
._group
_suffix
_pat
, "Expecting an item or `)` (end of group)"
640 return _Group(items
, begin_text_loc
)
642 # Returns a stripped expression string and an AST expression node
643 # from the expression string `expr_str` at text location `text_loc`.
644 def _ast_expr_from_str(self
, expr_str
: str, text_loc
: TextLoc
):
645 # Create an expression node from the expression string
646 expr_str
= expr_str
.strip().replace("\n", " ")
649 expr
= ast
.parse(expr_str
, mode
="eval")
652 "Invalid expression `{}`: invalid syntax".format(expr_str
),
656 return expr_str
, expr
658 # Patterns for _try_parse_val_and_attr()
659 _val_expr_pat
= re
.compile(r
"([^}:]+):\s*")
660 _fl_int_len_attr_pat
= re
.compile(r
"8|16|24|32|40|48|56|64")
661 _leb128_int_attr_pat
= re
.compile(r
"(u|s)leb128")
663 # Tries to parse a value and attribute (fixed length in bits or
664 # `leb128`), returning a value item on success.
665 def _try_parse_val_and_attr(self
):
666 begin_text_loc
= self
._text
_loc
669 m_expr
= self
._try
_parse
_pat
(self
._val
_expr
_pat
)
675 # Create an expression node from the expression string
676 expr_str
, expr
= self
._ast
_expr
_from
_str
(m_expr
.group(1), begin_text_loc
)
679 m_attr
= self
._try
_parse
_pat
(self
._fl
_int
_len
_attr
_pat
)
683 m_attr
= self
._try
_parse
_pat
(self
._leb
128_int
_attr
_pat
)
686 # At this point it's invalid
688 "Expecting a length (multiple of eight bits), `uleb128`, or `sleb128`"
691 # Return LEB128 integer item
692 cls
= _ULeb128Int
if m_attr
.group(1) == "u" else _SLeb128Int
693 return cls(expr_str
, expr
, begin_text_loc
)
695 # Return fixed-length integer item
699 int(m_attr
.group(0)),
703 # Patterns for _try_parse_val_and_attr()
704 _var_assign_pat
= re
.compile(
705 r
"(?P<name>{})\s*=\s*(?P<expr>[^}}]+)".format(_py_name_pat
.pattern
)
708 # Tries to parse a variable assignment, returning a variable
709 # assignment item on success.
710 def _try_parse_var_assign(self
):
711 begin_text_loc
= self
._text
_loc
714 m
= self
._try
_parse
_pat
(self
._var
_assign
_pat
)
721 name
= m
.group("name")
723 if name
== _icitte_name
:
725 "`{}` is a reserved variable name".format(_icitte_name
), begin_text_loc
728 if name
in self
._label
_names
:
729 _raise_error("Existing label named `{}`".format(name
), begin_text_loc
)
731 # Add to known variable names
732 self
._var
_names
.add(name
)
734 # Create an expression node from the expression string
735 expr_str
, expr
= self
._ast
_expr
_from
_str
(m
.group("expr"), begin_text_loc
)
745 # Pattern for _try_parse_set_bo()
746 _bo_pat
= re
.compile(r
"[bl]e")
748 # Tries to parse a byte order name, returning a byte order setting
750 def _try_parse_set_bo(self
):
751 begin_text_loc
= self
._text
_loc
754 m
= self
._try
_parse
_pat
(self
._bo
_pat
)
760 # Return corresponding item
761 if m
.group(0) == "be":
762 return _SetBo(ByteOrder
.BE
, begin_text_loc
)
764 assert m
.group(0) == "le"
765 return _SetBo(ByteOrder
.LE
, begin_text_loc
)
767 # Patterns for _try_parse_val_or_bo()
768 _val_var_assign_set_bo_prefix_pat
= re
.compile(r
"\{\s*")
769 _val_var_assign_set_bo_suffix_pat
= re
.compile(r
"\s*}")
771 # Tries to parse a value, a variable assignment, or a byte order
772 # setting, returning an item on success.
773 def _try_parse_val_or_var_assign_or_set_bo(self
):
775 if self
._try
_parse
_pat
(self
._val
_var
_assign
_set
_bo
_prefix
_pat
) is None:
779 # Variable assignment item?
780 item
= self
._try
_parse
_var
_assign
()
783 # Fixed-length value item?
784 item
= self
._try
_parse
_val
_and
_attr
()
787 # Byte order setting item?
788 item
= self
._try
_parse
_set
_bo
()
791 # At this point it's invalid
793 "Expecting a fixed-length integer, a variable assignment, or a byte order setting"
797 self
._expect
_pat
(self
._val
_var
_assign
_set
_bo
_suffix
_pat
, "Expecting `}`")
800 # Pattern for _try_parse_set_offset_val() and _try_parse_rep()
801 _pos_const_int_pat
= re
.compile(r
"0[Xx][A-Fa-f0-9]+|\d+")
803 # Tries to parse an offset setting value (after the initial `<`),
804 # returning an offset item on success.
805 def _try_parse_set_offset_val(self
):
806 begin_text_loc
= self
._text
_loc
809 m
= self
._try
_parse
_pat
(self
._pos
_const
_int
_pat
)
816 return _SetOffset(int(m
.group(0), 0), begin_text_loc
)
818 # Tries to parse a label name (after the initial `<`), returning a
819 # label item on success.
820 def _try_parse_label_name(self
):
821 begin_text_loc
= self
._text
_loc
824 m
= self
._try
_parse
_pat
(_py_name_pat
)
833 if name
== _icitte_name
:
835 "`{}` is a reserved label name".format(_icitte_name
), begin_text_loc
838 if name
in self
._label
_names
:
839 _raise_error("Duplicate label name `{}`".format(name
), begin_text_loc
)
841 if name
in self
._var
_names
:
842 _raise_error("Existing variable named `{}`".format(name
), begin_text_loc
)
844 # Add to known label names
845 self
._label
_names
.add(name
)
848 return _Label(name
, begin_text_loc
)
850 # Patterns for _try_parse_label_or_set_offset()
851 _label_set_offset_prefix_pat
= re
.compile(r
"<\s*")
852 _label_set_offset_suffix_pat
= re
.compile(r
"\s*>")
854 # Tries to parse a label or an offset setting, returning an item on
856 def _try_parse_label_or_set_offset(self
):
858 if self
._try
_parse
_pat
(self
._label
_set
_offset
_prefix
_pat
) is None:
862 # Offset setting item?
863 item
= self
._try
_parse
_set
_offset
_val
()
867 item
= self
._try
_parse
_label
_name
()
870 # At this point it's invalid
871 self
._raise
_error
("Expecting a label name or an offset setting value")
874 self
._expect
_pat
(self
._label
_set
_offset
_suffix
_pat
, "Expecting `>`")
877 # Tries to parse a base item (anything except a repetition),
878 # returning it on success.
879 def _try_parse_base_item(self
):
881 item
= self
._try
_parse
_byte
()
887 item
= self
._try
_parse
_str
()
892 # Value, variable assignment, or byte order setting item?
893 item
= self
._try
_parse
_val
_or
_var
_assign
_or
_set
_bo
()
898 # Label or offset setting item?
899 item
= self
._try
_parse
_label
_or
_set
_offset
()
905 item
= self
._try
_parse
_group
()
910 # Pattern for _try_parse_rep()
911 _rep_prefix_pat
= re
.compile(r
"\*\s*")
912 _rep_expr_prefix_pat
= re
.compile(r
"\{")
913 _rep_expr_pat
= re
.compile(r
"[^}p]+")
914 _rep_expr_suffix_pat
= re
.compile(r
"\}")
916 # Tries to parse a repetition, returning the expression string and
917 # AST expression node on success.
918 def _try_parse_rep(self
):
920 if self
._try
_parse
_pat
(self
._rep
_prefix
_pat
) is None:
924 # Expect and return a decimal multiplier
925 self
._skip
_ws
_and
_comments
()
928 m
= self
._try
_parse
_pat
(self
._pos
_const
_int
_pat
)
932 if self
._try
_parse
_pat
(self
._rep
_expr
_prefix
_pat
) is None:
933 # At this point it's invalid
934 self
._raise
_error
("Expecting a positive integral multiplier or `{`")
936 # Expect an expression
937 expr_str_loc
= self
._text
_loc
938 m
= self
._expect
_pat
(self
._rep
_expr
_pat
, "Expecting an expression")
939 expr_str
= self
._ast
_expr
_from
_str
(m
.group(0), expr_str_loc
)
942 self
._expect
_pat
(self
._rep
_expr
_suffix
_pat
, "Expecting `}`")
943 expr_str
= m
.group(0)
945 expr_str_loc
= self
._text
_loc
946 expr_str
= m
.group(0)
948 return self
._ast
_expr
_from
_str
(expr_str
, expr_str_loc
)
950 # Tries to parse an item, possibly followed by a repetition,
951 # returning `True` on success.
953 # Appends any parsed item to `items`.
954 def _try_append_item(self
, items
: List
[_Item
]):
955 self
._skip
_ws
_and
_comments
()
958 item
= self
._try
_parse
_base
_item
()
964 # Parse repetition if the base item is repeatable
965 if isinstance(item
, _RepableItem
):
966 self
._skip
_ws
_and
_comments
()
967 rep_text_loc
= self
._text
_loc
968 rep_ret
= self
._try
_parse
_rep
()
970 if rep_ret
is not None:
971 item
= _Rep(item
, rep_ret
[0], rep_ret
[1], rep_text_loc
)
976 # Parses and returns items, skipping whitespaces, insignificant
977 # symbols, and comments when allowed, and stopping at the first
979 def _parse_items(self
) -> List
[_Item
]:
980 items
= [] # type: List[_Item]
982 while self
._isnt
_done
():
984 if not self
._try
_append
_item
(items
):
985 # Unknown at this point
990 # Parses the whole Normand input, setting `self._res` to the main
991 # group item on success.
993 if len(self
._normand
.strip()) == 0:
994 # Special case to make sure there's something to consume
995 self
._res
= _Group([], self
._text
_loc
)
998 # Parse first level items
999 items
= self
._parse
_items
()
1001 # Make sure there's nothing left
1002 self
._skip
_ws
_and
_comments
()
1004 if self
._isnt
_done
():
1006 "Unexpected character `{}`".format(self
._normand
[self
._at
])
1009 # Set main group item
1010 self
._res
= _Group(items
, self
._text
_loc
)
1013 # The return type of parse().
1019 variables
: SymbolsT
,
1022 bo
: Optional
[ByteOrder
],
1024 self
= cls
.__new
__(cls
)
1025 self
._init
(data
, variables
, labels
, offset
, bo
)
1028 def __init__(self
, *args
, **kwargs
): # type: ignore
1029 raise NotImplementedError
1034 variables
: SymbolsT
,
1037 bo
: Optional
[ByteOrder
],
1040 self
._vars
= variables
1041 self
._labels
= labels
1042 self
._offset
= offset
1050 # Dictionary of updated variable names to their last computed value.
1052 def variables(self
):
1055 # Dictionary of updated main group label names to their computed
1066 # Updated byte order.
1068 def byte_order(self
):
1072 # Raises a parse error for the item `item`, creating it using the
1074 def _raise_error_for_item(msg
: str, item
: _Item
) -> NoReturn
:
1075 _raise_error(msg
, item
.text_loc
)
1078 # The `ICITTE` reserved name.
1079 _icitte_name
= "ICITTE"
1082 # Base node visitor.
1084 # Calls the _visit_name() method for each name node which isn't the name
1086 class _NodeVisitor(ast
.NodeVisitor
):
1088 self
._parent
_is
_call
= False
1090 def generic_visit(self
, node
: ast
.AST
):
1091 if type(node
) is ast
.Call
:
1092 self
._parent
_is
_call
= True
1093 elif type(node
) is ast
.Name
and not self
._parent
_is
_call
:
1094 self
._visit
_name
(node
.id)
1096 super().generic_visit(node
)
1097 self
._parent
_is
_call
= False
1100 def _visit_name(self
, name
: str):
1104 # Expression validator: validates that all the names within the
1105 # expression are allowed.
1106 class _ExprValidator(_NodeVisitor
):
1107 def __init__(self
, item
: _ExprItemT
, allowed_names
: Set
[str], icitte_allowed
: bool):
1110 self
._allowed
_names
= allowed_names
1111 self
._icitte
_allowed
= icitte_allowed
1113 def _visit_name(self
, name
: str):
1114 # Make sure the name refers to a known and reachable
1115 # variable/label name.
1116 if name
== _icitte_name
and not self
._icitte
_allowed
:
1118 "Illegal reserved name `{}` in expression `{}`".format(
1119 _icitte_name
, self
._item
.expr_str
1121 self
._item
.text_loc
,
1123 elif name
!= _icitte_name
and name
not in self
._allowed
_names
:
1124 msg
= "Illegal (unknown or unreachable) variable/label name `{}` in expression `{}`".format(
1125 name
, self
._item
.expr_str
1128 allowed_names
= self
._allowed
_names
.copy()
1130 if self
._icitte
_allowed
:
1131 allowed_names
.add(_icitte_name
)
1133 if len(allowed_names
) > 0:
1134 allowed_names_str
= ", ".join(
1135 sorted(["`{}`".format(name
) for name
in allowed_names
])
1137 msg
+= "; the legal names are {{{}}}".format(allowed_names_str
)
1141 self
._item
.text_loc
,
1145 # Expression visitor getting all the contained names.
1146 class _ExprNamesVisitor(_NodeVisitor
):
1148 self
._parent
_is
_call
= False
1149 self
._names
= set() # type: Set[str]
1155 def _visit_name(self
, name
: str):
1156 self
._names
.add(name
)
1163 variables
: SymbolsT
,
1166 bo
: Optional
[ByteOrder
],
1168 self
.variables
= variables
.copy()
1169 self
.labels
= labels
.copy()
1170 self
.offset
= offset
1174 # Generator of data and final state from a group item.
1176 # Generation happens in memory at construction time. After building, use
1177 # the `data`, `variables`, `labels`, `offset`, and `bo` properties to
1178 # get the resulting context.
1180 # The steps of generation are:
1182 # 1. Validate that each repetition and LEB128 integer expression uses
1183 # only reachable names and not `ICITTE`.
1185 # 2. Compute and keep the effective repetition count and LEB128 integer
1186 # value for each repetition and LEB128 integer instance.
1188 # 3. Generate bytes, updating the initial state as it goes which becomes
1189 # the final state after the operation.
1191 # During the generation, when handling a `_Rep` or `_Leb128Int` item,
1192 # we already have the effective repetition count or value of the
1195 # When handling a `_Group` item, first update the current labels with
1196 # all the immediate (not nested) labels, and then handle each
1197 # contained item. This gives contained item access to "future" outer
1198 # labels. Then remove the immediate labels from the state so that
1199 # outer items don't have access to inner labels.
1204 variables
: SymbolsT
,
1207 bo
: Optional
[ByteOrder
],
1209 self
._validate
_vl
_exprs
(group
, set(variables
.keys()), set(labels
.keys()))
1210 self
._vl
_instance
_vals
= self
._compute
_vl
_instance
_vals
(
1211 group
, _GenState(variables
, labels
, offset
, bo
)
1213 self
._gen
(group
, _GenState(variables
, labels
, offset
, bo
))
1220 # Updated variables.
1222 def variables(self
):
1223 return self
._final
_state
.variables
1225 # Updated main group labels.
1228 return self
._final
_state
.labels
1233 return self
._final
_state
.offset
1235 # Updated byte order.
1238 return self
._final
_state
.bo
1240 # Returns the set of used, non-called names within the AST
1241 # expression `expr`.
1243 def _names_of_expr(expr
: ast
.Expression
):
1244 visitor
= _ExprNamesVisitor()
1246 return visitor
.names
1248 # Validates that all the repetition and LEB128 integer expressions
1249 # within `group` don't refer, directly or indirectly, to subsequent
1252 # The strategy here is to keep a set of allowed label names, per
1253 # group, initialized to `allowed_label_names`, and a set of allowed
1254 # variable names initialized to `allowed_variable_names`.
1256 # Then, depending on the type of `item`:
1259 # Add its name to the local allowed label names: a label
1260 # occurring before a repetition, and not within a nested group,
1261 # is always reachable.
1264 # If all the names within its expression are allowed, then add
1265 # its name to the allowed variable names.
1267 # Otherwise, remove its name from the allowed variable names (if
1268 # it's in there): a variable which refers to an unreachable name
1269 # is unreachable itself.
1271 # `_Rep` and `_Leb128`:
1272 # Make sure all the names within its expression are allowed.
1275 # Call this function for each contained item with a _copy_ of
1276 # the current allowed label names and the same current allowed
1279 def _validate_vl_exprs(
1280 item
: _Item
, allowed_variable_names
: Set
[str], allowed_label_names
: Set
[str]
1282 if type(item
) is _Label
:
1283 allowed_label_names
.add(item
.name
)
1284 elif type(item
) is _VarAssign
:
1285 # Check if this variable name is allowed
1288 for name
in _Gen
._names
_of
_expr
(item
.expr
):
1290 allowed_label_names | allowed_variable_names | {_icitte_name}
1297 allowed_variable_names
.add(item
.name
)
1298 elif item
.name
in allowed_variable_names
:
1299 allowed_variable_names
.remove(item
.name
)
1300 elif isinstance(item
, _Leb128Int
):
1301 # Validate the expression (`ICITTE` allowed)
1303 item
, allowed_label_names | allowed_variable_names
, True
1305 elif type(item
) is _Rep
:
1306 # Validate the expression first (`ICITTE` not allowed)
1308 item
, allowed_label_names | allowed_variable_names
, False
1311 # Validate inner item
1312 _Gen
._validate
_vl
_exprs
(
1313 item
.item
, allowed_variable_names
, allowed_label_names
1315 elif type(item
) is _Group
:
1316 # Copy `allowed_label_names` so that this frame cannot
1317 # access the nested label names.
1318 group_allowed_label_names
= allowed_label_names
.copy()
1320 for subitem
in item
.items
:
1321 _Gen
._validate
_vl
_exprs
(
1322 subitem
, allowed_variable_names
, group_allowed_label_names
1325 # Evaluates the expression of `item` considering the current
1326 # generation state `state`.
1328 # If `allow_icitte` is `True`, then the `ICITTE` name is available
1329 # for the expression to evaluate.
1331 def _eval_item_expr(item
: _ExprItemT
, state
: _GenState
, allow_icitte
: bool):
1332 syms
= state
.labels
.copy()
1334 # Set the `ICITTE` name to the current offset, if any
1336 syms
[_icitte_name
] = state
.offset
1338 # Add the current variables
1339 syms
.update(state
.variables
)
1341 # Validate the node and its children
1342 _ExprValidator(item
, set(syms
.keys()), True).visit(item
.expr
)
1344 # Compile and evaluate expression node
1346 val
= eval(compile(item
.expr
, "", "eval"), None, syms
)
1347 except Exception as exc
:
1348 _raise_error_for_item(
1349 "Failed to evaluate expression `{}`: {}".format(item
.expr_str
, exc
),
1354 if type(val
) is not int:
1355 _raise_error_for_item(
1356 "Invalid expression `{}`: expecting result type `int`, not `{}`".format(
1357 item
.expr_str
, type(val
).__name
__
1364 # Returns the size, in bytes, required to encode the value `val`
1365 # with LEB128 (signed version if `is_signed` is `True`).
1367 def _leb128_size_for_val(val
: int, is_signed
: bool):
1369 # Equivalent upper bound.
1371 # For example, if `val` is -128, then the full integer for
1372 # this number of bits would be [-128, 127].
1375 # Number of bits (add one for the sign if needed)
1376 bits
= val
.bit_length() + int(is_signed
)
1381 # Seven bits per byte
1382 return math
.ceil(bits
/ 7)
1384 # Computes the effective value for each repetition and LEB128
1385 # integer instance, filling `instance_vals` (if not `None`) and
1386 # returning `instance_vals`.
1388 # At this point it must be known that, for a given variable-length
1389 # item, its expression only contains reachable names.
1391 # When handling a `_Rep` item, this function appends its effective
1392 # multiplier to `instance_vals` _before_ handling its repeated item.
1394 # When handling a `_VarAssign` item, this function only evaluates it
1395 # if all its names are reachable.
1397 def _compute_vl_instance_vals(
1398 item
: _Item
, state
: _GenState
, instance_vals
: Optional
[List
[int]] = None
1400 if instance_vals
is None:
1403 if isinstance(item
, _ScalarItem
):
1404 state
.offset
+= item
.size
1405 elif type(item
) is _Label
:
1406 state
.labels
[item
.name
] = state
.offset
1407 elif type(item
) is _VarAssign
:
1408 # Check if all the names are reachable
1411 for name
in _Gen
._names
_of
_expr
(item
.expr
):
1413 name
!= _icitte_name
1414 and name
not in state
.variables
1415 and name
not in state
.labels
1417 # A name is unknown: cannot evaluate
1422 # Evaluate the expression and keep the result
1423 state
.variables
[item
.name
] = _Gen
._eval
_item
_expr
(item
, state
, True)
1424 elif type(item
) is _SetOffset
:
1425 state
.offset
= item
.val
1426 elif isinstance(item
, _Leb128Int
):
1427 # Evaluate the expression
1428 val
= _Gen
._eval
_item
_expr
(item
, state
, True)
1431 if type(item
) is _ULeb128Int
and val
< 0:
1432 _raise_error_for_item(
1433 "Invalid expression `{}`: unexpected negative result {:,} for a ULEB128 encoding".format(
1439 # Add the evaluation result to the to variable-length item
1441 instance_vals
.append(val
)
1444 state
.offset
+= _Gen
._leb
128_size
_for
_val
(val
, type(item
) is _SLeb128Int
)
1445 elif type(item
) is _Rep
:
1446 # Evaluate the expression and keep the result
1447 val
= _Gen
._eval
_item
_expr
(item
, state
, False)
1451 _raise_error_for_item(
1452 "Invalid expression `{}`: unexpected negative result {:,}".format(
1458 # Add to repetition instance values
1459 instance_vals
.append(val
)
1461 # Process the repeated item `val` times
1462 for _
in range(val
):
1463 _Gen
._compute
_vl
_instance
_vals
(item
.item
, state
, instance_vals
)
1464 elif type(item
) is _Group
:
1465 prev_labels
= state
.labels
.copy()
1468 for subitem
in item
.items
:
1469 _Gen
._compute
_vl
_instance
_vals
(subitem
, state
, instance_vals
)
1471 state
.labels
= prev_labels
1473 return instance_vals
1475 def _zero_item_size(self
, item
: _Item
, next_vl_instance
: int):
1476 return 0, next_vl_instance
1478 def _scalar_item_size(self
, item
: _ScalarItem
, next_vl_instance
: int):
1479 return item
.size
, next_vl_instance
1481 def _leb128_int_item_size(self
, item
: _Leb128Int
, next_vl_instance
: int):
1482 # Get the value from `self._vl_instance_vals` _before_
1483 # incrementing `next_vl_instance` to honor the order of
1484 # _compute_vl_instance_vals().
1486 self
._leb
128_size
_for
_val
(
1487 self
._vl
_instance
_vals
[next_vl_instance
], type(item
) is _SLeb128Int
1489 next_vl_instance
+ 1,
1492 def _group_item_size(self
, item
: _Group
, next_vl_instance
: int):
1495 for subitem
in item
.items
:
1496 subitem_size
, next_vl_instance
= self
._item
_size
(subitem
, next_vl_instance
)
1497 size
+= subitem_size
1499 return size
, next_vl_instance
1501 def _rep_item_size(self
, item
: _Rep
, next_vl_instance
: int):
1502 # Get the value from `self._vl_instance_vals` _before_
1503 # incrementing `next_vl_instance` to honor the order of
1504 # _compute_vl_instance_vals().
1505 mul
= self
._vl
_instance
_vals
[next_vl_instance
]
1506 next_vl_instance
+= 1
1509 for _
in range(mul
):
1510 iter_size
, next_vl_instance
= self
._item
_size
(item
.item
, next_vl_instance
)
1513 return size
, next_vl_instance
1515 # Returns the size of `item` and the new next repetition instance.
1516 def _item_size(self
, item
: _Item
, next_vl_instance
: int):
1517 return self
._item
_size
_funcs
[type(item
)](item
, next_vl_instance
)
1519 # Handles the byte item `item`.
1520 def _handle_byte_item(self
, item
: _Byte
, state
: _GenState
, next_vl_instance
: int):
1521 self
._data
.append(item
.val
)
1522 state
.offset
+= item
.size
1523 return next_vl_instance
1525 # Handles the string item `item`.
1526 def _handle_str_item(self
, item
: _Str
, state
: _GenState
, next_vl_instance
: int):
1527 self
._data
+= item
.data
1528 state
.offset
+= item
.size
1529 return next_vl_instance
1531 # Handles the byte order setting item `item`.
1532 def _handle_set_bo_item(
1533 self
, item
: _SetBo
, state
: _GenState
, next_vl_instance
: int
1535 # Update current byte order
1537 return next_vl_instance
1539 # Handles the variable assignment item `item`.
1540 def _handle_var_assign_item(
1541 self
, item
: _VarAssign
, state
: _GenState
, next_vl_instance
: int
1544 state
.variables
[item
.name
] = self
._eval
_item
_expr
(item
, state
, True)
1545 return next_vl_instance
1547 # Handles the fixed-length integer item `item`.
1548 def _handle_fl_int_item(
1549 self
, item
: _FlInt
, state
: _GenState
, next_vl_instance
: int
1552 val
= self
._eval
_item
_expr
(item
, state
, True)
1555 if val
< -(2 ** (item
.len - 1)) or val
> 2**item
.len - 1:
1556 _raise_error_for_item(
1557 "Value {:,} is outside the {}-bit range when evaluating expression `{}` at byte offset {:,}".format(
1558 val
, item
.len, item
.expr_str
, state
.offset
1563 # Encode result on 64 bits (to extend the sign bit whatever the
1564 # value of `item.len`).
1565 if state
.bo
is None and item
.len > 8:
1566 _raise_error_for_item(
1567 "Current byte order isn't defined at first value (`{}`) to encode on more than 8 bits".format(
1575 ">" if state
.bo
in (None, ByteOrder
.BE
) else "<",
1576 "Q" if val
>= 0 else "q",
1581 # Keep only the requested length
1582 len_bytes
= item
.len // 8
1584 if state
.bo
in (None, ByteOrder
.BE
):
1585 # Big endian: keep last bytes
1586 data
= data
[-len_bytes
:]
1588 # Little endian: keep first bytes
1589 assert state
.bo
== ByteOrder
.LE
1590 data
= data
[:len_bytes
]
1592 # Append to current bytes and update offset
1594 state
.offset
+= len(data
)
1595 return next_vl_instance
1597 # Handles the LEB128 integer item `item`.
1598 def _handle_leb128_int_item(
1599 self
, item
: _Leb128Int
, state
: _GenState
, next_vl_instance
: int
1601 # Get the precomputed value
1602 val
= self
._vl
_instance
_vals
[next_vl_instance
]
1605 size
= self
._leb
128_size
_for
_val
(val
, type(item
) is _SLeb128Int
)
1608 for _
in range(size
):
1609 # Seven LSBs, MSB of the byte set (continue)
1610 self
._data
.append((val
& 0x7F) |
0x80)
1613 # Clear MSB of last byte (stop)
1614 self
._data
[-1] &= ~
0x80
1616 # Consumed this instance
1617 return next_vl_instance
+ 1
1619 # Handles the group item `item`, only removing the immediate labels
1620 # from `state.labels` if `remove_immediate_labels` is `True`.
1621 def _handle_group_item(
1625 next_vl_instance
: int,
1626 remove_immediate_labels
: bool = True,
1628 # Compute the values of the immediate (not nested) labels. Those
1629 # labels are reachable by any expression within the group.
1630 offset
= state
.offset
1631 immediate_label_names
= set() # type: Set[str]
1632 tmp_next_vl_instance
= next_vl_instance
1634 for subitem
in item
.items
:
1635 if type(subitem
) is _SetOffset
:
1637 offset
= subitem
.val
1638 elif type(subitem
) is _Label
:
1639 # New immediate label
1640 state
.labels
[subitem
.name
] = offset
1641 immediate_label_names
.add(subitem
.name
)
1643 subitem_size
, tmp_next_vl_instance
= self
._item
_size
(
1644 subitem
, tmp_next_vl_instance
1646 offset
+= subitem_size
1648 # Handle each item now with the actual state
1649 for subitem
in item
.items
:
1650 next_vl_instance
= self
._handle
_item
(subitem
, state
, next_vl_instance
)
1652 # Remove immediate labels if required so that outer items won't
1653 # reach inner labels.
1654 if remove_immediate_labels
:
1655 for name
in immediate_label_names
:
1656 del state
.labels
[name
]
1658 return next_vl_instance
1660 # Handles the repetition item `item`.
1661 def _handle_rep_item(self
, item
: _Rep
, state
: _GenState
, next_vl_instance
: int):
1662 # Get the precomputed repetition count
1663 mul
= self
._vl
_instance
_vals
[next_vl_instance
]
1665 # Consumed this instance
1666 next_vl_instance
+= 1
1668 for _
in range(mul
):
1669 next_vl_instance
= self
._handle
_item
(item
.item
, state
, next_vl_instance
)
1671 return next_vl_instance
1673 # Handles the offset setting item `item`.
1674 def _handle_set_offset_item(
1675 self
, item
: _SetOffset
, state
: _GenState
, next_vl_instance
: int
1677 state
.offset
= item
.val
1678 return next_vl_instance
1680 # Handles the label item `item`.
1681 def _handle_label_item(self
, item
: _Label
, state
: _GenState
, next_vl_instance
: int):
1682 return next_vl_instance
1684 # Handles the item `item`, returning the updated next repetition
1686 def _handle_item(self
, item
: _Item
, state
: _GenState
, next_vl_instance
: int):
1687 return self
._item
_handlers
[type(item
)](item
, state
, next_vl_instance
)
1689 # Generates the data (`self._data`) and final state
1690 # (`self._final_state`) from `group` and the initial state `state`.
1691 def _gen(self
, group
: _Group
, state
: _GenState
):
1693 self
._data
= bytearray()
1696 self
._item
_handlers
= {
1697 _Byte
: self
._handle
_byte
_item
,
1698 _FlInt
: self
._handle
_fl
_int
_item
,
1699 _Group
: self
._handle
_group
_item
,
1700 _Label
: self
._handle
_label
_item
,
1701 _Rep
: self
._handle
_rep
_item
,
1702 _SetBo
: self
._handle
_set
_bo
_item
,
1703 _SetOffset
: self
._handle
_set
_offset
_item
,
1704 _SLeb128Int
: self
._handle
_leb
128_int
_item
,
1705 _Str
: self
._handle
_str
_item
,
1706 _ULeb128Int
: self
._handle
_leb
128_int
_item
,
1707 _VarAssign
: self
._handle
_var
_assign
_item
,
1708 } # type: Dict[type, Callable[[Any, _GenState, int], int]]
1711 self
._item
_size
_funcs
= {
1712 _Byte
: self
._scalar
_item
_size
,
1713 _FlInt
: self
._scalar
_item
_size
,
1714 _Group
: self
._group
_item
_size
,
1715 _Label
: self
._zero
_item
_size
,
1716 _Rep
: self
._rep
_item
_size
,
1717 _SetBo
: self
._zero
_item
_size
,
1718 _SetOffset
: self
._zero
_item
_size
,
1719 _SLeb128Int
: self
._leb
128_int
_item
_size
,
1720 _Str
: self
._scalar
_item
_size
,
1721 _ULeb128Int
: self
._leb
128_int
_item
_size
,
1722 _VarAssign
: self
._zero
_item
_size
,
1723 } # type: Dict[type, Callable[[Any, int], Tuple[int, int]]]
1725 # Handle the group item, _not_ removing the immediate labels
1726 # because the `labels` property offers them.
1727 self
._handle
_group
_item
(group
, state
, 0, False)
1729 # This is actually the final state
1730 self
._final
_state
= state
1733 # Returns a `ParseResult` instance containing the bytes encoded by the
1734 # input string `normand`.
1736 # `init_variables` is a dictionary of initial variable names (valid
1737 # Python names) to integral values. A variable name must not be the
1738 # reserved name `ICITTE`.
1740 # `init_labels` is a dictionary of initial label names (valid Python
1741 # names) to integral values. A label name must not be the reserved name
1744 # `init_offset` is the initial offset.
1746 # `init_byte_order` is the initial byte order.
1748 # Raises `ParseError` on any parsing error.
1751 init_variables
: Optional
[SymbolsT
] = None,
1752 init_labels
: Optional
[SymbolsT
] = None,
1753 init_offset
: int = 0,
1754 init_byte_order
: Optional
[ByteOrder
] = None,
1756 if init_variables
is None:
1759 if init_labels
is None:
1763 _Parser(normand
, init_variables
, init_labels
).res
,
1769 return ParseResult
._create
( # pyright: ignore[reportPrivateUsage]
1770 gen
.data
, gen
.variables
, gen
.labels
, gen
.offset
, gen
.bo
1774 # Parses the command-line arguments.
1775 def _parse_cli_args():
1779 ap
= argparse
.ArgumentParser()
1786 help="initial offset (positive)",
1792 choices
=["be", "le"],
1794 help="initial byte order (`be` or `le`)",
1800 help="add an initial variable (may be repeated)",
1807 help="add an initial label (may be repeated)",
1810 "--version", action
="version", version
="Normand {}".format(__version__
)
1817 help="input path (none means standard input)",
1821 return ap
.parse_args()
1824 # Raises a command-line error with the message `msg`.
1825 def _raise_cli_error(msg
: str) -> NoReturn
:
1826 raise RuntimeError("Command-line error: {}".format(msg
))
1829 # Returns a dictionary of string to integers from the list of strings
1830 # `args` containing `NAME=VAL` entries.
1831 def _dict_from_arg(args
: Optional
[List
[str]]):
1832 d
= {} # type: Dict[str, int]
1838 m
= re
.match(r
"({})=(\d+)$".format(_py_name_pat
.pattern
), arg
)
1841 _raise_cli_error("Invalid assignment {}".format(arg
))
1843 d
[m
.group(1)] = int(m
.group(2))
1848 # CLI entry point without exception handling.
1853 args
= _parse_cli_args()
1856 if args
.path
is None:
1857 normand
= sys
.stdin
.read()
1859 with
open(args
.path
) as f
:
1862 # Variables and labels
1863 variables
= _dict_from_arg(args
.var
)
1864 labels
= _dict_from_arg(args
.label
)
1868 _raise_cli_error("Invalid negative offset {}")
1870 # Validate and set byte order
1871 bo
= None # type: Optional[ByteOrder]
1873 if args
.byte_order
is not None:
1874 if args
.byte_order
== "be":
1877 assert args
.byte_order
== "le"
1882 res
= parse(normand
, variables
, labels
, args
.offset
, bo
)
1883 except ParseError
as exc
:
1886 if args
.path
is not None:
1887 prefix
= "{}:".format(os
.path
.abspath(args
.path
))
1890 "{}{}:{} - {}".format(
1891 prefix
, exc
.text_loc
.line_no
, exc
.text_loc
.col_no
, str(exc
)
1896 sys
.stdout
.buffer.write(res
.data
)
1899 # Prints the exception message `msg` and exits with status 1.
1900 def _fail(msg
: str) -> NoReturn
:
1901 if not msg
.endswith("."):
1904 print(msg
, file=sys
.stderr
)
1912 except Exception as exc
:
1916 if __name__
== "__main__":