From cdfc214a9f961ac9a9a6ed8c419bd2b4d7edc9bc Mon Sep 17 00:00:00 2001 From: Daniel Weipert Date: Sun, 22 Sep 2024 20:20:49 +0200 Subject: interpreting!! --- henshin | 308 +++++++++++++++++++++++++++++++++++++++++-------- test/hello-world.hnshn | 28 ----- test/test.test | 91 +++++++-------- 3 files changed, 306 insertions(+), 121 deletions(-) delete mode 100644 test/hello-world.hnshn diff --git a/henshin b/henshin index 7f5d755..da03c62 100755 --- a/henshin +++ b/henshin @@ -21,6 +21,13 @@ parser.add_argument('filename') parser.add_argument( '--lex', default=False, + action=argparse.BooleanOptionalAction, +) + +parser.add_argument( + '--debug', + default=False, + action=argparse.BooleanOptionalAction, ) args = parser.parse_args() @@ -148,6 +155,8 @@ def t_NUMBER(t): def t_STRING(t): r'(".*?")' + t.value = t.value.replace('"', '') + return t def t_COMMENT(t): @@ -171,7 +180,7 @@ def t_error(t): lexer = lex.lex(debug=True, debuglog=log) -if args.lex: +if args.debug and args.lex: lexer.input(input) lineno = 0 @@ -201,10 +210,10 @@ class AstNodeExpressionType(Enum): class AstNode: pass class AstNodeVariableDeclarationStatement(AstNode): - def __init__(self, type, name, value_type, value): + def __init__(self, type, name, variable_type, value): self.type = type self.name = name - self.value_type = value_type + self.variable_type = variable_type self.value = value class AstNodeVariableReassignmentStatement(AstNode): @@ -227,6 +236,10 @@ class AstNodeVariableTypeMap(AstNode): self.key_type = key_type self.value_type = value_type +class AstNodeVariableTypeType(AstNode): + def __init__(self, base_type): + self.base_type = base_type + class AstNodeMapElement(AstNode): def __init__(self, key, value): self.key = key @@ -244,8 +257,7 @@ class AstNodeOperatorExpression(AstNode): self.right = right class AstNodeFunctionDeclaration(AstNode): - def __init__(self, name, parameters, return_type, body): - self.name = name + def __init__(self, parameters, return_type, body): self.parameters = parameters self.return_type = return_type self.body = body @@ -269,18 +281,19 @@ class AstNodeFunctionCallParameter(AstNode): self.name = name self.value = value -class AstNodeLoop(AstNode): - def __init__(self, iteratee, key_id, value_id, body): - self.iteratee = iteratee - self.key_id = key_id - self.value_id = value_id - self.body = body - class AstNodePipe(AstNode): def __init__(self, left, right): self.left = left self.right = right +class AstNodeTypeDeclaration(AstNode): + def __init__(self, body): + self.body = body + +class AstNodeNamespaceAccess(AstNode): + def __init__(self, left, right): + self.left = left + self.right = right precedence = ( @@ -292,9 +305,9 @@ precedence = ( -def p_statements(p): - '''statements : statement - | statement statements''' +def p_program(p): + '''program : program_statement + | program_statement program''' def resolve_nodes(node, level): node_dict = node.__dict__ @@ -308,18 +321,39 @@ def p_statements(p): list_length = len(node_value) print('> '*level, property + '[' + str(list_length) + ']:') - for idx in range(list_length): - item = node_value[idx] + if list_length == 0: + print('> '*(level+1), 'None') + else: + for idx in range(list_length): + item = node_value[idx] - print('> '*(level+1), str(idx+1)+'.', item.__class__.__name__) - resolve_nodes(item, level+2) + print('> '*(level+1), str(idx+1)+'.', item.__class__.__name__) + resolve_nodes(item, level+2) else: print('> '*level, property, node_dict[property]) - if p[1]: + if args.debug and p[1]: print(p[1].__class__.__name__) resolve_nodes(p[1], 0) + program = [p[1]] + if len(p) > 2: + program.extend(p[2]) + p[0] = program + +def p_program_statement(p): + '''program_statement : variable_declaration_statement + | variable_reassignment_statement + | function_call + | pipe''' + + p[0] = p[1] + + +def p_statements(p): + '''statements : statement + | statement statements''' + statements = [p[1]] if len(p) > 2: statements.extend(p[2]) @@ -330,9 +364,9 @@ def p_statement(p): '''statement : variable_declaration_statement | variable_reassignment_statement | function_declaration - | function_call SEMICOLON - | loop - | pipe SEMICOLON + | function_call + | type_declaration + | pipe | return_statement''' p[0] = p[1] @@ -341,9 +375,9 @@ def p_statement(p): def p_function_declaration(p): - '''function_declaration : FUNCTION IDENTIFIER PARENTHESIS_LEFT function_declaration_parameters PARENTHESIS_RIGHT COLON return_type BRACE_LEFT statements BRACE_RIGHT''' + '''function_declaration : PARENTHESIS_LEFT function_declaration_parameters PARENTHESIS_RIGHT COLON return_type BRACE_LEFT statements BRACE_RIGHT''' - p[0] = AstNodeFunctionDeclaration(p[2], p[4], p[7], p[9]) + p[0] = AstNodeFunctionDeclaration(p[2], p[5], p[7]) def p_function_declaration_parameters(p): @@ -352,7 +386,9 @@ def p_function_declaration_parameters(p): | function_declaration_parameter |''' - parameters = [p[1]] + parameters = [] + if len(p) == 2: + parameters.append(p[1]) if len(p) == 4: parameters.extend(p[3]) @@ -368,7 +404,8 @@ def p_function_declaration_parameter(p): def p_function_call(p): - '''function_call : IDENTIFIER PARENTHESIS_LEFT function_call_parameters PARENTHESIS_RIGHT''' + '''function_call : IDENTIFIER PARENTHESIS_LEFT function_call_parameters PARENTHESIS_RIGHT + | namespace_access PARENTHESIS_LEFT function_call_parameters PARENTHESIS_RIGHT''' p[0] = AstNodeFunctionCall(p[1], p[3]) @@ -401,14 +438,6 @@ def p_function_call_parameter(p): -def p_loop(p): - '''loop : LOOP PARENTHESIS_LEFT expression PARENTHESIS_RIGHT PIPE identifier COMMA identifier PIPE BRACE_LEFT statements BRACE_RIGHT''' - - p[0] = AstNodeLoop(p[3], p[6], p[8], p[11]) - - - - def p_pipe(p): '''pipe : expression OPERATOR_PIPE pipe | expression OPERATOR_PIPE function_call''' @@ -419,13 +448,14 @@ def p_pipe(p): def p_variable_declaration_statement(p): - '''variable_declaration_statement : variable_declarator IDENTIFIER COLON variable_type ASSIGN expression SEMICOLON''' + '''variable_declaration_statement : variable_declarator IDENTIFIER COLON variable_type ASSIGN expression''' p[0] = AstNodeVariableDeclarationStatement(p[1], p[2], p[4], p[6]) -def p_variable_reassignment_statement(p): - '''variable_reassignment_statement : IDENTIFIER ASSIGN expression SEMICOLON''' +def p_variable_reassignment_statement(p): # TODO: adjust for object parameter assignment + '''variable_reassignment_statement : IDENTIFIER ASSIGN expression + | namespace_access ASSIGN expression''' p[0] = AstNodeVariableReassignmentStatement(p[1], p[3]) @@ -455,7 +485,9 @@ def p_variable_types_primitive(p): def p_variable_type(p): '''variable_type : variable_type_array | variable_type_map + | variable_type_type | variable_type_primitive + | identifier | TYPE_TYPE | FUNCTION''' p[0] = p[1] @@ -483,21 +515,41 @@ def p_variable_type_map(p): p[0] = AstNodeVariableTypeMap(p[2], p[5]) +def p_variable_type_type(p): + '''variable_type_type : TYPE_TYPE BRACKET_LEFT variable_type BRACKET_RIGHT''' + + p[0] = AstNodeVariableTypeType(p[3]) + def p_return_statement(p): - '''return_statement : RETURN expression SEMICOLON''' + '''return_statement : RETURN expression''' p[0] = AstNodeReturnStatement(p[2]) def p_return_type(p): '''return_type : variable_type - | TYPE_VOID''' + | TYPE_VOID''' p[0] = p[1] +def p_type_declaration(p): + '''type_declaration : BRACE_LEFT type_body BRACE_RIGHT''' + + p[0] = AstNodeTypeDeclaration(p[2]) + +def p_type_body(p): + '''type_body : variable_declaration_statement + | variable_declaration_statement type_body''' + + type_body = [p[1]] + if len(p) > 2: + type_body.extend(p[2]) + p[0] = type_body + + def p_identifier(p): '''identifier : IDENTIFIER''' @@ -572,13 +624,24 @@ def p_map_element_key(p): p[0] = p[1] +def p_namespace_access(p): + '''namespace_access : identifier NAMESPACE_ACCESSOR identifier + | namespace_access NAMESPACE_ACCESSOR identifier''' + + p[0] = AstNodeNamespaceAccess(p[1], p[3]) + + def p_expression(p): '''expression : identifier | number | string | array | map + | function_declaration | function_call + | type_declaration + | variable_type + | namespace_access | expression OPERATOR_PLUS expression | expression OPERATOR_MINUS expression | expression OPERATOR_MULTIPLY expression @@ -593,9 +656,12 @@ def p_expression(p): def p_error(p): - line_start_column = input.rfind('\n', 0, p.lexpos) + 1 - column = (p.lexpos - line_start_column) + 1 - print("Syntax error in input! Line: {line}, Column: {column}".format(line=p.lineno, column=column), p) + if p: + line_start_column = input.rfind('\n', 0, p.lexpos) + 1 + column = (p.lexpos - line_start_column) + 1 + print("Syntax error in input! Line: {line}, Column: {column}".format(line=p.lineno, column=column), p) + else: + print("Unknown Error :(") @@ -605,10 +671,162 @@ result = parser.parse(input) +variables = { + "print": print, +} + + +def is_statement(statement): + return ( + type(statement) is AstNodeVariableDeclarationStatement or + type(statement) is AstNodeVariableReassignmentStatement or + type(statement) is AstNodeFunctionCall or + type(statement) is AstNodePipe + ) + + +def to_parameters_dict(parameters, context): + dictionary = {} + + for idx, parameter in enumerate(parameters): + if type(parameter) is AstNodeFunctionDeclarationParameter: + dictionary[parameter.name] = None + elif type(parameter) is AstNodeFunctionCallParameter: + value = parameter.value + + if value == "$": + value = context["$"] + + if parameter.name == "": + dictionary[idx] = value + else: + dictionary[parameter.name] = value + + return dictionary + + +def evaluate_statement(statement, context): + scoped_variables = {} + + # variable declaration + if type(statement) is AstNodeVariableDeclarationStatement: + value = evaluate_expression(statement.value, context) + scoped_variables[statement.name] = value + + # variavle 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)) + else: + scoped_variables[statement.name] = value + + # function call + elif type(statement) is AstNodeFunctionCall: + function = context[statement.name] + + # in-built + if callable(function): + pass + function(*[evaluate_expression(parameter, context) for parameter in statement.parameters]) + + # else + else: + call_parameters = to_parameters_dict(statement.parameters, context) + declaration_parameters = to_parameters_dict(function.parameters, context) + + parameters = {} + for idx, key in enumerate(declaration_parameters.keys()): + if key in call_parameters: + parameters[key] = evaluate_expression(call_parameters[key], context) + else: + parameters[key] = evaluate_expression(call_parameters[idx], context) + + statements = {} + for body_statement in function.body: + call_context_parameters = context | parameters | statements + if type(body_statement) is AstNodeReturnStatement: + scoped_variables = evaluate_expression( + body_statement.expression, + call_context_parameters + ) + else: + statements |= evaluate_statement(body_statement, call_context_parameters) + + # pipe + elif type(statement) is AstNodePipe: + left = evaluate_statement(statement.left, context) + right = evaluate_statement(statement.right, context | {"$": left}) + + scoped_variables = right + + return scoped_variables + + +def evaluate_expression(expression, scoped_variables): + result = expression + + # operator expression + if type(expression) is AstNodeOperatorExpression: + left = evaluate_expression(expression.left, scoped_variables) + right = evaluate_expression(expression.right, scoped_variables) + + if type(left) != type(right): + print("ERROR: Can't do math with non-matching types. Left: {left} - Right: {right}".format(left=type(left), right=type(right))) + else: + if expression.type == "+": + result = left + right + elif expression.type == "-": + result = left - right + elif expression.type == "*": + result = left * right + elif expression.type == "/": + result = left / right + + # general expression + elif type(expression) is AstNodeExpression: + if expression.type is AstNodeExpressionType.IDENTIFIER: + 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: + result = {} + for element in expression.value: + result[evaluate_expression(element.key, scoped_variables)] = evaluate_expression(element.value, scoped_variables) + else: + result = evaluate_expression(expression.value, scoped_variables) + + # function call / type instantiation + elif type(expression) is AstNodeFunctionCall: + 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) + + result = instance_variables + + # function call parameter + elif type(expression) is AstNodeFunctionCallParameter: + result = evaluate_expression(expression.value, scoped_variables) + if result == "$": + result = scoped_variables["$"] + + return result + if result: - for ast_node in result: - print(ast_node) + for node in result: + if args.debug: + print(node) + + variables |= evaluate_statement(node, variables) diff --git a/test/hello-world.hnshn b/test/hello-world.hnshn deleted file mode 100644 index 4d4af3d..0000000 --- a/test/hello-world.hnshn +++ /dev/null @@ -1,28 +0,0 @@ -const std = import('@std'); - -function main(): void { - const integer: integer32 = 123; - const string: string = '123'; - const array: [integer32][3] = [1, 2, 3]; - const map: [string][string|integer32] = [ - 'first': 1, - 'second': 'two', - 'third': 3, - ]; - - for (array) |index, value| { - // cool - } - - for (map) |key, value| { - // also cool - } - - for (string) |index, char| { - // cool? - const char2 = std.str.get_char_at_index(string, index); - } - - std.str.format('cool %s', string) - |> print($); -} diff --git a/test/test.test b/test/test.test index 3778099..149f1a6 100644 --- a/test/test.test +++ b/test/test.test @@ -1,65 +1,60 @@ -const henshin: integer = 2; // comment +const henshin: integer = 2 // comment // next comment -var ply: string = "cool!"; -ply = "way cooler!!"; +var ply: string = "cool!" +ply = "way cooler!!" -const new: integer = henshin * 5 + 10; +const new: integer = henshin + 5 * 10 -const test: integer = 1 + 1; +const test: integer = 1 + 1 -function main(input: string or integer): void { - const hello: string = "world!"; - const bye: string = "bye!"; +const main: function = (input: string or integer): void { + const hello: string = input + " world!" + const bye: string = "bye!" - return hello; + return hello + " " + bye } -main(input = "hello!"); +const x: string = main(input = "hello!") +print(x) -const array: [integer] = [1, 2, 3]; +const array: [integer] = [1, 2, 3] const map: [string][string or integer] = [ "first" = 1, "second" = "two", "third" = "3", - "fourth" = 4 -]; - -for (array) |index, value| { - ply = value; -} - -for (map) |key, value| { - ply = key; + "fourth" = 4, + "fifth" = (): void { + return "5." + }, +] + +main("pipe1") => print($) +main("pipe2") => main($) => print($) + +const test_type: type = { + const test_field: string = "test" + var another_field: integer = 4 + + const test_function: function = (): void { + return self.test_field + } + + const another_function: function = (add: integer): integer { + return self.another_field + add + } } -main() => print($); -main() => main2($) => print($); - -//type test_type = { -// const test_field: string = "test", -// var another_field: integer = 4, -// -// function test_function(): void { -// return test_type.test_field; -// }, -// function_field: function = another_function(): void { -// return test_type.another_field; -// }, -//}; +const other_int: type = integer -// type other_int = integer; +const inherited_type: type[test_type] = { + const nested: test_type = test_type() -//const test_type: type = {}; -//const other_int: type = integer; - -//const main: function = function(input: string): void { -// return input; -//}; - -//const main: function = (input: string): void { -// return input; -//}; + const new_function: function = (): void { + return self.another_field + 4 + } +} -//iterate(array, (index: integer, value: integer) { -// -//}); +const object: inherited_type = inherited_type() +//object.test_field = "hey" +//object.test_function() => print($) +//object.nested.another_function(add = 2) -- cgit v1.2.3