Allow users to define new zero-argument functions
authorDavid Kerkeslager <kerkeslager@gmail.com>
Wed, 9 Aug 2017 15:34:44 +0000 (11:34 -0400)
committerDavid Kerkeslager <kerkeslager@gmail.com>
Wed, 9 Aug 2017 15:34:44 +0000 (11:34 -0400)
examples/16_functions.fur [new file with mode: 0644]
examples/16_functions.fur.output.txt [new file with mode: 0644]
generation.py
normalization.py
parsing.py
templates/program.c
tokenization.py
transformation.py

diff --git a/examples/16_functions.fur b/examples/16_functions.fur
new file mode 100644 (file)
index 0000000..5786384
--- /dev/null
@@ -0,0 +1,5 @@
+def get_answer() do
+  42
+end
+
+print(get_answer())
diff --git a/examples/16_functions.fur.output.txt b/examples/16_functions.fur.output.txt
new file mode 100644 (file)
index 0000000..f70d7bb
--- /dev/null
@@ -0,0 +1 @@
+42
\ No newline at end of file
index 6bc2384..c058f91 100644 (file)
@@ -113,9 +113,13 @@ def generate_if_else_statement(statement):
         indent('\n'.join(generate_statement(s) for s in statement.else_statements)),
     )
 
+def generate_function_declaration(statement):
+    return 'Environment_set(environment, "{}", user${});'.format(statement.name, statement.name)
+
 def generate_statement(statement):
     return {
         transformation.CExpressionStatement: generate_expression_statement,
+        transformation.CFunctionDeclaration: generate_function_declaration,
         transformation.CIfElseStatement: generate_if_else_statement,
         transformation.CSymbolAssignmentStatement: generate_symbol_assignment_statement,
         transformation.CArrayVariableInitializationStatement: generate_array_variable_initialization_statement,
@@ -126,8 +130,10 @@ def generate_statement(statement):
 def generate(program):
     template = ENV.get_template('program.c')
     return template.render(
-        builtins=list(sorted(program.builtin_set)),
-        statements=[generate_statement(statement) for statement in program.statements],
+        builtins=tuple(sorted(program.builtin_set)),
+        function_definition_list=program.function_definition_list,
+        generate_statement=generate_statement,
+        statements=program.statements,
         standard_libraries=list(sorted(program.standard_libraries)),
         string_literal_list=program.string_literal_list,
         symbol_list=program.symbol_list,
index a41eb8d..6f27b55 100644 (file)
@@ -1,6 +1,7 @@
 import collections
 
 import parsing
+import util
 
 NormalVariableExpression = collections.namedtuple(
     'NormalVariableExpression',
@@ -75,6 +76,14 @@ NormalIfElseStatement = collections.namedtuple(
     ],
 )
 
+NormalFunctionDefinitionStatement = collections.namedtuple(
+    'NormalFunctionDefinitionStatement',
+    [
+        'name',
+        'statement_list',
+    ],
+)
+
 NormalProgram = collections.namedtuple(
     'NormalProgram',
     [
@@ -82,6 +91,7 @@ NormalProgram = collections.namedtuple(
     ],
 )
 
+# TODO Get rid of this
 def fake_normalization(counter, thing):
     return (counter, (), thing)
 
@@ -294,6 +304,7 @@ def normalize_expression(counter, expression):
 def normalize_expression_statement(counter, statement):
     counter, prestatements, normalized = {
         parsing.FurFunctionCallExpression: normalize_function_call_expression,
+        parsing.FurIntegerLiteralExpression: normalize_expression,
     }[type(statement.expression)](counter, statement.expression)
 
     return (
@@ -302,22 +313,35 @@ def normalize_expression_statement(counter, statement):
         NormalExpressionStatement(expression=normalized),
     )
 
+def normalize_function_definition_statement(counter, statement):
+    return (
+        counter,
+        (),
+        NormalFunctionDefinitionStatement(
+            name=statement.name,
+            statement_list=normalize_statement_list(statement.statement_list),
+        ),
+    )
+
 def normalize_statement(counter, statement):
     return {
+        parsing.FurAssignmentStatement: fake_normalization, # TODO unfake this
         parsing.FurExpressionStatement: normalize_expression_statement,
-        parsing.FurAssignmentStatement: fake_normalization,
+        parsing.FurFunctionDefinitionStatement: normalize_function_definition_statement,
     }[type(statement)](counter, statement)
 
-def normalize(program):
+@util.force_generator(tuple)
+def normalize_statement_list(statement_list):
     counter = 0
-    statement_list = []
 
-    for statement in program.statement_list:
+    for statement in statement_list:
         counter, prestatements, normalized = normalize_statement(counter, statement)
         for s in prestatements:
-            statement_list.append(s)
-        statement_list.append(normalized)
+            yield s
+        yield normalized
+
+def normalize(program):
 
     return NormalProgram(
-        statement_list=statement_list,
+        statement_list=normalize_statement_list(program.statement_list),
     )
index adc96e9..041f3aa 100644 (file)
@@ -213,7 +213,7 @@ def _or_level_expression_parser(index, tokens):
     )(index, tokens)
 
 def _comma_separated_list_parser(index, tokens):
-    failure = (False, index, None)
+    start_index = index
 
     expressions = []
 
@@ -222,7 +222,7 @@ def _comma_separated_list_parser(index, tokens):
     if success:
         expressions.append(expression)
     else:
-        return failure
+        return (True, start_index, ())
 
     while success and index < len(tokens) and tokens[index].type == 'comma':
         success = False
@@ -260,6 +260,14 @@ FurAssignmentStatement = collections.namedtuple(
     ],
 )
 
+FurFunctionDefinitionStatement = collections.namedtuple(
+    'FurFunctionDefinitionStatement',
+    [
+        'name',
+        'statement_list',
+    ],
+)
+
 FurProgram = collections.namedtuple(
     'FurProgram',
     [
@@ -330,6 +338,55 @@ 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,
+        ))
+
+    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, statement_list=statement_list)
+
 def _statement_parser(index, tokens):
     _, index, _ = consume_newlines(index, tokens)
 
