You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
897 lines
30 KiB
897 lines
30 KiB
#!/usr/bin/python3
|
|
# -*-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
|
|
|
|
# For Python 2.x/3.x compatibility
|
|
try:
|
|
from itertools import izip as zip
|
|
from itertools import imap as map
|
|
except:
|
|
pass
|
|
|
|
# =====================================================================
|
|
""" 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(zip(optionNames, options))
|
|
# print(options,optionNames,self.options)
|
|
|
|
|
|
def __str__(self):
|
|
ops2str = []
|
|
for name, argument in list(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(map(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
|
|
|
|
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 as ex:
|
|
ex.setLine(line, num + 1)
|
|
if verbose:
|
|
sys.stderr.write("{}\n".format(ex))
|
|
|
|
except MySyntaxError as ex:
|
|
ex.setLine(line, num + 1)
|
|
raise
|
|
|
|
return myroot
|
|
|