Defining symbols and retrieving values from them
authorDavid Kerkeslager <kerkeslager@gmail.com>
Sat, 22 Aug 2015 01:08:13 +0000 (21:08 -0400)
committerDavid Kerkeslager <kerkeslager@gmail.com>
Tue, 1 Sep 2015 02:28:47 +0000 (22:28 -0400)
integration_tests/0003_define.stt [new file with mode: 0644]
integration_tests/0003_define.txt [new file with mode: 0644]
stutter.py
stutter_test.py

diff --git a/integration_tests/0003_define.stt b/integration_tests/0003_define.stt
new file mode 100644 (file)
index 0000000..a0d8b77
--- /dev/null
@@ -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 (file)
index 0000000..dbe9dba
--- /dev/null
@@ -0,0 +1 @@
+Hello, world
\ No newline at end of file
index 3e4dd2c..7b70dce 100644 (file)
@@ -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 <assert.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
 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
index d950432..12c4a2f 100644 (file)
@@ -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')