1#
2# Copyright (C) 2011 - 2013 Satoru SATOH <ssato @ redhat.com>
3# License: MIT
4#
5"""Misc parsers
6"""
7from __future__ import absolute_import
8
9import re
10
11
12INT_PATTERN = re.compile(r"^(\d|([1-9]\d+))$")
13BOOL_PATTERN = re.compile(r"^(true|false)$", re.I)
14STR_PATTERN = re.compile(r"^['\"](.*)['\"]$")
15
16
17def parse_single(str_):
18    """
19    Very simple parser to parse expressions represent some single values.
20
21    :param str_: a string to parse
22    :return: Int | Bool | String
23
24    >>> parse_single(None)
25    ''
26    >>> parse_single("0")
27    0
28    >>> parse_single("123")
29    123
30    >>> parse_single("True")
31    True
32    >>> parse_single("a string")
33    'a string'
34    >>> parse_single('"a string"')
35    'a string'
36    >>> parse_single("'a string'")
37    'a string'
38    >>> parse_single("0.1")
39    '0.1'
40    >>> parse_single("    a string contains extra whitespaces     ")
41    'a string contains extra whitespaces'
42    """
43    if str_ is None:
44        return ''
45
46    str_ = str_.strip()
47
48    if not str_:
49        return ''
50
51    if BOOL_PATTERN.match(str_) is not None:
52        return bool(str_)
53
54    if INT_PATTERN.match(str_) is not None:
55        return int(str_)
56
57    if STR_PATTERN.match(str_) is not None:
58        return str_[1:-1]
59
60    return str_
61
62
63def parse_list(str_, sep=","):
64    """
65    Simple parser to parse expressions reprensent some list values.
66
67    :param str_: a string to parse
68    :param sep: Char to separate items of list
69    :return: [Int | Bool | String]
70
71    >>> parse_list("")
72    []
73    >>> parse_list("1")
74    [1]
75    >>> parse_list("a,b")
76    ['a', 'b']
77    >>> parse_list("1,2")
78    [1, 2]
79    >>> parse_list("a,b,")
80    ['a', 'b']
81    """
82    return [parse_single(x) for x in str_.split(sep) if x]
83
84
85def attr_val_itr(str_, avs_sep=":", vs_sep=",", as_sep=";"):
86    """
87    Atrribute and value pair parser.
88
89    :param str_: String represents a list of pairs of attribute and value
90    :param avs_sep: char to separate attribute and values
91    :param vs_sep: char to separate values
92    :param as_sep: char to separate attributes
93    """
94    for rel in parse_list(str_, as_sep):
95        if avs_sep not in rel or rel.endswith(avs_sep):
96            continue
97
98        (_attr, _values) = parse_list(rel, avs_sep)
99
100        if vs_sep in str(_values):
101            _values = parse_list(_values, vs_sep)
102
103        if _values:
104            yield (_attr, _values)
105
106
107def parse_attrlist_0(str_, avs_sep=":", vs_sep=",", as_sep=";"):
108    """
109    Simple parser to parse expressions in the form of
110    [ATTR1:VAL0,VAL1,...;ATTR2:VAL0,VAL2,..].
111
112    :param str_: input string
113    :param avs_sep:  char to separate attribute and values
114    :param vs_sep:  char to separate values
115    :param as_sep:  char to separate attributes
116
117    :return:
118        a list of tuples of (key, value | [value])
119            where key = (Int | String | ...),
120            value = (Int | Bool | String | ...) | [Int | Bool | String | ...]
121
122    >>> parse_attrlist_0("a:1")
123    [('a', 1)]
124    >>> parse_attrlist_0("a:1;b:xyz")
125    [('a', 1), ('b', 'xyz')]
126    >>> parse_attrlist_0("requires:bash,zsh")
127    [('requires', ['bash', 'zsh'])]
128    >>> parse_attrlist_0("obsoletes:sysdata;conflicts:sysdata-old")
129    [('obsoletes', 'sysdata'), ('conflicts', 'sysdata-old')]
130    """
131    return [(a, vs) for a, vs in attr_val_itr(str_, avs_sep, vs_sep, as_sep)]
132
133
134def parse_attrlist(str_, avs_sep=":", vs_sep=",", as_sep=";"):
135    """
136    Simple parser to parse expressions in the form of
137    [ATTR1:VAL0,VAL1,...;ATTR2:VAL0,VAL2,..].
138
139    :param str_: input string
140    :param avs_sep:  char to separate attribute and values
141    :param vs_sep:  char to separate values
142    :param as_sep:  char to separate attributes
143
144    >>> parse_attrlist("requires:bash,zsh")
145    {'requires': ['bash', 'zsh']}
146    """
147    return dict(parse_attrlist_0(str_, avs_sep, vs_sep, as_sep))
148
149
150def parse(str_, lsep=",", avsep=":", vssep=",", avssep=";"):
151    """Generic parser"""
152    if avsep in str_:
153        return parse_attrlist(str_, avsep, vssep, avssep)
154    if lsep in str_:
155        return parse_list(str_, lsep)
156
157    return parse_single(str_)
158
159# vim:sw=4:ts=4:et:
160