Add support for parenthesized functions
[fur] / parsing.py
index 23bf65e..7240b52 100644 (file)
@@ -64,13 +64,6 @@ FurNegationExpression = collections.namedtuple(
     ],
 )
 
-FurParenthesizedExpression = collections.namedtuple(
-    'FurParenthesizedExpression',
-    [
-        'internal',
-    ],
-)
-
 FurInfixExpression = collections.namedtuple(
     'FurInfixExpression',
     [
@@ -123,7 +116,7 @@ def _parenthesized_expression_parser(index, tokens):
             tokens[index].match,
         ))
 
-    return True, index, FurParenthesizedExpression(internal=internal)
+    return True, index, internal
 
 def _negation_expression_parser(index, tokens):
     failure = (False, index, None)
@@ -211,30 +204,35 @@ def _or_level_expression_parser(index, tokens):
         'or_level',
     )(index, tokens)
 
-def _comma_separated_list_parser(index, tokens):
-    failure = (False, index, None)
+def _comma_separated_list_parser(subparser):
+    def result_parser(index, tokens):
+        start_index = index
 
-    expressions = []
+        items = []
 
-    success, index, expression = _expression_parser(index, tokens)
+        success, index, item = subparser(index, tokens)
 
-    if success:
-        expressions.append(expression)
-    else:
-        return failure
+        if success:
+            items.append(item)
+        else:
+            return (True, start_index, ())
+
+        while success and index < len(tokens) and tokens[index].type == 'comma':
+            success = False
 
-    while success and index < len(tokens) and tokens[index].type == 'comma':
-        success = False
+            if index + 1 < len(tokens):
+                success, try_index, item = subparser(index + 1, tokens)
 
-        if index + 1 < len(tokens):
-            success, try_index, expression = _expression_parser(index + 1, tokens)
+            if success:
+                items.append(item)
+                index = try_index
 
-        if success:
-            expressions.append(expression)
-            index = try_index
+        return True, index, tuple(items)
 
-    return True, index, tuple(expressions)
+    return result_parser
 
+def _comma_separated_expression_list_parser(index, tokens):
+    return _comma_separated_list_parser(_expression_parser)(index, tokens)
 
 FurFunctionCallExpression = collections.namedtuple(
     'FurFunctionCallExpression',
@@ -259,6 +257,15 @@ FurAssignmentStatement = collections.namedtuple(
     ],
 )
 
+FurFunctionDefinitionStatement = collections.namedtuple(
+    'FurFunctionDefinitionStatement',
+    [
+        'name',
+        'argument_name_list',
+        'statement_list',
+    ],
+)
+
 FurProgram = collections.namedtuple(
     'FurProgram',
     [
@@ -267,10 +274,16 @@ FurProgram = collections.namedtuple(
 )
 
 def _function_call_expression_parser(index, tokens):
-    # TODO Use a FurSymbolExpression for the name
+    # TODO Allow function calls as the source of the function. This requires a
+    # left-recursive parser, however.
     failure = (False, index, None)
 
-    success, index, function = _symbol_expression_parser(index, tokens)
+    # We have to be careful what expressions we add here. Otherwise expressions
+    # like "a + b()" become ambiguous to the parser.
+    success, index, function = _or_parser(
+        _symbol_expression_parser,
+        _parenthesized_expression_parser,
+    )(index, tokens)
 
     if not success:
         return failure
@@ -279,7 +292,7 @@ def _function_call_expression_parser(index, tokens):
         return failure
     index += 1
 
-    success, index, arguments = _comma_separated_list_parser(index, tokens)
+    success, index, arguments = _comma_separated_expression_list_parser(index, tokens)
 
     if not success:
         return failure
@@ -329,6 +342,64 @@ def _assignment_statement_parser(index, tokens):
 
     return True, index, FurAssignmentStatement(target=target, expression=expression)
 
+def _function_definition_statement_parser(index, tokens):
+    failure = (False, index, None)
+
+    if tokens[index].type == 'keyword' and tokens[index].match == 'def':
+        index += 1
+    else:
+        return failure
+
+    if tokens[index].type == 'symbol':
+        name = tokens[index].match
+        index += 1
+    else:
+        raise Exception('Expected function name, found "{}" on line {}'.format(
+            tokens[index].match,
+            tokens[index].line,
+        ))
+
+    if tokens[index].type == 'open_parenthese':
+        index += 1
+    else:
+        raise Exception('Expected "(", found "{}" on line {}'.format(
+            tokens[index].match,
+            tokens[index].line,
+        ))
+
+    success, index, argument_name_list = _comma_separated_list_parser(_symbol_expression_parser)(
+        index,
+        tokens,
+    )
+
+    if tokens[index].type == 'close_parenthese':
+        index += 1
+    else:
+        raise Exception('Expected ")", found "{}" on line {}'.format(
+            tokens[index].match,
+            tokens[index].line,
+        ))
+
+    if tokens[index].type == 'symbol' and tokens[index].match == 'do':
+        index += 1
+    else:
+        return failure
+
+    success, index, statement_list = _zero_or_more_parser(tuple, _statement_parser)(index, tokens)
+
+    _, index, _ = consume_newlines(index, tokens)
+
+    if tokens[index].type == 'keyword' and tokens[index].match == 'end':
+        index += 1
+    else:
+        return failure
+
+    return True, index, FurFunctionDefinitionStatement(
+        name=name,
+        argument_name_list=tuple(an.value for an in argument_name_list),
+        statement_list=statement_list,
+    )
+
 def _statement_parser(index, tokens):
     _, index, _ = consume_newlines(index, tokens)
 
@@ -338,6 +409,7 @@ def _statement_parser(index, tokens):
     return _or_parser(
         _assignment_statement_parser,
         _expression_statement_parser,
+        _function_definition_statement_parser,
     )(index, tokens)
 
 def _program_formatter(statement_list):