use crate::{StringFormatOptions, StringQuote, ast::AstIndex, constant_pool::ConstantIndex};
use smallvec::SmallVec;
use std::fmt;
pub type AstVec<T> = SmallVec<[T; 4]>;
pub use smallvec::smallvec as astvec;
#[derive(Clone, Debug, Default, PartialEq, Eq, derive_name::VariantName)]
pub enum Node {
#[default]
Null,
Nested(AstIndex),
Id(ConstantIndex, Option<AstIndex>),
Meta(MetaKeyId, Option<ConstantIndex>),
Chain((ChainNode, Option<AstIndex>)),
BoolTrue,
BoolFalse,
SmallInt(i16),
Int(ConstantIndex),
Float(ConstantIndex),
Str(AstString),
List(AstVec<AstIndex>),
Tuple {
elements: AstVec<AstIndex>,
parentheses: bool,
},
TempTuple(AstVec<AstIndex>),
Range {
start: AstIndex,
end: AstIndex,
inclusive: bool,
},
RangeFrom {
start: AstIndex,
},
RangeTo {
end: AstIndex,
inclusive: bool,
},
RangeFull,
Map {
entries: AstVec<AstIndex>,
braces: bool,
},
MapEntry(AstIndex, AstIndex),
Self_,
MainBlock {
body: AstVec<AstIndex>,
local_count: usize,
},
Block(AstVec<AstIndex>),
Function(Function),
FunctionArgs {
args: AstVec<AstIndex>,
variadic: bool,
output_type: Option<AstIndex>,
},
Import {
from: AstVec<AstIndex>,
items: Vec<ImportItem>,
},
Export(AstIndex),
Assign {
target: AstIndex,
expression: AstIndex,
let_assignment: bool,
},
MultiAssign {
targets: AstVec<AstIndex>,
expression: AstIndex,
let_assignment: bool,
},
UnaryOp {
op: AstUnaryOp,
value: AstIndex,
},
BinaryOp {
op: AstBinaryOp,
lhs: AstIndex,
rhs: AstIndex,
},
If(AstIf),
Match {
expression: AstIndex,
arms: AstVec<AstIndex>,
},
MatchArm {
patterns: AstVec<AstIndex>,
condition: Option<AstIndex>,
expression: AstIndex,
},
Switch(AstVec<AstIndex>),
SwitchArm {
condition: Option<AstIndex>,
expression: AstIndex,
},
Ignored(Option<ConstantIndex>, Option<AstIndex>),
PackedId(Option<ConstantIndex>),
PackedExpression(AstIndex),
For(AstFor),
Loop {
body: AstIndex,
},
While {
condition: AstIndex,
body: AstIndex,
},
Until {
condition: AstIndex,
body: AstIndex,
},
Break(Option<AstIndex>),
Continue,
Return(Option<AstIndex>),
Try(AstTry),
Throw(AstIndex),
Yield(AstIndex),
Debug {
expression_string: ConstantIndex,
expression: AstIndex,
},
Type {
type_index: ConstantIndex,
allow_null: bool,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Function {
pub args: AstIndex,
pub local_count: usize,
pub accessed_non_locals: AstVec<ConstantIndex>,
pub body: AstIndex,
pub is_generator: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AstString {
pub quote: StringQuote,
pub contents: StringContents,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum StringContents {
Literal(ConstantIndex),
Raw {
constant: ConstantIndex,
hash_count: u8,
},
Interpolated(Vec<StringNode>),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum StringNode {
Literal(ConstantIndex),
Expression {
expression: AstIndex,
format: StringFormatOptions,
},
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AstFor {
pub args: AstVec<AstIndex>,
pub iterable: AstIndex,
pub body: AstIndex,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AstIf {
pub condition: AstIndex,
pub then_node: AstIndex,
pub else_if_blocks: AstVec<(AstIndex, AstIndex)>,
pub else_node: Option<AstIndex>,
pub inline: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum AstUnaryOp {
Negate,
Not,
}
impl AstUnaryOp {
pub fn as_str(&self) -> &'static str {
match self {
AstUnaryOp::Negate => "-",
AstUnaryOp::Not => "not",
}
}
}
impl fmt::Display for AstUnaryOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum AstBinaryOp {
Add,
Subtract,
Multiply,
Divide,
Remainder,
Power,
AddAssign,
SubtractAssign,
MultiplyAssign,
DivideAssign,
RemainderAssign,
PowerAssign,
Equal,
NotEqual,
Less,
LessOrEqual,
Greater,
GreaterOrEqual,
And,
Or,
Pipe,
}
impl AstBinaryOp {
pub fn as_str(&self) -> &'static str {
match self {
AstBinaryOp::Add => "+",
AstBinaryOp::Subtract => "-",
AstBinaryOp::Multiply => "*",
AstBinaryOp::Divide => "/",
AstBinaryOp::Remainder => "%",
AstBinaryOp::Power => "^",
AstBinaryOp::AddAssign => "+=",
AstBinaryOp::SubtractAssign => "-=",
AstBinaryOp::MultiplyAssign => "*=",
AstBinaryOp::DivideAssign => "/=",
AstBinaryOp::RemainderAssign => "%=",
AstBinaryOp::PowerAssign => "^=",
AstBinaryOp::Equal => "==",
AstBinaryOp::NotEqual => "!=",
AstBinaryOp::Less => "<",
AstBinaryOp::LessOrEqual => "<=",
AstBinaryOp::Greater => ">",
AstBinaryOp::GreaterOrEqual => ">=",
AstBinaryOp::And => "and",
AstBinaryOp::Or => "or",
AstBinaryOp::Pipe => "->",
}
}
}
impl fmt::Display for AstBinaryOp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AstTry {
pub try_block: AstIndex,
pub catch_blocks: AstVec<AstCatch>,
pub finally_block: Option<AstIndex>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AstCatch {
pub arg: AstIndex,
pub block: AstIndex,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ChainNode {
Root(AstIndex),
Id(ConstantIndex),
Str(AstString),
Index(AstIndex),
Call {
args: AstVec<AstIndex>,
with_parens: bool,
},
NullCheck,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum MetaKeyId {
Add,
Subtract,
Multiply,
Divide,
Remainder,
Power,
AddRhs,
SubtractRhs,
MultiplyRhs,
DivideRhs,
RemainderRhs,
PowerRhs,
AddAssign,
SubtractAssign,
MultiplyAssign,
DivideAssign,
RemainderAssign,
PowerAssign,
Less,
LessOrEqual,
Greater,
GreaterOrEqual,
Equal,
NotEqual,
Index,
IndexMut,
Debug,
Display,
Iterator,
Next,
NextBack,
Negate,
Size,
Type,
Base,
Call,
Test,
PreTest,
PostTest,
Main,
Named,
Invalid,
}
impl MetaKeyId {
pub fn as_str(&self) -> &'static str {
use MetaKeyId::*;
match self {
Add => "@+",
Subtract => "@-",
Multiply => "@*",
Divide => "@/",
Remainder => "@%",
Power => "@^",
AddRhs => "@r+",
SubtractRhs => "@r-",
MultiplyRhs => "@r*",
DivideRhs => "@r/",
RemainderRhs => "@r%",
PowerRhs => "@r^",
AddAssign => "@+=",
SubtractAssign => "@-=",
MultiplyAssign => "@*=",
DivideAssign => "@/=",
RemainderAssign => "@%=",
PowerAssign => "@^=",
Less => "@<",
LessOrEqual => "@<=",
Greater => "@>",
GreaterOrEqual => "@>=",
Equal => "@==",
NotEqual => "@!=",
Index => "@index",
IndexMut => "@index_mut",
Debug => "@debug",
Display => "@display",
Iterator => "@iterator",
Next => "@next",
NextBack => "@next_back",
Negate => "@negate",
Size => "@size",
Type => "@type",
Base => "@base",
Call => "@call",
Test => "@test",
PreTest => "@pre_test",
PostTest => "@post_test",
Main => "@main",
Named => "@meta",
Invalid => unreachable!(),
}
}
}
impl TryFrom<u8> for MetaKeyId {
type Error = u8;
fn try_from(byte: u8) -> Result<Self, Self::Error> {
if byte < Self::Invalid as u8 {
Ok(unsafe { std::mem::transmute::<u8, Self>(byte) })
} else {
Err(byte)
}
}
}
impl fmt::Display for MetaKeyId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ImportItem {
pub item: AstIndex,
pub name: Option<AstIndex>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn node_size() {
let size = std::mem::size_of::<Node>();
let maximum_size = 72;
assert!(
size <= maximum_size,
"Node has a size of {size} bytes, the allowed maximum is {maximum_size} bytes"
);
}
}