diff --git a/.idea/desolation.iml b/.idea/desolation.iml
index c254557e13fb4b03b0be7bf124f66ba797513a94..0b461e8a18575ad8acd01af5f6fdf17aa90699cb 100644
--- a/.idea/desolation.iml
+++ b/.idea/desolation.iml
@@ -2,8 +2,11 @@
 <module type="CPP_MODULE" version="4">
   <component name="NewModuleRootManager">
     <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/desolation-vm/src" isTestSource="false" />
       <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="false" />
       <excludeFolder url="file://$MODULE_DIR$/target" />
+      <excludeFolder url="file://$MODULE_DIR$/desolation-vm/target" />
     </content>
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
diff --git a/.idea/jpa-buddy.xml b/.idea/jpa-buddy.xml
new file mode 100644
index 0000000000000000000000000000000000000000..966d5f56a99523a01d4ba0a48fa95c4a881d44ac
--- /dev/null
+++ b/.idea/jpa-buddy.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="JpaBuddyIdeaProjectConfig">
+    <option name="renamerInitialized" value="true" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000000000000000000000000000000000000..59780fefca7703166dd7ca621588d13674222617
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectType">
+    <option name="id" value="jpab" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
index 9388bbe9cc5ba9e1d79ca107b13940b96a41d90e..9de420cd63d81b6868b116410cd3f44d8e9e65fc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,4 +5,20 @@ edition = "2021"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
+[profile.dev]
+incremental = true
+
+[profile.test]
+incremental = true
+
 [dependencies]
