//! AbleScript's Abstract Syntax tree //! //! Statements are the type which is AST made of, as they //! express an effect. //! //! Expressions are just operations and they cannot be //! used as statements. Functions in AbleScript are in fact //! just plain subroutines and they do not return any value, //! so their calls are statements. use crate::{base_55::char2num, variables::Value}; use std::{fmt::Debug, hash::Hash}; type Span = std::ops::Range; #[derive(Clone)] pub struct Spanned { pub item: T, pub span: Span, } impl Spanned { pub fn new(item: T, span: Span) -> Self { Self { item, span } } } impl Debug for Spanned { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if f.alternate() { write!(f, "{:#?} @ {:?}", self.item, self.span) } else { write!(f, "{:?} @ {:?}", self.item, self.span) } } } impl PartialEq for Spanned { fn eq(&self, other: &Self) -> bool { self.item == other.item } } impl Hash for Spanned { fn hash(&self, state: &mut H) { self.item.hash(state); } } #[derive(Debug, PartialEq, Clone, Hash)] pub struct Assignable { pub ident: Spanned, pub kind: AssignableKind, } #[derive(Debug, PartialEq, Clone, Hash)] pub enum AssignableKind { Variable, Index { indices: Vec> }, } pub struct InvalidAssignable; impl Assignable { pub fn from_expr(expr: Spanned) -> Result { match expr.item { Expr::Variable(ident) => Ok(Assignable { ident: Spanned::new(ident, expr.span), kind: AssignableKind::Variable, }), Expr::Index { expr, index } => Self::from_index(*expr, *index), _ => Err(InvalidAssignable), } } fn from_index( mut buf: Spanned, index: Spanned, ) -> Result { let mut indices = vec![index]; let ident = loop { match buf.item { Expr::Variable(ident) => break ident, Expr::Index { expr, index } => { indices.push(*index); buf = *expr; } _ => return Err(InvalidAssignable), } }; indices.reverse(); Ok(Assignable { ident: Spanned::new(ident, buf.span), kind: AssignableKind::Index { indices }, }) } } pub type Block = Vec>; /// A syntactic unit expressing an effect. #[derive(Debug, PartialEq, Clone, Hash)] pub enum Stmt { // Control flow Unless { cond: Spanned, body: Block, }, Loop { body: Block, }, Break, HopBack, Dim { ident: Spanned, init: Option>, }, Assign { assignable: Assignable, value: Spanned, }, Functio { ident: Spanned, params: Vec>, body: Block, }, BfFunctio { ident: Spanned, tape_len: Option>, code: Vec, }, Call { expr: Spanned, args: Vec>, }, Print(Spanned), Read(Assignable), Melo(Spanned), Rlyeh, Rickroll, } /// Expression is parse unit which do not cause any effect, /// like math and logical operations or values. #[derive(Debug, PartialEq, Clone, Hash)] pub enum Expr { BinOp { lhs: Box>, rhs: Box>, kind: BinOpKind, }, Aint(Box>), Literal(Literal), Cart(Vec<(Spanned, Spanned)>), Index { expr: Box>, index: Box>, }, Len(Box>), Keys(Box>), Variable(String), } #[derive(Debug, PartialEq, Clone, Hash)] pub enum Literal { Char(char), Int(isize), Str(String), } impl From for Value { fn from(lit: Literal) -> Self { match lit { Literal::Char(c) => Self::Int(char2num(c)), Literal::Int(i) => Self::Int(i), Literal::Str(s) => Self::Str(s), } } } #[derive(Debug, PartialEq, Clone, Hash)] pub enum BinOpKind { Add, Subtract, Multiply, Divide, Greater, Less, Equal, NotEqual, } impl BinOpKind { pub fn from_token(t: crate::lexer::Token) -> Result { use crate::lexer::Token; match t { Token::Plus => Ok(Self::Add), Token::Minus => Ok(Self::Subtract), Token::Star => Ok(Self::Multiply), Token::FwdSlash => Ok(Self::Divide), Token::GreaterThan => Ok(Self::Greater), Token::LessThan => Ok(Self::Less), Token::Equals => Ok(Self::Equal), Token::Aint => Ok(Self::NotEqual), t => Err(crate::error::ErrorKind::UnexpectedToken(t)), } } }