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 Finally { index: usize },
255 FinallyInto { index: usize, dst: RegId },
258 PopErrorHandler,
261 PopFinallyRun,
263 ReturnEarly { src: RegId },
267 Return { src: RegId },
269}
270
271impl Instruction {
272 pub fn display<'a>(
275 &'a self,
276 engine_state: &'a EngineState,
277 data: &'a [u8],
278 ) -> FmtInstruction<'a> {
279 FmtInstruction {
280 engine_state,
281 instruction: self,
282 data,
283 }
284 }
285
286 pub fn output_register(&self) -> Option<RegId> {
288 match *self {
289 Instruction::Unreachable => None,
290 Instruction::LoadLiteral { dst, .. } => Some(dst),
291 Instruction::LoadValue { dst, .. } => Some(dst),
292 Instruction::Move { dst, .. } => Some(dst),
293 Instruction::Clone { dst, .. } => Some(dst),
294 Instruction::Collect { src_dst } => Some(src_dst),
295 Instruction::Span { src_dst } => Some(src_dst),
296 Instruction::Drop { .. } => None,
297 Instruction::Drain { .. } => None,
298 Instruction::DrainIfEnd { .. } => None,
299 Instruction::LoadVariable { dst, .. } => Some(dst),
300 Instruction::StoreVariable { .. } => None,
301 Instruction::DropVariable { .. } => None,
302 Instruction::LoadEnv { dst, .. } => Some(dst),
303 Instruction::LoadEnvOpt { dst, .. } => Some(dst),
304 Instruction::StoreEnv { .. } => None,
305 Instruction::PushPositional { .. } => None,
306 Instruction::AppendRest { .. } => None,
307 Instruction::PushFlag { .. } => None,
308 Instruction::PushShortFlag { .. } => None,
309 Instruction::PushNamed { .. } => None,
310 Instruction::PushShortNamed { .. } => None,
311 Instruction::PushParserInfo { .. } => None,
312 Instruction::RedirectOut { .. } => None,
313 Instruction::RedirectErr { .. } => None,
314 Instruction::CheckErrRedirected { .. } => None,
315 Instruction::OpenFile { .. } => None,
316 Instruction::WriteFile { .. } => None,
317 Instruction::CloseFile { .. } => None,
318 Instruction::Call { src_dst, .. } => Some(src_dst),
319 Instruction::StringAppend { src_dst, .. } => Some(src_dst),
320 Instruction::GlobFrom { src_dst, .. } => Some(src_dst),
321 Instruction::ListPush { src_dst, .. } => Some(src_dst),
322 Instruction::ListSpread { src_dst, .. } => Some(src_dst),
323 Instruction::RecordInsert { src_dst, .. } => Some(src_dst),
324 Instruction::RecordSpread { src_dst, .. } => Some(src_dst),
325 Instruction::Not { src_dst } => Some(src_dst),
326 Instruction::BinaryOp { lhs_dst, .. } => Some(lhs_dst),
327 Instruction::FollowCellPath { src_dst, .. } => Some(src_dst),
328 Instruction::CloneCellPath { dst, .. } => Some(dst),
329 Instruction::UpsertCellPath { src_dst, .. } => Some(src_dst),
330 Instruction::Jump { .. } => None,
331 Instruction::BranchIf { .. } => None,
332 Instruction::BranchIfEmpty { .. } => None,
333 Instruction::Match { .. } => None,
334 Instruction::CheckMatchGuard { .. } => None,
335 Instruction::Iterate { dst, .. } => Some(dst),
336 Instruction::OnError { .. } => None,
337 Instruction::Finally { .. } => None,
338 Instruction::OnErrorInto { .. } => None,
339 Instruction::FinallyInto { .. } => None,
340 Instruction::PopErrorHandler => None,
341 Instruction::PopFinallyRun => None,
342 Instruction::ReturnEarly { .. } => None,
343 Instruction::Return { .. } => None,
344 }
345 }
346
347 pub fn branch_target(&self) -> Option<usize> {
349 match self {
350 Instruction::Jump { index } => Some(*index),
351 Instruction::BranchIf { cond: _, index } => Some(*index),
352 Instruction::BranchIfEmpty { src: _, index } => Some(*index),
353 Instruction::Match {
354 pattern: _,
355 src: _,
356 index,
357 } => Some(*index),
358
359 Instruction::Iterate {
360 dst: _,
361 stream: _,
362 end_index,
363 } => Some(*end_index),
364 Instruction::OnError { index } => Some(*index),
365 Instruction::OnErrorInto { index, dst: _ } => Some(*index),
366 Instruction::Finally { index } => Some(*index),
367 Instruction::FinallyInto { index, dst: _ } => Some(*index),
368 _ => None,
369 }
370 }
371
372 pub fn set_branch_target(&mut self, target_index: usize) -> Result<(), usize> {
376 match self {
377 Instruction::Jump { index } => *index = target_index,
378 Instruction::BranchIf { cond: _, index } => *index = target_index,
379 Instruction::BranchIfEmpty { src: _, index } => *index = target_index,
380 Instruction::Match {
381 pattern: _,
382 src: _,
383 index,
384 } => *index = target_index,
385
386 Instruction::Iterate {
387 dst: _,
388 stream: _,
389 end_index,
390 } => *end_index = target_index,
391 Instruction::OnError { index } => *index = target_index,
392 Instruction::OnErrorInto { index, dst: _ } => *index = target_index,
393 Instruction::Finally { index } => *index = target_index,
394 Instruction::FinallyInto { index, dst: _ } => *index = target_index,
395 _ => return Err(target_index),
396 }
397 Ok(())
398 }
399
400 pub fn check_interrupt(
402 &self,
403 engine_state: &EngineState,
404 span: &Span,
405 ) -> Result<(), ShellError> {
406 match self {
407 Instruction::Jump { .. } | Instruction::Return { .. } => {
408 engine_state.signals().check(span)
409 }
410 _ => Ok(()),
411 }
412 }
413}
414
415const _: () = assert!(std::mem::size_of::<Instruction>() <= 24);
419
420#[derive(Debug, Clone, Serialize, Deserialize)]
422pub enum Literal {
423 Bool(bool),
424 Int(i64),
425 Float(f64),
426 Filesize(Filesize),
427 Duration(i64),
428 Binary(DataSlice),
429 Block(BlockId),
430 Closure(BlockId),
431 RowCondition(BlockId),
432 Range {
433 start: RegId,
434 step: RegId,
435 end: RegId,
436 inclusion: RangeInclusion,
437 },
438 List {
439 capacity: usize,
440 },
441 Record {
442 capacity: usize,
443 },
444 Filepath {
445 val: DataSlice,
446 no_expand: bool,
447 },
448 Directory {
449 val: DataSlice,
450 no_expand: bool,
451 },
452 GlobPattern {
453 val: DataSlice,
454 no_expand: bool,
455 },
456 String(DataSlice),
457 RawString(DataSlice),
458 CellPath(Box<CellPath>),
459 Date(Box<DateTime<FixedOffset>>),
460 Nothing,
461 Empty,
464}
465
466#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
476pub enum RedirectMode {
477 Pipe,
478 PipeSeparate,
479 Value,
480 Null,
481 Inherit,
482 Print,
483 File {
485 file_num: u32,
486 },
487 Caller,
489}
490
491mod serde_arc_u8_array {
493 use serde::{Deserialize, Serialize};
494 use std::sync::Arc;
495
496 pub fn serialize<S>(data: &Arc<[u8]>, ser: S) -> Result<S::Ok, S::Error>
497 where
498 S: serde::Serializer,
499 {
500 data.as_ref().serialize(ser)
501 }
502
503 pub fn deserialize<'de, D>(de: D) -> Result<Arc<[u8]>, D::Error>
504 where
505 D: serde::Deserializer<'de>,
506 {
507 let data: Vec<u8> = Deserialize::deserialize(de)?;
508 Ok(data.into())
509 }
510}