more instructions for read/write variables, cell path, env var
This commit is contained in:
parent
20a214a4b9
commit
6eb035f071
|
@ -1,11 +1,11 @@
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{
|
ast::{
|
||||||
Argument, Block, Call, CellPath, Expr, Expression, Operator, Pipeline, PipelineRedirection,
|
Argument, Block, Call, CellPath, Expr, Expression, Operator, PathMember, Pipeline,
|
||||||
RedirectionSource, RedirectionTarget,
|
PipelineRedirection, RedirectionSource, RedirectionTarget,
|
||||||
},
|
},
|
||||||
engine::StateWorkingSet,
|
engine::StateWorkingSet,
|
||||||
ir::{Instruction, IrBlock, Literal, RedirectMode},
|
ir::{Instruction, IrBlock, Literal, RedirectMode},
|
||||||
IntoSpanned, OutDest, RegId, ShellError, Span, Spanned,
|
IntoSpanned, OutDest, RegId, ShellError, Span, Spanned, ENV_VARIABLE_ID,
|
||||||
};
|
};
|
||||||
|
|
||||||
const BLOCK_INPUT: RegId = RegId(0);
|
const BLOCK_INPUT: RegId = RegId(0);
|
||||||
|
@ -264,7 +264,13 @@ fn compile_expression(
|
||||||
Expr::Float(f) => lit(builder, Literal::Float(*f)),
|
Expr::Float(f) => lit(builder, Literal::Float(*f)),
|
||||||
Expr::Binary(bin) => lit(builder, Literal::Binary(bin.as_slice().into())),
|
Expr::Binary(bin) => lit(builder, Literal::Binary(bin.as_slice().into())),
|
||||||
Expr::Range(_) => Err(CompileError::Todo("Range")),
|
Expr::Range(_) => Err(CompileError::Todo("Range")),
|
||||||
Expr::Var(_) => Err(CompileError::Todo("Var")),
|
Expr::Var(var_id) => builder.push(
|
||||||
|
Instruction::LoadVariable {
|
||||||
|
dst: out_reg,
|
||||||
|
var_id: *var_id,
|
||||||
|
}
|
||||||
|
.into_spanned(expr.span),
|
||||||
|
),
|
||||||
Expr::VarDecl(_) => Err(CompileError::Todo("VarDecl")),
|
Expr::VarDecl(_) => Err(CompileError::Todo("VarDecl")),
|
||||||
Expr::Call(call) => {
|
Expr::Call(call) => {
|
||||||
// Ensure that out_reg contains the input value, because a call only uses one register
|
// Ensure that out_reg contains the input value, because a call only uses one register
|
||||||
|
@ -332,38 +338,42 @@ fn compile_expression(
|
||||||
Expr::RawString(rs) => lit(builder, Literal::RawString(rs.as_str().into())),
|
Expr::RawString(rs) => lit(builder, Literal::RawString(rs.as_str().into())),
|
||||||
Expr::CellPath(path) => lit(builder, Literal::CellPath(Box::new(path.clone()))),
|
Expr::CellPath(path) => lit(builder, Literal::CellPath(Box::new(path.clone()))),
|
||||||
Expr::FullCellPath(full_cell_path) => {
|
Expr::FullCellPath(full_cell_path) => {
|
||||||
compile_expression(
|
if matches!(full_cell_path.head.expr, Expr::Var(ENV_VARIABLE_ID)) {
|
||||||
working_set,
|
compile_load_env(builder, expr.span, &full_cell_path.tail, out_reg)
|
||||||
builder,
|
} else {
|
||||||
&full_cell_path.head,
|
compile_expression(
|
||||||
RedirectModes::capture_out(expr.span),
|
working_set,
|
||||||
in_reg,
|
builder,
|
||||||
out_reg,
|
&full_cell_path.head,
|
||||||
)?;
|
RedirectModes::capture_out(expr.span),
|
||||||
// Only do the follow if this is actually needed
|
in_reg,
|
||||||
if !full_cell_path.tail.is_empty() {
|
out_reg,
|
||||||
let cell_path_reg = builder.literal(
|
|
||||||
Literal::CellPath(Box::new(CellPath {
|
|
||||||
members: full_cell_path.tail.clone(),
|
|
||||||
}))
|
|
||||||
.into_spanned(expr.span),
|
|
||||||
)?;
|
|
||||||
builder.push(
|
|
||||||
Instruction::FollowCellPath {
|
|
||||||
src_dst: out_reg,
|
|
||||||
path: cell_path_reg,
|
|
||||||
}
|
|
||||||
.into_spanned(expr.span),
|
|
||||||
)?;
|
)?;
|
||||||
|
// Only do the follow if this is actually needed
|
||||||
|
if !full_cell_path.tail.is_empty() {
|
||||||
|
let cell_path_reg = builder.literal(
|
||||||
|
Literal::CellPath(Box::new(CellPath {
|
||||||
|
members: full_cell_path.tail.clone(),
|
||||||
|
}))
|
||||||
|
.into_spanned(expr.span),
|
||||||
|
)?;
|
||||||
|
builder.push(
|
||||||
|
Instruction::FollowCellPath {
|
||||||
|
src_dst: out_reg,
|
||||||
|
path: cell_path_reg,
|
||||||
|
}
|
||||||
|
.into_spanned(expr.span),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
Expr::ImportPattern(_) => Err(CompileError::Todo("ImportPattern")),
|
Expr::ImportPattern(_) => Err(CompileError::Todo("ImportPattern")),
|
||||||
Expr::Overlay(_) => Err(CompileError::Todo("Overlay")),
|
Expr::Overlay(_) => Err(CompileError::Todo("Overlay")),
|
||||||
Expr::Signature(_) => Err(CompileError::Todo("Signature")),
|
Expr::Signature(_) => Err(CompileError::Todo("Signature")),
|
||||||
Expr::StringInterpolation(_) => Err(CompileError::Todo("StringInterpolation")),
|
Expr::StringInterpolation(_) => Err(CompileError::Todo("StringInterpolation")),
|
||||||
Expr::Nothing => Err(CompileError::Todo("Nothing ")),
|
Expr::Nothing => Err(CompileError::Todo("Nothing")),
|
||||||
Expr::Garbage => Err(CompileError::Todo("Garbage ")),
|
Expr::Garbage => Err(CompileError::Todo("Garbage")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,6 +529,53 @@ fn compile_binary_op(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compile_load_env(
|
||||||
|
builder: &mut BlockBuilder,
|
||||||
|
span: Span,
|
||||||
|
path: &[PathMember],
|
||||||
|
out_reg: RegId,
|
||||||
|
) -> Result<(), CompileError> {
|
||||||
|
if path.is_empty() {
|
||||||
|
builder.push(
|
||||||
|
Instruction::LoadVariable {
|
||||||
|
dst: out_reg,
|
||||||
|
var_id: ENV_VARIABLE_ID,
|
||||||
|
}
|
||||||
|
.into_spanned(span),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let (key, optional) = match &path[0] {
|
||||||
|
PathMember::String { val, optional, .. } => (val.as_str().into(), *optional),
|
||||||
|
PathMember::Int { span, .. } => return Err(CompileError::AccessEnvByInt(*span)),
|
||||||
|
};
|
||||||
|
let tail = &path[1..];
|
||||||
|
|
||||||
|
if optional {
|
||||||
|
builder.push(Instruction::LoadEnvOpt { dst: out_reg, key }.into_spanned(span))?;
|
||||||
|
} else {
|
||||||
|
builder.push(Instruction::LoadEnv { dst: out_reg, key }.into_spanned(span))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tail.is_empty() {
|
||||||
|
let path = builder.literal(
|
||||||
|
Literal::CellPath(Box::new(CellPath {
|
||||||
|
members: tail.to_vec(),
|
||||||
|
}))
|
||||||
|
.into_spanned(span),
|
||||||
|
)?;
|
||||||
|
builder.push(
|
||||||
|
Instruction::FollowCellPath {
|
||||||
|
src_dst: out_reg,
|
||||||
|
path,
|
||||||
|
}
|
||||||
|
.into_spanned(span),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An internal compiler error, generally means a Nushell bug rather than an issue with user error
|
/// An internal compiler error, generally means a Nushell bug rather than an issue with user error
|
||||||
/// since parsing and typechecking has already passed.
|
/// since parsing and typechecking has already passed.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -528,11 +585,12 @@ enum CompileError {
|
||||||
InvalidRedirectMode,
|
InvalidRedirectMode,
|
||||||
Garbage,
|
Garbage,
|
||||||
UnsupportedOperatorExpression,
|
UnsupportedOperatorExpression,
|
||||||
|
AccessEnvByInt(Span),
|
||||||
Todo(&'static str),
|
Todo(&'static str),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompileError {
|
impl CompileError {
|
||||||
fn to_shell_error(self, span: Option<Span>) -> ShellError {
|
fn to_shell_error(self, mut span: Option<Span>) -> ShellError {
|
||||||
let ice = "internal compiler error: ";
|
let ice = "internal compiler error: ";
|
||||||
let message = match self {
|
let message = match self {
|
||||||
CompileError::RegisterOverflow => format!("{ice}register overflow"),
|
CompileError::RegisterOverflow => format!("{ice}register overflow"),
|
||||||
|
@ -548,6 +606,10 @@ impl CompileError {
|
||||||
CompileError::UnsupportedOperatorExpression => {
|
CompileError::UnsupportedOperatorExpression => {
|
||||||
format!("{ice}unsupported operator expression")
|
format!("{ice}unsupported operator expression")
|
||||||
}
|
}
|
||||||
|
CompileError::AccessEnvByInt(local_span) => {
|
||||||
|
span = Some(local_span);
|
||||||
|
format!("{ice}attempted access of $env by integer path")
|
||||||
|
}
|
||||||
CompileError::Todo(msg) => {
|
CompileError::Todo(msg) => {
|
||||||
format!("{ice}TODO: {msg}")
|
format!("{ice}TODO: {msg}")
|
||||||
}
|
}
|
||||||
|
@ -631,8 +693,8 @@ impl BlockBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert an instruction into the block, automatically freeing any registers consumed by the
|
/// Insert an instruction into the block, automatically marking any registers populated by
|
||||||
/// instruction.
|
/// the instruction, and freeing any registers consumed by the instruction.
|
||||||
fn push(&mut self, instruction: Spanned<Instruction>) -> Result<(), CompileError> {
|
fn push(&mut self, instruction: Spanned<Instruction>) -> Result<(), CompileError> {
|
||||||
match &instruction.item {
|
match &instruction.item {
|
||||||
Instruction::LoadLiteral { dst, lit: _ } => self.mark_register(*dst)?,
|
Instruction::LoadLiteral { dst, lit: _ } => self.mark_register(*dst)?,
|
||||||
|
@ -643,6 +705,11 @@ impl BlockBuilder {
|
||||||
Instruction::Clone { dst, src: _ } => self.mark_register(*dst)?,
|
Instruction::Clone { dst, src: _ } => self.mark_register(*dst)?,
|
||||||
Instruction::Collect { src_dst: _ } => (),
|
Instruction::Collect { src_dst: _ } => (),
|
||||||
Instruction::Drain { src } => self.free_register(*src)?,
|
Instruction::Drain { src } => self.free_register(*src)?,
|
||||||
|
Instruction::LoadVariable { dst, var_id: _ } => self.mark_register(*dst)?,
|
||||||
|
Instruction::StoreVariable { var_id: _, src } => self.free_register(*src)?,
|
||||||
|
Instruction::LoadEnv { dst, key: _ } => self.mark_register(*dst)?,
|
||||||
|
Instruction::LoadEnvOpt { dst, key: _ } => self.mark_register(*dst)?,
|
||||||
|
Instruction::StoreEnv { key: _, src } => self.free_register(*src)?,
|
||||||
Instruction::PushPositional { src } => self.free_register(*src)?,
|
Instruction::PushPositional { src } => self.free_register(*src)?,
|
||||||
Instruction::AppendRest { src } => self.free_register(*src)?,
|
Instruction::AppendRest { src } => self.free_register(*src)?,
|
||||||
Instruction::PushFlag { name: _ } => (),
|
Instruction::PushFlag { name: _ } => (),
|
||||||
|
@ -661,6 +728,18 @@ impl BlockBuilder {
|
||||||
rhs,
|
rhs,
|
||||||
} => self.free_register(*rhs)?,
|
} => self.free_register(*rhs)?,
|
||||||
Instruction::FollowCellPath { src_dst: _, path } => self.free_register(*path)?,
|
Instruction::FollowCellPath { src_dst: _, path } => self.free_register(*path)?,
|
||||||
|
Instruction::CloneCellPath { dst, src: _, path } => {
|
||||||
|
self.mark_register(*dst)?;
|
||||||
|
self.free_register(*path)?;
|
||||||
|
}
|
||||||
|
Instruction::UpsertCellPath {
|
||||||
|
src_dst: _,
|
||||||
|
path,
|
||||||
|
new_value,
|
||||||
|
} => {
|
||||||
|
self.free_register(*path)?;
|
||||||
|
self.free_register(*new_value)?;
|
||||||
|
}
|
||||||
Instruction::Jump { index: _ } => (),
|
Instruction::Jump { index: _ } => (),
|
||||||
Instruction::BranchIf { cond, index: _ } => self.free_register(*cond)?,
|
Instruction::BranchIf { cond, index: _ } => self.free_register(*cond)?,
|
||||||
Instruction::Return { src } => self.free_register(*src)?,
|
Instruction::Return { src } => self.free_register(*src)?,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use crate::{engine::EngineState, DeclId};
|
use crate::{engine::EngineState, DeclId, VarId};
|
||||||
|
|
||||||
use super::{Instruction, IrBlock, RedirectMode};
|
use super::{Instruction, IrBlock, RedirectMode};
|
||||||
|
|
||||||
|
@ -60,6 +60,23 @@ impl<'a> fmt::Display for FmtInstruction<'a> {
|
||||||
Instruction::Drain { src } => {
|
Instruction::Drain { src } => {
|
||||||
write!(f, "{:WIDTH$} {src}", "drain")
|
write!(f, "{:WIDTH$} {src}", "drain")
|
||||||
}
|
}
|
||||||
|
Instruction::LoadVariable { dst, var_id } => {
|
||||||
|
let var = FmtVar::new(self.engine_state, *var_id);
|
||||||
|
write!(f, "{:WIDTH$} {dst}, {var}", "load-variable")
|
||||||
|
}
|
||||||
|
Instruction::StoreVariable { var_id, src } => {
|
||||||
|
let var = FmtVar::new(self.engine_state, *var_id);
|
||||||
|
write!(f, "{:WIDTH$} {var}, {src}", "store-variable")
|
||||||
|
}
|
||||||
|
Instruction::LoadEnv { dst, key } => {
|
||||||
|
write!(f, "{:WIDTH$} {dst}, {key:?}", "load-env")
|
||||||
|
}
|
||||||
|
Instruction::LoadEnvOpt { dst, key } => {
|
||||||
|
write!(f, "{:WIDTH$} {dst}, {key:?}", "load-env-opt")
|
||||||
|
}
|
||||||
|
Instruction::StoreEnv { key, src } => {
|
||||||
|
write!(f, "{:WIDTH$} {key:?}, {src}", "store-env")
|
||||||
|
}
|
||||||
Instruction::PushPositional { src } => {
|
Instruction::PushPositional { src } => {
|
||||||
write!(f, "{:WIDTH$} {src}", "push-positional")
|
write!(f, "{:WIDTH$} {src}", "push-positional")
|
||||||
}
|
}
|
||||||
|
@ -88,6 +105,20 @@ impl<'a> fmt::Display for FmtInstruction<'a> {
|
||||||
Instruction::FollowCellPath { src_dst, path } => {
|
Instruction::FollowCellPath { src_dst, path } => {
|
||||||
write!(f, "{:WIDTH$} {src_dst}, {path}", "follow-cell-path")
|
write!(f, "{:WIDTH$} {src_dst}, {path}", "follow-cell-path")
|
||||||
}
|
}
|
||||||
|
Instruction::CloneCellPath { dst, src, path } => {
|
||||||
|
write!(f, "{:WIDTH$} {dst}, {src}, {path}", "clone-cell-path")
|
||||||
|
}
|
||||||
|
Instruction::UpsertCellPath {
|
||||||
|
src_dst,
|
||||||
|
path,
|
||||||
|
new_value,
|
||||||
|
} => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{:WIDTH$} {src_dst}, {path}, {new_value}",
|
||||||
|
"upsert-cell-path"
|
||||||
|
)
|
||||||
|
}
|
||||||
Instruction::Jump { index } => {
|
Instruction::Jump { index } => {
|
||||||
write!(f, "{:WIDTH$} {index}", "jump")
|
write!(f, "{:WIDTH$} {index}", "jump")
|
||||||
}
|
}
|
||||||
|
@ -115,6 +146,30 @@ impl fmt::Display for FmtDecl<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FmtVar<'a>(DeclId, Option<&'a str>);
|
||||||
|
|
||||||
|
impl<'a> FmtVar<'a> {
|
||||||
|
fn new(engine_state: &'a EngineState, var_id: VarId) -> Self {
|
||||||
|
// Search for the name of the variable
|
||||||
|
let name: Option<&str> = engine_state
|
||||||
|
.active_overlays(&[])
|
||||||
|
.flat_map(|overlay| overlay.vars.iter())
|
||||||
|
.find(|(_, v)| **v == var_id)
|
||||||
|
.map(|(k, _)| std::str::from_utf8(k).unwrap_or("<utf-8 error>"));
|
||||||
|
FmtVar(var_id, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FmtVar<'_> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
if let Some(name) = self.1 {
|
||||||
|
write!(f, "var {} {:?}", self.0, name)
|
||||||
|
} else {
|
||||||
|
write!(f, "var {}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for RedirectMode {
|
impl std::fmt::Display for RedirectMode {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{CellPath, Operator},
|
ast::{CellPath, Operator},
|
||||||
engine::EngineState,
|
engine::EngineState,
|
||||||
BlockId, DeclId, RegId, Span,
|
BlockId, DeclId, RegId, Span, VarId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -39,6 +39,17 @@ pub enum Instruction {
|
||||||
Collect { src_dst: RegId },
|
Collect { src_dst: RegId },
|
||||||
/// Drain the value/stream in a register and discard (e.g. semicolon)
|
/// Drain the value/stream in a register and discard (e.g. semicolon)
|
||||||
Drain { src: RegId },
|
Drain { src: RegId },
|
||||||
|
/// Load the value of a variable into the `dst` register
|
||||||
|
LoadVariable { dst: RegId, var_id: VarId },
|
||||||
|
/// Store the value of a variable from the `src` register
|
||||||
|
StoreVariable { var_id: VarId, src: RegId },
|
||||||
|
/// Load the value of an environment variable into the `dst` register
|
||||||
|
LoadEnv { dst: RegId, key: Box<str> },
|
||||||
|
/// Load the value of an environment variable into the `dst` register, or `Nothing` if it
|
||||||
|
/// doesn't exist
|
||||||
|
LoadEnvOpt { dst: RegId, key: Box<str> },
|
||||||
|
/// Store the value of an environment variable from the `src` register
|
||||||
|
StoreEnv { key: Box<str>, src: RegId },
|
||||||
/// Add a positional arg to the next call
|
/// Add a positional arg to the next call
|
||||||
PushPositional { src: RegId },
|
PushPositional { src: RegId },
|
||||||
/// Add a list of args to the next call (spread/rest)
|
/// Add a list of args to the next call (spread/rest)
|
||||||
|
@ -61,8 +72,19 @@ pub enum Instruction {
|
||||||
op: Operator,
|
op: Operator,
|
||||||
rhs: RegId,
|
rhs: RegId,
|
||||||
},
|
},
|
||||||
/// Follow a cell path on the `path`
|
/// Follow a cell path on the value in `src_dst`, storing the result back to `src_dst`
|
||||||
FollowCellPath { src_dst: RegId, path: RegId },
|
FollowCellPath { src_dst: RegId, path: RegId },
|
||||||
|
/// Clone the value at a cell path in `src`, storing the result to `dst`. The original value
|
||||||
|
/// remains in `src`. Must be a collected value.
|
||||||
|
CloneCellPath { dst: RegId, src: RegId, path: RegId },
|
||||||
|
/// Update/insert a cell path to `new_value` on the value in `src_dst`, storing the modified
|
||||||
|
/// value back to `src_dst`
|
||||||
|
UpsertCellPath {
|
||||||
|
src_dst: RegId,
|
||||||
|
path: RegId,
|
||||||
|
new_value: RegId,
|
||||||
|
},
|
||||||
|
/// Update a cell path
|
||||||
/// Jump to an offset in this block
|
/// Jump to an offset in this block
|
||||||
Jump { index: usize },
|
Jump { index: usize },
|
||||||
/// Branch to an offset in this block if the value of the `cond` register is a true boolean,
|
/// Branch to an offset in this block if the value of the `cond` register is a true boolean,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user