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 },
116 TryCollect { src_dst: RegId },
120 Span { src_dst: RegId },
122 Drop { src: RegId },
124 Drain { src: RegId },
130 DrainIfEnd { src: RegId },
133 LoadVariable { dst: RegId, var_id: VarId },
135 StoreVariable { var_id: VarId, src: RegId },
137 DropVariable { var_id: VarId },
139 LoadEnv { dst: RegId, key: DataSlice },
141 LoadEnvOpt { dst: RegId, key: DataSlice },
144 StoreEnv { key: DataSlice, src: RegId },
146 PushPositional { src: RegId },
148 AppendRest { src: RegId },
150 PushFlag { name: DataSlice },
152 PushShortFlag { short: DataSlice },
154 PushNamed { name: DataSlice, src: RegId },
156 PushShortNamed { short: DataSlice, src: RegId },
158 PushParserInfo {
160 name: DataSlice,
161 info: Box<Expression>,
162 },
163 RedirectOut { mode: RedirectMode },
167 RedirectErr { mode: RedirectMode },
171 CheckErrRedirected { src: RegId },
173 OpenFile {
175 file_num: u32,
176 path: RegId,
177 append: bool,
178 },
179 WriteFile { file_num: u32, src: RegId },
182 CloseFile { file_num: u32 },
184 Call { decl_id: DeclId, src_dst: RegId },
187 StringAppend { src_dst: RegId, val: RegId },
190 GlobFrom { src_dst: RegId, no_expand: bool },
193 ListPush { src_dst: RegId, item: RegId },
195 ListSpread { src_dst: RegId, items: RegId },
197 RecordInsert {
200 src_dst: RegId,
201 key: RegId,
202 val: RegId,
203 },
204 RecordSpread { src_dst: RegId, items: RegId },
207 Not { src_dst: RegId },
209 BinaryOp {
212 lhs_dst: RegId,
213 op: Operator,
214 rhs: RegId,
215 },
216 FollowCellPath { src_dst: RegId, path: RegId },
218 CloneCellPath { dst: RegId, src: RegId, path: RegId },
221 UpsertCellPath {
224 src_dst: RegId,
225 path: RegId,
226 new_value: RegId,
227 },
228 Jump { index: usize },
230 BranchIf { cond: RegId, index: usize },
233 BranchIfEmpty { src: RegId, index: usize },
236 Match {
240 pattern: Box<Pattern>,
241 src: RegId,
242 index: usize,
243 },
244 CheckMatchGuard { src: RegId },
247 Iterate {
250 dst: RegId,
251 stream: RegId,
252 end_index: usize,
253 },
254 OnError { index: usize },
256 OnErrorInto { index: usize, dst: RegId },
259 Finally { index: usize },
261 FinallyInto { index: usize, dst: RegId },
264 PopErrorHandler,
267 PopFinallyRun,
269 ReturnEarly { src: RegId },
273 Return { src: RegId },
275}
276
277impl Instruction {
278 pub fn display<'a>(
281 &'a self,
282 engine_state: &'a EngineState,
283 data: &'a [u8],
284 ) -> FmtInstruction<'a> {
285 FmtInstruction {
286 engine_state,
287 instruction: self,
288 data,
289 }
290 }
291
292 pub fn output_register(&self) -> Option<RegId> {
294 match *self {
295 Instruction::Unreachable => None,
296 Instruction::LoadLiteral { dst, .. } => Some(dst),
297 Instruction::LoadValue { dst, .. } => Some(dst),
298 Instruction::Move { dst, .. } => Some(dst),
299 Instruction::Clone { dst, .. } => Some(dst),
300 Instruction::Collect { src_dst } => Some(src_dst),
301 Instruction::TryCollect { src_dst } => Some(src_dst),
302 Instruction::Span { src_dst } => Some(src_dst),
303 Instruction::Drop { .. } => None,
304 Instruction::Drain { .. } => None,
305 Instruction::DrainIfEnd { .. } => None,
306 Instruction::LoadVariable { dst, .. } => Some(dst),
307 Instruction::StoreVariable { .. } => None,
308 Instruction::DropVariable { .. } => None,
309 Instruction::LoadEnv { dst, .. } => Some(dst),
310 Instruction::LoadEnvOpt { dst, .. } => Some(dst),
311 Instruction::StoreEnv { .. } => None,
312 Instruction::PushPositional { .. } => None,
313 Instruction::AppendRest { .. } => None,
314 Instruction::PushFlag { .. } => None,
315 Instruction::PushShortFlag { .. } => None,
316 Instruction::PushNamed { .. } => None,
317 Instruction::PushShortNamed { .. } => None,
318 Instruction::PushParserInfo { .. } => None,
319 Instruction::RedirectOut { .. } => None,
320 Instruction::RedirectErr { .. } => None,
321 Instruction::CheckErrRedirected { .. } => None,
322 Instruction::OpenFile { .. } => None,
323 Instruction::WriteFile { .. } => None,
324 Instruction::CloseFile { .. } => None,
325 Instruction::Call { src_dst, .. } => Some(src_dst),
326 Instruction::StringAppend { src_dst, .. } => Some(src_dst),
327 Instruction::GlobFrom { src_dst, .. } => Some(src_dst),
328 Instruction::ListPush { src_dst, .. } => Some(src_dst),
329 Instruction::ListSpread { src_dst, .. } => Some(src_dst),
330 Instruction::RecordInsert { src_dst, .. } => Some(src_dst),
331 Instruction::RecordSpread { src_dst, .. } => Some(src_dst),
332 Instruction::Not { src_dst } => Some(src_dst),
333 Instruction::BinaryOp { lhs_dst, .. } => Some(lhs_dst),
334 Instruction::FollowCellPath { src_dst, .. } => Some(src_dst),
335 Instruction::CloneCellPath { dst, .. } => Some(dst),
336 Instruction::UpsertCellPath { src_dst, .. } => Some(src_dst),
337 Instruction::Jump { .. } => None,
338 Instruction::BranchIf { .. } => None,
339 Instruction::BranchIfEmpty { .. } => None,
340 Instruction::Match { .. } => None,
341 Instruction::CheckMatchGuard { .. } => None,
342 Instruction::Iterate { dst, .. } => Some(dst),
343 Instruction::OnError { .. } => None,
344 Instruction::Finally { .. } => None,
345 Instruction::OnErrorInto { .. } => None,
346 Instruction::FinallyInto { .. } => None,
347 Instruction::PopErrorHandler => None,
348 Instruction::PopFinallyRun => None,
349 Instruction::ReturnEarly { .. } => None,
350 Instruction::Return { .. } => None,
351 }
352 }
353
354 pub fn branch_target(&self) -> Option<usize> {
356 match self {
357 Instruction::Jump { index } => Some(*index),
358 Instruction::BranchIf { cond: _, index } => Some(*index),
359 Instruction::BranchIfEmpty { src: _, index } => Some(*index),
360 Instruction::Match {
361 pattern: _,
362 src: _,
363 index,
364 } => Some(*index),
365
366 Instruction::Iterate {
367 dst: _,
368 stream: _,
369 end_index,
370 } => Some(*end_index),
371 Instruction::OnError { index } => Some(*index),
372 Instruction::OnErrorInto { index, dst: _ } => Some(*index),
373 Instruction::Finally { index } => Some(*index),
374 Instruction::FinallyInto { index, dst: _ } => Some(*index),
375 _ => None,
376 }
377 }
378
379 pub fn set_branch_target(&mut self, target_index: usize) -> Result<(), usize> {
383 match self {
384 Instruction::Jump { index } => *index = target_index,
385 Instruction::BranchIf { cond: _, index } => *index = target_index,
386 Instruction::BranchIfEmpty { src: _, index } => *index = target_index,
387 Instruction::Match {
388 pattern: _,
389 src: _,
390 index,
391 } => *index = target_index,
392
393 Instruction::Iterate {
394 dst: _,
395 stream: _,
396 end_index,
397 } => *end_index = target_index,
398 Instruction::OnError { index } => *index = target_index,
399 Instruction::OnErrorInto { index, dst: _ } => *index = target_index,
400 Instruction::Finally { index } => *index = target_index,
401 Instruction::FinallyInto { index, dst: _ } => *index = target_index,
402 _ => return Err(target_index),
403 }
404 Ok(())
405 }
406
407 pub fn check_interrupt(
409 &self,
410 engine_state: &EngineState,
411 span: &Span,
412 ) -> Result<(), ShellError> {
413 match self {
414 Instruction::Jump { .. } | Instruction::Return { .. } => {
415 engine_state.signals().check(span)
416 }
417 _ => Ok(()),
418 }
419 }
420}
421
422const _: () = assert!(std::mem::size_of::<Instruction>() <= 24);
426
427#[derive(Debug, Clone, Serialize, Deserialize)]
429pub enum Literal {
430 Bool(bool),
431 Int(i64),
432 Float(f64),
433 Filesize(Filesize),
434 Duration(i64),
435 Binary(DataSlice),
436 Block(BlockId),
437 Closure(BlockId),
438 RowCondition(BlockId),
439 Range {
440 start: RegId,
441 step: RegId,
442 end: RegId,
443 inclusion: RangeInclusion,
444 },
445 List {
446 capacity: usize,
447 },
448 Record {
449 capacity: usize,
450 },
451 Filepath {
452 val: DataSlice,
453 no_expand: bool,
454 },
455 Directory {
456 val: DataSlice,
457 no_expand: bool,
458 },
459 GlobPattern {
460 val: DataSlice,
461 no_expand: bool,
462 },
463 String(DataSlice),
464 RawString(DataSlice),
465 CellPath(Box<CellPath>),
466 Date(Box<DateTime<FixedOffset>>),
467 Nothing,
468 Empty,
471}
472
473#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
483pub enum RedirectMode {
484 Pipe,
485 PipeSeparate,
486 Value,
487 Null,
488 Inherit,
489 Print,
490 File {
492 file_num: u32,
493 },
494 Caller,
496}
497
498mod serde_arc_u8_array {
500 use serde::{Deserialize, Serialize};
501 use std::sync::Arc;
502
503 pub fn serialize<S>(data: &Arc<[u8]>, ser: S) -> Result<S::Ok, S::Error>
504 where
505 S: serde::Serializer,
506 {
507 data.as_ref().serialize(ser)
508 }
509
510 pub fn deserialize<'de, D>(de: D) -> Result<Arc<[u8]>, D::Error>
511 where
512 D: serde::Deserializer<'de>,
513 {
514 let data: Vec<u8> = Deserialize::deserialize(de)?;
515 Ok(data.into())
516 }
517}