Enable varargs in signatures

This commit is contained in:
Bartłomiej Pluta
2019-07-10 00:32:42 +02:00
parent 99647df061
commit 39eaa2b4d5
5 changed files with 63 additions and 21 deletions

View File

@@ -20,7 +20,7 @@ class ArgumentsDeclarationNode(Node):
class ArgumentDefinitionNode(ExpressionNode):
def __init__(self, pos):
super().__init__(pos)
self.children.append(NoneNode())
self.children.extend([NoneNode(), False])
@property
def type(self):
@@ -38,8 +38,39 @@ class ArgumentDefinitionNode(ExpressionNode):
def variable(self, value):
self[1] = value
@property
def vararg(self):
return self[2]
@vararg.setter
def vararg(self, value):
self[2] = value
@classmethod
def parser(cls):
return Parser.oneOf(
cls._varargParser(),
cls._normalParser()
)
@classmethod
def _varargParser(cls):
def createNode(type, variable, dots):
node = ArgumentDefinitionNode(type.pos)
node.type = type
node.variable = variable
node.vararg = True
return node
return Parser.allOf(
TypeNode.parse,
Parser.doAssert(IdentifierNode.identifierParser(), "variable name"),
Parser.terminalParser(TokenType.DOTS),
createNode=createNode
)
@classmethod
def _normalParser(cls):
def createNode(type, variable):
node = ArgumentDefinitionNode(type.pos)
node.type = type

View File

@@ -1,3 +0,0 @@

View File

@@ -37,7 +37,7 @@ class Environment():
if method.typeSignature.check([object])[0] and method.name == name: #Todo sprawdzic sygnature typu
signatureCheckresult = method.signature.check(args)
if signatureCheckresult[0]:
self.scopes.append({argName: argValue for argName, argValue in zip(method.arguments, args)})
self.scopes.append({argName: argValue for argName, argValue in zip(method.arguments, list(signatureCheckresult[1:]))})
self.scopes[-1][method.alias] = object
result = BodyEvaluator.evaluate(method.body, self).value # TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
self.scopes.pop(-1)
@@ -71,7 +71,7 @@ class Environment():
if function.name == name:
signatureCheckresult = function.signature.check(args)
if signatureCheckresult[0]:
self.scopes.append({ argName: argValue for argName, argValue in zip(function.arguments, args) })
self.scopes.append({ argName: argValue for argName, argValue in zip(function.arguments, list(signatureCheckresult[1:])) })
result = BodyEvaluator.evaluate(function.body, self).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
self.scopes.pop(-1)
return (True, result)

View File

@@ -1,5 +1,6 @@
# from smnp.type.model import Type
#
from smnp.type.model import Type
class Signature:
@@ -9,7 +10,7 @@ class Signature:
self.matchers = matchers
def varargSignature(varargMatcher, *basicSignature):
def varargSignature(varargMatcher, *basicSignature, wrapVarargInValue=False):
def check(args):
if len(basicSignature) > len(args):
return doesNotMatchVararg(basicSignature)
@@ -22,7 +23,10 @@ def varargSignature(varargMatcher, *basicSignature):
if not varargMatcher.match(args[i]):
return doesNotMatchVararg(basicSignature)
return True, (*args[:len(basicSignature)]), args[len(basicSignature):]
if wrapVarargInValue:
return True, (*args[:len(basicSignature)]), Type.list(args[len(basicSignature):])
else:
return True, (*args[:len(basicSignature)]), args[len(basicSignature):]
string = f"({', '.join([str(m) for m in basicSignature])}{', ' if len(basicSignature) > 0 else ''}{str(varargMatcher)}...)"
@@ -32,8 +36,6 @@ def varargSignature(varargMatcher, *basicSignature):
def doesNotMatchVararg(basicSignature):
return (False, *[None for n in basicSignature], None)
def signature(*signature):
def check(args):
if len(signature) != len(args):

View File

@@ -2,7 +2,7 @@ from smnp.ast.node.function import ArgumentDefinitionNode
from smnp.ast.node.none import NoneNode
from smnp.ast.node.ret import ReturnNode
from smnp.error.runtime import RuntimeException
from smnp.function.signature import signature
from smnp.function.signature import signature, varargSignature
from smnp.runtime.evaluator import Evaluator, evaluate
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.runtime.evaluators.iterable import abstractIterableEvaluator
@@ -43,23 +43,35 @@ class FunctionDefinitionEvaluator(Evaluator):
def argumentsNodeToMethodSignature(node):
try:
sign = []
for child in node.children:
vararg = None
argumentsCount = len(node.children)
for i, child in enumerate(node.children):
if type(child) == ArgumentDefinitionNode:
if type(child.type.specifiers) == NoneNode:
sign.append(ofType(child.type.type))
elif child.type.type == Type.LIST and len(child.type.specifiers) == 1:
sign.append(listSpecifier(child.type.specifiers[0]))
elif child.type.type == Type.MAP and len(child.type.specifiers) == 2:
sign.append(mapSpecifier(child.type.specifiers[0], child.type.specifiers[1]))
if child.vararg:
if i != argumentsCount-1:
raise RuntimeException("Vararg must be the last argument in signature", child.pos)
vararg = typeMatcher(child)
else:
raise RuntimeException("Unknown type", child.pos) # Todo: Improve pointing position
sign.append(typeMatcher(child))
return signature(*sign)
return varargSignature(vararg, *sign, wrapVarargInValue=True) if vararg is not None else signature(*sign)
except RuntimeException as e:
raise updatePos(e, node)
def typeMatcher(typeNode):
if type(typeNode.type.specifiers) == NoneNode:
return ofType(typeNode.type.type)
elif typeNode.type.type == Type.LIST and len(typeNode.type.specifiers) == 1:
return listSpecifier(typeNode.type.specifiers[0])
elif typeNode.type.type == Type.MAP and len(typeNode.type.specifiers) == 2:
return mapSpecifier(typeNode.type.specifiers[0], typeNode.type.specifiers[1])
raise RuntimeException("Unknown type", typeNode.pos) # Todo: Improve pointing position
def listSpecifier(specifier):
subSignature = []