diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,3 @@
+# Changelog for S.P.A.D.E
+
+## Unreleased changes
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,74 @@
+# S.P.A.D.E
+
+S.P.A.D.E stands of Simple Programming And Debugging Environment.
+
+It contains a simple programming language and a built-in Terminal based
+IDE.
+
+The following is a small spade program that draws a bunch of random circles
+in red color, in an SDL window.
+
+```
+graphicswindow(400, 400, true)
+setcolor(255, 0, 0)
+for i = 1 to 100
+  circle(random(10, 300), random(10, 300), random(10, 40))
+endfor
+drawscreen()
+waitforkey()
+```
+
+The entire language and function reference is available in the IDE in an
+easily searchable way. Press F1 or use the help menu to access it.
+
+### IDE Demo
+A small screen recording of SPADE in action can be seen here
+
+[![asciicast](https://asciinema.org/a/SM9tZ2ZiJofikPRxHCQyqDvRY.svg)](https://asciinema.org/a/SM9tZ2ZiJofikPRxHCQyqDvRY)
+
+### Installing
+
+You can download the source package and use `stack` tool to build and install it.
+
+After extracting the source to a folder, running the following command in the folder
+should install it.
+
+```
+stack build && stack install
+
+```
+
+For the time being, this does not seem to build on Windows operating system.
+
+### Getting started
+
+The program should be started by providing a file name. For example,
+
+```
+spade /tmp/temp.spd
+```
+
+If the file does not exist, it will be created on save. If it exist then
+contents will loaded into the editor.
+
+Other than this are no provisions to select or open a file from within the IDE.
+The user is supposed to only work with a single file at one time.
+
+To interpret a spade program without starting the IDE, you can use
+the `run` command.
+
+```
+spade run /tmp/temp.spd
+```
+
+Will execute the program without opening the IDE.
+
+### Dependencies
+
+To install depdendencies:
+
+```
+  apt-get install libsdl2-dev
+  apt-get install libsdl2-mixer-dev
+  apt-get install libtinfo-dev
+```
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/app/Main.hs b/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,52 @@
+module Main where
+
+import Control.Concurrent
+import Control.Concurrent.STM
+import Control.Monad
+import Control.Monad.IO.Class
+import Data.Text.IO as T
+import System.Environment
+import System.IO as SIO
+
+import Compiler.Parser
+import UI.Widgets.Common
+import IDE.IDE
+import Interpreter
+import Interpreter.Common
+
+data CommandLineOptions
+  = StartIDE FilePath
+  | RunProgram FilePath
+
+getCommandLineOption :: IO CommandLineOptions
+getCommandLineOption = do
+  getArgs >>= \case
+    ["run", filePath] -> pure $ RunProgram filePath
+    [filePath] -> pure $ StartIDE filePath
+    _ -> do
+      error "Unrecognized cmd line arguments. Pass a file name (existing or new) to load the program in the IDE, or using 'spade run filename.spd' command to just run the program without starting the IDE."
+
+main :: IO ()
+main = do
+  SIO.hSetEcho stdin False
+  SIO.hSetBuffering stdin NoBuffering
+  SIO.hSetBuffering stdout (BlockBuffering Nothing)
+
+  vty <- initializeVty
+  inputBroadcastChan <- liftIO newBroadcastTChanIO
+  void $ forkIO $ forever $ do
+    keys' <- readVtyEvent vty
+    mapM (atomically . writeTChan inputBroadcastChan) keys'
+
+  getCommandLineOption >>= \case
+    RunProgram filePath -> do
+      content <- T.readFile filePath
+      program <- compile content
+      inputChan <- liftIO $ atomically $ dupTChan inputBroadcastChan
+      is <- interpret_ inputChan program
+      tryTakeMVar (isDebugOut is) >>= \case
+        Just (Errored t) -> T.putStrLn t
+        _ -> pure ()
+
+    StartIDE filePath -> runIDE filePath inputBroadcastChan
+  liftIO $ shutdownVty vty
diff --git a/spade.cabal b/spade.cabal
new file mode 100644
--- /dev/null
+++ b/spade.cabal
@@ -0,0 +1,361 @@
+cabal-version: 2.2
+
+-- This file has been generated from package.yaml by hpack version 0.34.4.
+--
+-- see: https://github.com/sol/hpack
+
+name:           spade
+version:        0.1.0.0
+synopsis:       A simple programming and debugging environment.
+description:    A simple weakly typed, dynamic, interpreted programming langauge and terminal IDE.
+category:       language, interpreter, ide
+author:         Sandeep.C.R
+maintainer:     sandeep@sras.me
+copyright:      2022 Sandeep.C.R
+license:        GPL-3.0-only
+build-type:     Simple
+extra-source-files:
+    README.md
+    ChangeLog.md
+
+library
+  exposed-modules:
+      Common
+      Compiler.AST
+      Compiler.AST.Common
+      Compiler.AST.Expression
+      Compiler.AST.FunctionDef
+      Compiler.AST.FunctionStatement
+      Compiler.AST.Parser.Common
+      Compiler.AST.Program
+      Compiler.Lexer
+      Compiler.Lexer.Comments
+      Compiler.Lexer.Delimeters
+      Compiler.Lexer.Identifiers
+      Compiler.Lexer.Keywords
+      Compiler.Lexer.Literals
+      Compiler.Lexer.Operators
+      Compiler.Lexer.Tokens
+      Compiler.Lexer.Whitespaces
+      Compiler.Parser
+      Highlighter.Highlighter
+      IDE.Common
+      IDE.Help
+      IDE.Help.Contents
+      IDE.Help.Parser
+      IDE.IDE
+      Interpreter
+      Interpreter.Common
+      Interpreter.Initialize
+      Interpreter.Interpreter
+      Interpreter.Lib.Concurrency
+      Interpreter.Lib.Math
+      Interpreter.Lib.Misc
+      Interpreter.Lib.SDL
+      Parser
+      Parser.Lib
+      Parser.Parser
+      Test
+      Test.Common
+      UI.Chars
+      UI.Terminal.IO
+      UI.Widgets
+      UI.Widgets.AutoComplete
+      UI.Widgets.BorderBox
+      UI.Widgets.Common
+      UI.Widgets.Editor
+      UI.Widgets.Editor.Cursor
+      UI.Widgets.Layout
+      UI.Widgets.LogWidget
+      UI.Widgets.MenuContainer
+      UI.Widgets.NullWidget
+      UI.Widgets.TextContainer
+      UI.Widgets.WatchWidget
+  other-modules:
+      Paths_spade
+  hs-source-dirs:
+      src
+  default-extensions:
+      AllowAmbiguousTypes
+      DeriveLift
+      NumericUnderscores
+      DerivingStrategies
+      BangPatterns
+      ConstraintKinds
+      DataKinds
+      DefaultSignatures
+      DeriveDataTypeable
+      DeriveFoldable
+      DeriveFunctor
+      DeriveGeneric
+      DeriveTraversable
+      EmptyCase
+      FunctionalDependencies
+      FlexibleContexts
+      FlexibleInstances
+      GADTs
+      GeneralizedNewtypeDeriving
+      LambdaCase
+      MultiParamTypeClasses
+      MultiWayIf
+      NamedFieldPuns
+      NegativeLiterals
+      NumDecimals
+      OverloadedLabels
+      OverloadedStrings
+      PatternSynonyms
+      PolyKinds
+      QuasiQuotes
+      RankNTypes
+      RecordWildCards
+      ImpredicativeTypes
+      RecursiveDo
+      ScopedTypeVariables
+      StandaloneDeriving
+      TemplateHaskell
+      TupleSections
+      TypeApplications
+      TypeFamilies
+      TypeOperators
+      UndecidableInstances
+      ViewPatterns
+      PackageImports
+      InstanceSigs
+  build-depends:
+      Decimal
+    , WAVE
+    , aeson
+    , ansi-terminal
+    , base >=4.9 && <5
+    , bytestring
+    , constraints
+    , containers
+    , exceptions
+    , file-embed
+    , hedgehog
+    , hex-text
+    , hspec
+    , hspec-hedgehog
+    , monad-loops
+    , mtl
+    , ordered-containers
+    , process
+    , random
+    , scientific
+    , sdl2
+    , sdl2-mixer
+    , stm
+    , template-haskell
+    , text
+    , time
+    , unordered-containers
+    , vector
+    , vty
+  if os(windows)
+    ghc-options: -optl-mwindows -optl-mconsole -Wall -Wno-type-defaults
+    build-depends:
+        Win32 >=2.10.0.1
+  else
+    ghc-options: -Wall -Wno-type-defaults
+  default-language: Haskell2010
+  autogen-modules: Paths_spade
+
+executable spade
+  main-is: Main.hs
+  other-modules:
+      Paths_spade
+  hs-source-dirs:
+      app
+  default-extensions:
+      AllowAmbiguousTypes
+      DeriveLift
+      NumericUnderscores
+      DerivingStrategies
+      BangPatterns
+      ConstraintKinds
+      DataKinds
+      DefaultSignatures
+      DeriveDataTypeable
+      DeriveFoldable
+      DeriveFunctor
+      DeriveGeneric
+      DeriveTraversable
+      EmptyCase
+      FunctionalDependencies
+      FlexibleContexts
+      FlexibleInstances
+      GADTs
+      GeneralizedNewtypeDeriving
+      LambdaCase
+      MultiParamTypeClasses
+      MultiWayIf
+      NamedFieldPuns
+      NegativeLiterals
+      NumDecimals
+      OverloadedLabels
+      OverloadedStrings
+      PatternSynonyms
+      PolyKinds
+      QuasiQuotes
+      RankNTypes
+      RecordWildCards
+      ImpredicativeTypes
+      RecursiveDo
+      ScopedTypeVariables
+      StandaloneDeriving
+      TemplateHaskell
+      TupleSections
+      TypeApplications
+      TypeFamilies
+      TypeOperators
+      UndecidableInstances
+      ViewPatterns
+      PackageImports
+      InstanceSigs
+  ghc-options: -threaded
+  build-depends:
+      Decimal
+    , WAVE
+    , aeson
+    , ansi-terminal
+    , base >=4.9 && <5
+    , bytestring
+    , constraints
+    , containers
+    , exceptions
+    , file-embed
+    , hedgehog
+    , hex-text
+    , hspec
+    , hspec-hedgehog
+    , monad-loops
+    , mtl
+    , ordered-containers
+    , process
+    , random
+    , scientific
+    , sdl2
+    , sdl2-mixer
+    , spade
+    , stm
+    , template-haskell
+    , text
+    , time
+    , unordered-containers
+    , vector
+    , vty
+  if os(windows)
+    ghc-options: -optl-mwindows -optl-mconsole -Wall -Wno-type-defaults
+    build-depends:
+        Win32 >=2.10.0.1
+  else
+    ghc-options: -Wall -Wno-type-defaults
+  default-language: Haskell2010
+  autogen-modules: Paths_spade
+
+test-suite spade-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Common
+      Compiler.AST.ParserSpec
+      Compiler.Lexer.CommentsSpec
+      Compiler.Lexer.DelimeterSpec
+      Compiler.Lexer.IdentifierSpec
+      Compiler.Lexer.KeywordSpec
+      Compiler.Lexer.LiteralSpec
+      Compiler.Lexer.OperatorSpec
+      Compiler.Lexer.TokenSpec
+      Interpreter.InterpreterSpec
+      UI.EditorSpec
+      UI.WidgetSpec
+      Paths_spade
+  hs-source-dirs:
+      test
+  default-extensions:
+      AllowAmbiguousTypes
+      DeriveLift
+      NumericUnderscores
+      DerivingStrategies
+      BangPatterns
+      ConstraintKinds
+      DataKinds
+      DefaultSignatures
+      DeriveDataTypeable
+      DeriveFoldable
+      DeriveFunctor
+      DeriveGeneric
+      DeriveTraversable
+      EmptyCase
+      FunctionalDependencies
+      FlexibleContexts
+      FlexibleInstances
+      GADTs
+      GeneralizedNewtypeDeriving
+      LambdaCase
+      MultiParamTypeClasses
+      MultiWayIf
+      NamedFieldPuns
+      NegativeLiterals
+      NumDecimals
+      OverloadedLabels
+      OverloadedStrings
+      PatternSynonyms
+      PolyKinds
+      QuasiQuotes
+      RankNTypes
+      RecordWildCards
+      ImpredicativeTypes
+      RecursiveDo
+      ScopedTypeVariables
+      StandaloneDeriving
+      TemplateHaskell
+      TupleSections
+      TypeApplications
+      TypeFamilies
+      TypeOperators
+      UndecidableInstances
+      ViewPatterns
+      PackageImports
+      InstanceSigs
+  build-depends:
+      Decimal
+    , WAVE
+    , aeson
+    , ansi-terminal
+    , base >=4.9 && <5
+    , bytestring
+    , constraints
+    , containers
+    , exceptions
+    , file-embed
+    , hedgehog
+    , hex-text
+    , hspec
+    , hspec-discover
+    , hspec-hedgehog
+    , monad-loops
+    , mtl
+    , neat-interpolation
+    , ordered-containers
+    , process
+    , random
+    , scientific
+    , sdl2
+    , sdl2-mixer
+    , spade
+    , stm
+    , strip-ansi-escape
+    , template-haskell
+    , text
+    , time
+    , unordered-containers
+    , vector
+    , vty
+  if os(windows)
+    ghc-options: -optl-mwindows -optl-mconsole -Wall -Wno-type-defaults
+    build-depends:
+        Win32 >=2.10.0.1
+  else
+    ghc-options: -Wall -Wno-type-defaults
+  default-language: Haskell2010
diff --git a/src/Common.hs b/src/Common.hs
new file mode 100644
--- /dev/null
+++ b/src/Common.hs
@@ -0,0 +1,258 @@
+module Common
+  ( module Common
+  , TH.Lift
+  ) where
+
+import Control.Concurrent
+import Control.Exception
+import Control.Monad
+import Data.Time.Clock.POSIX (getPOSIXTime)
+import Data.List as DL
+import Data.Text as T
+import Data.Text.IO as T
+import qualified Language.Haskell.TH.Syntax as TH
+import System.Console.ANSI (Color(..), ColorIntensity(..), ConsoleLayer(..),
+                            SGR(..), Underlining(..), setSGRCode)
+import Test.Common
+
+class HReadable a where
+  hReadable :: a -> Text
+
+getTimestamp :: IO Int
+getTimestamp = (round . (* 10e12)) <$> getPOSIXTime
+
+wait :: Double -> IO ()
+wait s = threadDelay (floor $ s * 1000_000)
+
+origin :: ScreenPos
+origin = ScreenPos 0 0
+
+data Dimensions = Dimensions
+  { diW :: Int
+  , diH :: Int
+  } deriving (Eq, Show)
+
+amendHeight :: Dimensions -> (Int -> Int) -> Dimensions
+amendHeight d fn = d { diH = fn $ diH d }
+
+amendWidth :: Dimensions -> (Int -> Int) -> Dimensions
+amendWidth d fn = d { diW = fn $ diW d }
+
+moveLeft :: Int -> ScreenPos -> ScreenPos
+moveLeft i d = d { sX = sX d - i }
+
+moveRight :: Int -> ScreenPos -> ScreenPos
+moveRight i d = d { sX = sX d + i }
+
+moveUp :: Int -> ScreenPos -> ScreenPos
+moveUp i d = d { sY = sY d - i }
+
+moveDown :: Int -> ScreenPos -> ScreenPos
+moveDown i d = d { sY = sY d + i }
+
+addSp :: ScreenPos -> ScreenPos -> ScreenPos
+addSp s1 s2 = ScreenPos (sX s1 + sX s2) (sY s1 + sY s2)
+
+type IntType = Integer
+type FloatType = Double
+
+data Style = FgBg Color Color| Fg Color | Bg Color | TextUnderline | NoStyle
+  deriving (Eq, Show)
+
+data StyledText = StyledText Style [StyledText] | Plain Text
+  deriving (Show, Eq)
+
+renderLines :: [[StyledText]] -> Text
+renderLines st = T.intercalate "\n" $ (\i -> (T.concat $ stRender <$> i)) <$> st
+
+instance HasGen StyledText where
+  getGen = recursive choice
+    [Plain . T.pack <$> txGen]
+    [StyledText NoStyle <$> list (linear 1 100) getGen]
+    where
+      txGen = list (linear 1 100) (choice [lower, upper])
+
+-- Punch a hole starting from offset of size length. Columns start
+-- from 0. Returns chunks left and right of the hole.
+punchHole :: (Int, Int) -> [StyledText] -> ([StyledText], [StyledText])
+punchHole (tk, ln) sts = (stTake tk sts, stDrop (tk + ln) sts)
+
+stInsert :: [StyledText] -> Int -> StyledText -> [StyledText]
+stInsert sts cx t =
+  let (lr, rg) = punchHole (cx, stLength t) sts
+  in (lr <> [t] <> rg)
+
+stDrop :: Int -> [StyledText] -> [StyledText]
+stDrop l stsIn = snd $ DL.foldl' stDrop' (l, []) stsIn
+  where
+    stDrop' :: (Int, [StyledText]) -> StyledText -> (Int, [StyledText])
+    stDrop' (0, sts) st = (0, sts <> [st])
+    stDrop' (s, sts) (Plain t) = let
+      r = T.drop s t
+      rlen = T.length r
+      in if rlen > 0
+        then (s - (T.length t - rlen), sts <> [Plain r])
+        else (s - T.length t, sts)
+    stDrop' (s, sts) (StyledText st sts') =
+     case DL.foldl' stDrop' (s, []) sts' of
+      (s', sts''@(_:_)) -> (s', sts <> [StyledText st sts''])
+      (s', _)           -> (s', sts)
+
+stTake :: Int -> [StyledText] -> [StyledText]
+stTake l stsIn = snd $ DL.foldl' stTake' (l, []) stsIn
+  where
+    stTake' :: (Int, [StyledText]) -> StyledText -> (Int, [StyledText])
+    stTake' (0, sts) _ = (0, sts)
+    stTake' (s, sts) (Plain t) = let
+      r = T.take s t
+      in (s - T.length r, sts <> [Plain r])
+    stTake' (s, sts) (StyledText st sts') = let
+      (s', sts'') = DL.foldl' stTake' (s, []) sts'
+      in (s', sts <> [StyledText st sts''])
+
+applyStyleToRange :: (StyledText -> StyledText) -> Int -> (Int, Int) -> [StyledText] -> [StyledText]
+applyStyleToRange fn sStart (rStart, rEnd) segments =
+  let
+    sEnd = sStart + stTotalLength segments - 1
+  in if rEnd >= sStart && rStart <= sEnd
+    then
+      let
+        r1 = max 0 (rStart - sStart)
+        r2 = rEnd - (max sStart rStart) + 1
+      in (stTake r1 segments) <> (fn <$> (stTake r2 (stDrop r1 segments))) <> (stDrop (r1 + r2) segments)
+    else segments
+
+stLength :: StyledText -> Int
+stLength (Plain t)          = T.length t
+stLength (StyledText _ sts) = sum (stLength <$> sts)
+
+stTotalLength :: [StyledText] -> Int
+stTotalLength sts = sum $ stLength <$> sts
+
+-- This implimentation is not optimal and does not correctly
+-- render nested styles. But this appear to be good enough for now.
+styleToPrefix :: Style -> Text
+styleToPrefix st = T.pack $ case st of
+  FgBg fg bg -> setSGRCode [SetColor Foreground Vivid fg] <> setSGRCode [SetColor Background Vivid bg]
+  Bg bg -> setSGRCode [SetColor Background Vivid bg]
+  Fg fg -> setSGRCode [SetColor Foreground Vivid fg]
+  TextUnderline -> setSGRCode [SetUnderlining SingleUnderline]
+  NoStyle -> mempty
+
+stRender :: StyledText -> Text
+stRender st = stRender' NoStyle st
+
+mergeStyles :: Style -> Style -> Style
+mergeStyles NoStyle a     = a
+mergeStyles a NoStyle     = a
+mergeStyles (Fg a) (Bg b) = (FgBg a b)
+mergeStyles (Bg a) (Fg b) = (FgBg b a)
+mergeStyles _ a           = a
+
+stRender' :: Style -> StyledText -> Text
+stRender' _ (Plain t) = T.replace "\n" " " t
+stRender' pst (StyledText st sts) = let
+  suffix = T.pack $ setSGRCode []
+  prefix = styleToPrefix (mergeStyles pst st)
+  in T.concat (((\x -> prefix <> x <> suffix) . stRender' st) <$> sts)
+
+data ScreenPos = ScreenPos { sX :: Int, sY :: Int }
+  deriving (Eq, Ord, Show)
+
+type CursorInfo = (ScreenPos, CursorStyle)
+
+emptyCursorInfo :: CursorInfo
+emptyCursorInfo = (origin, Bar)
+
+pass :: Monad m => m ()
+pass = pure ()
+
+data CursorStyle
+  = Bar
+  | Underline
+  | Hidden
+  deriving (Eq, Show)
+
+class HasEmpty s where
+  isEmpty :: s -> Bool
+
+class HasLog m where
+  appendLog :: Show a => a -> m ()
+
+class ToSource a where
+  toSource :: a -> Text
+  toSourcePretty :: Int -> a -> Text
+  toSource a = toSourcePretty 0 a
+  toSourcePretty _ a = toSource a
+
+instance ToSource a => ToSource [a] where
+  toSourcePretty i fss = T.intercalate "\n" (toSourcePretty i <$> fss)
+
+instance ToSource a => ToSource (Maybe a) where
+  toSourcePretty i (Just x) = toSourcePretty i x
+  toSourcePretty _ Nothing  = mempty
+
+instance ToSource Text where
+  toSource = id
+
+indent :: Int -> Text
+indent i = T.replicate i "  "
+
+instance HasLog IO where
+  appendLog a = void $ try @SomeException (T.appendFile "/tmp/spade.log" $ pack (show a <> "\n"))
+
+-- Mostly for use with highlighting and not actual token parsing.
+-- This represent any tokens we want to show in editors with highlighting.
+class Highlightable a where
+  getTokenLoc :: a -> Location
+  highlight :: (StyledText, Maybe a) -> StyledText
+  pairWithTokens :: [a] -> Int -> Text -> ([(Text, Maybe a)], [a])
+  -- ^ Associate token type with their text sources
+  -- First arg is stack of tokens at current location
+  -- Second arg is the text offset at current location
+  -- Third arg is the text at the current location
+  --
+  -- This is so that in an editor, a line of text can break at somewhere in the
+  -- middle of a single token. So an editor cannot display tokenwise without more
+  -- complex tracking of current offset in source text.
+
+instance Highlightable Text where
+  highlight (x, _) = x
+  pairWithTokens _ _ t = ([(t, Nothing)], [])
+  getTokenLoc = error "No token location for text"
+
+genericPairWithTokens :: (a -> Int) -> (a -> Location) -> Int -> Text -> [a] -> ([(Text, Maybe a)], [a])
+genericPairWithTokens _ _ _ "" [] = ([], [])
+genericPairWithTokens _ _ _ src [] = ([(src, Nothing)], [])
+genericPairWithTokens getOffsetEnd getLoc offset src ti@(tk:rst)
+  | src == "" =  ([], ti)
+  | (lcOffset (getLoc tk)) > offset = let
+      tokenLen = (getOffsetEnd tk) - offset + 1
+      frag = T.take tokenLen src
+      (fragRst, ts) = genericPairWithTokens getOffsetEnd getLoc (offset+tokenLen) (T.drop tokenLen src) ti
+      in ((frag, Just tk): fragRst, ts)
+  | (lcOffset (getLoc tk)) <= offset = let
+      tokenLen = (getOffsetEnd tk) - offset + 1
+      frag = T.take tokenLen src
+      rst' = if (getOffsetEnd tk) > (offset + T.length src - 1) then ti else rst
+      (fragRst, ts) = genericPairWithTokens getOffsetEnd getLoc (offset+tokenLen) (T.drop tokenLen src) rst'
+      in ((frag, Just tk): fragRst, ts)
+  | otherwise = ([], ti)
+
+data Location = Location { lcLine :: Int, lcColumn :: Int, lcOffset :: Int }
+  deriving (TH.Lift, Show, Eq)
+
+instance HReadable Location where
+  hReadable l = T.pack $ "Line: " <> (show (lcLine l)) <> " Column: " <> show (lcColumn l) <> " Offset: " <> show (lcOffset l)
+
+emptyLocation :: Location
+emptyLocation = Location 1 1 0
+
+toInt :: IntType -> Int
+toInt x = if x > fromIntegral (maxBound @Int) then error "Int conversion out of bound" else fromIntegral x
+
+safeIndex :: [a] -> Int -> Maybe a
+safeIndex l i = fst $ DL.foldl' fn (Nothing, 0) l
+  where
+    fn (Just x, ci) _  = (Just x, ci)
+    fn (Nothing, ci) a = if ci == i then (Just a, ci) else (Nothing, ci+1)
diff --git a/src/Compiler/AST.hs b/src/Compiler/AST.hs
new file mode 100644
--- /dev/null
+++ b/src/Compiler/AST.hs
@@ -0,0 +1,11 @@
+module Compiler.AST
+  ( module Expression
+  , module FunctionDef
+  , module FunctionStatement
+  , module Program
+  ) where
+
+import Compiler.AST.Expression as Expression
+import Compiler.AST.FunctionDef as FunctionDef
+import Compiler.AST.FunctionStatement as FunctionStatement
+import Compiler.AST.Program as Program
diff --git a/src/Compiler/AST/Common.hs b/src/Compiler/AST/Common.hs
new file mode 100644
--- /dev/null
+++ b/src/Compiler/AST/Common.hs
@@ -0,0 +1,117 @@
+module Compiler.AST.Common where
+
+import Common
+import Control.Applicative
+import Control.Monad
+import Data.List.NonEmpty as NE
+import Data.Text
+
+import Compiler.AST.Parser.Common
+import Compiler.Lexer
+import Compiler.Lexer.Comments
+import Parser
+import Test.Common
+
+parseComment :: AstParser Comment
+parseComment = parseToken "comment" (\case
+  TkComment c -> Just c
+  _           -> Nothing)
+
+parseToken :: Text -> (TokenRaw -> Maybe a) -> AstParser a
+parseToken name fn = ParserM name $ liftModifier $ \s -> case s of
+  ((Token tr _ _) : rst) -> pure $ case fn tr of
+    Just a  -> (Right a, rst)
+    Nothing -> (Left CantHandle, s)
+  _ -> pure (Left CantHandle, s)
+
+parseIdentifier :: AstParser Identifier
+parseIdentifier =  parseToken "Identifier" (\case
+  TkIdentifier i -> Just i
+  _              -> Nothing)
+
+parseDelimeter :: Delimeter -> AstParser Delimeter
+parseDelimeter dl =  parseToken (toSource dl) (\case
+  TkDelimeter l -> if l == dl then Just l else Nothing
+  _             -> Nothing)
+
+parseKeyword :: Keyword -> AstParser Keyword
+parseKeyword kw =  parseToken (toSource kw) (\case
+  TkKeyword k -> if kw == k then Just k else Nothing
+  _           -> Nothing)
+
+parseOperator :: AstParser Operator
+parseOperator = parseToken "Operator" (\case
+  TkOperator k -> Just k
+  _            -> Nothing)
+
+parseOperator' :: Operator -> AstParser ()
+parseOperator' op = parseToken "Operator" (\case
+  TkOperator k -> if k == op then Just () else Nothing
+  _            -> Nothing)
+
+mandatoryNewline :: AstParser ()
+mandatoryNewline = parseToken "NL" (\case
+  TkWhitespace (NewLine _) -> Just ()
+  _                        -> Nothing)
+
+whitespace :: AstParser ()
+whitespace = void $ optional $ parseToken "WS" (\case
+  TkWhitespace (Space _) -> Just ()
+  TkWhitespace (Tab _)   -> Just ()
+  _                      -> Nothing)
+
+whitespaceNL :: AstParser ()
+whitespaceNL = void $ optional $ parseToken "NL" (\case
+  TkWhitespace (NewLine _) -> Just ()
+  _                        -> Nothing)
+
+surroundWs :: AstParser a -> AstParser a
+surroundWs p@(ParserM name _) = nameParser (name <> " surrounded by WS or NL ") $ do
+  whitespaceOrNl
+  a <- p
+  whitespaceOrNl
+  pure a
+
+whitespaceOrNl :: AstParser ()
+whitespaceOrNl = void $ many $ parseToken "WS" (\case
+  TkWhitespace (Space _)   -> Just ()
+  TkWhitespace (Tab _)     -> Just ()
+  TkWhitespace (NewLine _) -> Just ()
+  _                        -> Nothing)
+
+surroundWs_ :: AstParser a -> AstParser ()
+surroundWs_ p@(ParserM name _) = nameParser (name <> " surrounded by WS") $ void $ surroundWs p
+
+mandatory :: AstParser a -> AstParser a
+mandatory (ParserM name fn) = ParserM ("mandatory " <> name) $ \s -> do
+  case (astInput s) of
+    [] -> pure (Left $ FatalError $ CustomError $ "Expecting " <> name <> " but ran out of input ", s)
+    _ -> fn s  >>= \case
+        r@(Right _, _) -> pure r
+        (Left fe@(FatalError _), _)    ->  pure (Left fe, s)
+        (Left fe@(FatalErrorWithLocation _ _), _)    ->  pure (Left fe, s)
+        (Left e, s'') -> case astInput s'' of
+          [] -> pure (Left e, s)
+          (h: _) -> pure (Left $ FatalErrorWithLocation (tkLocation h) $ CustomError $ "Expecting " <> name <> " but got " <> (pack $ show h), s)
+
+nonEmptyGen :: Gen a -> Gen (NonEmpty a)
+nonEmptyGen p = do h <- p; t <- list (linear 0 3) p; pure (h :| t)
+
+wst :: Text
+wst = toSource $ TkWhitespace (Space 1)
+
+nlt :: Text
+nlt = toSource $ TkWhitespace (NewLine 1)
+
+parseItemListInParen :: AstParser a -> AstParser (Maybe (NonEmpty a))
+parseItemListInParen itemParser = do
+  surroundWs_ (parseDelimeter DlParenOpen)
+  args <- optional itemParser >>= \case
+    Just argHead -> do
+      argsTail <- many $ do
+        surroundWs_ $ parseDelimeter DlComma
+        itemParser
+      pure $ Just (argHead :| argsTail)
+    Nothing -> pure Nothing
+  surroundWs_ (parseDelimeter DlParenClose)
+  pure args
diff --git a/src/Compiler/AST/Expression.hs b/src/Compiler/AST/Expression.hs
new file mode 100644
--- /dev/null
+++ b/src/Compiler/AST/Expression.hs
@@ -0,0 +1,329 @@
+module Compiler.AST.Expression where
+
+import Control.Applicative
+import Control.Monad
+import Data.List.NonEmpty as NE
+import qualified Data.Map as Map
+import Data.Maybe hiding (maybe)
+import Data.Text as T
+
+import Common
+import Compiler.AST.Common
+import Compiler.AST.Parser.Common
+import Compiler.Lexer
+import Parser.Lib
+import Parser.Parser
+import Test.Common as C
+
+-- This is only used to modify nested bindings in a
+-- let statement, and is not actually a part of the expression.
+data Subscript
+ = SubscriptExpr Subscript ExpressionWithLoc
+ | PropertySubscript Subscript Identifier
+ | NoSubscript Identifier
+ deriving (Eq, Show)
+
+instance ToSource Subscript where
+  toSource = \case
+    SubscriptExpr sub i     -> T.concat [toSource sub, "[", toSource i, "]"]
+    PropertySubscript sub i -> T.concat [toSource sub, ".", toSource i]
+    NoSubscript i           -> toSource i
+
+instance HasGen Subscript where
+  getGen = recursive choice [NoSubscript <$> getGen]
+    [ SubscriptExpr <$> getGen <*> getGen
+    , PropertySubscript <$> getGen <*> getGen
+    ]
+
+data LiteralExpression
+  = LAtomic Literal
+  | LArray [ExpressionWithLoc]
+  | LObject (Map.Map Text ExpressionWithLoc)
+  deriving (Eq, Show)
+
+instance ToSource LiteralExpression where
+  toSource (LAtomic l) = toSource l
+  toSource (LArray l)  = T.concat ["[", T.intercalate ", " (toSource <$> l), "]"]
+  toSource (LObject l)  = T.concat ["{", T.intercalate ", " (toSource' <$> Map.toList l), "}"]
+    where
+      toSource' (x, y) = T.concat [toSource x, ": ", toSource y]
+
+instance HasGen LiteralExpression where
+  getGen = choice
+    [ LAtomic <$> getGen
+    , LArray <$> getGen
+    , LObject <$> (Map.fromList <$> list (linear 1 2) (do a <- (text (linear 1 50) (enum 'a' 'z')); b <- getGen; pure ("key"<>a, b)))
+    ]
+
+data SubscriptedExpression
+  = EArraySubscript ExpressionWithLoc ExpressionWithLoc
+  | EPropertySubscript ExpressionWithLoc Identifier
+  deriving (Show, Eq)
+
+instance ToSource SubscriptedExpression where
+  toSource = \case
+    EArraySubscript sub i    -> T.concat [toSource sub, "[", toSource i, "]"]
+    EPropertySubscript sub i -> T.concat [toSource sub, ".", toSource i]
+
+instance HasGen SubscriptedExpression where
+  getGen = choice
+    [ EArraySubscript <$> getGen <*> getGen
+    , EPropertySubscript <$> getGen <*> getGen
+    ]
+
+data ExpressionWithLoc = ExpressionWithLoc { elExpression :: Expression, elLocation ::  Location }
+  deriving (Show)
+
+instance Eq ExpressionWithLoc where
+  (ExpressionWithLoc e1 _) == (ExpressionWithLoc e2 _) = e1 == e2
+
+instance ToSource ExpressionWithLoc where
+  toSource (ExpressionWithLoc e1 _) = toSource e1
+
+data Expression
+  = ELiteral LiteralExpression
+  | EVar Identifier
+  | ESubscripted SubscriptedExpression
+  | EOperator Operator ExpressionWithLoc ExpressionWithLoc
+  | ECall Identifier [ExpressionWithLoc] Bool -- Boolean is used during execution to mark tail calls
+  | EConditional ExpressionWithLoc ExpressionWithLoc ExpressionWithLoc
+  | EParan ExpressionWithLoc
+  | EUnnamedFn (Maybe (NonEmpty Identifier)) ExpressionWithLoc
+  | ENegated ExpressionWithLoc
+  deriving (Show)
+
+instance Eq Expression where -- Specialized implementation to implement equality for EParen wrapped expressions
+  (ELiteral l1) == (ELiteral l2) = l1 == l2
+  (EVar l1) == (EVar l2) = l1 == l2
+  (ESubscripted l1) == (ESubscripted l2) = l1 == l2
+  (EOperator o ex1 ex2) == (EOperator o1 ex3 ex4) = (o == o1) && (ex1 == ex3) && (ex2 == ex4)
+  (ECall idef args _) == (ECall idef1 args2 _) = idef == idef1 && (args == args2)
+  (EConditional ex1 ex2 ex3) == (EConditional ex4 ex5 ex6) = (ex1 == ex4) && (ex2 == ex5) && (ex3 == ex6)
+  (EUnnamedFn args2 ex2) == (EUnnamedFn args3 ex3) = (args2 == args3) && (ex2 == ex3)
+  (EParan ex1) == (EParan ex2) = ex1 == ex2
+  (EParan ex1) == ex2 = (elExpression ex1) == ex2
+  ex1 == (EParan ex2) = ex1 == (elExpression ex2)
+  _ == _ = False
+
+instance ToSource Expression where
+  toSource = \case
+    ELiteral l           -> toSource l
+    ENegated l           -> toSource OpMinus <> toSource l
+    EVar subscript               -> toSource subscript
+    ESubscripted subscript               -> toSource subscript
+    EOperator op exp1 exp2 -> T.concat [pOpen, toSource exp1, ws, toSource op, ws, toSource exp2, pClose]
+    ECall i args _ -> T.concat $ [toSource i, toSource DlParenOpen] <> [T.intercalate ", " (toSource <$> args)] <>  [toSource DlParenClose]
+    EConditional bexp exp1 exp2 -> T.concat
+      [ pOpen
+      , toSource KwIf
+      , ws
+      , toSource DlParenOpen
+      , toSource bexp
+      , toSource DlParenClose
+      , ws
+      , toSource KwThen
+      , ws
+      , toSource exp1
+      , ws
+      , toSource KwElse
+      , ws
+      , toSource exp2
+      , pClose
+      ]
+    EParan exp1 -> T.concat [toSource exp1]
+    EUnnamedFn args expr ->
+      let
+        argsSrc = case args of
+          Just args' ->  T.intercalate ", " (toSource <$> (NE.toList args'))
+          Nothing -> ""
+      in T.concat [toSource KwFn, ws, toSource DlParenOpen, argsSrc, toSource DlParenClose, ws, toSource expr, ws, toSource KwEndFn]
+
+    where
+      ws = toSource (Space 1)
+      pOpen = toSource DlParenOpen
+      pClose = toSource DlParenClose
+
+instance HasGen ExpressionWithLoc where
+  getGen = ExpressionWithLoc <$> getGen <*> (pure emptyLocation)
+
+instance HasGen Expression where
+  getGen = recursive choice
+    [ ELiteral <$> getGen
+    , EVar <$> getGen
+    , ESubscripted <$> getGen
+    ]
+    [ EOperator <$> getGen <*> getGen <*> getGen
+    , ECall <$> getGen <*> (list (linear 1 2) getGen) <*> (pure False)
+    , EConditional <$> getGen <*> getGen <*> getGen
+    , EParan <$> getGen
+    , EUnnamedFn <$> (C.maybe (nonEmptyGen getGen)) <*> getGen
+    ]
+
+addLRecursion :: ExpressionWithLoc -> AstParser ExpressionWithLoc
+addLRecursion exp0 = do
+    exp1 <- (parseAnyDots exp0) <|> (pure exp0)
+    exp2 <- (parseAnySubscripts exp1) <|> (pure exp1)
+    ((precedenceSort <$> (operatorParser exp2)) <|> (pure exp2))
+
+parseAnyDots :: ExpressionWithLoc -> AstParser ExpressionWithLoc
+parseAnyDots exp0 = do
+  surroundWs_ (parseDelimeter DlPeriod)
+  idf <- mandatory parseIdentifier
+  addLRecursion (ExpressionWithLoc (ESubscripted (EPropertySubscript exp0 idf)) (elLocation exp0))
+
+parseAnySubscripts :: ExpressionWithLoc -> AstParser ExpressionWithLoc
+parseAnySubscripts exp0 = do
+  surroundWs_ (parseDelimeter DlSquareParenOpen)
+  indexExpr <- mandatory (astParser @ExpressionWithLoc)
+  surroundWs_ (parseDelimeter DlSquareParenClose)
+  addLRecursion (ExpressionWithLoc (ESubscripted (EArraySubscript exp0 indexExpr)) (elLocation exp0))
+
+instance HasAstParser Expression where
+  astParser = nameParser "Expression" $ elExpression <$> astParser
+
+instance HasAstParser ExpressionWithLoc where
+  astParser = nameParser "ExpressionWithLoc" $ do
+      loc <- getParserLocation
+      expr <- parserWithoutLR
+      addLRecursion (ExpressionWithLoc expr loc)
+    where
+      parserWithoutLR
+        =   literalParser
+        <|> unnamedFnParser
+        <|> callParser
+        <|> varParser
+        <|> conditionalParser
+        <|> parenthesisParser
+        <|> negatedExpressionParser
+
+unnamedFnParser :: AstParser Expression
+unnamedFnParser = surroundWs $ do
+  surroundWs_ (parseKeyword KwFn)
+  args <- parseItemListInParen parseIdentifier
+  expr <- mandatory (surroundWs (astParser @ExpressionWithLoc))
+  mandatory $ surroundWs_ $ parseKeyword KwEndFn
+  pure $ EUnnamedFn args expr
+
+parenthesisParser :: AstParser Expression
+parenthesisParser = surroundWs $ do
+  void $ parseDelimeter DlParenOpen
+  expr <- surroundWs (mandatory (astParser @ExpressionWithLoc))
+  void $ mandatory (parseDelimeter DlParenClose)
+  pure $ EParan expr
+
+negatedExpressionParser :: AstParser Expression
+negatedExpressionParser = surroundWs $ do
+  parseOperator >>= \case
+    OpMinus -> do
+      e <- (mandatory (astParser @ExpressionWithLoc))
+      pure $ ENegated e
+    _ -> cantHandle
+
+literalParser :: AstParser Expression
+literalParser = ELiteral <$> (atomicLiteralParser <|> arrayLiteralParser <|> objectLiteralParser)
+
+atomicLiteralParser :: AstParser LiteralExpression
+atomicLiteralParser = surroundWs $ do
+  l <- parseToken "Atomic Literal" (\case
+    TkLiteral l             -> Just l
+    _                       -> Nothing)
+  pure $ LAtomic l
+
+arrayLiteralParser :: AstParser LiteralExpression
+arrayLiteralParser = do
+  let itemParser = astParser @ExpressionWithLoc
+  surroundWs_ (parseDelimeter DlSquareParenOpen)
+  args <- optional itemParser >>= \case
+    Just argHead -> do
+      argsTail <- many $ do
+        surroundWs_ $ parseDelimeter DlComma
+        mandatory itemParser
+      pure (argHead : argsTail)
+    Nothing -> pure []
+  surroundWs_ $ mandatory (parseDelimeter DlSquareParenClose)
+  pure $ LArray args
+
+objectLiteralParser :: AstParser LiteralExpression
+objectLiteralParser = do
+  let mapKeyParser = (unIdentifer <$> parseIdentifier) <|> (parseToken "Map key" $ \case
+        TkLiteral (LitString t) -> Just t
+        _                       -> Nothing)
+  let itemParser = do
+        key <- mapKeyParser
+        surroundWs_ $ mandatory $ nameParser "Colon" $ parseDelimeter DlColon
+        expr <- mandatory (astParser @ExpressionWithLoc)
+        pure (key, expr)
+  surroundWs_ (parseDelimeter DlBraceParenOpen)
+  args <- optional itemParser >>= \case
+    Just argHead -> do
+      argsTail <- many $ do
+        surroundWs_ $ parseDelimeter DlComma
+        mandatory itemParser
+      pure (argHead : argsTail)
+    Nothing -> pure []
+  surroundWs_ (mandatory $ parseDelimeter DlBraceParenClose)
+  pure $ LObject $ Map.fromList args
+
+parseSubscript :: AstParser Subscript
+parseSubscript = (NoSubscript <$> parseIdentifier) >>= parseSubscript'
+
+parseSubscript' :: Subscript -> AstParser Subscript
+parseSubscript' subin =
+  optional (parseKeySubscript subin <|> parsePropertySubscript subin) >>= \case
+    Just x  -> pure x
+    Nothing -> pure subin
+
+parsePropertySubscript :: Subscript -> AstParser Subscript
+parsePropertySubscript subin = do
+  whitespaceOrNl
+  void $ parseDelimeter DlPeriod
+  identi <- mandatory $ surroundWs parseIdentifier
+  parseSubscript' (PropertySubscript subin identi)
+
+parseKeySubscript :: Subscript -> AstParser Subscript
+parseKeySubscript subin = do
+  whitespaceOrNl
+  void $ parseDelimeter DlSquareParenOpen
+  s <- mandatory $ surroundWs (astParser @ExpressionWithLoc)
+  void $ parseDelimeter DlSquareParenClose
+  parseSubscript' (SubscriptExpr subin s)
+
+varParser :: AstParser Expression
+varParser = nameParser "Variable" $ surroundWs (EVar <$> parseIdentifier)
+
+callParser :: AstParser Expression
+callParser = do
+  (idf, args) <- callParser_
+  pure $ ECall idf args False
+
+callParser_ :: AstParser (Identifier, [ExpressionWithLoc])
+callParser_ = surroundWs $ do
+  idf <- parseIdentifier
+  margs <- parseItemListInParen (astParser @ExpressionWithLoc)
+  pure (idf, fromMaybe [] (NE.toList <$> margs))
+
+conditionalParser :: AstParser Expression
+conditionalParser = surroundWs $ do
+  _ <- parseKeyword KwIf
+  whitespace
+  boolExp <- mandatory (astParser @ExpressionWithLoc)
+  surroundWs_ $ mandatory (parseKeyword KwThen)
+  exp1 <- mandatory (astParser @ExpressionWithLoc)
+  surroundWs_ $ mandatory (parseKeyword KwElse)
+  exp2 <- mandatory (astParser @ExpressionWithLoc)
+  pure $ EConditional boolExp exp1 exp2
+
+operatorParser :: ExpressionWithLoc -> AstParser ExpressionWithLoc
+operatorParser lexp = surroundWs $ do
+  optional parseOperator >>= \case
+    Just operator -> do
+      rexp <- surroundWs (mandatory (astParser @ExpressionWithLoc))
+      pure $ ExpressionWithLoc (EOperator operator lexp rexp) (elLocation lexp)
+    Nothing -> pure lexp
+
+precedenceSort :: ExpressionWithLoc -> ExpressionWithLoc
+precedenceSort (ExpressionWithLoc (EOperator op exL (ExpressionWithLoc (EOperator op1 exRL exRR) l2)) l1) =
+  if op > op1
+      then ExpressionWithLoc (EOperator op1 (ExpressionWithLoc (EOperator op (precedenceSort exL) (precedenceSort exRL)) l2) (precedenceSort exRR)) l1
+      else ExpressionWithLoc (EOperator op (precedenceSort exL) (ExpressionWithLoc (EOperator op1 (precedenceSort exRL) (precedenceSort exRR)) l2)) l1
+precedenceSort ex = ex
diff --git a/src/Compiler/AST/FunctionDef.hs b/src/Compiler/AST/FunctionDef.hs
new file mode 100644
--- /dev/null
+++ b/src/Compiler/AST/FunctionDef.hs
@@ -0,0 +1,39 @@
+module Compiler.AST.FunctionDef where
+
+import Common
+import Control.Applicative
+import Data.List as L
+import Data.List.NonEmpty as NE
+import Data.Maybe
+import Data.Text as T
+
+import Compiler.AST.Common
+import Compiler.AST.FunctionStatement
+import Compiler.AST.Parser.Common
+import Compiler.Lexer
+import Parser.Lib
+import Test.Common
+
+data FunctionDef
+  = FunctionDef Identifier [Identifier] (NonEmpty FunctionStatementWithLoc)
+  deriving (Show, Eq)
+
+instance HasAstParser FunctionDef where
+  astParser = nameParser "Function Definition" $ do
+    surroundWs_ (parseKeyword KwProc)
+    fnName <- surroundWs parseIdentifier
+    args <- parseItemListInParen parseIdentifier
+    stms <- mandatory (some (surroundWs (astParser @FunctionStatementWithLoc)))
+    mandatory $ surroundWs_ $ parseKeyword KwEndProc
+    pure $ FunctionDef fnName (fromMaybe [] (NE.toList <$> args)) (NE.fromList stms)
+
+instance ToSource FunctionDef where
+  toSourcePretty i (FunctionDef name args stms) =
+    T.concat $
+      [nlt, indent i, toSource KwProc, wst, toSource name, toSource DlParenOpen] <>
+      (L.intersperse (toSource DlComma <> " ") (toSource <$> args)) <>
+      [toSource DlParenClose] <> [nlt] <>
+      [toSourcePretty (i+1) (NE.toList stms)] <> [nlt, indent i, toSource KwEndProc]
+
+instance HasGen FunctionDef where
+  getGen = FunctionDef <$> getGen <*> getGen <*> (nonEmptyGen getGen)
diff --git a/src/Compiler/AST/FunctionStatement.hs b/src/Compiler/AST/FunctionStatement.hs
new file mode 100644
--- /dev/null
+++ b/src/Compiler/AST/FunctionStatement.hs
@@ -0,0 +1,243 @@
+module Compiler.AST.FunctionStatement where
+
+import Control.Applicative
+import Data.List as L
+import Data.List.NonEmpty as NE
+import Data.Maybe
+import Data.Text as T
+
+import Common
+import Compiler.AST.Common
+import Compiler.AST.Expression
+import Compiler.AST.Parser.Common
+import Compiler.Lexer
+import Compiler.Lexer.Comments
+import Parser.Lib
+import Parser.Parser
+import Test.Common
+
+data FunctionStatementWithLoc
+  = FunctionStatementWithLoc FunctionStatement Location
+  deriving Show
+
+instance Eq FunctionStatementWithLoc where
+  (FunctionStatementWithLoc fs1 _) == (FunctionStatementWithLoc fs2 _) = fs1 == fs2
+
+instance ToSource FunctionStatementWithLoc where
+  toSourcePretty i (FunctionStatementWithLoc fs _) = toSourcePretty i fs
+
+instance HasAstParser FunctionStatementWithLoc where
+  astParser = nameParser "FunctionStatement" $ do
+    loc <- getParserLocation
+    fsr <- astParser
+    pure $ FunctionStatementWithLoc fsr loc
+
+instance HasGen FunctionStatementWithLoc where
+  getGen = FunctionStatementWithLoc <$> getGen <*> (pure emptyLocation)
+
+data FunctionStatement
+  = Let Subscript ExpressionWithLoc
+  | Call Identifier [ExpressionWithLoc]
+  | If ExpressionWithLoc (NonEmpty FunctionStatementWithLoc) (NonEmpty FunctionStatementWithLoc)
+  | MultiIf ExpressionWithLoc (NonEmpty FunctionStatementWithLoc) (NonEmpty (ExpressionWithLoc, NonEmpty FunctionStatementWithLoc)) (Maybe (NonEmpty FunctionStatementWithLoc))
+  | IfThen ExpressionWithLoc (NonEmpty FunctionStatementWithLoc)
+  | For Identifier ExpressionWithLoc ExpressionWithLoc (NonEmpty FunctionStatementWithLoc)
+  | ForEach Identifier ExpressionWithLoc (NonEmpty FunctionStatementWithLoc)
+  | While ExpressionWithLoc (NonEmpty FunctionStatementWithLoc)
+  | Loop (NonEmpty FunctionStatementWithLoc)
+  | Return ExpressionWithLoc
+  | Break
+  | FnComment Comment
+  deriving (Show, Eq)
+
+instance ToSource FunctionStatement where
+  toSourcePretty i Break = T.concat [indent i, toSource KwBreak]
+  toSourcePretty i (FnComment c) = stripEnd (toSourcePretty i c)
+  toSourcePretty i (Let idf expr) =
+    T.concat [indent i, toSource KwLet, wst, toSource idf, wst, toSource KwAssignment, wst, toSource expr]
+  toSourcePretty i (Call idf args) =
+    T.concat $ [indent i, toSource idf, toSource DlParenOpen] <> argsSrc <> [toSource DlParenClose]
+    where
+      argsSrc = (L.intersperse (toSource DlComma <> " ") (toSource <$> args))
+  toSourcePretty i (Return expr) = T.concat $ [indent i, toSource KwReturn, wst, toSource expr]
+  toSourcePretty i (ForEach idf expr1 stms) =
+    T.concat $
+      [indent i, toSource KwForEach, wst, toSource expr1, wst, toSource KwAs, wst, toSource idf, wst, nlt] <>
+      [toSourcePretty (i+1) (NE.toList stms)] <>
+      [nlt, indent i, toSource KwEndForEach]
+  toSourcePretty i (For idf expr1 expr2 stms) =
+    T.concat $
+      [indent i, toSource KwFor, wst, toSource idf, wst, toSource KwAssignment, wst, toSource expr1, wst, toSource KwTo, wst, toSource expr2, nlt] <>
+      [toSourcePretty (i+1) (NE.toList stms)] <>
+      [nlt, indent i, toSource KwEndFor]
+  toSourcePretty i (IfThen expr stms) =
+    T.concat $
+      [indent i, toSource KwIf, wst, toSource expr, wst, toSource KwThen, nlt] <>
+      [toSourcePretty (i+1) (NE.toList stms)] <>
+      [nlt, indent i, toSourcePretty i KwEndIf]
+  toSourcePretty i (MultiIf expr stms1 rst mstms2) =
+    T.concat $
+      [indent i, toSource KwIf, wst, toSource expr, wst, toSource KwThen, nlt] <>
+      [toSourcePretty (i+1) (NE.toList stms1)] <>
+      (toSourceElseIf <$> (NE.toList rst)) <>
+      [nlt, toSourceElse] <>
+      [indent i, toSource KwEndIf]
+    where
+      toSourceElse :: Text
+      toSourceElse = case mstms2 of
+        Just stms -> T.concat $
+          [indent i, toSource KwElse, nlt] <> [toSourcePretty (i+1) (NE.toList stms)] <> [nlt]
+        Nothing -> ""
+      toSourceElseIf :: (ExpressionWithLoc, NonEmpty FunctionStatementWithLoc) -> Text
+      toSourceElseIf (expr1, stms3) =
+        T.concat $
+          [nlt, indent i, toSource KwElseIf, wst, toSource expr1, wst, toSource KwThen, nlt] <>
+          [toSourcePretty (i+1) (NE.toList stms3)]
+  toSourcePretty i (If expr stms1 stms2) =
+    T.concat $
+      [indent i, toSource KwIf, wst, toSource expr, wst, toSource KwThen, nlt] <>
+      [toSourcePretty (i+1) (NE.toList stms1)] <>
+      [nlt, indent i, toSource KwElse, nlt] <>
+      [toSourcePretty (i+1) (NE.toList stms2)] <>
+      [nlt, indent i, toSource KwEndIf]
+  toSourcePretty i (While expr1 stms) =
+    T.concat $
+      [indent i, toSource KwWhile, wst, toSource expr1, nlt] <>
+      [toSourcePretty (i+1) (NE.toList stms)] <>
+      [nlt, indent i, toSource KwEndWhile]
+  toSourcePretty i (Loop stms) =
+    T.concat $
+      [indent i, toSource KwLoop, nlt] <>
+      [toSourcePretty (i+1) (NE.toList stms)] <>
+      [nlt, indent i, toSource KwEndLoop]
+
+instance HasGen FunctionStatement where
+  getGen = recursive choice
+    [ Let <$> getGen <*> getGen
+    , Call <$> getGen <*> getGen
+    , Return <$> getGen
+    , FnComment <$> getGen
+    , pure Break
+    ]
+    [ If <$> getGen <*> (nonEmptyGen getGen) <*> (nonEmptyGen getGen)
+    , IfThen <$> getGen <*> (nonEmptyGen getGen)
+    , MultiIf <$> getGen <*> (nonEmptyGen getGen) <*> (nonEmptyGen ((,) <$> getGen <*> (nonEmptyGen getGen))) <*> (Test.Common.maybe (nonEmptyGen getGen))
+    , For <$> getGen <*> getGen <*> getGen <*> (nonEmptyGen getGen)
+    , Loop <$> (nonEmptyGen getGen)
+    , While <$> getGen <*> (nonEmptyGen getGen)
+    ]
+
+instance HasAstParser FunctionStatement where
+  astParser = nameParser "Function statement (raw)" $ do
+    (whitespaceNL <|> whitespace)
+    functionStatementParser
+
+functionStatementParser :: AstParser FunctionStatement
+functionStatementParser
+    = ifParser
+    <|> letParser
+    <|> forEachParser
+    <|> forParser
+    <|> whileParser
+    <|> loopParser
+    <|> returnParser
+    <|> breakParser
+    <|> callStatementParser
+    <|> (FnComment <$> (surroundWs parseComment))
+
+letParser :: AstParser FunctionStatement
+letParser = nameParser "Let statement" $ do
+  surroundWs_ (parseKeyword KwLet)
+  idf <- surroundWs (mandatory parseSubscript)
+  surroundWs_ (mandatory (parseToken "assignment" isAssignment))
+  expr <- surroundWs (mandatory (astParser @ExpressionWithLoc))
+  pure $ Let idf expr
+  where
+    isAssignment (TkKeyword KwAssignment) = Just ()
+    isAssignment _                        = Nothing
+
+callStatementParser :: AstParser FunctionStatement
+callStatementParser = nameParser "Function call" $ do
+  idf <- surroundWs parseIdentifier
+  args <- parseItemListInParen (astParser @ExpressionWithLoc)
+  pure $ Call idf $ fromMaybe [] (NE.toList <$> args)
+
+loopParser :: AstParser FunctionStatement
+loopParser = nameParser "Loop Statement" $ do
+  surroundWs_ (parseKeyword KwLoop)
+  stms <- mandatory (some $ astParser @FunctionStatementWithLoc)
+  surroundWs_ (mandatory (parseKeyword KwEndLoop))
+  pure $  Loop (NE.fromList stms)
+
+whileParser :: AstParser FunctionStatement
+whileParser = nameParser "While Statement" $ do
+  surroundWs_ (parseKeyword KwWhile)
+  guardBool <- surroundWs (mandatory $ astParser @ExpressionWithLoc)
+  stms <- mandatory (some $ astParser @FunctionStatementWithLoc)
+  surroundWs_ (mandatory (parseKeyword KwEndWhile))
+  pure $  While guardBool (NE.fromList stms)
+
+forParser :: AstParser FunctionStatement
+forParser = nameParser "For Statement" $ do
+  surroundWs_ (parseKeyword KwFor)
+  idf <- surroundWs (mandatory parseIdentifier)
+  surroundWs_ (mandatory $ parseKeyword KwAssignment)
+  startExp <- surroundWs (mandatory $ astParser @ExpressionWithLoc)
+  surroundWs_ (mandatory (parseKeyword KwTo))
+  endExp <- surroundWs (mandatory $ astParser @ExpressionWithLoc)
+  stms <- mandatory (some $ astParser @FunctionStatementWithLoc)
+  surroundWs_ (mandatory (parseKeyword KwEndFor))
+  pure $ For idf startExp endExp (NE.fromList stms)
+
+forEachParser :: AstParser FunctionStatement
+forEachParser = nameParser "ForEach Statement" $ do
+  surroundWs_ (parseKeyword KwForEach)
+  expr <- surroundWs (mandatory $ astParser @ExpressionWithLoc)
+  surroundWs_ (mandatory $ parseKeyword KwAs)
+  idf <- surroundWs (mandatory parseIdentifier)
+  stms <- mandatory (some $ astParser @FunctionStatementWithLoc)
+  surroundWs_ (mandatory (parseKeyword KwEndForEach))
+  pure $ ForEach idf expr (NE.fromList stms)
+
+ifParser :: AstParser FunctionStatement
+ifParser = nameParser "If Statement" $ do
+  surroundWs_ (parseKeyword KwIf)
+  expr <- surroundWs (mandatory $ astParser @ExpressionWithLoc)
+  surroundWs_ (mandatory $ parseKeyword KwThen)
+  stms1 <- mandatory (some $ astParser @FunctionStatementWithLoc)
+  surroundWs (mandatory (parseKeyword KwElseIf <|> parseKeyword KwElse <|> parseKeyword KwEndIf)) >>= \case
+    KwElseIf -> do
+      expr' <- surroundWs (mandatory $ astParser @ExpressionWithLoc)
+      surroundWs_ (mandatory $ parseKeyword KwThen)
+      stms2 <- mandatory (some $ astParser @FunctionStatementWithLoc)
+      remaining <- many $ do
+        surroundWs_ $ parseKeyword KwElseIf
+        expr1 <- surroundWs (mandatory $ astParser @ExpressionWithLoc)
+        surroundWs_ (mandatory $ parseKeyword KwThen)
+        stms3 <- mandatory (some $ astParser @FunctionStatementWithLoc)
+        pure (expr1, NE.fromList stms3)
+      surroundWs (mandatory (parseKeyword KwElse <|> parseKeyword KwEndIf)) >>= \case
+        KwElse ->  do
+          elseStms <- mandatory (some $ astParser @FunctionStatementWithLoc)
+          surroundWs_ (mandatory $ parseKeyword KwEndIf)
+          pure $ MultiIf expr (NE.fromList stms1) ((expr', NE.fromList stms2) :| remaining) (Just $ NE.fromList elseStms)
+        KwEndIf ->
+          pure $ MultiIf expr (NE.fromList stms1) ((expr', NE.fromList stms2) :| remaining) Nothing
+        _ -> fail "Impossible"
+    KwElse -> do
+      stms2 <- mandatory (some $ astParser @FunctionStatementWithLoc)
+      surroundWs_ (mandatory $ parseKeyword KwEndIf)
+      pure $ If expr (NE.fromList stms1) (NE.fromList stms2)
+    KwEndIf ->
+      pure $ IfThen expr (NE.fromList stms1)
+    _ -> fail "Impossible"
+
+returnParser :: AstParser FunctionStatement
+returnParser = nameParser "return statement" $ do
+  surroundWs_ (parseKeyword KwReturn)
+  Return <$> (surroundWs $ astParser @ExpressionWithLoc)
+
+breakParser :: AstParser FunctionStatement
+breakParser = nameParser "break statement" $ do
+  surroundWs_ (parseKeyword KwBreak)
+  pure Break
diff --git a/src/Compiler/AST/Parser/Common.hs b/src/Compiler/AST/Parser/Common.hs
new file mode 100644
--- /dev/null
+++ b/src/Compiler/AST/Parser/Common.hs
@@ -0,0 +1,44 @@
+module Compiler.AST.Parser.Common where
+
+import Compiler.Lexer
+import Parser
+import Common
+
+type AstParser a = ParserM IO AstParserState a
+
+data AstParserState = AstParserState
+  { astInput  :: [Token]
+  , astIndent :: Int
+  } deriving Show
+
+instance HaveLocation AstParserState where
+  getLocation (astInput -> (t:_)) =  tkLocation t
+  getLocation _ = error "No location for empty input"
+
+instance HasEmpty AstParserState where
+  isEmpty s = astInput s == []
+
+mkAstParserState :: [Token] -> AstParserState
+mkAstParserState ts = AstParserState ts 0
+
+liftModifier
+  :: Monad m
+  => ([Token] -> m (Either ParseError a, [Token]))
+  -> (AstParserState -> m (Either ParseError a, AstParserState))
+liftModifier fn = \s -> do
+  (fn $ astInput s) >>= \case
+    (r, rst) -> pure (r, s { astInput = rst })
+
+instance {-# OVERLAPS #-} HasLogIndent AstParserState where
+  incIndent a = a { astIndent = astIndent a + 1 }
+  decIndent a = a { astIndent = astIndent a - 1 }
+  logInfo _ _ = pure ()
+  -- logInfo name ps = liftIO $ do
+  --   T.putStr $ T.replicate (astIndent ps) " "
+  --   T.putStrLn $ T.pack $ show (name, Prelude.take 2 $ astInput ps)
+
+instance ToSource AstParserState where
+  toSource s = toSource $ astInput s
+
+class HasAstParser a where
+  astParser :: AstParser a
diff --git a/src/Compiler/AST/Program.hs b/src/Compiler/AST/Program.hs
new file mode 100644
--- /dev/null
+++ b/src/Compiler/AST/Program.hs
@@ -0,0 +1,48 @@
+module Compiler.AST.Program
+  ( module Compiler.AST.Expression
+  , module Compiler.AST.Program
+  , module Compiler.AST.FunctionDef
+  )
+where
+
+import Control.Applicative
+import Data.Text as T
+
+import Common
+import Compiler.AST.Common
+import Compiler.AST.Expression
+import Compiler.AST.FunctionDef
+import Compiler.AST.FunctionStatement
+import Compiler.AST.Parser.Common
+import Compiler.Lexer.Comments
+import Test.Common
+
+type StringType = Text
+
+data ProgramStatement
+  = FunctionDefStatement FunctionDef
+  | NakedStatement FunctionStatementWithLoc
+  | TopLevelComment Comment
+  deriving (Show, Eq)
+
+type Program = [ProgramStatement]
+
+instance ToSource ProgramStatement where
+  toSource (FunctionDefStatement def) = toSource def
+  toSource (NakedStatement fns)       = toSource fns
+  toSource (TopLevelComment cmm)      = toSource cmm
+
+instance HasAstParser ProgramStatement where
+  astParser
+    =   (FunctionDefStatement <$> astParser)
+    <|> (NakedStatement <$> (astParser @FunctionStatementWithLoc))
+    <|> (TopLevelComment <$> parseComment)
+
+instance HasGen ProgramStatement where
+  getGen = choice
+    [ FunctionDefStatement <$> getGen
+    , NakedStatement <$> getGen
+    ]
+
+instance HasAstParser Program where
+  astParser = many (astParser @ProgramStatement)
diff --git a/src/Compiler/Lexer.hs b/src/Compiler/Lexer.hs
new file mode 100644
--- /dev/null
+++ b/src/Compiler/Lexer.hs
@@ -0,0 +1,17 @@
+module Compiler.Lexer
+  ( module Compiler.Lexer.Delimeters
+  , module Compiler.Lexer.Identifiers
+  , module Compiler.Lexer.Keywords
+  , module Compiler.Lexer.Literals
+  , module Compiler.Lexer.Operators
+  , module Compiler.Lexer.Tokens
+  , module Compiler.Lexer.Whitespaces
+  ) where
+
+import Compiler.Lexer.Delimeters
+import Compiler.Lexer.Identifiers
+import Compiler.Lexer.Keywords
+import Compiler.Lexer.Literals
+import Compiler.Lexer.Operators
+import Compiler.Lexer.Tokens
+import Compiler.Lexer.Whitespaces
diff --git a/src/Compiler/Lexer/Comments.hs b/src/Compiler/Lexer/Comments.hs
new file mode 100644
--- /dev/null
+++ b/src/Compiler/Lexer/Comments.hs
@@ -0,0 +1,28 @@
+module Compiler.Lexer.Comments where
+
+import Common
+import Control.Applicative
+import Control.Monad
+import Data.Text as T
+import Parser
+import Test.Common
+
+newtype Comment = Comment Text
+  deriving (Show, Eq)
+
+commentPrefix :: Text
+commentPrefix = "-- "
+
+instance HasParser Comment where
+  parser = do
+    void $ pText commentPrefix
+    c <- many (nameParser "comment_char" $ pAny (\c -> c /= '\n'))
+    (void $ pChar '\n') <|> eof
+    incLine 1
+    pure $ Comment (pack c)
+
+instance ToSource Comment where
+  toSourcePretty i (Comment c) = indent i <> commentPrefix <> c  <> "\n"
+
+instance HasGen Comment where
+  getGen = (Comment . T.append "comment_prefix") <$> (text (linear 0 50) (enum 'a' 'z'))
diff --git a/src/Compiler/Lexer/Delimeters.hs b/src/Compiler/Lexer/Delimeters.hs
new file mode 100644
--- /dev/null
+++ b/src/Compiler/Lexer/Delimeters.hs
@@ -0,0 +1,48 @@
+module Compiler.Lexer.Delimeters where
+
+import Common
+import Control.Applicative
+import Parser
+import Test.Common
+
+data Delimeter
+  = DlColon
+  | DlComma
+  | DlPeriod
+  | DlParenOpen
+  | DlParenClose
+  | DlSquareParenOpen
+  | DlSquareParenClose
+  | DlBraceParenOpen
+  | DlBraceParenClose
+  deriving (Show, Lift, Eq, Enum)
+
+instance HasParser Delimeter where
+  parser
+    = parseFor DlColon
+    <|> parseFor DlPeriod
+    <|> parseFor DlParenOpen
+    <|> parseFor DlParenClose
+    <|> parseFor DlSquareParenOpen
+    <|> parseFor DlSquareParenClose
+    <|> parseFor DlBraceParenOpen
+    <|> parseFor DlBraceParenClose
+    <|> parseFor DlComma
+    where
+      parseFor :: Delimeter -> Parser Delimeter
+      parseFor kw = parseAndReturn (toSource kw) kw
+
+instance ToSource Delimeter where
+  toSource = \case
+    DlColon            -> ":"
+    DlPeriod           -> "."
+    DlParenOpen        -> "("
+    DlParenClose       -> ")"
+    DlSquareParenOpen  -> "["
+    DlSquareParenClose -> "]"
+    DlBraceParenOpen   -> "{"
+    DlBraceParenClose  -> "}"
+    DlComma            -> ","
+
+instance HasGen Delimeter where
+  getGen = enum DlColon DlBraceParenClose
diff --git a/src/Compiler/Lexer/Identifiers.hs b/src/Compiler/Lexer/Identifiers.hs
new file mode 100644
--- /dev/null
+++ b/src/Compiler/Lexer/Identifiers.hs
@@ -0,0 +1,33 @@
+module Compiler.Lexer.Identifiers where
+
+import Common
+import Control.Applicative
+import Data.Char
+import Data.String
+import Data.Text as T
+import Parser
+import Test.Common
+
+newtype Identifier = Identifier { unIdentifer :: Text }
+  deriving (Show, Eq, Ord)
+
+instance IsString Identifier where
+  fromString = Identifier . T.pack
+
+instance HasParser Identifier where
+  parser = (\x -> Identifier $ pack x) <$> (do
+    s <- pAny isAsciiLower
+    rst <- many (pAny (\x -> isAlphaNum x || x == '_'))
+    pure (s : rst)
+    )
+
+instance ToSource Identifier where
+  toSource = unIdentifer
+
+instance HasGen Identifier where
+  getGen = Identifier <$> choice
+    [ gen
+    , do x <- gen;  s <- int (linear 0 999); pure (x <> (pack $ show s))
+    ]
+    where
+      gen = ((pack . (\x -> "identi" <> x)) <$> (list (linear 1 10) (choice [lower, upper])))
diff --git a/src/Compiler/Lexer/Keywords.hs b/src/Compiler/Lexer/Keywords.hs
new file mode 100644
--- /dev/null
+++ b/src/Compiler/Lexer/Keywords.hs
@@ -0,0 +1,106 @@
+module Compiler.Lexer.Keywords where
+
+import Common
+import Compiler.Lexer.Whitespaces
+import Control.Applicative
+import Control.Monad
+import Parser
+import Test.Common
+
+data Keyword
+  = KwFor
+  | KwEndFor
+  | KwForEach
+  | KwAs
+  | KwEndForEach
+  | KwBreak
+  | KwWhile
+  | KwEndWhile
+  | KwLet
+  | KwIf
+  | KwEndFn
+  | KwThen
+  | KwElseIf
+  | KwElse
+  | KwEndIf
+  | KwProc
+  | KwLoop
+  | KwEndLoop
+  | KwEndProc
+  | KwTo
+  | KwReturn
+  | KwAssignment
+  | KwFn  -- We don't want to generate this in automatic tests because of the lookahead for (
+  deriving (Show, Lift,Eq, Enum, Bounded)
+
+instance HasParser Keyword where
+  parser
+    = parseFor KwForEach
+    <|> parseFor KwEndForEach
+    <|> parseFor KwFor
+    <|> parseFor KwAs
+    <|> parseFor KwEndFor
+    <|> parseFor KwWhile
+    <|> parseFor KwBreak
+    <|> parseFor KwEndWhile
+    <|> parseFor KwLet
+    <|> parseFor KwIf
+    <|> parseFor KwFn
+    <|> parseFor KwEndFn
+    <|> parseFor KwThen
+    <|> parseFor KwElseIf
+    <|> parseFor KwElse
+    <|> parseFor KwEndIf
+    <|> parseFor KwProc
+    <|> parseFor KwLoop
+    <|> parseFor KwEndLoop
+    <|> parseFor KwEndProc
+    <|> parseFor KwTo
+    <|> parseFor KwReturn
+    <|> parseFor KwAssignment
+    where
+      parseFor :: Keyword -> Parser Keyword
+      parseFor kw = do
+        pkw <- parseAndReturn (toSource kw) kw
+        case pkw of
+          KwAssignment -> pure pkw
+          KwFn -> do
+            lookAhead ((void $ do
+              void $ many (parser @Whitespace)
+              pText "(")) >>= \case
+                Just _ -> pure pkw
+                Nothing -> cantHandle
+          KwEndFn -> pure pkw
+          _ -> do
+            lookAhead ((void $ parser @Whitespace) <|> eof) >>= \case
+              Just _ -> pure pkw
+              Nothing -> cantHandle
+
+instance ToSource Keyword where
+  toSource = \case
+    KwFor        -> "for"
+    KwEndFor     -> "endfor"
+    KwForEach    -> "foreach"
+    KwAs         -> "as"
+    KwEndForEach -> "endforeach"
+    KwBreak      -> "break"
+    KwWhile      -> "while"
+    KwEndWhile   -> "endwhile"
+    KwLet        -> "let"
+    KwIf         -> "if"
+    KwFn         -> "fn"
+    KwEndFn      -> "endfn"
+    KwThen       -> "then"
+    KwElseIf     -> "elseif"
+    KwElse       -> "else"
+    KwEndIf      -> "endif"
+    KwProc       -> "proc"
+    KwLoop       -> "loop"
+    KwEndProc    -> "endproc"
+    KwEndLoop    -> "endloop"
+    KwTo         -> "to"
+    KwReturn     -> "return"
+    KwAssignment -> "="
+
+instance HasGen Keyword where
+  getGen = enum KwFor KwAssignment
diff --git a/src/Compiler/Lexer/Literals.hs b/src/Compiler/Lexer/Literals.hs
new file mode 100644
--- /dev/null
+++ b/src/Compiler/Lexer/Literals.hs
@@ -0,0 +1,87 @@
+module Compiler.Lexer.Literals where
+
+import Control.Applicative
+import Control.Monad
+import qualified Data.ByteString as BS
+import Data.Char
+import Data.Text as T
+import Data.Decimal
+import Text.Read hiding (choice)
+import Text.Hex (decodeHex, encodeHex)
+
+import Parser.Lib
+import Parser.Parser
+import Test.Common
+import Common
+
+data Literal
+  = LitString Text
+  | LitNumber IntType
+  | LitFloat Decimal -- This needs to be a decimal so that string round tripping can work.
+  | LitBool Bool
+  | LitBytes BS.ByteString
+  deriving (Eq, Show)
+
+instance HasGen Literal where
+  getGen = choice
+    [ LitString <$> (text (linear 0 50) (enum 'a' 'z'))
+    , LitFloat <$> realFrac_ (linearFrac 0 999.00)
+    , (LitNumber . fromIntegral) <$> int (linear 0 999)
+    , LitBool <$> bool
+    ]
+
+instance ToSource Literal where
+  toSource = \case
+    LitBytes t -> "0x" <> (encodeHex t) <> ""
+    LitString t -> "\"" <> t <> "\""
+    LitNumber n -> (T.pack $ show n)
+    LitFloat n  -> let o = T.pack $ show n in
+      if T.isInfixOf "." o then o else o <> ".0"
+    LitBool n   -> (T.toLower $ T.pack $ show n)
+
+instance HasParser Literal where
+  parser
+    = strLiteralParser
+    <|> floatLiteralParser
+    <|> hexLiteralParser
+    <|> intLiteralParser
+    <|> boolLiteralParser
+
+strLiteralParser :: Parser Literal
+strLiteralParser = do
+  void $ pChar '"'
+  c <- many (pAny (\c -> c /= '"'))
+  void $ pChar '"'
+  pure $ LitString (pack c)
+
+hexLiteralParser :: Parser Literal
+hexLiteralParser = do
+  void $ pText "0x"
+  c <- many (pAny isHexDigit)
+  case decodeHex $ T.pack c of
+    Just b -> pure $ LitBytes b
+    Nothing -> cantHandle
+
+intLiteralParser :: Parser Literal
+intLiteralParser = do
+  c <- many (pAny isDigit)
+  case c of
+    ['0'] -> pure $ LitNumber 0
+    ('0':_) -> cantHandle
+    _ -> case readEither c of
+      Right n -> pure $ LitNumber n
+      Left _  -> cantHandle
+
+floatLiteralParser :: Parser Literal
+floatLiteralParser = do
+  c <- many (pAny (\c -> isDigit c))
+  _ <- pChar '.'
+  m <- many (pAny isDigit)
+  case readEither (c <> "." <> m) of
+    Right a -> pure $ LitFloat a
+    Left _  -> cantHandle
+
+boolLiteralParser :: Parser Literal
+boolLiteralParser =
+  (do void $ pText "true"; pure $ LitBool True) <|>
+  (do void $ pText "false"; pure $ LitBool False)
diff --git a/src/Compiler/Lexer/Operators.hs b/src/Compiler/Lexer/Operators.hs
new file mode 100644
--- /dev/null
+++ b/src/Compiler/Lexer/Operators.hs
@@ -0,0 +1,57 @@
+module Compiler.Lexer.Operators where
+
+import Common
+import Control.Applicative
+import Parser
+import Test.Common
+
+data Operator
+  = OpAnd
+  | OpOr
+  | OpLT -- Order here sets the operator precedence (bottom ones have higher)
+  | OpGT
+  | OpLTE
+  | OpGTE
+  | OpEquality
+  | OpNotEquality
+  | OpPlus
+  | OpMinus
+  | OpStar
+  | OpDivide
+  deriving (Show, Ord, Eq, Enum, Bounded)
+
+instance HasParser Operator where
+  parser
+    = parseFor OpPlus
+    <|> parseFor OpStar
+    <|> parseFor OpMinus
+    <|> parseFor OpDivide
+    <|> parseFor OpLTE
+    <|> parseFor OpGTE
+    <|> parseFor OpLT
+    <|> parseFor OpGT
+    <|> parseFor OpEquality
+    <|> parseFor OpNotEquality
+    <|> parseFor OpAnd
+    <|> parseFor OpOr
+    where
+      parseFor :: Operator -> Parser Operator
+      parseFor kw = parseAndReturn (toSource kw) kw
+
+instance ToSource Operator where
+  toSource = \case
+    OpPlus        -> "+"
+    OpStar        -> "*"
+    OpMinus       -> "-"
+    OpDivide      -> "/"
+    OpLT          -> "<"
+    OpGT          -> ">"
+    OpLTE         -> "<="
+    OpGTE         -> ">="
+    OpEquality    -> "=="
+    OpNotEquality -> "!=="
+    OpAnd         -> "and"
+    OpOr          -> "or"
+
+instance HasGen Operator where
+  getGen = enum OpPlus OpOr
diff --git a/src/Compiler/Lexer/Tokens.hs b/src/Compiler/Lexer/Tokens.hs
new file mode 100644
--- /dev/null
+++ b/src/Compiler/Lexer/Tokens.hs
@@ -0,0 +1,118 @@
+module Compiler.Lexer.Tokens where
+
+import Control.Applicative
+import Data.List (intersperse)
+import qualified Data.Text as T
+import System.Console.ANSI (Color(..))
+
+import Common
+import Compiler.Lexer.Comments
+import Compiler.Lexer.Delimeters
+import Compiler.Lexer.Identifiers
+import Compiler.Lexer.Keywords
+import Compiler.Lexer.Literals
+import Compiler.Lexer.Operators
+import Compiler.Lexer.Whitespaces
+import Parser
+import Test.Common
+
+data Token = Token
+   { tkRaw       :: TokenRaw
+   , tkLocation  :: Location
+   , tkOffsetEnd :: Int
+   } deriving (Show)
+
+highlightTokens :: (StyledText, Maybe Token) -> StyledText
+highlightTokens (src, Nothing) = src
+highlightTokens (src, Just (tkRaw -> tk)) =
+  case tk of
+    TkKeyword _    -> StyledText (Fg Red) [src] --setSGRCode [SetColor Foreground Vivid Red]
+    TkDelimeter _  -> StyledText (Fg Blue) [src] -- setSGRCode [SetColor Foreground Vivid Blue]
+    TkLiteral _    -> StyledText (Fg Magenta) [src] -- setSGRCode [SetColor Foreground Vivid Magenta]
+    TkOperator _   -> StyledText (Fg Cyan) [src] -- setSGRCode [SetColor Foreground Vivid Cyan]
+    TkIdentifier _ -> StyledText (Fg Yellow) [src] --  setSGRCode [SetColor Foreground Vivid Yellow]
+    TkComment _    -> StyledText (Fg Black) [src]
+    TkWhitespace _ -> src
+
+instance Highlightable Token where
+  getTokenLoc = tkLocation
+  highlight = highlightTokens
+  pairWithTokens tokenStack startOffset source =
+    genericPairWithTokens tkOffsetEnd tkLocation startOffset source tokenStack
+
+instance Eq Token where
+  (Token tr1 _ _ ) == (Token tr2 _ _) = tr1 == tr2
+
+data TokenRaw
+  = TkKeyword Keyword
+  | TkDelimeter Delimeter
+  | TkLiteral Literal
+  | TkOperator Operator
+  | TkWhitespace Whitespace
+  | TkIdentifier Identifier
+  | TkComment Comment
+  deriving (Show, Eq)
+
+instance HasParser TokenRaw where
+  parser
+    = (TkComment <$> parser)
+    <|> (TkOperator <$> parser)
+    <|> (TkKeyword <$> parser)
+    <|> (TkDelimeter <$> parser)
+    <|> (TkLiteral <$> parser)
+    <|> (TkWhitespace <$> parser)
+    <|> (TkIdentifier <$> parser)
+
+-- This is expected to return the exact source
+-- that was parsed into the token, and this property
+-- is used in syntax-highlighting.
+instance ToSource TokenRaw where
+  toSource = \case
+    TkKeyword kw   -> toSource kw
+    TkDelimeter d  -> toSource d
+    TkLiteral l    -> toSource l
+    TkOperator o   -> toSource o
+    TkWhitespace w -> toSource w
+    TkComment w    -> toSource w
+    TkIdentifier i -> toSource i
+
+instance {-# OVERLAPPING #-} ToSource [TokenRaw] where
+  toSource a = T.concat $ toSource <$> a
+
+instance {-# OVERLAPPING #-} ToSource [Token] where
+  toSource a = T.concat $ toSource <$> a
+
+instance HasGen TokenRaw where
+  getGen = choice
+    [ (TkKeyword <$> getGen)
+    , (TkDelimeter <$> getGen)
+    , (TkLiteral <$> getGen)
+    , (TkOperator <$> getGen)
+    , (TkWhitespace <$> getGen)
+    , (TkComment <$> getGen)
+    ]
+
+instance {-# OVERLAPS #-} HasGen [TokenRaw] where
+  getGen =
+    (normalizeWhiteSpace . intersperse (TkWhitespace $ Space 1)) <$> (list (linear 0 500) getGen)
+
+normalizeWhiteSpace :: [TokenRaw] -> [TokenRaw]
+normalizeWhiteSpace (TkWhitespace (Space w1) : TkWhitespace (Space w2) : rst) =
+  normalizeWhiteSpace (TkWhitespace (Space (w1 + w2)) : rst)
+normalizeWhiteSpace (TkWhitespace (NewLine w1) : TkWhitespace (NewLine w2) : rst) =
+  normalizeWhiteSpace (TkWhitespace (NewLine (w1 + w2)) : rst)
+normalizeWhiteSpace (h:rst) = h : normalizeWhiteSpace rst
+normalizeWhiteSpace [] = []
+
+instance HasInnerParseable Token where
+  type InnerToken Token = TokenRaw
+  assemble = Token
+
+instance HasGen Token where
+  getGen = Token <$> getGen <*> (pure emptyLocation) <*> (pure 0)
+
+instance ToSource Token where
+  toSource (Token tr _ _) = toSource tr
+
+instance {-# OVERLAPS #-} HasGen [Token] where
+  getGen = (fmap (\tr -> Token tr emptyLocation 0)) <$> getGen
diff --git a/src/Compiler/Lexer/Whitespaces.hs b/src/Compiler/Lexer/Whitespaces.hs
new file mode 100644
--- /dev/null
+++ b/src/Compiler/Lexer/Whitespaces.hs
@@ -0,0 +1,38 @@
+module Compiler.Lexer.Whitespaces where
+
+import Common
+import Control.Applicative
+import Data.Text as T hiding (length)
+import Parser
+import Test.Common
+
+data Whitespace
+  = Space Int
+  | Tab Int
+  | NewLine Int
+  deriving (Show, Eq)
+
+instance HasParser Whitespace where
+  parser
+    =  spaceParser <|> tabParser <|> newlineParser
+    where
+      spaceParser = ((\c -> Space (length c)) <$> (some (pChar ' ')))
+      tabParser = ((\c -> Tab (length c)) <$> (some (pChar '\t')))
+      newlineParser = do
+        nls <- some (pChar '\n')
+        let lc = length nls
+        incLine lc
+        pure $ NewLine lc
+
+instance ToSource Whitespace where
+  toSource = \case
+    Space c   -> T.replicate c " "
+    Tab c     -> T.replicate c "\t"
+    NewLine c -> T.replicate c "\n"
+
+instance HasGen Whitespace where
+  getGen = choice
+    [ Space <$> (int (linear 1 10))
+    , Tab <$> (int (linear 1 10))
+    , NewLine <$> (int (linear 1 10))
+    ]
diff --git a/src/Compiler/Parser.hs b/src/Compiler/Parser.hs
new file mode 100644
--- /dev/null
+++ b/src/Compiler/Parser.hs
@@ -0,0 +1,36 @@
+module Compiler.Parser where
+
+import Control.Monad.IO.Class (liftIO)
+
+import Compiler.AST.Parser.Common
+import Compiler.AST.Program
+import Compiler.Lexer
+import Data.Text as T
+import Parser
+
+tokenize :: Text -> IO [Token]
+tokenize src = liftIO $ runParserEither (parser @[Token]) (toTextWithOffset src) >>= \case
+  Right ts -> pure ts
+  Left (ParseErrorWithParsed (Just partial) _ (FatalError IncompleteParse)) -> pure partial
+  Left _ -> pure []
+
+parse :: [Token] -> IO Program
+parse tks = parseEither tks >>= \case
+  Right p  -> pure p
+  Left err -> error $ show err
+
+parseEither :: (Show a, HasAstParser a) => [Token] -> IO (Either (ParseErrorWithParsed a) a)
+parseEither tks = runParserEither astParser (mkAstParserState tks)
+
+parseRaw :: Text -> AstParser a -> IO (Either (ParseErrorWithParsed a) a)
+parseRaw src astP = do
+  tokens <- tokenize src
+  runParserEither astP (mkAstParserState tokens)
+
+compile :: (Show a, HasAstParser a) => Text -> IO a
+compile src = compileEither src >>= \case
+  Right p  -> pure p
+  Left err -> error $ show err
+
+compileEither :: forall a. (Show a, HasAstParser a) => Text -> IO (Either (ParseErrorWithParsed a) a)
+compileEither src = tokenize src >>= parseEither
diff --git a/src/Highlighter/Highlighter.hs b/src/Highlighter/Highlighter.hs
new file mode 100644
--- /dev/null
+++ b/src/Highlighter/Highlighter.hs
@@ -0,0 +1,17 @@
+module Highlighter.Highlighter where
+
+import Data.Text
+import Common
+import System.Console.ANSI (Color(..), Underlining(..), SGR(..), setSGRCode)
+
+underlineText :: Text -> Text
+underlineText t = let
+  startCode = (setSGRCode [SetUnderlining SingleUnderline])
+  endCode = setSGRCode []
+  in (pack startCode) <> t <> (pack endCode)
+
+colorText :: Color -> Color -> Text -> StyledText
+colorText fg bg t = StyledText (FgBg fg bg) [Plain t]
+
+colorTextFg :: Color -> Text -> StyledText
+colorTextFg fg t = StyledText (Fg fg) [Plain t]
diff --git a/src/IDE/Common.hs b/src/IDE/Common.hs
new file mode 100644
--- /dev/null
+++ b/src/IDE/Common.hs
@@ -0,0 +1,30 @@
+module IDE.Common where
+
+import UI.Widgets.Common
+import Common
+import Interpreter.Common
+
+data EditAction
+  = Cut
+  | Copy
+  | Paste
+  | SelectAll
+  deriving Show
+
+data IDEEvent
+  = IDETerminalEvent TerminalEvent
+  | IDEReqEditorContent
+  | IDEParseErrorUpdate (Maybe (Location, Text))
+  | IDEACUpdateIdentifiers [Text]
+  | IDEDraw
+  | IDEToggleHelp
+  | IDESave
+  | IDEExit
+  | IDEEdit EditAction
+  | IDEBeautify
+  | IDEClearLog
+  | IDEStep
+  | IDEDebugUpdate (Maybe DebugState)
+  | IDERun
+  deriving Show
+
diff --git a/src/IDE/Help.hs b/src/IDE/Help.hs
new file mode 100644
--- /dev/null
+++ b/src/IDE/Help.hs
@@ -0,0 +1,317 @@
+module IDE.Help where
+
+import Compiler.Lexer.Keywords (Keyword)
+import Compiler.Lexer.Operators (Operator)
+import Control.Concurrent
+import Control.Concurrent.STM (atomically)
+import Control.Concurrent.STM.TChan
+import Control.Monad as CM
+import qualified Data.ByteString as BS
+import Data.IORef
+import qualified Data.List as DL
+import qualified Data.Map as Map
+import qualified Data.Set as Set
+import qualified Data.Text as T
+import Data.Text.Encoding (decodeUtf8)
+import Data.Vector as V
+
+import Common
+import Compiler.AST.Program
+import Compiler.Parser
+import IDE.Common
+import IDE.Help.Contents
+import IDE.Help.Parser
+import UI.Widgets
+import UI.Widgets.Layout
+import UI.Widgets.NullWidget
+
+newtype SearchPage = SearchPage Text
+  deriving Show
+
+type HelpPage = Either SearchPage FilePath
+
+data HelpWidget = HelpWidget
+  { hwSearchWidget          :: WRef EditorWidget
+  , hwContentWidget         :: WRef EditorWidget
+  , hwMainLayout            :: WRef LayoutWidget
+  , hwVisible               :: Bool
+  , hwCursorRef             :: MVar Int
+  , hwTokenProcessingThread :: Maybe ThreadId
+  , hwIdeEventsChan         :: TChan IDEEvent
+  , hwTokensRef             :: IORef (V.Vector DisplayToken)
+  , hwContent               :: Map.Map FilePath (Text, V.Vector DisplayToken)
+  , hwContentIndex          :: Map.Map Text (FilePath, Int)
+  , hwCurrentPage           :: Maybe HelpPage
+  , hwHistory               :: [(HelpPage, Int)]
+  , hwSearchResult          :: MVar (SearchPage, V.Vector HToken)
+  , hwSearchChan            :: TChan SearchPage
+  }
+
+instance Drawable HelpWidget where
+  draw r = do
+    w <- readWRef r
+    case hwVisible w of
+      True -> do
+        (liftIO $ tryTakeMVar (hwSearchResult w)) >>= \case
+          Just (sp, htokens) -> do
+            let (contentText, dTokens) = convertToDisplay htokens
+            loadTokens r (contentText, dTokens)
+            modifyWRef r (\wd -> wd { hwCurrentPage = Just (Left sp) })
+          Nothing -> pass
+        draw $ hwMainLayout w
+      _    -> pass
+  setVisibility r v = do
+    modifyWRef r (\hw -> hw { hwVisible = v })
+  getVisibility r =
+    hwVisible <$> readWRef r
+
+instance KeyInput HelpWidget where
+  getCursorInfo r = do
+    w <- readWRef r
+    getCursorInfo $ hwSearchWidget w
+  handleInput :: forall m. WidgetC m => WRef HelpWidget -> KeyEvent -> m ()
+  handleInput r ke = do
+    w <- readWRef r
+    let
+      pushCurrentPageToHistory :: m ()
+      pushCurrentPageToHistory = do
+        case hwCurrentPage w of
+          Just p -> do
+            cw <- readWRef (hwContentWidget w)
+            let currentOffset = ewCursor cw
+            modifyWRef r (\hw ->
+              hw { hwHistory = (p, currentOffset) : hwHistory hw }
+              )
+          Nothing -> pure ()
+
+      loadPage' :: HelpPage -> Int -> m ()
+      loadPage' page idx  = do
+        loadPage r page
+        modifyWRef (hwContentWidget w) (\x -> let
+          newEw = putCursor Nothing idx x
+          newSo = computeScrollOffset newEw (fst $ ewCursorInfo newEw)
+          in newEw { ewScrollOffset = if newSo /= ewScrollOffset x then newSo + (div (diH $ ewDim newEw) 2) else newSo }
+          )
+    case ke of
+      KeyCtrl _ _ _ Esc    -> do
+        case hwHistory w of
+          [] -> pure ()
+          ((p, o) :rst) -> do
+            modifyWRef r (\hw -> hw { hwHistory = rst })
+            loadPage' p o
+      KeyCtrl _ _ _ ArrowUp    -> handleInput (hwContentWidget w) ke
+      KeyCtrl _ _ _ ArrowDown  -> handleInput (hwContentWidget w) ke
+      KeyCtrl _ _ _ ArrowRight -> handleInput (hwContentWidget w) ke
+      KeyCtrl _ _ _ ArrowLeft  -> handleInput (hwContentWidget w) ke
+      KeyCtrl _ _ _ Return     -> do
+        tokens <- liftIO $ readIORef (hwTokensRef w)
+        case V.find (\case
+          DisplayToken (DTLink True _) _ _ -> True
+          _                                -> False) tokens of
+            Just (DisplayToken (DTLink _ n) _ _) -> case Map.lookup n (hwContentIndex w) of
+              Just (page, idx) -> do
+                pushCurrentPageToHistory
+                loadPage' (Right page) idx
+              Nothing -> pure ()
+            _ -> pure ()
+      _                        -> do
+        handleInput (hwSearchWidget w) ke
+        swr <- readWRef (hwSearchWidget w)
+        loadPage' (Left (SearchPage $ ewContent swr)) 0
+
+    ew <- readWRef (hwContentWidget w)
+    void $ liftIO $ tryPutMVar (hwCursorRef w) (ewCursor ew)
+
+instance Moveable HelpWidget where
+  move r pos = do
+    w <- hwMainLayout <$> readWRef r
+    move w pos
+  getPos r = do
+    w <- hwMainLayout <$> readWRef r
+    getPos w
+  getDim r = do
+    w <- hwMainLayout <$> readWRef r
+    getDim w
+  resize r fn = do
+    w <- hwMainLayout <$> readWRef r
+    resize w fn
+
+instance Widget HelpWidget where
+  hasCapability (DrawableCap _) = Just Dict
+  hasCapability (KeyInputCap _) = Just Dict
+  hasCapability (MoveableCap _) = Just Dict
+  hasCapability _               = Nothing
+
+searchThread
+  :: Map.Map Text (FilePath, Int)
+  -> TChan SearchPage
+  -> MVar (SearchPage, V.Vector HToken)
+  -> IO ()
+searchThread contentIndex spChan resultRef = do
+  let indexKeys = Map.keys contentIndex
+  forever $ do
+    SearchPage (T.toLower -> skey) <- atomically $ readTChan spChan
+    let results = DL.foldl' (foldFn skey) [] indexKeys
+    putMVar resultRef (SearchPage skey, V.fromList (mkHToken <$> results))
+  where
+    mkHToken x = HToken x emptyLocation 0
+    foldFn :: Text -> [HTokenRaw] -> Text -> [HTokenRaw]
+    foldFn k rs hd = if k `T.isInfixOf` (T.toLower hd) then (Link hd : NewLine : rs) else rs
+
+loadPage
+  :: WidgetC m
+  => WRef HelpWidget
+  -> HelpPage
+  -> m ()
+loadPage ref page' = do
+  HelpWidget { hwContent = map' } <- readWRef ref
+  case page' of
+    Right page -> case Map.lookup page map' of
+      Nothing -> pure ()
+      Just (contentText, dtTokens) -> do
+        loadTokens ref (contentText, dtTokens)
+        modifyWRef ref (\hw -> hw { hwCurrentPage = Just page' })
+    Left (SearchPage "") -> loadPage ref (Right "toc.md")
+    Left sk -> do
+      w <- readWRef ref
+      liftIO $ atomically $ writeTChan (hwSearchChan w) sk
+
+loadTokens
+  :: WidgetC m
+  => WRef HelpWidget
+  -> (Text, V.Vector DisplayToken)
+  -> m ()
+loadTokens helpWidgetRef (contentText, dtTokens) = do
+  HelpWidget { hwIdeEventsChan = ideEventRef, hwContentWidget = ewRef, hwTokensRef = tokensRef} <- readWRef helpWidgetRef
+  liftIO $ writeIORef tokensRef dtTokens
+  modifyWRef ewRef (\ew -> ew { ewScrollOffset = 0, ewContent = contentText, ewShowVirtualCursor = True })
+  liftIO $ atomically $ writeTChan ideEventRef IDEDraw
+
+collectLinks
+  :: Map.Map FilePath (Text, V.Vector DisplayToken)
+  -> [Text]
+collectLinks docs = Map.foldrWithKey mapFoldFn mempty docs
+  where
+    mapFoldFn
+      :: FilePath
+      -> (Text, V.Vector DisplayToken)
+      -> [Text]
+      -> [Text]
+    mapFoldFn fp (_, tkns) idx = V.foldl' (foldFn fp) idx tkns
+
+    foldFn
+      :: FilePath
+      -> [Text]
+      -> DisplayToken
+      -> [Text]
+    foldFn _ idx (DisplayToken (DTLink _ t) _ _) = t : idx
+    foldFn _ idx _                               = idx
+
+buildIndex
+  :: Map.Map FilePath (Text, V.Vector DisplayToken)
+  -> Map.Map Text (FilePath, Int)
+buildIndex docs = Map.foldrWithKey mapFoldFn mempty docs
+  where
+    mapFoldFn
+      :: FilePath
+      -> (Text, V.Vector DisplayToken)
+      -> Map.Map Text (FilePath, Int)
+      -> Map.Map Text (FilePath, Int)
+    mapFoldFn fp (_, tkns) idx = V.foldl' (foldFn fp) idx tkns
+
+    foldFn
+      :: FilePath
+      -> Map.Map Text (FilePath, Int)
+      -> DisplayToken
+      -> Map.Map Text (FilePath, Int)
+    foldFn fp idx (DisplayToken (DTHeading _ t) l _) = Map.insert t (fp, lcOffset l) idx
+    foldFn _ idx _ = idx
+
+tokenProcessing :: TChan IDEEvent -> MVar Int -> IORef (V.Vector DisplayToken) -> IO ()
+tokenProcessing ideEventRef cursorRef tokensRef = forever $ do
+  cursor <- takeMVar cursorRef
+  modifyIORef tokensRef (V.map (mapFn cursor))
+  liftIO $ atomically $ writeTChan ideEventRef IDEDraw
+  where
+    mapFn :: Int -> DisplayToken -> DisplayToken
+    mapFn cursor dt = if cursor >= (lcOffset $ dtLocation dt) && cursor <= (dtOffsetEnd dt)
+      then case dt of
+        DisplayToken (DTLink _ n) l e -> DisplayToken (DTLink True n) l e
+        _                             -> dt
+      else case dt of
+        DisplayToken (DTLink _ n) l e -> DisplayToken (DTLink False n) l e
+        _                             -> dt
+
+parsedDocContent :: IO (Map.Map FilePath (Text, V.Vector DisplayToken))
+parsedDocContent = UI.Widgets.foldM foldFn mempty docContent
+  where
+    foldFn
+      :: Map.Map FilePath (Text, V.Vector DisplayToken)
+      -> (FilePath, BS.ByteString)
+      -> IO (Map.Map FilePath (Text, V.Vector DisplayToken))
+    foldFn idx (fp,sbs) = do
+      parseHelp (decodeUtf8 sbs) >>= \case
+        Right tokens -> do
+          checkSamples fp tokens
+          pure $ Map.insert fp (convertToDisplay (V.fromList tokens)) idx
+        Left err -> do
+          putStrLn err
+          error $ "Error while parsing help file: " <> fp
+
+checkSamples :: FilePath -> [HToken] -> IO ()
+checkSamples fp = CM.mapM_ checkOneSample . fmap tkRaw
+  where
+    checkOneSample :: HTokenRaw -> IO ()
+    checkOneSample (Code _ tks) = parseEither @Program tks >>= \case
+      Right _  -> pure ()
+      Left err -> do
+        error ("Error parsing code in help file:"<> fp <> ":" <> show err)
+    checkOneSample _ = pure ()
+
+helpWidget :: WidgetC m => [Text] -> TChan IDEEvent -> m (WRef HelpWidget)
+helpWidget builtins ideEventRef = do
+    -- The help window
+    let helpWidgetDimDistrbution = \case
+          1 -> [1]
+          2 -> [0, 1]
+          _ -> error "Unsupported widget count"
+    let helpWidgetTopDimDistrbution = \case
+          2 -> [0.1, 0.9]
+          _ -> error "Unsupported widget count"
+    cursorRef <- liftIO $ newMVar @Int 0
+    searchResultRef <- liftIO newEmptyMVar
+    searchChan <- liftIO (newTChanIO @SearchPage)
+    helpLayoutRef <- layoutWidget Vertical helpWidgetDimDistrbution Nothing
+    helptopLayoutRef <- layoutWidget Horizontal helpWidgetTopDimDistrbution Nothing
+    helpSearchInput <- editor (\_ -> pure []) Nothing
+
+    tokensRef <- liftIO $ newIORef @(V.Vector DisplayToken) V.empty
+
+    tokenProcessingThreadId <- liftIO $ forkIO (tokenProcessing ideEventRef cursorRef tokensRef)
+
+    helpContent <- editor (\_ -> pure []) (Just $ SomeTokenStream (V.toList <$> readIORef tokensRef))
+    modifyWRef helpSearchInput (\hw -> hw { ewParams = (ewParams hw) { epLineNos = False} })
+    modifyWRef helpContent (\hw -> hw { ewShowVirtualCursor = True, ewParams = (ewParams hw) { epLineNos = False} })
+    addWidget helptopLayoutRef "helpsearchinput" helpSearchInput
+    (newWRef nullWidget) >>= addWidget helptopLayoutRef "helpsearchspacer"
+    addWidget helpLayoutRef "helpsearch" helptopLayoutRef
+    addWidget helpLayoutRef "helpcontent" helpContent
+    docs <- liftIO parsedDocContent
+    let docIndex = buildIndex docs
+    void $ liftIO $ forkIO (searchThread docIndex searchChan searchResultRef)
+    let builtinOperators = "=" : ((\x -> toSource $ toEnum @Operator x) <$> [0 .. fromEnum $ maxBound @Operator])
+    let builtinKeywords = (\x -> toSource $ toEnum @Keyword x) <$> [0 .. fromEnum $ maxBound @Keyword]
+    let linksInDocs = Set.fromList $ collectLinks docs
+    let linksInIndex = Set.fromList $ Map.keys docIndex
+    let builtinSymbols = Set.fromList builtins
+    let missingLinks = Set.difference linksInDocs linksInIndex
+    let missingDocs = Set.difference (Set.difference builtinSymbols (Set.fromList (builtinKeywords <> builtinOperators))) linksInIndex
+    if (Set.size(missingLinks) > 0)
+      then error $ "Missing links in document: " <> show missingLinks
+      else do
+        if (Set.size(missingDocs) > 0)
+          then error $ "Missing documents for: " <> show missingDocs
+          else do
+            r <- newWRef $ HelpWidget helpSearchInput helpContent helpLayoutRef True cursorRef (Just tokenProcessingThreadId) ideEventRef tokensRef docs docIndex Nothing [] searchResultRef searchChan
+            loadPage r (Right "toc.md")
+            pure r
diff --git a/src/IDE/Help/Contents.hs b/src/IDE/Help/Contents.hs
new file mode 100644
--- /dev/null
+++ b/src/IDE/Help/Contents.hs
@@ -0,0 +1,12 @@
+module IDE.Help.Contents where
+
+import qualified Data.ByteString as BS
+import Data.FileEmbed
+
+docContent :: [(FilePath, BS.ByteString)]
+docContent = $(embedDir "docs")
+
+-- helpTokens :: TH.Q TH.Exp
+-- helpTokens = TH.runIO $ do
+--   tokens <- parseHelp (decodeUtf8 docContent)
+--   TH.lift tokens
diff --git a/src/IDE/Help/Parser.hs b/src/IDE/Help/Parser.hs
new file mode 100644
--- /dev/null
+++ b/src/IDE/Help/Parser.hs
@@ -0,0 +1,193 @@
+module IDE.Help.Parser where
+
+import Control.Applicative
+import Control.Monad
+import Data.Text as T
+import qualified Data.Vector as V
+import System.Console.ANSI (Color(..))
+
+import Common
+import qualified Compiler.Lexer.Tokens as CT
+import Parser.Lib
+import Parser.Parser
+
+data DisplayTokenRaw
+  = DTPlain Text
+  | DTHeading Int Text
+  | DTCode CT.Token
+  | DTInlineCode CT.Token
+  | DTLink Bool Text
+  | DTNewLine
+  deriving (Show, Eq)
+
+instance ToSource DisplayTokenRaw where
+  toSource = \case
+    DTPlain t -> t
+    DTHeading s t -> "\n" <> renderHeader s t
+    DTCode t -> toSource t
+    DTInlineCode t -> toSource t
+    DTLink _ t -> t
+    DTNewLine -> "\n"
+
+underlineWith :: Text -> Text -> Text
+underlineWith s t = t <> "\n" <> (T.replicate (T.length t) s)
+
+renderHeader :: Int -> Text -> Text
+renderHeader 1 t = underlineWith "*" t
+renderHeader 2 t = underlineWith "_" t
+renderHeader 3 t = underlineWith "=" t
+renderHeader 4 t = underlineWith "-" t
+renderHeader 5 t = "| " <> t
+renderHeader _ t = t
+
+data DisplayToken = DisplayToken { dtTr :: DisplayTokenRaw, dtLocation :: Location, dtOffsetEnd :: Int }
+  deriving (Show, Eq)
+
+convertToDisplay :: V.Vector HToken -> (Text, V.Vector DisplayToken)
+convertToDisplay tokens = let (_, src, dt) = V.foldl' fn (0, "", V.empty) tokens in (src, dt)
+  where
+  fn :: (Int, Text, V.Vector DisplayToken) -> HToken -> (Int, Text, V.Vector DisplayToken)
+  fn (lastOffset, srcin, tkns) a = let
+    thisTokens = case tkRaw a of
+      PlainText t -> V.singleton (DTPlain t)
+      Heading _ s t -> V.singleton (DTHeading s t)
+      Code _ t -> (V.cons DTNewLine (V.fromList (DTCode <$> t))) <> (V.singleton DTNewLine)
+      InlineCode _ t -> V.fromList $ DTInlineCode <$> t
+      Link t -> V.singleton (DTLink False t)
+      NewLine -> V.singleton (DTNewLine)
+    in V.foldl' fn2 (lastOffset, srcin, tkns) thisTokens
+
+  fn2 :: (Int, Text, V.Vector DisplayToken) -> DisplayTokenRaw -> (Int, Text, V.Vector DisplayToken)
+  fn2 (thisOffset, srcin, tkns) tkraw = let
+    thisSize = T.length $ toSource tkraw
+    in (thisOffset + thisSize, srcin <> (toSource tkraw), V.snoc tkns (DisplayToken tkraw (Location 0 0 thisOffset) (thisOffset + thisSize - 1)))
+
+data HTokenRaw
+  = PlainText Text
+  | Heading Text Int Text
+  | Code Text [CT.Token]
+  | InlineCode Text [CT.Token]
+  | Link Text
+  | NewLine
+  deriving (Show, Eq)
+
+instance ToSource HTokenRaw where
+  toSource (PlainText t)    = t
+  toSource (Heading t _ _)  = t
+  toSource (Code t _)       = t
+  toSource (InlineCode t _) = t
+  toSource (Link t) = t
+  toSource NewLine          = "\n"
+
+data HToken = HToken
+   { tkRaw       :: HTokenRaw
+   , tkLocation  :: Location
+   , tkOffsetEnd :: Int
+   } deriving (Show)
+
+type HelpParser a = ParserM IO TextWithOffset a
+
+parseChar :: (Char -> Bool) -> HelpParser Char
+parseChar fn = ParserM "Help - Parse Char" $ \t ->
+  case uncons (twText t) of
+    Just (c, rst) -> case fn c of
+      True -> pure (Right c, t { twText = rst, twLocation = moveCols (twLocation t) 1 })
+      False -> pure (Left CantHandle, t)
+    Nothing -> pure (Left CantHandle, t)
+
+plainTextParser :: HelpParser HTokenRaw
+plainTextParser = nameParser "Plain Text" $ (PlainText . T.pack) <$> (some $ parseChar fn)
+  where
+    fn '#'  = False
+    fn '`'  = False
+    fn '\n' = False
+    fn _    = True
+
+parseNLorStartWith :: HelpParser a -> HelpParser a
+parseNLorStartWith pin = nlOrStart >> pin
+  where
+  nlOrStart = (void $ parseChar (\c -> c == '\n')) <|> (ParserM "Help - Steam start" $ \t ->
+    case lcOffset $ twLocation t of
+      0 -> pure (Right (), t)
+      _ -> pure (Left CantHandle, t))
+
+linkParser :: HelpParser HTokenRaw
+linkParser = do
+  void $ pText "#link: "
+  name <- some (parseChar (/= '#'))
+  void $ pChar '#'
+  pure $ Link (T.pack name)
+
+headingParser :: Int -> HelpParser HTokenRaw
+headingParser s = parseNLorStartWith (headingParser' s)
+
+codeParser :: HelpParser HTokenRaw
+codeParser = parseNLorStartWith codeParser'
+
+headingParser' :: Int -> HelpParser HTokenRaw
+headingParser' hs = ParserM "Help Heading" $ \t ->
+  let
+    prefix = (T.replicate hs "#") <> " "
+    prefixLen = T.length prefix
+  in if T.take prefixLen (twText t) == prefix
+    then do
+      let rst = T.drop prefixLen (twText t)
+      let c = T.takeWhile (\c' -> c' /= '\n') rst
+      let ln = T.length c
+      pure (Right $ Heading (prefix <> c) hs c, t { twText = T.drop ln rst, twLocation = moveCols (twLocation t) (ln+prefixLen)})
+    else pure (Left CantHandle, t)
+
+codeParser' :: HelpParser HTokenRaw
+codeParser' = ParserM "Help Code" $ \t ->
+  case T.take 4 (twText t) of
+    "```\n" -> do
+      let rst = T.drop 4 (twText t)
+      let (c, rst') = T.breakOn "\n```\n" rst
+      let ln = T.length c
+      runParserEither (parser @[CT.Token]) (toTextWithOffset c) >>= \case
+        Left _ -> pure (Left CantHandle, t)
+        Right tokens ->
+          pure (Right $ Code ("```\n" <> c <> "\n```\n") tokens, t { twText = T.drop 5 rst', twLocation = moveCols (twLocation t) (ln+9)})
+    _ -> pure (Left CantHandle, t)
+
+inlineCodeParser :: HelpParser HTokenRaw
+inlineCodeParser = nameParser "Help Inline Code" $ do
+  void $ parseChar (== '`')
+  tokens <- parser @[CT.Token]
+  void $ parseChar (== '`')
+  pure $ InlineCode ("`" <> (T.concat $ toSource <$> tokens) <> "`") tokens
+
+instance HasParser HTokenRaw where
+  parser = linkParser <|> (headingParser 1 <|> headingParser 2 <|> headingParser 3 <|> headingParser 4 <|> headingParser 5) <|> codeParser <|> inlineCodeParser <|> plainTextParser <|> newLineParser
+
+instance HasInnerParseable HToken where
+  type InnerToken HToken = HTokenRaw
+  assemble = HToken
+
+instance Highlightable DisplayToken where
+  getTokenLoc (DisplayToken _ loc _) = loc
+  highlight = highlightTokens
+  pairWithTokens tokenStack startOffset source =
+    genericPairWithTokens dtOffsetEnd dtLocation startOffset source tokenStack
+
+highlightTokens :: (StyledText, Maybe DisplayToken) -> StyledText
+highlightTokens (src, Nothing) = src
+highlightTokens (src, Just (dtTr -> tk')) =
+  case tk' of
+    DTPlain _ -> src
+    DTHeading _ _-> StyledText (Fg Green) [src]
+    DTCode tk -> highlight @CT.Token (src, Just tk)
+    DTInlineCode tk -> highlight @CT.Token (src, Just tk)
+    DTLink True _ -> StyledText (Fg Red) [src]
+    DTLink _ _ -> StyledText (Fg Blue) [src]
+    DTNewLine -> src
+
+newLineParser :: HelpParser HTokenRaw
+newLineParser = nameParser "Help Newline" $ do
+  void $ parseChar (== '\n')
+  pure NewLine
+
+parseHelp :: Text -> IO (Either String [HToken])
+parseHelp t = runParserEither (some (parser @HToken)) (toTextWithOffset t) >>= \case
+  Right r  -> pure $ Right r
+  Left err -> pure $ Left (show err)
diff --git a/src/IDE/IDE.hs b/src/IDE/IDE.hs
new file mode 100644
--- /dev/null
+++ b/src/IDE/IDE.hs
@@ -0,0 +1,599 @@
+module IDE.IDE where
+
+import Common
+import Compiler.Lexer
+import Compiler.Parser
+import Control.Concurrent
+import Control.Concurrent.STM (TChan, TVar, atomically, dupTChan, modifyTVar,
+                               newTChan, newTChanIO, newTVarIO, readTChan,
+                               readTVar, readTVarIO, writeTChan, writeTVar)
+import Control.Exception
+import Control.Monad
+import Data.IORef
+import qualified Data.List as DL
+import Data.Map as Map
+import Data.Maybe
+import qualified Data.Set as S
+import Data.Text as T
+import Data.Text.IO as T
+
+import Compiler.AST.Program
+import IDE.Common
+import IDE.Help
+import Interpreter
+import Interpreter.Common
+import Interpreter.Initialize
+import Parser.Parser
+import UI.Widgets
+import UI.Widgets.AutoComplete
+import UI.Widgets.Layout
+import UI.Widgets.LogWidget
+import UI.Widgets.WatchWidget
+
+data IDEState = IDEState
+  { idsDebugThreadId       :: Maybe ThreadId
+  , idsDebugIn             :: Maybe (MVar DebugIn)
+  , idsDebugOut            :: Maybe (MVar DebugOut)
+  , idsDebugThreadInput    :: Maybe (TChan TerminalEvent)
+  , idsCodeAutocompletions :: [(Text, Text)]
+  , idsCodeParseError      :: Maybe (Location, Text)
+  , idsKeyInputReciever    :: Maybe SomeKeyInputWidget
+  }
+
+ideStartState :: IDEState
+ideStartState = IDEState Nothing Nothing Nothing Nothing [] Nothing Nothing
+
+type Clipboard = (Text -> IO (), IO (Maybe Text))
+
+type IDEM a = WidgetM IO a
+
+editorId :: Text
+editorId = "editor"
+
+watchWidgetId :: Text
+watchWidgetId = "watch"
+
+extractBuiltIns :: IO ([(Text, Text)], [(Text, Text)])
+extractBuiltIns = do
+  debugIn <- newEmptyMVar
+  debugOut <- newEmptyMVar
+  sdlWindowsRef <- newIORef []
+  scope <- isGlobalScope . snd <$> runStateT loadBuiltIns (emptyIs sdlWindowsRef debugIn debugOut (error "Input stream not available"))
+  let keywords = (\x -> let s = toSource $ toEnum @Keyword x in (s, s)) <$> [0 .. fromEnum $ maxBound @Keyword]
+  let builtIns = DL.foldl' extractOneDoc [] (Map.assocs scope)
+  pure $ (keywords, builtIns)
+  where
+    extractOneDoc :: [(Text, Text)] -> (ScopeKey, Value) -> [(Text, Text)]
+    extractOneDoc in_ (SkIdentifier (unIdentifer ->ide), BuiltIn (BuiltinVal (ObjectValue m))) =
+      in_ <> ((\k -> let a = ide <> "." <> k in (a, a)) <$> Map.keys m)
+    extractOneDoc in_ (SkIdentifier ide, BuiltIn (BuiltinCallWithDoc s)) =
+      (unIdentifer ide, T.concat [
+        unIdentifer ide,
+        "(",
+        T.intercalate ", " ((\(l, r) -> T.concat [l, ": ", r]) <$> extractDoc s),
+        ")"
+        ]
+      ) : in_
+    extractOneDoc in_ (SkIdentifier ide, BuiltIn (BuiltinCall _)) = (unIdentifer ide, unIdentifer ide) : in_
+    extractOneDoc in_ (SkOperator op, _) = (toSource op, toSource op) : in_
+    extractOneDoc in_ _ = in_
+
+getAutocompleteSuggestions :: MonadIO m => TVar IDEState -> [(Text, Text)] -> Text -> m [(Text, Text)]
+getAutocompleteSuggestions ideStateRef dict key = do
+  acs <- liftIO $ idsCodeAutocompletions <$> readTVarIO ideStateRef
+  pure $ Prelude.filter fn (acs <> dict)
+  where
+    fn :: (Text, Text) -> Bool
+    fn (i, _) = T.isPrefixOf key i
+
+getTerminalSize' :: IO Dimensions
+getTerminalSize' = do
+    s <- getTerminalSizeIO
+    case s of
+      Just (xs, ys) -> pure $ Dimensions xs ys
+      _             -> error "Cannot read terminal size"
+
+-- This thread reads the editor contents and try to parse into
+-- tokens and ast. And then fills some autocomplete suggestions and
+-- makes any parse error location.
+codeProcessingThread :: IORef [Token] -> TChan IDEEvent -> MVar Text -> IO ()
+codeProcessingThread tokensRef ideEventRef editorContentRef =  forever $ do
+  liftIO $ atomically $ writeTChan ideEventRef IDEReqEditorContent
+  code <- takeMVar editorContentRef
+  tokens <- tokenize code
+  liftIO $ writeIORef tokensRef tokens
+  mError <- parseEither @Program tokens >>= \case
+    Left (ParseErrorWithParsed _ loc (FatalError a)) -> pure $ Just (loc, T.pack $ show a)
+    Left (ParseErrorWithParsed _ _ (FatalErrorWithLocation loc fpe)) -> pure $ Just (loc, T.pack $ show fpe)
+    _ -> pure Nothing
+  liftIO $ atomically $ writeTChan ideEventRef $ IDEParseErrorUpdate mError
+  liftIO $ atomically $ writeTChan ideEventRef $ IDEACUpdateIdentifiers $ S.toList $ S.fromList $ catMaybes $ filterIdentifiers <$> tokens
+  wait 1
+  where
+    filterIdentifiers :: Token -> Maybe Text
+    filterIdentifiers Token { tkRaw = (TkIdentifier (unIdentifer -> idf))} = Just idf
+    filterIdentifiers _ = Nothing
+
+-- | A type to differentiat debuging mode in IDE when input events
+-- need to be fed to the interpreted program, and not to the IDE.
+data InputRouting
+  = RouteIDE
+  | RouteProgram (TChan TerminalEvent)
+
+instance Show InputRouting where
+  show RouteIDE = "Route To IDE"
+  show (RouteProgram _) = "Route To Program"
+
+runIDE :: FilePath -> TChan TerminalEvent -> IO ()
+runIDE filePath inputBroadcastChan = do
+  dim <- getTerminalSize'
+  content <- try (T.readFile filePath) >>= \case
+    Right c                   -> pure c
+    Left (_ :: SomeException) -> pure ""
+  (keywords, builtIns) <- extractBuiltIns
+  ideStateRef <- newTVarIO ideStartState
+  ideEventsRef <- liftIO newTChanIO
+
+  clipboardRef <- newIORef @(Maybe Text) Nothing
+  let clipboard = (\t -> modifyIORef clipboardRef (\_ -> Just t), readIORef clipboardRef)
+  editorContentMVar <- newEmptyMVar @Text
+
+  tokensRef <- newIORef @[Token] []
+
+  void $ forkIO $ codeProcessingThread tokensRef ideEventsRef editorContentMVar
+
+  inputRouteToRef <- newTVarIO RouteIDE
+
+  inputChan <- liftIO $ atomically $ dupTChan inputBroadcastChan
+
+  void $ liftIO $ forkIO $ forever $ do
+    (atomically $ readTVar inputRouteToRef) >>= appendLog
+    atomically $ do
+      kv <- readTChan inputChan
+      readTVar inputRouteToRef >>= \case
+        RouteIDE          -> writeTChan ideEventsRef (IDETerminalEvent kv)
+        RouteProgram chan -> writeTChan chan kv
+
+  void $ runWidgetM $ do
+
+    -- The editor
+    editorRef <- editor (getAutocompleteSuggestions ideStateRef (keywords <> ((\(a, b) -> (a <>"()", b)) <$> builtIns))) (Just $ SomeTokenStream (readIORef tokensRef))
+    setContent editorRef content
+    liftIO $ atomically $ modifyTVar ideStateRef (\ids -> ids { idsKeyInputReciever = Just $ SomeKeyInputWidget editorRef })
+
+    -- Watch widget
+    watchWidgetRef <- watchWidget
+
+    let dimensionDistributionFn = \case
+          1 -> [1]
+          2 -> [0.85, 0.15]
+          3 -> [0.70, 0.15, 0.15]
+          a -> error $ "Unsupported widget count:" <> show a
+
+    watchAndLogLayout <- layoutWidget Horizontal dimensionDistributionFn Nothing
+    logRef <- logWidget
+    insertLog logRef "Welcome to S.P.A.D.E: The Simple Programming And Debugging Environment."
+    insertLog logRef "By Sandeep.C.R"
+    addWidget watchAndLogLayout "log" logRef
+    addWidget watchAndLogLayout watchWidgetId watchWidgetRef
+
+    helpWidgetRef <- helpWidget ((Prelude.head . T.split (== '.'). fst) <$> builtIns) ideEventsRef
+    -- The autocomplete widget
+    ac <- autoComplete
+    modifyWRef editorRef (\ed -> ed { ewAutocompleteWidget = Just $ SomeWidgetRef ac })
+
+    -- The Outermost layout
+    layoutRef <- layoutWidget Vertical dimensionDistributionFn (Just $ SomeKeyInputWidget editorRef)
+    modifyWRef layoutRef (\lw -> lw { lowFloatingContent = Just $ SomeWidgetRef ac })
+
+    addWidget layoutRef editorId editorRef
+    addWidget layoutRef "helpwidget" helpWidgetRef
+    addWidget layoutRef "watchandlog" watchAndLogLayout
+    setVisibility helpWidgetRef False
+    setTextFocus layoutRef editorId
+
+    -- The menu
+    let menu = Menu
+          [ ("File", SubMenu ["Save (F6)", "Beautify", "Exit"])
+          , ("Edit", SubMenu ["Cut", "Copy", "Paste", "Select All"])
+          , ("Run", SubMenu ["Run (F5)", "Step (Ctrl+F8)"])
+          , ("Windows", SubMenu ["Clear log"])
+          , ("Help", SubMenu ["Contents"])
+          ] Nothing
+    menuContainerRef <- menuContainer (SomeWidgetRef layoutRef) menu (selectionHandler ideEventsRef)
+
+    modifyWRef menuContainerRef (\mc -> mc { mcwDim = dim })
+
+    modify (\ws -> ws { wsCursorWidget = Just $ SomeKeyInputWidget editorRef })
+
+    -- Initial screen paint
+    csInitialize dim
+    draw menuContainerRef
+    csDraw
+
+    -- A snippet that refresh the screen.
+    let ideRedraw = do
+          csClear
+          draw menuContainerRef
+          csDraw
+
+    let getActiveEditor = do
+          (getVisibility editorRef) >>= \case
+            True -> pure editorRef
+            _ -> getVisibility helpWidgetRef >>= \case
+              True -> hwContentWidget <$> (readWRef helpWidgetRef)
+              _    -> error "No visible editor"
+    let
+      launchProgram = do
+        source <- getContent editorRef
+        liftIO (compileEither source >>= \case
+            Right p  -> do
+              debugIn <- newEmptyMVar
+              debugOut <- newEmptyMVar
+              programInputChan <- liftIO $ atomically newTChan
+              threadId <- forkOS (void $ do
+                liftIO $ atomically $ do
+                  writeTVar inputRouteToRef (RouteProgram programInputChan)
+                interpret debugIn debugOut programInputChan p)
+              atomically $
+                modifyTVar ideStateRef
+                  (\idestate ->
+                      idestate
+                        { idsDebugThreadId = Just threadId
+                        , idsDebugIn = Just debugIn
+                        , idsDebugOut = Just debugOut
+                        , idsDebugThreadInput = Just programInputChan
+                        })
+              pure $ Right (threadId, debugIn, debugOut, programInputChan)
+            Left err -> do
+              pure $ Left (hReadable err)
+            )
+
+    uiLoop ideEventsRef (\case
+      IDEToggleHelp ->  do
+        getVisibility helpWidgetRef >>= \case
+          True -> do
+            setVisibility helpWidgetRef False
+            setVisibility editorRef True
+            setVisibility watchAndLogLayout True
+            modify (\ws -> ws { wsCursorWidget = Just $ SomeKeyInputWidget editorRef })
+            liftIO $ atomically (modifyTVar ideStateRef (\is -> is { idsKeyInputReciever = Just $ SomeKeyInputWidget editorRef }))
+          False -> do
+            setVisibility helpWidgetRef True
+            setVisibility editorRef False
+            setVisibility watchAndLogLayout False
+            modify (\ws -> ws { wsCursorWidget = Just $ SomeKeyInputWidget helpWidgetRef })
+            liftIO $ atomically (modifyTVar ideStateRef (\is -> is { idsKeyInputReciever = Just $ SomeKeyInputWidget helpWidgetRef }))
+        d <- getScreenBounds
+        csInitialize d
+        draw menuContainerRef
+        csDraw
+        pure True
+      IDESave -> do
+        c <- getContent editorRef
+        liftIO $ T.writeFile filePath c
+        insertLog logRef ("Saved to " <> (T.pack filePath) <>".")
+        pure True
+      IDEEdit Copy -> do
+        ew <- getActiveEditor >>= readWRef
+        case ewSelection ew of
+          Just (r1, r2) -> let
+            contents = ewContent ew
+            removeLength = (r2 - r1 + 1)
+            copyContent = T.take (removeLength + 1) (T.drop r1 contents)
+            in liftIO $ (fst clipboard) copyContent
+          Nothing -> pass
+        pure True
+      IDEEdit Cut -> do
+        modifyWRefM editorRef (\ew -> case ewSelection ew of
+          Just (r1, r2) -> let
+            contents = ewContent ew
+            (p1, p2) = T.splitAt r1 contents
+            removeLength = (r2 - r1 + 1)
+            cutContent = T.take removeLength p2
+            in do
+              liftIO $ (fst clipboard) cutContent
+              let newCursor = max 0 (r1 - 1)
+              pure $ putCursor Nothing newCursor $ ew { ewSelection = Nothing, ewContent = p1 <> (T.drop removeLength p2) }
+          Nothing -> pure ew
+          )
+        ideRedraw
+        pure True
+      IDEEdit Paste -> do
+        modifyWRefM editorRef (\ew -> do
+          let
+            contents = ewContent ew
+            (p1, p2) = T.splitAt (ewCursor ew) contents
+          (liftIO (snd clipboard)) >>= \case
+            Nothing -> pure ew
+            Just insertContent ->
+              pure $ putCursor Nothing (ewCursor ew + T.length insertContent) $ ew { ewContent = p1 <> insertContent <> p2 }
+          )
+        ideRedraw
+        pure True
+      IDEEdit SelectAll -> do
+        modifyWRef editorRef (\ew -> ew { ewSelection = Just (0, (T.length $ ewContent ew) - 1) })
+        ideRedraw
+        pure True
+      IDEClearLog -> do
+        modifyWRef logRef (\lw -> lw { lwContent = [], lwScrollOffset = 0 })
+        pure True
+      IDEBeautify -> do
+        insertLog logRef "Formatting..."
+        code <- getContent editorRef
+        (liftIO $ compileEither @Program code) >>= \case
+          Right a -> do
+            setContent editorRef (T.strip $ toSource a)
+            liftIO $ atomically $ writeTChan ideEventsRef IDEDraw
+          Left (ParseErrorWithParsed _ _ e) -> insertLog logRef ("Parse error:" <> T.pack (show e))
+        pure True
+      IDEDraw -> do
+        d <- getScreenBounds
+        csInitialize d
+        draw menuContainerRef
+        csDraw
+        pure True
+      IDEACUpdateIdentifiers acs -> do
+        liftIO $ atomically (modifyTVar ideStateRef (\is -> is { idsCodeAutocompletions = (\x -> (x, x)) <$> acs }))
+        pure True
+      IDEParseErrorUpdate mError -> do
+        liftIO $ atomically (modifyTVar ideStateRef (\is -> is { idsCodeParseError = mError }))
+        idestate <- liftIO $ readTVarIO ideStateRef
+        case idsCodeParseError idestate of
+          Just (loc, _) -> modifyWRef editorRef (\ew -> ew { ewParseErrorLocation = Just loc })
+          Nothing -> modifyWRef editorRef (\ew -> ew { ewParseErrorLocation = Nothing })
+        ideRedraw
+        pure True
+      IDEReqEditorContent ->  do
+        c <- getContent editorRef
+        liftIO $ void $ putMVar editorContentMVar c
+        pure True
+      IDEExit ->  do
+        pure False
+      IDERun -> do
+        (\x' -> (idsDebugThreadId x', idsDebugIn x', idsDebugOut x', idsDebugThreadInput x')) <$> liftIO (readTVarIO ideStateRef) >>= \case
+          -- If we are in a debug session, just run to completion.
+          (Just threadId, Just debugIn, Just debugOut, Just programInputChan) -> do
+            insertLog logRef "Continuing debug session to end..."
+            executeAndUpdateIDEDebugState inputRouteToRef logRef editorRef ideEventsRef ideStateRef (Just Run) threadId debugIn debugOut programInputChan
+          _ -> do
+            -- Or else launch a new run of the program.
+            clearscreen
+            setCursorPosition 0 0
+            launchProgram >>= \case
+              Right (threadId, debugIn, debugOut, programInputChan) -> do
+                insertLog logRef "Starting..."
+                executeAndUpdateIDEDebugState inputRouteToRef logRef editorRef ideEventsRef ideStateRef (Just Run) threadId debugIn debugOut programInputChan
+              Left err -> do
+                insertLog logRef $ "Compile Error:" <> err
+                liftIO $ atomically $ writeTChan ideEventsRef IDEDraw
+        pure True
+      IDEStep -> do
+        csSetCursorPosition 0 0
+        (\x' -> (idsDebugThreadId x', idsDebugIn x', idsDebugOut x', idsDebugThreadInput x')) <$> liftIO (readTVarIO ideStateRef) >>= \case
+          -- A debugging session exists already.
+          -- Send the step command, wait for updated state to arrive.
+          (Just threadId, Just debugIn, Just debugOut, Just programInputChan) ->
+            executeAndUpdateIDEDebugState inputRouteToRef logRef editorRef ideEventsRef ideStateRef (Just StepIn) threadId debugIn debugOut programInputChan
+          _ -> do
+            -- No debugging session exist. Launch one.
+            launchProgram >>= \case
+              Right (threadId, debugIn, debugOut, programInputChan) -> do
+                insertLog logRef "Starting..."
+                executeAndUpdateIDEDebugState inputRouteToRef logRef editorRef ideEventsRef ideStateRef Nothing threadId debugIn debugOut programInputChan
+              Left err -> insertLog logRef $ "Compile Error:" <> err
+        pure True
+      IDETerminalEvent (TerminalResize w h)  -> do
+        let dim' = Dimensions w h
+        csInitialize dim'
+        modifyWRef menuContainerRef (\mc -> mc { mcwDim = dim' })
+        draw menuContainerRef
+        adjustScrollOffset editorRef
+        draw menuContainerRef
+        csDraw
+        pure True
+      IDEDebugUpdate mds -> do
+        case mds of
+          Just ds -> do
+            modifyWRef editorRef (\ew -> ew { ewReadOnly = True, ewDebugLocation = Just $ dsLocation ds, ewCursor = lcOffset (dsLocation ds) - 1 })
+            modifyWRef watchWidgetRef (\ww -> ww { wwVisible = True, wwContent = scopeToWatchItems $ dsScope ds })
+            case dsCurrenEvaluation ds of
+              Just cv -> insertLog logRef ("Evaluating: " <> cv)
+              Nothing -> pass
+
+          Nothing -> do
+            modifyWRef editorRef (\ew -> ew { ewReadOnly = False, ewDebugLocation = Nothing })
+            modifyWRef watchWidgetRef (\ww -> ww { wwVisible = False })
+        liftIO $ atomically $ writeTChan ideEventsRef IDEDraw
+        pure True
+      IDETerminalEvent (TerminalKey ev)  -> case ev of
+        KeyChar True _ _ 'c' -> pure False
+        KeyChar True _ _ 'C' -> pure False
+        k -> do
+          updateMenuOnKey menuContainerRef k >>= \case
+            True -> pure ()
+            False -> shortcutsHandler ideEventsRef k >>= \case
+              True -> pure ()
+              False -> idsKeyInputReciever <$> (liftIO $ readTVarIO ideStateRef) >>= \case
+                Just (SomeKeyInputWidget w) -> handleInput w k
+                Nothing                     -> pure ()
+          idestate <- liftIO $ readTVarIO ideStateRef
+          case idsCodeParseError idestate of
+            Just (loc, _) -> modifyWRef editorRef (\ew -> ew { ewParseErrorLocation = Just loc })
+            Nothing -> modifyWRef editorRef (\ew -> ew { ewParseErrorLocation = Nothing })
+          ideRedraw
+          pure True
+      )
+    where
+      executeAndUpdateIDEDebugState inputRouteToRef logRef editorRef ideEventRef ideStateRef mcmd threadId debugIn debugOut programInputChan  = do
+          case mcmd of
+            Just cmd -> do
+                liftIO $ do
+                  -- Empty out any pending debug message, and discard it.
+                  void $ tryTakeMVar debugOut
+                  -- before sending in a debug input.
+                  atomically $ writeTVar inputRouteToRef (RouteProgram programInputChan)
+                  putMVar debugIn cmd
+            _        -> pure ()
+          (liftIO (catch (do
+              dd <- takeMVar debugOut
+              atomically $ writeTVar inputRouteToRef RouteIDE
+              pure dd
+              )
+              -- Possibly handle a control-c triggered by user to break
+              -- a long runing operation.
+              (\(e :: SomeException) -> do
+                killThread threadId
+                pure $ Errored $ T.pack $ show e
+              )
+            )) >>= \case
+            -- Update the IDE debug state with the received debug state.
+            DebugData ds -> do
+              liftIO $ atomically $ writeTChan ideEventRef (IDEDebugUpdate $ Just ds)
+            Errored err -> do
+              liftIO $ atomically $ modifyTVar ideStateRef (\i -> ideStartState { idsKeyInputReciever = idsKeyInputReciever i })
+              modifyWRef editorRef (\ew -> ew { ewReadOnly = False })
+              insertLog logRef err
+              liftIO $ atomically $ do
+                writeTChan ideEventRef (IDEDebugUpdate Nothing)
+            Finished -> do
+              modifyWRef editorRef (\ew -> ew { ewReadOnly = False })
+              liftIO $ atomically $ modifyTVar ideStateRef (\i -> ideStartState { idsKeyInputReciever = idsKeyInputReciever i })
+              insertLog logRef "Finished program"
+              liftIO $ atomically $ do
+                writeTChan ideEventRef (IDEDebugUpdate Nothing)
+
+updateMenuOnKey :: WidgetC m => WRef MenuContainerWidget -> KeyEvent -> m Bool
+updateMenuOnKey ref kv = do
+  w <- readWRef ref
+  case (kv, menuActive $ mcwMenu w) of
+    (KeyCtrl _ _ _ Esc, Just _) -> do
+        modifyWRef ref (\w' -> w' { mcwMenu = (mcwMenu w) { menuActive = Nothing }})
+        setCursorVisibility True
+        pure True
+    (KeyChar _ _ True c, _) -> do
+      foldM_ (\x (n, _) -> case T.uncons (T.toLower n) of
+        Nothing -> pure $ x + 1
+        Just (c1, _) ->
+          if c == c1
+            then do
+              modifyWRef ref (\w' -> w' { mcwMenu = (mcwMenu w) { menuActive = Just (x, 0) }})
+              setCursorVisibility False
+              pure $ x + 1
+            else
+              pure $ x + 1) 0 (menuItems $ mcwMenu w)
+      pure True
+    (KeyCtrl _ _ _ ArrowLeft, Just (x, _))
+      | x > 0 -> do
+          modifyWRef ref (\w' -> w' { mcwMenu = (mcwMenu w) { menuActive = Just (x - 1, 0) }})
+          pure True
+      | otherwise -> do
+          modifyWRef ref (\w' -> w' { mcwMenu = (mcwMenu w) { menuActive = Just (Prelude.length (menuItems $ mcwMenu w) - 1, 0) }})
+          pure True
+
+    (KeyCtrl _ _ _ ArrowRight, Just (x, _)) -> do
+      let menuItemsLength = Prelude.length $ menuItems $ mcwMenu w
+      let newX = x + 1
+      if newX < menuItemsLength
+        then do
+          modifyWRef ref (\w' -> w' { mcwMenu = (mcwMenu w) { menuActive = Just (newX, 0) }})
+          pure True
+        else do
+          modifyWRef ref (\w' -> w' { mcwMenu = (mcwMenu w) { menuActive = Just (0, 0) }})
+          pure True
+
+    (KeyCtrl _ _ _ ArrowUp, Just (x, y))
+      | y > 0 -> do
+              modifyWRef ref (\w' -> w' { mcwMenu = (mcwMenu w) { menuActive = Just (x, y - 1) }})
+              pure True
+      | otherwise -> do
+          case getSubmenuAt (mcwMenu w) x of
+            Nothing -> pure True
+            Just (SubMenu smi) -> do
+              modifyWRef ref (\w' -> w' { mcwMenu = (mcwMenu w) { menuActive = Just (x, Prelude.length smi - 1) }})
+              pure True
+
+    (KeyCtrl _ _ _ ArrowDown, Just (x, y)) -> do
+      case getSubmenuAt (mcwMenu w) x of
+        Just (SubMenu smi) -> do
+          let newY = y + 1
+          if newY < (Prelude.length smi)
+            then do
+              modifyWRef ref (\w' -> w' { mcwMenu = (mcwMenu w) { menuActive = Just (x, newY) }})
+              pure True
+            else do
+              modifyWRef ref (\w' -> w' { mcwMenu = (mcwMenu w) { menuActive = Just (x, 0) }})
+              pure True
+        Nothing -> pure True
+    (KeyCtrl _ _ _ Return, Just (x, y)) -> do
+      (mcwSelectionHandler w) ref (x, y)
+      setCursorVisibility True
+      pure True
+    _ -> pure False
+
+scopeToWatchItems :: Scope -> [(Text, Text)]
+scopeToWatchItems s = Map.foldlWithKey' (\o k v ->
+  let
+    vKey = case k of
+      SkIdentifier i -> unIdentifer i
+      SkOperator op  -> T.pack $ show op
+    vText = case v of
+      StringValue t                    -> Just t
+      NumberValue (NumberInt t)        -> Just $ T.pack $ show t
+      NumberValue (NumberFractional t) -> Just $ T.pack $ show t
+      BoolValue t                      -> Just $ T.pack $ show t
+      ArrayValue t                     -> Just $ T.pack $ show t
+      ObjectValue t                    -> Just $ T.pack $ show t
+      _                                -> Nothing
+
+  in case vText of
+    Just t  -> (vKey, t) : o
+    Nothing -> o
+  ) [] s
+
+selectionHandler
+  :: forall m. WidgetC m
+  => TChan IDEEvent
+  -> WRef MenuContainerWidget
+  -> (Int, Int)
+  -> m ()
+selectionHandler ideEventsRef ref selection = do
+  case selection of
+    (0, 0) -> liftIO $ atomically $ writeTChan ideEventsRef IDESave
+    (0, 1) -> liftIO $ atomically $ writeTChan ideEventsRef IDEBeautify
+    (0, 2) -> liftIO $ atomically $ writeTChan ideEventsRef IDEExit
+    (1, 0) -> liftIO $ atomically $ writeTChan ideEventsRef (IDEEdit Cut)
+    (1, 1) -> liftIO $ atomically $ writeTChan ideEventsRef (IDEEdit Copy)
+    (1, 2) -> liftIO $ atomically $ writeTChan ideEventsRef (IDEEdit Paste)
+    (1, 3) -> liftIO $ atomically $ writeTChan ideEventsRef (IDEEdit SelectAll)
+    (2, 0) -> liftIO $ atomically $ writeTChan ideEventsRef IDERun
+    (2, 1) -> liftIO $ atomically $ writeTChan ideEventsRef IDEStep
+    (3, 0) -> liftIO $ atomically $ writeTChan ideEventsRef IDEClearLog
+    (4, 0) -> liftIO $ atomically $ writeTChan ideEventsRef IDEToggleHelp
+    _      -> pass
+  modifyWRef ref (\mcw -> mcw { mcwMenu = (mcwMenu mcw) { menuActive = Nothing } })
+  liftIO $ atomically $ writeTChan ideEventsRef IDEDraw
+
+shortcutsHandler
+  :: forall m. WidgetC m
+  => TChan IDEEvent
+  -> KeyEvent
+  -> m Bool
+shortcutsHandler ideEventRef kv = do
+    case kv of
+      KeyCtrl _ _ _ (Fun 6) -> do
+        liftIO $ atomically $ writeTChan ideEventRef IDESave
+        pure True
+
+      KeyCtrl _ _ _ (Fun 1) -> do
+        liftIO $ atomically $ writeTChan ideEventRef IDEToggleHelp
+        pure True
+
+      KeyCtrl True _ _ (Fun 8) -> do
+        liftIO $ atomically $ writeTChan ideEventRef IDEStep
+        pure True
+
+      KeyCtrl _ _ _ (Fun 5) -> do
+        liftIO $ atomically $ writeTChan ideEventRef IDERun
+        pure True
+      _               -> pure False
diff --git a/src/Interpreter.hs b/src/Interpreter.hs
new file mode 100644
--- /dev/null
+++ b/src/Interpreter.hs
@@ -0,0 +1,62 @@
+module Interpreter where
+
+import Prelude hiding (map)
+
+import Control.Concurrent.MVar
+import Control.Exception (SomeException, displayException)
+import Control.Concurrent.STM
+import Control.Monad
+import Control.Monad.Catch (catch)
+import Data.IORef (newIORef)
+import Data.Map as M hiding (map)
+import Data.Text as T hiding (index, map)
+
+import Compiler.AST.Program
+import Control.Monad.State.Strict
+import Interpreter.Common
+import Interpreter.Initialize
+import Interpreter.Interpreter
+import Interpreter.Lib.SDL
+import UI.Widgets.Common
+
+interpret_ :: TChan TerminalEvent -> Program -> IO InterpreterState
+interpret_ teventChan prg = do
+  debugIn <- newEmptyMVar
+  debugOut <- newEmptyMVar
+  sdlWindowsRef <- newIORef []
+  let istate = emptyIs sdlWindowsRef debugIn debugOut teventChan
+  interpret' (istate { isRunMode = NormalMode })  prg
+
+interpret :: MVar DebugIn -> MVar DebugOut -> TChan TerminalEvent -> Program -> IO InterpreterState
+interpret debugIn debugOut teventsChan prg = do
+  sdlWindowsRef <- newIORef []
+  interpret' (emptyIs sdlWindowsRef debugIn debugOut teventsChan) prg
+
+interpret' :: InterpreterState -> Program -> IO InterpreterState
+interpret' istate prg = snd <$> do
+  let debugOut = isDebugOut istate
+  flip runStateT istate $ catch (do
+    loadBuiltIns
+    interpretPassOne prg
+    interpretPassTwo prg
+    cleanupSDL
+    liftIO $ putMVar debugOut Finished
+    ) (\(e :: SomeException) -> do
+      cleanupSDL
+      liftIO $ putMVar debugOut (Errored $ T.pack $ displayException e))
+
+interpretPassOne :: Program -> InterpretM ()
+interpretPassOne x = mapM_ (\a -> fn a) x
+  where
+    fn :: ProgramStatement -> InterpretM ()
+    fn (FunctionDefStatement fdef@(FunctionDef name _ _)) = modify $ mapGlobalScope $
+      \s -> insert (SkIdentifier name) (ProcedureValue fdef) s
+    fn _ = pure ()
+
+interpretPassTwo :: Program -> InterpretM ()
+interpretPassTwo x = mapM_ (\a -> fn a) x
+  where
+    fn :: ProgramStatement -> InterpretM ()
+    fn (FunctionDefStatement (FunctionDef _ _ _)) = pure ()
+    fn (NakedStatement fs)                        = void $ executeStatement fs
+    fn (TopLevelComment _)                        = pure ()
diff --git a/src/Interpreter/Common.hs b/src/Interpreter/Common.hs
new file mode 100644
--- /dev/null
+++ b/src/Interpreter/Common.hs
@@ -0,0 +1,556 @@
+module Interpreter.Common where
+
+import Control.Concurrent
+import Control.Concurrent.STM.TChan
+import Control.Concurrent.STM.TMVar
+import Control.Exception
+import Control.Monad.Catch (MonadCatch, MonadThrow, throwM)
+import qualified Data.ByteString as BS
+import Data.IORef
+import Data.Kind (Type)
+import Data.List.NonEmpty
+import Data.Map as M
+import Data.Maybe (isJust)
+import Data.Proxy
+import Data.Text as T
+import qualified Data.Vector as V
+import qualified Data.Vector.Storable as VS
+import Data.Word
+import Foreign.C.Types
+import GHC.OverloadedLabels
+import GHC.TypeLits
+import SDL hiding (Keycode, Scancode)
+import qualified SDL as SDL
+import qualified SDL.Mixer as SDLM
+
+import Common
+import UI.Widgets.Common
+import Compiler.AST.Program
+import Compiler.Lexer
+
+audioSampleCount :: Int
+audioSampleCount = 44100
+
+type Sample = SDLM.Chunk
+
+data BuiltinVal
+  = BuiltinCallWithDoc SomeBuiltin
+  | BuiltinCall BuiltInFn
+  | BuiltinVal Value
+
+instance Show BuiltinVal where
+  show _ = "_builtin_"
+
+newtype SDLKeyboardStateCallback = SDLKeyboardStateCallback (SDL.Scancode -> Bool)
+
+data SDLValue
+  = Renderer SDL.Renderer
+  | Color (V4 Int)
+  | Keycode SDL.Keycode
+  | Scancode SDL.Scancode
+  | KeyboardState SDLKeyboardStateCallback
+  | SoundSample Sample
+
+instance Show SDLValue where
+  show (Keycode k)       = "(SDL_KEY)" <> show k
+  show (Scancode k)      = "(SDL_SCANCODE)" <> show k
+  show (KeyboardState _) = "(SDL_KEYBOARD_STATE)"
+  show (Color _)         = "(SDL_COLOR)"
+  show (Renderer _)      = "(SDL_RENDERER)"
+  show (SoundSample _)   = "(SDL_SOUND)"
+
+data Number
+  = NumberInt IntType
+  | NumberFractional FloatType
+  deriving Show
+
+negateValue :: Number -> Number
+negateValue (NumberInt x)        = NumberInt $ negate x
+negateValue (NumberFractional x) = NumberFractional $ negate x
+
+numberBinaryFn :: (forall n. Num n => n -> n -> n) -> Number -> Number -> Number
+numberBinaryFn fn n1 n2 = case (n1, n2) of
+  (NumberInt x, NumberInt y) -> NumberInt $ fn x y
+  (NumberFractional x, NumberFractional y) -> NumberFractional $ fn x y
+  (NumberInt x, NumberFractional y) -> NumberFractional $ fn (realToFrac x) y
+  (NumberFractional x, NumberInt y) -> NumberFractional $ fn x (realToFrac y)
+
+numberBinaryFractionalFn :: (forall n. Fractional n => n -> n -> n) -> Number -> Number -> Number
+numberBinaryFractionalFn fn n1 n2 = case (n1, n2) of
+  (NumberInt x, NumberInt y) -> NumberFractional $ fn (realToFrac x) (realToFrac y)
+  (NumberFractional x, NumberFractional y) -> NumberFractional $ fn x y
+  (NumberInt x, NumberFractional y) -> NumberFractional $ fn (realToFrac x) y
+  (NumberFractional x, NumberInt y) -> NumberFractional $ fn x (realToFrac y)
+
+instance Eq Number where
+  (NumberInt n1) == (NumberInt n2)               = n1 == n2
+  (NumberFractional n1) == (NumberFractional n2) = n1 == n2
+  (NumberFractional n1) == (NumberInt n2)        = n1 == (realToFrac n2)
+  (NumberInt n1) == (NumberFractional n2)        = (realToFrac n1) == n2
+
+instance Ord Number where
+  compare (NumberInt x) (NumberInt y)               = compare x y
+  compare (NumberFractional x) (NumberFractional y) = compare x y
+  compare (NumberInt x) (NumberFractional y)        = compare (realToFrac x) y
+  compare (NumberFractional x) (NumberInt y)        = compare x (realToFrac y)
+
+data UnNamedFn = UnNamedFn (Maybe (NonEmpty Identifier)) ExpressionWithLoc
+  deriving Show
+
+data ThreadInfo = ThreadInfo ThreadId (TMVar (Either SomeException Value))
+
+instance Show ThreadInfo where
+  show _ = "(ThreadInfo)"
+
+newtype ChannelRef = ChannelRef (TChan Value)
+
+instance Show ChannelRef where
+  show _ = "(ConcurrencyChannel)"
+
+newtype MutableRef = MutableRef (TMVar Value)
+
+instance Show MutableRef where
+  show _ = "(MutableRef)"
+
+data Value
+  = StringValue Text
+  | NumberValue Number
+  | BoolValue Bool
+  | BytesValue BS.ByteString
+  | ArrayValue (V.Vector Value)
+  | ObjectValue (Map Text Value)
+  | ProcedureValue FunctionDef
+  | UnnamedFnValue UnNamedFn
+  | BuiltIn BuiltinVal
+  | SDLValue SDLValue
+  | ThreadRef ThreadInfo
+  | Channel ChannelRef
+  | Ref MutableRef
+  | ErrorValue Text
+  | Void
+  deriving Show
+
+instance Ord Value where
+  compare (NumberValue x) (NumberValue y) = compare x y
+  compare _ _                             = error "Cannot be compared"
+
+data ProcResult
+  = ProcReturn Bool Value -- Bool indicates tail call
+  | ProcBreak
+  | ProcContinue
+
+instance Eq Value where
+ (StringValue t) == (StringValue v) = t == v
+ (NumberValue t) == (NumberValue v)       = t == v
+ (BoolValue t) == (BoolValue v)     = t == v
+ (BytesValue t) == (BytesValue v)     = t == v
+ (ArrayValue t) == (ArrayValue v)   = t == v
+ (ObjectValue t) == (ObjectValue v) = t == v
+ (SDLValue (Keycode kc1)) == (SDLValue (Keycode kc2)) = kc1 == kc2
+ (ProcedureValue _) == _            = error "Procedures cannot be compared"
+ _ == (ProcedureValue _)            = error "Procedures cannot be compared"
+ (BuiltIn _) == _                   = error "Procedures cannot be compared"
+ _ == (BuiltIn _)                   = error "Procedures cannot be compared"
+ Void == _                          = error "Void cannot be compared"
+ _ == Void                          = error "Void cannot be compared"
+ _ == _                             = False
+
+data ScopeKey
+  = SkIdentifier Identifier
+  | SkOperator Operator
+  deriving (Ord, Eq)
+
+instance Show ScopeKey where
+  show (SkIdentifier i) = unpack $ unIdentifer i
+  show (SkOperator i)   = show i
+
+type Scope = Map ScopeKey Value
+
+data RunMode
+  = NormalMode
+  | DebugMode
+  deriving Show
+
+data DebugIn
+  = AddWatch Text
+  | StepIn
+  | StepOver
+  | Run
+  deriving Show
+
+data StepMode
+  = SingleStep
+  | Continue
+  deriving Show
+
+data DebugState = DebugState
+  { dsScope            :: Scope
+  , dsLocation         :: Location
+  , dsCurrenEvaluation :: Maybe Text
+  } deriving Show
+
+data DebugOut
+  = Finished
+  | Errored Text
+  | DebugData DebugState
+  deriving Show
+
+data InterpreterState = InterpreterState
+  { isLocal             :: [Scope]
+  , isRunMode           :: RunMode
+  , isGlobalScope       :: Scope
+  , isDefaultRenderer   :: Maybe SDL.Renderer
+  , isAccelerated       :: Maybe Bool
+  , isDefaultWindow     :: Maybe SDL.Window
+  , isSDLWindows        :: IORef [SDL.Window] -- We need these to cleanup any SDL windows after an exception.
+  , isDebugIn           :: MVar DebugIn
+  , isDebugOut          :: MVar DebugOut
+  , isStepMode          :: StepMode
+  , isThreadId          :: Maybe ThreadId
+  , isTerminalEventChan :: TChan TerminalEvent
+  }
+
+instance Show InterpreterState where
+  show InterpreterState {..} = show (isJust isDefaultWindow)
+
+emptyIs :: IORef [SDL.Window] -> MVar DebugIn -> MVar DebugOut -> TChan TerminalEvent -> InterpreterState
+emptyIs sdlWindowsRef din dout inputChan = InterpreterState mempty DebugMode mempty Nothing Nothing Nothing sdlWindowsRef din dout SingleStep Nothing inputChan
+
+mapLocal :: ([Scope] -> [Scope]) -> (InterpreterState -> InterpreterState)
+mapLocal fn = \is -> is { isLocal = fn $ isLocal is }
+
+mapGlobalScope :: (Scope -> Scope) -> (InterpreterState -> InterpreterState)
+mapGlobalScope fn = \is -> is { isGlobalScope = fn $ isGlobalScope is }
+
+type InterpretM a = forall m. (MonadCatch m, MonadIO m) => StateT InterpreterState m a
+
+instance {-# OVERLAPPING #-} (MonadCatch m, MonadIO m) => MonadIO (StateT InterpreterState m) where
+  liftIO act = lift $ liftIO @m (wrapSomeException IOError act)
+
+type BuiltInFnWithDoc s = NamedArgs s -> InterpretM (Maybe Value)
+type BuiltInFn = [Value] -> InterpretM (Maybe Value)
+
+data Callback
+  = CallbackUnNamed UnNamedFn
+  | CallbackNamed Identifier
+
+instance FromValue (M.Map Text Value) where
+  fromValue (ObjectValue t) = t
+  fromValue a               = throwErr $ UnexpectedType ("object", a)
+  typeName = "object"
+
+instance FromValue ThreadInfo where
+  fromValue (ThreadRef t) = t
+  fromValue a             = throwErr $ UnexpectedType ("thread_ref", a)
+  typeName = "thread_ref"
+
+instance FromValue ChannelRef where
+  fromValue (Channel t) = t
+  fromValue a           = throwErr $ UnexpectedType ("concurrency_channel", a)
+  typeName = "concurrency_channel"
+
+instance FromValue MutableRef where
+  fromValue (Ref t) = t
+  fromValue a       = throwErr $ UnexpectedType ("mutable_reference", a)
+  typeName = "mutable_reference"
+
+instance FromValue BytesOrText where
+  fromValue (StringValue t) = BTText t
+  fromValue (BytesValue t)  = BTBytes t
+  fromValue a               = throwErr $ UnexpectedType ("filepath", a)
+  typeName = "filepath"
+
+instance FromValue FilePath where
+  fromValue (StringValue t) = T.unpack t
+  fromValue a               = throwErr $ UnexpectedType ("filepath", a)
+  typeName = "filepath"
+
+instance FromValue Sample where
+  fromValue (SDLValue (SoundSample s)) = s
+  fromValue a = throwErr $ UnexpectedType ("soundsample", a)
+  typeName = "soundsample"
+
+instance FromValue Text where
+  fromValue (StringValue t) = t
+  fromValue a               = throwErr $ UnexpectedType ("string", a)
+  typeName = "string"
+
+instance FromValue BS.ByteString where
+  fromValue (BytesValue b) = b
+  fromValue a              = throwErr $ UnexpectedType ("bytes", a)
+  typeName = "bytes"
+
+instance FromValue SDL.Scancode where
+  fromValue (SDLValue (Scancode sc)) = sc
+  fromValue a                        = throwErr $ UnexpectedType ("scancode", a)
+  typeName = "scancode"
+
+instance FromValue SDLKeyboardStateCallback where
+  fromValue (SDLValue (KeyboardState cb)) = cb
+  fromValue a = throwErr $ UnexpectedType ("callback", a)
+  typeName = "keyboardstate"
+
+instance FromValue Callback where
+  fromValue (UnnamedFnValue un) = CallbackUnNamed un
+  fromValue (ProcedureValue (FunctionDef idf _ _)) = CallbackNamed idf
+  fromValue a = throwErr $ UnexpectedType ("callback", a)
+  typeName = "callback"
+
+class FromValue a where
+  fromValue :: Value -> a
+  typeName :: Text
+
+instance FromValue SDL.Keycode where
+  fromValue (SDLValue (Keycode keycode)) = keycode
+  fromValue a = throwErr $ UnexpectedType ("keycode", a)
+  typeName = "keycode"
+
+instance FromValue Int where
+  fromValue (NumberValue (NumberInt i)) = fromIntegral i
+  fromValue (NumberValue (NumberFractional i)) = round i
+  fromValue a            = throwErr $ UnexpectedType ("number", a)
+  typeName = "integer"
+
+instance FromValue SDLM.Channel where
+  fromValue = fromIntegral . fromValue @Int
+  typeName = "channel"
+
+instance FromValue Bool where
+  fromValue (BoolValue b) = b
+  fromValue a             = throwErr $ UnexpectedType ("boolean", a)
+  typeName = "boolean"
+
+instance FromValue Integer where
+  fromValue (NumberValue (NumberInt i)) = i
+  fromValue (NumberValue (NumberFractional i)) = round i
+  fromValue a            = throwErr $ UnexpectedType ("number", a)
+  typeName = "integer"
+
+instance FromValue Double where
+  fromValue (NumberValue (NumberInt i)) = realToFrac i
+  fromValue (NumberValue (NumberFractional i)) = i
+  fromValue a            = throwErr $ UnexpectedType ("fractional", a)
+  typeName = "fractional"
+
+instance FromValue a => FromValue (V.Vector a) where
+  fromValue (ArrayValue v) = V.map fromValue v
+  fromValue a              = throwErr $ UnexpectedType ("number", a)
+  typeName = "list of " <> (typeName @a)
+
+instance FromValue Number where
+  fromValue (NumberValue i) = i
+  fromValue a               = throwErr $ UnexpectedType ("number", a)
+  typeName = "number"
+
+instance FromValue CInt where
+  fromValue (NumberValue (NumberInt i)) = fromIntegral i
+  fromValue (NumberValue (NumberFractional i)) = fromIntegral $ round i
+  fromValue a            = throwErr $ UnexpectedType ("number", a)
+  typeName = "number"
+
+instance FromValue Value where
+  fromValue = id
+  typeName = "any"
+
+mkPoint :: CInt -> CInt -> SDL.Point SDL.V2 CInt
+mkPoint x y = SDL.P (SDL.V2 x y)
+
+instance FromValue (VS.Vector (SDL.Point V2 CInt)) where
+  fromValue (ArrayValue v) = VS.fromList $ V.toList $ fn <$> v
+    where
+      fn :: Value -> SDL.Point V2 CInt
+      fn (ArrayValue (V.toList -> [v1, v2])) = mkPoint (fromValue v1) (fromValue v2)
+      fn a = throwErr $ UnexpectedType ("list(2)", a)
+  fromValue a = throwErr $ UnexpectedType ("list", a)
+  typeName = "list"
+
+instance FromValue (V.Vector (Number, Number)) where
+  fromValue (ArrayValue v) = V.map fn v
+    where
+      fn (ArrayValue (V.toList -> ((NumberValue idx0) : (NumberValue idx1) : _))) = (idx0, idx1)
+      fn a = throwErr $ UnexpectedType ("list with two values", a)
+  fromValue a              = throwErr $ UnexpectedType ("list", a)
+  typeName = "list"
+
+instance FromValue Word8 where
+  fromValue = \case
+    (NumberValue (NumberInt i)) -> fromInt i
+    (NumberValue (NumberFractional i)) -> fromInt (round i)
+    a -> throwErr $ UnexpectedType ("number", a)
+    where
+      fromInt i = if (fromIntegral i) <= (maxBound @Word8) then fromIntegral i else throwErr $ CustomRTE "Number too large to be converted to a byte"
+  typeName = "number"
+
+newtype Variadic = Variadic [Value]
+
+instance FromValue Variadic where
+  fromValue v = Variadic [v]
+  typeName = "variadic argument"
+
+newtype NamedArg (s :: Symbol) a = NamedArg a
+
+instance KnownSymbol s => IsLabel s (a -> NamedArg s a) where
+  fromLabel = NamedArg
+
+data NamedArgs (s :: [(Symbol, Type)]) where
+  EmptyArgs :: NamedArgs '[]
+  (:>) :: (KnownSymbol s, FromValue a) => NamedArg s a -> NamedArgs r -> NamedArgs ('(s, a) ': r)
+infixr 8 :>
+
+class KnownArgs a where
+  toArgDoc :: [(Text, Text)]
+  toArgs :: [Value] -> NamedArgs a
+
+instance {-# OVERLAPPING #-} KnownSymbol n => KnownArgs '[ '(n, Variadic)] where
+  toArgDoc = [("variadic", "Value")]
+  toArgs x = NamedArg (Variadic x) :> EmptyArgs
+
+instance KnownArgs '[] where
+  toArgDoc = []
+  toArgs [] = EmptyArgs
+  toArgs _  = error "Unexpected arguments"
+
+instance (KnownSymbol n, FromValue t, KnownArgs s) => KnownArgs (('(n, t) ': s)) where
+  toArgDoc = (pack $ symbolVal (Proxy @n), typeName @t) : toArgDoc @s
+  toArgs []        = throwErr  $ BadArguments ((pack $ symbolVal (Proxy @n)) <> " of type " <> typeName @t, "No argument")
+  toArgs (ErrorValue err : _) = throwErr $ CustomRTE err
+  toArgs (v : rst) = (NamedArg $ fromValue v) :> (toArgs @s rst)
+
+instance FromValue a => FromValue (Maybe a) where
+  fromValue v = Just $ fromValue v
+  typeName = (typeName @a) <> "(optional)"
+
+instance {-# OVERLAPPING #-} (KnownSymbol n, FromValue t, KnownArgs s) => KnownArgs (('(n, Maybe t) ': s)) where
+  toArgDoc = (pack $ symbolVal (Proxy @n), typeName @(Maybe t)) : toArgDoc @s
+  toArgs []        = (NamedArg (Nothing :: Maybe t)) :> (toArgs @s [])
+  toArgs (v : rst) = (NamedArg $ fromValue v) :> (toArgs @s rst)
+
+data SomeBuiltin where
+  SomeBuiltin :: KnownArgs s => (NamedArgs s -> InterpretM (Maybe Value)) -> SomeBuiltin
+
+extractDoc :: SomeBuiltin -> [(Text, Text)]
+extractDoc (SomeBuiltin (_ :: NamedArgs s -> InterpretM (Maybe Value))) = toArgDoc @s
+
+wrapSomeException :: (Text -> RuntimeError) -> IO a -> IO a
+wrapSomeException fn act =
+  flip catch (\(e :: SomeException) -> case fromException @AsyncException e of
+    Just _ -> throwM e
+    _ -> case fromException @IOException e of
+      Just _  -> throwM $ fn $ T.pack $ displayException e
+      Nothing -> throwM e) act
+
+instance HReadable (Either RuntimeError ProgramError) where
+  hReadable (Right x) = hReadable x
+  hReadable (Left x)  = hReadable x
+
+data RuntimeErrorWithLoc =  RuntimeErrorWithLoc (Either RuntimeError ProgramError) Location
+
+instance HReadable RuntimeErrorWithLoc where
+  hReadable (RuntimeErrorWithLoc msg loc) = "Runtime error: " <> (hReadable msg) <> ", at: " <> (hReadable loc)
+
+instance Show RuntimeErrorWithLoc where
+  show = T.unpack . hReadable
+
+instance Exception RuntimeErrorWithLoc where
+
+data ProgramError
+  = SymbolNotFound Text
+  | UnexpectedType (Text, Value)
+  | MissingProcedureReturn
+  | EmptyScopeStack
+  | BadArguments (Text, Text)
+
+data RuntimeError
+  = IOError Text
+  | SDLError Text
+  | ListIndexOutOfBounds Int
+  | KeyNotFound Text
+  | CustomRTE Text
+
+instance Show RuntimeError where
+  show = T.unpack . hReadable
+
+instance Show ProgramError where
+  show = T.unpack . hReadable
+
+instance HReadable ProgramError where
+  hReadable = \case
+    SymbolNotFound s -> "Unknown symbol reference: '" <> s <>"'"
+    UnexpectedType (e, f) -> case f of
+      ErrorValue t -> t
+      _ -> "Unexpected type, expected " <> e <> ", but got: " <> (T.pack $ show f)
+    MissingProcedureReturn -> "Function or callback call did not return a value as expected"
+    EmptyScopeStack -> "Stack was unexpectedly empty"
+    BadArguments (e, f) -> "Unexpected argument count or type, expected: " <> e <> ", but got: " <> f
+
+instance HReadable RuntimeError where
+  hReadable = \case
+    IOError t -> "Input/Output error: " <> t
+    SDLError t -> "SDL error: " <> t
+    ListIndexOutOfBounds t -> "Out of bound access of a list at index: " <> (T.pack $ show t)
+    KeyNotFound t -> "Non-existing key access in dictionary at key: " <> t
+    CustomRTE t -> "Run time error: " <> t
+
+throwBadArgs :: [Value] -> Text -> InterpretM (Maybe Value)
+throwBadArgs v' m = throwBadArgs' v' [] m
+  where
+    throwBadArgs' :: [Value] -> [Value] -> Text -> InterpretM (Maybe Value)
+    throwBadArgs' (v@(ErrorValue _): _) _ _ = pure $ Just v
+    throwBadArgs' (v : rst) bargs msg  = throwBadArgs' rst (v:bargs) msg
+    throwBadArgs' [] bargs msg = throwErr $ BadArguments (msg, T.pack $ show $ Prelude.reverse bargs)
+
+data BytesOrText
+  = BTBytes BS.ByteString
+  | BTText Text
+  deriving Show
+
+instance Exception RuntimeError where
+  displayException e = T.unpack $ hReadable e
+
+instance Exception ProgramError where
+  displayException e = T.unpack $ hReadable e
+
+class InterpreterException a where
+  throwErr :: (Exception e) => e -> a
+
+instance InterpreterException a where
+  throwErr r = throw r
+
+instance {-# OVERLAPPING #-} InterpreterException ([] a) where
+  throwErr r = throw r
+
+instance {-# OVERLAPPING #-} MonadThrow m => InterpreterException (StateT InterpreterState m a) where
+  throwErr = throwM
+
+waitMillisec' :: Number -> IO ()
+waitMillisec' number = case number of
+  NumberFractional v -> do
+    threadDelay (round $ v * 1000000)
+  NumberInt v -> do
+    threadDelay (fromInteger $ v * 1000000)
+
+insertBuiltInVal :: ScopeKey -> Value -> InterpretM ()
+insertBuiltInVal sk val = modify $ mapGlobalScope fn
+  where
+    fn :: Scope -> Scope
+    fn s = M.insert sk (BuiltIn $ BuiltinVal val) s
+
+insertBuiltIn :: ScopeKey -> BuiltInFn -> InterpretM ()
+insertBuiltIn sk val = modify $ mapGlobalScope fn
+  where
+    fn :: Scope -> Scope
+    fn s = M.insert sk (BuiltIn $ BuiltinCall val) s
+
+insertBuiltInWithDoc :: ScopeKey -> SomeBuiltin -> InterpretM ()
+insertBuiltInWithDoc sk val = modify $ mapGlobalScope fn
+  where
+    fn :: Scope -> Scope
+    fn s = M.insert sk (BuiltIn $ BuiltinCallWithDoc val) s
+
+insertBinding :: ScopeKey -> Value -> InterpretM ()
+insertBinding sk val = modify fn
+  where
+    fn :: InterpreterState -> InterpreterState
+    fn is@(InterpreterState { isGlobalScope, isLocal }) = case isLocal of
+      []        -> is { isGlobalScope = M.insert sk val isGlobalScope }
+      (s : rst) -> is { isLocal = (M.insert sk val s) : rst }
diff --git a/src/Interpreter/Initialize.hs b/src/Interpreter/Initialize.hs
new file mode 100644
--- /dev/null
+++ b/src/Interpreter/Initialize.hs
@@ -0,0 +1,123 @@
+module Interpreter.Initialize where
+
+import Prelude hiding (map)
+
+import Compiler.Lexer
+import Interpreter.Common
+import Interpreter.Interpreter
+import Interpreter.Lib.Math
+import Interpreter.Lib.Misc
+import Interpreter.Lib.Concurrency
+import Interpreter.Lib.SDL
+
+loadBuiltIns :: InterpretM ()
+loadBuiltIns = do
+  insertBuiltIn (SkOperator OpStar) multiplication
+  insertBuiltIn (SkOperator OpPlus) addition
+  insertBuiltIn (SkOperator OpMinus) substraction
+  insertBuiltIn (SkOperator OpDivide) division
+  insertBuiltIn (SkOperator OpEquality) (comparison (==))
+  insertBuiltIn (SkOperator OpLT) (comparison (<))
+  insertBuiltIn (SkOperator OpGT) (comparison (>))
+  insertBuiltIn (SkOperator OpLTE) (comparison (<=))
+  insertBuiltIn (SkOperator OpGTE) (comparison (>=))
+  insertBuiltIn (SkOperator OpNotEquality) (comparison (/=))
+  insertBuiltIn (SkOperator OpAnd) (boolean (&&))
+  insertBuiltIn (SkOperator OpOr) (boolean (||))
+
+  -- Containers
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "size") (SomeBuiltin valueSize)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "contains") (SomeBuiltin contains)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "haskey") (SomeBuiltin haskey)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "filter") (SomeBuiltin filter_)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "drop") (SomeBuiltin arrayDrop)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "take") (SomeBuiltin arrayTake)
+
+  -- Time
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "timestamp") (SomeBuiltin builtInTimestamp)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "wait") (SomeBuiltin waitMillisec)
+
+  -- Keyboard
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "getkey") (SomeBuiltin waitForKey)
+
+  -- Files
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "readfile") (SomeBuiltin builtInReadFile)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "readtextfile") (SomeBuiltin builtInReadTextFile)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "writefile") (SomeBuiltin builtInWriteFile)
+
+  -- Math
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "mod") (SomeBuiltin builtInMod)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "random") (SomeBuiltin builtInRandom)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "sin") (SomeBuiltin builtInSin)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "cos") (SomeBuiltin builtInCos)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "tan") (SomeBuiltin builtInTan)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "asin") (SomeBuiltin builtInASin)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "acos") (SomeBuiltin builtInACos)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "atan") (SomeBuiltin builtInATan)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "sum") (SomeBuiltin builtInSum)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "round") (SomeBuiltin builtInRound)
+
+  -- List/Map
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "insertleft") (SomeBuiltin builtInArrayInsertLeft)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "insertright") (SomeBuiltin builtInArrayInsertRight)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "head") (SomeBuiltin builtInHead)
+
+  -- Misc
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "try") (SomeBuiltin builtInTry)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "jsondecode") (SomeBuiltin builtInJSONParse)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "jsonencode") (SomeBuiltin builtInJSONSerialize)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "decodeutf8") (SomeBuiltin builtInDecodeUTF8Bytes)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "encodeutf8") (SomeBuiltin builtInEncodeUTF8Bytes)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "not") (SomeBuiltin not')
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "debug") (SomeBuiltin builtInDebug)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "inspect") (SomeBuiltin builtInInspect)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "println") (SomeBuiltin printValLn)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "print") (SomeBuiltin printVal)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "inputline") (SomeBuiltin builtinInputLine)
+
+  -- SDL Sound
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "maketone") (SomeBuiltin builtInMakeTone)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "makesound") (SomeBuiltin builtInMakeSoundSample)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "loadsound") (SomeBuiltin builtInMakeSoundSampleFromFile)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "playsound") (SomeBuiltin builtInPlaySoundSample)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "setvolume") (SomeBuiltin builtInSetSampleVolume)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "setvolumelr") (SomeBuiltin builtInSetSampleLRVolume)
+
+  -- SDL Graphics
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "graphicswindow") (SomeBuiltin createGraphicsWindow)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "graphics") (SomeBuiltin createGraphicsFullscreen)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "point") (SomeBuiltin drawPoint)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "points") (SomeBuiltin drawPoints)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "line") (SomeBuiltin drawLine)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "lines") (SomeBuiltin drawLines)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "circle") (SomeBuiltin drawCircle)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "box") (SomeBuiltin drawBox)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "drawscreen") (SomeBuiltin draw)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "setcolor") (SomeBuiltin setDrawColor)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "clearscreen") (SomeBuiltin clear)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "getwindowsize") (SomeBuiltin getWindowSize)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "setlogicalsize") (SomeBuiltin setLogicalSize)
+
+  -- SDL Keyboard
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "getkeypresses") (SomeBuiltin getKeys)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "waitforkey") (SomeBuiltin waitForSDLKey)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "getkeystate") (SomeBuiltin getKeyboardState)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "inkeystate") (SomeBuiltin wasKeyDownIn)
+
+  --  Concurrency
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "startthread") (SomeBuiltin builtInLaunchThread)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "killthread") (SomeBuiltin builtInKillThread)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "await") (SomeBuiltin builtInAwait)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "awaitresult") (SomeBuiltin builtInAwaitResult)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "newchannel") (SomeBuiltin builtInNewChannel)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "writechannel") (SomeBuiltin builtInWriteChannel)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "readchannel") (SomeBuiltin builtInReadChannel)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "newref") (SomeBuiltin builtInNewRef)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "readref") (SomeBuiltin builtInReadRef)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "writeref") (SomeBuiltin builtInWriteRef)
+  insertBuiltInWithDoc (SkIdentifier $ Identifier "modifyref") (SomeBuiltin builtInModifyRef)
+
+  -- Constants
+  insertBuiltInVal (SkIdentifier $ Identifier "keycodes") keycodes
+  insertBuiltInVal (SkIdentifier $ Identifier "scancodes") scancodes
+
diff --git a/src/Interpreter/Interpreter.hs b/src/Interpreter/Interpreter.hs
new file mode 100644
--- /dev/null
+++ b/src/Interpreter/Interpreter.hs
@@ -0,0 +1,366 @@
+module Interpreter.Interpreter where
+
+import Prelude hiding (map)
+
+import Control.Concurrent.MVar
+import Control.Exception (throw)
+import Control.Monad
+import Control.Monad.Catch (catch)
+import Control.Monad.Loops (iterateWhile)
+import Data.Coerce
+import qualified Data.List.NonEmpty as NE
+import Data.Map as M hiding (map)
+import Data.Text as T hiding (index, map)
+import qualified Data.Vector as V
+
+import Common
+import Compiler.AST.FunctionStatement
+import Compiler.AST.Program
+import Compiler.Lexer
+import Control.Monad.State.Strict
+import Interpreter.Common
+
+lookupScope :: ScopeKey -> InterpretM Value
+lookupScope key = ((lookupInTopScope key . isLocal) <$> get) >>= \case
+  Just v -> pure v
+  Nothing -> ((M.lookup key . isGlobalScope) <$> get) >>= \case
+    Just v -> pure v
+    Nothing ->
+      throwErr $ SymbolNotFound $ (pack $ show key)
+
+lookupInTopScope :: ScopeKey -> [Scope] -> Maybe Value
+lookupInTopScope _ [] = Nothing
+lookupInTopScope key (h: _) = M.lookup key h
+
+evaluateExpression :: ExpressionWithLoc -> InterpretM Value
+evaluateExpression exp'@(ExpressionWithLoc _ loc) =
+  catch
+    (catch (executeDebugStepable exp') rteHandler) peHandler
+  where
+    rteHandler (r :: RuntimeError) = pure $ ErrorValue $ (hReadable r) <> " at " <> hReadable loc
+    peHandler (r :: ProgramError) = throwErr @(InterpretM Value) $ RuntimeErrorWithLoc (Right r) loc
+
+evaluateExpression_ :: Expression -> InterpretM Value
+evaluateExpression_ (EParan le) = evaluateExpression le
+evaluateExpression_ (ENegated le) = evaluateExpression le >>= \case
+  NumberValue n -> pure $ NumberValue $ negateValue n
+  x             -> throwErr $ UnexpectedType ("Number", x)
+evaluateExpression_ (ELiteral le) = evaluateLiteralExpression le
+evaluateExpression_ (EVar idf)     = lookupScope (SkIdentifier idf) >>= \case
+  BuiltIn (BuiltinVal v) -> pure v
+  v                      -> pure v
+evaluateExpression_ (ESubscripted subscript) = evaluateSubscriptedExpr subscript
+evaluateExpression_ (EConditional boolEx ex1 ex2)  = evaluateExpression boolEx >>= \case
+  BoolValue True  -> evaluateExpression ex1
+  BoolValue False -> evaluateExpression ex2
+  x               -> throwErr $ UnexpectedType ("Bool", x)
+evaluateExpression_ (EOperator op e1 e2)  = do
+  evaluateFn (FnOp op) [e1, e2] False >>= \case
+    Just v  -> pure v
+    Nothing -> throwErr MissingProcedureReturn
+evaluateExpression_ (ECall iden exprs isTail)  =  evaluateFn (FnName iden) exprs isTail >>= \case
+  Just v  -> pure v
+  Nothing -> throwErr MissingProcedureReturn
+evaluateExpression_ (EUnnamedFn args expr)  = pure $ UnnamedFnValue $ UnNamedFn args expr
+
+popScope :: InterpretM ()
+popScope = do
+  (isLocal <$> get) >>= \case
+    (_:rst) -> do
+      modify (\is -> is { isLocal = rst })
+    _ -> throwErr EmptyScopeStack
+
+data FnId
+  = FnOp Operator
+  | FnName Identifier
+
+evaluateCallback :: Callback -> [Value] -> InterpretM (Maybe Value)
+evaluateCallback (CallbackUnNamed un) args = Just <$> evaluateUnnamedFn un args
+evaluateCallback (CallbackNamed idf) args =
+  evaluateProcedure (SkIdentifier idf) args
+
+insertEmptyScope :: InterpretM ()
+insertEmptyScope = modify $ mapLocal (\s -> mempty : s)
+
+evaluateUnnamedFn :: UnNamedFn -> [Value] -> InterpretM Value
+evaluateUnnamedFn (UnNamedFn Nothing expr) _ = evaluateExpression expr
+evaluateUnnamedFn (UnNamedFn (Just (NE.toList -> argNames)) expr) argsVals = do
+  insertEmptyScope
+  zipWithM_ (\a1 a2 -> insertBinding a1 a2) (SkIdentifier <$> argNames) argsVals -- @TODO Check argument counts
+  r <- evaluateExpression expr
+  popScope
+  pure r
+
+evaluateProcedure :: ScopeKey -> [Value] -> InterpretM (Maybe Value)
+evaluateProcedure sk args = do
+  lookupScope sk >>= \case
+    UnnamedFnValue un -> Just <$> evaluateUnnamedFn un args
+    (ProcedureValue (FunctionDef _ argNames (NE.toList -> stms))) -> do
+      insertEmptyScope
+      zipWithM_ (\a1 a2 -> insertBinding a1 a2) (SkIdentifier <$> argNames) args -- @TODO Check argument counts
+      executeStatements stms >>= \case
+        ProcReturn False v -> do
+          popScope
+          pure $ Just v
+        ProcReturn True v -> do
+          -- Don't pop stack if the return was a tail call return
+          -- because the stack was popped before entering the
+          -- call.
+          pure $ Just v
+        ProcBreak -> do
+          popScope
+          pure Nothing
+        ProcContinue -> do
+          popScope
+          pure Nothing
+    (BuiltIn (BuiltinCall cb)) -> cb args
+    (BuiltIn (BuiltinCallWithDoc (SomeBuiltin cb))) -> cb (toArgs args)
+    a -> throwErr $ UnexpectedType ("Procedure", a)
+
+evaluateFn :: FnId -> [ExpressionWithLoc] -> Bool -> InterpretM (Maybe Value)
+evaluateFn fnId argsExps isTail = do
+  let sk = case fnId of
+        FnOp op    -> SkOperator op
+        FnName idf -> SkIdentifier idf
+  args <- mapM (\x -> evaluateExpression x) argsExps
+  when isTail popScope
+  evaluateProcedure sk args
+
+evaluateSubscriptedExpr :: SubscriptedExpression -> InterpretM Value
+evaluateSubscriptedExpr (EArraySubscript expr indexExpr) = evaluateExpression expr >>= \case
+  ArrayValue v -> evaluateExpression indexExpr >>= \case
+    NumberValue (NumberInt i) -> do
+      let index :: Int = fromIntegral i
+      if index <= V.length v && index >= 0 then (pure $ v V.! (index - 1)) else (throwErr $ ListIndexOutOfBounds index)
+    a -> throwErr $ UnexpectedType ("Integer index", a)
+  ObjectValue mp -> evaluateExpression indexExpr >>= \case
+    StringValue key -> case M.lookup key mp of
+      Just v  -> pure v
+      Nothing -> throwErr $ KeyNotFound (pack $ show key)
+    a -> throwErr $ UnexpectedType ("Property index", a)
+  a -> throwErr $ UnexpectedType ("Array/Map", a)
+evaluateSubscriptedExpr (EPropertySubscript expr (unIdentifer -> key)) = evaluateExpression expr >>= \case
+  ObjectValue mp -> case M.lookup key mp of
+      Just v  -> pure v
+      Nothing -> throwErr $ KeyNotFound (pack $ show key)
+  a -> throwErr $ UnexpectedType ("Map", a)
+
+evaluateVar :: Subscript -> InterpretM Value
+evaluateVar (NoSubscript idf) = lookupScope (SkIdentifier idf) >>= \case
+  BuiltIn (BuiltinVal v) -> pure $ v
+  v                      -> pure v
+evaluateVar (SubscriptExpr sub expr) =
+  -- Arrays are indexed from 1, not 0.
+  evaluateExpression expr >>= \case
+      NumberValue (NumberInt int) ->
+        evaluateVar sub >>= \case
+          ArrayValue v -> do
+            let index :: Int = fromIntegral int
+            if index <= V.length v && index >= 0 then (pure $ v V.! (index - 1)) else (throwErr $ ListIndexOutOfBounds index)
+          a -> throwErr $ UnexpectedType ("Array/Object", a)
+      StringValue key -> lookupInMapVar sub key
+      a  -> throwErr $ UnexpectedType ("String/Integer container key", a)
+evaluateVar (PropertySubscript sub idf) = lookupInMapVar sub (unIdentifer idf)
+
+lookupInMapVar :: Subscript -> Text -> InterpretM Value
+lookupInMapVar sub key = evaluateVar sub >>= \case
+  ObjectValue mp -> case M.lookup key mp of
+    Just v  -> pure v
+    Nothing -> throwErr $ KeyNotFound (pack $ show key)
+  a            ->  throwErr $ UnexpectedType ("Expecting Object Looking for key: " <> (T.pack $ show sub) <> ":" <> key, a)
+
+evaluateLiteralExpression :: LiteralExpression -> InterpretM Value
+evaluateLiteralExpression (LAtomic (LitString t)) = pure $ StringValue t
+evaluateLiteralExpression (LAtomic (LitBytes t))  = pure $ BytesValue t
+evaluateLiteralExpression (LAtomic (LitNumber n)) = pure $ NumberValue $ NumberInt n
+evaluateLiteralExpression (LAtomic (LitFloat f))  = pure $ NumberValue $ NumberFractional (realToFrac f)
+evaluateLiteralExpression (LAtomic (LitBool b))   = pure $ BoolValue b
+evaluateLiteralExpression (LArray l)   = do
+  v <- mapM (\x -> evaluateExpression x) l
+  pure $ ArrayValue (V.fromList v)
+evaluateLiteralExpression (LObject l)   = do
+  v <- mapM (\x -> evaluateExpression x) l
+  pure $ ObjectValue v
+
+voidStm :: () -> InterpretM (Maybe Value)
+voidStm _ = pure Nothing
+
+executeStatements :: [FunctionStatementWithLoc] -> InterpretM ProcResult
+executeStatements x = foldM (\a1 a2 -> fn a1 a2) ProcContinue x
+  where
+    fn :: ProcResult -> FunctionStatementWithLoc -> InterpretM ProcResult
+    fn (ProcReturn tc x') _ = pure $ ProcReturn tc x'
+    fn ProcBreak _       = pure ProcBreak
+    fn ProcContinue fs   = executeStatement fs
+
+modifyBinding :: Subscript -> Value -> InterpretM ()
+modifyBinding (NoSubscript idf) val = insertBinding (SkIdentifier idf) val
+modifyBinding (PropertySubscript sub (unIdentifer -> key)) val = do
+  evaluateVar sub >>= \case
+    ObjectValue v -> case M.lookup key v of
+      Just _  -> modifyBinding sub (ObjectValue $ M.insert key val v)
+      Nothing -> throwErr (KeyNotFound key)
+    a -> throwErr $ UnexpectedType ("Map", a)
+modifyBinding (SubscriptExpr sub expr) val = do
+  evaluateVar sub >>= \case
+    ArrayValue v -> evaluateExpression expr >>= \case
+      NumberValue (NumberInt idx) -> do
+        let index :: Int = fromIntegral idx
+        if (index <= V.length v && index > 0)
+          then modifyBinding sub (ArrayValue $ V.update v (V.fromList [(index - 1, val)]))
+          else throwErr $ ListIndexOutOfBounds index
+      a -> throwErr $ UnexpectedType ("Integer Index", a)
+    ObjectValue v -> evaluateExpression expr >>= \case
+      StringValue key -> case M.lookup key v of
+        Just _  -> modifyBinding sub (ObjectValue $ M.insert key val v)
+        Nothing -> throwErr (KeyNotFound key)
+      a -> throwErr $ UnexpectedType ("String", a)
+    a -> throwErr $ UnexpectedType ("Map", a)
+
+class ToSource a => DebugStepable a b | a -> b where
+  getLocation :: a -> Location
+  execute :: a -> InterpretM b
+
+instance DebugStepable FunctionStatementWithLoc ProcResult where
+  getLocation (FunctionStatementWithLoc _ l) = l
+  execute (FunctionStatementWithLoc fs _) = executeStatement_ fs
+
+instance DebugStepable ExpressionWithLoc Value where
+  getLocation (ExpressionWithLoc _ l) = l
+  execute (ExpressionWithLoc exp' _) = evaluateExpression_ exp'
+
+executeStatement :: FunctionStatementWithLoc -> InterpretM ProcResult
+executeStatement fs@(FunctionStatementWithLoc _ loc) =
+  catch
+    (catch (executeDebugStepable fs) rteHandler) peHandler
+    where
+      rteHandler (r :: RuntimeError) = case r of
+        CustomRTE msg -> throw (RuntimeErrorWithLoc (Left $ CustomRTE msg) loc)
+        _ -> throw (RuntimeErrorWithLoc (Left r) loc)
+      peHandler (r :: ProgramError) = throw (RuntimeErrorWithLoc (Right r) loc)
+
+executeDebugStepable :: DebugStepable a b => a -> InterpretM b
+executeDebugStepable dbs = do
+  isRunMode <$> get >>= \case
+    NormalMode -> do
+      execute dbs
+    DebugMode -> do
+      isStepMode <$> get >>= \case
+        Continue -> execute dbs
+        SingleStep -> do
+          isDebugOut <$> get >>= sendDebugOut
+          isDebugIn <$> get >>= (liftIO . takeMVar) >>= \case
+            Run -> do
+              modify (\is -> is { isRunMode = NormalMode })
+              execute dbs
+            StepIn -> execute dbs
+            AddWatch _ -> execute dbs
+            StepOver -> do
+              modify (\is -> is { isStepMode = Continue })
+              r <- execute dbs
+              modify (\is -> is { isStepMode = SingleStep })
+              pure r
+  where
+    sendDebugOut debugOut = do
+      currentScope <- isLocal <$> get >>= \case
+        [] -> isGlobalScope <$> get
+        (scope : _) -> pure scope
+      let
+        dd = DebugState currentScope (getLocation dbs) (Just $ toSource dbs)
+      liftIO $ putMVar debugOut $ DebugData dd
+
+executeStatement_ :: FunctionStatement -> InterpretM ProcResult
+executeStatement_ (FnComment _) = pure ProcContinue
+executeStatement_ (Let sub exp') = do
+  sourceValue <- evaluateExpression exp'
+  modifyBinding sub sourceValue
+  pure ProcContinue
+executeStatement_ (Call iden args) = do
+  _ <- evaluateFn (FnName iden) args False
+  pure ProcContinue
+executeStatement_ (IfThen expr stms) = evaluateExpression expr >>= \case
+  BoolValue True -> executeStatements (NE.toList stms)
+  BoolValue _    -> pure ProcContinue
+  a              ->  throwErr $ UnexpectedType ("Bool", a)
+executeStatement_ (If expr stms1 stms2) = evaluateExpression expr >>= \case
+  BoolValue b -> case b of
+    True  -> executeStatements (NE.toList stms1)
+    False -> executeStatements (NE.toList stms2)
+
+  a            ->  throwErr $ UnexpectedType ("Bool", a)
+executeStatement_ (MultiIf expr stms1 elseifs mstms2) = evaluateExpression expr >>= \case
+  BoolValue True -> executeStatements (NE.toList stms1)
+  BoolValue False -> foldM executeElseIf Nothing elseifs >>= \case
+    Just r  -> pure r
+    Nothing -> case mstms2 of
+      Just stms2 -> executeStatements (NE.toList stms2)
+      Nothing    -> pure ProcContinue
+  a            ->  throwErr $ UnexpectedType ("Bool", a)
+  where
+    executeElseIf a@(Just _) _ = pure a
+    executeElseIf Nothing (bexpr, stms) = evaluateExpression bexpr >>= \case
+      BoolValue True  -> Just <$> executeStatements (NE.toList stms)
+      BoolValue False -> pure Nothing
+      a               ->  throwErr $ UnexpectedType ("Bool", a)
+
+executeStatement_ (Return eloc@(ExpressionWithLoc { elExpression = ECall idf args _ })) =
+  -- TCO
+  evaluateExpression (eloc { elExpression = ECall idf args True }) >>= pure . ProcReturn True
+executeStatement_ (Return expr) = evaluateExpression expr >>= pure . ProcReturn False
+executeStatement_ Break = pure ProcBreak
+executeStatement_ (Loop (NE.toList -> stms)) = iterateWhile (\case
+  ProcBreak    -> False
+  ProcContinue -> True
+  ProcReturn _ _ -> False) (executeStatements stms)
+
+executeStatement_ (While exprBool (NE.toList -> stms)) = do
+  r <- iterateWhile (\case
+    ProcBreak    -> False
+    ProcContinue -> True
+    ProcReturn _ _ -> False)
+    (evaluateExpression exprBool >>= \case
+        BoolValue True  -> executeStatements stms
+        BoolValue False -> pure ProcBreak
+        a               -> throwErr $ UnexpectedType ("Bool", a))
+  case r of
+    ProcBreak -> pure ProcContinue
+    a         -> pure a
+executeStatement_ (For iden exprFrom exprTo (NE.toList -> stms)) = evaluateExpression exprFrom >>= \case
+  NumberValue (NumberInt start) -> evaluateExpression exprTo >>= \case
+    NumberValue (NumberInt end) -> do
+      let
+        fn :: ProcResult -> IntType -> InterpretM ProcResult
+        fn ProcContinue current = do
+          insertBinding (SkIdentifier iden) (NumberValue $ NumberInt $ current)
+          executeStatements stms
+        fn r _ = pure r
+      foldM (\a1 a2 -> fn a1 a2) ProcContinue [start .. end] >>= \case
+        ProcReturn tc v -> pure $ ProcReturn tc v
+        _            -> pure ProcContinue
+    a            ->  throwErr $ UnexpectedType ("Int", a)
+  a            ->  throwErr $ UnexpectedType ("Int", a)
+
+executeStatement_ (ForEach iden expr (NE.toList -> stms)) = evaluateExpression expr >>= \case
+  ObjectValue map -> do
+    foldM (\a1 (k, v) -> fn a1 (ObjectValue $ M.fromList [("key", StringValue k), ("value", v)])) ProcContinue (M.assocs map) >>= \case
+      ProcReturn tc v -> pure $ ProcReturn tc v
+      _            -> pure ProcContinue
+  ArrayValue values -> do
+    V.foldM (\a1 a2 -> fn a1 a2) ProcContinue values >>= \case
+      ProcReturn tc v -> pure $ ProcReturn tc v
+      _            -> pure ProcContinue
+  a            ->  throwErr $ UnexpectedType ("Array/Object", a)
+  where
+    fn :: ProcResult -> Value -> InterpretM ProcResult
+    fn ProcContinue current = do
+      insertBinding (SkIdentifier iden) current
+      executeStatements stms
+    fn r _ = pure r
+
+filter_ :: BuiltInFnWithDoc '[ '("list", V.Vector Value), '("callback", Callback)]
+filter_ ((coerce -> v1) :> (coerce -> callback) :> _) =
+  (\x -> Just $ ArrayValue x) <$> V.filterM fn v1
+  where
+    fn v = evaluateCallback callback [v] >>= \case
+      Just (BoolValue x) -> pure x
+      _           -> throwErr $ CustomRTE "Callback returned a non-bool value"
diff --git a/src/Interpreter/Lib/Concurrency.hs b/src/Interpreter/Lib/Concurrency.hs
new file mode 100644
--- /dev/null
+++ b/src/Interpreter/Lib/Concurrency.hs
@@ -0,0 +1,79 @@
+module Interpreter.Lib.Concurrency where
+
+import Control.Concurrent
+import Control.Concurrent.STM (atomically)
+import Control.Concurrent.STM.TChan
+import Control.Concurrent.STM.TMVar
+import Control.Exception (AsyncException(..), SomeException, catch, toException)
+import Control.Monad.IO.Class
+import Control.Monad.State.Strict
+import Data.Coerce
+
+import Interpreter.Common
+import Interpreter.Interpreter
+
+builtInLaunchThread :: BuiltInFnWithDoc '[ '("callback_thread", Callback), '("callback_arg", Value) ]
+builtInLaunchThread ((coerce -> (processCb :: Callback)) :> (coerce -> threadArg) :> EmptyArgs) = do
+  istate <- get
+  liftIO $ do
+    resultRef <- newEmptyTMVarIO
+    threadId <- forkIO $ flip catch (asynExHandler resultRef) $ do
+      (r, _) <- flip runStateT istate (evaluateCallback processCb [threadArg])
+      atomically $ putTMVar resultRef $ case r of
+        Just r' -> Right r'
+        Nothing -> Left (toException MissingProcedureReturn)
+    pure $ Just $ ThreadRef $ ThreadInfo threadId resultRef
+  where
+    asynExHandler :: TMVar (Either SomeException Value) -> AsyncException -> IO ()
+    asynExHandler ref e = atomically $ putTMVar ref $ Left $ toException e
+
+builtInKillThread :: BuiltInFnWithDoc '[ '("thread_result", ThreadInfo) ]
+builtInKillThread ((coerce -> (ThreadInfo threadId _)) :> EmptyArgs) = do
+  liftIO $ killThread threadId
+  pure Nothing
+
+builtInAwait :: BuiltInFnWithDoc '[ '("thread_result", ThreadInfo) ]
+builtInAwait ((coerce -> (ThreadInfo _ pmvar)) :> EmptyArgs) = do
+  void $ liftIO $ atomically $ readTMVar pmvar
+  pure Nothing
+
+builtInAwaitResult :: BuiltInFnWithDoc '[ '("thread_result", ThreadInfo) ]
+builtInAwaitResult ((coerce -> (ThreadInfo _ pmvar)) :> EmptyArgs) =
+  (liftIO $ atomically $ readTMVar pmvar) >>= \case
+    Right x -> pure $ Just x
+    Left e  -> throwErr e
+
+builtInNewChannel :: BuiltInFnWithDoc '[]
+builtInNewChannel _ = (pure . Channel . ChannelRef) <$> liftIO newTChanIO
+
+builtInWriteChannel :: BuiltInFnWithDoc '[ '("channel_ref", ChannelRef), '("value", Value)]
+builtInWriteChannel ((coerce -> (ChannelRef chan)) :> (coerce -> val) :> _) = do
+  liftIO $ atomically $ writeTChan chan val
+  pure Nothing
+
+builtInReadChannel :: BuiltInFnWithDoc '[ '("channel_ref", ChannelRef)]
+builtInReadChannel ((coerce -> (ChannelRef chan)) :> _) =
+  Just <$> (liftIO $ atomically $ readTChan chan)
+
+builtInNewRef :: BuiltInFnWithDoc '[ '("init_value", Value) ]
+builtInNewRef ((coerce -> (v :: Value)) :> EmptyArgs) = do
+  ref <- liftIO $ newTMVarIO v
+  pure $ Just $ Ref $ MutableRef ref
+
+builtInWriteRef :: BuiltInFnWithDoc '[ '("ref", MutableRef), '("new_value", Value) ]
+builtInWriteRef ((coerce -> (MutableRef ref)) :> (coerce -> v) :> EmptyArgs) = do
+  void $ liftIO $ atomically $ swapTMVar ref v
+  pure Nothing
+
+builtInReadRef :: BuiltInFnWithDoc '[ '("ref", MutableRef) ]
+builtInReadRef ((coerce -> (MutableRef ref)) :>  EmptyArgs) = do
+  v <- liftIO $ atomically $ readTMVar ref
+  pure $ Just v
+
+builtInModifyRef :: BuiltInFnWithDoc '[ '("ref", MutableRef), '("callback", Callback) ]
+builtInModifyRef ((coerce -> (MutableRef ref)) :> (coerce -> callback) :> EmptyArgs) = do
+  v <- liftIO $ atomically $ takeTMVar ref
+  evaluateCallback callback [v] >>= \case
+    Just v' -> liftIO $ atomically $ putTMVar ref v'
+    Nothing -> throwErr MissingProcedureReturn
+  pure Nothing
diff --git a/src/Interpreter/Lib/Math.hs b/src/Interpreter/Lib/Math.hs
new file mode 100644
--- /dev/null
+++ b/src/Interpreter/Lib/Math.hs
@@ -0,0 +1,58 @@
+module Interpreter.Lib.Math where
+
+import Control.Monad.IO.Class
+import Data.Coerce
+import Data.Vector as V
+
+import Common
+import Interpreter.Common
+import System.Random
+
+builtInSum :: BuiltInFnWithDoc '[ '("numbers", Vector Number)]
+builtInSum ((coerce -> numbers) :> _)
+  = let
+    addFn = numberBinaryFn (+)
+    in pure $ Just $ NumberValue $ V.foldl' addFn (NumberInt 0) numbers
+
+builtInMod :: BuiltInFnWithDoc '[ '("divident", IntType), '("divisor", IntType)]
+builtInMod ((coerce -> divid) :> (coerce -> divis) :> _)
+  = pure $ Just $ NumberValue $ NumberInt $ mod divid divis
+
+builtInRound :: BuiltInFnWithDoc '[ '("value", FloatType)]
+builtInRound ((coerce -> (number :: FloatType)) :> _)
+  = pure $ Just $ NumberValue $ NumberInt $ round $ number
+
+builtInRandom :: BuiltInFnWithDoc '[ '("start_rage", IntType), '("end_range", IntType)]
+builtInRandom ((coerce -> start) :> (coerce -> end) :> _) = do
+  r <- liftIO $ randomRIO @IntType (start, end)
+  pure $ Just $ NumberValue $ NumberInt r
+
+builtInSin :: BuiltInFnWithDoc '[ '("angle", FloatType)]
+builtInSin ((coerce -> angle) :> _) = do
+  pure $ Just $ NumberValue $ NumberFractional (sin (degreeToRadian angle))
+
+builtInCos :: BuiltInFnWithDoc '[ '("angle", FloatType)]
+builtInCos ((coerce -> angle) :> _) = do
+  pure $ Just $ NumberValue $ NumberFractional (cos (degreeToRadian angle))
+
+builtInTan :: BuiltInFnWithDoc '[ '("angle", FloatType)]
+builtInTan ((coerce -> angle) :> _) = do
+  pure $ Just $ NumberValue $ NumberFractional (tan (degreeToRadian angle))
+
+builtInASin :: BuiltInFnWithDoc '[ '("arg", FloatType)]
+builtInASin ((coerce -> v) :> _) = do
+  pure $ Just $ NumberValue $ NumberFractional (radianToDegree $ asin v)
+
+builtInACos :: BuiltInFnWithDoc '[ '("arg", FloatType)]
+builtInACos ((coerce -> v) :> _) = do
+  pure $ Just $ NumberValue $ NumberFractional (radianToDegree $ acos v)
+
+builtInATan :: BuiltInFnWithDoc '[ '("arg", FloatType)]
+builtInATan ((coerce -> v) :> _) = do
+  pure $ Just $ NumberValue $ NumberFractional (radianToDegree $ atan v)
+
+radianToDegree :: FloatType -> FloatType
+radianToDegree x = (x/pi*180)
+
+degreeToRadian :: FloatType -> FloatType
+degreeToRadian x = (x/180*pi)
diff --git a/src/Interpreter/Lib/Misc.hs b/src/Interpreter/Lib/Misc.hs
new file mode 100644
--- /dev/null
+++ b/src/Interpreter/Lib/Misc.hs
@@ -0,0 +1,234 @@
+module Interpreter.Lib.Misc where
+
+import Control.Monad.IO.Class
+import Control.Concurrent.STM
+import qualified Data.Aeson as A
+import qualified Data.ByteString as BS
+import Data.Text.Encoding
+import qualified Data.ByteString.Lazy as BSL
+import Data.Coerce
+import qualified Data.Scientific as S
+import Data.Map as M
+import Text.Hex (encodeHex)
+import Data.HashMap.Strict as HM
+import Data.Text as T
+import Data.Text.IO as T
+import Data.Time.Clock.System
+import Data.Vector as V
+import qualified System.IO as S
+import Control.Monad.State.Strict as SM
+
+import UI.Widgets.Common
+import Interpreter.Common
+
+printValLn :: BuiltInFnWithDoc '[ '("value", Variadic)]
+printValLn ((coerce -> (Variadic vals)) :> EmptyArgs) = do
+  liftIO $ do
+    UI.Widgets.Common.mapM_ T.putStr (toStringVal <$> vals)
+    T.putStrLn ""
+    S.hFlush S.stdout
+  pure Nothing
+
+printVal :: BuiltInFnWithDoc '[ '("value", Variadic)]
+printVal ((coerce -> (Variadic vals)) :> EmptyArgs) = do
+  liftIO $ do
+    UI.Widgets.Common.mapM_ T.putStr (toStringVal <$> vals)
+    S.hFlush S.stdout
+  pure Nothing
+
+multiplication :: BuiltInFn
+multiplication (NumberValue v1: NumberValue v2 : []) = pure $ Just $ NumberValue $ numberBinaryFn (*) v1 v2
+multiplication a = throwBadArgs a "number"
+
+addition :: BuiltInFn
+addition (NumberValue v1: NumberValue v2 : []) = pure $ Just $ NumberValue $ numberBinaryFn (+) v1 v2
+addition (ArrayValue i1 : ArrayValue i2 : []) = pure $ Just $ ArrayValue $ i1 V.++ i2
+addition a = throwBadArgs a "number/list"
+
+substraction :: BuiltInFn
+substraction (NumberValue v1: NumberValue v2 : []) = pure $ Just $ NumberValue $ numberBinaryFn (-) v1 v2
+substraction a = throwBadArgs a "number"
+
+division :: BuiltInFn
+division (NumberValue _: NumberValue (NumberInt 0) : []) = throwErr $ CustomRTE "Divison by zero!"
+division (NumberValue _: NumberValue (NumberFractional 0.0) : []) = throwErr $ CustomRTE "Divison by zero!"
+division (NumberValue v1: NumberValue v2 : []) = pure $ Just $ NumberValue $ numberBinaryFractionalFn (/) v1 v2
+division a = throwBadArgs a "number"
+
+comparison :: (Value -> Value -> Bool) -> BuiltInFn
+comparison fn (v1: v2 : []) = pure $ Just $ BoolValue (fn v1 v2)
+comparison _ a              = throwBadArgs a "values"
+
+boolean :: (Bool -> Bool -> Bool) -> BuiltInFn
+boolean fn (BoolValue v1 : BoolValue v2 : []) = pure $ Just $ BoolValue (fn v1 v2)
+boolean _ a              = throwBadArgs a "bools"
+
+not' :: BuiltInFnWithDoc '[ '("bool", Bool)]
+not' ((coerce -> v1) :> _) = pure $ Just $ BoolValue (not v1)
+
+contains :: BuiltInFnWithDoc '[ '("list", Vector Value), '("item", Value)]
+contains ((coerce -> v1) :> (coerce -> v2) :> _) = pure $ Just $ BoolValue $ V.foldl' fn False v1
+  where
+    fn :: Bool -> Value -> Bool
+    fn True _  = True
+    fn False v = v2 == v
+
+haskey :: BuiltInFnWithDoc '[ '("dictionary",  M.Map Text Value), '("key", Text)]
+haskey ((coerce -> (map' :: M.Map Text Value)) :> (coerce -> key) :> _) = pure $ Just $ BoolValue $ M.member key map'
+
+arrayTake :: BuiltInFnWithDoc ['("source_list", Vector Value), '("count", Int)]
+arrayTake ((coerce -> v1) :> (coerce -> c) :> _) = pure $ Just $ ArrayValue (V.take c v1)
+
+arrayDrop :: BuiltInFnWithDoc ['("source_list", Vector Value), '("count", Int)]
+arrayDrop ((coerce -> v1) :> (coerce -> c) :> _) = pure $ Just $ ArrayValue (V.drop c v1)
+
+builtInArrayInsertLeft :: BuiltInFnWithDoc ['("item", Value), '("initial_list", Vector Value)]
+builtInArrayInsertLeft ((coerce -> c) :> (coerce -> v1) :> _) = pure $ Just $ ArrayValue (V.cons c v1)
+
+builtInArrayInsertRight :: BuiltInFnWithDoc ['("initial_list", Vector Value), '("item", Value)]
+builtInArrayInsertRight ((coerce -> v1) :> (coerce -> c) :> _) = pure $ Just $ ArrayValue (V.snoc v1 c)
+
+builtInWriteFile :: BuiltInFnWithDoc '[ '("filename", FilePath), '("data", BytesOrText)]
+builtInWriteFile ((coerce -> filepath) :> (coerce -> bot) :> EmptyArgs) = liftIO $ case bot of
+  BTBytes bin -> do
+      BS.writeFile filepath bin
+      pure Nothing
+  BTText dat -> do
+      T.writeFile filepath dat
+      pure Nothing
+
+builtInReadFile :: BuiltInFnWithDoc '[ '("filename", FilePath)]
+builtInReadFile ((coerce -> filepath) :> _) = liftIO $ do
+  c <- BS.readFile filepath
+  pure $ Just $ BytesValue c
+
+builtInReadTextFile :: BuiltInFnWithDoc '[ '("filename", FilePath)]
+builtInReadTextFile ((coerce -> filepath) :> _) = do
+  c <- liftIO $ T.readFile filepath
+  pure $ Just $ StringValue c
+
+builtInHead :: BuiltInFnWithDoc '[ '("source_list", Vector Value)]
+builtInHead ((coerce -> v1) :> _) = case V.uncons v1 of
+  Just (x, _) -> pure $ Just x
+  Nothing     -> throwErr $ CustomRTE "Empty list found for 'head' call"
+
+builtInTry :: BuiltInFnWithDoc '[ '("evaluation", Value), '("alternate", Maybe Value)]
+builtInTry ((coerce -> evaluation) :>  (coerce -> malternate) :> _) = case (evaluation, malternate) of
+  (ErrorValue _, Just a)      -> pure $ Just a
+  (e@(ErrorValue _), Nothing) -> pure $ Just e
+  (v, _)                      -> pure $ Just v
+
+builtInTimestamp :: BuiltInFnWithDoc '[]
+builtInTimestamp _ = do
+  st <- liftIO $ truncateSystemTimeLeapSecond <$> getSystemTime
+  pure $ Just $ NumberValue $ NumberInt $ ((fromIntegral $ systemSeconds st) * 1e9) + (fromIntegral $ systemNanoseconds st)
+
+builtInJSONSerialize :: BuiltInFnWithDoc '[ '("value", Value)]
+builtInJSONSerialize ((coerce -> (v :: Value)) :> _) =
+  (Just . BytesValue . BSL.toStrict . A.encode) <$> toAesonVal v
+
+builtInInspect :: BuiltInFnWithDoc '[ '("value", Value)]
+builtInInspect ((coerce -> (v :: Value)) :> _) = do
+  vText <- (decodeUtf8 . BSL.toStrict . A.encode) <$> toAesonVal v
+  liftIO $ do
+    T.putStr vText
+    S.hFlush S.stdout
+  pure Nothing
+
+builtInJSONParse :: BuiltInFnWithDoc '[ '("value", Value)]
+builtInJSONParse v  = let
+  bytes = case v of
+    ((coerce -> (StringValue b)) :> _) -> encodeUtf8 b
+    ((coerce -> (BytesValue b)) :> _) -> b
+    ((coerce -> (a :: Value)) :> _) -> throwErr $ BadArguments ("String/Bytes", T.pack $ show a)
+  in case A.eitherDecodeStrict bytes of
+    Right val -> pure $ Just $ fromAesonVal val
+    Left err -> pure $ Just $ ErrorValue ("JSON decoding failed with error:" <> (T.pack err))
+
+builtInDecodeUTF8Bytes :: BuiltInFnWithDoc '[ '("bytes", BS.ByteString)]
+builtInDecodeUTF8Bytes ((coerce -> b) :> _) = pure $ Just $ StringValue $ decodeUtf8 b
+
+builtInEncodeUTF8Bytes :: BuiltInFnWithDoc '[ '("string", Text)]
+builtInEncodeUTF8Bytes ((coerce -> b) :> _) = pure $ Just $ BytesValue $ encodeUtf8 b
+
+builtInDebug :: BuiltInFnWithDoc '[]
+builtInDebug _ = do
+  SM.modify (\is -> is { isRunMode = DebugMode, isStepMode = SingleStep })
+  pure Nothing
+
+fromAesonVal :: A.Value -> Value
+fromAesonVal (A.String s) = StringValue s
+fromAesonVal (A.Number s) = NumberValue $ if S.isInteger s then NumberInt (round s) else NumberFractional (realToFrac s)
+fromAesonVal (A.Bool b) = BoolValue b
+fromAesonVal (A.Array b) = ArrayValue (fromAesonVal <$> b)
+fromAesonVal (A.Object b) = ObjectValue (M.fromList $ HM.toList $ fromAesonVal <$> b)
+fromAesonVal A.Null = Void
+
+toAesonVal :: Value -> InterpretM A.Value
+toAesonVal Void = pure A.Null
+toAesonVal (StringValue s) = pure $ A.String s
+toAesonVal (NumberValue (NumberInt n)) = pure $ A.Number $ fromIntegral n
+toAesonVal (NumberValue (NumberFractional n)) = pure $ A.Number $ realToFrac n
+toAesonVal (BoolValue b) = pure $ A.Bool b
+toAesonVal (ArrayValue b) = do
+  vs <- V.mapM (\x -> toAesonVal x) b
+  pure $ A.Array vs
+toAesonVal (ObjectValue b) = do
+  vs <- Prelude.mapM (\(k, x) -> do v <- toAesonVal x; pure (k, v)) $ M.toList b
+  pure $ A.Object $ HM.fromList vs
+toAesonVal _ = throwErr $ CustomRTE "Unserializable value"
+
+waitMillisec :: BuiltInFnWithDoc '[ '("timeinseconds", Number)]
+waitMillisec ((coerce -> number) :> _) = (liftIO $ waitMillisec' number) >> pure Nothing
+
+waitForKey :: BuiltInFnWithDoc '[]
+waitForKey _ = do
+  inputChan <- isTerminalEventChan <$> get
+  (liftIO $ atomically $ readTChan inputChan) >>= \case
+    TerminalKey (KeyChar _ _ _ i) -> do
+      pure $ Just $ StringValue $ T.singleton i
+    _ -> pure $ Just $ StringValue ""
+
+readChannel :: TChan TerminalEvent -> String -> IO Text
+readChannel inputChan c = do
+  (liftIO $ atomically $ readTChan inputChan) >>= \case
+    TerminalKey (KeyCtrl _ _ _ Return) -> pure $ T.reverse $ T.pack c
+    TerminalKey (KeyChar _ _ _ i) -> do
+      S.putChar i
+      S.hFlush S.stdout
+      readChannel inputChan (i:c)
+    _ -> readChannel inputChan c
+
+builtinInputLine :: BuiltInFnWithDoc '[ '("prompt", Text)]
+builtinInputLine ((coerce -> prompt) :> _) = do
+  liftIO $ do
+    T.putStrLn prompt
+    S.hFlush S.stdout
+  inputChan <- isTerminalEventChan <$> get
+  (Just . StringValue) <$>  (liftIO $ readChannel inputChan "")
+
+toStringVal :: Value -> Text
+toStringVal = \case
+  StringValue t -> t
+  NumberValue (NumberInt i)    -> pack $ show i
+  NumberValue (NumberFractional i)    -> pack $ show i
+  BoolValue True   -> "true"
+  BoolValue False   -> "false"
+  BytesValue b -> "0x" <> encodeHex b
+  ArrayValue _  -> "[array]"
+  ObjectValue _ -> "[object]"
+  ProcedureValue _ -> "(procedure)"
+  ThreadRef _ -> "(thread_ref)"
+  Channel _ -> "(concurrency_channel)"
+  Ref _ -> "(mutable_ref)"
+  UnnamedFnValue _ -> "(unnamed_function)"
+  Void -> "(void)"
+  BuiltIn _ -> "(builtin)"
+  s@(ErrorValue _) -> pack $ show s
+  SDLValue s -> pack $ show s
+
+valueSize :: BuiltInFnWithDoc '[ '("list_or_map", Value)]
+valueSize ((coerce -> v1) :> _) = case v1 of
+  ArrayValue v  -> pure $ Just $ NumberValue $ NumberInt $ fromIntegral $ V.length v
+  ObjectValue m -> pure $ Just $ NumberValue $ NumberInt $ fromIntegral $ M.size m
+  v             -> throwErr (UnexpectedType ("Array or Object", v))
diff --git a/src/Interpreter/Lib/SDL.hs b/src/Interpreter/Lib/SDL.hs
new file mode 100644
--- /dev/null
+++ b/src/Interpreter/Lib/SDL.hs
@@ -0,0 +1,310 @@
+module Interpreter.Lib.SDL where
+
+import Control.Monad.IO.Class
+import Control.Monad.Loops
+import Control.Monad.State.Strict
+import qualified Data.ByteString as BS
+import System.Process (createPipe)
+import Data.Coerce
+import Data.IORef
+import Data.Int (Int32)
+import qualified Data.List as DL
+import qualified Data.Map as M
+import Data.Maybe
+import Data.WAVE
+import qualified Data.Vector as V
+import qualified Data.Vector.Storable as VS
+import Data.Word
+import Foreign.C.Types
+import SDL hiding (Keycode, Scancode, get)
+import qualified SDL
+import SDL.Mixer as SDLM
+
+import Interpreter.Common
+import Interpreter.Interpreter
+
+makeSinWaveChunk :: Double -> BS.ByteString
+makeSinWaveChunk freq = BS.pack $
+  fmap (\n ->
+         let t = fromIntegral n / 22050 :: Double
+         in round $ 127 + (127 * sin (2 * pi * freq * t)))
+      [0 :: Int32 .. 22050]
+
+createGraphicsWindow :: BuiltInFnWithDoc ['("width", Int), '("height", Int), '("accelerated", Maybe Bool)]
+createGraphicsWindow ((coerce -> w) :> (coerce -> h) :> (coerce -> maccelerated) :>_) =
+  initGraphics (Just (w, h)) (fromMaybe False maccelerated)
+
+createGraphicsFullscreen :: BuiltInFnWithDoc '[ '("accelerated", Maybe Bool)]
+createGraphicsFullscreen ((coerce -> maccelerated) :>_) =
+  initGraphics Nothing (fromMaybe False maccelerated)
+
+initGraphics :: Maybe (Int, Int) -> Bool -> InterpretM (Maybe Value)
+initGraphics md acc = do
+  let windowName = "S.P.A.D.E Program"
+  (renderer, window) <- liftIO $ do
+    SDL.initialize [SDL.InitVideo, SDL.InitAudio, SDL.InitEvents, SDL.InitTimer]
+    SDLM.openAudio SDLM.defaultAudio 256
+    window <- case md of
+      Just (w, h) -> SDL.createWindow windowName (windowConfig w h)
+      Nothing     -> SDL.createWindow windowName fullscreenConfig
+    renderer <- case acc of
+      True -> SDL.createRenderer window (-1) SDL.defaultRenderer
+      _ -> SDL.createRenderer window (-1) $ SDL.defaultRenderer { rendererType = SoftwareRenderer }
+    pure (renderer, window)
+  sdlWindowRefs <- isSDLWindows <$> get
+  liftIO $ modifyIORef sdlWindowRefs (\l -> (window : l))
+  modify (\x -> x
+    { isDefaultWindow = Just window
+    , isDefaultRenderer = Just renderer
+    , isAccelerated = Just acc
+    })
+  pure $ Just $ SDLValue $ Renderer renderer
+
+fullscreenConfig :: SDL.WindowConfig
+fullscreenConfig = SDL.defaultWindow
+  { windowHighDPI = False
+  , windowMode = FullscreenDesktop
+  }
+
+windowConfig :: Int -> Int -> SDL.WindowConfig
+windowConfig w h = SDL.defaultWindow
+  { windowHighDPI = False
+  , windowInitialSize = (SDL.V2 (fromIntegral w) (fromIntegral h))
+  , windowMode = Windowed
+  }
+
+setLogicalSize :: BuiltInFnWithDoc ['("x", CInt), '("y", CInt)]
+setLogicalSize ((coerce -> (lx :: CInt)) :> (coerce -> (ly :: CInt)) :> _) =
+  isDefaultRenderer <$> get >>= \case
+    Just renderer  -> do
+      SDL.V2 x y <- getWindowSize'
+      SDL.rendererScale renderer SDL.$= (SDL.V2 (realToFrac x/realToFrac lx) (realToFrac y/realToFrac ly))
+      pure Nothing
+    Nothing -> throwErr $ SDLError "Graphics not Initialized"
+
+getDefaultWindow :: InterpretM SDL.Window
+getDefaultWindow = isDefaultWindow <$> get >>= \case
+  Just x  -> pure x
+  Nothing -> throwErr $ SDLError "Graphics not Initialized"
+
+getDefaultRenderer :: InterpretM SDL.Renderer
+getDefaultRenderer = isDefaultRenderer <$> get >>= \case
+  Just x  -> pure x
+  Nothing -> throwErr $ SDLError "Graphics not Initialized"
+
+draw :: BuiltInFnWithDoc '[]
+draw _ = do
+  draw'
+  pure Nothing
+
+draw' :: InterpretM ()
+draw' = getDefaultRenderer >>= SDL.present
+
+drawIfNotAccelerated :: InterpretM ()
+drawIfNotAccelerated = (isAccelerated <$> get) >>= \case
+  (Just False) -> draw'
+  _            -> pure ()
+
+setDrawColor :: BuiltInFnWithDoc ['("red_component", Word8), '("green_component", Word8), '("blue_component", Word8)]
+setDrawColor ((coerce -> r) :> (coerce -> g) :> (coerce -> b) :> _) = do
+  getDefaultRenderer >>= \renderer -> do
+    SDL.rendererDrawColor renderer $= V4 r g b 0
+  pure Nothing
+
+clear :: BuiltInFnWithDoc '[]
+clear _ = do
+  getDefaultRenderer >>= SDL.clear
+  pure Nothing
+
+drawPoint :: BuiltInFnWithDoc ['("x", CInt), '("y", CInt)]
+drawPoint ((coerce -> x) :> (coerce -> y) :> _) = do
+  renderer <- getDefaultRenderer
+  SDL.drawPoint renderer (mkPoint x y)
+  drawIfNotAccelerated
+  pure Nothing
+
+drawPoints :: BuiltInFnWithDoc '[ '("points", VS.Vector (SDL.Point V2 CInt))]
+drawPoints ((coerce -> v) :> _) = do
+  renderer <- getDefaultRenderer
+  SDL.drawPoints renderer v
+  drawIfNotAccelerated
+  pure Nothing
+
+drawLines :: BuiltInFnWithDoc '[ '("points", VS.Vector (SDL.Point V2 CInt))]
+drawLines ((coerce -> v) :> _) = do
+  renderer <- getDefaultRenderer
+  SDL.drawLines renderer v
+  if VS.length v > 0 then do
+    let last' = VS.last v
+    SDL.drawPoint renderer last'
+    SDL.drawPoint renderer last'
+  else pure ()
+  drawIfNotAccelerated
+  pure Nothing
+
+drawLine :: BuiltInFnWithDoc ['("start_x", CInt), '("start_y", CInt), '("end_x", CInt), '("end_y", CInt)]
+drawLine ((coerce -> x) :> (coerce -> y) :> (coerce -> xEnd) :> (coerce -> yEnd) :>_) = do
+  renderer <- getDefaultRenderer
+  let endpoint = mkPoint xEnd yEnd
+  SDL.drawLine renderer (mkPoint x y) endpoint
+  SDL.drawPoint renderer endpoint
+  SDL.drawPoint renderer endpoint
+    -- Due to some bug in SDL, without these extra call
+    -- the next draw item appear to have a stray pixel with the same color
+    -- that was used to draw this one.
+  drawIfNotAccelerated
+  pure Nothing
+
+drawBox :: BuiltInFnWithDoc ['("start_x", CInt), '("start_y", CInt), '("width", CInt), '("height", CInt), '("fill", Maybe Bool)]
+drawBox ((coerce -> x) :> (coerce -> y) :> (coerce -> width) :> (coerce -> height) :> (coerce -> fill) :> _) = do
+  renderer <- getDefaultRenderer
+  let
+    f = case fill of
+      Just b -> b
+      Nothing -> False
+  if f
+    then SDL.fillRect renderer $ Just $ SDL.Rectangle (mkPoint x y) (SDL.V2 width height)
+    else SDL.drawRect renderer $ Just $ SDL.Rectangle (mkPoint x y) (SDL.V2 width height)
+  drawIfNotAccelerated
+  pure Nothing
+
+drawCircle :: BuiltInFnWithDoc ['("center_x", Double), '("center_y", Double), '("radius", Double)]
+drawCircle ((coerce -> x) :> (coerce -> y) :> (coerce -> radius) :> _) = do
+  renderer <- getDefaultRenderer
+  let fullCircle = 2.0 * pi
+  let segments = 50
+  let (oneSegment :: Double) = fullCircle/segments
+  let points = VS.fromList $ (\a -> mkPoint (round $ x + cos (oneSegment * a) * radius) (round $ y + sin (oneSegment * a) * radius)) <$> [0..segments]
+  SDL.drawLines renderer points
+  let last' = VS.last points
+  SDL.drawPoint renderer last'
+  SDL.drawPoint renderer last'
+  drawIfNotAccelerated
+  pure Nothing
+
+waitForSDLKey :: BuiltInFnWithDoc '[]
+waitForSDLKey _ = do
+  void $ iterateWhile id $ do
+    events <- pollEvents
+    pure $ ((length $ filter filterEvent events) == 0)
+  pure Nothing
+  where
+    filterEvent :: Event -> Bool
+    filterEvent event =
+      case eventPayload event of
+        KeyboardEvent keyboardEvent ->
+          (keyboardEventKeyMotion keyboardEvent == Pressed)
+        _ -> False
+
+getWindowSize' :: InterpretM (SDL.V2 CInt)
+getWindowSize' = do
+  window <- getDefaultWindow
+  liftIO $ SDL.get (windowSize window)
+
+getWindowSize :: BuiltInFnWithDoc '[]
+getWindowSize _ = do
+  (SDL.V2 x y) <- getWindowSize'
+  pure $ Just $ ObjectValue $ M.fromList [("width", NumberValue $ NumberInt $ fromIntegral x), ("height", NumberValue $ NumberInt $ fromIntegral y)]
+
+getKeyboardState :: BuiltInFnWithDoc '[]
+getKeyboardState _ = do
+  SDL.pumpEvents
+  fn <- SDL.getKeyboardState
+  pure $ Just $ SDLValue $ KeyboardState $ SDLKeyboardStateCallback fn
+
+wasKeyDownIn :: BuiltInFnWithDoc '[ '("keyboard_state", SDLKeyboardStateCallback), '("key", SDL.Scancode) ]
+wasKeyDownIn ((coerce -> (SDLKeyboardStateCallback cb)) :> (coerce -> scancode ) :> _) =
+  pure $ Just $ BoolValue $ cb scancode
+
+getKeys :: BuiltInFnWithDoc '[]
+getKeys _ = do
+  events <- pollEvents
+  pure $ Just $ ArrayValue $ DL.foldl' convertEvent V.empty events
+  where
+    convertEvent :: V.Vector Value -> Event -> V.Vector Value
+    convertEvent inp event =
+      case eventPayload event of
+        KeyboardEvent keyboardEvent ->
+            V.cons (SDLValue $ Keycode ((keysymKeycode (keyboardEventKeysym keyboardEvent)))) inp
+        _ -> inp
+
+builtInSetSampleVolume :: BuiltInFnWithDoc '[ '("channel", Channel), '("volume", Int)]
+builtInSetSampleVolume ((coerce -> (channel :: Channel)) :> (coerce -> volume) :> _) = do
+  SDLM.setVolume volume channel
+  pure Nothing
+
+builtInSetSampleLRVolume :: BuiltInFnWithDoc '[ '("sample", Channel), '("volume_left", Int), '("volume_right", Int)]
+builtInSetSampleLRVolume ((coerce -> (channel :: Channel)) :> (coerce -> volumel) :> (coerce -> volumer) :> _) = do
+  void $ SDLM.effectPan channel volumel volumer
+  pure Nothing
+
+builtInPlaySoundSample :: BuiltInFnWithDoc '[ '("sample", Sample), '("channel", Int)]
+builtInPlaySoundSample ((coerce -> sample) :> (coerce -> (channel :: Int)) :> _) = do
+  void $ SDLM.playOn (fromIntegral channel) SDLM.Forever sample
+  pure Nothing
+
+makeSound :: (Int, [WAVESample]) -> InterpretM Value
+makeSound (sampleCount, samplesRaw) = do
+  (rEnd, wEnd) <- liftIO createPipe
+  let waveData = WAVE (WAVEHeader 1 44100 16 $ Just sampleCount) [samplesRaw]
+  waveEncodedData <- liftIO $ do
+    hPutWAVE wEnd waveData
+    BS.hGetContents rEnd
+  chunk <- liftIO $ SDLM.decode waveEncodedData
+  pure $ SDLValue $ SoundSample chunk
+
+builtInMakeTone :: BuiltInFnWithDoc '[ '("freq", Double) ]
+builtInMakeTone ((coerce -> freq) :> _) = do
+  let samplesInOneCycle = 44100 / freq
+  let multiplier = (2 * pi)/samplesInOneCycle
+  let samplesRaw = [doubleToSample $ sin (realToFrac x * multiplier) | x <- [0 .. (round samplesInOneCycle - 1)]]
+  Just <$> makeSound (round samplesInOneCycle, samplesRaw)
+
+builtInMakeSoundSample :: BuiltInFnWithDoc '[ '("samplecount", Int), '("callback", Callback)]
+builtInMakeSoundSample ((coerce -> sampleCount) :> (coerce -> (cb :: Callback)) :> _) = do
+  samplesRaw <- getSamples sampleCount
+  Just <$> makeSound (sampleCount , samplesRaw)
+  where
+    getSamples :: Int -> InterpretM [WAVESample]
+    getSamples sc = mapM (\x -> mapFn x) [1..(fromIntegral sc)]
+
+    mapFn :: Integer -> InterpretM WAVESample
+    mapFn si = (doubleToSample . (fromValue @Double) . fromMaybe (throwErr MissingProcedureReturn)) <$>
+      evaluateCallback cb [NumberValue $ NumberInt si]
+
+builtInMakeSoundSampleFromFile :: BuiltInFnWithDoc '[ '("filepath", FilePath)]
+builtInMakeSoundSampleFromFile ((coerce -> filePath) :> _) = do
+  chunk <- liftIO $ SDLM.load filePath
+  pure $ Just $ SDLValue $ SoundSample chunk
+
+cleanupSDL :: InterpretM ()
+cleanupSDL = do
+  sdlWindowRefs <- isSDLWindows <$> get
+  windows <- liftIO $ readIORef sdlWindowRefs
+  mapM_ (liftIO . SDL.destroyWindow) windows
+  modify (\x -> x { isDefaultRenderer = Nothing })
+  modify (\x -> x { isDefaultWindow = Nothing })
+  SDLM.closeAudio
+  SDL.quit
+
+keycodes :: Value
+keycodes = ObjectValue $ M.fromList
+  [ ("up", SDLValue $ Keycode KeycodeUp)
+  , ("down", SDLValue $ Keycode KeycodeDown)
+  , ("left", SDLValue $ Keycode KeycodeLeft)
+  , ("right", SDLValue $ Keycode KeycodeRight)
+  , ("a", SDLValue $ Keycode KeycodeA)
+  , ("b", SDLValue $ Keycode KeycodeB)
+  , ("c", SDLValue $ Keycode KeycodeC)
+  , ("q", SDLValue $ Keycode KeycodeQ)
+  , ("s", SDLValue $ Keycode KeycodeS)
+  ]
+
+scancodes :: Value
+scancodes = ObjectValue $ M.fromList
+  [ ("up", SDLValue $ Scancode ScancodeUp)
+  , ("down", SDLValue $ Scancode ScancodeDown)
+  , ("left", SDLValue $ Scancode ScancodeLeft)
+  , ("right", SDLValue $ Scancode ScancodeRight)
+  , ("q", SDLValue $ Scancode ScancodeQ)
+  ]
diff --git a/src/Parser.hs b/src/Parser.hs
new file mode 100644
--- /dev/null
+++ b/src/Parser.hs
@@ -0,0 +1,8 @@
+module Parser
+  ( module Parser.Parser
+  , module Parser.Lib
+  ) where
+
+import Parser.Lib
+import Parser.Parser
+
diff --git a/src/Parser/Lib.hs b/src/Parser/Lib.hs
new file mode 100644
--- /dev/null
+++ b/src/Parser/Lib.hs
@@ -0,0 +1,101 @@
+module Parser.Lib where
+
+import Control.Applicative
+import Control.Monad
+import Control.Monad.IO.Class
+import Data.Text as T
+
+import Common
+import Parser.Parser
+
+runParserEither :: forall s a m. (Show s, HaveLocation s, HasEmpty s, MonadIO m) => ParserM m s a -> s -> m (Either (ParseErrorWithParsed a) a)
+runParserEither (ParserM _ fn) s = do
+  (r, rst) <- fn s
+  case r of
+    Right res ->
+      if isEmpty rst then pure $ Right res else pure $ Left $ ParseErrorWithParsed (Just res) (getLocation rst) (FatalError IncompleteParse)
+    Left err -> pure $ Left $ ParseErrorWithParsed Nothing (getLocation rst) err
+
+runParser :: forall a s m. (Show s, HaveLocation s, HasEmpty s, MonadIO m) => ParserM m s a -> s -> m (Maybe a)
+runParser p s = runParserEither p s >>= \case
+  Right a -> pure $ Just a
+  Left _  -> pure Nothing
+
+incLine :: Int -> Parser ()
+incLine lc = ParserM "" $ \s ->  pure (Right (), s { twLocation = moveLines (twLocation s) lc })
+
+lookAhead :: Monad m => ParserM m s a -> ParserM m s (Maybe a)
+lookAhead (ParserM name fn) = ParserM ("lookahead for (" <> name <> ")") $ \s ->
+  fn s >>= \case
+    (Right a, _) -> pure (Right $ Just a, s)
+    (_, _)     -> pure (Right Nothing, s)
+
+eof :: (HasEof s, Monad m) => ParserM m s ()
+eof = ParserM "EOF" $ \s -> case isEof s of
+  True  -> pure (Right (), s)
+  False -> pure (Left CantHandle, s)
+
+noteof :: (HasEof s, Monad m) => ParserM m s ()
+noteof = ParserM "NOT_EOF" $ \s -> case isEof s of
+  True -> pure (Left CantHandle, s)
+  False  -> pure (Right (), s)
+
+pAny :: (Char -> Bool) -> Parser Char
+pAny fn = ParserM "" (\t ->
+    pure $ case T.uncons $ twText t of
+      Just (h, rst) -> if (fn h)
+        then (Right h, t { twText = rst, twLocation = moveCols (twLocation t) 1 })
+        else (Left CantHandle, t)
+      Nothing -> (Left CantHandle, t)
+  )
+
+pChar :: Char -> Parser Char
+pChar c = pAny (== c)
+
+pText :: Text -> Parser Text
+pText l = ParserM l (\t ->
+  pure $ if isPrefixOf l $ twText t
+    then let tLen = T.length l
+      in (Right l, t { twText = T.drop tLen $ twText t, twLocation = moveCols (twLocation t) tLen})
+    else (Left CantHandle, t))
+
+parseAndReturn :: Text -> a -> Parser a
+parseAndReturn t a = do
+  void $ pText t
+  pure a
+
+testParser :: Parser [Char]
+testParser = do
+  a <- pChar 'a'
+  b <- pChar 'b'
+  pure [a, b]
+
+cantHandle :: Monad m => ParserM m s a
+cantHandle = ParserM "" (\s -> pure (Left CantHandle, s))
+
+nameParser :: Text -> ParserM m s a -> ParserM m s a
+nameParser name (ParserM _ f) = ParserM name f
+
+class HasInnerParseable a where
+  type InnerToken a
+  assemble :: InnerToken a -> Location -> Int -> a
+
+class HasParser a where
+  parser :: Parser a
+
+instance HasParser a => HasParser [a] where
+  parser = many parser
+
+instance {-# OVERLAPPABLE #-} (HasInnerParseable a, ToSource (InnerToken a), HasParser (InnerToken a)) => HasParser a where
+  parser = do
+    lc <- getParserLocation
+    tr <- parser @(InnerToken a)
+    let tLength = (T.length $ toSource tr)
+    incOffset tLength
+    pure $ assemble tr lc (lcOffset lc + tLength - 1)
+    where
+      incOffset :: Int -> Parser ()
+      incOffset size = ParserM "" $ \s -> let
+        location = twLocation s
+        currentOffset = lcOffset location
+        in pure (Right (), s { twLocation =  location { lcOffset = currentOffset + size }} )
diff --git a/src/Parser/Parser.hs b/src/Parser/Parser.hs
new file mode 100644
--- /dev/null
+++ b/src/Parser/Parser.hs
@@ -0,0 +1,158 @@
+{-# LANGUAGE InstanceSigs #-}
+module Parser.Parser where
+
+import Common
+
+import Control.Applicative
+import Control.Monad.IO.Class
+import Data.String (IsString(..))
+import Data.Text as T
+
+data ParseErrorWithParsed a = ParseErrorWithParsed
+  { parialResult :: Maybe a
+  , errorAt :: Location
+  , parseError :: ParseError
+  } deriving (Eq, Show)
+
+instance HReadable (ParseErrorWithParsed a) where
+  hReadable ParseErrorWithParsed {..} = "Incomplete parse: Error at: " <> (hReadable errorAt) <> " Error:" <> (hReadable parseError)
+
+data FatalParseError
+  = IncompleteParse
+  | CustomError Text
+  deriving (Show, Eq)
+
+instance HReadable FatalParseError where
+  hReadable = \case
+    IncompleteParse -> "Input does not match any known pattern at location"
+    CustomError msg -> "Fatal parse error:" <> msg
+
+moveLines :: Location -> Int -> Location
+moveLines Location {..} lc = let
+  lcNewLine = lcLine + lc
+  in Location {lcLine = lcNewLine, lcColumn = 0, ..}
+
+moveCols :: Location -> Int -> Location
+moveCols Location {..} lc = let
+  lcNewColumn = lcColumn + lc
+  in Location {lcColumn = lcNewColumn, ..}
+
+data ParseError
+  = CantHandle
+  | Empty
+  | FatalErrorWithLocation Location FatalParseError
+  | FatalError FatalParseError
+  deriving (Show, Eq)
+
+instance HReadable ParseError where
+  hReadable = \case
+    CantHandle -> "Parser can't handle this input"
+    Empty -> "The input was empty"
+    FatalErrorWithLocation l fp -> "Fatal parse error:" <> (hReadable fp) <>  " at:" <> (hReadable l)
+    FatalError fp -> "Fatal parse error:" <> (hReadable fp)
+
+class HaveLocation a where
+  getLocation :: a -> Location
+
+getParserLocation :: (Monad m, HaveLocation s) => ParserM m s Location
+getParserLocation = ParserM "" $ \s -> pure (Right $ getLocation s, s)
+
+data ParserM m s a = ParserM Text (s -> m (Either ParseError a, s))
+
+data TextWithOffset = TextWithOffset
+  { twText     :: Text
+  , twLocation :: Location
+  } deriving (Eq, Show)
+
+class HasEof a where
+  isEof :: a -> Bool
+
+instance ToSource TextWithOffset where
+  toSource = twText
+
+instance HasEof TextWithOffset where
+  isEof (TextWithOffset "" _) = True
+  isEof _                     = False
+
+instance HasEmpty TextWithOffset where
+  isEmpty t = twText t == ""
+
+toTextWithOffset :: Text -> TextWithOffset
+toTextWithOffset t = TextWithOffset t emptyLocation
+
+instance HaveLocation TextWithOffset where
+  getLocation = twLocation
+
+instance IsString s => IsString TextWithOffset where
+  fromString s = toTextWithOffset $ T.pack s
+
+class HasLogIndent a where
+  incIndent :: a -> a
+  decIndent :: a -> a
+  logInfo :: MonadIO m => Text -> a -> m ()
+
+instance Show a => HasLogIndent a where
+  incIndent a = a
+  decIndent a = a
+  logInfo _ _ = pure ()
+
+type Parser a = ParserM IO TextWithOffset a
+
+type ParserC m s = (MonadIO m, HasLogIndent s, ToSource s)
+
+instance ParserC m s => Functor (ParserM m s) where
+  fmap f (ParserM name a) = ParserM name (\s -> (a $ incIndent s) >>= \case
+    (Right a', s') -> pure (Right $ f a', s')
+    (Left e, _)    -> pure (Left e, s))
+
+instance ParserC m s => Applicative (ParserM m s) where
+  pure a = ParserM "pure" (\s -> pure (Right a, s))
+  (ParserM name1 f1) <*> (ParserM name2 f2) = ParserM (name2 <> " after " <> name1)
+    (\s -> (do logInfo name1 s; f1 $ incIndent s) >>= \case
+      (Right fn, rst)  -> (f2 $ decIndent rst) >>= \case
+        (mf, rst1) -> pure (fn <$> mf, decIndent rst1)
+      (Left err, s') -> pure (Left err, decIndent s')
+    )
+
+instance ParserC m s => Monad (ParserM m s) where
+  return = pure
+  (ParserM name1 f1) >>= f  =
+    ParserM name1 (\s -> (do logInfo name1 s; f1 $ incIndent s) >>= \case
+          (Right a1, rst) ->
+            let
+              (ParserM name2 f2) = f a1
+              rst' = decIndent rst
+            in do
+              logInfo ("success " <> name1) rst'
+              logInfo ("parsing " <> name2) rst'
+              f2 rst'
+          (Left err, s') -> do
+            logInfo ("failed " <> name1) s'
+            pure (Left err, s'))
+
+instance ParserC m s => Alternative (ParserM m s) where
+  (ParserM name1 f1) <|> (ParserM name2 f2) = ParserM ("(" <> name1 <> ")"  <> " or (" <> name2 <> ")") (\s -> (do logInfo name1 s; f1 s)>>= \case
+    (Left e@(FatalError _), s')               -> pure (Left e, s')
+    (Left e@(FatalErrorWithLocation _ _), s') -> pure (Left e, s')
+    (Left _, _)                               -> f2 s
+    a                                         -> pure a )
+  empty = ParserM "empty" (\s -> pure (Left Empty, s))
+  many p@(ParserM name _) = let (ParserM _ fn) = ((some p) <|> (pure [])) in ParserM ("many of " <> name) fn
+  some (ParserM name fn) = ParserM ("some of " <> name) (\s -> collect ([], s) >>= \case
+      (Right [], _)          -> pure (Left CantHandle, s)
+      (Right (r@(_:_)), rst) -> pure (Right (Prelude.reverse r), rst)
+      (Left err, rst)        -> pure (Left err, rst))
+    where
+      collect (i, s) = fn s >>= \case
+        (Right a, rst)                            -> collect ((a:i), rst)
+        (Left e@(FatalError _), s')               -> pure (Left e, s')
+        (Left e@(FatalErrorWithLocation _ _), s') -> pure (Left e, s')
+        (Left _, _)                               -> pure (Right i, s)
+
+instance (ParserC m s, HaveLocation s) => MonadFail (ParserM m s) where
+  fail err = ParserM "fail" (\s -> pure (Left (FatalErrorWithLocation (getLocation s) $ CustomError $ pack err), s))
+
+instance (ParserC m s, MonadIO m) => MonadIO (ParserM m s) where
+  liftIO io = ParserM "liftIO" (\s -> do
+    r <- liftIO io
+    pure (Right r, s))
diff --git a/src/Test.hs b/src/Test.hs
new file mode 100644
--- /dev/null
+++ b/src/Test.hs
@@ -0,0 +1,24 @@
+module Test where
+
+import Compiler.Parser
+import Control.Applicative
+import Data.Text as T
+import Data.Text.IO as T
+import Parser
+
+import Compiler.AST.Parser.Common
+import Compiler.AST.Program
+
+testDummy :: IO ()
+testDummy = do
+  src <- T.readFile "/tmp/input.txt"
+  tokens <- tokenize src
+  --T.putStrLn $ T.pack $ show tokens
+  x <- runParserEither (do
+         st <- optional testParser_
+         pure st
+    ) $ mkAstParserState tokens
+  T.putStrLn $ T.pack $ show x
+  pure ()
+  where
+    testParser_ = astParser @Program
diff --git a/src/Test/Common.hs b/src/Test/Common.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/Common.hs
@@ -0,0 +1,23 @@
+module Test.Common
+  ( module Test.Common
+  , module Hedgehog
+  , module Hedgehog.Gen
+  , module Hedgehog.Range
+  , module Test.Hspec
+  ) where
+
+import Hedgehog (Gen, Property, forAll, liftTest, property, (===))
+import Hedgehog.Gen (bool, choice, element, enum, int, list, lower, maybe,
+                     realFrac_, recursive, text, upper)
+import Hedgehog.Range (linear, linearFrac)
+import Test.Hspec (Spec, describe, it)
+import Test.Hspec.Hedgehog
+
+class HasGen a where
+  getGen :: Gen a
+
+instance HasGen a => HasGen [a] where
+  getGen = list (linear 0 5) getGen
+
+instance HasGen a => HasGen (Maybe a) where
+  getGen = Hedgehog.Gen.maybe getGen
diff --git a/src/UI/Chars.hs b/src/UI/Chars.hs
new file mode 100644
--- /dev/null
+++ b/src/UI/Chars.hs
@@ -0,0 +1,21 @@
+module UI.Chars where
+
+import Data.Char
+
+verticalLine :: Char
+verticalLine = chr 0x2502
+
+horizontalLine :: Char
+horizontalLine = chr 0x2500
+
+cornerLT :: Char
+cornerLT = chr 0x250c
+
+cornerRT :: Char
+cornerRT = chr 0x2510
+
+cornerRB :: Char
+cornerRB = chr 0x2518
+
+cornerLB :: Char
+cornerLB = chr 0x2514
diff --git a/src/UI/Terminal/IO.hs b/src/UI/Terminal/IO.hs
new file mode 100644
--- /dev/null
+++ b/src/UI/Terminal/IO.hs
@@ -0,0 +1,23 @@
+module UI.Terminal.IO
+  ( module UI.Terminal.IO
+  , S.stdin
+  , S.stdout
+  , S.BufferMode (..)
+  ) where
+
+import Data.Text (Text)
+import qualified System.IO as S
+
+class Monad m => HasTerminal m where
+  setCursorPosition :: Int -> Int -> m ()
+  hideCursor :: m ()
+  showCursor :: m ()
+  putText :: Text -> m ()
+  putTextFlush :: Text -> m ()
+  hFlush :: m ()
+  hSetEcho :: S.Handle -> Bool -> m ()
+  hSetBuffering :: S.Handle -> S.BufferMode -> m ()
+  hGetChar :: m Char
+  hWaitForInput :: m Bool
+  clearscreen :: m ()
+  clearline :: m ()
diff --git a/src/UI/Widgets.hs b/src/UI/Widgets.hs
new file mode 100644
--- /dev/null
+++ b/src/UI/Widgets.hs
@@ -0,0 +1,14 @@
+module UI.Widgets
+  ( module UI.Widgets.Common
+  , module UI.Widgets.Editor
+  , module UI.Widgets.BorderBox
+  , module UI.Widgets.MenuContainer
+  ) where
+
+import Control.Monad
+import System.IO
+
+import UI.Widgets.BorderBox
+import UI.Widgets.Common
+import UI.Widgets.Editor
+import UI.Widgets.MenuContainer
diff --git a/src/UI/Widgets/AutoComplete.hs b/src/UI/Widgets/AutoComplete.hs
new file mode 100644
--- /dev/null
+++ b/src/UI/Widgets/AutoComplete.hs
@@ -0,0 +1,99 @@
+module UI.Widgets.AutoComplete where
+
+import Data.Text as T
+import Data.Typeable (Proxy, eqT, (:~:)(..))
+import System.Console.ANSI (Color(..))
+import Text.Printf
+
+import Common
+import Highlighter.Highlighter
+import UI.Widgets.Common
+
+data AutoCompleteWidget = AutoCompleteWidget
+  { acwContent    :: [(Text, Text)]
+  , acwSelected   :: Int
+  , acwPos        :: ScreenPos
+  , acwVisibility :: Bool
+  }
+
+instance Container AutoCompleteWidget [(Text, Text)] where
+  setContent ref t = do
+    modifyWRef ref (\tcw -> tcw { acwContent = t, acwSelected = 0 })
+  getContent ref =
+    acwContent <$> readWRef ref
+
+instance Selectable AutoCompleteWidget  where
+  getSelection ref = do
+    w <- readWRef ref
+    case safeIndex (acwContent w) (acwSelected w) of
+      Just (x, _) -> pure x
+      Nothing -> pure ""
+
+instance KeyInput AutoCompleteWidget  where
+  getCursorInfo _ = pure Nothing
+  handleInput ref ev =
+    case ev of
+      KeyCtrl _ _ _ ArrowUp -> do
+        w <- readWRef ref
+        let selected = acwSelected w
+        if (selected > 0)
+          then modifyWRef ref (\a -> a { acwSelected = selected - 1 } )
+          else pure ()
+      KeyCtrl _ _  _ ArrowDown -> do
+        w <- readWRef ref
+        let selected = acwSelected w
+        if (selected < (Prelude.length (acwContent w) - 1))
+          then modifyWRef ref (\a -> a { acwSelected = selected + 1 } )
+          else pure ()
+      _ -> pure ()
+
+instance Moveable AutoCompleteWidget where
+  move r p = modifyWRef r (\w -> w { acwPos =  p })
+  getPos r = acwPos <$> readWRef r
+  getDim r = do
+    w <- readWRef r
+    let
+      c = acwContent w
+      contentLength = Prelude.length $ acwContent w
+      maxContentLength = case c of
+        [] -> 0
+        cnt@(_:_) -> Prelude.maximum $ (T.length . snd) <$> cnt
+    pure $ Dimensions (maxContentLength+2) (contentLength+2)
+  resize _ _ = pure ()
+
+instance Widget AutoCompleteWidget where
+  hasCapability (DrawableCap _) = Just Dict
+  hasCapability (SelectableCap _) = Just Dict
+  hasCapability (KeyInputCap _) = Just Dict
+  hasCapability (MoveableCap _) = Just Dict
+  hasCapability (ContainerCap _ (_ :: Proxy cnt)) = case eqT @cnt @([(Text, Text)]) of
+    Just Refl -> Just Dict
+    Nothing   -> Nothing
+
+instance Drawable AutoCompleteWidget where
+  setVisibility ref v = modifyWRef ref (\c -> c { acwVisibility = v })
+  getVisibility ref = acwVisibility <$> readWRef ref
+  draw ref = do
+    w <- readWRef ref
+    if (acwVisibility w)
+      then do
+        case acwContent w of
+          (_:_) -> do
+            let contentLen = Prelude.length $ acwContent w
+            let maxWidth = Prelude.maximum (0 : ((T.length . snd) <$> acwContent w))
+            drawBorderBox  (acwPos w) (Dimensions (maxWidth + 2) (2 + contentLen))
+            mapM_ (putOneItem w (acwPos w) maxWidth) $ Prelude.zip [0..] (acwContent w)
+          _ -> pure ()
+      else pure ()
+    where
+      putOneItem w sp mw (offset, item) = do
+        wSetCursor
+          $ moveDown (offset + 1) $ moveRight 1 sp
+        let stx = "%-" <> (show mw) <>"s"
+        if offset == (acwSelected w)
+            then csPutText (colorTextFg Blue  (T.pack $ printf stx (snd item)))
+            else csPutText (colorTextFg White (T.pack $ printf stx (snd item)))
+
+autoComplete
+  :: WidgetM m (WRef AutoCompleteWidget)
+autoComplete = newWRef $ AutoCompleteWidget [] 0 origin False
diff --git a/src/UI/Widgets/BorderBox.hs b/src/UI/Widgets/BorderBox.hs
new file mode 100644
--- /dev/null
+++ b/src/UI/Widgets/BorderBox.hs
@@ -0,0 +1,56 @@
+module UI.Widgets.BorderBox where
+
+import UI.Widgets.Common as C
+import Common
+
+data BorderBoxWidget = BorderBoxWidget
+  { bbwDim     :: Dimensions
+  , bbwContent :: SomeWidgetRef
+  , bbwPos     :: ScreenPos
+  , bbwVisible :: Bool
+  }
+
+instance Container BorderBoxWidget SomeWidgetRef where
+  setContent ref c =
+    modifyWRef ref (\bbw -> bbw { bbwContent = c })
+  getContent ref = bbwContent <$> readWRef ref
+
+instance Moveable BorderBoxWidget where
+  getPos ref = bbwPos <$> readWRef ref
+  move ref sp =
+    modifyWRef ref (\bbw -> bbw { bbwPos = sp })
+  getDim ref =
+    bbwDim <$> readWRef ref
+  resize ref cb =
+    modifyWRef ref (\low -> low { bbwDim = cb $ bbwDim low })
+
+instance Widget BorderBoxWidget where
+  hasCapability (DrawableCap _)  = Just Dict
+  hasCapability _            = Nothing
+
+instance Drawable BorderBoxWidget where
+  setVisibility ref v = modifyWRef ref (\b -> b { bbwVisible = v })
+  getVisibility ref = bbwVisible <$> readWRef ref
+  draw ref = do
+    w <- readWRef ref
+    drawBorderBox (bbwPos w) (bbwDim w)
+    case bbwContent w of
+      SomeWidgetRef a -> do
+        withCapability (DrawableCap a) $ do
+          withCapability (MoveableCap a) $ do
+            move a (moveDown 1 $ moveRight 1 $ bbwPos w)
+            draw a
+
+borderBox
+  :: forall m. ScreenPos
+  -> Dimensions
+  -> SomeWidgetRef
+  -> WidgetM m (WRef BorderBoxWidget)
+borderBox pos dim child = do
+  newWRef $
+    BorderBoxWidget
+    { bbwDim = dim
+    , bbwContent = child
+    , bbwPos = pos
+    , bbwVisible = True
+    }
diff --git a/src/UI/Widgets/Common.hs b/src/UI/Widgets/Common.hs
new file mode 100644
--- /dev/null
+++ b/src/UI/Widgets/Common.hs
@@ -0,0 +1,455 @@
+module UI.Widgets.Common
+  ( module UI.Widgets.Common
+  , module UI.Terminal.IO
+  , module Control.Monad
+  , module Data.Text
+  , module Control.Monad.IO.Class
+  , module Control.Monad.State.Strict
+  , module Data.Constraint
+  , module GHC.Stack
+  ) where
+
+import Common
+import Control.Concurrent.STM (atomically)
+import Control.Concurrent.STM.TChan
+import Control.Exception
+import Control.Monad.IO.Class
+import Control.Monad.Loops (iterateWhile)
+import Data.Constraint
+import Data.Kind (Type)
+import qualified Data.List as DL
+import Data.Map.Strict as M hiding (keys)
+import Data.Maybe
+import Data.Text as T
+import Data.Text hiding (lines)
+import qualified Data.Text as C
+import Data.Text.IO as T
+import Data.Typeable (Proxy(..), Typeable, cast, typeRep)
+import Data.Vector.Mutable (IOVector)
+import qualified Data.Vector.Mutable as MV
+import GHC.Stack
+import qualified Graphics.Vty as VTY
+import System.Random
+import UI.Chars
+import UI.Terminal.IO
+
+import Control.Monad
+import Control.Monad.State.Strict
+import qualified System.Console.ANSI as A
+import qualified System.IO as S
+
+data WidgetState = WidgetState
+  { wsWidgets         :: Map Int SomeWidget
+  , wsCursorWidget    :: Maybe SomeKeyInputWidget -- Widget that authoritativly decide the status/location of cursor. Does not decide what widgets receive keyboard input
+  , wsScreenState     :: ScreenState
+  , wsCursorVisible   :: Bool
+  , wsScreenStateBack :: ScreenState
+  }
+
+data ScreenState = ScreenState
+  { ssLines          :: IOVector [StyledText]
+  , ssCursorPos      :: ScreenPos
+  , ssColumns        :: Int
+  , ssCursorOverflow :: Bool
+  }
+
+setCursorVisibility :: WidgetC m => Bool ->  m ()
+setCursorVisibility b = modify (\ws -> ws { wsCursorVisible = b })
+
+emptyScreenState :: Int -> Int -> IO ScreenState
+emptyScreenState rows cols = do
+  stLines <- MV.generate rows (\_ -> [Plain (T.replicate cols " ")])
+  pure (ScreenState stLines (ScreenPos 0 0) cols True)
+
+emptyWidgetState :: Int -> Int -> IO WidgetState
+emptyWidgetState lineCount columns = do
+  ss <- emptyScreenState lineCount columns
+  ssBack <- emptyScreenState lineCount columns
+  pure $ WidgetState mempty Nothing ss True ssBack
+
+type WidgetM m a = MonadIO m => StateT WidgetState m a
+
+runWidgetM' :: MonadIO m => WidgetM m a -> m (a, WidgetState)
+runWidgetM' act = do
+  ws <- liftIO $ emptyWidgetState 0 0
+  flip runStateT ws act
+
+runWidgetM :: MonadIO m => WidgetM m a -> m a
+runWidgetM act =  fst <$> runWidgetM' act
+
+type WidgetC m =
+  ( HasCallStack
+  , HasCharScreen m
+  , HasRandom m
+  , HasLog m
+  , HasTerminal m
+  , MonadState WidgetState m
+  , MonadIO m
+  )
+
+getScreenBounds :: WidgetC m => m Dimensions
+getScreenBounds = do
+  screenState <- wsScreenState <$> get
+  let
+    screenLines = ssLines screenState
+    screenColumns = ssColumns screenState
+  pure $ Dimensions screenColumns (MV.length screenLines)
+
+instance MonadIO m => HasRandom (StateT WidgetState m) where
+  getRandom = liftIO randomIO
+
+instance MonadIO m => HasCharScreen (StateT WidgetState m) where
+  csInitialize (Dimensions cols rows) = do
+    -- Initialize the screen memory for the dimensions
+    -- and initialize to whitespaces.
+    (ss, ssBack) <- liftIO $ do
+      ss <- emptyScreenState rows cols
+      ssBack <- emptyScreenState rows cols
+      pure (ss, ssBack)
+    modify (\ws -> ws { wsScreenState = ss, wsScreenStateBack = ssBack })
+
+  csClear = do
+    -- Clears the back buffer before starting to write
+    -- stuff.
+    bb <- wsScreenStateBack <$> get
+    liftIO $ MV.set (ssLines bb) [Plain (T.replicate (ssColumns bb) " ")]
+
+  csDraw = do
+    -- ^ Compares the stuff that has been written to backbuffer
+    -- with the stuff already on frontbuffer, and send the instructions
+    -- to draw the changes. Then switch frontbuffer and backbuffers to
+    -- prepare for the next draw cycle.
+    WidgetState { wsScreenState = (ssLines -> ss), wsScreenStateBack = (ssLines -> ssb) } <- get
+    liftIO $ MV.imapM_ (\idx neLine -> do
+      oldLine <- MV.read ss idx
+      if (oldLine /= neLine)
+        then do
+          A.setCursorPosition idx 0
+          -- mapM_ (\x -> do T.putStr x; S.hFlush stdout; threadDelay 10000;) (stRender <$> neLine)
+          mapM_ T.putStr (stRender <$> neLine)
+          S.hFlush stdout
+        else pure ()
+      ) ssb
+    wsCursorVisible <$> get >>= \case
+      False -> pure ()
+      True ->
+        (wsCursorWidget <$> get) >>= \case
+          Just (SomeKeyInputWidget fref) -> getCursorInfo fref >>= \case
+            Just (cl, csst) -> do
+              liftIO $ A.setCursorPosition (sY cl) (sX cl)
+              putTextFlush $ cursorStyleCode csst
+            Nothing -> pure ()
+          Nothing -> pure ()
+    modify (\ws -> ws { wsScreenStateBack = wsScreenState ws, wsScreenState = wsScreenStateBack ws })
+
+  csPutText t = do
+    -- Write stuff to the backbuffer. If the cursor is in an overflow position, then do nothing.
+    (ScreenState {ssLines = ssLns, ssCursorOverflow = cursorOverflow, ssCursorPos = ScreenPos cx cy}) <- wsScreenStateBack <$> get
+    if cursorOverflow
+      then pure ()
+      else liftIO $ flip (MV.modify ssLns) cy $ \l -> stInsert l cx t
+
+  csSetCursorPosition x y = do
+    -- Sets the cursor position in the backbuffer.
+    modify (\ws ->
+      let
+        screenState = wsScreenStateBack ws
+        screenLines = ssLines screenState
+        screenColumns = ssColumns screenState
+      in if (x >= 0 && x < screenColumns) && (y >= 0 && y < (MV.length screenLines))
+        then ws { wsScreenStateBack = screenState { ssCursorOverflow = False, ssCursorPos = ScreenPos x y }}
+        else ws { wsScreenStateBack = screenState { ssCursorOverflow = True }})
+
+getTerminalSizeIO :: IO (Maybe (Int, Int))
+getTerminalSizeIO = do
+  A.getTerminalSize >>= \case
+    Just (y, x) -> pure $ Just (x, y)
+    Nothing     -> pure Nothing
+
+instance MonadIO m => HasTerminal (StateT WidgetState m) where
+  setCursorPosition x y = do
+    liftIO $ A.setCursorPosition y x
+    hFlush
+  hideCursor = liftIO A.hideCursor
+  showCursor = do
+    liftIO A.showCursor
+    hFlush
+  putText t = liftIO $ do
+    T.putStr t
+  putTextFlush t = do
+    putText t
+    hFlush
+  hFlush = liftIO $ S.hFlush S.stdout
+  hSetEcho h b = liftIO $ S.hSetEcho h b
+  hGetChar = liftIO $ S.hGetChar S.stdin
+  hSetBuffering h b = liftIO $ S.hSetBuffering h b
+  hWaitForInput = liftIO $ S.hWaitForInput stdin 0
+  clearscreen = do
+    liftIO A.clearScreen
+    hFlush
+  clearline = liftIO $ A.hClearFromCursorToLineEnd stdout
+
+instance MonadIO m => HasLog (StateT WidgetState m) where
+  appendLog a = liftIO (appendLog a)
+
+-- Below, the type parameter `a` is left in case we need to use tagged
+-- references, like an IORef.
+newtype WRef (a :: Type) = WRef Int
+  deriving newtype (Eq, Ord)
+
+strToKeyEvent :: String -> [KeyEvent]
+-- gnome-terminal
+strToKeyEvent ('\DEL': rst)  =  (KeyCtrl False False False Backspace):strToKeyEvent rst
+strToKeyEvent ('\ESC': '[' : 'A' : rst)  =  (KeyCtrl False False False ArrowUp) : strToKeyEvent rst
+strToKeyEvent ('\ESC': '[' : 'B' : rst)  =  (KeyCtrl False False False ArrowDown) : strToKeyEvent rst
+strToKeyEvent ('\ESC': '[' : 'H' : rst)  =  (KeyCtrl False False False Home) : strToKeyEvent rst
+strToKeyEvent ('\ESC': '[' : 'F' : rst)  =  (KeyCtrl False False False End) : strToKeyEvent rst
+strToKeyEvent ('\ESC': '[' : 'C' : rst)  =  (KeyCtrl False False False ArrowRight) : strToKeyEvent rst
+strToKeyEvent ('\ESC': '[' : 'D' : rst)  =  (KeyCtrl False False False ArrowLeft) : strToKeyEvent rst
+strToKeyEvent ('\ESC': '[' : '2' : '~': rst)  =  (KeyCtrl False False False Insert) : strToKeyEvent rst
+strToKeyEvent ('\ESC': '[' : '3' : '~': rst)  =  (KeyCtrl False False False Del) : strToKeyEvent rst
+strToKeyEvent ('\ESC': '[' : '1' : '5': '~': rst)  =  (KeyCtrl False False False (Fun 5)) : strToKeyEvent rst
+strToKeyEvent ('\ESC': '[' : '1' : '7': '~': rst)  =  (KeyCtrl False False False (Fun 6)) : strToKeyEvent rst
+strToKeyEvent ('\ESC': '[' : '2' : '1': '~': rst)  =  (KeyCtrl False False False (Fun 10)) : strToKeyEvent rst
+strToKeyEvent ('\ESC': 'O' : 'P': rst)  =  (KeyCtrl False False False (Fun 1)) : strToKeyEvent rst
+strToKeyEvent ('\ESC': 'O' : 'Q': rst)  =  (KeyCtrl False False False (Fun 2)) : strToKeyEvent rst
+strToKeyEvent ('\ESC': 'O' : 'R': rst)  =  (KeyCtrl False False False (Fun 3)) : strToKeyEvent rst
+strToKeyEvent ('\ESC': '[' : '1': '9': ';' : '5' : '~' : rst)  = (KeyCtrl True False False (Fun 8)) : strToKeyEvent rst
+strToKeyEvent ('\ESC': '[' : '1': ';' : '2' : 'C' : rst)  = (KeyCtrl True False False (ArrowRight)) : strToKeyEvent rst
+strToKeyEvent ('\ESC': '[' : '1': ';' : '2' : 'D' : rst)  = (KeyCtrl True False False (ArrowLeft)) : strToKeyEvent rst
+strToKeyEvent ('\ESC': '[' : '1': ';' : '2' : 'B' : rst)  = (KeyCtrl True False False (ArrowDown)) : strToKeyEvent rst
+strToKeyEvent ('\ESC': '[' : '1': ';' : '2' : 'A' : rst)  = (KeyCtrl True False False (ArrowUp)) : strToKeyEvent rst
+
+-- Linux term
+strToKeyEvent ('\ESC': '[' : '[' : 'E': rst)  =  (KeyCtrl False False False (Fun 5)) : strToKeyEvent rst
+--
+strToKeyEvent ('\ESC': c : rst )  =  (KeyChar False False True c) : strToKeyEvent rst
+strToKeyEvent ('\ESC' : rst)  =  (KeyCtrl False False False Esc) : strToKeyEvent rst
+strToKeyEvent ('\n': rst)  =  (KeyCtrl False False False Return) : strToKeyEvent rst
+strToKeyEvent str       =  KeyChar False False False <$> str
+
+initializeVty :: IO VTY.Vty
+initializeVty =
+  VTY.standardIOConfig >>= VTY.mkVty
+
+shutdownVty :: VTY.Vty -> IO ()
+shutdownVty vty = VTY.shutdown vty
+
+readVtyEvent :: VTY.Vty -> IO [TerminalEvent]
+readVtyEvent vty = VTY.nextEvent vty >>= \x -> do
+  case x of
+    VTY.EvResize w h -> pure [TerminalResize w h]
+    VTY.EvKey k mods -> pure $ TerminalKey <$> case k of
+      VTY.KChar c -> [setModifiers mods $ KeyChar False False False c]
+      VTY.KUp -> [setModifiers mods $ KeyCtrl False False False ArrowUp]
+      VTY.KDown -> [setModifiers mods $ KeyCtrl False False False ArrowDown]
+      VTY.KRight -> [setModifiers mods $ KeyCtrl False False False ArrowRight]
+      VTY.KLeft -> [setModifiers mods $ KeyCtrl False False False ArrowLeft]
+      VTY.KEsc -> [setModifiers mods $ KeyCtrl False False False Esc]
+      VTY.KEnter -> [setModifiers mods $ KeyCtrl False False False Return]
+      VTY.KFun 32 -> [setModifiers (VTY.MCtrl : mods) $ KeyCtrl False False False (Fun 8)]
+      VTY.KFun n -> [setModifiers mods $ KeyCtrl False False False (Fun n)]
+      VTY.KBS -> [setModifiers mods $ KeyCtrl False False False Backspace]
+      VTY.KHome -> [setModifiers mods $ KeyCtrl False False False Home]
+      VTY.KEnd -> [setModifiers mods $ KeyCtrl False False False End]
+      VTY.KDel -> [setModifiers mods $ KeyCtrl False False False Del]
+      _ -> []
+    _ -> pure []
+  where
+    setModifiers :: [VTY.Modifier] -> KeyEvent -> KeyEvent
+    setModifiers mods key = DL.foldl' foldFn key mods
+
+    foldFn :: KeyEvent -> VTY.Modifier -> KeyEvent
+    foldFn (KeyCtrl c _ a v) VTY.MShift = KeyCtrl c True a v
+    foldFn (KeyCtrl _ s a v) VTY.MCtrl = KeyCtrl True s a v
+    foldFn (KeyCtrl c s _ v) VTY.MMeta = KeyCtrl c s True v
+    foldFn (KeyCtrl c s _ v) VTY.MAlt = KeyCtrl c s True v
+    foldFn (KeyChar c _ a v) VTY.MShift = KeyChar c True a v
+    foldFn (KeyChar _ s a v) VTY.MCtrl = KeyChar True s a v
+    foldFn (KeyChar c s _ v) VTY.MMeta = KeyChar c s True v
+    foldFn (KeyChar c s _ v) VTY.MAlt = KeyChar c s True v
+
+readKey :: IO [KeyEvent]
+readKey = do
+  k <- readKey_
+  pure $ strToKeyEvent k
+
+readKey_ :: IO String
+readKey_ = do
+  char <- S.hGetChar S.stdin
+  readRest [char]
+  where
+    readRest :: [Char] -> IO [Char]
+    readRest t = S.hWaitForInput stdin 0 >>= \case
+      True -> do
+        c <- S.hGetChar S.stdin
+        readRest (c:t)
+      False -> pure $ Prelude.reverse t
+
+uiLoop :: forall m event. (Show event, WidgetC m) => TChan event -> (event -> m Bool) -> m ()
+uiLoop es cback = do
+  void $ iterateWhile id $ do
+    event <- liftIO $ atomically $ readTChan es
+    r <- cback event
+    pure r
+
+cursorStyleCode :: CursorStyle -> Text
+cursorStyleCode Bar       = T.pack $ "\ESC[5 q" <> A.showCursorCode
+cursorStyleCode Underline = T.pack $ "\ESC[4 q" <> A.showCursorCode
+cursorStyleCode Hidden    = T.pack A.hideCursorCode
+
+readWRef :: forall a m. (WidgetC m, Widget a) => WRef a -> m a
+readWRef (WRef ref) = do
+  (fromMaybe (error "not found") . M.lookup ref . wsWidgets) <$> get >>= \case
+    SomeWidget w -> case cast w of
+      Just a  -> pure a
+      Nothing -> error "Unexpected type"
+
+modifyWRef :: (WidgetC m, Widget a) => WRef a -> (a -> a) -> m ()
+modifyWRef (WRef ref) fn =
+  modify $ \s -> s { wsWidgets = M.update (Just . (modifySomeWidget fn)) ref $ wsWidgets s }
+
+modifyWRefM :: (WidgetC m, Widget a) => WRef a -> (a -> m a) -> m ()
+modifyWRefM (WRef ref) fn = do
+  m <- wsWidgets <$> get
+  case M.lookup ref m of
+    Just sw -> do
+      nSw <- modifySomeWidgetM fn sw
+      modify $ \s -> s { wsWidgets = M.update (\_ -> Just nSw) ref $ wsWidgets s }
+    Nothing -> pure ()
+
+modifySomeWidget :: Widget a => (a -> a) -> SomeWidget -> SomeWidget
+modifySomeWidget fn (SomeWidget w) = case cast w of
+  Just a  -> (SomeWidget (fn a))
+  Nothing -> error "unexpected type"
+
+modifySomeWidgetM :: (Monad m, Widget a) => (a -> m a) -> SomeWidget -> m SomeWidget
+modifySomeWidgetM fn (SomeWidget w) = case cast w of
+  Just a  -> do
+    n <- fn a
+    pure (SomeWidget n)
+  Nothing -> error "unexpected type"
+
+-- Insert the new widget at a random key in the Widget state map
+-- and return the key.
+newWRef :: (WidgetC m, Widget a) => a -> m (WRef a)
+newWRef a = do
+  ref <- getRandom
+  modify $ \s -> s { wsWidgets = M.insert ref (SomeWidget a) $ wsWidgets s }
+  pure (WRef ref)
+
+data CtrlKey
+  = Del
+  | Esc
+  | Insert
+  | End
+  | Home
+  | ArrowLeft
+  | ArrowRight
+  | ArrowUp
+  | ArrowDown
+  | Backspace
+  | Fun Int
+  | Return
+  deriving (Show, Ord, Eq)
+
+data KeyEvent
+  = KeyChar Bool Bool Bool Char
+  | KeyCtrl Bool Bool Bool CtrlKey
+  deriving (Show, Eq, Ord)
+
+data TerminalEvent
+  = TerminalKey KeyEvent
+  | TerminalResize Int Int
+  deriving Show
+
+data TerminalException
+  = TerminalException Text
+  deriving (Show)
+
+instance Exception TerminalException
+
+class HasRandom m where
+  getRandom :: Random a => m a
+
+class HasCursor m where
+  getCursor :: m CursorInfo
+
+class HasCharScreen m where
+  csInitialize :: Dimensions -> m ()
+  csClear :: m ()
+  csDraw :: m ()
+  csPutText :: StyledText -> m ()
+  csSetCursorPosition :: Int -> Int -> m ()
+
+class Layout a where
+  addWidget :: (WidgetC m, Widget child) => WRef a -> Text -> WRef child -> m ()
+  setTextFocus :: WidgetC m => WRef a -> Text -> m ()
+
+class Drawable a where
+  draw :: (WidgetC m) => WRef a -> m ()
+  setVisibility :: WidgetC m => WRef a -> Bool -> m ()
+  getVisibility :: WidgetC m => WRef a -> m Bool
+
+class Moveable a where
+  move ::  WidgetC m => WRef a -> ScreenPos -> m ()
+  getPos :: WidgetC m => WRef a -> m ScreenPos
+  getDim :: WidgetC m => WRef a -> m Dimensions
+  resize :: WidgetC m => WRef a -> (Dimensions -> Dimensions) -> m ()
+
+class Container a c | a -> c where
+  setContent :: WidgetC m => WRef a -> c -> m ()
+  getContent :: WidgetC m => WRef a -> m c
+
+class Selectable a where
+  getSelection :: WidgetC m => WRef a -> m Text
+
+class (Typeable a, Drawable a) => KeyInput a where
+  getCursorInfo :: WidgetC m => WRef a -> m (Maybe CursorInfo)
+  handleInput :: WidgetC m => WRef a -> KeyEvent -> m ()
+
+data WidgetCapability a (c :: Constraint) where
+  KeyInputCap :: WRef a -> WidgetCapability a (KeyInput a)
+  MoveableCap :: WRef a -> WidgetCapability a (Moveable a)
+  SelectableCap :: WRef a -> WidgetCapability a (Selectable a)
+  DrawableCap :: WRef a -> WidgetCapability a (Drawable a)
+  ContainerCap :: Typeable cnt => WRef a -> Proxy cnt -> WidgetCapability a (Container a cnt)
+
+class Typeable a => Widget a where
+  hasCapability :: WidgetCapability a c -> Maybe (Dict c)
+
+withCapability :: forall a c m b . (WidgetC m, Widget a, Typeable c) => WidgetCapability a c -> (c => m b)  -> m b
+withCapability cap fn = case hasCapability cap of
+  Just Dict -> fn
+  Nothing   -> error ("No capability:" <> (show $ typeRep (Proxy @c)))
+
+data SomeWidget where
+  SomeWidget :: forall a. Widget a => a -> SomeWidget
+
+data SomeWidgetRef where
+  SomeWidgetRef :: forall a. (Typeable a, Widget a) => WRef a -> SomeWidgetRef
+
+data SomeKeyInputWidget where
+  SomeKeyInputWidget :: KeyInput a => WRef a -> SomeKeyInputWidget
+
+wSetCursor :: (WidgetC m, HasTerminal m) => ScreenPos -> m ()
+wSetCursor ScreenPos {..} = csSetCursorPosition sX sY
+
+wSetCursorRel :: (HasTerminal m, WidgetC m) => ScreenPos -> ScreenPos -> m ScreenPos
+wSetCursorRel o rel = do
+  let n = moveRight (sX rel) $ moveDown (sY rel) o
+  wSetCursor n
+  pure n
+
+drawBorderBox :: WidgetC m => ScreenPos -> Dimensions -> m ()
+drawBorderBox sp Dimensions {..} = do
+  wSetCursor sp
+  csPutText $ Plain $ C.concat [C.singleton cornerLT, C.replicate (diW - 2) (C.singleton horizontalLine), C.singleton cornerRT]
+  wSetCursor $ moveDown (diH - 1) sp
+  csPutText $ Plain $ C.concat [C.singleton cornerLB, C.replicate (diW - 2) (C.singleton horizontalLine), C.singleton cornerRB]
+  forM_ [1..(diH - 2)] (\r -> do
+    wSetCursor $ moveDown r sp
+    csPutText $ Plain $ C.concat [C.singleton verticalLine]
+    wSetCursor $ moveRight (diW - 1) $ moveDown r sp
+    csPutText $ Plain $ C.concat [C.singleton verticalLine]
+    )
diff --git a/src/UI/Widgets/Editor.hs b/src/UI/Widgets/Editor.hs
new file mode 100644
--- /dev/null
+++ b/src/UI/Widgets/Editor.hs
@@ -0,0 +1,571 @@
+module UI.Widgets.Editor where
+
+import Control.Monad.Loops (iterateUntilM)
+import Data.List as DL hiding (lines)
+import Data.Maybe
+import Data.Proxy (Proxy(..))
+import Data.Text as T
+import Prelude hiding (lines)
+import System.Console.ANSI (Color(..))
+import Text.Printf
+
+import Common
+import Highlighter.Highlighter
+import UI.Widgets.Common
+import UI.Widgets.Editor.Cursor
+
+data EditorMode = InsertMode | ReplaceMode
+  deriving (Eq, Show)
+
+data SomeTokenStream where
+  SomeTokenStream :: (Show a, Highlightable a) => IO [a] -> SomeTokenStream
+
+data EditorWidget = EditorWidget
+  { ewContent            :: Text
+  , ewDim                :: Dimensions
+  , ewVisibility         :: Bool
+  , ewPos                :: ScreenPos
+  , ewCursor             :: Int -- Cursor position offset into content. Starts at 0
+  , ewDebugLocation      :: Maybe Location
+  , ewParseErrorLocation :: Maybe Location
+  , ewWordWrap           :: Bool
+  , eweHasFocus          :: Bool
+  , ewScrollOffset       :: Int
+  , ewInsertMode         :: EditorMode
+  , ewTokenStream        :: Maybe SomeTokenStream
+  , ewAutocompleteWidget :: Maybe SomeWidgetRef
+  , ewAutocompleteSuggestions :: (forall m. MonadIO m => Text -> m [(Text, Text)])
+  , ewReadOnly           :: Bool
+  , ewCursorInfo         :: CursorInfo -- Cursor info (location relative to the begining of text)
+  , ewSelection          :: Maybe (Int, Int)
+  , ewShowVirtualCursor  :: Bool
+  , ewParams             :: EditorParams
+  , ewCursorLine         :: Int -- Line on which the cursor is currently. 1 based
+  }
+
+data EditorParams = EditorParams
+  { epGutterSize         :: Int
+  , epLinenumberWidth    :: Int
+  , epLinenumberRightPad :: Int
+  , epBorderSize         :: Int
+  , epLineNos            :: Bool
+  }
+
+defaultEp :: EditorParams
+defaultEp = EditorParams 1 4 0 1 True
+
+instance Moveable EditorWidget where
+  getPos ref = ewPos <$> readWRef ref
+  move ref pos = modifyWRef ref (\ew -> ew { ewPos = pos })
+  getDim ref = ewDim <$> readWRef ref
+  resize ref cb =
+    modifyWRef ref (\low -> low { ewDim = cb $ ewDim low })
+
+instance Widget EditorWidget where
+  hasCapability (KeyInputCap _) = Just Dict
+  hasCapability (DrawableCap _) = Just Dict
+  hasCapability (MoveableCap _) = Just Dict
+  hasCapability _               = Nothing
+
+getTextPaddingAndSoftLineLength :: Dimensions -> EditorParams -> (Int, Int)
+getTextPaddingAndSoftLineLength d EditorParams {..} = let
+  linenumberWidth' = if epLineNos then epLinenumberWidth else 0
+  linenumberRightPad' = if epLineNos then epLinenumberRightPad else 0
+  textPadding = epBorderSize + epGutterSize + linenumberWidth' + linenumberRightPad'
+  softLineLength = (diW d) - textPadding - (2 * epBorderSize)
+  in (textPadding, softLineLength)
+
+getContentStart :: EditorParams -> ScreenPos
+getContentStart EditorParams {..} = ScreenPos (epBorderSize + epGutterSize) epBorderSize
+
+getTextContentStart :: Dimensions -> EditorParams -> ScreenPos
+getTextContentStart d ep = let
+  (tp, _) = getTextPaddingAndSoftLineLength d ep
+  in addSp (ScreenPos (tp - 1) 0) (getContentStart ep)
+
+instance Drawable EditorWidget where
+  setVisibility ref v = modifyWRefM ref (\b -> do
+    case ewAutocompleteWidget b of
+      Just (SomeWidgetRef acRef) -> withCapability (DrawableCap acRef) $ do
+        setVisibility acRef False
+      _ -> pure ()
+    pure $ b { ewVisibility = v })
+  getVisibility ref = ewVisibility <$> readWRef ref
+  draw ref = do
+    w <- readWRef ref
+    draw' w
+    where
+      draw' w = do
+        if (diH $ ewDim w) > 2 then drawBorderBox (ewPos w) (ewDim w) else pass
+        let (_, softLineLength) = getTextPaddingAndSoftLineLength (ewDim w) params
+        let content = ewContent w
+        let cursorLine = ewCursorLine w
+        let contentHeight = (diH $ ewDim w) - 2
+
+        let mDlocation = ewDebugLocation w
+        let mElocation = ewParseErrorLocation w
+        let mSelection = ewSelection w
+        let mCursorLoc = if (ewShowVirtualCursor w) then Just (ewCursor w) else Nothing
+        let contentStartPos = addSp (getContentStart params) (ewPos w)
+        case ewTokenStream w of
+          Nothing ->
+            void $ iterateUntilM isNothing
+              (printOneLine @Text params mCursorLoc (epLineNos params) cursorLine mSelection mElocation mDlocation (softLineLength, contentHeight) (ewScrollOffset w) contentStartPos) $ Just (content, 1, 0, 0, [])
+          Just (SomeTokenStream (readTokens :: IO [tokenstream])) -> do
+            tokens <- liftIO readTokens
+            void $ iterateUntilM isNothing
+              (printOneLine @tokenstream params mCursorLoc (epLineNos params) cursorLine mSelection mElocation mDlocation (softLineLength, contentHeight) (ewScrollOffset w) contentStartPos) $ Just (content, 1, 0, 0, tokens)
+
+        getCursorInfo ref >>= \case
+          Nothing -> pure ()
+          Just (cl, _) -> do
+            case (ewAutocompleteWidget w) of
+              Just (SomeWidgetRef a) ->
+                withCapability (DrawableCap a) $ do
+                  getVisibility a >>= \case
+                    False -> pure ()
+                    True -> do
+                      withCapability (MoveableCap a) $ do
+                        Dimensions bx by <- getScreenBounds
+                        Dimensions ax ay <- getDim a
+                        let
+                          newAcPos = moveDown 1 cl
+                          acEndY = sY $ moveDown ay newAcPos
+                          acEndX = sX $ moveRight ax newAcPos
+                          rightOf = acEndX - bx
+                          bottomOf = acEndY - by
+
+                          bottomOfFixed = if bottomOf > 0
+                            then moveUp (ay+1) newAcPos
+                            else newAcPos
+
+                          rightOfFixed = if rightOf > 0
+                            then moveLeft rightOf bottomOfFixed
+                            else bottomOfFixed
+                        move a rightOfFixed
+              Nothing -> pure ()
+        where
+          params = ewParams w
+
+printOneLine
+  :: forall a m. (Show a, WidgetC m, Highlightable a)
+  => EditorParams
+  -> Maybe Int
+  -> Bool
+  -> Int
+  -> Maybe (Int, Int)
+  -> Maybe Location
+  -> Maybe Location
+  -> (Int, Int)
+  -> Int
+  -> ScreenPos
+  -> Maybe (Text, Int, Int, Int, [a])
+  -> m (Maybe (Text, Int, Int, Int, [a]))
+printOneLine _ _ _ _ _ _ _ _ _ _ Nothing = pure Nothing
+printOneLine params@(EditorParams {..}) mCursorLoc showLineNos cursorLine selection errorLocation debugLocation (width, height) scrollOffset startPos (Just (cnt, lnNo, realoffset, hoffset, tokenStack)) = do
+   -- Take until next newline
+   let hl = T.takeWhile (/= '\n') cnt
+   let offset = realoffset - scrollOffset
+   case debugLocation of
+     Just dLocation -> do
+       when (lnNo == lcLine dLocation && ((lnNo - scrollOffset) < height) && ((lnNo - scrollOffset) > 0)) $ do
+         wSetCursor $ moveLeft 1 $ moveDown offset startPos
+         csPutText (colorTextFg Green ">")
+     Nothing -> pure ()
+   case errorLocation of
+     Just eLocation -> do
+       when (lnNo == lcLine eLocation && ((lnNo - scrollOffset) < height) && ((lnNo - scrollOffset) > 0)) $ do
+         wSetCursor $ moveLeft 1 $ moveDown offset startPos
+         csPutText (colorText White Red "X")
+     Nothing -> pure ()
+   wSetCursor $ moveDown offset startPos
+   when (showLineNos && isPrintableArea offset height) $ do
+     let formatstr = "%" <> show linenumberWidth' <> "d"
+     if lnNo == cursorLine
+       then csPutText $ colorText White Black $ pack $ printf formatstr lnNo
+       else csPutText $ Plain $ pack $ printf formatstr lnNo
+   let ct = T.chunksOf width hl
+   newTokenStack <- foldM (\stk x -> printOneSoftLine params startPos debugLocation selection errorLocation mCursorLoc width height hoffset offset stk x) tokenStack $ DL.zip [0..] ct
+   let thisLen = (T.length hl)
+   let remaining = T.drop thisLen cnt
+   let nextOffset = hoffset + thisLen
+
+   -- Process the new line in stream as well.
+   case T.uncons remaining of
+     Just ('\n', remaining') -> do
+       let (_, newTokenStack') = pairWithTokens @a newTokenStack nextOffset "\n"
+       pure $ Just (remaining', lnNo + 1, realoffset + (max 1 (DL.length ct)), nextOffset+1, newTokenStack')
+     Just _  -> do
+       pure $ Just (remaining, lnNo + 1, realoffset + (max 1 (DL.length ct)), nextOffset, newTokenStack)
+     Nothing -> pure Nothing
+   where
+
+     linenumberWidth' = if showLineNos then epLinenumberWidth else 0
+
+printOneSoftLine
+ :: forall a m . (Highlightable a, WidgetC m)
+ => EditorParams
+ -> ScreenPos
+ -> Maybe Location
+ -> Maybe (Int, Int)
+ -> Maybe Location
+ -> Maybe Int
+ -> Int
+ -> Int
+ -> Int
+ -> Int -- The screen position y co-ordinate where the main, real line starts
+ -> [a] -- The token stack at this point
+ -> (Int, Text) -- The offset of this soft line, and the content
+ -> m [a]  -- The token stack after printing this soft line
+printOneSoftLine EditorParams {..} startPos debugLocation selection errorLocation mCursorLoc width height hoffset startLine tokenStack' (sfOffset, softLine) = do
+  let startOffset = hoffset + (sfOffset * width)
+  let realoffset' = startLine + sfOffset
+  let (hlText, tokenStack'') = pairWithTokens @a tokenStack' startOffset softLine
+  if (isPrintableArea realoffset' height)
+      then do
+        wSetCursor
+          $ moveRight (gutterSize + linenumberWidth' + linenumberRightPad')
+          $ moveDown realoffset' startPos
+        hlText' <- case debugLocation of
+          Just dLocation -> do
+            pure $ (debugColor dLocation) <$> hlText
+          Nothing -> pure ((\(a, b) -> (Plain a, b)) <$> hlText)
+        hlText'' <- case selection of
+          Just sLocation -> do
+            pure $ (selectionColor startOffset sLocation) <$> hlText'
+          Nothing -> pure hlText'
+        hlText''' <- case errorLocation of
+          Just dLocation -> do
+            pure $ (errorColor dLocation) <$> hlText''
+          Nothing -> pure hlText''
+        hlText'''' <- case mCursorLoc of
+          Just off -> do
+            pure $ (markVirtualCursor startOffset off) <$> hlText'''
+          Nothing -> pure hlText'''
+        csPutText $ StyledText NoStyle ((highlight @a) <$> hlText'''')
+      else
+        pure ()
+  pure tokenStack''
+    where
+      gutterSize = epGutterSize
+      linenumberWidth = epLinenumberWidth
+      linenumberRightPad = epLinenumberRightPad
+      linenumberWidth' = if epLineNos then linenumberWidth else 0
+      linenumberRightPad' = if epLineNos then linenumberRightPad else 0
+
+      debugColor _ (src, Nothing) = (Plain src, Nothing)
+      debugColor debugLoc (src, Just token) = let
+         tokenLoc = getTokenLoc token
+         in if tokenLoc == debugLoc then (StyledText TextUnderline [Plain src], Just token) else (Plain src, Just token)
+
+      selectionColor :: Int -> (Int, Int) -> (StyledText, Maybe a) -> (StyledText, Maybe a)
+      selectionColor startOffset selectionLoc (src, Nothing) =
+        let
+          newSrc = StyledText NoStyle
+            (applyStyleToRange (\st -> StyledText (FgBg Black White) [st]) startOffset selectionLoc [src])
+        in (newSrc, Nothing)
+      selectionColor _ selectionLoc (src, Just token) =
+        let
+          newSrc = StyledText NoStyle (applyStyleToRange
+            (\st -> StyledText (FgBg Black White) [st]) (lcOffset $ getTokenLoc token) selectionLoc [src])
+        in (newSrc, Just token)
+
+      errorColor _ (src, Nothing) = (src, Nothing)
+      errorColor errorLoc (src, Just token) = let
+        tokenLoc = getTokenLoc token
+       in if tokenLoc == errorLoc then (StyledText TextUnderline [src], Just token) else (src, Just token)
+
+      markVirtualCursor :: Int -> Int -> (StyledText, Maybe a) -> (StyledText, Maybe a)
+      markVirtualCursor startOffset cursorLoc (src, Nothing) =
+        let
+          newSrc = StyledText NoStyle
+            (applyStyleToRange (\st -> StyledText (Bg Black) [st]) startOffset (cursorLoc, cursorLoc) [src])
+        in (newSrc, Nothing)
+      markVirtualCursor _ cursorLoc (src, Just token) =
+        let
+          newSrc = StyledText NoStyle (applyStyleToRange
+            (\st -> StyledText (FgBg White Red) [st]) (lcOffset $ getTokenLoc token) (cursorLoc, cursorLoc) [src])
+        in (newSrc, Just token)
+
+isPrintableArea :: Int -> Int -> Bool
+isPrintableArea y height = y >=0 && y < height
+
+instance KeyInput EditorWidget where
+  getCursorInfo ref = do
+    ew <- readWRef ref
+    let tp = getTextContentStart (ewDim ew) (ewParams ew)
+    let (sp, cs) = ewCursorInfo ew
+    pure $ Just (addSp (ewPos ew) (addSp tp (moveUp (ewScrollOffset ew) sp)), cs)
+  handleInput ref ev = do
+    modifyWRefM ref (updateOnInput ev)
+
+instance Container EditorWidget Text where
+  getContent ref = ewContent <$> readWRef ref
+  setContent ref content = modifyWRef ref (\w -> w { ewContent = content })
+
+between :: Int -> (Int, Int) -> Bool
+between a (b, c) = (b <= a) && (a <= c)
+
+insertCharAt :: Char -> Int -> Text -> Text
+insertCharAt c offset t = T.append (T.take offset t) (T.cons c (T.drop offset t))
+
+replaceCharAt :: Char -> Int -> Text -> Text
+replaceCharAt c offset t = T.append (T.take (max 0 (offset - 1)) t) (T.cons c (T.drop offset t))
+
+removeCharAt :: Int -> Text -> Text
+removeCharAt offset t = case T.drop offset t of
+  "" -> t
+  dropped -> T.append (T.take offset t) (T.tail dropped)
+
+-- TODO: Move this logic to autocomplete handler
+getAutoCompleteKey :: (HasLog m, MonadIO m) => Text -> Int -> m Text
+getAutoCompleteKey cnt cursoroffset = do
+  let contentTillCursor = T.take cursoroffset cnt
+  case T.unsnoc contentTillCursor of
+    Just (_, ' ') -> pure ""
+    _             -> pure $ getLastWord "" contentTillCursor
+  where
+    getLastWord :: Text -> Text -> Text
+    getLastWord r c = case T.unsnoc c of
+      Nothing -> r
+      Just (p, lc@((\ch -> T.cons ch "") -> lct)) ->
+        if lct `T.isInfixOf` " \n(.,+-*/:{"
+        then r else getLastWord (T.cons lc r) p
+
+replaceLastWord :: (HasLog m, MonadIO m) => Text -> Int -> Text -> m (Text, Int)
+replaceLastWord content cursor replacement = do
+  lastword <- getAutoCompleteKey content cursor
+  let
+    lastWordLen = T.length lastword
+    replacementLen = T.length replacement
+  let newContent = T.concat [T.take (cursor - lastWordLen) content, replacement, T.drop cursor content]
+  pure (newContent, (replacementLen - lastWordLen))
+
+mkRange :: (Int, Int) -> (Int, Int)
+mkRange (r1, r2) = (min r1 r2, max r1 r2)
+
+data CursorMovement
+  = CVert CursorMovementVertical | CRIGHT Int | CLEFT | CHOME | CEND
+
+data CursorMovementVertical
+  = CUP | CDOWN
+
+adjustScrollOffset :: WidgetC m => WRef EditorWidget -> m ()
+adjustScrollOffset r =
+  modifyWRef r (\ew -> putCursor Nothing 0 ew)
+
+putCursor :: Maybe [Int] -> Int -> EditorWidget -> EditorWidget
+putCursor mll nc ew = let
+  lineLengths = fromMaybe (getLineLengths ew) mll
+  (currentCursorSp, _) = ewCursorInfo ew
+  in case offsetToScreenPos contentWidth lineLengths nc of
+        Just (newsp, cl) -> let
+          newEw = ew { ewCursorLine = cl, ewCursorInfo = (newsp, cStyle), ewCursor = nc }
+          newScrollOffset = if (sY currentCursorSp /= sY newsp)
+            then computeScrollOffset newEw newsp
+            else ewScrollOffset newEw
+          in newEw { ewScrollOffset = newScrollOffset }
+        Nothing -> ew
+  where
+    (_, cStyle) = ewCursorInfo ew
+    (_, contentWidth) = getTextPaddingAndSoftLineLength (ewDim ew) (ewParams ew)
+
+getContetLines :: Text -> [Text]
+getContetLines =  T.split (== '\n')
+
+getLineLengths :: EditorWidget -> [Int]
+getLineLengths ew =
+  let
+    contentLines = getContetLines (ewContent ew)
+    -- Shoulnd't use T.lines here as behavior is different when the last char is a new line.
+  in T.length <$> contentLines
+
+moveCursor :: EditorWidget -> CursorMovement -> EditorWidget
+moveCursor ew cm = case cm of
+  CVert v -> moveCursorVertical ew v
+  CRIGHT o -> newWidget o
+  CLEFT -> newWidget (-1)
+  CHOME -> let
+    newLineOffset =  (sum (DL.take (ewCursorLine ew - 1) lineLengths)) + (ewCursorLine ew - 1)
+    in newWidget (newLineOffset - currentCursor)
+  CEND -> let
+    newLineOffset = case T.findIndex (== '\n') (T.drop currentCursor content) of
+      Just off -> off
+      Nothing  -> T.length content - currentCursor
+    in newWidget newLineOffset
+  where
+    newWidget off = let
+      nc = min (T.length (ewContent ew)) (currentCursor + off)
+      in putCursor (Just lineLengths) nc ew
+    currentCursor = ewCursor ew
+    lineLengths = getLineLengths ew
+    content = ewContent ew
+
+computeScrollOffset :: EditorWidget -> ScreenPos -> Int
+computeScrollOffset ew newSp = let
+  contentHeight = (diH $ ewDim ew) - (2 * (epBorderSize $ ewParams ew))
+  cursorOverflow = (sY newSp - (ewScrollOffset ew)) - (contentHeight - 1)
+  cursorUnderflow = (ewScrollOffset ew) - (sY newSp)
+  in if cursorOverflow > 0
+    then ewScrollOffset ew + cursorOverflow
+    else if cursorUnderflow > 0
+      then ewScrollOffset ew - cursorUnderflow
+      else ewScrollOffset ew
+
+moveCursorVertical :: EditorWidget -> CursorMovementVertical -> EditorWidget
+moveCursorVertical ew cm =
+    case screenPosToOffset lineLengths contentWidth newSp' of
+      Just (newCursor, cursorLine, newSp) -> let
+        newScrollOffset = computeScrollOffset ew newSp
+        in ew { ewCursorLine = cursorLine, ewScrollOffset = newScrollOffset, ewCursorInfo = (newSp, cStyle), ewCursor = newCursor }
+      Nothing -> ew
+
+  where
+    newSp' = case cm  of
+      CUP   -> moveUp 1 currentSp
+      CDOWN -> moveDown 1 currentSp
+    (currentSp, cStyle) = ewCursorInfo ew
+    lineLengths = T.length <$> contentLines
+    contentLines = getContetLines $ ewContent ew
+    (_, contentWidth) = getTextPaddingAndSoftLineLength (ewDim ew) (ewParams ew)
+
+updateOnInput :: forall m. WidgetC m => KeyEvent -> EditorWidget -> m EditorWidget
+updateOnInput ev ew = do
+  if (ewReadOnly ew)
+    then pure ew
+    else do
+      case ev of
+        KeyCtrl _ sh _ ArrowUp -> simpleCursorMovement (CVert CUP) sh
+        KeyCtrl _ sh _ ArrowDown -> simpleCursorMovement (CVert CDOWN) sh
+        KeyCtrl _ sh _ End -> simpleCursorMovement CEND sh
+        KeyCtrl _ sh _ Home -> simpleCursorMovement CHOME sh
+        KeyCtrl _ sh _ ArrowRight -> simpleCursorMovement (CRIGHT 1) sh
+        KeyCtrl _ sh _ ArrowLeft -> simpleCursorMovement CLEFT sh
+        KeyCtrl _ _ _ Del  -> do
+          let
+            existing = ewContent ew
+            cursor = ewCursor ew
+          let newEw = ew { ewContent = removeCharAt cursor existing }
+          checkShowAc newEw
+          pure newEw
+        KeyCtrl _ _ _ Esc  -> do
+          hideAc ew
+          pure ew
+        KeyCtrl _ _ _ Backspace  -> if ewCursor ew > 0
+          then
+            let
+              existing = ewContent ew
+              cursor = ewCursor ew
+            in do
+              let newEw = moveCursor (ew { ewContent = removeCharAt (cursor - 1) existing }) CLEFT
+              checkShowAc newEw
+              pure newEw
+          else pure ew
+        KeyCtrl _ _ _ Return -> do
+          isAutoCompleting ew ev >>= \case
+            False -> updateOnInput (KeyChar False False False '\n') ew
+            True -> do
+              case ewAutocompleteWidget ew of
+                Just (SomeWidgetRef ac) ->
+                  withCapability (SelectableCap ac) $ do
+                    selection <- getSelection ac
+                    (newContent, cursorShift) <- replaceLastWord (ewContent ew) (ewCursor ew) selection
+                    hideAc ew
+                    pure $ moveCursor (ew { ewContent = newContent }) (CRIGHT cursorShift)
+                Nothing -> pure ew
+        KeyChar _ _ _ (convertTab -> c)          -> case ewInsertMode ew of
+          InsertMode -> do
+            let newContent = insertCharAt c (ewCursor ew) (ewContent ew)
+            let newEw = moveCursor (ew { ewContent = newContent}) (CRIGHT 1)
+            checkShowAc newEw
+            pure newEw
+          ReplaceMode ->  pure ew -- disable replace mode now
+        _                  -> pure ew
+    where
+
+      simpleCursorMovement
+        :: CursorMovement
+        -> Bool
+        -> m EditorWidget
+      simpleCursorMovement cm sh = do
+        isAutoCompleting ew ev >>= \case
+          True -> pure ew
+          False -> do
+            let oldCursor = ewCursor ew
+            let newEw = moveCursor ew cm
+            let newCursor = ewCursor newEw
+            let newSelection = if sh
+                  then case ewSelection ew of
+                      Just (ss, _) -> Just $ mkRange (ss, newCursor - 1)
+                      Nothing      -> Just $ mkRange (oldCursor, newCursor - 1)
+                  else Nothing
+            pure $ newEw { ewSelection = newSelection }
+
+      isAutoCompleting ew' key =
+        case ewAutocompleteWidget ew' of
+          Just (SomeWidgetRef ac) ->
+            withCapability (DrawableCap ac) $ do
+              getVisibility ac >>= \case
+                True -> withCapability (KeyInputCap ac) $ do
+                  handleInput ac key
+                  pure True
+                False -> pure False
+          Nothing -> pure False
+      hideAc ew' = do
+        case ewAutocompleteWidget ew' of
+          Just (SomeWidgetRef ac) ->
+            withCapability (DrawableCap ac) $ do
+              setVisibility ac False
+          Nothing -> pure ()
+
+      checkShowAc ew' = do
+        case ewAutocompleteWidget ew' of
+          Just (SomeWidgetRef ac) -> do
+            key <- getAutoCompleteKey (ewContent ew') (ewCursor ew')
+            if T.null key
+              then do
+                withCapability (DrawableCap ac) $ do
+                  setVisibility ac False
+              else do
+              (ewAutocompleteSuggestions ew) key >>= \case
+                [] -> do
+                  withCapability (DrawableCap ac) $ do
+                    setVisibility ac False
+                suggestions@(_:_) -> withCapability (ContainerCap ac (Proxy @[(Text, Text)])) $ do
+                  setContent ac (sortBy (\(a1, _) (a2, _) -> compare (T.length a1) (T.length a2)) suggestions)
+                  withCapability (DrawableCap ac) $ do
+                    setVisibility ac True
+            pure ()
+          Nothing -> pure ()
+
+      convertTab '\t' = ' '
+      convertTab c    = c
+
+editor
+  :: forall m. WidgetC m
+  => (forall m1. MonadIO m1 => Text -> m1 [(Text, Text)])
+  -> Maybe SomeTokenStream
+  -> m (WRef EditorWidget)
+editor mautocomplete mts =
+  newWRef $ EditorWidget
+    { ewContent = ""
+    , ewDim = Dimensions 0 0
+    , ewVisibility = True
+    , ewPos = ScreenPos 0 0
+    , ewCursor = 0
+    , ewDebugLocation = Nothing
+    , ewParseErrorLocation = Nothing
+    , ewWordWrap = True
+    , eweHasFocus = False
+    , ewScrollOffset = 0
+    , ewInsertMode = InsertMode
+    , ewTokenStream = mts
+    , ewAutocompleteWidget = Nothing
+    , ewAutocompleteSuggestions = mautocomplete
+    , ewReadOnly           = False
+    , ewCursorInfo         = (ScreenPos 0 0, Bar)
+    , ewSelection          = Nothing
+    , ewShowVirtualCursor  = False
+    , ewParams             = defaultEp
+    , ewCursorLine         = 1
+    }
diff --git a/src/UI/Widgets/Editor/Cursor.hs b/src/UI/Widgets/Editor/Cursor.hs
new file mode 100644
--- /dev/null
+++ b/src/UI/Widgets/Editor/Cursor.hs
@@ -0,0 +1,70 @@
+module UI.Widgets.Editor.Cursor where
+
+import qualified Data.List as DL
+
+import Common
+
+offsetToScreenPos
+  :: Int  -- ^ Content width, where lines longer are wrapped.
+  -> [Int] -- ^ Line lengths
+  -> Int -- ^ Linear offset into cursor position. This is real offset that starts at 0
+  -> Maybe (ScreenPos, Int) -- Screen pos and actual line in content where cursor is at
+offsetToScreenPos _ [] _ = Just $ (ScreenPos 0 0, 1)
+offsetToScreenPos _ _ 0 = Just $ (ScreenPos 0 0, 1)
+offsetToScreenPos width lns offset =
+    let (sp, _, _, _) = DL.foldl' foldFn (Nothing, 0, 0, 1) lns in sp
+  where
+    foldFn :: (Maybe (ScreenPos, Int), Int, Int, Int) -> Int -> (Maybe (ScreenPos, Int), Int, Int, Int)
+    foldFn a@(Just _, _, _, _) _ = a
+    foldFn (Nothing, leftOffset, lineCount, realLineCount) lineLength =
+        case DL.foldl' foldFn' (Nothing, leftOffset, lineCount) (brokenLines width lineLength) of
+          (Just a, _, _) -> (Just (a, realLineCount), 0, 0, 0)
+          (Nothing, lo, lc) -> (Nothing, lo, lc, realLineCount+1)
+
+    foldFn' :: (Maybe ScreenPos, Int, Int) -> Int -> (Maybe ScreenPos, Int, Int)
+    foldFn' a@(Just _, _, _) _ = a
+    foldFn' (Nothing, leftOffset, lineCount) lineLength =
+      let rightOffset = leftOffset + lineLength - 1
+      in if offset >= leftOffset && offset <= rightOffset
+          then (Just $ ScreenPos (offset - leftOffset) lineCount, 0, 0)
+          else (Nothing, rightOffset+1, lineCount+1)
+
+brokenLines :: Int -> Int -> [Int]
+brokenLines _ 0 = [1]
+brokenLines width ll = let
+  (flc, lll) = divMod ll width
+  in if lll > 0
+    -- We add one char to the very endline to signify the ending newline.
+    -- and to allow positioning of cursor the very last char in a line.
+    then replicate flc width  <> [lll+1]
+    else replicate (flc - 1) width  <> [width + 1]
+
+screenPosToOffset
+  :: [Int] -- ^ Line lengths
+  -> Int -- ^ Content width, where lines longer are wrapped.
+  -> ScreenPos -- ^ Screen position relative to top left edget of content.
+  -> Maybe (Int, Int, ScreenPos) -- Offset starting at zero, cursor line, and adjusted ScreenPos so cursor is at end of line
+screenPosToOffset lns width (ScreenPos x y) =
+    let (sp, _, _, _) = DL.foldl' foldFn (Nothing, 0, 0, 1) lns in sp
+  where
+    foldFn :: (Maybe (Int, Int, ScreenPos), Int, Int, Int) -> Int -> (Maybe (Int, Int, ScreenPos), Int, Int, Int)
+    foldFn a@(Just _, _, _, _) _ = a
+    foldFn (Nothing, lineCount, leftOffset, currentLine) lineLength =
+        case DL.foldl' foldFn' (Nothing, lineCount, leftOffset) (brokenLines width lineLength) of
+          (Nothing, a, b) -> (Nothing, a, b, currentLine + 1)
+          (Just (a, b), _, _) -> (Just (a, currentLine, b), 0, 0, 0)
+
+    foldFn'
+      :: ( Maybe (Int, ScreenPos) -- Result
+         , Int -- current screen line no
+         , Int -- current offset at line start
+         )
+      -> Int -- current line width
+      -> (Maybe (Int, ScreenPos), Int, Int)
+    foldFn' a@(Just _, _, _) _ = a
+    foldFn' (Nothing, lno, offset) lw =
+      if lno == y
+        then let
+          aOffset = min (lw - 1) x
+          in (Just (offset + aOffset, ScreenPos aOffset y), 0, 0)
+        else (Nothing, lno+1, offset+lw)
diff --git a/src/UI/Widgets/Layout.hs b/src/UI/Widgets/Layout.hs
new file mode 100644
--- /dev/null
+++ b/src/UI/Widgets/Layout.hs
@@ -0,0 +1,138 @@
+module UI.Widgets.Layout where
+
+import Data.Coerce
+import Data.Map.Ordered as OMap
+import Data.Maybe
+
+import Common
+import UI.Widgets.Common
+
+data Orientation
+  = Vertical
+  | Horizontal
+
+newtype ContentId = ContentId Text
+  deriving (Show, Ord, Eq)
+
+data LayoutWidget = LayoutWidget
+  { lowDim              :: Dimensions
+  , lowPos              :: ScreenPos
+  , lowContent          :: OMap ContentId SomeWidgetRef
+  , lowOrientation      :: Orientation
+  , lowTextFocus        :: Maybe SomeKeyInputWidget
+  , lowVisibility       :: Bool
+  , lowFloatingContent  :: Maybe SomeWidgetRef
+  , lowDimensionDistribution :: Int -> [Double]
+  }
+
+instance Layout LayoutWidget where
+  addWidget ref (coerce -> contentId) child =
+    modifyWRef ref (\low -> low { lowContent = (lowContent low) |> (contentId, (SomeWidgetRef child)) })
+  setTextFocus ref (coerce -> child) = do
+    w <- readWRef ref
+    case OMap.lookup child (lowContent w) of
+      Nothing -> pure ()
+      Just (SomeWidgetRef ti) ->
+        withCapability (KeyInputCap ti) $
+          modifyWRef ref (\low -> low { lowTextFocus = Just $ SomeKeyInputWidget ti })
+
+instance KeyInput LayoutWidget where
+  getCursorInfo _ = pure Nothing
+  handleInput ref kv = do
+    w <- readWRef ref
+    case lowTextFocus w of
+      Just (SomeKeyInputWidget tr) -> handleInput tr kv
+      Nothing                       -> pure ()
+
+instance Widget LayoutWidget where
+  hasCapability (DrawableCap _)  = Just Dict
+  hasCapability (MoveableCap _)  = Just Dict
+  hasCapability (KeyInputCap _) = Just Dict
+  hasCapability _                = Nothing
+
+instance Moveable LayoutWidget where
+  getPos ref = lowPos <$> readWRef ref
+  move ref pos =
+    modifyWRef ref (\low -> low { lowPos = pos })
+  getDim ref =
+    lowDim <$> readWRef ref
+  resize ref cb =
+    modifyWRef ref (\low -> low { lowDim = cb $ lowDim low })
+
+instance Drawable LayoutWidget where
+  setVisibility ref v = modifyWRef ref (\b -> b { lowVisibility = v })
+  getVisibility ref = lowVisibility <$> readWRef ref
+  draw ref = do
+    w <- readWRef ref
+    let
+      dimension = case lowOrientation w of
+        Vertical   -> (diH $ lowDim w)
+        Horizontal -> (diW $ lowDim w)
+    visibileItems <- catMaybes <$> mapM (\(sw@(SomeWidgetRef cw)) -> case hasCapability (DrawableCap cw) of
+      Just Dict -> do
+        b <- getVisibility cw
+        case b of
+          True -> pure $ Just sw
+          _    -> pure Nothing
+      Nothing -> pure Nothing) (snd <$> (OMap.assocs $ lowContent w))
+
+    let
+      itemCount = Prelude.length visibileItems
+      dimensionDistribution = lowDimensionDistribution w itemCount
+      resizeCallback d = case lowOrientation w of
+        Vertical -> (\dm -> dm { diW = diW $ lowDim w , diH = d})
+        Horizontal -> (\dm -> dm { diH = diH $ lowDim w, diW = d })
+
+    foldM_ (\totalDim (idx, (fraction, (SomeWidgetRef cw))) ->
+      withCapability (MoveableCap cw) $ do
+        let thisDim' =
+              if (idx == itemCount) then (dimension - totalDim) else round $ (realToFrac dimension) * fraction
+        let thisDim =  max thisDim' 3
+        resize cw (resizeCallback thisDim)
+        pure (totalDim + thisDim)
+        ) 0 (Prelude.zip [1..] (Prelude.zip dimensionDistribution visibileItems))
+
+    case (lowOrientation w) of
+      Vertical   -> foldM_ (fny (sX $ lowPos w)) (sY $ lowPos w) visibileItems
+      Horizontal -> foldM_ (fn (sY $ lowPos w)) (sX $ lowPos w) visibileItems
+    case lowFloatingContent w of
+      Just (SomeWidgetRef f) -> do
+        withCapability (DrawableCap f) $ do
+          draw f
+      Nothing -> pure ()
+    where
+      fny x y w = case w of
+        SomeWidgetRef a ->
+          withCapability (MoveableCap a) $
+            withCapability (DrawableCap a) $ do
+              dim <- getDim a
+              move a (ScreenPos { sY = y, sX = x })
+              draw a
+              pure (y + (diH dim))
+      fn y x w = case w of
+        SomeWidgetRef a ->
+          withCapability (MoveableCap a) $
+            withCapability (DrawableCap a) $ do
+              dim <- getDim a
+              move a (ScreenPos { sY = y, sX = x })
+              draw a
+              pure (x + (diW dim))
+
+layoutWidget
+  :: WidgetC m
+  => Orientation
+  -> (Int -> [Double])
+  -> Maybe SomeKeyInputWidget
+  -> m (WRef LayoutWidget)
+layoutWidget ori distr tif = do
+  newWRef $
+    LayoutWidget
+    { lowDim = Dimensions 0 0
+    , lowPos = origin
+    , lowContent = OMap.empty
+    , lowOrientation = ori
+    , lowTextFocus = tif
+    , lowVisibility = True
+    , lowFloatingContent = Nothing
+    , lowDimensionDistribution = distr
+    }
diff --git a/src/UI/Widgets/LogWidget.hs b/src/UI/Widgets/LogWidget.hs
new file mode 100644
--- /dev/null
+++ b/src/UI/Widgets/LogWidget.hs
@@ -0,0 +1,81 @@
+module UI.Widgets.LogWidget where
+
+import Data.Text as T
+import Text.Printf (printf)
+import System.Console.ANSI (Color(..))
+import Common
+import Highlighter.Highlighter
+import UI.Widgets.Common as C
+
+data LogWidget = LogWidget
+  { lwDim     :: Dimensions
+  , lwContent :: [Text]
+  , lwPos     :: ScreenPos
+  , lwVisible :: Bool
+  , lwScrollOffset :: Int
+  }
+
+insertLog :: WidgetC m => WRef LogWidget -> Text -> m ()
+insertLog ref l = modifyWRef ref (\lw ->
+  let
+    h = diH $ lwDim lw
+    ln = Prelude.length (lwContent lw)
+    newContent = (lwContent lw) ++ [l]
+  in if ln > h then lw { lwScrollOffset = 0, lwContent = Prelude.drop (ln - h) newContent } else lw { lwContent = newContent })
+
+instance Moveable LogWidget where
+  getPos ref = lwPos <$> readWRef ref
+  move ref sp =
+    modifyWRef ref (\ww -> ww { lwPos = sp })
+  getDim ref =
+    lwDim <$> readWRef ref
+  resize ref cb =
+    modifyWRef ref (\ww -> ww { lwDim = cb $ lwDim ww })
+
+instance Widget LogWidget where
+  hasCapability (MoveableCap _)  = Just Dict
+  hasCapability (DrawableCap _)  = Just Dict
+  hasCapability _            = Nothing
+
+instance Drawable LogWidget where
+  setVisibility ref v = modifyWRef ref (\b -> b { lwVisible = v })
+  getVisibility ref = lwVisible <$> readWRef ref
+  draw ref = do
+    w <- readWRef ref
+    let height = (diH $ lwDim w) - 3
+    let width = (diW $ lwDim w) - 3
+    let scrollOffset = lwScrollOffset w
+    drawBorderBox (lwPos w) (lwDim w)
+    wSetCursor $ moveDown 1 $ moveRight 1 (lwPos w)
+    let fmt = "%-" <> (show (width + 1)) <> "s"
+    csPutText $ colorText White Black (T.pack $ printf fmt (" Logs" :: Text))
+    let lines' = Prelude.concat $ T.splitOn "\n" <$> lwContent w
+    lst <- foldM (\offset line -> do
+      let chunksize = (diW $ lwDim w) - 2
+      let segments = T.chunksOf chunksize line
+      foldM (\soffset chunk -> do
+        when ((soffset > scrollOffset) && ((soffset - scrollOffset) <= height) ) $ do
+          let cp = moveUp scrollOffset $ moveDown (soffset + 1)  $ moveRight 1 (lwPos w)
+          wSetCursor cp
+          csPutText $ Plain (T.replicate chunksize " ")
+          wSetCursor cp
+          csPutText $ Plain (T.strip chunk)
+        pure (soffset + 1)
+        ) offset segments
+      ) 1 lines'
+    let overflow = (((lst - 1) - scrollOffset) - height)
+    when (overflow > 0) $ do
+      modifyWRef ref (\a -> a { lwScrollOffset = lwScrollOffset a + overflow })
+      draw ref
+
+logWidget
+  :: forall m. WidgetM m (WRef LogWidget)
+logWidget = do
+  newWRef $
+    LogWidget
+    { lwDim = Dimensions 10 10
+    , lwContent = []
+    , lwPos = ScreenPos 0 0
+    , lwVisible = True
+    , lwScrollOffset = 0
+    }
diff --git a/src/UI/Widgets/MenuContainer.hs b/src/UI/Widgets/MenuContainer.hs
new file mode 100644
--- /dev/null
+++ b/src/UI/Widgets/MenuContainer.hs
@@ -0,0 +1,107 @@
+module UI.Widgets.MenuContainer where
+
+import qualified Data.Text as T
+import System.Console.ANSI (Color(..))
+import Text.Printf
+
+import Common
+import Highlighter.Highlighter
+import UI.Widgets.Common as C
+
+type KeyHandler = forall m. WidgetC m => KeyEvent -> WRef MenuContainerWidget -> m Bool
+type SelectionHandler = forall m. WidgetC m => WRef MenuContainerWidget -> (Int, Int) -> m ()
+
+data SubMenu = SubMenu [Text]
+
+data Menu = Menu
+  { menuItems  :: [(Text, SubMenu)]
+  , menuActive :: Maybe (Int, Int) -- Menu items and sub items are indexed from 0
+  }
+
+data MenuContainerWidget = MenuContainerWidget
+  { mcwDim              :: Dimensions
+  , mcwContent          :: SomeWidgetRef
+  , mcwMenu             :: Menu
+  , mcwSelectionHandler :: SelectionHandler
+  , mcwVisibility       :: Bool
+  }
+
+instance Container MenuContainerWidget SomeWidgetRef where
+  setContent ref c =
+    modifyWRef ref (\mcw -> mcw { mcwContent = c })
+  getContent ref = mcwContent <$> readWRef ref
+
+getSubmenuAt :: Menu -> Int -> Maybe SubMenu
+getSubmenuAt (Menu mis _) x =
+  case safeIndex mis x of
+    Just (_, sm) ->  Just sm
+    Nothing      -> Nothing
+
+getMenuItem :: Menu -> (Int, Int) -> Maybe [Text]
+getMenuItem (Menu mis _) (x, y) =
+  case safeIndex mis x of
+    Just (mmName, SubMenu sm) ->  case safeIndex sm y of
+      Just sName -> Just [mmName, sName]
+      Nothing    -> Nothing
+    Nothing      -> Nothing
+
+instance Widget MenuContainerWidget where
+  hasCapability (DrawableCap _)  = Just Dict
+  hasCapability _                = Nothing
+
+instance Drawable MenuContainerWidget where
+  draw :: forall m. WidgetC m => WRef MenuContainerWidget -> m ()
+  setVisibility ref v = modifyWRef ref (\b -> b { mcwVisibility = v })
+  getVisibility ref = mcwVisibility <$> readWRef ref
+  draw ref = do
+    w <- readWRef ref
+    case mcwMenu w of
+      Menu mi _ -> foldM_ (\x (n, _) -> do
+        csSetCursorPosition x 0
+        csPutText (colorText White Black (" "<> n <> " "))
+        pure (x + (T.length n + 2))
+        ) 1 mi
+    case mcwContent w of
+      SomeWidgetRef a -> do
+        withCapability (DrawableCap a) $ do
+          withCapability (MoveableCap a) $ do
+            move a (ScreenPos 0 1)
+            resize a (\_ -> amendHeight (mcwDim w) (\x -> x-2))
+            draw a
+    case menuActive $ mcwMenu w of
+      Just (m, s) -> do
+        case mcwMenu w of
+          Menu mi _ -> foldM_ (\(idx, x) (n, submenu) -> do
+            if idx == m then do
+                case submenu of
+                  SubMenu smi -> do
+                    let wi = max 30 (Prelude.maximum $ T.length <$> ("": smi))
+                    csSetCursorPosition x 0
+                    drawBorderBox (ScreenPos x 1) (Dimensions (wi + 2) (Prelude.length smi + 2))
+                    foldM_ (\idy n' -> do
+                      csSetCursorPosition (x + 1) (idy + 2)
+                      let stx = "%-" <> (show wi) <> "s"
+                      let cnt = " "<> n'
+                      if idy == s
+                        then csPutText (colorText White Black (T.pack $ printf stx cnt))
+                        else csPutText (colorTextFg White (T.pack $ printf stx cnt))
+                      pure (idy+1)) 0 smi
+            else pure ()
+            pure (idx + 1, x + T.length n + 2)
+            ) (0, 1) mi
+      Nothing -> pure ()
+
+menuContainer
+  :: SomeWidgetRef
+  -> Menu
+  -> SelectionHandler
+  -> WidgetM m (WRef MenuContainerWidget)
+menuContainer child menu handler = do
+  newWRef $
+    MenuContainerWidget
+    { mcwDim = Dimensions 0 0
+    , mcwContent = child
+    , mcwMenu = menu
+    , mcwSelectionHandler = handler
+    , mcwVisibility = True
+    }
diff --git a/src/UI/Widgets/NullWidget.hs b/src/UI/Widgets/NullWidget.hs
new file mode 100644
--- /dev/null
+++ b/src/UI/Widgets/NullWidget.hs
@@ -0,0 +1,25 @@
+module UI.Widgets.NullWidget where
+
+import Common
+import UI.Widgets.Common
+
+data NullWidget = NullWidget { nwDimensions :: Dimensions, nwPos :: ScreenPos }
+
+instance Widget NullWidget where
+  hasCapability (DrawableCap _) = Just Dict
+  hasCapability (MoveableCap _) = Just Dict
+  hasCapability _               = Nothing
+
+instance Moveable NullWidget where
+  move _ _ = pure ()
+  getPos r = nwPos <$> readWRef r
+  getDim r = nwDimensions <$> readWRef r
+  resize r fn = modifyWRef r (\w -> w { nwDimensions = fn (nwDimensions w) })
+
+instance Drawable NullWidget where
+  setVisibility _ _ = pure ()
+  getVisibility _ = pure True
+  draw _ = pure ()
+
+nullWidget :: NullWidget
+nullWidget = NullWidget (Dimensions 0 0) (ScreenPos 0 0)
diff --git a/src/UI/Widgets/TextContainer.hs b/src/UI/Widgets/TextContainer.hs
new file mode 100644
--- /dev/null
+++ b/src/UI/Widgets/TextContainer.hs
@@ -0,0 +1,50 @@
+module UI.Widgets.TextContainer where
+
+import UI.Widgets.Common
+import Common
+
+data TextContainerWidget = TextContainerWidget
+  { tcwContent    :: Text
+  , tcwDim        :: Dimensions
+  , tcwPos        :: ScreenPos
+  , tcwVisibility :: Bool
+  }
+
+instance Container TextContainerWidget Text where
+  setContent ref t = do
+    modifyWRef ref (\tcw -> tcw { tcwContent = t })
+  getContent ref =
+    tcwContent <$> readWRef ref
+
+instance Widget TextContainerWidget where
+  hasCapability _ = Nothing
+
+instance Moveable TextContainerWidget where
+  getPos ref = tcwPos <$> readWRef ref
+  move ref pos =
+    modifyWRef ref (\tcw -> tcw { tcwPos = pos })
+  getDim ref = tcwDim <$> readWRef ref
+  resize ref cb =
+    modifyWRef ref (\tcw -> tcw { tcwDim = cb $ tcwDim tcw })
+
+instance Drawable TextContainerWidget where
+  setVisibility ref v = modifyWRef ref (\b -> b { tcwVisibility = v })
+  getVisibility ref = tcwVisibility <$> readWRef ref
+  draw ref = do
+    w <- readWRef ref
+    foldM_ (printOneLine w) 0 (splitOn "\n" (tcwContent w))
+    where
+      printOneLine w ln lineContent = do
+        ll <- foldM printOneSoftLine ln (Prelude.zip (chunksOf ((diW $ tcwDim w) - 2) lineContent) [0..])
+        pure (ll + 1)
+        where
+          printOneSoftLine lns (sLineContent, ln') = do
+            wSetCursor $ moveDown (lns + ln') (tcwPos w)
+            csPutText $ Plain sLineContent
+            pure lns
+
+textContainer
+  :: ScreenPos
+  -> Dimensions
+  -> WidgetM m (WRef TextContainerWidget)
+textContainer sp dim = newWRef $ TextContainerWidget "" dim sp True
diff --git a/src/UI/Widgets/WatchWidget.hs b/src/UI/Widgets/WatchWidget.hs
new file mode 100644
--- /dev/null
+++ b/src/UI/Widgets/WatchWidget.hs
@@ -0,0 +1,59 @@
+module UI.Widgets.WatchWidget where
+
+import Data.Proxy (Proxy)
+import Data.Typeable (eqT, (:~:)(..))
+import Data.Text as T
+
+import Common
+import UI.Widgets.Common as C
+
+data WatchWidget = WatchWidget
+  { wwDim     :: Dimensions
+  , wwContent :: [(Text, Text)]
+  , wwPos     :: ScreenPos
+  , wwVisible :: Bool
+  }
+
+instance Container WatchWidget [(Text, Text)] where
+  setContent ref c =
+    modifyWRef ref (\ww -> ww { wwContent = c })
+  getContent ref = wwContent <$> readWRef ref
+
+instance Moveable WatchWidget where
+  getPos ref = wwPos <$> readWRef ref
+  move ref sp =
+    modifyWRef ref (\ww -> ww { wwPos = sp })
+  getDim ref =
+    wwDim <$> readWRef ref
+  resize ref cb =
+    modifyWRef ref (\ww -> ww { wwDim = cb $ wwDim ww })
+
+instance Widget WatchWidget where
+  hasCapability (MoveableCap _)  = Just Dict
+  hasCapability (DrawableCap _)  = Just Dict
+  hasCapability (ContainerCap _ (_ :: Proxy cnt)) = case eqT @cnt @([(Text, Text)]) of
+    Just Refl -> Just Dict
+    Nothing   -> Nothing
+  hasCapability _            = Nothing
+
+instance Drawable WatchWidget where
+  setVisibility ref v = modifyWRef ref (\b -> b { wwVisible = v })
+  getVisibility ref = wwVisible <$> readWRef ref
+  draw ref = do
+    w <- readWRef ref
+    drawBorderBox (wwPos w) (wwDim w)
+    wSetCursor $ moveDown 1 $ moveRight 1 (wwPos w)
+    flip mapM_ (Prelude.zip [1..] (wwContent w)) $ \(i, (k, v)) -> do
+      wSetCursor $ moveDown i $ moveRight 1 (wwPos w)
+      csPutText $ Plain $ T.take ((diW $ wwDim w) - 2) $ T.intercalate "|" [k, v]
+
+watchWidget
+  :: forall m. WidgetM m (WRef WatchWidget)
+watchWidget = do
+  newWRef $
+    WatchWidget
+    { wwDim = Dimensions 10 10
+    , wwContent = []
+    , wwPos = ScreenPos 0 0
+    , wwVisible = False
+    }
diff --git a/test/Common.hs b/test/Common.hs
new file mode 100644
--- /dev/null
+++ b/test/Common.hs
@@ -0,0 +1,33 @@
+module Common where
+
+import "spade" Common
+import Control.Monad.IO.Class
+import Data.Text as T
+import Parser
+import Test.Common
+import Test.Hspec.Hedgehog
+
+import Compiler.AST.Parser.Common
+
+specGenerateAndParse :: forall a.(Show a, Eq a, HasParser a, HasGen a, ToSource a) => Spec
+specGenerateAndParse = modifyMaxSuccess (const 100) $ describe "Parser for element" $
+  it "can parse generated code" $ hedgehog $ do
+    e <- forAll (getGen @a)
+    r <- liftIO $ runParser parser $ toTextWithOffset (toSource e)
+    footnote $ "Failed source: " <> (T.unpack $ toSource e)
+    footnote $ "Tokens: " <> (show e)
+    r === (Just e)
+
+specGenerateAndParseAst :: forall a.(Show a, Eq a, HasAstParser a, HasGen a, ToSource a) => Spec
+specGenerateAndParseAst = modifyMaxSuccess (const 100) $ describe "Parser for code" $
+  it "can parse generated code <> ast" $ hedgehog $ do
+    e <- forAll (getGen @a)
+    let src = toSource e
+    (r, tokens) <- liftIO $ runParserEither parser (toTextWithOffset src) >>= \case
+      Right tokens -> do
+        r <- runParserEither astParser (mkAstParserState tokens)
+        pure (r, tokens)
+      Left err     -> error $ "Tokinzer failed" <> (show err)
+    footnote $ "Failed source: " <> (T.unpack src)
+    footnote $ "Tokens: " <> (show tokens)
+    (r === (Right e))
diff --git a/test/Compiler/AST/ParserSpec.hs b/test/Compiler/AST/ParserSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Compiler/AST/ParserSpec.hs
@@ -0,0 +1,11 @@
+module Compiler.AST.ParserSpec where
+
+import Common
+import Compiler.AST.Program
+import Test.Hspec
+
+spec :: Spec
+spec = do
+  specGenerateAndParseAst @Expression
+  specGenerateAndParseAst @FunctionDef
+  specGenerateAndParseAst @ProgramStatement
diff --git a/test/Compiler/Lexer/CommentsSpec.hs b/test/Compiler/Lexer/CommentsSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Compiler/Lexer/CommentsSpec.hs
@@ -0,0 +1,18 @@
+module Compiler.Lexer.CommentsSpec where
+
+import Test.Hspec
+
+import "spade" Common
+import Compiler.Lexer.Comments
+import Compiler.Lexer.Tokens
+import Compiler.Lexer.Whitespaces
+import Parser.Lib
+import Parser.Parser
+
+spec :: Spec
+spec = do
+  describe "Comments parsing" $ do
+    it "can parse simple comment" $ runParser parser ("-- comment" :: TextWithOffset) `shouldReturn` (Just $ Comment "comment")
+    it "can parse simple consecutive comments" $ runParser parser ("-- comment\n-- comment 2" :: TextWithOffset) `shouldReturn` (Just $ [Comment "comment", Comment "comment 2"])
+    it "can parse simple consecutive indented comments" $ runParser @[Token] parser ("   -- comment\n   -- comment 2" :: TextWithOffset) `shouldReturn`
+      (Just $ [Token {tkRaw = TkWhitespace (Space 3), tkLocation = emptyLocation, tkOffsetEnd = 3},Token {tkRaw = TkComment (Comment "comment"), tkLocation = emptyLocation, tkOffsetEnd = 14},Token {tkRaw = TkWhitespace (Space 3), tkLocation = emptyLocation, tkOffsetEnd = 17},Token {tkRaw = TkComment (Comment "comment 2"), tkLocation = emptyLocation, tkOffsetEnd = 30}])
diff --git a/test/Compiler/Lexer/DelimeterSpec.hs b/test/Compiler/Lexer/DelimeterSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Compiler/Lexer/DelimeterSpec.hs
@@ -0,0 +1,9 @@
+module Compiler.Lexer.DelimeterSpec where
+
+import Test.Hspec
+
+import Common
+import Compiler.Lexer.Delimeters
+
+spec :: Spec
+spec = specGenerateAndParse @Delimeter
diff --git a/test/Compiler/Lexer/IdentifierSpec.hs b/test/Compiler/Lexer/IdentifierSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Compiler/Lexer/IdentifierSpec.hs
@@ -0,0 +1,9 @@
+module Compiler.Lexer.IdentifierSpec where
+
+import Test.Hspec
+
+import Common
+import Compiler.Lexer.Identifiers
+
+spec :: Spec
+spec = specGenerateAndParse @Identifier
diff --git a/test/Compiler/Lexer/KeywordSpec.hs b/test/Compiler/Lexer/KeywordSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Compiler/Lexer/KeywordSpec.hs
@@ -0,0 +1,9 @@
+module Compiler.Lexer.KeywordSpec where
+
+import Test.Hspec
+
+import Common
+import Compiler.Lexer.Keywords
+
+spec :: Spec
+spec = specGenerateAndParse @Keyword
diff --git a/test/Compiler/Lexer/LiteralSpec.hs b/test/Compiler/Lexer/LiteralSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Compiler/Lexer/LiteralSpec.hs
@@ -0,0 +1,26 @@
+module Compiler.Lexer.LiteralSpec where
+
+import Test.Hspec
+
+import Common
+import Compiler.Lexer.Literals
+import Parser.Lib
+import Parser.Parser
+
+spec :: Spec
+spec = do
+  let
+    unwrapParseError :: Either (ParseErrorWithParsed a) a -> Either ParseError a
+    unwrapParseError (Right a)  = Right a
+    unwrapParseError (Left a)  = Left $ parseError a
+
+  describe "Literal parsing" $ do
+    it "can parse strings" $ runParser parser ("\"test\"" :: TextWithOffset) `shouldReturn` (Just $ LitString "test")
+    it "can parse numbers" $ runParser parser "90" `shouldReturn` (Just $ LitNumber 90)
+    it "reject numbers starting with 0" $ (runParser @Literal) parser "09" `shouldReturn` Nothing
+    it "can parse float: 1" $ runParser parser "90.05" `shouldReturn` (Just $ LitFloat 90.05)
+    it "can parse float: 2" $ runParser parser "0.05" `shouldReturn` (Just $ LitFloat 0.05)
+    it "reject float without leading zero" $ (unwrapParseError <$> runParserEither @TextWithOffset @Literal parser ".05") `shouldReturn` (Left CantHandle)
+    it "can parse bool:true" $ runParser parser "true" `shouldReturn` (Just $ LitBool True)
+    it "can parse bool:false" $ runParser parser "false" `shouldReturn` (Just $ LitBool False)
+  specGenerateAndParse @Literal
diff --git a/test/Compiler/Lexer/OperatorSpec.hs b/test/Compiler/Lexer/OperatorSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Compiler/Lexer/OperatorSpec.hs
@@ -0,0 +1,9 @@
+module Compiler.Lexer.OperatorSpec where
+
+import Test.Hspec
+
+import Common
+import Compiler.Lexer.Operators
+
+spec :: Spec
+spec = specGenerateAndParse @Operator
diff --git a/test/Compiler/Lexer/TokenSpec.hs b/test/Compiler/Lexer/TokenSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Compiler/Lexer/TokenSpec.hs
@@ -0,0 +1,9 @@
+module Compiler.Lexer.TokenSpec where
+
+import Test.Hspec
+
+import Common
+import Compiler.Lexer.Tokens
+
+spec :: Spec
+spec = specGenerateAndParse @[Token]
diff --git a/test/Interpreter/InterpreterSpec.hs b/test/Interpreter/InterpreterSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Interpreter/InterpreterSpec.hs
@@ -0,0 +1,55 @@
+module Interpreter.InterpreterSpec where
+
+import Test.Hspec
+import NeatInterpolation
+
+import Compiler.Parser
+import Interpreter
+import Interpreter.Interpreter
+import Interpreter.Common
+
+
+spec :: Spec
+spec = do
+  describe "While loop" $ do
+    it "loops as expected" $ do
+      let program = [text|
+        let a = 100
+        while a > 10
+          let a = a - 1
+        endwhile
+        |]
+      compile program >>= interpret'' >>= ensureVarIs (SkIdentifier "a") (NumberValue $ NumberInt 10)
+
+    it "breaks as expected" $ do
+      let program = [text|
+        let a = 100
+        while a > 10
+          let a = a - 1
+          if a == 20 then
+            break
+          endif
+        endwhile
+        |]
+      compile program >>= interpret'' >>= ensureVarIs (SkIdentifier "a") (NumberValue $ NumberInt 20)
+
+    it "break only exist one nesting" $ do
+      let program = [text|
+        let a = 100
+        while a > 10
+          let b = 100
+          let a = a - 1
+          while true
+            let b = b - 1
+            if b == 20 then
+              break
+            endif
+          endwhile
+        endwhile
+        |]
+      compile program >>= interpret'' >>= ensureVarIs (SkIdentifier "a") (NumberValue $ NumberInt 10)
+  where
+    interpret'' = interpret_ (error "Input stream not available")
+
+ensureVarIs :: ScopeKey -> Value -> InterpreterState -> Expectation
+ensureVarIs sk v (isGlobalScope -> scope) = (lookupInTopScope sk [scope]) `shouldBe` (Just v)
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
diff --git a/test/UI/EditorSpec.hs b/test/UI/EditorSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/UI/EditorSpec.hs
@@ -0,0 +1,185 @@
+module UI.EditorSpec where
+
+import qualified Data.List as DL
+import qualified Data.Vector.Mutable as MV
+import System.Console.ANSI (Color(..))
+import Test.Hspec
+import Test.Hspec.Hedgehog
+
+import "spade" Common
+import Compiler.Lexer
+import Compiler.Parser (tokenize)
+import qualified Data.Text as T
+import qualified Data.Text.IO as T
+import Hedgehog.Gen (int, list)
+import Hedgehog.Range (linear)
+import UI.Widgets.Common as C
+import UI.Widgets.Editor
+import UI.Widgets.Editor.Cursor
+
+data ReferenceScreenState = ReferenceScreenState
+  { rssLines      :: [[StyledText]]
+  , rssCursorInfo :: CursorInfo
+  }
+
+assertReferenceScreenState :: ReferenceScreenState -> WRef EditorWidget -> WidgetM IO ()
+assertReferenceScreenState ReferenceScreenState {..} ewRef = do
+  EditorWidget {..} <- readWRef ewRef
+  ss <- wsScreenState <$> get
+  let linesRef = (ssLines ss)
+  lines' <- MV.foldr'(:) [] linesRef
+  liftIO $ if lines' == rssLines then do
+        T.putStrLn $ "Got expected screen\n" <> (renderLines rssLines)
+      else expectationFailure $ "Screens differ, expected:\n" <> (T.unpack $ renderLines rssLines) <> "\nBut got\n" <> (T.unpack $ renderLines lines') <> "\n" <> show lines'
+  liftIO $ ewCursorInfo `shouldBe` rssCursorInfo
+
+testEditor :: Text -> [KeyEvent] -> ReferenceScreenState ->  Expectation
+testEditor content kEvents expectedSs = do
+  runWidgetM $ do
+    csInitialize $ Dimensions 18 10
+    ewRef <- editor (\_ -> pure []) Nothing
+    modifyWRef ewRef (\ew -> ew { ewPos = ScreenPos 0 0, ewDim = Dimensions 18 10, ewContent = content })
+    csClear
+    mapM_ (handleInput ewRef) kEvents
+    draw ewRef
+    assertReferenceScreenState expectedSs ewRef
+
+mkKeys :: Int -> CtrlKey -> [KeyEvent]
+mkKeys count' ctrlKey = DL.take count' $ DL.repeat (KeyCtrl False False False ctrlKey)
+
+mkCharKeys :: Int -> Char -> [KeyEvent]
+mkCharKeys count' c = DL.take count' $ DL.repeat (KeyChar False False False c)
+
+spec :: Spec
+spec = do
+  modifyMaxSuccess (const 100) $ describe "Screenpos round tripping" $
+    it "has consitent behavior" $ hedgehog $ do
+      lineWidths <- forAll (list (linear 0 5) (int (linear 0 10)))
+      offset <- forAll (int (linear 0 10))
+      -- liftIO $ putStrLn $ show (lineWidths, offset)
+      case offsetToScreenPos 5 lineWidths offset of
+        Just (sp, _) -> case screenPosToOffset lineWidths 5 sp of
+          Just (offset', _, _) -> do
+            footnote $ "Failed data: " <> (show lineWidths)
+            (offset === offset')
+          Nothing    -> pure ()
+        Nothing      -> pure ()
+
+  describe "Editor Behavior" $ do
+    let expected = [[Plain "\9484\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9488"],[Plain "\9474",Plain " ",StyledText (FgBg White Black) [Plain "   1"],Plain " ",StyledText NoStyle [Plain "abcd"],Plain "      ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9492\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9496"]]
+    it "when content length is less than width and no key pressed" $ do
+      -- Cursor is at the begining of text on load.
+      testEditor "abcd" [] (ReferenceScreenState expected (ScreenPos 0 0, Bar))
+    it "when content length is less than width and left key pressed" $ do
+      -- Cursor remains at the begining, even when left arrow is pressed, because there is no space on left.
+      testEditor "abcd" (mkKeys 1 ArrowLeft) (ReferenceScreenState expected (ScreenPos 0 0, Bar))
+    it "when content length is less than width and right key pressed" $ do
+      -- Cursor moves one place right when right arrow is pressed.
+      testEditor "abcd" (mkKeys 1 ArrowRight) (ReferenceScreenState expected (ScreenPos 1 0, Bar))
+
+    let expected_1 = [[Plain "\9484\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9488"],[Plain "\9474",Plain " ",StyledText (FgBg White Black) [Plain "   1"],Plain " ",StyledText NoStyle [Plain "abcd"],Plain "      ",Plain "\9474"],[Plain "\9474",Plain " ",Plain "   2",Plain " ",StyledText NoStyle [Plain "efgh"],Plain "      ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9492\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9496"]]
+    it "when there is two lines and no key pressed" $ do
+      testEditor "abcd\nefgh" [] (ReferenceScreenState expected_1 (ScreenPos 0 0, Bar))
+    it "when there is two lines and 4 right arrow pressed" $ do
+      -- After four right arrows, Cursor is just beyond the last char in first line.
+      testEditor "abcd\nefgh" (mkKeys 4 ArrowRight) (ReferenceScreenState expected_1 (ScreenPos 4 0, Bar))
+    it "when there is two lines and 5 right arrow pressed" $ do
+      let expectedSecondLineActive = [[Plain "\9484\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9488"],[Plain "\9474",Plain " ",Plain "   1",Plain " ",StyledText NoStyle [Plain "abcd"],Plain "      ",Plain "\9474"],[Plain "\9474",Plain " ",StyledText (FgBg White Black) [Plain "   2"],Plain " ",StyledText NoStyle [Plain "efgh"],Plain "      ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9492\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9496"]]
+      -- After five right arrows, Cursor wraps to the start of the next line.
+      testEditor "abcd\nefgh" (mkKeys 5 ArrowRight) (ReferenceScreenState expectedSecondLineActive (ScreenPos 0 1, Bar))
+
+    let expected_2 = [[Plain "\9484\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9488"],[Plain "\9474",Plain " ",StyledText (FgBg White Black) [Plain "   1"],Plain " ",StyledText NoStyle [Plain "abcd 12345"],Plain "\9474"],[Plain "\9474",Plain "      ",StyledText NoStyle [Plain "6789"],Plain "      ",Plain "\9474"],[Plain "\9474",Plain " ",Plain "   2",Plain " ",StyledText NoStyle [Plain "efgh"],Plain "      ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9492\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9496"]]
+    it "when there is two lines and first line is longer than width and no key pressed" $ do
+      -- The long line wrap into the line below.
+      testEditor "abcd 123456789\nefgh" [] (ReferenceScreenState expected_2 (ScreenPos 0 0, Bar))
+    it "when there is two lines and first line is longer than width and right arrow pressed beyond screen width" $ do
+      -- TODO: Ideally it should move to ScreenPos 7 2, but right now it goes to ScreenPos 8 2.
+      testEditor "abcd 123456789\nefgh" (mkKeys 11 ArrowRight) (ReferenceScreenState expected_2 (ScreenPos 1 1, Bar))
+    it "when there is two lines and first line is longer than width and down arrow pressed" $ do
+      let expected' = [[Plain "\9484\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9488"],[Plain "\9474",Plain " ",StyledText (FgBg White Black) [Plain "   1"],Plain " ",StyledText NoStyle [Plain "abcd 12345"],Plain "\9474"],[Plain "\9474",Plain "      ",StyledText NoStyle [Plain "6789"],Plain "      ",Plain "\9474"],[Plain "\9474",Plain " ",Plain "   2",Plain " ",StyledText NoStyle [Plain "efgh"],Plain "      ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9492\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9496"]]
+      -- The cursor moves into the part of the same line.
+      testEditor "abcd 123456789\nefgh" (mkKeys 1 ArrowDown) (ReferenceScreenState expected' (ScreenPos 0 1, Bar))
+
+    let expected_3 = [[Plain "\9484\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9488"],[Plain "\9474",Plain " ",StyledText (FgBg White Black) [Plain "   1"],Plain " ",StyledText NoStyle [Plain "abcdefghij"],Plain "\9474"],[Plain "\9474",Plain " ",Plain "   2",Plain " ",StyledText NoStyle [Plain "klm"],Plain "       ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9492\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9496"]]
+    it "when one line length is exactly same as width and there is a second line" $ do
+      testEditor "abcdefghij\nklm" [] (ReferenceScreenState expected_3 (ScreenPos 0 0, Bar))
+
+    let expected_4 = [[Plain "\9484\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9488"],[Plain "\9474",Plain " ",StyledText (FgBg White Black) [Plain "   1"],Plain "           ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9492\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9496"]]
+    it "backspace erases last char" $ do
+      -- Cursor is at the begining of text on load.
+      testEditor "" (mkCharKeys 1 'a' <> mkKeys 1 Backspace) (ReferenceScreenState expected_4 (ScreenPos 0 0, Bar))
+
+    let expected_5 = [[Plain "\9484\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9488"],[Plain "\9474",Plain " ",StyledText (FgBg White Black) [Plain "   1"],Plain " ",StyledText NoStyle [Plain "a"],Plain "         ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9492\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9496"]]
+    it "when initial content is empty and a key is pressed" $ do
+      -- Cursor is at the begining of text on load.
+      testEditor "" (mkCharKeys 1 'a') (ReferenceScreenState expected_5 (ScreenPos 1 0, Bar))
+
+    let expected_6 = [[Plain "\9484\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9488"],[Plain "\9474",Plain " ",Plain "   1",Plain " ",StyledText NoStyle [Plain "aaa"],Plain "       ",Plain "\9474"],[Plain "\9474",Plain " ",StyledText (FgBg White Black) [Plain "   2"],Plain "           ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9492\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9496"]]
+    it "when enter key is pressed at the end of a line" $ do
+      -- Cursor is at the begining of text on load.
+      testEditor "" (mkCharKeys 3 'a' <> mkKeys 1 Return) (ReferenceScreenState expected_6 (ScreenPos 0 1, Bar))
+
+    let expected_7 = [[Plain "\9484\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9488"],[Plain "\9474",Plain " ",Plain "   1",Plain " ",StyledText NoStyle [Plain "aaa"],Plain "       ",Plain "\9474"],[Plain "\9474",Plain " ",StyledText (FgBg White Black) [Plain "   2"],Plain "           ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9474",Plain "                ",Plain "\9474"],[Plain "\9492\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9496"]]
+    it "when down arrow key is pressed at the start of a line and the next line is empty" $ do
+      -- Cursor is at the begining of text on load.
+      testEditor "" (mkCharKeys 3 'a' <> mkKeys 1 Return <> mkKeys 1 ArrowUp <> mkKeys 1 ArrowDown) (ReferenceScreenState expected_7 (ScreenPos 0 1, Bar))
+
+    let expected_8 = [[Plain "\9484\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9488"],[Plain "\9474",Plain " ",Plain "   2",Plain "           ",Plain "\9474"],[Plain "\9474",Plain " ",Plain "   3",Plain "           ",Plain "\9474"],[Plain "\9474",Plain " ",Plain "   4",Plain "           ",Plain "\9474"],[Plain "\9474",Plain " ",Plain "   5",Plain "           ",Plain "\9474"],[Plain "\9474",Plain " ",Plain "   6",Plain "           ",Plain "\9474"],[Plain "\9474",Plain " ",Plain "   7",Plain "           ",Plain "\9474"],[Plain "\9474",Plain " ",Plain "   8",Plain "           ",Plain "\9474"],[Plain "\9474",Plain " ",StyledText (FgBg White Black) [Plain "   9"],Plain "           ",Plain "\9474"],[Plain "\9492\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9472\9496"]]
+    it "when enter is pressed and moves cursor beyond screen bottom" $ do
+      -- Cursor is at the begining of text on load.
+      testEditor "" (mkKeys 8 Return) (ReferenceScreenState expected_8 (ScreenPos 0 8, Bar))
+
+  describe "Highlighting" $ do
+    it "test 1" $ do
+      tokenStack <- tokenize "for i = 1 to 10"
+      let
+        unwrap' (Token tr _ _) = tr
+        unwrap :: ([(Text, Maybe Token)], [Token]) -> ([(Text, Maybe TokenRaw)], [TokenRaw])
+        unwrap (lst1, tokenlst) = ((\(a, b) -> (a, unwrap' <$> b)) <$> lst1, unwrap' <$> tokenlst)
+      (unwrap $ pairWithTokens tokenStack 0 "for i = 1 to 10") `shouldBe` ([("for",Just $ TkKeyword KwFor),(" ",Just $ TkWhitespace (Space 1)),("i",Just $ TkIdentifier (Identifier {unIdentifer = "i"})),(" ",Just $ TkWhitespace (Space 1)),("=",Just $ TkKeyword KwAssignment),(" ",Just $ TkWhitespace (Space 1)),("1",Just $ TkLiteral (LitNumber 1)),(" ",Just $ TkWhitespace (Space 1)),("to",Just $ TkKeyword KwTo),(" ",Just $ TkWhitespace (Space 1)),("10",Just $ TkLiteral (LitNumber 10))],[])
+      pure ()
+
+  describe "Token pairing" $ do
+    it "Should pair single token" $ do
+      genericPairWithTokens stOffsetEnd stLoc 0 "Something" [mkSimpleToken "Something" 0] `shouldBe` ([("Something", Just $ mkSimpleToken "Something" 0)], [])
+    it "Should pair single partial start" $ do
+      genericPairWithTokens stOffsetEnd stLoc 0 "Somet" [mkSimpleToken "Something" 0] `shouldBe` ([("Somet", Just $ mkSimpleToken "Something" 0)], [mkSimpleToken "Something" 0])
+    it "Should pair single partial end" $ do
+      genericPairWithTokens stOffsetEnd stLoc 5 "hing" [mkSimpleToken "Something" 0] `shouldBe` ([("hing", Just $ mkSimpleToken "Something" 0)], [])
+    it "Should pair single partial end incomplete" $ do
+      genericPairWithTokens stOffsetEnd stLoc 5 "hin" [mkSimpleToken "Something" 0] `shouldBe` ([("hin", Just $ mkSimpleToken "Something" 0)], [mkSimpleToken "Something" 0])
+
+  describe "Editor cursor positioning" $ do
+    it "test1" $ do
+      (fst <$> offsetToScreenPos 5 [5,5] 9) `shouldBe` (Just $ ScreenPos 3 1)
+    it "test2" $ do
+      (fst <$> offsetToScreenPos 5 [5,3] 5) `shouldBe` (Just $ ScreenPos 5 0)
+    it "test2.1" $ do
+      (fst <$> offsetToScreenPos 5 [5,3] 6) `shouldBe` (Just $ ScreenPos 0 1)
+    it "test3" $ do
+      (fst <$> offsetToScreenPos 5 [5,3] 0) `shouldBe` (Just $ ScreenPos 0 0)
+    it "test5" $ do
+      (fst <$> offsetToScreenPos 5 [10,5] 9) `shouldBe` (Just $ ScreenPos 4 1)
+    it "test5.1" $ do
+      (fst <$> offsetToScreenPos 5 [10,5] 10) `shouldBe` (Just $ ScreenPos 5 1)
+    it "test6" $ do
+      (fst <$> offsetToScreenPos 5 [10,5] 5) `shouldBe` (Just $ ScreenPos 0 1)
+    it "test7" $ do
+      (fst <$> offsetToScreenPos 5 [8,5] 7) `shouldBe` (Just $ ScreenPos 2 1)
+    it "test8" $ do
+      (fst <$> offsetToScreenPos 5 [5,3,5] 7) `shouldBe` (Just $ ScreenPos 1 1)
+    it "test9" $ do
+      (fst <$> offsetToScreenPos 5 [5,3,0,5] 10) `shouldBe` (Just $ ScreenPos 0 2)
+    it "test10" $ do
+      (fst <$> offsetToScreenPos 5 [0,5,3] 1) `shouldBe` (Just $ ScreenPos 0 1)
+
+  describe "Editor reverse cursor positioning" $ do
+    it "test1" $ do
+      (screenPosToOffset [2,5] 5 (ScreenPos 5 0)) `shouldBe` (Just (2, 1, ScreenPos 2 0))
+
+mkSimpleToken :: Text -> Int -> SimpleToken
+mkSimpleToken x offset = NormalToken x (Location 0 0 offset) (offset + T.length x - 1)
+
+data SimpleToken
+  = NormalToken { stContent :: Text, stLoc :: Location, stOffsetEnd :: Int }
+  deriving (Show, Eq)
diff --git a/test/UI/WidgetSpec.hs b/test/UI/WidgetSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/UI/WidgetSpec.hs
@@ -0,0 +1,17 @@
+module UI.WidgetSpec where
+
+import qualified Data.Text as T
+import Test.Hspec
+
+import "spade" Common
+import Test.Hspec.Hedgehog
+import Test.Common
+
+spec :: Spec
+spec =
+  modifyMaxSuccess (const 10) $ describe "StyledText insertion behavior" $
+    it "insertion works as expected" $ hedgehog $ do
+      e <- forAll (list (linear 1 100) (getGen @StyledText))
+      let i = Plain (T.replicate (stTotalLength e - 5) " ")
+      let inserted = stInsert e 0 i
+      (stTotalLength e === stTotalLength inserted)
