use crate::token; pub type Recipe<'a> = Vec>; #[derive(Debug, Clone, PartialEq, Eq)] pub enum RecipeStmt<'a> { BodyStmt(BodyStmt<'a>), Fetch { name: Ident<'a>, body: Body<'a>, }, Task { name: Ident<'a>, params: Vec>, body: Body<'a>, }, } pub type Body<'a> = Vec>; #[derive(Debug, Clone, PartialEq, Eq)] pub enum BodyStmt<'a> { Assign { left: Box>, op: Option, right: Box>, }, } impl<'a> BodyStmt<'a> { pub(crate) fn assign(left: TypedExpr<'a>, op: Option, right: Expr<'a>) -> Self { BodyStmt::Assign { left: Box::new(left), op, right: Box::new(right), } } } #[derive(Debug, Clone, PartialEq, Eq)] pub enum Expr<'a> { Binary { left: Box>, op: OpBinary, right: Box>, }, Unary { op: OpUnary, expr: Box>, }, Apply { expr: Box>, params: Vec>, }, Method { expr: Box>, method: Ident<'a>, params: Vec>, }, Index { expr: Box>, index: Box>, }, Field { expr: Box>, field: Ident<'a>, }, Paren(Box>), Path(Path<'a>), Literal(Literal<'a>), } impl<'a> Expr<'a> { pub(crate) fn binary(left: Expr<'a>, op: OpBinary, right: Expr<'a>) -> Self { Expr::Binary { left: Box::new(left), op, right: Box::new(right), } } pub(crate) fn unary(op: OpUnary, expr: Expr<'a>) -> Self { Expr::Unary { op, expr: Box::new(expr), } } pub(crate) fn apply(expr: Expr<'a>, params: Vec>) -> Self { Expr::Apply { expr: Box::new(expr), params, } } pub(crate) fn method(expr: Expr<'a>, method: Ident<'a>, params: Vec>) -> Self { Expr::Method { expr: Box::new(expr), method, params, } } pub(crate) fn index(expr: Expr<'a>, index: Expr<'a>) -> Self { Expr::Index { expr: Box::new(expr), index: Box::new(index), } } pub(crate) fn field(expr: Expr<'a>, field: Ident<'a>) -> Self { Expr::Field { expr: Box::new(expr), field, } } pub(crate) fn paren(expr: Expr<'a>) -> Self { Expr::Paren(Box::new(expr)) } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct TypedExpr<'a> { pub expr: Expr<'a>, pub typ: Option>, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct FuncParam<'a> { pub name: Ident<'a>, pub typ: Expr<'a>, } #[derive(Debug, Clone, PartialEq, Eq)] pub enum Literal<'a> { Unit, Boolean(bool), Integer(u64), String(Vec>), Tuple(Vec>), Array(Vec>), Map(Vec>), } impl<'a> Literal<'a> { pub(crate) fn number(s: &'a str) -> Result { let (radix, rest) = if let Some(rest) = s.strip_prefix("0x") { (16, rest) } else if let Some(rest) = s.strip_prefix("0o") { (8, rest) } else if let Some(rest) = s.strip_prefix("0b") { (2, rest) } else { (10, s) }; let digits = rest.replace('_', ""); let value = u64::from_str_radix(&digits, radix).or(Err("number"))?; Ok(Literal::Integer(value)) } } #[derive(Clone, Debug, PartialEq, Eq)] pub enum StringPiece<'a> { Chars(&'a str), Escape(char), Interp(Expr<'a>), } impl<'a> TryFrom<&token::StringPiece<'a>> for StringPiece<'a> { type Error = &'static str; fn try_from(value: &token::StringPiece<'a>) -> Result { use crate::recipe; Ok(match value { token::StringPiece::Chars(chars) => StringPiece::Chars(chars), token::StringPiece::Escape(c) => StringPiece::Escape(*c), token::StringPiece::Interp(tokens) => StringPiece::Interp( recipe::expr(tokens).or(Err("Invalid expression in string interpolation"))?, ), }) } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct MapEntry<'a> { pub key: &'a str, pub value: Expr<'a>, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum OpUnary { Not, Neg, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum OpBinary { Add, Sub, Mul, Div, Rem, And, Or, Eq, Lt, Le, Ne, Ge, Gt, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct Path<'a> { pub components: Vec>, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Ident<'a> { pub name: &'a str, }