Added if expression statements
authorDavid Kerkeslager <kerkeslager@gmail.com>
Thu, 17 Aug 2017 15:33:33 +0000 (11:33 -0400)
committerDavid Kerkeslager <kerkeslager@gmail.com>
Thu, 17 Aug 2017 15:33:33 +0000 (11:33 -0400)
examples/25_if_statements.fur [new file with mode: 0644]
examples/25_if_statements.fur.output.txt [new file with mode: 0644]
generation.py
normalization.py
parsing.py
templates/function_definition.c
templates/program.c
tokenization.py
transformation.py

diff --git a/examples/25_if_statements.fur b/examples/25_if_statements.fur
new file mode 100644 (file)
index 0000000..4695cee
--- /dev/null
@@ -0,0 +1,14 @@
+first_greeting = if true do
+  'Hello, world'
+else
+  'Goodnight, moon'
+end
+
+second_greeting = if false do
+  'Hello, world'
+else
+  'Goodnight, moon'
+end
+
+print(first_greeting, '\n')
+print(second_greeting, '\n')
diff --git a/examples/25_if_statements.fur.output.txt b/examples/25_if_statements.fur.output.txt
new file mode 100644 (file)
index 0000000..f48e806
--- /dev/null
@@ -0,0 +1,2 @@
+Hello, world
+Goodnight, moon
index 55b3ade..4fe8de4 100644 (file)
@@ -108,31 +108,31 @@ def generate_if_else_statement(statement):
         generate_expression(statement.condition_expression),
     )
 
-    if len(statement.if_statements) == 0:
+    if len(statement.if_statement_list) == 0:
         condition_expression = '!({})'.format(condition_expression)
-        if_statements = statement.else_statements
-        else_statements = ()
+        if_statement_list = statement.else_statement_list
+        else_statement_list = ()
     else:
-        if_statements = statement.if_statements
-        else_statements = statement.else_statements
+        if_statement_list = statement.if_statement_list
+        else_statement_list = statement.else_statement_list
 
     generated_if_clause = 'if({})'.format(condition_expression)
 
-    if len(if_statements) == 0:
-        generated_if_statements = ';'
+    if len(if_statement_list) == 0:
+        generated_if_statement_list = ';'
     else:
