vexity 0.0.4

Tiny scripting language for hacking on abstractions of financial markets.
Documentation
use std::collections::HashMap;

/// Represents a top level statement in the AST
#[derive(Debug, Clone)]
pub enum Statement {
    /// Declares a variable binding some expression
    ///
    /// # Examples:
    /// ```ignore
    /// let x = 42
    /// let y = 58
    /// let coordinates = { x, y }
    /// ```
    LetExpression { name: String, value: Expr },

    /// A top-level expression
    ///
    /// # Examples:
    /// ```ignore
    /// 1 + 1
    /// |x| x * 2
    /// true
    /// "vex"
    /// ```
    Expression { value: Expr },

    /// Prints a value to stdout
    ///
    /// # Example:
    /// ```ignore
    /// let x = 10
    /// print(x)
    /// ```
    Print { name: String },

    /// Calls a function with a list of arguments
    ///
    /// # Example:
    /// ```ignore
    /// plot_position(x, y)
    /// ```
    Call { function: String, args: Vec<Expr> },

    /// Defines a named function with parameters and a body
    ///
    /// # Example:
    /// ```ignore
    /// fn square(x) {
    ///     x * x
    /// }
    /// ```
    FunctionDef {
        name: String,
        params: Vec<String>,
        body: Vec<Statement>,
    },
}

/// Represents any valid expression in vex
#[derive(Debug, Clone)]
pub enum Expr {
    /// A string literal, e.g., `"hello"`
    String(String),

    /// A boolean literal, either `true` or `false`
    Boolean(bool),

    /// A floating point number, e.g., `3.14`
    Float(f64),

    /// An integer, e.g., `42`
    Integer(i32),

    /// An array of expressions, e.g., `[1, 2, 3]`
    Array(Vec<Expr>),

    /// A hashmap literal, e.g., `{ key: 42 }`
    HashMap(HashMap<String, Expr>),

    /// An identifier or variable name, e.g., `x`
    Identifier(String),

    /// A range expression for generating sequences, e.g, `0..10`
    Range { start: Box<Expr>, end: Box<Expr> },

    /// Access a field of a hashmap or object
    ///
    /// # Example:
    /// ```ignore
    /// user.name
    /// ```
    Field { target: Box<Expr>, name: String },

    /// A binary operation (e.g., `1 + 2`)
    BinaryOp {
        lhs: Box<Expr>,
        op: BinaryOpKind,
        rhs: Box<Expr>,
    },

    /// A lambda expression (anonymous function)
    ///
    /// # Example:
    /// ```ignore
    /// |x| x + 1
    /// ```
    Lambda { param: String, body: Vec<Statement> },

    /// A block of statements grouped as a single expression.
    /// # Example:
    /// ```ignore
    /// {
    ///     let x = 5
    ///     let y = 1
    ///     x + y
    /// }
    /// ```
    Block(Vec<Statement>),

    /// A method call on an object, e.g., `arr.map(|x| x + 1)`
    MethodCall {
        target: Box<Expr>,
        method: String,
        arg: Box<Expr>,
    },

    /// A function call expression, e.g., `sum(x, y)`
    Call { function: String, args: Vec<Expr> },

    /// A index on an array, e.g., `some_arr[0]`
    Index { target: Box<Expr>, index: Box<Expr> },
}

/// Supported binary operators
#[derive(Debug, Clone)]
pub enum BinaryOpKind {
    /// Addition
    Add,

    /// Subtraction
    Sub,

    /// Multiplication
    Mul,

    /// Division
    Div,
}
impl TryFrom<&str> for BinaryOpKind {
    type Error = String;

    fn try_from(op: &str) -> Result<Self, Self::Error> {
        match op {
            "+" => Ok(BinaryOpKind::Add),
            "-" => Ok(BinaryOpKind::Sub),
            "*" => Ok(BinaryOpKind::Mul),
            "/" => Ok(BinaryOpKind::Div),
            other => Err(format!("Unknown binary operator '{other}'")),
        }
    }
}
impl std::fmt::Display for BinaryOpKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let symbol = match self {
            BinaryOpKind::Add => "+",
            BinaryOpKind::Sub => "-",
            BinaryOpKind::Mul => "*",
            BinaryOpKind::Div => "/",
        };
        write!(f, "{symbol}")
    }
}