Repository: vyperlang/vyper

Issue:
Vyper can't serialize ast for programs that contain decimal literals
### Version Information
* vyper Version (output of `vyper --version`): 0.2.3
* OS: linux
* Python Version (output of `python --version`): 3.8.4

### What's your issue about?
The following program cannot serialize it's AST:
```vyper
var: public(decimal)

@external
def foo(a: decimal):
    self.var += 0.1
```
Gives this error:
```bash
$ vyper -f ast DecimalLiteral.vy 
TypeError: Object of type Decimal is not JSON serializable
```

### How can it be fixed?
When serializing the `ast.Decimal` node, the `Decimal.value` attribute of the node is a `decimal.Decimal` type, which is not serializable to JSON. Instead, it should be converted to an engineering string via `decimal.Decimal.to_eng_string()`. Unsure if it's possible to convert _back_ from this JSON format, that should be considered in a possible solution.

I overrided with this fix and it did the trick:
```diff
diff --git a/vyper/ast/nodes.py b/vyper/ast/nodes.py
index 6400d721..bdd642f3 100644
--- a/vyper/ast/nodes.py
+++ b/vyper/ast/nodes.py
@@ -701,6 +701,11 @@ class Decimal(Num):
             raise InvalidLiteral("Vyper supports a maximum of ten decimal points", self)
         super().validate()
 
+    def to_dict(self):
+        d = super().to_dict()
+        d["value"] = d["value"].to_eng_string()
+        return d
+
 
 class Hex(Num):
     """
```


Discussion:

Resolving patch:
diff --git a/vyper/ast/nodes.py b/vyper/ast/nodes.py
--- a/vyper/ast/nodes.py
+++ b/vyper/ast/nodes.py
@@ -696,6 +696,16 @@ class Decimal(Num):
 
     __slots__ = ()
 
+    def __init__(self, parent: Optional["VyperNode"] = None, **kwargs: dict):
+        super().__init__(parent, **kwargs)
+        if not isinstance(self.value, decimal.Decimal):
+            self.value = decimal.Decimal(self.value)
+
+    def to_dict(self):
+        ast_dict = super().to_dict()
+        ast_dict["value"] = self.node_source_code
+        return ast_dict
+
     def validate(self):
         if self.value.as_tuple().exponent < -MAX_DECIMAL_PLACES:
             raise InvalidLiteral("Vyper supports a maximum of ten decimal points", self)
@@ -738,6 +748,16 @@ class Bytes(Constant):
     __slots__ = ()
     _translated_fields = {"s": "value"}
 
+    def __init__(self, parent: Optional["VyperNode"] = None, **kwargs: dict):
+        super().__init__(parent, **kwargs)
+        if isinstance(self.value, str):
+            self.value = self.value.encode("utf8")
+
+    def to_dict(self):
+        ast_dict = super().to_dict()
+        ast_dict["value"] = self.value.decode("utf8")
+        return ast_dict
+
     @property
     def s(self):
         return self.value


Test patch:
diff --git a/tests/parser/ast_utils/test_ast_dict.py b/tests/parser/ast_utils/test_ast_dict.py
--- a/tests/parser/ast_utils/test_ast_dict.py
+++ b/tests/parser/ast_utils/test_ast_dict.py
@@ -1,3 +1,5 @@
+import json
+
 from vyper import compiler
 from vyper.ast.utils import ast_to_dict, dict_to_ast, parse_to_ast
 
@@ -78,15 +80,20 @@ def test_dict_to_ast():
 def test() -> int128:
     a: uint256 = 100
     b: int128 = -22
-    c: decimal = 3.31337
+    c: decimal = -3.3133700
     d: Bytes[11] = b"oh hai mark"
-    e: address = 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
-    f: bool = False
+    e: Bytes[1] = 0b01010101
+    f: Bytes[88] = b"\x01\x70"
+    g: String[100] = "  baka baka   "
+    h: address = 0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef
+    i: bool = False
     return 123
     """
 
     original_ast = parse_to_ast(code)
     out_dict = ast_to_dict(original_ast)
-    new_ast = dict_to_ast(out_dict)
+    out_json = json.dumps(out_dict)
+    new_dict = json.loads(out_json)
+    new_ast = dict_to_ast(new_dict)
 
     assert new_ast == original_ast