-        generated_if_statements = indent('\n{{\n{}\n}}'.format(
-            indent('\n'.join(generate_statement(s) for s in if_statements)),
+        generated_if_statement_list = indent('\n{{\n{}\n}}'.format(
+            indent('\n'.join(generate_statement(s) for s in if_statement_list)),
         ))
 
-    if len(else_statements) == 0:
-        generated_else_statements = ''
+    if len(else_statement_list) == 0:
+        generated_else_statement_list = ''
     else:
-        generated_else_statements = indent('\nelse\n{{\n{}\n}}'.format(
-            indent('\n'.join(generate_statement(s) for s in else_statements)),
+        generated_else_statement_list = indent('\nelse\n{{\n{}\n}}'.format(
+            indent('\n'.join(generate_statement(s) for s in else_statement_list)),
         ))
 
-    return generated_if_clause + generated_if_statements + generated_else_statements
+    return generated_if_clause + generated_if_statement_list + generated_else_statement_list
 
 def generate_function_declaration(statement):
     return 'Environment_set(environment, "{}", (Object){{ CLOSURE, (Instance)(Closure){{ environment, user${}$implementation }} }});'.format(statement.name, statement.name)
index ff2e327..c96e455 100644 (file)
@@ -100,8 +100,8 @@ NormalIfElseStatement = collections.namedtuple(
     'NormalIfElseStatement',
     [
         'condition_expression',
-        'if_statements',
-        'else_statements',
+        'if_statement_list',
+        'else_statement_list',
     ],
 )
 
@@ -125,27 +125,41 @@ def fake_normalization(counter, thing):
     return (counter, (), thing)
 
 def normalize_integer_literal_expression(counter, expression):
+    variable = '${}'.format(counter)
     return (
-        counter,
-        (),
-        NormalIntegerLiteralExpression(integer=expression.integer),
+        counter + 1,
+        (
+            NormalVariableInitializationStatement(
+                variable=variable,
+                expression=NormalIntegerLiteralExpression(integer=expression.integer),
+            ),
+        ),
+        NormalVariableExpression(variable=variable),
     )
 
 def normalize_string_literal_expression(counter, expression):
+    variable = '${}'.format(counter)
     return (
-        counter,
-        (),
-        NormalStringLiteralExpression(string=expression.string),
+        counter + 1,
+        (
+            NormalVariableInitializationStatement(
+                variable=variable,
+                expression=NormalStringLiteralExpression(string=expression.string),
+            ),
+        ),
+        NormalVariableExpression(variable=variable),
     )
 
 def normalize_symbol_expression(counter, expression):
     variable = '${}'.format(counter)
     return (
         counter + 1,
-        (NormalVariableInitializationStatement(
-            variable=variable,
-            expression=NormalSymbolExpression(symbol=expression.symbol),
-        ),),
+        (
+            NormalVariableInitializationStatement(
+                variable=variable,
+                expression=NormalSymbolExpression(symbol=expression.symbol),
+            ),
+        ),
         NormalVariableExpression(variable=variable),
     )
 
@@ -162,10 +176,12 @@ def normalize_function_call_expression(counter, expression):
             prestatements.append(s)
 
         variable = '${}'.format(counter)
-        prestatements.append(NormalVariableInitializationStatement(
-            variable=variable,
-            expression=normalized_argument,
-        ))
+        prestatements.append(
+            NormalVariableInitializationStatement(
+                variable=variable,
+                expression=normalized_argument,
+            )
+        )
         arguments.append(NormalVariableExpression(
             variable=variable,
         ))
@@ -190,10 +206,12 @@ def normalize_function_call_expression(counter, expression):
     if not isinstance(function_expression, NormalVariableExpression):
         function_variable = '${}'.format(counter)
 
-        prestatements.append(NormalVariableInitializationStatement(
-            variable=function_variable,
-            expression=function_expression,
-        ))
+        prestatements.append(
+            NormalVariableInitializationStatement(
+                variable=function_variable,
+                expression=function_expression,
+            )
+        )
 
         function_expression = NormalVariableExpression(variable=function_variable)
         counter += 1
@@ -216,6 +234,8 @@ def normalize_basic_infix_operation(counter, expression):
     counter += 1
     right_variable = '${}'.format(counter)
     counter += 1
+    center_variable = '${}'.format(counter)
+    counter += 1
 
     root_prestatements = (
         NormalVariableInitializationStatement(
@@ -226,17 +246,21 @@ def normalize_basic_infix_operation(counter, expression):
             variable=right_variable,
             expression=right_expression,
         ),
+        NormalVariableInitializationStatement(
+            variable=center_variable,
+            expression=NormalInfixExpression(
+                order=expression.order,
+                operator=expression.operator,
+                left=NormalVariableExpression(variable=left_variable),
+                right=NormalVariableExpression(variable=right_variable),
+            ),
+        ),
     )
 
     return (
         counter,
         left_prestatements + right_prestatements + root_prestatements,
-        NormalInfixExpression(
-            order=expression.order,
-            operator=expression.operator,
-            left=NormalVariableExpression(variable=left_variable),
-            right=NormalVariableExpression(variable=right_variable),
-        ),
+        NormalVariableExpression(variable=center_variable),
     )
 
 def normalize_comparison_expression(counter, expression):
@@ -306,7 +330,10 @@ def normalize_boolean_expression(counter, expression):
     counter, right_prestatements, right_expression = normalize_expression(counter, expression.right)
 
     result_variable = '${}'.format(counter)
-    if_else_prestatment = NormalVariableInitializationStatement(variable=result_variable, expression=left_expression)
+    if_else_prestatment = NormalVariableInitializationStatement(
+        variable=result_variable,
+        expression=left_expression,
+    )
     counter += 1
 
     condition_expression=NormalVariableExpression(variable=result_variable)
@@ -315,15 +342,15 @@ def normalize_boolean_expression(counter, expression):
     if expression.operator == 'and':
         if_else_statement = NormalIfElseStatement(
             condition_expression=condition_expression,
-            if_statements=short_circuited_statements,
-            else_statements=(),
+            if_statement_list=short_circuited_statements,
+            else_statement_list=(),
         )
 
     elif expression.operator == 'or':
         if_else_statement = NormalIfElseStatement(
             condition_expression=condition_expression,
-            if_statements=(),
-            else_statements=short_circuited_statements,
+            if_statement_list=(),
+            else_statement_list=short_circuited_statements,
         )
 
     else:
@@ -345,6 +372,45 @@ def normalize_infix_expression(counter, expression):
         'or_level': normalize_boolean_expression,
     }[expression.order](counter, expression)
 
+def normalize_if_expression(counter, expression):
+    counter, condition_prestatements, condition_expression = normalize_expression(
+        counter,
+        expression.condition_expression,
+    )
+
+    result_variable = '${}'.format(counter)
+    counter += 1
+
+    counter, if_statement_list = normalize_statement_list(
+        counter,
+        expression.if_statement_list,
+        assign_result_to=result_variable,
+    )
+    counter, else_statement_list = normalize_statement_list(
+        counter,
+        expression.else_statement_list,
+        assign_result_to=result_variable,
+    )
+
+    return (
+        counter,
+        condition_prestatements + (
+            NormalVariableInitializationStatement(
+                variable=result_variable,
+                expression=NormalVariableExpression(variable='builtin$nil'),
+            ),
+            NormalIfElseStatement(
+                condition_expression=condition_expression,
+                if_statement_list=if_statement_list,
+                else_statement_list=else_statement_list,
+            ),
+        ),
+        NormalVariableExpression(variable=result_variable),
+    )
+
+
+
+
 def normalize_negation_expression(counter, expression):
     counter, prestatements, internal_expression = normalize_expression(counter, expression.value)
 
@@ -353,7 +419,12 @@ def normalize_negation_expression(counter, expression):
 
     return (
         counter,
-        prestatements + (NormalVariableInitializationStatement(variable=internal_variable, expression=internal_expression),),
+        prestatements + (
+            NormalVariableInitializationStatement(
+                variable=internal_variable,
+                expression=internal_expression,
+            ),
+        ),
         NormalNegationExpression(internal_expression=NormalVariableExpression(variable=internal_variable)),
     )
 
@@ -362,6 +433,7 @@ def normalize_expression(counter, expression):
         NormalInfixExpression: fake_normalization,
         NormalVariableExpression: fake_normalization,
         parsing.FurFunctionCallExpression: normalize_function_call_expression,
+        parsing.FurIfExpression: normalize_if_expression,
         parsing.FurInfixExpression: normalize_infix_expression,
         parsing.FurIntegerLiteralExpression: normalize_integer_literal_expression,
         parsing.FurNegationExpression: normalize_negation_expression,
@@ -383,13 +455,18 @@ def normalize_expression_statement(counter, statement):
     )
 
 def normalize_function_definition_statement(counter, statement):
+    _, statement_list = normalize_statement_list(
+        0,
+        statement.statement_list,
+        assign_result_to='result',
+    )
     return (
         counter,
         (),
         NormalFunctionDefinitionStatement(
             name=statement.name,
             argument_name_list=statement.argument_name_list,
-            statement_list=normalize_statement_list(statement.statement_list),
+            statement_list=statement_list,
         ),
     )
 
@@ -412,16 +489,40 @@ def normalize_statement(counter, statement):
     }[type(statement)](counter, statement)
 
 @util.force_generator(tuple)
-def normalize_statement_list(statement_list):
-    counter = 0
+def normalize_statement_list(counter, statement_list, **kwargs):
+    assign_result_to = kwargs.pop('assign_result_to', None)
+
+    assert len(kwargs) == 0
+
+    result_statement_list = []
 
     for statement in statement_list:
         counter, prestatements, normalized = normalize_statement(counter, statement)
         for s in prestatements:
-            yield s
-        yield normalized
+            result_statement_list.append(s)
+        result_statement_list.append(normalized)
+
+    last_statement = result_statement_list[-1]
+
+    if isinstance(last_statement, NormalExpressionStatement) and isinstance(last_statement.expression, NormalVariableExpression):
+        result_expression = result_statement_list.pop().expression
+
+        if assign_result_to is not None:
+            result_statement_list.append(
+                NormalVariableReassignmentStatement(
+                    variable=assign_result_to,
+                    expression=result_expression,
+                )
+            )
+
+    return (
+        counter,
+        result_statement_list,
+    )
 
 def normalize(program):
+    _, statement_list = normalize_statement_list(0, program.statement_list)
+
     return NormalProgram(
-        statement_list=normalize_statement_list(program.statement_list),
+        statement_list=statement_list,
     )
index e2bd9d0..f2198ad 100644 (file)
@@ -74,6 +74,15 @@ FurInfixExpression = collections.namedtuple(
     ],
 )
 
