68 lines
2.4 KiB
Python
68 lines
2.4 KiB
Python
from typing import List, Tuple, Union
|
|
|
|
import pyparsing as pp
|
|
|
|
CommandTokenType = Tuple[str, List[str]]
|
|
TokenType = Union[str, CommandTokenType]
|
|
TokenList = List[TokenType]
|
|
|
|
|
|
class ListParser(object):
|
|
_parser: pp.ParserElement
|
|
|
|
def __init__(self) -> None:
|
|
newline = "\n"
|
|
space_plus = pp.Regex("[ \t]+")
|
|
space_star = pp.Optional(space_plus)
|
|
|
|
quoted_element = pp.Regex(r'[^\\"]|\\[^A-Za-z0-9]|\\[trn]')
|
|
quoted_argument = pp.Combine('"' + pp.ZeroOrMore(quoted_element) + '"')
|
|
|
|
bracket_content = pp.Forward()
|
|
|
|
def action_bracket_open(tokens: pp.ParseResults) -> None:
|
|
nonlocal bracket_content
|
|
marker = "]" + "=" * (len(tokens[0]) - 2) + "]"
|
|
bracket_content <<= pp.SkipTo(marker, include=True)
|
|
|
|
bracket_open = pp.Regex(r"\[=*\[").setParseAction(action_bracket_open)
|
|
bracket_argument = pp.Combine(bracket_open + bracket_content)
|
|
|
|
unquoted_element = pp.Regex(r'[^\s()#"\\]|\\[^A-Za-z0-9]|\\[trn]')
|
|
unquoted_argument = pp.Combine(pp.OneOrMore(unquoted_element))
|
|
|
|
argument = bracket_argument | quoted_argument | unquoted_argument
|
|
|
|
line_comment = pp.Combine("#" + ~bracket_open + pp.SkipTo(pp.LineEnd()))
|
|
bracket_comment = pp.Combine("#" + bracket_argument)
|
|
line_ending = (
|
|
space_star
|
|
+ pp.ZeroOrMore(bracket_comment + space_star)
|
|
+ pp.Optional(line_comment)
|
|
+ (newline | pp.lineEnd)
|
|
)
|
|
|
|
identifier = pp.Word(pp.alphas + "_", pp.alphanums + "_")
|
|
arguments = pp.Forward()
|
|
arguments << pp.ZeroOrMore(
|
|
argument | line_ending | space_plus | "(" + arguments + ")"
|
|
).leaveWhitespace()
|
|
arguments = pp.Group(arguments)
|
|
PAREN_L, PAREN_R = map(pp.Suppress, "()")
|
|
command_invocation = (
|
|
identifier + space_star.suppress() + PAREN_L + arguments + PAREN_R
|
|
).setParseAction(lambda t: (t[0], t[1].asList()))
|
|
|
|
file_element = (
|
|
space_star + command_invocation + line_ending | line_ending
|
|
).leaveWhitespace()
|
|
file = pp.ZeroOrMore(file_element)
|
|
|
|
self._parser = file
|
|
|
|
def parse(self, liststr: str) -> Tuple[TokenList, str]:
|
|
for t, s, e in self._parser.scanString(liststr, maxMatches=1):
|
|
if s == 0:
|
|
return t.asList(), liststr[e:]
|
|
return [], liststr
|