#[cfg(test)]
use strum::IntoEnumIterator;
use crate::{
args::{ArgExprs, Signature},
builtins::Builtins,
fstring::FStringPart,
intern::{BytesId, LongIntId, StringId},
namespace::NamespaceId,
parse::{CodeRange, ParsedSignature, Try},
value::{EitherStr, Marker, Value},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
pub enum NameScope {
#[default]
Local,
Global,
Cell,
CompVar,
}
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub enum CaptureSource {
Namespace(NamespaceId),
CompVar(u16),
}
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub struct Identifier {
pub position: CodeRange,
pub name_id: StringId,
opt_namespace_id: Option<NamespaceId>,
pub scope: NameScope,
}
impl Identifier {
pub fn new(name_id: StringId, position: CodeRange) -> Self {
Self {
name_id,
position,
opt_namespace_id: None,
scope: NameScope::Local,
}
}
pub fn new_with_scope(name_id: StringId, position: CodeRange, namespace_id: NamespaceId, scope: NameScope) -> Self {
Self {
name_id,
position,
opt_namespace_id: Some(namespace_id),
scope,
}
}
pub fn namespace_id(&self) -> NamespaceId {
self.opt_namespace_id
.expect("Identifier not prepared with namespace_id")
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ImportName {
pub module_name: StringId,
pub binding: Identifier,
}
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub enum Callable {
Builtin(Builtins),
Name(Identifier),
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub(crate) enum SequenceItem {
Value(ExprLoc),
Unpack(ExprLoc),
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub(crate) enum DictItem {
Pair(ExprLoc, ExprLoc),
Unpack(ExprLoc),
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum Expr {
Literal(Literal),
Builtin(Builtins),
Name(Identifier),
Call {
callable: Callable,
args: Box<ArgExprs>,
},
AttrCall {
object: Box<ExprLoc>,
attr: EitherStr,
args: Box<ArgExprs>,
},
IndirectCall {
callable: Box<ExprLoc>,
args: Box<ArgExprs>,
},
AttrGet {
object: Box<ExprLoc>,
attr: EitherStr,
},
Op {
left: Box<ExprLoc>,
op: Operator,
right: Box<ExprLoc>,
},
CmpOp {
left: Box<ExprLoc>,
op: CmpOperator,
right: Box<ExprLoc>,
},
ChainCmp {
left: Box<ExprLoc>,
comparisons: Vec<(CmpOperator, ExprLoc)>,
},
List(Vec<SequenceItem>),
Tuple(Vec<SequenceItem>),
Subscript {
object: Box<ExprLoc>,
index: Box<ExprLoc>,
},
Slice {
lower: Option<Box<ExprLoc>>,
upper: Option<Box<ExprLoc>>,
step: Option<Box<ExprLoc>>,
},
Dict(Vec<DictItem>),
Set(Vec<SequenceItem>),
Not(Box<ExprLoc>),
UnaryMinus(Box<ExprLoc>),
UnaryPlus(Box<ExprLoc>),
UnaryInvert(Box<ExprLoc>),
Await(Box<ExprLoc>),
FString(Vec<FStringPart>),
IfElse {
test: Box<ExprLoc>,
body: Box<ExprLoc>,
orelse: Box<ExprLoc>,
},
ListComp {
elt: Box<ExprLoc>,
generators: Vec<Comprehension>,
captured_slots: Vec<u16>,
},
SetComp {
elt: Box<ExprLoc>,
generators: Vec<Comprehension>,
captured_slots: Vec<u16>,
},
DictComp {
key: Box<ExprLoc>,
value: Box<ExprLoc>,
generators: Vec<Comprehension>,
captured_slots: Vec<u16>,
},
LambdaRaw {
name_id: StringId,
signature: ParsedSignature,
body: Box<ExprLoc>,
},
Lambda {
func_def: Box<PreparedFunctionDef>,
},
Named {
target: Identifier,
value: Box<ExprLoc>,
},
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum UnpackTarget {
Name(Identifier),
Tuple {
targets: Vec<Self>,
position: CodeRange,
},
Starred(Identifier),
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum AssignTarget {
Name(Identifier),
Subscript {
target: ExprLoc,
index: ExprLoc,
target_position: CodeRange,
},
Attr {
object: ExprLoc,
attr: EitherStr,
target_position: CodeRange,
},
Unpack {
targets: Vec<UnpackTarget>,
targets_position: CodeRange,
},
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Comprehension {
pub target: UnpackTarget,
pub iter: ExprLoc,
pub ifs: Vec<ExprLoc>,
}
impl Expr {
pub fn is_none(&self) -> bool {
matches!(self, Self::Literal(Literal::None))
}
}
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub enum Literal {
Ellipsis,
None,
Bool(bool),
Int(i64),
Float(f64),
Str(StringId),
Bytes(BytesId),
LongInt(LongIntId),
Marker(Marker),
}
impl From<Literal> for Value {
fn from(literal: Literal) -> Self {
match literal {
Literal::Ellipsis => Self::Ellipsis,
Literal::None => Self::None,
Literal::Bool(b) => Self::Bool(b),
Literal::Int(v) => Self::Int(v),
Literal::Float(v) => Self::Float(v),
Literal::Str(string_id) => Self::InternString(string_id),
Literal::Bytes(bytes_id) => Self::InternBytes(bytes_id),
Literal::LongInt(long_int_id) => Self::InternLongInt(long_int_id),
Literal::Marker(marker) => Self::Marker(marker),
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ExprLoc {
pub position: CodeRange,
pub expr: Expr,
}
impl ExprLoc {
pub fn new(position: CodeRange, expr: Expr) -> Self {
Self { position, expr }
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum Node<F> {
Pass,
Expr(ExprLoc),
Return(Option<ExprLoc>),
Raise(Option<ExprLoc>),
Assert {
test: ExprLoc,
msg: Option<ExprLoc>,
},
Assign {
target: Identifier,
object: ExprLoc,
},
UnpackAssign {
targets: Vec<UnpackTarget>,
targets_position: CodeRange,
object: ExprLoc,
},
OpAssign {
target: Identifier,
op: Operator,
value: ExprLoc,
},
SubscriptOpAssign {
target: ExprLoc,
index: ExprLoc,
op: Operator,
value: ExprLoc,
target_position: CodeRange,
},
SubscriptAssign {
target: ExprLoc,
index: ExprLoc,
value: ExprLoc,
target_position: CodeRange,
},
AttrOpAssign {
object: ExprLoc,
attr: EitherStr,
op: Operator,
value: ExprLoc,
target_position: CodeRange,
},
AttrAssign {
object: ExprLoc,
attr: EitherStr,
target_position: CodeRange,
value: ExprLoc,
},
ChainAssign {
targets: Vec<AssignTarget>,
object: ExprLoc,
},
For {
target: UnpackTarget,
iter: ExprLoc,
body: Vec<Self>,
or_else: Vec<Self>,
},
While {
test: ExprLoc,
body: Vec<Self>,
or_else: Vec<Self>,
},
Break {
position: CodeRange,
},
Continue {
position: CodeRange,
},
If {
test: ExprLoc,
body: Vec<Self>,
or_else: Vec<Self>,
},
FunctionDef {
def: F,
decorators: Vec<ExprLoc>,
},
ClassDef {
name: Identifier,
body: F,
members: Vec<Identifier>,
decorators: Vec<ExprLoc>,
position: CodeRange,
},
Global {
position: CodeRange,
names: Vec<StringId>,
},
Nonlocal {
position: CodeRange,
names: Vec<StringId>,
},
Try(Try<Self>),
With {
context: ExprLoc,
target: Option<UnpackTarget>,
body: Vec<Self>,
position: CodeRange,
},
Import {
names: Vec<ImportName>,
},
ImportFrom {
module_name: StringId,
names: Vec<(StringId, Identifier)>,
position: CodeRange,
},
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PreparedFunctionDef {
pub name: Identifier,
pub signature: Signature,
pub body: Vec<Node<Self>>,
pub namespace_size: usize,
pub free_var_enclosing_slots: Vec<CaptureSource>,
pub free_var_slots: Vec<NamespaceId>,
pub cell_var_slots: Vec<NamespaceId>,
pub cell_param_indices: Vec<Option<usize>>,
pub default_exprs: Vec<ExprLoc>,
pub is_async: bool,
}
pub type PreparedNode = Node<PreparedFunctionDef>;
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum Operator {
Add,
Sub,
Mult,
MatMult,
Div,
Mod,
Pow,
LShift,
RShift,
BitOr,
BitXor,
BitAnd,
FloorDiv,
And,
Or,
}
#[repr(u8)]
#[derive(
Clone,
Copy,
Debug,
PartialEq,
serde::Serialize,
serde::Deserialize,
strum::Display,
strum::EnumIter,
strum::FromRepr,
strum::IntoStaticStr,
)]
pub enum CmpOperator {
#[strum(serialize = "==")]
Eq = 0,
#[strum(serialize = "!=")]
NotEq = 1,
#[strum(serialize = "<")]
Lt = 2,
#[strum(serialize = "<=")]
LtE = 3,
#[strum(serialize = ">")]
Gt = 4,
#[strum(serialize = ">=")]
GtE = 5,
#[strum(serialize = "is")]
Is = 6,
#[strum(serialize = "is not")]
IsNot = 7,
#[strum(serialize = "in")]
In = 8,
#[strum(serialize = "not in")]
NotIn = 9,
}
impl CmpOperator {
pub fn as_str(self) -> &'static str {
self.into()
}
pub const fn as_operand(self) -> u8 {
self as u8
}
}
#[cfg(test)]
pub(crate) fn comparison_operators_fingerprint() -> u64 {
const OFFSET_BASIS: u64 = 0xcbf2_9ce4_8422_2325;
const PRIME: u64 = 0x0100_0000_01b3;
fn update(hash: &mut u64, bytes: &[u8]) {
for byte in u32::try_from(bytes.len())
.expect("fingerprint field length fits u32")
.to_le_bytes()
{
*hash ^= u64::from(byte);
*hash = hash.wrapping_mul(PRIME);
}
for byte in bytes {
*hash ^= u64::from(*byte);
*hash = hash.wrapping_mul(PRIME);
}
}
let mut operators = CmpOperator::iter().collect::<Vec<_>>();
operators.sort_unstable_by_key(|operator| *operator as u8);
let mut hash = OFFSET_BASIS;
for operator in operators {
update(&mut hash, &[operator as u8]);
update(&mut hash, format!("{operator:?}").as_bytes());
update(&mut hash, operator.as_str().as_bytes());
update(
&mut hash,
&postcard::to_allocvec(&operator).expect("CmpOperator serialization cannot fail"),
);
}
hash
}