+FurIfExpression = collections.namedtuple(
+    'FurIfExpression',
+    [
+        'condition_expression',
+        'if_statement_list',
+        'else_statement_list',
+    ],
+)
+
 def _integer_literal_expression_parser(index, tokens):
     failure = (False, index, None)
 
@@ -315,7 +324,57 @@ def _function_call_expression_parser(index, tokens):
 
     return True, index, function
 
-_expression_parser = _or_level_expression_parser
+def _if_expression_parser(index, tokens):
+    failure = (False, index, None)
+
+    if tokens[index].match == 'if':
+        index += 1
+    else:
+        return failure
+
+    success, index, condition_expression = _or_level_expression_parser(index, tokens)
+
+    if not success:
+        raise Exception('Expected condition after "if" on line {}'.format(tokens[index].line))
+
+    if tokens[index].match == 'do':
+        index += 1
+    else:
+        raise Exception('Expected "do" after "if" on line {}'.format(tokens[index].line))
+
+
+    success, index, if_statement_list = _zero_or_more_parser(tuple, _statement_parser)(index, tokens)
+    _, index, _ = consume_newlines(index, tokens)
+
+    if tokens[index].match == 'else':
+        index += 1
+        success, index, else_statement_list = _zero_or_more_parser(tuple, _statement_parser)(index, tokens)
+        _, index, _ = consume_newlines(index, tokens)
+    else:
+        else_statement_list = ()
+
+    if tokens[index].match == 'end':
+        index += 1
+    else:
+        raise Exception('Expected "end" after "if" on line {}'.format(tokens[index].line))
+
+    return (
+        True,
+        index,
+        FurIfExpression(
+            condition_expression=condition_expression,
+            if_statement_list=if_statement_list,
+            else_statement_list=else_statement_list,
+        ),
+    )
+
+
+
+
+_expression_parser = _or_parser(
+    _or_level_expression_parser,
+    _if_expression_parser, # This should always be at the top level
+)
 
 def _expression_statement_parser(index, tokens):
     failure = (False, index, None)
index 3120fb9..0c60f30 100644 (file)
@@ -5,17 +5,16 @@ Object user${{name}}$implementation(EnvironmentPool* environmentPool, Environmen
 
   Environment* environment = EnvironmentPool_allocate(environmentPool);
   Environment_initialize(environment, parent);
+  Object result = builtin$nil;
 
   {% for argument_name in argument_name_list %}
   Environment_set(environment, "{{ argument_name }}", args[{{ loop.index0 }}]);
   {% endfor %}
 
-  {% for statement in statement_list[:-1] %}
+  {% for statement in statement_list %}
   {{ statement }}
   {% endfor %}
 
-  Object result = {{ statement_list[-1] }}
-
   Environment_setLive(environment, false);
   return result;
 }
index 3410e1b..4ac6289 100644 (file)
@@ -52,7 +52,8 @@ enum Type
   BOOLEAN,
   CLOSURE,
   INTEGER,
-  STRING
+  STRING,
+  VOID
 };
 
 struct Closure;
@@ -79,6 +80,7 @@ struct Object
 
 const Object builtin$true = { BOOLEAN, (Instance)(bool){ true } };
 const Object builtin$false = { BOOLEAN, (Instance)(bool){ false } };
+const Object builtin$nil = { VOID, { 0 } };
 
 struct EnvironmentNode
 {
@@ -137,6 +139,7 @@ void Environment_mark(Environment* self)
       case BOOLEAN:
       case INTEGER:
       case STRING:
+      case VOID:
         break;
 
       case CLOSURE:
@@ -391,6 +394,10 @@ Object builtin$print$implementation(EnvironmentPool* environmentPool, Environmen
         printf("%s", output.instance.string);
         break;
 
+      case VOID:
+        printf("nil");
+        break;
+
       default:
         assert(false);
     }
index 914d05a..f73ddf8 100644 (file)
@@ -32,7 +32,7 @@ def _make_token_matcher(definition):
     return token_matcher
 
 _TOKEN_MATCHERS = [
-    ('keyword',                         r'(def|do|end)(?![a-z_])'),
+    ('keyword',                         r'(def|do|else|end|if)(?![a-z_])'),
     ('open_parenthese',                 r'\('),
     ('close_parenthese',                r'\)'),
     ('comma',                           r','),
index ba10109..0b58907 100644 (file)
@@ -103,8 +103,8 @@ CIfElseStatement = collections.namedtuple(
     'CIfElseStatement',
     [
         'condition_expression',
-        'if_statements',
-        'else_statements',
+        'if_statement_list',
+        'else_statement_list',
     ],
 )
 
@@ -304,8 +304,8 @@ def transform_expression_statement(accumulators, statement):
 def transform_if_else_statement(accumulators, statement):
     return CIfElseStatement(
         condition_expression=transform_expression(accumulators, statement.condition_expression),
-        if_statements=tuple(transform_statement(accumulators, s) for s in statement.if_statements),
-        else_statements=tuple(transform_statement(accumulators, s) for s in statement.else_statements),
+        if_statement_list=tuple(transform_statement(accumulators, s) for s in statement.if_statement_list),
+        else_statement_list=tuple(transform_statement(accumulators, s) for s in statement.else_statement_list),
     )
 
 def transform_array_variable_initialization_statement(accumulators, statement):