# -*- coding: utf-8 -*-
"""
syntax_element
----------------------------------
Each syntax element is defined as a class in the syntax_element module, to
enable basic formatting and query function for the parse tree.
"""
[docs]class SyntaxElement(object):
"""
The class is a base implementation for all the syntax elements
All the derived classes would inherit this syntax element, and they only
differ in their names.
The base implemenation provides possibility for inspection and querying
the parsed syntax elements.
"""
def __init__(self, elements):
"""
Constructor used by `>>` operator after parsing
This is a standard interface used in funcparserlib
:param elements: the parsed subelements provided by parser
"""
self.elements = elements
def __str__(self):
return "{0}({1})".format(type(self).__name__, str(self.elements))
def __repr__(self):
return self.__str__()
[docs] def original_code(self):
"""
Return the token arranged by original string with only space and
newline, and leave out all trailing newline and spaces
Intended for roundtripping
:return: string contained in the instance
"""
return SyntaxElement._create_string(self.search('Token'))
[docs] def code(self):
"""
Create a predefined representation of all the tokens in the instance
:return: formatted tokens contained in the instance
"""
ROW = 0
COLUMN = 1
tokens = self.search('Token')
min_row_token = min(tokens, key=lambda t: t.start[ROW])
min_row_position = min_row_token.start[ROW]
min_column_token = min(tokens, key=lambda t: t.start[COLUMN])
min_column_position = min_column_token.start[COLUMN]
row_offset = 1 - min_row_position # nominal start position is 1
column_offset = 1 - min_column_position # nominal start position is 1
return SyntaxElement._create_string(tokens, row_offset, column_offset)
@staticmethod
def _create_string(tokens, row_offset=0, column_offset=0):
"""
Return the token arranged by original string with only space and
newline, and leave out all trailing newline and spaces with a
predefined offset.
:return: string created by the tokens and offset
"""
def append_line(old_string, start_position, value):
"""
:return: appended string at predefined positions
"""
assert start_position >= len(old_string)
no_of_missing_char = start_position - len(old_string) - 1
old_string += ' ' * no_of_missing_char
old_string += value
return old_string
ROW = 0
COLUMN = 1
return_results = []
for token in tokens:
token_row = token.start[ROW] + row_offset
token_column = token.start[COLUMN] + column_offset
if token_row > len(return_results):
no_of_missing_lines = token_row - len(return_results)
return_results.extend([''] * (no_of_missing_lines - 1))
new_line = append_line('', token_column, token.value)
return_results.append(new_line)
else:
old_line = return_results[token_row - 1]
new_line = append_line(old_line, token_column, token.value)
return_results[token_row - 1] = new_line
return "\n".join(return_results)
[docs] def search(self, type_name):
"""
Create a predefined representation of all the tokens in the instance
:param type_name: use the class name to find all matching subelements
:return: list of found elements of the class type_name
"""
found_elements = []
SyntaxElement.find(self, type_name, found_elements)
return found_elements
[docs] @staticmethod
def find(syntax_element, type_name, result):
"""
Search the matching elements with pre-order tree trasversal
:param syntax_element: the syntax element to be explored
:param type_name: type name to be searched
:param result: mutable list to store the results
:return: no explicit return, communication through result variable
"""
if type(syntax_element).__name__ == type_name:
result.append(syntax_element)
if hasattr(syntax_element, 'elements'):
SyntaxElement.find(syntax_element.elements,
type_name, result)
elif hasattr(syntax_element, '__getitem__'):
for el in syntax_element:
SyntaxElement.find(el, type_name, result)
class _Definition(SyntaxElement):
"""
Base class for definition class, to support additional functionalities
"""
def name(self):
"""
:return: the name of the definition
"""
token_name = self.search('ClassSpecifier')[0].elements[0]
assert token_name.type == 'ident'
return token_name.value
def prefix(self):
"""
:return: the prefix of the definition
"""
class_prefix = self.search('ClassPrefixes')[0]
return class_prefix.code()
def class_type(self):
"""
:return: the type of the definition
"""
class_prefix = self.search('ClassPrefixes')[0]
return class_prefix.elements[-1].value
classes = ['Expression', 'SimpleExpression', 'LogicalExpression',
'LogicalTerm', 'LogicalFactor', 'Relation',
'ArithmeticExpression', 'Term', 'Factor', 'Primary', 'RelOp',
'MulOp', 'AddOp', 'Name', 'NamedArgument', 'NamedArguments',
'FunctionArgument', 'FunctionArguments', 'FunctionCallArgs',
'ExpressionList', 'OutputExpressionList', 'Subscript',
'ArraySubscript', 'ComponentReference', 'StringComment',
'Annotation', 'Comment', "LanguageSpecification", "BasePrefix",
"ExternalFunctionCall", "Element", "ElementList",
"Composition", "ClassSpecifier", "ClassPrefixes",
"EnumerationLiteral", "EnumList", "ImportList", "ImportClause",
"TypePrefix", "TypeSpecifier", "ConditionAttribute", "Declaration",
"ComponentDeclaration", "ComponentList", "ComponentClause",
"ForIndex", "ForIndices", "ConnectClause", "Equation", "IfEquation",
"ForEquation", "WhileEquation", "WhenEquation", "Statement",
"IfStatement", "ForStatement", "WhileStatement", "WhenStatement",
"EquationSection", "AlgorithmSection", "ExtendsClause",
"ConstrainingClause", "Modification", "ShortClassDefinition",
"ComponentDeclaration1", "ComponentClause1", "ElementReplaceable",
"ElementRedeclaration", "ElementModification",
"ElementModificationOrReplaceable", "Argument", "ArgumentList",
"ClassModification", "Assertion"]
definition_classes = ["ClassDefinition", "StoredDefinition"]
variables = globals()
for class_name in classes:
variables[class_name] = type(class_name, (SyntaxElement,), {})
for class_name in definition_classes:
variables[class_name] = type(class_name, (_Definition,), {})