From de35d2d26a58b9979dcd274f2c1969e0504bc2a9 Mon Sep 17 00:00:00 2001 From: Daniel Weipert Date: Sun, 22 Sep 2024 23:13:24 +0200 Subject: nested namespace access @.@ --- henshin | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++------ test/test.test | 7 ++-- 2 files changed, 117 insertions(+), 16 deletions(-) diff --git a/henshin b/henshin index da03c62..44ba911 100755 --- a/henshin +++ b/henshin @@ -7,6 +7,7 @@ import logging import ply.lex as lex import ply.yacc as yacc from enum import Enum +import collections.abc @@ -674,6 +675,7 @@ result = parser.parse(input) variables = { "print": print, } +types = {} def is_statement(statement): @@ -705,6 +707,51 @@ def to_parameters_dict(parameters, context): return dictionary +def merge_dict(dict1, dict2): + for key, val in dict1.items(): + if type(val) == dict: + if key in dict2 and type(dict2[key] == dict): + merge_dict(dict1[key], dict2[key]) + else: + if key in dict2: + dict1[key] = dict2[key] + + for key, val in dict2.items(): + if not key in dict1: + dict1[key] = val + + return dict1 + +def nested_set(dic, keys, value): + for key in keys[:-1]: + dic = dic.setdefault(key, {}) + dic[keys[-1]] = value + +def update(d, u): + for k, v in u.items(): + if isinstance(v, collections.abc.Mapping): + d[k] = update(d.get(k, {}), v) + else: + d[k] = v + return d + + +def get_namespace_path(namespace, context): + path = [] + + left = evaluate_expression(namespace.left, context | {"NAMESPACE_ACCESS": True}) + right = evaluate_expression(namespace.right, context | {"NAMESPACE_ACCESS": True}) + + if type(left) is list: + path.extend(left) + else: + path.append(left) + + path.append(right) + + return path + + def evaluate_statement(statement, context): scoped_variables = {} @@ -712,22 +759,36 @@ def evaluate_statement(statement, context): if type(statement) is AstNodeVariableDeclarationStatement: value = evaluate_expression(statement.value, context) scoped_variables[statement.name] = value + types[statement.name] = statement.variable_type - # variavle reassignment + # variable reassignment elif type(statement) is AstNodeVariableReassignmentStatement: value = evaluate_expression(statement.value, context) - if not statement.name in variables: - print("ERROR: Reassignment on non-existent variable: {variable}".format(variable=statement.name)) + name = statement.name + + if type(name) is AstNodeNamespaceAccess: + namespace_path = get_namespace_path(name, context) + + nested_update = {} + nested_set(nested_update, namespace_path, value) + scoped_variables = update(context, nested_update) else: - scoped_variables[statement.name] = value + if not name in context: + print("ERROR: Reassignment on non-existent variable: {variable}".format(variable=statement.name)) + else: + scoped_variables[name] = value # function call elif type(statement) is AstNodeFunctionCall: - function = context[statement.name] + if type(statement.name) is AstNodeNamespaceAccess: + namespace = evaluate_expression(statement.name, context) + function = namespace["function"] + context["self"] = namespace["self"] + else: + function = context[statement.name] # in-built if callable(function): - pass function(*[evaluate_expression(parameter, context) for parameter in statement.parameters]) # else @@ -760,6 +821,19 @@ def evaluate_statement(statement, context): scoped_variables = right + # namespace access + elif type(statement) is AstNodeNamespaceAccess: + left = evaluate_expression(statement.left, context) + right = evaluate_expression(statement.right, left) + + self = list(context.keys())[list(context.values()).index(left)] + + if type(right) is AstNodeFunctionDeclaration: + scoped_variables = {"self": self, "function": right} + else: + scoped_variables = right + + return scoped_variables @@ -786,7 +860,12 @@ def evaluate_expression(expression, scoped_variables): # general expression elif type(expression) is AstNodeExpression: if expression.type is AstNodeExpressionType.IDENTIFIER: - result = scoped_variables[expression.value] + if expression.value == "self": + result = scoped_variables[scoped_variables["self"]] + elif "NAMESPACE_ACCESS" in scoped_variables: + result = expression.value + else: + result = scoped_variables[expression.value] elif expression.type is AstNodeExpressionType.ARRAY: result = [evaluate_expression(element.value, scoped_variables) for element in expression.value] elif expression.type is AstNodeExpressionType.MAP: @@ -798,26 +877,47 @@ def evaluate_expression(expression, scoped_variables): # function call / type instantiation elif type(expression) is AstNodeFunctionCall: - function = scoped_variables[expression.name] + if type(expression.name) is AstNodeNamespaceAccess: + namespace = evaluate_expression(expression.name, scoped_variables) + function = namespace["function"] + scoped_variables["self"] = namespace["self"] + else: + function = scoped_variables[expression.name] # function call if type(function) is AstNodeFunctionDeclaration: result = evaluate_statement(expression, scoped_variables) # type instantiation - elif type(function) is AstNodeTypeDeclaration: - instance_variables = {} - for node in function.body: - instance_variables |= evaluate_statement(node, scoped_variables) - + elif type(function) is dict: + instance_variables = evaluate_expression(function, scoped_variables) result = instance_variables + variable_type = types[expression.name] + if type(variable_type) is AstNodeVariableTypeType: + type_type = evaluate_expression(variable_type.base_type, scoped_variables) + result |= type_type + # function call parameter elif type(expression) is AstNodeFunctionCallParameter: result = evaluate_expression(expression.value, scoped_variables) if result == "$": result = scoped_variables["$"] + elif type(expression) is AstNodeNamespaceAccess: + if "NAMESPACE_ACCESS" in scoped_variables: + result = get_namespace_path(expression, scoped_variables) + else: + result = evaluate_statement(expression, scoped_variables) + + elif type(expression) is AstNodeTypeDeclaration: + instance_variables = {} + for node in expression.body: + instance_variables |= evaluate_statement(node, scoped_variables) + + result = instance_variables + + return result diff --git a/test/test.test b/test/test.test index 149f1a6..58be29b 100644 --- a/test/test.test +++ b/test/test.test @@ -55,6 +55,7 @@ const inherited_type: type[test_type] = { } const object: inherited_type = inherited_type() -//object.test_field = "hey" -//object.test_function() => print($) -//object.nested.another_function(add = 2) +object.test_field = "hey" +object.test_function() => print($) +object.nested.another_field = 5 +object.nested.another_function(add = 2) => print($) -- cgit v1.2.3