diff --git a/smnp/runtime/evaluators/atom.py b/smnp/runtime/evaluators/atom.py index 0be1822..7918900 100644 --- a/smnp/runtime/evaluators/atom.py +++ b/smnp/runtime/evaluators/atom.py @@ -1,10 +1,11 @@ -from smnp.ast.node.atom import StringLiteral, IntegerLiteral, NoteLiteral, BoolLiteral, TypeLiteral +from smnp.ast.node.atom import StringLiteral, IntegerLiteral, NoteLiteral, BoolLiteral, TypeLiteral, FloatLiteral from smnp.ast.node.identifier import Identifier from smnp.ast.node.list import List from smnp.ast.node.map import Map from smnp.error.runtime import RuntimeException from smnp.runtime.evaluator import Evaluator from smnp.runtime.evaluators.expression import expressionEvaluator +from smnp.runtime.evaluators.float import FloatEvaluator from smnp.runtime.evaluators.iterable import abstractIterableEvaluator from smnp.runtime.tools.error import updatePos from smnp.type.model import Type @@ -85,6 +86,7 @@ class AtomEvaluator(Evaluator): return Evaluator.oneOf( Evaluator.forNodes(StringEvaluator.evaluate, StringLiteral), Evaluator.forNodes(IntegerEvaluator.evaluate, IntegerLiteral), + Evaluator.forNodes(FloatEvaluator.evaluate, FloatLiteral), Evaluator.forNodes(NoteEvaluator.evaluate, NoteLiteral), Evaluator.forNodes(BoolEvaluator.evaluate, BoolLiteral), Evaluator.forNodes(TypeEvaluator.evaluate, TypeLiteral), diff --git a/smnp/runtime/evaluators/float.py b/smnp/runtime/evaluators/float.py new file mode 100644 index 0000000..7a1c05b --- /dev/null +++ b/smnp/runtime/evaluators/float.py @@ -0,0 +1,9 @@ +from smnp.runtime.evaluator import Evaluator +from smnp.type.model import Type + + +class FloatEvaluator(Evaluator): + + @classmethod + def evaluator(cls, node, environment): + return Type.float(node.value) \ No newline at end of file diff --git a/smnp/runtime/evaluators/minus.py b/smnp/runtime/evaluators/minus.py index 5c20118..d844b44 100644 --- a/smnp/runtime/evaluators/minus.py +++ b/smnp/runtime/evaluators/minus.py @@ -11,6 +11,7 @@ class MinusEvaluator(Evaluator): try: return { Type.INTEGER: cls.evaluateForInteger, + Type.FLOAT: cls.evaluateForFloat, Type.STRING: cls.evaluateForString, Type.LIST: cls.evaluateForList }[value.type](value.value) @@ -19,9 +20,12 @@ class MinusEvaluator(Evaluator): @classmethod def evaluateForInteger(cls, value): - return Type.integer(-value) + @classmethod + def evaluateForFloat(cls, value): + return Type.float(-value) + @classmethod def evaluateForString(cls, value): return Type.string(value[::-1]) diff --git a/smnp/runtime/evaluators/power.py b/smnp/runtime/evaluators/power.py index 7168d48..5d9abb9 100644 --- a/smnp/runtime/evaluators/power.py +++ b/smnp/runtime/evaluators/power.py @@ -10,11 +10,12 @@ class PowerEvaluator(Evaluator): 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 left.type != Type.INTEGER: - raise RuntimeException( f"Operator '{node.operator.value}' is supported only by {Type.INTEGER.name.lower()} type", node.left.pos) + if not left.type in supportedTypes: + raise RuntimeException(f"Operator '{node.operator.value}' is supported only by {Type.INTEGER.name.lower()} type", node.left.pos) - if right.type != Type.INTEGER: - raise RuntimeException( f"Operator '{node.operator.value}' is supported only by {Type.INTEGER.name.lower()} type", node.right.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) return Type.integer(int(left.value ** right.value)) \ No newline at end of file diff --git a/smnp/runtime/evaluators/product.py b/smnp/runtime/evaluators/product.py index ecf513f..6cabbf0 100644 --- a/smnp/runtime/evaluators/product.py +++ b/smnp/runtime/evaluators/product.py @@ -10,22 +10,36 @@ class ProductEvaluator(Evaluator): 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 left.type != Type.INTEGER: + if not left.type in supportedTypes: raise RuntimeException( - f"Operator '{node.operator.value}' is supported only by {Type.INTEGER.name.lower()} type", node.left.pos) + f"Operator '{node.operator.value}' is supported only by {[t.name.lower() for t in supportedTypes]} type", node.left.pos) - if right.type != Type.INTEGER: + if not right.type in supportedTypes: raise RuntimeException( - f"Operator '{node.operator.value}' is supported only by {Type.INTEGER.name.lower()} type", node.right.pos) + 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 Type.integer(int(left.value * right.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) - return Type.integer(int(left.value / right.value)) + + 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 getProperTypeProvider(value): + return { + int: lambda v: Type.integer(v), + float: lambda v: Type.float(v) + }[type(value)](value) + diff --git a/smnp/runtime/evaluators/relation.py b/smnp/runtime/evaluators/relation.py index fb0de94..020c733 100644 --- a/smnp/runtime/evaluators/relation.py +++ b/smnp/runtime/evaluators/relation.py @@ -29,7 +29,7 @@ class RelationEvaluator(Evaluator): @classmethod def otherRelationOperatorsEvaluator(cls, left, operator, right): - if left.type == right.type == Type.INTEGER: + 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) diff --git a/smnp/runtime/evaluators/sum.py b/smnp/runtime/evaluators/sum.py index 1f1bdcd..d19d9f0 100644 --- a/smnp/runtime/evaluators/sum.py +++ b/smnp/runtime/evaluators/sum.py @@ -11,8 +11,8 @@ class SumEvaluator(Evaluator): left = expressionEvaluator(doAssert=True)(node.left, environment).value right = expressionEvaluator(doAssert=True)(node.right, environment).value - if left.type == right.type == Type.INTEGER: - return cls.integerEvaluator(left, node.operator, right) + 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) @@ -23,15 +23,17 @@ class SumEvaluator(Evaluator): 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) + raise RuntimeException( + f"Operator {node.operator.value} is not supported by {left.type.name.lower()} and {right.type.name.lower()} types", + node.operator.pos) @classmethod - def integerEvaluator(cls, left, operator, right): + def numberEvaluator(cls, left, operator, right): if operator.value == "+": - return Type.integer(left.value + right.value) + return getProperTypeProvider(left.value + right.value) if operator.value == "-": - return Type.integer(left.value - right.value) + return getProperTypeProvider(left.value - right.value) raise RuntimeError("This line should never be reached") @@ -63,4 +65,11 @@ class SumEvaluator(Evaluator): 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") \ No newline at end of file + raise RuntimeError("This line should never be reached") + + +def getProperTypeProvider(value): + return { + int: lambda v: Type.integer(v), + float: lambda v: Type.float(v) + }[type(value)](value) diff --git a/smnp/type/model.py b/smnp/type/model.py index 8e757b9..926a8e4 100644 --- a/smnp/type/model.py +++ b/smnp/type/model.py @@ -8,6 +8,7 @@ from smnp.type.value import Value class Type(Enum): INTEGER = (int, lambda x: str(x)) + FLOAT = (float, lambda x: str(x)) STRING = (str, lambda x: x) LIST = (list, lambda x: f"[{', '.join([e.stringify() for e in x])}]") MAP = (dict, lambda x: '{' + ', '.join(f"'{k.stringify()}' -> '{v.stringify()}'" for k, v in x.items()) + '}') @@ -25,6 +26,10 @@ class Type(Enum): def integer(value): return Value(Type.INTEGER, value, {}) + @staticmethod + def float(value): + return Value(Type.FLOAT, value, {}) + @staticmethod def string(value): return Value(Type.STRING, value, {