Merge branch 'optional-function-args'
This commit is contained in:
@@ -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):
|
||||||
@@ -13,11 +14,24 @@ def StatementParser(input):
|
|||||||
from smnp.ast.node.ret import ReturnParser
|
from smnp.ast.node.ret import ReturnParser
|
||||||
from smnp.ast.node.throw import ThrowParser
|
from smnp.ast.node.throw import ThrowParser
|
||||||
|
|
||||||
return Parser.oneOf(
|
return withSemicolon(
|
||||||
IfElseStatementParser,
|
Parser.oneOf(
|
||||||
ExpressionParser,
|
IfElseStatementParser,
|
||||||
BlockParser,
|
ExpressionParser, # Must be above BlockParser because of Map's syntax with curly braces
|
||||||
ReturnParser,
|
BlockParser,
|
||||||
ThrowParser,
|
ReturnParser,
|
||||||
name="statement"
|
ThrowParser,
|
||||||
)(input)
|
name="statement"
|
||||||
|
), 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 ""
|
||||||
|
)
|
||||||
|
|||||||
@@ -40,7 +40,8 @@ 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 = Type.void()
|
result = Type.void()
|
||||||
@@ -80,7 +81,8 @@ 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 = Type.void()
|
result = Type.void()
|
||||||
try:
|
try:
|
||||||
@@ -93,11 +95,11 @@ class Environment():
|
|||||||
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:
|
||||||
@@ -108,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):
|
||||||
@@ -175,18 +177,20 @@ class CallStackItem:
|
|||||||
|
|
||||||
|
|
||||||
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
|
||||||
@@ -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])
|
||||||
|
|
||||||
|
|||||||
@@ -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.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,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, And, Or
|
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
|
||||||
@@ -50,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)
|
||||||
|
|
||||||
|
|||||||
@@ -25,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)
|
||||||
|
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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 = '->'
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -25,4 +25,4 @@ def oneOf(*matchers):
|
|||||||
def check(value):
|
def check(value):
|
||||||
return any(matcher.match(value) for matcher in matchers)
|
return any(matcher.match(value) for matcher in matchers)
|
||||||
|
|
||||||
return Matcher(None, check, f"<{', '.join(m.string for m in matchers)}>")
|
return Matcher(None, check, f"<{', '.join(m.string for m in matchers)}>")
|
||||||
|
|||||||
Reference in New Issue
Block a user