Extend type specifiers to cover maps

This commit is contained in:
Bartłomiej Pluta
2019-07-09 01:32:09 +02:00
parent d23e7a1276
commit 7e7b5ec461
10 changed files with 116 additions and 20 deletions

View File

@@ -41,7 +41,7 @@ PITCH_MODIFIER = 'b' | '#'
<typeSpecifier> ::= '<' '>' | '<' <typeSpecifierItem> <typeSpecifierTail>
<typeSpecifierTail> ::= <typeSpecifierItem> ', ' <typeSpecifierTail> | '>'
<typeSpecifierItem> ::= <type> | <type> <typeSpecifier>
<typeSpecifierItem> ::= <type> | <type> <typeSpecifier> [ '&' <typeSpecifier ]*
<block> ::= '{' <stmt>* '}'

View File

@@ -59,6 +59,12 @@ class Node:
def __str__(self):
return self.__class__.__name__
@classmethod
def withChildren(cls, children, pos):
node = cls(pos)
node.children = children
return node
class ParseResult():
def __init__(self, result, node):

View File

@@ -20,6 +20,9 @@ class TypeSpecifier(Node):
cls.parse
)
class TypeSpecifiers(Node):
pass
class TypeNode(AccessNode):
def __init__(self, pos):
@@ -34,24 +37,24 @@ class TypeNode(AccessNode):
self[0] = value
@property
def specifier(self):
def specifiers(self):
return self[1]
@specifier.setter
def specifier(self, value):
@specifiers.setter
def specifiers(self, value):
self[1] = value
@classmethod
def _parse(cls, input):
def createNode(type, specifier):
def createNode(type, specifiers):
node = TypeNode(type.pos)
node.type = Type[type.value.upper()]
node.specifier = specifier
node.specifiers = specifiers
return node
return Parser.allOf(
cls._rawTypeParser(),
Parser.optional(TypeSpecifier.parse),
Parser.many(TypeSpecifier.parse, lambda specifiers, pos: TypeSpecifiers.withChildren(specifiers, pos)),
createNode=createNode
)(input)

View File

@@ -151,3 +151,20 @@ class Parser:
return ParseResult.OK(NoneNode())
return parse
@staticmethod
def many(parser, createNode):
def parse(input):
results = []
snap = input.snapshot()
pos = input.currentPos()
while True:
result = parser(input)
if result.result:
results.append(result.node)
snap = input.snapshot()
else:
input.reset(snap)
return ParseResult.OK(createNode(results, pos) if len(results) > 0 else NoneNode())
return parse

View File

@@ -53,6 +53,8 @@ def types(args, parentheses=True):
for arg in args:
if arg.type == Type.LIST:
output.append(listTypes(arg.value, []))
if arg.type == Type.MAP:
output.append(mapTypes(arg.value, {}))
else:
output.append(arg.type.name.lower())
return f"({', '.join(output)})" if parentheses else ', '.join(output)
@@ -64,6 +66,22 @@ def listTypes(l, output=None):
for item in l:
if item.type == Type.LIST:
output.append(listTypes(item.value, []))
if item.type == Type.MAP:
output.append(mapTypes(item.value, {}))
else:
output.append(item.type.name.lower())
return f"{Type.LIST.name.lower()}<{', '.join(set(output))}>"
def mapTypes(map, output=None):
if output is None:
output = {}
for k, v in map.items():
if v.type == Type.LIST:
output[k] = (listTypes(v.value, []))
elif v.type == Type.MAP:
output[k] = mapTypes(v.value, {})
else:
output[k] = v.type.name.lower()
return f"{Type.MAP.name.lower()}<{', '.join(set([ k.type.name.lower() for k, v in output.items() ]))}><{', '.join(set([ str(v) for k, v in output.items() ]))}>"

View File

@@ -120,6 +120,18 @@ def listOfMatchers(*matchers):
return Matcher(Type.LIST, check, f"{Type.LIST.name.lower()}<{', '.join([m.string for m in matchers])}>")
def mapOfMatchers(keyMatchers, valueMatchers):
def check(map):
matched = 0
for key, value in map.value.items():
matched += 1 if any(matcher.match(key) for matcher in keyMatchers) \
and any(matcher.match(value) for matcher in valueMatchers) else 0
return matched == len(map.value)
return Matcher(Type.MAP, check, f"{Type.MAP.name.lower()}<{', '.join([m.string for m in keyMatchers])}><{', '.join([m.string for m in valueMatchers])}>")
def listMatches(*pattern):
def check(value):
return signature(*pattern).check(value.value)[0]