+enum_dispatch = "0.3.8"
+serde = { version = "1.0.147", features = ["derive"] }
+regex = "1.6.0"
+anyhow = "1.0.66"
+thiserror = "1.0.37"
+log = "0.4.17"
+env_logger = "0.9.0"
+pretty_env_logger = "0.4.0"
+sensible-env-logger = "0.3.1"
+lazy_static = "1.4.0"
\ No newline at end of file
diff --git a/desolation-vm/Cargo.lock b/desolation-vm/Cargo.lock
new file mode 100644
index 0000000000000000000000000000000000000000..be20905be2ee76657b954e61e96d120c092b5c44
--- /dev/null
+++ b/desolation-vm/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "desolation-vm"
+version = "0.1.0"
diff --git a/desolation-vm/Cargo.toml b/desolation-vm/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..27d59fbb7e01c5a0c93de9d346177bdc5307a165
--- /dev/null
+++ b/desolation-vm/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "desolation-vm"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/desolation-vm/src/lib.rs b/desolation-vm/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..7d12d9af8195bf5e19d10c7b592b359ccd014149
--- /dev/null
+++ b/desolation-vm/src/lib.rs
@@ -0,0 +1,14 @@
+pub fn add(left: usize, right: usize) -> usize {
+    left + right
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn it_works() {
+        let result = add(2, 2);
+        assert_eq!(result, 4);
+    }
+}
diff --git a/desolation-vm/target/.rustc_info.json b/desolation-vm/target/.rustc_info.json
new file mode 100644
index 0000000000000000000000000000000000000000..a215ce7c026565d485e659c3eeba8815f82f0fe4
--- /dev/null
+++ b/desolation-vm/target/.rustc_info.json
@@ -0,0 +1 @@
+{"rustc_fingerprint":14710574181565541982,"outputs":{"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.65.0 (897e37553 2022-11-02)\nbinary: rustc\ncommit-hash: 897e37553bba8b42751c67658967889d11ecd120\ncommit-date: 2022-11-02\nhost: aarch64-apple-darwin\nrelease: 1.65.0\nLLVM version: 15.0.0\n","stderr":""},"15697416045686424142":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n","stderr":""},"10376369925670944939":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/owen_hayes/.rustup/toolchains/stable-aarch64-apple-darwin\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"llvm14-builtins-abi\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"v8.1a\"\ntarget_feature=\"v8.2a\"\ntarget_feature=\"v8.3a\"\ntarget_feature=\"v8.4a\"\ntarget_feature=\"vh\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"128\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store=\"128\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"apple\"\nunix\n","stderr":""}},"successes":{}}
\ No newline at end of file
diff --git a/desolation-vm/target/CACHEDIR.TAG b/desolation-vm/target/CACHEDIR.TAG
new file mode 100644
index 0000000000000000000000000000000000000000..20d7c319cda945dc07729797750a49b232206ab5
--- /dev/null
+++ b/desolation-vm/target/CACHEDIR.TAG
@@ -0,0 +1,3 @@
+Signature: 8a477f597d28d172789f06886806bc55
+# This file is a cache directory tag created by cargo.
+# For information about cache directory tags see https://bford.info/cachedir/
diff --git a/desolation-vm/target/debug/.cargo-lock b/desolation-vm/target/debug/.cargo-lock
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-09423ef64e221e17/dep-test-lib-desolation-vm b/desolation-vm/target/debug/.fingerprint/desolation-vm-09423ef64e221e17/dep-test-lib-desolation-vm
new file mode 100644
index 0000000000000000000000000000000000000000..6a29cd01c5fcb2942f188fcfb22fb240121ecc02
Binary files /dev/null and b/desolation-vm/target/debug/.fingerprint/desolation-vm-09423ef64e221e17/dep-test-lib-desolation-vm differ
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-09423ef64e221e17/invoked.timestamp b/desolation-vm/target/debug/.fingerprint/desolation-vm-09423ef64e221e17/invoked.timestamp
new file mode 100644
index 0000000000000000000000000000000000000000..e00328da5aa8e7fba830f8cc8d04777646c36cff
--- /dev/null
+++ b/desolation-vm/target/debug/.fingerprint/desolation-vm-09423ef64e221e17/invoked.timestamp
@@ -0,0 +1 @@
+This file has an mtime of when this was started.
\ No newline at end of file
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-09423ef64e221e17/test-lib-desolation-vm b/desolation-vm/target/debug/.fingerprint/desolation-vm-09423ef64e221e17/test-lib-desolation-vm
new file mode 100644
index 0000000000000000000000000000000000000000..e0cfa441a3c76ac220e48d0a914b244a8d711f40
--- /dev/null
+++ b/desolation-vm/target/debug/.fingerprint/desolation-vm-09423ef64e221e17/test-lib-desolation-vm
@@ -0,0 +1 @@
+3688374470c61a2f
\ No newline at end of file
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-09423ef64e221e17/test-lib-desolation-vm.json b/desolation-vm/target/debug/.fingerprint/desolation-vm-09423ef64e221e17/test-lib-desolation-vm.json
new file mode 100644
index 0000000000000000000000000000000000000000..18b9d7c749a8d8af80e4a45df4b1f20208f7434a
--- /dev/null
+++ b/desolation-vm/target/debug/.fingerprint/desolation-vm-09423ef64e221e17/test-lib-desolation-vm.json
@@ -0,0 +1 @@
+{"rustc":7780067038968745159,"features":"[]","target":15758094893599340748,"profile":11506243869495082934,"path":17523903030608720598,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/desolation-vm-09423ef64e221e17/dep-test-lib-desolation-vm"}}],"rustflags":[],"metadata":7797948686568424061,"config":2202906307356721367,"compile_kind":0}
\ No newline at end of file
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-3381e5862f394e72/invoked.timestamp b/desolation-vm/target/debug/.fingerprint/desolation-vm-3381e5862f394e72/invoked.timestamp
new file mode 100644
index 0000000000000000000000000000000000000000..e00328da5aa8e7fba830f8cc8d04777646c36cff
--- /dev/null
+++ b/desolation-vm/target/debug/.fingerprint/desolation-vm-3381e5862f394e72/invoked.timestamp
@@ -0,0 +1 @@
+This file has an mtime of when this was started.
\ No newline at end of file
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-3381e5862f394e72/test-lib-desolation-vm b/desolation-vm/target/debug/.fingerprint/desolation-vm-3381e5862f394e72/test-lib-desolation-vm
new file mode 100644
index 0000000000000000000000000000000000000000..15880c6e7bdd4d8940e1fc7fc82e94ea5a8a5e22
--- /dev/null
+++ b/desolation-vm/target/debug/.fingerprint/desolation-vm-3381e5862f394e72/test-lib-desolation-vm
@@ -0,0 +1 @@
+629780d79e7ddda8
\ No newline at end of file
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-3381e5862f394e72/test-lib-desolation-vm.json b/desolation-vm/target/debug/.fingerprint/desolation-vm-3381e5862f394e72/test-lib-desolation-vm.json
new file mode 100644
index 0000000000000000000000000000000000000000..9cc0bb7ecc67c8f54d6c22944bce5d5fe88ff1bd
--- /dev/null
+++ b/desolation-vm/target/debug/.fingerprint/desolation-vm-3381e5862f394e72/test-lib-desolation-vm.json
@@ -0,0 +1 @@
+{"rustc":17204927582409307511,"features":"[]","target":15758094893599340748,"profile":11506243869495082934,"path":17523903030608720598,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/desolation-vm-3381e5862f394e72/dep-test-lib-desolation-vm"}}],"rustflags":[],"metadata":7797948686568424061,"config":2202906307356721367,"compile_kind":0}
\ No newline at end of file
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-869063d655d09d21/invoked.timestamp b/desolation-vm/target/debug/.fingerprint/desolation-vm-869063d655d09d21/invoked.timestamp
new file mode 100644
index 0000000000000000000000000000000000000000..e00328da5aa8e7fba830f8cc8d04777646c36cff
--- /dev/null
+++ b/desolation-vm/target/debug/.fingerprint/desolation-vm-869063d655d09d21/invoked.timestamp
@@ -0,0 +1 @@
+This file has an mtime of when this was started.
\ No newline at end of file
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-869063d655d09d21/lib-desolation-vm b/desolation-vm/target/debug/.fingerprint/desolation-vm-869063d655d09d21/lib-desolation-vm
new file mode 100644
index 0000000000000000000000000000000000000000..aab7844a07b2cbafda433df733fb7ec8fd760c09
--- /dev/null
+++ b/desolation-vm/target/debug/.fingerprint/desolation-vm-869063d655d09d21/lib-desolation-vm
@@ -0,0 +1 @@
+e89a86d2fcbdd214
\ No newline at end of file
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-869063d655d09d21/lib-desolation-vm.json b/desolation-vm/target/debug/.fingerprint/desolation-vm-869063d655d09d21/lib-desolation-vm.json
new file mode 100644
index 0000000000000000000000000000000000000000..f22abf8c2b25297f73b28add6fe64c50c7ccb8f6
--- /dev/null
+++ b/desolation-vm/target/debug/.fingerprint/desolation-vm-869063d655d09d21/lib-desolation-vm.json
@@ -0,0 +1 @@
+{"rustc":7780067038968745159,"features":"[]","target":15758094893599340748,"profile":17483045194147818835,"path":17523903030608720598,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/desolation-vm-869063d655d09d21/dep-lib-desolation-vm"}}],"rustflags":[],"metadata":7797948686568424061,"config":2202906307356721367,"compile_kind":0}
\ No newline at end of file
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-b8b5aa740124daab/invoked.timestamp b/desolation-vm/target/debug/.fingerprint/desolation-vm-b8b5aa740124daab/invoked.timestamp
new file mode 100644
index 0000000000000000000000000000000000000000..e00328da5aa8e7fba830f8cc8d04777646c36cff
--- /dev/null
+++ b/desolation-vm/target/debug/.fingerprint/desolation-vm-b8b5aa740124daab/invoked.timestamp
@@ -0,0 +1 @@
+This file has an mtime of when this was started.
\ No newline at end of file
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-b8b5aa740124daab/lib-desolation-vm b/desolation-vm/target/debug/.fingerprint/desolation-vm-b8b5aa740124daab/lib-desolation-vm
new file mode 100644
index 0000000000000000000000000000000000000000..4c79fa6387f7868c7e000b3094e6169674c0a774
--- /dev/null
+++ b/desolation-vm/target/debug/.fingerprint/desolation-vm-b8b5aa740124daab/lib-desolation-vm
@@ -0,0 +1 @@
+f6cc552731041da6
\ No newline at end of file
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-b8b5aa740124daab/lib-desolation-vm.json b/desolation-vm/target/debug/.fingerprint/desolation-vm-b8b5aa740124daab/lib-desolation-vm.json
new file mode 100644
index 0000000000000000000000000000000000000000..51674288dd2aa5860313d17b9b95ee1e5e8b690c
--- /dev/null
+++ b/desolation-vm/target/debug/.fingerprint/desolation-vm-b8b5aa740124daab/lib-desolation-vm.json
@@ -0,0 +1 @@
+{"rustc":17204927582409307511,"features":"[]","target":15758094893599340748,"profile":17483045194147818835,"path":17523903030608720598,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/desolation-vm-b8b5aa740124daab/dep-lib-desolation-vm"}}],"rustflags":[],"metadata":7797948686568424061,"config":2202906307356721367,"compile_kind":0}
\ No newline at end of file
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-dc96445ade0ee411/invoked.timestamp b/desolation-vm/target/debug/.fingerprint/desolation-vm-dc96445ade0ee411/invoked.timestamp
new file mode 100644
index 0000000000000000000000000000000000000000..e00328da5aa8e7fba830f8cc8d04777646c36cff
--- /dev/null
+++ b/desolation-vm/target/debug/.fingerprint/desolation-vm-dc96445ade0ee411/invoked.timestamp
@@ -0,0 +1 @@
+This file has an mtime of when this was started.
\ No newline at end of file
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-dc96445ade0ee411/test-lib-desolation-vm b/desolation-vm/target/debug/.fingerprint/desolation-vm-dc96445ade0ee411/test-lib-desolation-vm
new file mode 100644
index 0000000000000000000000000000000000000000..75d6a7393428eafbd619ea7e41d99e2b786e87ce
--- /dev/null
+++ b/desolation-vm/target/debug/.fingerprint/desolation-vm-dc96445ade0ee411/test-lib-desolation-vm
@@ -0,0 +1 @@
+b27e5e8e636d0ccc
\ No newline at end of file
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-dc96445ade0ee411/test-lib-desolation-vm.json b/desolation-vm/target/debug/.fingerprint/desolation-vm-dc96445ade0ee411/test-lib-desolation-vm.json
new file mode 100644
index 0000000000000000000000000000000000000000..123abb48a4a43f27fcf80bc593f87df43e841b70
--- /dev/null
+++ b/desolation-vm/target/debug/.fingerprint/desolation-vm-dc96445ade0ee411/test-lib-desolation-vm.json
@@ -0,0 +1 @@
+{"rustc":7780067038968745159,"features":"[]","target":15758094893599340748,"profile":11506243869495082934,"path":17523903030608720598,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/desolation-vm-dc96445ade0ee411/dep-test-lib-desolation-vm"}}],"rustflags":[],"metadata":7797948686568424061,"config":2202906307356721367,"compile_kind":0}
\ No newline at end of file
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-e748ee338f4aef83/dep-lib-desolation-vm b/desolation-vm/target/debug/.fingerprint/desolation-vm-e748ee338f4aef83/dep-lib-desolation-vm
new file mode 100644
index 0000000000000000000000000000000000000000..6a29cd01c5fcb2942f188fcfb22fb240121ecc02
Binary files /dev/null and b/desolation-vm/target/debug/.fingerprint/desolation-vm-e748ee338f4aef83/dep-lib-desolation-vm differ
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-e748ee338f4aef83/invoked.timestamp b/desolation-vm/target/debug/.fingerprint/desolation-vm-e748ee338f4aef83/invoked.timestamp
new file mode 100644
index 0000000000000000000000000000000000000000..e00328da5aa8e7fba830f8cc8d04777646c36cff
--- /dev/null
+++ b/desolation-vm/target/debug/.fingerprint/desolation-vm-e748ee338f4aef83/invoked.timestamp
@@ -0,0 +1 @@
+This file has an mtime of when this was started.
\ No newline at end of file
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-e748ee338f4aef83/lib-desolation-vm b/desolation-vm/target/debug/.fingerprint/desolation-vm-e748ee338f4aef83/lib-desolation-vm
new file mode 100644
index 0000000000000000000000000000000000000000..28dc716ee7e06bdb73041b52e07859a537b4345f
--- /dev/null
+++ b/desolation-vm/target/debug/.fingerprint/desolation-vm-e748ee338f4aef83/lib-desolation-vm
@@ -0,0 +1 @@
+ab8c5c199a75b3d6
\ No newline at end of file
diff --git a/desolation-vm/target/debug/.fingerprint/desolation-vm-e748ee338f4aef83/lib-desolation-vm.json b/desolation-vm/target/debug/.fingerprint/desolation-vm-e748ee338f4aef83/lib-desolation-vm.json
new file mode 100644
index 0000000000000000000000000000000000000000..be7f3b89006f58fa6cf1d0f26e2e03a18a393b77
--- /dev/null
+++ b/desolation-vm/target/debug/.fingerprint/desolation-vm-e748ee338f4aef83/lib-desolation-vm.json
@@ -0,0 +1 @@
+{"rustc":7780067038968745159,"features":"[]","target":15758094893599340748,"profile":17483045194147818835,"path":17523903030608720598,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/desolation-vm-e748ee338f4aef83/dep-lib-desolation-vm"}}],"rustflags":[],"metadata":7797948686568424061,"config":2202906307356721367,"compile_kind":0}
\ No newline at end of file
diff --git a/desolation-vm/target/debug/deps/desolation_vm-09423ef64e221e17.d b/desolation-vm/target/debug/deps/desolation_vm-09423ef64e221e17.d
new file mode 100644
index 0000000000000000000000000000000000000000..f12976896e538ceb8693e766ef99f4c94ed37133
--- /dev/null
+++ b/desolation-vm/target/debug/deps/desolation_vm-09423ef64e221e17.d
@@ -0,0 +1,7 @@
+/Users/owen_hayes/coding/rust/desolation/desolation-vm/target/debug/deps/desolation_vm-09423ef64e221e17.rmeta: src/lib.rs
+
+/Users/owen_hayes/coding/rust/desolation/desolation-vm/target/debug/deps/desolation_vm-09423ef64e221e17.d: src/lib.rs
+
+src/lib.rs:
+
+# env-dep:CLIPPY_ARGS=
diff --git a/desolation-vm/target/debug/deps/desolation_vm-e748ee338f4aef83.d b/desolation-vm/target/debug/deps/desolation_vm-e748ee338f4aef83.d
new file mode 100644
index 0000000000000000000000000000000000000000..1acfb31fffed6ccccfd774e6f27f08eb92e962c5
--- /dev/null
+++ b/desolation-vm/target/debug/deps/desolation_vm-e748ee338f4aef83.d
@@ -0,0 +1,7 @@
+/Users/owen_hayes/coding/rust/desolation/desolation-vm/target/debug/deps/desolation_vm-e748ee338f4aef83.rmeta: src/lib.rs
+
+/Users/owen_hayes/coding/rust/desolation/desolation-vm/target/debug/deps/desolation_vm-e748ee338f4aef83.d: src/lib.rs
+
+src/lib.rs:
+
+# env-dep:CLIPPY_ARGS=
diff --git a/desolation-vm/target/debug/deps/libdesolation_vm-09423ef64e221e17.rmeta b/desolation-vm/target/debug/deps/libdesolation_vm-09423ef64e221e17.rmeta
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/desolation-vm/target/debug/deps/libdesolation_vm-e748ee338f4aef83.rmeta b/desolation-vm/target/debug/deps/libdesolation_vm-e748ee338f4aef83.rmeta
new file mode 100644
index 0000000000000000000000000000000000000000..0248b47d56c15d9e680de05fb95c08ce654f69d4
Binary files /dev/null and b/desolation-vm/target/debug/deps/libdesolation_vm-e748ee338f4aef83.rmeta differ
diff --git a/desolation-vm/target/debug/incremental/desolation_vm-318ufpgp6ls4r/s-gew6nes818-15r96xo-29pqihecrg913/dep-graph.bin b/desolation-vm/target/debug/incremental/desolation_vm-318ufpgp6ls4r/s-gew6nes818-15r96xo-29pqihecrg913/dep-graph.bin
new file mode 100644
index 0000000000000000000000000000000000000000..0db29f3395dfeeca42eb5605d598c19e5057f1bc
Binary files /dev/null and b/desolation-vm/target/debug/incremental/desolation_vm-318ufpgp6ls4r/s-gew6nes818-15r96xo-29pqihecrg913/dep-graph.bin differ
diff --git a/desolation-vm/target/debug/incremental/desolation_vm-318ufpgp6ls4r/s-gew6nes818-15r96xo-29pqihecrg913/query-cache.bin b/desolation-vm/target/debug/incremental/desolation_vm-318ufpgp6ls4r/s-gew6nes818-15r96xo-29pqihecrg913/query-cache.bin
new file mode 100644
index 0000000000000000000000000000000000000000..43bcf93d78594086d95e2f835288ec567d570799
Binary files /dev/null and b/desolation-vm/target/debug/incremental/desolation_vm-318ufpgp6ls4r/s-gew6nes818-15r96xo-29pqihecrg913/query-cache.bin differ
diff --git a/desolation-vm/target/debug/incremental/desolation_vm-318ufpgp6ls4r/s-gew6nes818-15r96xo-29pqihecrg913/work-products.bin b/desolation-vm/target/debug/incremental/desolation_vm-318ufpgp6ls4r/s-gew6nes818-15r96xo-29pqihecrg913/work-products.bin
new file mode 100644
index 0000000000000000000000000000000000000000..2e8fe2d7567c65a29d1e46809ff828d9702c29ea
Binary files /dev/null and b/desolation-vm/target/debug/incremental/desolation_vm-318ufpgp6ls4r/s-gew6nes818-15r96xo-29pqihecrg913/work-products.bin differ
diff --git a/desolation-vm/target/debug/incremental/desolation_vm-318ufpgp6ls4r/s-gew6nes818-15r96xo.lock b/desolation-vm/target/debug/incremental/desolation_vm-318ufpgp6ls4r/s-gew6nes818-15r96xo.lock
new file mode 100755
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/desolation-vm/target/debug/incremental/desolation_vm-pf5l7rcd08ac/s-gew6nes80m-1s21xde-5jv50nchg6bz/dep-graph.bin b/desolation-vm/target/debug/incremental/desolation_vm-pf5l7rcd08ac/s-gew6nes80m-1s21xde-5jv50nchg6bz/dep-graph.bin
new file mode 100644
index 0000000000000000000000000000000000000000..0618d2aee716086b6bc75d080fcb240cba84aa90
Binary files /dev/null and b/desolation-vm/target/debug/incremental/desolation_vm-pf5l7rcd08ac/s-gew6nes80m-1s21xde-5jv50nchg6bz/dep-graph.bin differ
diff --git a/desolation-vm/target/debug/incremental/desolation_vm-pf5l7rcd08ac/s-gew6nes80m-1s21xde-5jv50nchg6bz/query-cache.bin b/desolation-vm/target/debug/incremental/desolation_vm-pf5l7rcd08ac/s-gew6nes80m-1s21xde-5jv50nchg6bz/query-cache.bin
new file mode 100644
index 0000000000000000000000000000000000000000..8ea11963024ebbacad50fcee44201f4f28747b3d
Binary files /dev/null and b/desolation-vm/target/debug/incremental/desolation_vm-pf5l7rcd08ac/s-gew6nes80m-1s21xde-5jv50nchg6bz/query-cache.bin differ
diff --git a/desolation-vm/target/debug/incremental/desolation_vm-pf5l7rcd08ac/s-gew6nes80m-1s21xde-5jv50nchg6bz/work-products.bin b/desolation-vm/target/debug/incremental/desolation_vm-pf5l7rcd08ac/s-gew6nes80m-1s21xde-5jv50nchg6bz/work-products.bin
new file mode 100644
index 0000000000000000000000000000000000000000..2e8fe2d7567c65a29d1e46809ff828d9702c29ea
Binary files /dev/null and b/desolation-vm/target/debug/incremental/desolation_vm-pf5l7rcd08ac/s-gew6nes80m-1s21xde-5jv50nchg6bz/work-products.bin differ
diff --git a/desolation-vm/target/debug/incremental/desolation_vm-pf5l7rcd08ac/s-gew6nes80m-1s21xde.lock b/desolation-vm/target/debug/incremental/desolation_vm-pf5l7rcd08ac/s-gew6nes80m-1s21xde.lock
new file mode 100755
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/examples/lex.rs b/examples/lex.rs
new file mode 100644
index 0000000000000000000000000000000000000000..9640c15bb6d01802b905d9e6108a5ae4a7391644
--- /dev/null
+++ b/examples/lex.rs
@@ -0,0 +1,24 @@
+use std::fs;
+use log::{error, info};
+use desolation::lex::Lexer;
+
+fn main() {
+    sensible_env_logger::init!();
+    let mut lexer = Lexer::new();
+
+    let source = fs::read_to_string("examples/sq.t").unwrap();
+    info!("{:#?}", source);
+    let tokens = lexer.lex(source);
+    match tokens {
+        Ok(tokens) => {
+            println!("Keywords:\n{}", tokens.get_keywords());
+            println!("Identifiers:\n{}", tokens.get_identifiers());
+            println!("Integers:\n{}", tokens.get_integer_literals());
+            println!("Strings:\n{}", tokens.get_string_literals());
+            println!("Characters:\n{}", tokens.get_character_literals());
+        }
+        Err(e) => {
+            error!("Failed to lex: {}", e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/examples/sq.t b/examples/sq.t
new file mode 100644
index 0000000000000000000000000000000000000000..fc720bca57de64c745bbff6e678b37f850037011
--- /dev/null
+++ b/examples/sq.t
@@ -0,0 +1,18 @@
+fun sq(n) {
+    return .n * .n
+}
+
+
+fun init() {
+    var i
+    sprint("Table of squares:\n")
+    i : 1
+    loop {
+        until .i > 10
+        iprint(.i)
+        sprint(" squared equals ")
+        iprint(sq(.i))
+        nl()
+        i : .i + 1
+    }
+}
\ No newline at end of file
diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index 20e5d7f9ec0a90cf759a7d7026d9035cd3e4df6a..274142d3454fd9b31ddfd33c173ca3e96400a812 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -1 +1 @@
-mod node;
\ No newline at end of file
+mod node;
diff --git a/src/ast/node.rs b/src/ast/node.rs
index 4166c85bd55f2e8b1da8c386b57a5d0c19406649..188a0ef9ec3d32baa833d104293c9709331de8ec 100644
--- a/src/ast/node.rs
+++ b/src/ast/node.rs
@@ -6,9 +6,7 @@ pub struct Node<'a> {
     pub line_no: i32,
     pub left: &'a Node<'a>,
     pub right: &'a Node<'a>,
-    pub next: &'a Node<'a>
+    pub next: &'a Node<'a>,
 }
 
-pub struct IdList {
-
-}
\ No newline at end of file
+pub struct IdList {}
diff --git a/src/lex/consts.rs b/src/lex/consts.rs
new file mode 100644
index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc
--- /dev/null
+++ b/src/lex/consts.rs
@@ -0,0 +1 @@
+
diff --git a/src/lex/keyword.rs b/src/lex/keyword.rs
new file mode 100644
index 0000000000000000000000000000000000000000..92214e68a19f8a5af97421c3a3163f0550e3ea57
--- /dev/null
+++ b/src/lex/keyword.rs
@@ -0,0 +1,25 @@
+#[derive(Debug, Eq, PartialEq, Clone)]
+pub enum Keyword {
+    Var,
+    Fun,
+    If,
+    Else,
+    Until,
+    Loop,
+    Return,
+}
+
+impl Keyword {
+    pub fn from_str(s: &str) -> Option<Self> {
+        match s {
+            "var" => Some(Keyword::Var),
+            "fun" => Some(Keyword::Fun),
+            "if" => Some(Keyword::If),
+            "else" => Some(Keyword::Else),
+            "until" => Some(Keyword::Until),
+            "loop" => Some(Keyword::Loop),
+            "return" => Some(Keyword::Return),
+            _ => None,
+        }
+    }
+}
diff --git a/src/lex/lexer.rs b/src/lex/lexer.rs
new file mode 100644
index 0000000000000000000000000000000000000000..83e0243922d027f4a19bbfe0868da709dfbdfb05
--- /dev/null
+++ b/src/lex/lexer.rs
@@ -0,0 +1,318 @@
+use std::fmt::Display;
+use crate::lex::token::{Token, TokenType};
+use anyhow::{bail, ensure, Result};
+use log::{debug};
+use thiserror::Error;
+use crate::lex::keyword::Keyword;
+
+#[derive(Debug, Clone)]
+pub struct Lexer {
+    source: String,
+    index: usize,
+    curr_char: char,
+    line_no: usize,
+    col_no: usize,
+    length: usize,
+}
+
+#[derive(Debug, Error)]
+pub enum LexerError {
+    #[error("Unexpected character: {0:?} at {1}:{2}")]
+    InvalidCharacter(char, usize, usize),
+    #[error("Invalid string literal at {0}:{1}")]
+    InvalidStringLiteral(usize, usize),
+    #[error("Invalid character literal at {0}:{1}")]
+    InvalidCharacterLiteral(usize, usize),
+    #[error("Invalid integer literal")]
+    InvalidIntegerLiteral(#[from] std::num::ParseIntError),
+    #[error("Invalid identifier at {0}:{1}")]
+    InvalidIdentifier(usize, usize),
+    #[error("Invalid comment at {0}:{1}")]
+    InvalidComment(usize, usize),
+    #[error("Unexpected EOF at {0}:{1}")]
+    InvalidEOF(usize, usize),
+    #[error("Unexpected EOL at {0}:{1}")]
+    InvalidEOL(usize, usize),
+    #[error("Unknown lexer error at {0}:{1}")]
+    Unknown(usize, usize),
+}
+
+#[derive(Debug)]
+pub struct TokenStream {
+    tokens: Vec<Token>,
+}
+
+impl Display for TokenStream {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        for token in &self.tokens {
+            writeln!(f, "{:?}", token)?;
+        }
+        Ok(())
+    }
+}
+
+impl TokenStream {
+    pub fn get_keywords(&self) -> Self {
+        self.tokens.iter()
+            .filter(|t| t.is_keyword()).cloned()
+            .collect::<TokenStream>()
+    }
+
+    pub fn get_identifiers(&self) -> Self {
+        self.tokens.iter()
+            .filter(|t| t.is_identifier()).cloned()
+            .collect::<TokenStream>()
+    }
+
+    pub fn get_integer_literals(&self) -> Self {
+        self.tokens.iter()
+            .filter(|t| t.is_integer_literal()).cloned()
+            .collect::<TokenStream>()
+    }
+
+    pub fn get_string_literals(&self) -> Self {
+        self.tokens.iter()
+            .filter(|t| t.is_string_literal()).cloned()
+            .collect::<TokenStream>()
+    }
+
+    pub fn get_character_literals(&self) -> Self {
+        self.tokens.iter()
+            .filter(|t| t.is_character_literal()).cloned()
+            .collect::<TokenStream>()
+    }
+}
+
+impl FromIterator<Token> for TokenStream {
+    fn from_iter<T: IntoIterator<Item=Token>>(iter: T) -> Self {
+        let tokens = iter.into_iter().collect();
+        TokenStream { tokens }
+    }
+}
+
+impl Lexer {
+    pub fn new() -> Self {
+        Lexer {
+            source: String::new(),
+            index: 0,
+            curr_char: '\0',
+            line_no: 1,
+            col_no: 1,
+            length: 0,
+        }
+    }
+
+    pub fn lex(&mut self, source: String) -> Result<TokenStream> {
+        self.source = source;
+        self.length = self.source.len();
+        self.curr_char = self.get_curr()?;
+        let mut tokens = Vec::new();
+        while self.has_next() {
+            tokens.push(self.get_next_token()?);
+        }
+
+        info!("Lexed {} tokens", tokens.len());
+
+        // Post processing.
+        // fold consecutive NL tokens into one.
+        // find a NL token, then delete all the NL tokens until the next non-NL token.
+        let indices = tokens.iter()
+            .enumerate()
+            .filter(|(_, t)| t.token_type() == TokenType::NL)
+            .map(|(i, _)| i)
+            .collect::<Vec<usize>>();
+        let indices_folded = indices.iter().fold((Vec::new(), Vec::new()), |mut acc, i| {
+            if let Some(last) = acc.0.last() {
+                if i - last == 1 {
+                    acc.1.push(*last);
+                    acc.0.pop();
+                }
+            }
+            acc.0.push(*i);
+            acc
+        }).1;
+
+        for i in indices_folded.iter().rev() {
+            tokens.remove(*i);
+        }
+
+        info!("Folded {} NL tokens", indices_folded.len());
+        info!("Now {} tokens", tokens.len());
+
+        Ok(TokenStream::from_iter(tokens))
+    }
+
+    fn has_next(&self) -> bool {
+        self.index < self.length
+    }
+
+    fn reset(&mut self) {
+        self.index = 0;
+        self.curr_char = self.source.chars().next().unwrap();
+        self.line_no = 1;
+        self.col_no = 1;
+        self.length = self.source.chars().count();
+    }
+
+    fn get_curr(&self) -> Result<char> {
+        ensure!(self.has_next(), LexerError::InvalidEOF(self.line_no, self.col_no));
+        let c = self.source.chars().nth(self.index).unwrap();
+        trace!("Got current character: {:?} at {}:{}[{}]", c, self.line_no, self.col_no, self.index);
+        Ok(c)
+    }
+
+    fn advance(&mut self) -> Result<()> {
+        self.index += 1;
+        self.col_no += 1;
+        if self.has_next() {
+            self.curr_char = self.get_curr()?;
+            // trace!("Advanced to {}:{}[{}]", self.line_no, self.col_no, self.index);
+        }
+        Ok(())
+    }
+
+    fn advance_n(&mut self, n: usize) -> Result<()> {
+        for _ in 0..n {
+            self.advance()?;
+        }
+        Ok(())
+    }
+
+    fn advance_until(&mut self, pred: Box<dyn Fn() -> bool>) -> Result<()> {
+        while self.has_next() && !pred() {
+            self.advance()?;
+        }
+        Ok(())
+    }
+
+    fn advance_eol(&mut self) -> Result<()> {
+        if let Some(n) = self.find_next('\n') {
+            self.advance_n(n)?;
+        } else {
+            bail!(LexerError::InvalidEOL(self.line_no, self.col_no));
+        }
+        Ok(())
+    }
+
+    fn consume(&mut self) -> Result<char> {
+        let c = self.curr_char;
+        self.advance()?;
+        Ok(c)
+    }
+
+    fn collect(&mut self, n: usize) -> Result<&str> {
+        let start = self.index;
+        let end = self.index + n;
+        self.advance_n(n)?;
+        Ok(&self.source[start..end])
+    }
+
+    fn find_next(&mut self, c: char) -> Option<usize> {
+        let mut i = self.index;
+        while i < self.length {
+            if self.source.chars().nth(i).unwrap() == c {
+                return Some(i);
+            }
+            i += 1;
+        }
+        None
+    }
+
+    // Advance the index until the current character is not a whitespace character. This excludes \n
+    fn skip_whitespace(&mut self) -> Result<()> {
+        while self.is_whitespace() && self.has_next() {
+            self.advance()?;
+        }
+        Ok(())
+    }
+
+    fn collect_string(&mut self) -> Result<String> {
+        let mut result = String::new();
+        while self.curr_char != '"' {
+            result.push(self.consume()?);
+        }
+        self.advance()?;
+        Ok(result)
+    }
+
+    fn collect_identifier(&mut self) -> Result<String> {
+        let mut result = String::new();
+        while self.curr_char.is_alphanumeric() {
+            result.push(self.consume()?);
+        }
+        Ok(result)
+    }
+
+    fn collect_integer(&mut self) -> Result<i64> {
+        let mut result = String::new();
+        while self.curr_char.is_numeric() && self.has_next() {
+            result.push(self.consume()?);
+        }
+        let parsed = result.parse::<i64>()?;
+        Ok(parsed)
+    }
+
+    fn is_whitespace(&self) -> bool {
+        matches!(self.curr_char, ' ' | '\t' | '\r' | '\x0B' | '\x0C')
+    }
+
+    fn get_next_token(&mut self) -> Result<Token> {
+        if !self.has_next() {
+            return Ok(TokenType::Eof.at(self.index, self.line_no, self.col_no));
+        }
+        let start = (self.index, self.line_no, self.col_no);
+        let token = match self.curr_char {
+            n if self.is_whitespace() => {
+                trace!("Found whitespace {:?} at {}:{}[{}]", n, self.line_no, self.col_no, self.index);
+                self.skip_whitespace()?;
+                debug!("Skipped {} whitespace from {}:{}[{}] to {}:{}[{}]", self.index - start.0, start.1, start.2, start.0, self.line_no, self.col_no, self.index);
+                return self.get_next_token();
+            }
+            '\n' => {
+                trace!("Found newline at {}:{}[{}]", self.line_no, self.col_no, self.index);
+                // fold newlines into a single token
+                self.col_no = 1;
+                self.line_no += 1;
+                self.advance()?;
+                TokenType::NL
+            }
+            // is alphabetic ensures the the identifies starts with a letter. This may not be the behaviour I want.
+            // TODO: Look into this.
+            n if n.is_alphabetic() => {
+                let identifier = self.collect_identifier()?;
+                if let Some(keyword) = Keyword::from_str(&identifier) {
+                    debug!("Found keyword {:?} at {}:{}[{}]", keyword, self.line_no, self.col_no, self.index);
+                    TokenType::Keyword(keyword)
+                } else {
+                    debug!("Found identifier {:?} at {}:{}[{}]", identifier, self.line_no, self.col_no, self.index);
+                    TokenType::Identifier(identifier)
+                }
+            }
+            n if n.is_numeric() => {
+                let integer = self.collect_integer()?;
+                debug!("Collected integer: {} at {}:{}[{}] to {}:{}[{}]", integer, start.1, start.2, start.0, self.line_no, self.col_no, self.index);
+                TokenType::IntegerLiteral(integer)
+            }
+            '"' => {
+                self.advance()?;
+                let string = self.collect_string()?;
+                debug!("Collected string: {} at {}:{}[{}] to {}:{}[{}]", string, start.1, start.2, start.0, self.line_no, self.col_no, self.index);
+                TokenType::StringLiteral(string)
+            }
+            '\'' => {
+                self.advance()?;
+                let character = self.consume()?;
+                ensure!(self.consume()? == '\'', LexerError::InvalidCharacterLiteral(self.line_no, self.col_no));
+                debug!("Collected character literal: {:?} at {}:{}[{}] to {}:{}[{}]", character, start.1, start.2, start.0, self.line_no, self.col_no, self.index);
+                TokenType::CharacterLiteral(character)
+            }
+            '#' => {
+                debug!("Found comment at {}:{}[{}]", self.line_no, self.col_no, self.index);
+                self.advance_eol()?;
+                return self.get_next_token();
+            }
+            _ => TokenType::from_char(self.consume()?)
+        }.at(start.0, start.1, start.2);
+        Ok(token)
+    }
+}
diff --git a/src/lex/mod.rs b/src/lex/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..5a0774db899ab4dafa14f7121650b8c483fb0d8b
--- /dev/null
+++ b/src/lex/mod.rs
@@ -0,0 +1,6 @@
+mod consts;
+mod keyword;
+mod lexer;
+mod token;
+
+pub use lexer::Lexer;
\ No newline at end of file
diff --git a/src/lex/token.rs b/src/lex/token.rs
new file mode 100644
index 0000000000000000000000000000000000000000..46359243df22fd25fec7737cb9c1d061eae5c823
--- /dev/null
+++ b/src/lex/token.rs
@@ -0,0 +1,118 @@
+use crate::lex::keyword::Keyword;
+
+#[derive(Debug, Eq, PartialEq, Clone)]
+pub enum TokenType {
+    Keyword(Keyword),
+    LBrace,
+    RBrace,
+    LParen,
+    RParen,
+    Assign,
+    Comma,
+    Dot,
+    Minus,
+    Not,
+    Plus,
+    Times,
+    Slash,
+    And,
+    Or,
+    Xor,
+    Mod,
+    Eq,
+    Neq,
+    Lt,
+    Leq,
+    Gt,
+    Geq,
+    LShift,
+    RShift,
+    CharacterLiteral(char),
+    Identifier(String),
+    IntegerLiteral(i64),
+    StringLiteral(String),
+    Unknown(char),
+    Eof,
+    NL
+}
+
+impl TokenType {
+    pub fn at(self, index: usize, line_no: usize, col_no: usize) -> Token {
+        Token {
+            token_type: self,
+            index,
+            line_no,
+            col_no,
+        }
+    }
+
+    pub fn from_char(c: char) -> Self {
+        match c {
+            '{' => TokenType::LBrace,
+            '}' => TokenType::RBrace,
+            '(' => TokenType::LParen,
+            ')' => TokenType::RParen,
+            ':' => TokenType::Assign,
+            ',' => TokenType::Comma,
+            '.' => TokenType::Dot,
+            '-' => TokenType::Minus,
+            '!' => TokenType::Not,
+            '+' => TokenType::Plus,
+            '*' => TokenType::Times,
+            '/' => TokenType::Slash,
+            '&' => TokenType::And,
+            '|' => TokenType::Or,
+            '^' => TokenType::Xor,
+            '%' => TokenType::Mod,
+            '<' => TokenType::Lt,
+            '>' => TokenType::Gt,
+            _ => TokenType::Unknown(c),
+        }
+    }
+}
+
+#[derive(Debug, Eq, PartialEq, Clone)]
+pub struct Token {
+    token_type: TokenType,
+    index: usize,
+    line_no: usize,
+    col_no: usize,
+}
+
+impl Token {
+    pub fn token_type(&self) -> TokenType {
+        self.token_type.clone()
+    }
+
+    pub fn is_keyword(&self) -> bool {
+        matches!(self.token_type, TokenType::Keyword(_))
+    }
+
+    pub fn is_identifier(&self) -> bool {
+        matches!(self.token_type, TokenType::Identifier(_))
+    }
+
+    pub fn is_integer_literal(&self) -> bool {
+        matches!(self.token_type, TokenType::IntegerLiteral(_))
+    }
+
+    pub fn is_string_literal(&self) -> bool {
+        matches!(self.token_type, TokenType::StringLiteral(_))
+    }
+
+    pub fn is_character_literal(&self) -> bool {
+        matches!(self.token_type, TokenType::CharacterLiteral(_))
+    }
+
+    pub fn index(&self) -> usize {
+        self.index
+    }
+
+    pub fn line_no(&self) -> usize {
+        self.line_no
+    }
+
+    pub fn col_no(&self) -> usize {
+        self.col_no
+    }
+}
diff --git a/src/lexer/lex.rs b/src/lexer/lex.rs
deleted file mode 100644
index 921dd11b57a797fdabc7f10ca4f3dd0ec0f98d07..0000000000000000000000000000000000000000
--- a/src/lexer/lex.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub struct Lexer();
\ No newline at end of file
diff --git a/src/lexer/mod.rs b/src/lexer/mod.rs
deleted file mode 100644
index bb10b2ea64358359c7312b0118a26fe95999a8a4..0000000000000000000000000000000000000000
--- a/src/lexer/mod.rs
+++ /dev/null
@@ -1 +0,0 @@
-mod lex;
diff --git a/src/lib.rs b/src/lib.rs
index 90524b8bd96a02b072886a1ccd2c2806dd7bdffd..22310259fb2e7cd29f98462485b37639a228060f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,17 +1,5 @@
-mod lexer;
 mod ast;
+pub mod lex;
 
-pub fn add(left: usize, right: usize) -> usize {
-    left + right
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn it_works() {
-        let result = add(2, 2);
-        assert_eq!(result, 4);
-    }
-}
+extern crate pretty_env_logger;
+#[macro_use] extern crate log;
\ No newline at end of file