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
class FloatLiteral(Atom):
pass
class StringLiteral(Atom):
pass
@@ -47,6 +51,10 @@ def IntegerParser(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):
return Parser.terminal(TokenType.STRING, createNode=StringLiteral.withValue)(input)
@@ -66,6 +74,7 @@ def TypeLiteralParser(input):
def LiteralParser(input):
return Parser.oneOf(
IntegerParser,
FloatParser,
StringParser,
NoteParser,
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 ]
methods = [ *system.methods, *mic.methods, *note.methods, *iterable.methods, *sound.methods, *synth.methods, *string.methods, *util.methods ]
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, *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.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),

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:
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])

View File

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

View File

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

View File

@@ -21,15 +21,21 @@ class RelationEvaluator(Evaluator):
@classmethod
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)
@classmethod
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)
@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)
@@ -40,7 +46,7 @@ class RelationEvaluator(Evaluator):
return Type.bool(left.value < right.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)

View File

@@ -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")
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.tokenizers.bool import boolTokenizer
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.keyword import typeTokenizer
from smnp.token.tokenizers.note import noteTokenizer
@@ -41,6 +42,7 @@ tokenizers = (
defaultTokenizer(TokenType.DOT),
# Types
separated(floatTokenizer),
mapValue(separated(regexPatternTokenizer(TokenType.INTEGER, r'\d')), int),
stringTokenizer,
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 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'
INTEGER = 'integer'
STRING = 'string'
FLOAT = 'float'
NOTE = 'note'
BOOL = 'bool'
TYPE = 'type'

View File

@@ -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, {