forked from extern/nushell
Merge branch 'source-command' of https://github.com/moonrise-tk/engine-q into source-command
This commit is contained in:
@ -57,6 +57,14 @@ pub enum ParseError {
|
||||
#[diagnostic(code(nu::parser::expected_keyword), url(docsrs))]
|
||||
ExpectedKeyword(String, #[label("expected {0}")] Span),
|
||||
|
||||
#[error("Unexpected keyword.")]
|
||||
#[diagnostic(
|
||||
code(nu::parser::unexpected_keyword),
|
||||
url(docsrs),
|
||||
help("'export' keyword is allowed only in a module.")
|
||||
)]
|
||||
UnexpectedKeyword(String, #[label("unexpected {0}")] Span),
|
||||
|
||||
#[error("Multiple rest params.")]
|
||||
#[diagnostic(code(nu::parser::multiple_rest_params), url(docsrs))]
|
||||
MultipleRestParams(#[label = "multiple rest params"] Span),
|
||||
@ -69,6 +77,10 @@ pub enum ParseError {
|
||||
#[diagnostic(code(nu::parser::module_not_found), url(docsrs))]
|
||||
ModuleNotFound(#[label = "module not found"] Span),
|
||||
|
||||
#[error("Duplicate command definition within a block.")]
|
||||
#[diagnostic(code(nu::parser::duplicate_command_def), url(docsrs))]
|
||||
DuplicateCommandDef(#[label = "defined more than once"] Span),
|
||||
|
||||
#[error("Unknown command.")]
|
||||
#[diagnostic(
|
||||
code(nu::parser::unknown_command),
|
||||
|
@ -79,6 +79,16 @@ pub fn flatten_expression(
|
||||
Expr::Float(_) => {
|
||||
vec![(expr.span, FlatShape::Float)]
|
||||
}
|
||||
Expr::CellPath(cell_path) => {
|
||||
let mut output = vec![];
|
||||
for path_element in &cell_path.members {
|
||||
match path_element {
|
||||
PathMember::String { span, .. } => output.push((*span, FlatShape::String)),
|
||||
PathMember::Int { span, .. } => output.push((*span, FlatShape::Int)),
|
||||
}
|
||||
}
|
||||
output
|
||||
}
|
||||
Expr::FullCellPath(cell_path) => {
|
||||
let mut output = vec![];
|
||||
output.extend(flatten_expression(working_set, &cell_path.head));
|
||||
|
@ -14,9 +14,16 @@ use crate::{
|
||||
ParseError,
|
||||
};
|
||||
|
||||
pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) {
|
||||
pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) -> Option<ParseError> {
|
||||
let name = working_set.get_span_contents(spans[0]);
|
||||
|
||||
// handle "export def" same as "def"
|
||||
let (name, spans) = if name == b"export" && spans.len() >= 2 {
|
||||
(working_set.get_span_contents(spans[1]), &spans[1..])
|
||||
} else {
|
||||
(name, spans)
|
||||
};
|
||||
|
||||
if name == b"def" && spans.len() >= 4 {
|
||||
let (name_expr, ..) = parse_string(working_set, spans[1]);
|
||||
let name = name_expr.as_string();
|
||||
@ -36,9 +43,13 @@ pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) {
|
||||
signature.name = name;
|
||||
let decl = signature.predeclare();
|
||||
|
||||
working_set.add_decl(decl);
|
||||
if working_set.add_predecl(decl).is_some() {
|
||||
return Some(ParseError::DuplicateCommandDef(spans[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn parse_def(
|
||||
@ -89,17 +100,22 @@ pub fn parse_def(
|
||||
call.positional.push(block);
|
||||
|
||||
if let (Some(name), Some(mut signature), Some(block_id)) =
|
||||
(name, signature, block_id)
|
||||
(&name, signature, block_id)
|
||||
{
|
||||
let decl_id = working_set
|
||||
.find_decl(name.as_bytes())
|
||||
.expect("internal error: predeclaration failed to add definition");
|
||||
if let Some(decl_id) = working_set.find_decl(name.as_bytes()) {
|
||||
let declaration = working_set.get_decl_mut(decl_id);
|
||||
|
||||
let declaration = working_set.get_decl_mut(decl_id);
|
||||
signature.name = name.clone();
|
||||
|
||||
signature.name = name;
|
||||
|
||||
*declaration = signature.into_block_command(block_id);
|
||||
*declaration = signature.into_block_command(block_id);
|
||||
} else {
|
||||
error = error.or_else(|| {
|
||||
Some(ParseError::UnknownState(
|
||||
"Could not define hidden command".into(),
|
||||
spans[1],
|
||||
))
|
||||
});
|
||||
};
|
||||
}
|
||||
} else {
|
||||
let err_span = Span {
|
||||
@ -112,6 +128,19 @@ pub fn parse_def(
|
||||
}
|
||||
working_set.exit_scope();
|
||||
|
||||
if let Some(name) = name {
|
||||
// It's OK if it returns None: The decl was already merged in previous parse
|
||||
// pass.
|
||||
working_set.merge_predecl(name.as_bytes());
|
||||
} else {
|
||||
error = error.or_else(|| {
|
||||
Some(ParseError::UnknownState(
|
||||
"Could not get string from string expression".into(),
|
||||
*name_span,
|
||||
))
|
||||
});
|
||||
}
|
||||
|
||||
call
|
||||
} else {
|
||||
let err_span = Span {
|
||||
@ -219,6 +248,71 @@ pub fn parse_alias(
|
||||
)
|
||||
}
|
||||
|
||||
pub fn parse_export(
|
||||
working_set: &mut StateWorkingSet,
|
||||
spans: &[Span],
|
||||
) -> (Statement, Option<ParseError>) {
|
||||
let bytes = working_set.get_span_contents(spans[0]);
|
||||
|
||||
if bytes == b"export" && spans.len() >= 3 {
|
||||
let export_name = working_set.get_span_contents(spans[1]);
|
||||
|
||||
match export_name {
|
||||
b"def" => {
|
||||
let (stmt, err) = parse_def(working_set, &spans[1..]);
|
||||
|
||||
let export_def_decl_id = working_set
|
||||
.find_decl(b"export def")
|
||||
.expect("internal error: missing 'export def' command");
|
||||
|
||||
// Trying to warp the 'def' call into the 'export def' in a very clumsy way
|
||||
let stmt = if let Statement::Pipeline(ref pipe) = stmt {
|
||||
if !pipe.expressions.is_empty() {
|
||||
if let Expr::Call(ref call) = pipe.expressions[0].expr {
|
||||
let mut call = call.clone();
|
||||
|
||||
call.head = span(&spans[0..=1]);
|
||||
call.decl_id = export_def_decl_id;
|
||||
|
||||
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
||||
expr: Expr::Call(call),
|
||||
span: span(spans),
|
||||
ty: Type::Unknown,
|
||||
custom_completion: None,
|
||||
}]))
|
||||
} else {
|
||||
stmt
|
||||
}
|
||||
} else {
|
||||
stmt
|
||||
}
|
||||
} else {
|
||||
stmt
|
||||
};
|
||||
|
||||
(stmt, err)
|
||||
}
|
||||
_ => (
|
||||
garbage_statement(spans),
|
||||
Some(ParseError::Expected(
|
||||
// TODO: Fill in more as they come
|
||||
"def keyword".into(),
|
||||
spans[1],
|
||||
)),
|
||||
),
|
||||
}
|
||||
} else {
|
||||
(
|
||||
garbage_statement(spans),
|
||||
Some(ParseError::UnknownState(
|
||||
// TODO: fill in more as they come
|
||||
"Expected structure: export def [] {}".into(),
|
||||
span(spans),
|
||||
)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_module(
|
||||
working_set: &mut StateWorkingSet,
|
||||
spans: &[Span],
|
||||
@ -308,15 +402,21 @@ pub fn parse_module(
|
||||
b"def" => {
|
||||
let (stmt, err) = parse_def(working_set, &pipeline.commands[0].parts);
|
||||
|
||||
(stmt, err)
|
||||
}
|
||||
b"export" => {
|
||||
let (stmt, err) =
|
||||
parse_export(working_set, &pipeline.commands[0].parts);
|
||||
|
||||
if err.is_none() {
|
||||
let decl_name =
|
||||
working_set.get_span_contents(pipeline.commands[0].parts[1]);
|
||||
// parts[2] is safe since it's checked in parse_def already
|
||||
working_set.get_span_contents(pipeline.commands[0].parts[2]);
|
||||
|
||||
let decl_id = working_set
|
||||
.find_decl(decl_name)
|
||||
.expect("internal error: failed to find added declaration");
|
||||
|
||||
// TODO: Later, we want to put this behind 'export'
|
||||
exports.push((decl_name.into(), decl_id));
|
||||
}
|
||||
|
||||
@ -325,7 +425,8 @@ pub fn parse_module(
|
||||
_ => (
|
||||
garbage_statement(&pipeline.commands[0].parts),
|
||||
Some(ParseError::Expected(
|
||||
"def".into(),
|
||||
// TODO: Fill in more as they com
|
||||
"def or export keyword".into(),
|
||||
pipeline.commands[0].parts[0],
|
||||
)),
|
||||
),
|
||||
@ -394,8 +495,6 @@ pub fn parse_use(
|
||||
let mut error = None;
|
||||
let bytes = working_set.get_span_contents(spans[0]);
|
||||
|
||||
// TODO: Currently, this directly imports the module's definitions into the current scope.
|
||||
// Later, we want to put them behind the module's name and add selective importing
|
||||
if bytes == b"use" && spans.len() >= 2 {
|
||||
let (module_name_expr, err) = parse_string(working_set, spans[1]);
|
||||
error = error.or(err);
|
||||
@ -404,8 +503,6 @@ pub fn parse_use(
|
||||
error = error.or(err);
|
||||
|
||||
let exports = if let Some(block_id) = working_set.find_module(&import_pattern.head) {
|
||||
// TODO: Since we don't use the Block at all, we might just as well create a separate
|
||||
// Module that holds only the exports, without having Blocks in the way.
|
||||
working_set.get_block(block_id).exports.clone()
|
||||
} else {
|
||||
return (
|
||||
@ -493,6 +590,57 @@ pub fn parse_use(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_hide(
|
||||
working_set: &mut StateWorkingSet,
|
||||
spans: &[Span],
|
||||
) -> (Statement, Option<ParseError>) {
|
||||
let mut error = None;
|
||||
let bytes = working_set.get_span_contents(spans[0]);
|
||||
|
||||
if bytes == b"hide" && spans.len() >= 2 {
|
||||
let (name_expr, err) = parse_string(working_set, spans[1]);
|
||||
error = error.or(err);
|
||||
|
||||
let name_bytes: Vec<u8> = working_set.get_span_contents(spans[1]).into();
|
||||
|
||||
// TODO: Do the import pattern stuff for bulk-hiding
|
||||
|
||||
if working_set.hide_decl(&name_bytes).is_none() {
|
||||
error = error.or_else(|| Some(ParseError::UnknownCommand(spans[1])));
|
||||
}
|
||||
|
||||
// Create the Hide command call
|
||||
let hide_decl_id = working_set
|
||||
.find_decl(b"hide")
|
||||
.expect("internal error: missing hide command");
|
||||
|
||||
let call = Box::new(Call {
|
||||
head: spans[0],
|
||||
decl_id: hide_decl_id,
|
||||
positional: vec![name_expr],
|
||||
named: vec![],
|
||||
});
|
||||
|
||||
(
|
||||
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
||||
expr: Expr::Call(call),
|
||||
span: span(spans),
|
||||
ty: Type::Unknown,
|
||||
custom_completion: None,
|
||||
}])),
|
||||
error,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
garbage_statement(spans),
|
||||
Some(ParseError::UnknownState(
|
||||
"Expected structure: hide <name>".into(),
|
||||
span(spans),
|
||||
)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_let(
|
||||
working_set: &mut StateWorkingSet,
|
||||
spans: &[Span],
|
||||
|
@ -7,15 +7,15 @@ use crate::{
|
||||
|
||||
use nu_protocol::{
|
||||
ast::{
|
||||
Block, Call, Expr, Expression, FullCellPath, ImportPattern, ImportPatternMember, Operator,
|
||||
PathMember, Pipeline, RangeInclusion, RangeOperator, Statement,
|
||||
Block, Call, CellPath, Expr, Expression, FullCellPath, ImportPattern, ImportPatternMember,
|
||||
Operator, PathMember, Pipeline, RangeInclusion, RangeOperator, Statement,
|
||||
},
|
||||
engine::StateWorkingSet,
|
||||
span, Flag, PositionalArg, Signature, Span, SyntaxShape, Type, VarId,
|
||||
};
|
||||
|
||||
use crate::parse_keywords::{
|
||||
parse_alias, parse_def, parse_def_predecl, parse_let, parse_module, parse_use,
|
||||
parse_alias, parse_def, parse_def_predecl, parse_hide, parse_let, parse_module, parse_use,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@ -1158,6 +1158,62 @@ pub fn parse_variable_expr(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_cell_path(
|
||||
working_set: &mut StateWorkingSet,
|
||||
tokens: impl Iterator<Item = Token>,
|
||||
mut expect_dot: bool,
|
||||
span: Span,
|
||||
) -> (Vec<PathMember>, Option<ParseError>) {
|
||||
let mut error = None;
|
||||
let mut tail = vec![];
|
||||
|
||||
for path_element in tokens {
|
||||
let bytes = working_set.get_span_contents(path_element.span);
|
||||
|
||||
if expect_dot {
|
||||
expect_dot = false;
|
||||
if bytes.len() != 1 || bytes[0] != b'.' {
|
||||
error = error.or_else(|| Some(ParseError::Expected('.'.into(), path_element.span)));
|
||||
}
|
||||
} else {
|
||||
expect_dot = true;
|
||||
|
||||
match parse_int(bytes, path_element.span) {
|
||||
(
|
||||
Expression {
|
||||
expr: Expr::Int(val),
|
||||
span,
|
||||
..
|
||||
},
|
||||
None,
|
||||
) => tail.push(PathMember::Int {
|
||||
val: val as usize,
|
||||
span,
|
||||
}),
|
||||
_ => {
|
||||
let (result, err) = parse_string(working_set, path_element.span);
|
||||
error = error.or(err);
|
||||
match result {
|
||||
Expression {
|
||||
expr: Expr::String(string),
|
||||
span,
|
||||
..
|
||||
} => {
|
||||
tail.push(PathMember::String { val: string, span });
|
||||
}
|
||||
_ => {
|
||||
error =
|
||||
error.or_else(|| Some(ParseError::Expected("string".into(), span)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(tail, error)
|
||||
}
|
||||
|
||||
pub fn parse_full_cell_path(
|
||||
working_set: &mut StateWorkingSet,
|
||||
implicit_head: Option<VarId>,
|
||||
@ -1174,7 +1230,7 @@ pub fn parse_full_cell_path(
|
||||
let mut tokens = tokens.into_iter().peekable();
|
||||
if let Some(head) = tokens.peek() {
|
||||
let bytes = working_set.get_span_contents(head.span);
|
||||
let (head, mut expect_dot) = if bytes.starts_with(b"(") {
|
||||
let (head, expect_dot) = if bytes.starts_with(b"(") {
|
||||
let mut start = head.span.start;
|
||||
let mut end = head.span.end;
|
||||
|
||||
@ -1248,52 +1304,8 @@ pub fn parse_full_cell_path(
|
||||
);
|
||||
};
|
||||
|
||||
let mut tail = vec![];
|
||||
|
||||
for path_element in tokens {
|
||||
let bytes = working_set.get_span_contents(path_element.span);
|
||||
|
||||
if expect_dot {
|
||||
expect_dot = false;
|
||||
if bytes.len() != 1 || bytes[0] != b'.' {
|
||||
error =
|
||||
error.or_else(|| Some(ParseError::Expected('.'.into(), path_element.span)));
|
||||
}
|
||||
} else {
|
||||
expect_dot = true;
|
||||
|
||||
match parse_int(bytes, path_element.span) {
|
||||
(
|
||||
Expression {
|
||||
expr: Expr::Int(val),
|
||||
span,
|
||||
..
|
||||
},
|
||||
None,
|
||||
) => tail.push(PathMember::Int {
|
||||
val: val as usize,
|
||||
span,
|
||||
}),
|
||||
_ => {
|
||||
let (result, err) = parse_string(working_set, path_element.span);
|
||||
error = error.or(err);
|
||||
match result {
|
||||
Expression {
|
||||
expr: Expr::String(string),
|
||||
span,
|
||||
..
|
||||
} => {
|
||||
tail.push(PathMember::String { val: string, span });
|
||||
}
|
||||
_ => {
|
||||
error = error
|
||||
.or_else(|| Some(ParseError::Expected("string".into(), span)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let (tail, err) = parse_cell_path(working_set, tokens, expect_dot, span);
|
||||
error = error.or(err);
|
||||
|
||||
(
|
||||
Expression {
|
||||
@ -2352,6 +2364,28 @@ pub fn parse_value(
|
||||
)
|
||||
}
|
||||
}
|
||||
SyntaxShape::CellPath => {
|
||||
let source = working_set.get_span_contents(span);
|
||||
let mut error = None;
|
||||
|
||||
let (tokens, err) = lex(source, span.start, &[b'\n'], &[b'.']);
|
||||
error = error.or(err);
|
||||
|
||||
let tokens = tokens.into_iter().peekable();
|
||||
|
||||
let (cell_path, err) = parse_cell_path(working_set, tokens, false, span);
|
||||
error = error.or(err);
|
||||
|
||||
(
|
||||
Expression {
|
||||
expr: Expr::CellPath(CellPath { members: cell_path }),
|
||||
span,
|
||||
ty: Type::CellPath,
|
||||
custom_completion: None,
|
||||
},
|
||||
error,
|
||||
)
|
||||
}
|
||||
SyntaxShape::Any => {
|
||||
if bytes.starts_with(b"[") {
|
||||
parse_value(working_set, span, &SyntaxShape::Table)
|
||||
@ -2587,6 +2621,11 @@ pub fn parse_statement(
|
||||
b"module" => parse_module(working_set, spans),
|
||||
b"use" => parse_use(working_set, spans),
|
||||
b"source" => parse_source(working_set, spans),
|
||||
b"export" => (
|
||||
garbage_statement(spans),
|
||||
Some(ParseError::UnexpectedKeyword("export".into(), spans[0])),
|
||||
),
|
||||
b"hide" => parse_hide(working_set, spans),
|
||||
_ => {
|
||||
let (expr, err) = parse_expression(working_set, spans);
|
||||
(Statement::Pipeline(Pipeline::from_vec(vec![expr])), err)
|
||||
@ -2603,16 +2642,18 @@ pub fn parse_block(
|
||||
working_set.enter_scope();
|
||||
}
|
||||
|
||||
let mut error = None;
|
||||
|
||||
// Pre-declare any definition so that definitions
|
||||
// that share the same block can see each other
|
||||
for pipeline in &lite_block.block {
|
||||
if pipeline.commands.len() == 1 {
|
||||
parse_def_predecl(working_set, &pipeline.commands[0].parts);
|
||||
if let Some(err) = parse_def_predecl(working_set, &pipeline.commands[0].parts) {
|
||||
error = error.or(Some(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut error = None;
|
||||
|
||||
let block: Block = lite_block
|
||||
.block
|
||||
.iter()
|
||||
|
Reference in New Issue
Block a user