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> <typeSpecifier> ::= '<' '>' | '<' <typeSpecifierItem> <typeSpecifierTail>
<typeSpecifierTail> ::= <typeSpecifierItem> ', ' <typeSpecifierTail> | '>' <typeSpecifierTail> ::= <typeSpecifierItem> ', ' <typeSpecifierTail> | '>'
<typeSpecifierItem> ::= <type> | <type> <typeSpecifier> <typeSpecifierItem> ::= <type> | <type> <typeSpecifier> [ '&' <typeSpecifier ]*
<block> ::= '{' <stmt>* '}' <block> ::= '{' <stmt>* '}'

View File

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

View File

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

View File

@@ -151,3 +151,20 @@ class Parser:
return ParseResult.OK(NoneNode()) return ParseResult.OK(NoneNode())
return parse 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: for arg in args:
if arg.type == Type.LIST: if arg.type == Type.LIST:
output.append(listTypes(arg.value, [])) output.append(listTypes(arg.value, []))
if arg.type == Type.MAP:
output.append(mapTypes(arg.value, {}))
else: else:
output.append(arg.type.name.lower()) output.append(arg.type.name.lower())
return f"({', '.join(output)})" if parentheses else ', '.join(output) return f"({', '.join(output)})" if parentheses else ', '.join(output)
@@ -64,6 +66,22 @@ def listTypes(l, output=None):
for item in l: for item in l:
if item.type == Type.LIST: if item.type == Type.LIST:
output.append(listTypes(item.value, [])) output.append(listTypes(item.value, []))
if item.type == Type.MAP:
output.append(mapTypes(item.value, {}))
else: else:
output.append(item.type.name.lower()) output.append(item.type.name.lower())
return f"{Type.LIST.name.lower()}<{', '.join(set(output))}>" 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])}>") 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 listMatches(*pattern):
def check(value): def check(value):
return signature(*pattern).check(value.value)[0] return signature(*pattern).check(value.value)[0]

View File

@@ -6,6 +6,7 @@ from smnp.program.interpreter import Interpreter
def main(): def main():
try: try:
Interpreter.interpretFile(sys.argv[1], printAst=True) Interpreter.interpretFile(sys.argv[1], printAst=True)
except SmnpException as e: 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.ret import ReturnNode
from smnp.ast.node.variable import TypedVariableNode from smnp.ast.node.variable import TypedVariableNode
from smnp.error.runtime import RuntimeException 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.evaluator import Evaluator, evaluate
from smnp.runtime.evaluators.expression import expressionEvaluator from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.runtime.evaluators.iterable import abstractIterableEvaluator from smnp.runtime.evaluators.iterable import abstractIterableEvaluator
@@ -38,29 +38,66 @@ class FunctionDefinitionEvaluator(Evaluator):
def argumentsNodeToMethodSignature(node): def argumentsNodeToMethodSignature(node):
sign = [] try:
sign = []
for child in node.children: for child in node.children:
if type(child) == TypedVariableNode: if type(child) == TypedVariableNode:
if type(child.type.specifier) == NoneNode: if type(child.type.specifiers) == NoneNode:
sign.append(ofType(child.type.type)) sign.append(ofType(child.type.type))
elif child.type.type == Type.LIST: elif child.type.type == Type.LIST and len(child.type.specifiers) == 1:
sign.append(listSpecifier(child.type.specifier)) 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): def listSpecifier(specifier):
subSignature = [] subSignature = []
for child in specifier.children: for child in specifier.children:
if type(child.specifier) == NoneNode: if type(child.specifiers) == NoneNode:
subSignature.append(ofType(child.type)) subSignature.append(ofType(child.type))
elif child.type == Type.LIST: elif child.type == Type.LIST and len(child.type.specifiers) == 1:
subSignature.append(listSpecifier(child.specifier)) 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) 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): class BodyEvaluator(Evaluator):

View File

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

View File

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