From 5f0f1147b18895e512dc41739979b0fcea21850c Mon Sep 17 00:00:00 2001
From: Reid 'arrdem' McKenzie <me@arrdem.com>
Date: Sat, 21 Aug 2021 17:14:57 -0600
Subject: [PATCH] Get arrays and mappings working

---
 projects/lilith/src/python/lilith/grammar.lark | 15 ++++++++++++---
 projects/lilith/src/python/lilith/parser.py    | 14 ++++++++++----
 projects/lilith/src/python/lilith/repl.py      |  6 ++++--
 projects/lilith/test/python/conftest.py        |  5 +++++
 projects/lilith/test/python/test_parser.py     | 15 +++++++++++++++
 5 files changed, 46 insertions(+), 9 deletions(-)

diff --git a/projects/lilith/src/python/lilith/grammar.lark b/projects/lilith/src/python/lilith/grammar.lark
index 3d88fa1..c6245bc 100644
--- a/projects/lilith/src/python/lilith/grammar.lark
+++ b/projects/lilith/src/python/lilith/grammar.lark
@@ -10,9 +10,18 @@ STRING: /""".*?"""/ | /".*?"/
 int: INT
 float: FLOAT
 number: int | float
-word: WORD ("." WORD)*
+word: "nil" -> nil
+    | "true" -> true
+    | "false" -> false
+    | WORD ("." WORD)* -> word
 string: STRING
-atom: number | word | string
+
+list: "[" (expr ("," expr)*)? "]"
+
+pair: expr ":" expr
+dict: "{" (pair ("," pair)*)? "}"
+
+atom: number | word | string | list | dict
 
 application: word "[" arguments? "]"
 
@@ -20,7 +29,7 @@ expr: application | atom
 
 args: expr ("," args)?
 
-kwargs: expr ":" expr ("," kwargs)?
+kwargs: pair ("," kwargs)*
 
 a_args_kwargs: args ";" kwargs
 a_args: args
diff --git a/projects/lilith/src/python/lilith/parser.py b/projects/lilith/src/python/lilith/parser.py
index fb48391..c6d2e97 100644
--- a/projects/lilith/src/python/lilith/parser.py
+++ b/projects/lilith/src/python/lilith/parser.py
@@ -54,13 +54,16 @@ class TreeToTuples(Transformer):
     def string(self, s):
         return s[1:-1].replace('\\"', '"')
 
-    null = lambda self, _: None
+    nil = lambda self, _: None
     true = lambda self, _: True
     false = lambda self, _: False
     int = v_args(inline=True)(lambda self, x: int(x))
     float = v_args(inline=True)(lambda self, x: float(x))
     number = v_args(inline=True)(lambda self, x: x)
 
+    list = list
+    dict = dict
+
     def word(self, args):
         """args: ['a'] ['a' ['b', 'c', 'd']]"""
         return Symbol(".".join(a.value for a in args))
@@ -82,11 +85,14 @@ class TreeToTuples(Transformer):
             _args = _args + args[1]
         return _args
 
+    def pair(self, args):
+        return (args[0], args[1])
+
     def kwargs(self, args):
         d = {}
-        key, val = args[0:2]
-        if len(args) == 3:
-            d.update(args[2])
+        key, val = args[0]
+        if len(args) == 2:
+            d.update(args[1])
         d[key] = val
         return d
 
diff --git a/projects/lilith/src/python/lilith/repl.py b/projects/lilith/src/python/lilith/repl.py
index bb4ff66..8680146 100644
--- a/projects/lilith/src/python/lilith/repl.py
+++ b/projects/lilith/src/python/lilith/repl.py
@@ -1,5 +1,7 @@
 """A simple Lilith shell."""
 
+import traceback
+
 from lilith.interpreter import Bindings, eval, Runtime
 from lilith.parser import Apply, Args, parse_expr
 from lilith.reader import Module
@@ -49,12 +51,12 @@ if __name__ == "__main__":
         try:
             expr = parse_expr(line)
         except Exception as e:
-            print(e)
+            traceback.print_exc()
             continue
 
         try:
             result = eval(runtime, module, Bindings("__root__", None), expr)
             print_([("class:result", f"⇒ {result!r}")], style=STYLE)
         except Exception as e:
-            print(e)
+            traceback.print_exc()
             continue
diff --git a/projects/lilith/test/python/conftest.py b/projects/lilith/test/python/conftest.py
index 7be6349..6723c47 100644
--- a/projects/lilith/test/python/conftest.py
+++ b/projects/lilith/test/python/conftest.py
@@ -22,6 +22,11 @@ def arguments_grammar():
     return parser_with_transformer(GRAMMAR, "arguments")
 
 
+@pytest.fixture
+def expr_grammar():
+    return parser_with_transformer(GRAMMAR, "expr")
+
+
 @pytest.fixture
 def header_grammar():
     return parser_with_transformer(GRAMMAR, "header")
diff --git a/projects/lilith/test/python/test_parser.py b/projects/lilith/test/python/test_parser.py
index 0ceca97..3b13657 100644
--- a/projects/lilith/test/python/test_parser.py
+++ b/projects/lilith/test/python/test_parser.py
@@ -60,6 +60,21 @@ def test_parse_arguments(arguments_grammar, example, expected):
     assert arguments_grammar.parse(example) == expected
 
 
+@pytest.mark.parametrize(
+    "example, expected",
+    [
+        ("1", 1),
+        ("[1, 2, 3]", [1, 2, 3]),
+        ('{"a": 1}', {"a": 1}),
+        ("true", True),
+        ("false", False),
+        ("nil", None),
+    ],
+)
+def test_parse_expr(expr_grammar, example, expected):
+    assert expr_grammar.parse(example) == expected
+
+
 @pytest.mark.parametrize(
     "example, expected",
     [