mirror of
https://github.com/nushell/nushell.git
synced 2025-01-03 21:09:52 +01:00
Add support for module imports
This commit is contained in:
parent
47421e9ca7
commit
abb0d7bd22
@ -156,10 +156,10 @@ pub fn eval_expression(
|
|||||||
Expr::Var(var_id) => context
|
Expr::Var(var_id) => context
|
||||||
.get_var(*var_id)
|
.get_var(*var_id)
|
||||||
.map_err(move |_| ShellError::VariableNotFoundAtRuntime(expr.span)),
|
.map_err(move |_| ShellError::VariableNotFoundAtRuntime(expr.span)),
|
||||||
Expr::FullCellPath(column_path) => {
|
Expr::FullCellPath(cell_path) => {
|
||||||
let value = eval_expression(context, &column_path.head)?;
|
let value = eval_expression(context, &cell_path.head)?;
|
||||||
|
|
||||||
value.follow_cell_path(&column_path.tail)
|
value.follow_cell_path(&cell_path.tail)
|
||||||
}
|
}
|
||||||
Expr::RowCondition(_, expr) => eval_expression(context, expr),
|
Expr::RowCondition(_, expr) => eval_expression(context, expr),
|
||||||
Expr::Call(call) => eval_call(context, call, Value::nothing()),
|
Expr::Call(call) => eval_call(context, call, Value::nothing()),
|
||||||
|
@ -151,4 +151,12 @@ pub enum ParseError {
|
|||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
#[diagnostic(code(nu::parser::assignment_mismatch), url(docsrs))]
|
#[diagnostic(code(nu::parser::assignment_mismatch), url(docsrs))]
|
||||||
AssignmentMismatch(String, String, #[label("{1}")] Span),
|
AssignmentMismatch(String, String, #[label("{1}")] Span),
|
||||||
|
|
||||||
|
#[error("Missing import pattern.")]
|
||||||
|
#[diagnostic(code(nu::parser::missing_import_pattern), url(docsrs))]
|
||||||
|
MissingImportPattern(#[label = "needs an import pattern"] Span),
|
||||||
|
|
||||||
|
#[error("Module export not found.")]
|
||||||
|
#[diagnostic(code(nu::parser::export_not_found), url(docsrs))]
|
||||||
|
ExportNotFound(#[label = "could not find imports"] Span),
|
||||||
}
|
}
|
||||||
|
@ -79,10 +79,10 @@ pub fn flatten_expression(
|
|||||||
Expr::Float(_) => {
|
Expr::Float(_) => {
|
||||||
vec![(expr.span, FlatShape::Float)]
|
vec![(expr.span, FlatShape::Float)]
|
||||||
}
|
}
|
||||||
Expr::FullCellPath(column_path) => {
|
Expr::FullCellPath(cell_path) => {
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
output.extend(flatten_expression(working_set, &column_path.head));
|
output.extend(flatten_expression(working_set, &cell_path.head));
|
||||||
for path_element in &column_path.tail {
|
for path_element in &cell_path.tail {
|
||||||
match path_element {
|
match path_element {
|
||||||
PathMember::String { span, .. } => output.push((*span, FlatShape::String)),
|
PathMember::String { span, .. } => output.push((*span, FlatShape::String)),
|
||||||
PathMember::Int { span, .. } => output.push((*span, FlatShape::Int)),
|
PathMember::Int { span, .. } => output.push((*span, FlatShape::Int)),
|
||||||
|
@ -2,6 +2,7 @@ mod errors;
|
|||||||
mod flatten;
|
mod flatten;
|
||||||
mod lex;
|
mod lex;
|
||||||
mod lite_parse;
|
mod lite_parse;
|
||||||
|
mod parse_keywords;
|
||||||
mod parser;
|
mod parser;
|
||||||
mod type_check;
|
mod type_check;
|
||||||
|
|
||||||
@ -9,4 +10,7 @@ pub use errors::ParseError;
|
|||||||
pub use flatten::{flatten_block, FlatShape};
|
pub use flatten::{flatten_block, FlatShape};
|
||||||
pub use lex::{lex, Token, TokenContents};
|
pub use lex::{lex, Token, TokenContents};
|
||||||
pub use lite_parse::{lite_parse, LiteBlock};
|
pub use lite_parse::{lite_parse, LiteBlock};
|
||||||
|
pub use parse_keywords::{
|
||||||
|
parse_alias, parse_def, parse_def_predecl, parse_let, parse_module, parse_use,
|
||||||
|
};
|
||||||
pub use parser::{parse, Import, VarDecl};
|
pub use parser::{parse, Import, VarDecl};
|
||||||
|
484
crates/nu-parser/src/parse_keywords.rs
Normal file
484
crates/nu-parser/src/parse_keywords.rs
Normal file
@ -0,0 +1,484 @@
|
|||||||
|
use nu_protocol::{
|
||||||
|
ast::{Block, Call, Expr, Expression, ImportPatternMember, Pipeline, Statement},
|
||||||
|
engine::StateWorkingSet,
|
||||||
|
span, DeclId, Span, SyntaxShape, Type,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
lex, lite_parse,
|
||||||
|
parser::{
|
||||||
|
check_name, garbage, garbage_statement, parse_block_expression, parse_import_pattern,
|
||||||
|
parse_internal_call, parse_signature, parse_string,
|
||||||
|
},
|
||||||
|
ParseError,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) {
|
||||||
|
let name = working_set.get_span_contents(spans[0]);
|
||||||
|
|
||||||
|
if name == b"def" && spans.len() >= 4 {
|
||||||
|
let (name_expr, ..) = parse_string(working_set, spans[1]);
|
||||||
|
let name = name_expr.as_string();
|
||||||
|
|
||||||
|
working_set.enter_scope();
|
||||||
|
// FIXME: because parse_signature will update the scope with the variables it sees
|
||||||
|
// we end up parsing the signature twice per def. The first time is during the predecl
|
||||||
|
// so that we can see the types that are part of the signature, which we need for parsing.
|
||||||
|
// The second time is when we actually parse the body itworking_set.
|
||||||
|
// We can't reuse the first time because the variables that are created during parse_signature
|
||||||
|
// are lost when we exit the scope below.
|
||||||
|
let (sig, ..) = parse_signature(working_set, spans[2]);
|
||||||
|
let signature = sig.as_signature();
|
||||||
|
working_set.exit_scope();
|
||||||
|
|
||||||
|
if let (Some(name), Some(mut signature)) = (name, signature) {
|
||||||
|
signature.name = name;
|
||||||
|
let decl = signature.predeclare();
|
||||||
|
|
||||||
|
working_set.add_decl(decl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_def(
|
||||||
|
working_set: &mut StateWorkingSet,
|
||||||
|
spans: &[Span],
|
||||||
|
) -> (Statement, Option<ParseError>) {
|
||||||
|
let mut error = None;
|
||||||
|
let name = working_set.get_span_contents(spans[0]);
|
||||||
|
|
||||||
|
if name == b"def" && spans.len() >= 4 {
|
||||||
|
//FIXME: don't use expect here
|
||||||
|
let (name_expr, err) = parse_string(working_set, spans[1]);
|
||||||
|
error = error.or(err);
|
||||||
|
|
||||||
|
working_set.enter_scope();
|
||||||
|
let (sig, err) = parse_signature(working_set, spans[2]);
|
||||||
|
error = error.or(err);
|
||||||
|
|
||||||
|
let (block, err) =
|
||||||
|
parse_block_expression(working_set, &SyntaxShape::Block(Some(vec![])), spans[3]);
|
||||||
|
error = error.or(err);
|
||||||
|
working_set.exit_scope();
|
||||||
|
|
||||||
|
if error.is_some() {
|
||||||
|
return (
|
||||||
|
Statement::Pipeline(Pipeline::from_vec(vec![garbage(span(spans))])),
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = name_expr.as_string();
|
||||||
|
|
||||||
|
let signature = sig.as_signature();
|
||||||
|
|
||||||
|
let block_id = block.as_block();
|
||||||
|
|
||||||
|
match (name, signature, block_id) {
|
||||||
|
(Some(name), Some(mut signature), Some(block_id)) => {
|
||||||
|
let decl_id = working_set
|
||||||
|
.find_decl(name.as_bytes())
|
||||||
|
.expect("internal error: predeclaration failed to add definition");
|
||||||
|
|
||||||
|
let declaration = working_set.get_decl_mut(decl_id);
|
||||||
|
|
||||||
|
signature.name = name;
|
||||||
|
|
||||||
|
*declaration = signature.into_block_command(block_id);
|
||||||
|
|
||||||
|
let def_decl_id = working_set
|
||||||
|
.find_decl(b"def")
|
||||||
|
.expect("internal error: missing def command");
|
||||||
|
|
||||||
|
let call = Box::new(Call {
|
||||||
|
head: spans[0],
|
||||||
|
decl_id: def_decl_id,
|
||||||
|
positional: vec![name_expr, sig, block],
|
||||||
|
named: vec![],
|
||||||
|
});
|
||||||
|
|
||||||
|
(
|
||||||
|
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
||||||
|
expr: Expr::Call(call),
|
||||||
|
span: span(spans),
|
||||||
|
ty: Type::Unknown,
|
||||||
|
custom_completion: None,
|
||||||
|
}])),
|
||||||
|
error,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => (
|
||||||
|
Statement::Pipeline(Pipeline::from_vec(vec![garbage(span(spans))])),
|
||||||
|
error,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
garbage_statement(spans),
|
||||||
|
Some(ParseError::UnknownState(
|
||||||
|
"Expected structure: def <name> [] {}".into(),
|
||||||
|
span(spans),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_alias(
|
||||||
|
working_set: &mut StateWorkingSet,
|
||||||
|
spans: &[Span],
|
||||||
|
) -> (Statement, Option<ParseError>) {
|
||||||
|
let name = working_set.get_span_contents(spans[0]);
|
||||||
|
|
||||||
|
if name == b"alias" {
|
||||||
|
if let Some((span, err)) = check_name(working_set, spans) {
|
||||||
|
return (
|
||||||
|
Statement::Pipeline(Pipeline::from_vec(vec![garbage(*span)])),
|
||||||
|
Some(err),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(decl_id) = working_set.find_decl(b"alias") {
|
||||||
|
let (call, call_span, _) =
|
||||||
|
parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
|
||||||
|
|
||||||
|
if spans.len() >= 4 {
|
||||||
|
let alias_name = working_set.get_span_contents(spans[1]);
|
||||||
|
|
||||||
|
let alias_name = if alias_name.starts_with(b"\"")
|
||||||
|
&& alias_name.ends_with(b"\"")
|
||||||
|
&& alias_name.len() > 1
|
||||||
|
{
|
||||||
|
alias_name[1..(alias_name.len() - 1)].to_vec()
|
||||||
|
} else {
|
||||||
|
alias_name.to_vec()
|
||||||
|
};
|
||||||
|
let _equals = working_set.get_span_contents(spans[2]);
|
||||||
|
|
||||||
|
let replacement = spans[3..].to_vec();
|
||||||
|
|
||||||
|
//println!("{:?} {:?}", alias_name, replacement);
|
||||||
|
|
||||||
|
working_set.add_alias(alias_name, replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
||||||
|
expr: Expr::Call(call),
|
||||||
|
span: call_span,
|
||||||
|
ty: Type::Unknown,
|
||||||
|
custom_completion: None,
|
||||||
|
}])),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(
|
||||||
|
garbage_statement(spans),
|
||||||
|
Some(ParseError::UnknownState(
|
||||||
|
"internal error: alias statement unparseable".into(),
|
||||||
|
span(spans),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_module(
|
||||||
|
working_set: &mut StateWorkingSet,
|
||||||
|
spans: &[Span],
|
||||||
|
) -> (Statement, Option<ParseError>) {
|
||||||
|
// TODO: Currently, module is closing over its parent scope (i.e., defs in the parent scope are
|
||||||
|
// visible and usable in this module's scope). We might want to disable that. How?
|
||||||
|
|
||||||
|
let mut error = None;
|
||||||
|
let bytes = working_set.get_span_contents(spans[0]);
|
||||||
|
|
||||||
|
// parse_def() equivalent
|
||||||
|
if bytes == b"module" && spans.len() >= 3 {
|
||||||
|
let (module_name_expr, err) = parse_string(working_set, spans[1]);
|
||||||
|
error = error.or(err);
|
||||||
|
|
||||||
|
let module_name = module_name_expr
|
||||||
|
.as_string()
|
||||||
|
.expect("internal error: module name is not a string");
|
||||||
|
|
||||||
|
// parse_block_expression() equivalent
|
||||||
|
let block_span = spans[2];
|
||||||
|
let block_bytes = working_set.get_span_contents(block_span);
|
||||||
|
let mut start = block_span.start;
|
||||||
|
let mut end = block_span.end;
|
||||||
|
|
||||||
|
if block_bytes.starts_with(b"{") {
|
||||||
|
start += 1;
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
garbage_statement(spans),
|
||||||
|
Some(ParseError::Expected("block".into(), block_span)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if block_bytes.ends_with(b"}") {
|
||||||
|
end -= 1;
|
||||||
|
} else {
|
||||||
|
error = error.or_else(|| {
|
||||||
|
Some(ParseError::Unclosed(
|
||||||
|
"}".into(),
|
||||||
|
Span {
|
||||||
|
start: end,
|
||||||
|
end: end + 1,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let block_span = Span { start, end };
|
||||||
|
|
||||||
|
let source = working_set.get_span_contents(block_span);
|
||||||
|
|
||||||
|
let (output, err) = lex(source, start, &[], &[]);
|
||||||
|
error = error.or(err);
|
||||||
|
|
||||||
|
working_set.enter_scope();
|
||||||
|
|
||||||
|
// Do we need block parameters?
|
||||||
|
|
||||||
|
let (output, err) = lite_parse(&output);
|
||||||
|
error = error.or(err);
|
||||||
|
|
||||||
|
// We probably don't need $it
|
||||||
|
|
||||||
|
// we're doing parse_block() equivalent
|
||||||
|
// let (mut output, err) = parse_block(working_set, &output, false);
|
||||||
|
|
||||||
|
for pipeline in &output.block {
|
||||||
|
if pipeline.commands.len() == 1 {
|
||||||
|
parse_def_predecl(working_set, &pipeline.commands[0].parts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut exports: Vec<(Vec<u8>, DeclId)> = vec![];
|
||||||
|
|
||||||
|
let block: Block = output
|
||||||
|
.block
|
||||||
|
.iter()
|
||||||
|
.map(|pipeline| {
|
||||||
|
if pipeline.commands.len() == 1 {
|
||||||
|
// this one here is doing parse_statement() equivalent
|
||||||
|
// let (stmt, err) = parse_statement(working_set, &pipeline.commands[0].parts);
|
||||||
|
let name = working_set.get_span_contents(pipeline.commands[0].parts[0]);
|
||||||
|
|
||||||
|
let (stmt, err) = match name {
|
||||||
|
// TODO: Here we can add other stuff that's alowed for modules
|
||||||
|
b"def" => {
|
||||||
|
let (stmt, err) = parse_def(working_set, &pipeline.commands[0].parts);
|
||||||
|
|
||||||
|
if err.is_none() {
|
||||||
|
let decl_name =
|
||||||
|
working_set.get_span_contents(pipeline.commands[0].parts[1]);
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
(stmt, err)
|
||||||
|
}
|
||||||
|
_ => (
|
||||||
|
garbage_statement(&pipeline.commands[0].parts),
|
||||||
|
Some(ParseError::Expected("def".into(), block_span)),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
if error.is_none() {
|
||||||
|
error = err;
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt
|
||||||
|
} else {
|
||||||
|
error = Some(ParseError::Expected("not a pipeline".into(), block_span));
|
||||||
|
garbage_statement(spans)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let block = block.with_exports(exports);
|
||||||
|
|
||||||
|
working_set.exit_scope();
|
||||||
|
|
||||||
|
let block_id = working_set.add_module(&module_name, block);
|
||||||
|
|
||||||
|
let block_expr = Expression {
|
||||||
|
expr: Expr::Block(block_id),
|
||||||
|
span: block_span,
|
||||||
|
ty: Type::Block,
|
||||||
|
custom_completion: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let module_decl_id = working_set
|
||||||
|
.find_decl(b"module")
|
||||||
|
.expect("internal error: missing module command");
|
||||||
|
|
||||||
|
let call = Box::new(Call {
|
||||||
|
head: spans[0],
|
||||||
|
decl_id: module_decl_id,
|
||||||
|
positional: vec![module_name_expr, block_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: module <name> {}".into(),
|
||||||
|
span(spans),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_use(
|
||||||
|
working_set: &mut StateWorkingSet,
|
||||||
|
spans: &[Span],
|
||||||
|
) -> (Statement, Option<ParseError>) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
let (import_pattern, err) = parse_import_pattern(working_set, spans[1]);
|
||||||
|
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 (
|
||||||
|
garbage_statement(spans),
|
||||||
|
Some(ParseError::ModuleNotFound(spans[1])),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let exports = if import_pattern.members.is_empty() {
|
||||||
|
exports
|
||||||
|
.into_iter()
|
||||||
|
.map(|(name, id)| {
|
||||||
|
let mut new_name = import_pattern.head.to_vec();
|
||||||
|
new_name.push(b'.');
|
||||||
|
new_name.extend(&name);
|
||||||
|
(new_name, id)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
match &import_pattern.members[0] {
|
||||||
|
ImportPatternMember::Glob { .. } => exports,
|
||||||
|
ImportPatternMember::Name { name, span } => {
|
||||||
|
let new_exports: Vec<(Vec<u8>, usize)> =
|
||||||
|
exports.into_iter().filter(|x| &x.0 == name).collect();
|
||||||
|
|
||||||
|
if new_exports.is_empty() {
|
||||||
|
error = error.or(Some(ParseError::ExportNotFound(*span)))
|
||||||
|
}
|
||||||
|
|
||||||
|
new_exports
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Extend the current scope with the module's exports
|
||||||
|
working_set.activate_overlay(exports);
|
||||||
|
|
||||||
|
// Create the Use command call
|
||||||
|
let use_decl_id = working_set
|
||||||
|
.find_decl(b"use")
|
||||||
|
.expect("internal error: missing use command");
|
||||||
|
|
||||||
|
let call = Box::new(Call {
|
||||||
|
head: spans[0],
|
||||||
|
decl_id: use_decl_id,
|
||||||
|
positional: vec![module_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: use <name>".into(),
|
||||||
|
span(spans),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_let(
|
||||||
|
working_set: &mut StateWorkingSet,
|
||||||
|
spans: &[Span],
|
||||||
|
) -> (Statement, Option<ParseError>) {
|
||||||
|
let name = working_set.get_span_contents(spans[0]);
|
||||||
|
|
||||||
|
if name == b"let" {
|
||||||
|
if let Some((span, err)) = check_name(working_set, spans) {
|
||||||
|
return (
|
||||||
|
Statement::Pipeline(Pipeline::from_vec(vec![garbage(*span)])),
|
||||||
|
Some(err),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(decl_id) = working_set.find_decl(b"let") {
|
||||||
|
let (call, call_span, err) =
|
||||||
|
parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
|
||||||
|
|
||||||
|
// Update the variable to the known type if we can.
|
||||||
|
if err.is_none() {
|
||||||
|
let var_id = call.positional[0]
|
||||||
|
.as_var()
|
||||||
|
.expect("internal error: expected variable");
|
||||||
|
let rhs_type = call.positional[1].ty.clone();
|
||||||
|
|
||||||
|
working_set.set_variable_type(var_id, rhs_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
||||||
|
expr: Expr::Call(call),
|
||||||
|
span: call_span,
|
||||||
|
ty: Type::Unknown,
|
||||||
|
custom_completion: None,
|
||||||
|
}])),
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(
|
||||||
|
garbage_statement(spans),
|
||||||
|
Some(ParseError::UnknownState(
|
||||||
|
"internal error: let statement unparseable".into(),
|
||||||
|
span(spans),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
@ -6,11 +6,15 @@ use crate::{
|
|||||||
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{
|
ast::{
|
||||||
Block, Call, Expr, Expression, FullCellPath, Operator, PathMember, Pipeline,
|
Block, Call, Expr, Expression, FullCellPath, ImportPattern, ImportPatternMember, Operator,
|
||||||
RangeInclusion, RangeOperator, Statement,
|
PathMember, Pipeline, RangeInclusion, RangeOperator, Statement,
|
||||||
},
|
},
|
||||||
engine::StateWorkingSet,
|
engine::StateWorkingSet,
|
||||||
span, DeclId, Flag, PositionalArg, Signature, Span, SyntaxShape, Type, VarId,
|
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,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -22,11 +26,11 @@ pub struct VarDecl {
|
|||||||
expression: Expression,
|
expression: Expression,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn garbage(span: Span) -> Expression {
|
pub fn garbage(span: Span) -> Expression {
|
||||||
Expression::garbage(span)
|
Expression::garbage(span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn garbage_statement(spans: &[Span]) -> Statement {
|
pub fn garbage_statement(spans: &[Span]) -> Statement {
|
||||||
Statement::Pipeline(Pipeline::from_vec(vec![garbage(span(spans))]))
|
Statement::Pipeline(Pipeline::from_vec(vec![garbage(span(spans))]))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +67,7 @@ fn check_call(command: Span, sig: &Signature, call: &Call) -> Option<ParseError>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_name<'a>(
|
pub fn check_name<'a>(
|
||||||
working_set: &mut StateWorkingSet,
|
working_set: &mut StateWorkingSet,
|
||||||
spans: &'a [Span],
|
spans: &'a [Span],
|
||||||
) -> Option<(&'a Span, ParseError)> {
|
) -> Option<(&'a Span, ParseError)> {
|
||||||
@ -963,7 +967,7 @@ pub(crate) fn parse_dollar_expr(
|
|||||||
} else if let (expr, None) = parse_range(working_set, span) {
|
} else if let (expr, None) = parse_range(working_set, span) {
|
||||||
(expr, None)
|
(expr, None)
|
||||||
} else {
|
} else {
|
||||||
parse_full_column_path(working_set, None, span)
|
parse_full_cell_path(working_set, None, span)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1035,7 +1039,7 @@ pub fn parse_string_interpolation(
|
|||||||
end: b + 1,
|
end: b + 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (expr, err) = parse_full_column_path(working_set, None, span);
|
let (expr, err) = parse_full_cell_path(working_set, None, span);
|
||||||
error = error.or(err);
|
error = error.or(err);
|
||||||
output.push(expr);
|
output.push(expr);
|
||||||
}
|
}
|
||||||
@ -1071,7 +1075,7 @@ pub fn parse_string_interpolation(
|
|||||||
end,
|
end,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (expr, err) = parse_full_column_path(working_set, None, span);
|
let (expr, err) = parse_full_cell_path(working_set, None, span);
|
||||||
error = error.or(err);
|
error = error.or(err);
|
||||||
output.push(expr);
|
output.push(expr);
|
||||||
}
|
}
|
||||||
@ -1153,13 +1157,13 @@ pub fn parse_variable_expr(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_full_column_path(
|
pub fn parse_full_cell_path(
|
||||||
working_set: &mut StateWorkingSet,
|
working_set: &mut StateWorkingSet,
|
||||||
implicit_head: Option<VarId>,
|
implicit_head: Option<VarId>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> (Expression, Option<ParseError>) {
|
) -> (Expression, Option<ParseError>) {
|
||||||
// FIXME: assume for now a paren expr, but needs more
|
// FIXME: assume for now a paren expr, but needs more
|
||||||
let full_column_span = span;
|
let full_cell_span = span;
|
||||||
let source = working_set.get_span_contents(span);
|
let source = working_set.get_span_contents(span);
|
||||||
let mut error = None;
|
let mut error = None;
|
||||||
|
|
||||||
@ -1294,7 +1298,7 @@ pub fn parse_full_column_path(
|
|||||||
Expression {
|
Expression {
|
||||||
expr: Expr::FullCellPath(Box::new(FullCellPath { head, tail })),
|
expr: Expr::FullCellPath(Box::new(FullCellPath { head, tail })),
|
||||||
ty: Type::Unknown,
|
ty: Type::Unknown,
|
||||||
span: full_column_span,
|
span: full_cell_span,
|
||||||
custom_completion: None,
|
custom_completion: None,
|
||||||
},
|
},
|
||||||
error,
|
error,
|
||||||
@ -1371,6 +1375,63 @@ pub fn parse_type(_working_set: &StateWorkingSet, bytes: &[u8]) -> Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_import_pattern(
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
span: Span,
|
||||||
|
) -> (ImportPattern, Option<ParseError>) {
|
||||||
|
let source = working_set.get_span_contents(span);
|
||||||
|
let mut error = None;
|
||||||
|
|
||||||
|
let (tokens, err) = lex(source, span.start, &[], &[b'.']);
|
||||||
|
error = error.or(err);
|
||||||
|
|
||||||
|
if tokens.is_empty() {
|
||||||
|
return (
|
||||||
|
ImportPattern {
|
||||||
|
head: vec![],
|
||||||
|
members: vec![],
|
||||||
|
},
|
||||||
|
Some(ParseError::MissingImportPattern(span)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let head = working_set.get_span_contents(tokens[0].span).to_vec();
|
||||||
|
|
||||||
|
if let Some(tail) = tokens.get(2) {
|
||||||
|
// FIXME: expand this to handle deeper imports once we support module imports
|
||||||
|
let tail_span = tail.span;
|
||||||
|
let tail = working_set.get_span_contents(tail.span);
|
||||||
|
if tail == b"*" {
|
||||||
|
(
|
||||||
|
ImportPattern {
|
||||||
|
head,
|
||||||
|
members: vec![ImportPatternMember::Glob { span: tail_span }],
|
||||||
|
},
|
||||||
|
error,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
ImportPattern {
|
||||||
|
head,
|
||||||
|
members: vec![ImportPatternMember::Name {
|
||||||
|
name: tail.to_vec(),
|
||||||
|
span: tail_span,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
error,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
ImportPattern {
|
||||||
|
head,
|
||||||
|
members: vec![],
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_var_with_opt_type(
|
pub fn parse_var_with_opt_type(
|
||||||
working_set: &mut StateWorkingSet,
|
working_set: &mut StateWorkingSet,
|
||||||
spans: &[Span],
|
spans: &[Span],
|
||||||
@ -1436,7 +1497,7 @@ pub fn expand_to_cell_path(
|
|||||||
} = expression
|
} = expression
|
||||||
{
|
{
|
||||||
// Re-parse the string as if it were a cell-path
|
// Re-parse the string as if it were a cell-path
|
||||||
let (new_expression, _err) = parse_full_column_path(working_set, Some(var_id), *span);
|
let (new_expression, _err) = parse_full_cell_path(working_set, Some(var_id), *span);
|
||||||
|
|
||||||
*expression = new_expression;
|
*expression = new_expression;
|
||||||
}
|
}
|
||||||
@ -2180,7 +2241,7 @@ pub fn parse_value(
|
|||||||
if let (expr, None) = parse_range(working_set, span) {
|
if let (expr, None) = parse_range(working_set, span) {
|
||||||
return (expr, None);
|
return (expr, None);
|
||||||
} else {
|
} else {
|
||||||
return parse_full_column_path(working_set, None, span);
|
return parse_full_cell_path(working_set, None, span);
|
||||||
}
|
}
|
||||||
} else if bytes.starts_with(b"{") {
|
} else if bytes.starts_with(b"{") {
|
||||||
if matches!(shape, SyntaxShape::Block(_)) || matches!(shape, SyntaxShape::Any) {
|
if matches!(shape, SyntaxShape::Block(_)) || matches!(shape, SyntaxShape::Any) {
|
||||||
@ -2480,453 +2541,6 @@ pub fn parse_variable(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) {
|
|
||||||
let name = working_set.get_span_contents(spans[0]);
|
|
||||||
|
|
||||||
if name == b"def" && spans.len() >= 4 {
|
|
||||||
let (name_expr, ..) = parse_string(working_set, spans[1]);
|
|
||||||
let name = name_expr.as_string();
|
|
||||||
|
|
||||||
working_set.enter_scope();
|
|
||||||
// FIXME: because parse_signature will update the scope with the variables it sees
|
|
||||||
// we end up parsing the signature twice per def. The first time is during the predecl
|
|
||||||
// so that we can see the types that are part of the signature, which we need for parsing.
|
|
||||||
// The second time is when we actually parse the body itworking_set.
|
|
||||||
// We can't reuse the first time because the variables that are created during parse_signature
|
|
||||||
// are lost when we exit the scope below.
|
|
||||||
let (sig, ..) = parse_signature(working_set, spans[2]);
|
|
||||||
let signature = sig.as_signature();
|
|
||||||
working_set.exit_scope();
|
|
||||||
|
|
||||||
if let (Some(name), Some(mut signature)) = (name, signature) {
|
|
||||||
signature.name = name;
|
|
||||||
let decl = signature.predeclare();
|
|
||||||
|
|
||||||
working_set.add_decl(decl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_def(
|
|
||||||
working_set: &mut StateWorkingSet,
|
|
||||||
spans: &[Span],
|
|
||||||
) -> (Statement, Option<ParseError>) {
|
|
||||||
let mut error = None;
|
|
||||||
let name = working_set.get_span_contents(spans[0]);
|
|
||||||
|
|
||||||
if name == b"def" && spans.len() >= 4 {
|
|
||||||
//FIXME: don't use expect here
|
|
||||||
let (name_expr, err) = parse_string(working_set, spans[1]);
|
|
||||||
error = error.or(err);
|
|
||||||
|
|
||||||
working_set.enter_scope();
|
|
||||||
let (sig, err) = parse_signature(working_set, spans[2]);
|
|
||||||
error = error.or(err);
|
|
||||||
|
|
||||||
let (block, err) =
|
|
||||||
parse_block_expression(working_set, &SyntaxShape::Block(Some(vec![])), spans[3]);
|
|
||||||
error = error.or(err);
|
|
||||||
working_set.exit_scope();
|
|
||||||
|
|
||||||
if error.is_some() {
|
|
||||||
return (
|
|
||||||
Statement::Pipeline(Pipeline::from_vec(vec![garbage(span(spans))])),
|
|
||||||
error,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = name_expr.as_string();
|
|
||||||
|
|
||||||
let signature = sig.as_signature();
|
|
||||||
|
|
||||||
let block_id = block.as_block();
|
|
||||||
|
|
||||||
match (name, signature, block_id) {
|
|
||||||
(Some(name), Some(mut signature), Some(block_id)) => {
|
|
||||||
let decl_id = working_set
|
|
||||||
.find_decl(name.as_bytes())
|
|
||||||
.expect("internal error: predeclaration failed to add definition");
|
|
||||||
|
|
||||||
let declaration = working_set.get_decl_mut(decl_id);
|
|
||||||
|
|
||||||
signature.name = name;
|
|
||||||
|
|
||||||
*declaration = signature.into_block_command(block_id);
|
|
||||||
|
|
||||||
let def_decl_id = working_set
|
|
||||||
.find_decl(b"def")
|
|
||||||
.expect("internal error: missing def command");
|
|
||||||
|
|
||||||
let call = Box::new(Call {
|
|
||||||
head: spans[0],
|
|
||||||
decl_id: def_decl_id,
|
|
||||||
positional: vec![name_expr, sig, block],
|
|
||||||
named: vec![],
|
|
||||||
});
|
|
||||||
|
|
||||||
(
|
|
||||||
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
|
||||||
expr: Expr::Call(call),
|
|
||||||
span: span(spans),
|
|
||||||
ty: Type::Unknown,
|
|
||||||
custom_completion: None,
|
|
||||||
}])),
|
|
||||||
error,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => (
|
|
||||||
Statement::Pipeline(Pipeline::from_vec(vec![garbage(span(spans))])),
|
|
||||||
error,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(
|
|
||||||
garbage_statement(spans),
|
|
||||||
Some(ParseError::UnknownState(
|
|
||||||
"Expected structure: def <name> [] {}".into(),
|
|
||||||
span(spans),
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_alias(
|
|
||||||
working_set: &mut StateWorkingSet,
|
|
||||||
spans: &[Span],
|
|
||||||
) -> (Statement, Option<ParseError>) {
|
|
||||||
let name = working_set.get_span_contents(spans[0]);
|
|
||||||
|
|
||||||
if name == b"alias" {
|
|
||||||
if let Some((span, err)) = check_name(working_set, spans) {
|
|
||||||
return (
|
|
||||||
Statement::Pipeline(Pipeline::from_vec(vec![garbage(*span)])),
|
|
||||||
Some(err),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(decl_id) = working_set.find_decl(b"alias") {
|
|
||||||
let (call, call_span, _) =
|
|
||||||
parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
|
|
||||||
|
|
||||||
if spans.len() >= 4 {
|
|
||||||
let alias_name = working_set.get_span_contents(spans[1]);
|
|
||||||
|
|
||||||
let alias_name = if alias_name.starts_with(b"\"")
|
|
||||||
&& alias_name.ends_with(b"\"")
|
|
||||||
&& alias_name.len() > 1
|
|
||||||
{
|
|
||||||
alias_name[1..(alias_name.len() - 1)].to_vec()
|
|
||||||
} else {
|
|
||||||
alias_name.to_vec()
|
|
||||||
};
|
|
||||||
let _equals = working_set.get_span_contents(spans[2]);
|
|
||||||
|
|
||||||
let replacement = spans[3..].to_vec();
|
|
||||||
|
|
||||||
//println!("{:?} {:?}", alias_name, replacement);
|
|
||||||
|
|
||||||
working_set.add_alias(alias_name, replacement);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
|
||||||
expr: Expr::Call(call),
|
|
||||||
span: call_span,
|
|
||||||
ty: Type::Unknown,
|
|
||||||
custom_completion: None,
|
|
||||||
}])),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(
|
|
||||||
garbage_statement(spans),
|
|
||||||
Some(ParseError::UnknownState(
|
|
||||||
"internal error: alias statement unparseable".into(),
|
|
||||||
span(spans),
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_module(
|
|
||||||
working_set: &mut StateWorkingSet,
|
|
||||||
spans: &[Span],
|
|
||||||
) -> (Statement, Option<ParseError>) {
|
|
||||||
// TODO: Currently, module is closing over its parent scope (i.e., defs in the parent scope are
|
|
||||||
// visible and usable in this module's scope). We might want to disable that. How?
|
|
||||||
|
|
||||||
let mut error = None;
|
|
||||||
let bytes = working_set.get_span_contents(spans[0]);
|
|
||||||
|
|
||||||
// parse_def() equivalent
|
|
||||||
if bytes == b"module" && spans.len() >= 3 {
|
|
||||||
let (module_name_expr, err) = parse_string(working_set, spans[1]);
|
|
||||||
error = error.or(err);
|
|
||||||
|
|
||||||
let module_name = module_name_expr
|
|
||||||
.as_string()
|
|
||||||
.expect("internal error: module name is not a string");
|
|
||||||
|
|
||||||
// parse_block_expression() equivalent
|
|
||||||
let block_span = spans[2];
|
|
||||||
let block_bytes = working_set.get_span_contents(block_span);
|
|
||||||
let mut start = block_span.start;
|
|
||||||
let mut end = block_span.end;
|
|
||||||
|
|
||||||
if block_bytes.starts_with(b"{") {
|
|
||||||
start += 1;
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
garbage_statement(spans),
|
|
||||||
Some(ParseError::Expected("block".into(), block_span)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if block_bytes.ends_with(b"}") {
|
|
||||||
end -= 1;
|
|
||||||
} else {
|
|
||||||
error = error.or_else(|| {
|
|
||||||
Some(ParseError::Unclosed(
|
|
||||||
"}".into(),
|
|
||||||
Span {
|
|
||||||
start: end,
|
|
||||||
end: end + 1,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let block_span = Span { start, end };
|
|
||||||
|
|
||||||
let source = working_set.get_span_contents(block_span);
|
|
||||||
|
|
||||||
let (output, err) = lex(source, start, &[], &[]);
|
|
||||||
error = error.or(err);
|
|
||||||
|
|
||||||
working_set.enter_scope();
|
|
||||||
|
|
||||||
// Do we need block parameters?
|
|
||||||
|
|
||||||
let (output, err) = lite_parse(&output);
|
|
||||||
error = error.or(err);
|
|
||||||
|
|
||||||
// We probably don't need $it
|
|
||||||
|
|
||||||
// we're doing parse_block() equivalent
|
|
||||||
// let (mut output, err) = parse_block(working_set, &output, false);
|
|
||||||
|
|
||||||
for pipeline in &output.block {
|
|
||||||
if pipeline.commands.len() == 1 {
|
|
||||||
parse_def_predecl(working_set, &pipeline.commands[0].parts);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut exports: Vec<(Vec<u8>, DeclId)> = vec![];
|
|
||||||
|
|
||||||
let block: Block = output
|
|
||||||
.block
|
|
||||||
.iter()
|
|
||||||
.map(|pipeline| {
|
|
||||||
if pipeline.commands.len() == 1 {
|
|
||||||
// this one here is doing parse_statement() equivalent
|
|
||||||
// let (stmt, err) = parse_statement(working_set, &pipeline.commands[0].parts);
|
|
||||||
let name = working_set.get_span_contents(pipeline.commands[0].parts[0]);
|
|
||||||
|
|
||||||
let (stmt, err) = match name {
|
|
||||||
// TODO: Here we can add other stuff that's alowed for modules
|
|
||||||
b"def" => {
|
|
||||||
let (stmt, err) = parse_def(working_set, &pipeline.commands[0].parts);
|
|
||||||
|
|
||||||
if err.is_none() {
|
|
||||||
let decl_name =
|
|
||||||
working_set.get_span_contents(pipeline.commands[0].parts[1]);
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
(stmt, err)
|
|
||||||
}
|
|
||||||
_ => (
|
|
||||||
garbage_statement(&pipeline.commands[0].parts),
|
|
||||||
Some(ParseError::Expected("def".into(), block_span)),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
if error.is_none() {
|
|
||||||
error = err;
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt
|
|
||||||
} else {
|
|
||||||
error = Some(ParseError::Expected("not a pipeline".into(), block_span));
|
|
||||||
garbage_statement(spans)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.into();
|
|
||||||
|
|
||||||
let block = block.with_exports(exports);
|
|
||||||
|
|
||||||
working_set.exit_scope();
|
|
||||||
|
|
||||||
let block_id = working_set.add_module(&module_name, block);
|
|
||||||
|
|
||||||
let block_expr = Expression {
|
|
||||||
expr: Expr::Block(block_id),
|
|
||||||
span: block_span,
|
|
||||||
ty: Type::Block,
|
|
||||||
custom_completion: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let module_decl_id = working_set
|
|
||||||
.find_decl(b"module")
|
|
||||||
.expect("internal error: missing module command");
|
|
||||||
|
|
||||||
let call = Box::new(Call {
|
|
||||||
head: spans[0],
|
|
||||||
decl_id: module_decl_id,
|
|
||||||
positional: vec![module_name_expr, block_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: module <name> {}".into(),
|
|
||||||
span(spans),
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_use(
|
|
||||||
working_set: &mut StateWorkingSet,
|
|
||||||
spans: &[Span],
|
|
||||||
) -> (Statement, Option<ParseError>) {
|
|
||||||
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);
|
|
||||||
|
|
||||||
let module_name = module_name_expr
|
|
||||||
.as_string()
|
|
||||||
.expect("internal error: module name is not a string");
|
|
||||||
|
|
||||||
let module_name_bytes = module_name.as_bytes().to_vec();
|
|
||||||
|
|
||||||
let exports = if let Some(block_id) = working_set.find_module(&module_name_bytes) {
|
|
||||||
// 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 (
|
|
||||||
garbage_statement(spans),
|
|
||||||
Some(ParseError::ModuleNotFound(spans[1])),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Extend the current scope with the module's exports
|
|
||||||
working_set.activate_overlay(exports);
|
|
||||||
|
|
||||||
// Create the Use command call
|
|
||||||
let use_decl_id = working_set
|
|
||||||
.find_decl(b"use")
|
|
||||||
.expect("internal error: missing use command");
|
|
||||||
|
|
||||||
let call = Box::new(Call {
|
|
||||||
head: spans[0],
|
|
||||||
decl_id: use_decl_id,
|
|
||||||
positional: vec![module_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: use <name>".into(),
|
|
||||||
span(spans),
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_let(
|
|
||||||
working_set: &mut StateWorkingSet,
|
|
||||||
spans: &[Span],
|
|
||||||
) -> (Statement, Option<ParseError>) {
|
|
||||||
let name = working_set.get_span_contents(spans[0]);
|
|
||||||
|
|
||||||
if name == b"let" {
|
|
||||||
if let Some((span, err)) = check_name(working_set, spans) {
|
|
||||||
return (
|
|
||||||
Statement::Pipeline(Pipeline::from_vec(vec![garbage(*span)])),
|
|
||||||
Some(err),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(decl_id) = working_set.find_decl(b"let") {
|
|
||||||
let (call, call_span, err) =
|
|
||||||
parse_internal_call(working_set, spans[0], &spans[1..], decl_id);
|
|
||||||
|
|
||||||
// Update the variable to the known type if we can.
|
|
||||||
if err.is_none() {
|
|
||||||
let var_id = call.positional[0]
|
|
||||||
.as_var()
|
|
||||||
.expect("internal error: expected variable");
|
|
||||||
let rhs_type = call.positional[1].ty.clone();
|
|
||||||
|
|
||||||
working_set.set_variable_type(var_id, rhs_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
|
||||||
expr: Expr::Call(call),
|
|
||||||
span: call_span,
|
|
||||||
ty: Type::Unknown,
|
|
||||||
custom_completion: None,
|
|
||||||
}])),
|
|
||||||
err,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(
|
|
||||||
garbage_statement(spans),
|
|
||||||
Some(ParseError::UnknownState(
|
|
||||||
"internal error: let statement unparseable".into(),
|
|
||||||
span(spans),
|
|
||||||
)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_statement(
|
pub fn parse_statement(
|
||||||
working_set: &mut StateWorkingSet,
|
working_set: &mut StateWorkingSet,
|
||||||
spans: &[Span],
|
spans: &[Span],
|
||||||
|
13
crates/nu-protocol/src/ast/import_pattern.rs
Normal file
13
crates/nu-protocol/src/ast/import_pattern.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use crate::Span;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ImportPatternMember {
|
||||||
|
Glob { span: Span },
|
||||||
|
Name { name: Vec<u8>, span: Span },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ImportPattern {
|
||||||
|
pub head: Vec<u8>,
|
||||||
|
pub members: Vec<ImportPatternMember>,
|
||||||
|
}
|
@ -3,6 +3,7 @@ mod call;
|
|||||||
mod cell_path;
|
mod cell_path;
|
||||||
mod expr;
|
mod expr;
|
||||||
mod expression;
|
mod expression;
|
||||||
|
mod import_pattern;
|
||||||
mod operator;
|
mod operator;
|
||||||
mod pipeline;
|
mod pipeline;
|
||||||
mod statement;
|
mod statement;
|
||||||
@ -12,6 +13,7 @@ pub use call::*;
|
|||||||
pub use cell_path::*;
|
pub use cell_path::*;
|
||||||
pub use expr::*;
|
pub use expr::*;
|
||||||
pub use expression::*;
|
pub use expression::*;
|
||||||
|
pub use import_pattern::*;
|
||||||
pub use operator::*;
|
pub use operator::*;
|
||||||
pub use pipeline::*;
|
pub use pipeline::*;
|
||||||
pub use statement::*;
|
pub use statement::*;
|
||||||
|
@ -33,6 +33,9 @@ pub enum SyntaxShape {
|
|||||||
/// A glob pattern is allowed, eg `foo*`
|
/// A glob pattern is allowed, eg `foo*`
|
||||||
GlobPattern,
|
GlobPattern,
|
||||||
|
|
||||||
|
/// A module path pattern used for imports
|
||||||
|
ImportPattern,
|
||||||
|
|
||||||
/// A block is allowed, eg `{start this thing}`
|
/// A block is allowed, eg `{start this thing}`
|
||||||
Block(Option<Vec<SyntaxShape>>),
|
Block(Option<Vec<SyntaxShape>>),
|
||||||
|
|
||||||
@ -87,6 +90,7 @@ impl SyntaxShape {
|
|||||||
SyntaxShape::Filesize => Type::Filesize,
|
SyntaxShape::Filesize => Type::Filesize,
|
||||||
SyntaxShape::FullCellPath => Type::Unknown,
|
SyntaxShape::FullCellPath => Type::Unknown,
|
||||||
SyntaxShape::GlobPattern => Type::String,
|
SyntaxShape::GlobPattern => Type::String,
|
||||||
|
SyntaxShape::ImportPattern => Type::Unknown,
|
||||||
SyntaxShape::Int => Type::Int,
|
SyntaxShape::Int => Type::Int,
|
||||||
SyntaxShape::List(x) => {
|
SyntaxShape::List(x) => {
|
||||||
let contents = x.to_type();
|
let contents = x.to_type();
|
||||||
|
@ -177,9 +177,9 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn follow_cell_path(self, column_path: &[PathMember]) -> Result<Value, ShellError> {
|
pub fn follow_cell_path(self, cell_path: &[PathMember]) -> Result<Value, ShellError> {
|
||||||
let mut current = self;
|
let mut current = self;
|
||||||
for member in column_path {
|
for member in cell_path {
|
||||||
// FIXME: this uses a few extra clones for simplicity, but there may be a way
|
// FIXME: this uses a few extra clones for simplicity, but there may be a way
|
||||||
// to traverse the path without them
|
// to traverse the path without them
|
||||||
match member {
|
match member {
|
||||||
|
32
src/tests.rs
32
src/tests.rs
@ -342,3 +342,35 @@ fn better_block_types() -> TestResult {
|
|||||||
"1 is 2",
|
"1 is 2",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn module_imports_1() -> TestResult {
|
||||||
|
run_test(
|
||||||
|
r#"module foo { def a [] { 1 }; def b [] { 2 } }; use foo; foo.a"#,
|
||||||
|
"1",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn module_imports_2() -> TestResult {
|
||||||
|
run_test(
|
||||||
|
r#"module foo { def a [] { 1 }; def b [] { 2 } }; use foo.a; a"#,
|
||||||
|
"1",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn module_imports_3() -> TestResult {
|
||||||
|
run_test(
|
||||||
|
r#"module foo { def a [] { 1 }; def b [] { 2 } }; use foo.*; b"#,
|
||||||
|
"2",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn module_imports_4() -> TestResult {
|
||||||
|
fail_test(
|
||||||
|
r#"module foo { def a [] { 1 }; def b [] { 2 } }; use foo.c"#,
|
||||||
|
"not find import",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user