1#!/usr/bin/python3 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 36 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" \ 412 and its children options""" 413 script = "" 414 415 script += prefix + \ 416 self.syntax[self.getName()] + "{ " 417 418 rules = self.extractChildrenByClass([Rule, Operator]) 419 420 PFWRules = [] 421 for rule in rules: 422 PFWRules.append(rule.PFWSyntax(prefix + " ")) 423 424 script += (" , ").join(PFWRules) 425 426 script += prefix + " }" 427 428 return script 429 430# ---------------------------------------------------------- 431 432class Configuration(ElementWithRuleInheritance, ElementWithTag): 433 tag = "configuration" 434 optionNames = ["Name"] 435 match = re.compile(r"conf *:").match 436 childWhiteList = ["Rule", "Operator", "Path", "GroupPath"] 437 438 def composition(self, context): 439 """make all needed composition 440 441 Composition is the fact that group configuration with the same name defined 442 in a parent will give their rule children to this configuration 443 """ 444 445 name = self.getName() 446 sameNameConf = context.getConfigurations().getElementsFromName(name) 447 448 sameNameConf.reverse() 449 450 for configuration in sameNameConf: 451 # add same name configuration rule children to self child list 452 self.addChildren(configuration.extractChildrenByClass([Operator, Rule]), append=False) 453 454 455 def propagate(self, context=PropagationContext): 456 """propagate proprieties to children 457 458 make needed compositions, join ancestor name to its name, 459 and add rules previously defined rules""" 460 461 # make all needed composition 462 self.composition(context) 463 464 super(Configuration, self).propagate(context) 465 466 def Inheritance(self, context): 467 """make configuration name and rule inheritance""" 468 # check for configuration name inheritance 469 self.OptionsInheritance(context) 470 471 # check for rule inheritance 472 self.ruleInheritance(context) 473 474 def OptionsInheritance(self, context): 475 """make configuration name inheritance """ 476 477 context.getConfigurationOptions().append(self.option.copy()) 478 self.setName(".".join(context.getConfigurationOptions().getOptionItems("Name"))) 479 480 481 def getRootPath(self): 482 483 paths = self.extractChildrenByClass([Path, GroupPath]) 484 485 rootPath = GroupPath() 486 rootPath.addChildren(paths) 487 488 return rootPath 489 490 def getConfigurableElements(self): 491 """return all path name defined in this configuration""" 492 493 return self.getRootPath().getPathNames() 494 495 def getRuleString(self): 496 """Output this configuration's rule as a string""" 497 498 # Create a rootRule 499 ruleChildren = self.extractChildrenByClass([Rule, Operator]) 500 501 # Do not create a root rule if there is only one fist level Operator rule 502 if len(ruleChildren) == 1 and ruleChildren[0].__class__ == Operator: 503 ruleroot = ruleChildren[0] 504 505 else: 506 ruleroot = Operator() 507 ruleroot.setName("ALL") 508 ruleroot.addChildren(ruleChildren) 509 510 return ruleroot.PFWSyntax() 511 512 def translate(self, translator): 513 translator.createConfiguration(self.getName()) 514 translator.setRule(self.getRuleString()) 515 516 paths = self.extractChildrenByClass([Path, GroupPath]) 517 translator.setElementSequence(self.getConfigurableElements()) 518 for path in paths: 519 path.translate(translator) 520 521 def copy(self): 522 """return a shallow copy of the configuration""" 523 524 # create configuration or subclass copy 525 confCopy = self.__class__() 526 527 # add children 528 confCopy.children = list(self.children) 529 530 # add option 531 confCopy.option = self.option.copy() 532 533 return confCopy 534 535class GroupConfiguration(Configuration): 536 tag = "GroupConfiguration" 537 optionNames = ["Name"] 538 match = re.compile(r"(supConf|confGroup|confType) *:").match 539 childWhiteList = ["Rule", "Operator", "GroupConfiguration", "Configuration", "GroupPath"] 540 541 def composition(self, context): 542 """add itself in context for configuration composition 543 544 Composition is the fact that group configuration with the same name defined 545 in a parent will give their rule children to this configuration 546 """ 547 548 # copyItself 549 selfCopy = self.copy() 550 551 # make all needed composition 552 super(GroupConfiguration, self).composition(context) 553 554 # add the copy in context for futur configuration composition 555 context.getConfigurations().append(selfCopy) 556 557 558 def getConfigurableElements(self): 559 """return a list. Each elements consist of a list of configurable element of a configuration 560 561 return a list consisting of all configurable elements for each configuration. 562 These configurable elements are organized in a list""" 563 configurableElements = [] 564 565 configurations = self.extractChildrenByClass([Configuration]) 566 for configuration in configurations: 567 configurableElements.append(configuration.getConfigurableElements()) 568 569 groudeConfigurations = self.extractChildrenByClass([GroupConfiguration]) 570 for groudeConfiguration in groudeConfigurations: 571 configurableElements += groudeConfiguration.getConfigurableElements() 572 573 return configurableElements 574 575 def translate(self, translator): 576 for child in self.extractChildrenByClass([Configuration, GroupConfiguration]): 577 child.translate(translator) 578 579# ---------------------------------------------------------- 580 581class Domain(ElementWithRuleInheritance, ElementWithTag): 582 tag = "domain" 583 sequenceAwareKeyword = "sequenceAware" 584 585 match = re.compile(r"domain *:").match 586 optionNames = ["Name", sequenceAwareKeyword] 587 childWhiteList = ["Configuration", "GroupConfiguration", "Rule", "Operator"] 588 589 def propagate(self, context=PropagationContext): 590 """ propagate name, sequenceAwareness and rule to children""" 591 592 # call the propagate method of all children 593 super(Domain, self).propagate(context) 594 595 self.checkConfigurableElementUnicity() 596 597 def Inheritance(self, context): 598 """check for domain name, sequence awarness and rules inheritance""" 599 # check for domain name and sequence awarness inheritance 600 self.OptionsInheritance(context) 601 602 # check for rule inheritance 603 self.ruleInheritance(context) 604 605 def OptionsInheritance(self, context): 606 """ make domain name and sequence awareness inheritance 607 608 join to the domain name all domain names defined in context and 609 if any domain in context is sequence aware, set sequenceAwareness to True""" 610 611 # add domain options to context 612 context.getDomainOptions().append(self.option.copy()) 613 614 # set name to the junction of all domain name in context 615 self.setName(".".join(context.getDomainOptions().getOptionItems("Name"))) 616 617 # get sequenceAwareness of all domains in context 618 sequenceAwareList = context.getDomainOptions().getOptionItems(self.sequenceAwareKeyword) 619 # or operation on all booleans in sequenceAwareList 620 sequenceAwareness = False 621 for sequenceAware in sequenceAwareList: 622 sequenceAwareness = sequenceAwareness or sequenceAware 623 # current domain sequenceAwareness = sequenceAwareness 624 self.option.setOption(self.sequenceAwareKeyword, sequenceAwareness) 625 626 627 def extractOptions(self, line): 628 """Extract options from the definition line""" 629 options = super(Domain, self).extractOptions(line) 630 631 sequenceAwareIndex = self.optionNames.index(self.sequenceAwareKeyword) 632 633 # translate the keyword self.sequenceAwareKeyword if specified to boolean True, 634 # to False otherwise 635 try: 636 if options[sequenceAwareIndex] == self.sequenceAwareKeyword: 637 options[sequenceAwareIndex] = True 638 else: 639 options[sequenceAwareIndex] = False 640 except IndexError: 641 options = options + [None] * (sequenceAwareIndex - len(options)) + [False] 642 return options 643 644 def getRootConfiguration(self): 645 """return the root configuration group""" 646 configurations = self.extractChildrenByClass([Configuration, GroupConfiguration]) 647 648 configurationRoot = GroupConfiguration() 649 650 configurationRoot.addChildren(configurations) 651 652 return configurationRoot 653 654 # TODO: don't do that in the parser, let the PFW tell you that 655 def checkConfigurableElementUnicity(self): 656 """ check that all configurable elements defined in child configuration are the sames""" 657 658 # get a list. Each elements of is the configurable element list of a configuration 659 configurableElementsList = self.getRootConfiguration().getConfigurableElements() 660 661 # if at least two configurations in the domain 662 if len(configurableElementsList) > 1: 663 664 # get first configuration configurable element list sort 665 configurableElementsList0 = list(configurableElementsList[0]) 666 configurableElementsList0.sort() 667 668 for configurableElements in configurableElementsList: 669 # sort current configurable element list 670 auxConfigurableElements = list(configurableElements) 671 auxConfigurableElements.sort() 672 673 if auxConfigurableElements != configurableElementsList0: 674 # if different, 2 configurations those not have the same configurable element 675 # list => one or more configurable element is missing in one of the 2 676 # configuration 677 raise UndefinedParameter(self.getName()) 678 679 680 def translate(self, translator): 681 sequence_aware = self.option.getOption(self.sequenceAwareKeyword) 682 translator.createDomain(self.getName(), sequence_aware) 683 684 configurations = self.getRootConfiguration() 685 configurableElementsList = configurations.getConfigurableElements() 686 687 # add configurable elements 688 if len(configurableElementsList) != 0: 689 for configurableElement in configurableElementsList[0]: 690 translator.addElement(configurableElement) 691 692 configurations.translate(translator) 693 694class GroupDomain(Domain): 695 tag = "groupDomain" 696 match = re.compile(r"(supDomain|domainGroup) *:").match 697 childWhiteList = ["GroupDomain", "Domain", "GroupConfiguration", "Rule", "Operator"] 698 699 def translate(self, translator): 700 for child in self.extractChildrenByClass([Domain, GroupDomain]): 701 child.translate(translator) 702 703# ---------------------------------------------------------- 704 705class Root(Element): 706 tag = "root" 707 childWhiteList = ["Domain", "GroupDomain"] 708 709 710# =========================================== 711""" Syntax error Exceptions""" 712# =========================================== 713 714class MySyntaxProblems(SyntaxError): 715 comment = "syntax error in %(line)s " 716 717 def __init__(self, line=None, num=None): 718 self.setLine(line, num) 719 720 def __str__(self): 721 722 if self.line: 723 self.comment = self.comment % {"line" : repr(self.line)} 724 if self.num: 725 self.comment = "Line " + str(self.num) + ", " + self.comment 726 return self.comment 727 728 def setLine(self, line, num): 729 self.line = str(line) 730 self.num = num 731 732 733# --------------------------------------------------------- 734 735class MyPropagationError(MySyntaxProblems): 736 """ Syntax error Exceptions used in the propagation step""" 737 pass 738 739class UndefinedParameter(MyPropagationError): 740 comment = "Configurations in domain '%(domainName)s' do not all set the same parameters " 741 def __init__(self, domainName): 742 self.domainName = domainName 743 def __str__(self): 744 return self.comment % {"domainName" : self.domainName} 745 746 747# ----------------------------------------------------- 748""" Syntax error Exceptions used by parser""" 749 750class MySyntaxError(MySyntaxProblems): 751 """ Syntax error Exceptions used by parser""" 752 pass 753 754class MySyntaxWarning(MySyntaxProblems): 755 """ Syntax warning Exceptions used by parser""" 756 pass 757 758class IndentationSyntaxError(MySyntaxError): 759 comment = """syntax error in %(line)s has no father element. 760 You can only increment indentation by one tabutation per line")""" 761 762class EmptyLineWarning(MySyntaxWarning): 763 comment = "warning : %(line)s is an empty line and has been ommited" 764 765class CommentWarning(MySyntaxWarning): 766 comment = "warning : %(line)s is a commentary and has been ommited" 767 768class ChildNotPermitedError(MySyntaxError): 769 def __init__(self, line, fatherElement, childElement): 770 self.comment = "syntax error in %(line)s, " + fatherElement.tag + " should not have a " \ 771 + childElement.tag + " child." 772 super(ChildNotPermitedError, self).__init__(line) 773 774 775class UnknownElementTypeError(MySyntaxError): 776 comment = " error in line %(line)s , not known element type were matched " 777 778class SpaceInIndentationError(MySyntaxError): 779 comment = " error in ,%(line)s space is not permited in indentation" 780 781 782# ============================================ 783"""Class creating the DOM elements from a stream""" 784# ============================================ 785 786class ElementsFactory(object): 787 """Element factory, return an instance of the first matching element 788 789 Test each element list in elementClass and instanciate it if it's methode match returns True 790 The method match is called with input line as argument 791 """ 792 def __init__(self): 793 self.elementClass = [ 794 EmptyLine, 795 Commentary, 796 GroupDomain, 797 Domain, 798 Path, 799 GroupConfiguration, 800 Configuration, 801 Operator, 802 Rule, 803 GroupPath 804 ] 805 806 def createElementFromLine(self, line): 807 """return an instance of the first matching element 808 809 Test each element list in elementClass and instanciate it if it's methode match returns True 810 The method match is called with the argument line. 811 Raise UnknownElementTypeError if no element matched. 812 """ 813 for element in self.elementClass: 814 if element.match(line): 815 # print (line + element.__class__.__name__) 816 return element(line) 817 # if we have not find any 818 raise UnknownElementTypeError(line) 819 820#------------------------------------------------------ 821 822class Parser(object): 823 """Class implementing the parser""" 824 def __init__(self): 825 self.rankPattern = re.compile(r"^([\t ]*)(.*)") 826 self.elementFactory = ElementsFactory() 827 self.previousRank = 0 828 829 def __parseLine__(self, line): 830 831 rank, rest = self.__getRank__(line) 832 833 # instanciate the coresponding element 834 element = self.elementFactory.createElementFromLine(rest) 835 836 self.__checkIndentation__(rank) 837 838 return rank, element 839 840 def __getRank__(self, line): 841 """return the rank, the name and the option of the input line 842 843the rank is the number of tabulation (\t) at the line beginning. 844the rest is the rest of the line.""" 845 # split line in rank and rest 846 rank = self.rankPattern.match(line) 847 if rank: 848 rank, rest = rank.group(1, 2) 849 else: 850 raise MySyntaxError(line) 851 852 # check for empty line 853 if rest == "": 854 raise EmptyLineWarning(line) 855 856 # check for space in indentation 857 if rank.find(" ") > -1: 858 raise SpaceInIndentationError(line) 859 860 rank = len(rank) + 1 # rank starts at 1 861 862 863 return rank, rest 864 865 866 def __checkIndentation__(self, rank): 867 """check if indentation > previous indentation + 1. If so, raise IndentationSyntaxError""" 868 if rank > self.previousRank + 1: 869 raise IndentationSyntaxError() 870 self.previousRank = rank 871 872 def parse(self, stream, verbose=False): 873 """parse a stream, usually a opened file""" 874 myroot = Root("root") 875 context = [myroot] # root is element of rank 0 876 877 for num, line in enumerate(stream): 878 try: 879 rank, myelement = self.__parseLine__(line) 880 881 while len(context) > rank: 882 context.pop() 883 context.append(myelement) 884 context[-2].addChild(myelement) 885 886 except MySyntaxWarning as ex: 887 ex.setLine(line, num + 1) 888 if verbose: 889 sys.stderr.write("{}\n".format(ex)) 890 891 except MySyntaxError as ex: 892 ex.setLine(line, num + 1) 893 raise 894 895 return myroot 896 897