Merge branch 'add-float-type'

This commit is contained in:
Bartłomiej Pluta
2019-07-28 09:32:31 +02:00
20 changed files with 178 additions and 24 deletions

View File

@@ -27,6 +27,10 @@ class IntegerLiteral(Atom):
pass pass
class FloatLiteral(Atom):
pass
class StringLiteral(Atom): class StringLiteral(Atom):
pass pass
@@ -47,6 +51,10 @@ def IntegerParser(input):
return Parser.terminal(TokenType.INTEGER, createNode=IntegerLiteral.withValue)(input) return Parser.terminal(TokenType.INTEGER, createNode=IntegerLiteral.withValue)(input)
def FloatParser(input):
return Parser.terminal(TokenType.FLOAT, createNode=FloatLiteral.withValue)(input)
def StringParser(input): def StringParser(input):
return Parser.terminal(TokenType.STRING, createNode=StringLiteral.withValue)(input) return Parser.terminal(TokenType.STRING, createNode=StringLiteral.withValue)(input)
@@ -66,6 +74,7 @@ def TypeLiteralParser(input):
def LiteralParser(input): def LiteralParser(input):
return Parser.oneOf( return Parser.oneOf(
IntegerParser, IntegerParser,
FloatParser,
StringParser, StringParser,
NoteParser, NoteParser,
BoolParser, BoolParser,

View File

@@ -1,4 +1,4 @@
from smnp.module import system, mic, note, iterable, sound, synth, string, util from smnp.module import system, mic, note, iterable, sound, synth, string, util, integer, float
functions = [ *system.functions, *mic.functions, *note.functions, *iterable.functions, *sound.functions, *synth.functions, *string.functions, *util.functions ] functions = [ *system.functions, *mic.functions, *note.functions, *iterable.functions, *sound.functions, *synth.functions, *string.functions, *util.functions, *integer.functions, *float.functions ]
methods = [ *system.methods, *mic.methods, *note.methods, *iterable.methods, *sound.methods, *synth.methods, *string.methods, *util.methods ] methods = [ *system.methods, *mic.methods, *note.methods, *iterable.methods, *sound.methods, *synth.methods, *string.methods, *util.methods, *integer.methods, *float.methods ]

View File

@@ -0,0 +1,4 @@
from smnp.module.float.function import float
functions = [ float.function ]
methods = []

View File

View File

@@ -0,0 +1,26 @@
from smnp.function.model import CombinedFunction, Function
from smnp.function.signature import signature
from smnp.type.model import Type
from smnp.type.signature.matcher.type import ofType
_signature1 = signature(ofType(Type.INTEGER))
def _function1(env, value):
return Type.float(float(value.value))
_signature2 = signature(ofType(Type.STRING))
def _function2(env, value):
return Type.float(float(value.value))
_signature3 = signature(ofType(Type.FLOAT))
def _function3(env, value):
return value
function = CombinedFunction(
'Float',
Function(_signature1, _function1),
Function(_signature2, _function2),
Function(_signature3, _function3),
)

View File

@@ -0,0 +1,4 @@
from smnp.module.integer.function import integer
functions = [ integer.function ]
methods = []

View File

View File

@@ -0,0 +1,25 @@
from smnp.function.model import CombinedFunction, Function
from smnp.function.signature import signature
from smnp.type.model import Type
from smnp.type.signature.matcher.type import ofType
_signature1 = signature(ofType(Type.FLOAT))
def _function1(env, value):
return Type.integer(int(value.value))
_signature2 = signature(ofType(Type.STRING))
def _function2(env, value):
return Type.integer(int(value.value))
_signature3 = signature(ofType(Type.INTEGER))
def _function3(env, value):
return value
function = CombinedFunction(
'Integer',
Function(_signature1, _function1),
Function(_signature2, _function2),
Function(_signature3, _function3),
)

View File

@@ -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.identifier import Identifier
from smnp.ast.node.list import List from smnp.ast.node.list import List
from smnp.ast.node.map import Map from smnp.ast.node.map import Map
from smnp.error.runtime import RuntimeException from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import expressionEvaluator from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.runtime.evaluators.float import FloatEvaluator
from smnp.runtime.evaluators.iterable import abstractIterableEvaluator from smnp.runtime.evaluators.iterable import abstractIterableEvaluator
from smnp.runtime.tools.error import updatePos from smnp.runtime.tools.error import updatePos
from smnp.type.model import Type from smnp.type.model import Type
@@ -85,6 +86,7 @@ class AtomEvaluator(Evaluator):
return Evaluator.oneOf( return Evaluator.oneOf(
Evaluator.forNodes(StringEvaluator.evaluate, StringLiteral), Evaluator.forNodes(StringEvaluator.evaluate, StringLiteral),
Evaluator.forNodes(IntegerEvaluator.evaluate, IntegerLiteral), Evaluator.forNodes(IntegerEvaluator.evaluate, IntegerLiteral),
Evaluator.forNodes(FloatEvaluator.evaluate, FloatLiteral),
Evaluator.forNodes(NoteEvaluator.evaluate, NoteLiteral), Evaluator.forNodes(NoteEvaluator.evaluate, NoteLiteral),
Evaluator.forNodes(BoolEvaluator.evaluate, BoolLiteral), Evaluator.forNodes(BoolEvaluator.evaluate, BoolLiteral),
Evaluator.forNodes(TypeEvaluator.evaluate, TypeLiteral), Evaluator.forNodes(TypeEvaluator.evaluate, TypeLiteral),

View File

@@ -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)

View File

@@ -11,6 +11,7 @@ class MinusEvaluator(Evaluator):
try: try:
return { return {
Type.INTEGER: cls.evaluateForInteger, Type.INTEGER: cls.evaluateForInteger,
Type.FLOAT: cls.evaluateForFloat,
Type.STRING: cls.evaluateForString, Type.STRING: cls.evaluateForString,
Type.LIST: cls.evaluateForList Type.LIST: cls.evaluateForList
}[value.type](value.value) }[value.type](value.value)
@@ -19,9 +20,12 @@ class MinusEvaluator(Evaluator):
@classmethod @classmethod
def evaluateForInteger(cls, value): def evaluateForInteger(cls, value):
return Type.integer(-value) return Type.integer(-value)
@classmethod
def evaluateForFloat(cls, value):
return Type.float(-value)
@classmethod @classmethod
def evaluateForString(cls, value): def evaluateForString(cls, value):
return Type.string(value[::-1]) return Type.string(value[::-1])

View File

@@ -10,11 +10,12 @@ class PowerEvaluator(Evaluator):
def evaluator(cls, node, environment): def evaluator(cls, node, environment):
left = expressionEvaluator(doAssert=True)(node.left, environment).value left = expressionEvaluator(doAssert=True)(node.left, environment).value
right = expressionEvaluator(doAssert=True)(node.right, 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) raise RuntimeException(f"Operator '{node.operator.value}' is supported only by {Type.INTEGER.name.lower()} 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) 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)) return Type.integer(int(left.value ** right.value))

View File

@@ -10,22 +10,36 @@ class ProductEvaluator(Evaluator):
def evaluator(cls, node, environment): def evaluator(cls, node, environment):
left = expressionEvaluator(doAssert=True)(node.left, environment).value left = expressionEvaluator(doAssert=True)(node.left, environment).value
right = expressionEvaluator(doAssert=True)(node.right, 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( 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( 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 == "*": if node.operator.value == "*":
return Type.integer(int(left.value * right.value)) return getProperTypeProvider(left.value * right.value)
if node.operator.value == "/": if node.operator.value == "/":
if right.value == 0: if right.value == 0:
raise RuntimeException("Attempt to divide by 0", node.right.pos) 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") 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)

View File

@@ -21,15 +21,21 @@ class RelationEvaluator(Evaluator):
@classmethod @classmethod
def equalOperatorEvaluator(cls, left, operator, right): def equalOperatorEvaluator(cls, left, operator, right):
if left.type in [Type.INTEGER, Type.FLOAT] and right.type in [Type.INTEGER, Type.FLOAT]:
return Type.bool(left.value == right.value)
return Type.bool(left.type == right.type and left.value == right.value) return Type.bool(left.type == right.type and left.value == right.value)
@classmethod @classmethod
def notEqualOperatorEvaluator(cls, left, operator, right): def notEqualOperatorEvaluator(cls, left, operator, right):
if left.type in [Type.INTEGER, Type.FLOAT] and right.type in [Type.INTEGER, Type.FLOAT]:
return Type.bool(left.value != right.value)
return Type.bool(left.type != right.type or left.value != right.value) return Type.bool(left.type != right.type or left.value != right.value)
@classmethod @classmethod
def otherRelationOperatorsEvaluator(cls, left, operator, right): 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 == ">": if operator.value == ">":
return Type.bool(left.value > right.value) return Type.bool(left.value > right.value)
@@ -40,7 +46,7 @@ class RelationEvaluator(Evaluator):
return Type.bool(left.value < right.value) return Type.bool(left.value < right.value)
if operator.value == "<=": if operator.value == "<=":
return Type.bool(left.value < right.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) raise RuntimeException(f"Operator {operator.value} is not supported by {left.type.name.lower()} and {right.type.name.lower()} types", operator.pos)

View File

@@ -11,8 +11,8 @@ class SumEvaluator(Evaluator):
left = expressionEvaluator(doAssert=True)(node.left, environment).value left = expressionEvaluator(doAssert=True)(node.left, environment).value
right = expressionEvaluator(doAssert=True)(node.right, environment).value right = expressionEvaluator(doAssert=True)(node.right, environment).value
if left.type == right.type == Type.INTEGER: if left.type in [Type.INTEGER, Type.FLOAT] and right.type in [Type.INTEGER, Type.FLOAT]:
return cls.integerEvaluator(left, node.operator, right) return cls.numberEvaluator(left, node.operator, right)
if left.type == right.type == Type.STRING: if left.type == right.type == Type.STRING:
return cls.stringEvaluator(left, node.operator, right) return cls.stringEvaluator(left, node.operator, right)
@@ -23,15 +23,17 @@ class SumEvaluator(Evaluator):
if left.type == right.type == Type.MAP: if left.type == right.type == Type.MAP:
return cls.mapEvaluator(left, node.operator, right) 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 @classmethod
def integerEvaluator(cls, left, operator, right): def numberEvaluator(cls, left, operator, right):
if operator.value == "+": if operator.value == "+":
return Type.integer(left.value + right.value) return getProperTypeProvider(left.value + right.value)
if operator.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") raise RuntimeError("This line should never be reached")
@@ -64,3 +66,10 @@ class SumEvaluator(Evaluator):
raise RuntimeException(f"Operator {operator.value} is not supported by map types", operator.pos) raise RuntimeException(f"Operator {operator.value} is not supported by map types", operator.pos)
raise RuntimeError("This line should never be reached") 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)

View File

@@ -2,6 +2,7 @@ from smnp.error.syntax import SyntaxException
from smnp.token.model import TokenList from smnp.token.model import TokenList
from smnp.token.tokenizers.bool import boolTokenizer from smnp.token.tokenizers.bool import boolTokenizer
from smnp.token.tokenizers.comment import commentTokenizer from smnp.token.tokenizers.comment import commentTokenizer
from smnp.token.tokenizers.float import floatTokenizer
from smnp.token.tokenizers.identifier import identifierTokenizer from smnp.token.tokenizers.identifier import identifierTokenizer
from smnp.token.tokenizers.keyword import typeTokenizer from smnp.token.tokenizers.keyword import typeTokenizer
from smnp.token.tokenizers.note import noteTokenizer from smnp.token.tokenizers.note import noteTokenizer
@@ -41,6 +42,7 @@ tokenizers = (
defaultTokenizer(TokenType.DOT), defaultTokenizer(TokenType.DOT),
# Types # Types
separated(floatTokenizer),
mapValue(separated(regexPatternTokenizer(TokenType.INTEGER, r'\d')), int), mapValue(separated(regexPatternTokenizer(TokenType.INTEGER, r'\d')), int),
stringTokenizer, stringTokenizer,
noteTokenizer, noteTokenizer,

View File

@@ -0,0 +1,17 @@
from smnp.token.model import Token
from smnp.token.tools import regexPatternTokenizer, keywordTokenizer, allOf
from smnp.token.type import TokenType
def createToken(pos, beforeDot, dot, afterDot):
rawValue = f"{beforeDot.value}.{afterDot.value}"
value = float(rawValue)
return Token(TokenType.FLOAT, value, pos, rawValue)
floatTokenizer = allOf(
regexPatternTokenizer(TokenType.INTEGER, r'\d'),
keywordTokenizer(None, "."),
regexPatternTokenizer(TokenType.INTEGER, r'\d'),
createToken=createToken
)

View File

@@ -61,3 +61,19 @@ def mapValue(tokenizer, mapper):
return (0, None) return (0, None)
return tokenize return tokenize
def allOf(*tokenizers, createToken):
def combinedTokenizer(input, current, line):
consumedChars = 0
tokens = []
for tokenizer in tokenizers:
consumed, token = tokenizer(input, current+consumedChars, line)
if consumed > 0:
consumedChars += consumed
tokens.append(token)
else:
return (0, None)
return (consumedChars, createToken((current, line), *tokens))
return combinedTokenizer

View File

@@ -30,6 +30,7 @@ class TokenType(Enum):
NOT = 'not' NOT = 'not'
INTEGER = 'integer' INTEGER = 'integer'
STRING = 'string' STRING = 'string'
FLOAT = 'float'
NOTE = 'note' NOTE = 'note'
BOOL = 'bool' BOOL = 'bool'
TYPE = 'type' TYPE = 'type'

View File

@@ -8,6 +8,7 @@ from smnp.type.value import Value
class Type(Enum): class Type(Enum):
INTEGER = (int, lambda x: str(x)) INTEGER = (int, lambda x: str(x))
FLOAT = (float, lambda x: str(x))
STRING = (str, lambda x: x) STRING = (str, lambda x: x)
LIST = (list, lambda x: f"[{', '.join([e.stringify() for e in 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()) + '}') 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): def integer(value):
return Value(Type.INTEGER, value, {}) return Value(Type.INTEGER, value, {})
@staticmethod
def float(value):
return Value(Type.FLOAT, value, {})
@staticmethod @staticmethod
def string(value): def string(value):
return Value(Type.STRING, value, { return Value(Type.STRING, value, {