From 151f60b119247efb1bcf05a664f4324b71fac782 Mon Sep 17 00:00:00 2001 From: David Kerkeslager Date: Thu, 24 Aug 2017 19:55:20 -0400 Subject: [PATCH] Add a string concatenation operator --- examples/27_string_concatenation.fur | 1 + .../27_string_concatenation.fur.output.txt | 1 + generation.py | 8 +- normalization.py | 21 ++- templates/program.c | 126 ++++++++++++++---- tokenization.py | 2 +- transformation.py | 26 +++- 7 files changed, 144 insertions(+), 41 deletions(-) create mode 100644 examples/27_string_concatenation.fur create mode 100644 examples/27_string_concatenation.fur.output.txt diff --git a/examples/27_string_concatenation.fur b/examples/27_string_concatenation.fur new file mode 100644 index 0000000..56bad3c --- /dev/null +++ b/examples/27_string_concatenation.fur @@ -0,0 +1 @@ +print('Hello' ++ ', ' ++ 'world' ++ '\n') diff --git a/examples/27_string_concatenation.fur.output.txt b/examples/27_string_concatenation.fur.output.txt new file mode 100644 index 0000000..a5c1966 --- /dev/null +++ b/examples/27_string_concatenation.fur.output.txt @@ -0,0 +1 @@ +Hello, world diff --git a/generation.py b/generation.py index b060376..bc2e2c1 100644 --- a/generation.py +++ b/generation.py @@ -12,7 +12,10 @@ def generate_integer_literal(c_integer_literal): return 'integerLiteral({})'.format(c_integer_literal.value) def generate_string_literal(c_string_literal): - return 'stringLiteral(STRING_LITERAL_LIST[{}])'.format(c_string_literal.index) + return 'stringLiteral(STRING_LITERAL_LIST[{}] /* string: {} */)'.format( + c_string_literal.index, + repr(c_string_literal.value), + ) def generate_symbol_expression(symbol_expression): return 'Environment_get(environment, SYMBOL_LIST[{}] /* symbol: {} */)'.format( @@ -72,8 +75,7 @@ def generate_function_call(function_call): ) def generate_expression_statement(statement): - # TODO Do we need to garbage collect the results of arbitrary statements? - return '{};'.format(generate_expression(statement.expression)) + return 'Object_deinitialize(&({}));'.format(generate_expression(statement.expression)) def generate_symbol_assignment_statement(statement): return 'Environment_set(environment, SYMBOL_LIST[{}] /* symbol: {} */, {});'.format( diff --git a/normalization.py b/normalization.py index ca6f57f..fbe3d04 100644 --- a/normalization.py +++ b/normalization.py @@ -293,14 +293,23 @@ def normalize_function_call_expression(counter, expression): function_expression = NormalVariableExpression(variable=function_variable) counter += 1 + result_variable = '${}'.format(counter) + + prestatements.append( + NormalVariableInitializationStatement( + variable=result_variable, + expression=NormalFunctionCallExpression( + function_expression=function_expression, + argument_count=len(arguments), + argument_items=NormalVariableExpression(variable=arguments_variable), + ), + ) + ) + return ( - counter, + counter + 1, tuple(prestatements), - NormalFunctionCallExpression( - function_expression=function_expression, - argument_count=len(arguments), - argument_items=NormalVariableExpression(variable=arguments_variable), - ), + NormalVariableExpression(variable=result_variable), ) def normalize_basic_infix_operation(counter, expression): diff --git a/templates/program.c b/templates/program.c index 1766967..25bb413 100644 --- a/templates/program.c +++ b/templates/program.c @@ -53,7 +53,8 @@ enum Type CLOSURE, INTEGER, LIST, - STRING, + STRING_CONCATENATION, + STRING_LITERAL, VOID }; @@ -74,13 +75,17 @@ struct List Object* items; }; +struct StringConcatenation; +typedef struct StringConcatenation StringConcatenation; + union Instance { bool boolean; Closure closure; int32_t integer; List list; - const char* string; + StringConcatenation* string_concatenation; + const char* string_literal; }; struct Object @@ -93,6 +98,13 @@ const Object builtin$true = { BOOLEAN, (Instance)(bool){ true } }; const Object builtin$false = { BOOLEAN, (Instance)(bool){ false } }; const Object builtin$nil = { VOID, { 0 } }; +struct StringConcatenation +{ + size_t referenceCount; + Object left; + Object right; +}; + Object List_construct(size_t allocate) { Object* items = malloc(sizeof(Object) * allocate); @@ -151,30 +163,52 @@ void Environment_initialize(Environment* self, Environment* parent) self->live = true; } -void Environment_deinitialize(Environment* self) +void Object_deinitialize(Object* self) { - EnvironmentNode* next; - for(EnvironmentNode* node = self->root; node != NULL; node = next) + switch(self->type) { - next = node->next; + case BOOLEAN: + break; + case CLOSURE: + break; + case INTEGER: + break; + case STRING_LITERAL: + break; + case VOID: + break; + + case LIST: + for(size_t i = 0; i < self->instance.list.length; i++) { + Object_deinitialize(&(self->instance.list.items[i])); + } - switch(node->value.type) - { - case BOOLEAN: - case CLOSURE: - case INTEGER: - case STRING: - case VOID: - break; + free(self->instance.list.items); + break; - case LIST: - free(node->value.instance.list.items); - break; + case STRING_CONCATENATION: + self->instance.string_concatenation->referenceCount--; - default: - assert(false); - } + if(self->instance.string_concatenation->referenceCount == 0) + { + Object_deinitialize(&(self->instance.string_concatenation->left)); + Object_deinitialize(&(self->instance.string_concatenation->right)); + free(self->instance.string_concatenation); + } + break; + default: + assert(false); + } +} + +void Environment_deinitialize(Environment* self) +{ + EnvironmentNode* next; + for(EnvironmentNode* node = self->root; node != NULL; node = next) + { + next = node->next; + Object_deinitialize(&(node->value)); free(node); } } @@ -199,7 +233,7 @@ void Environment_mark(Environment* self) { case BOOLEAN: case INTEGER: - case STRING: + case STRING_LITERAL: case VOID: break; @@ -380,8 +414,8 @@ Object integerLiteral(int32_t literal) Object stringLiteral(const char* literal) { Object result; - result.type = STRING; - result.instance.string = literal; + result.type = STRING_LITERAL; + result.instance.string_literal = literal; return result; } @@ -396,6 +430,42 @@ Object operator$negate(Object input) return result; } +// TODO Make this conditionally added +Object operator$concatenate(Object left, Object right) +{ + switch(left.type) { + case STRING_CONCATENATION: + left.instance.string_concatenation->referenceCount++; + break; + + case STRING_LITERAL: + break; + + default: + assert(false); + } + + switch(right.type) { + case STRING_CONCATENATION: + right.instance.string_concatenation->referenceCount++; + break; + + case STRING_LITERAL: + break; + + default: + assert(false); + } + + StringConcatenation* concatenation = malloc(sizeof(StringConcatenation)); + concatenation->referenceCount = 1; + concatenation->left = left; + concatenation->right = right; + + Object result = { STRING_CONCATENATION, (Instance)concatenation }; + return result; +} + {% for id in infix_declarations %} Object operator${{ id.name }}(Object left, Object right) { @@ -450,9 +520,14 @@ Object builtin$print$implementation(EnvironmentPool* environmentPool, Environmen printf("%" PRId32, output.instance.integer); break; - case STRING: + case STRING_CONCATENATION: + builtin$print$implementation(NULL, NULL, 1, &(output.instance.string_concatenation->left)); + builtin$print$implementation(NULL, NULL, 1, &(output.instance.string_concatenation->right)); + break; + + case STRING_LITERAL: // Using fwrite instead of printf to handle size_t length - printf("%s", output.instance.string); + printf("%s", output.instance.string_literal); break; case VOID: @@ -462,6 +537,7 @@ Object builtin$print$implementation(EnvironmentPool* environmentPool, Environmen default: assert(false); } + Object_deinitialize(&output); } // TODO Return something better diff --git a/tokenization.py b/tokenization.py index e12b8ec..33f4331 100644 --- a/tokenization.py +++ b/tokenization.py @@ -43,7 +43,7 @@ _TOKEN_MATCHERS = [ ('single_quoted_string_literal', r"'.*?'"), ('comparison_level_operator', r'(<=|>=|==|!=|<|>)'), ('assignment_operator', r'='), - ('addition_level_operator', r'(\+|-)'), + ('addition_level_operator', r'(\+\+|\+|-)'), ('multiplication_level_operator', r'(\*|//|%)'), ('newline', r'\n'), ] diff --git a/transformation.py b/transformation.py index 51b0244..e06bcf0 100644 --- a/transformation.py +++ b/transformation.py @@ -140,6 +140,7 @@ CProgram = collections.namedtuple( ) BUILTINS = { + 'concatenate': [], 'false': [], 'pow': ['math.h'], 'print': ['stdio.h'], @@ -185,7 +186,11 @@ CInfixDeclaration = collections.namedtuple( ], ) -INFIX_OPERATOR_TO_DECLARATION = { +FUR_INFIX_OPERATOR_TO_C_FUNCTION = { + '++': 'concatenate', +} + +FUR_INFIX_OPERATOR_TO_C_INFIX_OPERATOR = { '+': CInfixDeclaration(name='add', in_type='integer', out_type='integer', operator='+'), '-': CInfixDeclaration(name='subtract', in_type='integer', out_type='integer', operator='-'), '*': CInfixDeclaration(name='multiply', in_type='integer', out_type='integer', operator='*'), @@ -202,7 +207,7 @@ INFIX_OPERATOR_TO_DECLARATION = { } def transform_comparison_level_expression(accumulators, expression): - accumulators.operator_set.add(INFIX_OPERATOR_TO_DECLARATION[expression.operator]) + accumulators.operator_set.add(FUR_INFIX_OPERATOR_TO_C_INFIX_OPERATOR[expression.operator]) # Transform expressions like 1 < 2 < 3 into expressions like 1 < 2 && 2 < 3 if isinstance(expression.left, parsing.FurInfixExpression) and expression.left.order == 'comparison_level': @@ -223,26 +228,35 @@ def transform_comparison_level_expression(accumulators, expression): name='and', left=left, right=CFunctionCallForFurInfixOperator( - name=INFIX_OPERATOR_TO_DECLARATION[expression.operator].name, + name=FUR_INFIX_OPERATOR_TO_C_INFIX_OPERATOR[expression.operator].name, left=middle, right=right, ), ) return CFunctionCallForFurInfixOperator( - name=INFIX_OPERATOR_TO_DECLARATION[expression.operator].name, + name=FUR_INFIX_OPERATOR_TO_C_INFIX_OPERATOR[expression.operator].name, left=transform_expression(accumulators, expression.left), right=transform_expression(accumulators, expression.right), ) +def transform_infix_operator_without_c_equivalent(accumulators, expression): + return CFunctionCallForFurInfixOperator( + name='concatenate', + left=transform_expression(accumulators, expression.left), + right=transform_expression(accumulators, expression.right), + ) def transform_infix_expression(accumulators, expression): + if expression.operator in FUR_INFIX_OPERATOR_TO_C_FUNCTION: + return transform_infix_operator_without_c_equivalent(accumulators, expression) + if expression.order == 'comparison_level': return transform_comparison_level_expression(accumulators, expression) - accumulators.operator_set.add(INFIX_OPERATOR_TO_DECLARATION[expression.operator]) + accumulators.operator_set.add(FUR_INFIX_OPERATOR_TO_C_INFIX_OPERATOR[expression.operator]) return CFunctionCallForFurInfixOperator( - name=INFIX_OPERATOR_TO_DECLARATION[expression.operator].name, + name=FUR_INFIX_OPERATOR_TO_C_INFIX_OPERATOR[expression.operator].name, left=transform_expression(accumulators, expression.left), right=transform_expression(accumulators, expression.right), ) -- 2.20.1