# EMACS settings: -*- tab-width: 2; indent-tabs-mode: t; python-indent-offset: 2 -*-
# vim: tabstop=2:shiftwidth=2:noexpandtab
# kate: tab-width 2; replace-tabs off; indent-width 2;
#
# ==============================================================================
# Authors: Patrick Lehmann
# Martin Zabel
#
# Python Module: TODO
#
# License:
# ==============================================================================
# Copyright 2007-2016 Patrick Lehmann - Dresden, Germany
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
#
# load dependencies
from lib.Functions import Init
from lib.Parser import MismatchingParserResult, MatchingParserResult, EmptyChoiseParserResult, GreedyMatchingParserResult
from lib.Parser import SpaceToken, CharacterToken, StringToken, NumberToken, Tokenizer
__api__ = [
'CodeDOMMeta',
'CodeDOMObject',
'Expression',
'UnaryExpression',
'NotExpression',
'BinaryExpression',
'LogicalExpression',
'CompareExpression',
'EqualExpression',
'UnequalExpression',
'LessThanExpression',
'LessThanEqualExpression',
'GreaterThanExpression',
'GreaterThanEqualExpression',
'AndExpression',
'OrExpression',
'XorExpression',
'InExpression',
'NotInExpression',
'Function',
'ListElement',
'Literal',
'StringLiteral',
'IntegerLiteral',
'Identifier',
'Statement',
'BlockStatement',
'ConditionalBlockStatement',
'EmptyLine',
'CommentLine',
'BlockedStatement',
'ExpressionChoice'
]
__all__ = __api__
DEBUG = False#True
# ==============================================================================
# Base classes
# ==============================================================================
[docs]class CodeDOMObject(metaclass=CodeDOMMeta):
def __init__(self):
super().__init__()
@classmethod
[docs] def Parse(cls, string, printChar):
parser = cls.GetParser()
parser.send(None)
try:
for token in Tokenizer.GetWordTokenizer(string):
if printChar: print("{BLUE}{token!s}{NOCOLOR}".format(token=token, **Init.Foreground))
parser.send(token)
# FIXME: print("send empty token")
parser.send(None)
except MatchingParserResult as ex:
return ex.value
except MismatchingParserResult as ex:
print("ERROR: {0}".format(ex.value))
# print("close root parser")
# parser.close()
# ==============================================================================
# Expressions
# ==============================================================================
[docs]class Expression(CodeDOMObject):
pass
[docs]class UnaryExpression(Expression):
def __init__(self, child):
super().__init__()
self._child = child
@property
def Child(self):
return self._child
[docs]class NotExpression(UnaryExpression):
def __init__(self, child):
super().__init__(child)
__PARSER_EXPRESSIONS__ = None
@classmethod
[docs] def GetParser(cls):
if DEBUG: print("init NotExpressionParser")
child = None
# match for "!"
token = yield
# if (not isinstance(token, StringToken)): raise MismatchingParserResult()
# if (token.Value != "not"): raise MismatchingParserResult()
if (not isinstance(token, CharacterToken)): raise MismatchingParserResult()
if (token.Value != "!"): raise MismatchingParserResult()
# match for optional whitespace
token = yield
if isinstance(token, SpaceToken): token = yield
# match for sub expression
# ==========================================================================
parser = cls.__PARSER_EXPRESSIONS__.GetParser()
parser.send(None)
try:
while True:
parser.send(token)
token = yield
except MatchingParserResult as ex:
child = ex.value
# construct result
result = cls(child)
if DEBUG: print("NotExpressionParser: matched {0}".format(result))
raise MatchingParserResult(result)
def __str__(self):
return "not {0}".format(self._child.__str__())
[docs]class BinaryExpression(Expression):
def __init__(self, leftChild, rightChild):
super().__init__()
self._leftChild = leftChild
self._rightChild = rightChild
@property
def LeftChild(self):
return self._leftChild
@property
def RightChild(self):
return self._rightChild
__PARSER_NAME__ = None
__PARSER_LHS_EXPRESSIONS__ = None
__PARSER_RHS_EXPRESSIONS__ = None
__PARSER_OPERATOR__ = None
@classmethod
[docs] def GetParser(cls):
if DEBUG: print("init " + cls.__PARSER_NAME__)
leftChild = None
rightChild = None
# match for opening (
token = yield
if (not isinstance(token, CharacterToken)): raise MismatchingParserResult()
if (token.Value != "("): raise MismatchingParserResult()
# match for optional whitespace
token = yield
if isinstance(token, SpaceToken): token = yield
# match for sub expression
# ==========================================================================
parser = cls.__PARSER_LHS_EXPRESSIONS__.GetParser()
parser.send(None)
try:
while True:
parser.send(token)
token = yield
except GreedyMatchingParserResult as ex:
leftChild = ex.value
except MatchingParserResult as ex:
leftChild = ex.value
token = yield
# match for optional whitespace
if isinstance(token, SpaceToken): token = yield
# match for operator keyword or sign(s)
if isinstance(cls.__PARSER_OPERATOR__, str):
if (not isinstance(token, StringToken)): raise MismatchingParserResult()
if (token.Value != cls.__PARSER_OPERATOR__): raise MismatchingParserResult()
token = yield
if (not isinstance(token, SpaceToken)): raise MismatchingParserResult()
token = yield
elif isinstance(cls.__PARSER_OPERATOR__, tuple):
for sign in cls.__PARSER_OPERATOR__:
if (not isinstance(token, CharacterToken)): raise MismatchingParserResult()
if (token.Value != sign): raise MismatchingParserResult()
token = yield
# match for optional whitespace
if isinstance(token, SpaceToken): token = yield
elif isinstance(cls.__PARSER_OPERATOR__, list):
for kw in cls.__PARSER_OPERATOR__[:-1]:
if (not isinstance(token, StringToken)): raise MismatchingParserResult()
if (token.Value != kw): raise MismatchingParserResult()
token = yield
if (not isinstance(token, SpaceToken)): raise MismatchingParserResult()
token = yield
kw = cls.__PARSER_OPERATOR__[-1]
if (not isinstance(token, StringToken)): raise MismatchingParserResult()
if (token.Value != kw): raise MismatchingParserResult()
token = yield
if (not isinstance(token, SpaceToken)): raise MismatchingParserResult()
token = yield
# match for sub expression
# ==========================================================================
parser = cls.__PARSER_RHS_EXPRESSIONS__.GetParser()
parser.send(None)
try:
while True:
parser.send(token)
token = yield
except GreedyMatchingParserResult as ex:
rightChild = ex.value
except MatchingParserResult as ex:
rightChild = ex.value
token = yield
# match for optional whitespace
if isinstance(token, SpaceToken): token = yield
# match for closing )
if (not isinstance(token, CharacterToken)): raise MismatchingParserResult()
if (token.Value != ")"): raise MismatchingParserResult()
# construct result
result = cls(leftChild, rightChild)
if DEBUG: print(cls.__PARSER_NAME__ + ": matched {0}".format(result))
raise MatchingParserResult(result)
def __str__(self):
if isinstance(self.__PARSER_OPERATOR__, tuple): op = "".join(self.__PARSER_OPERATOR__)
elif isinstance(self.__PARSER_OPERATOR__, list): op = " ".join(self.__PARSER_OPERATOR__)
else: op = self.__PARSER_OPERATOR__
return "({left!s} {op} {right!s})".format(left=self._leftChild, op=op, right=self._rightChild)
[docs]class LogicalExpression(BinaryExpression):
pass
[docs]class CompareExpression(LogicalExpression):
pass
[docs]class EqualExpression(CompareExpression):
__PARSER_NAME__ = "EqualExpressionParser"
__PARSER_OPERATOR__ = ("=",)
[docs]class UnequalExpression(CompareExpression):
__PARSER_NAME__ = "UnequalExpressionParser"
__PARSER_OPERATOR__ = ("!", "=")
[docs]class LessThanExpression(CompareExpression):
__PARSER_NAME__ = "LessThanExpressionParser"
__PARSER_OPERATOR__ = ("<",)
[docs]class LessThanEqualExpression(CompareExpression):
__PARSER_NAME__ = "LessThanEqualExpressionParser"
__PARSER_OPERATOR__ = ("<", "=")
[docs]class GreaterThanExpression(CompareExpression):
__PARSER_NAME__ = "GreaterThanExpressionParser"
__PARSER_OPERATOR__ = (">",)
[docs]class GreaterThanEqualExpression(CompareExpression):
__PARSER_NAME__ = "GreaterThanEqualExpressionParser"
__PARSER_OPERATOR__ = (">", "=")
[docs]class AndExpression(LogicalExpression):
__PARSER_NAME__ = "AndExpressionParser"
__PARSER_OPERATOR__ = "and"
[docs]class OrExpression(LogicalExpression):
__PARSER_NAME__ = "OrExpressionParser"
__PARSER_OPERATOR__ = "or"
[docs]class XorExpression(LogicalExpression):
__PARSER_NAME__ = "XorExpressionParser"
__PARSER_OPERATOR__ = "xor"
[docs]class InExpression(LogicalExpression):
__PARSER_NAME__ = "InExpressionParser"
__PARSER_OPERATOR__ = "in"
[docs]class NotInExpression(LogicalExpression):
__PARSER_NAME__ = "NotInExpressionParser"
__PARSER_OPERATOR__ = ["not", "in"]
[docs]class Function(Expression):
pass
[docs]class ListElement(Expression):
def __init__(self):
super().__init__()
__PARSER_LIST_ELEMENT_EXPRESSIONS__ = None
@classmethod
[docs] def GetParser(cls):
if DEBUG: print("init ListElementParser")
# match for EXISTS keyword
token = yield
if (not isinstance(token, CharacterToken)): raise MismatchingParserResult()
if (token.Value != ","): raise MismatchingParserResult()
# match for optional whitespace
token = yield
if isinstance(token, SpaceToken): token = yield
parser = cls.__PARSER_LIST_ELEMENT_EXPRESSIONS__.GetParser()
parser.send(None)
while True:
parser.send(token)
token = yield
# ==============================================================================
# Literals
# ==============================================================================
[docs]class Literal(Expression):
pass
[docs]class StringLiteral(Literal):
def __init__(self, value):
super().__init__()
self._value = value
@property
def Value(self):
return self._value
@classmethod
[docs] def GetParser(cls):
if DEBUG: print("init StringLiteralParser")
# match for opening "
token = yield
if (not isinstance(token, CharacterToken)): raise MismatchingParserResult()
if (token.Value != "\""): raise MismatchingParserResult()
# match for string: value
value = ""
wasEscapeSign = False
while True:
token = yield
if isinstance(token, CharacterToken):
if (token.Value == "\""):
if (wasEscapeSign is True):
wasEscapeSign = False
value += "\""
continue
else:
break
elif (token.Value == "\\"):
if (wasEscapeSign is True):
wasEscapeSign = False
value += "\\"
continue
else:
wasEscapeSign = True
continue
value += token.Value
# construct result
result = cls(value)
if DEBUG: print("StringLiteralParser: matched {0}".format(result))
raise MatchingParserResult(result)
def __str__(self):
return "\"{0}\"".format(self._value)
[docs]class IntegerLiteral(Literal):
def __init__(self, value):
super().__init__()
self._value = value
@property
def Value(self):
return self._value
@classmethod
[docs] def GetParser(cls):
if DEBUG: print("init IntegerLiteralParser")
# match for opening "
token = yield
if (not isinstance(token, NumberToken)): raise MismatchingParserResult()
value = int(token.Value)
# construct result
result = cls(value)
if DEBUG: print("IntegerLiteralParser: matched {0}".format(result))
raise MatchingParserResult(result)
def __str__(self):
return str(self._value)
[docs]class Identifier(Expression):
def __init__(self, name):
super().__init__()
self._name = name
@property
def Name(self):
return self._name
@classmethod
[docs] def GetParser(cls):
if DEBUG: print("init IdentifierParser")
name = ""
while True:
token = yield
if isinstance(token, StringToken):
name += token.Value
elif isinstance(token, NumberToken):
if (name != ""):
name += token.Value
else:
raise MismatchingParserResult("IdentifierParser: Expected identifier name. Got a number.")
elif (isinstance(token, CharacterToken) and (token.Value == "_")):
name += token.Value
elif (name == ""):
raise MismatchingParserResult("IdentifierParser: Expected identifier name.")
else:
break
# construct result
result = cls(name)
if DEBUG: print("IdentifierParser: matched {0}".format(result))
raise GreedyMatchingParserResult(result)
def __str__(self):
return self._name
# ==============================================================================
# Statements
# ==============================================================================
[docs]class Statement(CodeDOMObject):
def __init__(self, commentText=""):
super().__init__()
self._commentText = commentText
@property
def CommentText(self): return self._commentText
@CommentText.setter
def CommentText(self, value): self._commentText = value
[docs]class BlockStatement(Statement):
def __init__(self, commentText=""):
super().__init__(commentText)
self._statements = []
[docs] def AddStatement(self, stmt):
self._statements.append(stmt)
@property
def Statements(self):
return self._statements
def __str__(self, indent=0):
_indent = " " * indent
buffer = _indent + "BlockStatement"
for stmt in self._statements:
buffer += "\n{0}".format(stmt.__str__(indent + 1))
return buffer
[docs]class ConditionalBlockStatement(BlockStatement):
def __init__(self, expression, commentText=""):
super().__init__(commentText)
self._expression = expression
@property
def Expression(self):
return self._expression
def __str__(self, indent=0):
_indent = " " * indent
buffer = _indent + "ConditionalBlockStatement " + self._expression.__str__()
for stmt in self._statements:
buffer += "\n{0}".format(stmt.__str__(indent + 1))
return buffer
# ==============================================================================
# Empty and comment lines
# ==============================================================================
[docs]class EmptyLine(CodeDOMObject):
def __init__(self):
super().__init__()
@classmethod
[docs] def GetParser(cls):
# match for optional whitespace
token = yield
if isinstance(token, SpaceToken): token = yield
# match for delimiter sign: \n
if (not isinstance(token, CharacterToken)): raise MismatchingParserResult()
if (token.Value.lower() != "\n"): raise MismatchingParserResult()
# construct result
result = cls()
raise MatchingParserResult(result)
def __str__(self, indent=0):
return " " * indent + "<empty>"
# ==============================================================================
# Forward declarations
# ==============================================================================
[docs]class BlockedStatement(CodeDOMObject):
_allowedStatements = []
@classmethod
[docs] def AddChoice(cls, value):
cls._allowedStatements.append(value)
@classmethod
[docs] def GetParser(cls):
return cls.GetChoiceParser(cls._allowedStatements)
[docs]class ExpressionChoice(CodeDOMObject):
_allowedExpressions = []
@classmethod
[docs] def AddChoice(cls, value):
cls._allowedExpressions.append(value)
@classmethod
[docs] def GetParser(cls):
return cls.GetChoiceParser(cls._allowedExpressions)