From dcb644b4eda56accca31792ba4701228d17f6056 Mon Sep 17 00:00:00 2001 From: David Kerkeslager Date: Fri, 21 Aug 2015 21:08:13 -0400 Subject: [PATCH] Defining symbols and retrieving values from them --- integration_tests/0003_define.stt | 3 + integration_tests/0003_define.txt | 1 + stutter.py | 216 ++++++++++++++++++++++++++++-- stutter_test.py | 82 +++++++++++- 4 files changed, 283 insertions(+), 19 deletions(-) create mode 100644 integration_tests/0003_define.stt create mode 100644 integration_tests/0003_define.txt diff --git a/integration_tests/0003_define.stt b/integration_tests/0003_define.stt new file mode 100644 index 0000000..a0d8b77 --- /dev/null +++ b/integration_tests/0003_define.stt @@ -0,0 +1,3 @@ +(define hello-world "Hello, world") +(print hello-world) +0 diff --git a/integration_tests/0003_define.txt b/integration_tests/0003_define.txt new file mode 100644 index 0000000..dbe9dba --- /dev/null +++ b/integration_tests/0003_define.txt @@ -0,0 +1 @@ +Hello, world \ No newline at end of file diff --git a/stutter.py b/stutter.py index 3e4dd2c..7b70dce 100644 --- a/stutter.py +++ b/stutter.py @@ -78,7 +78,7 @@ class Symbol(object): TOKEN = re.compile(r'\s*({})'.format('|'.join('(?P<{}>{})'.format(*token) for token in [ ('open_parenthese', r'\('), ('close_parenthese', r'\)'), - ('identifier', r'[a-z]+'), # We can expand this as needed + ('identifier', r'[a-z\-]+'), # We can expand this as needed ('integer_literal', r'\d+'), ('string_literal', r'"(\\"|[^"])*"'), ('unexpected_character', r'.'), @@ -133,9 +133,9 @@ class CPointerType(CType): self.pointer_to = pointer_to class CArgumentDeclaration(object): - def __init__(self, type, name): - assert isinstance(type, CType) - self.type = type + def __init__(self, _type, name): + assert isinstance(_type, CType) + self._type = _type self.name = name class CExpression(object): @@ -168,6 +168,11 @@ class CVariableExpression(CExpression): assert isinstance(other, CVariableExpression) return self.name == other.name +class CReferenceExpression(CExpression): + def __init__(self, referee): + assert isinstance(referee, CVariableExpression) + self.referee = referee + class CFunctionCallExpression(CExpression): def __init__(self, name, arguments): assert all(isinstance(argument, CExpression) for argument in arguments) @@ -189,6 +194,16 @@ class CReturnStatement(CStatement): def __init__(self, expression): self.expression = expression +class CDefinitionStatement(CStatement): + def __init__(self, _type, name, definition): + assert isinstance(_type, CType) + assert isinstance(name, str) + assert isinstance(definition, CExpression) + + self._type = _type + self.name = name + self.definition = definition + class CFunctionBody(object): def __init__(self, statements): statements = list(statements) @@ -222,7 +237,13 @@ def quote_to_c(s_expression): [CStringLiteralExpression(s_expression)], ) - raise Exception('Not implemented') + if isinstance(s_expression, Symbol): + return CFunctionCallExpression( + 'getSymbol', + [CStringLiteralExpression(s_expression.string)], + ) + + raise Exception('Not implemented for type {}'.format(type(s_expression))) def evaluate_application_arguments_to_c( arguments, @@ -249,7 +270,10 @@ def evaluate_application_to_c( if isinstance(s_expression[0], Symbol): return CFunctionCallExpression( s_expression[0].string, - (evaluate_application_arguments_to_c(s_expression[1:]),), + ( + CReferenceExpression(CVariableExpression('env')), + evaluate_application_arguments_to_c(s_expression[1:]), + ), ) raise Exception('Not implemented') @@ -274,6 +298,11 @@ def evaluate_all_to_c(s_expressions): c_expressions = list(map(evaluate_to_c, s_expressions)) return CFunctionBody(itertools.chain( + [CDefinitionStatement( + CPointerType(CType('Environment')), + 'env', + CVariableExpression('NULL'), + )], map(CExpressionStatement, c_expressions[:-1]), [CReturnStatement(c_expressions[-1])], )) @@ -296,7 +325,10 @@ def generate_type( def generate_argument_declaration(argument_declaration): assert isinstance(argument_declaration, CArgumentDeclaration) - return '{} {}'.format(generate_type(argument_declaration.type), argument_declaration.name) + return '{} {}'.format( + generate_type(argument_declaration._type), + argument_declaration.name, + ) def generate_argument_declaration_list(argument_declarations): return ', '.join(generate_argument_declaration(ad) for ad in argument_declarations) @@ -336,6 +368,10 @@ def generate_variable_expression(expression): assert isinstance(expression, CVariableExpression) return expression.name +def generate_reference_expression(expression): + assert isinstance(expression, CReferenceExpression) + return '&{}'.format(generate_variable_expression(expression.referee)) + def generate_function_call_expression(expression): assert isinstance(expression, CFunctionCallExpression) return '{}({})'.format( @@ -348,6 +384,7 @@ def generate_expression( generate_integer_literal_expression = generate_integer_literal_expression, generate_string_literal_expression = generate_string_literal_expression, generate_variable_expression = generate_variable_expression, + generate_reference_expression = generate_reference_expression, generate_function_call_expression = generate_function_call_expression, ): @@ -360,6 +397,9 @@ def generate_expression( if isinstance(expression, CVariableExpression): return generate_variable_expression(expression) + if isinstance(expression, CReferenceExpression): + return generate_reference_expression(expression) + if isinstance(expression, CFunctionCallExpression): return generate_function_call_expression(expression) @@ -371,10 +411,18 @@ def generate_expression_statement(statement): def generate_return_statement(statement): return 'return {};'.format(generate_expression(statement.expression)) +def generate_definition_statement(statement): + return '{} {} = {};'.format( + generate_type(statement._type), + statement.name, + generate_expression(statement.definition), + ) + def generate_statement( statement, generate_expression_statement = generate_expression_statement, - generate_return_statement = generate_return_statement): + generate_return_statement = generate_return_statement, + generate_definition_statement = generate_definition_statement): if isinstance(statement, CExpressionStatement): return generate_expression_statement(statement) @@ -382,7 +430,10 @@ def generate_statement( if isinstance(statement, CReturnStatement): return generate_return_statement(statement) - raise Exception('Handling for statements of type {} not implemented'.format(type(statement.type))) + if isinstance(statement, CDefinitionStatement): + return generate_definition_statement(statement) + + raise Exception('Handling for statements of type {} not implemented'.format(type(statement))) def generate_function_body(function_body): assert isinstance(function_body, CFunctionBody) @@ -408,8 +459,10 @@ def generate_function_declaration(function_declaration): PROGRAM_TEMPLATE = string.Template( ''' #include +#include #include #include +#include struct Object; typedef struct Object Object; @@ -417,10 +470,35 @@ typedef struct Object Object; enum Type { CELL, - STRING + STRING, + SYMBOL }; typedef enum Type Type; +#define MAX_TYPE_STRING_LENGTH 7 + +void typeToString(Type type, char* target) +{ + switch(type) + { + case CELL: + snprintf(target, MAX_TYPE_STRING_LENGTH, "CELL"); + return; + + case STRING: + snprintf(target, MAX_TYPE_STRING_LENGTH, "STRING"); + return; + + case SYMBOL: + snprintf(target, MAX_TYPE_STRING_LENGTH, "%s", "SYMBOL"); + return; + + default: + fprintf(stderr, "ERROR: Unknown type"); + exit(1); + } +} + struct Cell; typedef struct Cell Cell; struct Cell @@ -429,10 +507,41 @@ struct Cell Object* right; }; +struct Environment; +typedef struct Environment Environment; +struct Environment +{ + char* key; + Object* value; + Environment* next; +}; + +Environment makeEnvironment(char* key, Object* value, Environment* next) +{ + Environment result; + result.key = key; + result.value = value; + result.next = next; + return result; +} + +Environment* makeEnvironmentPointerFromEnvironment(Environment env) +{ + Environment* result = malloc(sizeof(Environment)); + *result = env; + return result; +} + +Environment* makeEnvironmentPointer(char* key, Object* value, Environment* next) +{ + return makeEnvironmentPointerFromEnvironment(makeEnvironment(key, value, next)); +} + union Instance { Cell cell; char* string; + char* symbol; }; typedef union Instance Instance; @@ -450,6 +559,13 @@ Instance makeInstanceFromString(char* string) return result; } +Instance makeInstanceFromSymbol(char* symbol) +{ + Instance result; + result.symbol = symbol; + return result; +} + struct Object { Type type; @@ -474,6 +590,11 @@ Object makeObjectFromString(char* string) return makeObject(STRING, makeInstanceFromString(string)); } +Object makeObjectFromSymbol(char* symbol) +{ + return makeObject(SYMBOL, makeInstanceFromSymbol(symbol)); +} + Object* makeObjectPointerFromObject(Object o) { Object* result = malloc(sizeof(Object)); @@ -491,6 +612,17 @@ Object* makeObjectPointerFromString(char* string) return makeObjectPointerFromObject(makeObjectFromString(string)); } +Object* makeObjectPointerFromSymbol(char* symbol) +{ + return makeObjectPointerFromObject(makeObjectFromSymbol(symbol)); +} + +Object* getSymbol(char* symbol) +{ + // This will not always be how this is implemented + return makeObjectPointerFromSymbol(symbol); +} + Cell makeCell(Object* left, Object* right) { Cell result; @@ -507,11 +639,59 @@ Object* c_cons(Object* left, Object* right) void c_print(Object* stutter_string) { - assert(stutter_string->type == STRING); + if(stutter_string->type != STRING) + { + char typeName[MAX_TYPE_STRING_LENGTH]; + typeToString(stutter_string->type, typeName); + fprintf(stderr, "ERROR: Expected type STRING, got type %s.", typeName); + exit(1); + } + char* c_string = stutter_string->instance.string; printf("%s", c_string); } +bool c_symbol_equal(char* left, char* right) +{ + return strcmp(left, right) == 0; +} + +Object* c_evaluate_symbol(Environment* env, Object* s) +{ + if(env == NULL) + { + fprintf(stderr, "ERROR: symbol %s not found.", s->instance.symbol); + exit(1); + } + + if(c_symbol_equal(env->key, s->instance.symbol)) + { + return env->value; + } + + return c_evaluate_symbol(env->next, s); +} + +Object* c_evaluate(Environment** env, Object* o) +{ + switch(o->type) + { + case STRING: + return o; + + case SYMBOL: + return c_evaluate_symbol(*env, o); + + default: + break; + } + + char typeName[MAX_TYPE_STRING_LENGTH]; + typeToString(o->type, typeName); + fprintf(stderr, "ERROR: Could not evaluate type %s.", typeName); + exit(1); +} + int countArgs(Object* args) { if(args == NULL) return 0; @@ -527,13 +707,23 @@ Object* getArg(int index, Object* args) return getArg(index - 1, args->instance.cell.right); } -void print(Object* args) +void print(Environment** parent, Object* args) { assert(countArgs(args) == 1); - Object* stutter_string = getArg(0, args); + Object* stutter_string = c_evaluate(parent, getArg(0, args)); c_print(stutter_string); } +void define(Environment** parent, Object* args) +{ + assert(countArgs(args) == 2); + Object* name = getArg(0, args); + Object* value = getArg(1, args); + + assert(name->type == SYMBOL); + *parent = makeEnvironmentPointer(name->instance.symbol, value, *parent); +} + int main(int argc, char** argv) { $body diff --git a/stutter_test.py b/stutter_test.py index d950432..12c4a2f 100644 --- a/stutter_test.py +++ b/stutter_test.py @@ -81,6 +81,12 @@ class ParseAllTests(unittest.TestCase): self.assertEqual(expected, actual) + def test_parses_identifiers_with_dashes(self): + expected = [stutter.Symbol('hello-world')] + actual = stutter.parse_all('hello-world') + + self.assertEqual(expected, actual) + def test_parses_strings(self): expected = ['Hello, world'] actual = stutter.parse_all('"Hello, world"') @@ -146,6 +152,17 @@ class QuoteToCTests(unittest.TestCase): self.assertEqual(expected, actual) + def test_quotes_symbols(self): + s = 'symbol' + expected = stutter.CFunctionCallExpression( + 'getSymbol', + [stutter.CStringLiteralExpression(s)], + ) + + actual = stutter.quote_to_c(stutter.Symbol(s)) + + self.assertEqual(expected, actual) + class EvaluateApplicationArgumentsToCTests(unittest.TestCase): def test_evaluates_empty_to_null(self): expected = stutter.CVariableExpression('NULL') @@ -190,8 +207,12 @@ class EvaluateApplicationToCTests(unittest.TestCase): ) self.assertEqual(result.name, name) - self.assertEqual(len(result.arguments), 1) - self.assertIs(result.arguments[0], sentinel) + self.assertEqual(len(result.arguments), 2) + self.assertTrue(isinstance( + result.arguments[0], + stutter.CReferenceExpression, + )) + self.assertIs(result.arguments[1], sentinel) def test_evaluates_function_calls_with_arguments(self): name = 'print' @@ -217,8 +238,12 @@ class EvaluateApplicationToCTests(unittest.TestCase): ) self.assertEqual(result.name, name) - self.assertEqual(len(result.arguments), 1) - self.assertIs(result.arguments[0], sentinel) + self.assertEqual(len(result.arguments), 2) + self.assertTrue(isinstance( + result.arguments[0], + stutter.CReferenceExpression, + )) + self.assertIs(result.arguments[1], sentinel) class EvaluateToCTests(unittest.TestCase): def test_evaluates_integers(self): @@ -258,9 +283,10 @@ class EvaluateAllToCTests(unittest.TestCase): def test_main_contains_expression_statements_followed_by_return_statement(self): result = stutter.evaluate_all_to_c([0,0,0]) - self.assertIsInstance(result.statements[0],stutter.CExpressionStatement) + self.assertIsInstance(result.statements[0],stutter.CDefinitionStatement) self.assertIsInstance(result.statements[1],stutter.CExpressionStatement) - self.assertIsInstance(result.statements[2],stutter.CReturnStatement) + self.assertIsInstance(result.statements[2],stutter.CExpressionStatement) + self.assertIsInstance(result.statements[3],stutter.CReturnStatement) class GeneratePointerTypeTests(unittest.TestCase): def test_basic(self): @@ -331,6 +357,15 @@ class GenerateVariableExpressionTests(unittest.TestCase): self.assertEqual(expected, actual) +class GenerateReferenceExpressionTests(unittest.TestCase): + def test_generates(self): + expected = '&name'; + actual = stutter.generate_reference_expression( + stutter.CReferenceExpression(stutter.CVariableExpression('name')), + ) + + self.assertEqual(expected, actual) + class GenerateFunctionCallExpressionTests(unittest.TestCase): def test_no_arguments(self): expected = 'name()' @@ -389,6 +424,14 @@ class GenerateExpressionTests(unittest.TestCase): self.assertIs(expected, actual) + def test_generates_variable_expression(self): + expected = object() + actual = stutter.generate_expression( + stutter.CReferenceExpression(stutter.CVariableExpression('name')), + generate_reference_expression = lambda x : expected) + + self.assertIs(expected, actual) + def test_generates_function_call_expression(self): expected = object() actual = stutter.generate_expression( @@ -418,6 +461,20 @@ class GenerateStatement(unittest.TestCase): self.assertIs(expected, actual) + def test_generates_definition_statement(self): + definition_statement = stutter.CDefinitionStatement( + stutter.CType('int'), + 'number', + stutter.CIntegerLiteralExpression(0), + ) + + expected = object() + actual = stutter.generate_statement( + definition_statement, + generate_definition_statement = lambda _ : expected) + + self.assertIs(expected, actual) + class GenerateExpressionStatementTests(unittest.TestCase): def test_generates_return_statement(self): expression_statement = stutter.CExpressionStatement(stutter.CIntegerLiteralExpression(0)) @@ -436,6 +493,19 @@ class GenerateReturnStatementTests(unittest.TestCase): self.assertEqual(expected, actual) +class GenerateDefinitionStatementTests(unittest.TestCase): + def test_generates_definition_statement(self): + definition_statement = stutter.CDefinitionStatement( + stutter.CType('int'), + 'number', + stutter.CIntegerLiteralExpression(0), + ) + + expected = 'int number = 0;' + actual = stutter.generate_definition_statement(definition_statement) + + self.assertEqual(expected, actual) + class GenerateFunctionDeclarationTests(unittest.TestCase): def test_basic(self): return_type = stutter.CType('int') -- 2.20.1