217 lines
6.4 KiB
Python
217 lines
6.4 KiB
Python
# -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
|
|
|
|
"""Parser pour un fichier de configuration
|
|
|
|
Syntaxe:
|
|
~~~
|
|
# comment
|
|
object id var=value
|
|
continuation="line starting with a space"
|
|
-link otherid
|
|
|
|
-link objectref1 predicate -link objectref2
|
|
~~~
|
|
"""
|
|
|
|
__all__ = (
|
|
'split_namev', 'split_nvalue', 'split_nlist',
|
|
'Parser',
|
|
)
|
|
|
|
import logging; log = logging.getLogger(__name__)
|
|
import sys, re
|
|
|
|
from .utils import *
|
|
from .lexer import *
|
|
from .objects import *
|
|
|
|
RE_NAMETYPE = re.compile(r'(\S+):(\w+)$')
|
|
RE_NAMEVALUE = re.compile(r'(\S+)=(\S*)')
|
|
|
|
def split_namev(arg, sep=None):
|
|
"""spliter un argument de la forme
|
|
name[method][=value]
|
|
|
|
Si value n'est pas spécifié, il vaut None
|
|
method peut être % (reset), + (add), - (del) et sa valeur par défaut est ADD_UNIQUE
|
|
|
|
si sep is not None, splitter values sur cette valeur
|
|
"""
|
|
if '=' in arg:
|
|
name, value = arg.split('=', 1)
|
|
if sep is not None:
|
|
value = value.split(sep)
|
|
else:
|
|
name = arg
|
|
value = None
|
|
if name.endswith('%'):
|
|
name = name[:-1]
|
|
method = RESET_ADD
|
|
elif name.endswith('+'):
|
|
name = name[:-1]
|
|
method = ADD
|
|
elif name.endswith('-'):
|
|
name = name[:-1]
|
|
method = REMOVE
|
|
else:
|
|
method = ADD_UNIQUE
|
|
mo = RE_NAMETYPE.match(name)
|
|
if mo is not None:
|
|
name, type = mo.groups()
|
|
else:
|
|
type = None
|
|
return name, value, method
|
|
|
|
def split_nvalue(arg):
|
|
"""spliter un argument de la forme
|
|
[name=]value
|
|
|
|
Si name n'est pas spécifié, il vaut None
|
|
"""
|
|
if '=' in arg:
|
|
name, value = arg.split('=', 1)
|
|
else:
|
|
name = None
|
|
value = arg
|
|
return name, value
|
|
|
|
def split_nlist(arg):
|
|
"""spliter un argument de la forme
|
|
[name=]values
|
|
|
|
Si name n'est pas spécifié, il vaut None
|
|
values est un ensemble de valeurs séparées par des virgules
|
|
"""
|
|
if '=' in arg:
|
|
name, values = arg.split('=', 1)
|
|
values = values.split(',')
|
|
else:
|
|
name = None
|
|
values = arg.split(',')
|
|
return name, values
|
|
|
|
def split_list(arg):
|
|
"""spliter un argument de la forme
|
|
values
|
|
|
|
values est un ensemble de valeurs séparées par des virgules
|
|
"""
|
|
return arg.split(',')
|
|
|
|
class LoneError(ValueError):
|
|
"""Exception lancée quand un verbe attend un argument inexistant
|
|
"""
|
|
|
|
class Parser(object):
|
|
args = None
|
|
|
|
def __init__(self, file=None):
|
|
if file is not None:
|
|
self.parse(file=file)
|
|
|
|
def parse(self, predicates=None, file=None):
|
|
if file is not None:
|
|
lexer = Lexer(file)
|
|
predicates = lexer.get_predicates()
|
|
for args in predicates:
|
|
self.args = args
|
|
if self.isa_link(): self.handle_fact()
|
|
else: self.handle_object()
|
|
return self
|
|
|
|
def eop(self):
|
|
return not self.args
|
|
def isa_verb(self):
|
|
return self.args and not self.args[0].startswith('-')
|
|
def isa_link(self):
|
|
return self.args and self.args[0].startswith('-')
|
|
def isa_namevalue(self):
|
|
return self.args and RE_NAMEVALUE.match(self.args[0]) is not None
|
|
|
|
def pop(self, desc=None):
|
|
arg = self.args.pop(0)
|
|
if desc is not None and self.eop():
|
|
log.warning("lone %s '%s' was ignored", desc, arg)
|
|
raise LoneError(arg)
|
|
return arg
|
|
def pop_link(self, desc=None):
|
|
if not self.isa_link():
|
|
raise ValueError("expected -otype")
|
|
return self.pop(desc)[1:]
|
|
def pop_namev(self, sep=None):
|
|
return split_namev(self.pop(), sep)
|
|
def pop_nvalue(self):
|
|
return split_nvalue(self.pop())
|
|
def pop_nlist(self):
|
|
return split_nlist(self.pop())
|
|
def pop_list(self):
|
|
return split_list(self.pop())
|
|
|
|
def handle_object(self):
|
|
# créer ou mettre à jour un objet
|
|
try:
|
|
otype = self.pop("object type")
|
|
if self.isa_namevalue():
|
|
oid, values, method = self.pop_namev(',')
|
|
else:
|
|
oid = self.pop()
|
|
values = ()
|
|
method = ADD_UNIQUE
|
|
object = catalog.create_object(otype, oid)
|
|
object.update('values', values, method)
|
|
while not self.isa_link():
|
|
if self.eop(): break
|
|
name, value, method = self.pop_namev()
|
|
if value is None: value = 'true'
|
|
object.update(name, value, method)
|
|
while self.isa_link():
|
|
ltype = self.pop_link()
|
|
lids = self.pop_list()
|
|
links = [object.linkto(lid, ltype) for lid in lids]
|
|
while not self.isa_link():
|
|
if self.eop(): break
|
|
name, value, method = self.pop_namev()
|
|
if value is None: value = 'true'
|
|
for link in links:
|
|
link.update(name, value, method)
|
|
except LoneError:
|
|
pass
|
|
|
|
def handle_fact(self):
|
|
# créer un ou plusieurs liens
|
|
try:
|
|
sotype = self.pop_link("source link type")
|
|
soids = self.pop_list()
|
|
defo = Object() # pour les attributs par défaut
|
|
while self.isa_namevalue():
|
|
name, value, method = self.pop_namev()
|
|
if value is None: value = 'true'
|
|
defo.update(name, value, method)
|
|
while not self.eop():
|
|
verb = self.pop("verb")
|
|
totype = self.pop_link("dest link type")
|
|
toids = self.pop_list()
|
|
facts = []
|
|
for soid in soids:
|
|
for toid in toids:
|
|
fact = catalog.create_fact(sotype, soid, verb, totype, toid)
|
|
fact.update(defo.attrs)
|
|
facts.append(fact)
|
|
while self.isa_namevalue():
|
|
name, value, method = self.pop_namev()
|
|
if value is None: value = 'true'
|
|
for fact in facts:
|
|
fact.update(name, value, method)
|
|
except LoneError:
|
|
pass
|
|
|
|
def parse_attrs(self, namevalues, object):
|
|
"""analyser une liste de définition d'attributs name=value et mettre à jour
|
|
object
|
|
"""
|
|
self.args = listof(namevalues)
|
|
while not self.eop():
|
|
name, value, method = self.pop_namev()
|
|
if value is None: value = 'true'
|
|
object.update(name, value, method)
|