use crate::{
BlockId, DeclId, Filesize, RegId, ShellError, Span, Value, VarId,
ast::{CellPath, Expression, Operator, Pattern, RangeInclusion},
engine::EngineState,
};
use chrono::{DateTime, FixedOffset};
use serde::{Deserialize, Serialize};
use std::{fmt, sync::Arc};
mod call;
mod display;
pub use call::*;
pub use display::{FmtInstruction, FmtIrBlock};
#[derive(Clone, Serialize, Deserialize)]
pub struct IrBlock {
pub instructions: Vec<Instruction>,
pub spans: Vec<Span>,
#[serde(with = "serde_arc_u8_array")]
pub data: Arc<[u8]>,
pub ast: Vec<Option<IrAstRef>>,
pub comments: Vec<Box<str>>,
pub register_count: u32,
pub file_count: u32,
}
impl fmt::Debug for IrBlock {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("IrBlock")
.field("instructions", &self.instructions)
.field("spans", &self.spans)
.field("data", &self.data)
.field("comments", &self.comments)
.field("register_count", &self.register_count)
.field("file_count", &self.file_count)
.finish_non_exhaustive()
}
}
impl IrBlock {
pub fn display<'a>(&'a self, engine_state: &'a EngineState) -> FmtIrBlock<'a> {
FmtIrBlock {
engine_state,
ir_block: self,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct DataSlice {
pub start: u32,
pub len: u32,
}
impl DataSlice {
pub const fn empty() -> DataSlice {
DataSlice { start: 0, len: 0 }
}
}
impl std::ops::Index<DataSlice> for [u8] {
type Output = [u8];
fn index(&self, index: DataSlice) -> &Self::Output {
&self[index.start as usize..(index.start as usize + index.len as usize)]
}
}
#[derive(Debug, Clone)]
pub struct IrAstRef(pub Arc<Expression>);
impl Serialize for IrAstRef {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.0.as_ref().serialize(serializer)
}
}
impl<'de> Deserialize<'de> for IrAstRef {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Expression::deserialize(deserializer).map(|expr| IrAstRef(Arc::new(expr)))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Instruction {
Unreachable,
LoadLiteral { dst: RegId, lit: Literal },
LoadValue { dst: RegId, val: Box<Value> },
Move { dst: RegId, src: RegId },
Clone { dst: RegId, src: RegId },
Collect { src_dst: RegId },
TryCollect { src_dst: RegId },
Span { src_dst: RegId },
Drop { src: RegId },
Drain { src: RegId },
DrainIfEnd { src: RegId },
LoadVariable { dst: RegId, var_id: VarId },
StoreVariable { var_id: VarId, src: RegId },
DropVariable { var_id: VarId },
LoadEnv { dst: RegId, key: DataSlice },
LoadEnvOpt { dst: RegId, key: DataSlice },
StoreEnv { key: DataSlice, src: RegId },
PushPositional { src: RegId },
AppendRest { src: RegId },
PushFlag { name: DataSlice },
PushShortFlag { short: DataSlice },
PushNamed { name: DataSlice, src: RegId },
PushShortNamed { short: DataSlice, src: RegId },
PushParserInfo {
name: DataSlice,
info: Box<Expression>,
},
RedirectOut { mode: RedirectMode },
RedirectErr { mode: RedirectMode },
CheckErrRedirected { src: RegId },
OpenFile {
file_num: u32,
path: RegId,
append: bool,
},
WriteFile { file_num: u32, src: RegId },
CloseFile { file_num: u32 },
Call { decl_id: DeclId, src_dst: RegId },
StringAppend { src_dst: RegId, val: RegId },
GlobFrom { src_dst: RegId, no_expand: bool },
ListPush { src_dst: RegId, item: RegId },
ListSpread { src_dst: RegId, items: RegId },
RecordInsert {
src_dst: RegId,
key: RegId,
val: RegId,
},
RecordSpread { src_dst: RegId, items: RegId },
Not { src_dst: RegId },
BinaryOp {
lhs_dst: RegId,
op: Operator,
rhs: RegId,
},
FollowCellPath { src_dst: RegId, path: RegId },
CloneCellPath { dst: RegId, src: RegId, path: RegId },
UpsertCellPath {
src_dst: RegId,
path: RegId,
new_value: RegId,
},
Jump { index: usize },
BranchIf { cond: RegId, index: usize },
BranchIfEmpty { src: RegId, index: usize },
Match {
pattern: Box<Pattern>,
src: RegId,
index: usize,
},
CheckMatchGuard { src: RegId },
Iterate {
dst: RegId,
stream: RegId,
end_index: usize,
},
OnError { index: usize },
OnErrorInto { index: usize, dst: RegId },
Finally { index: usize },
FinallyInto { index: usize, dst: RegId },
PopErrorHandler,
PopFinallyRun,
ReturnEarly { src: RegId },
Return { src: RegId },
}
impl Instruction {
pub fn display<'a>(
&'a self,
engine_state: &'a EngineState,
data: &'a [u8],
) -> FmtInstruction<'a> {
FmtInstruction {
engine_state,
instruction: self,
data,
}
}
pub fn output_register(&self) -> Option<RegId> {
match *self {
Instruction::Unreachable => None,
Instruction::LoadLiteral { dst, .. } => Some(dst),
Instruction::LoadValue { dst, .. } => Some(dst),
Instruction::Move { dst, .. } => Some(dst),
Instruction::Clone { dst, .. } => Some(dst),
Instruction::Collect { src_dst } => Some(src_dst),
Instruction::TryCollect { src_dst } => Some(src_dst),
Instruction::Span { src_dst } => Some(src_dst),
Instruction::Drop { .. } => None,
Instruction::Drain { .. } => None,
Instruction::DrainIfEnd { .. } => None,
Instruction::LoadVariable { dst, .. } => Some(dst),
Instruction::StoreVariable { .. } => None,
Instruction::DropVariable { .. } => None,
Instruction::LoadEnv { dst, .. } => Some(dst),
Instruction::LoadEnvOpt { dst, .. } => Some(dst),
Instruction::StoreEnv { .. } => None,
Instruction::PushPositional { .. } => None,
Instruction::AppendRest { .. } => None,
Instruction::PushFlag { .. } => None,
Instruction::PushShortFlag { .. } => None,
Instruction::PushNamed { .. } => None,
Instruction::PushShortNamed { .. } => None,
Instruction::PushParserInfo { .. } => None,
Instruction::RedirectOut { .. } => None,
Instruction::RedirectErr { .. } => None,
Instruction::CheckErrRedirected { .. } => None,
Instruction::OpenFile { .. } => None,
Instruction::WriteFile { .. } => None,
Instruction::CloseFile { .. } => None,
Instruction::Call { src_dst, .. } => Some(src_dst),
Instruction::StringAppend { src_dst, .. } => Some(src_dst),
Instruction::GlobFrom { src_dst, .. } => Some(src_dst),
Instruction::ListPush { src_dst, .. } => Some(src_dst),
Instruction::ListSpread { src_dst, .. } => Some(src_dst),
Instruction::RecordInsert { src_dst, .. } => Some(src_dst),
Instruction::RecordSpread { src_dst, .. } => Some(src_dst),
Instruction::Not { src_dst } => Some(src_dst),
Instruction::BinaryOp { lhs_dst, .. } => Some(lhs_dst),
Instruction::FollowCellPath { src_dst, .. } => Some(src_dst),
Instruction::CloneCellPath { dst, .. } => Some(dst),
Instruction::UpsertCellPath { src_dst, .. } => Some(src_dst),
Instruction::Jump { .. } => None,
Instruction::BranchIf { .. } => None,
Instruction::BranchIfEmpty { .. } => None,
Instruction::Match { .. } => None,
Instruction::CheckMatchGuard { .. } => None,
Instruction::Iterate { dst, .. } => Some(dst),
Instruction::OnError { .. } => None,
Instruction::Finally { .. } => None,
Instruction::OnErrorInto { .. } => None,
Instruction::FinallyInto { .. } => None,
Instruction::PopErrorHandler => None,
Instruction::PopFinallyRun => None,
Instruction::ReturnEarly { .. } => None,
Instruction::Return { .. } => None,
}
}
pub fn branch_target(&self) -> Option<usize> {
match self {
Instruction::Jump { index } => Some(*index),
Instruction::BranchIf { cond: _, index } => Some(*index),
Instruction::BranchIfEmpty { src: _, index } => Some(*index),
Instruction::Match {
pattern: _,
src: _,
index,
} => Some(*index),
Instruction::Iterate {
dst: _,
stream: _,
end_index,
} => Some(*end_index),
Instruction::OnError { index } => Some(*index),
Instruction::OnErrorInto { index, dst: _ } => Some(*index),
Instruction::Finally { index } => Some(*index),
Instruction::FinallyInto { index, dst: _ } => Some(*index),
_ => None,
}
}
pub fn set_branch_target(&mut self, target_index: usize) -> Result<(), usize> {
match self {
Instruction::Jump { index } => *index = target_index,
Instruction::BranchIf { cond: _, index } => *index = target_index,
Instruction::BranchIfEmpty { src: _, index } => *index = target_index,
Instruction::Match {
pattern: _,
src: _,
index,
} => *index = target_index,
Instruction::Iterate {
dst: _,
stream: _,
end_index,
} => *end_index = target_index,
Instruction::OnError { index } => *index = target_index,
Instruction::OnErrorInto { index, dst: _ } => *index = target_index,
Instruction::Finally { index } => *index = target_index,
Instruction::FinallyInto { index, dst: _ } => *index = target_index,
_ => return Err(target_index),
}
Ok(())
}
pub fn check_interrupt(
&self,
engine_state: &EngineState,
span: &Span,
) -> Result<(), ShellError> {
match self {
Instruction::Jump { .. } | Instruction::Return { .. } => {
engine_state.signals().check(span)
}
_ => Ok(()),
}
}
}
const _: () = assert!(std::mem::size_of::<Instruction>() <= 24);
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Literal {
Bool(bool),
Int(i64),
Float(f64),
Filesize(Filesize),
Duration(i64),
Binary(DataSlice),
Block(BlockId),
Closure(BlockId),
RowCondition(BlockId),
Range {
start: RegId,
step: RegId,
end: RegId,
inclusion: RangeInclusion,
},
List {
capacity: usize,
},
Record {
capacity: usize,
},
Filepath {
val: DataSlice,
no_expand: bool,
},
Directory {
val: DataSlice,
no_expand: bool,
},
GlobPattern {
val: DataSlice,
no_expand: bool,
},
String(DataSlice),
RawString(DataSlice),
CellPath(Box<CellPath>),
Date(Box<DateTime<FixedOffset>>),
Nothing,
Empty,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum RedirectMode {
Pipe,
PipeSeparate,
Value,
Null,
Inherit,
Print,
File {
file_num: u32,
},
Caller,
}
mod serde_arc_u8_array {
use serde::{Deserialize, Serialize};
use std::sync::Arc;
pub fn serialize<S>(data: &Arc<[u8]>, ser: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
data.as_ref().serialize(ser)
}
pub fn deserialize<'de, D>(de: D) -> Result<Arc<[u8]>, D::Error>
where
D: serde::Deserializer<'de>,
{
let data: Vec<u8> = Deserialize::deserialize(de)?;
Ok(data.into())
}
}