127 lines
4.1 KiB
Python
127 lines
4.1 KiB
Python
import re
|
|
from StringIO import StringIO
|
|
|
|
from ostack_validator.model import *
|
|
from ostack_validator.config_formats.common import *
|
|
|
|
class IniConfigParser:
|
|
key_value_re = re.compile("^(\S+?)\s*([:=])\s*('.*'|\".*\"|.*)\s*$")
|
|
|
|
def parse(self, name, base_mark, io):
|
|
if not hasattr(io, 'readlines'):
|
|
io = StringIO(io)
|
|
|
|
def mark(line, column=0):
|
|
return base_mark.merge(Mark('', line, column))
|
|
|
|
errors = []
|
|
current_section_name = ConfigSectionName(mark(0), mark(0), '')
|
|
current_param_name = None
|
|
current_param_value = None
|
|
current_param_delimiter = None
|
|
sections = []
|
|
parameters = []
|
|
|
|
line_number = -1
|
|
for line in io.readlines():
|
|
line = line.rstrip()
|
|
|
|
line_number += 1
|
|
|
|
if current_param_name and (current_param_value.quotechar or (line == '' or not line[0].isspace())):
|
|
param = ConfigParameter(current_param_name.start_mark, current_param_value.end_mark, current_param_name, current_param_value, current_param_delimiter)
|
|
parameters.append(param)
|
|
|
|
current_param_name = None
|
|
current_param_value = None
|
|
current_param_delimiter = None
|
|
|
|
if line == '': continue
|
|
|
|
if line[0] in '#;': continue
|
|
|
|
if line[0].isspace():
|
|
if current_param_name:
|
|
current_param_value.end_mark = mark(line_number, len(line))
|
|
current_param_value.text += line.lstrip()
|
|
continue
|
|
else:
|
|
errors.append(ParseError('Unexpected multiline value continuation', mark(line_number)))
|
|
continue
|
|
|
|
if line[0] == '[':
|
|
end_index = line.find(']')
|
|
if end_index == -1:
|
|
errors.append(ParseError('Unclosed section', mark(line_number, len(line))))
|
|
|
|
end_index = len(line)
|
|
while line[end_index-1].isspace(): end_index -= 1
|
|
if end_index <= 1:
|
|
errors.append(ParseError('Missing section name', mark(line_number)))
|
|
continue
|
|
else:
|
|
i = end_index+1
|
|
while i < len(line):
|
|
if not line[i].isspace():
|
|
errors.append(ParseError('Extra chars after section name', mark(line_number, i)))
|
|
break
|
|
i += 1
|
|
|
|
if current_section_name.text != '' or len(parameters) > 0:
|
|
section = ConfigSection(current_section_name.start_mark, mark(line_number), current_section_name, parameters)
|
|
sections.append(section)
|
|
parameters = []
|
|
|
|
current_section_name = ConfigSectionName(
|
|
mark(line_number, 0),
|
|
mark(line_number, end_index),
|
|
line[1:end_index]
|
|
)
|
|
else:
|
|
m = self.key_value_re.match(line)
|
|
if m:
|
|
current_param_name = ConfigParameterName(
|
|
mark(line_number, m.start(1)),
|
|
mark(line_number, m.end(1)),
|
|
m.group(1)
|
|
)
|
|
current_param_delimiter = TextElement(
|
|
mark(line_number, m.start(2)),
|
|
mark(line_number, m.end(2)),
|
|
m.group(2)
|
|
)
|
|
|
|
# Unquote value
|
|
value = m.group(3)
|
|
quotechar = None
|
|
if len(value) > 0 and (value[0] == value[-1] and value[0] in "\"'"):
|
|
quotechar = value[0]
|
|
value = value[1:-1]
|
|
|
|
current_param_value = ConfigParameterValue(
|
|
mark(line_number, m.start(3)),
|
|
mark(line_number, m.end(3)),
|
|
value,
|
|
quotechar=quotechar
|
|
)
|
|
else:
|
|
errors.append(ParseError('Syntax error in line "%s"' % line, mark(line_number)))
|
|
|
|
if current_param_name:
|
|
param = ConfigParameter(current_param_name.start_mark, current_param_value.end_mark, current_param_name, current_param_value, current_param_delimiter)
|
|
parameters.append(param)
|
|
|
|
if current_section_name.text != '' or len(parameters) > 0:
|
|
section = ConfigSection(current_section_name.start_mark, mark(line_number), current_section_name, parameters)
|
|
sections.append(section)
|
|
parameters = []
|
|
|
|
end_mark = base_mark
|
|
if len(sections) > 0:
|
|
end_mark = base_mark.merge(sections[-1].end_mark)
|
|
|
|
config = ComponentConfig(base_mark, end_mark, name, sections, errors)
|
|
|
|
return config
|
|
|