# -*- 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)