Merge pull request #70 from nushell/import_patterns

Add support for module imports
This commit is contained in:
JT 2021-09-27 07:47:50 +13:00 committed by GitHub
commit 756269ee8d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 630 additions and 469 deletions

View file

@ -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()),

View file

@ -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),
}

View file

@ -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)),

View file

@ -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};

View 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),
)),
)
}

View file

@ -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],

View 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>,
}

View file

@ -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::*;

View file

@ -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();

View file

@ -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 {

View file

@ -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",
)
}