def evaluator(cls, node, environment): left = expressionEvaluator(doAssert=True)(node.left, environment).value right = expressionEvaluator(doAssert=True)(node.right, environment).value supportedTypes = [Type.INTEGER, Type.FLOAT] if not left.type in supportedTypes: raise RuntimeException( f"Operator '{node.operator.value}' is supported only by {[t.name.lower() for t in supportedTypes]} type", node.left.pos) if not right.type in supportedTypes: raise RuntimeException( f"Operator '{node.operator.value}' is supported only by {[t.name.lower() for t in supportedTypes]} type", node.right.pos) if node.operator.value == "*": return getProperTypeProvider(left.value * right.value) if node.operator.value == "/": if right.value == 0: raise RuntimeException("Attempt to divide by 0", node.right.pos) value = left.value / right.value if left.type == right.type == Type.INTEGER and int(value) == value: return Type.integer(int(value)) return getProperTypeProvider(value) raise RuntimeError("This line should never be reached")
def getValueAccordingToType(value, type): try: if type.value == Type.STRING: return Type.string(value) if type.value == Type.INTEGER: return Type.integer(int(value)) if type.value == Type.BOOL: consumedChars, token = boolTokenizer(value, 0, 0) if consumedChars > 0: return Type.bool(token.value) return ValueError() if type.value == Type.NOTE: consumedChars, token = noteTokenizer(value, 0, 0) if consumedChars > 0: return Type.note(token.value) raise ValueError() raise RuntimeException( f"Type {type.value.name.lower()} is not supported", None) except ValueError: raise RuntimeException( f"Invalid value '{value}' for type {type.value.name.lower()}", None)
def mapEvaluator(cls, node, environment, evaluatedIterator, parameters, filter): output = [] if len(parameters) > 3: raise RuntimeException( f"Loop with map iterator can handle only three parameters", node.parameters.pos) i = 0 for key, value in evaluatedIterator.value.items(): if len(parameters) == 1: environment.scopes[-1][parameters[0]] = value if len(parameters) == 2: environment.scopes[-1][parameters[0]] = key environment.scopes[-1][parameters[1]] = value if len(parameters) == 3: environment.scopes[-1][parameters[0]] = Type.integer(i) environment.scopes[-1][parameters[1]] = key environment.scopes[-1][parameters[2]] = value i += 1 if cls.doFilter(filter, environment): output.append(evaluate(node.right, environment).value) return output
def evaluator(cls, node, environment): value = expressionEvaluator(doAssert=True)(node.value, environment).value if value.type != Type.BOOL: raise RuntimeException(f"Operator '{node.operator.value}' is supported only by {Type.BOOL.name.lower()} type", node.value.pos) return Type.bool(not value.value)
def evaluate(node, environment): from smnp.runtime.evaluators.program import ProgramEvaluator from smnp.runtime.evaluators.expression import expressionEvaluator from smnp.runtime.evaluators.condition import IfElseStatementEvaluator from smnp.runtime.evaluators.block import BlockEvaluator from smnp.runtime.evaluators.imports import ImportEvaluator from smnp.runtime.evaluators.function import FunctionDefinitionEvaluator from smnp.runtime.evaluators.function import ReturnEvaluator from smnp.runtime.evaluators.extend import ExtendEvaluator from smnp.runtime.evaluators.throw import ThrowEvaluator result = Evaluator.oneOf( Evaluator.forNodes(ProgramEvaluator.evaluate, Program), Evaluator.forNodes(IfElseStatementEvaluator.evaluate, IfElse), Evaluator.forNodes(BlockEvaluator.evaluate, Block), Evaluator.forNodes(ImportEvaluator.evaluate, Import), Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinition), Evaluator.forNodes(ReturnEvaluator.evaluate, Return), Evaluator.forNodes(ExtendEvaluator.evaluate, Extend), Evaluator.forNodes(ThrowEvaluator.evaluate, Throw), #Evaluator.forNodes(ImportEvaluator.evaluate, ImportNode), #Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinitionNode), #Evaluator.forNodes(ExtendEvaluator.evaluate, ExtendNode), #Evaluator.forNodes(BlockEvaluator.evaluate, BlockNode), #Evaluator.forNodes(ReturnEvaluator.evaluate, ReturnNode), expressionEvaluator())(node, environment) if not result.result: raise RuntimeException("Cannot evaluate program", node.pos) return result
def getTuning(config): key = Type.string("tuning") if key in config.value: tuning = config.value[key] if not tuning.type in [Type.INTEGER, Type.FLOAT] or tuning.value < 0: raise RuntimeException("The 'tuning' property must be non-negative integer or float", None) return tuning.value return DEFAULT_TUNING
def getAttack(config): key = Type.string("attack") if key in config.value: attack = config.value[key] if not attack.type in [Type.INTEGER, Type.FLOAT] or attack.value < 0: raise RuntimeException("The 'attack' property must be non-negative integer or float", None) return attack.value return DEFAULT_ATTACK
def getDecay(config): key = Type.string("decay") if key in config.value: decay = config.value[key] if not decay.type in [Type.INTEGER, Type.FLOAT] or decay.value < 0: raise RuntimeException("The 'decay' property must be non-negative integer or float", None) return decay.value return DEFAULT_DECAY
def getBpm(config): key = Type.string("bpm") if key in config.value: bpm = config.value[key] if bpm.type != Type.INTEGER or bpm.value <= 0: raise RuntimeException("The 'bpm' property must be positive integer", None) return bpm.value return DEFAULT_BPM
def addCustomFunction(self, name, signature, arguments, body, defaultArguments): if len([ fun for fun in self.functions + self.customFunctions if fun.name == name ]) > 0: raise RuntimeException(f"Cannot redeclare function '{name}'", None) self.customFunctions.append( CustomFunction(name, signature, arguments, body, defaultArguments))
def evaluator(cls, node, environment): string = expressionEvaluator(doAssert=True)(node.value, environment).value if string.type != Type.STRING: raise RuntimeException( f"Only {Type.STRING.name.lower()} types can be thrown", node.value.pos) raise CustomException(string.value, node.pos)
def mapEvaluator(cls, left, operator, right): if operator.value == "+": return Type.map({**left.value, **right.value}) if operator.value == "-": raise RuntimeException( f"Operator {operator.value} is not supported by map types", operator.pos) raise RuntimeError("This line should never be reached")
def listEvaluator(cls, left, operator, right): if operator.value == "+": return Type.list(left.value + right.value) if operator.value == "-": raise RuntimeException( f"Operator {operator.value} is not supported by list types", operator.pos) raise RuntimeError("This line should never be reached")
def getOvertones(config): key = Type.string("overtones") if key in config.value: overtones = config.value[key] rawOvertones = [overtone.value for overtone in overtones.value] if overtones.type != Type.LIST or not all(overtone.type in [Type.FLOAT, Type.INTEGER] for overtone in overtones.value): raise RuntimeException("The 'overtones' property must be list of floats", None) if len(rawOvertones) < 1: raise RuntimeException("The 'overtones' property must contain one overtone at least", None) if any(overtone < 0 for overtone in rawOvertones): raise RuntimeException("The 'overtones' property mustn't contain negative values", None) if sum(rawOvertones) > 1.0: raise RuntimeException("The 'overtones' property must contain overtones which sum is not greater than 1.0", None) return rawOvertones return DEFAULT_OVERTONES
def findVariable(self, name, type=None, pos=None): for scope in reversed(self.scopes): if name in scope: value = scope[name] if type is not None: if isinstance(value, type): return value else: return value raise RuntimeException( f"Variable '{name}' is not declared" + ("" if type is None else f" (expected type: {type})"), pos)
def doFilter(cls, filter, environment): if type(filter) is not NoneNode: evaluation = expressionEvaluator(doAssert=True)(filter, environment).value if evaluation.type != Type.BOOL: raise RuntimeException( f"Expected {Type.BOOL.name.lower()} as filter expression, found {evaluation.type.name.lower()}", filter.pos) return evaluation.value return True
def addCustomMethod(self, typeSignature, alias, name, signature, arguments, body, defaultArguments): if len([ m for m in self.methods if m.name == name and m.signature.matchers[0] == typeSignature.matchers[0] ]) > 0: raise RuntimeException( f"Cannot redeclare method '{name}' for type '{typeSignature.matchers[0]}'", None) if len([ m for m in self.customMethods if m.name == name and m.typeSignature.matchers[0] == typeSignature.matchers[0] ]) > 0: raise RuntimeException( f"Cannot redeclare method '{name}' for type '{typeSignature.matchers[0]}'", None) self.customMethods.append( CustomMethod(typeSignature, alias, name, signature, arguments, body, defaultArguments))
def evaluator(cls, node, environment): value = evaluate(node.value, environment).value try: return { Type.INTEGER: cls.evaluateForInteger, Type.FLOAT: cls.evaluateForFloat, Type.STRING: cls.evaluateForString, Type.LIST: cls.evaluateForList }[value.type](value.value) except KeyError: raise RuntimeException( f"Type {value.type.name.lower()} does not support '{node.operator.value}' operator", node.pos)
def evaluator(cls, node, environment): map = {} keyEvaluator = Evaluator.oneOf( Evaluator.forNodes(lambda node, environment: EvaluationResult.OK(Type.string(node.value)), Identifier), expressionEvaluator(doAssert=True) ) for entry in node.children: key = keyEvaluator(entry.key, environment).value if key in map: raise RuntimeException(f"Duplicated key '{key.stringify()}' found in map", entry.pos) map[key] = expressionEvaluator(doAssert=True)(entry.value, environment).value return Type.map(map)
def evaluator(cls, node, environment): condition = expressionEvaluator(doAssert=True)(node.condition, environment).value if condition.type != Type.BOOL: raise RuntimeException( f"Only {Type.BOOL.name.lower()} types can be used as conditions in conditional expression", node.condition.pos) if condition.value: evaluate(node.ifNode, environment) else: evaluate(node.elseNode, environment)
def evaluator(cls, node, environment): if len(environment.callStack) > 0: returnValue = expressionEvaluator()(node.value, environment).value raise Return(returnValue) # Disclaimer # Exception system usage to control program execution flow is really bad idea. # However because of lack of 'goto' instruction equivalent in Python # there is a need to use some mechanism to break function execution on 'return' statement # and immediately go to Environment's method 'invokeFunction()' or 'invokeMethod()', # which can handle value that came with exception and return it to code being executed. else: raise RuntimeException( "Cannot use 'return' statement outside a function or method", node.pos, environment)
def boolEvaluator(cls, node, environment, evaluatedIterator, parameters, filter): output = [] if len(parameters) > 0: raise RuntimeException( f"Loop with logic iterator can't' handle any parameters", node.parameters.pos) condition = evaluatedIterator while condition.value: if cls.doFilter(filter, environment): output.append(evaluate(node.right, environment).value) condition = expressionEvaluator(doAssert=True)(node.left, environment).value return output
def numberEvaluator(cls, node, environment, evaluatedIterator, parameters, filter): output = [] if len(parameters) > 1: raise RuntimeException( f"Loop with numeric iterator can handle only one parameter", node.parameters.pos) for i in range(evaluatedIterator.value): if len(parameters) > 0: environment.scopes[-1][parameters[0]] = Type.integer(i) if cls.doFilter(filter, environment): output.append(evaluate(node.right, environment).value) return output
def otherRelationOperatorsEvaluator(cls, left, operator, right): if left.type in [Type.INTEGER, Type.FLOAT ] and right.type in [Type.INTEGER, Type.FLOAT]: if operator.value == ">": return Type.bool(left.value > right.value) if operator.value == ">=": return Type.bool(left.value >= right.value) if operator.value == "<": return Type.bool(left.value < right.value) if operator.value == "<=": return Type.bool(left.value <= right.value) raise RuntimeException( f"Operator {operator.value} is not supported by {left.type.name.lower()} and {right.type.name.lower()} types", operator.pos)
def listEvaluator(cls, node, environment, evaluatedIterator, parameters, filter): output = [] if len(parameters) > 2: raise RuntimeException( f"Loop with list iterator can handle only two parameters", node.parameters.pos) for i, value in enumerate(evaluatedIterator.value): if len(parameters) == 1: environment.scopes[-1][parameters[0]] = value if len(parameters) == 2: environment.scopes[-1][parameters[0]] = Type.integer(i) environment.scopes[-1][parameters[1]] = value if cls.doFilter(filter, environment): output.append(evaluate(node.right, environment).value) return output
def evaluator(cls, node, environment): left = expressionEvaluator(doAssert=True)(node.left, environment).value right = expressionEvaluator(doAssert=True)(node.right, environment).value if left.type in [Type.INTEGER, Type.FLOAT ] and right.type in [Type.INTEGER, Type.FLOAT]: return cls.numberEvaluator(left, node.operator, right) if left.type == right.type == Type.STRING: return cls.stringEvaluator(left, node.operator, right) if left.type == right.type == Type.LIST: return cls.listEvaluator(left, node.operator, right) if left.type == right.type == Type.MAP: return cls.mapEvaluator(left, node.operator, right) raise RuntimeException( f"Operator {node.operator.value} is not supported by {left.type.name.lower()} and {right.type.name.lower()} types", node.operator.pos)
def evaluator(cls, node, environment): left = expressionEvaluator(doAssert=True)( node.left, environment ).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult right = node.right if type(right) == Identifier: try: return left.properties[right.value] except KeyError: raise RuntimeException( f"Unknown property '{right.value}' of type '{left.type.name.lower()}'", right.pos) if type(right) == FunctionCall: try: arguments = abstractIterableEvaluator( expressionEvaluator(doAssert=True))(right.arguments, environment) return environment.invokeMethod(left, right.name.value, arguments) except RuntimeException as e: raise updatePos(e, right)
def evaluator(cls, node, environment): iterator = expressionEvaluator(doAssert=True)(node.left, environment).value parameters = [identifier.value for identifier in node.parameters ] if type(node.parameters) != NoneNode() else [] try: environment.appendScope() output = { Type.INTEGER: cls.numberEvaluator, Type.BOOL: cls.boolEvaluator, Type.LIST: cls.listEvaluator, Type.MAP: cls.mapEvaluator }[iterator.type](node, environment, iterator, parameters, node.filter) environment.popScope() except KeyError: raise RuntimeException( f"The {iterator.type.name.lower()} type cannot stand as an iterator for loop statement", node.left.pos) return Type.list(output)
def evaluateExpression(node, environment): from smnp.runtime.evaluators.function import FunctionCallEvaluator from smnp.runtime.evaluators.minus import MinusEvaluator from smnp.runtime.evaluators.atom import AtomEvaluator from smnp.runtime.evaluators.access import AccessEvaluator from smnp.runtime.evaluators.negation import NotEvaluator from smnp.runtime.evaluators.power import PowerEvaluator from smnp.runtime.evaluators.loop import LoopEvaluator from smnp.runtime.evaluators.assignment import AssignmentEvaluator from smnp.runtime.evaluators.product import ProductEvaluator from smnp.runtime.evaluators.sum import SumEvaluator from smnp.runtime.evaluators.relation import RelationEvaluator from smnp.runtime.evaluators.condition import IfElseEvaluator from smnp.runtime.evaluators.logic import AndEvaluator from smnp.runtime.evaluators.logic import OrEvaluator result = Evaluator.oneOf( Evaluator.forNodes(FunctionCallEvaluator.evaluate, FunctionCall), Evaluator.forNodes(MinusEvaluator.evaluate, MinusOperator), Evaluator.forNodes(AccessEvaluator.evaluate, Access), Evaluator.forNodes(NotEvaluator.evaluate, NotOperator), Evaluator.forNodes(PowerEvaluator.evaluate, Power), Evaluator.forNodes(LoopEvaluator.evaluate, Loop), Evaluator.forNodes(AssignmentEvaluator.evaluate, Assignment), Evaluator.forNodes(ProductEvaluator.evaluate, Product), Evaluator.forNodes(SumEvaluator.evaluate, Sum), Evaluator.forNodes(RelationEvaluator.evaluate, Relation), Evaluator.forNodes(IfElseEvaluator.evaluate, IfElse), Evaluator.forNodes(AndEvaluator.evaluate, And), Evaluator.forNodes(OrEvaluator.evaluate, Or), AtomEvaluator.evaluate)(node, environment) if doAssert and result.result and result.value.type == Type.VOID: raise RuntimeException(f"Expected expression", node.pos) return result
def _failStringify(t): raise RuntimeException(f"Not able to interpret {t.name}'", None)