aboutsummaryrefslogtreecommitdiffstats
path: root/tools/xmlGenerator/EddParser.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/xmlGenerator/EddParser.py')
-rwxr-xr-xtools/xmlGenerator/EddParser.py890
1 files changed, 890 insertions, 0 deletions
diff --git a/tools/xmlGenerator/EddParser.py b/tools/xmlGenerator/EddParser.py
new file mode 100755
index 0000000..97a59a7
--- /dev/null
+++ b/tools/xmlGenerator/EddParser.py
@@ -0,0 +1,890 @@
+#!/usr/bin/python2
+# -*-coding:utf-8 -*
+
+# Copyright (c) 2011-2014, Intel Corporation
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without modification,
+# are permitted provided that the following conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation and/or
+# other materials provided with the distribution.
+#
+# 3. Neither the name of the copyright holder nor the names of its contributors
+# may be used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+import re
+import sys
+import copy
+from itertools import izip
+from itertools import imap
+
+# =====================================================================
+""" Context classes, used during propagation and the "to PFW script" step """
+# =====================================================================
+
+class PropagationContextItem(list) :
+ """Handle an item during the propagation step"""
+ def __copy__(self):
+ """C.__copy__() -> a shallow copy of C"""
+ return self.__class__(self)
+
+class PropagationContextElement(PropagationContextItem) :
+ """Handle an Element during the propagation step"""
+ def getElementsFromName(self, name):
+ matchingElements = []
+ for element in self :
+ if element.getName() == name :
+ matchingElements.append(element)
+ return matchingElements
+
+
+class PropagationContextOption(PropagationContextItem) :
+ """Handle an Option during the propagation step"""
+ def getOptionItems (self, itemName):
+ items = []
+ for options in self :
+ items.append(options.getOption(itemName))
+ return items
+
+
+class PropagationContext() :
+ """Handle the context during the propagation step"""
+ def __init__(self, propagationContext=None) :
+
+ if propagationContext == None :
+ self._context = {
+ "DomainOptions" : PropagationContextOption() ,
+ "Configurations" : PropagationContextElement() ,
+ "ConfigurationOptions" : PropagationContextOption() ,
+ "Rules" : PropagationContextElement() ,
+ "PathOptions" : PropagationContextOption() ,
+ }
+ else :
+ self._context = propagationContext
+
+ def copy(self):
+ """return a copy of the context"""
+ contextCopy = self._context.copy()
+
+ for key in iter(self._context) :
+ contextCopy[key] = contextCopy[key].__copy__()
+
+ return self.__class__(contextCopy)
+
+ def getDomainOptions (self):
+ return self._context["DomainOptions"]
+
+ def getConfigurations (self):
+ return self._context["Configurations"]
+
+ def getConfigurationOptions (self):
+ return self._context["ConfigurationOptions"]
+
+ def getRules (self):
+ return self._context["Rules"]
+
+ def getPathOptions (self):
+ return self._context["PathOptions"]
+
+
+# =====================================================
+"""Element option container"""
+# =====================================================
+
+class Options () :
+ """handle element options"""
+ def __init__(self, options=[], optionNames=[]) :
+ self.options = dict(izip(optionNames, options))
+ # print(options,optionNames,self.options)
+
+
+ def __str__(self) :
+ ops2str = []
+ for name, argument in self.options.items() :
+ ops2str.append(str(name) + "=\"" + str(argument) + "\"")
+
+ return " ".join(ops2str)
+
+ def getOption(self, name):
+ """get option by its name, if it does not exist return empty string"""
+ return self.options.get(name, "")
+
+ def setOption(self, name, newOption):
+ """set option by its name"""
+ self.options[name] = newOption
+
+ def copy (self):
+ """D.copy() -> a shallow copy of D"""
+ copy = Options()
+ copy.options = self.options.copy()
+ return copy
+
+# ====================================================
+"""Definition of all element class"""
+# ====================================================
+
+class Element(object):
+ """ implement a basic element
+
+ It is the class base for all other elements as Domain, Configuration..."""
+ tag = "unknown"
+ optionNames = ["Name"]
+ childWhiteList = []
+ optionDelimiter = " "
+
+ def __init__(self, line=None) :
+
+ if line == None :
+ self.option = Options([], self.optionNames)
+ else :
+ self.option = self.optionFromLine(line)
+
+ self.children = []
+
+ def optionFromLine(self, line) :
+ # get ride of spaces
+ line = line.strip()
+
+ options = self.extractOptions(line)
+
+ return Options(options, self.optionNames)
+
+ def extractOptions(self, line) :
+ """return the line splited by the optionDelimiter atribute
+
+ Option list length is less or equal to the optionNames list length
+ """
+ options = line.split(self.optionDelimiter, len(self.optionNames) - 1)
+
+ # get ride of leftover spaces
+ optionsStrip = list(imap(str.strip, options))
+
+ return optionsStrip
+
+ def addChild(self, child, append=True) :
+ """ A.addChid(B) -> add B to A child list if B class name is in A white List"""
+ try:
+ # Will raise an exception if this child is not in the white list
+ self.childWhiteList.index(child.__class__.__name__)
+ # If no exception was raised, add child to child list
+
+ if append :
+ self.children.append(child)
+ else :
+ self.children.insert(0, child)
+
+ except ValueError:
+ # the child class is not in the white list
+ raise ChildNotPermitedError("", self, child)
+
+ def addChildren(self, children, append=True) :
+ """Add a list of child"""
+ if append:
+ # Add children at the end of the child list
+ self.children.extend(children)
+ else:
+ # Add children at the begining of the child list
+ self.children = children + self.children
+
+ def childrenToString(self, prefix=""):
+ """return raw printed children """
+ body = ""
+ for child in self.children :
+ body = body + child.__str__(prefix)
+
+ return body
+
+ def __str__(self, prefix="") :
+ """return raw printed element"""
+ selfToString = prefix + " " + self.tag + " " + str(self.option)
+ return selfToString + "\n" + self.childrenToString(prefix + "\t")
+
+ def extractChildrenByClass(self, classTypeList) :
+ """return all children whose class is in the list argument
+
+ return a list of all children whose class in the list "classTypeList" (second arguments)"""
+ selectedChildren = []
+
+ for child in self.children :
+ for classtype in classTypeList :
+ if child.__class__ == classtype :
+ selectedChildren.append(child)
+ break
+ return selectedChildren
+
+ def propagate (self, context=PropagationContext()):
+ """call the propagate method of all children"""
+ for child in self.children :
+ child.propagate(context)
+
+ def getName(self):
+ """return name option value. If none return "" """
+ return self.option.getOption("Name")
+
+ def setName(self, name):
+ self.option.setOption("Name", name)
+
+ def translate(self, translator):
+ for child in self.children:
+ child.translate(translator)
+
+# ----------------------------------------------------------
+
+class ElementWithTag (Element):
+ """Element of this class are declared with a tag => line == "tag: .*" """
+ def extractOptions(self, line) :
+ lineWithoutTag = line.split(":", 1)[-1].strip()
+ options = super(ElementWithTag, self).extractOptions(lineWithoutTag)
+ return options
+
+# ----------------------------------------------------------
+
+class ElementWithInheritance(Element):
+ def propagate (self, context=PropagationContext) :
+ """propagate some proprieties to children"""
+
+ # copy the context so that everything that hapend next will only affect
+ # children
+ contextCopy = context.copy()
+
+ # check for inheritance
+ self.Inheritance(contextCopy)
+
+ # call the propagate method of all children
+ super(ElementWithInheritance, self).propagate(contextCopy)
+
+
+class ElementWithRuleInheritance(ElementWithInheritance):
+ """class that will give to its children its rules"""
+ def ruleInheritance(self, context):
+ """Add its rules to the context and get context rules"""
+
+ # extract all children rule and operator
+ childRules = self.extractChildrenByClass([Operator, Rule])
+
+ # get context rules
+ contextRules = context.getRules()
+
+ # adopt rules of the beginning of the context
+ self.addChildren(contextRules, append=False)
+
+ # add previously extract rules to the context
+ contextRules += childRules
+
+
+# ----------------------------------------------------------
+
+class EmptyLine (Element) :
+ """This class represents an empty line.
+
+ Will raise "EmptyLineWarning" exception at instanciation."""
+
+ tag = "emptyLine"
+ match = re.compile(r"[ \t]*\n?$").match
+ def __init__ (self, line):
+ raise EmptyLineWarning(line)
+
+# ----------------------------------------------------------
+
+class Commentary(Element):
+ """This class represents a commentary.
+
+ Will raise "CommentWarning" exception at instanciation."""
+
+ tag = "commentary"
+ optionNames = ["comment"]
+ match = re.compile(r"#").match
+ def __init__ (self, line):
+ raise CommentWarning(line)
+
+# ----------------------------------------------------------
+
+class Path (ElementWithInheritance) :
+ """class implementing the "path = value" concept"""
+ tag = "path"
+ optionNames = ["Name", "value"]
+ match = re.compile(r".+=").match
+ optionDelimiter = "="
+
+ def translate(self, translator):
+ translator.setParameter(self.getName(), self.option.getOption("value"))
+
+ def Inheritance (self, context) :
+ """check for path name inheritance"""
+ self.OptionsInheritance(context)
+
+ def OptionsInheritance (self, context) :
+ """make configuration name inheritance """
+
+ context.getPathOptions().append(self.option.copy())
+ self.setName("/".join(context.getPathOptions().getOptionItems("Name")))
+
+
+class GroupPath (Path, ElementWithTag) :
+ tag = "component"
+ match = re.compile(tag + r" *:").match
+ optionNames = ["Name"]
+ childWhiteList = ["Path", "GroupPath"]
+
+ def getPathNames (self) :
+ """Return the list of all path child name"""
+
+ pathNames = []
+
+ paths = self.extractChildrenByClass([Path])
+ for path in paths :
+ pathNames.append(path.getName())
+
+ groupPaths = self.extractChildrenByClass([GroupPath])
+ for groupPath in groupPaths :
+ pathNames += groupPath.getPathNames()
+
+ return pathNames
+
+ def translate(self, translator):
+ for child in self.extractChildrenByClass([Path, GroupPath]):
+ child.translate(translator)
+
+# ----------------------------------------------------------
+
+class Rule (Element) :
+ """class implementing the rule concept
+
+ A rule is composed of a criterion, a rule type and an criterion state.
+ It should not have any child and is propagated to all configuration in parent descendants.
+ """
+
+ tag = "rule"
+ optionNames = ["criterion", "type", "element"]
+ match = re.compile(r"[a-zA-Z0-9_.]+ +(Is|IsNot|Includes|Excludes) +[a-zA-Z0-9_.]+").match
+ childWhiteList = []
+
+ def PFWSyntax (self, prefix=""):
+
+ script = prefix + \
+ self.option.getOption("criterion") + " " + \
+ self.option.getOption("type") + " " + \
+ self.option.getOption("element")
+
+ return script
+
+
+class Operator (Rule) :
+ """class implementing the operator concept
+
+ An operator contains rules and other operators
+ It is as rules propagated to all configuration children in parent descendants.
+ It should only have the name ANY or ALL to be understood by PFW.
+ """
+
+ tag = "operator"
+ optionNames = ["Name"]
+ match = re.compile(r"ANY|ALL").match
+ childWhiteList = ["Rule", "Operator"]
+
+ syntax = { "ANY" : "Any" , "ALL" : "All"}
+
+ def PFWSyntax (self, prefix=""):
+ """ return a pfw rule (ex : "Any{criterion1 is state1}") generated from "self" and its children options"""
+ script = ""
+
+ script += prefix + \
+ self.syntax[self.getName()] + "{ "
+
+ rules = self.extractChildrenByClass([Rule, Operator])
+
+ PFWRules = []
+ for rule in rules :
+ PFWRules.append(rule.PFWSyntax(prefix + " "))
+
+ script += (" , ").join(PFWRules)
+
+ script += prefix + " }"
+
+ return script
+
+# ----------------------------------------------------------
+
+class Configuration (ElementWithRuleInheritance, ElementWithTag) :
+ tag = "configuration"
+ optionNames = ["Name"]
+ match = re.compile(r"conf *:").match
+ childWhiteList = ["Rule", "Operator", "Path", "GroupPath"]
+
+ def composition (self, context):
+ """make all needed composition
+
+ Composition is the fact that group configuration with the same name defined
+ in a parent will give their rule children to this configuration
+ """
+
+ name = self.getName()
+ sameNameConf = context.getConfigurations().getElementsFromName(name)
+
+ sameNameConf.reverse()
+
+ for configuration in sameNameConf :
+ # add same name configuration rule children to self child list
+ self.addChildren(configuration.extractChildrenByClass([Operator, Rule]), append=False)
+
+
+ def propagate (self, context=PropagationContext) :
+ """propagate proprieties to children
+
+ make needed compositions, join ancestor name to its name,
+ and add rules previously defined rules"""
+
+ # make all needed composition
+ self.composition(context)
+
+ super(Configuration, self).propagate(context)
+
+ def Inheritance (self, context) :
+ """make configuration name and rule inheritance"""
+ # check for configuration name inheritance
+ self.OptionsInheritance(context)
+
+ # check for rule inheritance
+ self.ruleInheritance(context)
+
+ def OptionsInheritance (self, context) :
+ """make configuration name inheritance """
+
+ context.getConfigurationOptions().append(self.option.copy())
+ self.setName(".".join(context.getConfigurationOptions().getOptionItems("Name")))
+
+
+ def getRootPath (self) :
+
+ paths = self.extractChildrenByClass([Path, GroupPath])
+
+ rootPath = GroupPath()
+ rootPath.addChildren(paths)
+
+ return rootPath
+
+ def getConfigurableElements (self) :
+ """return all path name defined in this configuration"""
+
+ return self.getRootPath().getPathNames()
+
+ def getRuleString(self):
+ """Output this configuration's rule as a string"""
+
+ # Create a rootRule
+ ruleChildren = self.extractChildrenByClass([Rule, Operator])
+
+ # Do not create a root rule if there is only one fist level Operator rule
+ if len(ruleChildren) == 1 and ruleChildren[0].__class__ == Operator :
+ ruleroot = ruleChildren[0]
+
+ else :
+ ruleroot = Operator()
+ ruleroot.setName("ALL")
+ ruleroot.addChildren(ruleChildren)
+
+ return ruleroot.PFWSyntax()
+
+ def translate(self, translator):
+ translator.createConfiguration(self.getName())
+ translator.setRule(self.getRuleString())
+
+ paths = self.extractChildrenByClass([Path, GroupPath])
+ translator.setElementSequence(self.getConfigurableElements())
+ for path in paths:
+ path.translate(translator)
+
+ def copy (self) :
+ """return a shallow copy of the configuration"""
+
+ # create configuration or subclass copy
+ confCopy = self.__class__()
+
+ # add children
+ confCopy.children = list(self.children)
+
+ # add option
+ confCopy.option = self.option.copy()
+
+ return confCopy
+
+class GroupConfiguration (Configuration) :
+ tag = "GroupConfiguration"
+ optionNames = ["Name"]
+ match = re.compile(r"(supConf|confGroup|confType) *:").match
+ childWhiteList = ["Rule", "Operator", "GroupConfiguration", "Configuration", "GroupPath"]
+
+ def composition (self, context) :
+ """add itself in context for configuration composition
+
+ Composition is the fact that group configuration with the same name defined
+ in a parent will give their rule children to this configuration
+ """
+
+ # copyItself
+ selfCopy = self.copy()
+
+ # make all needed composition
+ super(GroupConfiguration, self).composition(context)
+
+ # add the copy in context for futur configuration composition
+ context.getConfigurations().append(selfCopy)
+
+
+ def getConfigurableElements (self) :
+ """return a list. Each elements consist of a list of configurable element of a configuration
+
+ return a list consisting of all configurable elements for each configuration.
+ These configurable elements are organized in a list"""
+ configurableElements = []
+
+ configurations = self.extractChildrenByClass([Configuration])
+ for configuration in configurations :
+ configurableElements.append(configuration.getConfigurableElements())
+
+ groudeConfigurations = self.extractChildrenByClass([GroupConfiguration])
+ for groudeConfiguration in groudeConfigurations :
+ configurableElements += groudeConfiguration.getConfigurableElements()
+
+ return configurableElements
+
+ def translate(self, translator):
+ for child in self.extractChildrenByClass([Configuration, GroupConfiguration]):
+ child.translate(translator)
+
+# ----------------------------------------------------------
+
+class Domain (ElementWithRuleInheritance, ElementWithTag) :
+ tag = "domain"
+ sequenceAwareKeyword = "sequenceAware"
+
+ match = re.compile(r"domain *:").match
+ optionNames = ["Name", sequenceAwareKeyword]
+ childWhiteList = ["Configuration", "GroupConfiguration", "Rule", "Operator"]
+
+ def propagate (self, context=PropagationContext) :
+ """ propagate name, sequenceAwareness and rule to children"""
+
+ # call the propagate method of all children
+ super(Domain, self).propagate(context)
+
+ self.checkConfigurableElementUnicity()
+
+ def Inheritance (self, context) :
+ """check for domain name, sequence awarness and rules inheritance"""
+ # check for domain name and sequence awarness inheritance
+ self.OptionsInheritance(context)
+
+ # check for rule inheritance
+ self.ruleInheritance(context)
+
+ def OptionsInheritance(self, context) :
+ """ make domain name and sequence awareness inheritance
+
+ join to the domain name all domain names defined in context and
+ if any domain in context is sequence aware, set sequenceAwareness to True"""
+
+ # add domain options to context
+ context.getDomainOptions().append(self.option.copy())
+
+ # set name to the junction of all domain name in context
+ self.setName(".".join(context.getDomainOptions().getOptionItems("Name")))
+
+ # get sequenceAwareness of all domains in context
+ sequenceAwareList = context.getDomainOptions().getOptionItems(self.sequenceAwareKeyword)
+ # or operation on all booleans in sequenceAwareList
+ sequenceAwareness = False
+ for sequenceAware in sequenceAwareList :
+ sequenceAwareness = sequenceAwareness or sequenceAware
+ # current domain sequenceAwareness = sequenceAwareness
+ self.option.setOption(self.sequenceAwareKeyword, sequenceAwareness)
+
+
+ def extractOptions(self, line) :
+ """Extract options from the definition line"""
+ options = super(Domain, self).extractOptions(line)
+
+ sequenceAwareIndex = self.optionNames.index(self.sequenceAwareKeyword)
+
+ # translate the keyword self.sequenceAwareKeyword if specified to boolean True,
+ # to False otherwise
+ try :
+ if options[sequenceAwareIndex] == self.sequenceAwareKeyword :
+ options[sequenceAwareIndex] = True
+ else:
+ options[sequenceAwareIndex] = False
+ except IndexError :
+ options = options + [None] * (sequenceAwareIndex - len(options)) + [False]
+ return options
+
+ def getRootConfiguration (self) :
+ """return the root configuration group"""
+ configurations = self.extractChildrenByClass([Configuration, GroupConfiguration])
+
+ configurationRoot = GroupConfiguration()
+
+ configurationRoot.addChildren(configurations)
+
+ return configurationRoot
+
+ # TODO: don't do that in the parser, let the PFW tell you that
+ def checkConfigurableElementUnicity (self):
+ """ check that all configurable elements defined in child configuration are the sames"""
+
+ # get a list. Each elements of is the configurable element list of a configuration
+ configurableElementsList = self.getRootConfiguration().getConfigurableElements()
+
+ # if at least two configurations in the domain
+ if len(configurableElementsList) > 1 :
+
+ # get first configuration configurable element list sort
+ configurableElementsList0 = list(configurableElementsList[0])
+ configurableElementsList0.sort()
+
+ for configurableElements in configurableElementsList :
+ # sort current configurable element list
+ auxConfigurableElements = list(configurableElements)
+ auxConfigurableElements.sort()
+
+ if auxConfigurableElements != configurableElementsList0 :
+ # if different, 2 configurations those not have the same configurable element list
+ # => one or more configurable element is missing in one of the 2 configuration
+ raise UndefinedParameter(self.getName())
+
+
+ def translate(self, translator):
+ sequence_aware = self.option.getOption(self.sequenceAwareKeyword)
+ translator.createDomain(self.getName(), sequence_aware)
+
+ configurations = self.getRootConfiguration()
+ configurableElementsList = configurations.getConfigurableElements()
+
+ # add configurable elements
+ if len(configurableElementsList) != 0 :
+ for configurableElement in configurableElementsList[0] :
+ translator.addElement(configurableElement)
+
+ configurations.translate(translator)
+
+class GroupDomain (Domain) :
+ tag = "groupDomain"
+ match = re.compile(r"(supDomain|domainGroup) *:").match
+ childWhiteList = ["GroupDomain", "Domain", "GroupConfiguration", "Rule", "Operator"]
+
+ def translate(self, translator):
+ for child in self.extractChildrenByClass([Domain, GroupDomain]):
+ child.translate(translator)
+
+# ----------------------------------------------------------
+
+class Root(Element):
+ tag = "root"
+ childWhiteList = ["Domain", "GroupDomain"]
+
+
+# ===========================================
+""" Syntax error Exceptions"""
+# ===========================================
+
+class MySyntaxProblems(SyntaxError) :
+ comment = "syntax error in %(line)s "
+
+ def __init__(self, line=None, num=None):
+ self.setLine(line, num)
+
+ def __str__(self):
+
+ if self.line :
+ self.comment = self.comment % {"line" : repr(self.line)}
+ if self.num :
+ self.comment = "Line " + str(self.num) + ", " + self.comment
+ return self.comment
+
+ def setLine (self, line, num):
+ self.line = str(line)
+ self.num = num
+
+
+# ---------------------------------------------------------
+
+class MyPropagationError(MySyntaxProblems) :
+ """ Syntax error Exceptions used in the propagation step"""
+ pass
+
+class UndefinedParameter(MyPropagationError) :
+ comment = "Configurations in domain '%(domainName)s' do not all set the same parameters "
+ def __init__ (self, domainName):
+ self.domainName = domainName
+ def __str__ (self):
+ return self.comment % { "domainName" : self.domainName }
+
+
+# -----------------------------------------------------
+""" Syntax error Exceptions used by parser"""
+
+class MySyntaxError(MySyntaxProblems) :
+ """ Syntax error Exceptions used by parser"""
+ pass
+
+class MySyntaxWarning(MySyntaxProblems) :
+ """ Syntax warning Exceptions used by parser"""
+ pass
+
+class IndentationSyntaxError(MySyntaxError) :
+ comment = """syntax error in %(line)s has no father element.
+ You can only increment indentation by one tabutation per line")"""
+
+class EmptyLineWarning(MySyntaxWarning):
+ comment = "warning : %(line)s is an empty line and has been ommited"
+
+class CommentWarning(MySyntaxWarning):
+ comment = "warning : %(line)s is a commentary and has been ommited"
+
+class ChildNotPermitedError(MySyntaxError):
+ def __init__(self, line, fatherElement, childElement):
+ self.comment = "syntax error in %(line)s, " + fatherElement.tag + " should not have a " + childElement.tag + " child."
+ super(ChildNotPermitedError, self).__init__(line)
+
+
+class UnknownElementTypeError(MySyntaxError):
+ comment = " error in line %(line)s , not known element type were matched "
+
+class SpaceInIndentationError(MySyntaxError):
+ comment = " error in ,%(line)s space is not permited in indentation"
+
+
+# ============================================
+"""Class creating the DOM elements from a stream"""
+# ============================================
+
+class ElementsFactory(object) :
+ """Element factory, return an instance of the first matching element
+
+ Test each element list in elementClass and instanciate it if it's methode match returns True
+ The method match is called with input line as argument
+ """
+ def __init__ (self):
+ self.elementClass = [
+ EmptyLine ,
+ Commentary,
+ GroupDomain,
+ Domain,
+ Path,
+ GroupConfiguration,
+ Configuration,
+ Operator,
+ Rule,
+ GroupPath
+ ]
+
+ def createElementFromLine (self, line) :
+ """return an instance of the first matching element
+
+ Test each element list in elementClass and instanciate it if it's methode match returns True
+ The method match is called with the argument line.
+ Raise UnknownElementTypeError if no element matched.
+ """
+ for element in self.elementClass :
+ if element.match(line) :
+ # print (line + element.__class__.__name__)
+ return element(line)
+ # if we have not find any
+ raise UnknownElementTypeError(line)
+
+#------------------------------------------------------
+
+class Parser(object) :
+ """Class implementing the parser"""
+ def __init__(self):
+ self.rankPattern = re.compile(r"^([\t ]*)(.*)")
+ self.elementFactory = ElementsFactory()
+ self.previousRank = 0
+
+ def __parseLine__(self, line):
+
+ rank, rest = self.__getRank__(line)
+
+ # instanciate the coresponding element
+ element = self.elementFactory.createElementFromLine(rest)
+
+ self.__checkIndentation__(rank)
+
+ return rank, element
+
+ def __getRank__(self, line):
+ """return the rank, the name and the option of the input line
+
+the rank is the number of tabulation (\t) at the line beginning.
+the rest is the rest of the line."""
+ # split line in rank and rest
+ rank = self.rankPattern.match(line)
+ if rank :
+ rank, rest = rank.group(1, 2)
+ else :
+ raise MySyntaxError(line)
+
+ # check for empty line
+ if rest == "" :
+ raise EmptyLineWarning(line)
+
+ # check for space in indentation
+ if rank.find(" ") > -1 :
+ raise SpaceInIndentationError(line)
+
+ rank = len (rank) + 1 # rank starts at 1
+
+
+ return rank, rest
+
+
+ def __checkIndentation__(self, rank):
+ """check if indentation > previous indentation + 1. If so, raise IndentationSyntaxError"""
+ if (rank > self.previousRank + 1) :
+ raise IndentationSyntaxError()
+ self.previousRank = rank
+
+ def parse(self, stream, verbose=False):
+ """parse a stream, usually a opened file"""
+ myroot = Root("root")
+ context = [myroot] # root is element of rank 0
+ warnings = ""
+
+ for num, line in enumerate(stream):
+ try:
+ rank, myelement = self.__parseLine__(line)
+
+ while len(context) > rank :
+ context.pop()
+ context.append(myelement)
+ context[-2].addChild(myelement)
+
+ except MySyntaxWarning, ex:
+ ex.setLine(line, num + 1)
+ if verbose :
+ print >>sys.stderr, ex
+
+ except MySyntaxError, ex :
+ ex.setLine(line, num + 1)
+ raise
+
+ return myroot
+