Source code for mau.parsers.arguments_parser
from copy import deepcopy
from mau.lexers.base_lexer import Token, TokenTypes
from mau.lexers.arguments_lexer import ArgumentsLexer
from mau.parsers.base_parser import BaseParser, ParserError
# The ArgumentsParser is used to parse
# unnamed and named arguments passed to macros.
# It parses a string into a list of unnamed
# arguments and a dictionary of named ones.
[docs]class ArgumentsParser(BaseParser):
def __init__(self):
super().__init__()
self.lexer = ArgumentsLexer()
# This flag is turned on as soon as
# a named argument is parsed
self._named_arguments = False
self.kwargs = {}
self.args = []
[docs] def set_names_and_defaults(self, positional_names, default_values=None):
"""
Gives names to positional arguments and assigns
default values to the ones that have not been
initialised.
"""
if default_values:
_default_values = default_values.copy()
else:
_default_values = {}
# This happens if we passed too many positional
# values. The case where we pass less positional
# values than required is checked later when named
# arguments are merged.
if len(self.args) > len(positional_names):
self.error(
f"Error while parsing arguments {self.args} through names {positional_names}"
)
positional_arguments = dict(zip(positional_names, self.args))
# Named arguments win over the defaults
_default_values.update(self.kwargs)
# Positional arguments with win over all the rest
_default_values.update(positional_arguments)
# Positional arguments are mandatory and strict
# so all the names have to be present in the
# final dictionary.
if not set(positional_names).issubset(set(_default_values.keys())):
self.error(
f"The following attributes need to be specified: {positional_names}"
)
self.kwargs = _default_values
self.args = []
[docs] def pop(self):
"""
Return the first unnamed argument,
removing it from the list.
If no arguments are available it
returns None.
"""
try:
return self.args.pop(0)
except IndexError:
return None
[docs] def get_arguments_and_reset(self):
args = deepcopy(self.args)
kwargs = deepcopy(self.kwargs)
self.args = []
self.kwargs = {}
self._named_arguments = False
return args, kwargs
def _parse_named_argument(self):
# This parses a named argument
# in the form name=value or name="value"
name = self.get_token(TokenTypes.TEXT).value
self.get_token(TokenTypes.LITERAL, "=")
# Values can be surrounded by quotes
if self.peek_token_is(TokenTypes.LITERAL, '"'):
self.get_token(TokenTypes.LITERAL, '"')
value = self.collect_join([Token(TokenTypes.LITERAL, '"')])
self.get_token(TokenTypes.LITERAL, '"')
else:
value = self.collect_join(
[Token(TokenTypes.LITERAL, ","), Token(TokenTypes.EOF)]
)
return name, value
def _parse_unnamed_argument(self):
# This parses a named argument
# in the form value or "value"
# Values can be surrounded by quotes
if self.peek_token_is(TokenTypes.LITERAL, '"'):
self.get_token(TokenTypes.LITERAL, '"')
value = self.collect_join([Token(TokenTypes.LITERAL, '"')])
self.get_token(TokenTypes.LITERAL, '"')
else:
value = self.collect_join(
[Token(TokenTypes.LITERAL, ","), Token(TokenTypes.EOF)]
)
return value
def _parse_single_argument(self):
# This parses a single argument, named or unnamed
with self:
name, value = self._parse_named_argument()
self.kwargs[name] = value
self._named_arguments = True
return
with self:
value = self._parse_unnamed_argument()
if self._named_arguments:
raise ParserError(
"Unnamed arguments after named arguments are forbidden"
)
self.args.append(value)
return
def _parse_arguments(self):
# Just run until you can't find any more commas
while True:
self._parse_single_argument()
with self:
self.get_token(TokenTypes.LITERAL, ",")
with self:
self.get_token(TokenTypes.WHITESPACE)
continue
break
[docs] def parse(self):
self._parse_arguments()