///|
pub enum UnaryOp {
  Not
  Negate
} derive(Debug)

///|
pub enum BinaryOp {
  Or
  And
  Equal
  NotEqual
  Less
  LessEqual
  Greater
  GreaterEqual
  In
  Add
  Subtract
  Multiply
  Divide
  Remainder
} derive(Debug)

///|
pub enum Expr {
  Literal(Json, Span)
  Variable(String, Span)
  ArrayLiteral(Array[Expr], Span)
  Member(Expr, String, Span)
  Index(Expr, Expr, Span)
  OptionalMember(Expr, String, Span)
  OptionalIndex(Expr, Expr, Span)
  Call(String, Array[Expr], Span)
  Unary(UnaryOp, Expr, Span)
  Binary(Expr, BinaryOp, Expr, Span)
} derive(Debug)

///|
fn Expr::span(self : Expr) -> Span {
  match self {
    Literal(_, span)
    | Variable(_, span)
    | ArrayLiteral(_, span)
    | Member(_, _, span)
    | Index(_, _, span)
    | OptionalMember(_, _, span)
    | OptionalIndex(_, _, span)
    | Call(_, _, span)
    | Unary(_, _, span)
    | Binary(_, _, _, span) => span
  }
}

///|
/// A compiled expression. Its representation is intentionally opaque so the
/// parser can evolve without breaking consumers.
pub struct Program {
  root : Expr
  source : String
  node_count : Int
  ast_depth : Int
}

///|
pub fn Program::source(self : Program) -> String {
  self.source
}

///|
pub fn Program::span(self : Program) -> Span {
  self.root.span()
}

///|
/// Number of nodes in the compiled abstract syntax tree.
pub fn Program::node_count(self : Program) -> Int {
  self.node_count
}

///|
/// Maximum nesting depth of the compiled abstract syntax tree.
pub fn Program::ast_depth(self : Program) -> Int {
  self.ast_depth
}