nutools/lib/nulib/python/deploydb/parser.py

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)