add support for non-expression, i.e. full program compositions
also stop renaming function definitions to `func`

Fixes #5
diff --git a/action/__main__.py b/action/__main__.py
index a75de50..ec9839e 100755
--- a/action/__main__.py
+++ b/action/__main__.py
@@ -19,6 +19,9 @@
 
 from composer import deserialize
 
+class My:
+    composition = None
+
 #
 # this is the main method for running the pycomposer as an OpenWhisk action
 #
@@ -28,11 +31,44 @@
 
     if 'composition' in args:
         print('accepting composition as input')
-        print(args['composition'])
+#        print(args['composition'])
         composition = deserialize(args['composition'])
     else:
-        print(args['source'])
-        composition = eval(args['source'])
+        print('accepting source as input')
+#        print(args['source'])
+#        composition = eval(args['source'])
+        code = args['source']
+
+        try:
+            print('trying eval')
+            composition = eval(code)
+            print('eval worked!')
+
+        except SyntaxError as error:
+            # if the code isn't an expression, eval will fail with a syntax error;
+            # admittedly the eval might've failed for a more "true" syntax error, but
+            # the best we can do is hope for the best, and resort to an exec
+            print('eval did not work; falling back to exec')
+
+            name = args['name'] if 'name' in args else 'action'
+            path = f'/tmp/{name}'
+            file = open(path, 'w')
+            file.write(code)
+            file.close()
+
+            file = open(path, 'r')
+
+            try:
+                x = compile(file.read(), path, 'exec')
+                my = My()
+                exec(x, {'my': my, 'composer': composer})  # we use `my` as an outval
+                composition = my.composition
+
+            finally:
+                file.close()
+
+            if composition is None:
+                raise Exception('Source did not produce a composition')
 
     if 'lower' in args:
         res = composer.lower(composition, args['lower'])
@@ -50,8 +86,6 @@
         comp['composition'] = json.loads(str(composer.lower(comp['composition'], compat)))
 
         print('success in encode')
-        print(comp)
-        print(str(comp))
 
         return comp
 #        return { "code": composer.encode(composer.composition(name, composition), args['encode'])['actions'][-1]['action']['exec']['code'] }
diff --git a/src/composer/composer.py b/src/composer/composer.py
index 324694b..57ec5e1 100644
--- a/src/composer/composer.py
+++ b/src/composer/composer.py
@@ -201,8 +201,11 @@
         if isinstance(exc, str):
             if exc.startswith('def'):
                 # standardize function name
-                exc = re.sub(r'def\s+([a-zA-Z_][a-zA-Z_0-9]*)\s*\(', 'def func(', exc)
-                exc = { 'kind': 'python:3', 'code': exc }
+                pattern = re.compile('def\s+([a-zA-Z_][a-zA-Z_0-9]*)\s*\(')
+                match = pattern.match(exc)
+                functionName = match.group(1)
+#                exc = pattern.sub('def func(', exc)
+                exc = { 'kind': 'python:3', 'code': exc, 'functionName': functionName }
             else: # lambda 
                 exc = { 'kind': 'python:3+lambda', 'code': exc }
 
diff --git a/src/composer/conductor.py b/src/composer/conductor.py
index c9fd247..139212d 100644
--- a/src/composer/conductor.py
+++ b/src/composer/conductor.py
@@ -159,7 +159,7 @@
             inspect_errors() # handle error objects when resuming
 
         # run function f on current stack
-        def run(f, kind):
+        def run(f, kind, functionName=None):
             # handle let/mask pairs
             view = []
             n = 0
@@ -194,7 +194,7 @@
             # collapse stack for invocation
             env = reduceRight(lambda acc, cur: update(acc, cur['let']) if 'let' in cur and isinstance(cur['let'], dict) else acc, {}, view)
             if kind == 'python:3':
-                main = '''exec(code + "\\n__out__['value'] = func(env, args)", {'env': env, 'args': args, '__out__':__out__})'''
+                main = '''exec(code + "\\n__out__['value'] = ''' + functionName + '''(env, args)", {'env': env, 'args': args, '__out__':__out__})'''
                 code = f
             else: # lambda
                 main = '''__out__['value'] = code(env, args)'''
@@ -239,7 +239,8 @@
             elif jsonv['type'] == 'function':
                 result = None
                 try:
-                    result = run(jsonv['exec']['code'], jsonv['exec']['kind'])
+                    functionName = jsonv['exec']['functionName'] if 'functionName' in jsonv['exec'] else None
+                    result = run(jsonv['exec']['code'], jsonv['exec']['kind'], functionName)
                 except Exception as error:
                     print(error)
                     result = { 'error': 'An exception was caught at state '+str(current)+' (see log for details)' }