forked from extern/nushell
commit
7cac5bb633
32
src/eval.rs
32
src/eval.rs
@ -1,4 +1,4 @@
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, fmt::Display};
|
||||
|
||||
use crate::{
|
||||
parser::Operator, Block, BlockId, Call, Expr, Expression, ParserState, Span, Statement, VarId,
|
||||
@ -20,6 +20,24 @@ pub enum Value {
|
||||
Block(BlockId),
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl Display for Value {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Value::Bool { val, .. } => {
|
||||
write!(f, "{}", val)
|
||||
}
|
||||
Value::Int { val, .. } => {
|
||||
write!(f, "{}", val)
|
||||
}
|
||||
Value::String { val, .. } => write!(f, "{}", val),
|
||||
Value::List(..) => write!(f, "<list>"),
|
||||
Value::Block(..) => write!(f, "<block>"),
|
||||
Value::Unknown => write!(f, "<unknown>"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn add(&self, rhs: &Value) -> Result<Value, ShellError> {
|
||||
match (self, rhs) {
|
||||
@ -138,6 +156,18 @@ fn eval_call(state: &State, stack: &mut Stack, call: &Call) -> Result<Value, She
|
||||
}
|
||||
_ => Err(ShellError::Mismatch("bool".into(), Span::unknown())),
|
||||
}
|
||||
} else if decl.signature.name == "build-string" {
|
||||
let mut output = vec![];
|
||||
|
||||
for expr in &call.positional {
|
||||
let val = eval_expression(state, stack, expr)?;
|
||||
|
||||
output.push(val.to_string());
|
||||
}
|
||||
Ok(Value::String {
|
||||
val: output.join(""),
|
||||
span: call.head,
|
||||
})
|
||||
} else {
|
||||
Ok(Value::Unknown)
|
||||
}
|
||||
|
@ -49,6 +49,9 @@ fn main() -> std::io::Result<()> {
|
||||
);
|
||||
working_set.add_decl(sig.into());
|
||||
|
||||
let sig = Signature::build("build-string").rest(SyntaxShape::String, "list of string");
|
||||
working_set.add_decl(sig.into());
|
||||
|
||||
let sig = Signature::build("def")
|
||||
.required("def_name", SyntaxShape::String, "definition name")
|
||||
.required("params", SyntaxShape::Signature, "parameters")
|
||||
|
137
src/parser.rs
137
src/parser.rs
@ -894,8 +894,145 @@ impl<'a> ParserWorkingSet<'a> {
|
||||
}
|
||||
|
||||
pub(crate) fn parse_dollar_expr(&mut self, span: Span) -> (Expression, Option<ParseError>) {
|
||||
let contents = self.get_span_contents(span);
|
||||
|
||||
if contents.starts_with(b"$\"") {
|
||||
self.parse_string_interpolation(span)
|
||||
} else {
|
||||
self.parse_variable_expr(span)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_string_interpolation(&mut self, span: Span) -> (Expression, Option<ParseError>) {
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
enum InterpolationMode {
|
||||
String,
|
||||
Expression,
|
||||
}
|
||||
let mut error = None;
|
||||
|
||||
let contents = self.get_span_contents(span);
|
||||
|
||||
let start = if contents.starts_with(b"$\"") {
|
||||
span.start + 2
|
||||
} else {
|
||||
span.start
|
||||
};
|
||||
|
||||
let end = if contents.ends_with(b"\"") && contents.len() > 2 {
|
||||
span.end - 1
|
||||
} else {
|
||||
span.end
|
||||
};
|
||||
|
||||
let inner_span = Span { start, end };
|
||||
let contents = self.get_span_contents(inner_span).to_vec();
|
||||
|
||||
let mut output = vec![];
|
||||
let mut mode = InterpolationMode::String;
|
||||
let mut token_start = start;
|
||||
let mut depth = 0;
|
||||
|
||||
let mut b = start;
|
||||
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
while b != end {
|
||||
if contents[b - start] == b'(' && mode == InterpolationMode::String {
|
||||
depth = 1;
|
||||
mode = InterpolationMode::Expression;
|
||||
if token_start < b {
|
||||
let span = Span {
|
||||
start: token_start,
|
||||
end: b,
|
||||
};
|
||||
let str_contents = self.get_span_contents(span);
|
||||
output.push(Expression {
|
||||
expr: Expr::String(String::from_utf8_lossy(str_contents).to_string()),
|
||||
span,
|
||||
ty: Type::String,
|
||||
});
|
||||
}
|
||||
token_start = b;
|
||||
} else if contents[b - start] == b'(' && mode == InterpolationMode::Expression {
|
||||
depth += 1;
|
||||
} else if contents[b - start] == b')' && mode == InterpolationMode::Expression {
|
||||
match depth {
|
||||
0 => {}
|
||||
1 => {
|
||||
mode = InterpolationMode::String;
|
||||
|
||||
if token_start < b {
|
||||
let span = Span {
|
||||
start: token_start,
|
||||
end: b + 1,
|
||||
};
|
||||
|
||||
let (expr, err) = self.parse_full_column_path(span);
|
||||
error = error.or(err);
|
||||
output.push(expr);
|
||||
}
|
||||
|
||||
token_start = b + 1;
|
||||
}
|
||||
_ => depth -= 1,
|
||||
}
|
||||
}
|
||||
b += 1;
|
||||
}
|
||||
|
||||
match mode {
|
||||
InterpolationMode::String => {
|
||||
if token_start < end {
|
||||
let span = Span {
|
||||
start: token_start,
|
||||
end,
|
||||
};
|
||||
let str_contents = self.get_span_contents(span);
|
||||
output.push(Expression {
|
||||
expr: Expr::String(String::from_utf8_lossy(str_contents).to_string()),
|
||||
span,
|
||||
ty: Type::String,
|
||||
});
|
||||
}
|
||||
}
|
||||
InterpolationMode::Expression => {
|
||||
if token_start < end {
|
||||
let span = Span {
|
||||
start: token_start,
|
||||
end,
|
||||
};
|
||||
|
||||
let (expr, err) = self.parse_full_column_path(span);
|
||||
error = error.or(err);
|
||||
output.push(expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(decl_id) = self.find_decl(b"build-string") {
|
||||
(
|
||||
Expression {
|
||||
expr: Expr::Call(Box::new(Call {
|
||||
head: Span {
|
||||
start: span.start,
|
||||
end: span.start + 2,
|
||||
},
|
||||
named: vec![],
|
||||
positional: output,
|
||||
decl_id,
|
||||
})),
|
||||
span,
|
||||
ty: Type::String,
|
||||
},
|
||||
error,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
Expression::garbage(span),
|
||||
Some(ParseError::UnknownCommand(span)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_variable_expr(&mut self, span: Span) -> (Expression, Option<ParseError>) {
|
||||
let contents = self.get_span_contents(span);
|
||||
|
@ -102,6 +102,17 @@ impl Signature {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn rest(mut self, shape: impl Into<SyntaxShape>, desc: impl Into<String>) -> Signature {
|
||||
self.rest_positional = Some(PositionalArg {
|
||||
name: "rest".into(),
|
||||
desc: desc.into(),
|
||||
shape: shape.into(),
|
||||
var_id: None,
|
||||
});
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Add an optional named flag argument to the signature
|
||||
pub fn named(
|
||||
mut self,
|
||||
|
Loading…
Reference in New Issue
Block a user