Add symbol and structure support
[fur] / parsing.py
index 12b93f5..20ac376 100644 (file)
@@ -36,14 +36,6 @@ def _zero_or_more_parser(formatter, parser):
 
     return result_parser
 
-NodeMetadata = collections.namedtuple(
-    'NodeMetadata',
-    [
-        'index',
-        'line',
-    ],
-)
-
 FurIntegerLiteralExpression = collections.namedtuple(
     'FurIntegerLiteralExpression',
     [
@@ -51,6 +43,14 @@ FurIntegerLiteralExpression = collections.namedtuple(
     ],
 )
 
+FurLambdaExpression = collections.namedtuple(
+    'FurLambdaExpression',
+    (
+        'argument_name_list',
+        'statement_list',
+    ),
+)
+
 FurStringLiteralExpression = collections.namedtuple(
     'FurStringLiteralExpression',
     [
@@ -61,6 +61,7 @@ FurStringLiteralExpression = collections.namedtuple(
 FurSymbolExpression = collections.namedtuple(
     'FurSymbolExpression',
     [
+        'metadata',
         'symbol',
     ],
 )
@@ -68,6 +69,7 @@ FurSymbolExpression = collections.namedtuple(
 FurNegationExpression = collections.namedtuple(
     'FurNegationExpression',
     [
+        'metadata',
         'value',
     ],
 )
@@ -135,7 +137,14 @@ def _string_literal_expression_parser(index, tokens):
 
 def _symbol_expression_parser(index, tokens):
     if tokens[index].type == 'symbol':
-        return (True, index + 1, FurSymbolExpression(symbol=tokens[index].match))
+        return (
+            True,
+            index + 1,
+            FurSymbolExpression(
+                metadata=tokens[index].metadata,
+                symbol=tokens[index].match,
+            ),
+        )
 
     return (False, index, None)
 
@@ -212,6 +221,56 @@ def _structure_literal_parser(index, tokens):
         ),
     )
 
+def _lambda_expression_parser(index, tokens):
+    failure = (False, index, None)
+
+    if tokens[index].type == 'keyword' and tokens[index].match == 'lambda':
+        index += 1
+    else:
+        return failure
+
+    if tokens[index].type == 'open_parenthese':
+        index += 1
+    else:
+        raise Exception('Expected "(", found "{}" on line {}'.format(
+            tokens[index].match,
+            tokens[index].metadata.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].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, FurLambdaExpression(
+        argument_name_list=tuple(an.symbol for an in argument_name_list),
+        statement_list=statement_list,
+    )
+
+
+
 def _list_literal_expression_parser(index, tokens):
     failure = (False, index, None)
 
@@ -232,6 +291,7 @@ def _literal_level_expression_parser(index, tokens):
         _integer_literal_expression_parser,
         _string_literal_expression_parser,
         _list_literal_expression_parser,
+        _lambda_expression_parser,
         _symbol_expression_parser,
         _structure_literal_parser,
     )(index, tokens)
@@ -249,12 +309,14 @@ def _negation_expression_parser(index, tokens):
     if tokens[index].match != '-':
         return failure
 
+    metadata = tokens[index].metadata
+
     success, index, value = _dot_expression_parser(index + 1, tokens)
 
     if not success:
         return failure
 
-    return (True, index, FurNegationExpression(value=value))
+    return (True, index, FurNegationExpression(metadata=metadata, value=value))
 
 def _negation_level_expression_parser(index, tokens):
     return _or_parser(
@@ -279,10 +341,7 @@ def _left_recursive_infix_operator_parser(operator_token_matcher, operand_parser
 
             if success:
                 result = FurInfixExpression(
-                    metadata=NodeMetadata(
-                        index=tokens[index].index,
-                        line=tokens[index].line,
-                    ),
+                    metadata=tokens[index].metadata,
                     order=order,
                     operator=tokens[index].match,
                     left=result,
@@ -368,6 +427,7 @@ FurListItemExpression = collections.namedtuple(
     'FurListItemExpression',
     [
         'list_expression',
+        'metadata',
         'index_expression',
     ],
 )
@@ -375,6 +435,7 @@ FurListItemExpression = collections.namedtuple(
 FurFunctionCallExpression = collections.namedtuple(
     'FurFunctionCallExpression',
     [
+        'metadata',
         'function',
         'arguments',
     ],
@@ -424,6 +485,8 @@ def _list_item_expression_parser(index, tokens):
     if not success:
         return failure
 
+    metadata = tokens[index].metadata
+
     success, index, index_expression = _bracket_wrapped_parser(_expression_parser)(
         index,
         tokens,
@@ -437,9 +500,12 @@ def _list_item_expression_parser(index, tokens):
         # We can't give this a better name without a bunch of checks, however.
         list_expression = FurListItemExpression(
             list_expression=list_expression,
+            metadata=metadata,
             index_expression=index_expression,
         )
 
+        metadata = tokens[index].metadata
+
         success, index, index_expression = _bracket_wrapped_parser(_expression_parser)(
             index,
             tokens,
@@ -460,6 +526,8 @@ def _function_call_expression_parser(index, tokens):
     if not success:
         return failure
 
+    metadata = tokens[index].metadata
+
     success, index, arguments = _parenthese_wrapped_parser(_comma_separated_expression_list_parser)(
         index,
         tokens,
@@ -472,10 +540,13 @@ def _function_call_expression_parser(index, tokens):
         # "function" is actually the full function call if the next parse attempt doesn't succeed
         # We can't give this a better name without a bunch of checks, however.
         function = FurFunctionCallExpression(
+            metadata=metadata,
             function=function,
             arguments=arguments,
         )
 
+        metadata = tokens[index].metadata
+
         success, index, arguments = _parenthese_wrapped_parser(_comma_separated_expression_list_parser)(
             index,
             tokens,
@@ -549,7 +620,7 @@ def _assignment_statement_parser(index, tokens):
 
     if tokens[index].type == 'symbol':
         target = tokens[index].match
-        target_assignment_line = tokens[index].line
+        target_assignment_line = tokens[index].metadata.line
 
         index += 1
     else:
@@ -630,7 +701,7 @@ def _function_definition_statement_parser(index, tokens):
 
     return True, index, FurFunctionDefinitionStatement(
         name=name,
-    argument_name_list=tuple(an.symbol for an in argument_name_list),
+        argument_name_list=tuple(an.symbol for an in argument_name_list),
         statement_list=statement_list,
     )