From 7e7b5ec4618ab8eef7d367d31820c7d8cf8a866d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Pluta?= Date: Tue, 9 Jul 2019 01:32:09 +0200 Subject: [PATCH] Extend type specifiers to cover maps --- grammar | 2 +- smnp/ast/node/model.py | 6 +++ smnp/ast/node/type.py | 15 ++++--- smnp/ast/parser.py | 17 ++++++++ smnp/library/model.py | 20 +++++++++- smnp/library/signature.py | 12 ++++++ smnp/main.py | 1 + smnp/runtime/evaluators/function.py | 61 +++++++++++++++++++++++------ smnp/token/tokenizer.py | 1 + smnp/token/type.py | 1 + 10 files changed, 116 insertions(+), 20 deletions(-) diff --git a/grammar b/grammar index 8333f38..98a4a00 100644 --- a/grammar +++ b/grammar @@ -41,7 +41,7 @@ PITCH_MODIFIER = 'b' | '#' ::= '<' '>' | '<' ::= ', ' | '>' - ::= | + ::= | [ '&' ::= '{' * '}' diff --git a/smnp/ast/node/model.py b/smnp/ast/node/model.py index 6a44748..0aa143a 100644 --- a/smnp/ast/node/model.py +++ b/smnp/ast/node/model.py @@ -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): diff --git a/smnp/ast/node/type.py b/smnp/ast/node/type.py index 77627d6..847f27b 100644 --- a/smnp/ast/node/type.py +++ b/smnp/ast/node/type.py @@ -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) diff --git a/smnp/ast/parser.py b/smnp/ast/parser.py index 746cc66..bad5af7 100644 --- a/smnp/ast/parser.py +++ b/smnp/ast/parser.py @@ -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 diff --git a/smnp/library/model.py b/smnp/library/model.py index 7ce6872..8546c69 100644 --- a/smnp/library/model.py +++ b/smnp/library/model.py @@ -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))}>" \ No newline at end of file + 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() ]))}>" \ No newline at end of file diff --git a/smnp/library/signature.py b/smnp/library/signature.py index 20de4af..1b562d1 100644 --- a/smnp/library/signature.py +++ b/smnp/library/signature.py @@ -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] diff --git a/smnp/main.py b/smnp/main.py index 6f41788..1ec7502 100644 --- a/smnp/main.py +++ b/smnp/main.py @@ -6,6 +6,7 @@ from smnp.program.interpreter import Interpreter def main(): try: + Interpreter.interpretFile(sys.argv[1], printAst=True) except SmnpException as e: diff --git a/smnp/runtime/evaluators/function.py b/smnp/runtime/evaluators/function.py index 539e50b..33fb577 100644 --- a/smnp/runtime/evaluators/function.py +++ b/smnp/runtime/evaluators/function.py @@ -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): diff --git a/smnp/token/tokenizer.py b/smnp/token/tokenizer.py index 13ba9b1..02df488 100644 --- a/smnp/token/tokenizer.py +++ b/smnp/token/tokenizer.py @@ -24,6 +24,7 @@ tokenizers = ( defaultTokenizer(TokenType.ARROW), defaultTokenizer(TokenType.COMMA), defaultTokenizer(TokenType.MINUS), + defaultTokenizer(TokenType.AMP), defaultTokenizer(TokenType.DOT), # Types diff --git a/smnp/token/type.py b/smnp/token/type.py index ebbea9a..7f4f87e 100644 --- a/smnp/token/type.py +++ b/smnp/token/type.py @@ -15,6 +15,7 @@ class TokenType(Enum): ARROW = '->' COMMA = ',' MINUS = '-' + AMP = '&' DOT = '.' INTEGER = 'integer' STRING = 'string'