-
-
Save NaPs/ed4b5152a707b0ad2696 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Created by PLY version 3.4 (http://www.dabeaz.com/ply) | |
Grammar | |
Rule 0 S' -> top | |
Rule 1 top -> section_content | |
Rule 2 assignation -> NAME ASSIGN value | |
Rule 3 assignation -> NAME ASSIGN list | |
Rule 4 value -> TEXT | |
Rule 5 value -> YES | |
Rule 6 value -> NO | |
Rule 7 value -> NUMBER | |
Rule 8 list -> value LIST_SEP EOL list_next | |
Rule 9 list -> value LIST_SEP list_next | |
Rule 10 list -> value LIST_SEP | |
Rule 11 list_next -> value LIST_SEP EOL list_next | |
Rule 12 list_next -> value LIST_SEP list_next | |
Rule 13 list_next -> value LIST_SEP | |
Rule 14 list_next -> value | |
Rule 15 section_content -> section_content EOL | |
Rule 16 section_content -> empty | |
Rule 17 section_content -> section_content assignation | |
Rule 18 section_content -> section_content section | |
Rule 19 section -> NAME LBRACE section_content RBRACE | |
Rule 20 empty -> <empty> | |
Terminals, with rules where they appear | |
ASSIGN : 2 3 | |
EOL : 8 11 15 | |
LBRACE : 19 | |
LIST_SEP : 8 9 10 11 12 13 | |
NAME : 2 3 19 | |
NO : 6 | |
NUMBER : 7 | |
RBRACE : 19 | |
TEXT : 4 | |
YES : 5 | |
error : | |
Nonterminals, with rules where they appear | |
assignation : 17 | |
empty : 16 | |
list : 3 | |
list_next : 8 9 11 12 | |
section : 18 | |
section_content : 1 15 17 18 19 | |
top : 0 | |
value : 2 8 9 10 11 12 13 14 | |
Parsing method: LALR | |
state 0 | |
(0) S' -> . top | |
(1) top -> . section_content | |
(15) section_content -> . section_content EOL | |
(16) section_content -> . empty | |
(17) section_content -> . section_content assignation | |
(18) section_content -> . section_content section | |
(20) empty -> . | |
EOL reduce using rule 20 (empty -> .) | |
NAME reduce using rule 20 (empty -> .) | |
$end reduce using rule 20 (empty -> .) | |
section_content shift and go to state 1 | |
top shift and go to state 2 | |
empty shift and go to state 3 | |
state 1 | |
(1) top -> section_content . | |
(15) section_content -> section_content . EOL | |
(17) section_content -> section_content . assignation | |
(18) section_content -> section_content . section | |
(2) assignation -> . NAME ASSIGN value | |
(3) assignation -> . NAME ASSIGN list | |
(19) section -> . NAME LBRACE section_content RBRACE | |
$end reduce using rule 1 (top -> section_content .) | |
EOL shift and go to state 6 | |
NAME shift and go to state 4 | |
section shift and go to state 5 | |
assignation shift and go to state 7 | |
state 2 | |
(0) S' -> top . | |
state 3 | |
(16) section_content -> empty . | |
EOL reduce using rule 16 (section_content -> empty .) | |
NAME reduce using rule 16 (section_content -> empty .) | |
$end reduce using rule 16 (section_content -> empty .) | |
RBRACE reduce using rule 16 (section_content -> empty .) | |
state 4 | |
(2) assignation -> NAME . ASSIGN value | |
(3) assignation -> NAME . ASSIGN list | |
(19) section -> NAME . LBRACE section_content RBRACE | |
ASSIGN shift and go to state 9 | |
LBRACE shift and go to state 8 | |
state 5 | |
(18) section_content -> section_content section . | |
EOL reduce using rule 18 (section_content -> section_content section .) | |
NAME reduce using rule 18 (section_content -> section_content section .) | |
$end reduce using rule 18 (section_content -> section_content section .) | |
RBRACE reduce using rule 18 (section_content -> section_content section .) | |
state 6 | |
(15) section_content -> section_content EOL . | |
EOL reduce using rule 15 (section_content -> section_content EOL .) | |
NAME reduce using rule 15 (section_content -> section_content EOL .) | |
$end reduce using rule 15 (section_content -> section_content EOL .) | |
RBRACE reduce using rule 15 (section_content -> section_content EOL .) | |
state 7 | |
(17) section_content -> section_content assignation . | |
EOL reduce using rule 17 (section_content -> section_content assignation .) | |
NAME reduce using rule 17 (section_content -> section_content assignation .) | |
$end reduce using rule 17 (section_content -> section_content assignation .) | |
RBRACE reduce using rule 17 (section_content -> section_content assignation .) | |
state 8 | |
(19) section -> NAME LBRACE . section_content RBRACE | |
(15) section_content -> . section_content EOL | |
(16) section_content -> . empty | |
(17) section_content -> . section_content assignation | |
(18) section_content -> . section_content section | |
(20) empty -> . | |
RBRACE reduce using rule 20 (empty -> .) | |
EOL reduce using rule 20 (empty -> .) | |
NAME reduce using rule 20 (empty -> .) | |
section_content shift and go to state 10 | |
empty shift and go to state 3 | |
state 9 | |
(2) assignation -> NAME ASSIGN . value | |
(3) assignation -> NAME ASSIGN . list | |
(4) value -> . TEXT | |
(5) value -> . YES | |
(6) value -> . NO | |
(7) value -> . NUMBER | |
(8) list -> . value LIST_SEP EOL list_next | |
(9) list -> . value LIST_SEP list_next | |
(10) list -> . value LIST_SEP | |
TEXT shift and go to state 12 | |
YES shift and go to state 16 | |
NO shift and go to state 11 | |
NUMBER shift and go to state 14 | |
list shift and go to state 13 | |
value shift and go to state 15 | |
state 10 | |
(19) section -> NAME LBRACE section_content . RBRACE | |
(15) section_content -> section_content . EOL | |
(17) section_content -> section_content . assignation | |
(18) section_content -> section_content . section | |
(2) assignation -> . NAME ASSIGN value | |
(3) assignation -> . NAME ASSIGN list | |
(19) section -> . NAME LBRACE section_content RBRACE | |
RBRACE shift and go to state 17 | |
EOL shift and go to state 6 | |
NAME shift and go to state 4 | |
section shift and go to state 5 | |
assignation shift and go to state 7 | |
state 11 | |
(6) value -> NO . | |
LIST_SEP reduce using rule 6 (value -> NO .) | |
EOL reduce using rule 6 (value -> NO .) | |
NAME reduce using rule 6 (value -> NO .) | |
$end reduce using rule 6 (value -> NO .) | |
RBRACE reduce using rule 6 (value -> NO .) | |
state 12 | |
(4) value -> TEXT . | |
LIST_SEP reduce using rule 4 (value -> TEXT .) | |
EOL reduce using rule 4 (value -> TEXT .) | |
NAME reduce using rule 4 (value -> TEXT .) | |
$end reduce using rule 4 (value -> TEXT .) | |
RBRACE reduce using rule 4 (value -> TEXT .) | |
state 13 | |
(3) assignation -> NAME ASSIGN list . | |
RBRACE reduce using rule 3 (assignation -> NAME ASSIGN list .) | |
EOL reduce using rule 3 (assignation -> NAME ASSIGN list .) | |
NAME reduce using rule 3 (assignation -> NAME ASSIGN list .) | |
$end reduce using rule 3 (assignation -> NAME ASSIGN list .) | |
state 14 | |
(7) value -> NUMBER . | |
LIST_SEP reduce using rule 7 (value -> NUMBER .) | |
EOL reduce using rule 7 (value -> NUMBER .) | |
NAME reduce using rule 7 (value -> NUMBER .) | |
$end reduce using rule 7 (value -> NUMBER .) | |
RBRACE reduce using rule 7 (value -> NUMBER .) | |
state 15 | |
(2) assignation -> NAME ASSIGN value . | |
(8) list -> value . LIST_SEP EOL list_next | |
(9) list -> value . LIST_SEP list_next | |
(10) list -> value . LIST_SEP | |
RBRACE reduce using rule 2 (assignation -> NAME ASSIGN value .) | |
EOL reduce using rule 2 (assignation -> NAME ASSIGN value .) | |
NAME reduce using rule 2 (assignation -> NAME ASSIGN value .) | |
$end reduce using rule 2 (assignation -> NAME ASSIGN value .) | |
LIST_SEP shift and go to state 18 | |
state 16 | |
(5) value -> YES . | |
LIST_SEP reduce using rule 5 (value -> YES .) | |
EOL reduce using rule 5 (value -> YES .) | |
NAME reduce using rule 5 (value -> YES .) | |
$end reduce using rule 5 (value -> YES .) | |
RBRACE reduce using rule 5 (value -> YES .) | |
state 17 | |
(19) section -> NAME LBRACE section_content RBRACE . | |
RBRACE reduce using rule 19 (section -> NAME LBRACE section_content RBRACE .) | |
EOL reduce using rule 19 (section -> NAME LBRACE section_content RBRACE .) | |
NAME reduce using rule 19 (section -> NAME LBRACE section_content RBRACE .) | |
$end reduce using rule 19 (section -> NAME LBRACE section_content RBRACE .) | |
state 18 | |
(8) list -> value LIST_SEP . EOL list_next | |
(9) list -> value LIST_SEP . list_next | |
(10) list -> value LIST_SEP . | |
(11) list_next -> . value LIST_SEP EOL list_next | |
(12) list_next -> . value LIST_SEP list_next | |
(13) list_next -> . value LIST_SEP | |
(14) list_next -> . value | |
(4) value -> . TEXT | |
(5) value -> . YES | |
(6) value -> . NO | |
(7) value -> . NUMBER | |
! shift/reduce conflict for EOL resolved as shift | |
EOL shift and go to state 20 | |
NAME reduce using rule 10 (list -> value LIST_SEP .) | |
$end reduce using rule 10 (list -> value LIST_SEP .) | |
RBRACE reduce using rule 10 (list -> value LIST_SEP .) | |
TEXT shift and go to state 12 | |
YES shift and go to state 16 | |
NO shift and go to state 11 | |
NUMBER shift and go to state 14 | |
! EOL [ reduce using rule 10 (list -> value LIST_SEP .) ] | |
list_next shift and go to state 21 | |
value shift and go to state 19 | |
state 19 | |
(11) list_next -> value . LIST_SEP EOL list_next | |
(12) list_next -> value . LIST_SEP list_next | |
(13) list_next -> value . LIST_SEP | |
(14) list_next -> value . | |
LIST_SEP shift and go to state 22 | |
EOL reduce using rule 14 (list_next -> value .) | |
NAME reduce using rule 14 (list_next -> value .) | |
$end reduce using rule 14 (list_next -> value .) | |
RBRACE reduce using rule 14 (list_next -> value .) | |
state 20 | |
(8) list -> value LIST_SEP EOL . list_next | |
(11) list_next -> . value LIST_SEP EOL list_next | |
(12) list_next -> . value LIST_SEP list_next | |
(13) list_next -> . value LIST_SEP | |
(14) list_next -> . value | |
(4) value -> . TEXT | |
(5) value -> . YES | |
(6) value -> . NO | |
(7) value -> . NUMBER | |
TEXT shift and go to state 12 | |
YES shift and go to state 16 | |
NO shift and go to state 11 | |
NUMBER shift and go to state 14 | |
list_next shift and go to state 23 | |
value shift and go to state 19 | |
state 21 | |
(9) list -> value LIST_SEP list_next . | |
EOL reduce using rule 9 (list -> value LIST_SEP list_next .) | |
NAME reduce using rule 9 (list -> value LIST_SEP list_next .) | |
$end reduce using rule 9 (list -> value LIST_SEP list_next .) | |
RBRACE reduce using rule 9 (list -> value LIST_SEP list_next .) | |
state 22 | |
(11) list_next -> value LIST_SEP . EOL list_next | |
(12) list_next -> value LIST_SEP . list_next | |
(13) list_next -> value LIST_SEP . | |
(11) list_next -> . value LIST_SEP EOL list_next | |
(12) list_next -> . value LIST_SEP list_next | |
(13) list_next -> . value LIST_SEP | |
(14) list_next -> . value | |
(4) value -> . TEXT | |
(5) value -> . YES | |
(6) value -> . NO | |
(7) value -> . NUMBER | |
! shift/reduce conflict for EOL resolved as shift | |
EOL shift and go to state 24 | |
NAME reduce using rule 13 (list_next -> value LIST_SEP .) | |
$end reduce using rule 13 (list_next -> value LIST_SEP .) | |
RBRACE reduce using rule 13 (list_next -> value LIST_SEP .) | |
TEXT shift and go to state 12 | |
YES shift and go to state 16 | |
NO shift and go to state 11 | |
NUMBER shift and go to state 14 | |
! EOL [ reduce using rule 13 (list_next -> value LIST_SEP .) ] | |
list_next shift and go to state 25 | |
value shift and go to state 19 | |
state 23 | |
(8) list -> value LIST_SEP EOL list_next . | |
EOL reduce using rule 8 (list -> value LIST_SEP EOL list_next .) | |
NAME reduce using rule 8 (list -> value LIST_SEP EOL list_next .) | |
$end reduce using rule 8 (list -> value LIST_SEP EOL list_next .) | |
RBRACE reduce using rule 8 (list -> value LIST_SEP EOL list_next .) | |
state 24 | |
(11) list_next -> value LIST_SEP EOL . list_next | |
(11) list_next -> . value LIST_SEP EOL list_next | |
(12) list_next -> . value LIST_SEP list_next | |
(13) list_next -> . value LIST_SEP | |
(14) list_next -> . value | |
(4) value -> . TEXT | |
(5) value -> . YES | |
(6) value -> . NO | |
(7) value -> . NUMBER | |
TEXT shift and go to state 12 | |
YES shift and go to state 16 | |
NO shift and go to state 11 | |
NUMBER shift and go to state 14 | |
list_next shift and go to state 26 | |
value shift and go to state 19 | |
state 25 | |
(12) list_next -> value LIST_SEP list_next . | |
EOL reduce using rule 12 (list_next -> value LIST_SEP list_next .) | |
NAME reduce using rule 12 (list_next -> value LIST_SEP list_next .) | |
$end reduce using rule 12 (list_next -> value LIST_SEP list_next .) | |
RBRACE reduce using rule 12 (list_next -> value LIST_SEP list_next .) | |
state 26 | |
(11) list_next -> value LIST_SEP EOL list_next . | |
EOL reduce using rule 11 (list_next -> value LIST_SEP EOL list_next .) | |
NAME reduce using rule 11 (list_next -> value LIST_SEP EOL list_next .) | |
$end reduce using rule 11 (list_next -> value LIST_SEP EOL list_next .) | |
RBRACE reduce using rule 11 (list_next -> value LIST_SEP EOL list_next .) | |
WARNING: | |
WARNING: Conflicts: | |
WARNING: | |
WARNING: shift/reduce conflict for EOL in state 18 resolved as shift | |
WARNING: shift/reduce conflict for EOL in state 22 resolved as shift |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" Dotconf configuration parser. | |
""" | |
import ply.lex as lex | |
import ply.yacc as yacc | |
from dotconf.section import Section | |
# | |
# Lexer | |
# | |
class DotconfLexer(object): | |
""" Lexer for the DotConf format. | |
:param **kwargs: arguments to give to the ply lexer | |
Usage example:: | |
>>> lexer = DotConfLexer() | |
>>> lexer.input('test { key = yes }') | |
>>> print lexer.next() | |
""" | |
def __init__(self, **kwargs): | |
self._lexer = lex.lex(module=self, **kwargs) | |
# | |
# Tokens definition | |
# | |
reserved = {'yes': 'YES', 'no': 'NO'} | |
tokens = ['LBRACE', 'RBRACE', 'NAME', 'TEXT', 'NUMBER', | |
'ASSIGN', 'EOL', 'LIST_SEP'] + reserved.values() | |
t_LBRACE = '{' | |
t_RBRACE = '}' | |
t_ASSIGN = '=' | |
t_LIST_SEP = ',' | |
t_ignore = ' \t' | |
def t_NAME(self, token): | |
r'[a-zA-Z_][a-zA-Z0-9_-]*' | |
token.type = self.reserved.get(token.value, 'NAME') | |
# Handle the boolean case: | |
if token.type == 'YES': | |
token.value = True | |
elif token.type == 'NO': | |
token.value = False | |
return token | |
def t_TEXT(self, token): | |
r'(["]([\\]["]|[^"]|)*["]|[\']([\\][\']|[^\'])*[\'])' | |
value = token.value[1:-1].replace('\\' + token.value[0], token.value[0]) | |
token.value = value | |
# Count the lines in the string: | |
token.lexer.lineno += value.count('\n') | |
return token | |
def t_NUMBER(self, token): | |
r'[-+]?[0-9]+(\.[0-9]+)?' | |
if token.value.isdigit(): | |
token.value = int(token.value) | |
else: | |
token.value = float(token.value) | |
return token | |
def t_EOL(self, token): | |
r'[\n]+' | |
token.lexer.lineno += len(token.value) | |
return token | |
# | |
# Bindings to the internal _lexer object | |
# | |
def __getattr__(self, name): | |
attr = getattr(self._lexer, name) | |
if attr is None: | |
raise AttributeError("'%s' object has no attribute '%s'" % (self, name)) | |
else: | |
return attr | |
# | |
# Parser | |
# | |
class DotconfParser(object): | |
""" Parser for the Dotconf format. | |
""" | |
tokens = DotconfLexer.tokens | |
def __init__(self, **kwargs): | |
self._parser = yacc.yacc(module=self, **kwargs) | |
# | |
# Rules | |
# | |
start = 'top' | |
def p_top(self, p): | |
"""top : section_content""" | |
section = Section('__top__') | |
for name, child, is_section in p[1]: | |
if is_section: | |
child.parent = section | |
section.register_child(name, child) | |
p[0] = section | |
def p_assignation(self, p): | |
"""assignation : NAME ASSIGN value | |
| NAME ASSIGN list""" | |
p[0] = {'name': p[1], | |
'value': p[3]} | |
def p_value(self, p): | |
"""value : TEXT | |
| YES | |
| NO | |
| NUMBER""" | |
p[0] = p[1] | |
def p_list_multiline(self, p): | |
"""list : value LIST_SEP EOL list_next""" | |
p[4].insert(0, p[1]) | |
p[0] = p[4] | |
def p_list(self, p): | |
"""list : value LIST_SEP list_next""" | |
p[3].insert(0, p[1]) | |
p[0] = p[3] | |
def p_list_singleton(self, p): | |
"""list : value LIST_SEP""" | |
p[0] = [p[1]] | |
def p_list_next_multiline(self, p): | |
"""list_next : value LIST_SEP EOL list_next""" | |
p[4].insert(0, p[1]) | |
p[0] = p[4] | |
def p_list_next(self, p): | |
"""list_next : value LIST_SEP list_next""" | |
p[3].insert(0, p[1]) | |
p[0] = p[3] | |
def p_list_next_last(self, p): | |
"""list_next : value LIST_SEP | |
| value""" | |
p[0] = [p[1]] | |
def p_section_content(self, p): | |
"""section_content : section_content EOL""" | |
p[0] = p[1] | |
def p_section_content_empty(self, p): | |
"""section_content : empty""" | |
p[0] = [] | |
def p_section_content_assignation(self, p): | |
"""section_content : section_content assignation""" | |
p[1].append((p[2]['name'], p[2]['value'], False)) | |
p[0] = p[1] | |
def p_section_content_section(self, p): | |
"""section_content : section_content section""" | |
p[1].append((p[2].name, p[2], True)) | |
p[0] = p[1] | |
def p_section(self, p): | |
"""section : NAME LBRACE section_content RBRACE""" | |
section = Section(p[1]) | |
for name, child, is_section in p[3]: | |
if is_section: | |
child.parent = section | |
section.register_child(name, child) | |
p[0] = section | |
def p_empty(self, p): | |
"""empty :""" | |
pass | |
#def p_error(self, p): | |
# print p | |
# | |
# Bindings to the internel _parser object | |
# | |
def __getattr__(self, name): | |
attr = getattr(self._parser, name) | |
if attr is None: | |
raise AttributeError("'%s' object has no attribute '%s'" % (self, name)) | |
else: | |
return attr | |
# | |
# Tests | |
# | |
from nose.tools import eq_ | |
def test_lexer(): | |
# Test string Expected tok Expected tok value | |
tokens = (('name', 'NAME', 'name'), | |
('"test"', 'TEXT', 'test'), | |
("'test'", 'TEXT', 'test'), | |
(r"'te\'st'", 'TEXT', "te'st"), | |
('42', 'NUMBER', 42), | |
('42.1', 'NUMBER', 42.1), | |
('+42', 'NUMBER', 42), | |
('+42.1', 'NUMBER', 42.1), | |
('-42', 'NUMBER', -42), | |
('-42.1', 'NUMBER', -42.1), | |
('{', 'LBRACE', '{'), | |
('}', 'RBRACE', '}'), | |
('=', 'ASSIGN', '=')) | |
for test, expected_type, expected_value in tokens: | |
yield check_token, test, expected_type, expected_value | |
def check_token(test, expected_type, expected_value): | |
lexer = DotconfLexer() | |
lexer.input(test) | |
token = lexer.next() | |
eq_(token.type, expected_type) | |
eq_(token.value, expected_value) | |
def test_parser(): | |
test = ''' | |
daemon = yes | |
lol = 42 | |
list = 1 | |
list2_ = 1,2,3,4 | |
test { | |
tust = 42 | |
section { | |
test = "lol :D | |
mdr | |
" | |
} | |
section { | |
lul = no | |
} | |
} | |
''' | |
lexer = DotconfLexer() | |
#lexer.input(test) | |
#list(lexer._lexer) | |
parser = DotconfParser(debug=True, write_tables=False, errorlog=yacc.NullLogger()) | |
output = parser.parse(test, lexer=lexer, tracking=True) | |
import pprint | |
pprint.pprint(output) | |
return output | |
#eq_(output, 'test') | |
if __name__ == '__main__': | |
out = test_parser() | |
print out.get('list2_') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" Classes used to represent the configuration tree | |
""" | |
class Section(object): | |
""" Represent a (sub-)section of the configuration. | |
""" | |
def __init__(self, name, parent=None): | |
self._name = name | |
self._parent = parent | |
self._children = {} # All children of this section stored as a dict | |
# key -> list of children for the key. | |
def __repr__(self): | |
return "<Section '%s' with %d children>" % (self.name, | |
len(self._children)) | |
# | |
# Public API -- Tree construction methods | |
# These methods are intended as internal usage for the configuration | |
# tree construction by the parser. | |
# | |
def register_child(self, name, child): | |
key_children = self._children.get(name, []) | |
key_children.append(child) | |
self._children[name] = key_children | |
# | |
# Public API -- User methods | |
# These methods are intended for configuration tree traversal for | |
# user usage. | |
# | |
@property | |
def name(self): | |
return self._name | |
@property | |
def parent(self): | |
return self._parent | |
@parent.setter | |
def parent(self, parent): | |
self._parent = parent | |
def subsections(self, name): | |
""" Iterate over sub-sections with the specified name. | |
""" | |
for children in self._children.get(name, ()): | |
if isinstance(children, Section): | |
yield children | |
def get(self, name, default=None): | |
""" Get the last value found with the specified name or | |
return default. | |
""" | |
found = False | |
child = None | |
for child in self._children.get(name, ()): | |
if not isinstance(child, Section): | |
found = True | |
if found: | |
return child | |
else: | |
return default | |
def getiter(self, name): | |
""" Iterate over all values with the specified name. | |
""" | |
for children in self._children.get(name, ()): | |
if not isinstance(children, Section): | |
yield children |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment