From a96e96f3f783930707122f691cd6a08a90416a74 Mon Sep 17 00:00:00 2001 From: David Kerkeslager Date: Thu, 17 Aug 2017 11:33:33 -0400 Subject: [PATCH] Added if expression statements --- examples/25_if_statements.fur | 14 ++ examples/25_if_statements.fur.output.txt | 2 + generation.py | 28 ++-- normalization.py | 177 ++++++++++++++++++----- parsing.py | 61 +++++++- templates/function_definition.c | 5 +- templates/program.c | 9 +- tokenization.py | 2 +- transformation.py | 8 +- 9 files changed, 244 insertions(+), 62 deletions(-) create mode 100644 examples/25_if_statements.fur create mode 100644 examples/25_if_statements.fur.output.txt diff --git a/examples/25_if_statements.fur b/examples/25_if_statements.fur new file mode 100644 index 0000000..4695cee --- /dev/null +++ b/examples/25_if_statements.fur @@ -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 index 0000000..f48e806 --- /dev/null +++ b/examples/25_if_statements.fur.output.txt @@ -0,0 +1,2 @@ +Hello, world +Goodnight, moon diff --git a/generation.py b/generation.py index 55b3ade..4fe8de4 100644 --- a/generation.py +++ b/generation.py @@ -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) diff --git a/normalization.py b/normalization.py index ff2e327..c96e455 100644 --- a/normalization.py +++ b/normalization.py @@ -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, ) diff --git a/parsing.py b/parsing.py index e2bd9d0..f2198ad 100644 --- a/parsing.py +++ b/parsing.py @@ -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) diff --git a/templates/function_definition.c b/templates/function_definition.c index 3120fb9..0c60f30 100644 --- a/templates/function_definition.c +++ b/templates/function_definition.c @@ -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; } diff --git a/templates/program.c b/templates/program.c index 3410e1b..4ac6289 100644 --- a/templates/program.c +++ b/templates/program.c @@ -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); } diff --git a/tokenization.py b/tokenization.py index 914d05a..f73ddf8 100644 --- a/tokenization.py +++ b/tokenization.py @@ -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','), diff --git a/transformation.py b/transformation.py index ba10109..0b58907 100644 --- a/transformation.py +++ b/transformation.py @@ -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): -- 2.20.1