Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow parsing modules as scripts #6357

Merged
merged 6 commits into from Aug 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
207 changes: 179 additions & 28 deletions crates/nu-parser/src/parse_keywords.rs
Expand Up @@ -274,8 +274,16 @@ pub fn parse_def(

// Checking that the function is used with the correct name
// Maybe this is not necessary but it is a sanity check
// Note: "export def" is treated the same as "def"

let def_call = working_set.get_span_contents(spans[0]).to_vec();
let (name_span, split_id) =
if spans.len() > 1 && working_set.get_span_contents(spans[0]) == b"export" {
(spans[1], 2)
} else {
(spans[0], 1)
};

let def_call = working_set.get_span_contents(name_span).to_vec();
if def_call != b"def" && def_call != b"def-env" {
return (
garbage_pipeline(spans),
Expand All @@ -301,14 +309,15 @@ pub fn parse_def(
}
Some(decl_id) => {
working_set.enter_scope();
let (command_spans, rest_spans) = spans.split_at(split_id);
let ParsedInternalCall {
call,
error: mut err,
output,
} = parse_internal_call(
working_set,
spans[0],
&spans[1..],
span(command_spans),
rest_spans,
decl_id,
expand_aliases_denylist,
);
Expand Down Expand Up @@ -381,7 +390,7 @@ pub fn parse_def(
error = error.or_else(|| {
Some(ParseError::InternalError(
"Predeclaration failed to add declaration".into(),
spans[1],
name_expr.span,
))
});
};
Expand Down Expand Up @@ -415,15 +424,22 @@ pub fn parse_extern(
lite_command: &LiteCommand,
expand_aliases_denylist: &[usize],
) -> (Pipeline, Option<ParseError>) {
let spans = &lite_command.parts[..];
let spans = &lite_command.parts;
let mut error = None;

let usage = build_usage(working_set, &lite_command.comments);

// Checking that the function is used with the correct name
// Maybe this is not necessary but it is a sanity check

let extern_call = working_set.get_span_contents(spans[0]).to_vec();
let (name_span, split_id) =
if spans.len() > 1 && working_set.get_span_contents(spans[0]) == b"export" {
(spans[1], 2)
} else {
(spans[0], 1)
};

let extern_call = working_set.get_span_contents(name_span).to_vec();
if extern_call != b"extern" {
return (
garbage_pipeline(spans),
Expand All @@ -449,12 +465,15 @@ pub fn parse_extern(
}
Some(decl_id) => {
working_set.enter_scope();

let (command_spans, rest_spans) = spans.split_at(split_id);

let ParsedInternalCall {
call, error: err, ..
} = parse_internal_call(
working_set,
spans[0],
&spans[1..],
span(command_spans),
rest_spans,
decl_id,
expand_aliases_denylist,
);
Expand Down Expand Up @@ -491,7 +510,7 @@ pub fn parse_extern(
error = error.or_else(|| {
Some(ParseError::InternalError(
"Predeclaration failed to add declaration".into(),
spans[1],
spans[split_id],
))
});
};
Expand Down Expand Up @@ -525,18 +544,27 @@ pub fn parse_alias(
spans: &[Span],
expand_aliases_denylist: &[usize],
) -> (Pipeline, Option<ParseError>) {
let name = working_set.get_span_contents(spans[0]);
let (name_span, split_id) =
if spans.len() > 1 && working_set.get_span_contents(spans[0]) == b"export" {
(spans[1], 2)
} else {
(spans[0], 1)
};

let name = working_set.get_span_contents(name_span);

if name == b"alias" {
if let Some((span, err)) = check_name(working_set, spans) {
return (Pipeline::from_vec(vec![garbage(*span)]), Some(err));
}

if let Some(decl_id) = working_set.find_decl(b"alias", &Type::Any) {
let (command_spans, rest_spans) = spans.split_at(split_id);

let ParsedInternalCall { call, output, .. } = parse_internal_call(
working_set,
spans[0],
&spans[1..],
span(command_spans),
rest_spans,
decl_id,
expand_aliases_denylist,
);
Expand All @@ -553,8 +581,8 @@ pub fn parse_alias(
);
}

if spans.len() >= 4 {
let alias_name = working_set.get_span_contents(spans[1]);
if spans.len() >= split_id + 3 {
let alias_name = working_set.get_span_contents(spans[split_id]);

let alias_name = if alias_name.starts_with(b"\"")
&& alias_name.ends_with(b"\"")
Expand All @@ -564,17 +592,17 @@ pub fn parse_alias(
} else {
alias_name.to_vec()
};
let _equals = working_set.get_span_contents(spans[2]);
let _equals = working_set.get_span_contents(spans[split_id + 1]);

let replacement = spans[3..].to_vec();
let replacement = spans[(split_id + 2)..].to_vec();

working_set.add_alias(alias_name, replacement);
}

let err = if spans.len() < 4 {
Some(ParseError::IncorrectValue(
"Incomplete alias".into(),
spans[0],
span(&spans[..split_id]),
"incomplete alias".into(),
))
} else {
Expand Down Expand Up @@ -602,7 +630,107 @@ pub fn parse_alias(
)
}

pub fn parse_export(
// This one will trigger if `export` appears during eval, e.g., in a script
pub fn parse_export_in_block(
working_set: &mut StateWorkingSet,
lite_command: &LiteCommand,
expand_aliases_denylist: &[usize],
) -> (Pipeline, Option<ParseError>) {
let call_span = span(&lite_command.parts);

let full_name = if lite_command.parts.len() > 1 {
let sub = working_set.get_span_contents(lite_command.parts[1]);
match sub {
b"alias" | b"def" | b"def-env" | b"extern" | b"use" => [b"export ", sub].concat(),
_ => b"export".to_vec(),
}
} else {
b"export".to_vec()
};

if let Some(decl_id) = working_set.find_decl(&full_name, &Type::Any) {
let ParsedInternalCall {
call,
error: mut err,
output,
..
} = parse_internal_call(
working_set,
if full_name == b"export" {
lite_command.parts[0]
} else {
span(&lite_command.parts[0..2])
},
if full_name == b"export" {
&lite_command.parts[1..]
} else {
&lite_command.parts[2..]
},
decl_id,
expand_aliases_denylist,
);

let decl = working_set.get_decl(decl_id);
err = check_call(call_span, &decl.signature(), &call).or(err);

if err.is_some() || call.has_flag("help") {
return (
Pipeline::from_vec(vec![Expression {
expr: Expr::Call(call),
span: call_span,
ty: output,
custom_completion: None,
}]),
err,
);
}
} else {
return (
garbage_pipeline(&lite_command.parts),
Some(ParseError::UnknownState(
format!(
"internal error: '{}' declaration not found",
String::from_utf8_lossy(&full_name)
),
span(&lite_command.parts),
)),
);
};

if &full_name == b"export" {
// export by itself is meaningless
return (
garbage_pipeline(&lite_command.parts),
Some(ParseError::UnexpectedKeyword(
"export".into(),
lite_command.parts[0],
)),
);
}

match full_name.as_slice() {
b"export alias" => parse_alias(working_set, &lite_command.parts, expand_aliases_denylist),
b"export def" | b"export def-env" => {
parse_def(working_set, lite_command, expand_aliases_denylist)
}
b"export use" => {
let (pipeline, _, err) =
parse_use(working_set, &lite_command.parts, expand_aliases_denylist);
(pipeline, err)
}
b"export extern" => parse_extern(working_set, lite_command, expand_aliases_denylist),
_ => (
garbage_pipeline(&lite_command.parts),
Some(ParseError::UnexpectedKeyword(
String::from_utf8_lossy(&full_name).to_string(),
lite_command.parts[0],
)),
),
}
}

// This one will trigger only in a module
pub fn parse_export_in_module(
working_set: &mut StateWorkingSet,
lite_command: &LiteCommand,
expand_aliases_denylist: &[usize],
Expand Down Expand Up @@ -1163,7 +1291,7 @@ pub fn parse_module_block(
// will work only if you call `use foo *; b` but not with `use foo; foo b`
// since in the second case, the name of the env var would be $env."foo a".
b"export" => {
let (pipe, exportables, err) = parse_export(
let (pipe, exportables, err) = parse_export_in_module(
working_set,
&pipeline.commands[0],
expand_aliases_denylist,
Expand Down Expand Up @@ -1309,7 +1437,26 @@ pub fn parse_use(
spans: &[Span],
expand_aliases_denylist: &[usize],
) -> (Pipeline, Vec<Exportable>, Option<ParseError>) {
if working_set.get_span_contents(spans[0]) != b"use" {
let (name_span, split_id) =
if spans.len() > 1 && working_set.get_span_contents(spans[0]) == b"export" {
(spans[1], 2)
} else {
(spans[0], 1)
};

let use_call = working_set.get_span_contents(name_span).to_vec();
if use_call != b"use" {
return (
garbage_pipeline(spans),
vec![],
Some(ParseError::UnknownState(
"internal error: Wrong call name for 'use' command".into(),
span(spans),
)),
);
}

if working_set.get_span_contents(name_span) != b"use" {
return (
garbage_pipeline(spans),
vec![],
Expand All @@ -1322,14 +1469,16 @@ pub fn parse_use(

let (call, call_span, use_decl_id) = match working_set.find_decl(b"use", &Type::Any) {
Some(decl_id) => {
let (command_spans, rest_spans) = spans.split_at(split_id);

let ParsedInternalCall {
call,
error: mut err,
output,
} = parse_internal_call(
working_set,
spans[0],
&spans[1..],
span(command_spans),
rest_spans,
decl_id,
expand_aliases_denylist,
);
Expand Down Expand Up @@ -1420,7 +1569,7 @@ pub fn parse_use(
custom_completion: None,
}]),
vec![],
Some(ParseError::ModuleNotFound(spans[1])),
Some(ParseError::ModuleNotFound(import_pattern.head.span)),
);
};

Expand Down Expand Up @@ -1458,7 +1607,7 @@ pub fn parse_use(
head: ImportPatternHead {
name: module_name.into(),
id: Some(module_id),
span: spans[1],
span: import_pattern.head.span,
},
members: import_pattern.members,
hidden: HashSet::new(),
Expand All @@ -1474,22 +1623,24 @@ pub fn parse_use(
custom_completion: None,
}]),
vec![],
Some(ParseError::ModuleNotFound(spans[1])),
Some(ParseError::ModuleNotFound(import_pattern.head.span)),
);
}
} else {
error = error.or(Some(ParseError::ModuleNotFound(import_pattern.head.span)));

let old_span = import_pattern.head.span;

let mut import_pattern = ImportPattern::new();
import_pattern.head.span = spans[1];
import_pattern.head.span = old_span;

(import_pattern, Module::new())
}
} else {
return (
garbage_pipeline(spans),
vec![],
Some(ParseError::NonUtf8(spans[1])),
Some(ParseError::NonUtf8(import_pattern.head.span)),
);
}
};
Expand Down Expand Up @@ -1565,7 +1716,7 @@ pub fn parse_use(
};

let call = Box::new(Call {
head: spans[0],
head: span(spans.split_at(split_id).0),
decl_id: use_decl_id,
arguments: vec![Argument::Positional(import_pattern_expr)],
redirect_stdout: true,
Expand Down