View File

@@ -6,6 +6,7 @@ from smnp.program.interpreter import Interpreter
def main():
try:
Interpreter.interpretFile(sys.argv[1], printAst=True)
except SmnpException as e:

View File

@@ -2,7 +2,7 @@ from smnp.ast.node.none import NoneNode
from smnp.ast.node.ret import ReturnNode
from smnp.ast.node.variable import TypedVariableNode
from smnp.error.runtime import RuntimeException
from smnp.library.signature import signature, listOfMatchers, ofType
from smnp.library.signature import signature, listOfMatchers, ofType, mapOfMatchers
from smnp.runtime.evaluator import Evaluator, evaluate
from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.runtime.evaluators.iterable import abstractIterableEvaluator
@@ -38,29 +38,66 @@ class FunctionDefinitionEvaluator(Evaluator):
def argumentsNodeToMethodSignature(node):
sign = []
try:
sign = []
for child in node.children:
if type(child) == TypedVariableNode:
if type(child.type.specifier) == NoneNode:
sign.append(ofType(child.type.type))
elif child.type.type == Type.LIST:
sign.append(listSpecifier(child.type.specifier))
for child in node.children:
if type(child) == TypedVariableNode:
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]))
else:
raise RuntimeException("Unknown type", child.pos) # Todo: Improve pointing position
return signature(*sign)
return signature(*sign)
except RuntimeException as e:
raise updatePos(e, node)
def listSpecifier(specifier):
subSignature = []
for child in specifier.children:
if type(child.specifier) == NoneNode:
if type(child.specifiers) == NoneNode:
subSignature.append(ofType(child.type))
elif child.type == Type.LIST:
subSignature.append(listSpecifier(child.specifier))
elif child.type == Type.LIST and len(child.type.specifiers) == 1:
subSignature.append(listSpecifier(child.specifiers[0]))
elif child.type == Type.MAP and len(child.specifiers) == 2:
subSignature.append(mapSpecifier(child.specifiers[0], child.specifiers[1]))
else:
raise RuntimeException("Unknown type", None)
return listOfMatchers(*subSignature)
def mapSpecifier(keySpecifier, valueSpecifier):
keySubSignature = []
valueSubSignature = []
for child in keySpecifier.children:
if type(child.specifiers) == NoneNode:
keySubSignature.append(ofType(child.type))
elif child.type == Type.LIST and len(child.specifiers) == 1:
keySubSignature.append(listSpecifier(child.specifiers[0]))
elif child.type == Type.MAP and len(child.specifiers) == 2:
keySubSignature.append(mapSpecifier(child.specifiers[0], child.specifiers[1]))
else:
raise RuntimeException("Unknown type", None)
for child in valueSpecifier.children:
if type(child.specifiers) == NoneNode:
valueSubSignature.append(ofType(child.type))
elif child.type == Type.LIST and len(child.specifiers) == 1:
valueSubSignature.append(listSpecifier(child.specifiers[0]))
elif child.type == Type.MAP and len(child.specifiers) == 2:
valueSubSignature.append(mapSpecifier(child.specifiers[0], child.specifiers[1]))
else:
raise RuntimeException("Unknown type", None)
return mapOfMatchers(keySubSignature, valueSubSignature)
class BodyEvaluator(Evaluator):

View File

@@ -24,6 +24,7 @@ tokenizers = (
defaultTokenizer(TokenType.ARROW),
defaultTokenizer(TokenType.COMMA),
defaultTokenizer(TokenType.MINUS),
defaultTokenizer(TokenType.AMP),
defaultTokenizer(TokenType.DOT),
# Types

View File

@@ -15,6 +15,7 @@ class TokenType(Enum):
ARROW = '->'
COMMA = ','
MINUS = '-'
AMP = '&'
DOT = '.'
INTEGER = 'integer'
STRING = 'string'