diff --git a/projects/shogoth/src/python/shogoth/parser/impl.py b/projects/shogoth/src/python/shogoth/parser/impl.py
index 5945007..693105d 100644
--- a/projects/shogoth/src/python/shogoth/parser/impl.py
+++ b/projects/shogoth/src/python/shogoth/parser/impl.py
@@ -15,22 +15,19 @@ mapping: "{" kv* "}"
 kv: expr expr
 quote: "'" expr
 
-atom: KEYWORD | PATTERN | INT | FLOAT | STRING | true | false | nil | SYMBOL
+atom: KEYWORD | PATTERN | INT | FLOAT | STRING | TRUE | FALSE | NIL | SYMBOL
 KEYWORD: ":" SYMBOL
 SYMBOL: /[^(){}#'"\s]+/
 
+// FIXME: These two don't deal with escapes correctly at all
 STRING: /".*?"/
 PATTERN: /\/.*?\//
 
-true: TRUE
 TRUE: /true/
-false: FALSE
 FALSE: /false/
-nil: NIL
 NIL: /nil/
 INT: /[+-]?[0-9]+/
 
-%import common.WORD   // imports from terminal library
 %import common.FLOAT  // float
 %ignore " "           // Disregard spaces in text
 %ignore ","           // Treat commas as whitespace
@@ -39,6 +36,6 @@ INT: /[+-]?[0-9]+/
 
 
 def parse(input: str) -> Any:
-    """Parse a string using the shogoth (lisp) gramar, returning an unmodified tree."""
+    '''Parse a string using the shogoth (lisp) gramar, returning an unmodified tree.'''
 
     return PARSER.parse(input)
diff --git a/projects/shogoth/src/python/shogoth/reader/__init__.py b/projects/shogoth/src/python/shogoth/reader/__init__.py
index a2bc282..0f5eb24 100644
--- a/projects/shogoth/src/python/shogoth/reader/__init__.py
+++ b/projects/shogoth/src/python/shogoth/reader/__init__.py
@@ -2,4 +2,5 @@
 
 from .impl import Reader
 
+
 __all__ = ["Reader"]
diff --git a/projects/shogoth/src/python/shogoth/reader/impl.py b/projects/shogoth/src/python/shogoth/reader/impl.py
index a4b72f6..7b465c6 100644
--- a/projects/shogoth/src/python/shogoth/reader/impl.py
+++ b/projects/shogoth/src/python/shogoth/reader/impl.py
@@ -1,11 +1,19 @@
 """The shogoth reader."""
 
 
+import re
 from typing import Any
-from shogoth.parser import parse
 
-from lark import Tree, Token
-from lark import Lark, Transformer, v_args
+from lark import (
+    Lark,
+    Token,
+    Transformer,
+    Tree,
+    v_args,
+)
+from shogoth.parser import parse
+from shogoth.types import Keyword, Symbol
+
 
 # Monkeypatching for py3.10 matching
 Tree.__match_args__ = ("data", "children")
@@ -22,41 +30,57 @@ class Reader(object):
         return parse(buffer)
 
     def symbol(self, x):
-        return ["read-eval", ["symbol", x]]
+        return Symbol(x)
 
     def keyword(self, x):
-        return ["read-eval", ["keyword", x]]
+        return Keyword(x)
+
+    def pattern(self, x):
+        return re.compile(x[1:-1].replace("\\/", "/"))
 
     def _read(self, tree: Tree) -> Any:
         match tree:
-          case Tree(Token('RULE', 'expr'), children):
+          case Tree(Token("RULE", "expr"), children):
             return self._read(children[0])
 
-          case Tree(Token('RULE', 'plist'), children):
+          case Tree(Token("RULE", "plist"), children):
             return [self._read(c) for c in children]
 
-          case Tree(Token('RULE', 'blist'), children):
+          case Tree(Token("RULE", "blist"), children):
             return [self._read(c) for c in children]
 
-          case Tree(Token('RULE', 'mapping'), children):
+          case Tree(Token("RULE", "mapping"), children):
             return dict(self._read(c) for c in children)
 
-          case Tree(Token('RULE', 'kv'), [k, v]):
+          case Tree(Token("RULE", "kv"), [k, v]):
             return (self._read(k), self._read(v))
 
-          case Tree(Token('RULE', 'atom'), [a]):
+          case Tree(Token("RULE", "atom"), [a]):
             return self._read(a)
 
-          case Token('INT', x):
+          case Token("INT", x):
             return int(x)
 
-          case Token('FLOAT', x):
+          case Token("FLOAT", x):
             return float(x)
 
-          case Token('KEYWORD', x):
+          case Token("KEYWORD", x):
             return self.keyword(x)
 
-          case Token('SYMBOL', x):
+          case Token("PATTERN", x):
+            return self.pattern(x)
+
+          case Token("TRUE", _):
+            return True
+
+          case Token("FALSE", _):
+            return False
+
+          case Token("NIL", _):
+            return None
+
+          # Symbol is very much the catch-all of the grammar
+          case Token("SYMBOL", x):
             return self.symbol(x)
 
           case _:
diff --git a/projects/shogoth/src/python/shogoth/repl/__main__.py b/projects/shogoth/src/python/shogoth/repl/__main__.py
index f1e668b..c5e7305 100644
--- a/projects/shogoth/src/python/shogoth/repl/__main__.py
+++ b/projects/shogoth/src/python/shogoth/repl/__main__.py
@@ -1,8 +1,6 @@
 """A testing REPL."""
 
-import sys
 
-from shogoth.parser import parse
 from shogoth.reader import Reader
 
 
diff --git a/projects/shogoth/src/python/shogoth/types/__init__.py b/projects/shogoth/src/python/shogoth/types/__init__.py
new file mode 100644
index 0000000..a4e1c4b
--- /dev/null
+++ b/projects/shogoth/src/python/shogoth/types/__init__.py
@@ -0,0 +1,7 @@
+"""The public interface for shogoth's baked-in types."""
+
+from .keyword import Keyword
+from .symbol import Symbol
+
+
+__all__ = ["Keyword", "Symbol"]
diff --git a/projects/shogoth/src/python/shogoth/types/keyword.py b/projects/shogoth/src/python/shogoth/types/keyword.py
new file mode 100644
index 0000000..da154b2
--- /dev/null
+++ b/projects/shogoth/src/python/shogoth/types/keyword.py
@@ -0,0 +1,5 @@
+from .symbol import Symbol
+
+
+class Keyword(Symbol):
+    pass
diff --git a/projects/shogoth/src/python/shogoth/types/symbol.py b/projects/shogoth/src/python/shogoth/types/symbol.py
new file mode 100644
index 0000000..19b3709
--- /dev/null
+++ b/projects/shogoth/src/python/shogoth/types/symbol.py
@@ -0,0 +1,5 @@
+from typing import NamedTuple
+
+
+class Symbol(NamedTuple):
+    name: str