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.parser import Parser
|
||||
from smnp.token.type import TokenType
|
||||
@@ -20,7 +22,34 @@ class Or(BinaryOperator):
|
||||
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(
|
||||
TermParser,
|
||||
[TokenType.PLUS, TokenType.MINUS],
|
||||
@@ -49,3 +78,34 @@ def ExpressionParser(input):
|
||||
lambda left, op, right: Or.withValues(left, op, right)
|
||||
)(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.unit import UnitParser
|
||||
from smnp.ast.parser import Parser
|
||||
@@ -13,38 +10,8 @@ class NotOperator(UnaryOperator):
|
||||
class Power(BinaryOperator):
|
||||
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):
|
||||
from smnp.ast.node.expression import ExpressionParser
|
||||
from smnp.ast.node.statement import StatementParser
|
||||
from smnp.ast.node.identifier import IdentifierLiteralParser
|
||||
|
||||
parentheses = Parser.allOf(
|
||||
Parser.terminal(TokenType.OPEN_PAREN),
|
||||
@@ -75,27 +42,7 @@ def FactorParser(input):
|
||||
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(
|
||||
loopFactor,
|
||||
notOperator,
|
||||
powerFactor,
|
||||
name="factor"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
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.iterable import abstractIterableParser
|
||||
from smnp.ast.node.model import Node
|
||||
@@ -16,7 +17,7 @@ class Argument(Node):
|
||||
|
||||
def __init__(self, pos):
|
||||
super().__init__(pos)
|
||||
self.children = [NoneNode(), NoneNode(), False]
|
||||
self.children = [NoneNode(), NoneNode(), False, NoneNode()]
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
@@ -48,6 +49,14 @@ class Argument(Node):
|
||||
self[2] = value
|
||||
|
||||
|
||||
@property
|
||||
def optionalValue(self):
|
||||
return self[3]
|
||||
|
||||
@optionalValue.setter
|
||||
def optionalValue(self, value):
|
||||
self[3] = value
|
||||
|
||||
class VarargNode(Node):
|
||||
pass
|
||||
|
||||
@@ -90,7 +99,7 @@ class FunctionDefinition(Node):
|
||||
return node
|
||||
|
||||
|
||||
def ArgumentParser(input):
|
||||
def RegularArgumentParser(input):
|
||||
def createNode(type, variable, vararg):
|
||||
pos = type.pos if isinstance(type, Type) else variable.pos
|
||||
node = Argument(pos)
|
||||
@@ -104,6 +113,33 @@ def ArgumentParser(input):
|
||||
Parser.doAssert(IdentifierLiteralParser, "argument name"),
|
||||
Parser.optional(Parser.terminal(TokenType.DOTS, lambda val, pos: True)),
|
||||
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"
|
||||
)(input)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from smnp.ast.node.model import Node
|
||||
from smnp.ast.parser import Parser
|
||||
from smnp.token.type import TokenType
|
||||
|
||||
|
||||
class Statement(Node):
|
||||
@@ -13,11 +14,24 @@ def StatementParser(input):
|
||||
from smnp.ast.node.ret import ReturnParser
|
||||
from smnp.ast.node.throw import ThrowParser
|
||||
|
||||
return Parser.oneOf(
|
||||
IfElseStatementParser,
|
||||
ExpressionParser,
|
||||
BlockParser,
|
||||
ReturnParser,
|
||||
ThrowParser,
|
||||
name="statement"
|
||||
)(input)
|
||||
return withSemicolon(
|
||||
Parser.oneOf(
|
||||
IfElseStatementParser,
|
||||
ExpressionParser, # Must be above BlockParser because of Map's syntax with curly braces
|
||||
BlockParser,
|
||||
ReturnParser,
|
||||
ThrowParser,
|
||||
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
|
||||
signatureCheckresult = method.signature.check(args)
|
||||
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.callStack.append(CallStackItem(name))
|
||||
result = Type.void()
|
||||
@@ -80,7 +81,8 @@ 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, 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))
|
||||
result = Type.void()
|
||||
try:
|
||||
@@ -93,11 +95,11 @@ class Environment():
|
||||
raise IllegalFunctionInvocationException(f"{function.name}{function.signature.string}", f"{name}{argsTypesToString(args)}")
|
||||
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:
|
||||
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:
|
||||
# There is still problem with checking existing of generic types, like lists:
|
||||
@@ -108,14 +110,14 @@ class Environment():
|
||||
# function foo() { return 2 }
|
||||
# }
|
||||
# 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:
|
||||
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:
|
||||
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):
|
||||
for scope in reversed(self.scopes):
|
||||
@@ -175,18 +177,20 @@ class CallStackItem:
|
||||
|
||||
|
||||
class CustomFunction:
|
||||
def __init__(self, name, signature, arguments, body):
|
||||
def __init__(self, name, signature, arguments, body, defaultArgs):
|
||||
self.name = name
|
||||
self.signature = signature
|
||||
self.arguments = arguments
|
||||
self.body = body
|
||||
self.defaultArgs = defaultArgs
|
||||
|
||||
|
||||
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.alias = alias
|
||||
self.name = name
|
||||
self.signature = signature
|
||||
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 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):
|
||||
return doesNotMatchVararg(basicSignature)
|
||||
|
||||
@@ -38,7 +41,7 @@ def doesNotMatchVararg(basicSignature):
|
||||
|
||||
def signature(*signature):
|
||||
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)
|
||||
|
||||
for s, a in zip(signature, args):
|
||||
@@ -52,6 +55,12 @@ def signature(*signature):
|
||||
return Signature(check, string, signature)
|
||||
|
||||
|
||||
def optional(matcher):
|
||||
matcher.optional = True
|
||||
matcher.string += "?"
|
||||
return matcher
|
||||
|
||||
|
||||
def doesNotMatch(sign):
|
||||
return (False, *[None for n in sign])
|
||||
|
||||
|
||||
@@ -9,9 +9,6 @@ def main():
|
||||
try:
|
||||
stdLibraryEnv = loadStandardLibrary()
|
||||
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:
|
||||
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 ]
|
||||
@@ -1,6 +1,6 @@
|
||||
from smnp.ast.node.condition import IfElse
|
||||
from smnp.ast.node.expression import Sum, Relation, And, Or
|
||||
from smnp.ast.node.factor import NotOperator, Power, Loop
|
||||
from smnp.ast.node.expression import Sum, Relation, And, Or, Loop
|
||||
from smnp.ast.node.factor import NotOperator, Power
|
||||
from smnp.ast.node.identifier import FunctionCall, Assignment
|
||||
from smnp.ast.node.term import Product
|
||||
from smnp.ast.node.unit import MinusOperator, Access
|
||||
@@ -50,3 +50,15 @@ def expressionEvaluator(doAssert=False):
|
||||
|
||||
|
||||
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
|
||||
def _evaluateMethodDefinition(cls, node, environment, type, variable):
|
||||
name = node.name.value
|
||||
signature = argumentsNodeToMethodSignature(node.arguments)
|
||||
defaultArguments, signature = argumentsNodeToMethodSignature(node.arguments, environment)
|
||||
arguments = [arg.variable.value for arg in node.arguments]
|
||||
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):
|
||||
try:
|
||||
name = node.name.value
|
||||
signature = argumentsNodeToMethodSignature(node.arguments)
|
||||
defaultArguments, signature = argumentsNodeToMethodSignature(node.arguments, environment)
|
||||
arguments = [ arg.variable.value for arg in node.arguments ]
|
||||
body = node.body
|
||||
environment.addCustomFunction(name, signature, arguments, body)
|
||||
environment.addCustomFunction(name, signature, arguments, body, defaultArguments)
|
||||
except RuntimeException as e:
|
||||
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.type import TypesList
|
||||
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.type.model import Type
|
||||
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
|
||||
|
||||
|
||||
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:
|
||||
sign = []
|
||||
vararg = None
|
||||
argumentsCount = len(node.children)
|
||||
checkPositionOfOptionalArguments(node)
|
||||
defaultArgs = {}
|
||||
for i, child in enumerate(node.children):
|
||||
matchers = {
|
||||
ast.Type: (lambda c: c.type, typeMatcher),
|
||||
@@ -22,19 +31,34 @@ def argumentsNodeToMethodSignature(node):
|
||||
TypesList: (lambda c: c, multipleTypeMatcher)
|
||||
}
|
||||
evaluatedMatcher = matchers[type(child.type)][1](matchers[type(child.type)][0](child))
|
||||
|
||||
if child.vararg:
|
||||
if i != argumentsCount - 1:
|
||||
raise RuntimeException("Vararg must be the last argument in signature", child.pos)
|
||||
vararg = evaluatedMatcher
|
||||
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)
|
||||
|
||||
|
||||
return varargSignature(vararg, *sign, wrapVarargInValue=True) if vararg is not None else signature(*sign)
|
||||
return defaultArgs, (varargSignature(vararg, *sign, wrapVarargInValue=True) if vararg is not None else signature(*sign))
|
||||
except RuntimeException as e:
|
||||
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):
|
||||
subSignature = []
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ tokenizers = (
|
||||
defaultTokenizer(TokenType.CLOSE_SQUARE),
|
||||
defaultTokenizer(TokenType.OPEN_ANGLE),
|
||||
defaultTokenizer(TokenType.CLOSE_ANGLE),
|
||||
defaultTokenizer(TokenType.SEMICOLON),
|
||||
defaultTokenizer(TokenType.ASTERISK),
|
||||
defaultTokenizer(TokenType.ASSIGN),
|
||||
defaultTokenizer(TokenType.COMMA),
|
||||
|
||||
@@ -12,6 +12,7 @@ class TokenType(Enum):
|
||||
CLOSE_SQUARE = ']'
|
||||
OPEN_ANGLE = '<'
|
||||
CLOSE_ANGLE = '>'
|
||||
SEMICOLON = ';'
|
||||
ASTERISK = '*'
|
||||
ASSIGN = '='
|
||||
ARROW = '->'
|
||||
|
||||
@@ -3,6 +3,7 @@ class Matcher:
|
||||
self.type = objectType
|
||||
self.matcher = matcher
|
||||
self.string = string
|
||||
self.optional = False
|
||||
|
||||
def match(self, value):
|
||||
if self.type is not None and self.type != value.type:
|
||||
|
||||
@@ -25,4 +25,4 @@ def oneOf(*matchers):
|
||||
def check(value):
|
||||
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