1use crate::{
2 BlockId, DeclId, Filesize, RegId, ShellError, Span, Value, VarId,
3 ast::{CellPath, Expression, Operator, Pattern, RangeInclusion},
4 engine::EngineState,
5};
6use chrono::{DateTime, FixedOffset};
7use serde::{Deserialize, Serialize};
8use std::{fmt, sync::Arc};
9
10mod call;
11mod display;
12
13pub use call::*;
14pub use display::{FmtInstruction, FmtIrBlock};
15
16#[derive(Clone, Serialize, Deserialize)]
17pub struct IrBlock {
18 pub instructions: Vec<Instruction>,
19 pub spans: Vec<Span>,
20 #[serde(with = "serde_arc_u8_array")]
21 pub data: Arc<[u8]>,
22 pub ast: Vec<Option<IrAstRef>>,
23 pub comments: Vec<Box<str>>,
25 pub register_count: u32,
26 pub file_count: u32,
27}
28
29impl fmt::Debug for IrBlock {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 f.debug_struct("IrBlock")
33 .field("instructions", &self.instructions)
34 .field("spans", &self.spans)
35 .field("data", &self.data)
36 .field("comments", &self.comments)
37 .field("register_count", &self.register_count)
38 .field("file_count", &self.file_count)
39 .finish_non_exhaustive()
40 }
41}
42
43impl IrBlock {
44 pub fn display<'a>(&'a self, engine_state: &'a EngineState) -> FmtIrBlock<'a> {
47 FmtIrBlock {
48 engine_state,
49 ir_block: self,
50 }
51 }
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
57pub struct DataSlice {
58 pub start: u32,
59 pub len: u32,
60}
61
62impl DataSlice {
63 pub const fn empty() -> DataSlice {
65 DataSlice { start: 0, len: 0 }
66 }
67}
68
69impl std::ops::Index<DataSlice> for [u8] {
70 type Output = [u8];
71
72 fn index(&self, index: DataSlice) -> &Self::Output {
73 &self[index.start as usize..(index.start as usize + index.len as usize)]
74 }
75}
76
77#[derive(Debug, Clone)]
80pub struct IrAstRef(pub Arc<Expression>);
81
82impl Serialize for IrAstRef {
83 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
84 where
85 S: serde::Serializer,
86 {
87 self.0.as_ref().serialize(serializer)
88 }
89}
90
91impl<'de> Deserialize<'de> for IrAstRef {
92 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
93 where
94 D: serde::Deserializer<'de>,
95 {
96 Expression::deserialize(deserializer).map(|expr| IrAstRef(Arc::new(expr)))
97 }
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub enum Instruction {
102 Unreachable,
104 LoadLiteral { dst: RegId, lit: Literal },
106 LoadValue { dst: RegId, val: Box<Value> },
108 Move { dst: RegId, src: RegId },
110 Clone { dst: RegId, src: RegId },
112 Collect { src_dst: RegId },
114 Span { src_dst: RegId },
116 Drop { src: RegId },
118 Drain { src: RegId },
124 DrainIfEnd { src: RegId },
127 LoadVariable { dst: RegId, var_id: VarId },
129 StoreVariable { var_id: VarId, src: RegId },
131 DropVariable { var_id: VarId },
133 LoadEnv { dst: RegId, key: DataSlice },
135 LoadEnvOpt { dst: RegId, key: DataSlice },
138 StoreEnv { key: DataSlice, src: RegId },
140 PushPositional { src: RegId },
142 AppendRest { src: RegId },
144 PushFlag { name: DataSlice },
146 PushShortFlag { short: DataSlice },
148 PushNamed { name: DataSlice, src: RegId },
150 PushShortNamed { short: DataSlice, src: RegId },
152 PushParserInfo {
154 name: DataSlice,
155 info: Box<Expression>,
156 },
157 RedirectOut { mode: RedirectMode },
161 RedirectErr { mode: RedirectMode },
165 CheckErrRedirected { src: RegId },
167 OpenFile {
169 file_num: u32,
170 path: RegId,
171 append: bool,
172 },
173 WriteFile { file_num: u32, src: RegId },
176 CloseFile { file_num: u32 },
178 Call { decl_id: DeclId, src_dst: RegId },
181 StringAppend { src_dst: RegId, val: RegId },
184 GlobFrom { src_dst: RegId, no_expand: bool },
187 ListPush { src_dst: RegId, item: RegId },
189 ListSpread { src_dst: RegId, items: RegId },
191 RecordInsert {
194 src_dst: RegId,
195 key: RegId,
196 val: RegId,
197 },
198 RecordSpread { src_dst: RegId, items: RegId },
201 Not { src_dst: RegId },
203 BinaryOp {
206 lhs_dst: RegId,
207 op: Operator,
208 rhs: RegId,
209 },
210 FollowCellPath { src_dst: RegId, path: RegId },
212 CloneCellPath { dst: RegId, src: RegId, path: RegId },
215 UpsertCellPath {
218 src_dst: RegId,
219 path: RegId,
220 new_value: RegId,
221 },
222 Jump { index: usize },
224 BranchIf { cond: RegId, index: usize },
227 BranchIfEmpty { src: RegId, index: usize },
230 Match {
234 pattern: Box<Pattern>,
235 src: RegId,
236 index: usize,
237 },
238 CheckMatchGuard { src: RegId },
241 Iterate {
244 dst: RegId,
245 stream: RegId,
246 end_index: usize,
247 },
248 OnError { index: usize },
250 OnErrorInto { index: usize, dst: RegId },
253 PopErrorHandler,
256 ReturnEarly { src: RegId },
260 Return { src: RegId },
262}
263
264impl Instruction {
265 pub fn display<'a>(
268 &'a self,
269 engine_state: &'a EngineState,
270 data: &'a [u8],
271 ) -> FmtInstruction<'a> {
272 FmtInstruction {
273 engine_state,
274 instruction: self,
275 data,
276 }
277 }
278
279 pub fn output_register(&self) -> Option<RegId> {
281 match *self {
282 Instruction::Unreachable => None,
283 Instruction::LoadLiteral { dst, .. } => Some(dst),
284 Instruction::LoadValue { dst, .. } => Some(dst),
285 Instruction::Move { dst, .. } => Some(dst),
286 Instruction::Clone { dst, .. } => Some(dst),
287 Instruction::Collect { src_dst } => Some(src_dst),
288 Instruction::Span { src_dst } => Some(src_dst),
289 Instruction::Drop { .. } => None,
290 Instruction::Drain { .. } => None,
291 Instruction::DrainIfEnd { .. } => None,
292 Instruction::LoadVariable { dst, .. } => Some(dst),
293 Instruction::StoreVariable { .. } => None,
294 Instruction::DropVariable { .. } => None,
295 Instruction::LoadEnv { dst, .. } => Some(dst),
296 Instruction::LoadEnvOpt { dst, .. } => Some(dst),
297 Instruction::StoreEnv { .. } => None,
298 Instruction::PushPositional { .. } => None,
299 Instruction::AppendRest { .. } => None,
300 Instruction::PushFlag { .. } => None,
301 Instruction::PushShortFlag { .. } => None,
302 Instruction::PushNamed { .. } => None,
303 Instruction::PushShortNamed { .. } => None,
304 Instruction::PushParserInfo { .. } => None,
305 Instruction::RedirectOut { .. } => None,
306 Instruction::RedirectErr { .. } => None,
307 Instruction::CheckErrRedirected { .. } => None,
308 Instruction::OpenFile { .. } => None,
309 Instruction::WriteFile { .. } => None,
310 Instruction::CloseFile { .. } => None,
311 Instruction::Call { src_dst, .. } => Some(src_dst),
312 Instruction::StringAppend { src_dst, .. } => Some(src_dst),
313 Instruction::GlobFrom { src_dst, .. } => Some(src_dst),
314 Instruction::ListPush { src_dst, .. } => Some(src_dst),
315 Instruction::ListSpread { src_dst, .. } => Some(src_dst),
316 Instruction::RecordInsert { src_dst, .. } => Some(src_dst),
317 Instruction::RecordSpread { src_dst, .. } => Some(src_dst),
318 Instruction::Not { src_dst } => Some(src_dst),
319 Instruction::BinaryOp { lhs_dst, .. } => Some(lhs_dst),
320 Instruction::FollowCellPath { src_dst, .. } => Some(src_dst),
321 Instruction::CloneCellPath { dst, .. } => Some(dst),
322 Instruction::UpsertCellPath { src_dst, .. } => Some(src_dst),
323 Instruction::Jump { .. } => None,
324 Instruction::BranchIf { .. } => None,
325 Instruction::BranchIfEmpty { .. } => None,
326 Instruction::Match { .. } => None,
327 Instruction::CheckMatchGuard { .. } => None,
328 Instruction::Iterate { dst, .. } => Some(dst),
329 Instruction::OnError { .. } => None,
330 Instruction::OnErrorInto { .. } => None,
331 Instruction::PopErrorHandler => None,
332 Instruction::ReturnEarly { .. } => None,
333 Instruction::Return { .. } => None,
334 }
335 }
336
337 pub fn branch_target(&self) -> Option<usize> {
339 match self {
340 Instruction::Jump { index } => Some(*index),
341 Instruction::BranchIf { cond: _, index } => Some(*index),
342 Instruction::BranchIfEmpty { src: _, index } => Some(*index),
343 Instruction::Match {
344 pattern: _,
345 src: _,
346 index,
347 } => Some(*index),
348
349 Instruction::Iterate {
350 dst: _,
351 stream: _,
352 end_index,
353 } => Some(*end_index),
354 Instruction::OnError { index } => Some(*index),
355 Instruction::OnErrorInto { index, dst: _ } => Some(*index),
356 _ => None,
357 }
358 }
359
360 pub fn set_branch_target(&mut self, target_index: usize) -> Result<(), usize> {
364 match self {
365 Instruction::Jump { index } => *index = target_index,
366 Instruction::BranchIf { cond: _, index } => *index = target_index,
367 Instruction::BranchIfEmpty { src: _, index } => *index = target_index,
368 Instruction::Match {
369 pattern: _,
370 src: _,
371 index,
372 } => *index = target_index,
373
374 Instruction::Iterate {
375 dst: _,
376 stream: _,
377 end_index,
378 } => *end_index = target_index,
379 Instruction::OnError { index } => *index = target_index,
380 Instruction::OnErrorInto { index, dst: _ } => *index = target_index,
381 _ => return Err(target_index),
382 }
383 Ok(())
384 }
385
386 pub fn check_interrupt(
388 &self,
389 engine_state: &EngineState,
390 span: &Span,
391 ) -> Result<(), ShellError> {
392 match self {
393 Instruction::Jump { .. } | Instruction::Return { .. } => {
394 engine_state.signals().check(span)
395 }
396 _ => Ok(()),
397 }
398 }
399}
400
401const _: () = assert!(std::mem::size_of::<Instruction>() <= 24);
405
406#[derive(Debug, Clone, Serialize, Deserialize)]
408pub enum Literal {
409 Bool(bool),
410 Int(i64),
411 Float(f64),
412 Filesize(Filesize),
413 Duration(i64),
414 Binary(DataSlice),
415 Block(BlockId),
416 Closure(BlockId),
417 RowCondition(BlockId),
418 Range {
419 start: RegId,
420 step: RegId,
421 end: RegId,
422 inclusion: RangeInclusion,
423 },
424 List {
425 capacity: usize,
426 },
427 Record {
428 capacity: usize,
429 },
430 Filepath {
431 val: DataSlice,
432 no_expand: bool,
433 },
434 Directory {
435 val: DataSlice,
436 no_expand: bool,
437 },
438 GlobPattern {
439 val: DataSlice,
440 no_expand: bool,
441 },
442 String(DataSlice),
443 RawString(DataSlice),
444 CellPath(Box<CellPath>),
445 Date(Box<DateTime<FixedOffset>>),
446 Nothing,
447}
448
449#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
459pub enum RedirectMode {
460 Pipe,
461 PipeSeparate,
462 Value,
463 Null,
464 Inherit,
465 Print,
466 File {
468 file_num: u32,
469 },
470 Caller,
472}
473
474mod serde_arc_u8_array {
476 use serde::{Deserialize, Serialize};
477 use std::sync::Arc;
478
479 pub fn serialize<S>(data: &Arc<[u8]>, ser: S) -> Result<S::Ok, S::Error>
480 where
481 S: serde::Serializer,
482 {
483 data.as_ref().serialize(ser)
484 }
485
486 pub fn deserialize<'de, D>(de: D) -> Result<Arc<[u8]>, D::Error>
487 where
488 D: serde::Deserializer<'de>,
489 {
490 let data: Vec<u8> = Deserialize::deserialize(de)?;
491 Ok(data.into())
492 }
493}