summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xhenshin308
-rw-r--r--test/hello-world.hnshn28
-rw-r--r--test/test.test91
3 files changed, 306 insertions, 121 deletions
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)