@@ -339,6 +396,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):
index 0d6d947..73d9334 100644 (file)
@@ -326,6 +326,23 @@ Object builtin$print$implementation(size_t argc, Object* args)
 Object builtin$print = { CLOSURE, (Instance)builtin$print$implementation };
 {% endif %}
 
+{% for function_definition in function_definition_list %}
+Object user${{function_definition.name}}$implementation(size_t argc, Object* args)
+{
+  Environment* environment = Environment_construct();
+
+  {% for statement in function_definition.statement_list %}
+  {{ generate_statement(statement) }}
+  {% endfor %}
+
+  Object result = {{ generate_statement(function_definition.statement_list[-1]) }}
+  Environment_destruct(environment);
+  return result;
+}
+
+Object user${{function_definition.name}} = { CLOSURE, (Instance)user${{function_definition.name}}$implementation };
+{% endfor %}
+
 int main(int argc, char** argv)
 {
   Environment* environment = Environment_construct();
@@ -336,7 +353,7 @@ int main(int argc, char** argv)
   {% endfor %}
 
   {% for statement in statements %}
-  {{ statement }}
+  {{ generate_statement(statement) }}
   {% endfor %}
 
   Environment_destruct(environment);
index a736912..1a2653e 100644 (file)
@@ -31,13 +31,13 @@ def _make_token_matcher(definition):
 
     return token_matcher
 
-
 _TOKEN_MATCHERS = [
+    ('keyword',                         r'(def|end)(?![a-z_])'),
     ('open_parenthese',                 r'\('),
     ('close_parenthese',                r'\)'),
     ('comma',                           r','),
     ('integer_literal',                 r'\d+'),
-    ('symbol',                          r'[a-z]+'),
+    ('symbol',                          r'[a-z_]+'),
     ('single_quoted_string_literal',    r"'.*?'"),
     ('comparison_level_operator',         r'(<=|>=|==|!=|<|>)'),
     ('assignment_operator',             r'='),
index b636286..2825257 100644 (file)
@@ -114,10 +114,26 @@ CIfElseStatement = collections.namedtuple(
     ],
 )
 
+CFunctionDeclaration = collections.namedtuple(
+    'CFunctionDeclaration',
+    [
+        'name',
+    ],
+)
+
+CFunctionDefinition = collections.namedtuple(
+    'CFunctionDefinition',
+    [
+        'name',
+        'statement_list',
+    ],
+)
+
 CProgram = collections.namedtuple(
     'CProgram',
     [
         'builtin_set',
+        'function_definition_list',
         'statements',
         'standard_libraries',
         'string_literal_list',
@@ -270,18 +286,17 @@ def transform_function_call_expression(accumulators, function_call):
         # TODO Check that the builtin is actually callable
         accumulators.builtin_set.add(function_call.function.value)
 
-        # TODO Use the symbol from SYMBOL LIST
-        return CFunctionCallExpression(
-            name=function_call.function.value,
-            argument_count=function_call.argument_count,
-            argument_items=transform_expression(accumulators, function_call.argument_items),
-        )
-
-    raise Exception()
+    # TODO Use the symbol from SYMBOL LIST
+    return CFunctionCallExpression(
+        name=function_call.function.value,
+        argument_count=function_call.argument_count,
+        argument_items=transform_expression(accumulators, function_call.argument_items),
+    )
 
 def transform_expression_statement(accumulators, statement):
     expression = {
         parsing.FurFunctionCallExpression: transform_function_call_expression,
+        parsing.FurIntegerLiteralExpression: transform_expression,
         normalization.NormalFunctionCallExpression: transform_function_call_expression,
     }[type(statement.expression)](accumulators, statement.expression)
 
@@ -314,13 +329,26 @@ def transform_variable_reassignment_statement(accumulators, statement):
         expression=transform_expression(accumulators, statement.expression),
     )
 
+def transform_function_definition_statement(accumulators, statement):
+    # TODO Allow defining the same function in different contexts
+    if any(fd.name == statement.name for fd in accumulators.function_definition_list):
+        raise Exception('A function with name "{}" already exists'.format(statement.name))
+
+    accumulators.function_definition_list.append(CFunctionDefinition(
+        name=statement.name,
+        statement_list=tuple(transform_statement(accumulators, s) for s in statement.statement_list)
+    ))
+
+    return CFunctionDeclaration(name=statement.name)
+
 def transform_statement(accumulators, statement):
     return {
         parsing.FurAssignmentStatement: transform_symbol_assignment_statement,
         parsing.FurExpressionStatement: transform_expression_statement,
+        normalization.NormalArrayVariableInitializationStatement: transform_array_variable_initialization_statement,
         normalization.NormalExpressionStatement: transform_expression_statement,
+        normalization.NormalFunctionDefinitionStatement: transform_function_definition_statement,
         normalization.NormalIfElseStatement: transform_if_else_statement,
-        normalization.NormalArrayVariableInitializationStatement: transform_array_variable_initialization_statement,
         normalization.NormalVariableInitializationStatement: transform_variable_initialization_statement,
         normalization.NormalVariableReassignmentStatement: transform_variable_reassignment_statement,
     }[type(statement)](accumulators, statement)
@@ -330,6 +358,7 @@ Accumulators = collections.namedtuple(
     'Accumulators',
     [
         'builtin_set',
+        'function_definition_list',
         'symbol_list',
         'string_literal_list',
     ],
@@ -338,6 +367,7 @@ Accumulators = collections.namedtuple(
 def transform(program):
     accumulators = Accumulators(
         builtin_set=set(),
+        function_definition_list=[],
         symbol_list=[],
         string_literal_list=[],
     )
@@ -353,6 +383,7 @@ def transform(program):
 
     return CProgram(
         builtin_set=accumulators.builtin_set,
+        function_definition_list=accumulators.function_definition_list,
         statements=statement_list,
         standard_libraries=standard_library_set,
         string_literal_list=accumulators.string_literal_list,