Compare commits
15 Commits
left-assoc
...
optional-f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79a7b8bb1d | ||
|
|
2737139962 | ||
|
|
c5435e66ff | ||
|
|
460deb4981 | ||
|
|
69bac69946 | ||
|
|
6bd8046346 | ||
|
|
e70b5fa71a | ||
|
|
44d234d36a | ||
|
|
78ea26ea08 | ||
|
|
b6983df2d3 | ||
|
|
86cf5d01f3 | ||
|
|
a07b226edb | ||
|
|
9ae9da089b | ||
|
|
4f2058eaac | ||
|
|
a68f870037 |
@@ -1,4 +1,6 @@
|
|||||||
from smnp.ast.node.operator import BinaryOperator
|
from smnp.ast.node.model import Node
|
||||||
|
from smnp.ast.node.none import NoneNode
|
||||||
|
from smnp.ast.node.operator import BinaryOperator, Operator
|
||||||
from smnp.ast.node.term import TermParser
|
from smnp.ast.node.term import TermParser
|
||||||
from smnp.ast.parser import Parser
|
from smnp.ast.parser import Parser
|
||||||
from smnp.token.type import TokenType
|
from smnp.token.type import TokenType
|
||||||
@@ -20,7 +22,34 @@ class Or(BinaryOperator):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def ExpressionParser(input):
|
class Loop(BinaryOperator):
|
||||||
|
def __init__(self, pos):
|
||||||
|
super().__init__(pos)
|
||||||
|
self.children.append(NoneNode())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def parameters(self):
|
||||||
|
return self[3]
|
||||||
|
|
||||||
|
@parameters.setter
|
||||||
|
def parameters(self, value):
|
||||||
|
self[3] = value
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def loop(cls, left, parameters, operator, right):
|
||||||
|
node = cls(left.pos)
|
||||||
|
node.left = left
|
||||||
|
node.parameters = parameters
|
||||||
|
node.operator = operator
|
||||||
|
node.right = right
|
||||||
|
return node
|
||||||
|
|
||||||
|
|
||||||
|
class LoopParameters(Node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def ExpressionWithoutLoopParser(input):
|
||||||
expr1 = Parser.leftAssociativeOperatorParser(
|
expr1 = Parser.leftAssociativeOperatorParser(
|
||||||
TermParser,
|
TermParser,
|
||||||
[TokenType.PLUS, TokenType.MINUS],
|
[TokenType.PLUS, TokenType.MINUS],
|
||||||
@@ -49,3 +78,34 @@ def ExpressionParser(input):
|
|||||||
lambda left, op, right: Or.withValues(left, op, right)
|
lambda left, op, right: Or.withValues(left, op, right)
|
||||||
)(input)
|
)(input)
|
||||||
|
|
||||||
|
|
||||||
|
def LoopParser(input):
|
||||||
|
from smnp.ast.node.identifier import IdentifierLiteralParser
|
||||||
|
from smnp.ast.node.iterable import abstractIterableParser
|
||||||
|
from smnp.ast.node.statement import StatementParser
|
||||||
|
|
||||||
|
loopParameters = Parser.allOf(
|
||||||
|
Parser.terminal(TokenType.AS),
|
||||||
|
Parser.oneOf(
|
||||||
|
Parser.wrap(IdentifierLiteralParser, lambda id: LoopParameters.withChildren([id], id.pos)),
|
||||||
|
abstractIterableParser(LoopParameters, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN, IdentifierLiteralParser)
|
||||||
|
),
|
||||||
|
createNode=lambda asKeyword, parameters: parameters,
|
||||||
|
name="loop parameters"
|
||||||
|
)
|
||||||
|
|
||||||
|
return Parser.allOf(
|
||||||
|
ExpressionWithoutLoopParser,
|
||||||
|
Parser.optional(loopParameters),
|
||||||
|
Parser.terminal(TokenType.DASH, createNode=Operator.withValue),
|
||||||
|
StatementParser,
|
||||||
|
createNode=Loop.loop,
|
||||||
|
name="dash-loop"
|
||||||
|
)(input)
|
||||||
|
|
||||||
|
|
||||||
|
def ExpressionParser(input):
|
||||||
|
return Parser.oneOf(
|
||||||
|
LoopParser,
|
||||||
|
ExpressionWithoutLoopParser
|
||||||
|
)(input)
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
from smnp.ast.node.iterable import abstractIterableParser
|
|
||||||
from smnp.ast.node.model import Node
|
|
||||||
from smnp.ast.node.none import NoneNode
|
|
||||||
from smnp.ast.node.operator import BinaryOperator, Operator, UnaryOperator
|
from smnp.ast.node.operator import BinaryOperator, Operator, UnaryOperator
|
||||||
from smnp.ast.node.unit import UnitParser
|
from smnp.ast.node.unit import UnitParser
|
||||||
from smnp.ast.parser import Parser
|
from smnp.ast.parser import Parser
|
||||||
@@ -13,38 +10,8 @@ class NotOperator(UnaryOperator):
|
|||||||
class Power(BinaryOperator):
|
class Power(BinaryOperator):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Loop(BinaryOperator):
|
|
||||||
def __init__(self, pos):
|
|
||||||
super().__init__(pos)
|
|
||||||
self.children.append(NoneNode())
|
|
||||||
|
|
||||||
@property
|
|
||||||
def parameters(self):
|
|
||||||
return self[3]
|
|
||||||
|
|
||||||
@parameters.setter
|
|
||||||
def parameters(self, value):
|
|
||||||
self[3] = value
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def loop(cls, left, parameters, operator, right):
|
|
||||||
node = cls(left.pos)
|
|
||||||
node.left = left
|
|
||||||
node.parameters = parameters
|
|
||||||
node.operator = operator
|
|
||||||
node.right = right
|
|
||||||
return node
|
|
||||||
|
|
||||||
|
|
||||||
class LoopParameters(Node):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def FactorParser(input):
|
def FactorParser(input):
|
||||||
from smnp.ast.node.expression import ExpressionParser
|
from smnp.ast.node.expression import ExpressionParser
|
||||||
from smnp.ast.node.statement import StatementParser
|
|
||||||
from smnp.ast.node.identifier import IdentifierLiteralParser
|
|
||||||
|
|
||||||
parentheses = Parser.allOf(
|
parentheses = Parser.allOf(
|
||||||
Parser.terminal(TokenType.OPEN_PAREN),
|
Parser.terminal(TokenType.OPEN_PAREN),
|
||||||
@@ -75,27 +42,7 @@ def FactorParser(input):
|
|||||||
name="not"
|
name="not"
|
||||||
)
|
)
|
||||||
|
|
||||||
loopParameters = Parser.allOf(
|
|
||||||
Parser.terminal(TokenType.AS),
|
|
||||||
Parser.oneOf(
|
|
||||||
Parser.wrap(IdentifierLiteralParser, lambda id: LoopParameters.withChildren([id], id.pos)),
|
|
||||||
abstractIterableParser(LoopParameters, TokenType.OPEN_PAREN, TokenType.CLOSE_PAREN, IdentifierLiteralParser)
|
|
||||||
),
|
|
||||||
createNode=lambda asKeyword, parameters: parameters,
|
|
||||||
name="loop parameters"
|
|
||||||
)
|
|
||||||
|
|
||||||
loopFactor = Parser.allOf(
|
|
||||||
powerFactor,
|
|
||||||
Parser.optional(loopParameters),
|
|
||||||
Parser.terminal(TokenType.DASH, createNode=Operator.withValue),
|
|
||||||
StatementParser,
|
|
||||||
createNode=Loop.loop,
|
|
||||||
name="dash-loop"
|
|
||||||
)
|
|
||||||
|
|
||||||
return Parser.oneOf(
|
return Parser.oneOf(
|
||||||
loopFactor,
|
|
||||||
notOperator,
|
notOperator,
|
||||||
powerFactor,
|
powerFactor,
|
||||||
name="factor"
|
name="factor"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from smnp.ast.node.block import BlockParser
|
from smnp.ast.node.block import BlockParser
|
||||||
|
from smnp.ast.node.expression import ExpressionParser
|
||||||
from smnp.ast.node.identifier import IdentifierLiteralParser
|
from smnp.ast.node.identifier import IdentifierLiteralParser
|
||||||
from smnp.ast.node.iterable import abstractIterableParser
|
from smnp.ast.node.iterable import abstractIterableParser
|
||||||
from smnp.ast.node.model import Node
|
from smnp.ast.node.model import Node
|
||||||
@@ -16,7 +17,7 @@ class Argument(Node):
|
|||||||
|
|
||||||
def __init__(self, pos):
|
def __init__(self, pos):
|
||||||
super().__init__(pos)
|
super().__init__(pos)
|
||||||
self.children = [NoneNode(), NoneNode(), False]
|
self.children = [NoneNode(), NoneNode(), False, NoneNode()]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self):
|
def type(self):
|
||||||
@@ -48,6 +49,14 @@ class Argument(Node):
|
|||||||
self[2] = value
|
self[2] = value
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def optionalValue(self):
|
||||||
|
return self[3]
|
||||||
|
|
||||||
|
@optionalValue.setter
|
||||||
|
def optionalValue(self, value):
|
||||||
|
self[3] = value
|
||||||
|
|
||||||
class VarargNode(Node):
|
class VarargNode(Node):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -90,7 +99,7 @@ class FunctionDefinition(Node):
|
|||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
def ArgumentParser(input):
|
def RegularArgumentParser(input):
|
||||||
def createNode(type, variable, vararg):
|
def createNode(type, variable, vararg):
|
||||||
pos = type.pos if isinstance(type, Type) else variable.pos
|
pos = type.pos if isinstance(type, Type) else variable.pos
|
||||||
node = Argument(pos)
|
node = Argument(pos)
|
||||||
@@ -104,6 +113,33 @@ def ArgumentParser(input):
|
|||||||
Parser.doAssert(IdentifierLiteralParser, "argument name"),
|
Parser.doAssert(IdentifierLiteralParser, "argument name"),
|
||||||
Parser.optional(Parser.terminal(TokenType.DOTS, lambda val, pos: True)),
|
Parser.optional(Parser.terminal(TokenType.DOTS, lambda val, pos: True)),
|
||||||
createNode=createNode,
|
createNode=createNode,
|
||||||
|
name="regular function argument"
|
||||||
|
)(input)
|
||||||
|
|
||||||
|
|
||||||
|
def OptionalArgumentParser(input):
|
||||||
|
def createNode(type, variable, _, optional):
|
||||||
|
pos = type.pos if isinstance(type, Type) else variable.pos
|
||||||
|
node = Argument(pos)
|
||||||
|
node.type = type
|
||||||
|
node.variable = variable
|
||||||
|
node.optionalValue = optional
|
||||||
|
return node
|
||||||
|
|
||||||
|
return Parser.allOf(
|
||||||
|
Parser.optional(TypeParser),
|
||||||
|
Parser.doAssert(IdentifierLiteralParser, "argument name"),
|
||||||
|
Parser.terminal(TokenType.ASSIGN),
|
||||||
|
Parser.doAssert(ExpressionParser, "expression"),
|
||||||
|
createNode=createNode,
|
||||||
|
name="optional function argument"
|
||||||
|
)(input)
|
||||||
|
|
||||||
|
|
||||||
|
def ArgumentParser(input):
|
||||||
|
return Parser.oneOf(
|
||||||
|
OptionalArgumentParser,
|
||||||
|
RegularArgumentParser,
|
||||||
name="function argument"
|
name="function argument"
|
||||||
)(input)
|
)(input)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from smnp.ast.node.model import Node
|
from smnp.ast.node.model import Node
|
||||||
from smnp.ast.parser import Parser
|
from smnp.ast.parser import Parser
|
||||||
|
from smnp.token.type import TokenType
|
||||||
|
|
||||||
|
|
||||||
class Statement(Node):
|
class Statement(Node):
|
||||||
@@ -10,12 +11,27 @@ def StatementParser(input):
|
|||||||
from smnp.ast.node.block import BlockParser
|
from smnp.ast.node.block import BlockParser
|
||||||
from smnp.ast.node.condition import IfElseStatementParser
|
from smnp.ast.node.condition import IfElseStatementParser
|
||||||
from smnp.ast.node.expression import ExpressionParser
|
from smnp.ast.node.expression import ExpressionParser
|
||||||
|
|
||||||
from smnp.ast.node.ret import ReturnParser
|
from smnp.ast.node.ret import ReturnParser
|
||||||
return Parser.oneOf(
|
from smnp.ast.node.throw import ThrowParser
|
||||||
|
|
||||||
|
return withSemicolon(
|
||||||
|
Parser.oneOf(
|
||||||
IfElseStatementParser,
|
IfElseStatementParser,
|
||||||
ExpressionParser,
|
ExpressionParser, # Must be above BlockParser because of Map's syntax with curly braces
|
||||||
BlockParser,
|
BlockParser,
|
||||||
ReturnParser,
|
ReturnParser,
|
||||||
|
ThrowParser,
|
||||||
name="statement"
|
name="statement"
|
||||||
)(input)
|
), optional=True)(input)
|
||||||
|
|
||||||
|
|
||||||
|
def withSemicolon(parser, optional=False, doAssert=False):
|
||||||
|
semicolonParser = Parser.optional(Parser.terminal(TokenType.SEMICOLON)) if optional else Parser.terminal(
|
||||||
|
TokenType.SEMICOLON, doAssert=doAssert)
|
||||||
|
|
||||||
|
return Parser.allOf(
|
||||||
|
parser,
|
||||||
|
semicolonParser,
|
||||||
|
createNode=lambda stmt, semicolon: stmt,
|
||||||
|
name="semicolon" + "?" if optional else ""
|
||||||
|
)
|
||||||
|
|||||||
17
smnp/ast/node/throw.py
Normal file
17
smnp/ast/node/throw.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from smnp.ast.node.expression import ExpressionParser
|
||||||
|
from smnp.ast.node.valuable import Valuable
|
||||||
|
from smnp.ast.parser import Parser
|
||||||
|
from smnp.token.type import TokenType
|
||||||
|
|
||||||
|
|
||||||
|
class Throw(Valuable):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def ThrowParser(input):
|
||||||
|
return Parser.allOf(
|
||||||
|
Parser.terminal(TokenType.THROW),
|
||||||
|
Parser.doAssert(ExpressionParser, "error message as string"),
|
||||||
|
createNode=lambda throw, message: Throw.withValue(message, throw.pos),
|
||||||
|
name="throw"
|
||||||
|
)(input)
|
||||||
@@ -1,17 +1,19 @@
|
|||||||
from smnp.error.function import FunctionNotFoundException, MethodNotFoundException, IllegalFunctionInvocationException
|
from smnp.error.function import FunctionNotFoundException, MethodNotFoundException, IllegalFunctionInvocationException
|
||||||
from smnp.error.runtime import RuntimeException
|
from smnp.error.runtime import RuntimeException
|
||||||
from smnp.function.tools import argsTypesToString
|
from smnp.function.tools import argsTypesToString
|
||||||
from smnp.runtime.evaluators.function import BodyEvaluator
|
from smnp.runtime.evaluators.function import BodyEvaluator, Return
|
||||||
|
from smnp.type.model import Type
|
||||||
|
|
||||||
|
|
||||||
class Environment():
|
class Environment():
|
||||||
def __init__(self, scopes, functions, methods):
|
def __init__(self, scopes, functions, methods, source):
|
||||||
self.scopes = scopes
|
self.scopes = scopes
|
||||||
self.functions = functions
|
self.functions = functions
|
||||||
self.methods = methods
|
self.methods = methods
|
||||||
self.customFunctions = []
|
self.customFunctions = []
|
||||||
self.customMethods = []
|
self.customMethods = []
|
||||||
self.callStack = []
|
self.callStack = []
|
||||||
|
self.source = source
|
||||||
|
|
||||||
def invokeMethod(self, object, name, args):
|
def invokeMethod(self, object, name, args):
|
||||||
builtinMethodResult = self._invokeBuiltinMethod(object, name, args)
|
builtinMethodResult = self._invokeBuiltinMethod(object, name, args)
|
||||||
@@ -38,10 +40,15 @@ class Environment():
|
|||||||
if method.typeSignature.check([object])[0] and method.name == name: #Todo sprawdzic sygnature typu
|
if method.typeSignature.check([object])[0] and method.name == name: #Todo sprawdzic sygnature typu
|
||||||
signatureCheckresult = method.signature.check(args)
|
signatureCheckresult = method.signature.check(args)
|
||||||
if signatureCheckresult[0]:
|
if signatureCheckresult[0]:
|
||||||
self.scopes.append({argName: argValue for argName, argValue in zip(method.arguments, list(signatureCheckresult[1:]))})
|
self.scopes.append(method.defaultArgs)
|
||||||
|
self.scopes[-1].update({argName: argValue for argName, argValue in zip(method.arguments, list(signatureCheckresult[1:]))})
|
||||||
self.scopes[-1][method.alias] = object
|
self.scopes[-1][method.alias] = object
|
||||||
self.callStack.append(CallStackItem(name))
|
self.callStack.append(CallStackItem(name))
|
||||||
result = BodyEvaluator.evaluate(method.body, self).value # TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
|
result = Type.void()
|
||||||
|
try:
|
||||||
|
BodyEvaluator.evaluate(method.body, self).value # TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
|
||||||
|
except Return as r:
|
||||||
|
result = r.value
|
||||||
self.callStack.pop(-1)
|
self.callStack.pop(-1)
|
||||||
self.scopes.pop(-1)
|
self.scopes.pop(-1)
|
||||||
return (True, result)
|
return (True, result)
|
||||||
@@ -74,20 +81,25 @@ class Environment():
|
|||||||
if function.name == name:
|
if function.name == name:
|
||||||
signatureCheckresult = function.signature.check(args)
|
signatureCheckresult = function.signature.check(args)
|
||||||
if signatureCheckresult[0]:
|
if signatureCheckresult[0]:
|
||||||
self.scopes.append({ argName: argValue for argName, argValue in zip(function.arguments, list(signatureCheckresult[1:])) })
|
self.scopes.append(function.defaultArgs)
|
||||||
|
self.scopes[-1].update({ argName: argValue for argName, argValue in zip(function.arguments, list(signatureCheckresult[1:])) })
|
||||||
self.callStack.append(CallStackItem(name))
|
self.callStack.append(CallStackItem(name))
|
||||||
result = BodyEvaluator.evaluate(function.body, self).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
|
result = Type.void()
|
||||||
|
try:
|
||||||
|
BodyEvaluator.evaluate(function.body, self).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
|
||||||
|
except Return as r:
|
||||||
|
result = r.value
|
||||||
self.callStack.pop(-1)
|
self.callStack.pop(-1)
|
||||||
self.scopes.pop(-1)
|
self.scopes.pop(-1)
|
||||||
return (True, result)
|
return (True, result)
|
||||||
raise IllegalFunctionInvocationException(f"{function.name}{function.signature.string}", f"{name}{argsTypesToString(args)}")
|
raise IllegalFunctionInvocationException(f"{function.name}{function.signature.string}", f"{name}{argsTypesToString(args)}")
|
||||||
return (False, None)
|
return (False, None)
|
||||||
|
|
||||||
def addCustomFunction(self, name, signature, arguments, body):
|
def addCustomFunction(self, name, signature, arguments, body, defaultArguments):
|
||||||
if len([fun for fun in self.functions + self.customFunctions if fun.name == name]) > 0:
|
if len([fun for fun in self.functions + self.customFunctions if fun.name == name]) > 0:
|
||||||
raise RuntimeException(f"Cannot redeclare function '{name}'", None)
|
raise RuntimeException(f"Cannot redeclare function '{name}'", None)
|
||||||
|
|
||||||
self.customFunctions.append(CustomFunction(name, signature, arguments, body))
|
self.customFunctions.append(CustomFunction(name, signature, arguments, body, defaultArguments))
|
||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
# There is still problem with checking existing of generic types, like lists:
|
# There is still problem with checking existing of generic types, like lists:
|
||||||
@@ -98,14 +110,14 @@ class Environment():
|
|||||||
# function foo() { return 2 }
|
# function foo() { return 2 }
|
||||||
# }
|
# }
|
||||||
# Then calling [1, 2, 3, 4].foo() will produce 1, when the second method is more suitable
|
# Then calling [1, 2, 3, 4].foo() will produce 1, when the second method is more suitable
|
||||||
def addCustomMethod(self, typeSignature, alias, name, signature, arguments, body):
|
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:
|
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)
|
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:
|
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)
|
raise RuntimeException(f"Cannot redeclare method '{name}' for type '{typeSignature.matchers[0]}'", None)
|
||||||
|
|
||||||
self.customMethods.append(CustomMethod(typeSignature, alias, name, signature, arguments, body))
|
self.customMethods.append(CustomMethod(typeSignature, alias, name, signature, arguments, body, defaultArguments))
|
||||||
|
|
||||||
def findVariable(self, name, type=None, pos=None):
|
def findVariable(self, name, type=None, pos=None):
|
||||||
for scope in reversed(self.scopes):
|
for scope in reversed(self.scopes):
|
||||||
@@ -162,22 +174,23 @@ class Environment():
|
|||||||
class CallStackItem:
|
class CallStackItem:
|
||||||
def __init__(self, function):
|
def __init__(self, function):
|
||||||
self.function = function
|
self.function = function
|
||||||
self.value = None
|
|
||||||
|
|
||||||
|
|
||||||
class CustomFunction:
|
class CustomFunction:
|
||||||
def __init__(self, name, signature, arguments, body):
|
def __init__(self, name, signature, arguments, body, defaultArgs):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.signature = signature
|
self.signature = signature
|
||||||
self.arguments = arguments
|
self.arguments = arguments
|
||||||
self.body = body
|
self.body = body
|
||||||
|
self.defaultArgs = defaultArgs
|
||||||
|
|
||||||
|
|
||||||
class CustomMethod:
|
class CustomMethod:
|
||||||
def __init__(self, typeSignature, alias, name, signature, arguments, body):
|
def __init__(self, typeSignature, alias, name, signature, arguments, body, defaultArgs):
|
||||||
self.typeSignature = typeSignature
|
self.typeSignature = typeSignature
|
||||||
self.alias = alias
|
self.alias = alias
|
||||||
self.name = name
|
self.name = name
|
||||||
self.signature = signature
|
self.signature = signature
|
||||||
self.arguments = arguments
|
self.arguments = arguments
|
||||||
self.body = body
|
self.body = body
|
||||||
|
self.defaultArgs = defaultArgs
|
||||||
@@ -1,7 +1,3 @@
|
|||||||
from smnp.environment.environment import Environment
|
|
||||||
from smnp.module import functions, methods
|
|
||||||
|
|
||||||
|
|
||||||
def createEnvironment():
|
def createEnvironment():
|
||||||
return Environment([{}], functions, methods)
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ class SmnpException(Exception):
|
|||||||
def __init__(self, msg, pos):
|
def __init__(self, msg, pos):
|
||||||
self.msg = msg
|
self.msg = msg
|
||||||
self.pos = pos
|
self.pos = pos
|
||||||
|
self.file = None
|
||||||
|
|
||||||
def _title(self):
|
def _title(self):
|
||||||
pass
|
pass
|
||||||
@@ -10,7 +11,10 @@ class SmnpException(Exception):
|
|||||||
return ""
|
return ""
|
||||||
|
|
||||||
def _position(self):
|
def _position(self):
|
||||||
return "" if self.pos is None else f" [line {self.pos[0]+1}, col {self.pos[1]+1}]"
|
return "" if self.pos is None else f"[line {self.pos[0]+1}, col {self.pos[1]+1}]"
|
||||||
|
|
||||||
|
def _file(self):
|
||||||
|
return "" if self.file is None else f"File: {self.file}"
|
||||||
|
|
||||||
def message(self):
|
def message(self):
|
||||||
return f"{self._title()}{self._position()}:\n{self.msg}\n{self._postMessage()}"
|
return f"{self._title()}\n{self._file()} {self._position()}\n\n{self.msg}\n{self._postMessage()}"
|
||||||
|
|||||||
12
smnp/error/custom.py
Normal file
12
smnp/error/custom.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from smnp.error.runtime import RuntimeException
|
||||||
|
|
||||||
|
|
||||||
|
class CustomException(RuntimeException):
|
||||||
|
def __init__(self, message, pos):
|
||||||
|
super().__init__(message, pos)
|
||||||
|
|
||||||
|
def _title(self):
|
||||||
|
return "Execution Error"
|
||||||
|
|
||||||
|
def _postMessage(self):
|
||||||
|
return "\n" + self.environment.callStackToString() if len(self.environment.callStack) > 0 else ""
|
||||||
@@ -12,6 +12,9 @@ class Signature:
|
|||||||
|
|
||||||
def varargSignature(varargMatcher, *basicSignature, wrapVarargInValue=False):
|
def varargSignature(varargMatcher, *basicSignature, wrapVarargInValue=False):
|
||||||
def check(args):
|
def check(args):
|
||||||
|
if any([ matcher.optional for matcher in [ varargMatcher, *basicSignature ]]):
|
||||||
|
raise RuntimeError("Vararg signature can't have optional arguments")
|
||||||
|
|
||||||
if len(basicSignature) > len(args):
|
if len(basicSignature) > len(args):
|
||||||
return doesNotMatchVararg(basicSignature)
|
return doesNotMatchVararg(basicSignature)
|
||||||
|
|
||||||
@@ -38,7 +41,7 @@ def doesNotMatchVararg(basicSignature):
|
|||||||
|
|
||||||
def signature(*signature):
|
def signature(*signature):
|
||||||
def check(args):
|
def check(args):
|
||||||
if len(signature) != len(args):
|
if len(args) > len(signature) or len(args) < len([ matcher for matcher in signature if not matcher.optional ]):
|
||||||
return doesNotMatch(signature)
|
return doesNotMatch(signature)
|
||||||
|
|
||||||
for s, a in zip(signature, args):
|
for s, a in zip(signature, args):
|
||||||
@@ -52,6 +55,12 @@ def signature(*signature):
|
|||||||
return Signature(check, string, signature)
|
return Signature(check, string, signature)
|
||||||
|
|
||||||
|
|
||||||
|
def optional(matcher):
|
||||||
|
matcher.optional = True
|
||||||
|
matcher.string += "?"
|
||||||
|
return matcher
|
||||||
|
|
||||||
|
|
||||||
def doesNotMatch(sign):
|
def doesNotMatch(sign):
|
||||||
return (False, *[None for n in sign])
|
return (False, *[None for n in sign])
|
||||||
|
|
||||||
|
|||||||
@@ -5,5 +5,5 @@ from smnp.program.interpreter import Interpreter
|
|||||||
|
|
||||||
def loadStandardLibrary():
|
def loadStandardLibrary():
|
||||||
mainSource = resource_string('smnp.library.code', 'main.mus').decode("utf-8")
|
mainSource = resource_string('smnp.library.code', 'main.mus').decode("utf-8")
|
||||||
env = Interpreter.interpretString(mainSource)
|
env = Interpreter.interpretString(mainSource, "<stdlib>")
|
||||||
return env
|
return env
|
||||||
|
|||||||
@@ -9,9 +9,6 @@ def main():
|
|||||||
try:
|
try:
|
||||||
stdLibraryEnv = loadStandardLibrary()
|
stdLibraryEnv = loadStandardLibrary()
|
||||||
Interpreter.interpretFile(sys.argv[1], printTokens=False, printAst=False, execute=True, baseEnvironment=stdLibraryEnv)
|
Interpreter.interpretFile(sys.argv[1], printTokens=False, printAst=False, execute=True, baseEnvironment=stdLibraryEnv)
|
||||||
#draft()
|
|
||||||
#tokens = tokenize(['function a(b...) { x+y}'])
|
|
||||||
#FunctionDefinitionParser(tokens).node.print()
|
|
||||||
|
|
||||||
except SmnpException as e:
|
except SmnpException as e:
|
||||||
print(e.message())
|
print(e.message())
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
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 ]
|
||||||
methods = [ *system.methods, *mic.methods, *note.methods, *iterable.methods, *sound.methods, *synth.methods, *string.functions, *util.methods ]
|
methods = [ *system.methods, *mic.methods, *note.methods, *iterable.methods, *sound.methods, *synth.methods, *string.methods, *util.methods ]
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from smnp.module.iterable.function import combine, map, range, get
|
from smnp.module.iterable.function import combine, map, get
|
||||||
|
|
||||||
functions = [ combine.function, map.function, range.function ]
|
functions = [ combine.function, map.function ]
|
||||||
methods = [ get.function ]
|
methods = [ get.function ]
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from smnp.module.note.function import tuplet, transpose, semitones, octave, duration, interval
|
from smnp.module.note.function import note
|
||||||
|
|
||||||
functions = [ semitones.function, interval.function, transpose.function, tuplet.function ]
|
functions = [ note.function ]
|
||||||
methods = [ duration.function, octave.function ]
|
methods = []
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
from smnp.function.model import Function
|
|
||||||
from smnp.function.signature import signature
|
|
||||||
from smnp.type.model import Type
|
|
||||||
from smnp.type.signature.matcher.type import ofType
|
|
||||||
|
|
||||||
_signature = signature(ofType(Type.NOTE), ofType(Type.INTEGER))
|
|
||||||
def _function(env, note, duration):
|
|
||||||
return Type.note(note.value.withDuration(duration.value))
|
|
||||||
|
|
||||||
|
|
||||||
function = Function(_signature, _function, 'withDuration')
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
from smnp.function.model import Function, CombinedFunction
|
|
||||||
from smnp.function.signature import varargSignature
|
|
||||||
from smnp.note.interval import intervalToString
|
|
||||||
from smnp.note.model import Note
|
|
||||||
from smnp.type.model import Type
|
|
||||||
from smnp.type.signature.matcher.list import listOf
|
|
||||||
from smnp.type.signature.matcher.type import ofTypes
|
|
||||||
|
|
||||||
_signature1 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
|
|
||||||
def _function1(env, vararg):
|
|
||||||
withoutPauses = [note.value for note in vararg if note.type == Type.NOTE]
|
|
||||||
if len(withoutPauses) < 2:
|
|
||||||
return Type.list([])
|
|
||||||
semitones = [Note.checkInterval(withoutPauses[i-1], withoutPauses[i]) for i in range(1, len(withoutPauses))]
|
|
||||||
return Type.list([Type.string(intervalToString(s)) for s in semitones]).decompose()
|
|
||||||
|
|
||||||
|
|
||||||
_signature2 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
|
|
||||||
def _function2(env, vararg):
|
|
||||||
return Type.list([_function1(env, arg.value) for arg in vararg]).decompose()
|
|
||||||
|
|
||||||
|
|
||||||
function = CombinedFunction(
|
|
||||||
'interval',
|
|
||||||
Function(_signature1, _function1),
|
|
||||||
Function(_signature2, _function2)
|
|
||||||
)
|
|
||||||
11
smnp/module/note/function/note.py
Normal file
11
smnp/module/note/function/note.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from smnp.function.model import Function
|
||||||
|
from smnp.function.signature import signature
|
||||||
|
from smnp.note.model import Note
|
||||||
|
from smnp.type.model import Type
|
||||||
|
from smnp.type.signature.matcher.type import ofType
|
||||||
|
|
||||||
|
_signature = signature(ofType(Type.STRING), ofType(Type.INTEGER), ofType(Type.INTEGER), ofType(Type.BOOL))
|
||||||
|
def _function(env, note, octave, duration, dot):
|
||||||
|
return Type.note(Note(note.value, octave.value, duration.value, dot.value))
|
||||||
|
|
||||||
|
function = Function(_signature, _function, 'Note')
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
from smnp.function.model import Function
|
|
||||||
from smnp.function.signature import signature
|
|
||||||
from smnp.type.model import Type
|
|
||||||
from smnp.type.signature.matcher.type import ofType
|
|
||||||
|
|
||||||
_signature = signature(ofType(Type.NOTE), ofType(Type.INTEGER))
|
|
||||||
def _function(env, note, octave):
|
|
||||||
return Type.note(note.value.withOctave(octave.value))
|
|
||||||
|
|
||||||
|
|
||||||
function = Function(_signature, _function, 'withOctave')
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
from smnp.function.model import Function, CombinedFunction
|
|
||||||
from smnp.function.signature import varargSignature
|
|
||||||
from smnp.note.model import Note
|
|
||||||
from smnp.type.model import Type
|
|
||||||
from smnp.type.signature.matcher.list import listOf
|
|
||||||
from smnp.type.signature.matcher.type import ofTypes
|
|
||||||
|
|
||||||
_signature1 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
|
|
||||||
def _function1(env, vararg):
|
|
||||||
withoutPauses = [note.value for note in vararg if note.type == Type.NOTE]
|
|
||||||
if len(withoutPauses) < 2:
|
|
||||||
return Type.list([])
|
|
||||||
return Type.list([Type.integer(Note.checkInterval(withoutPauses[i-1], withoutPauses[i])) for i in range(1, len(withoutPauses))]).decompose()
|
|
||||||
|
|
||||||
|
|
||||||
_signature2 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
|
|
||||||
def _function2(env, vararg):
|
|
||||||
return Type.list([_function1(env, arg.value) for arg in vararg]).decompose()
|
|
||||||
|
|
||||||
|
|
||||||
function = CombinedFunction(
|
|
||||||
"semitones",
|
|
||||||
Function(_signature1, _function1),
|
|
||||||
Function(_signature2, _function2),
|
|
||||||
)
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
from smnp.function.model import CombinedFunction, Function
|
|
||||||
from smnp.function.signature import varargSignature
|
|
||||||
from smnp.type.model import Type
|
|
||||||
from smnp.type.signature.matcher.list import listOf
|
|
||||||
from smnp.type.signature.matcher.type import ofTypes
|
|
||||||
|
|
||||||
_signature1 = varargSignature(ofTypes(Type.INTEGER, Type.NOTE), ofTypes(Type.INTEGER))
|
|
||||||
def _function1(env, value, vararg):
|
|
||||||
transposed = [Type.note(arg.value.transpose(value.value)) if arg.type == Type.NOTE else arg for arg in vararg]
|
|
||||||
return Type.list(transposed).decompose()
|
|
||||||
|
|
||||||
|
|
||||||
_signature2 = varargSignature(listOf(Type.INTEGER, Type.NOTE), ofTypes(Type.INTEGER))
|
|
||||||
def _function2(env, value, vararg):
|
|
||||||
return Type.list([_function1(env, value, arg.value) for arg in vararg]).decompose()
|
|
||||||
|
|
||||||
|
|
||||||
function = CombinedFunction(
|
|
||||||
'transpose',
|
|
||||||
Function(_signature1, _function1),
|
|
||||||
Function(_signature2, _function2)
|
|
||||||
)
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
from smnp.function.model import CombinedFunction, Function
|
|
||||||
from smnp.function.signature import signature, varargSignature
|
|
||||||
from smnp.type.model import Type
|
|
||||||
from smnp.type.signature.matcher.list import listOf
|
|
||||||
from smnp.type.signature.matcher.type import ofTypes
|
|
||||||
|
|
||||||
_signature1 = varargSignature(ofTypes(Type.NOTE), ofTypes(Type.INTEGER), ofTypes(Type.INTEGER))
|
|
||||||
def _function1(env, n, m, vararg):
|
|
||||||
t = [Type.note(arg.value.withDuration(int(arg.value.duration * n.value / m.value))) for arg in vararg]
|
|
||||||
return Type.list(t).decompose()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_signature2 = signature(ofTypes(Type.INTEGER), ofTypes(Type.INTEGER), listOf(Type.NOTE))
|
|
||||||
def _function2(env, n, m, notes):
|
|
||||||
return _function1(env, n, m, notes.value)
|
|
||||||
|
|
||||||
|
|
||||||
function = CombinedFunction(
|
|
||||||
'tuplet',
|
|
||||||
Function(_signature1, _function1),
|
|
||||||
Function(_signature2, _function2)
|
|
||||||
)
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from smnp.module.string.function import concat
|
from smnp.module.string.function import concat, stringify
|
||||||
|
|
||||||
functions = [ concat.function ]
|
functions = [ concat.function ]
|
||||||
methods = []
|
methods = [ stringify.function ]
|
||||||
10
smnp/module/string/function/stringify.py
Normal file
10
smnp/module/string/function/stringify.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from smnp.function.model import Function
|
||||||
|
from smnp.function.signature import signature
|
||||||
|
from smnp.type.model import Type
|
||||||
|
from smnp.type.signature.matcher.type import allTypes
|
||||||
|
|
||||||
|
_signature = signature(allTypes())
|
||||||
|
def _function(env, object):
|
||||||
|
return Type.string(object.stringify())
|
||||||
|
|
||||||
|
function = Function(_signature, _function, 'toString')
|
||||||
@@ -1,25 +1,13 @@
|
|||||||
from smnp.function.model import CombinedFunction, Function
|
from smnp.function.model import Function
|
||||||
from smnp.function.signature import varargSignature
|
from smnp.function.signature import signature
|
||||||
from smnp.module.synth.lib.player import playNotes
|
from smnp.module.synth.lib.player import play
|
||||||
from smnp.type.model import Type
|
from smnp.type.model import Type
|
||||||
from smnp.type.signature.matcher.list import listOf
|
from smnp.type.signature.matcher.type import ofType
|
||||||
from smnp.type.signature.matcher.type import ofTypes
|
|
||||||
|
|
||||||
_signature1 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
|
_signature = signature(ofType(Type.NOTE))
|
||||||
def _function1(env, vararg):
|
def _function(env, note):
|
||||||
notes = [arg.value for arg in vararg]
|
|
||||||
bpm = env.findVariable('bpm')
|
bpm = env.findVariable('bpm')
|
||||||
playNotes(notes, bpm.value)
|
play(note.value, bpm.value)
|
||||||
|
|
||||||
|
|
||||||
_signature2 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
|
function = Function(_signature, _function, 'synthNote')
|
||||||
def _function2(env, vararg):
|
|
||||||
for arg in vararg:
|
|
||||||
_function1(env, arg.value)
|
|
||||||
|
|
||||||
|
|
||||||
function = CombinedFunction(
|
|
||||||
'synth',
|
|
||||||
Function(_signature1, _function1),
|
|
||||||
Function(_signature2, _function2)
|
|
||||||
)
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
from smnp.module.system.function import sleep, display, debug, exit, type
|
from smnp.module.system.function import sleep, display, displayln, debug, exit, type
|
||||||
|
|
||||||
functions = [ debug.function, display.function, exit.function, sleep.function, type.function ]
|
functions = [ debug.function, display.function, displayln.function, exit.function, sleep.function, type.function ]
|
||||||
methods = []
|
methods = []
|
||||||
@@ -4,7 +4,7 @@ from smnp.type.signature.matcher.type import allTypes
|
|||||||
|
|
||||||
_signature = varargSignature(allTypes())
|
_signature = varargSignature(allTypes())
|
||||||
def _function(env, vararg):
|
def _function(env, vararg):
|
||||||
print("".join([arg.stringify() for arg in vararg]))
|
print("".join([arg.stringify() for arg in vararg]), end="")
|
||||||
|
|
||||||
|
|
||||||
function = Function(_signature, _function, 'print')
|
function = Function(_signature, _function, 'print')
|
||||||
10
smnp/module/system/function/displayln.py
Normal file
10
smnp/module/system/function/displayln.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from smnp.function.model import Function
|
||||||
|
from smnp.function.signature import varargSignature
|
||||||
|
from smnp.type.signature.matcher.type import allTypes
|
||||||
|
|
||||||
|
_signature = varargSignature(allTypes())
|
||||||
|
def _function(env, vararg):
|
||||||
|
print("".join([arg.stringify() for arg in vararg]))
|
||||||
|
|
||||||
|
|
||||||
|
function = Function(_signature, _function, 'println')
|
||||||
@@ -1,29 +1,12 @@
|
|||||||
import random as r
|
import random
|
||||||
|
|
||||||
from smnp.error.function import IllegalArgumentException
|
from smnp.function.model import Function
|
||||||
from smnp.function.model import Function, CombinedFunction
|
from smnp.function.signature import signature
|
||||||
from smnp.function.signature import varargSignature
|
|
||||||
from smnp.type.model import Type
|
from smnp.type.model import Type
|
||||||
from smnp.type.signature.matcher.list import listMatches
|
from smnp.type.signature.matcher.type import ofType
|
||||||
from smnp.type.signature.matcher.type import ofTypes
|
|
||||||
|
|
||||||
|
_signature = signature(ofType(Type.INTEGER), ofType(Type.INTEGER))
|
||||||
|
def _function(env, min, max):
|
||||||
|
return Type.integer(random.randint(min.value, max.value))
|
||||||
|
|
||||||
def forType(t):
|
function = Function(_signature, _function, 'rand')
|
||||||
_signature = varargSignature(listMatches(ofTypes(Type.PERCENT), ofTypes(t)))
|
|
||||||
def _function(env, vararg):
|
|
||||||
choice = r.random()
|
|
||||||
acc = 0
|
|
||||||
if sum(arg.value[0].value for arg in vararg) != 1.0:
|
|
||||||
raise IllegalArgumentException("Sum of all percentage values must be equal 100%")
|
|
||||||
for arg in vararg:
|
|
||||||
percent, item = arg.value
|
|
||||||
acc += percent.value
|
|
||||||
if choice <= acc:
|
|
||||||
return item
|
|
||||||
|
|
||||||
return Function(_signature, _function)
|
|
||||||
|
|
||||||
|
|
||||||
function = CombinedFunction('random', *[ forType(t) for t in Type if t != Type.VOID ])
|
|
||||||
|
|
||||||
#TODO: sample
|
|
||||||
@@ -41,11 +41,15 @@ class NotePitch(Enum):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def toPitch(string):
|
def toPitch(string):
|
||||||
try:
|
if string.lower() in stringToPitch:
|
||||||
return stringToPitch[string.lower()]
|
return stringToPitch[string.lower()]
|
||||||
except KeyError as e:
|
|
||||||
|
if string.upper() in NotePitch.__members__:
|
||||||
|
return NotePitch[string.upper()]
|
||||||
|
|
||||||
raise NoteException(f"Note '{string}' does not exist")
|
raise NoteException(f"Note '{string}' does not exist")
|
||||||
|
|
||||||
|
|
||||||
stringToPitch = {
|
stringToPitch = {
|
||||||
'cb': NotePitch.H,
|
'cb': NotePitch.H,
|
||||||
'c': NotePitch.C,
|
'c': NotePitch.C,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from smnp.ast.parser import parse
|
from smnp.ast.parser import parse
|
||||||
from smnp.environment.factory import createEnvironment
|
from smnp.environment.environment import Environment
|
||||||
from smnp.error.runtime import RuntimeException
|
from smnp.error.runtime import RuntimeException
|
||||||
|
from smnp.module import functions, methods
|
||||||
from smnp.program.FileReader import readLines
|
from smnp.program.FileReader import readLines
|
||||||
from smnp.runtime.evaluator import evaluate
|
from smnp.runtime.evaluator import evaluate
|
||||||
from smnp.token.tokenizer import tokenize
|
from smnp.token.tokenizer import tokenize
|
||||||
@@ -9,16 +10,31 @@ from smnp.token.tokenizer import tokenize
|
|||||||
class Interpreter:
|
class Interpreter:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def interpretString(string, printTokens=False, printAst=False, execute=True, baseEnvironment=None):
|
def interpretString(string, source, printTokens=False, printAst=False, execute=True, baseEnvironment=None):
|
||||||
return Interpreter._interpret(string.splitlines(), printTokens, printAst, execute, baseEnvironment)
|
return Interpreter._interpret(
|
||||||
|
string.splitlines(),
|
||||||
|
source,
|
||||||
|
printTokens,
|
||||||
|
printAst,
|
||||||
|
execute,
|
||||||
|
baseEnvironment,
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def interpretFile(file, printTokens=False, printAst=False, execute=True, baseEnvironment=None):
|
def interpretFile(file, printTokens=False, printAst=False, execute=True, baseEnvironment=None, source=None):
|
||||||
return Interpreter._interpret(readLines(file), printTokens, printAst, execute, baseEnvironment)
|
return Interpreter._interpret(
|
||||||
|
readLines(file),
|
||||||
|
source if source is not None else file,
|
||||||
|
printTokens,
|
||||||
|
printAst,
|
||||||
|
execute,
|
||||||
|
baseEnvironment,
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _interpret(lines, printTokens=False, printAst=False, execute=True, baseEnvironment=None):
|
def _interpret(lines, source, printTokens=False, printAst=False, execute=True, baseEnvironment=None):
|
||||||
environment = createEnvironment()
|
environment = Environment([{}], functions, methods, source=source)
|
||||||
|
|
||||||
if baseEnvironment is not None:
|
if baseEnvironment is not None:
|
||||||
environment.extend(baseEnvironment)
|
environment.extend(baseEnvironment)
|
||||||
|
|
||||||
@@ -37,4 +53,5 @@ class Interpreter:
|
|||||||
return environment
|
return environment
|
||||||
except RuntimeException as e:
|
except RuntimeException as e:
|
||||||
e.environment = environment
|
e.environment = environment
|
||||||
|
e.file = environment.source
|
||||||
raise e
|
raise e
|
||||||
@@ -5,6 +5,7 @@ from smnp.ast.node.function import FunctionDefinition
|
|||||||
from smnp.ast.node.imports import Import
|
from smnp.ast.node.imports import Import
|
||||||
from smnp.ast.node.program import Program
|
from smnp.ast.node.program import Program
|
||||||
from smnp.ast.node.ret import Return
|
from smnp.ast.node.ret import Return
|
||||||
|
from smnp.ast.node.throw import Throw
|
||||||
from smnp.error.runtime import RuntimeException
|
from smnp.error.runtime import RuntimeException
|
||||||
from smnp.type.model import Type
|
from smnp.type.model import Type
|
||||||
|
|
||||||
@@ -69,7 +70,6 @@ class EvaluationResult():
|
|||||||
|
|
||||||
def evaluate(node, environment):
|
def evaluate(node, environment):
|
||||||
from smnp.runtime.evaluators.program import ProgramEvaluator
|
from smnp.runtime.evaluators.program import ProgramEvaluator
|
||||||
|
|
||||||
from smnp.runtime.evaluators.expression import expressionEvaluator
|
from smnp.runtime.evaluators.expression import expressionEvaluator
|
||||||
from smnp.runtime.evaluators.condition import IfElseStatementEvaluator
|
from smnp.runtime.evaluators.condition import IfElseStatementEvaluator
|
||||||
from smnp.runtime.evaluators.block import BlockEvaluator
|
from smnp.runtime.evaluators.block import BlockEvaluator
|
||||||
@@ -77,6 +77,8 @@ def evaluate(node, environment):
|
|||||||
from smnp.runtime.evaluators.function import FunctionDefinitionEvaluator
|
from smnp.runtime.evaluators.function import FunctionDefinitionEvaluator
|
||||||
from smnp.runtime.evaluators.function import ReturnEvaluator
|
from smnp.runtime.evaluators.function import ReturnEvaluator
|
||||||
from smnp.runtime.evaluators.extend import ExtendEvaluator
|
from smnp.runtime.evaluators.extend import ExtendEvaluator
|
||||||
|
from smnp.runtime.evaluators.throw import ThrowEvaluator
|
||||||
|
|
||||||
result = Evaluator.oneOf(
|
result = Evaluator.oneOf(
|
||||||
Evaluator.forNodes(ProgramEvaluator.evaluate, Program),
|
Evaluator.forNodes(ProgramEvaluator.evaluate, Program),
|
||||||
Evaluator.forNodes(IfElseStatementEvaluator.evaluate, IfElse),
|
Evaluator.forNodes(IfElseStatementEvaluator.evaluate, IfElse),
|
||||||
@@ -85,6 +87,7 @@ def evaluate(node, environment):
|
|||||||
Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinition),
|
Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinition),
|
||||||
Evaluator.forNodes(ReturnEvaluator.evaluate, Return),
|
Evaluator.forNodes(ReturnEvaluator.evaluate, Return),
|
||||||
Evaluator.forNodes(ExtendEvaluator.evaluate, Extend),
|
Evaluator.forNodes(ExtendEvaluator.evaluate, Extend),
|
||||||
|
Evaluator.forNodes(ThrowEvaluator.evaluate, Throw),
|
||||||
#Evaluator.forNodes(ImportEvaluator.evaluate, ImportNode),
|
#Evaluator.forNodes(ImportEvaluator.evaluate, ImportNode),
|
||||||
#Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinitionNode),
|
#Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinitionNode),
|
||||||
#Evaluator.forNodes(ExtendEvaluator.evaluate, ExtendNode),
|
#Evaluator.forNodes(ExtendEvaluator.evaluate, ExtendNode),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from smnp.ast.node.condition import IfElse
|
from smnp.ast.node.condition import IfElse
|
||||||
from smnp.ast.node.expression import Sum, Relation
|
from smnp.ast.node.expression import Sum, Relation, And, Or, Loop
|
||||||
from smnp.ast.node.factor import NotOperator, Power, Loop
|
from smnp.ast.node.factor import NotOperator, Power
|
||||||
from smnp.ast.node.identifier import FunctionCall, Assignment
|
from smnp.ast.node.identifier import FunctionCall, Assignment
|
||||||
from smnp.ast.node.term import Product
|
from smnp.ast.node.term import Product
|
||||||
from smnp.ast.node.unit import MinusOperator, Access
|
from smnp.ast.node.unit import MinusOperator, Access
|
||||||
@@ -24,6 +24,8 @@ def expressionEvaluator(doAssert=False):
|
|||||||
from smnp.runtime.evaluators.sum import SumEvaluator
|
from smnp.runtime.evaluators.sum import SumEvaluator
|
||||||
from smnp.runtime.evaluators.relation import RelationEvaluator
|
from smnp.runtime.evaluators.relation import RelationEvaluator
|
||||||
from smnp.runtime.evaluators.condition import IfElseEvaluator
|
from smnp.runtime.evaluators.condition import IfElseEvaluator
|
||||||
|
from smnp.runtime.evaluators.logic import AndEvaluator
|
||||||
|
from smnp.runtime.evaluators.logic import OrEvaluator
|
||||||
result = Evaluator.oneOf(
|
result = Evaluator.oneOf(
|
||||||
Evaluator.forNodes(FunctionCallEvaluator.evaluate, FunctionCall),
|
Evaluator.forNodes(FunctionCallEvaluator.evaluate, FunctionCall),
|
||||||
Evaluator.forNodes(MinusEvaluator.evaluate, MinusOperator),
|
Evaluator.forNodes(MinusEvaluator.evaluate, MinusOperator),
|
||||||
@@ -36,6 +38,8 @@ def expressionEvaluator(doAssert=False):
|
|||||||
Evaluator.forNodes(SumEvaluator.evaluate, Sum),
|
Evaluator.forNodes(SumEvaluator.evaluate, Sum),
|
||||||
Evaluator.forNodes(RelationEvaluator.evaluate, Relation),
|
Evaluator.forNodes(RelationEvaluator.evaluate, Relation),
|
||||||
Evaluator.forNodes(IfElseEvaluator.evaluate, IfElse),
|
Evaluator.forNodes(IfElseEvaluator.evaluate, IfElse),
|
||||||
|
Evaluator.forNodes(AndEvaluator.evaluate, And),
|
||||||
|
Evaluator.forNodes(OrEvaluator.evaluate, Or),
|
||||||
AtomEvaluator.evaluate
|
AtomEvaluator.evaluate
|
||||||
)(node, environment)
|
)(node, environment)
|
||||||
|
|
||||||
@@ -46,3 +50,15 @@ def expressionEvaluator(doAssert=False):
|
|||||||
|
|
||||||
|
|
||||||
return evaluateExpression
|
return evaluateExpression
|
||||||
|
|
||||||
|
|
||||||
|
def expressionEvaluatorWithMatcher(matcher, exceptionProvider, doAssert=True):
|
||||||
|
def evaluate(node, environment):
|
||||||
|
value = expressionEvaluator(doAssert=doAssert)(node, environment).value
|
||||||
|
|
||||||
|
if not matcher.match(value):
|
||||||
|
raise exceptionProvider(value)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
return evaluate
|
||||||
@@ -32,8 +32,8 @@ class ExtendEvaluator(Evaluator):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def _evaluateMethodDefinition(cls, node, environment, type, variable):
|
def _evaluateMethodDefinition(cls, node, environment, type, variable):
|
||||||
name = node.name.value
|
name = node.name.value
|
||||||
signature = argumentsNodeToMethodSignature(node.arguments)
|
defaultArguments, signature = argumentsNodeToMethodSignature(node.arguments, environment)
|
||||||
arguments = [arg.variable.value for arg in node.arguments]
|
arguments = [arg.variable.value for arg in node.arguments]
|
||||||
body = node.body
|
body = node.body
|
||||||
environment.addCustomMethod(type, variable, name, signature, arguments, body)
|
environment.addCustomMethod(type, variable, name, signature, arguments, body, defaultArguments)
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from smnp.runtime.evaluators.expression import expressionEvaluator
|
|||||||
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.runtime.tools.signature import argumentsNodeToMethodSignature
|
from smnp.runtime.tools.signature import argumentsNodeToMethodSignature
|
||||||
|
from smnp.type.model import Type
|
||||||
|
|
||||||
|
|
||||||
class FunctionCallEvaluator(Evaluator):
|
class FunctionCallEvaluator(Evaluator):
|
||||||
@@ -24,10 +25,10 @@ class FunctionDefinitionEvaluator(Evaluator):
|
|||||||
def evaluator(cls, node, environment):
|
def evaluator(cls, node, environment):
|
||||||
try:
|
try:
|
||||||
name = node.name.value
|
name = node.name.value
|
||||||
signature = argumentsNodeToMethodSignature(node.arguments)
|
defaultArguments, signature = argumentsNodeToMethodSignature(node.arguments, environment)
|
||||||
arguments = [ arg.variable.value for arg in node.arguments ]
|
arguments = [ arg.variable.value for arg in node.arguments ]
|
||||||
body = node.body
|
body = node.body
|
||||||
environment.addCustomFunction(name, signature, arguments, body)
|
environment.addCustomFunction(name, signature, arguments, body, defaultArguments)
|
||||||
except RuntimeException as e:
|
except RuntimeException as e:
|
||||||
raise updatePos(e, node)
|
raise updatePos(e, node)
|
||||||
|
|
||||||
@@ -38,8 +39,6 @@ class BodyEvaluator(Evaluator):
|
|||||||
def evaluator(cls, node, environment):
|
def evaluator(cls, node, environment):
|
||||||
for child in node.children:
|
for child in node.children:
|
||||||
evaluate(child, environment)
|
evaluate(child, environment)
|
||||||
if environment.callStack[-1].value is not None:
|
|
||||||
return environment.callStack[-1].value
|
|
||||||
|
|
||||||
|
|
||||||
class ReturnEvaluator(Evaluator):
|
class ReturnEvaluator(Evaluator):
|
||||||
@@ -47,7 +46,21 @@ class ReturnEvaluator(Evaluator):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def evaluator(cls, node, environment):
|
def evaluator(cls, node, environment):
|
||||||
if len(environment.callStack) > 0:
|
if len(environment.callStack) > 0:
|
||||||
returnValue = expressionEvaluator(doAssert=True)(node.value, environment)
|
returnValue = expressionEvaluator()(node.value, environment).value
|
||||||
environment.callStack[-1].value = returnValue.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 to 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:
|
else:
|
||||||
raise RuntimeException("Cannot use 'return' statement outside a function or method", node.pos, environment)
|
raise RuntimeException("Cannot use 'return' statement outside a function or method", node.pos, environment)
|
||||||
|
|
||||||
|
|
||||||
|
class Return(Exception):
|
||||||
|
def __init__(self, value):
|
||||||
|
if value is None:
|
||||||
|
value = Type.void()
|
||||||
|
|
||||||
|
self.value = value
|
||||||
21
smnp/runtime/evaluators/logic.py
Normal file
21
smnp/runtime/evaluators/logic.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
from smnp.runtime.evaluator import Evaluator
|
||||||
|
from smnp.runtime.evaluators.expression import expressionEvaluator
|
||||||
|
from smnp.type.model import Type
|
||||||
|
|
||||||
|
|
||||||
|
class AndEvaluator(Evaluator):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def evaluator(cls, node, environment):
|
||||||
|
left = expressionEvaluator(doAssert=True)(node.left, environment).value
|
||||||
|
right = expressionEvaluator(doAssert=True)(node.right, environment).value
|
||||||
|
return Type.bool(left.value and right.value)
|
||||||
|
|
||||||
|
|
||||||
|
class OrEvaluator(Evaluator):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def evaluator(cls, node, environment):
|
||||||
|
left = expressionEvaluator(doAssert=True)(node.left, environment).value
|
||||||
|
right = expressionEvaluator(doAssert=True)(node.right, environment).value
|
||||||
|
return Type.bool(left.value or right.value)
|
||||||
@@ -21,11 +21,11 @@ class RelationEvaluator(Evaluator):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def equalOperatorEvaluator(cls, left, operator, right):
|
def equalOperatorEvaluator(cls, left, operator, right):
|
||||||
return Type.bool(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):
|
||||||
return Type.bool(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):
|
||||||
|
|||||||
17
smnp/runtime/evaluators/throw.py
Normal file
17
smnp/runtime/evaluators/throw.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from smnp.error.custom import CustomException
|
||||||
|
from smnp.error.runtime import RuntimeException
|
||||||
|
from smnp.runtime.evaluator import Evaluator
|
||||||
|
from smnp.runtime.evaluators.expression import expressionEvaluator
|
||||||
|
from smnp.type.model import Type
|
||||||
|
|
||||||
|
|
||||||
|
class ThrowEvaluator(Evaluator):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
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)
|
||||||
@@ -2,7 +2,8 @@ from smnp.ast.node import type as ast
|
|||||||
from smnp.ast.node.none import NoneNode
|
from smnp.ast.node.none import NoneNode
|
||||||
from smnp.ast.node.type import TypesList
|
from smnp.ast.node.type import TypesList
|
||||||
from smnp.error.runtime import RuntimeException
|
from smnp.error.runtime import RuntimeException
|
||||||
from smnp.function.signature import varargSignature, signature
|
from smnp.function.signature import varargSignature, signature, optional
|
||||||
|
from smnp.runtime.evaluators.expression import expressionEvaluator, expressionEvaluatorWithMatcher
|
||||||
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
|
||||||
from smnp.type.signature.matcher.list import listOfMatchers
|
from smnp.type.signature.matcher.list import listOfMatchers
|
||||||
@@ -10,11 +11,19 @@ from smnp.type.signature.matcher.map import mapOfMatchers
|
|||||||
from smnp.type.signature.matcher.type import allTypes, oneOf, ofType
|
from smnp.type.signature.matcher.type import allTypes, oneOf, ofType
|
||||||
|
|
||||||
|
|
||||||
def argumentsNodeToMethodSignature(node):
|
def evaluateDefaultArguments(node, environment):
|
||||||
|
defaultValues = { arg.variable.value: expressionEvaluator(doAssert=True)(arg.optionalValue, environment).value for arg in node.children if type(arg.optionalValue) != NoneNode }
|
||||||
|
|
||||||
|
return defaultValues
|
||||||
|
|
||||||
|
|
||||||
|
def argumentsNodeToMethodSignature(node, environment):
|
||||||
try:
|
try:
|
||||||
sign = []
|
sign = []
|
||||||
vararg = None
|
vararg = None
|
||||||
argumentsCount = len(node.children)
|
argumentsCount = len(node.children)
|
||||||
|
checkPositionOfOptionalArguments(node)
|
||||||
|
defaultArgs = {}
|
||||||
for i, child in enumerate(node.children):
|
for i, child in enumerate(node.children):
|
||||||
matchers = {
|
matchers = {
|
||||||
ast.Type: (lambda c: c.type, typeMatcher),
|
ast.Type: (lambda c: c.type, typeMatcher),
|
||||||
@@ -22,19 +31,34 @@ def argumentsNodeToMethodSignature(node):
|
|||||||
TypesList: (lambda c: c, multipleTypeMatcher)
|
TypesList: (lambda c: c, multipleTypeMatcher)
|
||||||
}
|
}
|
||||||
evaluatedMatcher = matchers[type(child.type)][1](matchers[type(child.type)][0](child))
|
evaluatedMatcher = matchers[type(child.type)][1](matchers[type(child.type)][0](child))
|
||||||
|
|
||||||
if child.vararg:
|
if child.vararg:
|
||||||
if i != argumentsCount - 1:
|
if i != argumentsCount - 1:
|
||||||
raise RuntimeException("Vararg must be the last argument in signature", child.pos)
|
raise RuntimeException("Vararg must be the last argument in signature", child.pos)
|
||||||
vararg = evaluatedMatcher
|
vararg = evaluatedMatcher
|
||||||
else:
|
else:
|
||||||
|
if type(child.optionalValue) != NoneNode:
|
||||||
|
defaultArgs[child.variable.value] = expressionEvaluatorWithMatcher(
|
||||||
|
evaluatedMatcher,
|
||||||
|
exceptionProvider=lambda value: RuntimeException(
|
||||||
|
f"Value '{value.stringify()}' doesn't match declared type: {evaluatedMatcher.string}", child.optionalValue.pos)
|
||||||
|
)(child.optionalValue, environment)
|
||||||
|
evaluatedMatcher = optional(evaluatedMatcher)
|
||||||
sign.append(evaluatedMatcher)
|
sign.append(evaluatedMatcher)
|
||||||
|
|
||||||
|
return defaultArgs, (varargSignature(vararg, *sign, wrapVarargInValue=True) if vararg is not None else signature(*sign))
|
||||||
return varargSignature(vararg, *sign, wrapVarargInValue=True) if vararg is not None else signature(*sign)
|
|
||||||
except RuntimeException as e:
|
except RuntimeException as e:
|
||||||
raise updatePos(e, node)
|
raise updatePos(e, node)
|
||||||
|
|
||||||
|
|
||||||
|
def checkPositionOfOptionalArguments(node):
|
||||||
|
firstOptional = next((i for i, v in enumerate(node.children) if type(v.optionalValue) != NoneNode), None) #next(filter(lambda arg: type(arg.optionalValue) != NoneNode, node.children), None)
|
||||||
|
if firstOptional is not None:
|
||||||
|
regularAfterOptional = next((i for i, v in enumerate(node.children[firstOptional:]) if type(v.optionalValue) == NoneNode), None)
|
||||||
|
if regularAfterOptional is not None:
|
||||||
|
raise RuntimeException(f"Optional arguments should be declared at the end of the arguments list", node.children[regularAfterOptional].pos)
|
||||||
|
|
||||||
|
|
||||||
def multipleTypeMatcher(typeNode):
|
def multipleTypeMatcher(typeNode):
|
||||||
subSignature = []
|
subSignature = []
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ tokenizers = (
|
|||||||
defaultTokenizer(TokenType.CLOSE_SQUARE),
|
defaultTokenizer(TokenType.CLOSE_SQUARE),
|
||||||
defaultTokenizer(TokenType.OPEN_ANGLE),
|
defaultTokenizer(TokenType.OPEN_ANGLE),
|
||||||
defaultTokenizer(TokenType.CLOSE_ANGLE),
|
defaultTokenizer(TokenType.CLOSE_ANGLE),
|
||||||
|
defaultTokenizer(TokenType.SEMICOLON),
|
||||||
defaultTokenizer(TokenType.ASTERISK),
|
defaultTokenizer(TokenType.ASTERISK),
|
||||||
defaultTokenizer(TokenType.ASSIGN),
|
defaultTokenizer(TokenType.ASSIGN),
|
||||||
defaultTokenizer(TokenType.COMMA),
|
defaultTokenizer(TokenType.COMMA),
|
||||||
@@ -50,6 +51,7 @@ tokenizers = (
|
|||||||
separated(defaultTokenizer(TokenType.RETURN)),
|
separated(defaultTokenizer(TokenType.RETURN)),
|
||||||
separated(defaultTokenizer(TokenType.EXTEND)),
|
separated(defaultTokenizer(TokenType.EXTEND)),
|
||||||
separated(defaultTokenizer(TokenType.IMPORT)),
|
separated(defaultTokenizer(TokenType.IMPORT)),
|
||||||
|
separated(defaultTokenizer(TokenType.THROW)),
|
||||||
separated(defaultTokenizer(TokenType.FROM)),
|
separated(defaultTokenizer(TokenType.FROM)),
|
||||||
separated(defaultTokenizer(TokenType.WITH)),
|
separated(defaultTokenizer(TokenType.WITH)),
|
||||||
separated(defaultTokenizer(TokenType.ELSE)),
|
separated(defaultTokenizer(TokenType.ELSE)),
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class TokenType(Enum):
|
|||||||
CLOSE_SQUARE = ']'
|
CLOSE_SQUARE = ']'
|
||||||
OPEN_ANGLE = '<'
|
OPEN_ANGLE = '<'
|
||||||
CLOSE_ANGLE = '>'
|
CLOSE_ANGLE = '>'
|
||||||
|
SEMICOLON = ';'
|
||||||
ASTERISK = '*'
|
ASTERISK = '*'
|
||||||
ASSIGN = '='
|
ASSIGN = '='
|
||||||
ARROW = '->'
|
ARROW = '->'
|
||||||
@@ -35,6 +36,7 @@ class TokenType(Enum):
|
|||||||
RETURN = 'return'
|
RETURN = 'return'
|
||||||
EXTEND = 'extend'
|
EXTEND = 'extend'
|
||||||
IMPORT = 'import'
|
IMPORT = 'import'
|
||||||
|
THROW = 'throw'
|
||||||
FROM = 'from'
|
FROM = 'from'
|
||||||
WITH = 'with'
|
WITH = 'with'
|
||||||
ELSE = 'else'
|
ELSE = 'else'
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class Type(Enum):
|
|||||||
"pitch": Type.string(str(value.note)),
|
"pitch": Type.string(str(value.note)),
|
||||||
"octave": Type.integer(value.octave),
|
"octave": Type.integer(value.octave),
|
||||||
"duration": Type.integer(value.duration),
|
"duration": Type.integer(value.duration),
|
||||||
"dot": Type.string('.' if value.dot else '')
|
"dot": Type.bool(value.dot)
|
||||||
})
|
})
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ class Matcher:
|
|||||||
self.type = objectType
|
self.type = objectType
|
||||||
self.matcher = matcher
|
self.matcher = matcher
|
||||||
self.string = string
|
self.string = string
|
||||||
|
self.optional = False
|
||||||
|
|
||||||
def match(self, value):
|
def match(self, value):
|
||||||
if self.type is not None and self.type != value.type:
|
if self.type is not None and self.type != value.type:
|
||||||
|
|||||||
Reference in New Issue
Block a user