use std::{
fmt::{self, Debug, Display},
ops::Deref,
rc::Rc,
};
use jrsonnet_gcmodule::Trace;
use jrsonnet_interner::IStr;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "structdump")]
use structdump::Codegen;
use crate::source::Source;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[derive(Debug, PartialEq, Trace)]
pub enum FieldName {
Fixed(IStr),
Dyn(LocExpr),
}
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]
pub enum Visibility {
Normal,
Hidden,
Unhide,
}
impl Visibility {
pub fn is_visible(&self) -> bool {
matches!(self, Self::Normal | Self::Unhide)
}
}
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Trace)]
pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub struct FieldMember {
pub name: FieldName,
pub plus: bool,
pub params: Option<ParamsDesc>,
pub visibility: Visibility,
pub value: LocExpr,
}
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub enum Member {
Field(FieldMember),
BindStmt(BindSpec),
AssertStmt(AssertStmt),
}
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]
pub enum UnaryOpType {
Plus,
Minus,
BitNot,
Not,
}
impl Display for UnaryOpType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use UnaryOpType::*;
write!(
f,
"{}",
match self {
Plus => "+",
Minus => "-",
BitNot => "~",
Not => "!",
}
)
}
}
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Trace)]
pub enum BinaryOpType {
Mul,
Div,
Mod,
Add,
Sub,
Lhs,
Rhs,
Lt,
Gt,
Lte,
Gte,
BitAnd,
BitOr,
BitXor,
Eq,
Neq,
And,
Or,
#[cfg(feature = "exp-null-coaelse")]
NullCoaelse,
In,
}
impl Display for BinaryOpType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use BinaryOpType::*;
write!(
f,
"{}",
match self {
Mul => "*",
Div => "/",
Mod => "%",
Add => "+",
Sub => "-",
Lhs => "<<",
Rhs => ">>",
Lt => "<",
Gt => ">",
Lte => "<=",
Gte => ">=",
BitAnd => "&",
BitOr => "|",
BitXor => "^",
Eq => "==",
Neq => "!=",
And => "&&",
Or => "||",
In => "in",
#[cfg(feature = "exp-null-coaelse")]
NullCoaelse => "??",
}
)
}
}
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub struct Param(pub Destruct, pub Option<LocExpr>);
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Trace)]
pub struct ParamsDesc(pub Rc<Vec<Param>>);
impl Deref for ParamsDesc {
type Target = Vec<Param>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub struct ArgsDesc {
pub unnamed: Vec<LocExpr>,
pub named: Vec<(IStr, LocExpr)>,
}
impl ArgsDesc {
pub fn new(unnamed: Vec<LocExpr>, named: Vec<(IStr, LocExpr)>) -> Self {
Self { unnamed, named }
}
}
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Trace)]
pub enum DestructRest {
Keep(IStr),
Drop,
}
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Trace)]
pub enum Destruct {
Full(IStr),
#[cfg(feature = "exp-destruct")]
Skip,
#[cfg(feature = "exp-destruct")]
Array {
start: Vec<Destruct>,
rest: Option<DestructRest>,
end: Vec<Destruct>,
},
#[cfg(feature = "exp-destruct")]
Object {
fields: Vec<(IStr, Option<Destruct>, Option<LocExpr>)>,
rest: Option<DestructRest>,
},
}
impl Destruct {
pub fn name(&self) -> Option<IStr> {
match self {
Self::Full(name) => Some(name.clone()),
#[cfg(feature = "exp-destruct")]
_ => None,
}
}
pub fn capacity_hint(&self) -> usize {
#[cfg(feature = "exp-destruct")]
fn cap_rest(rest: &Option<DestructRest>) -> usize {
match rest {
Some(DestructRest::Keep(_)) => 1,
Some(DestructRest::Drop) => 0,
None => 0,
}
}
match self {
Self::Full(_) => 1,
#[cfg(feature = "exp-destruct")]
Self::Skip => 0,
#[cfg(feature = "exp-destruct")]
Self::Array { start, rest, end } => {
start.iter().map(Destruct::capacity_hint).sum::<usize>()
+ end.iter().map(Destruct::capacity_hint).sum::<usize>()
+ cap_rest(rest)
}
#[cfg(feature = "exp-destruct")]
Self::Object { fields, rest } => {
let mut out = 0;
for (_, into, _) in fields {
match into {
Some(v) => out += v.capacity_hint(),
None => out += 1,
}
}
out + cap_rest(rest)
}
}
}
}
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Trace)]
pub enum BindSpec {
Field {
into: Destruct,
value: LocExpr,
},
Function {
name: IStr,
params: ParamsDesc,
value: LocExpr,
},
}
impl BindSpec {
pub fn capacity_hint(&self) -> usize {
match self {
BindSpec::Field { into, .. } => into.capacity_hint(),
BindSpec::Function { .. } => 1,
}
}
}
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub struct IfSpecData(pub LocExpr);
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub struct ForSpecData(pub Destruct, pub LocExpr);
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub enum CompSpec {
IfSpec(IfSpecData),
ForSpec(ForSpecData),
}
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub struct ObjComp {
pub pre_locals: Vec<BindSpec>,
pub field: FieldMember,
pub post_locals: Vec<BindSpec>,
pub compspecs: Vec<CompSpec>,
}
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub enum ObjBody {
MemberList(Vec<Member>),
ObjComp(ObjComp),
}
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Eq, Clone, Copy, Trace)]
pub enum LiteralType {
This,
Super,
Dollar,
Null,
True,
False,
}
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub struct SliceDesc {
pub start: Option<LocExpr>,
pub end: Option<LocExpr>,
pub step: Option<LocExpr>,
}
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, PartialEq, Trace)]
pub enum Expr {
Literal(LiteralType),
Str(IStr),
Num(f64),
Var(IStr),
Arr(Vec<LocExpr>),
ArrComp(LocExpr, Vec<CompSpec>),
Obj(ObjBody),
ObjExtend(LocExpr, ObjBody),
Parened(LocExpr),
UnaryOp(UnaryOpType, LocExpr),
BinaryOp(LocExpr, BinaryOpType, LocExpr),
AssertExpr(AssertStmt, LocExpr),
LocalExpr(Vec<BindSpec>, LocExpr),
Import(LocExpr),
ImportStr(LocExpr),
ImportBin(LocExpr),
ErrorStmt(LocExpr),
Apply(LocExpr, ArgsDesc, bool),
Index {
indexable: LocExpr,
index: LocExpr,
#[cfg(feature = "exp-null-coaelse")]
null_coaelse: bool,
},
Function(ParamsDesc, LocExpr),
IfElse {
cond: IfSpecData,
cond_then: LocExpr,
cond_else: Option<LocExpr>,
},
Slice(LocExpr, SliceDesc),
}
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, PartialEq, Eq, Trace)]
#[trace(skip)]
#[repr(C)]
pub struct ExprLocation(pub Source, pub u32, pub u32);
impl ExprLocation {
pub fn belongs_to(&self, other: &ExprLocation) -> bool {
other.0 == self.0 && other.1 <= self.1 && other.2 >= self.2
}
}
#[cfg(target_pointer_width = "64")]
static_assertions::assert_eq_size!(ExprLocation, [u8; 16]);
impl Debug for ExprLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}:{:?}-{:?}", self.0, self.1, self.2)
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "structdump", derive(Codegen))]
#[derive(Clone, PartialEq, Trace)]
pub struct LocExpr(pub Rc<Expr>, pub ExprLocation);
#[cfg(target_pointer_width = "64")]
static_assertions::assert_eq_size!(LocExpr, [u8; 24]);
impl Debug for LocExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(f, "{:#?}", self.0)?;
} else {
write!(f, "{:?}", self.0)?;
}
write!(f, " from {:?}", self.1)?;
Ok(())
}
}