diff --git a/project.scala b/project.scala
index 7a42a9c..e6fb151 100644
--- a/project.scala
+++ b/project.scala
@@ -3,6 +3,7 @@
 
 // dependencies
 //> using dep com.github.j-mie6::parsley::5.0.0-M10
+//> using dep com.github.j-mie6::parsley-cats::1.3.0
 //> using dep com.lihaoyi::os-lib::0.11.3
 //> using dep com.github.scopt::scopt::4.1.0
 //> using test.dep org.scalatest::scalatest::3.2.19
diff --git a/src/main/wacc/ast.scala b/src/main/wacc/ast.scala
new file mode 100644
index 0000000..c6e743e
--- /dev/null
+++ b/src/main/wacc/ast.scala
@@ -0,0 +1,143 @@
+package wacc
+
+import parsley.generic._
+import cats.data.NonEmptyList
+
+object ast {
+  // Expressions
+  sealed trait Expr extends RValue
+  sealed trait Expr1 extends Expr
+  sealed trait Expr2 extends Expr1
+  sealed trait Expr3 extends Expr2
+  sealed trait Expr4 extends Expr3
+  sealed trait Expr5 extends Expr4
+  sealed trait Expr6 extends Expr5
+
+  // Atoms
+  case class IntLiter(v: Int) extends Expr6
+  object IntLiter extends ParserBridge1[Int, IntLiter]
+  case class BoolLiter(v: Boolean) extends Expr6
+  object BoolLiter extends ParserBridge1[Boolean, BoolLiter]
+  case class CharLiter(v: Char) extends Expr6
+  object CharLiter extends ParserBridge1[Char, CharLiter]
+  case class StrLiter(v: String) extends Expr6
+  object StrLiter extends ParserBridge1[String, StrLiter]
+  case object PairLiter extends Expr6 with ParserBridge0[PairLiter.type]
+  case class Ident(v: String) extends Expr6 with LValue
+  object Ident extends ParserBridge1[String, Ident]
+  case class ArrayElem(name: Ident, indices: NonEmptyList[Expr]) extends Expr6 with LValue
+  object ArrayElem extends ParserBridge2[Ident, NonEmptyList[Expr], ArrayElem]
+  case class Parens(expr: Expr) extends Expr6
+  object Parens extends ParserBridge1[Expr, Parens]
+
+  // Unary operators
+  case class Negate(x: Expr6) extends Expr6
+  object Negate extends ParserBridge1[Expr6, Negate]
+  case class Not(x: Expr6) extends Expr6
+  object Not extends ParserBridge1[Expr6, Not]
+  case class Len(x: Expr6) extends Expr6
+  object Len extends ParserBridge1[Expr6, Len]
+  case class Ord(x: Expr6) extends Expr6
+  object Ord extends ParserBridge1[Expr6, Ord]
+  case class Chr(x: Expr6) extends Expr6
+  object Chr extends ParserBridge1[Expr6, Chr]
+
+  // Binary operators
+  case class Add(x: Expr4, y: Expr5) extends Expr4
+  object Add extends ParserBridge2[Expr4, Expr5, Add]
+  case class Sub(x: Expr4, y: Expr5) extends Expr4
+  object Sub extends ParserBridge2[Expr4, Expr5, Sub]
+  case class Mul(x: Expr5, y: Expr6) extends Expr5
+  object Mul extends ParserBridge2[Expr5, Expr6, Mul]
+  case class Div(x: Expr5, y: Expr6) extends Expr5
+  object Div extends ParserBridge2[Expr5, Expr6, Div]
+  case class Mod(x: Expr5, y: Expr6) extends Expr5
+  object Mod extends ParserBridge2[Expr5, Expr6, Mod]
+  case class Greater(x: Expr4, y: Expr4) extends Expr3
+  object Greater extends ParserBridge2[Expr4, Expr4, Greater]
+  case class GreaterEq(x: Expr4, y: Expr4) extends Expr3
+  object GreaterEq extends ParserBridge2[Expr4, Expr4, GreaterEq]
+  case class Less(x: Expr4, y: Expr4) extends Expr3
+  object Less extends ParserBridge2[Expr4, Expr4, Less]
+  case class LessEq(x: Expr4, y: Expr4) extends Expr3
+  object LessEq extends ParserBridge2[Expr4, Expr4, LessEq]
+  case class Eq(x: Expr3, y: Expr3) extends Expr2
+  object Eq extends ParserBridge2[Expr3, Expr3, Eq]
+  case class Neq(x: Expr3, y: Expr3) extends Expr2
+  object Neq extends ParserBridge2[Expr3, Expr3, Neq]
+  case class And(x: Expr2, y: Expr1) extends Expr1
+  object And extends ParserBridge2[Expr2, Expr1, And]
+  case class Or(x: Expr1, y: Expr) extends Expr
+  object Or extends ParserBridge2[Expr1, Expr, Or]
+
+  // Types
+  sealed trait Type
+  sealed trait BaseType extends Type with PairElemType
+  case object IntType extends BaseType with ParserBridge0[IntType.type]
+  case object BoolType extends BaseType with ParserBridge0[BoolType.type]
+  case object CharType extends BaseType with ParserBridge0[CharType.type]
+  case object StringType extends BaseType with ParserBridge0[StringType.type]
+  case class ArrayType(elemType: Type, dimensions: Int) extends Type with PairElemType
+  object ArrayType extends ParserBridge2[Type, Int, ArrayType]
+  case class PairType(fst: PairElemType, snd: PairElemType) extends Type
+  object PairType extends ParserBridge2[PairElemType, PairElemType, PairType]
+
+  sealed trait PairElemType
+  case object UntypedPairType extends PairElemType with ParserBridge0[UntypedPairType.type]
+
+  // waccadoodledo
+  case class Program(funcs: List[FuncDecl], main: NonEmptyList[Stmt])
+  object Program extends ParserBridge2[List[FuncDecl], NonEmptyList[Stmt], Program]
+
+  // Function Definitions
+  case class FuncDecl(
+      returnType: Type,
+      name: Ident,
+      params: List[Param],
+      body: NonEmptyList[Stmt]
+  )
+  object FuncDecl extends ParserBridge4[Type, Ident, List[Param], NonEmptyList[Stmt], FuncDecl]
+
+  case class Param(paramType: Type, name: Ident)
+  object Param extends ParserBridge2[Type, Ident, Param]
+
+  // Statements
+  sealed trait Stmt
+  case object Skip extends Stmt with ParserBridge0[Skip.type]
+  case class VarDecl(varType: Type, name: Ident, value: RValue) extends Stmt
+  object VarDecl extends ParserBridge3[Type, Ident, RValue, VarDecl]
+  case class Assign(lhs: LValue, value: RValue) extends Stmt
+  object Assign extends ParserBridge2[LValue, RValue, Assign]
+  case class Read(lhs: LValue) extends Stmt
+  object Read extends ParserBridge1[LValue, Read]
+  case class Free(expr: Expr) extends Stmt
+  object Free extends ParserBridge1[Expr, Free]
+  case class Return(expr: Expr) extends Stmt
+  object Return extends ParserBridge1[Expr, Return]
+  case class Exit(expr: Expr) extends Stmt
+  object Exit extends ParserBridge1[Expr, Exit]
+  case class Print(expr: Expr, newline: Boolean) extends Stmt
+  object Print extends ParserBridge2[Expr, Boolean, Print]
+  case class If(cond: Expr, thenStmt: NonEmptyList[Stmt], elseStmt: NonEmptyList[Stmt]) extends Stmt
+  object If extends ParserBridge3[Expr, NonEmptyList[Stmt], NonEmptyList[Stmt], If]
+  case class While(cond: Expr, body: NonEmptyList[Stmt]) extends Stmt
+  object While extends ParserBridge2[Expr, NonEmptyList[Stmt], While]
+  case class Block(stmt: NonEmptyList[Stmt]) extends Stmt
+  object Block extends ParserBridge1[NonEmptyList[Stmt], Block]
+
+  sealed trait LValue
+
+  sealed trait RValue
+  case class ArrayLiter(elems: List[Expr]) extends RValue
+  object ArrayLiter extends ParserBridge1[List[Expr], ArrayLiter]
+  case class NewPair(fst: Expr, snd: Expr) extends RValue
+  object NewPair extends ParserBridge2[Expr, Expr, NewPair]
+  case class Call(name: Ident, args: List[Expr]) extends RValue
+  object Call extends ParserBridge2[Ident, List[Expr], Call]
+
+  sealed trait PairElem extends LValue with RValue
+  case class Fst(elem: LValue) extends PairElem
+  object Fst extends ParserBridge1[LValue, Fst]
+  case class Snd(elem: LValue) extends PairElem
+  object Snd extends ParserBridge1[LValue, Snd]
+}