Repository: vyperlang/vyper

Issue:
Expose LLL IR
The current compiler `v0.0.4` doesn't expose the LLL IR as it's own target. An implication of this is that you can't author LLL and then generate bytecode in an easy manner.

It would be ideal to eventually expose the LLL as a separate module that the vyper frontend (and any other frontend) could consume.


Discussion:
Important for Casper contract to remove it's reliance upon the Serpent compiler.

https://github.com/ethereum/casper/issues/69
https://github.com/ethereum/casper/issues/73
`sighasher` looks like something that might be of more general utility. is that true?

Perhaps we can implement some of these functions as vyper primitives, if that makes sense. Or the portions of them which might make sense as primitives which allows you to compose larger functions out of those new primitives.

What portion of those casper contracts cannot be implemented using Vyper?
I do think we absolutely need a dedicated LLL compiler. This should be a separate EIP, shouldn't it?
@daniel-jozsef for the time being, I will create `vyper-lll` command here.

@fubuloubu @ralexstokes 
I am thinking of going full parethesis for the files, having a json format will just be very nasty.
I will make to build in expression names of `main` and `init`.
lisp away!
yeah -- the LLL source should just be a bunch of nested parens :)

Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -37,6 +37,7 @@
     scripts=[
         'bin/vyper',
         'bin/vyper-serve',
+        'bin/vyper-lll'
     ],
     classifiers=[
         'Intended Audience :: Developers',
diff --git a/vyper/compiler.py b/vyper/compiler.py
--- a/vyper/compiler.py
+++ b/vyper/compiler.py
@@ -1,6 +1,6 @@
 from vyper.parser import parser
-from . import compile_lll
-from . import optimizer
+from vyper import compile_lll
+from vyper import optimizer
 
 
 def compile(code, *args, **kwargs):
diff --git a/vyper/parser/s_expressions.py b/vyper/parser/s_expressions.py
new file mode 100755
--- /dev/null
+++ b/vyper/parser/s_expressions.py
@@ -0,0 +1,40 @@
+# Adapted from https://en.wikipedia.org/wiki/S-expression#Parsing
+
+
+def parse_literal(word):
+    try:
+        return int(word)
+    except ValueError:
+        return word
+
+
+def parse_s_exp(string):
+    sexp = [[]]
+    word = ''
+    in_str = False
+    in_comment = False
+    for char in string:
+        if in_comment:
+            if char is '\n':  # comment ends at the end of a line
+                in_comment = False
+            continue
+        if char is ';':  # start of comment
+            in_comment = True
+            continue
+        if char is '(' and not in_str:
+            sexp.append([])
+        elif char is ')' and not in_str:
+            if word:
+                sexp[-1].append(parse_literal(word))
+                word = ''
+            temp = sexp.pop()
+            sexp[-1].append(temp)
+        elif char in (' ', '\n', '\t') and not in_str:
+            if word:
+                sexp[-1].append(parse_literal(word))
+                word = ''
+        elif char is '\"':
+            in_str = not in_str
+        else:
+            word += char
+    return sexp[0]


Test patch:
diff --git a/tests/compiler/LLL/test_compile_lll.py b/tests/compiler/LLL/test_compile_lll.py
--- a/tests/compiler/LLL/test_compile_lll.py
+++ b/tests/compiler/LLL/test_compile_lll.py
@@ -1,6 +1,7 @@
 import pytest
 
 from vyper.parser.parser import LLLnode
+from vyper.parser.s_expressions import parse_s_exp
 
 
 fail_list = [
@@ -35,3 +36,38 @@ def test_lll_compile_fail(bad_lll, get_contract_from_lll, assert_compile_failed)
 @pytest.mark.parametrize('good_lll', valid_list)
 def test_compile_lll_good(good_lll, get_contract_from_lll):
     get_contract_from_lll(LLLnode.from_list(good_lll))
+
+
+def test_lll_from_s_expression(get_contract_from_lll):
+    code = """
+(seq
+  (return
+    0
+    (lll ; just return 32 byte of calldata back
+      (seq
+          (calldatacopy 0 4 32)
+          (return 0 32)
+          stop
+        )
+      0)))
+    """
+    abi = [{
+        "name": "test",
+        "outputs": [{
+            "type": "int128",
+            "name": "out"
+        }],
+        "inputs": [{
+            "type": "int128",
+            "name": "a"
+        }],
+        "constant": False,
+        "payable": False,
+        "type": "function",
+        "gas": 394
+    }]
+
+    s_expressions = parse_s_exp(code)
+    lll = LLLnode.from_list(s_expressions[0])
+    c = get_contract_from_lll(lll, abi=abi)
+    assert c.test(-123456) == -123456
diff --git a/tests/conftest.py b/tests/conftest.py
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -169,7 +169,7 @@ def get_contract_from_lll(w3):
     def lll_compiler(lll, *args, **kwargs):
         lll = optimizer.optimize(LLLnode.from_list(lll))
         bytecode = compile_lll.assembly_to_evm(compile_lll.compile_to_assembly(lll))
-        abi = []
+        abi = kwargs.get('abi') or []
         contract = w3.eth.contract(bytecode=bytecode, abi=abi)
         deploy_transaction = {
             'data': contract._encode_constructor_data(args, kwargs)