mirror of
https://github.com/nushell/nushell.git
synced 2024-11-27 02:44:01 +01:00
Merge pull request #70 from nushell/import_patterns
Add support for module imports
This commit is contained in:
commit
756269ee8d
@ -156,10 +156,10 @@ pub fn eval_expression(
|
||||
Expr::Var(var_id) => context
|
||||
.get_var(*var_id)
|
||||
.map_err(move |_| ShellError::VariableNotFoundAtRuntime(expr.span)),
|
||||
Expr::FullCellPath(column_path) => {
|
||||
let value = eval_expression(context, &column_path.head)?;
|
||||
Expr::FullCellPath(cell_path) => {
|
||||
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::Call(call) => eval_call(context, call, Value::nothing()),
|
||||
|
@ -151,4 +151,12 @@ pub enum ParseError {
|
||||
#[error("{0}")]
|
||||
#[diagnostic(code(nu::parser::assignment_mismatch), url(docsrs))]
|
||||
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(_) => {
|
||||
vec![(expr.span, FlatShape::Float)]
|
||||
}
|
||||
Expr::FullCellPath(column_path) => {
|
||||
Expr::FullCellPath(cell_path) => {
|
||||
let mut output = vec![];
|
||||
output.extend(flatten_expression(working_set, &column_path.head));
|
||||
for path_element in &column_path.tail {
|
||||
output.extend(flatten_expression(working_set, &cell_path.head));
|
||||
for path_element in &cell_path.tail {
|
||||
match path_element {
|
||||
PathMember::String { span, .. } => output.push((*span, FlatShape::String)),
|
||||
PathMember::Int { span, .. } => output.push((*span, FlatShape::Int)),
|
||||
|
@ -2,6 +2,7 @@ mod errors;
|
||||
mod flatten;
|
||||
mod lex;
|
||||
mod lite_parse;
|
||||
mod parse_keywords;
|
||||
mod parser;
|
||||
mod type_check;
|
||||
|
||||
@ -9,4 +10,7 @@ pub use errors::ParseError;
|
||||
pub use flatten::{flatten_block, FlatShape};
|
||||
pub use lex::{lex, Token, TokenContents};
|
||||
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};
|
||||
|
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::{
|
||||
ast::{
|
||||
Block, Call, Expr, Expression, FullCellPath, Operator, PathMember, Pipeline,
|
||||
RangeInclusion, RangeOperator, Statement,
|
||||
Block, Call, Expr, Expression, FullCellPath, ImportPattern, ImportPatternMember, Operator,
|
||||
PathMember, Pipeline, RangeInclusion, RangeOperator, Statement,
|
||||
},
|
||||
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)]
|
||||
@ -22,11 +26,11 @@ pub struct VarDecl {
|
||||
expression: Expression,
|
||||
}
|
||||
|
||||
fn garbage(span: Span) -> Expression {
|
||||
pub fn garbage(span: Span) -> Expression {
|
||||
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))]))
|
||||
}
|
||||
|
||||
@ -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,
|
||||
spans: &'a [Span],
|
||||
) -> Option<(&'a Span, ParseError)> {
|
||||
@ -963,7 +967,7 @@ pub(crate) fn parse_dollar_expr(
|
||||
} else if let (expr, None) = parse_range(working_set, span) {
|
||||
(expr, None)
|
||||
} 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,
|
||||
};
|
||||
|
||||
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);
|
||||
output.push(expr);
|
||||
}
|
||||
@ -1071,7 +1075,7 @@ pub fn parse_string_interpolation(
|
||||
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);
|
||||
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,
|
||||
implicit_head: Option<VarId>,
|
||||
span: Span,
|
||||
) -> (Expression, Option<ParseError>) {
|
||||
// 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 mut error = None;
|
||||
|
||||
@ -1294,7 +1298,7 @@ pub fn parse_full_column_path(
|
||||
Expression {
|
||||
expr: Expr::FullCellPath(Box::new(FullCellPath { head, tail })),
|
||||
ty: Type::Unknown,
|
||||
span: full_column_span,
|
||||
span: full_cell_span,
|
||||
custom_completion: None,
|
||||
},
|
||||
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(
|
||||
working_set: &mut StateWorkingSet,
|
||||
spans: &[Span],
|
||||
@ -1436,7 +1497,7 @@ pub fn expand_to_cell_path(
|
||||
} = expression
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
@ -2180,7 +2241,7 @@ pub fn parse_value(
|
||||
if let (expr, None) = parse_range(working_set, span) {
|
||||
return (expr, None);
|
||||
} 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"{") {
|
||||
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(
|
||||
working_set: &mut StateWorkingSet,
|
||||
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 expr;
|
||||
mod expression;
|
||||
mod import_pattern;
|
||||
mod operator;
|
||||
mod pipeline;
|
||||
mod statement;
|
||||
@ -12,6 +13,7 @@ pub use call::*;
|
||||
pub use cell_path::*;
|
||||
pub use expr::*;
|
||||
pub use expression::*;
|
||||
pub use import_pattern::*;
|
||||
pub use operator::*;
|
||||
pub use pipeline::*;
|
||||
pub use statement::*;
|
||||
|
@ -33,6 +33,9 @@ pub enum SyntaxShape {
|
||||
/// A glob pattern is allowed, eg `foo*`
|
||||
GlobPattern,
|
||||
|
||||
/// A module path pattern used for imports
|
||||
ImportPattern,
|
||||
|
||||
/// A block is allowed, eg `{start this thing}`
|
||||
Block(Option<Vec<SyntaxShape>>),
|
||||
|
||||
@ -87,6 +90,7 @@ impl SyntaxShape {
|
||||
SyntaxShape::Filesize => Type::Filesize,
|
||||
SyntaxShape::FullCellPath => Type::Unknown,
|
||||
SyntaxShape::GlobPattern => Type::String,
|
||||
SyntaxShape::ImportPattern => Type::Unknown,
|
||||
SyntaxShape::Int => Type::Int,
|
||||
SyntaxShape::List(x) => {
|
||||
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;
|
||||
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
|
||||
// to traverse the path without them
|
||||
match member {
|
||||
|
32
src/tests.rs
32
src/tests.rs
@ -342,3 +342,35 @@ fn better_block_types() -> TestResult {
|
||||
"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