1#!/usr/bin/python2 2# -*-coding:utf-8 -* 3 4# Copyright (c) 2011-2014, Intel Corporation 5# All rights reserved. 6# 7# Redistribution and use in source and binary forms, with or without modification, 8# are permitted provided that the following conditions are met: 9# 10# 1. Redistributions of source code must retain the above copyright notice, this 11# list of conditions and the following disclaimer. 12# 13# 2. Redistributions in binary form must reproduce the above copyright notice, 14# this list of conditions and the following disclaimer in the documentation and/or 15# other materials provided with the distribution. 16# 17# 3. Neither the name of the copyright holder nor the names of its contributors 18# may be used to endorse or promote products derived from this software without 19# specific prior written permission. 20# 21# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 28# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 32 33 34import re 35import sys 36import copy 37# For Python 2.x/3.x compatibility 38try: 39 from itertools import izip as zip 40 from itertools import imap as map 41except: 42 pass 43 44# ===================================================================== 45""" Context classes, used during propagation and the "to PFW script" step """ 46# ===================================================================== 47 48class PropagationContextItem(list) : 49 """Handle an item during the propagation step""" 50 def __copy__(self): 51 """C.__copy__() -> a shallow copy of C""" 52 return self.__class__(self) 53 54class PropagationContextElement(PropagationContextItem) : 55 """Handle an Element during the propagation step""" 56 def getElementsFromName(self, name): 57 matchingElements = [] 58 for element in self : 59 if element.getName() == name : 60 matchingElements.append(element) 61 return matchingElements 62 63 64class PropagationContextOption(PropagationContextItem) : 65 """Handle an Option during the propagation step""" 66 def getOptionItems (self, itemName): 67 items = [] 68 for options in self : 69 items.append(options.getOption(itemName)) 70 return items 71 72 73class PropagationContext() : 74 """Handle the context during the propagation step""" 75 def __init__(self, propagationContext=None) : 76 77 if propagationContext == None : 78 self._context = { 79 "DomainOptions" : PropagationContextOption() , 80 "Configurations" : PropagationContextElement() , 81 "ConfigurationOptions" : PropagationContextOption() , 82 "Rules" : PropagationContextElement() , 83 "PathOptions" : PropagationContextOption() , 84 } 85 else : 86 self._context = propagationContext 87 88 def copy(self): 89 """return a copy of the context""" 90 contextCopy = self._context.copy() 91 92 for key in iter(self._context) : 93 contextCopy[key] = contextCopy[key].__copy__() 94 95 return self.__class__(contextCopy) 96 97 def getDomainOptions (self): 98 return self._context["DomainOptions"] 99 100 def getConfigurations (self): 101 return self._context["Configurations"] 102 103 def getConfigurationOptions (self): 104 return self._context["ConfigurationOptions"] 105 106 def getRules (self): 107 return self._context["Rules"] 108 109 def getPathOptions (self): 110 return self._context["PathOptions"] 111 112 113# ===================================================== 114"""Element option container""" 115# ===================================================== 116 117class Options () : 118 """handle element options""" 119 def __init__(self, options=[], optionNames=[]) : 120 self.options = dict(zip(optionNames, options)) 121 # print(options,optionNames,self.options) 122 123 124 def __str__(self) : 125 ops2str = [] 126 for name, argument in list(self.options.items()) : 127 ops2str.append(str(name) + "=\"" + str(argument) + "\"") 128 129 return " ".join(ops2str) 130 131 def getOption(self, name): 132 """get option by its name, if it does not exist return empty string""" 133 return self.options.get(name, "") 134 135 def setOption(self, name, newOption): 136 """set option by its name""" 137 self.options[name] = newOption 138 139 def copy (self): 140 """D.copy() -> a shallow copy of D""" 141 copy = Options() 142 copy.options = self.options.copy() 143 return copy 144 145# ==================================================== 146"""Definition of all element class""" 147# ==================================================== 148 149class Element(object): 150 """ implement a basic element 151 152 It is the class base for all other elements as Domain, Configuration...""" 153 tag = "unknown" 154 optionNames = ["Name"] 155 childWhiteList = [] 156 optionDelimiter = " " 157 158 def __init__(self, line=None) : 159 160 if line == None : 161 self.option = Options([], self.optionNames) 162 else : 163 self.option = self.optionFromLine(line) 164 165 self.children = [] 166 167 def optionFromLine(self, line) : 168 # get ride of spaces 169 line = line.strip() 170 171 options = self.extractOptions(line) 172 173 return Options(options, self.optionNames) 174 175 def extractOptions(self, line) : 176 """return the line splited by the optionDelimiter atribute 177 178 Option list length is less or equal to the optionNames list length 179 """ 180 options = line.split(self.optionDelimiter, len(self.optionNames) - 1) 181 182 # get ride of leftover spaces 183 optionsStrip = list(map(str.strip, options)) 184 185 return optionsStrip 186 187 def addChild(self, child, append=True) : 188 """ A.addChid(B) -> add B to A child list if B class name is in A white List""" 189 try: 190 # Will raise an exception if this child is not in the white list 191 self.childWhiteList.index(child.__class__.__name__) 192 # If no exception was raised, add child to child list 193 194 if append : 195 self.children.append(child) 196 else : 197 self.children.insert(0, child) 198 199 except ValueError: 200 # the child class is not in the white list 201 raise ChildNotPermitedError("", self, child) 202 203 def addChildren(self, children, append=True) : 204 """Add a list of child""" 205 if append: 206 # Add children at the end of the child list 207 self.children.extend(children) 208 else: 209 # Add children at the begining of the child list 210 self.children = children + self.children 211 212 def childrenToString(self, prefix=""): 213 """return raw printed children """ 214 body = "" 215 for child in self.children : 216 body = body + child.__str__(prefix) 217 218 return body 219 220 def __str__(self, prefix="") : 221 """return raw printed element""" 222 selfToString = prefix + " " + self.tag + " " + str(self.option) 223 return selfToString + "\n" + self.childrenToString(prefix + "\t") 224 225 def extractChildrenByClass(self, classTypeList) : 226 """return all children whose class is in the list argument 227 228 return a list of all children whose class in the list "classTypeList" (second arguments)""" 229 selectedChildren = [] 230 231 for child in self.children : 232 for classtype in classTypeList : 233 if child.__class__ == classtype : 234 selectedChildren.append(child) 235 break 236 return selectedChildren 237 238 def propagate (self, context=PropagationContext()): 239 """call the propagate method of all children""" 240 for child in self.children : 241 child.propagate(context) 242 243 def getName(self): 244 """return name option value. If none return "" """ 245 return self.option.getOption("Name") 246 247 def setName(self, name): 248 self.option.setOption("Name", name) 249 250 def translate(self, translator): 251 for child in self.children: 252 child.translate(translator) 253 254# ---------------------------------------------------------- 255 256class ElementWithTag (Element): 257 """Element of this class are declared with a tag => line == "tag: .*" """ 258 def extractOptions(self, line) : 259 lineWithoutTag = line.split(":", 1)[-1].strip() 260 options = super(ElementWithTag, self).extractOptions(lineWithoutTag) 261 return options 262 263# ---------------------------------------------------------- 264 265class ElementWithInheritance(Element): 266 def propagate (self, context=PropagationContext) : 267 """propagate some proprieties to children""" 268 269 # copy the context so that everything that hapend next will only affect 270 # children 271 contextCopy = context.copy() 272 273 # check for inheritance 274 self.Inheritance(contextCopy) 275 276 # call the propagate method of all children 277 super(ElementWithInheritance, self).propagate(contextCopy) 278 279 280class ElementWithRuleInheritance(ElementWithInheritance): 281 """class that will give to its children its rules""" 282 def ruleInheritance(self, context): 283 """Add its rules to the context and get context rules""" 284 285 # extract all children rule and operator 286 childRules = self.extractChildrenByClass([Operator, Rule]) 287 288 # get context rules 289 contextRules = context.getRules() 290 291 # adopt rules of the beginning of the context 292 self.addChildren(contextRules, append=False) 293 294 # add previously extract rules to the context 295 contextRules += childRules 296 297 298# ---------------------------------------------------------- 299 300class EmptyLine (Element) : 301 """This class represents an empty line. 302 303 Will raise "EmptyLineWarning" exception at instanciation.""" 304 305 tag = "emptyLine" 306 match = re.compile(r"[ \t]*\n?$").match 307 def __init__ (self, line): 308 raise EmptyLineWarning(line) 309 310# ---------------------------------------------------------- 311 312class Commentary(Element): 313 """This class represents a commentary. 314 315 Will raise "CommentWarning" exception at instanciation.""" 316 317 tag = "commentary" 318 optionNames = ["comment"] 319 match = re.compile(r"#").match 320 def __init__ (self, line): 321 raise CommentWarning(line) 322 323# ---------------------------------------------------------- 324 325class Path (ElementWithInheritance) : 326 """class implementing the "path = value" concept""" 327 tag = "path" 328 optionNames = ["Name", "value"] 329 match = re.compile(r".+=").match 330 optionDelimiter = "=" 331 332 def translate(self, translator): 333 translator.setParameter(self.getName(), self.option.getOption("value")) 334 335 def Inheritance (self, context) : 336 """check for path name inheritance""" 337 self.OptionsInheritance(context) 338 339 def OptionsInheritance (self, context) : 340 """make configuration name inheritance """ 341 342 context.getPathOptions().append(self.option.copy()) 343 self.setName("/".join(context.getPathOptions().getOptionItems("Name"))) 344 345 346class GroupPath (Path, ElementWithTag) : 347 tag = "component" 348 match = re.compile(tag + r" *:").match 349 optionNames = ["Name"] 350 childWhiteList = ["Path", "GroupPath"] 351 352 def getPathNames (self) : 353 """Return the list of all path child name""" 354 355 pathNames = [] 356 357 paths = self.extractChildrenByClass([Path]) 358 for path in paths : 359 pathNames.append(path.getName()) 360 361 groupPaths = self.extractChildrenByClass([GroupPath]) 362 for groupPath in groupPaths : 363 pathNames += groupPath.getPathNames() 364 365 return pathNames 366 367 def translate(self, translator): 368 for child in self.extractChildrenByClass([Path, GroupPath]): 369 child.translate(translator) 370 371# ---------------------------------------------------------- 372 373class Rule (Element) : 374 """class implementing the rule concept 375 376 A rule is composed of a criterion, a rule type and an criterion state. 377 It should not have any child and is propagated to all configuration in parent descendants. 378 """ 379 380 tag = "rule" 381 optionNames = ["criterion", "type", "element"] 382 match = re.compile(r"[a-zA-Z0-9_.]+ +(Is|IsNot|Includes|Excludes) +[a-zA-Z0-9_.]+").match 383 childWhiteList = [] 384 385 def PFWSyntax (self, prefix=""): 386 387 script = prefix + \ 388 self.option.getOption("criterion") + " " + \ 389 self.option.getOption("type") + " " + \ 390 self.option.getOption("element") 391 392 return script 393 394 395class Operator (Rule) : 396 """class implementing the operator concept 397 398 An operator contains rules and other operators 399 It is as rules propagated to all configuration children in parent descendants. 400 It should only have the name ANY or ALL to be understood by PFW. 401 """ 402 403 tag = "operator" 404 optionNames = ["Name"] 405 match = re.compile(r"ANY|ALL").match 406 childWhiteList = ["Rule", "Operator"] 407 408 syntax = { "ANY" : "Any" , "ALL" : "All"} 409 410 def PFWSyntax (self, prefix=""): 411 """ return a pfw rule (ex : "Any{criterion1 is state1}") generated from "self" and its children options""" 412 script = "" 413 414 script += prefix + \ 415 self.syntax[self.getName()] + "{ " 416 417 rules = self.extractChildrenByClass([Rule, Operator]) 418 419 PFWRules = [] 420 for rule in rules : 421 PFWRules.append(rule.PFWSyntax(prefix + " ")) 422 423 script += (" , ").join(PFWRules) 424 425 script += prefix + " }" 426 427 return script 428 429# ---------------------------------------------------------- 430 431class Configuration (ElementWithRuleInheritance, ElementWithTag) : 432 tag = "configuration" 433 optionNames = ["Name"] 434 match = re.compile(r"conf *:").match 435 childWhiteList = ["Rule", "Operator", "Path", "GroupPath"] 436 437 def composition (self, context): 438 """make all needed composition 439 440 Composition is the fact that group configuration with the same name defined 441 in a parent will give their rule children to this configuration 442 """ 443 444 name = self.getName() 445 sameNameConf = context.getConfigurations().getElementsFromName(name) 446 447 sameNameConf.reverse() 448 449 for configuration in sameNameConf : 450 # add same name configuration rule children to self child list 451 self.addChildren(configuration.extractChildrenByClass([Operator, Rule]), append=False) 452 453 454 def propagate (self, context=PropagationContext) : 455 """propagate proprieties to children 456 457 make needed compositions, join ancestor name to its name, 458 and add rules previously defined rules""" 459 460 # make all needed composition 461 self.composition(context) 462 463 super(Configuration, self).propagate(context) 464 465 def Inheritance (self, context) : 466 """make configuration name and rule inheritance""" 467 # check for configuration name inheritance 468 self.OptionsInheritance(context) 469 470 # check for rule inheritance 471 self.ruleInheritance(context) 472 473 def OptionsInheritance (self, context) : 474 """make configuration name inheritance """ 475 476 context.getConfigurationOptions().append(self.option.copy()) 477 self.setName(".".join(context.getConfigurationOptions().getOptionItems("Name"))) 478 479 480 def getRootPath (self) : 481 482 paths = self.extractChildrenByClass([Path, GroupPath]) 483 484 rootPath = GroupPath() 485 rootPath.addChildren(paths) 486 487 return rootPath 488 489 def getConfigurableElements (self) : 490 """return all path name defined in this configuration""" 491 492 return self.getRootPath().getPathNames() 493 494 def getRuleString(self): 495 """Output this configuration's rule as a string""" 496 497 # Create a rootRule 498 ruleChildren = self.extractChildrenByClass([Rule, Operator]) 499 500 # Do not create a root rule if there is only one fist level Operator rule 501 if len(ruleChildren) == 1 and ruleChildren[0].__class__ == Operator : 502 ruleroot = ruleChildren[0] 503 504 else : 505 ruleroot = Operator() 506 ruleroot.setName("ALL") 507 ruleroot.addChildren(ruleChildren) 508 509 return ruleroot.PFWSyntax() 510 511 def translate(self, translator): 512 translator.createConfiguration(self.getName()) 513 translator.setRule(self.getRuleString()) 514 515 paths = self.extractChildrenByClass([Path, GroupPath]) 516 translator.setElementSequence(self.getConfigurableElements()) 517 for path in paths: 518 path.translate(translator) 519 520 def copy (self) : 521 """return a shallow copy of the configuration""" 522 523 # create configuration or subclass copy 524 confCopy = self.__class__() 525 526 # add children 527 confCopy.children = list(self.children) 528 529 # add option 530 confCopy.option = self.option.copy() 531 532 return confCopy 533 534class GroupConfiguration (Configuration) : 535 tag = "GroupConfiguration" 536 optionNames = ["Name"] 537 match = re.compile(r"(supConf|confGroup|confType) *:").match 538 childWhiteList = ["Rule", "Operator", "GroupConfiguration", "Configuration", "GroupPath"] 539 540 def composition (self, context) : 541 """add itself in context for configuration composition 542 543 Composition is the fact that group configuration with the same name defined 544 in a parent will give their rule children to this configuration 545 """ 546 547 # copyItself 548 selfCopy = self.copy() 549 550 # make all needed composition 551 super(GroupConfiguration, self).composition(context) 552 553 # add the copy in context for futur configuration composition 554 context.getConfigurations().append(selfCopy) 555 556 557 def getConfigurableElements (self) : 558 """return a list. Each elements consist of a list of configurable element of a configuration 559 560 return a list consisting of all configurable elements for each configuration. 561 These configurable elements are organized in a list""" 562 configurableElements = [] 563 564 configurations = self.extractChildrenByClass([Configuration]) 565 for configuration in configurations : 566 configurableElements.append(configuration.getConfigurableElements()) 567 568 groudeConfigurations = self.extractChildrenByClass([GroupConfiguration]) 569 for groudeConfiguration in groudeConfigurations : 570 configurableElements += groudeConfiguration.getConfigurableElements() 571 572 return configurableElements 573 574 def translate(self, translator): 575 for child in self.extractChildrenByClass([Configuration, GroupConfiguration]): 576 child.translate(translator) 577 578# ---------------------------------------------------------- 579 580class Domain (ElementWithRuleInheritance, ElementWithTag) : 581 tag = "domain" 582 sequenceAwareKeyword = "sequenceAware" 583 584 match = re.compile(r"domain *:").match 585 optionNames = ["Name", sequenceAwareKeyword] 586 childWhiteList = ["Configuration", "GroupConfiguration", "Rule", "Operator"] 587 588 def propagate (self, context=PropagationContext) : 589 """ propagate name, sequenceAwareness and rule to children""" 590 591 # call the propagate method of all children 592 super(Domain, self).propagate(context) 593 594 self.checkConfigurableElementUnicity() 595 596 def Inheritance (self, context) : 597 """check for domain name, sequence awarness and rules inheritance""" 598 # check for domain name and sequence awarness inheritance 599 self.OptionsInheritance(context) 600 601 # check for rule inheritance 602 self.ruleInheritance(context) 603 604 def OptionsInheritance(self, context) : 605 """ make domain name and sequence awareness inheritance 606 607 join to the domain name all domain names defined in context and 608 if any domain in context is sequence aware, set sequenceAwareness to True""" 609 610 # add domain options to context 611 context.getDomainOptions().append(self.option.copy()) 612 613 # set name to the junction of all domain name in context 614 self.setName(".".join(context.getDomainOptions().getOptionItems("Name"))) 615 616 # get sequenceAwareness of all domains in context 617 sequenceAwareList = context.getDomainOptions().getOptionItems(self.sequenceAwareKeyword) 618 # or operation on all booleans in sequenceAwareList 619 sequenceAwareness = False 620 for sequenceAware in sequenceAwareList : 621 sequenceAwareness = sequenceAwareness or sequenceAware 622 # current domain sequenceAwareness = sequenceAwareness 623 self.option.setOption(self.sequenceAwareKeyword, sequenceAwareness) 624 625 626 def extractOptions(self, line) : 627 """Extract options from the definition line""" 628 options = super(Domain, self).extractOptions(line) 629 630 sequenceAwareIndex = self.optionNames.index(self.sequenceAwareKeyword) 631 632 # translate the keyword self.sequenceAwareKeyword if specified to boolean True, 633 # to False otherwise 634 try : 635 if options[sequenceAwareIndex] == self.sequenceAwareKeyword : 636 options[sequenceAwareIndex] = True 637 else: 638 options[sequenceAwareIndex] = False 639 except IndexError : 640 options = options + [None] * (sequenceAwareIndex - len(options)) + [False] 641 return options 642 643 def getRootConfiguration (self) : 644 """return the root configuration group""" 645 configurations = self.extractChildrenByClass([Configuration, GroupConfiguration]) 646 647 configurationRoot = GroupConfiguration() 648 649 configurationRoot.addChildren(configurations) 650 651 return configurationRoot 652 653 # TODO: don't do that in the parser, let the PFW tell you that 654 def checkConfigurableElementUnicity (self): 655 """ check that all configurable elements defined in child configuration are the sames""" 656 657 # get a list. Each elements of is the configurable element list of a configuration 658 configurableElementsList = self.getRootConfiguration().getConfigurableElements() 659 660 # if at least two configurations in the domain 661 if len(configurableElementsList) > 1 : 662 663 # get first configuration configurable element list sort 664 configurableElementsList0 = list(configurableElementsList[0]) 665 configurableElementsList0.sort() 666 667 for configurableElements in configurableElementsList : 668 # sort current configurable element list 669 auxConfigurableElements = list(configurableElements) 670 auxConfigurableElements.sort() 671 672 if auxConfigurableElements != configurableElementsList0 : 673 # if different, 2 configurations those not have the same configurable element list 674 # => one or more configurable element is missing in one of the 2 configuration 675 raise UndefinedParameter(self.getName()) 676 677 678 def translate(self, translator): 679 sequence_aware = self.option.getOption(self.sequenceAwareKeyword) 680 translator.createDomain(self.getName(), sequence_aware) 681 682 configurations = self.getRootConfiguration() 683 configurableElementsList = configurations.getConfigurableElements() 684 685 # add configurable elements 686 if len(configurableElementsList) != 0 : 687 for configurableElement in configurableElementsList[0] : 688 translator.addElement(configurableElement) 689 690 configurations.translate(translator) 691 692class GroupDomain (Domain) : 693 tag = "groupDomain" 694 match = re.compile(r"(supDomain|domainGroup) *:").match 695 childWhiteList = ["GroupDomain", "Domain", "GroupConfiguration", "Rule", "Operator"] 696 697 def translate(self, translator): 698 for child in self.extractChildrenByClass([Domain, GroupDomain]): 699 child.translate(translator) 700 701# ---------------------------------------------------------- 702 703class Root(Element): 704 tag = "root" 705 childWhiteList = ["Domain", "GroupDomain"] 706 707 708# =========================================== 709""" Syntax error Exceptions""" 710# =========================================== 711 712class MySyntaxProblems(SyntaxError) : 713 comment = "syntax error in %(line)s " 714 715 def __init__(self, line=None, num=None): 716 self.setLine(line, num) 717 718 def __str__(self): 719 720 if self.line : 721 self.comment = self.comment % {"line" : repr(self.line)} 722 if self.num : 723 self.comment = "Line " + str(self.num) + ", " + self.comment 724 return self.comment 725 726 def setLine (self, line, num): 727 self.line = str(line) 728 self.num = num 729 730 731# --------------------------------------------------------- 732 733class MyPropagationError(MySyntaxProblems) : 734 """ Syntax error Exceptions used in the propagation step""" 735 pass 736 737class UndefinedParameter(MyPropagationError) : 738 comment = "Configurations in domain '%(domainName)s' do not all set the same parameters " 739 def __init__ (self, domainName): 740 self.domainName = domainName 741 def __str__ (self): 742 return self.comment % { "domainName" : self.domainName } 743 744 745# ----------------------------------------------------- 746""" Syntax error Exceptions used by parser""" 747 748class MySyntaxError(MySyntaxProblems) : 749 """ Syntax error Exceptions used by parser""" 750 pass 751 752class MySyntaxWarning(MySyntaxProblems) : 753 """ Syntax warning Exceptions used by parser""" 754 pass 755 756class IndentationSyntaxError(MySyntaxError) : 757 comment = """syntax error in %(line)s has no father element. 758 You can only increment indentation by one tabutation per line")""" 759 760class EmptyLineWarning(MySyntaxWarning): 761 comment = "warning : %(line)s is an empty line and has been ommited" 762 763class CommentWarning(MySyntaxWarning): 764 comment = "warning : %(line)s is a commentary and has been ommited" 765 766class ChildNotPermitedError(MySyntaxError): 767 def __init__(self, line, fatherElement, childElement): 768 self.comment = "syntax error in %(line)s, " + fatherElement.tag + " should not have a " + childElement.tag + " child." 769 super(ChildNotPermitedError, self).__init__(line) 770 771 772class UnknownElementTypeError(MySyntaxError): 773 comment = " error in line %(line)s , not known element type were matched " 774 775class SpaceInIndentationError(MySyntaxError): 776 comment = " error in ,%(line)s space is not permited in indentation" 777 778 779# ============================================ 780"""Class creating the DOM elements from a stream""" 781# ============================================ 782 783class ElementsFactory(object) : 784 """Element factory, return an instance of the first matching element 785 786 Test each element list in elementClass and instanciate it if it's methode match returns True 787 The method match is called with input line as argument 788 """ 789 def __init__ (self): 790 self.elementClass = [ 791 EmptyLine , 792 Commentary, 793 GroupDomain, 794 Domain, 795 Path, 796 GroupConfiguration, 797 Configuration, 798 Operator, 799 Rule, 800 GroupPath 801 ] 802 803 def createElementFromLine (self, line) : 804 """return an instance of the first matching element 805 806 Test each element list in elementClass and instanciate it if it's methode match returns True 807 The method match is called with the argument line. 808 Raise UnknownElementTypeError if no element matched. 809 """ 810 for element in self.elementClass : 811 if element.match(line) : 812 # print (line + element.__class__.__name__) 813 return element(line) 814 # if we have not find any 815 raise UnknownElementTypeError(line) 816 817#------------------------------------------------------ 818 819class Parser(object) : 820 """Class implementing the parser""" 821 def __init__(self): 822 self.rankPattern = re.compile(r"^([\t ]*)(.*)") 823 self.elementFactory = ElementsFactory() 824 self.previousRank = 0 825 826 def __parseLine__(self, line): 827 828 rank, rest = self.__getRank__(line) 829 830 # instanciate the coresponding element 831 element = self.elementFactory.createElementFromLine(rest) 832 833 self.__checkIndentation__(rank) 834 835 return rank, element 836 837 def __getRank__(self, line): 838 """return the rank, the name and the option of the input line 839 840the rank is the number of tabulation (\t) at the line beginning. 841the rest is the rest of the line.""" 842 # split line in rank and rest 843 rank = self.rankPattern.match(line) 844 if rank : 845 rank, rest = rank.group(1, 2) 846 else : 847 raise MySyntaxError(line) 848 849 # check for empty line 850 if rest == "" : 851 raise EmptyLineWarning(line) 852 853 # check for space in indentation 854 if rank.find(" ") > -1 : 855 raise SpaceInIndentationError(line) 856 857 rank = len (rank) + 1 # rank starts at 1 858 859 860 return rank, rest 861 862 863 def __checkIndentation__(self, rank): 864 """check if indentation > previous indentation + 1. If so, raise IndentationSyntaxError""" 865 if (rank > self.previousRank + 1) : 866 raise IndentationSyntaxError() 867 self.previousRank = rank 868 869 def parse(self, stream, verbose=False): 870 """parse a stream, usually a opened file""" 871 myroot = Root("root") 872 context = [myroot] # root is element of rank 0 873 warnings = "" 874 875 for num, line in enumerate(stream): 876 try: 877 rank, myelement = self.__parseLine__(line) 878 879 while len(context) > rank : 880 context.pop() 881 context.append(myelement) 882 context[-2].addChild(myelement) 883 884 except MySyntaxWarning as ex: 885 ex.setLine(line, num + 1) 886 if verbose : 887 sys.stderr.write("{}\n".format(ex)) 888 889 except MySyntaxError as ex : 890 ex.setLine(line, num + 1) 891 raise 892 893 return myroot 894 895